Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-11-17 12:16:31 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-11-17 12:16:31 +0300
commitf665874e9ee6c28d5098248852f07ae7469d8b2b (patch)
tree21f326636c5fef83972bec8d6e8209665345aa22 /app
parent4cb45018de85caf62c6338988d6a48b8466abdfd (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue39
-rw-r--r--app/controllers/activity_pub/application_controller.rb2
-rw-r--r--app/controllers/activity_pub/projects/releases_controller.rb47
-rw-r--r--app/finders/branches_finder.rb8
-rw-r--r--app/finders/git_refs_finder.rb12
-rw-r--r--app/finders/repositories/tree_finder.rb9
-rw-r--r--app/finders/tags_finder.rb5
-rw-r--r--app/models/activity_pub/releases_subscription.rb6
-rw-r--r--app/models/vulnerability.rb2
-rw-r--r--app/services/activity_pub/projects/releases_follow_service.rb43
-rw-r--r--app/services/activity_pub/projects/releases_subscription_service.rb35
-rw-r--r--app/services/activity_pub/projects/releases_unfollow_service.rb18
12 files changed, 193 insertions, 33 deletions
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 9c4582ece21..ff2ece99f87 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -2,7 +2,6 @@
import { GlFormRadio, GlFormRadioGroup, GlIcon, GlLink, GlTooltipDirective } from '@gitlab/ui';
import { getWeekdayNames } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
const KEY_EVERY_DAY = 'everyDay';
@@ -10,6 +9,12 @@ const KEY_EVERY_WEEK = 'everyWeek';
const KEY_EVERY_MONTH = 'everyMonth';
const KEY_CUSTOM = 'custom';
+const MINUTE = 60; // minute between 0-59
+const HOUR = 24; // hour between 0-23
+const WEEKDAY_INDEX = 7; // week index Sun-Sat
+const DAY = 29; // day between 0-28
+const getRandomCronValue = (max) => Math.floor(Math.random() * max);
+
export default {
components: {
GlFormRadio,
@@ -20,7 +25,6 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [glFeatureFlagMixin()],
props: {
initialCronInterval: {
type: String,
@@ -41,9 +45,10 @@ export default {
data() {
return {
isEditingCustom: false,
- randomHour: this.generateRandomHour(),
- randomWeekDayIndex: this.generateRandomWeekDayIndex(),
- randomDay: this.generateRandomDay(),
+ randomMinute: getRandomCronValue(MINUTE),
+ randomHour: getRandomCronValue(HOUR),
+ randomWeekDayIndex: getRandomCronValue(WEEKDAY_INDEX),
+ randomDay: getRandomCronValue(DAY),
inputNameAttribute: 'schedule[cron]',
radioValue: this.initialCronInterval ? KEY_CUSTOM : KEY_EVERY_DAY,
cronInterval: this.initialCronInterval,
@@ -53,19 +58,22 @@ export default {
computed: {
cronIntervalPresets() {
return {
- [KEY_EVERY_DAY]: `0 ${this.randomHour} * * *`,
- [KEY_EVERY_WEEK]: `0 ${this.randomHour} * * ${this.randomWeekDayIndex}`,
- [KEY_EVERY_MONTH]: `0 ${this.randomHour} ${this.randomDay} * *`,
+ [KEY_EVERY_DAY]: `${this.randomMinute} ${this.randomHour} * * *`,
+ [KEY_EVERY_WEEK]: `${this.randomMinute} ${this.randomHour} * * ${this.randomWeekDayIndex}`,
+ [KEY_EVERY_MONTH]: `${this.randomMinute} ${this.randomHour} ${this.randomDay} * *`,
};
},
+ formattedMinutes() {
+ return String(this.randomMinute).padStart(2, '0');
+ },
formattedTime() {
if (this.randomHour > 12) {
- return `${this.randomHour - 12}:00pm`;
+ return `${this.randomHour - 12}:${this.formattedMinutes}pm`;
}
if (this.randomHour === 12) {
- return `12:00pm`;
+ return `12:${this.formattedMinutes}pm`;
}
- return `${this.randomHour}:00am`;
+ return `${this.randomHour}:${this.formattedMinutes}am`;
},
radioOptions() {
return [
@@ -133,15 +141,6 @@ export default {
onCustomInput() {
this.radioValue = KEY_CUSTOM;
},
- generateRandomHour() {
- return Math.floor(Math.random() * 23);
- },
- generateRandomWeekDayIndex() {
- return Math.floor(Math.random() * 6);
- },
- generateRandomDay() {
- return Math.floor(Math.random() * 28);
- },
showDailyLimitMessage({ value }) {
return value === KEY_CUSTOM && this.dailyLimit;
},
diff --git a/app/controllers/activity_pub/application_controller.rb b/app/controllers/activity_pub/application_controller.rb
index f9c2b14fe77..70cf881c857 100644
--- a/app/controllers/activity_pub/application_controller.rb
+++ b/app/controllers/activity_pub/application_controller.rb
@@ -8,6 +8,8 @@ module ActivityPub
skip_before_action :authenticate_user!
after_action :set_content_type
+ protect_from_forgery with: :null_session
+
def can?(object, action, subject = :global)
Ability.allowed?(object, action, subject)
end
diff --git a/app/controllers/activity_pub/projects/releases_controller.rb b/app/controllers/activity_pub/projects/releases_controller.rb
index 7c4c2a0322b..eeff96a5ef7 100644
--- a/app/controllers/activity_pub/projects/releases_controller.rb
+++ b/app/controllers/activity_pub/projects/releases_controller.rb
@@ -5,15 +5,27 @@ module ActivityPub
class ReleasesController < ApplicationController
feature_category :release_orchestration
+ before_action :enforce_payload, only: :inbox
+
def index
opts = {
- inbox: nil,
+ inbox: inbox_project_releases_url(@project),
outbox: outbox_project_releases_url(@project)
}
render json: ActivityPub::ReleasesActorSerializer.new.represent(@project, opts)
end
+ def inbox
+ service = inbox_service
+ success = service ? service.execute : true
+
+ response = { success: success }
+ response[:errors] = service.errors unless success
+
+ render json: response
+ end
+
def outbox
serializer = ActivityPub::ReleasesOutboxSerializer.new.with_pagination(request, response)
render json: serializer.represent(releases)
@@ -24,6 +36,39 @@ module ActivityPub
def releases(params = {})
ReleasesFinder.new(@project, current_user, params).execute
end
+
+ def enforce_payload
+ return if payload
+
+ head :unprocessable_entity
+ false
+ end
+
+ def payload
+ @payload ||= begin
+ Gitlab::Json.parse(request.body.read)
+ rescue JSON::ParserError
+ nil
+ end
+ end
+
+ def follow?
+ payload['type'] == 'Follow'
+ end
+
+ def unfollow?
+ undo = payload['type'] == 'Undo'
+ object = payload['object']
+ follow = object.present? && object.is_a?(Hash) && object['type'] == 'Follow'
+ undo && follow
+ end
+
+ def inbox_service
+ return ReleasesFollowService.new(project, payload) if follow?
+ return ReleasesUnfollowService.new(project, payload) if unfollow?
+
+ nil
+ end
end
end
end
diff --git a/app/finders/branches_finder.rb b/app/finders/branches_finder.rb
index dc7b9f6a0ce..8f90ce40bb4 100644
--- a/app/finders/branches_finder.rb
+++ b/app/finders/branches_finder.rb
@@ -1,13 +1,11 @@
# frozen_string_literal: true
class BranchesFinder < GitRefsFinder
- def initialize(repository, params = {})
- super(repository, params)
- end
-
def execute(gitaly_pagination: false)
if gitaly_pagination && names.blank? && search.blank? && regex.blank?
- repository.branches_sorted_by(sort, pagination_params)
+ repository.branches_sorted_by(sort, pagination_params).tap do |branches|
+ set_next_cursor(branches)
+ end
else
branches = repository.branches_sorted_by(sort)
branches = by_search(branches)
diff --git a/app/finders/git_refs_finder.rb b/app/finders/git_refs_finder.rb
index 3c8d53051d6..521b4aa171f 100644
--- a/app/finders/git_refs_finder.rb
+++ b/app/finders/git_refs_finder.rb
@@ -3,9 +3,12 @@
class GitRefsFinder
include Gitlab::Utils::StrongMemoize
+ attr_reader :next_cursor
+
def initialize(repository, params = {})
@repository = repository
@params = params
+ @next_cursor = nil
end
protected
@@ -54,4 +57,13 @@ class GitRefsFinder
def unescape_regex_operators(regex_string)
regex_string.sub('\^', '^').gsub('\*', '.*?').sub('\$', '$')
end
+
+ def set_next_cursor(records)
+ return if records.blank?
+
+ # TODO: Gitaly should be responsible for a cursor generation
+ # Follow-up for branches: https://gitlab.com/gitlab-org/gitlab/-/issues/431903
+ # Follow-up for tags: https://gitlab.com/gitlab-org/gitlab/-/issues/431904
+ @next_cursor = records.last.name
+ end
end
diff --git a/app/finders/repositories/tree_finder.rb b/app/finders/repositories/tree_finder.rb
index 2a8971d4d86..8280908ff42 100644
--- a/app/finders/repositories/tree_finder.rb
+++ b/app/finders/repositories/tree_finder.rb
@@ -4,10 +4,13 @@ module Repositories
class TreeFinder
CommitMissingError = Class.new(StandardError)
+ attr_reader :next_cursor
+
def initialize(project, params = {})
@project = project
@repository = project.repository
@params = params
+ @next_cursor = nil
end
def execute(gitaly_pagination: false)
@@ -16,7 +19,11 @@ module Repositories
request_params = { recursive: recursive, rescue_not_found: rescue_not_found }
request_params[:pagination_params] = pagination_params if gitaly_pagination
- repository.tree(commit.id, path, **request_params).sorted_entries
+ tree = repository.tree(commit.id, path, **request_params)
+
+ @next_cursor = tree.cursor&.next_cursor if gitaly_pagination
+
+ tree.sorted_entries
end
def total
diff --git a/app/finders/tags_finder.rb b/app/finders/tags_finder.rb
index 52b1fff4883..a25d17dbaf4 100644
--- a/app/finders/tags_finder.rb
+++ b/app/finders/tags_finder.rb
@@ -8,8 +8,9 @@ class TagsFinder < GitRefsFinder
repository.tags_sorted_by(sort)
end
- by_search(tags)
-
+ by_search(tags).tap do |records|
+ set_next_cursor(records) if gitaly_pagination
+ end
rescue ArgumentError => e
raise Gitlab::Git::InvalidPageToken, "Invalid page token: #{page_token}" if e.message.include?('page token')
diff --git a/app/models/activity_pub/releases_subscription.rb b/app/models/activity_pub/releases_subscription.rb
index a6304f1fc35..0a4293b2bde 100644
--- a/app/models/activity_pub/releases_subscription.rb
+++ b/app/models/activity_pub/releases_subscription.rb
@@ -11,12 +11,12 @@ module ActivityPub
validates :payload, json_schema: { filename: 'activity_pub_follow_payload' }, allow_blank: true
validates :subscriber_url, presence: true, uniqueness: { case_sensitive: false, scope: :project_id },
public_url: true
- validates :subscriber_inbox_url, uniqueness: { case_sensitive: false, scope: :project_id },
+ validates :subscriber_inbox_url, uniqueness: { case_sensitive: false, scope: :project_id, allow_nil: true },
public_url: { allow_nil: true }
validates :shared_inbox_url, public_url: { allow_nil: true }
- def self.find_by_subscriber_url(subscriber_url)
- find_by('LOWER(subscriber_url) = ?', subscriber_url.downcase)
+ def self.find_by_project_and_subscriber(project_id, subscriber_url)
+ find_by('project_id = ? AND LOWER(subscriber_url) = ?', project_id, subscriber_url.downcase)
end
end
end
diff --git a/app/models/vulnerability.rb b/app/models/vulnerability.rb
index abdf585af81..1dff78354db 100644
--- a/app/models/vulnerability.rb
+++ b/app/models/vulnerability.rb
@@ -5,7 +5,7 @@ class Vulnerability < ApplicationRecord
include EachBatch
include IgnorableColumns
- ignore_column :milestone_id, remove_with: '16.9', remove_after: '2023-01-13'
+ ignore_column %i[epic_id milestone_id], remove_with: '16.9', remove_after: '2023-01-13'
alias_attribute :vulnerability_id, :id
diff --git a/app/services/activity_pub/projects/releases_follow_service.rb b/app/services/activity_pub/projects/releases_follow_service.rb
new file mode 100644
index 00000000000..3d877a1d083
--- /dev/null
+++ b/app/services/activity_pub/projects/releases_follow_service.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+module ActivityPub
+ module Projects
+ class ReleasesFollowService < ReleasesSubscriptionService
+ def execute
+ unless subscriber_url
+ errors << "You need to provide an actor id for your subscriber"
+ return false
+ end
+
+ return true if previous_subscription.present?
+
+ subscription = ReleasesSubscription.new(
+ subscriber_url: subscriber_url,
+ subscriber_inbox_url: subscriber_inbox_url,
+ payload: payload,
+ project: project
+ )
+
+ unless subscription.save
+ errors.concat(subscription.errors.full_messages)
+ return false
+ end
+
+ enqueue_subscription(subscription)
+ true
+ end
+
+ private
+
+ def subscriber_inbox_url
+ return unless payload['actor'].is_a?(Hash)
+
+ payload['actor']['inbox']
+ end
+
+ def enqueue_subscription(subscription)
+ ReleasesSubscriptionWorker.perform_async(subscription.id)
+ end
+ end
+ end
+end
diff --git a/app/services/activity_pub/projects/releases_subscription_service.rb b/app/services/activity_pub/projects/releases_subscription_service.rb
new file mode 100644
index 00000000000..27d0e19a172
--- /dev/null
+++ b/app/services/activity_pub/projects/releases_subscription_service.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module ActivityPub
+ module Projects
+ class ReleasesSubscriptionService
+ attr_reader :errors
+
+ def initialize(project, payload)
+ @project = project
+ @payload = payload
+ @errors = []
+ end
+
+ def execute
+ raise "not implemented: abstract class, do not use directly."
+ end
+
+ private
+
+ attr_reader :project, :payload
+
+ def subscriber_url
+ return unless payload['actor']
+ return payload['actor'] if payload['actor'].is_a?(String)
+ return unless payload['actor'].is_a?(Hash) && payload['actor']['id'].is_a?(String)
+
+ payload['actor']['id']
+ end
+
+ def previous_subscription
+ @previous_subscription ||= ReleasesSubscription.find_by_project_and_subscriber(project.id, subscriber_url)
+ end
+ end
+ end
+end
diff --git a/app/services/activity_pub/projects/releases_unfollow_service.rb b/app/services/activity_pub/projects/releases_unfollow_service.rb
new file mode 100644
index 00000000000..df5dcefbb87
--- /dev/null
+++ b/app/services/activity_pub/projects/releases_unfollow_service.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module ActivityPub
+ module Projects
+ class ReleasesUnfollowService < ReleasesSubscriptionService
+ def execute
+ unless subscriber_url
+ errors << "You need to provide an actor id for your unsubscribe activity"
+ return false
+ end
+
+ return true unless previous_subscription.present?
+
+ previous_subscription.destroy
+ end
+ end
+ end
+end