Commit 30703023 authored by Andy Soiron's avatar Andy Soiron

Merge branch 'refactor/base-third-party-wiki' into 'master'

Refactor: Introduce BaseThirdPartyWiki

See merge request gitlab-org/gitlab!82616
parents a97858b0 ffc0924b
...@@ -96,6 +96,9 @@ class Integration < ApplicationRecord ...@@ -96,6 +96,9 @@ class Integration < ApplicationRecord
validate :validate_belongs_to_project_or_group validate :validate_belongs_to_project_or_group
scope :external_issue_trackers, -> { where(category: 'issue_tracker').active } scope :external_issue_trackers, -> { where(category: 'issue_tracker').active }
# TODO: Will be modified in 15.0
# Details: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/74501#note_744393645
scope :third_party_wikis, -> { where(type: %w[Integrations::Confluence Integrations::Shimo]).active }
scope :by_name, ->(name) { by_type(integration_name_to_type(name)) } scope :by_name, ->(name) { by_type(integration_name_to_type(name)) }
scope :external_wikis, -> { by_name(:external_wiki).active } scope :external_wikis, -> { by_name(:external_wiki).active }
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
......
# frozen_string_literal: true
module Integrations
class BaseThirdPartyWiki < Integration
default_value_for :category, 'third_party_wiki'
validate :only_one_third_party_wiki, if: :activated?, on: :manual_change
after_commit :cache_project_has_integration
def self.supported_events
%w()
end
private
def only_one_third_party_wiki
return unless project_level?
if project.integrations.third_party_wikis.id_not_in(id).any?
errors.add(:base, _('Another third-party wiki is already in use. '\
'Only one third-party wiki integration can be active at a time'))
end
end
def cache_project_has_integration
return unless project && !project.destroyed?
project_setting = project.project_setting
project_setting.public_send("#{project_settings_cache_key}=", active?) # rubocop:disable GitlabSecurity/PublicSend
project_setting.save!
end
def project_settings_cache_key
"has_#{self.class.to_param}"
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
module Integrations module Integrations
class Confluence < Integration class Confluence < BaseThirdPartyWiki
VALID_SCHEME_MATCH = %r{\Ahttps?\Z}.freeze VALID_SCHEME_MATCH = %r{\Ahttps?\Z}.freeze
VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze VALID_HOST_MATCH = %r{\A.+\.atlassian\.net\Z}.freeze
VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze VALID_PATH_MATCH = %r{\A/wiki(/|\Z)}.freeze
...@@ -11,16 +11,10 @@ module Integrations ...@@ -11,16 +11,10 @@ module Integrations
validates :confluence_url, presence: true, if: :activated? validates :confluence_url, presence: true, if: :activated?
validate :validate_confluence_url_is_cloud, if: :activated? validate :validate_confluence_url_is_cloud, if: :activated?
after_commit :cache_project_has_confluence
def self.to_param def self.to_param
'confluence' 'confluence'
end end
def self.supported_events
%w()
end
def title def title
s_('ConfluenceService|Confluence Workspace') s_('ConfluenceService|Confluence Workspace')
end end
...@@ -80,12 +74,5 @@ module Integrations ...@@ -80,12 +74,5 @@ module Integrations
rescue URI::InvalidURIError rescue URI::InvalidURIError
false false
end end
def cache_project_has_confluence
return unless project && !project.destroyed?
project.project_setting.save! unless project.project_setting.persisted?
project.project_setting.update_column(:has_confluence, active?)
end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
module Integrations module Integrations
class Shimo < Integration class Shimo < BaseThirdPartyWiki
prop_accessor :external_wiki_url prop_accessor :external_wiki_url
validates :external_wiki_url, presence: true, public_url: true, if: :activated? validates :external_wiki_url, presence: true, public_url: true, if: :activated?
after_commit :cache_project_has_shimo
def render? def render?
return false unless Feature.enabled?(:shimo_integration, project) return false unless Feature.enabled?(:shimo_integration, project)
...@@ -33,10 +31,6 @@ module Integrations ...@@ -33,10 +31,6 @@ module Integrations
nil nil
end end
def self.supported_events
%w()
end
def fields def fields
[ [
{ {
...@@ -47,14 +41,5 @@ module Integrations ...@@ -47,14 +41,5 @@ module Integrations
} }
] ]
end end
private
def cache_project_has_shimo
return unless project && !project.destroyed?
project.project_setting.save! unless project.project_setting.persisted?
project.project_setting.update_column(:has_shimo, activated?)
end
end end
end end
# frozen_string_literal: true
class MigrateShimoConfluenceServiceCategory < Gitlab::Database::Migration[1.0]
MIGRATION = 'MigrateShimoConfluenceIntegrationCategory'
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 10_000
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
define_batchable_model('integrations').where(type_new: %w[Integrations::Confluence Integrations::Shimo]),
MIGRATION,
DELAY_INTERVAL,
batch_size: BATCH_SIZE,
track_jobs: true)
end
def down
end
end
fcfead40cfa1d75303bd0d392c09b5b0399ce5163e7ad6f8650360fe3adbe85d
\ No newline at end of file
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# The class to migrate category of integrations to third_party_wiki for confluence and shimo
class MigrateShimoConfluenceIntegrationCategory
include Gitlab::Database::DynamicModelHelpers
def perform(start_id, end_id)
define_batchable_model('integrations', connection: ::ActiveRecord::Base.connection)
.where(id: start_id..end_id, type_new: %w[Integrations::Confluence Integrations::Shimo])
.update_all(category: 'third_party_wiki')
mark_job_as_succeeded(start_id, end_id)
end
private
def mark_job_as_succeeded(*arguments)
Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
self.class.name.demodulize,
arguments
)
end
end
end
end
...@@ -4256,6 +4256,9 @@ msgstr "" ...@@ -4256,6 +4256,9 @@ msgstr ""
msgid "Another issue tracker is already in use. Only one issue tracker service can be active at a time" msgid "Another issue tracker is already in use. Only one issue tracker service can be active at a time"
msgstr "" msgstr ""
msgid "Another third-party wiki is already in use. Only one third-party wiki integration can be active at a time"
msgstr ""
msgid "Anti-spam verification" msgid "Anti-spam verification"
msgstr "" msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::MigrateShimoConfluenceIntegrationCategory do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:integrations) { table(:integrations) }
let(:perform) { described_class.new.perform(1, 5) }
before do
namespace = namespaces.create!(name: 'test', path: 'test')
projects.create!(id: 1, namespace_id: namespace.id, name: 'gitlab', path: 'gitlab')
integrations.create!(id: 1, active: true, type_new: "Integrations::SlackSlashCommands",
category: 'chat', project_id: 1)
integrations.create!(id: 3, active: true, type_new: "Integrations::Confluence", category: 'common', project_id: 1)
integrations.create!(id: 5, active: true, type_new: "Integrations::Shimo", category: 'common', project_id: 1)
end
describe '#up' do
it 'updates category to third_party_wiki for Shimo and Confluence' do
perform
expect(integrations.where(category: 'third_party_wiki').count).to eq(2)
expect(integrations.where(category: 'chat').count).to eq(1)
end
end
end
...@@ -17,6 +17,7 @@ RSpec.describe Sidebars::Projects::Panel do ...@@ -17,6 +17,7 @@ RSpec.describe Sidebars::Projects::Panel do
subject { described_class.new(context).instance_variable_get(:@menus) } subject { described_class.new(context).instance_variable_get(:@menus) }
context 'when integration is present and active' do context 'when integration is present and active' do
context 'confluence only' do
let_it_be(:confluence) { create(:confluence_integration, active: true) } let_it_be(:confluence) { create(:confluence_integration, active: true) }
let(:project) { confluence.project } let(:project) { confluence.project }
...@@ -30,6 +31,29 @@ RSpec.describe Sidebars::Projects::Panel do ...@@ -30,6 +31,29 @@ RSpec.describe Sidebars::Projects::Panel do
end end
end end
context 'shimo only' do
let_it_be(:shimo) { create(:shimo_integration, active: true) }
let(:project) { shimo.project }
it 'contains Shimo menu item' do
expect(subject.index { |i| i.is_a?(Sidebars::Projects::Menus::ShimoMenu) }).not_to be_nil
end
end
context 'confluence & shimo' do
let_it_be(:confluence) { create(:confluence_integration, active: true) }
let_it_be(:shimo) { create(:shimo_integration, active: true) }
let(:project) { confluence.project }
it 'contains Confluence menu item, not Shimo' do
expect(subject.index { |i| i.is_a?(Sidebars::Projects::Menus::ConfluenceMenu) }).not_to be_nil
expect(subject.index { |i| i.is_a?(Sidebars::Projects::Menus::ShimoMenu) }).to be_nil
end
end
end
context 'when integration is not present' do context 'when integration is not present' do
it 'does not contain Confluence menu item' do it 'does not contain Confluence menu item' do
expect(subject.index { |i| i.is_a?(Sidebars::Projects::Menus::ConfluenceMenu) }).to be_nil expect(subject.index { |i| i.is_a?(Sidebars::Projects::Menus::ConfluenceMenu) }).to be_nil
......
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe MigrateShimoConfluenceServiceCategory, :migration do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:integrations) { table(:integrations) }
before do
namespace = namespaces.create!(name: 'test', path: 'test')
projects.create!(id: 1, namespace_id: namespace.id, name: 'gitlab', path: 'gitlab')
integrations.create!(id: 1, active: true, type_new: "Integrations::SlackSlashCommands",
category: 'chat', project_id: 1)
integrations.create!(id: 3, active: true, type_new: "Integrations::Confluence", category: 'common', project_id: 1)
integrations.create!(id: 5, active: true, type_new: "Integrations::Shimo", category: 'common', project_id: 1)
end
describe '#up' do
it 'correctly schedules background migrations', :aggregate_failures do
stub_const("#{described_class.name}::BATCH_SIZE", 2)
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(described_class::MIGRATION).to be_scheduled_migration(3, 5)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
end
end
end
end
...@@ -60,6 +60,17 @@ RSpec.describe Integration do ...@@ -60,6 +60,17 @@ RSpec.describe Integration do
end end
describe 'Scopes' do describe 'Scopes' do
describe '.third_party_wikis' do
let!(:integration1) { create(:jira_integration) }
let!(:integration2) { create(:redmine_integration) }
let!(:integration3) { create(:confluence_integration) }
let!(:integration4) { create(:shimo_integration) }
it 'returns the right group integration' do
expect(described_class.third_party_wikis).to contain_exactly(integration3, integration4)
end
end
describe '.with_default_settings' do describe '.with_default_settings' do
it 'returns the correct integrations' do it 'returns the correct integrations' do
instance_integration = create(:integration, :instance) instance_integration = create(:integration, :instance)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Integrations::BaseThirdPartyWiki do
describe 'Validations' do
let_it_be_with_reload(:project) { create(:project) }
describe 'only one third party wiki per project' do
subject(:integration) { create(:shimo_integration, project: project, active: true) }
before_all do
create(:confluence_integration, project: project, active: true)
end
context 'when integration is changed manually by user' do
it 'executes the validation' do
valid = integration.valid?(:manual_change)
expect(valid).to be_falsey
error_message = 'Another third-party wiki is already in use. '\
'Only one third-party wiki integration can be active at a time'
expect(integration.errors[:base]).to include _(error_message)
end
end
context 'when integration is changed internally' do
it 'does not execute the validation' do
expect(integration.valid?).to be_truthy
end
end
context 'when integration is not on the project level' do
subject(:integration) { create(:shimo_integration, :instance, active: true) }
it 'executes the validation' do
expect(integration.valid?(:manual_change)).to be_truthy
end
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment