Commit 18e2e0ca authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Denys Mishunov

Alert integrations form cleanup - 2nd part

Dynamic step numbers, open config tab on edit, minor cleanup
parent ad7c6878
......@@ -10,6 +10,7 @@ import {
GlTooltipDirective,
GlSprintf,
} from '@gitlab/ui';
import { capitalize } from 'lodash';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
import {
......@@ -77,6 +78,7 @@ export default {
{
key: 'type',
label: __('Type'),
formatter: (value) => (value === typeSet.prometheus ? capitalize(value) : value),
},
{
key: 'actions',
......@@ -172,7 +174,7 @@ export default {
<template #cell(actions)="{ item }">
<gl-button-group class="gl-ml-3">
<gl-button icon="pencil" @click="editIntegration(item)" />
<gl-button icon="settings" @click="editIntegration(item)" />
<gl-button
v-gl-modal.deleteIntegration
:disabled="item.type === $options.typeSet.prometheus"
......
......@@ -19,9 +19,13 @@ import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
integrationTypes,
integrationSteps,
createStepNumbers,
editStepNumbers,
JSON_VALIDATE_DELAY,
targetPrometheusUrlPlaceholder,
typeSet,
viewCredentialsTabIndex,
i18n,
} from '../constants';
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
......@@ -35,6 +39,7 @@ export default {
},
JSON_VALIDATE_DELAY,
typeSet,
integrationSteps,
i18n,
components: {
ClipboardButton,
......@@ -108,6 +113,7 @@ export default {
parsingPayload: false,
currentIntegration: null,
parsedPayload: [],
activeTabIndex: 0,
};
},
computed: {
......@@ -170,20 +176,23 @@ export default {
canEditPayload() {
return this.hasSamplePayload && !this.resetPayloadAndMappingConfirmed;
},
canParseSamplePayload() {
return !this.active || !this.isSampePayloadValid || !this.samplePayload.json;
},
isResetAuthKeyDisabled() {
return !this.active && !this.integrationForm.token !== '';
},
isPayloadEditDisabled() {
return this.glFeatures.multipleHttpIntegrationsCustomMapping
? !this.active || this.canEditPayload
: !this.active;
},
isSubmitTestPayloadDisabled() {
return !this.active || Boolean(this.samplePayload.error) || this.samplePayload.json === '';
return !this.active || this.canEditPayload;
},
isSelectDisabled() {
return this.currentIntegration !== null || !this.canAddIntegration;
},
viewCredentialsHelpMsg() {
return this.isPrometheus
? i18n.integrationFormSteps.setupCredentials.prometheusHelp
: i18n.integrationFormSteps.setupCredentials.help;
},
},
watch: {
currentIntegration(val) {
......@@ -203,6 +212,8 @@ export default {
);
this.updateMapping(mapping);
}
this.activeTabIndex = viewCredentialsTabIndex;
this.$el.scrollIntoView({ block: 'center' });
},
},
methods: {
......@@ -320,18 +331,32 @@ export default {
this.parsedPayload = [];
this.updateMapping([]);
},
getLabelWithStepNumber(step, label) {
let stepNumber = editStepNumbers[step];
if (this.isCreating) {
stepNumber = createStepNumbers[step];
}
return stepNumber ? `${stepNumber}.${label}` : label;
},
},
};
</script>
<template>
<gl-form class="gl-mt-6" @submit.prevent="submit" @reset.prevent="reset">
<gl-tabs>
<gl-tabs v-model="activeTabIndex">
<gl-tab :title="$options.i18n.integrationTabs.configureDetails">
<gl-form-group
v-if="isCreating"
id="integration-type"
:label="$options.i18n.integrationFormSteps.selectType.label"
:label="
getLabelWithStepNumber(
$options.integrationSteps.selectType,
$options.i18n.integrationFormSteps.selectType.label,
)
"
label-for="integration-type"
>
<gl-form-select
......@@ -354,7 +379,12 @@ export default {
<gl-form-group
v-if="isHttp"
id="name-integration"
:label="$options.i18n.integrationFormSteps.nameIntegration.label"
:label="
getLabelWithStepNumber(
$options.integrationSteps.nameIntegration,
$options.i18n.integrationFormSteps.nameIntegration.label,
)
"
label-for="name-integration"
>
<gl-form-input
......@@ -364,15 +394,6 @@ export default {
/>
</gl-form-group>
<alert-settings-form-help-block
:message="
isPrometheus
? $options.i18n.integrationFormSteps.setupCredentials.prometheusHelp
: $options.i18n.integrationFormSteps.setupCredentials.help
"
link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
/>
<gl-toggle
v-model="active"
:is-loading="loading"
......@@ -382,7 +403,12 @@ export default {
<div v-if="isPrometheus" class="gl-my-4">
<span class="gl-font-weight-bold">
{{ $options.i18n.integrationFormSteps.prometheusFormUrl.label }}
{{
getLabelWithStepNumber(
$options.integrationSteps.setPrometheusApiUrl,
$options.i18n.integrationFormSteps.prometheusFormUrl.label,
)
}}
</span>
<gl-form-input
......@@ -397,34 +423,38 @@ 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-form-group
data-testid="sample-payload-section"
:label="
getLabelWithStepNumber(
$options.integrationSteps.setSamplePayload,
$options.i18n.integrationFormSteps.setSamplePayload.label,
)
"
label-for="sample-payload"
class="gl-mb-0!"
: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>
<gl-button
v-if="canEditPayload"
v-gl-modal.resetPayloadModal
......@@ -439,7 +469,7 @@ export default {
v-else
data-testid="payload-action-btn"
:class="{ 'gl-mt-3': samplePayload.error }"
:disabled="!active || !isSampePayloadValid"
:disabled="canParseSamplePayload"
:loading="parsingPayload"
@click="parseMapping"
>
......@@ -454,23 +484,27 @@ export default {
>
{{ $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>
<gl-form-group
id="mapping-builder"
class="gl-mt-5"
:label="
getLabelWithStepNumber(
$options.integrationSteps.customizeMapping,
$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>
</template>
</div>
<div class="gl-display-flex gl-justify-content-start gl-py-3">
......@@ -490,6 +524,11 @@ export default {
</gl-tab>
<gl-tab :title="$options.i18n.integrationTabs.viewCredentials" :disabled="isCreating">
<alert-settings-form-help-block
:message="viewCredentialsHelpMsg"
link="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
/>
<gl-form-group id="integration-webhook">
<div class="gl-my-4">
<span class="gl-font-weight-bold">
......
......@@ -165,6 +165,9 @@ export default {
if (error) {
return createFlash({ message: error });
}
const { integration } = httpIntegrationCreate || prometheusIntegrationCreate;
this.editIntegration(integration);
return createFlash({
message: this.$options.i18n.changesSaved,
......
import { s__, __ } from '~/locale';
// TODO: Remove this as part of the form old removal
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 alert settings.'),
errorKeyMsg: s__(
'AlertSettings|There was an error while trying to reset the key. Please refresh the page to try again.',
),
restKeyInfo: s__(
'AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.',
),
changesSaved: s__('AlertSettings|Your integration was successfully updated.'),
prometheusInfo: s__('AlertSettings|Add URL and auth key to your Prometheus config file'),
integrationsInfo: s__(
'AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}',
),
resetKey: s__('AlertSettings|Reset key'),
copyToClipboard: s__('AlertSettings|Copy'),
apiBaseUrlLabel: s__('AlertSettings|API 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'),
testAlertInfo: s__('AlertSettings|Test alert payload'),
alertJson: s__('AlertSettings|Alert test payload'),
alertJsonPlaceholder: s__('AlertSettings|Enter test alert JSON....'),
testAlertFailed: s__('AlertSettings|Test failed. Do you still want to save your changes anyway?'),
testAlertSuccess: s__(
'AlertSettings|Test alert sent successfully. If you have made other changes, please save them now.',
),
authKeyRest: s__(
'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'),
......@@ -87,9 +50,9 @@ export const i18n = {
),
},
mapFields: {
label: s__('AlertSettings|Map fields (optional)'),
label: s__('AlertSettings|Customize alert payload mapping (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.",
'AlertSettings|If you intend to create a custom mapping, provide an example payload from your monitoring tool and click "parse payload fields" button to continue. The sample payload is required for completing the custom mapping; if you want to skip the mapping step, progress straight to saving your integration.',
),
},
prometheusFormUrl: {
......@@ -103,11 +66,36 @@ export const i18n = {
},
},
saveIntegration: s__('AlertSettings|Save integration'),
changesSaved: s__('AlertSettings|Your integration was successfully updated.'),
cancelAndClose: __('Cancel and close'),
send: s__('AlertSettings|Send'),
copy: __('Copy'),
};
export const integrationSteps = {
selectType: 'SELECT_TYPE',
nameIntegration: 'NAME_INTEGRATION',
setPrometheusApiUrl: 'SET_PROMETHEUS_API_URL',
setSamplePayload: 'SET_SAMPLE_PAYLOAD',
customizeMapping: 'CUSTOMIZE_MAPPING',
};
export const createStepNumbers = {
[integrationSteps.selectType]: 1,
[integrationSteps.nameIntegration]: 2,
[integrationSteps.setPrometheusApiUrl]: 2,
[integrationSteps.setSamplePayload]: 3,
[integrationSteps.customizeMapping]: 4,
};
export const editStepNumbers = {
[integrationSteps.selectType]: 1,
[integrationSteps.nameIntegration]: 1,
[integrationSteps.setPrometheusApiUrl]: null,
[integrationSteps.setSamplePayload]: 2,
[integrationSteps.customizeMapping]: 3,
};
export const integrationTypes = {
none: { value: '', text: s__('AlertSettings|Select integration type') },
http: { value: 'HTTP', text: s__('AlertSettings|HTTP Endpoint') },
......@@ -125,14 +113,11 @@ export const JSON_VALIDATE_DELAY = 250;
export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/';
export const sectionHash = 'js-alert-management-settings';
/* eslint-disable @gitlab/require-i18n-strings */
/**
* Tracks snowplow event when user views alerts integration list
*/
export const trackAlertIntegrationsViewsOptions = {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
category: 'Alert Integrations',
action: 'view_alert_integrations_list',
};
......@@ -141,3 +126,5 @@ export const mappingFields = {
mapping: 'mapping',
fallback: 'fallback',
};
export const viewCredentialsTabIndex = 1;
---
title: Alert integrations UX cleanup
merge_request: 55786
author:
type: changed
......@@ -2837,31 +2837,16 @@ msgstr ""
msgid "AlertMappingBuilder|Title is a required field for alerts in GitLab. Should the payload field you specified not be available, specifiy which field we should use instead. "
msgstr ""
msgid "AlertSettings|API URL"
msgstr ""
msgid "AlertSettings|Active"
msgstr ""
msgid "AlertSettings|Add URL and auth key to your Prometheus config file"
msgstr ""
msgid "AlertSettings|Add new integration"
msgstr ""
msgid "AlertSettings|Alert test payload"
msgstr ""
msgid "AlertSettings|Authorization key"
msgstr ""
msgid "AlertSettings|Authorization key has been successfully reset. Please save your changes now."
msgstr ""
msgid "AlertSettings|Configure details"
msgstr ""
msgid "AlertSettings|Copy"
msgid "AlertSettings|Customize alert payload mapping (optional)"
msgstr ""
msgid "AlertSettings|Delete integration"
......@@ -2873,9 +2858,6 @@ msgstr ""
msgid "AlertSettings|Enter integration name"
msgstr ""
msgid "AlertSettings|Enter test alert JSON...."
msgstr ""
msgid "AlertSettings|External Prometheus"
msgstr ""
......@@ -2885,21 +2867,12 @@ msgstr ""
msgid "AlertSettings|If you edit the payload, the stored mapping will be reset, and you'll need to re-map the fields."
msgstr ""
msgid "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."
msgid "AlertSettings|If you intend to create a custom mapping, provide an example payload from your monitoring tool and click \"parse payload fields\" button to continue. The sample payload is required for completing the custom mapping; if you want to skip the mapping step, progress straight to saving your integration."
msgstr ""
msgid "AlertSettings|In free versions of GitLab, only one integration for each type can be added. %{linkStart}Upgrade your subscription%{linkEnd} to add additional integrations."
msgstr ""
msgid "AlertSettings|Integration"
msgstr ""
msgid "AlertSettings|Learn more about our our upcoming %{linkStart}integrations%{linkEnd}"
msgstr ""
msgid "AlertSettings|Map fields (optional)"
msgstr ""
msgid "AlertSettings|Name integration"
msgstr ""
......@@ -2921,18 +2894,12 @@ msgstr ""
msgid "AlertSettings|Reset Key"
msgstr ""
msgid "AlertSettings|Reset key"
msgstr ""
msgid "AlertSettings|Reset the mapping"
msgstr ""
msgid "AlertSettings|Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in."
msgstr ""
msgid "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."
msgstr ""
msgid "AlertSettings|Sample alert payload (optional)"
msgstr ""
......@@ -2951,21 +2918,6 @@ msgstr ""
msgid "AlertSettings|Send test alert"
msgstr ""
msgid "AlertSettings|Test alert payload"
msgstr ""
msgid "AlertSettings|Test alert sent successfully. If you have made other changes, please save them now."
msgstr ""
msgid "AlertSettings|Test failed. Do you still want to save your changes anyway?"
msgstr ""
msgid "AlertSettings|There was an error updating the alert settings."
msgstr ""
msgid "AlertSettings|There was an error while trying to reset the key. Please refresh the page to try again."
msgstr ""
msgid "AlertSettings|URL cannot be blank and must start with http or https"
msgstr ""
......@@ -2984,9 +2936,6 @@ msgstr ""
msgid "AlertSettings|You can now set up alert endpoints for manually configured Prometheus instances in the Alerts section on the Operations settings page. Alert endpoint fields on this page have been deprecated."
msgstr ""
msgid "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."
msgstr ""
msgid "AlertSettings|Your integration was successfully updated."
msgstr ""
......
......@@ -41,14 +41,14 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
role="presentation"
>
<a
aria-controls="__BVID__22"
aria-controls="__BVID__19"
aria-disabled="true"
aria-posinset="2"
aria-selected="false"
aria-setsize="3"
class="nav-link disabled disabled gl-tab-nav-item"
href="#"
id="__BVID__22___BV_tab_button__"
id="__BVID__19___BV_tab_button__"
role="tab"
tabindex="-1"
target="_self"
......@@ -112,7 +112,7 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
for="integration-type"
id="integration-type__BV_label_"
>
Select integration type
1.Select integration type
</label>
<div
class="bv-no-focus-ring"
......@@ -150,19 +150,6 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
>
<!---->
<span>
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
<a
class="gl-link gl-display-inline-block"
href="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
rel="noopener noreferrer"
target="_blank"
>
GitLab documentation
</a>
to learn more about configuring your endpoint.
</span>
<label
class="gl-display-flex gl-flex-direction-column gl-mb-0 gl-w-max-content gl-my-4 gl-font-weight-normal"
>
......@@ -206,10 +193,6 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
<!---->
<!---->
<!---->
<!---->
</div>
<div
......@@ -264,12 +247,25 @@ exports[`AlertsSettingsForm with default values renders the initial template 1`]
>
<div
aria-hidden="true"
aria-labelledby="__BVID__22___BV_tab_button__"
aria-labelledby="__BVID__19___BV_tab_button__"
class="tab-pane disabled"
id="__BVID__22"
id="__BVID__19"
role="tabpanel"
style="display: none;"
>
<span>
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
<a
class="gl-link gl-display-inline-block"
href="https://docs.gitlab.com/ee/operations/incident_management/alert_integrations.html"
rel="noopener noreferrer"
target="_blank"
>
GitLab documentation
</a>
to learn more about configuring your endpoint.
</span>
<fieldset
class="form-group gl-form-group"
id="integration-webhook"
......
......@@ -43,10 +43,10 @@ describe('AlertsSettingsForm', () => {
});
};
const findForm = () => wrapper.find(GlForm);
const findSelect = () => wrapper.find(GlFormSelect);
const findFormFields = () => wrapper.findAll(GlFormInput);
const findFormToggle = () => wrapper.find(GlToggle);
const findForm = () => wrapper.findComponent(GlForm);
const findSelect = () => wrapper.findComponent(GlFormSelect);
const findFormFields = () => wrapper.findAllComponents(GlFormInput);
const findFormToggle = () => wrapper.findComponent(GlToggle);
const findSamplePayloadSection = () => wrapper.find('[data-testid="sample-payload-section"]');
const findMappingBuilderSection = () => wrapper.find(`[id = "mapping-builder"]`);
const findMappingBuilder = () => wrapper.findComponent(MappingBuilder);
......@@ -56,7 +56,7 @@ describe('AlertsSettingsForm', () => {
const findJsonTestSubmit = () => wrapper.find(`[data-testid="send-test-alert"]`);
const findJsonTextArea = () => wrapper.find(`[id = "test-payload"]`);
const findActionBtn = () => wrapper.find(`[data-testid="payload-action-btn"]`);
const findTabs = () => wrapper.findAll(GlTab);
const findTabs = () => wrapper.findAllComponents(GlTab);
afterEach(() => {
if (wrapper) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment