summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue7
-rw-r--r--app/models/ci/job_artifact.rb7
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/wiki_page.rb4
-rw-r--r--app/models/wiki_page/meta.rb108
-rw-r--r--app/services/event_create_service.rb28
-rw-r--r--app/services/git/wiki_push_service.rb57
-rw-r--r--app/services/git/wiki_push_service/change.rb67
-rw-r--r--app/services/wiki_pages/base_service.rb7
-rw-r--r--app/services/wiki_pages/event_create_service.rb30
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml5
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