Commit 5fcbbf27 authored by Peter Leitzen's avatar Peter Leitzen

Merge branch 'alert-endpoint' into 'master'

Moving alerts endpoint configuration

See merge request gitlab-org/gitlab!35187
parents bb368ce0 da018269
...@@ -64,6 +64,11 @@ export default { ...@@ -64,6 +64,11 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
isDisabled: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -118,6 +123,9 @@ export default { ...@@ -118,6 +123,9 @@ export default {
.then(() => { .then(() => {
this.activated = value; this.activated = value;
this.loadingActivated = false; this.loadingActivated = false;
if (value) {
window.location.reload();
}
}) })
.catch(() => { .catch(() => {
createFlash(__('Update failed. Please try again.')); createFlash(__('Update failed. Please try again.'));
...@@ -142,7 +150,7 @@ export default { ...@@ -142,7 +150,7 @@ export default {
<gl-form-group :label="__('Active')" label-for="activated" label-class="label-bold"> <gl-form-group :label="__('Active')" label-for="activated" label-class="label-bold">
<toggle-button <toggle-button
id="activated" id="activated"
:disabled-input="loadingActivated" :disabled-input="loadingActivated || isDisabled"
:is-loading="loadingActivated" :is-loading="loadingActivated"
:value="activated" :value="activated"
@change="toggleActivated" @change="toggleActivated"
...@@ -152,7 +160,11 @@ export default { ...@@ -152,7 +160,11 @@ export default {
<div class="input-group"> <div class="input-group">
<gl-form-input id="url" :readonly="true" :value="url" /> <gl-form-input id="url" :readonly="true" :value="url" />
<span class="input-group-append"> <span class="input-group-append">
<clipboard-button :text="url" :title="$options.COPY_TO_CLIPBOARD" /> <clipboard-button
:text="url"
:title="$options.COPY_TO_CLIPBOARD"
:disabled="isDisabled"
/>
</span> </span>
</div> </div>
</gl-form-group> </gl-form-group>
...@@ -164,10 +176,16 @@ export default { ...@@ -164,10 +176,16 @@ export default {
<div class="input-group"> <div class="input-group">
<gl-form-input id="authorization-key" :readonly="true" :value="authorizationKey" /> <gl-form-input id="authorization-key" :readonly="true" :value="authorizationKey" />
<span class="input-group-append"> <span class="input-group-append">
<clipboard-button :text="authorizationKey" :title="$options.COPY_TO_CLIPBOARD" /> <clipboard-button
:text="authorizationKey"
:title="$options.COPY_TO_CLIPBOARD"
:disabled="isDisabled"
/>
</span> </span>
</div> </div>
<gl-button v-gl-modal.authKeyModal class="mt-2">{{ $options.RESET_KEY }}</gl-button> <gl-button v-gl-modal.authKeyModal class="mt-2" :disabled="isDisabled">{{
$options.RESET_KEY
}}</gl-button>
<gl-modal <gl-modal
modal-id="authKeyModal" modal-id="authKeyModal"
:title="$options.RESET_KEY" :title="$options.RESET_KEY"
......
...@@ -14,8 +14,11 @@ export default el => { ...@@ -14,8 +14,11 @@ export default el => {
formPath, formPath,
authorizationKey, authorizationKey,
url, url,
disabled,
} = el.dataset; } = el.dataset;
const activated = parseBoolean(activatedStr); const activated = parseBoolean(activatedStr);
const isDisabled = parseBoolean(disabled);
return new Vue({ return new Vue({
el, el,
...@@ -28,6 +31,7 @@ export default el => { ...@@ -28,6 +31,7 @@ export default el => {
formPath, formPath,
initialAuthorizationKey: authorizationKey, initialAuthorizationKey: authorizationKey,
url, url,
isDisabled,
}, },
}); });
}, },
......
import mountErrorTrackingForm from '~/error_tracking_settings'; import mountErrorTrackingForm from '~/error_tracking_settings';
import initAlertsSettings from '~/alerts_service_settings';
import mountOperationSettings from '~/operation_settings'; import mountOperationSettings from '~/operation_settings';
import mountGrafanaIntegration from '~/grafana_integration'; import mountGrafanaIntegration from '~/grafana_integration';
import initSettingsPanels from '~/settings_panels'; import initSettingsPanels from '~/settings_panels';
...@@ -10,4 +11,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -10,4 +11,5 @@ document.addEventListener('DOMContentLoaded', () => {
if (!IS_EE) { if (!IS_EE) {
initSettingsPanels(); initSettingsPanels();
} }
initAlertsSettings(document.querySelector('.js-alerts-service-settings'));
}); });
...@@ -5,6 +5,7 @@ const PERSISTENT_USER_CALLOUTS = [ ...@@ -5,6 +5,7 @@ const PERSISTENT_USER_CALLOUTS = [
'.js-users-over-license-callout', '.js-users-over-license-callout',
'.js-admin-licensed-user-count-threshold', '.js-admin-licensed-user-count-threshold',
'.js-buy-pipeline-minutes-notification-callout', '.js-buy-pipeline-minutes-notification-callout',
'.js-alerts-moved-alert',
]; ];
const initCallouts = () => { const initCallouts = () => {
......
...@@ -11,7 +11,7 @@ module Projects ...@@ -11,7 +11,7 @@ module Projects
helper_method :error_tracking_setting helper_method :error_tracking_setting
def show def show
render locals: { prometheus_service: prometheus_service } render locals: { prometheus_service: prometheus_service, alerts_service: alerts_service }
end end
def update def update
...@@ -52,6 +52,10 @@ module Projects ...@@ -52,6 +52,10 @@ module Projects
project.find_or_initialize_service(::PrometheusService.to_param) project.find_or_initialize_service(::PrometheusService.to_param)
end end
def alerts_service
project.find_or_initialize_service(::AlertsService.to_param)
end
def render_update_response(result) def render_update_response(result)
respond_to do |format| respond_to do |format|
format.html do format.html do
......
...@@ -7,6 +7,7 @@ module UserCalloutsHelper ...@@ -7,6 +7,7 @@ module UserCalloutsHelper
SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed' SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'
TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight' TABS_POSITION_HIGHLIGHT = 'tabs_position_highlight'
WEBHOOKS_MOVED = 'webhooks_moved' WEBHOOKS_MOVED = 'webhooks_moved'
ALERTS_MOVED = 'alerts_moved'
def show_admin_integrations_moved? def show_admin_integrations_moved?
!user_dismissed?(ADMIN_INTEGRATIONS_MOVED) !user_dismissed?(ADMIN_INTEGRATIONS_MOVED)
...@@ -43,6 +44,10 @@ module UserCalloutsHelper ...@@ -43,6 +44,10 @@ module UserCalloutsHelper
!user_dismissed?(WEBHOOKS_MOVED) !user_dismissed?(WEBHOOKS_MOVED)
end end
def show_alerts_moved_alert?
!user_dismissed?(ALERTS_MOVED)
end
private private
def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil) def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil)
......
...@@ -17,7 +17,8 @@ module UserCalloutEnums ...@@ -17,7 +17,8 @@ module UserCalloutEnums
suggest_popover_dismissed: 9, suggest_popover_dismissed: 9,
tabs_position_highlight: 10, tabs_position_highlight: 10,
webhooks_moved: 13, webhooks_moved: 13,
admin_integrations_moved: 15 admin_integrations_moved: 15,
alerts_moved: 20
} }
end end
end end
......
- if lookup_context.template_exists?('top', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/top"
.row.gl-mt-3.gl-mb-3 .row.gl-mt-3.gl-mb-3
.col-lg-4 .col-lg-4
%h4.gl-mt-0 %h4.gl-mt-0
...@@ -12,7 +15,7 @@ ...@@ -12,7 +15,7 @@
.col-lg-8 .col-lg-8
= form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form| = form_for(@service, as: :service, url: scoped_integration_path(@service), method: :put, html: { class: 'gl-show-field-errors integration-settings-form js-integration-settings-form', data: { 'can-test' => @service.can_test?, 'test-url' => test_project_service_path(@project, @service) } }) do |form|
= render 'shared/service_settings', form: form, integration: @service = render 'shared/service_settings', form: form, integration: @service
.footer-block.row-content-block .footer-block.row-content-block{ :class => "#{'gl-display-none' if @service.is_a?(AlertsService)}" }
%input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer } %input{ id: 'services_redirect_to', type: 'hidden', name: 'redirect_to', value: request.referrer }
= service_save_button = service_save_button
&nbsp; &nbsp;
......
...@@ -2,5 +2,6 @@ ...@@ -2,5 +2,6 @@
form_path: scoped_integration_path(@service), form_path: scoped_integration_path(@service),
authorization_key: @service.token, authorization_key: @service.token,
url: @service.url || _('<namespace / project>'), url: @service.url || _('<namespace / project>'),
disabled: 'true',
alerts_setup_url: help_page_path('user/project/integrations/generic_alerts.html', anchor: 'setting-up-generic-alerts'), alerts_setup_url: help_page_path('user/project/integrations/generic_alerts.html', anchor: 'setting-up-generic-alerts'),
alerts_usage_url: help_page_path('user/project/operations/alert_management.html') } } alerts_usage_url: help_page_path('user/project/operations/alert_management.html') } }
- return unless show_alerts_moved_alert?
.row
.col-lg-12
.gl-alert.gl-alert-info.js-alerts-moved-alert{ role: 'alert', data: { feature_id: UserCalloutsHelper::ALERTS_MOVED, dismiss_endpoint: user_callouts_path } }
= sprite_icon('information-o', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
%button.js-close.gl-alert-dismiss{ type: 'button', 'aria-label' => _('Dismiss') }
= sprite_icon('close', size: 16, css_class: 'gl-icon')
.gl-alert-body
= _('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page. Fields on this page have been deprecated.')
.gl-alert-actions
= link_to _('Visit settings page'), project_settings_operations_path(@project), class: 'btn gl-alert-action btn-info new-gl-button'
- return unless can?(current_user, :admin_operations, @project)
%section.settings.no-animate.js-alert-management-settings
.settings-header
%h3{ :class => "h4" }
= _('Alerts')
%button.btn.js-settings-toggle{ type: 'button' }
= _('Expand')
%p
= _('Display alerts from all your monitoring tools directly within GitLab.')
= link_to _('More information'), help_page_path('user/project/operations/alert_management'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
.js-alerts-service-settings{ data: { activated: service.activated?.to_s,
form_path: scoped_integration_path(service),
authorization_key: service.token,
url: service.url || _('<namespace / project>'),
alerts_setup_url: help_page_path('user/project/integrations/generic_alerts.html', anchor: 'setting-up-generic-alerts'),
alerts_usage_url: project_alert_management_index_path(@project) } }
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
- page_title _('Operations Settings') - page_title _('Operations Settings')
- breadcrumb_title _('Operations Settings') - breadcrumb_title _('Operations Settings')
= render 'projects/settings/operations/alert_management', service: alerts_service
= render 'projects/settings/operations/incidents' = render 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking' = render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service) = render 'projects/settings/operations/prometheus', service: prometheus_service if Feature.enabled?(:settings_operations_prometheus_service)
......
---
title: Move configuration for Alerts endpoint from "Settings > Integration" to "Settings > Operations > Alerts"
merge_request: 35187
author:
type: other
...@@ -3,8 +3,15 @@ ...@@ -3,8 +3,15 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'projects/settings/operations/show' do RSpec.describe 'projects/settings/operations/show' do
let(:project) { create(:project, :repository) } let_it_be(:project, refind: true) { create(:project, :repository) }
let(:error_tracking_setting) { create(:project_error_tracking_setting, project: project) } let_it_be(:error_tracking_setting) { create(:project_error_tracking_setting, project: project) }
let(:operations_show_locals) do
{
prometheus_service: project.find_or_initialize_service('prometheus'),
alerts_service: project.find_or_initialize_service('alerts')
}
end
before do before do
assign(:project, project) assign(:project, project)
...@@ -26,7 +33,7 @@ RSpec.describe 'projects/settings/operations/show' do ...@@ -26,7 +33,7 @@ RSpec.describe 'projects/settings/operations/show' do
end end
it 'links to project.tracing_external_url' do it 'links to project.tracing_external_url' do
render template: "projects/settings/operations/show", locals: { prometheus_service: project.find_or_initialize_service('prometheus') } render template: 'projects/settings/operations/show', locals: operations_show_locals
expect(rendered).to have_link('Tracing', href: tracing_url) expect(rendered).to have_link('Tracing', href: tracing_url)
end end
...@@ -40,7 +47,7 @@ RSpec.describe 'projects/settings/operations/show' do ...@@ -40,7 +47,7 @@ RSpec.describe 'projects/settings/operations/show' do
end end
it 'sanitizes external_url' do it 'sanitizes external_url' do
render template: "projects/settings/operations/show", locals: { prometheus_service: project.find_or_initialize_service('prometheus') } render template: 'projects/settings/operations/show', locals: operations_show_locals
expect(tracing_setting.external_url).to eq(malicious_tracing_url) expect(tracing_setting.external_url).to eq(malicious_tracing_url)
expect(rendered).to have_link('Tracing', href: cleaned_url) expect(rendered).to have_link('Tracing', href: cleaned_url)
...@@ -59,7 +66,7 @@ RSpec.describe 'projects/settings/operations/show' do ...@@ -59,7 +66,7 @@ RSpec.describe 'projects/settings/operations/show' do
end end
it 'links to Tracing page' do it 'links to Tracing page' do
render template: "projects/settings/operations/show", locals: { prometheus_service: project.find_or_initialize_service('prometheus') } render template: 'projects/settings/operations/show', locals: operations_show_locals
expect(rendered).to have_link('Tracing', href: project_tracing_path(project)) expect(rendered).to have_link('Tracing', href: project_tracing_path(project))
end end
......
...@@ -8009,6 +8009,9 @@ msgstr "" ...@@ -8009,6 +8009,9 @@ msgstr ""
msgid "Dismissed on pipeline %{pipelineLink} at %{projectLink}" msgid "Dismissed on pipeline %{pipelineLink} at %{projectLink}"
msgstr "" msgstr ""
msgid "Display alerts from all your monitoring tools directly within GitLab."
msgstr ""
msgid "Display name" msgid "Display name"
msgstr "" msgstr ""
...@@ -25389,6 +25392,9 @@ msgstr "" ...@@ -25389,6 +25392,9 @@ msgstr ""
msgid "VisibilityLevel|Unknown" msgid "VisibilityLevel|Unknown"
msgstr "" msgstr ""
msgid "Visit settings page"
msgstr ""
msgid "VisualReviewApp|%{stepStart}Step 1%{stepEnd}. Copy the following script:" msgid "VisualReviewApp|%{stepStart}Step 1%{stepEnd}. Copy the following script:"
msgstr "" msgstr ""
...@@ -26111,6 +26117,9 @@ msgstr "" ...@@ -26111,6 +26117,9 @@ msgstr ""
msgid "You can now export your security dashboard to a CSV report." msgid "You can now export your security dashboard to a CSV report."
msgstr "" msgstr ""
msgid "You can now manage alert endpoint configuration in the Alerts section on the Operations settings page. Fields on this page have been deprecated."
msgstr ""
msgid "You can now submit a merge request to get this change into the original branch." msgid "You can now submit a merge request to get this change into the original branch."
msgstr "" msgstr ""
......
...@@ -15,35 +15,32 @@ RSpec.describe 'User activates Alerts', :js do ...@@ -15,35 +15,32 @@ RSpec.describe 'User activates Alerts', :js do
end end
context 'when service is deactivated' do context 'when service is deactivated' do
it 'activates service' do it 'user cannot activate service' do
visit_project_services visit_project_services
expect(page).to have_link(service_title) expect(page).to have_link(service_title)
click_link(service_title) click_link(service_title)
expect(page).to have_callout_message
expect(page).not_to have_active_service expect(page).not_to have_active_service
expect(page).to have_toggle_active_disabled
click_activate_service
wait_for_requests
expect(page).to have_active_service
end end
end end
context 'when service is activated' do context 'when service is activated' do
let_it_be(:activated_alerts_service) do
create(:alerts_service, :active, project: project)
end
before do before do
visit_alerts_service visit_alerts_service
click_activate_service
end end
it 're-generates key' do it 'user cannot change settings' do
expect(reset_key.value).to be_blank expect(page).to have_callout_message
expect(page).to have_active_service
click_reset_key expect(page).to have_toggle_active_disabled
click_confirm_reset_key expect(page).to have_button_reset_key_disabled
wait_for_requests
expect(reset_key.value).to be_present
end end
end end
...@@ -57,25 +54,21 @@ RSpec.describe 'User activates Alerts', :js do ...@@ -57,25 +54,21 @@ RSpec.describe 'User activates Alerts', :js do
visit(edit_project_service_path(project, service_name)) visit(edit_project_service_path(project, service_name))
end end
def click_activate_service def have_callout_message
find('#activated').click within('.gl-alert') do
end have_content('You can now manage alert endpoint configuration in the Alerts section on the Operations settings page.')
end
def click_reset_key
click_button('Reset key')
end end
def click_confirm_reset_key def have_active_service
within '.modal-content' do have_selector('.js-service-active-status[data-value="true"]')
click_reset_key
end
end end
def reset_key def have_toggle_active_disabled
find_field('Authorization key') have_selector('#activated .project-feature-toggle.is-disabled')
end end
def have_active_service def have_button_reset_key_disabled
have_selector('.js-service-active-status[data-value="true"]') have_button('Reset key', disabled: true)
end end
end end
...@@ -15,6 +15,7 @@ const defaultProps = { ...@@ -15,6 +15,7 @@ const defaultProps = {
alertsSetupUrl: 'http://invalid', alertsSetupUrl: 'http://invalid',
alertsUsageUrl: 'http://invalid', alertsUsageUrl: 'http://invalid',
initialActivated: false, initialActivated: false,
isDisabled: false,
}; };
describe('AlertsServiceForm', () => { describe('AlertsServiceForm', () => {
...@@ -166,4 +167,17 @@ describe('AlertsServiceForm', () => { ...@@ -166,4 +167,17 @@ describe('AlertsServiceForm', () => {
}); });
}); });
}); });
describe('form is disabled', () => {
beforeEach(() => {
createComponent({ isDisabled: true });
});
it('cannot be toggled', () => {
wrapper.find(ToggleButton).vm.$emit('change');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(ToggleButton).props('disabledInput')).toBe(true);
});
});
});
}); });
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
require "spec_helper" require "spec_helper"
RSpec.describe UserCalloutsHelper do RSpec.describe UserCalloutsHelper do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
before do before do
allow(helper).to receive(:current_user).and_return(user) allow(helper).to receive(:current_user).and_return(user)
end end
describe '.show_gke_cluster_integration_callout?' do describe '.show_gke_cluster_integration_callout?' do
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
subject { helper.show_gke_cluster_integration_callout?(project) } subject { helper.show_gke_cluster_integration_callout?(project) }
...@@ -67,6 +67,26 @@ RSpec.describe UserCalloutsHelper do ...@@ -67,6 +67,26 @@ RSpec.describe UserCalloutsHelper do
end end
end end
describe '.show_alerts_moved_alert?' do
subject { helper.show_alerts_moved_alert? }
context 'when user has not dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::ALERTS_MOVED) { false }
end
it { is_expected.to be true }
end
context 'when user dismissed' do
before do
allow(helper).to receive(:user_dismissed?).with(described_class::ALERTS_MOVED) { true }
end
it { is_expected.to be false }
end
end
describe '.render_flash_user_callout' do describe '.render_flash_user_callout' do
it 'renders the flash_user_callout partial' do it 'renders the flash_user_callout partial' do
expect(helper).to receive(:render) expect(helper).to receive(:render)
......
...@@ -3,8 +3,15 @@ ...@@ -3,8 +3,15 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'projects/settings/operations/show' do RSpec.describe 'projects/settings/operations/show' do
let(:project) { create(:project) } let_it_be(:project) { create(:project) }
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:operations_show_locals) do
{
prometheus_service: project.find_or_initialize_service('prometheus'),
alerts_service: project.find_or_initialize_service('alerts')
}
end
before do before do
assign :project, project assign :project, project
...@@ -20,13 +27,13 @@ RSpec.describe 'projects/settings/operations/show' do ...@@ -20,13 +27,13 @@ RSpec.describe 'projects/settings/operations/show' do
allow(view).to receive(:incident_management_available?) { false } allow(view).to receive(:incident_management_available?) { false }
end end
let!(:error_tracking_setting) do let_it_be(:error_tracking_setting) do
create(:project_error_tracking_setting, project: project) create(:project_error_tracking_setting, project: project)
end end
context 'Settings page ' do context 'Settings page ' do
it 'renders the Operations Settings page' do it 'renders the Operations Settings page' do
render template: "projects/settings/operations/show", locals: { prometheus_service: project.find_or_initialize_service('prometheus') } render template: 'projects/settings/operations/show', locals: operations_show_locals
expect(rendered).to have_content _('Error Tracking') expect(rendered).to have_content _('Error Tracking')
expect(rendered).to have_content _('To link Sentry to GitLab, enter your Sentry URL and Auth Token') expect(rendered).to have_content _('To link Sentry to GitLab, enter your Sentry URL and Auth Token')
......
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