diff --git a/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue b/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue new file mode 100644 index 0000000000000000000000000000000000000000..bfb16779854fc35c22dde8d738d667d2adafe691 --- /dev/null +++ b/app/assets/javascripts/integrations/overrides/components/integration_overrides.vue @@ -0,0 +1,15 @@ + + + diff --git a/app/assets/javascripts/integrations/overrides/index.js b/app/assets/javascripts/integrations/overrides/index.js new file mode 100644 index 0000000000000000000000000000000000000000..0f03b23ba218d3411bbfa35a1fec288fcd2ca097 --- /dev/null +++ b/app/assets/javascripts/integrations/overrides/index.js @@ -0,0 +1,23 @@ +import Vue from 'vue'; +import IntegrationOverrides from './components/integration_overrides.vue'; + +export default () => { + const el = document.querySelector('.js-vue-integration-overrides'); + + if (!el) { + return null; + } + + const { overridesPath } = el.dataset; + + return new Vue({ + el, + render(createElement) { + return createElement(IntegrationOverrides, { + props: { + overridesPath, + }, + }); + }, + }); +}; diff --git a/app/assets/javascripts/pages/admin/integrations/overrides/index.js b/app/assets/javascripts/pages/admin/integrations/overrides/index.js new file mode 100644 index 0000000000000000000000000000000000000000..b150470914433fac8149178606c5bd5c652b5b8e --- /dev/null +++ b/app/assets/javascripts/pages/admin/integrations/overrides/index.js @@ -0,0 +1,3 @@ +import initIntegrationOverrides from '~/integrations/overrides'; + +initIntegrationOverrides(); diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb index a605ee0297fe239c74a457f85ff22541da5ab624..e273c0269932032d2d8f073d7c2b84de735841ac 100644 --- a/app/controllers/admin/integrations_controller.rb +++ b/app/controllers/admin/integrations_controller.rb @@ -2,13 +2,14 @@ class Admin::IntegrationsController < Admin::ApplicationController include IntegrationsActions - include IntegrationsHelper before_action :not_found, unless: -> { instance_level_integrations? } feature_category :integrations def overrides + return render_404 unless instance_level_integration_overrides? + respond_to do |format| format.json do projects = Project.with_active_integration(integration.class).merge(::Integration.not_inherited) @@ -16,7 +17,7 @@ def overrides render json: serializer.represent(projects) end - # TODO frontend will add format.html + format.html { render 'shared/integrations/overrides' } end end @@ -26,7 +27,7 @@ def find_or_initialize_non_project_specific_integration(name) Integration.find_or_initialize_non_project_specific_integration(name, instance: true) end - def scoped_edit_integration_path(integration) - edit_admin_application_settings_integration_path(integration) + def instance_level_integration_overrides? + Feature.enabled?(:instance_level_integration_overrides, default_enabled: :yaml) end end diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index f1fa5c845e22a93c29524f1cc586543c92d88af9..dd066cc1b02ccb1765155ea190de4bab70786cf4 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -5,8 +5,9 @@ module IntegrationsActions included do include Integrations::Params + include IntegrationsHelper - before_action :integration, only: [:edit, :update, :test] + before_action :integration, only: [:edit, :update, :overrides, :test] end def edit diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb index 8e3b2cb5d1ba63c043c1a12fa19b298384f116f7..a7a1de03224e378498623fd39fb72dcb1fd65890 100644 --- a/app/controllers/groups/settings/integrations_controller.rb +++ b/app/controllers/groups/settings/integrations_controller.rb @@ -26,10 +26,6 @@ def edit def find_or_initialize_non_project_specific_integration(name) Integration.find_or_initialize_non_project_specific_integration(name, group_id: group.id) end - - def scoped_edit_integration_path(integration) - edit_group_settings_integration_path(group, integration) - end end end end diff --git a/app/helpers/integrations_helper.rb b/app/helpers/integrations_helper.rb index ab305d822e834ab61e3623974e5cbc6081fdab79..734820f0e74b1f68fab6a24365b37caa8fd326ba 100644 --- a/app/helpers/integrations_helper.rb +++ b/app/helpers/integrations_helper.rb @@ -47,6 +47,10 @@ def scoped_edit_integration_path(integration) end end + def scoped_overrides_integration_path(integration, options = {}) + overrides_admin_application_settings_integration_path(integration, options) + end + def scoped_test_integration_path(integration) if @project.present? test_project_service_path(@project, integration) @@ -97,6 +101,12 @@ def integration_form_data(integration, group: nil) form_data end + def integration_overrides_data(integration) + { + overrides_path: scoped_overrides_integration_path(integration, format: :json) + } + end + def integration_list_data(integrations) { integrations: integrations.map { |i| serialize_integration(i) }.to_json diff --git a/app/views/shared/integrations/_tabs.html.haml b/app/views/shared/integrations/_tabs.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..ff97e371374d12079f914d11e4e365338a5bd190 --- /dev/null +++ b/app/views/shared/integrations/_tabs.html.haml @@ -0,0 +1,14 @@ +.tabs.gl-tabs + %div + %ul.nav.gl-tabs-nav{ role: 'tablist' } + %li.nav-item{ role: 'presentation' } + %a.nav-link.gl-tab-nav-item{ role: 'tab', href: scoped_edit_integration_path(integration) } + = _('Settings') + + %li.nav-item{ role: 'presentation' } + %a.nav-link.gl-tab-nav-item.gl-tab-nav-item-active.gl-tab-nav-item-active-indigo.active{ role: 'tab', href: scoped_overrides_integration_path(integration) } + = s_('Integrations|Projects using custom settings') + + .tab-content.gl-tab-content + .tab-pane.active{ role: 'tabpanel' } + = yield diff --git a/app/views/shared/integrations/overrides.html.haml b/app/views/shared/integrations/overrides.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..4d8cc94e967bbef562689dce642a4c9b316a7972 --- /dev/null +++ b/app/views/shared/integrations/overrides.html.haml @@ -0,0 +1,10 @@ +- add_to_breadcrumbs _('Integrations'), scoped_integrations_path +- breadcrumb_title @integration.title +- page_title @integration.title, _('Integrations') +- @content_class = 'limit-container-width' unless fluid_layout + +%h3.page-title + = @integration.title + += render 'shared/integrations/tabs', integration: @integration do + .js-vue-integration-overrides{ data: integration_overrides_data(@integration) } diff --git a/config/feature_flags/development/instance_level_integration_overrides.yml b/config/feature_flags/development/instance_level_integration_overrides.yml new file mode 100644 index 0000000000000000000000000000000000000000..f99b85b3c051f3a96cfc19b8ee691185b751acd5 --- /dev/null +++ b/config/feature_flags/development/instance_level_integration_overrides.yml @@ -0,0 +1,8 @@ +--- +name: instance_level_integration_overrides +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66723 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/336750 +milestone: '14.2' +type: development +group: group::ecosystem +default_enabled: false diff --git a/locale/gitlab.pot b/locale/gitlab.pot index de72d782daf86147ec702f539330c099db204527..ca6d44c3274d017ffa8115596c9a9f03c1f6c3c8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17656,6 +17656,9 @@ msgstr "" msgid "Integrations|Note: this integration only works with accounts on GitLab.com (SaaS)." msgstr "" +msgid "Integrations|Projects using custom settings" +msgstr "" + msgid "Integrations|Projects using custom settings will not be affected." msgstr "" diff --git a/spec/controllers/admin/integrations_controller_spec.rb b/spec/controllers/admin/integrations_controller_spec.rb index 617a43b37850d06108519c855db4cb8c3338e04e..3d37fe7bf79e17389f5ad8e99c0558f6832df9f7 100644 --- a/spec/controllers/admin/integrations_controller_spec.rb +++ b/spec/controllers/admin/integrations_controller_spec.rb @@ -105,17 +105,44 @@ let_it_be(:overridden_other_integration) { create(:confluence_integration) } subject do - get :overrides, params: { id: instance_integration.class.to_param }, format: :json + get :overrides, params: { id: instance_integration.class.to_param }, format: format end - include_context 'JSON response' + context 'when format is JSON' do + let(:format) { :json } - it 'returns projects with overrides', :aggregate_failures do - subject + include_context 'JSON response' - expect(response).to have_gitlab_http_status(:ok) - expect(response).to include_pagination_headers - expect(json_response).to contain_exactly(a_hash_including('full_name' => overridden_integration.project.full_name)) + it 'returns projects with overrides', :aggregate_failures do + subject + + expect(response).to have_gitlab_http_status(:ok) + expect(response).to include_pagination_headers + expect(json_response).to contain_exactly(a_hash_including('full_name' => overridden_integration.project.full_name)) + end + end + + context 'when format is HTML' do + let(:format) { :html } + + it 'renders template' do + subject + + expect(response).to render_template 'shared/integrations/overrides' + expect(assigns(:integration)).to eq(instance_integration) + end + + context 'when `instance_level_integration_overrides` is not enabled' do + before do + stub_feature_flags(instance_level_integration_overrides: false) + end + + it 'renders a 404' do + subject + + expect(response).to have_gitlab_http_status(:not_found) + end + end end end end