Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-16 00:08:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-16 00:08:49 +0300
commitcf1d4237a4f226ba2deed26240544da0675a41e5 (patch)
tree926a71b9279659bc52db0187b463603934718bf2 /app
parent0ac82f99553ce12009970a14c0afc02d1f6515bb (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/alerts_service_settings/components/alerts_service_form.vue168
-rw-r--r--app/assets/javascripts/alerts_service_settings/index.js27
-rw-r--r--app/assets/javascripts/editor/editor_lite.js6
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue2
-rw-r--r--app/assets/javascripts/ide/index.js2
-rw-r--r--app/assets/javascripts/ide/lib/editor.js11
-rw-r--r--app/assets/javascripts/ide/lib/themes/dark.js268
-rw-r--r--app/assets/javascripts/ide/lib/themes/gl_theme.js15
-rw-r--r--app/assets/javascripts/ide/lib/themes/index.js15
-rw-r--r--app/assets/javascripts/ide/lib/themes/white.js12
-rw-r--r--app/assets/javascripts/ide/stores/state.js2
-rw-r--r--app/assets/javascripts/pages/projects/services/edit/index.js3
-rw-r--r--app/assets/stylesheets/page_bundles/ide.scss4
-rw-r--r--app/controllers/projects/settings/operations_controller.rb21
-rw-r--r--app/helpers/projects_helper.rb5
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/service.rb1
-rw-r--r--app/services/projects/operations/update_service.rb5
-rw-r--r--app/views/projects/services/alerts/_help.html.haml3
-rw-r--r--app/views/projects/settings/operations/_incidents.html.haml32
-rw-r--r--app/views/projects/settings/operations/show.html.haml2
22 files changed, 580 insertions, 28 deletions
diff --git a/app/assets/javascripts/alerts_service_settings/components/alerts_service_form.vue b/app/assets/javascripts/alerts_service_settings/components/alerts_service_form.vue
new file mode 100644
index 00000000000..5e16f6f3873
--- /dev/null
+++ b/app/assets/javascripts/alerts_service_settings/components/alerts_service_form.vue
@@ -0,0 +1,168 @@
+<script>
+import { GlButton, GlFormGroup, GlFormInput, GlModal, GlModalDirective } from '@gitlab/ui';
+import _ from 'underscore';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import ToggleButton from '~/vue_shared/components/toggle_button.vue';
+import axios from '~/lib/utils/axios_utils';
+import { s__, __, sprintf } from '~/locale';
+import createFlash from '~/flash';
+
+export default {
+ COPY_TO_CLIPBOARD: __('Copy'),
+ RESET_KEY: __('Reset key'),
+ components: {
+ GlButton,
+ GlFormGroup,
+ GlFormInput,
+ GlModal,
+ ClipboardButton,
+ ToggleButton,
+ },
+ directives: {
+ 'gl-modal': GlModalDirective,
+ },
+ props: {
+ initialAuthorizationKey: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ formPath: {
+ type: String,
+ required: true,
+ },
+ url: {
+ type: String,
+ required: true,
+ },
+ learnMoreUrl: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ initialActivated: {
+ type: Boolean,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ activated: this.initialActivated,
+ loadingActivated: false,
+ authorizationKey: this.initialAuthorizationKey,
+ };
+ },
+ computed: {
+ learnMoreDescription() {
+ return sprintf(
+ s__(
+ 'AlertService|%{linkStart}Learn more%{linkEnd} about configuring this endpoint to receive alerts.',
+ ),
+ {
+ linkStart: `<a href="${_.escape(
+ this.learnMoreUrl,
+ )}" target="_blank" rel="noopener noreferrer">`,
+ linkEnd: '</a>',
+ },
+ false,
+ );
+ },
+ sectionDescription() {
+ const desc = s__(
+ 'AlertService|Each alert source must be authorized using the following URL and authorization key.',
+ );
+ const learnMoreDesc = this.learnMoreDescription ? ` ${this.learnMoreDescription}` : '';
+
+ return `${desc}${learnMoreDesc}`;
+ },
+ },
+ watch: {
+ activated() {
+ this.updateIcon();
+ },
+ },
+ methods: {
+ updateIcon() {
+ return document.querySelectorAll('.js-service-active-status').forEach(icon => {
+ if (icon.dataset.value === this.activated.toString()) {
+ icon.classList.remove('d-none');
+ } else {
+ icon.classList.add('d-none');
+ }
+ });
+ },
+ resetKey() {
+ return axios
+ .put(this.formPath, { service: { token: '' } })
+ .then(res => {
+ this.authorizationKey = res.data.token;
+ })
+ .catch(() => {
+ createFlash(__('Failed to reset key. Please try again.'));
+ });
+ },
+ toggleActivated(value) {
+ this.loadingActivated = true;
+ return axios
+ .put(this.formPath, { service: { active: value } })
+ .then(() => {
+ this.activated = value;
+ this.loadingActivated = false;
+ })
+ .catch(() => {
+ createFlash(__('Update failed. Please try again.'));
+ this.loadingActivated = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <p v-html="sectionDescription"></p>
+ <gl-form-group :label="__('Active')" label-for="activated" label-class="label-bold">
+ <toggle-button
+ id="activated"
+ :disabled-input="loadingActivated"
+ :is-loading="loadingActivated"
+ :value="activated"
+ @change="toggleActivated"
+ />
+ </gl-form-group>
+ <gl-form-group :label="__('URL')" label-for="url" label-class="label-bold">
+ <div class="input-group">
+ <gl-form-input id="url" :readonly="true" :value="url" />
+ <span class="input-group-append">
+ <clipboard-button :text="url" :title="$options.COPY_TO_CLIPBOARD" />
+ </span>
+ </div>
+ </gl-form-group>
+ <gl-form-group
+ :label="__('Authorization key')"
+ label-for="authorization-key"
+ label-class="label-bold"
+ >
+ <div class="input-group">
+ <gl-form-input id="authorization-key" :readonly="true" :value="authorizationKey" />
+ <span class="input-group-append">
+ <clipboard-button :text="authorizationKey" :title="$options.COPY_TO_CLIPBOARD" />
+ </span>
+ </div>
+ <gl-button v-gl-modal.authKeyModal class="mt-2">{{ $options.RESET_KEY }}</gl-button>
+ <gl-modal
+ modal-id="authKeyModal"
+ :title="$options.RESET_KEY"
+ :ok-title="$options.RESET_KEY"
+ ok-variant="danger"
+ @ok="resetKey"
+ >
+ {{
+ __(
+ 'Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
+ )
+ }}
+ </gl-modal>
+ </gl-form-group>
+ </div>
+</template>
diff --git a/app/assets/javascripts/alerts_service_settings/index.js b/app/assets/javascripts/alerts_service_settings/index.js
new file mode 100644
index 00000000000..d49725c6a4d
--- /dev/null
+++ b/app/assets/javascripts/alerts_service_settings/index.js
@@ -0,0 +1,27 @@
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import AlertsServiceForm from './components/alerts_service_form.vue';
+
+export default el => {
+ if (!el) {
+ return null;
+ }
+
+ const { activated: activatedStr, formPath, authorizationKey, url, learnMoreUrl } = el.dataset;
+ const activated = parseBoolean(activatedStr);
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(AlertsServiceForm, {
+ props: {
+ initialActivated: activated,
+ formPath,
+ learnMoreUrl,
+ initialAuthorizationKey: authorizationKey,
+ url,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/editor/editor_lite.js b/app/assets/javascripts/editor/editor_lite.js
index bdfbcf71267..8711f6e65af 100644
--- a/app/assets/javascripts/editor/editor_lite.js
+++ b/app/assets/javascripts/editor/editor_lite.js
@@ -1,5 +1,5 @@
import { editor as monacoEditor, languages as monacoLanguages, Uri } from 'monaco-editor';
-import gitlabTheme from '~/ide/lib/themes/gl_theme';
+import whiteTheme from '~/ide/lib/themes/white';
import { defaultEditorOptions } from '~/ide/lib/editor_options';
import { clearDomElement } from './utils';
@@ -19,8 +19,8 @@ export default class Editor {
}
static setupMonacoTheme() {
- monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
- monacoEditor.setTheme('gitlab');
+ monacoEditor.defineTheme('white', whiteTheme);
+ monacoEditor.setTheme('white');
}
createInstance({ el = undefined, blobPath = '', blobContent = '' } = {}) {
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index c8c3036812e..8af34dcb5cc 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -38,6 +38,7 @@ export default {
'panelResizing',
'currentActivityView',
'renderWhitespaceInCode',
+ 'editorTheme',
]),
...mapGetters([
'currentMergeRequest',
@@ -85,6 +86,7 @@ export default {
editorOptions() {
return {
renderWhitespace: this.renderWhitespaceInCode ? 'all' : 'none',
+ theme: this.editorTheme,
};
},
},
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 4c4166e11f5..a3450522697 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -7,6 +7,7 @@ import store from './stores';
import router from './ide_router';
import { parseBoolean } from '../lib/utils/common_utils';
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
+import { DEFAULT_THEME } from './lib/themes';
Vue.use(Translate);
@@ -51,6 +52,7 @@ export function initIde(el, options = {}) {
this.setInitialData({
clientsidePreviewEnabled: parseBoolean(el.dataset.clientsidePreviewEnabled),
renderWhitespaceInCode: parseBoolean(el.dataset.renderWhitespaceInCode),
+ editorTheme: window.gon?.user_color_scheme || DEFAULT_THEME,
});
},
methods: {
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
index a0f689065aa..3d729463cb4 100644
--- a/app/assets/javascripts/ide/lib/editor.js
+++ b/app/assets/javascripts/ide/lib/editor.js
@@ -6,13 +6,14 @@ import DirtyDiffController from './diff/controller';
import Disposable from './common/disposable';
import ModelManager from './common/model_manager';
import editorOptions, { defaultEditorOptions } from './editor_options';
-import gitlabTheme from './themes/gl_theme';
+import { themes } from './themes';
import keymap from './keymap.json';
import { clearDomElement } from '~/editor/utils';
-function setupMonacoTheme() {
- monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
- monacoEditor.setTheme('gitlab');
+function setupThemes() {
+ themes.forEach(theme => {
+ monacoEditor.defineTheme(theme.name, theme.data);
+ });
}
export default class Editor {
@@ -35,7 +36,7 @@ export default class Editor {
...options,
};
- setupMonacoTheme();
+ setupThemes();
this.debouncedUpdate = _.debounce(() => {
this.updateDimensions();
diff --git a/app/assets/javascripts/ide/lib/themes/dark.js b/app/assets/javascripts/ide/lib/themes/dark.js
new file mode 100644
index 00000000000..96aaa0cbb50
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/themes/dark.js
@@ -0,0 +1,268 @@
+/*
+
+https://github.com/brijeshb42/monaco-themes/blob/master/themes/Tomorrow-Night.json
+
+The MIT License (MIT)
+
+Copyright (c) Brijesh Bittu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+*/
+
+export default {
+ base: 'vs-dark',
+ inherit: true,
+ rules: [
+ {
+ foreground: '969896',
+ token: 'comment',
+ },
+ {
+ foreground: 'ced1cf',
+ token: 'keyword.operator.class',
+ },
+ {
+ foreground: 'ced1cf',
+ token: 'constant.other',
+ },
+ {
+ foreground: 'ced1cf',
+ token: 'source.php.embedded.line',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'variable',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'support.other.variable',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'string.other.link',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'string.regexp',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'entity.name.tag',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'entity.other.attribute-name',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'meta.tag',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'declaration.tag',
+ },
+ {
+ foreground: 'cc6666',
+ token: 'markup.deleted.git_gutter',
+ },
+ {
+ foreground: 'de935f',
+ token: 'constant.numeric',
+ },
+ {
+ foreground: 'de935f',
+ token: 'constant.language',
+ },
+ {
+ foreground: 'de935f',
+ token: 'support.constant',
+ },
+ {
+ foreground: 'de935f',
+ token: 'constant.character',
+ },
+ {
+ foreground: 'de935f',
+ token: 'variable.parameter',
+ },
+ {
+ foreground: 'de935f',
+ token: 'punctuation.section.embedded',
+ },
+ {
+ foreground: 'de935f',
+ token: 'keyword.other.unit',
+ },
+ {
+ foreground: 'f0c674',
+ token: 'entity.name.class',
+ },
+ {
+ foreground: 'f0c674',
+ token: 'entity.name.type.class',
+ },
+ {
+ foreground: 'f0c674',
+ token: 'support.type',
+ },
+ {
+ foreground: 'f0c674',
+ token: 'support.class',
+ },
+ {
+ foreground: 'b5bd68',
+ token: 'string',
+ },
+ {
+ foreground: 'b5bd68',
+ token: 'constant.other.symbol',
+ },
+ {
+ foreground: 'b5bd68',
+ token: 'entity.other.inherited-class',
+ },
+ {
+ foreground: 'b5bd68',
+ token: 'markup.heading',
+ },
+ {
+ foreground: 'b5bd68',
+ token: 'markup.inserted.git_gutter',
+ },
+ {
+ foreground: '8abeb7',
+ token: 'keyword.operator',
+ },
+ {
+ foreground: '8abeb7',
+ token: 'constant.other.color',
+ },
+ {
+ foreground: '81a2be',
+ token: 'entity.name.function',
+ },
+ {
+ foreground: '81a2be',
+ token: 'meta.function-call',
+ },
+ {
+ foreground: '81a2be',
+ token: 'support.function',
+ },
+ {
+ foreground: '81a2be',
+ token: 'keyword.other.special-method',
+ },
+ {
+ foreground: '81a2be',
+ token: 'meta.block-level',
+ },
+ {
+ foreground: '81a2be',
+ token: 'markup.changed.git_gutter',
+ },
+ {
+ foreground: 'b294bb',
+ token: 'keyword',
+ },
+ {
+ foreground: 'b294bb',
+ token: 'storage',
+ },
+ {
+ foreground: 'b294bb',
+ token: 'storage.type',
+ },
+ {
+ foreground: 'b294bb',
+ token: 'entity.name.tag.css',
+ },
+ {
+ foreground: 'ced2cf',
+ background: 'df5f5f',
+ token: 'invalid',
+ },
+ {
+ foreground: 'ced2cf',
+ background: '82a3bf',
+ token: 'meta.separator',
+ },
+ {
+ foreground: 'ced2cf',
+ background: 'b798bf',
+ token: 'invalid.deprecated',
+ },
+ {
+ foreground: 'ffffff',
+ token: 'markup.inserted.diff',
+ },
+ {
+ foreground: 'ffffff',
+ token: 'markup.deleted.diff',
+ },
+ {
+ foreground: 'ffffff',
+ token: 'meta.diff.header.to-file',
+ },
+ {
+ foreground: 'ffffff',
+ token: 'meta.diff.header.from-file',
+ },
+ {
+ foreground: '718c00',
+ token: 'markup.inserted.diff',
+ },
+ {
+ foreground: '718c00',
+ token: 'meta.diff.header.to-file',
+ },
+ {
+ foreground: 'c82829',
+ token: 'markup.deleted.diff',
+ },
+ {
+ foreground: 'c82829',
+ token: 'meta.diff.header.from-file',
+ },
+ {
+ foreground: 'ffffff',
+ background: '4271ae',
+ token: 'meta.diff.header.from-file',
+ },
+ {
+ foreground: 'ffffff',
+ background: '4271ae',
+ token: 'meta.diff.header.to-file',
+ },
+ {
+ foreground: '3e999f',
+ fontStyle: 'italic',
+ token: 'meta.diff.range',
+ },
+ ],
+ colors: {
+ 'editor.foreground': '#C5C8C6',
+ 'editor.background': '#1D1F21',
+ 'editor.selectionBackground': '#373B41',
+ 'editor.lineHighlightBackground': '#282A2E',
+ 'editorCursor.foreground': '#AEAFAD',
+ 'editorWhitespace.foreground': '#4B4E55',
+ },
+};
diff --git a/app/assets/javascripts/ide/lib/themes/gl_theme.js b/app/assets/javascripts/ide/lib/themes/gl_theme.js
deleted file mode 100644
index 439ae50448a..00000000000
--- a/app/assets/javascripts/ide/lib/themes/gl_theme.js
+++ /dev/null
@@ -1,15 +0,0 @@
-export default {
- themeName: 'gitlab',
- monacoTheme: {
- base: 'vs',
- inherit: true,
- rules: [],
- colors: {
- 'editorLineNumber.foreground': '#CCCCCC',
- 'diffEditor.insertedTextBackground': '#ddfbe6',
- 'diffEditor.removedTextBackground': '#f9d7dc',
- 'editor.selectionBackground': '#aad6f8',
- 'editorIndentGuide.activeBackground': '#cccccc',
- },
- },
-};
diff --git a/app/assets/javascripts/ide/lib/themes/index.js b/app/assets/javascripts/ide/lib/themes/index.js
new file mode 100644
index 00000000000..6ed9f6679a4
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/themes/index.js
@@ -0,0 +1,15 @@
+import white from './white';
+import dark from './dark';
+
+export const themes = [
+ {
+ name: 'white',
+ data: white,
+ },
+ {
+ name: 'dark',
+ data: dark,
+ },
+];
+
+export const DEFAULT_THEME = 'white';
diff --git a/app/assets/javascripts/ide/lib/themes/white.js b/app/assets/javascripts/ide/lib/themes/white.js
new file mode 100644
index 00000000000..273bc783fc6
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/themes/white.js
@@ -0,0 +1,12 @@
+export default {
+ base: 'vs',
+ inherit: true,
+ rules: [],
+ colors: {
+ 'editorLineNumber.foreground': '#CCCCCC',
+ 'diffEditor.insertedTextBackground': '#A0F5B420',
+ 'diffEditor.removedTextBackground': '#f9d7dc20',
+ 'editor.selectionBackground': '#aad6f8',
+ 'editorIndentGuide.activeBackground': '#cccccc',
+ },
+};
diff --git a/app/assets/javascripts/ide/stores/state.js b/app/assets/javascripts/ide/stores/state.js
index 6488389977c..828cec3e141 100644
--- a/app/assets/javascripts/ide/stores/state.js
+++ b/app/assets/javascripts/ide/stores/state.js
@@ -1,4 +1,5 @@
import { activityBarViews, viewerTypes } from '../constants';
+import { DEFAULT_THEME } from '../lib/themes';
export default () => ({
currentProjectId: '',
@@ -32,4 +33,5 @@ export default () => ({
},
clientsidePreviewEnabled: false,
renderWhitespaceInCode: false,
+ editorTheme: DEFAULT_THEME,
});
diff --git a/app/assets/javascripts/pages/projects/services/edit/index.js b/app/assets/javascripts/pages/projects/services/edit/index.js
index ba4b271f09e..2d77f2686f7 100644
--- a/app/assets/javascripts/pages/projects/services/edit/index.js
+++ b/app/assets/javascripts/pages/projects/services/edit/index.js
@@ -1,5 +1,6 @@
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
+import initAlertsSettings from '~/alerts_service_settings';
document.addEventListener('DOMContentLoaded', () => {
const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring');
@@ -10,4 +11,6 @@ document.addEventListener('DOMContentLoaded', () => {
const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
prometheusMetrics.loadActiveMetrics();
}
+
+ initAlertsSettings(document.querySelector('.js-alerts-service-settings'));
});
diff --git a/app/assets/stylesheets/page_bundles/ide.scss b/app/assets/stylesheets/page_bundles/ide.scss
index 990aca5f0c5..9c64714e5dd 100644
--- a/app/assets/stylesheets/page_bundles/ide.scss
+++ b/app/assets/stylesheets/page_bundles/ide.scss
@@ -296,8 +296,8 @@ $ide-commit-header-height: 48px;
height: 100%;
min-height: 0; // firefox fix
- &.is-readonly,
- .editor.original {
+ &.is-readonly .vs,
+ .vs .editor.original {
.monaco-editor,
.monaco-editor-background,
.monaco-editor .inputarea.ime-input {
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index 3817da8596b..12b4f9ac56c 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -19,19 +19,36 @@ module Projects
# overridden in EE
def track_events(result)
+ if result[:status] == :success
+ ::Gitlab::Tracking::IncidentManagement.track_from_params(
+ update_params[:incident_management_setting_attributes]
+ )
+ end
end
private
- # overridden in EE
def render_update_response(result)
respond_to do |format|
+ format.html do
+ render_update_html_response(result)
+ end
+
format.json do
render_update_json_response(result)
end
end
end
+ def render_update_html_response(result)
+ if result[:status] == :success
+ flash[:notice] = _('Your changes have been saved')
+ redirect_to project_settings_operations_path(@project)
+ else
+ render 'show'
+ end
+ end
+
def render_update_json_response(result)
if result[:status] == :success
flash[:notice] = _('Your changes have been saved')
@@ -61,6 +78,8 @@ module Projects
# overridden in EE
def permitted_project_params
project_params = {
+ incident_management_setting_attributes: ::Gitlab::Tracking::IncidentManagement.tracking_keys.keys,
+
metrics_setting_attributes: [:external_dashboard_url],
error_tracking_setting_attributes: [
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 1ce76fd57b1..4ed99b229b5 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -3,6 +3,11 @@
module ProjectsHelper
prepend_if_ee('::EE::ProjectsHelper') # rubocop: disable Cop/InjectEnterpriseEditionModule
+ def project_incident_management_setting
+ @project_incident_management_setting ||= @project.incident_management_setting ||
+ @project.build_incident_management_setting
+ end
+
def link_to_project(project)
link_to namespace_project_path(namespace_id: project.namespace, id: project), title: h(project.name) do
title = content_tag(:span, project.name, class: 'project-name')
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 88a2531d649..d328a609439 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -27,6 +27,8 @@ module ErrorTracking
validates :api_url, length: { maximum: 255 }, public_url: { enforce_sanitization: true, ascii_only: true }, allow_nil: true
+ validates :enabled, inclusion: { in: [true, false] }
+
validates :api_url, presence: { message: 'is a required field' }, if: :enabled
validate :validate_api_url_path, if: :enabled
diff --git a/app/models/project.rb b/app/models/project.rb
index a215b6c881c..1e27ce9f344 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2334,7 +2334,7 @@ class Project < ApplicationRecord
end
def alerts_service_activated?
- false
+ alerts_service&.active?
end
def self_monitoring?
diff --git a/app/models/service.rb b/app/models/service.rb
index 95b7c6927cf..e60dda59176 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -260,6 +260,7 @@ class Service < ApplicationRecord
def self.available_services_names
service_names = %w[
+ alerts
asana
assembla
bamboo
diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb
index f0144ad1213..27bbf5c6e57 100644
--- a/app/services/projects/operations/update_service.rb
+++ b/app/services/projects/operations/update_service.rb
@@ -16,6 +16,7 @@ module Projects
.merge(metrics_setting_params)
.merge(grafana_integration_params)
.merge(prometheus_integration_params)
+ .merge(incident_management_setting_params)
end
def metrics_setting_params
@@ -87,6 +88,10 @@ module Projects
{ prometheus_service_attributes: service.attributes.except(*%w(id project_id created_at updated_at)) }
end
+
+ def incident_management_setting_params
+ params.slice(:incident_management_setting_attributes)
+ end
end
end
end
diff --git a/app/views/projects/services/alerts/_help.html.haml b/app/views/projects/services/alerts/_help.html.haml
new file mode 100644
index 00000000000..be910203125
--- /dev/null
+++ b/app/views/projects/services/alerts/_help.html.haml
@@ -0,0 +1,3 @@
+.js-alerts-service-settings{ data: { activated: @service.activated?.to_s,
+ form_path: project_service_path(@project, @service.to_param),
+ authorization_key: @service.token, url: @service.url, learn_more_url: 'https://docs.gitlab.com/ee/user/project/integrations/generic_alerts.html' } }
diff --git a/app/views/projects/settings/operations/_incidents.html.haml b/app/views/projects/settings/operations/_incidents.html.haml
new file mode 100644
index 00000000000..fa2f3d7dc08
--- /dev/null
+++ b/app/views/projects/settings/operations/_incidents.html.haml
@@ -0,0 +1,32 @@
+- templates = []
+- setting = project_incident_management_setting
+- templates = setting.available_issue_templates.map { |t| [t.name, t.key] }
+
+%section.settings.no-animate.js-incident-management-settings
+ .settings-header
+ %h4= _('Incidents')
+ %button.btn.js-settings-toggle{ type: 'button' }
+ = _('Expand')
+ %p
+ = _('Action to take when receiving an alert.')
+ = link_to help_page_path('user/project/integrations/prometheus', anchor: 'taking-action-on-an-alert-ultimate') do
+ = _('More information')
+ .settings-content
+ = form_for @project, url: project_settings_operations_path(@project), method: :patch do |f|
+ = form_errors(@project.incident_management_setting)
+ .form-group
+ = f.fields_for :incident_management_setting_attributes, setting do |form|
+ .form-group
+ = form.check_box :create_issue
+ = form.label :create_issue, _('Create an issue. Issues are created for each alert triggered.'), class: 'form-check-label'
+ .form-group.col-sm-8
+ = form.label :issue_template_key, class: 'label-bold' do
+ = _('Issue template (optional)')
+ = link_to icon('question-circle'), help_page_path('user/project/description_templates', anchor: 'creating-issue-templates'), target: '_blank', rel: 'noopener noreferrer'
+ .select-wrapper
+ = form.select :issue_template_key, templates, {include_blank: 'No template selected'}, class: "form-control select-control"
+ = icon('chevron-down')
+ .form-group
+ = form.check_box :send_email
+ = form.label :send_email, _('Send a separate email notification to Developers.'), class: 'form-check-label'
+ = f.submit _('Save changes'), class: 'btn btn-success'
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index 3c955e5f558..30b914b5199 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -2,7 +2,7 @@
- page_title _('Operations Settings')
- breadcrumb_title _('Operations Settings')
-= render_if_exists 'projects/settings/operations/incidents'
+= render 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking'
= render 'projects/settings/operations/external_dashboard'
= render 'projects/settings/operations/grafana_integration'