Commit bdd5faad authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch 'leipert-update-vue-apollo' into 'master'

Update to ApolloClient@3

See merge request gitlab-org/gitlab!55062
parents 8c062bbb f07ac8a5
...@@ -22,6 +22,7 @@ query accessTokensGetProjects( ...@@ -22,6 +22,7 @@ query accessTokensGetProjects(
avatarUrl avatarUrl
} }
pageInfo { pageInfo {
__typename
...PageInfo ...PageInfo
} }
} }
......
import { ApolloLink, Observable } from 'apollo-link'; import { ApolloLink, Observable } from '@apollo/client/core';
import { print } from 'graphql'; import { print } from 'graphql';
import cable from '~/actioncable_consumer'; import cable from '~/actioncable_consumer';
import { uuids } from '~/lib/utils/uuids'; import { uuids } from '~/lib/utils/uuids';
......
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
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';
......
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import produce from 'immer'; import produce from 'immer';
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';
import introspectionQueryResultData from './graphql/fragmentTypes.json';
import getCurrentIntegrationQuery from './graphql/queries/get_current_integration.query.graphql'; import getCurrentIntegrationQuery from './graphql/queries/get_current_integration.query.graphql';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
Vue.use(VueApollo); Vue.use(VueApollo);
const resolvers = { const resolvers = {
...@@ -55,9 +49,5 @@ const resolvers = { ...@@ -55,9 +49,5 @@ const resolvers = {
}; };
export default new VueApollo({ export default new VueApollo({
defaultClient: createDefaultClient(resolvers, { defaultClient: createDefaultClient(resolvers),
cacheConfig: {
fragmentMatcher,
},
}),
}); });
{"__schema":{"types":[{"kind":"UNION","name":"AlertManagementIntegration","possibleTypes":[{"name":"AlertManagementHttpIntegration"},{"name":"AlertManagementPrometheusIntegration"}]}]}}
...@@ -5,6 +5,7 @@ query getIntegrations($projectPath: ID!) { ...@@ -5,6 +5,7 @@ query getIntegrations($projectPath: ID!) {
id id
alertManagementIntegrations { alertManagementIntegrations {
nodes { nodes {
__typename
...IntegrationItem ...IntegrationItem
} }
} }
......
fragment Count on UsageTrendsMeasurement { fragment Count on UsageTrendsMeasurement {
__typename
count count
recordedAt recordedAt
} }
import { IntrospectionFragmentMatcher, defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
export const gqlClient = createDefaultClient( export const gqlClient = createDefaultClient(
{}, {},
...@@ -14,8 +9,6 @@ export const gqlClient = createDefaultClient( ...@@ -14,8 +9,6 @@ export const gqlClient = createDefaultClient(
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object); return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object);
}, },
fragmentMatcher,
}, },
}, },
); );
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue'; import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue';
import store from '~/boards/stores'; import store from '~/boards/stores';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
Vue.use(VueApollo); Vue.use(VueApollo);
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(),
{},
{
cacheConfig: {
fragmentMatcher,
},
},
),
}); });
export default (params = {}) => { export default (params = {}) => {
......
import { ApolloLink, Observable } from 'apollo-link'; import { ApolloLink, Observable } from '@apollo/client/core';
export const apolloCaptchaLink = new ApolloLink((operation, forward) => export const apolloCaptchaLink = new ApolloLink((operation, forward) =>
forward(operation).flatMap((result) => { forward(operation).flatMap((result) => {
......
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
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';
import { vulnerabilityLocationTypes } from '~/graphql_shared/fragment_types/vulnerability_location_types';
Vue.use(VueApollo); Vue.use(VueApollo);
// We create a fragment matcher so that we can create a fragment from an interface const defaultClient = createDefaultClient();
// Without this, Apollo throws a heuristic fragment matcher warning
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData: vulnerabilityLocationTypes,
});
const defaultClient = createDefaultClient(
{},
{
cacheConfig: {
fragmentMatcher,
},
},
);
export default new VueApollo({ export default new VueApollo({
defaultClient, defaultClient,
......
import { defaultDataIdFromObject, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
import produce from 'immer'; import produce from 'immer';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
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';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import introspectionQueryResultData from './graphql/fragmentTypes.json';
import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql'; import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql';
import getDesignQuery from './graphql/queries/get_design.query.graphql'; import getDesignQuery from './graphql/queries/get_design.query.graphql';
import typeDefs from './graphql/typedefs.graphql'; import typeDefs from './graphql/typedefs.graphql';
...@@ -13,10 +12,6 @@ import { addPendingTodoToStore } from './utils/cache_update'; ...@@ -13,10 +12,6 @@ import { addPendingTodoToStore } from './utils/cache_update';
import { extractTodoIdFromDeletePath, createPendingTodo } from './utils/design_management_utils'; import { extractTodoIdFromDeletePath, createPendingTodo } from './utils/design_management_utils';
import { CREATE_DESIGN_TODO_EXISTS_ERROR } from './utils/error_messages'; import { CREATE_DESIGN_TODO_EXISTS_ERROR } from './utils/error_messages';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
Vue.use(VueApollo); Vue.use(VueApollo);
const resolvers = { const resolvers = {
...@@ -85,7 +80,6 @@ const defaultClient = createDefaultClient( ...@@ -85,7 +80,6 @@ const defaultClient = createDefaultClient(
} }
return defaultDataIdFromObject(object); return defaultDataIdFromObject(object);
}, },
fragmentMatcher,
}, },
typeDefs, typeDefs,
}, },
......
{"__schema":{"types":[{"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]},{"kind":"UNION","name":"NoteableType","possibleTypes":[{"name":"Design"},{"name":"Issue"},{"name":"MergeRequest"}]}]}}
export const vulnerabilityLocationTypes = {
__schema: {
types: [
{
kind: 'UNION',
name: 'VulnerabilityLocation',
possibleTypes: [
{ name: 'VulnerabilityLocationContainerScanning' },
{ name: 'VulnerabilityLocationDast' },
{ name: 'VulnerabilityLocationDependencyScanning' },
{ name: 'VulnerabilityLocationSast' },
{ name: 'VulnerabilityLocationSecretDetection' },
],
},
],
},
};
{"AlertManagementIntegration":["AlertManagementHttpIntegration","AlertManagementPrometheusIntegration"],"CurrentUserTodos":["BoardEpic","Design","Epic","EpicIssue","Issue","MergeRequest"],"DependencyLinkMetadata":["NugetDependencyLinkMetadata"],"DesignFields":["Design","DesignAtVersion"],"Entry":["Blob","Submodule","TreeEntry"],"Eventable":["BoardEpic","Epic"],"Issuable":["Epic","Issue","MergeRequest"],"JobNeedUnion":["CiBuildNeed","CiJob"],"MemberInterface":["GroupMember","ProjectMember"],"NoteableInterface":["AlertManagementAlert","BoardEpic","Design","Epic","EpicIssue","Issue","MergeRequest","Snippet","Vulnerability"],"NoteableType":["Design","Issue","MergeRequest"],"OrchestrationPolicy":["ScanExecutionPolicy","ScanResultPolicy"],"PackageFileMetadata":["ConanFileMetadata","HelmFileMetadata"],"PackageMetadata":["ComposerMetadata","ConanMetadata","MavenMetadata","NugetMetadata","PypiMetadata"],"ResolvableInterface":["Discussion","Note"],"Service":["BaseService","JiraService"],"TimeboxReportInterface":["Iteration","Milestone"],"User":["MergeRequestAssignee","MergeRequestReviewer","UserCore"],"VulnerabilityDetail":["VulnerabilityDetailBase","VulnerabilityDetailBoolean","VulnerabilityDetailCode","VulnerabilityDetailCommit","VulnerabilityDetailDiff","VulnerabilityDetailFileLocation","VulnerabilityDetailInt","VulnerabilityDetailList","VulnerabilityDetailMarkdown","VulnerabilityDetailModuleLocation","VulnerabilityDetailTable","VulnerabilityDetailText","VulnerabilityDetailUrl"],"VulnerabilityLocation":["VulnerabilityLocationClusterImageScanning","VulnerabilityLocationContainerScanning","VulnerabilityLocationCoverageFuzzing","VulnerabilityLocationDast","VulnerabilityLocationDependencyScanning","VulnerabilityLocationGeneric","VulnerabilityLocationSast","VulnerabilityLocationSecretDetection"]}
...@@ -181,6 +181,9 @@ export default { ...@@ -181,6 +181,9 @@ export default {
return data[this.namespace]?.issues.nodes ?? []; return data[this.namespace]?.issues.nodes ?? [];
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.pageInfo = data[this.namespace]?.issues.pageInfo ?? {}; this.pageInfo = data[this.namespace]?.issues.pageInfo ?? {};
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery(); this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
}, },
......
fragment IssueFragment on Issue { fragment IssueFragment on Issue {
__typename
id id
iid iid
closedAt closedAt
...@@ -18,6 +19,7 @@ fragment IssueFragment on Issue { ...@@ -18,6 +19,7 @@ fragment IssueFragment on Issue {
webUrl webUrl
assignees { assignees {
nodes { nodes {
__typename
id id
avatarUrl avatarUrl
name name
...@@ -26,6 +28,7 @@ fragment IssueFragment on Issue { ...@@ -26,6 +28,7 @@ fragment IssueFragment on Issue {
} }
} }
author { author {
__typename
id id
avatarUrl avatarUrl
name name
......
...@@ -51,7 +51,9 @@ export default { ...@@ -51,7 +51,9 @@ export default {
}, },
data() { data() {
return { return {
jobs: {}, jobs: {
list: [],
},
hasError: false, hasError: false,
isAlertDismissed: false, isAlertDismissed: false,
scope: null, scope: null,
......
import { ApolloLink } from 'apollo-link'; import { ApolloLink } from '@apollo/client/core';
import { memoize } from 'lodash'; import { memoize } from 'lodash';
export const FEATURE_CATEGORY_HEADER = 'x-gitlab-feature-category'; export const FEATURE_CATEGORY_HEADER = 'x-gitlab-feature-category';
......
import { Observable } from 'apollo-link'; import { Observable } from '@apollo/client/core';
import { onError } from 'apollo-link-error'; import { onError } from '@apollo/client/link/error';
import { isNavigatingAway } from '~/lib/utils/is_navigating_away'; import { isNavigatingAway } from '~/lib/utils/is_navigating_away';
/** /**
......
import { InMemoryCache } from 'apollo-cache-inmemory'; import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client/core';
import { ApolloClient } from 'apollo-client'; import { BatchHttpLink } from '@apollo/client/link/batch-http';
import { ApolloLink } from 'apollo-link';
import { BatchHttpLink } from 'apollo-link-batch-http';
import { HttpLink } from 'apollo-link-http';
import { createUploadLink } from 'apollo-upload-client'; import { createUploadLink } from 'apollo-upload-client';
import ActionCableLink from '~/actioncable_link'; import ActionCableLink from '~/actioncable_link';
import { apolloCaptchaLink } from '~/captcha/apollo_captcha_link'; import { apolloCaptchaLink } from '~/captcha/apollo_captcha_link';
import possibleTypes from '~/graphql_shared/possibleTypes.json';
import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link'; import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import { objectToQuery, queryToObject } from '~/lib/utils/url_utility'; import { objectToQuery, queryToObject } from '~/lib/utils/url_utility';
...@@ -21,6 +19,33 @@ export const fetchPolicies = { ...@@ -21,6 +19,33 @@ export const fetchPolicies = {
CACHE_ONLY: 'cache-only', CACHE_ONLY: 'cache-only',
}; };
export const typePolicies = {
Repository: {
merge: true,
},
UserPermissions: {
merge: true,
},
MergeRequestPermissions: {
merge: true,
},
ContainerRepositoryConnection: {
merge: true,
},
TimelogConnection: {
merge: true,
},
BranchList: {
merge: true,
},
InstanceSecurityDashboard: {
merge: true,
},
PipelinePermissions: {
merge: true,
},
};
export const stripWhitespaceFromQuery = (url, path) => { export const stripWhitespaceFromQuery = (url, path) => {
/* eslint-disable-next-line no-unused-vars */ /* eslint-disable-next-line no-unused-vars */
const [_, params] = url.split(path); const [_, params] = url.split(path);
...@@ -46,6 +71,30 @@ export const stripWhitespaceFromQuery = (url, path) => { ...@@ -46,6 +71,30 @@ export const stripWhitespaceFromQuery = (url, path) => {
return `${path}?${reassembled}`; return `${path}?${reassembled}`;
}; };
const acs = [];
let pendingApolloMutations = 0;
// ### Why track pendingApolloMutations, but calculate pendingApolloRequests?
//
// In Apollo 2, we had a single link for counting operations.
//
// With Apollo 3, the `forward().map(...)` of deduped queries is never called.
// So, we resorted to calculating the sum of `inFlightLinkObservables?.size`.
// However! Mutations don't use `inFLightLinkObservables`, but since they are likely
// not deduped we can count them...
//
// https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55062#note_838943715
// https://www.apollographql.com/docs/react/v2/networking/network-layer/#query-deduplication
Object.defineProperty(window, 'pendingApolloRequests', {
get() {
return acs.reduce(
(sum, ac) => sum + (ac?.queryManager?.inFlightLinkObservables?.size || 0),
pendingApolloMutations,
);
},
});
export default (resolvers = {}, config = {}) => { export default (resolvers = {}, config = {}) => {
const { const {
baseUrl, baseUrl,
...@@ -56,6 +105,7 @@ export default (resolvers = {}, config = {}) => { ...@@ -56,6 +105,7 @@ export default (resolvers = {}, config = {}) => {
path = '/api/graphql', path = '/api/graphql',
useGet = false, useGet = false,
} = config; } = config;
let ac = null;
let uri = `${gon.relative_url_root || ''}${path}`; let uri = `${gon.relative_url_root || ''}${path}`;
if (baseUrl) { if (baseUrl) {
...@@ -75,16 +125,6 @@ export default (resolvers = {}, config = {}) => { ...@@ -75,16 +125,6 @@ export default (resolvers = {}, config = {}) => {
batchMax, batchMax,
}; };
const requestCounterLink = new ApolloLink((operation, forward) => {
window.pendingApolloRequests = window.pendingApolloRequests || 0;
window.pendingApolloRequests += 1;
return forward(operation).map((response) => {
window.pendingApolloRequests -= 1;
return response;
});
});
/* /*
This custom fetcher intervention is to deal with an issue where we are using GET to access This custom fetcher intervention is to deal with an issue where we are using GET to access
eTag polling, but Apollo Client adds excessive whitespace, which causes the eTag polling, but Apollo Client adds excessive whitespace, which causes the
...@@ -138,6 +178,22 @@ export default (resolvers = {}, config = {}) => { ...@@ -138,6 +178,22 @@ export default (resolvers = {}, config = {}) => {
); );
}; };
const hasMutation = (operation) =>
(operation?.query?.definitions || []).some((x) => x.operation === 'mutation');
const requestCounterLink = new ApolloLink((operation, forward) => {
if (hasMutation(operation)) {
pendingApolloMutations += 1;
}
return forward(operation).map((response) => {
if (hasMutation(operation)) {
pendingApolloMutations -= 1;
}
return response;
});
});
const appLink = ApolloLink.split( const appLink = ApolloLink.split(
hasSubscriptionOperation, hasSubscriptionOperation,
new ActionCableLink(), new ActionCableLink(),
...@@ -155,19 +211,23 @@ export default (resolvers = {}, config = {}) => { ...@@ -155,19 +211,23 @@ export default (resolvers = {}, config = {}) => {
), ),
); );
return new ApolloClient({ ac = new ApolloClient({
typeDefs, typeDefs,
link: appLink, link: appLink,
cache: new InMemoryCache({ cache: new InMemoryCache({
typePolicies,
possibleTypes,
...cacheConfig, ...cacheConfig,
freezeResults: true,
}), }),
resolvers, resolvers,
assumeImmutableResults: true,
defaultOptions: { defaultOptions: {
query: { query: {
fetchPolicy, fetchPolicy,
}, },
}, },
}); });
acs.push(ac);
return ac;
}; };
import { ApolloLink, Observable } from 'apollo-link'; import { ApolloLink, Observable } from '@apollo/client/core';
import { parse } from 'graphql'; import { parse } from 'graphql';
import { isEqual, pickBy } from 'lodash'; import { isEqual, pickBy } from 'lodash';
......
...@@ -96,6 +96,9 @@ export default { ...@@ -96,6 +96,9 @@ export default {
return data[this.graphqlResource]?.containerRepositories.nodes; return data[this.graphqlResource]?.containerRepositories.nodes;
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo; this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo;
this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount; this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount;
}, },
......
{
"__schema": {
"types": [
{
"kind": "UNION",
"name": "PackageMetadata",
"possibleTypes": [
{ "name": "ComposerMetadata" },
{ "name": "ConanMetadata" },
{ "name": "MavenMetadata" },
{ "name": "NugetMetadata" },
{ "name": "PypiMetadata" }
]
}
]
}
}
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
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';
import introspectionQueryResultData from './fragmentTypes.json';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
Vue.use(VueApollo); Vue.use(VueApollo);
export const apolloProvider = new VueApollo({ export const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(),
{},
{
cacheConfig: {
fragmentMatcher,
},
},
),
}); });
...@@ -49,7 +49,7 @@ export default { ...@@ -49,7 +49,7 @@ export default {
pipelineEtag: { pipelineEtag: {
query: getPipelineEtag, query: getPipelineEtag,
update(data) { update(data) {
return data.etags.pipeline; return data.etags?.pipeline;
}, },
}, },
pipeline: { pipeline: {
......
...@@ -196,7 +196,7 @@ export default { ...@@ -196,7 +196,7 @@ export default {
currentBranch: { currentBranch: {
query: getCurrentBranch, query: getCurrentBranch,
update(data) { update(data) {
return data.workBranches.current.name; return data.workBranches?.current?.name;
}, },
}, },
starterTemplate: { starterTemplate: {
...@@ -214,7 +214,7 @@ export default { ...@@ -214,7 +214,7 @@ export default {
return data.project?.ciTemplate?.content || ''; return data.project?.ciTemplate?.content || '';
}, },
result({ data }) { result({ data }) {
this.updateCiConfig(data.project?.ciTemplate?.content || ''); this.updateCiConfig(data?.project?.ciTemplate?.content || '');
}, },
error() { error() {
this.reportFailure(LOAD_FAILURE_UNKNOWN); this.reportFailure(LOAD_FAILURE_UNKNOWN);
......
...@@ -36,6 +36,9 @@ export default { ...@@ -36,6 +36,9 @@ export default {
return data.project?.pipeline?.jobs?.nodes || []; return data.project?.pipeline?.jobs?.nodes || [];
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.jobsPageInfo = data.project?.pipeline?.jobs?.pageInfo || {}; this.jobsPageInfo = data.project?.pipeline?.jobs?.pageInfo || {};
}, },
error() { error() {
......
{"__schema":{"types":[{"kind":"UNION","name":"JobNeedUnion","possibleTypes":[{"name":"CiBuildNeed"},{"name":"CiJob"}]}]}}
\ No newline at end of file
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './graphql/fragmentTypes.json';
export const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
export const apolloProvider = new VueApollo({ export const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(
{}, {},
{ {
cacheConfig: {
fragmentMatcher,
},
useGet: true, useGet: true,
}, },
), ),
......
{"__schema":{"types":[{"kind":"INTERFACE","name":"Entry","possibleTypes":[{"name":"Blob"},{"name":"Submodule"},{"name":"TreeEntry"}]}]}}
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
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';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import introspectionQueryResultData from './fragmentTypes.json';
import { fetchLogsTree } from './log_tree'; import { fetchLogsTree } from './log_tree';
Vue.use(VueApollo); Vue.use(VueApollo);
// We create a fragment matcher so that we can create a fragment from an interface
// Without this, Apollo throws a heuristic fragment matcher warning
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
const defaultClient = createDefaultClient( const defaultClient = createDefaultClient(
{ {
Query: { Query: {
...@@ -43,7 +35,6 @@ const defaultClient = createDefaultClient( ...@@ -43,7 +35,6 @@ const defaultClient = createDefaultClient(
}, },
{ {
cacheConfig: { cacheConfig: {
fragmentMatcher,
dataIdFromObject: (obj) => { dataIdFromObject: (obj) => {
/* eslint-disable @gitlab/require-i18n-strings */ /* eslint-disable @gitlab/require-i18n-strings */
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
......
...@@ -28,10 +28,12 @@ query getGroupRunners( ...@@ -28,10 +28,12 @@ query getGroupRunners(
edges { edges {
webUrl webUrl
node { node {
__typename
...RunnerNode ...RunnerNode
} }
} }
pageInfo { pageInfo {
__typename
...PageInfo ...PageInfo
} }
} }
......
...@@ -4,6 +4,7 @@ query getRunner($id: CiRunnerID!) { ...@@ -4,6 +4,7 @@ query getRunner($id: CiRunnerID!) {
# We have an id in deeply nested fragment # We have an id in deeply nested fragment
# eslint-disable-next-line @graphql-eslint/require-id-when-available # eslint-disable-next-line @graphql-eslint/require-id-when-available
runner(id: $id) { runner(id: $id) {
__typename
...RunnerDetails ...RunnerDetails
} }
} }
...@@ -29,6 +29,7 @@ query getRunners( ...@@ -29,6 +29,7 @@ query getRunners(
editAdminUrl editAdminUrl
} }
pageInfo { pageInfo {
__typename
...PageInfo ...PageInfo
} }
} }
......
fragment RunnerNode on CiRunner { fragment RunnerNode on CiRunner {
__typename
id id
description description
runnerType runnerType
......
...@@ -96,6 +96,9 @@ export default { ...@@ -96,6 +96,9 @@ export default {
return data.workspace?.issuable; return data.workspace?.issuable;
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
const issuable = data.workspace?.issuable; const issuable = data.workspace?.issuable;
if (issuable) { if (issuable) {
this.selected = cloneDeep(issuable.assignees.nodes); this.selected = cloneDeep(issuable.assignees.nodes);
......
...@@ -66,6 +66,9 @@ export default { ...@@ -66,6 +66,9 @@ export default {
return data.workspace?.issuable?.confidential || false; return data.workspace?.issuable?.confidential || false;
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.$emit('confidentialityUpdated', data.workspace?.issuable?.confidential); this.$emit('confidentialityUpdated', data.workspace?.issuable?.confidential);
}, },
error() { error() {
......
...@@ -86,6 +86,9 @@ export default { ...@@ -86,6 +86,9 @@ export default {
return data.workspace?.issuable || {}; return data.workspace?.issuable || {};
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.$emit(`${this.dateType}Updated`, data.workspace?.issuable?.[this.dateType]); this.$emit(`${this.dateType}Updated`, data.workspace?.issuable?.[this.dateType]);
}, },
error() { error() {
......
...@@ -61,6 +61,9 @@ export default { ...@@ -61,6 +61,9 @@ export default {
return data.workspace?.issuable?.subscribed || false; return data.workspace?.issuable?.subscribed || false;
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.emailsDisabled = this.parentIsGroup this.emailsDisabled = this.parentIsGroup
? data.workspace?.emailsDisabled ? data.workspace?.emailsDisabled
: data.workspace?.issuable?.emailsDisabled; : data.workspace?.issuable?.emailsDisabled;
......
...@@ -59,6 +59,10 @@ export default { ...@@ -59,6 +59,10 @@ export default {
return data.workspace?.issuable?.currentUserTodos.nodes[0]?.id; return data.workspace?.issuable?.currentUserTodos.nodes[0]?.id;
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
const currentUserTodos = data.workspace?.issuable?.currentUserTodos?.nodes ?? []; const currentUserTodos = data.workspace?.issuable?.currentUserTodos?.nodes ?? [];
this.todoId = currentUserTodos[0]?.id; this.todoId = currentUserTodos[0]?.id;
this.$emit('todoUpdated', currentUserTodos.length > 0); this.$emit('todoUpdated', currentUserTodos.length > 0);
......
{"__schema":{"types":[{"kind":"UNION","name":"Issuable","possibleTypes":[{"name":"Issue"},{"name":"MergeRequest"}]}, {"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]}]}}
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import produce from 'immer'; import produce from 'immer';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql'; import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql';
import { resolvers as workItemResolvers } from '~/work_items/graphql/resolvers'; import { resolvers as workItemResolvers } from '~/work_items/graphql/resolvers';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import introspectionQueryResultData from './fragmentTypes.json';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
const resolvers = { const resolvers = {
...workItemResolvers, ...workItemResolvers,
...@@ -24,11 +18,7 @@ const resolvers = { ...@@ -24,11 +18,7 @@ const resolvers = {
}, },
}; };
export const defaultClient = createDefaultClient(resolvers, { export const defaultClient = createDefaultClient(resolvers);
cacheConfig: {
fragmentMatcher,
},
});
export const apolloProvider = new VueApollo({ export const apolloProvider = new VueApollo({
defaultClient, defaultClient,
......
import { defaultDataIdFromObject } from '@apollo/client/core';
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
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';
......
...@@ -3,7 +3,6 @@ query getState($projectPath: ID!, $iid: String!) { ...@@ -3,7 +3,6 @@ query getState($projectPath: ID!, $iid: String!) {
id id
archived archived
onlyAllowMergeIfPipelineSucceeds onlyAllowMergeIfPipelineSucceeds
mergeRequest(iid: $iid) { mergeRequest(iid: $iid) {
id id
autoMergeEnabled autoMergeEnabled
......
...@@ -4,6 +4,7 @@ query autoMergeEnabled($projectPath: ID!, $iid: String!) { ...@@ -4,6 +4,7 @@ query autoMergeEnabled($projectPath: ID!, $iid: String!) {
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
id id
mergeRequest(iid: $iid) { mergeRequest(iid: $iid) {
id
...autoMergeEnabled ...autoMergeEnabled
} }
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
query readyToMerge($projectPath: ID!, $iid: String!) { query readyToMerge($projectPath: ID!, $iid: String!) {
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
id
...ReadyToMerge ...ReadyToMerge
} }
} }
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
import produce from 'immer'; import produce from 'immer';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
......
...@@ -102,8 +102,8 @@ export default { ...@@ -102,8 +102,8 @@ export default {
error(error) { error(error) {
this.showError(error); this.showError(error);
}, },
result({ loading }) { result({ loading, data }) {
if (loading) { if (loading || !data) {
return; return;
} }
......
{"__schema":{"types":[{"kind":"INTERFACE","name":"LocalWorkItemWidget","possibleTypes":[{"name":"LocalTitleWidget"}]}]}}
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import workItemQuery from './work_item.query.graphql'; import workItemQuery from './work_item.query.graphql';
import introspectionQueryResultData from './fragmentTypes.json';
import { resolvers } from './resolvers'; import { resolvers } from './resolvers';
import typeDefs from './typedefs.graphql'; import typeDefs from './typedefs.graphql';
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
export function createApolloProvider() { export function createApolloProvider() {
Vue.use(VueApollo); Vue.use(VueApollo);
const defaultClient = createDefaultClient(resolvers, { const defaultClient = createDefaultClient(resolvers, {
cacheConfig: {
fragmentMatcher,
},
typeDefs, typeDefs,
}); });
......
...@@ -150,6 +150,8 @@ function generateEntries() { ...@@ -150,6 +150,8 @@ function generateEntries() {
} }
const alias = { const alias = {
// Map Apollo client to apollo/client/core to prevent react related imports from being loaded
'@apollo/client$': '@apollo/client/core',
'~': path.join(ROOT_PATH, 'app/assets/javascripts'), '~': path.join(ROOT_PATH, 'app/assets/javascripts'),
emojis: path.join(ROOT_PATH, 'fixtures/emojis'), emojis: path.join(ROOT_PATH, 'fixtures/emojis'),
empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'), empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'),
......
...@@ -47,7 +47,7 @@ module.exports = { ...@@ -47,7 +47,7 @@ module.exports = {
'bootstrap/dist/js/bootstrap.js', 'bootstrap/dist/js/bootstrap.js',
'sortablejs/modular/sortable.esm.js', 'sortablejs/modular/sortable.esm.js',
'popper.js', 'popper.js',
'apollo-client', '@apollo/client/core',
'source-map', 'source-map',
'mousetrap', 'mousetrap',
], ],
......
...@@ -152,7 +152,7 @@ Root-level queries are defined in ...@@ -152,7 +152,7 @@ Root-level queries are defined in
### Multiplex queries ### Multiplex queries
GitLab supports batching queries into a single request using GitLab supports batching queries into a single request using
[apollo-link-batch-http](https://www.apollographql.com/docs/link/links/batch-http/). More [`@apollo/client/link/batch-http`](https://www.apollographql.com/docs/react/api/link/apollo-link-batch-http/). More
information about multiplexed queries is also available for information about multiplexed queries is also available for
[GraphQL Ruby](https://graphql-ruby.org/queries/multiplex.html), the [GraphQL Ruby](https://graphql-ruby.org/queries/multiplex.html), the
library GitLab uses on the backend. library GitLab uses on the backend.
......
...@@ -105,6 +105,9 @@ export default { ...@@ -105,6 +105,9 @@ export default {
}; };
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
if (this.hasSubgroups === undefined) { if (this.hasSubgroups === undefined) {
this.hasSubgroups = data.groups?.nodes?.length > 0; this.hasSubgroups = data.groups?.nodes?.length > 0;
} }
......
...@@ -9,6 +9,7 @@ query devopsAdoptionEnabledNamespaces($displayNamespaceId: NamespaceID) { ...@@ -9,6 +9,7 @@ query devopsAdoptionEnabledNamespaces($displayNamespaceId: NamespaceID) {
...LatestSnapshot ...LatestSnapshot
} }
namespace { namespace {
__typename
...Namespace ...Namespace
} }
} }
......
import gql from 'graphql-tag'; import { gql } from '@apollo/client/core';
import { computeMonthRangeData } from '../utils'; import { computeMonthRangeData } from '../utils';
/** /**
......
...@@ -15,6 +15,7 @@ query getCodeQualityViolations($projectPath: ID!, $iid: ID!, $first: Int, $after ...@@ -15,6 +15,7 @@ query getCodeQualityViolations($projectPath: ID!, $iid: ID!, $first: Int, $after
severity severity
} }
pageInfo { pageInfo {
__typename
...PageInfo ...PageInfo
} }
} }
......
...@@ -7,6 +7,8 @@ import { parseIssuableData } from '~/issues/show/utils/parse_data'; ...@@ -7,6 +7,8 @@ import { parseIssuableData } from '~/issues/show/utils/parse_data';
import { defaultClient } from '~/sidebar/graphql'; import { defaultClient } from '~/sidebar/graphql';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store'; import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql';
import { IssuableType } from '~/issues/constants';
import EpicApp from './components/epic_app.vue'; import EpicApp from './components/epic_app.vue';
import createStore from './store'; import createStore from './store';
...@@ -17,6 +19,16 @@ const apolloProvider = new VueApollo({ ...@@ -17,6 +19,16 @@ const apolloProvider = new VueApollo({
defaultClient, defaultClient,
}); });
apolloProvider.clients.defaultClient.cache.writeQuery({
query: getIssueStateQuery,
data: {
issueState: {
isDirty: false,
issuableType: IssuableType.Epic,
},
},
});
export default () => { export default () => {
const el = document.getElementById('epic-app-root'); const el = document.getElementById('epic-app-root');
......
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
...@@ -16,23 +15,11 @@ import createDefaultClient from '~/lib/graphql'; ...@@ -16,23 +15,11 @@ import createDefaultClient from '~/lib/graphql';
import '~/boards/filters/due_date_filters'; import '~/boards/filters/due_date_filters';
import { NavigationType, parseBoolean } from '~/lib/utils/common_utils'; import { NavigationType, parseBoolean } from '~/lib/utils/common_utils';
import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
Vue.use(VueApollo); Vue.use(VueApollo);
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
const apolloProvider = new VueApollo({ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(),
{},
{
cacheConfig: {
fragmentMatcher,
},
},
),
}); });
function mountBoardApp(el) { function mountBoardApp(el) {
......
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
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';
......
import gql from 'graphql-tag'; import { gql } from '@apollo/client/core';
import PageInfo from '~/graphql_shared/fragments/pageInfo.fragment.graphql'; import PageInfo from '~/graphql_shared/fragments/pageInfo.fragment.graphql';
export default (graphQlFieldName) => { export default (graphQlFieldName) => {
......
...@@ -58,6 +58,9 @@ export default { ...@@ -58,6 +58,9 @@ export default {
}; };
}, },
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.formData = this.extractComplianceFramework(data); this.formData = this.extractComplianceFramework(data);
}, },
error(error) { error(error) {
......
fragment OnCallParticipant on OncallParticipantType { fragment OnCallParticipant on OncallParticipantType {
__typename
id id
user { user {
id id
......
#import "../fragments/oncall_schedule_participant.fragment.graphql" #import "../fragments/oncall_schedule_participant.fragment.graphql"
fragment OnCallRotation on IncidentManagementOncallRotation { fragment OnCallRotation on IncidentManagementOncallRotation {
__typename
id id
name name
startsAt startsAt
...@@ -13,6 +14,7 @@ fragment OnCallRotation on IncidentManagementOncallRotation { ...@@ -13,6 +14,7 @@ fragment OnCallRotation on IncidentManagementOncallRotation {
} }
participants { participants {
nodes { nodes {
__typename
...OnCallParticipant ...OnCallParticipant
} }
} }
......
...@@ -5,6 +5,7 @@ fragment OnCallRotationWithShifts on IncidentManagementOncallRotation { ...@@ -5,6 +5,7 @@ fragment OnCallRotationWithShifts on IncidentManagementOncallRotation {
shifts(startTime: $startsAt, endTime: $endsAt) { shifts(startTime: $startsAt, endTime: $endsAt) {
nodes { nodes {
participant { participant {
__typename
...OnCallParticipant ...OnCallParticipant
} }
endsAt endsAt
......
import gql from 'graphql-tag'; import { gql } from '@apollo/client/core';
import createFlash from '~/flash'; import createFlash from '~/flash';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
......
...@@ -152,7 +152,7 @@ export default { ...@@ -152,7 +152,7 @@ export default {
</template> </template>
<template #default> <template #default>
<gl-skeleton-loader v-if="$apollo.queries.projectQuality.loading" /> <gl-skeleton-loader v-if="$apollo.queries.projectQuality.loading" />
<div v-else class="row gl-ml-2"> <div v-else-if="projectQuality.testReportSummary" class="row gl-ml-2">
<gl-single-stat <gl-single-stat
class="col-sm-6 col-md-4" class="col-sm-6 col-md-4"
data-testid="test-runs-stat" data-testid="test-runs-stat"
......
{"__schema":{"types":[{"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]}]}}
import { defaultDataIdFromObject } from '@apollo/client/core';
import { GlToast } from '@gitlab/ui'; import { GlToast } from '@gitlab/ui';
import { defaultDataIdFromObject, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
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';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import introspectionQueryResultData from './queries/fragmentTypes.json';
import RequirementsRoot from './components/requirements_root.vue'; import RequirementsRoot from './components/requirements_root.vue';
...@@ -13,10 +12,6 @@ import { FilterState } from './constants'; ...@@ -13,10 +12,6 @@ import { FilterState } from './constants';
Vue.use(VueApollo); Vue.use(VueApollo);
Vue.use(GlToast); Vue.use(GlToast);
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData,
});
export default () => { export default () => {
const el = document.getElementById('js-requirements-app'); const el = document.getElementById('js-requirements-app');
...@@ -32,7 +27,6 @@ export default () => { ...@@ -32,7 +27,6 @@ export default () => {
dataIdFromObject: (object) => dataIdFromObject: (object) =>
// eslint-disable-next-line no-underscore-dangle, @gitlab/require-i18n-strings // eslint-disable-next-line no-underscore-dangle, @gitlab/require-i18n-strings
object.__typename === 'Requirement' ? object.iid : defaultDataIdFromObject(object), object.__typename === 'Requirement' ? object.iid : defaultDataIdFromObject(object),
fragmentMatcher,
}, },
}, },
), ),
......
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
import Vue from 'vue'; import Vue from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { helpPagePath } from '~/helpers/help_page_helper'; import { helpPagePath } from '~/helpers/help_page_helper';
......
import gql from 'graphql-tag'; import { gql } from '@apollo/client/core';
import { produce } from 'immer'; 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';
......
...@@ -9,13 +9,16 @@ query DastScannerProfiles( ...@@ -9,13 +9,16 @@ query DastScannerProfiles(
) { ) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
id id
__typename
scannerProfiles: dastScannerProfiles( scannerProfiles: dastScannerProfiles(
after: $after after: $after
before: $before before: $before
first: $first first: $first
last: $last last: $last
) { ) {
__typename
pageInfo { pageInfo {
__typename
...PageInfo ...PageInfo
} }
nodes { nodes {
......
...@@ -2,10 +2,13 @@ ...@@ -2,10 +2,13 @@
query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) { query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) {
project(fullPath: $fullPath) { project(fullPath: $fullPath) {
__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") { @connection(key: "dastSiteProfiles") {
__typename
pageInfo { pageInfo {
__typename
...PageInfo ...PageInfo
} }
nodes { nodes {
......
...@@ -74,6 +74,9 @@ export default { ...@@ -74,6 +74,9 @@ export default {
id: finding.uuid, id: finding.uuid,
})), })),
result({ data }) { result({ data }) {
if (!data) {
return;
}
this.pageInfo = preparePageInfo(data.project?.pipeline?.securityReportFindings?.pageInfo); this.pageInfo = preparePageInfo(data.project?.pipeline?.securityReportFindings?.pageInfo);
}, },
error() { error() {
......
...@@ -49,7 +49,7 @@ export default { ...@@ -49,7 +49,7 @@ export default {
update(data) { update(data) {
const summary = { const summary = {
reports: data?.project?.pipeline?.securityReportSummary, reports: data?.project?.pipeline?.securityReportSummary,
jobs: data?.project?.pipeline?.jobs.nodes, jobs: data?.project?.pipeline?.jobs?.nodes,
}; };
return summary?.reports && Object.keys(summary.reports).length ? summary : null; return summary?.reports && Object.keys(summary.reports).length ? summary : null;
}, },
......
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
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';
import { vulnerabilityLocationTypes } from '~/graphql_shared/fragment_types/vulnerability_location_types';
import tempResolvers from '~/security_configuration/resolver'; import tempResolvers from '~/security_configuration/resolver';
Vue.use(VueApollo); Vue.use(VueApollo);
// We create a fragment matcher so that we can create a fragment from an interface const defaultClient = createDefaultClient({
// Without this, Apollo throws a heuristic fragment matcher warning ...tempResolvers,
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData: vulnerabilityLocationTypes,
}); });
const defaultClient = createDefaultClient(
{
...tempResolvers,
},
{
cacheConfig: {
fragmentMatcher,
},
},
);
export default new VueApollo({ export default new VueApollo({
defaultClient, defaultClient,
}); });
...@@ -70,9 +70,6 @@ export default { ...@@ -70,9 +70,6 @@ export default {
update(data) { update(data) {
return data.workspace?.issuable?.weight; return data.workspace?.issuable?.weight;
}, },
result({ data }) {
return data.workspace?.issuable?.weight;
},
error() { error() {
createFlash({ createFlash({
message: sprintf(__('Something went wrong while setting %{issuableType} weight.'), { message: sprintf(__('Something went wrong while setting %{issuableType} weight.'), {
......
...@@ -62,6 +62,9 @@ export default { ...@@ -62,6 +62,9 @@ export default {
}, },
manual: true, manual: true,
result({ data }) { result({ data }) {
if (!data) {
return;
}
if (data.errors) { if (data.errors) {
this.hasError = true; this.hasError = true;
} else if (data.orderPreview) { } else if (data.orderPreview) {
......
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from '@apollo/client/core';
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';
......
...@@ -45,7 +45,7 @@ export default { ...@@ -45,7 +45,7 @@ export default {
id: convertToGraphQLId(TYPE_VULNERABILITY, this.vulnerability.id), id: convertToGraphQLId(TYPE_VULNERABILITY, this.vulnerability.id),
}; };
}, },
result({ data: { vulnerability } }) { result({ data: { vulnerability } = {} }) {
this.shouldSkipQuery = true; this.shouldSkipQuery = true;
this.vulnerability = { this.vulnerability = {
...this.vulnerability, ...this.vulnerability,
......
...@@ -202,10 +202,12 @@ describe('SubscriptionManagementApp', () => { ...@@ -202,10 +202,12 @@ describe('SubscriptionManagementApp', () => {
describe('activating the license', () => { describe('activating the license', () => {
it('shows the activation success notification', async () => { it('shows the activation success notification', async () => {
await findActivateSubscriptionCard().vm.$emit( findActivateSubscriptionCard().vm.$emit(
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT, SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
license.ULTIMATE, license.ULTIMATE,
); );
await waitForPromises();
expect(findSubscriptionActivationSuccessAlert().props('title')).toBe( expect(findSubscriptionActivationSuccessAlert().props('title')).toBe(
subscriptionActivationNotificationText, subscriptionActivationNotificationText,
); );
...@@ -225,9 +227,9 @@ describe('SubscriptionManagementApp', () => { ...@@ -225,9 +227,9 @@ describe('SubscriptionManagementApp', () => {
describe('activating the license', () => { describe('activating the license', () => {
beforeEach(async () => { beforeEach(async () => {
currentSubscriptionResolver = jest currentSubscriptionResolver = jest.fn().mockResolvedValue({
.fn() data: { currentLicense: { __typename: 'CurrentLicense', ...license.ULTIMATE } },
.mockResolvedValue({ data: { currentLicense: license.ULTIMATE } }); });
pastSubscriptionsResolver = jest.fn().mockResolvedValue({ pastSubscriptionsResolver = jest.fn().mockResolvedValue({
data: { licenseHistoryEntries: { nodes: subscriptionPastHistory } }, data: { licenseHistoryEntries: { nodes: subscriptionPastHistory } },
}); });
...@@ -272,9 +274,9 @@ describe('SubscriptionManagementApp', () => { ...@@ -272,9 +274,9 @@ describe('SubscriptionManagementApp', () => {
describe('with active license', () => { describe('with active license', () => {
beforeEach(async () => { beforeEach(async () => {
currentSubscriptionResolver = jest currentSubscriptionResolver = jest.fn().mockResolvedValue({
.fn() data: { currentLicense: { __typename: 'CurrentLicense', ...license.ULTIMATE } },
.mockResolvedValue({ data: { currentLicense: license.ULTIMATE } }); });
pastSubscriptionsResolver = jest.fn().mockResolvedValue({ pastSubscriptionsResolver = jest.fn().mockResolvedValue({
data: { licenseHistoryEntries: { nodes: subscriptionPastHistory } }, data: { licenseHistoryEntries: { nodes: subscriptionPastHistory } },
}); });
......
...@@ -193,7 +193,7 @@ describe('DevopsAdoptionAddDropdown', () => { ...@@ -193,7 +193,7 @@ describe('DevopsAdoptionAddDropdown', () => {
}); });
describe('on error', () => { describe('on error', () => {
beforeEach(() => { beforeEach(async () => {
jest.spyOn(Sentry, 'captureException'); jest.spyOn(Sentry, 'captureException');
createComponent({ createComponent({
...@@ -202,6 +202,7 @@ describe('DevopsAdoptionAddDropdown', () => { ...@@ -202,6 +202,7 @@ describe('DevopsAdoptionAddDropdown', () => {
}); });
clickFirstRow(); clickFirstRow();
await waitForPromises();
}); });
it('calls sentry', async () => { it('calls sentry', async () => {
......
...@@ -225,7 +225,6 @@ describe('DevopsAdoptionApp', () => { ...@@ -225,7 +225,6 @@ describe('DevopsAdoptionApp', () => {
}; };
wrapper = createComponent({ mockApollo, provide }); wrapper = createComponent({ mockApollo, provide });
await waitForPromises(); await waitForPromises();
await nextTick();
}); });
it('does not attempt to enable a group', () => { it('does not attempt to enable a group', () => {
......
...@@ -172,6 +172,7 @@ describe('Iteration Breadcrumb', () => { ...@@ -172,6 +172,7 @@ describe('Iteration Breadcrumb', () => {
iterationCadences: { iterationCadences: {
nodes: [ nodes: [
{ {
__typename: 'IterationCadence',
title: cadenceTitle, title: cadenceTitle,
id: 'cadenceid', id: 'cadenceid',
automatic: '', automatic: '',
......
...@@ -325,17 +325,20 @@ describe('Iteration cadence form', () => { ...@@ -325,17 +325,20 @@ describe('Iteration cadence form', () => {
}); });
it('updates roll over issues checkbox', async () => { it('updates roll over issues checkbox', async () => {
await waitForPromises();
const rollOver = true; const rollOver = true;
setRollOver(rollOver); setRollOver(rollOver);
const { __typename, ...cadenceWithoutTypename } = iterationCadence;
clickSave(); clickSave();
await nextTick(); await waitForPromises();
expect(findError().exists()).toBe(false); expect(findError().exists()).toBe(false);
expect(mutationMock).toHaveBeenCalledWith({ expect(mutationMock).toHaveBeenCalledWith({
input: { input: {
...iterationCadence, ...cadenceWithoutTypename,
rollOver, rollOver,
}, },
}); });
......
...@@ -60,6 +60,7 @@ describe('Iteration cadence list item', () => { ...@@ -60,6 +60,7 @@ describe('Iteration cadence list item', () => {
iterations: { iterations: {
nodes: iterations, nodes: iterations,
pageInfo: { pageInfo: {
__typename: 'PageInfo',
hasNextPage: true, hasNextPage: true,
hasPreviousPage: false, hasPreviousPage: false,
startCursor, startCursor,
......
...@@ -58,6 +58,7 @@ describe('Iteration cadences list', () => { ...@@ -58,6 +58,7 @@ describe('Iteration cadences list', () => {
iterationCadences: { iterationCadences: {
nodes: cadences, nodes: cadences,
pageInfo: { pageInfo: {
__typename: 'PageInfo',
hasNextPage: true, hasNextPage: true,
hasPreviousPage: false, hasPreviousPage: false,
startCursor, startCursor,
...@@ -85,6 +86,10 @@ describe('Iteration cadences list', () => { ...@@ -85,6 +86,10 @@ describe('Iteration cadences list', () => {
}, },
}; };
const queryErrorResponse = {
message: 'Network error',
};
function createComponent({ function createComponent({
canCreateCadence, canCreateCadence,
canEditCadence, canEditCadence,
...@@ -188,7 +193,7 @@ describe('Iteration cadences list', () => { ...@@ -188,7 +193,7 @@ describe('Iteration cadences list', () => {
it('shows alert on query error', async () => { it('shows alert on query error', async () => {
await createComponent({ await createComponent({
resolverMock: jest.fn().mockRejectedValue(queryEmptyResponse), resolverMock: jest.fn().mockRejectedValue(queryErrorResponse),
}); });
await waitForPromises(); await waitForPromises();
...@@ -203,7 +208,6 @@ describe('Iteration cadences list', () => { ...@@ -203,7 +208,6 @@ describe('Iteration cadences list', () => {
beforeEach(async () => { beforeEach(async () => {
resolverMock = jest.fn().mockResolvedValue(querySuccessResponse); resolverMock = jest.fn().mockResolvedValue(querySuccessResponse);
await createComponent({ resolverMock }); await createComponent({ resolverMock });
await waitForPromises(); await waitForPromises();
resolverMock.mockReset(); resolverMock.mockReset();
......
...@@ -38,6 +38,7 @@ export const mockProjectIterations = { ...@@ -38,6 +38,7 @@ export const mockProjectIterations = {
}; };
export const manualIterationCadence = { export const manualIterationCadence = {
__typename: 'IterationCadence',
active: true, active: true,
id: `gid://gitlab/Iterations::Cadence/72`, id: `gid://gitlab/Iterations::Cadence/72`,
title: 'A manual iteration cadence', title: 'A manual iteration cadence',
...@@ -65,6 +66,7 @@ export const updateMutationSuccess = { ...@@ -65,6 +66,7 @@ export const updateMutationSuccess = {
export const emptyGroupIterationsSuccess = { export const emptyGroupIterationsSuccess = {
data: { data: {
workspace: { workspace: {
__typename: 'Group',
id: 'gid://gitlab/Group/114', id: 'gid://gitlab/Group/114',
iterations: { iterations: {
nodes: [], nodes: [],
......
[ [
{ {
"__typename": "IncidentManagementOncallRotation",
"id": "gid://gitlab/IncidentManagement::OncallRotation/2", "id": "gid://gitlab/IncidentManagement::OncallRotation/2",
"name": "Rotation 242", "name": "Rotation 242",
"startsAt": "2021-01-13T11:04:56.333Z", "startsAt": "2021-01-13T11:04:56.333Z",
...@@ -13,6 +14,7 @@ ...@@ -13,6 +14,7 @@
"participants": { "participants": {
"nodes": [ "nodes": [
{ {
"__typename": "OncallParticipantType",
"id": "49", "id": "49",
"user": { "user": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/49", "id": "gid://gitlab/IncidentManagement::OncallParticipant/49",
...@@ -29,6 +31,7 @@ ...@@ -29,6 +31,7 @@
"nodes": [ "nodes": [
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "1", "id": "1",
"colorWeight": "500", "colorWeight": "500",
"colorPalette": "blue", "colorPalette": "blue",
...@@ -44,6 +47,7 @@ ...@@ -44,6 +47,7 @@
}, },
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "2", "id": "2",
"colorWeight": "500", "colorWeight": "500",
"colorPalette": "orange", "colorPalette": "orange",
...@@ -61,6 +65,7 @@ ...@@ -61,6 +65,7 @@
} }
}, },
{ {
"__typename": "IncidentManagementOncallRotation",
"id": "gid://gitlab/IncidentManagement::OncallRotation/55", "id": "gid://gitlab/IncidentManagement::OncallRotation/55",
"name": "Rotation 242", "name": "Rotation 242",
"startsAt": "2021-01-13T11:04:56.333Z", "startsAt": "2021-01-13T11:04:56.333Z",
...@@ -74,6 +79,7 @@ ...@@ -74,6 +79,7 @@
"participants": { "participants": {
"nodes": [ "nodes": [
{ {
"__typename": "OncallParticipantType",
"id": "99", "id": "99",
"user": { "user": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/99", "id": "gid://gitlab/IncidentManagement::OncallParticipant/99",
...@@ -90,6 +96,7 @@ ...@@ -90,6 +96,7 @@
"nodes": [ "nodes": [
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "38", "id": "38",
"colorWeight": "500", "colorWeight": "500",
"colorPalette": "aqua", "colorPalette": "aqua",
...@@ -105,6 +112,7 @@ ...@@ -105,6 +112,7 @@
}, },
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "39", "id": "39",
"colorWeight": "500", "colorWeight": "500",
"colorPalette": "green", "colorPalette": "green",
...@@ -122,6 +130,7 @@ ...@@ -122,6 +130,7 @@
} }
}, },
{ {
"__typename": "IncidentManagementOncallRotation",
"id": "gid://gitlab/IncidentManagement::OncallRotation/3", "id": "gid://gitlab/IncidentManagement::OncallRotation/3",
"name": "Rotation 244", "name": "Rotation 244",
"startsAt": "2021-01-06T10:04:56.333Z", "startsAt": "2021-01-06T10:04:56.333Z",
...@@ -135,6 +144,7 @@ ...@@ -135,6 +144,7 @@
"participants": { "participants": {
"nodes": [ "nodes": [
{ {
"__typename": "OncallParticipantType",
"id": "48", "id": "48",
"user": { "user": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/48", "id": "gid://gitlab/IncidentManagement::OncallParticipant/48",
...@@ -151,6 +161,7 @@ ...@@ -151,6 +161,7 @@
"nodes": [ "nodes": [
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "40", "id": "40",
"colorWeight": "500", "colorWeight": "500",
"colorPalette": "magenta", "colorPalette": "magenta",
...@@ -166,6 +177,7 @@ ...@@ -166,6 +177,7 @@
}, },
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "41", "id": "41",
"colorWeight": "600", "colorWeight": "600",
"colorPalette": "blue", "colorPalette": "blue",
...@@ -183,6 +195,7 @@ ...@@ -183,6 +195,7 @@
} }
}, },
{ {
"__typename": "IncidentManagementOncallRotation",
"id": "gid://gitlab/IncidentManagement::OncallRotation/5", "id": "gid://gitlab/IncidentManagement::OncallRotation/5",
"name": "Rotation 247", "name": "Rotation 247",
"startsAt": "2021-01-06T10:04:56.333Z", "startsAt": "2021-01-06T10:04:56.333Z",
...@@ -196,6 +209,7 @@ ...@@ -196,6 +209,7 @@
"participants": { "participants": {
"nodes": [ "nodes": [
{ {
"__typename": "OncallParticipantType",
"id": "51", "id": "51",
"user": { "user": {
"id": "gid://gitlab/IncidentManagement::OncallParticipant/51", "id": "gid://gitlab/IncidentManagement::OncallParticipant/51",
...@@ -212,6 +226,7 @@ ...@@ -212,6 +226,7 @@
"nodes": [ "nodes": [
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "43", "id": "43",
"colorWeight": "600", "colorWeight": "600",
"colorPalette": "orange", "colorPalette": "orange",
...@@ -227,6 +242,7 @@ ...@@ -227,6 +242,7 @@
}, },
{ {
"participant": { "participant": {
"__typename": "OncallParticipantType",
"id": "44", "id": "44",
"colorWeight": "600", "colorWeight": "600",
"colorPalette": "aqua", "colorPalette": "aqua",
......
...@@ -37,7 +37,6 @@ const createComponent = async (mockData = {}) => { ...@@ -37,7 +37,6 @@ const createComponent = async (mockData = {}) => {
downloadCode = userPermissionsMock.downloadCode, downloadCode = userPermissionsMock.downloadCode,
createMergeRequestIn = userPermissionsMock.createMergeRequestIn, createMergeRequestIn = userPermissionsMock.createMergeRequestIn,
isBinary, isBinary,
inject = {},
path = propsMock.projectPath, path = propsMock.projectPath,
} = mockData; } = mockData;
...@@ -66,7 +65,7 @@ const createComponent = async (mockData = {}) => { ...@@ -66,7 +65,7 @@ const createComponent = async (mockData = {}) => {
path, path,
}, },
mixins: [{ data: () => ({ ref: refMock }) }], mixins: [{ data: () => ({ ref: refMock }) }],
provide: { ...inject }, provide: { targetBranch: 'test', originalBranch: 'test' },
}); });
await waitForPromises(); await waitForPromises();
......
import { GlPagination, GlBadge } from '@gitlab/ui'; import { GlPagination, GlBadge } from '@gitlab/ui';
import { defaultDataIdFromObject } from '@apollo/client/core';
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
......
...@@ -105,6 +105,7 @@ export const getCorpusesQueryResponse = { ...@@ -105,6 +105,7 @@ export const getCorpusesQueryResponse = {
corpuses: { corpuses: {
nodes: corpuses, nodes: corpuses,
pageInfo: { pageInfo: {
__typename: 'PageInfo',
hasNextPage: true, hasNextPage: true,
hasPreviousPage: true, hasPreviousPage: true,
startCursor: 'start-cursor', startCursor: 'start-cursor',
......
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
} from 'ee/security_configuration/dast_site_validation/constants'; } from 'ee/security_configuration/dast_site_validation/constants';
import dastSiteValidationsQuery from 'ee/security_configuration/dast_site_validation/graphql/dast_site_validations.query.graphql'; import dastSiteValidationsQuery from 'ee/security_configuration/dast_site_validation/graphql/dast_site_validations.query.graphql';
import createApolloProvider from 'helpers/mock_apollo_helper'; import createApolloProvider from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import * as responses from '../mocks/apollo_mock'; import * as responses from '../mocks/apollo_mock';
import { siteProfiles } from '../mocks/mock_data'; import { siteProfiles } from '../mocks/mock_data';
...@@ -201,7 +202,9 @@ describe('EE - DastSiteProfileList', () => { ...@@ -201,7 +202,9 @@ describe('EE - DastSiteProfileList', () => {
}); });
}); });
it('fetches validation statuses for all profiles that are being validated and updates the cache', () => { it('fetches validation statuses for all profiles that are being validated and updates the cache', async () => {
await waitForPromises();
expect(requestHandlers.dastSiteValidations).toHaveBeenCalledWith({ expect(requestHandlers.dastSiteValidations).toHaveBeenCalledWith({
fullPath: defaultProps.fullPath, fullPath: defaultProps.fullPath,
urls: urlsPendingValidation, urls: urlsPendingValidation,
...@@ -215,7 +218,9 @@ describe('EE - DastSiteProfileList', () => { ...@@ -215,7 +218,9 @@ describe('EE - DastSiteProfileList', () => {
${2} | ${inProgressValidation.normalizedTargetUrl} | ${DAST_SITE_VALIDATION_STATUS.PASSED} ${2} | ${inProgressValidation.normalizedTargetUrl} | ${DAST_SITE_VALIDATION_STATUS.PASSED}
`( `(
'in the local cache, profile with normalized URL $normalizedTargetUrl has its status set to $status', 'in the local cache, profile with normalized URL $normalizedTargetUrl has its status set to $status',
({ nthCall, normalizedTargetUrl, status }) => { async ({ nthCall, normalizedTargetUrl, status }) => {
await waitForPromises();
expect(updateSiteProfilesStatuses).toHaveBeenNthCalledWith(nthCall, { expect(updateSiteProfilesStatuses).toHaveBeenNthCalledWith(nthCall, {
fullPath: defaultProps.fullPath, fullPath: defaultProps.fullPath,
normalizedTargetUrl, normalizedTargetUrl,
......
import gql from 'graphql-tag'; import { gql } from '@apollo/client/core';
import { import {
appendToPreviousResult, appendToPreviousResult,
removeProfile, removeProfile,
......
...@@ -230,6 +230,7 @@ export const pipelineSecurityReportSummaryWithErrors = merge({}, pipelineSecurit ...@@ -230,6 +230,7 @@ export const pipelineSecurityReportSummaryWithErrors = merge({}, pipelineSecurit
id: 'pipeline-1', id: 'pipeline-1',
securityReportSummary: { securityReportSummary: {
dast: { dast: {
__typename: 'SecurityReportSummarySection',
scans: { scans: {
nodes: scansWithErrors, nodes: scansWithErrors,
}, },
......
...@@ -116,8 +116,8 @@ describe('Pipeline findings', () => { ...@@ -116,8 +116,8 @@ describe('Pipeline findings', () => {
beforeEach(async () => { beforeEach(async () => {
createWrapperWithApollo( createWrapperWithApollo(
jest.fn().mockResolvedValue(mockPipelineFindingsResponse({ hasNextPage: true })), jest.fn().mockResolvedValue(mockPipelineFindingsResponse({ hasNextPage: true })),
await waitForPromises(),
); );
await waitForPromises();
}); });
it('shows the intersection loader', () => { it('shows the intersection loader', () => {
......
...@@ -4,10 +4,10 @@ import Vue, { nextTick } from 'vue'; ...@@ -4,10 +4,10 @@ import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import VulnerabilityCounts from 'ee/security_dashboard/components/shared/vulnerability_report/vulnerability_counts.vue'; import VulnerabilityCounts from 'ee/security_dashboard/components/shared/vulnerability_report/vulnerability_counts.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { DASHBOARD_TYPES, SEVERITY_LEVELS } from 'ee/security_dashboard/store/constants'; import { DASHBOARD_TYPES, SEVERITY_LEVELS } from 'ee/security_dashboard/store/constants';
import createFlash from '~/flash'; import createFlash from '~/flash';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
import countsQuery from 'ee/security_dashboard/graphql/queries/vulnerability_severities_count.query.graphql'; import countsQuery from 'ee/security_dashboard/graphql/queries/vulnerability_severities_count.query.graphql';
import { SEVERITIES } from '~/vulnerabilities/constants'; import { SEVERITIES } from '~/vulnerabilities/constants';
...@@ -26,7 +26,7 @@ const getCountsRequestHandler = ({ ...@@ -26,7 +26,7 @@ const getCountsRequestHandler = ({
data: { data: {
[dashboardType]: { [dashboardType]: {
id: '1', id: '1',
vulnerabilitySeveritiesCount: data, vulnerabilitySeveritiesCount: { __typename: 'VulnerabilitySeveritiesCount', ...data },
}, },
}, },
}); });
...@@ -92,7 +92,7 @@ describe('Vulnerability counts component', () => { ...@@ -92,7 +92,7 @@ describe('Vulnerability counts component', () => {
createWrapper({ countsHandler }); createWrapper({ countsHandler });
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledTimes(2); expect(createFlash).toHaveBeenCalled();
}); });
it.each([DASHBOARD_TYPES.PROJECT, DASHBOARD_TYPES.GROUP, DASHBOARD_TYPES.INSTANCE])( it.each([DASHBOARD_TYPES.PROJECT, DASHBOARD_TYPES.GROUP, DASHBOARD_TYPES.INSTANCE])(
......
...@@ -113,7 +113,7 @@ describe('Vulnerability list GraphQL component', () => { ...@@ -113,7 +113,7 @@ describe('Vulnerability list GraphQL component', () => {
createWrapper({ vulnerabilitiesHandler }); createWrapper({ vulnerabilitiesHandler });
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledTimes(2); expect(createFlash).toHaveBeenCalled();
}); });
}); });
......
export const mockProjectsWithSeverityCounts = () => [ export const mockProjectsWithSeverityCounts = () => [
{ {
__typename: 'Project',
id: 'gid://gitlab/Project/1', id: 'gid://gitlab/Project/1',
name: 'Gitlab Test', name: 'Gitlab Test',
nameWithNamespace: 'Gitlab Org / Gitlab Test', nameWithNamespace: 'Gitlab Org / Gitlab Test',
...@@ -8,6 +9,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -8,6 +9,7 @@ export const mockProjectsWithSeverityCounts = () => [
avatarUrl: null, avatarUrl: null,
path: 'gitlab-test', path: 'gitlab-test',
vulnerabilitySeveritiesCount: { vulnerabilitySeveritiesCount: {
__typename: 'VulnerabilitySeveritiesCount',
critical: 2, critical: 2,
high: 0, high: 0,
info: 4, info: 4,
...@@ -17,6 +19,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -17,6 +19,7 @@ export const mockProjectsWithSeverityCounts = () => [
}, },
}, },
{ {
__typename: 'Project',
id: 'gid://gitlab/Project/2', id: 'gid://gitlab/Project/2',
name: 'Gitlab Shell', name: 'Gitlab Shell',
nameWithNamespace: 'Gitlab Org / Gitlab Shell', nameWithNamespace: 'Gitlab Org / Gitlab Shell',
...@@ -25,6 +28,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -25,6 +28,7 @@ export const mockProjectsWithSeverityCounts = () => [
avatarUrl: null, avatarUrl: null,
path: 'gitlab-shell', path: 'gitlab-shell',
vulnerabilitySeveritiesCount: { vulnerabilitySeveritiesCount: {
__typename: 'VulnerabilitySeveritiesCount',
critical: 0, critical: 0,
high: 2, high: 2,
info: 2, info: 2,
...@@ -34,6 +38,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -34,6 +38,7 @@ export const mockProjectsWithSeverityCounts = () => [
}, },
}, },
{ {
__typename: 'Project',
id: 'gid://gitlab/Project/4', id: 'gid://gitlab/Project/4',
name: 'Gitlab Perfectly Secure', name: 'Gitlab Perfectly Secure',
nameWithNamespace: 'Gitlab Org / Perfectly Secure', nameWithNamespace: 'Gitlab Org / Perfectly Secure',
...@@ -42,6 +47,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -42,6 +47,7 @@ export const mockProjectsWithSeverityCounts = () => [
avatarUrl: null, avatarUrl: null,
path: 'gitlab-perfectly-secure', path: 'gitlab-perfectly-secure',
vulnerabilitySeveritiesCount: { vulnerabilitySeveritiesCount: {
__typename: 'VulnerabilitySeveritiesCount',
critical: 0, critical: 0,
high: 0, high: 0,
info: 0, info: 0,
...@@ -51,6 +57,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -51,6 +57,7 @@ export const mockProjectsWithSeverityCounts = () => [
}, },
}, },
{ {
__typename: 'Project',
id: 'gid://gitlab/Project/5', id: 'gid://gitlab/Project/5',
name: 'Gitlab Perfectly Secure 2 ', name: 'Gitlab Perfectly Secure 2 ',
nameWithNamespace: 'Gitlab Org / Perfectly Secure 2', nameWithNamespace: 'Gitlab Org / Perfectly Secure 2',
...@@ -59,6 +66,7 @@ export const mockProjectsWithSeverityCounts = () => [ ...@@ -59,6 +66,7 @@ export const mockProjectsWithSeverityCounts = () => [
avatarUrl: null, avatarUrl: null,
path: 'gitlab-perfectly-secure-2', path: 'gitlab-perfectly-secure-2',
vulnerabilitySeveritiesCount: { vulnerabilitySeveritiesCount: {
__typename: 'VulnerabilitySeveritiesCount',
critical: 0, critical: 0,
high: 0, high: 0,
info: 0, info: 0,
...@@ -222,12 +230,7 @@ export const mockVulnerableProjectsGroup = () => ({ ...@@ -222,12 +230,7 @@ export const mockVulnerableProjectsGroup = () => ({
group: { group: {
id: 'group-1', id: 'group-1',
projects: { projects: {
nodes: [ nodes: [{ __typename: 'Project', id: 'gid://gitlab/Project/2', name: 'Gitlab Shell' }],
{
id: 'gid://gitlab/Project/2',
name: 'Gitlab Shell',
},
],
}, },
}, },
}, },
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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