Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-11-06 21:09:07 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-11-06 21:09:07 +0300
commitf3db01da507f86cfed412c7d337e3747744cc914 (patch)
tree3862e3ca223038c1390e2d19708ebeeecb040e00 /app/assets/javascripts/alerts_settings
parenta268b09416c8dc3da3af38933028fa26375b88e0 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/alerts_settings')
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue44
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue94
-rw-r--r--app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue94
-rw-r--r--app/assets/javascripts/alerts_settings/constants.js2
-rw-r--r--app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql10
-rw-r--r--app/assets/javascripts/alerts_settings/utils/cache_updates.js84
-rw-r--r--app/assets/javascripts/alerts_settings/utils/error_messages.js9
7 files changed, 262 insertions, 75 deletions
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 5ecb2dd3e58..f24c52f61da 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_integrations_list.vue
@@ -4,12 +4,15 @@ import {
GlButton,
GlIcon,
GlLoadingIcon,
+ GlModal,
+ GlModalDirective,
GlTable,
GlTooltipDirective,
+ GlSprintf,
} from '@gitlab/ui';
import { s__, __ } from '~/locale';
import Tracking from '~/tracking';
-import { trackAlertIntegrationsViewsOptions } from '../constants';
+import { trackAlertIntegrationsViewsOptions, integrationToDeleteDefault } from '../constants';
export const i18n = {
title: s__('AlertsIntegrations|Current integrations'),
@@ -36,10 +39,13 @@ export default {
GlButton,
GlIcon,
GlLoadingIcon,
+ GlModal,
GlTable,
+ GlSprintf,
},
directives: {
GlTooltip: GlTooltipDirective,
+ GlModal: GlModalDirective,
},
props: {
integrations: {
@@ -71,6 +77,11 @@ export default {
label: __('Actions'),
},
],
+ data() {
+ return {
+ integrationToDelete: integrationToDeleteDefault,
+ };
+ },
computed: {
tbodyTrClass() {
return {
@@ -86,6 +97,14 @@ export default {
const { category, action } = trackAlertIntegrationsViewsOptions;
Tracking.event(category, action);
},
+ intergrationToDelete({ name, id }) {
+ this.integrationToDelete.id = id;
+ this.integrationToDelete.name = name;
+ },
+ deleteIntergration() {
+ this.$emit('delete-integration', { id: this.integrationToDelete.id });
+ this.integrationToDelete = { ...integrationToDeleteDefault };
+ },
},
};
</script>
@@ -127,7 +146,11 @@ export default {
<template #cell(actions)="{ item }">
<gl-button-group>
<gl-button icon="pencil" @click="$emit('edit-integration', { id: item.id })" />
- <gl-button icon="remove" @click="$emit('delete-integration', { id: item.id })" />
+ <gl-button
+ v-gl-modal.deleteIntegration
+ icon="remove"
+ @click="intergrationToDelete(item)"
+ />
</gl-button-group>
</template>
@@ -143,5 +166,22 @@ export default {
</div>
</template>
</gl-table>
+ <gl-modal
+ modal-id="deleteIntegration"
+ :title="__('Are you sure?')"
+ :ok-title="s__('AlertSettings|Delete integration')"
+ ok-variant="danger"
+ @ok="deleteIntergration"
+ >
+ <gl-sprintf
+ :message="
+ s__(
+ 'AlertsIntegrations|You have opted to delete the %{integrationName} integration. Do you want to proceed? It means you will no longer receive alerts from this endpoint in your alert list, and this action cannot be undone.',
+ )
+ "
+ >
+ <template #integrationName>{{ integrationToDelete.name }}</template>
+ </gl-sprintf>
+ </gl-modal>
</div>
</template>
diff --git a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue b/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue
index 059623ba11c..946da8ef34c 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_form_new.vue
@@ -22,14 +22,12 @@ import {
JSON_VALIDATE_DELAY,
targetPrometheusUrlPlaceholder,
typeSet,
- defaultFormState,
} from '../constants';
export default {
targetPrometheusUrlPlaceholder,
JSON_VALIDATE_DELAY,
typeSet,
- defaultFormState,
i18n: {
integrationFormSteps: {
step1: {
@@ -113,14 +111,18 @@ export default {
data() {
return {
selectedIntegration: integrationTypesNew[0].value,
- active: false,
options: integrationTypesNew,
+ active: false,
formVisible: false,
+ integrationTestPayload: {
+ json: null,
+ error: null,
+ },
};
},
computed: {
jsonIsValid() {
- return this.integrationForm.integrationTestPayload.error === null;
+ return this.integrationTestPayload.error === null;
},
selectedIntegrationType() {
switch (this.selectedIntegration) {
@@ -129,43 +131,42 @@ export default {
case this.$options.typeSet.prometheus:
return this.prometheus;
default:
- return this.defaultFormState;
+ return {};
}
},
integrationForm() {
return {
name: this.currentIntegration?.name || '',
- integrationTestPayload: {
- json: null,
- error: null,
- },
active: this.currentIntegration?.active || false,
- token: this.currentIntegration?.token || '',
- url: this.currentIntegration?.url || '',
+ token: this.currentIntegration?.token || this.selectedIntegrationType.token,
+ url: this.currentIntegration?.url || this.selectedIntegrationType.url,
apiUrl: this.currentIntegration?.apiUrl || '',
};
},
},
watch: {
currentIntegration(val) {
+ if (val === null) {
+ return this.reset();
+ }
this.selectedIntegration = val.type;
this.active = val.active;
- this.onIntegrationTypeSelect();
+ return this.integrationTypeSelect();
},
},
methods: {
- onIntegrationTypeSelect() {
+ integrationTypeSelect() {
if (this.selectedIntegration === integrationTypesNew[0].value) {
this.formVisible = false;
} else {
this.formVisible = true;
}
},
- onSubmitWithTestPayload() {
+ submitWithTestPayload() {
// TODO: Test payload before saving via GraphQL
- this.onSubmit();
+ this.submit();
},
- onSubmit() {
+ submit() {
const { name, apiUrl } = this.integrationForm;
const variables =
this.selectedIntegration === this.$options.typeSet.http
@@ -179,27 +180,45 @@ export default {
return this.$emit('create-new-integration', integrationPayload);
},
- onReset() {
- this.integrationForm = this.defaultFormState;
+ reset() {
this.selectedIntegration = integrationTypesNew[0].value;
- this.onIntegrationTypeSelect();
+ this.integrationTypeSelect();
+
+ if (this.currentIntegration) {
+ return this.$emit('clear-current-integration');
+ }
+
+ return this.resetFormValues();
+ },
+ resetFormValues() {
+ this.integrationForm.name = '';
+ this.integrationForm.apiUrl = '';
+ this.integrationTestPayload = {
+ json: null,
+ error: null,
+ };
+ this.active = false;
},
- onResetAuthKey() {
+ resetAuthKey() {
+ if (!this.currentIntegration) {
+ return;
+ }
+
this.$emit('reset-token', {
type: this.selectedIntegration,
variables: { id: this.currentIntegration.id },
});
},
validateJson() {
- this.integrationForm.integrationTestPayload.error = null;
- if (this.integrationForm.integrationTestPayload.json === '') {
+ this.integrationTestPayload.error = null;
+ if (this.integrationTestPayload.json === '') {
return;
}
try {
- JSON.parse(this.integrationForm.integrationTestPayload.json);
+ JSON.parse(this.integrationTestPayload.json);
} catch (e) {
- this.integrationForm.integrationTestPayload.error = JSON.stringify(e.message);
+ this.integrationTestPayload.error = JSON.stringify(e.message);
}
},
},
@@ -207,7 +226,7 @@ export default {
</script>
<template>
- <gl-form class="gl-mt-6" @submit.prevent="onSubmit" @reset.prevent="onReset">
+ <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
@@ -217,8 +236,9 @@ export default {
>
<gl-form-select
v-model="selectedIntegration"
+ :disabled="currentIntegration !== null"
:options="options"
- @change="onIntegrationTypeSelect"
+ @change="integrationTypeSelect"
/>
<alert-settings-form-help-block
@@ -279,7 +299,11 @@ export default {
<gl-form-input-group id="url" readonly :value="integrationForm.url">
<template #append>
- <clipboard-button :text="integrationForm.url" :title="__('Copy')" class="gl-m-0!" />
+ <clipboard-button
+ :text="integrationForm.url || ''"
+ :title="__('Copy')"
+ class="gl-m-0!"
+ />
</template>
</gl-form-input-group>
</div>
@@ -296,7 +320,11 @@ export default {
:value="integrationForm.token"
>
<template #append>
- <clipboard-button :text="integrationForm.token" :title="__('Copy')" class="gl-m-0!" />
+ <clipboard-button
+ :text="integrationForm.token || ''"
+ :title="__('Copy')"
+ class="gl-m-0!"
+ />
</template>
</gl-form-input-group>
@@ -308,7 +336,7 @@ export default {
:title="$options.i18n.integrationFormSteps.step3.reset"
:ok-title="$options.i18n.integrationFormSteps.step3.reset"
ok-variant="danger"
- @ok="onResetAuthKey"
+ @ok="resetAuthKey"
>
{{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
</gl-modal>
@@ -318,7 +346,7 @@ export default {
id="test-integration"
:label="$options.i18n.integrationFormSteps.step4.label"
label-for="test-integration"
- :invalid-feedback="integrationForm.integrationTestPayload.error"
+ :invalid-feedback="integrationTestPayload.error"
>
<alert-settings-form-help-block
:message="$options.i18n.integrationFormSteps.step4.help"
@@ -327,8 +355,8 @@ export default {
<gl-form-textarea
id="test-integration"
- v-model.trim="integrationForm.integrationTestPayload.json"
- :disabled="!integrationForm.active"
+ v-model.trim="integrationTestPayload.json"
+ :disabled="!active"
:state="jsonIsValid"
:placeholder="$options.i18n.integrationFormSteps.step4.placeholder"
class="gl-my-4"
@@ -354,7 +382,7 @@ export default {
category="secondary"
variant="success"
class="gl-mr-1 js-no-auto-disable"
- @click="onSubmitWithTestPayload"
+ @click="submitWithTestPayload"
>{{ s__('AlertSettings|Save and test payload') }}</gl-button
>
<gl-button
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 0a59a5981ef..e9e7b1407bc 100644
--- a/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
+++ b/app/assets/javascripts/alerts_settings/components/alerts_settings_wrapper.vue
@@ -1,5 +1,4 @@
<script>
-import produce from 'immer';
import { s__ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { fetchPolicies } from '~/lib/graphql';
@@ -9,12 +8,17 @@ import createHttpIntegrationMutation from '../graphql/mutations/create_http_inte
import createPrometheusIntegrationMutation from '../graphql/mutations/create_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 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 IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue';
import { typeSet } from '../constants';
+import {
+ updateStoreAfterIntegrationDelete,
+ updateStoreAfterIntegrationAdd,
+} from '../utils/cache_updates';
export default {
typeSet,
@@ -22,6 +26,7 @@ export default {
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.'),
},
components: {
IntegrationsList,
@@ -89,6 +94,8 @@ export default {
},
methods: {
createNewIntegration({ type, variables }) {
+ const { projectPath } = this;
+
this.isUpdating = true;
this.$apollo
.mutate({
@@ -98,9 +105,11 @@ export default {
: createPrometheusIntegrationMutation,
variables: {
...variables,
- projectPath: this.projectPath,
+ projectPath,
+ },
+ update(store, { data }) {
+ updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath });
},
- update: this.updateIntegrations,
})
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
const error = httpIntegrationCreate?.errors[0] || prometheusIntegrationCreate?.errors[0];
@@ -119,41 +128,6 @@ export default {
this.isUpdating = false;
});
},
- updateIntegrations(
- store,
- {
- data: { httpIntegrationCreate, prometheusIntegrationCreate },
- },
- ) {
- const integration =
- httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
- if (!integration) {
- return;
- }
-
- const sourceData = store.readQuery({
- query: getIntegrationsQuery,
- variables: {
- projectPath: this.projectPath,
- },
- });
-
- const data = produce(sourceData, draftData => {
- // eslint-disable-next-line no-param-reassign
- draftData.project.alertManagementIntegrations.nodes = [
- integration,
- ...draftData.project.alertManagementIntegrations.nodes,
- ];
- });
-
- store.writeQuery({
- query: getIntegrationsQuery,
- variables: {
- projectPath: this.projectPath,
- },
- data,
- });
- },
updateIntegration({ type, variables }) {
this.isUpdating = true;
this.$apollo
@@ -201,6 +175,12 @@ export default {
if (error) {
return createFlash({ message: error });
}
+
+ const integration =
+ httpIntegrationResetToken?.integration ||
+ prometheusIntegrationResetToken?.integration;
+ this.currentIntegration = integration;
+
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
@@ -217,8 +197,41 @@ export default {
editIntegration({ id }) {
this.currentIntegration = this.integrations.list.find(integration => integration.id === id);
},
- deleteIntegration() {
- // TODO, handle delete via GraphQL
+ deleteIntegration({ id }) {
+ const { projectPath } = this;
+
+ this.isUpdating = true;
+ this.$apollo
+ .mutate({
+ mutation: destroyHttpIntegrationMutation,
+ variables: {
+ id,
+ },
+ update(store, { data }) {
+ updateStoreAfterIntegrationDelete(store, getIntegrationsQuery, data, { projectPath });
+ },
+ })
+ .then(({ data: { httpIntegrationDestroy } = {} } = {}) => {
+ const error = httpIntegrationDestroy?.errors[0];
+ if (error) {
+ return createFlash({ message: error });
+ }
+ this.currentIntegration = null;
+ return createFlash({
+ message: this.$options.i18n.integrationRemoved,
+ type: FLASH_TYPES.SUCCESS,
+ });
+ })
+ .catch(err => {
+ this.errored = true;
+ createFlash({ message: err });
+ })
+ .finally(() => {
+ this.isUpdating = false;
+ });
+ },
+ clearCurrentIntegration() {
+ this.currentIntegration = null;
},
},
};
@@ -239,6 +252,7 @@ export default {
@create-new-integration="createNewIntegration"
@update-integration="updateIntegration"
@reset-token="resetToken"
+ @clear-current-integration="clearCurrentIntegration"
/>
<settings-form-old v-else />
</div>
diff --git a/app/assets/javascripts/alerts_settings/constants.js b/app/assets/javascripts/alerts_settings/constants.js
index 4ab8d215572..9cf2f356e0a 100644
--- a/app/assets/javascripts/alerts_settings/constants.js
+++ b/app/assets/javascripts/alerts_settings/constants.js
@@ -66,6 +66,8 @@ export const defaultFormState = {
integrationTestPayload: { json: null, error: null },
};
+export const integrationToDeleteDefault = { id: null, name: '' };
+
export const JSON_VALIDATE_DELAY = 250;
export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/';
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
new file mode 100644
index 00000000000..0a49c140e6a
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/graphql/mutations/destroy_http_integration.mutation.graphql
@@ -0,0 +1,10 @@
+#import "../fragments/integration_item.fragment.graphql"
+
+mutation destroyHttpIntegration($id: ID!) {
+ httpIntegrationDestroy(input: { id: $id }) {
+ errors
+ integration {
+ ...IntegrationItem
+ }
+ }
+}
diff --git a/app/assets/javascripts/alerts_settings/utils/cache_updates.js b/app/assets/javascripts/alerts_settings/utils/cache_updates.js
new file mode 100644
index 00000000000..18054b29fe9
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/utils/cache_updates.js
@@ -0,0 +1,84 @@
+import produce from 'immer';
+import createFlash from '~/flash';
+
+import { DELETE_INTEGRATION_ERROR, ADD_INTEGRATION_ERROR } from './error_messages';
+
+const deleteIntegrationFromStore = (store, query, { httpIntegrationDestroy }, variables) => {
+ const integration = httpIntegrationDestroy?.integration;
+ if (!integration) {
+ return;
+ }
+
+ const sourceData = store.readQuery({
+ query,
+ variables,
+ });
+
+ const data = produce(sourceData, draftData => {
+ // eslint-disable-next-line no-param-reassign
+ draftData.project.alertManagementIntegrations.nodes = draftData.project.alertManagementIntegrations.nodes.filter(
+ ({ id }) => id !== integration.id,
+ );
+ });
+
+ store.writeQuery({
+ query,
+ variables,
+ data,
+ });
+};
+
+const addIntegrationToStore = (
+ store,
+ query,
+ { httpIntegrationCreate, prometheusIntegrationCreate },
+ variables,
+) => {
+ const integration =
+ httpIntegrationCreate?.integration || prometheusIntegrationCreate?.integration;
+ if (!integration) {
+ return;
+ }
+
+ const sourceData = store.readQuery({
+ query,
+ variables,
+ });
+
+ const data = produce(sourceData, draftData => {
+ // eslint-disable-next-line no-param-reassign
+ draftData.project.alertManagementIntegrations.nodes = [
+ integration,
+ ...draftData.project.alertManagementIntegrations.nodes,
+ ];
+ });
+
+ store.writeQuery({
+ query,
+ variables,
+ data,
+ });
+};
+
+const onError = (data, message) => {
+ createFlash({ message });
+ throw new Error(data.errors);
+};
+
+export const hasErrors = ({ errors = [] }) => errors?.length;
+
+export const updateStoreAfterIntegrationDelete = (store, query, data, variables) => {
+ if (hasErrors(data)) {
+ onError(data, DELETE_INTEGRATION_ERROR);
+ } else {
+ deleteIntegrationFromStore(store, query, data, variables);
+ }
+};
+
+export const updateStoreAfterIntegrationAdd = (store, query, data, variables) => {
+ if (hasErrors(data)) {
+ onError(data, ADD_INTEGRATION_ERROR);
+ } else {
+ addIntegrationToStore(store, query, data, variables);
+ }
+};
diff --git a/app/assets/javascripts/alerts_settings/utils/error_messages.js b/app/assets/javascripts/alerts_settings/utils/error_messages.js
new file mode 100644
index 00000000000..2e6058fc81a
--- /dev/null
+++ b/app/assets/javascripts/alerts_settings/utils/error_messages.js
@@ -0,0 +1,9 @@
+import { s__ } from '~/locale';
+
+export const DELETE_INTEGRATION_ERROR = s__(
+ 'AlertsIntegrations|The integration could not be deleted. Please try again.',
+);
+
+export const ADD_INTEGRATION_ERROR = s__(
+ 'AlertsIntegrations|The integration could not be added. Please try again.',
+);