Commit cb9173ef authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch '321674-filter-integrations-by-id' into 'master'

Request HTTP integration by id

See merge request gitlab-org/gitlab!58140
parents 9dfc7611 505ba380
......@@ -116,7 +116,7 @@ export default {
methods: {
tbodyTrClass(item) {
return {
[bodyTrClass]: this.integrations.length,
[bodyTrClass]: this.integrations?.length,
'gl-bg-blue-50': (item !== null && item.id) === this.currentIntegration?.id,
};
},
......
......@@ -14,13 +14,12 @@ import updateCurrentHttpIntegrationMutation from '../graphql/mutations/update_cu
import updateCurrentPrometheusIntegrationMutation from '../graphql/mutations/update_current_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 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 service from '../services';
import {
updateStoreAfterIntegrationDelete,
updateStoreAfterIntegrationAdd,
updateStoreAfterHttpIntegrationAdd,
} from '../utils/cache_updates';
import {
DELETE_INTEGRATION_ERROR,
......@@ -68,33 +67,8 @@ export default {
};
},
update(data) {
const { alertManagementIntegrations: { nodes: list = [] } = {} } = data.project || {};
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,
};
const { alertManagementIntegrations: { nodes = [] } = {} } = data.project || {};
return nodes;
},
error(err) {
createFlash({ message: err });
......@@ -107,9 +81,9 @@ export default {
data() {
return {
isUpdating: false,
integrations: {},
httpIntegrations: {},
integrations: [],
currentIntegration: null,
currentHttpIntegration: null,
newIntegration: null,
formVisible: false,
showSuccessfulCreateAlert: false,
......@@ -121,7 +95,7 @@ export default {
return this.$apollo.queries.integrations.loading;
},
canAddIntegration() {
return this.multiIntegrations || this.integrations?.list?.length < 2;
return this.multiIntegrations || this.integrations.length < 2;
},
},
methods: {
......@@ -142,11 +116,6 @@ export default {
},
update(store, { data }) {
updateStoreAfterIntegrationAdd(store, getIntegrationsQuery, data, { projectPath });
if (isHttp) {
updateStoreAfterHttpIntegrationAdd(store, getHttpIntegrationsQuery, data, {
projectPath,
});
}
},
})
.then(({ data: { httpIntegrationCreate, prometheusIntegrationCreate } = {} } = {}) => {
......@@ -253,15 +222,38 @@ export default {
});
},
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 };
}
const currentIntegration = this.integrations.find((integration) => integration.id === id);
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) {
this.$apollo
......@@ -368,7 +360,7 @@ export default {
</gl-alert>
<integrations-list
:integrations="integrations.list"
:integrations="integrations"
:loading="loading"
@edit-integration="editIntegration"
@delete-integration="deleteIntegration"
......
......@@ -6,7 +6,6 @@ mutation updateCurrentPrometheusIntegration(
$type: String
$url: String
$apiUrl: String
$samplePayload: String
) {
updateCurrentIntegration(
id: $id
......@@ -16,6 +15,5 @@ mutation updateCurrentPrometheusIntegration(
type: $type
url: $url
apiUrl: $apiUrl
samplePayload: $samplePayload
) @client
}
#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 getHttpIntegrations($projectPath: ID!) {
query getHttpIntegration($projectPath: ID!, $id: ID) {
project(fullPath: $projectPath) {
alertManagementHttpIntegrations {
alertManagementHttpIntegrations(id: $id) {
nodes {
...HttpIntegrationPayloadData
}
......
......@@ -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) => {
createFlash({ message });
throw new Error(data.errors);
......@@ -105,11 +80,3 @@ 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);
}
};
......@@ -8,7 +8,6 @@ import AlertsSettingsForm from '~/alerts_settings/components/alerts_settings_for
import { typeSet } from '~/alerts_settings/constants';
import alertFields from '../mocks/alert_fields.json';
import parsedMapping from '../mocks/parsed_mapping.json';
import { defaultAlertSettingsConfig } from './util';
const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
......@@ -29,7 +28,6 @@ describe('AlertsSettingsForm', () => {
...props,
},
provide: {
...defaultAlertSettingsConfig,
multiIntegrations,
},
mocks: {
......@@ -50,7 +48,6 @@ describe('AlertsSettingsForm', () => {
const findFormToggle = () => wrapper.findComponent(GlToggle);
const findSamplePayloadSection = () => wrapper.findByTestId('sample-payload-section');
const findMappingBuilder = () => wrapper.findComponent(MappingBuilder);
const findSubmitButton = () => wrapper.findByTestId('integration-form-submit');
const findMultiSupportText = () => wrapper.findByTestId('multi-integrations-not-supported');
const findJsonTestSubmit = () => wrapper.findByTestId('send-test-alert');
......
......@@ -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 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 getHttpIntegrationQuery from '~/alerts_settings/graphql/queries/get_http_integration.query.graphql';
import getIntegrationsQuery from '~/alerts_settings/graphql/queries/get_integrations.query.graphql';
import alertsUpdateService from '~/alerts_settings/services';
import {
......@@ -47,7 +48,6 @@ import {
destroyIntegrationResponseWithErrors,
} from './mocks/apollo_mock';
import mockIntegrations from './mocks/integrations.json';
import { defaultAlertSettingsConfig } from './util';
jest.mock('~/flash');
......@@ -58,27 +58,12 @@ describe('AlertsSettingsWrapper', () => {
let fakeApollo;
let destroyIntegrationHandler;
useMockIntersectionObserver();
const httpMappingData = {
payloadExample: '{"test: : "field"}',
payloadAttributeMappings: [],
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 findIntegrationsList = () => wrapper.findComponent(IntegrationsList);
......@@ -109,13 +94,14 @@ describe('AlertsSettingsWrapper', () => {
return { ...data };
},
provide: {
...defaultAlertSettingsConfig,
...provide,
},
mocks: {
$apollo: {
mutate: jest.fn(),
query: jest.fn(),
addSmartQuery: jest.fn((_, options) => {
options.result.call(wrapper.vm);
}),
queries: {
integrations: {
loading,
......@@ -143,9 +129,6 @@ describe('AlertsSettingsWrapper', () => {
wrapper = mount(AlertsSettingsWrapper, {
localVue,
apolloProvider: fakeApollo,
provide: {
...defaultAlertSettingsConfig,
},
});
}
......@@ -158,17 +141,29 @@ describe('AlertsSettingsWrapper', () => {
beforeEach(() => {
createComponent({
data: {
integrations: { list: mockIntegrations },
httpIntegrations: { list: [] },
integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
},
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(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);
});
......@@ -177,6 +172,16 @@ describe('AlertsSettingsWrapper', () => {
});
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();
expect(addNewIntegrationBtn.exists()).toBe(true);
await addNewIntegrationBtn.trigger('click');
......@@ -186,7 +191,7 @@ describe('AlertsSettingsWrapper', () => {
it('shows loading indicator inside the IntegrationsList table', () => {
createComponent({
data: { integrations: {} },
data: { integrations: [] },
loading: true,
});
expect(wrapper.find(IntegrationsList).exists()).toBe(true);
......@@ -198,7 +203,7 @@ describe('AlertsSettingsWrapper', () => {
beforeEach(() => {
createComponent({
data: {
integrations: { list: mockIntegrations },
integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
formVisible: true,
},
......@@ -283,7 +288,7 @@ describe('AlertsSettingsWrapper', () => {
it('calls `$apollo.mutate` with `updatePrometheusIntegrationMutation`', () => {
createComponent({
data: {
integrations: { list: mockIntegrations },
integrations: mockIntegrations,
currentIntegration: mockIntegrations[3],
formVisible: true,
},
......@@ -374,39 +379,61 @@ describe('AlertsSettingsWrapper', () => {
});
});
it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation` on HTTP integration edit', () => {
createComponent({
data: {
integrations: { list: mockIntegrations },
currentIntegration: mockIntegrations[0],
httpIntegrations,
},
loading: false,
});
describe('Edit integration', () => {
describe('HTTP', () => {
beforeEach(() => {
createComponent({
data: {
integrations: mockIntegrations,
currentIntegration: mockIntegrations[0],
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({});
findIntegrationsList().vm.$emit('edit-integration', updateHttpVariables);
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateCurrentHttpIntegrationMutation,
variables: { ...mockIntegrations[0], ...httpMappingData },
});
});
it('requests `currentHttpIntegration`', () => {
expect(wrapper.vm.$apollo.addSmartQuery).toHaveBeenCalledWith(
'currentHttpIntegration',
expect.objectContaining({
query: getHttpIntegrationQuery,
result: expect.any(Function),
update: expect.any(Function),
variables: expect.any(Function),
}),
);
});
it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation` on PROMETHEUS integration edit', () => {
createComponent({
data: {
integrations: { list: mockIntegrations },
currentIntegration: mockIntegrations[3],
httpIntegrations,
},
loading: false,
it('calls `$apollo.mutate` with `updateCurrentHttpIntegrationMutation`', () => {
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateCurrentHttpIntegrationMutation,
variables: { ...mockIntegrations[0], ...httpMappingData },
});
});
});
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue();
findIntegrationsList().vm.$emit('edit-integration', updatePrometheusVariables);
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: updateCurrentPrometheusIntegrationMutation,
variables: mockIntegrations[3],
describe('Prometheus', () => {
it('calls `$apollo.mutate` with `updateCurrentPrometheusIntegrationMutation`', () => {
createComponent({
data: {
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