Commit a2f1404b authored by Arturo Herrero's avatar Arturo Herrero Committed by Mayra Cabrera

Find admin integration with group-level integrations

After adding the group_id column to the services table
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/38499, we can now
save integrations that belongs to a group
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39959.

Integrations can inherit the admin integration from the UI selector.
Now, having group-level integration we need to find the right
integration to inherit the values from.

We have to find the closest group integration or instance-level
integration.
parent 884a1e61
......@@ -12,7 +12,7 @@ module Groups
end
def edit
@admin_integration = Service.instance_for(integration.type)
@admin_integration = Service.default_integration(integration.type, group)
super
end
......
......@@ -20,7 +20,7 @@ class Projects::ServicesController < Projects::ApplicationController
layout "project_settings"
def edit
@admin_integration = Service.instance_for(service.type)
@admin_integration = Service.default_integration(service.type, project)
end
def update
......
......@@ -33,6 +33,7 @@ class Group < Namespace
has_many :milestones
has_many :iterations
has_many :services
has_many :shared_group_links, foreign_key: :shared_with_group_id, class_name: 'GroupGroupLink'
has_many :shared_with_group_links, foreign_key: :shared_group_id, class_name: 'GroupGroupLink'
has_many :shared_groups, through: :shared_group_links, source: :shared_group
......
......@@ -46,6 +46,7 @@ class Service < ApplicationRecord
after_commit :cache_project_has_external_wiki
belongs_to :project, inverse_of: :services
belongs_to :group, inverse_of: :services
has_one :service_hook
validates :project_id, presence: true, unless: -> { template? || instance? || group_id }
......@@ -236,10 +237,25 @@ class Service < ApplicationRecord
exists?(instance: true, type: type)
end
def self.instance_for(type)
find_by(instance: true, type: type)
def self.default_integration(type, scope)
closest_group_integration(type, scope) || instance_level_integration(type)
end
def self.closest_group_integration(type, scope)
group_ids = scope.ancestors.select(:id)
array = group_ids.to_sql.present? ? "array(#{group_ids.to_sql})" : 'ARRAY[]'
where(type: type, group_id: group_ids)
.order(Arel.sql("array_position(#{array}::bigint[], services.group_id)"))
.first
end
private_class_method :closest_group_integration
def self.instance_level_integration(type)
find_by(type: type, instance: true)
end
private_class_method :instance_level_integration
def activated?
active
end
......
......@@ -14,7 +14,7 @@ RSpec.describe IdInOrdered do
expect(Issue.id_in_ordered([issue3.id, issue1.id, issue4.id, issue5.id, issue2.id])).to eq([
issue3, issue1, issue4, issue5, issue2
])
])
end
context 'when the ids are not an array of integers' do
......
......@@ -27,6 +27,7 @@ RSpec.describe Group do
it { is_expected.to have_many(:milestones) }
it { is_expected.to have_many(:iterations) }
it { is_expected.to have_many(:group_deploy_keys) }
it { is_expected.to have_many(:services) }
describe '#members & #requesters' do
let(:requester) { create(:user) }
......
......@@ -8,6 +8,7 @@ RSpec.describe Service do
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to belong_to :group }
it { is_expected.to have_one :service_hook }
it { is_expected.to have_one :jira_tracker_data }
it { is_expected.to have_one :issue_tracker_data }
......@@ -419,29 +420,49 @@ RSpec.describe Service do
end
end
describe 'instance' do
describe '.instance_for' do
let_it_be(:jira_service) { create(:jira_service, :instance) }
let_it_be(:slack_service) { create(:slack_service, :instance) }
describe '.default_integration' do
context 'with an instance-level service' do
let_it_be(:instance_service) { create(:jira_service, :instance) }
subject { described_class.instance_for(type) }
it 'returns the instance service' do
expect(described_class.default_integration('JiraService', project)).to eq(instance_service)
end
it 'returns nil for nonexistent service type' do
expect(described_class.default_integration('HipchatService', project)).to eq(nil)
end
context 'Hipchat serivce' do
let(:type) { 'HipchatService' }
context 'with a group service' do
let_it_be(:group_service) { create(:jira_service, group_id: group.id, project_id: nil) }
it { is_expected.to eq(nil) }
it 'returns the group service for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(group_service)
end
context 'Jira serivce' do
let(:type) { 'JiraService' }
it 'returns the instance service for a group' do
expect(described_class.default_integration('JiraService', group)).to eq(instance_service)
end
context 'with a subgroup' do
let_it_be(:subgroup) { create(:group, parent: group) }
let!(:project) { create(:project, group: subgroup) }
it { is_expected.to eq(jira_service) }
it 'returns the closest group service for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(group_service)
end
context 'Slack serivce' do
let(:type) { 'SlackService' }
it 'returns the closest group service for a subgroup' do
expect(described_class.default_integration('JiraService', subgroup)).to eq(group_service)
end
context 'having a service' do
let!(:subgroup_service) { create(:jira_service, group_id: subgroup.id, project_id: nil) }
it { is_expected.to eq(slack_service) }
it 'returns the closest group service for a project' do
expect(described_class.default_integration('JiraService', project)).to eq(subgroup_service)
end
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