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-02-24 00:10:44 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-24 00:10:44 +0300
commit5e450e9022861d03048cc733c20585ad0891f5aa (patch)
tree6a5eb2f639fe66b3fa52008e2f99c31e1ce2d60b /app/assets/javascripts/alerts_settings
parentc37dd28c4afd33fee46cff8ddfdada8a3f54564c (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/alert_mapping_builder.vue43
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue13
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue140
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue109
-rw-r--r--app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json95
-rw-r--r--app/assets/javascripts/alerts_settings/constants.js15
-rw-r--r--app/assets/javascripts/alerts_settings/graphql.js16
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql7
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql14
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql4
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql4
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql4
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql25
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql (renamed from app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql)4
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql22
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql13
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql9
-rw-r--r--app/assets/javascripts/alerts_settings/utils/cache_updates.js34
-rw-r--r--app/assets/javascripts/alerts_settings/utils/mapping_transformations.js28
19 files changed, 343 insertions, 256 deletions
diff --git a/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue b/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue
index 1135562834a..07b2e59671e 100644
--- a/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue
+++ b/app/assets/javascripts/alerts_settings/components/alert_mapping_builder.vue
@@ -7,15 +7,12 @@ import {
GlSearchBoxByType,
GlTooltipDirective as GlTooltip,
} from '@gitlab/ui';
-import { cloneDeep } from 'lodash';
+import { cloneDeep, isEqual } from 'lodash';
import Vue from 'vue';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { s__, __ } from '~/locale';
-import {
- getMappingData,
- getPayloadFields,
- transformForSave,
-} from '../utils/mapping_transformations';
+import { mappingFields } from '../constants';
+import { getMappingData, transformForSave } from '../utils/mapping_transformations';
export const i18n = {
columns: {
@@ -33,6 +30,7 @@ export const i18n = {
export default {
i18n,
+ mappingFields,
components: {
GlIcon,
GlFormInput,
@@ -73,18 +71,15 @@ export default {
};
},
computed: {
- payloadFields() {
- return getPayloadFields(this.parsedPayload);
- },
mappingData() {
- return getMappingData(this.gitlabFields, this.payloadFields, this.savedMapping);
+ return getMappingData(this.gitlabFields, this.parsedPayload, this.savedMapping);
},
hasFallbackColumn() {
return this.gitlabFields.some(({ numberOfFallbacks }) => Boolean(numberOfFallbacks));
},
},
methods: {
- setMapping(gitlabKey, mappingKey, valueKey) {
+ setMapping(gitlabKey, mappingKey, valueKey = mappingFields.mapping) {
const fieldIndex = this.gitlabFields.findIndex((field) => field.name === gitlabKey);
const updatedField = { ...this.gitlabFields[fieldIndex], ...{ [valueKey]: mappingKey } };
Vue.set(this.gitlabFields, fieldIndex, updatedField);
@@ -100,11 +95,11 @@ export default {
return fields.filter((field) => field.label.toLowerCase().includes(search));
},
isSelected(fieldValue, mapping) {
- return fieldValue === mapping;
+ return isEqual(fieldValue, mapping);
},
- selectedValue(name) {
+ selectedValue(mapping) {
return (
- this.payloadFields.find((item) => item.name === name)?.label ||
+ this.parsedPayload.find((item) => isEqual(item.path, mapping))?.label ||
this.$options.i18n.makeSelection
);
},
@@ -150,7 +145,7 @@ export default {
:key="gitlabField.name"
class="gl-display-table-row"
>
- <div class="gl-display-table-cell gl-py-3 gl-pr-3 w-30p gl-vertical-align-middle">
+ <div class="gl-display-table-cell gl-py-3 gl-pr-3 gl-w-30p gl-vertical-align-middle">
<gl-form-input
aria-labelledby="gitlabFieldsHeader"
disabled
@@ -164,7 +159,7 @@ export default {
</div>
</div>
- <div class="gl-display-table-cell gl-py-3 gl-pr-3 w-30p gl-vertical-align-middle">
+ <div class="gl-display-table-cell gl-py-3 gl-pr-3 gl-w-30p gl-vertical-align-middle">
<gl-dropdown
:disabled="!gitlabField.mappingFields.length"
aria-labelledby="parsedFieldsHeader"
@@ -175,10 +170,10 @@ export default {
<gl-search-box-by-type @input="setSearchTerm($event, 'searchTerm', gitlabField.name)" />
<gl-dropdown-item
v-for="mappingField in filterFields(gitlabField.searchTerm, gitlabField.mappingFields)"
- :key="`${mappingField.name}__mapping`"
- :is-checked="isSelected(gitlabField.mapping, mappingField.name)"
+ :key="`${mappingField.path}__mapping`"
+ :is-checked="isSelected(gitlabField.mapping, mappingField.path)"
is-check-item
- @click="setMapping(gitlabField.name, mappingField.name, 'mapping')"
+ @click="setMapping(gitlabField.name, mappingField.path)"
>
{{ mappingField.label }}
</gl-dropdown-item>
@@ -188,7 +183,7 @@ export default {
</gl-dropdown>
</div>
- <div class="gl-display-table-cell gl-py-3 w-30p">
+ <div class="gl-display-table-cell gl-py-3 gl-w-30p">
<gl-dropdown
v-if="Boolean(gitlabField.numberOfFallbacks)"
:disabled="!gitlabField.mappingFields.length"
@@ -205,10 +200,12 @@ export default {
gitlabField.fallbackSearchTerm,
gitlabField.mappingFields,
)"
- :key="`${mappingField.name}__fallback`"
- :is-checked="isSelected(gitlabField.fallback, mappingField.name)"
+ :key="`${mappingField.path}__fallback`"
+ :is-checked="isSelected(gitlabField.fallback, mappingField.path)"
is-check-item
- @click="setMapping(gitlabField.name, mappingField.name, 'fallback')"
+ @click="
+ setMapping(gitlabField.name, mappingField.path, $options.mappingFields.fallback)
+ "
>
{{ mappingField.label }}
</gl-dropdown-item>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
index 6cfb4601192..3b8febfbb29 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
@@ -120,14 +120,17 @@ export default {
const { category, action } = trackAlertIntegrationsViewsOptions;
Tracking.event(category, action);
},
- setIntegrationToDelete({ name, id }) {
- this.integrationToDelete.id = id;
- this.integrationToDelete.name = name;
+ setIntegrationToDelete(integration) {
+ this.integrationToDelete = integration;
},
deleteIntegration() {
- this.$emit('delete-integration', { id: this.integrationToDelete.id });
+ const { id, type } = this.integrationToDelete;
+ this.$emit('delete-integration', { id, type });
this.integrationToDelete = { ...integrationToDeleteDefault };
},
+ editIntegration({ id, type }) {
+ this.$emit('edit-integration', { id, type });
+ },
},
};
</script>
@@ -169,7 +172,7 @@ export default {
<template #cell(actions)="{ item }">
<gl-button-group class="gl-ml-3">
- <gl-button icon="pencil" @click="$emit('edit-integration', { id: item.id })" />
+ <gl-button icon="pencil" @click="editIntegration(item)" />
<gl-button
v-gl-modal.deleteIntegration
:disabled="item.type === $options.typeSet.prometheus"
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 18372c54b84..ce595e848de 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form.vue
@@ -12,6 +12,8 @@ import {
GlModalDirective,
GlToggle,
} 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';
@@ -22,12 +24,9 @@ import {
typeSet,
} 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';
-// Mocks will be removed when integrating with BE is ready
-// data format is defined and will be the same as mocked (maybe with some minor changes)
-// feature rollout plan - https://gitlab.com/gitlab-org/gitlab/-/issues/262707#note_442529171
-import mockedCustomMapping from './mocks/parsedMapping.json';
export const i18n = {
integrationFormSteps: {
@@ -92,7 +91,6 @@ export const i18n = {
};
export default {
- integrationTypes,
placeholders: {
prometheus: targetPrometheusUrlPlaceholder,
},
@@ -128,6 +126,9 @@ export default {
multiIntegrations: {
default: false,
},
+ projectPath: {
+ default: '',
+ },
},
props: {
loading: {
@@ -151,18 +152,19 @@ export default {
},
data() {
return {
- selectedIntegration: integrationTypes[0].value,
+ integrationTypesOptions: Object.values(integrationTypes),
+ selectedIntegration: integrationTypes.none.value,
active: false,
formVisible: false,
integrationTestPayload: {
json: null,
error: null,
},
- resetSamplePayloadConfirmed: false,
- customMapping: null,
+ resetPayloadAndMappingConfirmed: false,
mapping: [],
parsingPayload: false,
currentIntegration: null,
+ parsedPayload: [],
};
},
computed: {
@@ -210,17 +212,11 @@ export default {
this.alertFields?.length
);
},
- parsedSamplePayload() {
- return this.customMapping?.samplePayload?.payloadAlerFields?.nodes;
- },
- savedMapping() {
- return this.customMapping?.storedMapping?.nodes;
- },
hasSamplePayload() {
- return Boolean(this.customMapping?.samplePayload);
+ return this.isValidNonEmptyJSON(this.currentIntegration?.payloadExample);
},
canEditPayload() {
- return this.hasSamplePayload && !this.resetSamplePayloadConfirmed;
+ return this.hasSamplePayload && !this.resetPayloadAndMappingConfirmed;
},
isResetAuthKeyDisabled() {
return !this.active && !this.integrationForm.token !== '';
@@ -240,25 +236,52 @@ export default {
isSelectDisabled() {
return this.currentIntegration !== null || !this.canAddIntegration;
},
+ savedMapping() {
+ return this.mapping;
+ },
},
watch: {
currentIntegration(val) {
if (val === null) {
- return this.reset();
+ this.reset();
+ return;
+ }
+ const { type, active, payloadExample, payloadAlertFields, payloadAttributeMappings } = val;
+ 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;
+ const mapping = payloadAttributeMappings.map((mappingItem) =>
+ omit(mappingItem, '__typename'),
+ );
+ this.updateMapping(mapping);
}
- this.selectedIntegration = val.type;
- this.active = val.active;
- if (val.type === typeSet.http && this.showMappingBuilder) this.getIntegrationMapping(val.id);
- return this.integrationTypeSelect();
+ this.toggleFormVisibility();
},
},
methods: {
- integrationTypeSelect() {
- if (this.selectedIntegration === integrationTypes[0].value) {
- this.formVisible = false;
- } else {
- this.formVisible = true;
+ isValidNonEmptyJSON(JSONString) {
+ if (JSONString) {
+ let parsed;
+ try {
+ parsed = JSON.parse(JSONString);
+ } catch (error) {
+ Sentry.captureException(error);
+ }
+ if (parsed) return !isEmpty(parsed);
}
+ return false;
+ },
+ toggleFormVisibility() {
+ this.formVisible = this.selectedIntegration !== integrationTypes.none.value;
},
submitWithTestPayload() {
this.$emit('set-test-alert-payload', this.testAlertPayload);
@@ -269,20 +292,15 @@ export default {
const customMappingVariables = this.glFeatures.multipleHttpIntegrationsCustomMapping
? {
payloadAttributeMappings: this.mapping,
- payloadExample: this.integrationTestPayload.json,
+ payloadExample: this.integrationTestPayload.json || '{}',
}
: {};
const variables =
this.selectedIntegration === typeSet.http
- ? {
- name,
- active: this.active,
- ...customMappingVariables,
- }
+ ? { name, active: this.active, ...customMappingVariables }
: { apiUrl, active: this.active };
const integrationPayload = { type: this.selectedIntegration, variables };
-
if (this.currentIntegration) {
return this.$emit('update-integration', integrationPayload);
}
@@ -291,11 +309,12 @@ export default {
return this.$emit('create-new-integration', integrationPayload);
},
reset() {
- this.selectedIntegration = integrationTypes[0].value;
- this.integrationTypeSelect();
+ this.selectedIntegration = integrationTypes.none.value;
+ this.toggleFormVisibility();
+ this.resetPayloadAndMapping();
if (this.currentIntegration) {
- return this.$emit('clear-current-integration');
+ return this.$emit('clear-current-integration', { type: this.currentIntegration.type });
}
return this.resetFormValues();
@@ -332,35 +351,40 @@ export default {
}
},
parseMapping() {
- // TODO: replace with real BE mutation when ready;
this.parsingPayload = true;
- return new Promise((resolve) => {
- setTimeout(() => resolve(mockedCustomMapping), 1000);
- })
- .then((res) => {
- const mapping = { ...res };
- delete mapping.storedMapping;
- this.customMapping = res;
- this.integrationTestPayload.json = res?.samplePayload.body;
- this.resetSamplePayloadConfirmed = false;
+ return this.$apollo
+ .query({
+ query: parseSamplePayloadQuery,
+ variables: { projectPath: this.projectPath, payload: this.integrationTestPayload.json },
+ })
+ .then(
+ ({
+ data: {
+ project: { alertManagementPayloadFields },
+ },
+ }) => {
+ this.parsedPayload = alertManagementPayloadFields;
+ this.resetPayloadAndMappingConfirmed = false;
- this.$toast.show(this.$options.i18n.integrationFormSteps.step4.payloadParsedSucessMsg);
+ this.$toast.show(this.$options.i18n.integrationFormSteps.step4.payloadParsedSucessMsg);
+ },
+ )
+ .catch(({ message }) => {
+ this.integrationTestPayload.error = message;
})
.finally(() => {
this.parsingPayload = false;
});
},
- getIntegrationMapping() {
- // TODO: replace with real BE mutation when ready;
- return Promise.resolve(mockedCustomMapping).then((res) => {
- this.customMapping = res;
- this.integrationTestPayload.json = res?.samplePayload.body;
- });
- },
updateMapping(mapping) {
this.mapping = mapping;
},
+ resetPayloadAndMapping() {
+ this.resetPayloadAndMappingConfirmed = true;
+ this.parsedPayload = [];
+ this.updateMapping([]);
+ },
},
};
</script>
@@ -377,8 +401,8 @@ export default {
v-model="selectedIntegration"
:disabled="isSelectDisabled"
class="mw-100"
- :options="$options.integrationTypes"
- @change="integrationTypeSelect"
+ :options="integrationTypesOptions"
+ @change="toggleFormVisibility"
/>
<div v-if="!canAddIntegration" class="gl-my-4" data-testid="multi-integrations-not-supported">
@@ -551,7 +575,7 @@ export default {
:title="$options.i18n.integrationFormSteps.step4.resetHeader"
:ok-title="$options.i18n.integrationFormSteps.step4.resetOk"
ok-variant="danger"
- @ok="resetSamplePayloadConfirmed = true"
+ @ok="resetPayloadAndMapping"
>
{{ $options.i18n.integrationFormSteps.step4.resetBody }}
</gl-modal>
@@ -566,7 +590,7 @@ export default {
>
<span>{{ $options.i18n.integrationFormSteps.step5.intro }}</span>
<mapping-builder
- :parsed-payload="parsedSamplePayload"
+ :parsed-payload="parsedPayload"
:saved-mapping="savedMapping"
:alert-fields="alertFields"
@onMappingUpdate="updateMapping"
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 366f2209fb2..f56dc05a5d2 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -8,15 +8,18 @@ import createPrometheusIntegrationMutation from '../graphql/mutations/create_pro
import destroyHttpIntegrationMutation from '../graphql/mutations/destroy_http_integration.mutation.graphql';
import resetHttpTokenMutation from '../graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '../graphql/mutations/reset_prometheus_token.mutation.graphql';
-import updateCurrentIntergrationMutation from '../graphql/mutations/update_current_intergration.mutation.graphql';
+import updateCurrentHttpIntegrationMutation from '../graphql/mutations/update_current_http_integration.mutation.graphql';
+import updateCurrentPrometheusIntegrationMutation from '../graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updateHttpIntegrationMutation from '../graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '../graphql/mutations/update_prometheus_integration.mutation.graphql';
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
+import getHttpIntegrationsQuery from '../graphql/queries/get_http_integrations.query.graphql';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import service from '../services';
import {
updateStoreAfterIntegrationDelete,
updateStoreAfterIntegrationAdd,
+ updateStoreAfterHttpIntegrationAdd,
} from '../utils/cache_updates';
import {
DELETE_INTEGRATION_ERROR,
@@ -84,6 +87,28 @@ export default {
createFlash({ message: err });
},
},
+ // TODO: we'll need to update the logic to request specific http integration by its id on edit
+ // when BE adds support for it https://gitlab.com/gitlab-org/gitlab/-/issues/321674
+ // currently the request for ALL http integrations is made and on specific integration edit we search it in the list
+ httpIntegrations: {
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ query: getHttpIntegrationsQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ };
+ },
+ update(data) {
+ const { alertManagementHttpIntegrations: { nodes: list = [] } = {} } = data.project || {};
+
+ return {
+ list,
+ };
+ },
+ error(err) {
+ createFlash({ message: err });
+ },
+ },
currentIntegration: {
query: getCurrentIntegrationQuery,
},
@@ -93,6 +118,7 @@ export default {
isUpdating: false,
testAlertPayload: null,
integrations: {},
+ httpIntegrations: {},
currentIntegration: null,
};
},
@@ -105,22 +131,28 @@ export default {
},
},
methods: {
+ isHttp(type) {
+ return type === typeSet.http;
+ },
createNewIntegration({ type, variables }) {
const { projectPath } = this;
+ const isHttp = this.isHttp(type);
this.isUpdating = true;
this.$apollo
.mutate({
- mutation:
- type === this.$options.typeSet.http
- ? createHttpIntegrationMutation
- : createPrometheusIntegrationMutation,
+ mutation: isHttp ? createHttpIntegrationMutation : createPrometheusIntegrationMutation,
variables: {
...variables,
projectPath,
},
update(store, { data }) {
updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath });
+ if (isHttp) {
+ updateStoreAfterHttpIntegrationAdd(store, getHttpIntegrationsQuery, data, {
+ projectPath,
+ });
+ }
},
})
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
@@ -157,10 +189,9 @@ export default {
this.isUpdating = true;
this.$apollo
.mutate({
- mutation:
- type === this.$options.typeSet.http
- ? updateHttpIntegrationMutation
- : updatePrometheusIntegrationMutation,
+ mutation: this.isHttp(type)
+ ? updateHttpIntegrationMutation
+ : updatePrometheusIntegrationMutation,
variables: {
...variables,
id: this.currentIntegration.id,
@@ -176,7 +207,7 @@ export default {
return this.validateAlertPayload();
}
- this.clearCurrentIntegration();
+ this.clearCurrentIntegration({ type });
return createFlash({
message: this.$options.i18n.changesSaved,
@@ -195,16 +226,13 @@ export default {
this.isUpdating = true;
this.$apollo
.mutate({
- mutation:
- type === this.$options.typeSet.http
- ? resetHttpTokenMutation
- : resetPrometheusTokenMutation,
+ mutation: this.isHttp(type) ? resetHttpTokenMutation : resetPrometheusTokenMutation,
variables,
})
.then(
({ data: { httpIntegrationResetToken, prometheusIntegrationResetToken } = {} } = {}) => {
- const error =
- httpIntegrationResetToken?.errors[0] || prometheusIntegrationResetToken?.errors[0];
+ const [error] =
+ httpIntegrationResetToken?.errors || prometheusIntegrationResetToken?.errors;
if (error) {
return createFlash({ message: error });
}
@@ -214,10 +242,10 @@ export default {
prometheusIntegrationResetToken?.integration;
this.$apollo.mutate({
- mutation: updateCurrentIntergrationMutation,
- variables: {
- ...integration,
- },
+ mutation: this.isHttp(type)
+ ? updateCurrentHttpIntegrationMutation
+ : updateCurrentPrometheusIntegrationMutation,
+ variables: integration,
});
return createFlash({
@@ -233,33 +261,30 @@ export default {
this.isUpdating = false;
});
},
- editIntegration({ id }) {
- const currentIntegration = this.integrations.list.find(
- (integration) => integration.id === id,
- );
+ editIntegration({ id, type }) {
+ let currentIntegration = this.integrations.list.find((integration) => integration.id === id);
+ if (this.isHttp(type)) {
+ const httpIntegrationMappingData = this.httpIntegrations.list.find(
+ (integration) => integration.id === id,
+ );
+ currentIntegration = { ...currentIntegration, ...httpIntegrationMappingData };
+ }
+
this.$apollo.mutate({
- mutation: updateCurrentIntergrationMutation,
- variables: {
- id: currentIntegration.id,
- name: currentIntegration.name,
- active: currentIntegration.active,
- token: currentIntegration.token,
- type: currentIntegration.type,
- url: currentIntegration.url,
- apiUrl: currentIntegration.apiUrl,
- },
+ mutation: this.isHttp(type)
+ ? updateCurrentHttpIntegrationMutation
+ : updateCurrentPrometheusIntegrationMutation,
+ variables: currentIntegration,
});
},
- deleteIntegration({ id }) {
+ deleteIntegration({ id, type }) {
const { projectPath } = this;
this.isUpdating = true;
this.$apollo
.mutate({
mutation: destroyHttpIntegrationMutation,
- variables: {
- id,
- },
+ variables: { id },
update(store, { data }) {
updateStoreAfterIntegrationDelete(store, getIntegrationsQuery, data, { projectPath });
},
@@ -269,7 +294,7 @@ export default {
if (error) {
return createFlash({ message: error });
}
- this.clearCurrentIntegration();
+ this.clearCurrentIntegration({ type });
return createFlash({
message: this.$options.i18n.integrationRemoved,
type: FLASH_TYPES.SUCCESS,
@@ -282,9 +307,11 @@ export default {
this.isUpdating = false;
});
},
- clearCurrentIntegration() {
+ clearCurrentIntegration({ type }) {
this.$apollo.mutate({
- mutation: updateCurrentIntergrationMutation,
+ mutation: this.isHttp(type)
+ ? updateCurrentHttpIntegrationMutation
+ : updateCurrentPrometheusIntegrationMutation,
variables: {},
});
},
diff --git a/app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json b/app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json
deleted file mode 100644
index 80fbebf2a60..00000000000
--- a/app/assets/javascripts/alerts_settings/components/mocks/parsedMapping.json
+++ /dev/null
@@ -1,95 +0,0 @@
-{
- "samplePayload": {
- "body": "{\n \"dashboardId\":1,\n \"evalMatches\":[\n {\n \"value\":1,\n \"metric\":\"Count\",\n \"tags\":{}\n }\n ],\n \"imageUrl\":\"https://grafana.com/static/assets/img/blog/mixed_styles.png\",\n \"message\":\"Notification Message\",\n \"orgId\":1,\n \"panelId\":2,\n \"ruleId\":1,\n \"ruleName\":\"Panel Title alert\",\n \"ruleUrl\":\"http://localhost:3000/d/hZ7BuVbWz/test-dashboard?fullscreen\\u0026edit\\u0026tab=alert\\u0026panelId=2\\u0026orgId=1\",\n \"state\":\"alerting\",\n \"tags\":{\n \"tag name\":\"tag value\"\n },\n \"title\":\"[Alerting] Panel Title alert\"\n}\n",
- "payloadAlerFields": {
- "nodes": [
- {
- "path": ["dashboardId"],
- "label": "Dashboard Id",
- "type": "string"
- },
- {
- "path": ["evalMatches"],
- "label": "Eval Matches",
- "type": "array"
- },
- {
- "path": ["createdAt"],
- "label": "Created At",
- "type": "datetime"
- },
- {
- "path": ["imageUrl"],
- "label": "Image Url",
- "type": "string"
- },
- {
- "path": ["message"],
- "label": "Message",
- "type": "string"
- },
- {
- "path": ["orgId"],
- "label": "Org Id",
- "type": "string"
- },
- {
- "path": ["panelId"],
- "label": "Panel Id",
- "type": "string"
- },
- {
- "path": ["ruleId"],
- "label": "Rule Id",
- "type": "string"
- },
- {
- "path": ["ruleName"],
- "label": "Rule Name",
- "type": "string"
- },
- {
- "path": ["ruleUrl"],
- "label": "Rule Url",
- "type": "string"
- },
- {
- "path": ["state"],
- "label": "State",
- "type": "string"
- },
- {
- "path": ["title"],
- "label": "Title",
- "type": "string"
- },
- {
- "path": ["tags", "tag"],
- "label": "Tags",
- "type": "string"
- }
- ]
- }
- },
- "storedMapping": {
- "nodes": [
- {
- "alertFieldName": "title",
- "payloadAlertPaths": "title",
- "fallbackAlertPaths": "ruleUrl"
- },
- {
- "alertFieldName": "description",
- "payloadAlertPaths": "message"
- },
- {
- "alertFieldName": "hosts",
- "payloadAlertPaths": "evalMatches"
- },
- {
- "alertFieldName": "startTime",
- "payloadAlertPaths": "createdAt"
- }
- ]
- }
-}
diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js
index ecd7c921b2f..cb3b4d2fc09 100644
--- a/app/assets/javascripts/alerts_settings/constants.js
+++ b/app/assets/javascripts/alerts_settings/constants.js
@@ -40,11 +40,11 @@ export const i18n = {
integration: s__('AlertSettings|Integration'),
};
-export const integrationTypes = [
- { value: '', text: s__('AlertSettings|Select integration type') },
- { value: 'HTTP', text: s__('AlertSettings|HTTP Endpoint') },
- { value: 'PROMETHEUS', text: s__('AlertSettings|External Prometheus') },
-];
+export const integrationTypes = {
+ none: { value: '', text: s__('AlertSettings|Select integration type') },
+ http: { value: 'HTTP', text: s__('AlertSettings|HTTP Endpoint') },
+ prometheus: { value: 'PROMETHEUS', text: s__('AlertSettings|External Prometheus') },
+};
export const typeSet = {
http: 'HTTP',
@@ -68,3 +68,8 @@ export const trackAlertIntegrationsViewsOptions = {
category: 'Alert Integrations',
action: 'view_alert_integrations_list',
};
+
+export const mappingFields = {
+ mapping: 'mapping',
+ fallback: 'fallback',
+};
diff --git a/app/assets/javascripts/alerts_settings/graphql.js b/app/assets/javascripts/alerts_settings/graphql.js
index 5fd05169533..c6c19d26adb 100644
--- a/app/assets/javascripts/alerts_settings/graphql.js
+++ b/app/assets/javascripts/alerts_settings/graphql.js
@@ -10,7 +10,18 @@ const resolvers = {
Mutation: {
updateCurrentIntegration: (
_,
- { id = null, name, active, token, type, url, apiUrl },
+ {
+ id = null,
+ name,
+ active,
+ token,
+ type,
+ url,
+ apiUrl,
+ payloadExample,
+ payloadAttributeMappings,
+ payloadAlertFields,
+ },
{ cache },
) => {
const sourceData = cache.readQuery({ query: getCurrentIntegrationQuery });
@@ -28,6 +39,9 @@ const resolvers = {
type,
url,
apiUrl,
+ payloadExample,
+ payloadAttributeMappings,
+ payloadAlertFields,
};
}
});
diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
new file mode 100644
index 00000000000..36446bbfe47
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_item.fragment.graphql
@@ -0,0 +1,7 @@
+#import "./integration_item.fragment.graphql"
+#import "./http_integration_payload_data.fragment.graphql"
+
+fragment HttpIntegrationItem on AlertManagementHttpIntegration {
+ ...IntegrationItem
+ ...HttpIntegrationPayloadData
+}
diff --git a/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
new file mode 100644
index 00000000000..b7ea50ebc44
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql
@@ -0,0 +1,14 @@
+fragment HttpIntegrationPayloadData on AlertManagementHttpIntegration {
+ payloadExample
+ payloadAttributeMappings {
+ fieldName
+ path
+ type
+ label
+ }
+ payloadAlertFields {
+ path
+ type
+ label
+ }
+}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
index f3fc10b4bd4..0c7d7627b6a 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql
@@ -1,4 +1,4 @@
-#import "../fragments/integration_item.fragment.graphql"
+#import "../fragments/http_integration_item.fragment.graphql"
mutation createHttpIntegration(
$projectPath: ID!
@@ -18,7 +18,7 @@ mutation createHttpIntegration(
) {
errors
integration {
- ...IntegrationItem
+ ...HttpIntegrationItem
}
}
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
index 0a49c140e6a..a3a50651fd0 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
@@ -1,10 +1,10 @@
-#import "../fragments/integration_item.fragment.graphql"
+#import "../fragments/http_integration_item.fragment.graphql"
mutation destroyHttpIntegration($id: ID!) {
httpIntegrationDestroy(input: { id: $id }) {
errors
integration {
- ...IntegrationItem
+ ...HttpIntegrationItem
}
}
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql
index 178d1e13047..c0754d8e32b 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql
@@ -1,10 +1,10 @@
-#import "../fragments/integration_item.fragment.graphql"
+#import "../fragments/http_integration_item.fragment.graphql"
mutation resetHttpIntegrationToken($id: ID!) {
httpIntegrationResetToken(input: { id: $id }) {
errors
integration {
- ...IntegrationItem
+ ...HttpIntegrationItem
}
}
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql
new file mode 100644
index 00000000000..5f3d305993c
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql
@@ -0,0 +1,25 @@
+mutation updateCurrentHttpIntegration(
+ $id: String
+ $name: String
+ $active: Boolean
+ $token: String
+ $type: String
+ $url: String
+ $apiUrl: String
+ $payloadExample: JsonString
+ $payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!]
+ $payloadAlertFields: [AlertManagementPayloadAlertField!]
+) {
+ updateCurrentIntegration(
+ id: $id
+ name: $name
+ active: $active
+ token: $token
+ type: $type
+ url: $url
+ apiUrl: $apiUrl
+ payloadExample: $payloadExample
+ payloadAttributeMappings: $payloadAttributeMappings
+ payloadAlertFields: $payloadAlertFields
+ ) @client
+}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
index 3505241309e..5bd63820629 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_intergration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql
@@ -1,4 +1,4 @@
-mutation updateCurrentIntegration(
+mutation updateCurrentPrometheusIntegration(
$id: String
$name: String
$active: Boolean
@@ -6,6 +6,7 @@ mutation updateCurrentIntegration(
$type: String
$url: String
$apiUrl: String
+ $samplePayload: String
) {
updateCurrentIntegration(
id: $id
@@ -15,5 +16,6 @@ mutation updateCurrentIntegration(
type: $type
url: $url
apiUrl: $apiUrl
+ samplePayload: $samplePayload
) @client
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql b/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
index bb5b334deeb..631937048c2 100644
--- a/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql
@@ -1,10 +1,24 @@
-#import "../fragments/integration_item.fragment.graphql"
+#import "../fragments/http_integration_item.fragment.graphql"
-mutation updateHttpIntegration($id: ID!, $name: String!, $active: Boolean!) {
- httpIntegrationUpdate(input: { id: $id, name: $name, active: $active }) {
+mutation updateHttpIntegration(
+ $id: ID!
+ $name: String!
+ $active: Boolean!
+ $payloadExample: JsonString
+ $payloadAttributeMappings: [AlertManagementPayloadAlertFieldInput!]
+) {
+ httpIntegrationUpdate(
+ input: {
+ id: $id
+ name: $name
+ active: $active
+ payloadExample: $payloadExample
+ payloadAttributeMappings: $payloadAttributeMappings
+ }
+ ) {
errors
integration {
- ...IntegrationItem
+ ...HttpIntegrationItem
}
}
}
diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
new file mode 100644
index 00000000000..e50dd89347f
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/graphql/queries/get_http_integrations.query.graphql
@@ -0,0 +1,13 @@
+#import "../fragments/http_integration_payload_data.fragment.graphql"
+
+# TODO: this query need to accept http integration id to request a sepcific integration
+query getHttpIntegrations($projectPath: ID!) {
+ project(fullPath: $projectPath) {
+ alertManagementHttpIntegrations {
+ nodes {
+ id
+ ...HttpIntegrationPayloadData
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql
new file mode 100644
index 00000000000..159b2661f0b
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/graphql/queries/parse_sample_payload.query.graphql
@@ -0,0 +1,9 @@
+query parsePayloadFields($projectPath: ID!, $payload: String!) {
+ project(fullPath: $projectPath) {
+ alertManagementPayloadFields(payloadExample: $payload) {
+ path
+ label
+ type
+ }
+ }
+}
diff --git a/app/assets/javascripts/alerts_settings/utils/cache_updates.js b/app/assets/javascripts/alerts_settings/utils/cache_updates.js
index 758f3eb6dd4..c29160c1e39 100644
--- a/app/assets/javascripts/alerts_settings/utils/cache_updates.js
+++ b/app/assets/javascripts/alerts_settings/utils/cache_updates.js
@@ -60,6 +60,32 @@ const addIntegrationToStore = (
});
};
+const addHttpIntegrationToStore = (store, query, { httpIntegrationCreate }, variables) => {
+ const integration = httpIntegrationCreate?.integration;
+ if (!integration) {
+ return;
+ }
+
+ const sourceData = store.readQuery({
+ query,
+ variables,
+ });
+
+ const data = produce(sourceData, (draftData) => {
+ // eslint-disable-next-line no-param-reassign
+ draftData.project.alertManagementHttpIntegrations.nodes = [
+ integration,
+ ...draftData.project.alertManagementHttpIntegrations.nodes,
+ ];
+ });
+
+ store.writeQuery({
+ query,
+ variables,
+ data,
+ });
+};
+
const onError = (data, message) => {
createFlash({ message });
throw new Error(data.errors);
@@ -82,3 +108,11 @@ export const updateStoreAfterIntegrationAdd = (store, query, data, variables) =>
addIntegrationToStore(store, query, data, variables);
}
};
+
+export const updateStoreAfterHttpIntegrationAdd = (store, query, data, variables) => {
+ if (hasErrors(data)) {
+ onError(data, ADD_INTEGRATION_ERROR);
+ } else {
+ addHttpIntegrationToStore(store, query, data, variables);
+ }
+};
diff --git a/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js b/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js
index a86103540c0..5c4b9bcd505 100644
--- a/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js
+++ b/app/assets/javascripts/alerts_settings/utils/mapping_transformations.js
@@ -1,3 +1,4 @@
+import { isEqual } from 'lodash';
/**
* Given data for GitLab alert fields, parsed payload fields data and previously stored mapping (if any)
* creates an object in a form convenient to build UI && interact with it
@@ -10,16 +11,19 @@
export const getMappingData = (gitlabFields, payloadFields, savedMapping) => {
return gitlabFields.map((gitlabField) => {
// find fields from payload that match gitlab alert field by type
- const mappingFields = payloadFields.filter(({ type }) => gitlabField.types.includes(type));
+ const mappingFields = payloadFields.filter(({ type }) =>
+ gitlabField.types.includes(type.toLowerCase()),
+ );
// find the mapping that was previously stored
- const foundMapping = savedMapping.find(({ fieldName }) => fieldName === gitlabField.name);
-
- const { fallbackAlertPaths, payloadAlertPaths } = foundMapping || {};
+ const foundMapping = savedMapping.find(
+ ({ fieldName }) => fieldName.toLowerCase() === gitlabField.name,
+ );
+ const { path: mapping, fallbackPath: fallback } = foundMapping || {};
return {
- mapping: payloadAlertPaths,
- fallback: fallbackAlertPaths,
+ mapping,
+ fallback,
searchTerm: '',
fallbackSearchTerm: '',
mappingFields,
@@ -36,7 +40,7 @@ export const getMappingData = (gitlabFields, payloadFields, savedMapping) => {
*/
export const transformForSave = (mappingData) => {
return mappingData.reduce((acc, field) => {
- const mapped = field.mappingFields.find(({ name }) => name === field.mapping);
+ const mapped = field.mappingFields.find(({ path }) => isEqual(path, field.mapping));
if (mapped) {
const { path, type, label } = mapped;
acc.push({
@@ -49,13 +53,3 @@ export const transformForSave = (mappingData) => {
return acc;
}, []);
};
-
-/**
- * Adds `name` prop to each provided by BE parsed payload field
- * @param {Object} payload - parsed sample payload
- *
- * @return {Object} same as input with an extra `name` property which basically serves as a key to make a match
- */
-export const getPayloadFields = (payload) => {
- return payload.map((field) => ({ ...field, name: field.path.join('_') }));
-};