diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-14 15:14:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-02-14 15:14:02 +0300 |
commit | 2896c7471aa75a7842fe6318a122f83ca6c211bb (patch) | |
tree | 5da5605d7632d654cf70a3bbfbf32dc12178fa04 /app | |
parent | 41482e5dce6765dc2ec019da1c652061fc8d8f83 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
69 files changed, 258 insertions, 220 deletions
diff --git a/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql b/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql index 09278e1776a..cdc8a952ead 100644 --- a/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql +++ b/app/assets/javascripts/access_tokens/graphql/queries/get_projects.query.graphql @@ -22,6 +22,7 @@ query accessTokensGetProjects( avatarUrl } pageInfo { + __typename ...PageInfo } } diff --git a/app/assets/javascripts/actioncable_link.js b/app/assets/javascripts/actioncable_link.js index 895a34ba157..cf53d9e21b4 100644 --- a/app/assets/javascripts/actioncable_link.js +++ b/app/assets/javascripts/actioncable_link.js @@ -1,4 +1,4 @@ -import { ApolloLink, Observable } from 'apollo-link'; +import { ApolloLink, Observable } from '@apollo/client/core'; import { print } from 'graphql'; import cable from '~/actioncable_consumer'; import { uuids } from '~/lib/utils/uuids'; diff --git a/app/assets/javascripts/alert_management/list.js b/app/assets/javascripts/alert_management/list.js index b23f8a8eba4..42cbeef56bf 100644 --- a/app/assets/javascripts/alert_management/list.js +++ b/app/assets/javascripts/alert_management/list.js @@ -1,4 +1,4 @@ -import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; +import { defaultDataIdFromObject } from '@apollo/client/core'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; diff --git a/app/assets/javascripts/alerts_settings/graphql.js b/app/assets/javascripts/alerts_settings/graphql.js index b64e2e3eefa..36a98145457 100644 --- a/app/assets/javascripts/alerts_settings/graphql.js +++ b/app/assets/javascripts/alerts_settings/graphql.js @@ -1,15 +1,9 @@ -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import produce from 'immer'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import introspectionQueryResultData from './graphql/fragmentTypes.json'; import getCurrentIntegrationQuery from './graphql/queries/get_current_integration.query.graphql'; -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); - Vue.use(VueApollo); const resolvers = { @@ -55,9 +49,5 @@ const resolvers = { }; export default new VueApollo({ - defaultClient: createDefaultClient(resolvers, { - cacheConfig: { - fragmentMatcher, - }, - }), + defaultClient: createDefaultClient(resolvers), }); diff --git a/app/assets/javascripts/alerts_settings/graphql/fragmentTypes.json b/app/assets/javascripts/alerts_settings/graphql/fragmentTypes.json deleted file mode 100644 index 07dfc43aa6c..00000000000 --- a/app/assets/javascripts/alerts_settings/graphql/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"UNION","name":"AlertManagementIntegration","possibleTypes":[{"name":"AlertManagementHttpIntegration"},{"name":"AlertManagementPrometheusIntegration"}]}]}} diff --git a/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql b/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql index 3cd3f2d92f8..ac9304391f9 100644 --- a/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql +++ b/app/assets/javascripts/alerts_settings/graphql/queries/get_integrations.query.graphql @@ -5,6 +5,7 @@ query getIntegrations($projectPath: ID!) { id alertManagementIntegrations { nodes { + __typename ...IntegrationItem } } diff --git a/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql b/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql index 2bde5973600..b353bcdfd0e 100644 --- a/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql +++ b/app/assets/javascripts/analytics/usage_trends/graphql/fragments/count.fragment.graphql @@ -1,4 +1,5 @@ fragment Count on UsageTrendsMeasurement { + __typename count recordedAt } diff --git a/app/assets/javascripts/boards/graphql.js b/app/assets/javascripts/boards/graphql.js index 64938cb42ed..95863d4d5ac 100644 --- a/app/assets/javascripts/boards/graphql.js +++ b/app/assets/javascripts/boards/graphql.js @@ -1,10 +1,5 @@ -import { IntrospectionFragmentMatcher, defaultDataIdFromObject } from 'apollo-cache-inmemory'; +import { defaultDataIdFromObject } from '@apollo/client/core'; import createDefaultClient from '~/lib/graphql'; -import introspectionQueryResultData from '~/sidebar/fragmentTypes.json'; - -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); export const gqlClient = createDefaultClient( {}, @@ -14,8 +9,6 @@ export const gqlClient = createDefaultClient( // eslint-disable-next-line no-underscore-dangle return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object); }, - - fragmentMatcher, }, }, ); diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js index ed32579a9c3..26dd8b99f98 100644 --- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js +++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js @@ -1,27 +1,14 @@ -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue'; import store from '~/boards/stores'; import createDefaultClient from '~/lib/graphql'; import { parseBoolean } from '~/lib/utils/common_utils'; -import introspectionQueryResultData from '~/sidebar/fragmentTypes.json'; Vue.use(VueApollo); -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); - const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient( - {}, - { - cacheConfig: { - fragmentMatcher, - }, - }, - ), + defaultClient: createDefaultClient(), }); export default (params = {}) => { diff --git a/app/assets/javascripts/captcha/apollo_captcha_link.js b/app/assets/javascripts/captcha/apollo_captcha_link.js index e49abc10b29..d63ffaf5f1a 100644 --- a/app/assets/javascripts/captcha/apollo_captcha_link.js +++ b/app/assets/javascripts/captcha/apollo_captcha_link.js @@ -1,4 +1,4 @@ -import { ApolloLink, Observable } from 'apollo-link'; +import { ApolloLink, Observable } from '@apollo/client/core'; export const apolloCaptchaLink = new ApolloLink((operation, forward) => forward(operation).flatMap((result) => { diff --git a/app/assets/javascripts/clusters/agents/graphql/provider.js b/app/assets/javascripts/clusters/agents/graphql/provider.js index 8b068fa1eee..9153c5252b3 100644 --- a/app/assets/javascripts/clusters/agents/graphql/provider.js +++ b/app/assets/javascripts/clusters/agents/graphql/provider.js @@ -1,25 +1,10 @@ -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import { vulnerabilityLocationTypes } from '~/graphql_shared/fragment_types/vulnerability_location_types'; 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: vulnerabilityLocationTypes, -}); - -const defaultClient = createDefaultClient( - {}, - { - cacheConfig: { - fragmentMatcher, - }, - }, -); +const defaultClient = createDefaultClient(); export default new VueApollo({ defaultClient, diff --git a/app/assets/javascripts/design_management/graphql.js b/app/assets/javascripts/design_management/graphql.js index 5cf32cb7fe3..8c44c5a5d0a 100644 --- a/app/assets/javascripts/design_management/graphql.js +++ b/app/assets/javascripts/design_management/graphql.js @@ -1,11 +1,10 @@ -import { defaultDataIdFromObject, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; +import { defaultDataIdFromObject } from '@apollo/client/core'; import produce from 'immer'; import { uniqueId } from 'lodash'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; import axios from '~/lib/utils/axios_utils'; -import introspectionQueryResultData from './graphql/fragmentTypes.json'; import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql'; import getDesignQuery from './graphql/queries/get_design.query.graphql'; import typeDefs from './graphql/typedefs.graphql'; @@ -13,10 +12,6 @@ import { addPendingTodoToStore } from './utils/cache_update'; import { extractTodoIdFromDeletePath, createPendingTodo } from './utils/design_management_utils'; import { CREATE_DESIGN_TODO_EXISTS_ERROR } from './utils/error_messages'; -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); - Vue.use(VueApollo); const resolvers = { @@ -85,7 +80,6 @@ const defaultClient = createDefaultClient( } return defaultDataIdFromObject(object); }, - fragmentMatcher, }, typeDefs, }, diff --git a/app/assets/javascripts/design_management/graphql/fragmentTypes.json b/app/assets/javascripts/design_management/graphql/fragmentTypes.json deleted file mode 100644 index 0953231ea4c..00000000000 --- a/app/assets/javascripts/design_management/graphql/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]},{"kind":"UNION","name":"NoteableType","possibleTypes":[{"name":"Design"},{"name":"Issue"},{"name":"MergeRequest"}]}]}} diff --git a/app/assets/javascripts/graphql_shared/fragment_types/vulnerability_location_types.js b/app/assets/javascripts/graphql_shared/fragment_types/vulnerability_location_types.js deleted file mode 100644 index 30888e20a46..00000000000 --- a/app/assets/javascripts/graphql_shared/fragment_types/vulnerability_location_types.js +++ /dev/null @@ -1,17 +0,0 @@ -export const vulnerabilityLocationTypes = { - __schema: { - types: [ - { - kind: 'UNION', - name: 'VulnerabilityLocation', - possibleTypes: [ - { name: 'VulnerabilityLocationContainerScanning' }, - { name: 'VulnerabilityLocationDast' }, - { name: 'VulnerabilityLocationDependencyScanning' }, - { name: 'VulnerabilityLocationSast' }, - { name: 'VulnerabilityLocationSecretDetection' }, - ], - }, - ], - }, -}; diff --git a/app/assets/javascripts/graphql_shared/possibleTypes.json b/app/assets/javascripts/graphql_shared/possibleTypes.json new file mode 100644 index 00000000000..9a24d2a3afc --- /dev/null +++ b/app/assets/javascripts/graphql_shared/possibleTypes.json @@ -0,0 +1 @@ +{"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"]} diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index a2bb6c3cd77..3866a7b3305 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -181,6 +181,9 @@ export default { return data[this.namespace]?.issues.nodes ?? []; }, result({ data }) { + if (!data) { + return; + } this.pageInfo = data[this.namespace]?.issues.pageInfo ?? {}; this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery(); }, diff --git a/app/assets/javascripts/issues/list/queries/issue.fragment.graphql b/app/assets/javascripts/issues/list/queries/issue.fragment.graphql index 07dae3fd756..430d494deab 100644 --- a/app/assets/javascripts/issues/list/queries/issue.fragment.graphql +++ b/app/assets/javascripts/issues/list/queries/issue.fragment.graphql @@ -1,4 +1,5 @@ fragment IssueFragment on Issue { + __typename id iid closedAt @@ -18,6 +19,7 @@ fragment IssueFragment on Issue { webUrl assignees { nodes { + __typename id avatarUrl name @@ -26,6 +28,7 @@ fragment IssueFragment on Issue { } } author { + __typename id avatarUrl name diff --git a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue index c786d35ac68..81f42c1e293 100644 --- a/app/assets/javascripts/jobs/components/table/jobs_table_app.vue +++ b/app/assets/javascripts/jobs/components/table/jobs_table_app.vue @@ -51,7 +51,9 @@ export default { }, data() { return { - jobs: {}, + jobs: { + list: [], + }, hasError: false, isAlertDismissed: false, scope: null, diff --git a/app/assets/javascripts/lib/apollo/instrumentation_link.js b/app/assets/javascripts/lib/apollo/instrumentation_link.js index 2ab364557b8..bbe16d260e7 100644 --- a/app/assets/javascripts/lib/apollo/instrumentation_link.js +++ b/app/assets/javascripts/lib/apollo/instrumentation_link.js @@ -1,4 +1,4 @@ -import { ApolloLink } from 'apollo-link'; +import { ApolloLink } from '@apollo/client/core'; import { memoize } from 'lodash'; export const FEATURE_CATEGORY_HEADER = 'x-gitlab-feature-category'; diff --git a/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js b/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js index 9b7901685b6..b2a86ac257b 100644 --- a/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js +++ b/app/assets/javascripts/lib/apollo/suppress_network_errors_during_navigation_link.js @@ -1,5 +1,5 @@ -import { Observable } from 'apollo-link'; -import { onError } from 'apollo-link-error'; +import { Observable } from '@apollo/client/core'; +import { onError } from '@apollo/client/link/error'; import { isNavigatingAway } from '~/lib/utils/is_navigating_away'; /** diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js index df2e85afe24..0fb7bf52562 100644 --- a/app/assets/javascripts/lib/graphql.js +++ b/app/assets/javascripts/lib/graphql.js @@ -1,11 +1,9 @@ -import { InMemoryCache } from 'apollo-cache-inmemory'; -import { ApolloClient } from 'apollo-client'; -import { ApolloLink } from 'apollo-link'; -import { BatchHttpLink } from 'apollo-link-batch-http'; -import { HttpLink } from 'apollo-link-http'; +import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client/core'; +import { BatchHttpLink } from '@apollo/client/link/batch-http'; import { createUploadLink } from 'apollo-upload-client'; import ActionCableLink from '~/actioncable_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 csrf from '~/lib/utils/csrf'; import { objectToQuery, queryToObject } from '~/lib/utils/url_utility'; @@ -21,6 +19,33 @@ export const fetchPolicies = { 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) => { /* eslint-disable-next-line no-unused-vars */ const [_, params] = url.split(path); @@ -46,6 +71,30 @@ export const stripWhitespaceFromQuery = (url, path) => { 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 = {}) => { const { baseUrl, @@ -56,6 +105,7 @@ export default (resolvers = {}, config = {}) => { path = '/api/graphql', useGet = false, } = config; + let ac = null; let uri = `${gon.relative_url_root || ''}${path}`; if (baseUrl) { @@ -75,16 +125,6 @@ export default (resolvers = {}, config = {}) => { 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 eTag polling, but Apollo Client adds excessive whitespace, which causes the @@ -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( hasSubscriptionOperation, new ActionCableLink(), @@ -155,19 +211,23 @@ export default (resolvers = {}, config = {}) => { ), ); - return new ApolloClient({ + ac = new ApolloClient({ typeDefs, link: appLink, cache: new InMemoryCache({ + typePolicies, + possibleTypes, ...cacheConfig, - freezeResults: true, }), resolvers, - assumeImmutableResults: true, defaultOptions: { query: { fetchPolicy, }, }, }); + + acs.push(ac); + + return ac; }; diff --git a/app/assets/javascripts/lib/utils/apollo_startup_js_link.js b/app/assets/javascripts/lib/utils/apollo_startup_js_link.js index 014823f3831..f240226e991 100644 --- a/app/assets/javascripts/lib/utils/apollo_startup_js_link.js +++ b/app/assets/javascripts/lib/utils/apollo_startup_js_link.js @@ -1,4 +1,4 @@ -import { ApolloLink, Observable } from 'apollo-link'; +import { ApolloLink, Observable } from '@apollo/client/core'; import { parse } from 'graphql'; import { isEqual, pickBy } from 'lodash'; diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue index ad3a3596991..e2acebf39d6 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue @@ -96,6 +96,9 @@ export default { return data[this.graphqlResource]?.containerRepositories.nodes; }, result({ data }) { + if (!data) { + return; + } this.pageInfo = data[this.graphqlResource]?.containerRepositories?.pageInfo; this.containerRepositoriesCount = data[this.graphqlResource]?.containerRepositoriesCount; }, diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragmentTypes.json b/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragmentTypes.json deleted file mode 100644 index c61a653d10b..00000000000 --- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/fragmentTypes.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "__schema": { - "types": [ - { - "kind": "UNION", - "name": "PackageMetadata", - "possibleTypes": [ - { "name": "ComposerMetadata" }, - { "name": "ConanMetadata" }, - { "name": "MavenMetadata" }, - { "name": "NugetMetadata" }, - { "name": "PypiMetadata" } - ] - } - ] - } -} diff --git a/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js b/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js index 21d6fbc9e1f..56f95fa2c1f 100644 --- a/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js +++ b/app/assets/javascripts/packages_and_registries/package_registry/graphql/index.js @@ -1,22 +1,9 @@ -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; -import introspectionQueryResultData from './fragmentTypes.json'; - -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); Vue.use(VueApollo); export const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient( - {}, - { - cacheConfig: { - fragmentMatcher, - }, - }, - ), + defaultClient: createDefaultClient(), }); diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue index 42c40cda601..d50945e0be2 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue +++ b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue @@ -2,7 +2,8 @@ import { GlProgressBar, GlSprintf, GlAlert } from '@gitlab/ui'; import eventHub from '~/invite_members/event_hub'; import { s__ } from '~/locale'; -import { ACTION_LABELS, ACTION_SECTIONS } from '../constants'; +import { getCookie, removeCookie, parseBoolean } from '~/lib/utils/common_utils'; +import { ACTION_LABELS, ACTION_SECTIONS, INVITE_MODAL_OPEN_COOKIE } from '../constants'; import LearnGitlabSectionCard from './learn_gitlab_section_card.vue'; export default { @@ -26,7 +27,7 @@ export default { required: true, type: Object, }, - inviteMembersOpen: { + inviteMembers: { type: Boolean, required: false, default: false, @@ -53,7 +54,7 @@ export default { }, }, mounted() { - if (this.inviteMembersOpen) { + if (this.inviteMembers && this.getCookieForInviteMembers()) { this.openInviteMembersModal('celebrate'); } @@ -63,6 +64,13 @@ export default { eventHub.$off('showSuccessfulInvitationsAlert', this.handleShowSuccessfulInvitationsAlert); }, methods: { + getCookieForInviteMembers() { + const value = parseBoolean(getCookie(INVITE_MODAL_OPEN_COOKIE)); + + removeCookie(INVITE_MODAL_OPEN_COOKIE); + + return value; + }, openInviteMembersModal(mode) { eventHub.$emit('openModal', { mode, inviteeType: 'members', source: 'learn-gitlab' }); }, diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js index 8a88ad299b4..880cf699e5e 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js +++ b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js @@ -95,3 +95,5 @@ export const ACTION_SECTIONS = { ), }, }; + +export const INVITE_MODAL_OPEN_COOKIE = 'confetti_post_signup'; diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js index 1f91cc46946..c62cab1a425 100644 --- a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js +++ b/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; -import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils'; import LearnGitlab from '../components/learn_gitlab.vue'; function initLearnGitlab() { @@ -13,13 +13,13 @@ function initLearnGitlab() { const actions = convertObjectPropsToCamelCase(JSON.parse(el.dataset.actions)); const sections = convertObjectPropsToCamelCase(JSON.parse(el.dataset.sections)); const project = convertObjectPropsToCamelCase(JSON.parse(el.dataset.project)); - const { inviteMembersOpen } = el.dataset; + const { inviteMembers } = el.dataset; return new Vue({ el, render(createElement) { return createElement(LearnGitlab, { - props: { actions, sections, project, inviteMembersOpen }, + props: { actions, sections, project, inviteMembers: parseBoolean(inviteMembers) }, }); }, }); diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue index 72b492a5877..4b9c98135ec 100644 --- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue +++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_status.vue @@ -49,7 +49,7 @@ export default { pipelineEtag: { query: getPipelineEtag, update(data) { - return data.etags.pipeline; + return data.etags?.pipeline; }, }, pipeline: { diff --git a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue index 604f87a0cda..1da50c55a68 100644 --- a/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue +++ b/app/assets/javascripts/pipeline_editor/pipeline_editor_app.vue @@ -196,7 +196,7 @@ export default { currentBranch: { query: getCurrentBranch, update(data) { - return data.workBranches.current.name; + return data.workBranches?.current?.name; }, }, starterTemplate: { @@ -214,7 +214,7 @@ export default { return data.project?.ciTemplate?.content || ''; }, result({ data }) { - this.updateCiConfig(data.project?.ciTemplate?.content || ''); + this.updateCiConfig(data?.project?.ciTemplate?.content || ''); }, error() { this.reportFailure(LOAD_FAILURE_UNKNOWN); diff --git a/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue b/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue index 6f35cdac641..99fb5c146ba 100644 --- a/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue +++ b/app/assets/javascripts/pipelines/components/jobs/jobs_app.vue @@ -36,6 +36,9 @@ export default { return data.project?.pipeline?.jobs?.nodes || []; }, result({ data }) { + if (!data) { + return; + } this.jobsPageInfo = data.project?.pipeline?.jobs?.pageInfo || {}; }, error() { diff --git a/app/assets/javascripts/pipelines/graphql/fragmentTypes.json b/app/assets/javascripts/pipelines/graphql/fragmentTypes.json deleted file mode 100644 index 4601b74b5c1..00000000000 --- a/app/assets/javascripts/pipelines/graphql/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"UNION","name":"JobNeedUnion","possibleTypes":[{"name":"CiBuildNeed"},{"name":"CiJob"}]}]}}
\ No newline at end of file diff --git a/app/assets/javascripts/pipelines/pipeline_shared_client.js b/app/assets/javascripts/pipelines/pipeline_shared_client.js index 84276588d6a..c3be487caae 100644 --- a/app/assets/javascripts/pipelines/pipeline_shared_client.js +++ b/app/assets/javascripts/pipelines/pipeline_shared_client.js @@ -1,19 +1,10 @@ import VueApollo from 'vue-apollo'; -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import createDefaultClient from '~/lib/graphql'; -import introspectionQueryResultData from './graphql/fragmentTypes.json'; - -export const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); export const apolloProvider = new VueApollo({ defaultClient: createDefaultClient( {}, { - cacheConfig: { - fragmentMatcher, - }, useGet: true, }, ), diff --git a/app/assets/javascripts/related_issues/components/add_issuable_form.vue b/app/assets/javascripts/related_issues/components/add_issuable_form.vue index f936c03c5d3..9ee2e7a4ffd 100644 --- a/app/assets/javascripts/related_issues/components/add_issuable_form.vue +++ b/app/assets/javascripts/related_issues/components/add_issuable_form.vue @@ -9,6 +9,8 @@ import { linkedIssueTypesMap, addRelatedIssueErrorMap, addRelatedItemErrorMap, + issuablesFormCategoryHeaderTextMap, + issuablesFormInputTextMap, } from '../constants'; import RelatedIssuableInput from './related_issuable_input.vue'; @@ -134,6 +136,12 @@ export default { epics: mergeUrlParams({ confidential_only: true }, this.autoCompleteSources.epics), }; }, + issuableCategoryHeaderText() { + return issuablesFormCategoryHeaderTextMap[this.issuableType]; + }, + issuableInputText() { + return issuablesFormInputTextMap[this.issuableType]; + }, }, methods: { onPendingIssuableRemoveRequest(params) { @@ -162,7 +170,7 @@ export default { <form @submit.prevent="onFormSubmit"> <template v-if="showCategorizedIssues"> <gl-form-group - :label="__('The current issue')" + :label="issuableCategoryHeaderText" label-for="linked-issue-type-radio" label-class="label-bold" class="mb-2" @@ -175,7 +183,7 @@ export default { /> </gl-form-group> <p class="bold"> - {{ __('the following issue(s)') }} + {{ issuableInputText }} </p> </template> <related-issuable-input diff --git a/app/assets/javascripts/related_issues/components/related_issues_block.vue b/app/assets/javascripts/related_issues/components/related_issues_block.vue index 94535e1b8c9..bc97fab9ad2 100644 --- a/app/assets/javascripts/related_issues/components/related_issues_block.vue +++ b/app/assets/javascripts/related_issues/components/related_issues_block.vue @@ -5,6 +5,9 @@ import { issuableQaClassMap, linkedIssueTypesMap, linkedIssueTypesTextMap, + issuablesBlockHeaderTextMap, + issuablesBlockHelpTextMap, + issuablesBlockAddButtonTextMap, } from '../constants'; import AddIssuableForm from './add_issuable_form.vue'; import RelatedIssuesList from './related_issues_list.vue'; @@ -105,6 +108,15 @@ export default { hasBody() { return this.isFormVisible || this.shouldShowTokenBody; }, + headerText() { + return issuablesBlockHeaderTextMap[this.issuableType]; + }, + helpLinkText() { + return issuablesBlockHelpTextMap[this.issuableType]; + }, + addIssuableButtonText() { + return issuablesBlockAddButtonTextMap[this.issuableType]; + }, badgeLabel() { return this.isFetching && this.relatedIssues.length === 0 ? '...' : this.relatedIssues.length; }, @@ -138,13 +150,14 @@ export default { href="#related-issues" aria-hidden="true" /> - <slot name="header-text">{{ __('Linked issues') }}</slot> + <slot name="header-text">{{ headerText }}</slot> <gl-link v-if="hasHelpPath" :href="helpPath" target="_blank" class="gl-display-flex gl-align-items-center gl-ml-2 gl-text-gray-500" - :aria-label="__('Read more about related issues')" + data-testid="help-link" + :aria-label="helpLinkText" > <gl-icon name="question" :size="12" /> </gl-link> @@ -160,7 +173,7 @@ export default { v-if="canAdmin" data-qa-selector="related_issues_plus_button" icon="plus" - :aria-label="__('Add a related issue')" + :aria-label="addIssuableButtonText" :class="qaClass" @click="$emit('toggleAddRelatedIssuesForm', $event)" /> diff --git a/app/assets/javascripts/related_issues/constants.js b/app/assets/javascripts/related_issues/constants.js index 89eae069a24..f911468d8f1 100644 --- a/app/assets/javascripts/related_issues/constants.js +++ b/app/assets/javascripts/related_issues/constants.js @@ -104,3 +104,28 @@ export const PathIdSeparator = { Epic: '&', Issue: '#', }; + +export const issuablesBlockHeaderTextMap = { + [issuableTypesMap.ISSUE]: __('Linked issues'), + [issuableTypesMap.EPIC]: __('Linked epics'), +}; + +export const issuablesBlockHelpTextMap = { + [issuableTypesMap.ISSUE]: __('Read more about related issues'), + [issuableTypesMap.EPIC]: __('Read more about related epics'), +}; + +export const issuablesBlockAddButtonTextMap = { + [issuableTypesMap.ISSUE]: __('Add a related issue'), + [issuableTypesMap.EPIC]: __('Add a related epic'), +}; + +export const issuablesFormCategoryHeaderTextMap = { + [issuableTypesMap.ISSUE]: __('The current issue'), + [issuableTypesMap.EPIC]: __('The current epic'), +}; + +export const issuablesFormInputTextMap = { + [issuableTypesMap.ISSUE]: __('the following issue(s)'), + [issuableTypesMap.EPIC]: __('the following epic(s)'), +}; diff --git a/app/assets/javascripts/related_issues/index.js b/app/assets/javascripts/related_issues/index.js index 0ee99df1455..bd9845e616b 100644 --- a/app/assets/javascripts/related_issues/index.js +++ b/app/assets/javascripts/related_issues/index.js @@ -8,6 +8,7 @@ export default function initRelatedIssues() { // eslint-disable-next-line no-new new Vue({ el: relatedIssuesRootElement, + name: 'RelatedIssuesApp', components: { relatedIssuesRoot: RelatedIssuesRoot, }, diff --git a/app/assets/javascripts/repository/fragmentTypes.json b/app/assets/javascripts/repository/fragmentTypes.json deleted file mode 100644 index 949ebca432b..00000000000 --- a/app/assets/javascripts/repository/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"INTERFACE","name":"Entry","possibleTypes":[{"name":"Blob"},{"name":"Submodule"},{"name":"TreeEntry"}]}]}} diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js index 96d712ce9b4..29aabe1b00f 100644 --- a/app/assets/javascripts/repository/graphql.js +++ b/app/assets/javascripts/repository/graphql.js @@ -1,19 +1,11 @@ -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; import axios from '~/lib/utils/axios_utils'; -import introspectionQueryResultData from './fragmentTypes.json'; import { fetchLogsTree } from './log_tree'; 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( { Query: { @@ -43,7 +35,6 @@ const defaultClient = createDefaultClient( }, { cacheConfig: { - fragmentMatcher, dataIdFromObject: (obj) => { /* eslint-disable @gitlab/require-i18n-strings */ // eslint-disable-next-line no-underscore-dangle diff --git a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql index f7bcd683718..986dd16b992 100644 --- a/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/get_group_runners.query.graphql @@ -28,10 +28,12 @@ query getGroupRunners( edges { webUrl node { + __typename ...RunnerNode } } pageInfo { + __typename ...PageInfo } } diff --git a/app/assets/javascripts/runner/graphql/get_runner.query.graphql b/app/assets/javascripts/runner/graphql/get_runner.query.graphql index 59c55eae060..f6ce8281c64 100644 --- a/app/assets/javascripts/runner/graphql/get_runner.query.graphql +++ b/app/assets/javascripts/runner/graphql/get_runner.query.graphql @@ -4,6 +4,7 @@ query getRunner($id: CiRunnerID!) { # We have an id in deeply nested fragment # eslint-disable-next-line @graphql-eslint/require-id-when-available runner(id: $id) { + __typename ...RunnerDetails } } diff --git a/app/assets/javascripts/runner/graphql/get_runners.query.graphql b/app/assets/javascripts/runner/graphql/get_runners.query.graphql index 05df399fa6a..ed03a8c34ae 100644 --- a/app/assets/javascripts/runner/graphql/get_runners.query.graphql +++ b/app/assets/javascripts/runner/graphql/get_runners.query.graphql @@ -29,6 +29,7 @@ query getRunners( editAdminUrl } pageInfo { + __typename ...PageInfo } } diff --git a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql b/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql index 4a771d779dc..fbdef817f2f 100644 --- a/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql +++ b/app/assets/javascripts/runner/graphql/runner_node.fragment.graphql @@ -1,4 +1,5 @@ fragment RunnerNode on CiRunner { + __typename id description runnerType diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue index 5b4dc20e9c8..18654b73ab3 100644 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue +++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue @@ -96,6 +96,9 @@ export default { return data.workspace?.issuable; }, result({ data }) { + if (!data) { + return; + } const issuable = data.workspace?.issuable; if (issuable) { this.selected = cloneDeep(issuable.assignees.nodes); diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue index dc0f2b54a7b..f234c5ea3c9 100644 --- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue +++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue @@ -66,6 +66,9 @@ export default { return data.workspace?.issuable?.confidential || false; }, result({ data }) { + if (!data) { + return; + } this.$emit('confidentialityUpdated', data.workspace?.issuable?.confidential); }, error() { diff --git a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue index 404bcc3122a..be7a89c2869 100644 --- a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue +++ b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue @@ -86,6 +86,9 @@ export default { return data.workspace?.issuable || {}; }, result({ data }) { + if (!data) { + return; + } this.$emit(`${this.dateType}Updated`, data.workspace?.issuable?.[this.dateType]); }, error() { diff --git a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue index 701833c4e95..7a10a9f3a4c 100644 --- a/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue +++ b/app/assets/javascripts/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue @@ -61,6 +61,9 @@ export default { return data.workspace?.issuable?.subscribed || false; }, result({ data }) { + if (!data) { + return; + } this.emailsDisabled = this.parentIsGroup ? data.workspace?.emailsDisabled : data.workspace?.issuable?.emailsDisabled; diff --git a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue index 8c56c3cbd57..eabba619af5 100644 --- a/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue +++ b/app/assets/javascripts/sidebar/components/todo_toggle/sidebar_todo_widget.vue @@ -59,6 +59,10 @@ export default { return data.workspace?.issuable?.currentUserTodos.nodes[0]?.id; }, result({ data }) { + if (!data) { + return; + } + const currentUserTodos = data.workspace?.issuable?.currentUserTodos?.nodes ?? []; this.todoId = currentUserTodos[0]?.id; this.$emit('todoUpdated', currentUserTodos.length > 0); diff --git a/app/assets/javascripts/sidebar/fragmentTypes.json b/app/assets/javascripts/sidebar/fragmentTypes.json deleted file mode 100644 index a1c68bba454..00000000000 --- a/app/assets/javascripts/sidebar/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"UNION","name":"Issuable","possibleTypes":[{"name":"Issue"},{"name":"MergeRequest"}]}, {"kind":"INTERFACE","name":"User","possibleTypes":[{"name":"UserCore"}]}]}} diff --git a/app/assets/javascripts/sidebar/graphql.js b/app/assets/javascripts/sidebar/graphql.js index c5d94cfa5e8..fc757922f09 100644 --- a/app/assets/javascripts/sidebar/graphql.js +++ b/app/assets/javascripts/sidebar/graphql.js @@ -1,14 +1,8 @@ -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import produce from 'immer'; import VueApollo from 'vue-apollo'; import getIssueStateQuery from '~/issues/show/queries/get_issue_state.query.graphql'; import { resolvers as workItemResolvers } from '~/work_items/graphql/resolvers'; import createDefaultClient from '~/lib/graphql'; -import introspectionQueryResultData from './fragmentTypes.json'; - -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); const resolvers = { ...workItemResolvers, @@ -24,11 +18,7 @@ const resolvers = { }, }; -export const defaultClient = createDefaultClient(resolvers, { - cacheConfig: { - fragmentMatcher, - }, -}); +export const defaultClient = createDefaultClient(resolvers); export const apolloProvider = new VueApollo({ defaultClient, diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js index 1b8cab0d51e..34261f3c4db 100644 --- a/app/assets/javascripts/terraform/index.js +++ b/app/assets/javascripts/terraform/index.js @@ -1,5 +1,5 @@ +import { defaultDataIdFromObject } from '@apollo/client/core'; import { GlToast } from '@gitlab/ui'; -import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; import createDefaultClient from '~/lib/graphql'; diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql index 0b8396b4461..25c44beaf18 100644 --- a/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql +++ b/app/assets/javascripts/vue_merge_request_widget/queries/get_state.query.graphql @@ -3,7 +3,6 @@ query getState($projectPath: ID!, $iid: String!) { id archived onlyAllowMergeIfPipelineSucceeds - mergeRequest(iid: $iid) { id autoMergeEnabled diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql index 2d79d35cf24..ad93a3a7371 100644 --- a/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql +++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/auto_merge_enabled.query.graphql @@ -4,6 +4,7 @@ query autoMergeEnabled($projectPath: ID!, $iid: String!) { project(fullPath: $projectPath) { id mergeRequest(iid: $iid) { + id ...autoMergeEnabled } } diff --git a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql index f713739f65a..556ecee254d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql +++ b/app/assets/javascripts/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql @@ -2,6 +2,7 @@ query readyToMerge($projectPath: ID!, $iid: String!) { project(fullPath: $projectPath) { + id ...ReadyToMerge } } diff --git a/app/assets/javascripts/vue_shared/alert_details/index.js b/app/assets/javascripts/vue_shared/alert_details/index.js index 9f1da9ae173..8a0de3b74bc 100644 --- a/app/assets/javascripts/vue_shared/alert_details/index.js +++ b/app/assets/javascripts/vue_shared/alert_details/index.js @@ -1,4 +1,4 @@ -import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; +import { defaultDataIdFromObject } from '@apollo/client/core'; import produce from 'immer'; import Vue from 'vue'; import VueApollo from 'vue-apollo'; diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue index 138284c9f6b..8008b85bbdb 100644 --- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue +++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue @@ -31,10 +31,6 @@ export default { type: Object, required: true, }, - enableLabelPermalinks: { - type: Boolean, - required: true, - }, labelFilterParam: { type: String, required: false, @@ -142,11 +138,8 @@ export default { return label.title || label.name; }, labelTarget(label) { - if (this.enableLabelPermalinks) { - const value = encodeURIComponent(this.labelTitle(label)); - return `?${this.labelFilterParam}[]=${value}`; - } - return '#'; + const value = encodeURIComponent(this.labelTitle(label)); + return `?${this.labelFilterParam}[]=${value}`; }, /** * This is needed as an independent method since diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue index 6fd2baeb2c6..028d48e7e8a 100644 --- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue +++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue @@ -133,11 +133,6 @@ export default { required: false, default: 2, }, - enableLabelPermalinks: { - type: Boolean, - required: false, - default: true, - }, labelFilterParam: { type: String, required: false, @@ -321,7 +316,6 @@ export default { :data-qa-issuable-title="issuable.title" :issuable-symbol="issuableSymbol" :issuable="issuable" - :enable-label-permalinks="enableLabelPermalinks" :label-filter-param="labelFilterParam" :show-checkbox="showBulkEditSidebar" :checked="issuableChecked(issuable)" diff --git a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue index 12f2bc71505..f6d85599dba 100644 --- a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue +++ b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue @@ -102,8 +102,8 @@ export default { error(error) { this.showError(error); }, - result({ loading }) { - if (loading) { + result({ loading, data }) { + if (loading || !data) { return; } diff --git a/app/assets/javascripts/work_items/graphql/fragmentTypes.json b/app/assets/javascripts/work_items/graphql/fragmentTypes.json deleted file mode 100644 index 3b837e84ee9..00000000000 --- a/app/assets/javascripts/work_items/graphql/fragmentTypes.json +++ /dev/null @@ -1 +0,0 @@ -{"__schema":{"types":[{"kind":"INTERFACE","name":"LocalWorkItemWidget","possibleTypes":[{"name":"LocalTitleWidget"}]}]}} diff --git a/app/assets/javascripts/work_items/graphql/provider.js b/app/assets/javascripts/work_items/graphql/provider.js index fb536a425c0..676fffb12d8 100644 --- a/app/assets/javascripts/work_items/graphql/provider.js +++ b/app/assets/javascripts/work_items/graphql/provider.js @@ -1,23 +1,14 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import createDefaultClient from '~/lib/graphql'; import workItemQuery from './work_item.query.graphql'; -import introspectionQueryResultData from './fragmentTypes.json'; import { resolvers } from './resolvers'; import typeDefs from './typedefs.graphql'; -const fragmentMatcher = new IntrospectionFragmentMatcher({ - introspectionQueryResultData, -}); - export function createApolloProvider() { Vue.use(VueApollo); const defaultClient = createDefaultClient(resolvers, { - cacheConfig: { - fragmentMatcher, - }, typeDefs, }); diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 63ae34f5ed3..f7f78ab3229 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -34,7 +34,7 @@ class Admin::RunnersController < Admin::ApplicationController end def destroy - @runner.destroy + Ci::UnregisterRunnerService.new(@runner).execute redirect_to admin_runners_path, status: :found end diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb index d171df12eac..b194aeff80d 100644 --- a/app/controllers/groups/runners_controller.rb +++ b/app/controllers/groups/runners_controller.rb @@ -35,7 +35,7 @@ class Groups::RunnersController < Groups::ApplicationController if @runner.belongs_to_more_than_one_project? redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found, alert: _('Runner was not deleted because it is assigned to multiple projects.') else - @runner.destroy + Ci::UnregisterRunnerService.new(@runner).execute redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), status: :found end diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 62a9f8a4625..192a29730d9 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -23,7 +23,7 @@ class Projects::RunnersController < Projects::ApplicationController def destroy if @runner.only_for?(project) - @runner.destroy + Ci::UnregisterRunnerService.new(@runner).execute end redirect_to project_runners_path(@project), status: :found diff --git a/app/graphql/mutations/ci/runner/delete.rb b/app/graphql/mutations/ci/runner/delete.rb index 88dc426398b..21c3d55881c 100644 --- a/app/graphql/mutations/ci/runner/delete.rb +++ b/app/graphql/mutations/ci/runner/delete.rb @@ -20,7 +20,7 @@ module Mutations error = authenticate_delete_runner!(runner) return { errors: [error] } if error - runner.destroy! + ::Ci::UnregisterRunnerService.new(runner).execute { errors: runner.errors.full_messages } end diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index 88b8cefd8d2..3e76239396a 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -32,13 +32,13 @@ module Mutations params = global_id_compatibility_params(attributes).merge(author_id: current_user.id) spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) - work_item = ::WorkItems::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute + create_result = ::WorkItems::CreateService.new(project: project, current_user: current_user, params: params, spam_params: spam_params).execute - check_spam_action_response!(work_item) + check_spam_action_response!(create_result[:work_item]) if create_result[:work_item] { - work_item: work_item.valid? ? work_item : nil, - errors: errors_on_object(work_item) + work_item: create_result.success? ? create_result[:work_item] : nil, + errors: create_result.errors } end diff --git a/app/services/ci/unregister_runner_service.rb b/app/services/ci/unregister_runner_service.rb new file mode 100644 index 00000000000..97d9852b7ed --- /dev/null +++ b/app/services/ci/unregister_runner_service.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Ci + class UnregisterRunnerService + attr_reader :runner + + # @param [Ci::Runner] runner the runner to unregister/destroy + def initialize(runner) + @runner = runner + end + + def execute + @runner&.destroy + end + end +end diff --git a/app/services/work_items/create_service.rb b/app/services/work_items/create_service.rb index 49f7b89158b..705735fe403 100644 --- a/app/services/work_items/create_service.rb +++ b/app/services/work_items/create_service.rb @@ -2,6 +2,8 @@ module WorkItems class CreateService + include ::Services::ReturnServiceResponses + def initialize(project:, current_user: nil, params: {}, spam_params:) @create_service = ::Issues::CreateService.new( project: project, @@ -10,10 +12,28 @@ module WorkItems spam_params: spam_params, build_service: ::WorkItems::BuildService.new(project: project, current_user: current_user, params: params) ) + @current_user = current_user + @project = project end def execute - @create_service.execute + unless @current_user.can?(:create_work_item, @project) + return error(_('Operation not allowed'), :forbidden) + end + + work_item = @create_service.execute + + if work_item.valid? + success(payload(work_item)) + else + error(work_item.errors.full_messages, :unprocessable_entity, pass_back: payload(work_item)) + end + end + + private + + def payload(work_item) + { work_item: work_item } end end end diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml index d73d171798e..fcd52f33121 100644 --- a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml +++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml @@ -5,4 +5,4 @@ = form_tag path do %input{ :name => "_method", :type => "hidden", :value => "delete" }/ - = submit_tag _('Revoke'), class: 'gl-button btn btn-danger btn-sm', data: { confirm: _('Are you sure?') } + = submit_tag _('Revoke'), class: 'gl-button btn btn-danger btn-sm', aria: { label: s_('AuthorizedApplication|Revoke application') }, data: { confirm: s_('AuthorizedApplication|Are you sure you want to revoke this application?'), confirm_btn_variant: 'danger' } diff --git a/app/views/projects/learn_gitlab/index.html.haml b/app/views/projects/learn_gitlab/index.html.haml index 6bca145dc18..9924b172875 100644 --- a/app/views/projects/learn_gitlab/index.html.haml +++ b/app/views/projects/learn_gitlab/index.html.haml @@ -2,7 +2,6 @@ - page_title _("Learn GitLab") - add_page_specific_style 'page_bundles/learn_gitlab' - data = learn_gitlab_data(@project) -- invite_members_open = session.delete(:confetti_post_signup) = render 'projects/invite_members_modal', project: @project @@ -10,4 +9,4 @@ - e.control do #js-learn-gitlab-app{ data: data } - e.candidate do - #js-learn-gitlab-app{ data: data.merge(invite_members_open: invite_members_open) } + #js-learn-gitlab-app{ data: data.merge(invite_members: 'true') } |