diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-17 12:16:31 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-11-17 12:16:31 +0300 |
commit | f665874e9ee6c28d5098248852f07ae7469d8b2b (patch) | |
tree | 21f326636c5fef83972bec8d6e8209665345aa22 /app | |
parent | 4cb45018de85caf62c6338988d6a48b8466abdfd (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
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 |