Commit 1634cb5f authored by Thong Kuah's avatar Thong Kuah

Merge branch '273265-github-and-jenkins-only-at-project-level' into 'master'

GitHub and Jenkins only available at project-level

See merge request gitlab-org/gitlab!46583
parents b30b6f5e c2a4bcdf
...@@ -53,7 +53,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -53,7 +53,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
def integrations def integrations
return not_found unless instance_level_integrations? return not_found unless instance_level_integrations?
@integrations = Service.find_or_initialize_all(Service.for_instance).sort_by(&:title) @integrations = Service.find_or_initialize_all_non_project_specific(Service.for_instance).sort_by(&:title)
end end
def update def update
......
...@@ -8,8 +8,8 @@ class Admin::IntegrationsController < Admin::ApplicationController ...@@ -8,8 +8,8 @@ class Admin::IntegrationsController < Admin::ApplicationController
private private
def find_or_initialize_integration(name) def find_or_initialize_non_project_specific_integration(name)
Service.find_or_initialize_integration(name, instance: true) Service.find_or_initialize_non_project_specific_integration(name, instance: true)
end end
def integrations_enabled? def integrations_enabled?
......
...@@ -52,7 +52,7 @@ module IntegrationsActions ...@@ -52,7 +52,7 @@ module IntegrationsActions
def integration def integration
# Using instance variable `@service` still required as it's used in ServiceParams. # Using instance variable `@service` still required as it's used in ServiceParams.
# Should be removed once that is refactored to use `@integration`. # Should be removed once that is refactored to use `@integration`.
@integration = @service ||= find_or_initialize_integration(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables @integration = @service ||= find_or_initialize_non_project_specific_integration(params[:id]) # rubocop:disable Gitlab/ModuleWithInstanceVariables
end end
def success_message def success_message
......
...@@ -10,7 +10,7 @@ module Groups ...@@ -10,7 +10,7 @@ module Groups
feature_category :integrations feature_category :integrations
def index def index
@integrations = Service.find_or_initialize_all(Service.for_group(group)).sort_by(&:title) @integrations = Service.find_or_initialize_all_non_project_specific(Service.for_group(group)).sort_by(&:title)
end end
def edit def edit
...@@ -21,8 +21,8 @@ module Groups ...@@ -21,8 +21,8 @@ module Groups
private private
def find_or_initialize_integration(name) def find_or_initialize_non_project_specific_integration(name)
Service.find_or_initialize_integration(name, group_id: group.id) Service.find_or_initialize_non_project_specific_integration(name, group_id: group.id)
end end
def integrations_enabled? def integrations_enabled?
......
...@@ -5,7 +5,7 @@ module Types ...@@ -5,7 +5,7 @@ module Types
class ServiceTypeEnum < BaseEnum class ServiceTypeEnum < BaseEnum
graphql_name 'ServiceType' graphql_name 'ServiceType'
::Service.services_types.each do |service_type| ::Service.available_services_types(include_dev: false).each do |service_type|
value service_type.underscore.upcase, value: service_type value service_type.underscore.upcase, value: service_type
end end
end end
......
...@@ -1341,8 +1341,7 @@ class Project < ApplicationRecord ...@@ -1341,8 +1341,7 @@ class Project < ApplicationRecord
end end
def find_or_initialize_services def find_or_initialize_services
available_services_names = available_services_names = Service.available_services_names - disabled_services
Service.available_services_names + Service.project_specific_services_names - disabled_services
available_services_names.map do |service_name| available_services_names.map do |service_name|
find_or_initialize_service(service_name) find_or_initialize_service(service_name)
......
...@@ -17,6 +17,7 @@ class Service < ApplicationRecord ...@@ -17,6 +17,7 @@ class Service < ApplicationRecord
pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack pivotaltracker prometheus pushover redmine slack slack_slash_commands teamcity unify_circuit webex_teams youtrack
].freeze ].freeze
# Fake services to help with local development.
DEV_SERVICE_NAMES = %w[ DEV_SERVICE_NAMES = %w[
mock_ci mock_deployment mock_monitoring mock_ci mock_deployment mock_monitoring
].freeze ].freeze
...@@ -65,9 +66,9 @@ class Service < ApplicationRecord ...@@ -65,9 +66,9 @@ class Service < ApplicationRecord
scope :by_type, -> (type) { where(type: type) } scope :by_type, -> (type) { where(type: type) }
scope :by_active_flag, -> (flag) { where(active: flag) } scope :by_active_flag, -> (flag) { where(active: flag) }
scope :inherit_from_id, -> (id) { where(inherit_from_id: id) } scope :inherit_from_id, -> (id) { where(inherit_from_id: id) }
scope :for_group, -> (group) { where(group_id: group, type: available_services_types) } scope :for_group, -> (group) { where(group_id: group, type: available_services_types(include_project_specific: false)) }
scope :for_template, -> { where(template: true, type: available_services_types) } scope :for_template, -> { where(template: true, type: available_services_types(include_project_specific: false)) }
scope :for_instance, -> { where(instance: true, type: available_services_types) } scope :for_instance, -> { where(instance: true, type: available_services_types(include_project_specific: false)) }
scope :push_hooks, -> { where(push_events: true, active: true) } scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) } scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
...@@ -168,13 +169,13 @@ class Service < ApplicationRecord ...@@ -168,13 +169,13 @@ class Service < ApplicationRecord
end end
private_class_method :create_nonexistent_templates private_class_method :create_nonexistent_templates
def self.find_or_initialize_integration(name, instance: false, group_id: nil) def self.find_or_initialize_non_project_specific_integration(name, instance: false, group_id: nil)
if name.in?(available_services_names) if name.in?(available_services_names(include_project_specific: false))
"#{name}_service".camelize.constantize.find_or_initialize_by(instance: instance, group_id: group_id) "#{name}_service".camelize.constantize.find_or_initialize_by(instance: instance, group_id: group_id)
end end
end end
def self.find_or_initialize_all(scope) def self.find_or_initialize_all_non_project_specific(scope)
scope + build_nonexistent_services_for(scope) scope + build_nonexistent_services_for(scope)
end end
...@@ -188,13 +189,14 @@ class Service < ApplicationRecord ...@@ -188,13 +189,14 @@ class Service < ApplicationRecord
def self.list_nonexistent_services_for(scope) def self.list_nonexistent_services_for(scope)
# Using #map instead of #pluck to save one query count. This is because # Using #map instead of #pluck to save one query count. This is because
# ActiveRecord loaded the object here, so we don't need to query again later. # ActiveRecord loaded the object here, so we don't need to query again later.
available_services_types - scope.map(&:type) available_services_types(include_project_specific: false) - scope.map(&:type)
end end
private_class_method :list_nonexistent_services_for private_class_method :list_nonexistent_services_for
def self.available_services_names def self.available_services_names(include_project_specific: true, include_dev: true)
service_names = services_names service_names = services_names
service_names += dev_services_names service_names += project_specific_services_names if include_project_specific
service_names += dev_services_names if include_dev
service_names.sort_by(&:downcase) service_names.sort_by(&:downcase)
end end
...@@ -213,12 +215,10 @@ class Service < ApplicationRecord ...@@ -213,12 +215,10 @@ class Service < ApplicationRecord
[] []
end end
def self.available_services_types def self.available_services_types(include_project_specific: true, include_dev: true)
available_services_names.map { |service_name| "#{service_name}_service".camelize } available_services_names(include_project_specific: include_project_specific, include_dev: include_dev).map do |service_name|
end "#{service_name}_service".camelize
end
def self.services_types
services_names.map { |service_name| "#{service_name}_service".camelize }
end end
def self.build_from_integration(integration, project_id: nil, group_id: nil) def self.build_from_integration(integration, project_id: nil, group_id: nil)
......
...@@ -58259,6 +58259,12 @@ ...@@ -58259,6 +58259,12 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "GITHUB_SERVICE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "HANGOUTS_CHAT_SERVICE", "name": "HANGOUTS_CHAT_SERVICE",
"description": null, "description": null,
...@@ -58277,6 +58283,12 @@ ...@@ -58277,6 +58283,12 @@
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
}, },
{
"name": "JENKINS_SERVICE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{ {
"name": "JIRA_SERVICE", "name": "JIRA_SERVICE",
"description": null, "description": null,
...@@ -58372,18 +58384,6 @@ ...@@ -58372,18 +58384,6 @@
"description": null, "description": null,
"isDeprecated": false, "isDeprecated": false,
"deprecationReason": null "deprecationReason": null
},
{
"name": "GITHUB_SERVICE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "JENKINS_SERVICE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
} }
], ],
"possibleTypes": null "possibleTypes": null
...@@ -4,28 +4,27 @@ module EE ...@@ -4,28 +4,27 @@ module EE
module Service module Service
extend ActiveSupport::Concern extend ActiveSupport::Concern
EE_SERVICE_NAMES = %w[ EE_COM_PROJECT_SPECIFIC_SERVICE_NAMES = %w[
github gitlab_slack_application
jenkins
].freeze ].freeze
EE_PROJECT_SPECIFIC_SERVICE_NAMES = %w[ EE_PROJECT_SPECIFIC_SERVICE_NAMES = %w[
gitlab_slack_application github
jenkins
].freeze ].freeze
class_methods do class_methods do
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
override :services_names
def services_names
super + EE_SERVICE_NAMES
end
override :project_specific_services_names override :project_specific_services_names
def project_specific_services_names def project_specific_services_names
return super unless ::Gitlab.dev_env_or_com? services = super + EE_PROJECT_SPECIFIC_SERVICE_NAMES
super + EE_PROJECT_SPECIFIC_SERVICE_NAMES if ::Gitlab.com?
services + EE_COM_PROJECT_SPECIFIC_SERVICE_NAMES
else
services
end
end end
end end
end end
......
...@@ -3,32 +3,32 @@ ...@@ -3,32 +3,32 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Service do RSpec.describe Service do
describe 'Available services' do let(:ee_project_services) do
let(:ee_services) do %w[
%w[ github
github jenkins
jenkins ]
] end
end
it { expect(described_class.available_services_names).to include(*ee_services) } describe '.available_services_names' do
it { expect(described_class.available_services_names).to include(*ee_project_services) }
end end
describe '.project_specific_services_names' do describe '.project_specific_services_names' do
before do before do
allow(::Gitlab).to receive(:dev_env_or_com?).and_return(dev_env_or_com) allow(::Gitlab).to receive(:com?).and_return(com)
end end
context 'when not on gitlab.com and not in development environment' do context 'when not on gitlab.com' do
let(:dev_env_or_com) { false } let(:com) { false }
it { expect(described_class.project_specific_services_names).to eq([]) } it { expect(described_class.project_specific_services_names).to match_array(ee_project_services) }
end end
context 'when on gitlab.com or in dev environment' do context 'when on gitlab.com' do
let(:dev_env_or_com) { true } let(:com) { true }
it { expect(described_class.project_specific_services_names).to eq(%w[gitlab_slack_application]) } it { expect(described_class.project_specific_services_names).to match_array(ee_project_services + ['gitlab_slack_application']) }
end end
end end
end end
...@@ -158,7 +158,7 @@ module Gitlab ...@@ -158,7 +158,7 @@ module Gitlab
if Service.available_services_names.include?(underscored_service) if Service.available_services_names.include?(underscored_service)
# We treat underscored_service as a trusted input because it is included # We treat underscored_service as a trusted input because it is included
# in the Service.available_services_names whitelist. # in the Service.available_services_names allowlist.
service = project.public_send("#{underscored_service}_service") # rubocop:disable GitlabSecurity/PublicSend service = project.public_send("#{underscored_service}_service") # rubocop:disable GitlabSecurity/PublicSend
if service && service.activated? && service.valid_token?(password) if service && service.activated? && service.valid_token?(password)
......
...@@ -46,7 +46,7 @@ RSpec.describe Groups::Settings::IntegrationsController do ...@@ -46,7 +46,7 @@ RSpec.describe Groups::Settings::IntegrationsController do
describe '#edit' do describe '#edit' do
context 'when user is not owner' do context 'when user is not owner' do
it 'renders not_found' do it 'renders not_found' do
get :edit, params: { group_id: group, id: Service.available_services_names.sample } get :edit, params: { group_id: group, id: Service.available_services_names(include_project_specific: false).sample }
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
...@@ -61,13 +61,13 @@ RSpec.describe Groups::Settings::IntegrationsController do ...@@ -61,13 +61,13 @@ RSpec.describe Groups::Settings::IntegrationsController do
it 'returns not_found' do it 'returns not_found' do
stub_feature_flags(group_level_integrations: false) stub_feature_flags(group_level_integrations: false)
get :edit, params: { group_id: group, id: Service.available_services_names.sample } get :edit, params: { group_id: group, id: Service.available_services_names(include_project_specific: false).sample }
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
Service.available_services_names.each do |integration_name| Service.available_services_names(include_project_specific: false).each do |integration_name|
context "#{integration_name}" do context "#{integration_name}" do
it 'successfully displays the template' do it 'successfully displays the template' do
get :edit, params: { group_id: group, id: integration_name } get :edit, params: { group_id: group, id: integration_name }
......
...@@ -11,5 +11,5 @@ RSpec.describe GitlabSchema.types['ServiceType'] do ...@@ -11,5 +11,5 @@ RSpec.describe GitlabSchema.types['ServiceType'] do
end end
def available_services_enum def available_services_enum
::Service.services_types.map(&:underscore).map(&:upcase) ::Service.available_services_types(include_dev: false).map(&:underscore).map(&:upcase)
end end
...@@ -5551,15 +5551,16 @@ RSpec.describe Project, factory_default: :keep do ...@@ -5551,15 +5551,16 @@ RSpec.describe Project, factory_default: :keep do
end end
describe '#find_or_initialize_services' do describe '#find_or_initialize_services' do
it 'returns only enabled services' do before do
allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover teamcity]) allow(Service).to receive(:available_services_names).and_return(%w[prometheus pushover teamcity])
allow(Service).to receive(:project_specific_services_names).and_return(%w[asana])
allow(subject).to receive(:disabled_services).and_return(%w[prometheus]) allow(subject).to receive(:disabled_services).and_return(%w[prometheus])
end
it 'returns only enabled services' do
services = subject.find_or_initialize_services services = subject.find_or_initialize_services
expect(services.count).to eq(3) expect(services.count).to eq(2)
expect(services.map(&:title)).to eq(['Asana', 'JetBrains TeamCity CI', 'Pushover']) expect(services.map(&:title)).to eq(['JetBrains TeamCity CI', 'Pushover'])
end end
end end
......
...@@ -208,27 +208,27 @@ RSpec.describe Service do ...@@ -208,27 +208,27 @@ RSpec.describe Service do
end end
end end
describe '.find_or_initialize_integration' do describe '.find_or_initialize_non_project_specific_integration' do
let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) } let!(:service1) { create(:jira_service, project_id: nil, group_id: group.id) }
let!(:service2) { create(:jira_service) } let!(:service2) { create(:jira_service) }
it 'returns the right service' do it 'returns the right service' do
expect(Service.find_or_initialize_integration('jira', group_id: group)).to eq(service1) expect(Service.find_or_initialize_non_project_specific_integration('jira', group_id: group)).to eq(service1)
end end
it 'does not create a new service' do it 'does not create a new service' do
expect { Service.find_or_initialize_integration('redmine', group_id: group) }.not_to change { Service.count } expect { Service.find_or_initialize_non_project_specific_integration('redmine', group_id: group) }.not_to change { Service.count }
end end
end end
describe '.find_or_initialize_all' do describe '.find_or_initialize_all_non_project_specific' do
shared_examples 'service instances' do shared_examples 'service instances' do
it 'returns the available service instances' do it 'returns the available service instances' do
expect(Service.find_or_initialize_all(Service.for_instance).pluck(:type)).to match_array(Service.available_services_types) expect(Service.find_or_initialize_all_non_project_specific(Service.for_instance).pluck(:type)).to match_array(Service.available_services_types(include_project_specific: false))
end end
it 'does not create service instances' do it 'does not create service instances' do
expect { Service.find_or_initialize_all(Service.for_instance) }.not_to change { Service.count } expect { Service.find_or_initialize_all_non_project_specific(Service.for_instance) }.not_to change { Service.count }
end end
end end
...@@ -237,7 +237,7 @@ RSpec.describe Service do ...@@ -237,7 +237,7 @@ RSpec.describe Service do
context 'with all existing instances' do context 'with all existing instances' do
before do before do
Service.insert_all( Service.insert_all(
Service.available_services_types.map { |type| { instance: true, type: type } } Service.available_services_types(include_project_specific: false).map { |type| { instance: true, type: type } }
) )
end end
...@@ -265,13 +265,13 @@ RSpec.describe Service do ...@@ -265,13 +265,13 @@ RSpec.describe Service do
describe 'template' do describe 'template' do
shared_examples 'retrieves service templates' do shared_examples 'retrieves service templates' do
it 'returns the available service templates' do it 'returns the available service templates' do
expect(Service.find_or_create_templates.pluck(:type)).to match_array(Service.available_services_types) expect(Service.find_or_create_templates.pluck(:type)).to match_array(Service.available_services_types(include_project_specific: false))
end end
end end
describe '.find_or_create_templates' do describe '.find_or_create_templates' do
it 'creates service templates' do it 'creates service templates' do
expect { Service.find_or_create_templates }.to change { Service.count }.from(0).to(Service.available_services_names.size) expect { Service.find_or_create_templates }.to change { Service.count }.from(0).to(Service.available_services_names(include_project_specific: false).size)
end end
it_behaves_like 'retrieves service templates' it_behaves_like 'retrieves service templates'
...@@ -279,7 +279,7 @@ RSpec.describe Service do ...@@ -279,7 +279,7 @@ RSpec.describe Service do
context 'with all existing templates' do context 'with all existing templates' do
before do before do
Service.insert_all( Service.insert_all(
Service.available_services_types.map { |type| { template: true, type: type } } Service.available_services_types(include_project_specific: false).map { |type| { template: true, type: type } }
) )
end end
...@@ -305,7 +305,7 @@ RSpec.describe Service do ...@@ -305,7 +305,7 @@ RSpec.describe Service do
end end
it 'creates the rest of the service templates' do it 'creates the rest of the service templates' do
expect { Service.find_or_create_templates }.to change { Service.count }.from(1).to(Service.available_services_names.size) expect { Service.find_or_create_templates }.to change { Service.count }.from(1).to(Service.available_services_names(include_project_specific: false).size)
end end
it_behaves_like 'retrieves service templates' it_behaves_like 'retrieves service templates'
...@@ -891,4 +891,30 @@ RSpec.describe Service do ...@@ -891,4 +891,30 @@ RSpec.describe Service do
end end
end end
end end
describe '.available_services_names' do
it 'calls the right methods' do
expect(described_class).to receive(:services_names).and_call_original
expect(described_class).to receive(:dev_services_names).and_call_original
expect(described_class).to receive(:project_specific_services_names).and_call_original
described_class.available_services_names
end
it 'does not call project_specific_services_names with include_project_specific false' do
expect(described_class).to receive(:services_names).and_call_original
expect(described_class).to receive(:dev_services_names).and_call_original
expect(described_class).not_to receive(:project_specific_services_names)
described_class.available_services_names(include_project_specific: false)
end
it 'does not call dev_services_names with include_dev false' do
expect(described_class).to receive(:services_names).and_call_original
expect(described_class).not_to receive(:dev_services_names)
expect(described_class).to receive(:project_specific_services_names).and_call_original
described_class.available_services_names(include_dev: false)
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