diff options
Diffstat (limited to 'app/assets/javascripts/ci_variable_list')
13 files changed, 294 insertions, 57 deletions
diff --git a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue index 59ddf4b19d8..8d891ff1746 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_admin_variables.vue @@ -1,5 +1,7 @@ <script> -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; +import { __ } from '~/locale'; +import { reportMessageToSentry } from '../utils'; import getAdminVariables from '../graphql/queries/variables.query.graphql'; import { ADD_MUTATION_ACTION, @@ -21,7 +23,11 @@ export default { data() { return { adminVariables: [], + hasNextPage: false, isInitialLoading: true, + isLoadingMoreItems: false, + loadingCounter: 0, + pageInfo: {}, }; }, apollo: { @@ -30,8 +36,29 @@ export default { update(data) { return data?.ciVariables?.nodes || []; }, + result({ data }) { + this.pageInfo = data?.ciVariables?.pageInfo || this.pageInfo; + this.hasNextPage = this.pageInfo?.hasNextPage || false; + + // Because graphQL has a limit of 100 items, + // we batch load all the variables by making successive queries + // to keep the same UX. As a safeguard, we make sure that we cannot go over + // 20 consecutive API calls, which means 2000 variables loaded maximum. + if (!this.hasNextPage) { + this.isLoadingMoreItems = false; + } else if (this.loadingCounter < 20) { + this.hasNextPage = false; + this.fetchMoreVariables(); + this.loadingCounter += 1; + } else { + createAlert({ message: this.$options.tooManyCallsError }); + reportMessageToSentry(this.$options.componentName, this.$options.tooManyCallsError, {}); + } + }, error() { - createFlash({ message: variableFetchErrorText }); + this.isLoadingMoreItems = false; + this.hasNextPage = false; + createAlert({ message: variableFetchErrorText }); }, watchLoading(flag) { if (!flag) { @@ -42,7 +69,10 @@ export default { }, computed: { isLoading() { - return this.$apollo.queries.adminVariables.loading && this.isInitialLoading; + return ( + (this.$apollo.queries.adminVariables.loading && this.isInitialLoading) || + this.isLoadingMoreItems + ); }, }, methods: { @@ -52,6 +82,15 @@ export default { deleteVariable(variable) { this.variableMutation(DELETE_MUTATION_ACTION, variable); }, + fetchMoreVariables() { + this.isLoadingMoreItems = true; + + this.$apollo.queries.adminVariables.fetchMore({ + variables: { + after: this.pageInfo.endCursor, + }, + }); + }, updateVariable(variable) { this.variableMutation(UPDATE_MUTATION_ACTION, variable); }, @@ -66,10 +105,9 @@ export default { }, }); - const { errors } = data[currentMutation.name]; - - if (errors.length > 0) { - createFlash({ message: errors[0] }); + if (data[currentMutation.name]?.errors?.length) { + const { errors } = data[currentMutation.name]; + createAlert({ message: errors[0] }); } else { // The writing to cache for admin variable is not working // because there is no ID in the cache at the top level. @@ -77,10 +115,14 @@ export default { this.$apollo.queries.adminVariables.refetch(); } } catch { - createFlash({ message: genericMutationErrorText }); + createAlert({ message: genericMutationErrorText }); } }, }, + componentName: 'InstanceVariables', + i18n: { + tooManyCallsError: __('Maximum number of variables loaded (2000)'), + }, mutationData: { [ADD_MUTATION_ACTION]: { action: addAdminVariable, name: 'addAdminVariable' }, [UPDATE_MUTATION_ACTION]: { action: updateAdminVariable, name: 'updateAdminVariable' }, diff --git a/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue index 3522243e3e7..4af696b8dab 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_group_variables.vue @@ -1,7 +1,9 @@ <script> -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; +import { __ } from '~/locale'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { reportMessageToSentry } from '../utils'; import getGroupVariables from '../graphql/queries/group_variables.query.graphql'; import { ADD_MUTATION_ACTION, @@ -25,6 +27,10 @@ export default { data() { return { groupVariables: [], + hasNextPage: false, + isLoadingMoreItems: false, + loadingCounter: 0, + pageInfo: {}, }; }, apollo: { @@ -38,8 +44,28 @@ export default { update(data) { return data?.group?.ciVariables?.nodes || []; }, + result({ data }) { + this.pageInfo = data?.group?.ciVariables?.pageInfo || this.pageInfo; + this.hasNextPage = this.pageInfo?.hasNextPage || false; + // Because graphQL has a limit of 100 items, + // we batch load all the variables by making successive queries + // to keep the same UX. As a safeguard, we make sure that we cannot go over + // 20 consecutive API calls, which means 2000 variables loaded maximum. + if (!this.hasNextPage) { + this.isLoadingMoreItems = false; + } else if (this.loadingCounter < 20) { + this.hasNextPage = false; + this.fetchMoreVariables(); + this.loadingCounter += 1; + } else { + createAlert({ message: this.$options.tooManyCallsError }); + reportMessageToSentry(this.$options.componentName, this.$options.tooManyCallsError, {}); + } + }, error() { - createFlash({ message: variableFetchErrorText }); + this.isLoadingMoreItems = false; + this.hasNextPage = false; + createAlert({ message: variableFetchErrorText }); }, }, }, @@ -48,7 +74,7 @@ export default { return this.glFeatures.groupScopedCiVariables; }, isLoading() { - return this.$apollo.queries.groupVariables.loading; + return this.$apollo.queries.groupVariables.loading || this.isLoadingMoreItems; }, }, methods: { @@ -58,6 +84,16 @@ export default { deleteVariable(variable) { this.variableMutation(DELETE_MUTATION_ACTION, variable); }, + fetchMoreVariables() { + this.isLoadingMoreItems = true; + + this.$apollo.queries.groupVariables.fetchMore({ + variables: { + fullPath: this.groupPath, + after: this.pageInfo.endCursor, + }, + }); + }, updateVariable(variable) { this.variableMutation(UPDATE_MUTATION_ACTION, variable); }, @@ -74,16 +110,19 @@ export default { }, }); - const { errors } = data[currentMutation.name]; - - if (errors.length > 0) { - createFlash({ message: errors[0] }); + if (data[currentMutation.name]?.errors?.length) { + const { errors } = data[currentMutation.name]; + createAlert({ message: errors[0] }); } } catch { - createFlash({ message: genericMutationErrorText }); + createAlert({ message: genericMutationErrorText }); } }, }, + componentName: 'GroupVariables', + i18n: { + tooManyCallsError: __('Maximum number of variables loaded (2000)'), + }, mutationData: { [ADD_MUTATION_ACTION]: { action: addGroupVariable, name: 'addGroupVariable' }, [UPDATE_MUTATION_ACTION]: { action: updateGroupVariable, name: 'updateGroupVariable' }, diff --git a/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue index 29db02a3c59..6bd549817f8 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_project_variables.vue @@ -1,9 +1,10 @@ <script> -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; +import { __ } from '~/locale'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import getProjectEnvironments from '../graphql/queries/project_environments.query.graphql'; import getProjectVariables from '../graphql/queries/project_variables.query.graphql'; -import { mapEnvironmentNames } from '../utils'; +import { mapEnvironmentNames, reportMessageToSentry } from '../utils'; import { ADD_MUTATION_ACTION, DELETE_MUTATION_ACTION, @@ -25,6 +26,10 @@ export default { inject: ['endpoint', 'projectFullPath', 'projectId'], data() { return { + hasNextPage: false, + isLoadingMoreItems: false, + loadingCounter: 0, + pageInfo: {}, projectEnvironments: [], projectVariables: [], }; @@ -41,21 +46,42 @@ export default { return mapEnvironmentNames(data?.project?.environments?.nodes); }, error() { - createFlash({ message: environmentFetchErrorText }); + createAlert({ message: environmentFetchErrorText }); }, }, projectVariables: { query: getProjectVariables, variables() { return { + after: null, fullPath: this.projectFullPath, }; }, update(data) { return data?.project?.ciVariables?.nodes || []; }, + result({ data }) { + this.pageInfo = data?.project?.ciVariables?.pageInfo || this.pageInfo; + this.hasNextPage = this.pageInfo?.hasNextPage || false; + // Because graphQL has a limit of 100 items, + // we batch load all the variables by making successive queries + // to keep the same UX. As a safeguard, we make sure that we cannot go over + // 20 consecutive API calls, which means 2000 variables loaded maximum. + if (!this.hasNextPage) { + this.isLoadingMoreItems = false; + } else if (this.loadingCounter < 20) { + this.hasNextPage = false; + this.fetchMoreVariables(); + this.loadingCounter += 1; + } else { + createAlert({ message: this.$options.tooManyCallsError }); + reportMessageToSentry(this.$options.componentName, this.$options.tooManyCallsError, {}); + } + }, error() { - createFlash({ message: variableFetchErrorText }); + this.isLoadingMoreItems = false; + this.hasNextPage = false; + createAlert({ message: variableFetchErrorText }); }, }, }, @@ -63,7 +89,8 @@ export default { isLoading() { return ( this.$apollo.queries.projectVariables.loading || - this.$apollo.queries.projectEnvironments.loading + this.$apollo.queries.projectEnvironments.loading || + this.isLoadingMoreItems ); }, }, @@ -74,6 +101,16 @@ export default { deleteVariable(variable) { this.variableMutation(DELETE_MUTATION_ACTION, variable); }, + fetchMoreVariables() { + this.isLoadingMoreItems = true; + + this.$apollo.queries.projectVariables.fetchMore({ + variables: { + fullPath: this.projectFullPath, + after: this.pageInfo.endCursor, + }, + }); + }, updateVariable(variable) { this.variableMutation(UPDATE_MUTATION_ACTION, variable); }, @@ -89,16 +126,19 @@ export default { variable, }, }); - - const { errors } = data[currentMutation.name]; - if (errors.length > 0) { - createFlash({ message: errors[0] }); + if (data[currentMutation.name]?.errors?.length) { + const { errors } = data[currentMutation.name]; + createAlert({ message: errors[0] }); } - } catch (e) { - createFlash({ message: genericMutationErrorText }); + } catch { + createAlert({ message: genericMutationErrorText }); } }, }, + componentName: 'ProjectVariables', + i18n: { + tooManyCallsError: __('Maximum number of variables loaded (2000)'), + }, mutationData: { [ADD_MUTATION_ACTION]: { action: addProjectVariable, name: 'addProjectVariable' }, [UPDATE_MUTATION_ACTION]: { action: updateProjectVariable, name: 'updateProjectVariable' }, diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue index 1bb94080694..959ef6864fb 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_table.vue @@ -75,8 +75,7 @@ export default { props: { isLoading: { type: Boolean, - required: false, - default: false, + required: true, }, variables: { type: Array, diff --git a/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_settings.vue b/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_settings.vue index 9acc9fbffb6..f1fe188348d 100644 --- a/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_settings.vue +++ b/app/assets/javascripts/ci_variable_list/components/legacy_ci_variable_settings.vue @@ -9,10 +9,10 @@ export default { LegacyCiVariableTable, }, computed: { - ...mapState(['isGroup']), + ...mapState(['isGroup', 'isProject']), }, mounted() { - if (!this.isGroup) { + if (this.isProject) { this.fetchEnvironments(); } }, diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js index e2dd28cdaa1..ccad08ef8b6 100644 --- a/app/assets/javascripts/ci_variable_list/constants.js +++ b/app/assets/javascripts/ci_variable_list/constants.js @@ -52,7 +52,7 @@ export const groupString = 'Group'; // eslint-disable-next-line @gitlab/require-i18n-strings export const instanceString = 'Instance'; // eslint-disable-next-line @gitlab/require-i18n-strings -export const projectString = 'Instance'; +export const projectString = 'Project'; export const AWS_TIP_DISMISSED_COOKIE_NAME = 'ci_variable_list_constants_aws_tip_dismissed'; export const AWS_TIP_MESSAGE = __( diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql index c6dd6d4faaf..b5555fe4401 100644 --- a/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql +++ b/app/assets/javascripts/ci_variable_list/graphql/queries/group_variables.query.graphql @@ -1,9 +1,13 @@ #import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" -query getGroupVariables($fullPath: ID!) { +query getGroupVariables($after: String, $first: Int = 100, $fullPath: ID!) { group(fullPath: $fullPath) { id - ciVariables { + ciVariables(after: $after, first: $first) { + pageInfo { + ...PageInfo + } nodes { ...BaseCiVariable ... on CiGroupVariable { diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql index a60a50e4bc4..08b5bf7af16 100644 --- a/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql +++ b/app/assets/javascripts/ci_variable_list/graphql/queries/project_variables.query.graphql @@ -1,9 +1,13 @@ #import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" -query getProjectVariables($fullPath: ID!) { +query getProjectVariables($after: String, $first: Int = 100, $fullPath: ID!) { project(fullPath: $fullPath) { id - ciVariables { + ciVariables(after: $after, first: $first) { + pageInfo { + ...PageInfo + } nodes { ...BaseCiVariable environmentScope diff --git a/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql b/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql index 95056842b49..2667d6606fe 100644 --- a/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql +++ b/app/assets/javascripts/ci_variable_list/graphql/queries/variables.query.graphql @@ -1,7 +1,11 @@ #import "~/ci_variable_list/graphql/fragments/ci_variable.fragment.graphql" +#import "~/graphql_shared/fragments/page_info.fragment.graphql" -query getVariables { - ciVariables { +query getVariables($after: String, $first: Int = 100) { + ciVariables(after: $after, first: $first) { + pageInfo { + ...PageInfo + } nodes { ...BaseCiVariable ... on CiInstanceVariable { diff --git a/app/assets/javascripts/ci_variable_list/graphql/resolvers.js b/app/assets/javascripts/ci_variable_list/graphql/settings.js index c041531ae30..ecdc4f220bd 100644 --- a/app/assets/javascripts/ci_variable_list/graphql/resolvers.js +++ b/app/assets/javascripts/ci_variable_list/graphql/settings.js @@ -3,7 +3,7 @@ import { convertObjectPropsToCamelCase, convertObjectPropsToSnakeCase, } from '../../lib/utils/common_utils'; -import { getIdFromGraphQLId } from '../../graphql_shared/utils'; +import { convertToGraphQLId, getIdFromGraphQLId } from '../../graphql_shared/utils'; import { GRAPHQL_GROUP_TYPE, GRAPHQL_PROJECT_TYPE, @@ -30,6 +30,7 @@ const mapVariableTypes = (variables = [], kind) => { return { __typename: `Ci${kind}Variable`, ...convertObjectPropsToCamelCase(ciVar), + id: convertToGraphQLId('Ci::Variable', ciVar.id), variableType: ciVar.variable_type ? ciVar.variable_type.toUpperCase() : ciVar.variableType, }; }); @@ -40,9 +41,16 @@ const prepareProjectGraphQLResponse = ({ data, projectId, errors = [] }) => { errors, project: { __typename: GRAPHQL_PROJECT_TYPE, - id: projectId, + id: convertToGraphQLId(GRAPHQL_PROJECT_TYPE, projectId), ciVariables: { - __typename: 'CiVariableConnection', + __typename: `Ci${GRAPHQL_PROJECT_TYPE}VariableConnection`, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + hasPreviousPage: false, + startCursor: '', + endCursor: '', + }, nodes: mapVariableTypes(data.variables, projectString), }, }, @@ -54,9 +62,16 @@ const prepareGroupGraphQLResponse = ({ data, groupId, errors = [] }) => { errors, group: { __typename: GRAPHQL_GROUP_TYPE, - id: groupId, + id: convertToGraphQLId(GRAPHQL_GROUP_TYPE, groupId), ciVariables: { - __typename: 'CiVariableConnection', + __typename: `Ci${GRAPHQL_GROUP_TYPE}VariableConnection`, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + hasPreviousPage: false, + startCursor: '', + endCursor: '', + }, nodes: mapVariableTypes(data.variables, groupString), }, }, @@ -68,24 +83,42 @@ const prepareAdminGraphQLResponse = ({ data, errors = [] }) => { errors, ciVariables: { __typename: `Ci${instanceString}VariableConnection`, + pageInfo: { + __typename: 'PageInfo', + hasNextPage: false, + hasPreviousPage: false, + startCursor: '', + endCursor: '', + }, nodes: mapVariableTypes(data.variables, instanceString), }, }; }; -const callProjectEndpoint = async ({ +async function callProjectEndpoint({ endpoint, fullPath, variable, projectId, cache, destroy = false, -}) => { +}) { try { const { data } = await axios.patch(endpoint, { variables_attributes: [prepareVariableForApi({ variable, destroy })], }); - return prepareProjectGraphQLResponse({ data, projectId }); + + const graphqlData = prepareProjectGraphQLResponse({ data, projectId }); + + cache.writeQuery({ + query: getProjectVariables, + variables: { + fullPath, + after: null, + }, + data: graphqlData, + }); + return graphqlData; } catch (e) { return prepareProjectGraphQLResponse({ data: cache.readQuery({ query: getProjectVariables, variables: { fullPath } }), @@ -93,7 +126,7 @@ const callProjectEndpoint = async ({ errors: [...e.response.data], }); } -}; +} const callGroupEndpoint = async ({ endpoint, @@ -107,7 +140,15 @@ const callGroupEndpoint = async ({ const { data } = await axios.patch(endpoint, { variables_attributes: [prepareVariableForApi({ variable, destroy })], }); - return prepareGroupGraphQLResponse({ data, groupId }); + + const graphqlData = prepareGroupGraphQLResponse({ data, groupId }); + + cache.writeQuery({ + query: getGroupVariables, + data: graphqlData, + }); + + return graphqlData; } catch (e) { return prepareGroupGraphQLResponse({ data: cache.readQuery({ query: getGroupVariables, variables: { fullPath } }), @@ -123,7 +164,14 @@ const callAdminEndpoint = async ({ endpoint, variable, cache, destroy = false }) variables_attributes: [prepareVariableForApi({ variable, destroy })], }); - return prepareAdminGraphQLResponse({ data }); + const graphqlData = prepareAdminGraphQLResponse({ data }); + + cache.writeQuery({ + query: getAdminVariables, + data: graphqlData, + }); + + return graphqlData; } catch (e) { return prepareAdminGraphQLResponse({ data: cache.readQuery({ query: getAdminVariables }), @@ -163,3 +211,46 @@ export const resolvers = { }, }, }; + +export const mergeVariables = (existing, incoming, { args }) => { + if (!existing || !args?.after) { + return incoming; + } + + const { nodes, ...rest } = incoming; + const result = rest; + result.nodes = [...existing.nodes, ...nodes]; + + return result; +}; + +export const cacheConfig = { + cacheConfig: { + typePolicies: { + Query: { + fields: { + ciVariables: { + keyArgs: false, + merge: mergeVariables, + }, + }, + }, + Project: { + fields: { + ciVariables: { + keyArgs: ['fullPath', 'endpoint', 'projectId'], + merge: mergeVariables, + }, + }, + }, + Group: { + fields: { + ciVariables: { + keyArgs: ['fullPath'], + merge: mergeVariables, + }, + }, + }, + }, + }, +}; diff --git a/app/assets/javascripts/ci_variable_list/index.js b/app/assets/javascripts/ci_variable_list/index.js index f5bdd4c7b1e..1b69da9e086 100644 --- a/app/assets/javascripts/ci_variable_list/index.js +++ b/app/assets/javascripts/ci_variable_list/index.js @@ -6,7 +6,7 @@ import CiAdminVariables from './components/ci_admin_variables.vue'; import CiGroupVariables from './components/ci_group_variables.vue'; import CiProjectVariables from './components/ci_project_variables.vue'; import LegacyCiVariableSettings from './components/legacy_ci_variable_settings.vue'; -import { resolvers } from './graphql/resolvers'; +import { cacheConfig, resolvers } from './graphql/settings'; import createStore from './store'; const mountCiVariableListApp = (containerEl) => { @@ -45,7 +45,7 @@ const mountCiVariableListApp = (containerEl) => { Vue.use(VueApollo); const apolloProvider = new VueApollo({ - defaultClient: createDefaultClient(resolvers), + defaultClient: createDefaultClient(resolvers, cacheConfig), }); return new Vue({ @@ -81,6 +81,7 @@ const mountLegacyCiVariableListApp = (containerEl) => { endpoint, projectId, isGroup, + isProject, maskableRegex, protectedByDefault, awsLogoSvgPath, @@ -92,6 +93,8 @@ const mountLegacyCiVariableListApp = (containerEl) => { maskedEnvironmentVariablesLink, environmentScopeLink, } = containerEl.dataset; + + const parsedIsProject = parseBoolean(isProject); const parsedIsGroup = parseBoolean(isGroup); const isProtectedByDefault = parseBoolean(protectedByDefault); @@ -99,6 +102,7 @@ const mountLegacyCiVariableListApp = (containerEl) => { endpoint, projectId, isGroup: parsedIsGroup, + isProject: parsedIsProject, maskableRegex, isProtectedByDefault, awsLogoSvgPath, diff --git a/app/assets/javascripts/ci_variable_list/store/actions.js b/app/assets/javascripts/ci_variable_list/store/actions.js index 8a182737e7b..ac31e845b0d 100644 --- a/app/assets/javascripts/ci_variable_list/store/actions.js +++ b/app/assets/javascripts/ci_variable_list/store/actions.js @@ -1,5 +1,5 @@ import Api from '~/api'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import axios from '~/lib/utils/axios_utils'; import { __ } from '~/locale'; import * as types from './mutation_types'; @@ -48,7 +48,7 @@ export const addVariable = ({ state, dispatch }) => { dispatch('fetchVariables'); }) .catch((error) => { - createFlash({ + createAlert({ message: error.response.data[0], }); dispatch('receiveAddVariableError', error); @@ -80,7 +80,7 @@ export const updateVariable = ({ state, dispatch }) => { dispatch('fetchVariables'); }) .catch((error) => { - createFlash({ + createAlert({ message: error.response.data[0], }); dispatch('receiveUpdateVariableError', error); @@ -109,7 +109,7 @@ export const fetchVariables = ({ dispatch, state }) => { dispatch('receiveVariablesSuccess', prepareDataForDisplay(data.variables)); }) .catch(() => { - createFlash({ + createAlert({ message: __('There was an error fetching the variables.'), }); }); @@ -139,7 +139,7 @@ export const deleteVariable = ({ dispatch, state }) => { dispatch('fetchVariables'); }) .catch((error) => { - createFlash({ + createAlert({ message: error.response.data[0], }); dispatch('receiveDeleteVariableError', error); @@ -162,7 +162,7 @@ export const fetchEnvironments = ({ dispatch, state }) => { dispatch('receiveEnvironmentsSuccess', prepareEnvironments(res.data)); }) .catch(() => { - createFlash({ + createAlert({ message: __('There was an error fetching the environments information.'), }); }); diff --git a/app/assets/javascripts/ci_variable_list/utils.js b/app/assets/javascripts/ci_variable_list/utils.js index 1faa97a5f73..eeca69274ce 100644 --- a/app/assets/javascripts/ci_variable_list/utils.js +++ b/app/assets/javascripts/ci_variable_list/utils.js @@ -1,3 +1,4 @@ +import * as Sentry from '@sentry/browser'; import { uniq } from 'lodash'; import { allEnvironments } from './constants'; @@ -48,3 +49,12 @@ export const convertEnvironmentScope = (environmentScope = '') => { export const mapEnvironmentNames = (nodes = []) => { return nodes.map((env) => env.name); }; + +export const reportMessageToSentry = (component, message, context) => { + Sentry.withScope((scope) => { + // eslint-disable-next-line @gitlab/require-i18n-strings + scope.setContext('Vue data', context); + scope.setTag('component', component); + Sentry.captureMessage(message); + }); +}; |