From 6ab5ace35386ae24b24eebae89b85d3dc84a1afd Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Mon, 5 Jan 2026 12:58:42 +0100 Subject: [PATCH] Fix task display Signed-off-by: Sacha Al Himdani --- .../src/components/tasks/TaskFormDialog.tsx | 34 +- .../src/components/tasks/TasksCard.tsx | 59 ++- .../TaskFormDialogUpdateMutation.graphql.ts | 3 +- .../__generated__/TaskGraphQuery.graphql.ts | 6 +- .../measures/tabs/MeasureTasksTab.tsx | 2 + .../MeasureTasksTabQuery.graphql.ts | 76 +-- .../pages/organizations/tasks/TasksPage.tsx | 2 + .../TasksPageFragment.graphql.ts | 20 +- .../TasksPageFragment_query.graphql.ts | 8 +- e2e/console/task_assignment_test.go | 33 +- e2e/console/task_test.go | 23 +- packages/helpers/src/date.ts | 33 ++ packages/helpers/src/index.ts | 2 +- pkg/probo/task_service.go | 14 + pkg/server/api/console/v1/schema.graphql | 18 +- pkg/server/api/console/v1/schema/schema.go | 490 +----------------- pkg/server/api/console/v1/types/types.go | 18 +- pkg/server/api/console/v1/v1_resolver.go | 30 +- 18 files changed, 216 insertions(+), 655 deletions(-) diff --git a/apps/console/src/components/tasks/TaskFormDialog.tsx b/apps/console/src/components/tasks/TaskFormDialog.tsx index 229cf67a2..3d7a489ad 100644 --- a/apps/console/src/components/tasks/TaskFormDialog.tsx +++ b/apps/console/src/components/tasks/TaskFormDialog.tsx @@ -73,10 +73,7 @@ const createTaskSchema = z.object({ name: z.string().min(1), description: z.string().optional().nullable(), timeEstimate: z.string().optional().nullable(), - assignedToId: z.preprocess( - (val) => (val === "" || val == null ? undefined : val), - z.string({ required_error: "Assigned to is required" }).min(1, "Assigned to is required") - ), + assignedToId: z.string().optional().nullable(), measureId: z.preprocess( (val) => (val === "" || val == null ? undefined : val), z.string({ required_error: "Measure is required" }).min(1, "Measure is required") @@ -88,7 +85,7 @@ const updateTaskSchema = z.object({ name: z.string().min(1), description: z.string().optional().nullable(), timeEstimate: z.string().optional().nullable(), - assignedToId: z.string().optional(), + assignedToId: z.string().optional().nullable(), measureId: z.string().optional(), deadline: z.string().optional().nullable(), }); @@ -137,6 +134,7 @@ export default function TaskFormDialog(props: Props) { description: data.description || null, timeEstimate: data.timeEstimate || null, deadline: formatDatetime(data.deadline) ?? null, + assignedToId: data.assignedToId ?? null, }, }, }); @@ -149,7 +147,7 @@ export default function TaskFormDialog(props: Props) { description: data.description || null, timeEstimate: data.timeEstimate || null, deadline: formatDatetime(data.deadline) ?? null, - assignedToId: data.assignedToId, + assignedToId: data.assignedToId || null, measureId: data.measureId, }, connections: [props.connection!], @@ -165,7 +163,6 @@ export default function TaskFormDialog(props: Props) { dialogRef.current?.close(); }); const showMeasure = !props.measureId && !isUpdating; - const isCreating = !isUpdating; return ( - {isCreating && ( - - - - )} + + + {showMeasure && ( & TaskFormDialogFragment$key; + task: ItemOf; connectionId: string; hasAnyAction: boolean; }; @@ -207,30 +208,48 @@ function TaskRow(props: TaskRowProps) { -
+

{props.task.name}

- {props.task.measure && ( -

- - - {props.task.measure?.name} - + {props.task.description && ( +

+ {props.task.description}

)} +
+ {props.task.measure && ( + + + + {props.task.measure?.name} + + + )} + {props.task.timeEstimate && ( + {formatDuration(props.task.timeEstimate, __)} + )} + {props.task.deadline && ( + + )} +
+ {props.task.assignedTo?.fullName && ( +
+ + {props.task.assignedTo.fullName} + +
+ )}
{isUpdating && } - {props.task.assignedTo && ( - - - - )} {props.hasAnyAction && ( {isAuthorized("Task", "updateTask") && ( diff --git a/apps/console/src/components/tasks/__generated__/TaskFormDialogUpdateMutation.graphql.ts b/apps/console/src/components/tasks/__generated__/TaskFormDialogUpdateMutation.graphql.ts index 7950f7b90..c260c3a14 100644 --- a/apps/console/src/components/tasks/__generated__/TaskFormDialogUpdateMutation.graphql.ts +++ b/apps/console/src/components/tasks/__generated__/TaskFormDialogUpdateMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<1cc9998f8dcbb02ac977744023d372d6>> + * @generated SignedSource<<1d9eed2918f5f58de18ef4ea5f0058b7>> * @lightSyntaxTransform * @nogrep */ @@ -12,6 +12,7 @@ import { ConcreteRequest } from 'relay-runtime'; import { FragmentRefs } from "relay-runtime"; export type TaskState = "DONE" | "TODO"; export type UpdateTaskInput = { + assignedToId?: string | null | undefined; deadline?: any | null | undefined; description?: string | null | undefined; name?: string | null | undefined; diff --git a/apps/console/src/hooks/graph/__generated__/TaskGraphQuery.graphql.ts b/apps/console/src/hooks/graph/__generated__/TaskGraphQuery.graphql.ts index 9650306e7..f31f2d99a 100644 --- a/apps/console/src/hooks/graph/__generated__/TaskGraphQuery.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/TaskGraphQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<40ebf241dc89f4b4b1705e4a2fabb935>> * @lightSyntaxTransform * @nogrep */ @@ -296,12 +296,12 @@ return { ] }, "params": { - "cacheID": "a0b3367a3288c70f0bbcde73ec237bee", + "cacheID": "72cbeaa7667c477f85e5c3ecb09b7bf4", "id": null, "metadata": {}, "name": "TaskGraphQuery", "operationKind": "query", - "text": "query TaskGraphQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n ...TasksPageFragment\n }\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n\nfragment TasksPageFragment on Organization {\n tasks(first: 500) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n measure {\n id\n name\n }\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n" + "text": "query TaskGraphQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n id\n ...TasksPageFragment\n }\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n\nfragment TasksPageFragment on Organization {\n tasks(first: 500) {\n edges {\n node {\n id\n name\n state\n description\n timeEstimate\n deadline\n ...TaskFormDialogFragment\n measure {\n id\n name\n }\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n" } }; })(); diff --git a/apps/console/src/pages/organizations/measures/tabs/MeasureTasksTab.tsx b/apps/console/src/pages/organizations/measures/tabs/MeasureTasksTab.tsx index 480cab491..36b50d09c 100644 --- a/apps/console/src/pages/organizations/measures/tabs/MeasureTasksTab.tsx +++ b/apps/console/src/pages/organizations/measures/tabs/MeasureTasksTab.tsx @@ -20,6 +20,8 @@ const tasksQuery = graphql` name state description + timeEstimate + deadline ...TaskFormDialogFragment assignedTo { id diff --git a/apps/console/src/pages/organizations/measures/tabs/__generated__/MeasureTasksTabQuery.graphql.ts b/apps/console/src/pages/organizations/measures/tabs/__generated__/MeasureTasksTabQuery.graphql.ts index 222a269dd..b9a3b8034 100644 --- a/apps/console/src/pages/organizations/measures/tabs/__generated__/MeasureTasksTabQuery.graphql.ts +++ b/apps/console/src/pages/organizations/measures/tabs/__generated__/MeasureTasksTabQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -25,10 +25,12 @@ export type MeasureTasksTabQuery$data = { readonly fullName: string; readonly id: string; } | null | undefined; + readonly deadline: any | null | undefined; readonly description: string | null | undefined; readonly id: string; readonly name: string; readonly state: TaskState; + readonly timeEstimate: any | null | undefined; readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">; }; }>; @@ -84,6 +86,20 @@ v5 = { "storageKey": null }, v6 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "timeEstimate", + "storageKey": null +}, +v7 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "deadline", + "storageKey": null +}, +v8 = { "alias": null, "args": null, "concreteType": "People", @@ -102,21 +118,21 @@ v6 = { ], "storageKey": null }, -v7 = { +v9 = { "alias": null, "args": null, "kind": "ScalarField", "name": "__typename", "storageKey": null }, -v8 = { +v10 = { "alias": null, "args": null, "kind": "ScalarField", "name": "cursor", "storageKey": null }, -v9 = { +v11 = { "alias": null, "args": null, "concreteType": "PageInfo", @@ -141,7 +157,7 @@ v9 = { ], "storageKey": null }, -v10 = { +v12 = { "kind": "ClientExtension", "selections": [ { @@ -153,7 +169,7 @@ v10 = { } ] }, -v11 = [ +v13 = [ { "kind": "Literal", "name": "first", @@ -207,22 +223,24 @@ return { (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), + (v6/*: any*/), + (v7/*: any*/), { "args": null, "kind": "FragmentSpread", "name": "TaskFormDialogFragment" }, - (v6/*: any*/), - (v7/*: any*/) + (v8/*: any*/), + (v9/*: any*/) ], "storageKey": null }, - (v8/*: any*/) + (v10/*: any*/) ], "storageKey": null }, - (v9/*: any*/), - (v10/*: any*/) + (v11/*: any*/), + (v12/*: any*/) ], "storageKey": null } @@ -251,14 +269,14 @@ return { "name": "node", "plural": false, "selections": [ - (v7/*: any*/), + (v9/*: any*/), (v2/*: any*/), { "kind": "InlineFragment", "selections": [ { "alias": null, - "args": (v11/*: any*/), + "args": (v13/*: any*/), "concreteType": "TaskConnection", "kind": "LinkedField", "name": "tasks", @@ -284,21 +302,9 @@ return { (v3/*: any*/), (v4/*: any*/), (v5/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "timeEstimate", - "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "deadline", - "storageKey": null - }, (v6/*: any*/), + (v7/*: any*/), + (v8/*: any*/), { "alias": null, "args": null, @@ -311,22 +317,22 @@ return { ], "storageKey": null }, - (v7/*: any*/) + (v9/*: any*/) ], "storageKey": null }, - (v8/*: any*/) + (v10/*: any*/) ], "storageKey": null }, - (v9/*: any*/), - (v10/*: any*/) + (v11/*: any*/), + (v12/*: any*/) ], "storageKey": "tasks(first:100)" }, { "alias": null, - "args": (v11/*: any*/), + "args": (v13/*: any*/), "filters": null, "handle": "connection", "key": "Measure__tasks", @@ -343,7 +349,7 @@ return { ] }, "params": { - "cacheID": "bc71d01128d96026a1e630b96e649409", + "cacheID": "7988e2a18d5929fe99d9cef8de244627", "id": null, "metadata": { "connection": [ @@ -360,11 +366,11 @@ return { }, "name": "MeasureTasksTabQuery", "operationKind": "query", - "text": "query MeasureTasksTabQuery(\n $measureId: ID!\n) {\n node(id: $measureId) {\n __typename\n ... on Measure {\n id\n tasks(first: 100) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n" + "text": "query MeasureTasksTabQuery(\n $measureId: ID!\n) {\n node(id: $measureId) {\n __typename\n ... on Measure {\n id\n tasks(first: 100) {\n edges {\n node {\n id\n name\n state\n description\n timeEstimate\n deadline\n ...TaskFormDialogFragment\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n" } }; })(); -(node as any).hash = "12445eb032af3bc00fea9edce6f427e2"; +(node as any).hash = "358a07bb3e13ea9828d0703541caf894"; export default node; diff --git a/apps/console/src/pages/organizations/tasks/TasksPage.tsx b/apps/console/src/pages/organizations/tasks/TasksPage.tsx index fd66555dd..84dcb1a4a 100644 --- a/apps/console/src/pages/organizations/tasks/TasksPage.tsx +++ b/apps/console/src/pages/organizations/tasks/TasksPage.tsx @@ -39,6 +39,8 @@ const tasksFragment = graphql` name state description + timeEstimate + deadline ...TaskFormDialogFragment measure { id diff --git a/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment.graphql.ts b/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment.graphql.ts index da8f3760b..c4c50b055 100644 --- a/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment.graphql.ts +++ b/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<8bba8d318de0f4b0d28a8fba8b8e0bdf>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -21,6 +21,7 @@ export type TasksPageFragment$data = { readonly fullName: string; readonly id: string; } | null | undefined; + readonly deadline: any | null | undefined; readonly description: string | null | undefined; readonly id: string; readonly measure: { @@ -29,6 +30,7 @@ export type TasksPageFragment$data = { } | null | undefined; readonly name: string; readonly state: TaskState; + readonly timeEstimate: any | null | undefined; readonly " $fragmentSpreads": FragmentRefs<"TaskFormDialogFragment">; }; }>; @@ -168,6 +170,20 @@ return { "name": "description", "storageKey": null }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "timeEstimate", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "deadline", + "storageKey": null + }, { "args": null, "kind": "FragmentSpread", @@ -286,6 +302,6 @@ return { }; })(); -(node as any).hash = "84864638db9ddf53e9ae7da7952803af"; +(node as any).hash = "466d3b82119334991a0714bb0b7cd6d0"; export default node; diff --git a/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment_query.graphql.ts b/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment_query.graphql.ts index 4b61a1ea4..2b0e070b8 100644 --- a/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment_query.graphql.ts +++ b/apps/console/src/pages/organizations/tasks/__generated__/TasksPageFragment_query.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<46a3b0baa64ec5a6a1c6d289a3737ade>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -369,16 +369,16 @@ return { ] }, "params": { - "cacheID": "58db4c11a870b12c21eef1c6f24271b0", + "cacheID": "d31450ee0ddd09b0e08b43c9f7728f5f", "id": null, "metadata": {}, "name": "TasksPageFragment_query", "operationKind": "query", - "text": "query TasksPageFragment_query(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 500\n $last: Int = null\n $order: TaskOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...TasksPageFragment_16fISc\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n\nfragment TasksPageFragment_16fISc on Organization {\n tasks(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n name\n state\n description\n ...TaskFormDialogFragment\n measure {\n id\n name\n }\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n" + "text": "query TasksPageFragment_query(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 500\n $last: Int = null\n $order: TaskOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...TasksPageFragment_16fISc\n id\n }\n}\n\nfragment TaskFormDialogFragment on Task {\n id\n description\n name\n state\n timeEstimate\n deadline\n assignedTo {\n id\n }\n measure {\n id\n }\n}\n\nfragment TasksPageFragment_16fISc on Organization {\n tasks(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n name\n state\n description\n timeEstimate\n deadline\n ...TaskFormDialogFragment\n measure {\n id\n name\n }\n assignedTo {\n id\n fullName\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n" } }; })(); -(node as any).hash = "84864638db9ddf53e9ae7da7952803af"; +(node as any).hash = "466d3b82119334991a0714bb0b7cd6d0"; export default node; diff --git a/e2e/console/task_assignment_test.go b/e2e/console/task_assignment_test.go index 4970bb3dc..8c9586c87 100644 --- a/e2e/console/task_assignment_test.go +++ b/e2e/console/task_assignment_test.go @@ -33,8 +33,8 @@ func TestTask_Assign(t *testing.T) { peopleID := factory.NewPeople(owner).WithFullName("Task Assignee").Create() query := ` - mutation AssignTask($input: AssignTaskInput!) { - assignTask(input: $input) { + mutation UpdateTask($input: UpdateTaskInput!) { + updateTask(input: $input) { task { id assignedTo { @@ -47,7 +47,7 @@ func TestTask_Assign(t *testing.T) { ` var result struct { - AssignTask struct { + UpdateTask struct { Task struct { ID string `json:"id"` AssignedTo struct { @@ -55,7 +55,7 @@ func TestTask_Assign(t *testing.T) { FullName string `json:"fullName"` } `json:"assignedTo"` } `json:"task"` - } `json:"assignTask"` + } `json:"updateTask"` } err := owner.Execute(query, map[string]any{ @@ -66,9 +66,9 @@ func TestTask_Assign(t *testing.T) { }, &result) require.NoError(t, err) - assert.Equal(t, taskID, result.AssignTask.Task.ID) - assert.Equal(t, peopleID, result.AssignTask.Task.AssignedTo.ID) - assert.Equal(t, "Task Assignee", result.AssignTask.Task.AssignedTo.FullName) + assert.Equal(t, taskID, result.UpdateTask.Task.ID) + assert.Equal(t, peopleID, result.UpdateTask.Task.AssignedTo.ID) + assert.Equal(t, "Task Assignee", result.UpdateTask.Task.AssignedTo.FullName) } func TestTask_Unassign(t *testing.T) { @@ -82,8 +82,8 @@ func TestTask_Unassign(t *testing.T) { // First assign the task assignQuery := ` - mutation AssignTask($input: AssignTaskInput!) { - assignTask(input: $input) { + mutation UpdateTask($input: UpdateTaskInput!) { + updateTask(input: $input) { task { id } @@ -100,8 +100,8 @@ func TestTask_Unassign(t *testing.T) { require.NoError(t, err) query := ` - mutation UnassignTask($input: UnassignTaskInput!) { - unassignTask(input: $input) { + mutation UpdateTask($input: UpdateTaskInput!) { + updateTask(input: $input) { task { id assignedTo { @@ -113,23 +113,24 @@ func TestTask_Unassign(t *testing.T) { ` var result struct { - UnassignTask struct { + UpdateTask struct { Task struct { ID string `json:"id"` AssignedTo *struct { ID string `json:"id"` } `json:"assignedTo"` } `json:"task"` - } `json:"unassignTask"` + } `json:"updateTask"` } err = owner.Execute(query, map[string]any{ "input": map[string]any{ - "taskId": taskID, + "taskId": taskID, + "assignedToId": nil, }, }, &result) require.NoError(t, err) - assert.Equal(t, taskID, result.UnassignTask.Task.ID) - assert.Nil(t, result.UnassignTask.Task.AssignedTo) + assert.Equal(t, taskID, result.UpdateTask.Task.ID) + assert.Nil(t, result.UpdateTask.Task.AssignedTo) } diff --git a/e2e/console/task_test.go b/e2e/console/task_test.go index 297552221..95e84313a 100644 --- a/e2e/console/task_test.go +++ b/e2e/console/task_test.go @@ -615,8 +615,8 @@ func TestTask_OmittableAssignee(t *testing.T) { t.Run("set assignee", func(t *testing.T) { query := ` - mutation AssignTask($input: AssignTaskInput!) { - assignTask(input: $input) { + mutation UpdateTask($input: UpdateTaskInput!) { + updateTask(input: $input) { task { id assignedTo { @@ -629,7 +629,7 @@ func TestTask_OmittableAssignee(t *testing.T) { ` var result struct { - AssignTask struct { + UpdateTask struct { Task struct { ID string `json:"id"` AssignedTo struct { @@ -637,7 +637,7 @@ func TestTask_OmittableAssignee(t *testing.T) { FullName string `json:"fullName"` } `json:"assignedTo"` } `json:"task"` - } `json:"assignTask"` + } `json:"updateTask"` } err := owner.Execute(query, map[string]any{ @@ -647,13 +647,13 @@ func TestTask_OmittableAssignee(t *testing.T) { }, }, &result) require.NoError(t, err) - assert.Equal(t, peopleID, result.AssignTask.Task.AssignedTo.ID) + assert.Equal(t, peopleID, result.UpdateTask.Task.AssignedTo.ID) }) t.Run("clear assignee", func(t *testing.T) { query := ` - mutation UnassignTask($input: UnassignTaskInput!) { - unassignTask(input: $input) { + mutation UpdateTask($input: UpdateTaskInput!) { + updateTask(input: $input) { task { id assignedTo { @@ -665,23 +665,24 @@ func TestTask_OmittableAssignee(t *testing.T) { ` var result struct { - UnassignTask struct { + UpdateTask struct { Task struct { ID string `json:"id"` AssignedTo *struct { ID string `json:"id"` } `json:"assignedTo"` } `json:"task"` - } `json:"unassignTask"` + } `json:"updateTask"` } err := owner.Execute(query, map[string]any{ "input": map[string]any{ - "taskId": taskID, + "taskId": taskID, + "assignedToId": nil, }, }, &result) require.NoError(t, err) - assert.Nil(t, result.UnassignTask.Task.AssignedTo) + assert.Nil(t, result.UpdateTask.Task.AssignedTo) }) } diff --git a/packages/helpers/src/date.ts b/packages/helpers/src/date.ts index 5cbcde6fa..7249e1dba 100644 --- a/packages/helpers/src/date.ts +++ b/packages/helpers/src/date.ts @@ -26,3 +26,36 @@ function parseDate(dateString: string): Date { parts[2] ? parseInt(parts[2], 10) : 1 ); } + +export function formatDuration(duration?: string | null, __?: (s: string) => string): string | null { + if (!duration || !__) return null; + + const timeMatch = duration.match(/PT(\d+)([MH])/); + if (timeMatch) { + const amount = parseInt(timeMatch[1], 10) || 0; + const unit = timeMatch[2]; + if (unit === "M") { + return `${amount} ${amount === 1 ? __("Minute") : __("Minutes")}`; + } else if (unit === "H") { + return `${amount} ${amount === 1 ? __("Hour") : __("Hours")}`; + } + } + + const dateMatch = duration.match(/P(\d+)([DW])/); + if (dateMatch) { + const amount = parseInt(dateMatch[1], 10) || 0; + const unit = dateMatch[2]; + if (unit === "W") { + return `${amount} ${amount === 1 ? __("Week") : __("Weeks")}`; + } else if (unit === "D") { + const days = amount; + if (days % 7 === 0 && days > 0) { + const weeks = days / 7; + return `${weeks} ${weeks === 1 ? __("Week") : __("Weeks")}`; + } + return `${days} ${days === 1 ? __("Day") : __("Days")}`; + } + } + + return null; +} diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 767a5c1bd..25fd8ae57 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -61,7 +61,7 @@ export { } from "./trustCenterVisibility"; export { promisifyMutation } from "./relay"; export { fileType, fileSize } from "./file"; -export { formatDatetime, formatDate, toDateInput } from "./date"; +export { formatDatetime, formatDate, toDateInput, formatDuration } from "./date"; export { getTrustCenterUrl } from "./trustCenter"; export { formatError, type GraphQLError } from "./error"; export { Role, getAssignableRoles } from "./roles"; diff --git a/pkg/probo/task_service.go b/pkg/probo/task_service.go index f2a71010c..a19570f87 100644 --- a/pkg/probo/task_service.go +++ b/pkg/probo/task_service.go @@ -49,6 +49,7 @@ type ( State *coredata.TaskState TimeEstimate **time.Duration Deadline **time.Time + AssignedToID **gid.GID } ) @@ -73,6 +74,7 @@ func (utr *UpdateTaskRequest) Validate() error { v.Check(utr.Description, "description", validator.SafeText(ContentMaxLength)) v.Check(utr.TimeEstimate, "time_estimate", validator.RangeDuration(0, 1000*time.Hour)) v.Check(utr.State, "state", validator.OneOfSlice(coredata.TaskStates())) + v.Check(utr.AssignedToID, "assigned_to_id", validator.GID(coredata.PeopleEntityType)) return v.Error() } @@ -261,6 +263,18 @@ func (s TaskService) Update( task.Deadline = *req.Deadline } + if req.AssignedToID != nil { + if *req.AssignedToID == nil { + task.AssignedToID = nil + } else { + people := &coredata.People{} + if err := people.LoadByID(ctx, conn, s.svc.scope, **req.AssignedToID); err != nil { + return fmt.Errorf("cannot load assignee: %w", err) + } + task.AssignedToID = *req.AssignedToID + } + } + task.UpdatedAt = time.Now() if err := task.Update(ctx, conn, s.svc.scope); err != nil { diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index ed7437b79..5b8a15bb5 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -3104,8 +3104,6 @@ type Mutation { createTask(input: CreateTaskInput!): CreateTaskPayload! updateTask(input: UpdateTaskInput!): UpdateTaskPayload! deleteTask(input: DeleteTaskInput!): DeleteTaskPayload! - assignTask(input: AssignTaskInput!): AssignTaskPayload! - unassignTask(input: UnassignTaskInput!): UnassignTaskPayload! # Risk mutations createRisk(input: CreateRiskInput!): CreateRiskPayload! updateRisk(input: UpdateRiskInput!): UpdateRiskPayload! @@ -3609,20 +3607,13 @@ input UpdateTaskInput { state: TaskState timeEstimate: Duration @goField(omittable: true) deadline: Datetime @goField(omittable: true) + assignedToId: ID @goField(omittable: true) } input DeleteTaskInput { taskId: ID! } -input AssignTaskInput { - taskId: ID! - assignedToId: ID! -} - -input UnassignTaskInput { - taskId: ID! -} input CreateControlMeasureMappingInput { controlId: ID! @@ -4321,13 +4312,6 @@ type DeleteTaskPayload { deletedTaskId: ID! } -type AssignTaskPayload { - task: Task! -} - -type UnassignTaskPayload { - task: Task! -} type CreateControlMeasureMappingPayload { controlEdge: ControlEdge! diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 06f663e86..88c5732fd 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -154,10 +154,6 @@ type ComplexityRoot struct { Node func(childComplexity int) int } - AssignTaskPayload struct { - Task func(childComplexity int) int - } - Audit struct { Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int CreatedAt func(childComplexity int) int @@ -947,7 +943,6 @@ type ComplexityRoot struct { Mutation struct { AcceptInvitation func(childComplexity int, input types.AcceptInvitationInput) int AssessVendor func(childComplexity int, input types.AssessVendorInput) int - AssignTask func(childComplexity int, input types.AssignTaskInput) int BulkDeleteDocuments func(childComplexity int, input types.BulkDeleteDocumentsInput) int BulkExportDocuments func(childComplexity int, input types.BulkExportDocumentsInput) int BulkPublishDocumentVersions func(childComplexity int, input types.BulkPublishDocumentVersionsInput) int @@ -1050,7 +1045,6 @@ type ComplexityRoot struct { RequestSignature func(childComplexity int, input types.RequestSignatureInput) int SendSigningNotifications func(childComplexity int, input types.SendSigningNotificationsInput) int SignDocument func(childComplexity int, input types.SignDocumentInput) int - UnassignTask func(childComplexity int, input types.UnassignTaskInput) int UpdateAsset func(childComplexity int, input types.UpdateAssetInput) int UpdateAudit func(childComplexity int, input types.UpdateAuditInput) int UpdateContinualImprovement func(childComplexity int, input types.UpdateContinualImprovementInput) int @@ -1605,10 +1599,6 @@ type ComplexityRoot struct { Node func(childComplexity int) int } - UnassignTaskPayload struct { - Task func(childComplexity int) int - } - UpdateAssetPayload struct { Asset func(childComplexity int) int } @@ -2127,8 +2117,6 @@ type MutationResolver interface { CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) - AssignTask(ctx context.Context, input types.AssignTaskInput) (*types.AssignTaskPayload, error) - UnassignTask(ctx context.Context, input types.UnassignTaskInput) (*types.UnassignTaskPayload, error) CreateRisk(ctx context.Context, input types.CreateRiskInput) (*types.CreateRiskPayload, error) UpdateRisk(ctx context.Context, input types.UpdateRiskInput) (*types.UpdateRiskPayload, error) DeleteRisk(ctx context.Context, input types.DeleteRiskInput) (*types.DeleteRiskPayload, error) @@ -2549,13 +2537,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.AssetEdge.Node(childComplexity), true - case "AssignTaskPayload.task": - if e.complexity.AssignTaskPayload.Task == nil { - break - } - - return e.complexity.AssignTaskPayload.Task(childComplexity), true - case "Audit.controls": if e.complexity.Audit.Controls == nil { break @@ -4916,17 +4897,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Mutation.AssessVendor(childComplexity, args["input"].(types.AssessVendorInput)), true - case "Mutation.assignTask": - if e.complexity.Mutation.AssignTask == nil { - break - } - - args, err := ec.field_Mutation_assignTask_args(ctx, rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.Mutation.AssignTask(childComplexity, args["input"].(types.AssignTaskInput)), true case "Mutation.bulkDeleteDocuments": if e.complexity.Mutation.BulkDeleteDocuments == nil { break @@ -6049,17 +6019,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Mutation.SignDocument(childComplexity, args["input"].(types.SignDocumentInput)), true - case "Mutation.unassignTask": - if e.complexity.Mutation.UnassignTask == nil { - break - } - - args, err := ec.field_Mutation_unassignTask_args(ctx, rawArgs) - if err != nil { - return 0, false - } - - return e.complexity.Mutation.UnassignTask(childComplexity, args["input"].(types.UnassignTaskInput)), true case "Mutation.updateAsset": if e.complexity.Mutation.UpdateAsset == nil { break @@ -8718,13 +8677,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.TrustCenterReferenceEdge.Node(childComplexity), true - case "UnassignTaskPayload.task": - if e.complexity.UnassignTaskPayload.Task == nil { - break - } - - return e.complexity.UnassignTaskPayload.Task(childComplexity), true - case "UpdateAssetPayload.asset": if e.complexity.UpdateAssetPayload.Asset == nil { break @@ -9755,7 +9707,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputAssessVendorInput, ec.unmarshalInputAssetFilter, ec.unmarshalInputAssetOrder, - ec.unmarshalInputAssignTaskInput, ec.unmarshalInputAuditOrder, ec.unmarshalInputBulkDeleteDocumentsInput, ec.unmarshalInputBulkExportDocumentsInput, @@ -9904,7 +9855,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputTrustCenterDocumentAccessOrder, ec.unmarshalInputTrustCenterFileOrder, ec.unmarshalInputTrustCenterReferenceOrder, - ec.unmarshalInputUnassignTaskInput, ec.unmarshalInputUpdateAssetInput, ec.unmarshalInputUpdateAuditInput, ec.unmarshalInputUpdateContinualImprovementInput, @@ -13153,8 +13103,6 @@ type Mutation { createTask(input: CreateTaskInput!): CreateTaskPayload! updateTask(input: UpdateTaskInput!): UpdateTaskPayload! deleteTask(input: DeleteTaskInput!): DeleteTaskPayload! - assignTask(input: AssignTaskInput!): AssignTaskPayload! - unassignTask(input: UnassignTaskInput!): UnassignTaskPayload! # Risk mutations createRisk(input: CreateRiskInput!): CreateRiskPayload! updateRisk(input: UpdateRiskInput!): UpdateRiskPayload! @@ -13658,20 +13606,13 @@ input UpdateTaskInput { state: TaskState timeEstimate: Duration @goField(omittable: true) deadline: Datetime @goField(omittable: true) + assignedToId: ID @goField(omittable: true) } input DeleteTaskInput { taskId: ID! } -input AssignTaskInput { - taskId: ID! - assignedToId: ID! -} - -input UnassignTaskInput { - taskId: ID! -} input CreateControlMeasureMappingInput { controlId: ID! @@ -14370,13 +14311,6 @@ type DeleteTaskPayload { deletedTaskId: ID! } -type AssignTaskPayload { - task: Task! -} - -type UnassignTaskPayload { - task: Task! -} type CreateControlMeasureMappingPayload { controlEdge: ControlEdge! @@ -15835,17 +15769,6 @@ func (ec *executionContext) field_Mutation_assessVendor_args(ctx context.Context return args, nil } -func (ec *executionContext) field_Mutation_assignTask_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { - var err error - args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNAssignTaskInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐAssignTaskInput) - if err != nil { - return nil, err - } - args["input"] = arg0 - return args, nil -} - func (ec *executionContext) field_Mutation_bulkDeleteDocuments_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -16968,17 +16891,6 @@ func (ec *executionContext) field_Mutation_signDocument_args(ctx context.Context return args, nil } -func (ec *executionContext) field_Mutation_unassignTask_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { - var err error - args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNUnassignTaskInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUnassignTaskInput) - if err != nil { - return nil, err - } - args["input"] = arg0 - return args, nil -} - func (ec *executionContext) field_Mutation_updateAsset_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -19591,61 +19503,6 @@ func (ec *executionContext) fieldContext_AssetEdge_node(_ context.Context, field return fc, nil } -func (ec *executionContext) _AssignTaskPayload_task(ctx context.Context, field graphql.CollectedField, obj *types.AssignTaskPayload) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - ec.fieldContext_AssignTaskPayload_task, - func(ctx context.Context) (any, error) { - return obj.Task, nil - }, - nil, - ec.marshalNTask2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐTask, - true, - true, - ) -} - -func (ec *executionContext) fieldContext_AssignTaskPayload_task(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "AssignTaskPayload", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Task_id(ctx, field) - case "name": - return ec.fieldContext_Task_name(ctx, field) - case "description": - return ec.fieldContext_Task_description(ctx, field) - case "state": - return ec.fieldContext_Task_state(ctx, field) - case "timeEstimate": - return ec.fieldContext_Task_timeEstimate(ctx, field) - case "deadline": - return ec.fieldContext_Task_deadline(ctx, field) - case "assignedTo": - return ec.fieldContext_Task_assignedTo(ctx, field) - case "organization": - return ec.fieldContext_Task_organization(ctx, field) - case "measure": - return ec.fieldContext_Task_measure(ctx, field) - case "evidences": - return ec.fieldContext_Task_evidences(ctx, field) - case "createdAt": - return ec.fieldContext_Task_createdAt(ctx, field) - case "updatedAt": - return ec.fieldContext_Task_updatedAt(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Task", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _Audit_id(ctx context.Context, field graphql.CollectedField, obj *types.Audit) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -35210,96 +35067,6 @@ func (ec *executionContext) fieldContext_Mutation_deleteTask(ctx context.Context return fc, nil } -func (ec *executionContext) _Mutation_assignTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - ec.fieldContext_Mutation_assignTask, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Mutation().AssignTask(ctx, fc.Args["input"].(types.AssignTaskInput)) - }, - nil, - ec.marshalNAssignTaskPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐAssignTaskPayload, - true, - true, - ) -} - -func (ec *executionContext) fieldContext_Mutation_assignTask(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "task": - return ec.fieldContext_AssignTaskPayload_task(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type AssignTaskPayload", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_assignTask_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - -func (ec *executionContext) _Mutation_unassignTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - ec.fieldContext_Mutation_unassignTask, - func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Mutation().UnassignTask(ctx, fc.Args["input"].(types.UnassignTaskInput)) - }, - nil, - ec.marshalNUnassignTaskPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUnassignTaskPayload, - true, - true, - ) -} - -func (ec *executionContext) fieldContext_Mutation_unassignTask(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Mutation", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "task": - return ec.fieldContext_UnassignTaskPayload_task(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type UnassignTaskPayload", field.Name) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_unassignTask_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil -} - func (ec *executionContext) _Mutation_createRisk(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -51510,61 +51277,6 @@ func (ec *executionContext) fieldContext_TrustCenterReferenceEdge_node(_ context return fc, nil } -func (ec *executionContext) _UnassignTaskPayload_task(ctx context.Context, field graphql.CollectedField, obj *types.UnassignTaskPayload) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - ec.fieldContext_UnassignTaskPayload_task, - func(ctx context.Context) (any, error) { - return obj.Task, nil - }, - nil, - ec.marshalNTask2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐTask, - true, - true, - ) -} - -func (ec *executionContext) fieldContext_UnassignTaskPayload_task(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "UnassignTaskPayload", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_Task_id(ctx, field) - case "name": - return ec.fieldContext_Task_name(ctx, field) - case "description": - return ec.fieldContext_Task_description(ctx, field) - case "state": - return ec.fieldContext_Task_state(ctx, field) - case "timeEstimate": - return ec.fieldContext_Task_timeEstimate(ctx, field) - case "deadline": - return ec.fieldContext_Task_deadline(ctx, field) - case "assignedTo": - return ec.fieldContext_Task_assignedTo(ctx, field) - case "organization": - return ec.fieldContext_Task_organization(ctx, field) - case "measure": - return ec.fieldContext_Task_measure(ctx, field) - case "evidences": - return ec.fieldContext_Task_evidences(ctx, field) - case "createdAt": - return ec.fieldContext_Task_createdAt(ctx, field) - case "updatedAt": - return ec.fieldContext_Task_updatedAt(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type Task", field.Name) - }, - } - return fc, nil -} - func (ec *executionContext) _UpdateAssetPayload_asset(ctx context.Context, field graphql.CollectedField, obj *types.UpdateAssetPayload) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -59571,40 +59283,6 @@ func (ec *executionContext) unmarshalInputAssetOrder(ctx context.Context, obj an return it, nil } -func (ec *executionContext) unmarshalInputAssignTaskInput(ctx context.Context, obj any) (types.AssignTaskInput, error) { - var it types.AssignTaskInput - asMap := map[string]any{} - for k, v := range obj.(map[string]any) { - asMap[k] = v - } - - fieldsInOrder := [...]string{"taskId", "assignedToId"} - for _, k := range fieldsInOrder { - v, ok := asMap[k] - if !ok { - continue - } - switch k { - case "taskId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("taskId")) - data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) - if err != nil { - return it, err - } - it.TaskID = data - case "assignedToId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("assignedToId")) - data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) - if err != nil { - return it, err - } - it.AssignedToID = data - } - } - - return it, nil -} - func (ec *executionContext) unmarshalInputAuditOrder(ctx context.Context, obj any) (types.AuditOrderBy, error) { var it types.AuditOrderBy asMap := map[string]any{} @@ -65337,33 +65015,6 @@ func (ec *executionContext) unmarshalInputTrustCenterReferenceOrder(ctx context. return it, nil } -func (ec *executionContext) unmarshalInputUnassignTaskInput(ctx context.Context, obj any) (types.UnassignTaskInput, error) { - var it types.UnassignTaskInput - asMap := map[string]any{} - for k, v := range obj.(map[string]any) { - asMap[k] = v - } - - fieldsInOrder := [...]string{"taskId"} - for _, k := range fieldsInOrder { - v, ok := asMap[k] - if !ok { - continue - } - switch k { - case "taskId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("taskId")) - data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) - if err != nil { - return it, err - } - it.TaskID = data - } - } - - return it, nil -} - func (ec *executionContext) unmarshalInputUpdateAssetInput(ctx context.Context, obj any) (types.UpdateAssetInput, error) { var it types.UpdateAssetInput asMap := map[string]any{} @@ -66807,7 +66458,7 @@ func (ec *executionContext) unmarshalInputUpdateTaskInput(ctx context.Context, o asMap[k] = v } - fieldsInOrder := [...]string{"taskId", "name", "description", "state", "timeEstimate", "deadline"} + fieldsInOrder := [...]string{"taskId", "name", "description", "state", "timeEstimate", "deadline", "assignedToId"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -66856,6 +66507,13 @@ func (ec *executionContext) unmarshalInputUpdateTaskInput(ctx context.Context, o return it, err } it.Deadline = graphql.OmittableOf(data) + case "assignedToId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("assignedToId")) + data, err := ec.unmarshalOID2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.AssignedToID = graphql.OmittableOf(data) } } @@ -68716,45 +68374,6 @@ func (ec *executionContext) _AssetEdge(ctx context.Context, sel ast.SelectionSet return out } -var assignTaskPayloadImplementors = []string{"AssignTaskPayload"} - -func (ec *executionContext) _AssignTaskPayload(ctx context.Context, sel ast.SelectionSet, obj *types.AssignTaskPayload) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, assignTaskPayloadImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("AssignTaskPayload") - case "task": - out.Values[i] = ec._AssignTaskPayload_task(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var auditImplementors = []string{"Audit", "Node"} func (ec *executionContext) _Audit(ctx context.Context, sel ast.SelectionSet, obj *types.Audit) graphql.Marshaler { @@ -77466,20 +77085,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } - case "assignTask": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_assignTask(ctx, field) - }) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "unassignTask": - out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_unassignTask(ctx, field) - }) - if out.Values[i] == graphql.Null { - out.Invalids++ - } case "createRisk": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_createRisk(ctx, field) @@ -84213,45 +83818,6 @@ func (ec *executionContext) _TrustCenterReferenceEdge(ctx context.Context, sel a return out } -var unassignTaskPayloadImplementors = []string{"UnassignTaskPayload"} - -func (ec *executionContext) _UnassignTaskPayload(ctx context.Context, sel ast.SelectionSet, obj *types.UnassignTaskPayload) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, unassignTaskPayloadImplementors) - - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("UnassignTaskPayload") - case "task": - out.Values[i] = ec._UnassignTaskPayload_task(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - - atomic.AddInt32(&ec.deferred, int32(len(deferred))) - - for label, dfs := range deferred { - ec.processDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } - - return out -} - var updateAssetPayloadImplementors = []string{"UpdateAssetPayload"} func (ec *executionContext) _UpdateAssetPayload(ctx context.Context, sel ast.SelectionSet, obj *types.UpdateAssetPayload) graphql.Marshaler { @@ -88155,25 +87721,6 @@ var ( } ) -func (ec *executionContext) unmarshalNAssignTaskInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐAssignTaskInput(ctx context.Context, v any) (types.AssignTaskInput, error) { - res, err := ec.unmarshalInputAssignTaskInput(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNAssignTaskPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐAssignTaskPayload(ctx context.Context, sel ast.SelectionSet, v types.AssignTaskPayload) graphql.Marshaler { - return ec._AssignTaskPayload(ctx, sel, &v) -} - -func (ec *executionContext) marshalNAssignTaskPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐAssignTaskPayload(ctx context.Context, sel ast.SelectionSet, v *types.AssignTaskPayload) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._AssignTaskPayload(ctx, sel, v) -} - func (ec *executionContext) marshalNAudit2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐAudit(ctx context.Context, sel ast.SelectionSet, v *types.Audit) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { @@ -95238,25 +94785,6 @@ var ( } ) -func (ec *executionContext) unmarshalNUnassignTaskInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUnassignTaskInput(ctx context.Context, v any) (types.UnassignTaskInput, error) { - res, err := ec.unmarshalInputUnassignTaskInput(ctx, v) - return res, graphql.ErrorOnPath(ctx, err) -} - -func (ec *executionContext) marshalNUnassignTaskPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUnassignTaskPayload(ctx context.Context, sel ast.SelectionSet, v types.UnassignTaskPayload) graphql.Marshaler { - return ec._UnassignTaskPayload(ctx, sel, &v) -} - -func (ec *executionContext) marshalNUnassignTaskPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUnassignTaskPayload(ctx context.Context, sel ast.SelectionSet, v *types.UnassignTaskPayload) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._UnassignTaskPayload(ctx, sel, v) -} - func (ec *executionContext) unmarshalNUpdateAssetInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐUpdateAssetInput(ctx context.Context, v any) (types.UpdateAssetInput, error) { res, err := ec.unmarshalInputUpdateAssetInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index 367b479d6..f10f152b4 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -64,15 +64,6 @@ type AssetFilter struct { SnapshotID *gid.GID `json:"snapshotId,omitempty"` } -type AssignTaskInput struct { - TaskID gid.GID `json:"taskId"` - AssignedToID gid.GID `json:"assignedToId"` -} - -type AssignTaskPayload struct { - Task *Task `json:"task"` -} - type Audit struct { ID gid.GID `json:"id"` Name *string `json:"name,omitempty"` @@ -1959,14 +1950,6 @@ type TrustCenterReferenceEdge struct { Node *TrustCenterReference `json:"node"` } -type UnassignTaskInput struct { - TaskID gid.GID `json:"taskId"` -} - -type UnassignTaskPayload struct { - Task *Task `json:"task"` -} - type UpdateAssetInput struct { ID gid.GID `json:"id"` Name *string `json:"name,omitempty"` @@ -2262,6 +2245,7 @@ type UpdateTaskInput struct { State *coredata.TaskState `json:"state,omitempty"` TimeEstimate graphql.Omittable[*time.Duration] `json:"timeEstimate,omitempty"` Deadline graphql.Omittable[*time.Time] `json:"deadline,omitempty"` + AssignedToID graphql.Omittable[*gid.GID] `json:"assignedToId,omitempty"` } type UpdateTaskPayload struct { diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 8348f8454..c32962508 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -2798,6 +2798,7 @@ func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTas Name: input.Name, Description: input.Description, TimeEstimate: input.TimeEstimate, + AssignedToID: input.AssignedToID, Deadline: input.Deadline, }) if err != nil { @@ -2826,6 +2827,7 @@ func (r *mutationResolver) UpdateTask(ctx context.Context, input types.UpdateTas State: input.State, TimeEstimate: UnwrapOmittable(input.TimeEstimate), Deadline: UnwrapOmittable(input.Deadline), + AssignedToID: UnwrapOmittable(input.AssignedToID), }) if err != nil { panic(fmt.Errorf("cannot update task: %w", err)) @@ -2852,34 +2854,6 @@ func (r *mutationResolver) DeleteTask(ctx context.Context, input types.DeleteTas }, nil } -// AssignTask is the resolver for the assignTask field. -func (r *mutationResolver) AssignTask(ctx context.Context, input types.AssignTaskInput) (*types.AssignTaskPayload, error) { - prb := r.ProboService(ctx, input.TaskID.TenantID()) - - task, err := prb.Tasks.Assign(ctx, input.TaskID, input.AssignedToID) - if err != nil { - panic(fmt.Errorf("cannot assign task: %w", err)) - } - - return &types.AssignTaskPayload{ - Task: types.NewTask(task), - }, nil -} - -// UnassignTask is the resolver for the unassignTask field. -func (r *mutationResolver) UnassignTask(ctx context.Context, input types.UnassignTaskInput) (*types.UnassignTaskPayload, error) { - prb := r.ProboService(ctx, input.TaskID.TenantID()) - - task, err := prb.Tasks.Unassign(ctx, input.TaskID) - if err != nil { - panic(fmt.Errorf("cannot unassign task: %w", err)) - } - - return &types.UnassignTaskPayload{ - Task: types.NewTask(task), - }, nil -} - // CreateRisk is the resolver for the createRisk field. func (r *mutationResolver) CreateRisk(ctx context.Context, input types.CreateRiskInput) (*types.CreateRiskPayload, error) { r.MustBeAuthorized(ctx, input.OrganizationID, authz.ActionCreateRisk)