Commit 9ce4ced5 authored by David O'Regan's avatar David O'Regan Committed by Olena Horal-Koretska

Add support for HTTP Create

Add support for alert HTTP
create supported via GraphQL
parent a7537882
<script> <script>
import { GlTable, GlIcon, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui'; import {
GlButtonGroup,
GlButton,
GlIcon,
GlLoadingIcon,
GlTable,
GlTooltipDirective,
} from '@gitlab/ui';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { trackAlertIntegrationsViewsOptions } from '../constants'; import { trackAlertIntegrationsViewsOptions } from '../constants';
...@@ -25,9 +32,11 @@ const bodyTrClass = ...@@ -25,9 +32,11 @@ const bodyTrClass =
export default { export default {
i18n, i18n,
components: { components: {
GlTable, GlButtonGroup,
GlButton,
GlIcon, GlIcon,
GlLoadingIcon, GlLoadingIcon,
GlTable,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -57,6 +66,10 @@ export default { ...@@ -57,6 +66,10 @@ export default {
key: 'type', key: 'type',
label: __('Type'), label: __('Type'),
}, },
{
key: 'actions',
label: __('Actions'),
},
], ],
computed: { computed: {
tbodyTrClass() { tbodyTrClass() {
...@@ -111,6 +124,13 @@ export default { ...@@ -111,6 +124,13 @@ export default {
</span> </span>
</template> </template>
<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-group>
</template>
<template #table-busy> <template #table-busy>
<gl-loading-icon size="lg" color="dark" class="mt-3" /> <gl-loading-icon size="lg" color="dark" class="mt-3" />
</template> </template>
......
...@@ -21,12 +21,14 @@ import { ...@@ -21,12 +21,14 @@ import {
JSON_VALIDATE_DELAY, JSON_VALIDATE_DELAY,
targetPrometheusUrlPlaceholder, targetPrometheusUrlPlaceholder,
typeSet, typeSet,
defaultFormState,
} from '../constants'; } from '../constants';
export default { export default {
targetPrometheusUrlPlaceholder, targetPrometheusUrlPlaceholder,
JSON_VALIDATE_DELAY, JSON_VALIDATE_DELAY,
typeSet, typeSet,
defaultFormState,
i18n: { i18n: {
integrationFormSteps: { integrationFormSteps: {
step1: { step1: {
...@@ -62,6 +64,11 @@ export default { ...@@ -62,6 +64,11 @@ export default {
label: s__('AlertSettings|Prometheus API base URL'), label: s__('AlertSettings|Prometheus API base URL'),
help: s__('AlertSettings|URL cannot be blank and must start with http or https'), 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.',
),
},
}, },
}, },
components: { components: {
...@@ -95,23 +102,18 @@ export default { ...@@ -95,23 +102,18 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
currentIntegration: {
type: Object,
required: false,
default: null,
},
}, },
data() { data() {
return { return {
selectedIntegration: integrationTypesNew[0].value, selectedIntegration: integrationTypesNew[0].value,
active: false,
options: integrationTypesNew, options: integrationTypesNew,
formVisible: false, formVisible: false,
integrationForm: {
name: '',
integrationTestPayload: {
json: null,
error: null,
},
active: false,
authKey: '',
url: '',
apiUrl: '',
},
}; };
}, },
computed: { computed: {
...@@ -125,9 +127,29 @@ export default { ...@@ -125,9 +127,29 @@ export default {
case this.$options.typeSet.prometheus: case this.$options.typeSet.prometheus:
return this.prometheus; return this.prometheus;
default: default:
return {}; return this.defaultFormState;
} }
}, },
integrationForm() {
return {
name: this.currentIntegration?.name || '',
integrationTestPayload: {
json: null,
error: null,
},
active: this.currentIntegration?.active || false,
token: this.currentIntegration?.token || '',
url: this.currentIntegration?.url || '',
apiUrl: this.currentIntegration?.apiUrl || '',
};
},
},
watch: {
currentIntegration(val) {
this.selectedIntegration = val.type;
this.active = val.active;
this.onIntegrationTypeSelect();
},
}, },
methods: { methods: {
onIntegrationTypeSelect() { onIntegrationTypeSelect() {
...@@ -142,18 +164,29 @@ export default { ...@@ -142,18 +164,29 @@ export default {
this.onSubmit(); this.onSubmit();
}, },
onSubmit() { onSubmit() {
const { name, apiUrl, active } = this.integrationForm; const { name, apiUrl } = this.integrationForm;
const variables = const variables =
this.selectedIntegration === this.$options.typeSet.http this.selectedIntegration === this.$options.typeSet.http
? { name, active } ? { name, active: this.active }
: { apiUrl, active }; : { apiUrl, active: this.active };
this.$emit('on-create-new-integration', { type: this.selectedIntegration, variables }); const integrationPayload = { type: this.selectedIntegration, variables };
if (this.currentIntegration) {
return this.$emit('update-integration', integrationPayload);
}
return this.$emit('create-new-integration', integrationPayload);
}, },
onReset() { onReset() {
// TODO: Reset form values this.integrationForm = this.defaultFormState;
this.selectedIntegration = integrationTypesNew[0].value;
this.onIntegrationTypeSelect();
}, },
onResetAuthKey() { onResetAuthKey() {
// TODO: Handle reset auth key via GraphQL this.$emit('reset-token', {
type: this.selectedIntegration,
variables: { id: this.currentIntegration.id },
});
}, },
validateJson() { validateJson() {
this.integrationForm.integrationTestPayload.error = null; this.integrationForm.integrationTestPayload.error = null;
...@@ -214,7 +247,7 @@ export default { ...@@ -214,7 +247,7 @@ export default {
/> />
<gl-toggle <gl-toggle
v-model="integrationForm.active" v-model="active"
:is-loading="loading" :is-loading="loading"
:label="__('Active')" :label="__('Active')"
class="gl-my-4 gl-font-weight-normal" class="gl-my-4 gl-font-weight-normal"
...@@ -242,13 +275,9 @@ export default { ...@@ -242,13 +275,9 @@ export default {
{{ s__('AlertSettings|Webhook URL') }} {{ s__('AlertSettings|Webhook URL') }}
</span> </span>
<gl-form-input-group id="url" readonly :value="selectedIntegrationType.url"> <gl-form-input-group id="url" readonly :value="integrationForm.url">
<template #append> <template #append>
<clipboard-button <clipboard-button :text="integrationForm.url" :title="__('Copy')" class="gl-m-0!" />
:text="selectedIntegrationType.url || ''"
:title="__('Copy')"
class="gl-m-0!"
/>
</template> </template>
</gl-form-input-group> </gl-form-input-group>
</div> </div>
...@@ -262,14 +291,10 @@ export default { ...@@ -262,14 +291,10 @@ export default {
id="authorization-key" id="authorization-key"
class="gl-mb-2" class="gl-mb-2"
readonly readonly
:value="selectedIntegrationType.authKey" :value="integrationForm.token"
> >
<template #append> <template #append>
<clipboard-button <clipboard-button :text="integrationForm.token" :title="__('Copy')" class="gl-m-0!" />
:text="selectedIntegrationType.authKey || ''"
:title="__('Copy')"
class="gl-m-0!"
/>
</template> </template>
</gl-form-input-group> </gl-form-input-group>
...@@ -281,9 +306,9 @@ export default { ...@@ -281,9 +306,9 @@ export default {
:title="$options.i18n.integrationFormSteps.step3.reset" :title="$options.i18n.integrationFormSteps.step3.reset"
:ok-title="$options.i18n.integrationFormSteps.step3.reset" :ok-title="$options.i18n.integrationFormSteps.step3.reset"
ok-variant="danger" ok-variant="danger"
@ok="() => {}" @ok="onResetAuthKey"
> >
{{ $options.i18n.integrationFormSteps.step3.reset }} {{ $options.i18n.integrationFormSteps.restKeyInfo.label }}
</gl-modal> </gl-modal>
</div> </div>
</gl-form-group> </gl-form-group>
......
...@@ -59,7 +59,7 @@ export default { ...@@ -59,7 +59,7 @@ export default {
selectedIntegration: integrationTypes[0].value, selectedIntegration: integrationTypes[0].value,
options: integrationTypes, options: integrationTypes,
active: false, active: false,
authKey: '', token: '',
targetUrl: '', targetUrl: '',
feedback: { feedback: {
variant: 'danger', variant: 'danger',
...@@ -98,7 +98,7 @@ export default { ...@@ -98,7 +98,7 @@ export default {
case 'HTTP': { case 'HTTP': {
return { return {
url: this.generic.url, url: this.generic.url,
authKey: this.generic.authKey, token: this.generic.token,
active: this.generic.active, active: this.generic.active,
resetKey: this.resetKey.bind(this), resetKey: this.resetKey.bind(this),
}; };
...@@ -106,7 +106,7 @@ export default { ...@@ -106,7 +106,7 @@ export default {
case 'PROMETHEUS': { case 'PROMETHEUS': {
return { return {
url: this.prometheus.url, url: this.prometheus.url,
authKey: this.prometheus.authKey, token: this.prometheus.token,
active: this.prometheus.active, active: this.prometheus.active,
resetKey: this.resetKey.bind(this, 'PROMETHEUS'), resetKey: this.resetKey.bind(this, 'PROMETHEUS'),
targetUrl: this.prometheus.prometheusApiUrl, targetUrl: this.prometheus.prometheusApiUrl,
...@@ -167,7 +167,7 @@ export default { ...@@ -167,7 +167,7 @@ export default {
this.setOpsgenieAsDefault(); this.setOpsgenieAsDefault();
} }
this.active = this.selectedIntegrationType.active; this.active = this.selectedIntegrationType.active;
this.authKey = this.selectedIntegrationType.authKey ?? ''; this.token = this.selectedIntegrationType.token ?? '';
}, },
methods: { methods: {
createUserErrorMessage(errors = {}) { createUserErrorMessage(errors = {}) {
...@@ -212,8 +212,8 @@ export default { ...@@ -212,8 +212,8 @@ export default {
return fn return fn
.then(({ data: { token } }) => { .then(({ data: { token } }) => {
this.authKey = token; this.token = token;
this.setFeedback({ feedbackMessage: this.$options.i18n.authKeyRest, variant: 'success' }); this.setFeedback({ feedbackMessage: this.$options.i18n.tokenRest, variant: 'success' });
}) })
.catch(() => { .catch(() => {
this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' }); this.setFeedback({ feedbackMessage: this.$options.i18n.errorKeyMsg, variant: 'danger' });
...@@ -313,7 +313,7 @@ export default { ...@@ -313,7 +313,7 @@ export default {
.updateTestAlert({ .updateTestAlert({
endpoint: this.selectedIntegrationType.url, endpoint: this.selectedIntegrationType.url,
data: this.testAlert.json, data: this.testAlert.json,
authKey: this.selectedIntegrationType.authKey, token: this.selectedIntegrationType.token,
}) })
.then(() => { .then(() => {
this.setFeedback({ this.setFeedback({
...@@ -439,21 +439,21 @@ export default { ...@@ -439,21 +439,21 @@ export default {
{{ prometheusInfo }} {{ prometheusInfo }}
</span> </span>
</gl-form-group> </gl-form-group>
<gl-form-group :label="$options.i18n.authKeyLabel" label-for="authorization-key"> <gl-form-group :label="$options.i18n.tokenLabel" label-for="authorization-key">
<gl-form-input-group id="authorization-key" class="gl-mb-2" readonly :value="authKey"> <gl-form-input-group id="authorization-key" class="gl-mb-2" readonly :value="token">
<template #append> <template #append>
<clipboard-button <clipboard-button
:text="authKey" :text="token"
:title="$options.i18n.copyToClipboard" :title="$options.i18n.copyToClipboard"
class="gl-m-0!" class="gl-m-0!"
/> />
</template> </template>
</gl-form-input-group> </gl-form-input-group>
<gl-button v-gl-modal.authKeyModal :disabled="!active" class="gl-mt-3">{{ <gl-button v-gl-modal.tokenModal :disabled="!active" class="gl-mt-3">{{
$options.i18n.resetKey $options.i18n.resetKey
}}</gl-button> }}</gl-button>
<gl-modal <gl-modal
modal-id="authKeyModal" modal-id="tokenModal"
:title="$options.i18n.resetKey" :title="$options.i18n.resetKey"
:ok-title="$options.i18n.resetKey" :ok-title="$options.i18n.resetKey"
ok-variant="danger" ok-variant="danger"
......
...@@ -7,6 +7,10 @@ import createFlash, { FLASH_TYPES } from '~/flash'; ...@@ -7,6 +7,10 @@ import createFlash, { FLASH_TYPES } from '~/flash';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql'; import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import createHttpIntegrationMutation from '../graphql/mutations/create_http_integration.mutation.graphql'; import createHttpIntegrationMutation from '../graphql/mutations/create_http_integration.mutation.graphql';
import createPrometheusIntegrationMutation from '../graphql/mutations/create_prometheus_integration.mutation.graphql'; 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 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 IntegrationsList from './alerts_integrations_list.vue';
import SettingsFormOld from './alerts_settings_form_old.vue'; import SettingsFormOld from './alerts_settings_form_old.vue';
import SettingsFormNew from './alerts_settings_form_new.vue'; import SettingsFormNew from './alerts_settings_form_new.vue';
...@@ -52,16 +56,16 @@ export default { ...@@ -52,16 +56,16 @@ export default {
list, list,
}; };
}, },
error() { error(err) {
this.errored = true; createFlash({ message: err });
}, },
}, },
}, },
data() { data() {
return { return {
errored: false,
isUpdating: false, isUpdating: false,
integrations: {}, integrations: {},
currentIntegration: null,
}; };
}, },
computed: { computed: {
...@@ -84,7 +88,7 @@ export default { ...@@ -84,7 +88,7 @@ export default {
}, },
}, },
methods: { methods: {
onCreateNewIntegration({ type, variables }) { createNewIntegration({ type, variables }) {
this.isUpdating = true; this.isUpdating = true;
this.$apollo this.$apollo
.mutate({ .mutate({
...@@ -109,7 +113,6 @@ export default { ...@@ -109,7 +113,6 @@ export default {
}); });
}) })
.catch(err => { .catch(err => {
this.errored = true;
createFlash({ message: err }); createFlash({ message: err });
}) })
.finally(() => { .finally(() => {
...@@ -151,6 +154,72 @@ export default { ...@@ -151,6 +154,72 @@ export default {
data, data,
}); });
}, },
updateIntegration({ type, variables }) {
this.isUpdating = true;
this.$apollo
.mutate({
mutation:
type === this.$options.typeSet.http
? updateHttpIntegrationMutation
: updatePrometheusIntegrationMutation,
variables: {
...variables,
id: this.currentIntegration.id,
},
})
.then(({ data: { httpIntegrationUpdate, prometheusIntegrationUpdate } = {} } = {}) => {
const error = httpIntegrationUpdate?.errors[0] || prometheusIntegrationUpdate?.errors[0];
if (error) {
return createFlash({ message: error });
}
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
});
})
.catch(err => {
createFlash({ message: err });
})
.finally(() => {
this.isUpdating = false;
});
},
resetToken({ type, variables }) {
this.isUpdating = true;
this.$apollo
.mutate({
mutation:
type === this.$options.typeSet.http
? resetHttpTokenMutation
: resetPrometheusTokenMutation,
variables,
})
.then(
({ data: { httpIntegrationResetToken, prometheusIntegrationResetToken } = {} } = {}) => {
const error =
httpIntegrationResetToken?.errors[0] || prometheusIntegrationResetToken?.errors[0];
if (error) {
return createFlash({ message: error });
}
return createFlash({
message: this.$options.i18n.changesSaved,
type: FLASH_TYPES.SUCCESS,
});
},
)
.catch(err => {
createFlash({ message: err });
})
.finally(() => {
this.isUpdating = false;
});
},
editIntegration({ id }) {
this.currentIntegration = this.integrations.list.find(integration => integration.id === id);
},
deleteIntegration() {
// TODO, handle delete via GraphQL
},
}, },
}; };
</script> </script>
...@@ -160,11 +229,16 @@ export default { ...@@ -160,11 +229,16 @@ export default {
<integrations-list <integrations-list
:integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld" :integrations="glFeatures.httpIntegrationsList ? integrations.list : intergrationsOptionsOld"
:loading="loading" :loading="loading"
@edit-integration="editIntegration"
@delete-integration="deleteIntegration"
/> />
<settings-form-new <settings-form-new
v-if="glFeatures.httpIntegrationsList" v-if="glFeatures.httpIntegrationsList"
:loading="loading" :loading="isUpdating"
@on-create-new-integration="onCreateNewIntegration" :current-integration="currentIntegration"
@create-new-integration="createNewIntegration"
@update-integration="updateIntegration"
@reset-token="resetToken"
/> />
<settings-form-old v-else /> <settings-form-old v-else />
</div> </div>
......
...@@ -57,6 +57,15 @@ export const typeSet = { ...@@ -57,6 +57,15 @@ export const typeSet = {
prometheus: 'PROMETHEUS', prometheus: 'PROMETHEUS',
}; };
export const defaultFormState = {
name: '',
active: false,
token: '',
url: '',
apiUrl: '',
integrationTestPayload: { json: null, error: null },
};
export const JSON_VALIDATE_DELAY = 250; export const JSON_VALIDATE_DELAY = 250;
export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/'; export const targetPrometheusUrlPlaceholder = 'http://prometheus.example.com/';
......
#import "../fragments/integration_item.fragment.graphql"
mutation resetHttpIntegrationToken($id: ID!) {
httpIntegrationResetToken(input: { id: $id }) {
errors
integration {
...IntegrationItem
}
}
}
#import "../fragments/integration_item.fragment.graphql"
mutation resetPrometheusIntegrationToken($id: ID!) {
prometheusIntegrationResetToken(input: { id: $id }) {
errors
integration {
...IntegrationItem
}
}
}
#import "../fragments/integration_item.fragment.graphql"
mutation updateHttpIntegration($id: ID!, $name: String!, $active: Boolean!) {
httpIntegrationUpdate(input: { id: $id, name: $name, active: $active }) {
errors
integration {
...IntegrationItem
}
}
}
#import "../fragments/integration_item.fragment.graphql"
mutation updatePrometheusIntegration($id: ID!, $apiUrl: String!, $active: Boolean!) {
prometheusIntegrationUpdate(input: { id: $id, apiUrl: $apiUrl, active: $active }) {
errors
integration {
...IntegrationItem
}
}
}
...@@ -50,7 +50,7 @@ export default el => { ...@@ -50,7 +50,7 @@ export default el => {
prometheus: { prometheus: {
active: parseBoolean(prometheusActivated), active: parseBoolean(prometheusActivated),
url: prometheusUrl, url: prometheusUrl,
authKey: prometheusAuthorizationKey, token: prometheusAuthorizationKey,
prometheusFormPath, prometheusFormPath,
prometheusResetKeyPath, prometheusResetKeyPath,
prometheusApiUrl, prometheusApiUrl,
...@@ -60,7 +60,7 @@ export default el => { ...@@ -60,7 +60,7 @@ export default el => {
alertsUsageUrl, alertsUsageUrl,
active: parseBoolean(activatedStr), active: parseBoolean(activatedStr),
formPath, formPath,
authKey: authorizationKey, token: authorizationKey,
url, url,
}, },
opsgenie: { opsgenie: {
......
...@@ -24,10 +24,10 @@ exports[`AlertsSettingsFormOld with default values renders the initial template ...@@ -24,10 +24,10 @@ exports[`AlertsSettingsFormOld with default values renders the initial template
</span> </span>
</gl-form-group-stub> </gl-form-group-stub>
<gl-form-group-stub label=\\"Authorization key\\" label-for=\\"authorization-key\\"> <gl-form-group-stub label-for=\\"authorization-key\\">
<gl-form-input-group-stub value=\\"\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub> <gl-form-input-group-stub value=\\"\\" predefinedoptions=\\"[object Object]\\" id=\\"authorization-key\\" readonly=\\"\\" class=\\"gl-mb-2\\"></gl-form-input-group-stub>
<gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub> <gl-button-stub category=\\"primary\\" variant=\\"default\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" disabled=\\"true\\" class=\\"gl-mt-3\\" role=\\"button\\" tabindex=\\"0\\">Reset key</gl-button-stub>
<gl-modal-stub modalid=\\"authKeyModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\"> <gl-modal-stub modalid=\\"tokenModal\\" titletag=\\"h4\\" modalclass=\\"\\" size=\\"md\\" title=\\"Reset key\\" ok-title=\\"Reset key\\" ok-variant=\\"danger\\">
Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in. Resetting the authorization key for this project will require updating the authorization key in every alert source it is enabled in.
</gl-modal-stub> </gl-modal-stub>
</gl-form-group-stub> </gl-form-group-stub>
......
...@@ -70,16 +70,70 @@ describe('AlertsSettingsFormNew', () => { ...@@ -70,16 +70,70 @@ describe('AlertsSettingsFormNew', () => {
}); });
}); });
describe('when form is invalid', () => { describe('submitting integration form', () => {
// TODO, implement specs for when form is invalid it('allows for create-new-integration with the correct form values for HTTP', async () => {
}); createComponent({});
describe('when form is valid', () => { const options = findSelect().findAll('option');
beforeEach(() => { await options.at(1).setSelected();
await findFormFields()
.at(0)
.setValue('Test integration');
await findFormToggle().trigger('click');
await wrapper.vm.$nextTick();
expect(findSubmitButton().exists()).toBe(true);
expect(findSubmitButton().text()).toBe('Save integration');
findForm().trigger('submit');
await wrapper.vm.$nextTick();
expect(wrapper.emitted('create-new-integration')).toBeTruthy();
expect(wrapper.emitted('create-new-integration')[0]).toEqual([
{ type: typeSet.http, variables: { name: 'Test integration', active: true } },
]);
});
it('allows for create-new-integration with the correct form values for PROMETHEUS', async () => {
createComponent({}); createComponent({});
const options = findSelect().findAll('option');
await options.at(2).setSelected();
await findFormFields()
.at(0)
.setValue('Test integration');
await findFormFields()
.at(1)
.setValue('https://test.com');
await findFormToggle().trigger('click');
await wrapper.vm.$nextTick();
expect(findSubmitButton().exists()).toBe(true);
expect(findSubmitButton().text()).toBe('Save integration');
findForm().trigger('submit');
await wrapper.vm.$nextTick();
expect(wrapper.emitted('create-new-integration')).toBeTruthy();
expect(wrapper.emitted('create-new-integration')[0]).toEqual([
{ type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } },
]);
}); });
it('allows for on-create-new-integration with the correct form values for HTTP', async () => { it('allows for update-integration with the correct form values for HTTP', async () => {
createComponent({
props: {
currentIntegration: { id: '1' },
loading: false,
},
});
const options = findSelect().findAll('option'); const options = findSelect().findAll('option');
await options.at(1).setSelected(); await options.at(1).setSelected();
...@@ -97,13 +151,20 @@ describe('AlertsSettingsFormNew', () => { ...@@ -97,13 +151,20 @@ describe('AlertsSettingsFormNew', () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.emitted('on-create-new-integration')).toBeTruthy(); expect(wrapper.emitted('update-integration')).toBeTruthy();
expect(wrapper.emitted('on-create-new-integration')[0]).toEqual([ expect(wrapper.emitted('update-integration')[0]).toEqual([
{ type: typeSet.http, variables: { name: 'Test integration', active: true } }, { type: typeSet.http, variables: { name: 'Test integration', active: true } },
]); ]);
}); });
it('allows for on-create-new-integration with the correct form values for PROMETHEUS', async () => { it('allows for update-integration with the correct form values for PROMETHEUS', async () => {
createComponent({
props: {
currentIntegration: { id: '1' },
loading: false,
},
});
const options = findSelect().findAll('option'); const options = findSelect().findAll('option');
await options.at(2).setSelected(); await options.at(2).setSelected();
...@@ -124,8 +185,8 @@ describe('AlertsSettingsFormNew', () => { ...@@ -124,8 +185,8 @@ describe('AlertsSettingsFormNew', () => {
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.emitted('on-create-new-integration')).toBeTruthy(); expect(wrapper.emitted('update-integration')).toBeTruthy();
expect(wrapper.emitted('on-create-new-integration')[0]).toEqual([ expect(wrapper.emitted('update-integration')[0]).toEqual([
{ type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } }, { type: typeSet.prometheus, variables: { apiUrl: 'https://test.com', active: true } },
]); ]);
}); });
......
...@@ -69,7 +69,7 @@ describe('AlertsSettingsFormOld', () => { ...@@ -69,7 +69,7 @@ describe('AlertsSettingsFormOld', () => {
createComponent( createComponent(
{}, {},
{ {
authKey: 'newToken', token: 'newToken',
}, },
); );
......
...@@ -6,14 +6,24 @@ import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_ ...@@ -6,14 +6,24 @@ import AlertsSettingsFormNew from '~/alerts_settings/components/alerts_settings_
import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue'; import IntegrationsList from '~/alerts_settings/components/alerts_integrations_list.vue';
import createHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql'; import createHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/create_http_integration.mutation.graphql';
import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/create_prometheus_integration.mutation.graphql'; import createPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/create_prometheus_integration.mutation.graphql';
import updateHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_http_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
import resetHttpTokenMutation from '~/alerts_settings/graphql/mutations/reset_http_token.mutation.graphql';
import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/reset_prometheus_token.mutation.graphql';
import { typeSet } from '~/alerts_settings/constants';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { defaultAlertSettingsConfig } from './util'; import { defaultAlertSettingsConfig } from './util';
import mockIntegrations from './mocks/integrations.json'; import mockIntegrations from './mocks/integrations.json';
import {
createHttpVariables,
updateHttpVariables,
createPrometheusVariables,
updatePrometheusVariables,
ID,
} from './mocks/apollo_mock';
jest.mock('~/flash'); jest.mock('~/flash');
const projectPath = '';
describe('AlertsSettingsWrapper', () => { describe('AlertsSettingsWrapper', () => {
let wrapper; let wrapper;
...@@ -80,7 +90,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -80,7 +90,7 @@ describe('AlertsSettingsWrapper', () => {
it('renders the IntegrationsList table using the API data', () => { it('renders the IntegrationsList table using the API data', () => {
createComponent({ createComponent({
data: { integrations: { list: mockIntegrations } }, data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } }, provide: { glFeatures: { httpIntegrationsList: true } },
loading: false, loading: false,
}); });
...@@ -100,7 +110,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -100,7 +110,7 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `createHttpIntegrationMutation`', () => { it('calls `$apollo.mutate` with `createHttpIntegrationMutation`', () => {
createComponent({ createComponent({
data: { integrations: { list: mockIntegrations } }, data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } }, provide: { glFeatures: { httpIntegrationsList: true } },
loading: false, loading: false,
}); });
...@@ -108,26 +118,66 @@ describe('AlertsSettingsWrapper', () => { ...@@ -108,26 +118,66 @@ describe('AlertsSettingsWrapper', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { createHttpIntegrationMutation: { integration: { id: '1' } } }, data: { createHttpIntegrationMutation: { integration: { id: '1' } } },
}); });
wrapper.find(AlertsSettingsFormNew).vm.$emit('on-create-new-integration', { wrapper.find(AlertsSettingsFormNew).vm.$emit('create-new-integration', {
type: 'HTTP', type: typeSet.http,
variables: { name: 'Test 1', active: true }, variables: createHttpVariables,
}); });
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: createHttpIntegrationMutation, mutation: createHttpIntegrationMutation,
update: expect.anything(), update: expect.anything(),
variables: createHttpVariables,
});
});
it('calls `$apollo.mutate` with `updateHttpIntegrationMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { updateHttpIntegrationMutation: { integration: { id: '1' } } },
});
wrapper.find(AlertsSettingsFormNew).vm.$emit('update-integration', {
type: typeSet.http,
variables: updateHttpVariables,
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateHttpIntegrationMutation,
variables: updateHttpVariables,
});
});
it('calls `$apollo.mutate` with `resetHttpTokenMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { resetHttpTokenMutation: { integration: { id: '1' } } },
});
wrapper.find(AlertsSettingsFormNew).vm.$emit('reset-token', {
type: typeSet.http,
variables: { id: ID },
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: resetHttpTokenMutation,
variables: { variables: {
name: 'Test 1', id: ID,
active: true,
projectPath,
}, },
}); });
}); });
it('calls `$apollo.mutate` with `createPrometheusIntegrationMutation`', () => { it('calls `$apollo.mutate` with `createPrometheusIntegrationMutation`', () => {
createComponent({ createComponent({
data: { integrations: { list: mockIntegrations } }, data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } }, provide: { glFeatures: { httpIntegrationsList: true } },
loading: false, loading: false,
}); });
...@@ -135,33 +185,107 @@ describe('AlertsSettingsWrapper', () => { ...@@ -135,33 +185,107 @@ describe('AlertsSettingsWrapper', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({ jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { createPrometheusIntegrationMutation: { integration: { id: '2' } } }, data: { createPrometheusIntegrationMutation: { integration: { id: '2' } } },
}); });
wrapper.find(AlertsSettingsFormNew).vm.$emit('on-create-new-integration', { wrapper.find(AlertsSettingsFormNew).vm.$emit('create-new-integration', {
type: 'PROMETHEUS', type: typeSet.prometheus,
variables: { apiUrl: 'https://test.com', active: true }, variables: createPrometheusVariables,
}); });
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1); expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: createPrometheusIntegrationMutation, mutation: createPrometheusIntegrationMutation,
update: expect.anything(), update: expect.anything(),
variables: createPrometheusVariables,
});
});
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { updatePrometheusIntegrationMutation: { integration: { id: '2' } } },
});
wrapper.find(AlertsSettingsFormNew).vm.$emit('update-integration', {
type: typeSet.prometheus,
variables: updatePrometheusVariables,
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updatePrometheusIntegrationMutation,
variables: updatePrometheusVariables,
});
});
it('calls `$apollo.mutate` with `resetPrometheusTokenMutation`', () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue({
data: { resetPrometheusTokenMutation: { integration: { id: '1' } } },
});
wrapper.find(AlertsSettingsFormNew).vm.$emit('reset-token', {
type: typeSet.prometheus,
variables: { id: ID },
});
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: resetPrometheusTokenMutation,
variables: { variables: {
apiUrl: 'https://test.com', id: ID,
active: true,
projectPath,
}, },
}); });
}); });
it('shows error alert when integration creation fails ', () => { it('shows error alert when integration creation fails ', async () => {
const errorMsg = 'Something went wrong'; const errorMsg = 'Something went wrong';
createComponent({ createComponent({
data: { integrations: { list: mockIntegrations } }, data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
wrapper.find(AlertsSettingsFormNew).vm.$emit('create-new-integration', {});
setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith({ message: errorMsg });
});
});
it('shows error alert when integration token reset fails ', () => {
const errorMsg = 'Something went wrong';
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } }, provide: { glFeatures: { httpIntegrationsList: true } },
loading: false, loading: false,
}); });
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg); jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
wrapper.find(AlertsSettingsFormNew).vm.$emit('on-create-new-integration', {});
wrapper.find(AlertsSettingsFormNew).vm.$emit('reset-token', {});
setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith({ message: errorMsg });
});
});
it('shows error alert when integration update fails ', () => {
const errorMsg = 'Something went wrong';
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockRejectedValue(errorMsg);
wrapper.find(AlertsSettingsFormNew).vm.$emit('update-integration', {});
setImmediate(() => { setImmediate(() => {
expect(createFlash).toHaveBeenCalledWith({ message: errorMsg }); expect(createFlash).toHaveBeenCalledWith({ message: errorMsg });
......
const projectPath = '';
export const ID = 'gid://gitlab/AlertManagement::HttpIntegration/7';
export const createHttpVariables = {
name: 'Test Pre',
active: true,
projectPath,
};
export const updateHttpVariables = {
name: 'Test Pre',
active: true,
id: ID,
};
export const createPrometheusVariables = {
apiUrl: 'https://test-pre.com',
active: true,
projectPath,
};
export const updatePrometheusVariables = {
apiUrl: 'https://test-pre.com',
active: true,
id: ID,
};
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