diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-02 21:11:20 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-02 21:11:20 +0300 |
commit | bf2439c21308c74e437b872180046b39a61734b5 (patch) | |
tree | d6f1fe33eb4b726e4e9f11350b8838dae2fc53f4 /app/assets/javascripts/alerts_settings | |
parent | 426384d091a0c229ff849ed6ba481bfbe700fb6a (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/alerts_settings')
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.', ); |