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
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-03 00:09:14 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-03 00:09:14 +0300
commit2c0e92d0314ca00907b75b103af507d9a28a2d62 (patch)
tree1f98c624119c49aafbda54dafb067aa2d6b66f8d /app/assets/javascripts/alerts_settings
parent075ce5ae315d1b1d0623647714bc69bfa9df721a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/alerts_settings')
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue343
-rw-r--r--app/assets/javascripts/alerts_settings/constants.js41
-rw-r--r--app/assets/javascripts/alerts_settings/index.js53
-rw-r--r--app/assets/javascripts/alerts_settings/services/index.js27
4 files changed, 464 insertions, 0 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
new file mode 100644
index 00000000000..7dde9edbd27
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -0,0 +1,343 @@
+<script>
+import {
+ GlAlert,
+ GlButton,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlLink,
+ GlModal,
+ GlModalDirective,
+ GlSprintf,
+ GlFormSelect,
+} from '@gitlab/ui';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import ToggleButton from '~/vue_shared/components/toggle_button.vue';
+import csrf from '~/lib/utils/csrf';
+import service from '../services';
+import { i18n, serviceOptions } from '../constants';
+
+export default {
+ i18n,
+ csrf,
+ components: {
+ GlAlert,
+ GlButton,
+ GlForm,
+ GlFormGroup,
+ GlFormInput,
+ GlFormSelect,
+ GlLink,
+ GlModal,
+ GlSprintf,
+ ClipboardButton,
+ ToggleButton,
+ },
+ directives: {
+ 'gl-modal': GlModalDirective,
+ },
+ mixins: [glFeatureFlagsMixin()],
+ props: {
+ prometheus: {
+ type: Object,
+ required: true,
+ validator: ({ prometheusIsActivated }) => {
+ return prometheusIsActivated !== undefined;
+ },
+ },
+ generic: {
+ type: Object,
+ required: true,
+ validator: ({ formPath }) => {
+ return formPath !== undefined;
+ },
+ },
+ },
+ data() {
+ return {
+ activated: {
+ generic: this.generic.initialActivated,
+ prometheus: this.prometheus.prometheusIsActivated,
+ },
+ loading: false,
+ authorizationKey: {
+ generic: this.generic.initialAuthorizationKey,
+ prometheus: this.prometheus.prometheusAuthorizationKey,
+ },
+ selectedEndpoint: null,
+ options: serviceOptions,
+ prometheusApiKey: this.prometheus.prometheusApiUrl,
+ feedback: {
+ variant: 'danger',
+ feedbackMessage: null,
+ isFeedbackDismissed: false,
+ },
+ };
+ },
+ computed: {
+ sections() {
+ return [
+ {
+ text: this.$options.i18n.usageSection,
+ url: this.generic.alertsUsageUrl,
+ },
+ {
+ text: this.$options.i18n.setupSection,
+ url: this.generic.alertsSetupUrl,
+ },
+ ];
+ },
+ isGeneric() {
+ return this.selectedEndpoint === 'generic';
+ },
+ selectedService() {
+ return this.isGeneric
+ ? {
+ url: this.generic.url,
+ authKey: this.authorizationKey.generic,
+ active: this.activated.generic,
+ resetKey: this.resetGenericKey.bind(this),
+ }
+ : {
+ authKey: this.authorizationKey.prometheus,
+ url: this.prometheus.prometheusUrl,
+ active: this.activated.prometheus,
+ resetKey: this.resetPrometheusKey.bind(this),
+ };
+ },
+ showFeedbackMsg() {
+ return this.feedback.feedbackMessage && !this.isFeedbackDismissed;
+ },
+ prometheusInfo() {
+ return !this.isGeneric ? this.$options.i18n.prometheusInfo : '';
+ },
+ prometheusFeatureEnabled() {
+ return !this.isGeneric && this.glFeatures.alertIntegrationsDropdown;
+ },
+ },
+ created() {
+ if (this.glFeatures.alertIntegrationsDropdown) {
+ this.selectedEndpoint = this.prometheus.prometheusIsActivated
+ ? this.options[1].value
+ : this.options[0].value;
+ } else {
+ this.selectedEndpoint = this.options[0].value;
+ }
+ },
+ methods: {
+ dismissFeedback() {
+ this.feedback = { ...this.feedback, feedbackMessage: null };
+ this.isFeedbackDismissed = false;
+ },
+ resetGenericKey() {
+ return service
+ .updateGenericKey({ endpoint: this.generic.formPath, params: { service: { token: '' } } })
+ .then(({ data: { token } }) => {
+ this.authorizationKey.generic = token;
+ })
+ .catch(() => {
+ this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' });
+ });
+ },
+ resetPrometheusKey() {
+ return service
+ .updatePrometheusKey({ endpoint: this.prometheus.prometheusResetKeyPath })
+ .then(({ data: { token } }) => {
+ this.authorizationKey.prometheus = token;
+ })
+ .catch(() => {
+ this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' });
+ });
+ },
+ toggleActivated(value) {
+ return this.isGeneric
+ ? this.toggleGenericActivated(value)
+ : this.togglePrometheusActive(value);
+ },
+ toggleGenericActivated(value) {
+ this.loading = true;
+ return service
+ .updateGenericActive({
+ endpoint: this.generic.formPath,
+ params: { service: { active: value } },
+ })
+ .then(() => {
+ this.activated.generic = value;
+
+ if (value) {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.endPointActivated,
+ variant: 'success',
+ });
+ }
+ })
+ .catch(() => {})
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ togglePrometheusActive(value) {
+ this.loading = true;
+ return service
+ .updatePrometheusActive({
+ endpoint: this.prometheus.prometheusFormPath,
+ params: {
+ token: this.$options.csrf.token,
+ config: value ? 1 : 0,
+ url: this.prometheusApiKey,
+ redirect: window.location,
+ },
+ })
+ .then(() => {
+ this.activated.prometheus = value;
+ if (value) {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.endPointActivated,
+ variant: 'success',
+ });
+ }
+ })
+ .catch(() => {
+ this.setFeedback({
+ feedbackMessage: this.$options.i18n.errorApiUrlMsg,
+ variant: 'danger',
+ });
+ })
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ setFeedback({ feedbackMessage, variant }) {
+ this.feedback = { feedbackMessage, variant };
+ },
+ onSubmit(evt) {
+ // TODO: Add form submit as part of https://gitlab.com/gitlab-org/gitlab/-/issues/215356
+ evt.preventDefault();
+ },
+ onReset(evt) {
+ // TODO: Add form reset as part of https://gitlab.com/gitlab-org/gitlab/-/issues/215356
+ evt.preventDefault();
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-alert v-if="showFeedbackMsg" :variant="feedback.variant" @dismiss="dismissFeedback">
+ {{ feedback.feedbackMessage }}
+ </gl-alert>
+ <div data-testid="alert-settings-description" class="gl-mt-5">
+ <p v-for="section in sections" :key="section.text">
+ <gl-sprintf :message="section.text">
+ <template #link="{ content }">
+ <gl-link :href="section.url" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </p>
+ </div>
+ <gl-form @submit="onSubmit" @reset="onReset">
+ <gl-form-group
+ v-if="glFeatures.alertIntegrationsDropdown"
+ :label="$options.i18n.integrationsLabel"
+ label-for="integrations"
+ label-class="label-bold"
+ >
+ <gl-form-select
+ v-model="selectedEndpoint"
+ :options="options"
+ data-testid="alert-settings-select"
+ />
+ <span class="gl-text-gray-400">
+ <gl-sprintf :message="$options.i18n.integrationsInfo">
+ <template #link="{ content }">
+ <gl-link
+ class="gl-display-inline-block"
+ href="https://gitlab.com/groups/gitlab-org/-/epics/3362"
+ target="_blank"
+ >{{ content }}</gl-link
+ >
+ </template>
+ </gl-sprintf>
+ </span>
+ </gl-form-group>
+ <gl-form-group
+ :label="$options.i18n.activeLabel"
+ label-for="activated"
+ label-class="label-bold"
+ >
+ <toggle-button
+ id="activated"
+ :disabled-input="loading"
+ :is-loading="loading"
+ :value="selectedService.active"
+ @change="toggleActivated"
+ />
+ </gl-form-group>
+ <gl-form-group
+ v-if="prometheusFeatureEnabled"
+ :label="$options.i18n.apiBaseUrlLabel"
+ label-for="api-url"
+ label-class="label-bold"
+ >
+ <gl-form-input
+ id="api-url"
+ v-model="prometheusApiKey"
+ type="url"
+ :value="prometheusApiKey"
+ :placeholder="$options.i18n.prometheusApiPlaceholder"
+ />
+ <span class="gl-text-gray-400">
+ {{ $options.i18n.apiBaseUrlHelpText }}
+ </span>
+ </gl-form-group>
+ <gl-form-group :label="$options.i18n.urlLabel" label-for="url" label-class="label-bold">
+ <div class="input-group">
+ <gl-form-input id="url" :readonly="true" :value="selectedService.url" />
+ <span class="input-group-append">
+ <clipboard-button :text="selectedService.url" :title="$options.i18n.copyToClipboard" />
+ </span>
+ </div>
+ <span class="gl-text-gray-400">
+ {{ prometheusInfo }}
+ </span>
+ </gl-form-group>
+ <gl-form-group
+ :label="$options.i18n.authKeyLabel"
+ label-for="authorization-key"
+ label-class="label-bold"
+ >
+ <div class="input-group">
+ <gl-form-input id="authorization-key" :readonly="true" :value="selectedService.authKey" />
+ <span class="input-group-append">
+ <clipboard-button
+ :text="selectedService.authKey"
+ :title="$options.i18n.copyToClipboard"
+ />
+ </span>
+ </div>
+ <gl-button v-gl-modal.authKeyModal class="gl-mt-3">{{ $options.i18n.resetKey }}</gl-button>
+ <gl-modal
+ modal-id="authKeyModal"
+ :title="$options.i18n.resetKey"
+ :ok-title="$options.i18n.resetKey"
+ ok-variant="danger"
+ @ok="selectedService.resetKey"
+ >
+ {{ $options.i18n.restKeyInfo }}
+ </gl-modal>
+ </gl-form-group>
+ <div
+ class="footer-block row-content-block gl-display-flex gl-justify-content-space-between d-none"
+ >
+ <gl-button type="submit" variant="success" category="primary">
+ {{ __('Save and test changes') }}
+ </gl-button>
+ <gl-button type="reset" variant="default" category="primary">
+ {{ __('Cancel') }}
+ </gl-button>
+ </div>
+ </gl-form>
+ </div>
+</template>
diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js
new file mode 100644
index 00000000000..15618978145
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/constants.js
@@ -0,0 +1,41 @@
+import { s__ } from '~/locale';
+
+export const i18n = {
+ usageSection: s__(
+ 'AlertSettings|You must provide this URL and authorization key to authorize an external service to send alerts to GitLab. You can provide this URL and key to multiple services. After configuring an external service, alerts from your service will display on the GitLab %{linkStart}Alerts%{linkEnd} page.',
+ ),
+ setupSection: s__(
+ "AlertSettings|Review your external service's documentation to learn where to provide this information to your external service, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.",
+ ),
+ errorMsg: s__(
+ 'AlertSettings|There was an error updating the the alert settings. Please refresh the page to try again.',
+ ),
+ errorKeyMsg: s__(
+ 'AlertSettings|There was an error while trying to reset the key. Please refresh the page to try again.',
+ ),
+ errorApiUrlMsg: s__(
+ 'AlertSettings|There was an error while trying to enable the alert settings. Please ensure you are using a valid URL.',
+ ),
+ prometheusApiPlaceholder: s__('AlertSettings|http://prometheus.example.com/'),
+ restKeyInfo: s__(
+ 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
+ ),
+ endPointActivated: s__('AlertSettings|Alerts endpoint successfully activated.'),
+ prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'),
+ integrationsInfo: s__(
+ 'AlertSettings|Learn more about our %{linkStart}upcoming integrations%{linkEnd}',
+ ),
+ resetKey: s__('AlertSettings|Reset key'),
+ copyToClipboard: s__('AlertSettings|Copy'),
+ integrationsLabel: s__('AlertSettings|Integrations'),
+ apiBaseUrlLabel: s__('AlertSettings|Prometheus API Base URL'),
+ authKeyLabel: s__('AlertSettings|Authorization key'),
+ urlLabel: s__('AlertSettings|Webhook URL'),
+ activeLabel: s__('AlertSettings|Active'),
+ apiBaseUrlHelpText: s__(' AlertSettings|URL cannot be blank and must start with http or https'),
+};
+
+export const serviceOptions = [
+ { value: 'generic', text: s__('AlertSettings|Generic') },
+ { value: 'prometheus', text: s__('AlertSettings|External Prometheus') },
+];
diff --git a/app/assets/javascripts/alerts_settings/index.js b/app/assets/javascripts/alerts_settings/index.js
new file mode 100644
index 00000000000..e7a810742b9
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/index.js
@@ -0,0 +1,53 @@
+import Vue from 'vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
+import AlertSettingsForm from './components/alerts_settings_form.vue';
+
+export default el => {
+ if (!el) {
+ return null;
+ }
+
+ const {
+ prometheusActivated,
+ prometheusUrl,
+ prometheusAuthorizationKey,
+ prometheusFormPath,
+ prometheusResetKeyPath,
+ prometheusApiUrl,
+ activated: activatedStr,
+ alertsSetupUrl,
+ alertsUsageUrl,
+ formPath,
+ authorizationKey,
+ url,
+ } = el.dataset;
+
+ const activated = parseBoolean(activatedStr);
+ const prometheusIsActivated = parseBoolean(prometheusActivated);
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(AlertSettingsForm, {
+ props: {
+ prometheus: {
+ prometheusIsActivated,
+ prometheusUrl,
+ prometheusAuthorizationKey,
+ prometheusFormPath,
+ prometheusResetKeyPath,
+ prometheusApiUrl,
+ },
+ generic: {
+ alertsSetupUrl,
+ alertsUsageUrl,
+ initialActivated: activated,
+ formPath,
+ initialAuthorizationKey: authorizationKey,
+ url,
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/alerts_settings/services/index.js b/app/assets/javascripts/alerts_settings/services/index.js
new file mode 100644
index 00000000000..669c40bc86b
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/services/index.js
@@ -0,0 +1,27 @@
+import axios from '~/lib/utils/axios_utils';
+
+export default {
+ updateGenericKey({ endpoint, params }) {
+ return axios.put(endpoint, params);
+ },
+ updatePrometheusKey({ endpoint }) {
+ return axios.post(endpoint);
+ },
+ updateGenericActive({ endpoint, params }) {
+ return axios.put(endpoint, params);
+ },
+ updatePrometheusActive({ endpoint, params: { token, config, url, redirect } }) {
+ const data = new FormData();
+ data.set('_method', 'put');
+ data.set('authenticity_token', token);
+ data.set('service[manual_configuration]', config);
+ data.set('service[api_url]', url);
+ data.set('redirect_to', redirect);
+
+ return axios.post(endpoint, data, {
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ });
+ },
+};