Commit ff1fe1dc authored by Etienne Baqué's avatar Etienne Baqué

Merge branch '328470-make-it-clearer-that-another-issue-tracker-is-enabled-frontend' into 'master'

Add alert and disable active checkbox when another issue tracker is enabled

See merge request gitlab-org/gitlab!81896
parents 0d75bf91 048859b8
<script> <script>
import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui'; import { GlFormGroup, GlFormCheckbox } from '@gitlab/ui';
import { mapGetters } from 'vuex'; import { mapGetters, mapState } from 'vuex';
export default { export default {
name: 'ActiveCheckbox', name: 'ActiveCheckbox',
...@@ -15,6 +15,10 @@ export default { ...@@ -15,6 +15,10 @@ export default {
}, },
computed: { computed: {
...mapGetters(['isInheriting', 'propsSource']), ...mapGetters(['isInheriting', 'propsSource']),
...mapState(['customState']),
disabled() {
return this.isInheriting || this.customState.activateDisabled;
},
}, },
mounted() { mounted() {
this.activated = this.propsSource.initialActivated; this.activated = this.propsSource.initialActivated;
...@@ -34,7 +38,7 @@ export default { ...@@ -34,7 +38,7 @@ export default {
<gl-form-checkbox <gl-form-checkbox
v-model="activated" v-model="activated"
class="gl-display-block" class="gl-display-block"
:disabled="isInheriting" :disabled="disabled"
@change="onChange" @change="onChange"
> >
{{ __('Active') }} {{ __('Active') }}
......
...@@ -39,6 +39,7 @@ function parseDatasetToProps(data) { ...@@ -39,6 +39,7 @@ function parseDatasetToProps(data) {
const { const {
showActive, showActive,
activated, activated,
activateDisabled,
editable, editable,
canTest, canTest,
commitEvents, commitEvents,
...@@ -54,6 +55,7 @@ function parseDatasetToProps(data) { ...@@ -54,6 +55,7 @@ function parseDatasetToProps(data) {
return { return {
initialActivated: activated, initialActivated: activated,
showActive, showActive,
activateDisabled,
type, type,
cancelPath, cancelPath,
editable, editable,
......
...@@ -104,7 +104,8 @@ module IntegrationsHelper ...@@ -104,7 +104,8 @@ module IntegrationsHelper
form_data = { form_data = {
id: integration.id, id: integration.id,
show_active: integration.show_active_box?.to_s, show_active: integration.show_active_box?.to_s,
activated: (integration.active || integration.new_record?).to_s, activated: (integration.active || (integration.new_record? && integration.activate_disabled_reason.nil?)).to_s,
activate_disabled: integration.activate_disabled_reason.present?.to_s,
type: integration.to_param, type: integration.to_param,
merge_request_events: integration.merge_requests_events.to_s, merge_request_events: integration.merge_requests_events.to_s,
commit_events: integration.commit_events.to_s, commit_events: integration.commit_events.to_s,
......
...@@ -360,6 +360,10 @@ class Integration < ApplicationRecord ...@@ -360,6 +360,10 @@ class Integration < ApplicationRecord
true true
end end
def activate_disabled_reason
nil
end
def category def category
read_attribute(:category).to_sym read_attribute(:category).to_sym
end end
......
...@@ -132,8 +132,18 @@ module Integrations ...@@ -132,8 +132,18 @@ module Integrations
# implement inside child # implement inside child
end end
def activate_disabled_reason
{ trackers: other_external_issue_trackers } if other_external_issue_trackers.any?
end
private private
def other_external_issue_trackers
return [] unless project_level?
@other_external_issue_trackers ||= project.integrations.external_issue_trackers.where.not(id: id)
end
def enabled_in_gitlab_config def enabled_in_gitlab_config
Gitlab.config.issues_tracker && Gitlab.config.issues_tracker &&
Gitlab.config.issues_tracker.values.any? && Gitlab.config.issues_tracker.values.any? &&
...@@ -148,7 +158,7 @@ module Integrations ...@@ -148,7 +158,7 @@ module Integrations
return if instance? return if instance?
return if project.blank? return if project.blank?
if project.integrations.external_issue_trackers.where.not(id: id).any? if other_external_issue_trackers.any?
errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time')) errors.add(:base, _('Another issue tracker is already in use. Only one issue tracker service can be active at a time'))
end end
end end
......
- if lookup_context.template_exists?('top', "projects/services/#{integration.to_param}", true) - if lookup_context.template_exists?('top', "projects/services/#{integration.to_param}", true)
= render "projects/services/#{integration.to_param}/top", integration: integration = render "projects/services/#{integration.to_param}/top", integration: integration
- if integration.activate_disabled_reason.present? && integration.activate_disabled_reason[:trackers].any?
-# When using integration.activate_disabled_reason[:trackers], it's potentially insecure to use the raw records
-# when passed directly to the frontend. Only use specific fields that are needed for render.
-# For example, we can get the link to each tracker with scoped_edit_integration_path(tracker, tracker.project)
= render 'shared/global_alert',
title: s_('ExternalIssueIntegration|Another issue tracker is already in use'),
variant: :warning,
dismissible: false do
.gl-alert-body
= s_('ExternalIssueIntegration|Only one issue tracker integration can be active at a time. Please disable the active tracker first and try again.')
%h3.page-title %h3.page-title
= integration.title = integration.title
- if integration.operating? - if integration.operating?
......
...@@ -14955,9 +14955,15 @@ msgstr "" ...@@ -14955,9 +14955,15 @@ msgstr ""
msgid "ExternalAuthorization|URL to which the projects make authorization requests. If the URL is blank, cross-project features are available and can still specify classification labels for projects." msgid "ExternalAuthorization|URL to which the projects make authorization requests. If the URL is blank, cross-project features are available and can still specify classification labels for projects."
msgstr "" msgstr ""
msgid "ExternalIssueIntegration|Another issue tracker is already in use"
msgstr ""
msgid "ExternalIssueIntegration|Not all data may be displayed here. To view more details or make changes to this issue, go to %{linkStart}%{trackerName}%{linkEnd}." msgid "ExternalIssueIntegration|Not all data may be displayed here. To view more details or make changes to this issue, go to %{linkStart}%{trackerName}%{linkEnd}."
msgstr "" msgstr ""
msgid "ExternalIssueIntegration|Only one issue tracker integration can be active at a time. Please disable the active tracker first and try again."
msgstr ""
msgid "ExternalIssueIntegration|This issue is synchronized with %{trackerName}" msgid "ExternalIssueIntegration|This issue is synchronized with %{trackerName}"
msgstr "" msgstr ""
......
...@@ -224,6 +224,12 @@ FactoryBot.define do ...@@ -224,6 +224,12 @@ FactoryBot.define do
recipients { 'test@example.com' } recipients { 'test@example.com' }
end end
factory :pivotaltracker_integration, class: 'Integrations::Pivotaltracker' do
project
active { true }
token { 'test' }
end
# this is for testing storing values inside properties, which is deprecated and will be removed in # this is for testing storing values inside properties, which is deprecated and will be removed in
# https://gitlab.com/gitlab-org/gitlab/issues/29404 # https://gitlab.com/gitlab-org/gitlab/issues/29404
trait :without_properties_callback do trait :without_properties_callback do
......
...@@ -35,6 +35,15 @@ describe('ActiveCheckbox', () => { ...@@ -35,6 +35,15 @@ describe('ActiveCheckbox', () => {
}); });
}); });
describe('when activateDisabled is true', () => {
it('renders GlFormCheckbox as disabled', () => {
createComponent({ activateDisabled: true });
expect(findGlFormCheckbox().exists()).toBe(true);
expect(findInputInCheckbox().attributes('disabled')).toBe('disabled');
});
});
describe('initialActivated is `false`', () => { describe('initialActivated is `false`', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
......
...@@ -55,6 +55,7 @@ RSpec.describe IntegrationsHelper do ...@@ -55,6 +55,7 @@ RSpec.describe IntegrationsHelper do
:id, :id,
:show_active, :show_active,
:activated, :activated,
:activate_disabled,
:type, :type,
:merge_request_events, :merge_request_events,
:commit_events, :commit_events,
......
...@@ -3,12 +3,12 @@ ...@@ -3,12 +3,12 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Integrations::BaseIssueTracker do RSpec.describe Integrations::BaseIssueTracker do
describe 'Validations' do
let(:project) { create :project }
describe 'only one issue tracker per project' do
let(:integration) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) } let(:integration) { Integrations::Redmine.new(project: project, active: true, issue_tracker_data: build(:issue_tracker_data)) }
let_it_be_with_refind(:project) { create :project }
describe 'Validations' do
describe 'only one issue tracker per project' do
before do before do
create(:custom_issue_tracker_integration, project: project) create(:custom_issue_tracker_integration, project: project)
end end
...@@ -31,4 +31,18 @@ RSpec.describe Integrations::BaseIssueTracker do ...@@ -31,4 +31,18 @@ RSpec.describe Integrations::BaseIssueTracker do
end end
end end
end end
describe '#activate_disabled_reason' do
subject { integration.activate_disabled_reason }
context 'when there is an existing issue tracker integration' do
let_it_be(:custom_tracker) { create(:custom_issue_tracker_integration, project: project) }
it { is_expected.to eq(trackers: [custom_tracker]) }
end
context 'when there is no existing issue tracker integration' do
it { is_expected.to be(nil) }
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