Commit ff91161d authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '268373-test-payload-opsgenie-1' into 'master'

Support test payloads through the UI for Alert intergrations

See merge request gitlab-org/gitlab!47079
parents ea48c52f 156cf30b
...@@ -17,6 +17,7 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; ...@@ -17,6 +17,7 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import AlertSettingsFormHelpBlock from './alert_settings_form_help_block.vue'; import AlertSettingsFormHelpBlock from './alert_settings_form_help_block.vue';
import service from '../services';
import { import {
integrationTypesNew, integrationTypesNew,
JSON_VALIDATE_DELAY, JSON_VALIDATE_DELAY,
...@@ -89,7 +90,7 @@ export default { ...@@ -89,7 +90,7 @@ export default {
MappingBuilder, MappingBuilder,
}, },
directives: { directives: {
'gl-modal': GlModalDirective, GlModal: GlModalDirective,
}, },
inject: { inject: {
generic: { generic: {
...@@ -150,6 +151,13 @@ export default { ...@@ -150,6 +151,13 @@ export default {
apiUrl: this.currentIntegration?.apiUrl || '', apiUrl: this.currentIntegration?.apiUrl || '',
}; };
}, },
testAlertPayload() {
return {
data: this.integrationTestPayload.json,
endpoint: this.integrationForm.url,
token: this.integrationForm.token,
};
},
}, },
watch: { watch: {
currentIntegration(val) { currentIntegration(val) {
...@@ -170,8 +178,14 @@ export default { ...@@ -170,8 +178,14 @@ export default {
} }
}, },
submitWithTestPayload() { submitWithTestPayload() {
// TODO: Test payload before saving via GraphQL return service
this.submit(); .updateTestAlert(this.testAlertPayload)
.then(() => {
this.submit();
})
.catch(() => {
this.$emit('test-payload-failure');
});
}, },
submit() { submit() {
const { name, apiUrl } = this.integrationForm; const { name, apiUrl } = this.integrationForm;
...@@ -359,7 +373,6 @@ export default { ...@@ -359,7 +373,6 @@ export default {
</div> </div>
</gl-form-group> </gl-form-group>
<gl-form-group <gl-form-group
id="test-integration"
:label="$options.i18n.integrationFormSteps.step4.label" :label="$options.i18n.integrationFormSteps.step4.label"
label-for="test-integration" label-for="test-integration"
:invalid-feedback="integrationTestPayload.error" :invalid-feedback="integrationTestPayload.error"
...@@ -395,6 +408,8 @@ export default { ...@@ -395,6 +408,8 @@ export default {
<div class="gl-display-flex gl-justify-content-end"> <div class="gl-display-flex gl-justify-content-end">
<gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button> <gl-button type="reset" class="gl-mr-3 js-no-auto-disable">{{ __('Cancel') }}</gl-button>
<gl-button <gl-button
data-testid="integration-test-and-submit"
:disabled="Boolean(integrationTestPayload.error)"
category="secondary" category="secondary"
variant="success" variant="success"
class="gl-mr-1 js-no-auto-disable" class="gl-mr-1 js-no-auto-disable"
......
...@@ -24,6 +24,7 @@ import { ...@@ -24,6 +24,7 @@ import {
ADD_INTEGRATION_ERROR, ADD_INTEGRATION_ERROR,
RESET_INTEGRATION_TOKEN_ERROR, RESET_INTEGRATION_TOKEN_ERROR,
UPDATE_INTEGRATION_ERROR, UPDATE_INTEGRATION_ERROR,
INTEGRATION_PAYLOAD_TEST_ERROR,
} from '../utils/error_messages'; } from '../utils/error_messages';
export default { export default {
...@@ -244,6 +245,9 @@ export default { ...@@ -244,6 +245,9 @@ export default {
clearCurrentIntegration() { clearCurrentIntegration() {
this.currentIntegration = null; this.currentIntegration = null;
}, },
testPayloadFailure() {
createFlash({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
},
}, },
}; };
</script> </script>
...@@ -266,6 +270,7 @@ export default { ...@@ -266,6 +270,7 @@ export default {
@update-integration="updateIntegration" @update-integration="updateIntegration"
@reset-token="resetToken" @reset-token="resetToken"
@clear-current-integration="clearCurrentIntegration" @clear-current-integration="clearCurrentIntegration"
@test-payload-failure="testPayloadFailure"
/> />
<settings-form-old v-else /> <settings-form-old v-else />
</div> </div>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
export default { export default {
// TODO: All this code save updateTestAlert will be deleted as part of: https://gitlab.com/gitlab-org/gitlab/-/issues/255501
updateGenericKey({ endpoint, params }) { updateGenericKey({ endpoint, params }) {
return axios.put(endpoint, params); return axios.put(endpoint, params);
}, },
...@@ -25,11 +26,11 @@ export default { ...@@ -25,11 +26,11 @@ export default {
}, },
}); });
}, },
updateTestAlert({ endpoint, data, authKey }) { updateTestAlert({ endpoint, data, token }) {
return axios.post(endpoint, data, { return axios.post(endpoint, data, {
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
Authorization: `Bearer ${authKey}`, Authorization: `Bearer ${token}`,
}, },
}); });
}, },
......
...@@ -15,3 +15,7 @@ export const UPDATE_INTEGRATION_ERROR = s__( ...@@ -15,3 +15,7 @@ export const UPDATE_INTEGRATION_ERROR = s__(
export const RESET_INTEGRATION_TOKEN_ERROR = s__( export const RESET_INTEGRATION_TOKEN_ERROR = s__(
'AlertsIntegrations|The integration token could not be reset. Please try again.', 'AlertsIntegrations|The integration token could not be reset. Please try again.',
); );
export const INTEGRATION_PAYLOAD_TEST_ERROR = s__(
'AlertsIntegrations|Integration payload is invalid. You can still save your changes.',
);
...@@ -2668,6 +2668,9 @@ msgstr "" ...@@ -2668,6 +2668,9 @@ msgstr ""
msgid "AlertsIntegrations|Integration Name" msgid "AlertsIntegrations|Integration Name"
msgstr "" msgstr ""
msgid "AlertsIntegrations|Integration payload is invalid. You can still save your changes."
msgstr ""
msgid "AlertsIntegrations|No integrations have been added yet" msgid "AlertsIntegrations|No integrations have been added yet"
msgstr "" msgstr ""
......
...@@ -72,7 +72,7 @@ exports[`AlertsSettingsFormNew with default values renders the initial template ...@@ -72,7 +72,7 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
<!----> <!---->
</div> </div>
</div> </div>
<div id=\\"test-integration\\" role=\\"group\\" class=\\"form-group gl-form-group\\"><label id=\\"test-integration__BV_label_\\" for=\\"test-integration\\" class=\\"d-block col-form-label\\">4. Test integration(optional)</label> <div role=\\"group\\" class=\\"form-group gl-form-group\\" id=\\"__BVID__40\\"><label for=\\"test-integration\\" class=\\"d-block col-form-label\\" id=\\"__BVID__40__BV_label_\\">4. Test integration(optional)</label>
<div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"http://invalid\\" class=\\"gl-link gl-display-inline-block\\">alerts page</a>. This payload can be used to test the integration using the \\"save and test payload\\" button.</span> <textarea id=\\"test-integration\\" disabled=\\"disabled\\" placeholder=\\"Enter test alert JSON....\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-4 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea> <div class=\\"bv-no-focus-ring\\"><span class=\\"gl-text-gray-500\\">Provide an example payload from the monitoring tool you intend to integrate with to send a test alert to the <a rel=\\"noopener noreferrer\\" target=\\"_blank\\" href=\\"http://invalid\\" class=\\"gl-link gl-display-inline-block\\">alerts page</a>. This payload can be used to test the integration using the \\"save and test payload\\" button.</span> <textarea id=\\"test-integration\\" disabled=\\"disabled\\" placeholder=\\"Enter test alert JSON....\\" wrap=\\"soft\\" class=\\"gl-form-input gl-form-textarea gl-my-4 form-control is-valid\\" style=\\"resize: none; overflow-y: scroll;\\"></textarea>
<!----> <!---->
<!----> <!---->
...@@ -82,7 +82,7 @@ exports[`AlertsSettingsFormNew with default values renders the initial template ...@@ -82,7 +82,7 @@ exports[`AlertsSettingsFormNew with default values renders the initial template
<!----> <!---->
<div class=\\"gl-display-flex gl-justify-content-end\\"><button type=\\"reset\\" class=\\"btn gl-mr-3 js-no-auto-disable btn-default btn-md gl-button\\"> <div class=\\"gl-display-flex gl-justify-content-end\\"><button type=\\"reset\\" class=\\"btn gl-mr-3 js-no-auto-disable btn-default btn-md gl-button\\">
<!----> <!---->
<!----> <span class=\\"gl-button-text\\">Cancel</span></button> <button type=\\"button\\" class=\\"btn gl-mr-1 js-no-auto-disable btn-success btn-md gl-button btn-success-secondary\\"> <!----> <span class=\\"gl-button-text\\">Cancel</span></button> <button data-testid=\\"integration-test-and-submit\\" type=\\"button\\" class=\\"btn gl-mr-1 js-no-auto-disable btn-success btn-md gl-button btn-success-secondary\\">
<!----> <!---->
<!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\"> <!----> <span class=\\"gl-button-text\\">Save and test payload</span></button> <button data-testid=\\"integration-form-submit\\" type=\\"submit\\" class=\\"btn js-no-auto-disable btn-success btn-md gl-button\\">
<!----> <!---->
......
...@@ -37,6 +37,8 @@ describe('AlertsSettingsFormNew', () => { ...@@ -37,6 +37,8 @@ describe('AlertsSettingsFormNew', () => {
const findSubmitButton = () => wrapper.find(`[type = "submit"]`); const findSubmitButton = () => wrapper.find(`[type = "submit"]`);
const findMultiSupportText = () => const findMultiSupportText = () =>
wrapper.find(`[data-testid="multi-integrations-not-supported"]`); wrapper.find(`[data-testid="multi-integrations-not-supported"]`);
const findJsonTestSubmit = () => wrapper.find(`[data-testid="integration-test-and-submit"]`);
const findJsonTextArea = () => wrapper.find(`[id = "test-integration"]`);
afterEach(() => { afterEach(() => {
if (wrapper) { if (wrapper) {
...@@ -203,6 +205,42 @@ describe('AlertsSettingsFormNew', () => { ...@@ -203,6 +205,42 @@ describe('AlertsSettingsFormNew', () => {
}); });
}); });
describe('submitting the integration with a JSON test payload', () => {
beforeEach(() => {
createComponent({
data: {
selectedIntegration: typeSet.http,
active: true,
},
props: {
currentIntegration: { id: '1', name: 'Test' },
loading: false,
},
});
});
it('should not allow a user to test invalid JSON', async () => {
jest.useFakeTimers();
await findJsonTextArea().setValue('Invalid JSON');
jest.runAllTimers();
await wrapper.vm.$nextTick();
expect(findJsonTestSubmit().exists()).toBe(true);
expect(findJsonTestSubmit().text()).toBe('Save and test payload');
expect(findJsonTestSubmit().props('disabled')).toBe(true);
});
it('should allow for the form to be automatically saved if the test payload is successfully submitted', async () => {
jest.useFakeTimers();
await findJsonTextArea().setValue('{ "value": "value" }');
jest.runAllTimers();
await wrapper.vm.$nextTick();
expect(findJsonTestSubmit().props('disabled')).toBe(false);
});
});
describe('Mapping builder section', () => { describe('Mapping builder section', () => {
beforeEach(() => { beforeEach(() => {
createComponent({}); createComponent({});
......
...@@ -20,6 +20,7 @@ import { ...@@ -20,6 +20,7 @@ import {
ADD_INTEGRATION_ERROR, ADD_INTEGRATION_ERROR,
RESET_INTEGRATION_TOKEN_ERROR, RESET_INTEGRATION_TOKEN_ERROR,
UPDATE_INTEGRATION_ERROR, UPDATE_INTEGRATION_ERROR,
INTEGRATION_PAYLOAD_TEST_ERROR,
} from '~/alerts_settings/utils/error_messages'; } from '~/alerts_settings/utils/error_messages';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { defaultAlertSettingsConfig } from './util'; import { defaultAlertSettingsConfig } from './util';
...@@ -327,6 +328,20 @@ describe('AlertsSettingsWrapper', () => { ...@@ -327,6 +328,20 @@ describe('AlertsSettingsWrapper', () => {
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: UPDATE_INTEGRATION_ERROR }); expect(createFlash).toHaveBeenCalledWith({ message: UPDATE_INTEGRATION_ERROR });
}); });
it('shows an error alert when integration test payload fails ', async () => {
createComponent({
data: { integrations: { list: mockIntegrations }, currentIntegration: mockIntegrations[0] },
provide: { glFeatures: { httpIntegrationsList: true } },
loading: false,
});
wrapper.find(AlertsSettingsFormNew).vm.$emit('test-payload-failure');
await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ message: INTEGRATION_PAYLOAD_TEST_ERROR });
expect(createFlash).toHaveBeenCalledTimes(1);
});
}); });
describe('with mocked Apollo client', () => { describe('with mocked Apollo client', () => {
......
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