Drop updateQuery from DAST profiles queries

This removes deprecated updateQuery usages from DAST profiles queries.
It is being replaced by ApolloClient's concatPagination merge strategy.
Additionally, the optimistic profile removal is now done with the
.evicts method to prevent the merge strategy from being hit again, which
would result in duplicated items being displayed.
parent bdd5faad
...@@ -138,7 +138,6 @@ export default { ...@@ -138,7 +138,6 @@ export default {
$apollo.queries[profileType] $apollo.queries[profileType]
.fetchMore({ .fetchMore({
variables: { after: pageInfo.endCursor }, variables: { after: pageInfo.endCursor },
updateQuery: cacheUtils.appendToPreviousResult(profileType),
}) })
.catch((error) => { .catch((error) => {
this.handleError({ this.handleError({
...@@ -157,12 +156,8 @@ export default { ...@@ -157,12 +156,8 @@ export default {
graphQL: { deletion }, graphQL: { deletion },
}, },
}, },
$apollo: {
queries: {
[profileType]: { options: queryOptions },
},
},
} = this; } = this;
const profile = this.profileTypes[profileType].profiles.find(({ id }) => id === profileId);
this.resetErrors(profileType); this.resetErrors(profileType);
...@@ -179,13 +174,8 @@ export default { ...@@ -179,13 +174,8 @@ export default {
if (errors.length === 0) { if (errors.length === 0) {
cacheUtils.removeProfile({ cacheUtils.removeProfile({
profileId, profile,
profileType,
store, store,
queryBody: {
query: queryOptions.query,
variables: queryOptions.variables,
},
}); });
} else { } else {
handleError({ handleError({
......
import { gql } from '@apollo/client/core'; import { gql } from '@apollo/client/core';
import { produce } from 'immer';
import dastSiteProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql'; import dastSiteProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql';
/** /**
* Appends paginated results to existing ones * Evicts a profile from the cache
* - to be used with $apollo.queries.x.fetchMore
* *
* @param {*} profileType * @param profile
* @returns {function(*, {fetchMoreResult: *}): *}
*/
export const appendToPreviousResult = (profileType) => (previousResult, { fetchMoreResult }) => {
const newResult = { ...fetchMoreResult };
const previousNodes = previousResult.project[profileType].nodes;
const newNodes = newResult.project[profileType].nodes;
newResult.project[profileType].nodes = [...previousNodes, ...newNodes];
return newResult;
};
/**
* Removes profile with given id from the cache and writes the result to it
*
* @param profileId
* @param profileType
* @param store * @param store
* @param queryBody
*/ */
export const removeProfile = ({ profileId, profileType, store, queryBody }) => { export const removeProfile = ({ profile, store }) => store.evict({ id: store.identify(profile) });
const sourceData = store.readQuery(queryBody);
const data = produce(sourceData, (draftState) => {
draftState.project[profileType].nodes = draftState.project[profileType].nodes.filter((node) => {
return node.id !== profileId;
});
});
store.writeQuery({ ...queryBody, data });
};
/** /**
* Returns an object representing a optimistic response for site-profile deletion * Returns an object representing a optimistic response for site-profile deletion
......
...@@ -4,8 +4,7 @@ query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: ...@@ -4,8 +4,7 @@ query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first:
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
__typename __typename
id id
siteProfiles: dastSiteProfiles(after: $after, before: $before, first: $first, last: $last) siteProfiles: dastSiteProfiles(after: $after, before: $before, first: $first, last: $last) {
@connection(key: "dastSiteProfiles") {
__typename __typename
pageInfo { pageInfo {
__typename __typename
......
import { concatPagination } from '@apollo/client/utilities';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo); Vue.use(VueApollo);
const profilesCachePolicy = () => ({
keyArgs: ['fullPath'],
});
const profilesPagination = () => ({
fields: {
nodes: concatPagination(),
},
});
export default new VueApollo({ export default new VueApollo({
defaultClient: createDefaultClient(), defaultClient: createDefaultClient(
{},
{
cacheConfig: {
typePolicies: {
Project: {
fields: {
dastSiteProfiles: profilesCachePolicy(),
dastScannerProfiles: profilesCachePolicy(),
},
},
DastSiteProfileConnection: profilesPagination(),
DastScannerProfileConnection: profilesPagination(),
},
},
},
),
}); });
import { gql } from '@apollo/client/core'; import { gql } from '@apollo/client/core';
import { import {
appendToPreviousResult,
removeProfile, removeProfile,
dastProfilesDeleteResponse, dastProfilesDeleteResponse,
updateSiteProfilesStatuses, updateSiteProfilesStatuses,
...@@ -8,58 +7,22 @@ import { ...@@ -8,58 +7,22 @@ import {
import { siteProfiles } from '../mocks/mock_data'; import { siteProfiles } from '../mocks/mock_data';
describe('EE - DastProfiles GraphQL CacheUtils', () => { describe('EE - DastProfiles GraphQL CacheUtils', () => {
describe('appendToPreviousResult', () => {
it.each(['siteProfiles', 'scannerProfiles'])(
'appends new results to previous',
(profileType) => {
const previousResult = { project: { [profileType]: { nodes: ['foo'] } } };
const fetchMoreResult = { project: { [profileType]: { nodes: ['bar'] } } };
const expected = { project: { [profileType]: { nodes: ['foo', 'bar'] } } };
const result = appendToPreviousResult(profileType)(previousResult, { fetchMoreResult });
expect(result).toEqual(expected);
},
);
});
describe('removeProfile', () => { describe('removeProfile', () => {
it.each(['foo', 'bar'])( it('removes the profile from the cache', () => {
'removes the profile with the given id from the cache', const [profileToBeRemoved] = siteProfiles;
(profileType) => { const mockStore = {
const mockQueryBody = { query: 'foo', variables: { foo: 'bar' } }; identify: jest.fn().mockReturnValue(profileToBeRemoved.id),
const mockProfiles = [{ id: 0 }, { id: 1 }]; evict: jest.fn(),
const mockData = { };
project: {
[profileType]: {
nodes: [mockProfiles[0], mockProfiles[1]],
},
},
};
const mockStore = {
readQuery: () => mockData,
writeQuery: jest.fn(),
};
removeProfile({ removeProfile({
store: mockStore, profile: profileToBeRemoved,
queryBody: mockQueryBody, store: mockStore,
profileId: mockProfiles[0].id, });
profileType,
});
expect(mockStore.writeQuery).toHaveBeenCalledWith({ expect(mockStore.identify).toHaveBeenCalledWith(profileToBeRemoved);
...mockQueryBody, expect(mockStore.evict).toHaveBeenCalledWith({ id: profileToBeRemoved.id });
data: { });
project: {
[profileType]: {
nodes: [mockProfiles[1]],
},
},
},
});
},
);
}); });
describe('dastProfilesDeleteResponse', () => { describe('dastProfilesDeleteResponse', () => {
......
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