Commit 505ba380 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska Committed by Nicolò Maria Mezzopera

Request HTTP integration by id

parent d6cca7e5
...@@ -116,7 +116,7 @@ export default { ...@@ -116,7 +116,7 @@ export default {
methods: { methods: {
tbodyTrClass(item) { tbodyTrClass(item) {
return { return {
[bodyTrClass]: this.integrations.length, [bodyTrClass]: this.integrations?.length,
'gl-bg-blue-50': (item !== null && item.id) === this.currentIntegration?.id, 'gl-bg-blue-50': (item !== null && item.id) === this.currentIntegration?.id,
}; };
}, },
......
...@@ -14,13 +14,12 @@ import updateCurrentHttpIntegrationMutation from '../graphql/mutations/update_cu ...@@ -14,13 +14,12 @@ import updateCurrentHttpIntegrationMutation from '../graphql/mutations/update_cu
import updateCurrentPrometheusIntegrationMutation from '../graphql/mutations/update_current_prometheus_integration.mutation.graphql'; import updateCurrentPrometheusIntegrationMutation from '../graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '../graphql/mutations/update_prometheus_integration.mutation.graphql'; import updatePrometheusIntegrationMutation from '../graphql/mutations/update_prometheus_integration.mutation.graphql';
import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql'; import getCurrentIntegrationQuery from '../graphql/queries/get_current_integration.query.graphql';
import getHttpIntegrationsQuery from '../graphql/queries/get_http_integrations.query.graphql'; import getHttpIntegrationQuery from '../graphql/queries/get_http_integration.query.graphql';
import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql'; import getIntegrationsQuery from '../graphql/queries/get_integrations.query.graphql';
import service from '../services'; import service from '../services';
import { import {
updateStoreAfterIntegrationDelete, updateStoreAfterIntegrationDelete,
updateStoreAfterIntegrationAdd, updateStoreAfterIntegrationAdd,
updateStoreAfterHttpIntegrationAdd,
} from '../utils/cache_updates'; } from '../utils/cache_updates';
import { import {
DELETE_INTEGRATION_ERROR, DELETE_INTEGRATION_ERROR,
...@@ -68,33 +67,8 @@ export default { ...@@ -68,33 +67,8 @@ export default {
}; };
}, },
update(data) { update(data) {
const { alertManagementIntegrations: { nodes: list = [] } = {} } = data.project || {}; const { alertManagementIntegrations: { nodes = [] } = {} } = data.project || {};
return nodes;
return {
list,
};
},
error(err) {
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) { error(err) {
createFlash({ message: err }); createFlash({ message: err });
...@@ -107,9 +81,9 @@ export default { ...@@ -107,9 +81,9 @@ export default {
data() { data() {
return { return {
isUpdating: false, isUpdating: false,
integrations: {}, integrations: [],
httpIntegrations: {},
currentIntegration: null, currentIntegration: null,
currentHttpIntegration: null,
newIntegration: null, newIntegration: null,
formVisible: false, formVisible: false,
showSuccessfulCreateAlert: false, showSuccessfulCreateAlert: false,
...@@ -121,7 +95,7 @@ export default { ...@@ -121,7 +95,7 @@ export default {
return this.$apollo.queries.integrations.loading; return this.$apollo.queries.integrations.loading;
}, },
canAddIntegration() { canAddIntegration() {
return this.multiIntegrations || this.integrations?.list?.length < 2; return this.multiIntegrations || this.integrations.length < 2;
}, },
}, },
methods: { methods: {
...@@ -142,11 +116,6 @@ export default { ...@@ -142,11 +116,6 @@ export default {
}, },
update(store, { data }) { update(store, { data }) {
updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath }); updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath });
if (isHttp) {
updateStoreAfterHttpIntegrationAdd(store, getHttpIntegrationsQuery, data, {
projectPath,
});
}
}, },
}) })
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => { .then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
...@@ -253,15 +222,38 @@ export default { ...@@ -253,15 +222,38 @@ export default {
}); });
}, },
editIntegration({ id, type }) { editIntegration({ id, type }) {
let currentIntegration = this.integrations.list.find((integration) => integration.id === id); const currentIntegration = this.integrations.find((integration) => integration.id === id);
if (this.isHttp(type)) {
const httpIntegrationMappingData = this.httpIntegrations.list.find(
(integration) => integration.id === id,
);
currentIntegration = { ...currentIntegration, ...httpIntegrationMappingData };
}
this.viewIntegration(currentIntegration, tabIndices.viewCredentials); if (this.multiIntegrations && this.isHttp(type)) {
this.$apollo.addSmartQuery('currentHttpIntegration', {
query: getHttpIntegrationQuery,
variables() {
return {
projectPath: this.projectPath,
id,
};
},
update(data) {
const {
project: {
alertManagementHttpIntegrations: { nodes = [{}] },
},
} = data;
return nodes[0];
},
result() {
this.viewIntegration(
{ ...currentIntegration, ...this.currentHttpIntegration },
tabIndices.viewCredentials,
);
},
error() {
createFlash({ message: DEFAULT_ERROR });
},
});
} else {
this.viewIntegration(currentIntegration, tabIndices.viewCredentials);
}
}, },
viewIntegration(integration, tabIndex) { viewIntegration(integration, tabIndex) {
this.$apollo this.$apollo
...@@ -368,7 +360,7 @@ export default { ...@@ -368,7 +360,7 @@ export default {
</gl-alert> </gl-alert>
<integrations-list <integrations-list
:integrations="integrations.list" :integrations="integrations"
:loading="loading" :loading="loading"
@edit-integration="editIntegration" @edit-integration="editIntegration"
@delete-integration="deleteIntegration" @delete-integration="deleteIntegration"
......
...@@ -6,7 +6,6 @@ mutation updateCurrentPrometheusIntegration( ...@@ -6,7 +6,6 @@ mutation updateCurrentPrometheusIntegration(
$type: String $type: String
$url: String $url: String
$apiUrl: String $apiUrl: String
$samplePayload: String
) { ) {
updateCurrentIntegration( updateCurrentIntegration(
id: $id id: $id
...@@ -16,6 +15,5 @@ mutation updateCurrentPrometheusIntegration( ...@@ -16,6 +15,5 @@ mutation updateCurrentPrometheusIntegration(
type: $type type: $type
url: $url url: $url
apiUrl: $apiUrl apiUrl: $apiUrl
samplePayload: $samplePayload
) @client ) @client
} }
#import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql" #import "ee_else_ce/alerts_settings/graphql/fragments/http_integration_payload_data.fragment.graphql"
# TODO: this query need to accept http integration id to request a sepcific integration query getHttpIntegration($projectPath: ID!, $id: ID) {
query getHttpIntegrations($projectPath: ID!) {
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
alertManagementHttpIntegrations { alertManagementHttpIntegrations(id: $id) {
nodes { nodes {
...HttpIntegrationPayloadData ...HttpIntegrationPayloadData
} }
......
...@@ -58,31 +58,6 @@ const addIntegrationToStore = ( ...@@ -58,31 +58,6 @@ 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) => {
draftData.project.alertManagementHttpIntegrations.nodes = [
integration,
...draftData.project.alertManagementHttpIntegrations.nodes,
];
});
store.writeQuery({
query,
variables,
data,
});
};
const onError = (data, message) => { const onError = (data, message) => {
createFlash({ message }); createFlash({ message });
throw new Error(data.errors); throw new Error(data.errors);
...@@ -105,11 +80,3 @@ export const updateStoreAfterIntegrationAdd = (store, query, data, variables) => ...@@ -105,11 +80,3 @@ export const updateStoreAfterIntegrationAdd = (store, query, data, variables) =>
addIntegrationToStore(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);
}
};
...@@ -8,7 +8,6 @@ import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_for ...@@ -8,7 +8,6 @@ import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_for
import { typeSet } from '~/alerts_settings/constants'; import { typeSet } from '~/alerts_settings/constants';
import alertFields from '../mocks/alert_fields.json'; import alertFields from '../mocks/alert_fields.json';
import parsedMapping from '../mocks/parsed_mapping.json'; import parsedMapping from '../mocks/parsed_mapping.json';
import { defaultAlertSettingsConfig } from './util';
const scrollIntoViewMock = jest.fn(); const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
...@@ -29,7 +28,6 @@ describe('AlertsSettingsForm', () => { ...@@ -29,7 +28,6 @@ describe('AlertsSettingsForm', () => {
...props, ...props,
}, },
provide: { provide: {
...defaultAlertSettingsConfig,
multiIntegrations, multiIntegrations,
}, },
mocks: { mocks: {
...@@ -50,7 +48,6 @@ describe('AlertsSettingsForm', () => { ...@@ -50,7 +48,6 @@ describe('AlertsSettingsForm', () => {
const findFormToggle = () => wrapper.findComponent(GlToggle); const findFormToggle = () => wrapper.findComponent(GlToggle);
const findSamplePayloadSection = () => wrapper.findByTestId('sample-payload-section'); const findSamplePayloadSection = () => wrapper.findByTestId('sample-payload-section');
const findMappingBuilder = () => wrapper.findComponent(MappingBuilder); const findMappingBuilder = () => wrapper.findComponent(MappingBuilder);
const findSubmitButton = () => wrapper.findByTestId('integration-form-submit'); const findSubmitButton = () => wrapper.findByTestId('integration-form-submit');
const findMultiSupportText = () => wrapper.findByTestId('multi-integrations-not-supported'); const findMultiSupportText = () => wrapper.findByTestId('multi-integrations-not-supported');
const findJsonTestSubmit = () => wrapper.findByTestId('send-test-alert'); const findJsonTestSubmit = () => wrapper.findByTestId('send-test-alert');
......
...@@ -20,6 +20,7 @@ import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/re ...@@ -20,6 +20,7 @@ import resetPrometheusTokenMutation from '~/alerts_settings/graphql/mutations/re
import updateCurrentHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql'; import updateCurrentHttpIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_http_integration.mutation.graphql';
import updateCurrentPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql'; import updateCurrentPrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_current_prometheus_integration.mutation.graphql';
import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql'; import updatePrometheusIntegrationMutation from '~/alerts_settings/graphql/mutations/update_prometheus_integration.mutation.graphql';
import getHttpIntegrationQuery from '~/alerts_settings/graphql/queries/get_http_integration.query.graphql';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql'; import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
import alertsUpdateService from '~/alerts_settings/services'; import alertsUpdateService from '~/alerts_settings/services';
import { import {
...@@ -47,7 +48,6 @@ import { ...@@ -47,7 +48,6 @@ import {
destroyIntegrationResponseWithErrors, destroyIntegrationResponseWithErrors,
} from './mocks/apollo_mock'; } from './mocks/apollo_mock';
import mockIntegrations from './mocks/integrations.json'; import mockIntegrations from './mocks/integrations.json';
import { defaultAlertSettingsConfig } from './util';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -58,27 +58,12 @@ describe('AlertsSettingsWrapper', () => { ...@@ -58,27 +58,12 @@ describe('AlertsSettingsWrapper', () => {
let fakeApollo; let fakeApollo;
let destroyIntegrationHandler; let destroyIntegrationHandler;
useMockIntersectionObserver(); useMockIntersectionObserver();
const httpMappingData = { const httpMappingData = {
payloadExample: '{"test: : "field"}', payloadExample: '{"test: : "field"}',
payloadAttributeMappings: [], payloadAttributeMappings: [],
payloadAlertFields: [], payloadAlertFields: [],
}; };
const httpIntegrations = {
list: [
{
id: mockIntegrations[0].id,
...httpMappingData,
},
{
id: mockIntegrations[1].id,
...httpMappingData,
},
{
id: mockIntegrations[2].id,
httpMappingData,
},
],
};
const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon); const findLoader = () => wrapper.findComponent(IntegrationsList).findComponent(GlLoadingIcon);
const findIntegrationsList = () => wrapper.findComponent(IntegrationsList); const findIntegrationsList = () => wrapper.findComponent(IntegrationsList);
...@@ -109,13 +94,14 @@ describe('AlertsSettingsWrapper', () => { ...@@ -109,13 +94,14 @@ describe('AlertsSettingsWrapper', () => {
return { ...data }; return { ...data };
}, },
provide: { provide: {
...defaultAlertSettingsConfig,
...provide, ...provide,
}, },
mocks: { mocks: {
$apollo: { $apollo: {
mutate: jest.fn(), mutate: jest.fn(),
query: jest.fn(), addSmartQuery: jest.fn((_, options) => {
options.result.call(wrapper.vm);
}),
queries: { queries: {
integrations: { integrations: {
loading, loading,
...@@ -143,9 +129,6 @@ describe('AlertsSettingsWrapper', () => { ...@@ -143,9 +129,6 @@ describe('AlertsSettingsWrapper', () => {
wrapper = mount(AlertsSettingsWrapper, { wrapper = mount(AlertsSettingsWrapper, {
localVue, localVue,
apolloProvider: fakeApollo, apolloProvider: fakeApollo,
provide: {
...defaultAlertSettingsConfig,
},
}); });
} }
...@@ -158,17 +141,29 @@ describe('AlertsSettingsWrapper', () => { ...@@ -158,17 +141,29 @@ describe('AlertsSettingsWrapper', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
data: { data: {
integrations: { list: mockIntegrations }, integrations: mockIntegrations,
httpIntegrations: { list: [] },
currentIntegration: mockIntegrations[0], currentIntegration: mockIntegrations[0],
}, },
loading: false, loading: false,
}); });
}); });
it('renders alerts integrations list and add new integration button by default', () => { it('renders alerts integrations list', () => {
expect(findLoader().exists()).toBe(false); expect(findLoader().exists()).toBe(false);
expect(findIntegrations()).toHaveLength(mockIntegrations.length); expect(findIntegrations()).toHaveLength(mockIntegrations.length);
});
it('renders `Add new integration` button when multiple integrations are supported ', () => {
createComponent({
data: {
integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
},
provide: {
multiIntegrations: true,
},
loading: false,
});
expect(findAddIntegrationBtn().exists()).toBe(true); expect(findAddIntegrationBtn().exists()).toBe(true);
}); });
...@@ -177,6 +172,16 @@ describe('AlertsSettingsWrapper', () => { ...@@ -177,6 +172,16 @@ describe('AlertsSettingsWrapper', () => {
}); });
it('hides `add new integration` button and displays setting form on btn click', async () => { it('hides `add new integration` button and displays setting form on btn click', async () => {
createComponent({
data: {
integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
},
provide: {
multiIntegrations: true,
},
loading: false,
});
const addNewIntegrationBtn = findAddIntegrationBtn(); const addNewIntegrationBtn = findAddIntegrationBtn();
expect(addNewIntegrationBtn.exists()).toBe(true); expect(addNewIntegrationBtn.exists()).toBe(true);
await addNewIntegrationBtn.trigger('click'); await addNewIntegrationBtn.trigger('click');
...@@ -186,7 +191,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -186,7 +191,7 @@ describe('AlertsSettingsWrapper', () => {
it('shows loading indicator inside the IntegrationsList table', () => { it('shows loading indicator inside the IntegrationsList table', () => {
createComponent({ createComponent({
data: { integrations: {} }, data: { integrations: [] },
loading: true, loading: true,
}); });
expect(wrapper.find(IntegrationsList).exists()).toBe(true); expect(wrapper.find(IntegrationsList).exists()).toBe(true);
...@@ -198,7 +203,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -198,7 +203,7 @@ describe('AlertsSettingsWrapper', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
data: { data: {
integrations: { list: mockIntegrations }, integrations: mockIntegrations,
currentIntegration: mockIntegrations[0], currentIntegration: mockIntegrations[0],
formVisible: true, formVisible: true,
}, },
...@@ -283,7 +288,7 @@ describe('AlertsSettingsWrapper', () => { ...@@ -283,7 +288,7 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => { it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({ createComponent({
data: { data: {
integrations: { list: mockIntegrations }, integrations: mockIntegrations,
currentIntegration: mockIntegrations[3], currentIntegration: mockIntegrations[3],
formVisible: true, formVisible: true,
}, },
...@@ -374,39 +379,61 @@ describe('AlertsSettingsWrapper', () => { ...@@ -374,39 +379,61 @@ describe('AlertsSettingsWrapper', () => {
}); });
}); });
it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation` on HTTP integration edit', () => { describe('Edit integration', () => {
createComponent({ describe('HTTP', () => {
data: { beforeEach(() => {
integrations: { list: mockIntegrations }, createComponent({
currentIntegration: mockIntegrations[0], data: {
httpIntegrations, integrations: mockIntegrations,
}, currentIntegration: mockIntegrations[0],
loading: false, currentHttpIntegration: { id: mockIntegrations[0].id, ...httpMappingData },
}); },
provide: {
multiIntegrations: true,
},
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValueOnce({});
findIntegrationsList().vm.$emit('edit-integration', updateHttpVariables);
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValueOnce({}); it('requests `currentHttpIntegration`', () => {
findIntegrationsList().vm.$emit('edit-integration', updateHttpVariables); expect(wrapper.vm.$apollo.addSmartQuery).toHaveBeenCalledWith(
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ 'currentHttpIntegration',
mutation: updateCurrentHttpIntegrationMutation, expect.objectContaining({
variables: { ...mockIntegrations[0], ...httpMappingData }, query: getHttpIntegrationQuery,
}); result: expect.any(Function),
}); update: expect.any(Function),
variables: expect.any(Function),
}),
);
});
it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation` on PROMETHEUS integration edit', () => { it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation`', () => {
createComponent({ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
data: { mutation: updateCurrentHttpIntegrationMutation,
integrations: { list: mockIntegrations }, variables: { ...mockIntegrations[0], ...httpMappingData },
currentIntegration: mockIntegrations[3], });
httpIntegrations, });
},
loading: false,
}); });
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(); describe('Prometheus', () => {
findIntegrationsList().vm.$emit('edit-integration', updatePrometheusVariables); it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation`', () => {
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ createComponent({
mutation: updateCurrentPrometheusIntegrationMutation, data: {
variables: mockIntegrations[3], integrations: mockIntegrations,
currentIntegration: mockIntegrations[3],
},
loading: false,
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue();
findIntegrationsList().vm.$emit('edit-integration', updatePrometheusVariables);
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateCurrentPrometheusIntegrationMutation,
variables: mockIntegrations[3],
});
});
}); });
}); });
......
const PROMETHEUS_URL = '/prometheus/alerts/notify.json';
const GENERIC_URL = '/alerts/notify.json';
const KEY = 'abcedfg123';
const INVALID_URL = 'http://invalid';
const ACTIVE = false;
export const defaultAlertSettingsConfig = {
generic: {
authorizationKey: KEY,
formPath: INVALID_URL,
url: GENERIC_URL,
alertsSetupUrl: INVALID_URL,
alertsUsageUrl: INVALID_URL,
active: ACTIVE,
},
prometheus: {
authorizationKey: KEY,
prometheusFormPath: INVALID_URL,
url: PROMETHEUS_URL,
active: ACTIVE,
},
projectPath: '',
multiIntegrations: true,
};
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