diff options
Diffstat (limited to 'app')
| -rw-r--r-- | app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue | 7 | ||||
| -rw-r--r-- | app/models/ci/job_artifact.rb | 7 | ||||
| -rw-r--r-- | app/models/event.rb | 2 | ||||
| -rw-r--r-- | app/models/wiki_page.rb | 4 | ||||
| -rw-r--r-- | app/models/wiki_page/meta.rb | 108 | ||||
| -rw-r--r-- | app/services/event_create_service.rb | 28 | ||||
| -rw-r--r-- | app/services/git/wiki_push_service.rb | 57 | ||||
| -rw-r--r-- | app/services/git/wiki_push_service/change.rb | 67 | ||||
| -rw-r--r-- | app/services/wiki_pages/base_service.rb | 7 | ||||
| -rw-r--r-- | app/services/wiki_pages/event_create_service.rb | 30 | ||||
| -rw-r--r-- | app/views/layouts/nav/sidebar/_admin.html.haml | 5 |
11 files changed, 258 insertions, 64 deletions
diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue index 5d0e39e8195..e106afea9f5 100644 --- a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue +++ b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue @@ -40,7 +40,12 @@ export default { <button type="button" class="btn btn-default append-right-10" @click="closeForm"> {{ __('Cancel') }} </button> - <button type="button" class="btn btn-close" @click.prevent="submitForm"> + <button + type="button" + class="btn btn-close" + data-testid="confidential-toggle" + @click.prevent="submitForm" + > {{ toggleButtonText }} </button> </div> diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 3e06fa0957c..5810322b088 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -35,7 +35,8 @@ module Ci lsif: 'lsif.json', dotenv: '.env', cobertura: 'cobertura-coverage.xml', - terraform: 'tfplan.json' + terraform: 'tfplan.json', + cluster_applications: 'gl-cluster-applications.json' }.freeze INTERNAL_TYPES = { @@ -52,6 +53,7 @@ module Ci lsif: :gzip, dotenv: :gzip, cobertura: :gzip, + cluster_applications: :gzip, # All these file formats use `raw` as we need to store them uncompressed # for Frontend to fetch the files and do analysis @@ -153,7 +155,8 @@ module Ci dotenv: 16, cobertura: 17, terraform: 18, # Transformed json - accessibility: 19 + accessibility: 19, + cluster_applications: 20 } enum file_format: { diff --git a/app/models/event.rb b/app/models/event.rb index 48f745649e4..12b85697690 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -96,6 +96,8 @@ class Event < ApplicationRecord end scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) } + scope :for_wiki_meta, ->(meta) { where(target_type: 'WikiPage::Meta', target_id: meta.id) } + scope :created_at, ->(time) { where(created_at: time) } # Authors are required as they're used to display who pushed data. # diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index b3fc4941b12..319cdd38d93 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -295,6 +295,10 @@ class WikiPage 'wiki_page' end + def version_commit_timestamp + version&.commit&.committed_date + end + private def serialize_front_matter(hash) diff --git a/app/models/wiki_page/meta.rb b/app/models/wiki_page/meta.rb index 2af7d86ebcc..474968122b1 100644 --- a/app/models/wiki_page/meta.rb +++ b/app/models/wiki_page/meta.rb @@ -5,6 +5,7 @@ class WikiPage include Gitlab::Utils::StrongMemoize CanonicalSlugConflictError = Class.new(ActiveRecord::RecordInvalid) + WikiPageInvalid = Class.new(ArgumentError) self.table_name = 'wiki_page_meta' @@ -23,46 +24,62 @@ class WikiPage alias_method :resource_parent, :project - # Return the (updated) WikiPage::Meta record for a given wiki page - # - # If none is found, then a new record is created, and its fields are set - # to reflect the wiki_page passed. - # - # @param [String] last_known_slug - # @param [WikiPage] wiki_page - # - # As with all `find_or_create` methods, this one raises errors on - # validation issues. - def self.find_or_create(last_known_slug, wiki_page) - project = wiki_page.wiki.project - known_slugs = [last_known_slug, wiki_page.slug].compact.uniq - raise 'no slugs!' if known_slugs.empty? - - transaction do - found = find_by_canonical_slug(known_slugs, project) - meta = found || create(title: wiki_page.title, project_id: project.id) - - meta.update_state(found.nil?, known_slugs, wiki_page) - - # We don't need to run validations here, since find_by_canonical_slug - # guarantees that there is no conflict in canonical_slug, and DB - # constraints on title and project_id enforce our other invariants - # This saves us a query. - meta + class << self + # Return the (updated) WikiPage::Meta record for a given wiki page + # + # If none is found, then a new record is created, and its fields are set + # to reflect the wiki_page passed. + # + # @param [String] last_known_slug + # @param [WikiPage] wiki_page + # + # This method raises errors on validation issues. + def find_or_create(last_known_slug, wiki_page) + raise WikiPageInvalid unless wiki_page.valid? + + project = wiki_page.wiki.project + known_slugs = [last_known_slug, wiki_page.slug].compact.uniq + raise 'No slugs found! This should not be possible.' if known_slugs.empty? + + transaction do + updates = wiki_page_updates(wiki_page) + found = find_by_canonical_slug(known_slugs, project) + meta = found || create!(updates.merge(project_id: project.id)) + + meta.update_state(found.nil?, known_slugs, wiki_page, updates) + + # We don't need to run validations here, since find_by_canonical_slug + # guarantees that there is no conflict in canonical_slug, and DB + # constraints on title and project_id enforce our other invariants + # This saves us a query. + meta + end end - end - def self.find_by_canonical_slug(canonical_slug, project) - meta, conflict = with_canonical_slug(canonical_slug) - .where(project_id: project.id) - .limit(2) + def find_by_canonical_slug(canonical_slug, project) + meta, conflict = with_canonical_slug(canonical_slug) + .where(project_id: project.id) + .limit(2) - if conflict.present? - meta.errors.add(:canonical_slug, 'Duplicate value found') - raise CanonicalSlugConflictError.new(meta) + if conflict.present? + meta.errors.add(:canonical_slug, 'Duplicate value found') + raise CanonicalSlugConflictError.new(meta) + end + + meta end - meta + private + + def wiki_page_updates(wiki_page) + last_commit_date = wiki_page.version_commit_timestamp || Time.now.utc + + { + title: wiki_page.title, + created_at: last_commit_date, + updated_at: last_commit_date + } + end end def canonical_slug @@ -85,24 +102,21 @@ class WikiPage @canonical_slug = slug end - def update_state(created, known_slugs, wiki_page) - update_wiki_page_attributes(wiki_page) + def update_state(created, known_slugs, wiki_page, updates) + update_wiki_page_attributes(updates) insert_slugs(known_slugs, created, wiki_page.slug) self.canonical_slug = wiki_page.slug end - def update_columns(attrs = {}) - super(attrs.reverse_merge(updated_at: Time.now.utc)) - end - - def self.update_all(attrs = {}) - super(attrs.reverse_merge(updated_at: Time.now.utc)) - end - private - def update_wiki_page_attributes(page) - update_columns(title: page.title) unless page.title == title + def update_wiki_page_attributes(updates) + # Remove all unnecessary updates: + updates.delete(:updated_at) if updated_at == updates[:updated_at] + updates.delete(:created_at) if created_at <= updates[:created_at] + updates.delete(:title) if title == updates[:title] + + update_columns(updates) unless updates.empty? end def insert_slugs(strings, is_new, canonical_slug) diff --git a/app/services/event_create_service.rb b/app/services/event_create_service.rb index 0b044e1679a..522f36cda46 100644 --- a/app/services/event_create_service.rb +++ b/app/services/event_create_service.rb @@ -85,18 +85,40 @@ class EventCreateService # Create a new wiki page event # # @param [WikiPage::Meta] wiki_page_meta The event target - # @param [User] current_user The event author + # @param [User] author The event author # @param [Integer] action One of the Event::WIKI_ACTIONS - def wiki_event(wiki_page_meta, current_user, action) + # + # @return a tuple of event and either :found or :created + def wiki_event(wiki_page_meta, author, action) return unless Feature.enabled?(:wiki_events) raise IllegalActionError, action unless Event::WIKI_ACTIONS.include?(action) - create_record_event(wiki_page_meta, current_user, action) + if duplicate = existing_wiki_event(wiki_page_meta, action) + return duplicate + end + + event = create_record_event(wiki_page_meta, author, action) + # Ensure that the event is linked in time to the metadata, for non-deletes + unless action == Event::DESTROYED + time_stamp = wiki_page_meta.updated_at + event.update_columns(updated_at: time_stamp, created_at: time_stamp) + end + + event end private + def existing_wiki_event(wiki_page_meta, action) + if action == Event::DESTROYED + most_recent = Event.for_wiki_meta(wiki_page_meta).recent.first + return most_recent if most_recent.present? && most_recent.action == action + else + Event.for_wiki_meta(wiki_page_meta).created_at(wiki_page_meta.updated_at).first + end + end + def create_record_event(record, current_user, status) create_event(record.resource_parent, current_user, status, target_id: record.id, target_type: record.class.name) end diff --git a/app/services/git/wiki_push_service.rb b/app/services/git/wiki_push_service.rb index d4267d4a3c5..8bdbc28f3e8 100644 --- a/app/services/git/wiki_push_service.rb +++ b/app/services/git/wiki_push_service.rb @@ -2,8 +2,63 @@ module Git class WikiPushService < ::BaseService + # Maximum number of change events we will process on any single push + MAX_CHANGES = 100 + def execute - # This is used in EE + process_changes + end + + private + + def process_changes + return unless can_process_wiki_events? + + push_changes.take(MAX_CHANGES).each do |change| # rubocop:disable CodeReuse/ActiveRecord + next unless change.page.present? + + response = create_event_for(change) + log_error(response.message) if response.error? + end + end + + def can_process_wiki_events? + Feature.enabled?(:wiki_events) && Feature.enabled?(:wiki_events_on_git_push, project) + end + + def push_changes + default_branch_changes.flat_map do |change| + raw_changes(change).map { |raw| Git::WikiPushService::Change.new(wiki, change, raw) } + end + end + + def raw_changes(change) + wiki.repository.raw.raw_changes_between(change[:oldrev], change[:newrev]) + end + + def wiki + project.wiki + end + + def create_event_for(change) + event_service.execute(change.last_known_slug, change.page, change.event_action) + end + + def event_service + @event_service ||= WikiPages::EventCreateService.new(current_user) + end + + def on_default_branch?(change) + project.wiki.default_branch == ::Gitlab::Git.branch_name(change[:ref]) + end + + # See: [Gitlab::GitPostReceive#changes] + def changes + params[:changes] || [] + end + + def default_branch_changes + @default_branch_changes ||= changes.select { |change| on_default_branch?(change) } end end end diff --git a/app/services/git/wiki_push_service/change.rb b/app/services/git/wiki_push_service/change.rb new file mode 100644 index 00000000000..8685850165a --- /dev/null +++ b/app/services/git/wiki_push_service/change.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Git + class WikiPushService + class Change + include Gitlab::Utils::StrongMemoize + + # @param [ProjectWiki] wiki + # @param [Hash] change - must have keys `:oldrev` and `:newrev` + # @param [Gitlab::Git::RawDiffChange] raw_change + def initialize(project_wiki, change, raw_change) + @wiki, @raw_change, @change = project_wiki, raw_change, change + end + + def page + strong_memoize(:page) { wiki.find_page(slug, revision) } + end + + # See [Gitlab::Git::RawDiffChange#extract_operation] for the + # definition of the full range of operation values. + def event_action + case raw_change.operation + when :added + Event::CREATED + when :deleted + Event::DESTROYED + else + Event::UPDATED + end + end + + def last_known_slug + strip_extension(raw_change.old_path || raw_change.new_path) + end + + private + + attr_reader :raw_change, :change, :wiki + + def filename + return raw_change.old_path if deleted? + + raw_change.new_path + end + + def slug + strip_extension(filename) + end + + def revision + return change[:oldrev] if deleted? + + change[:newrev] + end + + def deleted? + raw_change.operation == :deleted + end + + def strip_extension(filename) + return unless filename + + File.basename(filename, File.extname(filename)) + end + end + end +end diff --git a/app/services/wiki_pages/base_service.rb b/app/services/wiki_pages/base_service.rb index 2e774973ca5..2a2cbd7f7be 100644 --- a/app/services/wiki_pages/base_service.rb +++ b/app/services/wiki_pages/base_service.rb @@ -46,12 +46,9 @@ module WikiPages def create_wiki_event(page) return unless ::Feature.enabled?(:wiki_events) - slug = slug_for_page(page) + response = WikiPages::EventCreateService.new(current_user).execute(slug_for_page(page), page, event_action) - Event.transaction do - wiki_page_meta = WikiPage::Meta.find_or_create(slug, page) - EventCreateService.new.wiki_event(wiki_page_meta, current_user, event_action) - end + log_error(response.message) if response.error? end def slug_for_page(page) diff --git a/app/services/wiki_pages/event_create_service.rb b/app/services/wiki_pages/event_create_service.rb new file mode 100644 index 00000000000..18a45d057a9 --- /dev/null +++ b/app/services/wiki_pages/event_create_service.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module WikiPages + class EventCreateService + # @param [User] author The event author + def initialize(author) + raise ArgumentError, 'author must not be nil' unless author + + @author = author + end + + def execute(slug, page, action) + return ServiceResponse.success(message: 'No event created as `wiki_events` feature is disabled') unless ::Feature.enabled?(:wiki_events) + + event = Event.transaction do + wiki_page_meta = WikiPage::Meta.find_or_create(slug, page) + + ::EventCreateService.new.wiki_event(wiki_page_meta, author, action) + end + + ServiceResponse.success(payload: { event: event }) + rescue ::EventCreateService::IllegalActionError, ::ActiveRecord::ActiveRecordError => e + ServiceResponse.error(message: e.message, payload: { error: e }) + end + + private + + attr_reader :author + end +end diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml index 52964dd6739..3151368bb3f 100644 --- a/app/views/layouts/nav/sidebar/_admin.html.haml +++ b/app/views/layouts/nav/sidebar/_admin.html.haml @@ -271,11 +271,6 @@ = link_to network_admin_application_settings_path, title: _('Network'), data: { qa_selector: 'admin_settings_network_item' } do %span = _('Network') - - if template_exists?('admin/geo/settings/show') - = nav_link do - = link_to geo_admin_application_settings_path, title: _('Geo') do - %span - = _('Geo') = nav_link(path: 'application_settings#preferences') do = link_to preferences_admin_application_settings_path, title: _('Preferences'), data: { qa_selector: 'admin_settings_preferences_link' } do %span |
