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>2021-03-02 21:11:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-03-02 21:11:20 +0300
commitbf2439c21308c74e437b872180046b39a61734b5 (patch)
treed6f1fe33eb4b726e4e9f11350b8838dae2fc53f4 /app/assets/javascripts/alerts_settings
parent426384d091a0c229ff849ed6ba481bfbe700fb6a (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.vue470
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue81
-rw-r--r--app/assets/javascripts/alerts_settings/constants.js70
-rw-r--r--app/assets/javascripts/alerts_settings/utils/error_messages.js2
4 files changed, 330 insertions, 293 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
index ce595e848de..8e7a1b07bb6 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -1,7 +1,6 @@
<script>
import {
GlButton,
- GlCollapse,
GlForm,
GlFormGroup,
GlFormSelect,
@@ -11,10 +10,11 @@ import {
GlModal,
GlModalDirective,
GlToggle,
+ GlTabs,
+ GlTab,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { isEmpty, omit } from 'lodash';
-import { s__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
@@ -22,74 +22,13 @@ import {
JSON_VALIDATE_DELAY,
targetPrometheusUrlPlaceholder,
typeSet,
+ i18n,
} from '../constants';
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
import parseSamplePayloadQuery from '../graphql/queries/parse_sample_payload.query.graphql';
import MappingBuilder from './alert_mapping_builder.vue';
import AlertSettingsFormHelpBlock from './alert_settings_form_help_block.vue';
-export const i18n = {
- integrationFormSteps: {
- step1: {
- label: s__('AlertSettings|1. Select integration type'),
- enterprise: s__(
- 'AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations.',
- ),
- },
- step2: {
- label: s__('AlertSettings|2. Name integration'),
- placeholder: s__('AlertSettings|Enter integration name'),
- prometheus: s__('AlertSettings|Prometheus'),
- },
- step3: {
- label: s__('AlertSettings|3. Set up webhook'),
- help: s__(
- "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.",
- ),
- prometheusHelp: s__(
- 'AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.',
- ),
- info: s__('AlertSettings|Authorization key'),
- reset: s__('AlertSettings|Reset Key'),
- },
- step4: {
- label: s__('AlertSettings|4. Sample alert payload (optional)'),
- help: s__(
- 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional), or to test the integration (also optional).',
- ),
- prometheusHelp: s__(
- 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to test the integration (optional).',
- ),
- placeholder: s__('AlertSettings|{ "events": [{ "application": "Name of application" }] }'),
- resetHeader: s__('AlertSettings|Reset the mapping'),
- resetBody: s__(
- "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields.",
- ),
- resetOk: s__('AlertSettings|Proceed with editing'),
- editPayload: s__('AlertSettings|Edit payload'),
- submitPayload: s__('AlertSettings|Submit payload'),
- payloadParsedSucessMsg: s__(
- 'AlertSettings|Sample payload has been parsed. You can now map the fields.',
- ),
- },
- step5: {
- label: s__('AlertSettings|5. Map fields (optional)'),
- intro: s__(
- "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key.",
- ),
- },
- prometheusFormUrl: {
- label: s__('AlertSettings|Prometheus API base URL'),
- help: s__('AlertSettings|URL cannot be blank and must start with http or https'),
- },
- restKeyInfo: {
- label: s__(
- 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
- ),
- },
- },
-};
-
export default {
placeholders: {
prometheus: targetPrometheusUrlPlaceholder,
@@ -100,7 +39,6 @@ export default {
components: {
ClipboardButton,
GlButton,
- GlCollapse,
GlForm,
GlFormGroup,
GlFormInput,
@@ -109,6 +47,8 @@ export default {
GlFormSelect,
GlModal,
GlToggle,
+ GlTabs,
+ GlTab,
AlertSettingsFormHelpBlock,
MappingBuilder,
},
@@ -155,8 +95,11 @@ export default {
integrationTypesOptions: Object.values(integrationTypes),
selectedIntegration: integrationTypes.none.value,
active: false,
- formVisible: false,
- integrationTestPayload: {
+ samplePayload: {
+ json: null,
+ error: null,
+ },
+ testPayload: {
json: null,
error: null,
},
@@ -171,8 +114,17 @@ export default {
isPrometheus() {
return this.selectedIntegration === this.$options.typeSet.prometheus;
},
- jsonIsValid() {
- return this.integrationTestPayload.error === null;
+ isHttp() {
+ return this.selectedIntegration === this.$options.typeSet.http;
+ },
+ isCreating() {
+ return !this.currentIntegration;
+ },
+ isSampePayloadValid() {
+ return this.samplePayload.error === null;
+ },
+ isTestPayloadValid() {
+ return this.testPayload.error === null;
},
selectedIntegrationType() {
switch (this.selectedIntegration) {
@@ -199,7 +151,7 @@ export default {
},
testAlertPayload() {
return {
- data: this.integrationTestPayload.json,
+ data: this.testPayload.json,
endpoint: this.integrationForm.url,
token: this.integrationForm.token,
};
@@ -208,7 +160,7 @@ export default {
return (
this.multiIntegrations &&
this.glFeatures.multipleHttpIntegrationsCustomMapping &&
- this.selectedIntegration === typeSet.http &&
+ this.isHttp &&
this.alertFields?.length
);
},
@@ -227,18 +179,11 @@ export default {
: !this.active;
},
isSubmitTestPayloadDisabled() {
- return (
- !this.active ||
- Boolean(this.integrationTestPayload.error) ||
- this.integrationTestPayload.json === ''
- );
+ return !this.active || Boolean(this.samplePayload.error) || this.samplePayload.json === '';
},
isSelectDisabled() {
return this.currentIntegration !== null || !this.canAddIntegration;
},
- savedMapping() {
- return this.mapping;
- },
},
watch: {
currentIntegration(val) {
@@ -250,21 +195,14 @@ export default {
this.selectedIntegration = type;
this.active = active;
- if (type === typeSet.prometheus) {
- this.integrationTestPayload.json = null;
- }
-
if (type === typeSet.http && this.showMappingBuilder) {
this.parsedPayload = payloadAlertFields;
- this.integrationTestPayload.json = this.isValidNonEmptyJSON(payloadExample)
- ? payloadExample
- : null;
+ this.samplePayload.json = this.isValidNonEmptyJSON(payloadExample) ? payloadExample : null;
const mapping = payloadAttributeMappings.map((mappingItem) =>
omit(mappingItem, '__typename'),
);
this.updateMapping(mapping);
}
- this.toggleFormVisibility();
},
},
methods: {
@@ -280,19 +218,15 @@ export default {
}
return false;
},
- toggleFormVisibility() {
- this.formVisible = this.selectedIntegration !== integrationTypes.none.value;
- },
- submitWithTestPayload() {
- this.$emit('set-test-alert-payload', this.testAlertPayload);
- this.submit();
+ sendTestAlert() {
+ this.$emit('test-alert-payload', this.testAlertPayload);
},
submit() {
const { name, apiUrl } = this.integrationForm;
const customMappingVariables = this.glFeatures.multipleHttpIntegrationsCustomMapping
? {
payloadAttributeMappings: this.mapping,
- payloadExample: this.integrationTestPayload.json || '{}',
+ payloadExample: this.samplePayload.json || '{}',
}
: {};
@@ -300,7 +234,9 @@ export default {
this.selectedIntegration === typeSet.http
? { name, active: this.active, ...customMappingVariables }
: { apiUrl, active: this.active };
+
const integrationPayload = { type: this.selectedIntegration, variables };
+
if (this.currentIntegration) {
return this.$emit('update-integration', integrationPayload);
}
@@ -309,20 +245,15 @@ export default {
return this.$emit('create-new-integration', integrationPayload);
},
reset() {
- this.selectedIntegration = integrationTypes.none.value;
- this.toggleFormVisibility();
+ this.resetFormValues();
this.resetPayloadAndMapping();
-
- if (this.currentIntegration) {
- return this.$emit('clear-current-integration', { type: this.currentIntegration.type });
- }
-
- return this.resetFormValues();
+ this.$emit('clear-current-integration', { type: this.currentIntegration?.type });
},
resetFormValues() {
+ this.selectedIntegration = integrationTypes.none.value;
this.integrationForm.name = '';
this.integrationForm.apiUrl = '';
- this.integrationTestPayload = {
+ this.samplePayload = {
json: null,
error: null,
};
@@ -338,16 +269,18 @@ export default {
variables: { id: this.currentIntegration.id },
});
},
- validateJson() {
- this.integrationTestPayload.error = null;
- if (this.integrationTestPayload.json === '') {
+ validateJson(isSamplePayload = true) {
+ const payload = isSamplePayload ? this.samplePayload : this.testPayload;
+
+ payload.error = null;
+ if (payload.json === '') {
return;
}
try {
- JSON.parse(this.integrationTestPayload.json);
+ JSON.parse(payload.json);
} catch (e) {
- this.integrationTestPayload.error = JSON.stringify(e.message);
+ payload.error = JSON.stringify(e.message);
}
},
parseMapping() {
@@ -356,7 +289,7 @@ export default {
return this.$apollo
.query({
query: parseSamplePayloadQuery,
- variables: { projectPath: this.projectPath, payload: this.integrationTestPayload.json },
+ variables: { projectPath: this.projectPath, payload: this.samplePayload.json },
})
.then(
({
@@ -367,11 +300,13 @@ export default {
this.parsedPayload = alertManagementPayloadFields;
this.resetPayloadAndMappingConfirmed = false;
- this.$toast.show(this.$options.i18n.integrationFormSteps.step4.payloadParsedSucessMsg);
+ this.$toast.show(
+ this.$options.i18n.integrationFormSteps.setSamplePayload.payloadParsedSucessMsg,
+ );
},
)
.catch(({ message }) => {
- this.integrationTestPayload.error = message;
+ this.samplePayload.error = message;
})
.finally(() => {
this.parsingPayload = false;
@@ -391,55 +326,49 @@ export default {
<template>
<gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
- <h5 class="gl-font-lg gl-my-5">{{ s__('AlertSettings|Add new integrations') }}</h5>
- <gl-form-group
- id="integration-type"
- :label="$options.i18n.integrationFormSteps.step1.label"
- label-for="integration-type"
- >
- <gl-form-select
- v-model="selectedIntegration"
- :disabled="isSelectDisabled"
- class="mw-100"
- :options="integrationTypesOptions"
- @change="toggleFormVisibility"
- />
-
- <div v-if="!canAddIntegration" class="gl-my-4" data-testid="multi-integrations-not-supported">
- <alert-settings-form-help-block
- :message="$options.i18n.integrationFormSteps.step1.enterprise"
- link="https://about.gitlab.com/pricing"
- />
- </div>
- </gl-form-group>
- <gl-collapse v-model="formVisible" class="gl-mt-3">
- <div>
+ <gl-tabs>
+ <gl-tab :title="$options.i18n.integrationTabs.configureDetails">
<gl-form-group
- id="name-integration"
- :label="$options.i18n.integrationFormSteps.step2.label"
- label-for="name-integration"
+ v-if="isCreating"
+ id="integration-type"
+ :label="$options.i18n.integrationFormSteps.selectType.label"
+ label-for="integration-type"
>
- <gl-form-input
- v-model="integrationForm.name"
- :disabled="isPrometheus"
- type="text"
- :placeholder="
- isPrometheus
- ? $options.i18n.integrationFormSteps.step2.prometheus
- : $options.i18n.integrationFormSteps.step2.placeholder
- "
+ <gl-form-select
+ v-model="selectedIntegration"
+ :disabled="isSelectDisabled"
+ class="gl-max-w-full"
+ :options="integrationTypesOptions"
+ />
+
+ <alert-settings-form-help-block
+ v-if="!canAddIntegration"
+ disabled="true"
+ class="gl-display-inline-block gl-my-4"
+ :message="$options.i18n.integrationFormSteps.selectType.enterprise"
+ link="https://about.gitlab.com/pricing"
+ data-testid="multi-integrations-not-supported"
/>
</gl-form-group>
- <gl-form-group
- id="integration-webhook"
- :label="$options.i18n.integrationFormSteps.step3.label"
- label-for="integration-webhook"
- >
+ <div class="gl-mt-3">
+ <gl-form-group
+ v-if="isHttp"
+ id="name-integration"
+ :label="$options.i18n.integrationFormSteps.nameIntegration.label"
+ label-for="name-integration"
+ >
+ <gl-form-input
+ v-model="integrationForm.name"
+ type="text"
+ :placeholder="$options.i18n.integrationFormSteps.nameIntegration.placeholder"
+ />
+ </gl-form-group>
+
<alert-settings-form-help-block
:message="
isPrometheus
- ? $options.i18n.integrationFormSteps.step3.prometheusHelp
- : $options.i18n.integrationFormSteps.step3.help
+ ? $options.i18n.integrationFormSteps.setupCredentials.prometheusHelp
+ : $options.i18n.integrationFormSteps.setupCredentials.help
"
link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
/>
@@ -447,7 +376,7 @@ export default {
<gl-toggle
v-model="active"
:is-loading="loading"
- :label="__('Active')"
+ :label="$options.i18n.integrationFormSteps.nameIntegration.activeToggle"
class="gl-my-4 gl-font-weight-normal"
/>
@@ -468,16 +397,110 @@ export default {
</span>
</div>
+ <gl-form-group
+ v-if="isHttp"
+ data-testid="sample-payload-section"
+ :label="$options.i18n.integrationFormSteps.setSamplePayload.label"
+ label-for="sample-payload"
+ :class="{ 'gl-mb-0!': showMappingBuilder }"
+ :invalid-feedback="samplePayload.error"
+ >
+ <alert-settings-form-help-block
+ :message="$options.i18n.integrationFormSteps.setSamplePayload.testPayloadHelpHttp"
+ :link="generic.alertsUsageUrl"
+ />
+
+ <gl-form-textarea
+ id="sample-payload"
+ v-model.trim="samplePayload.json"
+ :disabled="isPayloadEditDisabled"
+ :state="isSampePayloadValid"
+ :placeholder="$options.i18n.integrationFormSteps.setSamplePayload.placeholder"
+ class="gl-my-3"
+ :debounce="$options.JSON_VALIDATE_DELAY"
+ rows="6"
+ max-rows="10"
+ @input="validateJson"
+ />
+ </gl-form-group>
+
+ <template v-if="showMappingBuilder">
+ <gl-button
+ v-if="canEditPayload"
+ v-gl-modal.resetPayloadModal
+ data-testid="payload-action-btn"
+ :disabled="!active"
+ class="gl-mt-3"
+ >
+ {{ $options.i18n.integrationFormSteps.setSamplePayload.editPayload }}
+ </gl-button>
+
+ <gl-button
+ v-else
+ data-testid="payload-action-btn"
+ :class="{ 'gl-mt-3': samplePayload.error }"
+ :disabled="!active || !isSampePayloadValid"
+ :loading="parsingPayload"
+ @click="parseMapping"
+ >
+ {{ $options.i18n.integrationFormSteps.setSamplePayload.parsePayload }}
+ </gl-button>
+ <gl-modal
+ modal-id="resetPayloadModal"
+ :title="$options.i18n.integrationFormSteps.setSamplePayload.resetHeader"
+ :ok-title="$options.i18n.integrationFormSteps.setSamplePayload.resetOk"
+ ok-variant="danger"
+ @ok="resetPayloadAndMapping"
+ >
+ {{ $options.i18n.integrationFormSteps.setSamplePayload.resetBody }}
+ </gl-modal>
+ </template>
+
+ <gl-form-group
+ v-if="showMappingBuilder"
+ id="mapping-builder"
+ class="gl-mt-5"
+ :label="$options.i18n.integrationFormSteps.mapFields.label"
+ label-for="mapping-builder"
+ >
+ <span>{{ $options.i18n.integrationFormSteps.mapFields.intro }}</span>
+ <mapping-builder
+ :parsed-payload="parsedPayload"
+ :saved-mapping="mapping"
+ :alert-fields="alertFields"
+ @onMappingUpdate="updateMapping"
+ />
+ </gl-form-group>
+ </div>
+
+ <div class="gl-display-flex gl-justify-content-start gl-py-3">
+ <gl-button
+ type="submit"
+ variant="confirm"
+ class="js-no-auto-disable"
+ data-testid="integration-form-submit"
+ >
+ {{ $options.i18n.saveIntegration }}
+ </gl-button>
+
+ <gl-button type="reset" class="gl-ml-3 js-no-auto-disable">{{
+ $options.i18n.cancelAndClose
+ }}</gl-button>
+ </div>
+ </gl-tab>
+
+ <gl-tab :title="$options.i18n.integrationTabs.viewCredentials" :disabled="isCreating">
+ <gl-form-group id="integration-webhook">
<div class="gl-my-4">
<span class="gl-font-weight-bold">
- {{ s__('AlertSettings|Webhook URL') }}
+ {{ $options.i18n.integrationFormSteps.setupCredentials.webhookUrl }}
</span>
<gl-form-input-group id="url" readonly :value="integrationForm.url">
<template #append>
<clipboard-button
:text="integrationForm.url || ''"
- :title="__('Copy')"
+ :title="$options.i18n.copy"
class="gl-m-0!"
/>
</template>
@@ -486,7 +509,7 @@ export default {
<div class="gl-my-4">
<span class="gl-font-weight-bold">
- {{ $options.i18n.integrationFormSteps.step3.info }}
+ {{ $options.i18n.integrationFormSteps.setupCredentials.authorizationKey }}
</span>
<gl-form-input-group
@@ -498,124 +521,67 @@ export default {
<template #append>
<clipboard-button
:text="integrationForm.token || ''"
- :title="__('Copy')"
+ :title="$options.i18n.copy"
class="gl-m-0!"
/>
</template>
</gl-form-input-group>
-
- <gl-button v-gl-modal.authKeyModal :disabled="isResetAuthKeyDisabled">
- {{ $options.i18n.integrationFormSteps.step3.reset }}
- </gl-button>
- <gl-modal
- modal-id="authKeyModal"
- :title="$options.i18n.integrationFormSteps.step3.reset"
- :ok-title="$options.i18n.integrationFormSteps.step3.reset"
- ok-variant="danger"
- @ok="resetAuthKey"
- >
- {{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
- </gl-modal>
</div>
</gl-form-group>
- <gl-form-group
- id="test-integration"
- :label="$options.i18n.integrationFormSteps.step4.label"
- label-for="test-integration"
- :class="{ 'gl-mb-0!': showMappingBuilder }"
- :invalid-feedback="integrationTestPayload.error"
+ <gl-button v-gl-modal.authKeyModal :disabled="isResetAuthKeyDisabled" variant="danger">
+ {{ $options.i18n.integrationFormSteps.setupCredentials.reset }}
+ </gl-button>
+
+ <gl-button type="reset" class="gl-ml-3 js-no-auto-disable">{{
+ $options.i18n.cancelAndClose
+ }}</gl-button>
+
+ <gl-modal
+ modal-id="authKeyModal"
+ :title="$options.i18n.integrationFormSteps.setupCredentials.reset"
+ :ok-title="$options.i18n.integrationFormSteps.setupCredentials.reset"
+ ok-variant="danger"
+ @ok="resetAuthKey"
>
+ {{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
+ </gl-modal>
+ </gl-tab>
+
+ <gl-tab :title="$options.i18n.integrationTabs.sendTestAlert" :disabled="isCreating">
+ <gl-form-group id="test-integration" :invalid-feedback="testPayload.error">
<alert-settings-form-help-block
- :message="
- isPrometheus || !showMappingBuilder
- ? $options.i18n.integrationFormSteps.step4.prometheusHelp
- : $options.i18n.integrationFormSteps.step4.help
- "
+ :message="$options.i18n.integrationFormSteps.setSamplePayload.testPayloadHelp"
:link="generic.alertsUsageUrl"
/>
<gl-form-textarea
id="test-payload"
- v-model.trim="integrationTestPayload.json"
- :disabled="isPayloadEditDisabled"
- :state="jsonIsValid"
- :placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
+ v-model.trim="testPayload.json"
+ :state="isTestPayloadValid"
+ :placeholder="$options.i18n.integrationFormSteps.setSamplePayload.placeholder"
class="gl-my-3"
:debounce="$options.JSON_VALIDATE_DELAY"
rows="6"
max-rows="10"
- @input="validateJson"
+ @input="validateJson(false)"
/>
</gl-form-group>
- <template v-if="showMappingBuilder">
- <gl-button
- v-if="canEditPayload"
- v-gl-modal.resetPayloadModal
- data-testid="payload-action-btn"
- :disabled="!active"
- class="gl-mt-3"
- >
- {{ $options.i18n.integrationFormSteps.step4.editPayload }}
- </gl-button>
-
- <gl-button
- v-else
- data-testid="payload-action-btn"
- :class="{ 'gl-mt-3': integrationTestPayload.error }"
- :disabled="!active"
- :loading="parsingPayload"
- @click="parseMapping"
- >
- {{ $options.i18n.integrationFormSteps.step4.submitPayload }}
- </gl-button>
- <gl-modal
- modal-id="resetPayloadModal"
- :title="$options.i18n.integrationFormSteps.step4.resetHeader"
- :ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
- ok-variant="danger"
- @ok="resetPayloadAndMapping"
- >
- {{ $options.i18n.integrationFormSteps.step4.resetBody }}
- </gl-modal>
- </template>
-
- <gl-form-group
- v-if="showMappingBuilder"
- id="mapping-builder"
- class="gl-mt-5"
- :label="$options.i18n.integrationFormSteps.step5.label"
- label-for="mapping-builder"
- >
- <span>{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
- <mapping-builder
- :parsed-payload="parsedPayload"
- :saved-mapping="savedMapping"
- :alert-fields="alertFields"
- @onMappingUpdate="updateMapping"
- />
- </gl-form-group>
- </div>
- <div class="gl-display-flex gl-justify-content-start gl-py-3">
<gl-button
- type="submit"
- variant="success"
+ :disabled="!isTestPayloadValid"
+ data-testid="send-test-alert"
+ variant="confirm"
class="js-no-auto-disable"
- data-testid="integration-form-submit"
- >{{ s__('AlertSettings|Save integration') }}
- </gl-button>
- <gl-button
- data-testid="integration-test-and-submit"
- :disabled="isSubmitTestPayloadDisabled"
- category="secondary"
- variant="success"
- class="gl-mx-3 js-no-auto-disable"
- @click="submitWithTestPayload"
- >{{ s__('AlertSettings|Save and test payload') }}</gl-button
+ @click="sendTestAlert"
>
- <gl-button type="reset" class="js-no-auto-disable">{{ __('Cancel') }}</gl-button>
- </div>
- </gl-collapse>
+ {{ $options.i18n.send }}
+ </gl-button>
+
+ <gl-button type="reset" class="gl-ml-3 js-no-auto-disable">{{
+ $options.i18n.cancelAndClose
+ }}</gl-button>
+ </gl-tab>
+ </gl-tabs>
</gl-form>
</template>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
index bdf52da209e..062d354330c 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -1,4 +1,5 @@
<script>
+import { GlButton } from '@gitlab/ui';
import createHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
import updateHttpIntegrationMutation from 'ee_else_ce/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import createFlash, { FLASH_TYPES } from '~/flash';
@@ -31,20 +32,24 @@ import {
import IntegrationsList from './alerts_integrations_list.vue';
import AlertSettingsForm from './alerts_settings_form.vue';
+export const i18n = {
+ changesSaved: s__(
+ 'AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list.',
+ ),
+ integrationRemoved: s__('AlertsIntegrations|The integration has been successfully removed.'),
+ alertSent: s__(
+ 'AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list.',
+ ),
+ addNewIntegration: s__('AlertSettings|Add new integration'),
+};
+
export default {
typeSet,
- i18n: {
- changesSaved: s__(
- 'AlertsIntegrations|The integration has been successfully saved. Alerts from this new integration should now appear on your alerts list.',
- ),
- integrationRemoved: s__('AlertsIntegrations|The integration has been successfully removed.'),
- alertSent: s__(
- 'AlertsIntegrations|The test alert has been successfully sent, and should now be visible on your alerts list.',
- ),
- },
+ i18n,
components: {
IntegrationsList,
AlertSettingsForm,
+ GlButton,
},
inject: {
generic: {
@@ -116,10 +121,10 @@ export default {
data() {
return {
isUpdating: false,
- testAlertPayload: null,
integrations: {},
httpIntegrations: {},
currentIntegration: null,
+ formVisible: false,
};
},
computed: {
@@ -161,18 +166,6 @@ export default {
return createFlash({ message: error });
}
- if (this.testAlertPayload) {
- const integration =
- httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
-
- const payload = {
- ...this.testAlertPayload,
- endpoint: integration.url,
- token: integration.token,
- };
- return this.validateAlertPayload(payload);
- }
-
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
@@ -203,10 +196,6 @@ export default {
return createFlash({ message: error });
}
- if (this.testAlertPayload) {
- return this.validateAlertPayload();
- }
-
this.clearCurrentIntegration({ type });
return createFlash({
@@ -219,7 +208,6 @@ export default {
})
.finally(() => {
this.isUpdating = false;
- this.testAlertPayload = null;
});
},
resetToken({ type, variables }) {
@@ -276,6 +264,7 @@ export default {
: updateCurrentPrometheusIntegrationMutation,
variables: currentIntegration,
});
+ this.setFormVisibility(true);
},
deleteIntegration({ id, type }) {
const { projectPath } = this;
@@ -308,19 +297,19 @@ export default {
});
},
clearCurrentIntegration({ type }) {
- this.$apollo.mutate({
- mutation: this.isHttp(type)
- ? updateCurrentHttpIntegrationMutation
- : updateCurrentPrometheusIntegrationMutation,
- variables: {},
- });
- },
- setTestAlertPayload(payload) {
- this.testAlertPayload = payload;
+ if (type) {
+ this.$apollo.mutate({
+ mutation: this.isHttp(type)
+ ? updateCurrentHttpIntegrationMutation
+ : updateCurrentPrometheusIntegrationMutation,
+ variables: {},
+ });
+ }
+ this.setFormVisibility(false);
},
- validateAlertPayload(payload) {
+ testAlertPayload(payload) {
return service
- .updateTestAlert(payload ?? this.testAlertPayload)
+ .updateTestAlert(payload)
.then(() => {
return createFlash({
message: this.$options.i18n.alertSent,
@@ -331,6 +320,9 @@ export default {
createFlash({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
});
},
+ setFormVisibility(visible) {
+ this.formVisible = visible;
+ },
},
};
</script>
@@ -343,7 +335,18 @@ export default {
@edit-integration="editIntegration"
@delete-integration="deleteIntegration"
/>
+ <gl-button
+ v-if="canAddIntegration && !formVisible"
+ category="secondary"
+ variant="confirm"
+ data-testid="add-integration-btn"
+ class="gl-mt-3"
+ @click="setFormVisibility(true)"
+ >
+ {{ $options.i18n.addNewIntegration }}
+ </gl-button>
<alert-settings-form
+ v-if="formVisible"
:loading="isUpdating"
:can-add-integration="canAddIntegration"
:alert-fields="alertFields"
@@ -351,7 +354,7 @@ export default {
@update-integration="updateIntegration"
@reset-token="resetToken"
@clear-current-integration="clearCurrentIntegration"
- @set-test-alert-payload="setTestAlertPayload"
+ @test-alert-payload="testAlertPayload"
/>
</div>
</template>
diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js
index cb3b4d2fc09..6ba9e83f96e 100644
--- a/app/assets/javascripts/alerts_settings/constants.js
+++ b/app/assets/javascripts/alerts_settings/constants.js
@@ -1,4 +1,4 @@
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
// TODO: Remove this as part of the form old removal
export const i18n = {
@@ -38,6 +38,74 @@ export const i18n = {
'AlertSettings|Authorization key has been successfully reset. Please save your changes now.',
),
integration: s__('AlertSettings|Integration'),
+ integrationTabs: {
+ configureDetails: s__('AlertSettings|Configure details'),
+ viewCredentials: s__('AlertSettings|View credentials'),
+ sendTestAlert: s__('AlertSettings|Send test alert'),
+ },
+ integrationFormSteps: {
+ selectType: {
+ label: s__('AlertSettings|Select integration type'),
+ enterprise: s__(
+ 'AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations.',
+ ),
+ },
+ nameIntegration: {
+ label: s__('AlertSettings|Name integration'),
+ placeholder: s__('AlertSettings|Enter integration name'),
+ activeToggle: __('Active'),
+ },
+ setupCredentials: {
+ help: s__(
+ "AlertSettings|Utilize the URL and authorization key below to authorize an external service to send alerts to GitLab. Review your external service's documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.",
+ ),
+ prometheusHelp: s__(
+ 'AlertSettings|Utilize the URL and authorization key below to authorize Prometheus to send alerts to GitLab. Review the Prometheus documentation to learn where to add these details, and the %{linkStart}GitLab documentation%{linkEnd} to learn more about configuring your endpoint.',
+ ),
+ webhookUrl: s__('AlertSettings|Webhook URL'),
+ authorizationKey: s__('AlertSettings|Authorization key'),
+ reset: s__('AlertSettings|Reset Key'),
+ },
+ setSamplePayload: {
+ label: s__('AlertSettings|Sample alert payload (optional)'),
+ testPayloadHelpHttp: s__(
+ 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This payload can be used to create a custom mapping (optional).',
+ ),
+ testPayloadHelp: s__(
+ 'AlertSettings|Provide an example payload from the monitoring tool you intend to integrate with. This will allow you to send an alert to an active GitLab alerting point.',
+ ),
+ placeholder: s__('AlertSettings|{ "events": [{ "application": "Name of application" }] }'),
+ resetHeader: s__('AlertSettings|Reset the mapping'),
+ resetBody: s__(
+ "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields.",
+ ),
+ resetOk: s__('AlertSettings|Proceed with editing'),
+ editPayload: s__('AlertSettings|Edit payload'),
+ parsePayload: s__('AlertSettings|Parse payload for custom mapping'),
+ payloadParsedSucessMsg: s__(
+ 'AlertSettings|Sample payload has been parsed. You can now map the fields.',
+ ),
+ },
+ mapFields: {
+ label: s__('AlertSettings|Map fields (optional)'),
+ intro: s__(
+ "AlertSettings|If you've provided a sample alert payload, you can create a custom mapping for your endpoint. The default GitLab alert keys are listed below. Please define which payload key should map to the specified GitLab key.",
+ ),
+ },
+ prometheusFormUrl: {
+ label: s__('AlertSettings|Prometheus API base URL'),
+ help: s__('AlertSettings|URL cannot be blank and must start with http or https'),
+ },
+ restKeyInfo: {
+ label: s__(
+ 'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
+ ),
+ },
+ },
+ saveIntegration: s__('AlertSettings|Save integration'),
+ cancelAndClose: __('Cancel and close'),
+ send: s__('AlertSettings|Send'),
+ copy: __('Copy'),
};
export const integrationTypes = {
diff --git a/app/assets/javascripts/alerts_settings/utils/error_messages.js b/app/assets/javascripts/alerts_settings/utils/error_messages.js
index 979d1ca3ccc..e380257f983 100644
--- a/app/assets/javascripts/alerts_settings/utils/error_messages.js
+++ b/app/assets/javascripts/alerts_settings/utils/error_messages.js
@@ -17,5 +17,5 @@ export const RESET_INTEGRATION_TOKEN_ERROR = s__(
);
export const INTEGRATION_PAYLOAD_TEST_ERROR = s__(
- 'AlertsIntegrations|Integration payload is invalid. You can still save your changes.',
+ 'AlertsIntegrations|Integration payload is invalid.',
);