From 8cf4e1496ac11e4df12491dbd75e0bfaaee5f7e6 Mon Sep 17 00:00:00 2001 From: Deepika Guliani <dguliani@gitlab.com> Date: Mon, 10 Mar 2025 19:13:18 +0530 Subject: [PATCH] Fix WorkItemType for reference work items Changelog: fixed --- .../components/work_item_parent.vue | 64 ++++--- .../graphql/project_work_items.query.graphql | 10 + .../work_items/graphql/resolvers.js | 179 +++++++++--------- .../work_items_by_references.query.graphql | 5 + .../attach_existing_issue_spec.js | 21 +- .../shared/work_item_token_input_spec.js | 48 +++++ spec/frontend/work_items/mock_data.js | 48 +++++ 7 files changed, 257 insertions(+), 118 deletions(-) diff --git a/app/assets/javascripts/work_items/components/work_item_parent.vue b/app/assets/javascripts/work_items/components/work_item_parent.vue index 676c6591798b6..cfab8ea34d0ff 100644 --- a/app/assets/javascripts/work_items/components/work_item_parent.vue +++ b/app/assets/javascripts/work_items/components/work_item_parent.vue @@ -204,41 +204,45 @@ export default { this.searchStarted = true; }, async updateParent() { - if (this.parent?.id === this.localSelectedItem) return; + try { + if (this.parent?.id === this.localSelectedItem) return; - this.updateInProgress = true; + this.updateInProgress = true; - if (this.workItemId === newWorkItemId(this.workItemType)) { - const visibleWorkItems = this.workItemsByReference.concat(this.workspaceWorkItems); + if (this.workItemId === newWorkItemId(this.workItemType)) { + const visibleWorkItems = this.workItemsByReference.concat(this.workspaceWorkItems); - this.$apollo - .mutate({ - mutation: updateNewWorkItemMutation, - variables: { - input: { - fullPath: this.fullPath, - parent: - this.localSelectedItem && visibleWorkItems.length - ? { - ...visibleWorkItems?.find(({ id }) => id === this.localSelectedItem), - webUrl: this.parentWebUrl ?? null, - } - : null, - workItemType: this.workItemType, + this.$apollo + .mutate({ + mutation: updateNewWorkItemMutation, + variables: { + input: { + fullPath: this.fullPath, + parent: + this.localSelectedItem && visibleWorkItems.length + ? { + ...visibleWorkItems?.find(({ id }) => id === this.localSelectedItem), + webUrl: this.parentWebUrl ?? null, + } + : null, + workItemType: this.workItemType, + }, }, - }, - }) - .catch((error) => { - Sentry.captureException(error); - }) - .finally(() => { - this.searchStarted = false; - this.updateInProgress = false; - }); - return; - } + }) + .catch((error) => { + this.$emit( + 'error', + sprintfWorkItem(I18N_WORK_ITEM_ERROR_UPDATING, this.workItemType), + ); + Sentry.captureException(error); + }) + .finally(() => { + this.searchStarted = false; + this.updateInProgress = false; + }); + return; + } - try { const { data: { workItemUpdate: { errors }, diff --git a/app/assets/javascripts/work_items/graphql/project_work_items.query.graphql b/app/assets/javascripts/work_items/graphql/project_work_items.query.graphql index 17b338f7a8d98..8010387f2eff2 100644 --- a/app/assets/javascripts/work_items/graphql/project_work_items.query.graphql +++ b/app/assets/javascripts/work_items/graphql/project_work_items.query.graphql @@ -15,6 +15,11 @@ query projectWorkItems( iid title confidential + workItemType { + id + name + iconName + } } } workItemsByIid: workItems(iid: $iid, types: $types) @include(if: $searchByIid) { @@ -23,6 +28,11 @@ query projectWorkItems( iid title confidential + workItemType { + id + name + iconName + } } } } diff --git a/app/assets/javascripts/work_items/graphql/resolvers.js b/app/assets/javascripts/work_items/graphql/resolvers.js index 347c2a4d1fd49..8246fb12ab846 100644 --- a/app/assets/javascripts/work_items/graphql/resolvers.js +++ b/app/assets/javascripts/work_items/graphql/resolvers.js @@ -1,5 +1,6 @@ import { set, isEmpty } from 'lodash'; import { produce } from 'immer'; +import * as Sentry from '~/sentry/sentry_browser_wrapper'; import { findWidget } from '~/issues/list/utils'; import { newDate, toISODateFormat } from '~/lib/utils/datetime_utility'; import { updateDraft } from '~/lib/utils/autosave'; @@ -68,93 +69,97 @@ export const updateNewWorkItemCache = (input, cache) => { parent, } = input; - const query = workItemByIidQuery; - const variables = { - fullPath: newWorkItemFullPath(fullPath, workItemType), - iid: NEW_WORK_ITEM_IID, - }; - - cache.updateQuery({ query, variables }, (sourceData) => - produce(sourceData, (draftData) => { - const widgetUpdates = [ - { - widgetType: WIDGET_TYPE_ASSIGNEES, - newData: assignees, - nodePath: 'assignees.nodes', - }, - { - widgetType: WIDGET_TYPE_LABELS, - newData: labels, - nodePath: 'labels.nodes', - }, - { - widgetType: WIDGET_TYPE_COLOR, - newData: color, - nodePath: 'color', - }, - { - widgetType: WIDGET_TYPE_CRM_CONTACTS, - newData: crmContacts, - nodePath: 'contacts.nodes', - }, - { - widgetType: WIDGET_TYPE_DESCRIPTION, - newData: description, - nodePath: 'description', - }, - { - widgetType: WIDGET_TYPE_HEALTH_STATUS, - newData: healthStatus, - nodePath: 'healthStatus', - }, - { - widgetType: WIDGET_TYPE_ITERATION, - newData: iteration, - nodePath: 'iteration', - }, - { - widgetType: WIDGET_TYPE_WEIGHT, - newData: weight, - nodePath: 'weight', - }, - { - widgetType: WIDGET_TYPE_MILESTONE, - newData: milestone, - nodePath: 'milestone', - }, - { - widgetType: WIDGET_TYPE_HIERARCHY, - newData: parent, - nodePath: 'parent', - }, - ]; - - widgetUpdates.forEach(({ widgetType, newData, nodePath }) => { - updateWidget(draftData, widgetType, newData, nodePath); - }); - - updateDatesWidget(draftData, rolledUpDates); - - // We want to allow users to delete a title for an in-progress work item draft - // as we check for the title being valid when submitting the form - if (title !== undefined) draftData.workspace.workItem.title = title; - - if (confidential !== undefined) draftData.workspace.workItem.confidential = confidential; - }), - ); - - const newData = cache.readQuery({ query, variables }); - - const autosaveKey = getNewWorkItemAutoSaveKey(fullPath, workItemType); - - const isQueryDataValid = !isEmpty(newData) && newData?.workspace?.workItem; - - const isWorkItemToResolveDiscussion = getParameterByName( - 'merge_request_to_resolve_discussions_of', - ); - - if (isQueryDataValid && autosaveKey && !isWorkItemToResolveDiscussion) { - updateDraft(autosaveKey, JSON.stringify(newData)); + try { + const query = workItemByIidQuery; + const variables = { + fullPath: newWorkItemFullPath(fullPath, workItemType), + iid: NEW_WORK_ITEM_IID, + }; + + cache.updateQuery({ query, variables }, (sourceData) => + produce(sourceData, (draftData) => { + const widgetUpdates = [ + { + widgetType: WIDGET_TYPE_ASSIGNEES, + newData: assignees, + nodePath: 'assignees.nodes', + }, + { + widgetType: WIDGET_TYPE_LABELS, + newData: labels, + nodePath: 'labels.nodes', + }, + { + widgetType: WIDGET_TYPE_COLOR, + newData: color, + nodePath: 'color', + }, + { + widgetType: WIDGET_TYPE_CRM_CONTACTS, + newData: crmContacts, + nodePath: 'contacts.nodes', + }, + { + widgetType: WIDGET_TYPE_DESCRIPTION, + newData: description, + nodePath: 'description', + }, + { + widgetType: WIDGET_TYPE_HEALTH_STATUS, + newData: healthStatus, + nodePath: 'healthStatus', + }, + { + widgetType: WIDGET_TYPE_ITERATION, + newData: iteration, + nodePath: 'iteration', + }, + { + widgetType: WIDGET_TYPE_WEIGHT, + newData: weight, + nodePath: 'weight', + }, + { + widgetType: WIDGET_TYPE_MILESTONE, + newData: milestone, + nodePath: 'milestone', + }, + { + widgetType: WIDGET_TYPE_HIERARCHY, + newData: parent, + nodePath: 'parent', + }, + ]; + + widgetUpdates.forEach(({ widgetType, newData, nodePath }) => { + updateWidget(draftData, widgetType, newData, nodePath); + }); + + updateDatesWidget(draftData, rolledUpDates); + + // We want to allow users to delete a title for an in-progress work item draft + // as we check for the title being valid when submitting the form + if (title !== undefined) draftData.workspace.workItem.title = title; + + if (confidential !== undefined) draftData.workspace.workItem.confidential = confidential; + }), + ); + + const newData = cache.readQuery({ query, variables }); + + const autosaveKey = getNewWorkItemAutoSaveKey(fullPath, workItemType); + + const isQueryDataValid = !isEmpty(newData) && newData?.workspace?.workItem; + + const isWorkItemToResolveDiscussion = getParameterByName( + 'merge_request_to_resolve_discussions_of', + ); + + if (isQueryDataValid && autosaveKey && !isWorkItemToResolveDiscussion) { + updateDraft(autosaveKey, JSON.stringify(newData)); + } + } catch (e) { + Sentry.captureException(e); } }; diff --git a/app/assets/javascripts/work_items/graphql/work_items_by_references.query.graphql b/app/assets/javascripts/work_items/graphql/work_items_by_references.query.graphql index 1e8d62596b747..1cb061205fd61 100644 --- a/app/assets/javascripts/work_items/graphql/work_items_by_references.query.graphql +++ b/app/assets/javascripts/work_items/graphql/work_items_by_references.query.graphql @@ -5,6 +5,11 @@ query getWorkItemsByReferences($contextNamespacePath: ID!, $refs: [String!]!) { iid title confidential + workItemType { + id + name + iconName + } } } } diff --git a/ee/spec/frontend/security_dashboard/components/shared/vulnerability_report/attach_existing_issue_spec.js b/ee/spec/frontend/security_dashboard/components/shared/vulnerability_report/attach_existing_issue_spec.js index 1b5255be0951c..755166b79c3fa 100644 --- a/ee/spec/frontend/security_dashboard/components/shared/vulnerability_report/attach_existing_issue_spec.js +++ b/ee/spec/frontend/security_dashboard/components/shared/vulnerability_report/attach_existing_issue_spec.js @@ -24,8 +24,27 @@ const defaultWorkItemsResponse = { iid: '1', title: 'Item 1', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, + __typename: 'WorkItem', + }, + { + id: 'gid://gitlab/WorkItem/2', + iid: '2', + title: 'Item 2', + confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, + __typename: 'WorkItem', }, - { id: 'gid://gitlab/WorkItem/2', iid: '2', title: 'Item 2', confidential: false }, ], }, }, diff --git a/spec/frontend/work_items/components/shared/work_item_token_input_spec.js b/spec/frontend/work_items/components/shared/work_item_token_input_spec.js index df8c5c2e367c4..7860ec86ea44b 100644 --- a/spec/frontend/work_items/components/shared/work_item_token_input_spec.js +++ b/spec/frontend/work_items/components/shared/work_item_token_input_spec.js @@ -48,6 +48,12 @@ describe('WorkItemTokenInput', () => { iid: '2', title: 'Task 1', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, { @@ -55,6 +61,12 @@ describe('WorkItemTokenInput', () => { iid: '3', title: 'Task 2', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, { @@ -62,6 +74,12 @@ describe('WorkItemTokenInput', () => { iid: '4', title: 'Task 3', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, ], @@ -157,6 +175,12 @@ describe('WorkItemTokenInput', () => { iid: '3', title: 'Task 2', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }; @@ -165,6 +189,12 @@ describe('WorkItemTokenInput', () => { iid: 'Task 2 <svg><use href=#/></svg>', title: 'Task 2 <svg><use href=#/></svg>', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }; @@ -298,6 +328,12 @@ describe('WorkItemTokenInput', () => { iid: '101', title: 'Task 3', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }; const mockWorkItemResponseItem2 = { @@ -305,6 +341,12 @@ describe('WorkItemTokenInput', () => { iid: '3', title: 'Task 123', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }; const mockWorkItemResponseItem3 = { @@ -312,6 +354,12 @@ describe('WorkItemTokenInput', () => { iid: '123', title: 'Task 2', confidential: false, + workItemType: { + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + iconName: 'issue-type-task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }; diff --git a/spec/frontend/work_items/mock_data.js b/spec/frontend/work_items/mock_data.js index 3ed5ebc70ceee..0781af2c3ff62 100644 --- a/spec/frontend/work_items/mock_data.js +++ b/spec/frontend/work_items/mock_data.js @@ -429,6 +429,12 @@ export const mockworkItemReferenceQueryResponse = { iid: '111', title: 'Objective linked items 104', confidential: false, + workItemType: { + iconName: 'issue-type-objective', + id: 'gid://gitlab/WorkItems::Type/6', + name: 'Objective', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, ], @@ -3232,6 +3238,12 @@ export const availableWorkItemsResponse = { iid: '2', title: 'Task 1', confidential: false, + workItemType: { + iconName: 'issue-type-task', + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, { @@ -3239,6 +3251,12 @@ export const availableWorkItemsResponse = { iid: '3', title: 'Task 2', confidential: false, + workItemType: { + iconName: 'issue-type-task', + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, { @@ -3246,6 +3264,12 @@ export const availableWorkItemsResponse = { iid: '4', title: 'Task 3', confidential: false, + workItemType: { + iconName: 'issue-type-task', + id: 'gid://gitlab/WorkItems::Type/5', + name: 'Task', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, ], @@ -3266,6 +3290,12 @@ export const availableObjectivesResponse = { iid: '122', title: 'Objective 101', confidential: false, + workItemType: { + iconName: 'issue-type-objective', + id: 'gid://gitlab/WorkItems::Type/6', + name: 'Objective', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, { @@ -3273,6 +3303,12 @@ export const availableObjectivesResponse = { iid: '118', title: 'Objective 103', confidential: false, + workItemType: { + iconName: 'issue-type-objective', + id: 'gid://gitlab/WorkItems::Type/6', + name: 'Objective', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, { @@ -3280,6 +3316,12 @@ export const availableObjectivesResponse = { iid: '117', title: 'Objective 102', confidential: false, + workItemType: { + iconName: 'issue-type-objective', + id: 'gid://gitlab/WorkItems::Type/6', + name: 'Objective', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, ], @@ -3300,6 +3342,12 @@ export const searchedObjectiveResponse = { iid: '122', title: 'Objective 101', confidential: false, + workItemType: { + iconName: 'issue-type-objective', + id: 'gid://gitlab/WorkItems::Type/6', + name: 'Objective', + __typename: 'WorkItemType', + }, __typename: 'WorkItem', }, ], -- GitLab