From 98d7e7b7b9620d9ba5a160ded497e30e981f8cbc Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Tue, 23 Sep 2025 14:02:18 +0200 Subject: [PATCH] Update obligations Signed-off-by: Sacha Al Himdani --- .../obligations/LinkedObligationsCard.tsx | 213 ++++ .../obligations/LinkedObligationsDialog.tsx | 199 ++++ .../LinkedObligationsCardFragment.graphql.ts | 96 ++ ...LinkedObligationsDialogFragment.graphql.ts | 260 +++++ .../LinkedObligationsDialogQuery.graphql.ts | 278 +++++ ...ObligationsDialogQuery_fragment.graphql.ts | 351 ++++++ .../src/hooks/graph/ObligationGraph.ts | 22 +- apps/console/src/hooks/graph/RiskGraph.ts | 4 + .../ObligationGraphCreateMutation.graphql.ts | 19 +- .../ObligationGraphListQuery.graphql.ts | 13 +- .../ObligationGraphNodeQuery.graphql.ts | 46 +- .../ObligationGraphUpdateMutation.graphql.ts | 19 +- .../RiskGraphNodeQuery.graphql.ts | 246 ++-- .../obligations/ObligationDetailsPage.tsx | 35 +- .../obligations/ObligationsPage.tsx | 18 +- .../ObligationsPageFragment.graphql.ts | 14 +- .../ObligationsPageQuery.graphql.ts | 13 +- .../ObligationsPageRefetchQuery.graphql.ts | 15 +- .../dialogs/CreateObligationDialog.tsx | 19 +- .../organizations/risks/RiskDetailPage.tsx | 5 + .../risks/tabs/RiskObligationsTab.tsx | 87 ++ ...iskObligationsTabCreateMutation.graphql.ts | 235 ++++ ...iskObligationsTabDetachMutation.graphql.ts | 133 +++ .../RiskObligationsTabFragment.graphql.ts | 155 +++ apps/console/src/routes/riskRoutes.ts | 7 + packages/helpers/src/index.ts | 1 + packages/helpers/src/obligationStatus.ts | 46 + pkg/coredata/migrations/20250923T120205Z.sql | 26 + pkg/coredata/obligation.go | 138 ++- pkg/coredata/obligation_order_field.go | 4 +- pkg/coredata/obligation_status.go | 18 +- pkg/coredata/risk_obligation.go | 99 ++ pkg/probo/obligation_service.go | 61 +- pkg/probo/risk_service.go | 70 ++ pkg/server/api/console/v1/schema.graphql | 55 +- pkg/server/api/console/v1/schema/schema.go | 1022 +++++++++++++++-- pkg/server/api/console/v1/types/obligation.go | 1 - pkg/server/api/console/v1/types/types.go | 24 +- pkg/server/api/console/v1/v1_resolver.go | 73 +- 39 files changed, 3746 insertions(+), 394 deletions(-) create mode 100644 apps/console/src/components/obligations/LinkedObligationsCard.tsx create mode 100644 apps/console/src/components/obligations/LinkedObligationsDialog.tsx create mode 100644 apps/console/src/components/obligations/__generated__/LinkedObligationsCardFragment.graphql.ts create mode 100644 apps/console/src/components/obligations/__generated__/LinkedObligationsDialogFragment.graphql.ts create mode 100644 apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery.graphql.ts create mode 100644 apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery_fragment.graphql.ts create mode 100644 apps/console/src/pages/organizations/risks/tabs/RiskObligationsTab.tsx create mode 100644 apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabCreateMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabDetachMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabFragment.graphql.ts create mode 100644 packages/helpers/src/obligationStatus.ts create mode 100644 pkg/coredata/migrations/20250923T120205Z.sql create mode 100644 pkg/coredata/risk_obligation.go diff --git a/apps/console/src/components/obligations/LinkedObligationsCard.tsx b/apps/console/src/components/obligations/LinkedObligationsCard.tsx new file mode 100644 index 000000000..6992cc514 --- /dev/null +++ b/apps/console/src/components/obligations/LinkedObligationsCard.tsx @@ -0,0 +1,213 @@ +import { graphql } from "relay-runtime"; +import { + Card, + IconPlusLarge, + Button, + Tr, + Td, + Table, + Thead, + Tbody, + Th, + IconChevronDown, + IconTrashCan, + TrButton, + Badge, +} from "@probo/ui"; +import { useTranslate } from "@probo/i18n"; +import type { LinkedObligationsCardFragment$key } from "./__generated__/LinkedObligationsCardFragment.graphql"; +import { useFragment } from "react-relay"; +import { useMemo, useState } from "react"; +import { sprintf, getObligationStatusVariant, getObligationStatusLabel } from "@probo/helpers"; +import { LinkedObligationDialog } from "./LinkedObligationsDialog"; +import clsx from "clsx"; +import { useParams } from "react-router"; +import { useOrganizationId } from "/hooks/useOrganizationId"; + +const linkedObligationFragment = graphql` + fragment LinkedObligationsCardFragment on Obligation { + id + requirement + area + source + status + owner { + fullName + } + } +`; + +type Mutation = (p: { + variables: { + input: { + obligationId: string; + } & Params; + connections: string[]; + }; +}) => void; + +type Props = { + obligations: (LinkedObligationsCardFragment$key & { id: string })[]; + connectionId: string; + disabled?: boolean; + variant?: "card" | "table"; + + params: Params; + + onAttach: Mutation; + onDetach: Mutation; +}; + +export function LinkedObligationsCard(props: Props) { + const { __ } = useTranslate(); + const [limit, setLimit] = useState( + props.variant === "card" ? 4 : null + ); + + const onAttach = (obligationId: string) => { + props.onAttach({ + variables: { + input: { + obligationId, + ...props.params, + }, + connections: [props.connectionId], + }, + }); + }; + + const onDetach = (obligationId: string) => { + props.onDetach({ + variables: { + input: { + obligationId, + ...props.params, + }, + connections: [props.connectionId], + }, + }); + }; + + const obligations = useMemo(() => { + return limit ? props.obligations.slice(0, limit) : props.obligations; + }, [props.obligations, limit]); + + const showMoreButton = limit !== null && props.obligations.length > limit; + const variant = props.variant ?? "table"; + + const Wrapper = variant === "card" ? Card : "div"; + + return ( + + {variant === "card" && ( +
+
{__("Obligations")}
+ + + +
+ )} + + + + + + + + + + + + {obligations.length === 0 && ( + + + + )} + {obligations.map((obligation) => ( + + ))} + {variant === "table" && ( + + + {__("Link obligation")} + + + )} + +
{__("Area")}{__("Source")}{__("Status")}{__("Owner")}
+ {__("No obligations linked")} +
+ {showMoreButton && ( + + )} +
+ ); +} + +function ObligationRow(props: { + obligation: LinkedObligationsCardFragment$key & { id: string }; + onClick: (obligationId: string) => void; +}) { + const { __ } = useTranslate(); + const obligation = useFragment(linkedObligationFragment, props.obligation); + const organizationId = useOrganizationId(); + const { snapshotId } = useParams<{ snapshotId?: string }>(); + const isSnapshotMode = Boolean(snapshotId); + + const onDetach = () => { + props.onClick(obligation.id); + }; + + const detailsUrl = isSnapshotMode + ? `/organizations/${organizationId}/snapshots/${snapshotId}/obligations/${obligation.id}` + : `/organizations/${organizationId}/obligations/${obligation.id}`; + + return ( + + + {obligation.area || __("No area specified")} + + + {obligation.source || __("No source specified")} + + + + {getObligationStatusLabel(obligation.status)} + + + + {obligation.owner?.fullName || __("Unassigned")} + + + + + + ); +} diff --git a/apps/console/src/components/obligations/LinkedObligationsDialog.tsx b/apps/console/src/components/obligations/LinkedObligationsDialog.tsx new file mode 100644 index 000000000..e54e67bfa --- /dev/null +++ b/apps/console/src/components/obligations/LinkedObligationsDialog.tsx @@ -0,0 +1,199 @@ +import { + Button, + Dialog, + DialogContent, + DialogFooter, + IconMagnifyingGlass, + IconPlusLarge, + IconTrashCan, + InfiniteScrollTrigger, + Input, + Spinner, + Badge, +} from "@probo/ui"; +import { useTranslate } from "@probo/i18n"; +import { getObligationStatusVariant, getObligationStatusLabel } from "@probo/helpers"; +import { Suspense, useMemo, useState, type ReactNode } from "react"; +import { graphql } from "relay-runtime"; +import { useLazyLoadQuery, usePaginationFragment } from "react-relay"; +import type { LinkedObligationsDialogQuery } from "./__generated__/LinkedObligationsDialogQuery.graphql"; +import { useOrganizationId } from "/hooks/useOrganizationId"; +import type { NodeOf } from "/types"; +import type { + LinkedObligationsDialogFragment$data, + LinkedObligationsDialogFragment$key, +} from "./__generated__/LinkedObligationsDialogFragment.graphql"; + +const obligationsQuery = graphql` + query LinkedObligationsDialogQuery($organizationId: ID!) { + organization: node(id: $organizationId) { + id + ... on Organization { + ...LinkedObligationsDialogFragment + } + } + } +`; + +const obligationsFragment = graphql` + fragment LinkedObligationsDialogFragment on Organization + @refetchable(queryName: "LinkedObligationsDialogQuery_fragment") + @argumentDefinitions( + first: { type: "Int", defaultValue: 20 } + order: { type: "ObligationOrder", defaultValue: null } + after: { type: "CursorKey", defaultValue: null } + before: { type: "CursorKey", defaultValue: null } + last: { type: "Int", defaultValue: null } + ) { + obligations( + first: $first + after: $after + last: $last + before: $before + orderBy: $order + ) @connection(key: "LinkedObligationsDialogQuery_obligations") { + edges { + node { + id + requirement + area + source + status + owner { + fullName + } + } + } + } + } +`; + +type Props = { + children: ReactNode; + connectionId: string; + disabled?: boolean; + linkedObligations?: { id: string }[]; + onLink: (obligationId: string) => void; + onUnlink: (obligationId: string) => void; +}; + +export function LinkedObligationDialog({ children, ...props }: Props) { + const { __ } = useTranslate(); + + return ( + + + }> + + + + + + ); +} + +function LinkedObligationsDialogContent(props: Omit) { + const organizationId = useOrganizationId(); + const query = useLazyLoadQuery(obligationsQuery, { + organizationId, + }, { fetchPolicy: "network-only" }); + const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment( + obligationsFragment, + query.organization as LinkedObligationsDialogFragment$key + ); + const { __ } = useTranslate(); + const [search, setSearch] = useState(""); + const obligations = data.obligations?.edges?.map((edge) => edge.node) ?? []; + const linkedIds = useMemo(() => { + return new Set(props.linkedObligations?.map((o) => o.id) ?? []); + }, [props.linkedObligations]); + + const filteredObligations = useMemo(() => { + return obligations.filter((obligation) => + obligation.area?.toLowerCase().includes(search.toLowerCase()) || + obligation.source?.toLowerCase().includes(search.toLowerCase()) || + obligation.owner?.fullName?.toLowerCase().includes(search.toLowerCase()) + ); + }, [obligations, search]); + + return ( + <> +
+ +
+
+ {filteredObligations.map((obligation) => ( + + ))} + {hasNext && ( + loadNext(20)} + /> + )} +
+ + ); +} + +type Obligation = NodeOf; + +function ObligationRow(props: { + obligation: Obligation; + linkedObligations: Set; + onLink: (obligationId: string) => void; + onUnlink: (obligationId: string) => void; + disabled?: boolean; +}) { + const { __ } = useTranslate(); + const isLinked = props.linkedObligations.has(props.obligation.id); + + const onToggle = () => { + if (isLinked) { + props.onUnlink(props.obligation.id); + } else { + props.onLink(props.obligation.id); + } + }; + + return ( +
+
+
+
+
+ {props.obligation.area || __("No area specified")} + {props.obligation.source || __("No source specified")} +
+
+ {props.obligation.owner?.fullName || __("Unassigned")} +
+
+ + {getObligationStatusLabel(props.obligation.status)} + +
+
+ +
+ ); +} diff --git a/apps/console/src/components/obligations/__generated__/LinkedObligationsCardFragment.graphql.ts b/apps/console/src/components/obligations/__generated__/LinkedObligationsCardFragment.graphql.ts new file mode 100644 index 000000000..7adc626fc --- /dev/null +++ b/apps/console/src/components/obligations/__generated__/LinkedObligationsCardFragment.graphql.ts @@ -0,0 +1,96 @@ +/** + * @generated SignedSource<<1126620dba70ca7a331c00347217eaa8>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +export type ObligationStatus = "COMPLIANT" | "NON_COMPLIANT" | "PARTIALLY_COMPLIANT"; +import { FragmentRefs } from "relay-runtime"; +export type LinkedObligationsCardFragment$data = { + readonly area: string | null | undefined; + readonly id: string; + readonly owner: { + readonly fullName: string; + }; + readonly requirement: string | null | undefined; + readonly source: string | null | undefined; + readonly status: ObligationStatus; + readonly " $fragmentType": "LinkedObligationsCardFragment"; +}; +export type LinkedObligationsCardFragment$key = { + readonly " $data"?: LinkedObligationsCardFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"LinkedObligationsCardFragment">; +}; + +const node: ReaderFragment = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "LinkedObligationsCardFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "requirement", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "area", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "source", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "owner", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "Obligation", + "abstractKey": null +}; + +(node as any).hash = "67ae0e6b0df090bb5a708bf7e029f4c5"; + +export default node; diff --git a/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogFragment.graphql.ts b/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogFragment.graphql.ts new file mode 100644 index 000000000..f82aed3f3 --- /dev/null +++ b/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogFragment.graphql.ts @@ -0,0 +1,260 @@ +/** + * @generated SignedSource<<4569a09dbc9fbf80fb787da46d0d058f>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +export type ObligationStatus = "COMPLIANT" | "NON_COMPLIANT" | "PARTIALLY_COMPLIANT"; +import { FragmentRefs } from "relay-runtime"; +export type LinkedObligationsDialogFragment$data = { + readonly id: string; + readonly obligations: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly area: string | null | undefined; + readonly id: string; + readonly owner: { + readonly fullName: string; + }; + readonly requirement: string | null | undefined; + readonly source: string | null | undefined; + readonly status: ObligationStatus; + }; + }>; + }; + readonly " $fragmentType": "LinkedObligationsDialogFragment"; +}; +export type LinkedObligationsDialogFragment$key = { + readonly " $data"?: LinkedObligationsDialogFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"LinkedObligationsDialogFragment">; +}; + +import LinkedObligationsDialogQuery_fragment_graphql from './LinkedObligationsDialogQuery_fragment.graphql'; + +const node: ReaderFragment = (function(){ +var v0 = [ + "obligations" +], +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "after" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "before" + }, + { + "defaultValue": 20, + "kind": "LocalArgument", + "name": "first" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "last" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "order" + } + ], + "kind": "Fragment", + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "bidirectional", + "path": (v0/*: any*/) + } + ], + "refetch": { + "connection": { + "forward": { + "count": "first", + "cursor": "after" + }, + "backward": { + "count": "last", + "cursor": "before" + }, + "path": (v0/*: any*/) + }, + "fragmentPathInResult": [ + "node" + ], + "operation": LinkedObligationsDialogQuery_fragment_graphql, + "identifierInfo": { + "identifierField": "id", + "identifierQueryVariableName": "id" + } + } + }, + "name": "LinkedObligationsDialogFragment", + "selections": [ + { + "alias": "obligations", + "args": [ + { + "kind": "Variable", + "name": "orderBy", + "variableName": "order" + } + ], + "concreteType": "ObligationConnection", + "kind": "LinkedField", + "name": "__LinkedObligationsDialogQuery_obligations_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v1/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "requirement", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "area", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "source", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "owner", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasPreviousPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "startCursor", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + (v1/*: any*/) + ], + "type": "Organization", + "abstractKey": null +}; +})(); + +(node as any).hash = "6e686f2a4bce776146b1985df2863e73"; + +export default node; diff --git a/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery.graphql.ts b/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery.graphql.ts new file mode 100644 index 000000000..ee8f959c9 --- /dev/null +++ b/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery.graphql.ts @@ -0,0 +1,278 @@ +/** + * @generated SignedSource<<44ee1c9c1b8d060d03736d1703d2a68a>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type LinkedObligationsDialogQuery$variables = { + organizationId: string; +}; +export type LinkedObligationsDialogQuery$data = { + readonly organization: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"LinkedObligationsDialogFragment">; + }; +}; +export type LinkedObligationsDialogQuery = { + response: LinkedObligationsDialogQuery$data; + variables: LinkedObligationsDialogQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "organizationId" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "organizationId" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v4 = [ + { + "kind": "Literal", + "name": "first", + "value": 20 + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "LinkedObligationsDialogQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "LinkedObligationsDialogFragment" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "LinkedObligationsDialogQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v4/*: any*/), + "concreteType": "ObligationConnection", + "kind": "LinkedField", + "name": "obligations", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "requirement", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "area", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "source", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "owner", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + }, + (v2/*: any*/) + ], + "storageKey": null + }, + (v3/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasPreviousPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "startCursor", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "obligations(first:20)" + }, + { + "alias": null, + "args": (v4/*: any*/), + "filters": [ + "orderBy" + ], + "handle": "connection", + "key": "LinkedObligationsDialogQuery_obligations", + "kind": "LinkedHandle", + "name": "obligations" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "8d4a5e57f79ae207dd1e27c436e49bd5", + "id": null, + "metadata": {}, + "name": "LinkedObligationsDialogQuery", + "operationKind": "query", + "text": "query LinkedObligationsDialogQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n ...LinkedObligationsDialogFragment\n }\n }\n}\n\nfragment LinkedObligationsDialogFragment on Organization {\n obligations(first: 20) {\n edges {\n node {\n id\n requirement\n area\n source\n status\n owner {\n fullName\n id\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 = "afd36a8a7dbe29044d2aefc3e3820fc8"; + +export default node; diff --git a/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery_fragment.graphql.ts b/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery_fragment.graphql.ts new file mode 100644 index 000000000..4a599d517 --- /dev/null +++ b/apps/console/src/components/obligations/__generated__/LinkedObligationsDialogQuery_fragment.graphql.ts @@ -0,0 +1,351 @@ +/** + * @generated SignedSource<<3d2709f51866093bcc78ec14501dae31>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type ObligationOrderField = "CREATED_AT" | "DUE_DATE" | "LAST_REVIEW_DATE" | "STATUS"; +export type OrderDirection = "ASC" | "DESC"; +export type ObligationOrder = { + direction: OrderDirection; + field: ObligationOrderField; +}; +export type LinkedObligationsDialogQuery_fragment$variables = { + after?: any | null | undefined; + before?: any | null | undefined; + first?: number | null | undefined; + id: string; + last?: number | null | undefined; + order?: ObligationOrder | null | undefined; +}; +export type LinkedObligationsDialogQuery_fragment$data = { + readonly node: { + readonly " $fragmentSpreads": FragmentRefs<"LinkedObligationsDialogFragment">; + }; +}; +export type LinkedObligationsDialogQuery_fragment = { + response: LinkedObligationsDialogQuery_fragment$data; + variables: LinkedObligationsDialogQuery_fragment$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "after" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "before" +}, +v2 = { + "defaultValue": 20, + "kind": "LocalArgument", + "name": "first" +}, +v3 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" +}, +v4 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "last" +}, +v5 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "order" +}, +v6 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v7 = { + "kind": "Variable", + "name": "after", + "variableName": "after" +}, +v8 = { + "kind": "Variable", + "name": "before", + "variableName": "before" +}, +v9 = { + "kind": "Variable", + "name": "first", + "variableName": "first" +}, +v10 = { + "kind": "Variable", + "name": "last", + "variableName": "last" +}, +v11 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v12 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v13 = [ + (v7/*: any*/), + (v8/*: any*/), + (v9/*: any*/), + (v10/*: any*/), + { + "kind": "Variable", + "name": "orderBy", + "variableName": "order" + } +]; +return { + "fragment": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/), + (v3/*: any*/), + (v4/*: any*/), + (v5/*: any*/) + ], + "kind": "Fragment", + "metadata": null, + "name": "LinkedObligationsDialogQuery_fragment", + "selections": [ + { + "alias": null, + "args": (v6/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": [ + (v7/*: any*/), + (v8/*: any*/), + (v9/*: any*/), + (v10/*: any*/), + { + "kind": "Variable", + "name": "order", + "variableName": "order" + } + ], + "kind": "FragmentSpread", + "name": "LinkedObligationsDialogFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/), + (v2/*: any*/), + (v4/*: any*/), + (v5/*: any*/), + (v3/*: any*/) + ], + "kind": "Operation", + "name": "LinkedObligationsDialogQuery_fragment", + "selections": [ + { + "alias": null, + "args": (v6/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v11/*: any*/), + (v12/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v13/*: any*/), + "concreteType": "ObligationConnection", + "kind": "LinkedField", + "name": "obligations", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v12/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "requirement", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "area", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "source", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "owner", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + }, + (v12/*: any*/) + ], + "storageKey": null + }, + (v11/*: any*/) + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasPreviousPage", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "startCursor", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": (v13/*: any*/), + "filters": [ + "orderBy" + ], + "handle": "connection", + "key": "LinkedObligationsDialogQuery_obligations", + "kind": "LinkedHandle", + "name": "obligations" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "a4640b163a373e0420a9fb1108ce7b3a", + "id": null, + "metadata": {}, + "name": "LinkedObligationsDialogQuery_fragment", + "operationKind": "query", + "text": "query LinkedObligationsDialogQuery_fragment(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: ObligationOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...LinkedObligationsDialogFragment_16fISc\n id\n }\n}\n\nfragment LinkedObligationsDialogFragment_16fISc on Organization {\n obligations(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n requirement\n area\n source\n status\n owner {\n fullName\n id\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 = "6e686f2a4bce776146b1985df2863e73"; + +export default node; diff --git a/apps/console/src/hooks/graph/ObligationGraph.ts b/apps/console/src/hooks/graph/ObligationGraph.ts index b33332eab..b088472d1 100644 --- a/apps/console/src/hooks/graph/ObligationGraph.ts +++ b/apps/console/src/hooks/graph/ObligationGraph.ts @@ -2,7 +2,7 @@ import { graphql } from "relay-runtime"; import { useMutation } from "react-relay"; import { useConfirm } from "@probo/ui"; import { useTranslate } from "@probo/i18n"; -import { promisifyMutation, sprintf } from "@probo/helpers"; +import { promisifyMutation } from "@probo/helpers"; import { useMutationWithToasts } from "../useMutationWithToasts"; export const ObligationsConnectionKey = "ObligationsPage_obligations"; @@ -24,7 +24,6 @@ export const obligationNodeQuery = graphql` id snapshotId sourceId - referenceId area source requirement @@ -57,7 +56,6 @@ export const createObligationMutation = graphql` obligationEdge @prependEdge(connections: $connections) { node { id - referenceId area source requirement @@ -82,7 +80,6 @@ export const updateObligationMutation = graphql` updateObligation(input: $input) { obligation { id - referenceId area source requirement @@ -113,7 +110,7 @@ export const deleteObligationMutation = graphql` `; export const useDeleteObligation = ( - obligation: { id: string; referenceId: string }, + obligation: { id: string }, connectionId: string ) => { const { __ } = useTranslate(); @@ -135,11 +132,8 @@ export const useDeleteObligation = ( }, }), { - message: sprintf( - __( - "This will permanently delete the obligation %s. This action cannot be undone." - ), - obligation.referenceId + message: __( + "This will permanently delete this obligation. This action cannot be undone." ), } ); @@ -152,7 +146,6 @@ export const useCreateObligation = (connectionId: string) => { return (input: { organizationId: string; - referenceId: string; area?: string; source?: string; requirement?: string; @@ -166,9 +159,6 @@ export const useCreateObligation = (connectionId: string) => { if (!input.organizationId) { return alert(__("Failed to create obligation: organization is required")); } - if (!input.referenceId) { - return alert(__("Failed to create obligation: reference ID is required")); - } if (!input.ownerId) { return alert(__("Failed to create obligation: owner is required")); } @@ -177,7 +167,6 @@ export const useCreateObligation = (connectionId: string) => { variables: { input: { organizationId: input.organizationId, - referenceId: input.referenceId, area: input.area, source: input.source, requirement: input.requirement, @@ -186,7 +175,7 @@ export const useCreateObligation = (connectionId: string) => { ownerId: input.ownerId, lastReviewDate: input.lastReviewDate, dueDate: input.dueDate, - status: input.status || "OPEN", + status: input.status || "NON_COMPLIANT", }, connections: [connectionId], }, @@ -200,7 +189,6 @@ export const useUpdateObligation = () => { return (input: { id: string; - referenceId?: string; area?: string; source?: string; requirement?: string; diff --git a/apps/console/src/hooks/graph/RiskGraph.ts b/apps/console/src/hooks/graph/RiskGraph.ts index c58963083..17ad0f038 100644 --- a/apps/console/src/hooks/graph/RiskGraph.ts +++ b/apps/console/src/hooks/graph/RiskGraph.ts @@ -125,11 +125,15 @@ export const riskNodeQuery = graphql` controlsInfo: controls(first: 0) { totalCount } + obligationsInfo: obligations(first: 0) { + totalCount + } ...useRiskFormFragment ...RiskOverviewTabFragment ...RiskMeasuresTabFragment ...RiskDocumentsTabFragment ...RiskControlsTabFragment + ...RiskObligationsTabFragment } } } diff --git a/apps/console/src/hooks/graph/__generated__/ObligationGraphCreateMutation.graphql.ts b/apps/console/src/hooks/graph/__generated__/ObligationGraphCreateMutation.graphql.ts index 3c9a3bd95..4eaf124a0 100644 --- a/apps/console/src/hooks/graph/__generated__/ObligationGraphCreateMutation.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/ObligationGraphCreateMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<0d77e4f7fb508d1c181575a45f0938d6>> + * @generated SignedSource<<6c659dc34af3bc7107a55d45f49075b5>> * @lightSyntaxTransform * @nogrep */ @@ -9,7 +9,7 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type ObligationStatus = "CLOSED" | "IN_PROGRESS" | "OPEN"; +export type ObligationStatus = "COMPLIANT" | "NON_COMPLIANT" | "PARTIALLY_COMPLIANT"; export type CreateObligationInput = { actionsToBeImplemented?: string | null | undefined; area?: string | null | undefined; @@ -17,7 +17,6 @@ export type CreateObligationInput = { lastReviewDate?: any | null | undefined; organizationId: string; ownerId: string; - referenceId: string; regulator?: string | null | undefined; requirement?: string | null | undefined; source?: string | null | undefined; @@ -41,7 +40,6 @@ export type ObligationGraphCreateMutation$data = { readonly fullName: string; readonly id: string; }; - readonly referenceId: string; readonly regulator: string | null | undefined; readonly requirement: string | null | undefined; readonly source: string | null | undefined; @@ -97,13 +95,6 @@ v4 = { "plural": false, "selections": [ (v3/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "referenceId", - "storageKey": null - }, { "alias": null, "args": null, @@ -258,16 +249,16 @@ return { ] }, "params": { - "cacheID": "8b01fc75c0c24f32a7892b7b8e38be72", + "cacheID": "76eae037102523c7378b07856332c9ff", "id": null, "metadata": {}, "name": "ObligationGraphCreateMutation", "operationKind": "mutation", - "text": "mutation ObligationGraphCreateMutation(\n $input: CreateObligationInput!\n) {\n createObligation(input: $input) {\n obligationEdge {\n node {\n id\n referenceId\n area\n source\n requirement\n actionsToBeImplemented\n regulator\n lastReviewDate\n dueDate\n status\n owner {\n id\n fullName\n }\n createdAt\n }\n }\n }\n}\n" + "text": "mutation ObligationGraphCreateMutation(\n $input: CreateObligationInput!\n) {\n createObligation(input: $input) {\n obligationEdge {\n node {\n id\n area\n source\n requirement\n actionsToBeImplemented\n regulator\n lastReviewDate\n dueDate\n status\n owner {\n id\n fullName\n }\n createdAt\n }\n }\n }\n}\n" } }; })(); -(node as any).hash = "67433dcbb82e3abbde25e2a8105c2789"; +(node as any).hash = "f3d8ddbef3566e26b3fe65543d93d07d"; export default node; diff --git a/apps/console/src/hooks/graph/__generated__/ObligationGraphListQuery.graphql.ts b/apps/console/src/hooks/graph/__generated__/ObligationGraphListQuery.graphql.ts index 1335e7f96..33a444579 100644 --- a/apps/console/src/hooks/graph/__generated__/ObligationGraphListQuery.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/ObligationGraphListQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<7d2959d10f1bc8e80f8ab1d1ea4cb45f>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -176,13 +176,6 @@ return { "name": "sourceId", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "referenceId", - "storageKey": null - }, { "alias": null, "args": null, @@ -347,12 +340,12 @@ return { ] }, "params": { - "cacheID": "1d94cb0e6a84428954ab8402ed2ca0ef", + "cacheID": "761aab4336741b33cc256ec5527011e4", "id": null, "metadata": {}, "name": "ObligationGraphListQuery", "operationKind": "query", - "text": "query ObligationGraphListQuery(\n $organizationId: ID!\n $snapshotId: ID\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n ...ObligationsPageFragment_3iomuz\n }\n id\n }\n}\n\nfragment ObligationsPageFragment_3iomuz on Organization {\n id\n obligations(first: 10, filter: {snapshotId: $snapshotId}) {\n totalCount\n edges {\n node {\n id\n snapshotId\n sourceId\n referenceId\n area\n source\n requirement\n status\n lastReviewDate\n dueDate\n actionsToBeImplemented\n regulator\n owner {\n id\n fullName\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n" + "text": "query ObligationGraphListQuery(\n $organizationId: ID!\n $snapshotId: ID\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n ...ObligationsPageFragment_3iomuz\n }\n id\n }\n}\n\nfragment ObligationsPageFragment_3iomuz on Organization {\n id\n obligations(first: 10, filter: {snapshotId: $snapshotId}) {\n totalCount\n edges {\n node {\n id\n snapshotId\n sourceId\n area\n source\n requirement\n status\n lastReviewDate\n dueDate\n actionsToBeImplemented\n regulator\n owner {\n id\n fullName\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n" } }; })(); diff --git a/apps/console/src/hooks/graph/__generated__/ObligationGraphNodeQuery.graphql.ts b/apps/console/src/hooks/graph/__generated__/ObligationGraphNodeQuery.graphql.ts index 8c131e437..170dcd265 100644 --- a/apps/console/src/hooks/graph/__generated__/ObligationGraphNodeQuery.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/ObligationGraphNodeQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -9,7 +9,7 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type ObligationStatus = "CLOSED" | "IN_PROGRESS" | "OPEN"; +export type ObligationStatus = "COMPLIANT" | "NON_COMPLIANT" | "PARTIALLY_COMPLIANT"; export type ObligationGraphNodeQuery$variables = { obligationId: string; }; @@ -29,7 +29,6 @@ export type ObligationGraphNodeQuery$data = { readonly fullName: string; readonly id: string; }; - readonly referenceId?: string; readonly regulator?: string | null | undefined; readonly requirement?: string | null | undefined; readonly snapshotId?: string | null | undefined; @@ -84,66 +83,59 @@ v5 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "referenceId", + "name": "area", "storageKey": null }, v6 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "area", + "name": "source", "storageKey": null }, v7 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "source", + "name": "requirement", "storageKey": null }, v8 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "requirement", + "name": "actionsToBeImplemented", "storageKey": null }, v9 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "actionsToBeImplemented", + "name": "regulator", "storageKey": null }, v10 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "regulator", + "name": "lastReviewDate", "storageKey": null }, v11 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "lastReviewDate", + "name": "dueDate", "storageKey": null }, v12 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "dueDate", - "storageKey": null -}, -v13 = { "alias": null, "args": null, "kind": "ScalarField", "name": "status", "storageKey": null }, -v14 = { +v13 = { "alias": null, "args": null, "concreteType": "People", @@ -162,7 +154,7 @@ v14 = { ], "storageKey": null }, -v15 = { +v14 = { "alias": null, "args": null, "concreteType": "Organization", @@ -181,14 +173,14 @@ v15 = { ], "storageKey": null }, -v16 = { +v15 = { "alias": null, "args": null, "kind": "ScalarField", "name": "createdAt", "storageKey": null }, -v17 = { +v16 = { "alias": null, "args": null, "kind": "ScalarField", @@ -227,8 +219,7 @@ return { (v13/*: any*/), (v14/*: any*/), (v15/*: any*/), - (v16/*: any*/), - (v17/*: any*/) + (v16/*: any*/) ], "type": "Obligation", "abstractKey": null @@ -278,8 +269,7 @@ return { (v13/*: any*/), (v14/*: any*/), (v15/*: any*/), - (v16/*: any*/), - (v17/*: any*/) + (v16/*: any*/) ], "type": "Obligation", "abstractKey": null @@ -290,16 +280,16 @@ return { ] }, "params": { - "cacheID": "1cc13435272f22a8167b370cca105184", + "cacheID": "6fd1c9d6e9f9a5baa60e4d270fc16db7", "id": null, "metadata": {}, "name": "ObligationGraphNodeQuery", "operationKind": "query", - "text": "query ObligationGraphNodeQuery(\n $obligationId: ID!\n) {\n node(id: $obligationId) {\n __typename\n ... on Obligation {\n id\n snapshotId\n sourceId\n referenceId\n area\n source\n requirement\n actionsToBeImplemented\n regulator\n lastReviewDate\n dueDate\n status\n owner {\n id\n fullName\n }\n organization {\n id\n name\n }\n createdAt\n updatedAt\n }\n id\n }\n}\n" + "text": "query ObligationGraphNodeQuery(\n $obligationId: ID!\n) {\n node(id: $obligationId) {\n __typename\n ... on Obligation {\n id\n snapshotId\n sourceId\n area\n source\n requirement\n actionsToBeImplemented\n regulator\n lastReviewDate\n dueDate\n status\n owner {\n id\n fullName\n }\n organization {\n id\n name\n }\n createdAt\n updatedAt\n }\n id\n }\n}\n" } }; })(); -(node as any).hash = "f7c98b6669bae79c42892dd296e9dd07"; +(node as any).hash = "ddefa1f2514f429a8ff174646bccf254"; export default node; diff --git a/apps/console/src/hooks/graph/__generated__/ObligationGraphUpdateMutation.graphql.ts b/apps/console/src/hooks/graph/__generated__/ObligationGraphUpdateMutation.graphql.ts index 8eaa1c554..485ce8d8a 100644 --- a/apps/console/src/hooks/graph/__generated__/ObligationGraphUpdateMutation.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/ObligationGraphUpdateMutation.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<7978f1bb5fdbb6129f0e6217574af621>> + * @generated SignedSource<<2fb713ad39d2976a25e4fee57edec2f3>> * @lightSyntaxTransform * @nogrep */ @@ -9,7 +9,7 @@ // @ts-nocheck import { ConcreteRequest } from 'relay-runtime'; -export type ObligationStatus = "CLOSED" | "IN_PROGRESS" | "OPEN"; +export type ObligationStatus = "COMPLIANT" | "NON_COMPLIANT" | "PARTIALLY_COMPLIANT"; export type UpdateObligationInput = { actionsToBeImplemented?: string | null | undefined; area?: string | null | undefined; @@ -17,7 +17,6 @@ export type UpdateObligationInput = { id: string; lastReviewDate?: any | null | undefined; ownerId?: string | null | undefined; - referenceId?: string | null | undefined; regulator?: string | null | undefined; requirement?: string | null | undefined; source?: string | null | undefined; @@ -38,7 +37,6 @@ export type ObligationGraphUpdateMutation$data = { readonly fullName: string; readonly id: string; }; - readonly referenceId: string; readonly regulator: string | null | undefined; readonly requirement: string | null | undefined; readonly source: string | null | undefined; @@ -91,13 +89,6 @@ v2 = [ "plural": false, "selections": [ (v1/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "referenceId", - "storageKey": null - }, { "alias": null, "args": null, @@ -205,16 +196,16 @@ return { "selections": (v2/*: any*/) }, "params": { - "cacheID": "f3f7c1657e914067e7543bf98c91c2d0", + "cacheID": "e42654b0cfd6ebeb8c78e8ec2f62c9c7", "id": null, "metadata": {}, "name": "ObligationGraphUpdateMutation", "operationKind": "mutation", - "text": "mutation ObligationGraphUpdateMutation(\n $input: UpdateObligationInput!\n) {\n updateObligation(input: $input) {\n obligation {\n id\n referenceId\n area\n source\n requirement\n actionsToBeImplemented\n regulator\n lastReviewDate\n dueDate\n status\n owner {\n id\n fullName\n }\n updatedAt\n }\n }\n}\n" + "text": "mutation ObligationGraphUpdateMutation(\n $input: UpdateObligationInput!\n) {\n updateObligation(input: $input) {\n obligation {\n id\n area\n source\n requirement\n actionsToBeImplemented\n regulator\n lastReviewDate\n dueDate\n status\n owner {\n id\n fullName\n }\n updatedAt\n }\n }\n}\n" } }; })(); -(node as any).hash = "4de068d83b83280ea680aad303c84098"; +(node as any).hash = "68013ca15f0e4eaaa0b1ef877b4f528c"; export default node; diff --git a/apps/console/src/hooks/graph/__generated__/RiskGraphNodeQuery.graphql.ts b/apps/console/src/hooks/graph/__generated__/RiskGraphNodeQuery.graphql.ts index 34eedc1d6..1478ae6d4 100644 --- a/apps/console/src/hooks/graph/__generated__/RiskGraphNodeQuery.graphql.ts +++ b/apps/console/src/hooks/graph/__generated__/RiskGraphNodeQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<9abcf06eed9cb70f62b2aed2b907e9e5>> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -30,6 +30,9 @@ export type RiskGraphNodeQuery$data = { }; readonly name?: string; readonly note?: string; + readonly obligationsInfo?: { + readonly totalCount: number; + }; readonly owner?: { readonly fullName: string; readonly id: string; @@ -37,7 +40,7 @@ export type RiskGraphNodeQuery$data = { readonly residualRiskScore?: number; readonly snapshotId?: string | null | undefined; readonly treatment?: RiskTreatment; - readonly " $fragmentSpreads": FragmentRefs<"RiskControlsTabFragment" | "RiskDocumentsTabFragment" | "RiskMeasuresTabFragment" | "RiskOverviewTabFragment" | "useRiskFormFragment">; + readonly " $fragmentSpreads": FragmentRefs<"RiskControlsTabFragment" | "RiskDocumentsTabFragment" | "RiskMeasuresTabFragment" | "RiskObligationsTabFragment" | "RiskOverviewTabFragment" | "useRiskFormFragment">; }; }; export type RiskGraphNodeQuery = { @@ -96,6 +99,13 @@ v6 = { "storageKey": null }, v7 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null +}, +v8 = { "alias": null, "args": null, "concreteType": "People", @@ -104,45 +114,39 @@ v7 = { "plural": false, "selections": [ (v2/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "fullName", - "storageKey": null - } + (v7/*: any*/) ], "storageKey": null }, -v8 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "note", - "storageKey": null -}, v9 = { "alias": null, "args": null, "kind": "ScalarField", - "name": "inherentRiskScore", + "name": "note", "storageKey": null }, v10 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "inherentRiskScore", + "storageKey": null +}, +v11 = { "alias": null, "args": null, "kind": "ScalarField", "name": "residualRiskScore", "storageKey": null }, -v11 = [ +v12 = [ { "kind": "Literal", "name": "first", "value": 0 } ], -v12 = [ +v13 = [ { "alias": null, "args": null, @@ -151,72 +155,82 @@ v12 = [ "storageKey": null } ], -v13 = { +v14 = { "alias": "measuresInfo", - "args": (v11/*: any*/), + "args": (v12/*: any*/), "concreteType": "MeasureConnection", "kind": "LinkedField", "name": "measures", "plural": false, - "selections": (v12/*: any*/), + "selections": (v13/*: any*/), "storageKey": "measures(first:0)" }, -v14 = { +v15 = { "alias": "documentsInfo", - "args": (v11/*: any*/), + "args": (v12/*: any*/), "concreteType": "DocumentConnection", "kind": "LinkedField", "name": "documents", "plural": false, - "selections": (v12/*: any*/), + "selections": (v13/*: any*/), "storageKey": "documents(first:0)" }, -v15 = { +v16 = { "alias": "controlsInfo", - "args": (v11/*: any*/), + "args": (v12/*: any*/), "concreteType": "ControlConnection", "kind": "LinkedField", "name": "controls", "plural": false, - "selections": (v12/*: any*/), + "selections": (v13/*: any*/), "storageKey": "controls(first:0)" }, -v16 = { +v17 = { + "alias": "obligationsInfo", + "args": (v12/*: any*/), + "concreteType": "ObligationConnection", + "kind": "LinkedField", + "name": "obligations", + "plural": false, + "selections": (v13/*: any*/), + "storageKey": "obligations(first:0)" +}, +v18 = { "alias": null, "args": null, "kind": "ScalarField", "name": "__typename", "storageKey": null }, -v17 = [ +v19 = [ { "kind": "Literal", "name": "first", "value": 100 } ], -v18 = { +v20 = { "alias": null, "args": null, "kind": "ScalarField", "name": "cursor", "storageKey": null }, -v19 = { +v21 = { "alias": null, "args": null, "kind": "ScalarField", "name": "endCursor", "storageKey": null }, -v20 = { +v22 = { "alias": null, "args": null, "kind": "ScalarField", "name": "hasNextPage", "storageKey": null }, -v21 = { +v23 = { "alias": null, "args": null, "concreteType": "PageInfo", @@ -224,12 +238,12 @@ v21 = { "name": "pageInfo", "plural": false, "selections": [ - (v19/*: any*/), - (v20/*: any*/) + (v21/*: any*/), + (v22/*: any*/) ], "storageKey": null }, -v22 = { +v24 = { "kind": "ClientExtension", "selections": [ { @@ -241,7 +255,14 @@ v22 = { } ] }, -v23 = [ +v25 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null +}, +v26 = [ { "kind": "Literal", "name": "first", @@ -271,13 +292,14 @@ return { (v4/*: any*/), (v5/*: any*/), (v6/*: any*/), - (v7/*: any*/), (v8/*: any*/), (v9/*: any*/), (v10/*: any*/), - (v13/*: any*/), + (v11/*: any*/), (v14/*: any*/), (v15/*: any*/), + (v16/*: any*/), + (v17/*: any*/), { "args": null, "kind": "FragmentSpread", @@ -302,6 +324,11 @@ return { "args": null, "kind": "FragmentSpread", "name": "RiskControlsTabFragment" + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "RiskObligationsTabFragment" } ], "type": "Risk", @@ -328,7 +355,7 @@ return { "name": "node", "plural": false, "selections": [ - (v16/*: any*/), + (v18/*: any*/), (v2/*: any*/), { "kind": "InlineFragment", @@ -337,13 +364,14 @@ return { (v4/*: any*/), (v5/*: any*/), (v6/*: any*/), - (v7/*: any*/), (v8/*: any*/), (v9/*: any*/), (v10/*: any*/), - (v13/*: any*/), + (v11/*: any*/), (v14/*: any*/), (v15/*: any*/), + (v16/*: any*/), + (v17/*: any*/), { "alias": null, "args": null, @@ -381,7 +409,7 @@ return { }, { "alias": null, - "args": (v17/*: any*/), + "args": (v19/*: any*/), "concreteType": "MeasureConnection", "kind": "LinkedField", "name": "measures", @@ -412,22 +440,22 @@ return { "name": "state", "storageKey": null }, - (v16/*: any*/) + (v18/*: any*/) ], "storageKey": null }, - (v18/*: any*/) + (v20/*: any*/) ], "storageKey": null }, - (v21/*: any*/), - (v22/*: any*/) + (v23/*: any*/), + (v24/*: any*/) ], "storageKey": "measures(first:100)" }, { "alias": null, - "args": (v17/*: any*/), + "args": (v19/*: any*/), "filters": null, "handle": "connection", "key": "Risk__measures", @@ -436,7 +464,7 @@ return { }, { "alias": null, - "args": (v17/*: any*/), + "args": (v19/*: any*/), "concreteType": "DocumentConnection", "kind": "LinkedField", "name": "documents", @@ -511,13 +539,7 @@ return { "plural": false, "selections": [ (v2/*: any*/), - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "status", - "storageKey": null - } + (v25/*: any*/) ], "storageKey": null } @@ -527,22 +549,22 @@ return { ], "storageKey": "versions(first:1)" }, - (v16/*: any*/) + (v18/*: any*/) ], "storageKey": null }, - (v18/*: any*/) + (v20/*: any*/) ], "storageKey": null }, - (v21/*: any*/), - (v22/*: any*/) + (v23/*: any*/), + (v24/*: any*/) ], "storageKey": "documents(first:100)" }, { "alias": null, - "args": (v17/*: any*/), + "args": (v19/*: any*/), "filters": null, "handle": "connection", "key": "Risk__documents", @@ -551,7 +573,7 @@ return { }, { "alias": null, - "args": (v23/*: any*/), + "args": (v26/*: any*/), "concreteType": "ControlConnection", "kind": "LinkedField", "name": "controls", @@ -595,11 +617,11 @@ return { ], "storageKey": null }, - (v16/*: any*/) + (v18/*: any*/) ], "storageKey": null }, - (v18/*: any*/) + (v20/*: any*/) ], "storageKey": null }, @@ -611,8 +633,8 @@ return { "name": "pageInfo", "plural": false, "selections": [ - (v19/*: any*/), - (v20/*: any*/), + (v21/*: any*/), + (v22/*: any*/), { "alias": null, "args": null, @@ -635,7 +657,7 @@ return { }, { "alias": null, - "args": (v23/*: any*/), + "args": (v26/*: any*/), "filters": [ "orderBy", "filter" @@ -644,6 +666,88 @@ return { "key": "RiskControlsTab_controls", "kind": "LinkedHandle", "name": "controls" + }, + { + "alias": null, + "args": (v19/*: any*/), + "concreteType": "ObligationConnection", + "kind": "LinkedField", + "name": "obligations", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "requirement", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "area", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "source", + "storageKey": null + }, + (v25/*: any*/), + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "owner", + "plural": false, + "selections": [ + (v7/*: any*/), + (v2/*: any*/) + ], + "storageKey": null + }, + (v18/*: any*/) + ], + "storageKey": null + }, + (v20/*: any*/) + ], + "storageKey": null + }, + (v23/*: any*/), + (v24/*: any*/) + ], + "storageKey": "obligations(first:100)" + }, + { + "alias": null, + "args": (v19/*: any*/), + "filters": null, + "handle": "connection", + "key": "Risk__obligations", + "kind": "LinkedHandle", + "name": "obligations" } ], "type": "Risk", @@ -655,16 +759,16 @@ return { ] }, "params": { - "cacheID": "c3d393180d6273b2e910c7ebffb75df7", + "cacheID": "a909af9427061d9e06e22798cbe1ba14", "id": null, "metadata": {}, "name": "RiskGraphNodeQuery", "operationKind": "query", - "text": "query RiskGraphNodeQuery(\n $riskId: ID!\n) {\n node(id: $riskId) {\n __typename\n ... on Risk {\n id\n snapshotId\n name\n description\n treatment\n owner {\n id\n fullName\n }\n note\n inherentRiskScore\n residualRiskScore\n measuresInfo: measures(first: 0) {\n totalCount\n }\n documentsInfo: documents(first: 0) {\n totalCount\n }\n controlsInfo: controls(first: 0) {\n totalCount\n }\n ...useRiskFormFragment\n ...RiskOverviewTabFragment\n ...RiskMeasuresTabFragment\n ...RiskDocumentsTabFragment\n ...RiskControlsTabFragment\n }\n id\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n\nfragment RiskControlsTabFragment on Risk {\n id\n controls(first: 20) {\n edges {\n node {\n id\n sectionTitle\n name\n framework {\n id\n name\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment RiskDocumentsTabFragment on Risk {\n id\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment RiskMeasuresTabFragment on Risk {\n id\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment RiskOverviewTabFragment on Risk {\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n inherentRiskScore\n residualRiskScore\n}\n\nfragment useRiskFormFragment on Risk {\n id\n name\n category\n description\n treatment\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n inherentRiskScore\n residualRiskScore\n note\n owner {\n id\n }\n}\n" + "text": "query RiskGraphNodeQuery(\n $riskId: ID!\n) {\n node(id: $riskId) {\n __typename\n ... on Risk {\n id\n snapshotId\n name\n description\n treatment\n owner {\n id\n fullName\n }\n note\n inherentRiskScore\n residualRiskScore\n measuresInfo: measures(first: 0) {\n totalCount\n }\n documentsInfo: documents(first: 0) {\n totalCount\n }\n controlsInfo: controls(first: 0) {\n totalCount\n }\n obligationsInfo: obligations(first: 0) {\n totalCount\n }\n ...useRiskFormFragment\n ...RiskOverviewTabFragment\n ...RiskMeasuresTabFragment\n ...RiskDocumentsTabFragment\n ...RiskControlsTabFragment\n ...RiskObligationsTabFragment\n }\n id\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n\nfragment LinkedObligationsCardFragment on Obligation {\n id\n requirement\n area\n source\n status\n owner {\n fullName\n id\n }\n}\n\nfragment RiskControlsTabFragment on Risk {\n id\n controls(first: 20) {\n edges {\n node {\n id\n sectionTitle\n name\n framework {\n id\n name\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment RiskDocumentsTabFragment on Risk {\n id\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment RiskMeasuresTabFragment on Risk {\n id\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment RiskObligationsTabFragment on Risk {\n id\n obligations(first: 100) {\n edges {\n node {\n id\n ...LinkedObligationsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment RiskOverviewTabFragment on Risk {\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n inherentRiskScore\n residualRiskScore\n}\n\nfragment useRiskFormFragment on Risk {\n id\n name\n category\n description\n treatment\n inherentLikelihood\n inherentImpact\n residualLikelihood\n residualImpact\n inherentRiskScore\n residualRiskScore\n note\n owner {\n id\n }\n}\n" } }; })(); -(node as any).hash = "43dcdf60f1d1b28414650fe5f9294faa"; +(node as any).hash = "f173ffcac7b82f1b62394cb74d1a78ee"; export default node; diff --git a/apps/console/src/pages/organizations/obligations/ObligationDetailsPage.tsx b/apps/console/src/pages/organizations/obligations/ObligationDetailsPage.tsx index 61921f868..ea70ff2bd 100644 --- a/apps/console/src/pages/organizations/obligations/ObligationDetailsPage.tsx +++ b/apps/console/src/pages/organizations/obligations/ObligationDetailsPage.tsx @@ -31,12 +31,11 @@ import { PeopleSelectField } from "/components/form/PeopleSelectField"; import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { Controller } from "react-hook-form"; import z from "zod"; -import { getStatusVariant, getStatusLabel, formatDatetime, getStatusOptions, validateSnapshotConsistency } from "@probo/helpers"; +import { getObligationStatusVariant, getObligationStatusLabel, formatDatetime, getObligationStatusOptions, validateSnapshotConsistency } from "@probo/helpers"; import { SnapshotBanner } from "/components/SnapshotBanner"; import type { ObligationGraphNodeQuery } from "/hooks/graph/__generated__/ObligationGraphNodeQuery.graphql"; const updateObligationSchema = z.object({ - referenceId: z.string().min(1, "Reference ID is required"), area: z.string().optional(), source: z.string().optional(), requirement: z.string().optional(), @@ -44,7 +43,7 @@ const updateObligationSchema = z.object({ regulator: z.string().optional(), lastReviewDate: z.string().optional(), dueDate: z.string().optional(), - status: z.enum(["OPEN", "IN_PROGRESS", "CLOSED"]), + status: z.enum(["NON_COMPLIANT", "PARTIALLY_COMPLIANT", "COMPLIANT"]), ownerId: z.string().min(1, "Owner is required"), }); @@ -68,7 +67,7 @@ export default function ObligationDetailsPage(props: Props) { validateSnapshotConsistency(obligation, snapshotId); const updateObligation = useUpdateObligation(); - const statusOptions = getStatusOptions(__); + const statusOptions = getObligationStatusOptions(__); const connectionId = ConnectionHandler.getConnectionID( organizationId, @@ -76,7 +75,7 @@ export default function ObligationDetailsPage(props: Props) { ); const deleteObligation = useDeleteObligation( - { id: obligation?.id!, referenceId: obligation?.referenceId! }, + { id: obligation?.id! }, connectionId ); @@ -84,7 +83,6 @@ export default function ObligationDetailsPage(props: Props) { updateObligationSchema, { defaultValues: { - referenceId: obligation?.referenceId || "", area: obligation?.area || "", source: obligation?.source || "", requirement: obligation?.requirement || "", @@ -96,7 +94,7 @@ export default function ObligationDetailsPage(props: Props) { dueDate: obligation?.dueDate ? new Date(obligation.dueDate).toISOString().split("T")[0] : "", - status: obligation?.status || "OPEN", + status: obligation?.status ?? "NON_COMPLIANT", ownerId: obligation?.owner?.id || "", }, } @@ -106,7 +104,6 @@ export default function ObligationDetailsPage(props: Props) { try { await updateObligation({ id: obligation.id!, - referenceId: formData.referenceId, area: formData.area || undefined, source: formData.source || undefined, requirement: formData.requirement || undefined, @@ -116,7 +113,6 @@ export default function ObligationDetailsPage(props: Props) { dueDate: formatDatetime(formData.dueDate), status: formData.status, ownerId: formData.ownerId, - }); toast({ @@ -147,13 +143,13 @@ export default function ObligationDetailsPage(props: Props) {
-

{obligation.referenceId}

- - {getStatusLabel(obligation.status || "OPEN")} +

{__("Obligation")}

+ + {getObligationStatusLabel(obligation.status ?? "NON_COMPLIANT")}
@@ -169,19 +165,6 @@ export default function ObligationDetailsPage(props: Props) {
- - - - -
- {__("Reference ID")} {__("Area")} {__("Source")} {__("Status")} @@ -218,11 +216,8 @@ function ObligationRow({ }, }), { - message: sprintf( - __( - "This will permanently delete the obligation %s. This action cannot be undone." - ), - obligation.referenceId + message: __( + "This will permanently delete this obligation. This action cannot be undone." ), } ); @@ -234,14 +229,11 @@ function ObligationRow({ return ( - - {obligation.referenceId} - {obligation.area || "-"} {obligation.source || "-"} - - {getStatusLabel(obligation.status || "OPEN")} + + {getObligationStatusLabel(obligation.status || "NON_COMPLIANT")} {obligation.owner?.fullName || "-"} diff --git a/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageFragment.graphql.ts b/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageFragment.graphql.ts index 319dcf5b8..3c42a813d 100644 --- a/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageFragment.graphql.ts +++ b/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageFragment.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<<5f839350ed835351175b07d38512ba37>> + * @generated SignedSource<<27cacc02131c03f5aeaaff764e3bb226>> * @lightSyntaxTransform * @nogrep */ @@ -9,7 +9,7 @@ // @ts-nocheck import { ReaderFragment } from 'relay-runtime'; -export type ObligationStatus = "CLOSED" | "IN_PROGRESS" | "OPEN"; +export type ObligationStatus = "COMPLIANT" | "NON_COMPLIANT" | "PARTIALLY_COMPLIANT"; import { FragmentRefs } from "relay-runtime"; export type ObligationsPageFragment$data = { readonly id: string; @@ -27,7 +27,6 @@ export type ObligationsPageFragment$data = { readonly fullName: string; readonly id: string; }; - readonly referenceId: string; readonly regulator: string | null | undefined; readonly requirement: string | null | undefined; readonly snapshotId: string | null | undefined; @@ -171,13 +170,6 @@ return { "name": "sourceId", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "referenceId", - "storageKey": null - }, { "alias": null, "args": null, @@ -333,6 +325,6 @@ return { }; })(); -(node as any).hash = "0d7452faf6e02f97885a65819dfb112b"; +(node as any).hash = "a5dda6d90c9205f7c7d6ea6d84d760f1"; export default node; diff --git a/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageQuery.graphql.ts b/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageQuery.graphql.ts index 3997b5281..985c7dd35 100644 --- a/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageQuery.graphql.ts +++ b/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<445c6cc243eadbe9e5eacdec0161ee62>> * @lightSyntaxTransform * @nogrep */ @@ -176,13 +176,6 @@ return { "name": "sourceId", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "referenceId", - "storageKey": null - }, { "alias": null, "args": null, @@ -347,12 +340,12 @@ return { ] }, "params": { - "cacheID": "7e61419b20d9cf30e5dd31b4500bc324", + "cacheID": "9a4d2f4ac3be91001f8c9bb602cd599f", "id": null, "metadata": {}, "name": "ObligationsPageQuery", "operationKind": "query", - "text": "query ObligationsPageQuery(\n $organizationId: ID!\n $snapshotId: ID\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n ...ObligationsPageFragment_3iomuz\n }\n id\n }\n}\n\nfragment ObligationsPageFragment_3iomuz on Organization {\n id\n obligations(first: 10, filter: {snapshotId: $snapshotId}) {\n totalCount\n edges {\n node {\n id\n snapshotId\n sourceId\n referenceId\n area\n source\n requirement\n status\n lastReviewDate\n dueDate\n actionsToBeImplemented\n regulator\n owner {\n id\n fullName\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n" + "text": "query ObligationsPageQuery(\n $organizationId: ID!\n $snapshotId: ID\n) {\n node(id: $organizationId) {\n __typename\n ... on Organization {\n ...ObligationsPageFragment_3iomuz\n }\n id\n }\n}\n\nfragment ObligationsPageFragment_3iomuz on Organization {\n id\n obligations(first: 10, filter: {snapshotId: $snapshotId}) {\n totalCount\n edges {\n node {\n id\n snapshotId\n sourceId\n area\n source\n requirement\n status\n lastReviewDate\n dueDate\n actionsToBeImplemented\n regulator\n owner {\n id\n fullName\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n" } }; })(); diff --git a/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageRefetchQuery.graphql.ts b/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageRefetchQuery.graphql.ts index a8d9c600b..06fee343b 100644 --- a/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageRefetchQuery.graphql.ts +++ b/apps/console/src/pages/organizations/obligations/__generated__/ObligationsPageRefetchQuery.graphql.ts @@ -1,5 +1,5 @@ /** - * @generated SignedSource<> + * @generated SignedSource<<3b2a48df3fdef9f91bc4520ed2448423>> * @lightSyntaxTransform * @nogrep */ @@ -200,13 +200,6 @@ return { "name": "sourceId", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "referenceId", - "storageKey": null - }, { "alias": null, "args": null, @@ -371,16 +364,16 @@ return { ] }, "params": { - "cacheID": "f34cf1412b7ff3e92971dfac07ca06ac", + "cacheID": "0500f4aee677d8d1ec53cd189e350395", "id": null, "metadata": {}, "name": "ObligationsPageRefetchQuery", "operationKind": "query", - "text": "query ObligationsPageRefetchQuery(\n $after: CursorKey\n $first: Int = 10\n $snapshotId: ID = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...ObligationsPageFragment_35e0S5\n id\n }\n}\n\nfragment ObligationsPageFragment_35e0S5 on Organization {\n id\n obligations(first: $first, after: $after, filter: {snapshotId: $snapshotId}) {\n totalCount\n edges {\n node {\n id\n snapshotId\n sourceId\n referenceId\n area\n source\n requirement\n status\n lastReviewDate\n dueDate\n actionsToBeImplemented\n regulator\n owner {\n id\n fullName\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n" + "text": "query ObligationsPageRefetchQuery(\n $after: CursorKey\n $first: Int = 10\n $snapshotId: ID = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...ObligationsPageFragment_35e0S5\n id\n }\n}\n\nfragment ObligationsPageFragment_35e0S5 on Organization {\n id\n obligations(first: $first, after: $after, filter: {snapshotId: $snapshotId}) {\n totalCount\n edges {\n node {\n id\n snapshotId\n sourceId\n area\n source\n requirement\n status\n lastReviewDate\n dueDate\n actionsToBeImplemented\n regulator\n owner {\n id\n fullName\n }\n createdAt\n updatedAt\n __typename\n }\n cursor\n }\n pageInfo {\n hasNextPage\n endCursor\n }\n }\n}\n" } }; })(); -(node as any).hash = "0d7452faf6e02f97885a65819dfb112b"; +(node as any).hash = "a5dda6d90c9205f7c7d6ea6d84d760f1"; export default node; diff --git a/apps/console/src/pages/organizations/obligations/dialogs/CreateObligationDialog.tsx b/apps/console/src/pages/organizations/obligations/dialogs/CreateObligationDialog.tsx index 5fe95e859..3a39f2a49 100644 --- a/apps/console/src/pages/organizations/obligations/dialogs/CreateObligationDialog.tsx +++ b/apps/console/src/pages/organizations/obligations/dialogs/CreateObligationDialog.tsx @@ -20,10 +20,9 @@ import { useFormWithSchema } from "/hooks/useFormWithSchema"; import { useCreateObligation } from "../../../../hooks/graph/ObligationGraph"; import { PeopleSelectField } from "/components/form/PeopleSelectField"; import { Controller } from "react-hook-form"; -import { formatDatetime, getStatusOptions } from "@probo/helpers"; +import { formatDatetime, getObligationStatusOptions } from "@probo/helpers"; const schema = z.object({ - referenceId: z.string().min(1, "Reference ID is required"), area: z.string().optional(), source: z.string().optional(), requirement: z.string().optional(), @@ -32,7 +31,7 @@ const schema = z.object({ ownerId: z.string().min(1, "Owner is required"), lastReviewDate: z.string().optional(), dueDate: z.string().optional(), - status: z.enum(["OPEN", "IN_PROGRESS", "CLOSED"]), + status: z.enum(["NON_COMPLIANT", "PARTIALLY_COMPLIANT", "COMPLIANT"]), }); type FormData = z.infer; @@ -53,11 +52,10 @@ export function CreateObligationDialog({ const dialogRef = useDialogRef(); const createObligation = useCreateObligation(connection || ""); - const statusOptions = getStatusOptions(__); + const statusOptions = getObligationStatusOptions(__); const { register, handleSubmit, formState, reset, control } = useFormWithSchema(schema, { defaultValues: { - referenceId: "", area: "", source: "", requirement: "", @@ -66,7 +64,7 @@ export function CreateObligationDialog({ ownerId: "", lastReviewDate: "", dueDate: "", - status: "OPEN" as const, + status: "NON_COMPLIANT" as const, }, }); @@ -74,7 +72,6 @@ export function CreateObligationDialog({ try { await createObligation({ organizationId, - referenceId: formData.referenceId, area: formData.area || undefined, source: formData.source || undefined, requirement: formData.requirement || undefined, @@ -112,14 +109,6 @@ export function CreateObligationDialog({ > - -
{controlsCount} + + {__("Obligations")} + {obligationsCount} + )} diff --git a/apps/console/src/pages/organizations/risks/tabs/RiskObligationsTab.tsx b/apps/console/src/pages/organizations/risks/tabs/RiskObligationsTab.tsx new file mode 100644 index 000000000..243792547 --- /dev/null +++ b/apps/console/src/pages/organizations/risks/tabs/RiskObligationsTab.tsx @@ -0,0 +1,87 @@ +import { graphql, useFragment } from "react-relay"; +import type { RiskObligationsTabFragment$key } from "./__generated__/RiskObligationsTabFragment.graphql"; +import { useOutletContext } from "react-router"; +import { LinkedObligationsCard } from "/components/obligations/LinkedObligationsCard"; +import { useMutationWithIncrement } from "/hooks/useMutationWithIncrement"; + +export const obligationsFragment = graphql` + fragment RiskObligationsTabFragment on Risk { + id + obligations(first: 100) @connection(key: "Risk__obligations") { + __id + edges { + node { + id + ...LinkedObligationsCardFragment + } + } + } + } +`; + +const attachObligationMutation = graphql` + mutation RiskObligationsTabCreateMutation( + $input: CreateRiskObligationMappingInput! + $connections: [ID!]! + ) { + createRiskObligationMapping(input: $input) { + obligationEdge @prependEdge(connections: $connections) { + node { + id + ...LinkedObligationsCardFragment + } + } + } + } +`; + +export const detachObligationMutation = graphql` + mutation RiskObligationsTabDetachMutation( + $input: DeleteRiskObligationMappingInput! + $connections: [ID!]! + ) { + deleteRiskObligationMapping(input: $input) { + deletedObligationId @deleteEdge(connections: $connections) + } + } +`; + +export default function RiskObligationsTab() { + const { risk } = useOutletContext<{ + risk: RiskObligationsTabFragment$key & { id: string }; + }>(); + const data = useFragment(obligationsFragment, risk); + const connectionId = data.obligations.__id; + const obligations = data.obligations?.edges?.map((edge) => edge.node) ?? []; + const incrementOptions = { + id: data.id, + node: "obligations(first:0)", + }; + const [detachObligation, isDetaching] = useMutationWithIncrement( + detachObligationMutation, + { + ...incrementOptions, + value: -1, + }, + ); + const [attachObligation, isAttaching] = useMutationWithIncrement( + attachObligationMutation, + { + ...incrementOptions, + value: 1, + }, + ); + const isLoading = isDetaching || isAttaching; + + return ( + + ); +} diff --git a/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabCreateMutation.graphql.ts b/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabCreateMutation.graphql.ts new file mode 100644 index 000000000..f3f2d5b1f --- /dev/null +++ b/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabCreateMutation.graphql.ts @@ -0,0 +1,235 @@ +/** + * @generated SignedSource<<545b2d50da8f79510029c5dba61ae821>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type CreateRiskObligationMappingInput = { + obligationId: string; + riskId: string; +}; +export type RiskObligationsTabCreateMutation$variables = { + connections: ReadonlyArray; + input: CreateRiskObligationMappingInput; +}; +export type RiskObligationsTabCreateMutation$data = { + readonly createRiskObligationMapping: { + readonly obligationEdge: { + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"LinkedObligationsCardFragment">; + }; + }; + }; +}; +export type RiskObligationsTabCreateMutation = { + response: RiskObligationsTabCreateMutation$data; + variables: RiskObligationsTabCreateMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "connections" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" +}, +v2 = [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } +], +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/) + ], + "kind": "Fragment", + "metadata": null, + "name": "RiskObligationsTabCreateMutation", + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "CreateRiskObligationMappingPayload", + "kind": "LinkedField", + "name": "createRiskObligationMapping", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "obligationEdge", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "LinkedObligationsCardFragment" + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + (v1/*: any*/), + (v0/*: any*/) + ], + "kind": "Operation", + "name": "RiskObligationsTabCreateMutation", + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "CreateRiskObligationMappingPayload", + "kind": "LinkedField", + "name": "createRiskObligationMapping", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "obligationEdge", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "requirement", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "area", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "source", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "status", + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "People", + "kind": "LinkedField", + "name": "owner", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "fullName", + "storageKey": null + }, + (v3/*: any*/) + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "filters": null, + "handle": "prependEdge", + "key": "", + "kind": "LinkedHandle", + "name": "obligationEdge", + "handleArgs": [ + { + "kind": "Variable", + "name": "connections", + "variableName": "connections" + } + ] + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "8681d141acf6036709241ba71bfeae4c", + "id": null, + "metadata": {}, + "name": "RiskObligationsTabCreateMutation", + "operationKind": "mutation", + "text": "mutation RiskObligationsTabCreateMutation(\n $input: CreateRiskObligationMappingInput!\n) {\n createRiskObligationMapping(input: $input) {\n obligationEdge {\n node {\n id\n ...LinkedObligationsCardFragment\n }\n }\n }\n}\n\nfragment LinkedObligationsCardFragment on Obligation {\n id\n requirement\n area\n source\n status\n owner {\n fullName\n id\n }\n}\n" + } +}; +})(); + +(node as any).hash = "eee2e37b1adaae513ca23b8c82f055c9"; + +export default node; diff --git a/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabDetachMutation.graphql.ts b/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabDetachMutation.graphql.ts new file mode 100644 index 000000000..af87d9554 --- /dev/null +++ b/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabDetachMutation.graphql.ts @@ -0,0 +1,133 @@ +/** + * @generated SignedSource<<58dd326af2caac72cf50ed8f289a3cac>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type DeleteRiskObligationMappingInput = { + obligationId: string; + riskId: string; +}; +export type RiskObligationsTabDetachMutation$variables = { + connections: ReadonlyArray; + input: DeleteRiskObligationMappingInput; +}; +export type RiskObligationsTabDetachMutation$data = { + readonly deleteRiskObligationMapping: { + readonly deletedObligationId: string; + }; +}; +export type RiskObligationsTabDetachMutation = { + response: RiskObligationsTabDetachMutation$data; + variables: RiskObligationsTabDetachMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "connections" +}, +v1 = { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" +}, +v2 = [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } +], +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "deletedObligationId", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [ + (v0/*: any*/), + (v1/*: any*/) + ], + "kind": "Fragment", + "metadata": null, + "name": "RiskObligationsTabDetachMutation", + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "DeleteRiskObligationMappingPayload", + "kind": "LinkedField", + "name": "deleteRiskObligationMapping", + "plural": false, + "selections": [ + (v3/*: any*/) + ], + "storageKey": null + } + ], + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + (v1/*: any*/), + (v0/*: any*/) + ], + "kind": "Operation", + "name": "RiskObligationsTabDetachMutation", + "selections": [ + { + "alias": null, + "args": (v2/*: any*/), + "concreteType": "DeleteRiskObligationMappingPayload", + "kind": "LinkedField", + "name": "deleteRiskObligationMapping", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "alias": null, + "args": null, + "filters": null, + "handle": "deleteEdge", + "key": "", + "kind": "ScalarHandle", + "name": "deletedObligationId", + "handleArgs": [ + { + "kind": "Variable", + "name": "connections", + "variableName": "connections" + } + ] + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "97b75f003c5e9518831c9699a2fcaff0", + "id": null, + "metadata": {}, + "name": "RiskObligationsTabDetachMutation", + "operationKind": "mutation", + "text": "mutation RiskObligationsTabDetachMutation(\n $input: DeleteRiskObligationMappingInput!\n) {\n deleteRiskObligationMapping(input: $input) {\n deletedObligationId\n }\n}\n" + } +}; +})(); + +(node as any).hash = "5923c7e9bcb08224ba1a60b0ad09642a"; + +export default node; diff --git a/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabFragment.graphql.ts b/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabFragment.graphql.ts new file mode 100644 index 000000000..b7303a72b --- /dev/null +++ b/apps/console/src/pages/organizations/risks/tabs/__generated__/RiskObligationsTabFragment.graphql.ts @@ -0,0 +1,155 @@ +/** + * @generated SignedSource<<1e3d6c1d36e2004b383479fb2e159c66>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type RiskObligationsTabFragment$data = { + readonly id: string; + readonly obligations: { + readonly __id: string; + readonly edges: ReadonlyArray<{ + readonly node: { + readonly id: string; + readonly " $fragmentSpreads": FragmentRefs<"LinkedObligationsCardFragment">; + }; + }>; + }; + readonly " $fragmentType": "RiskObligationsTabFragment"; +}; +export type RiskObligationsTabFragment$key = { + readonly " $data"?: RiskObligationsTabFragment$data; + readonly " $fragmentSpreads": FragmentRefs<"RiskObligationsTabFragment">; +}; + +const node: ReaderFragment = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "obligations" + ] + } + ] + }, + "name": "RiskObligationsTabFragment", + "selections": [ + (v0/*: any*/), + { + "alias": "obligations", + "args": null, + "concreteType": "ObligationConnection", + "kind": "LinkedField", + "name": "__Risk__obligations_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "ObligationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Obligation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "args": null, + "kind": "FragmentSpread", + "name": "LinkedObligationsCardFragment" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + }, + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null + } + ] + } + ], + "storageKey": null + } + ], + "type": "Risk", + "abstractKey": null +}; +})(); + +(node as any).hash = "e987e7c6dcfb73b71f769469b32c2b64"; + +export default node; diff --git a/apps/console/src/routes/riskRoutes.ts b/apps/console/src/routes/riskRoutes.ts index 990e4de0c..c57988f59 100644 --- a/apps/console/src/routes/riskRoutes.ts +++ b/apps/console/src/routes/riskRoutes.ts @@ -72,6 +72,13 @@ export const riskRoutes = [ () => import("/pages/organizations/risks/tabs/RiskControlsTab.tsx") ), }, + { + path: "obligations", + fallback: LinkCardSkeleton, + Component: lazy( + () => import("/pages/organizations/risks/tabs/RiskObligationsTab.tsx") + ), + }, ], }, { diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 1a0ceb0db..3f355ff9a 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -19,6 +19,7 @@ export { getAssetTypeVariant, getCriticityVariant } from "./assets"; export { getSnapshotTypeLabel, getSnapshotTypeUrlPath, snapshotTypes, validateSnapshotConsistency } from "./snapshots"; export { getAuditStateLabel, getAuditStateVariant, auditStates } from "./audits"; export { getStatusVariant, getStatusLabel, getStatusOptions } from "./registryStatus"; +export { getObligationStatusVariant, getObligationStatusLabel, getObligationStatusOptions } from "./obligationStatus"; export { promisifyMutation } from "./relay"; export { fileType, fileSize } from "./file"; export { formatDatetime, formatDate } from "./date"; diff --git a/packages/helpers/src/obligationStatus.ts b/packages/helpers/src/obligationStatus.ts new file mode 100644 index 000000000..5ad0f63cd --- /dev/null +++ b/packages/helpers/src/obligationStatus.ts @@ -0,0 +1,46 @@ +type Translator = (s: string) => string; + +export type ObligationStatus = "NON_COMPLIANT" | "PARTIALLY_COMPLIANT" | "COMPLIANT"; + +export const obligationStatuses = [ + "NON_COMPLIANT", + "PARTIALLY_COMPLIANT", + "COMPLIANT", +] as const; + +export const getObligationStatusVariant = (status: ObligationStatus) => { + switch (status) { + case "NON_COMPLIANT": + return "danger" as const; + case "PARTIALLY_COMPLIANT": + return "warning" as const; + case "COMPLIANT": + return "success" as const; + default: + return "neutral" as const; + } +}; + +export const getObligationStatusLabel = (status: ObligationStatus) => { + switch (status) { + case "NON_COMPLIANT": + return "Non-compliant"; + case "PARTIALLY_COMPLIANT": + return "Partially compliant"; + case "COMPLIANT": + return "Compliant"; + default: + return status; + } +}; + +export function getObligationStatusOptions(__: Translator) { + return obligationStatuses.map((status) => ({ + value: status, + label: __({ + "NON_COMPLIANT": "Non-compliant", + "PARTIALLY_COMPLIANT": "Partially compliant", + "COMPLIANT": "Compliant", + }[status]), + })); +} diff --git a/pkg/coredata/migrations/20250923T120205Z.sql b/pkg/coredata/migrations/20250923T120205Z.sql new file mode 100644 index 000000000..4294c6d2d --- /dev/null +++ b/pkg/coredata/migrations/20250923T120205Z.sql @@ -0,0 +1,26 @@ +ALTER TYPE obligations_status RENAME VALUE 'OPEN' TO 'NON_COMPLIANT'; +ALTER TYPE obligations_status RENAME VALUE 'IN_PROGRESS' TO 'PARTIALLY_COMPLIANT'; +ALTER TYPE obligations_status RENAME VALUE 'CLOSED' TO 'COMPLIANT'; + +ALTER TABLE obligations DROP COLUMN reference_id; + +CREATE TABLE risks_obligations ( + risk_id TEXT NOT NULL REFERENCES risks(id) ON UPDATE CASCADE ON DELETE CASCADE, + obligation_id TEXT NOT NULL REFERENCES obligations(id) ON UPDATE CASCADE ON DELETE CASCADE, + tenant_id TEXT NOT NULL, + created_at TIMESTAMP NOT NULL, + PRIMARY KEY (risk_id, obligation_id) +); + +ALTER TABLE obligations ADD COLUMN search_vector tsvector +GENERATED ALWAYS AS ( + to_tsvector('simple', + COALESCE(requirement, '') || ' ' || + COALESCE(area, '') || ' ' || + COALESCE(source, '') || ' ' || + COALESCE(regulator, '') || ' ' || + COALESCE(actions_to_be_implemented, '') + ) +) STORED; + +CREATE INDEX obligations_search_idx ON obligations USING gin(search_vector); diff --git a/pkg/coredata/obligation.go b/pkg/coredata/obligation.go index 07d3dc87a..eff29cade 100644 --- a/pkg/coredata/obligation.go +++ b/pkg/coredata/obligation.go @@ -30,7 +30,6 @@ type ( Obligation struct { ID gid.GID `db:"id"` OrganizationID gid.GID `db:"organization_id"` - ReferenceID string `db:"reference_id"` Area *string `db:"area"` Source *string `db:"source"` Requirement *string `db:"requirement"` @@ -59,8 +58,6 @@ func (o *Obligation) CursorKey(field ObligationOrderField) page.CursorKey { return page.NewCursorKey(o.ID, o.DueDate) case ObligationOrderFieldStatus: return page.NewCursorKey(o.ID, o.Status) - case ObligationOrderFieldReferenceId: - return page.NewCursorKey(o.ID, o.ReferenceID) } panic(fmt.Sprintf("unsupported order by: %s", field)) @@ -78,7 +75,6 @@ SELECT organization_id, snapshot_id, source_id, - reference_id, area, source, requirement, @@ -153,6 +149,52 @@ WHERE return count, nil } +func (os *Obligations) CountByRiskID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + riskID gid.GID, + filter *ObligationFilter, +) (int, error) { + q := ` +WITH obls AS ( + SELECT + o.id, + o.tenant_id, + o.snapshot_id, + o.search_vector + FROM + obligations o + INNER JOIN + risks_obligations ro ON o.id = ro.obligation_id + WHERE + ro.risk_id = @risk_id +) +SELECT + COUNT(id) +FROM + obls +WHERE %s + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) + + args := pgx.StrictNamedArgs{"risk_id": riskID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + + row := conn.QueryRow(ctx, q, args) + + var count int + err := row.Scan(&count) + if err != nil { + return 0, fmt.Errorf("cannot count obligations: %w", err) + } + + return count, nil +} + func (os *Obligations) LoadByOrganizationID( ctx context.Context, conn pg.Conn, @@ -165,7 +207,6 @@ func (os *Obligations) LoadByOrganizationID( SELECT id, organization_id, - reference_id, area, source, requirement, @@ -210,6 +251,86 @@ WHERE return nil } +func (os *Obligations) LoadByRiskID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + riskID gid.GID, + cursor *page.Cursor[ObligationOrderField], + filter *ObligationFilter, +) error { + q := ` +WITH obls AS ( + SELECT + o.id, + o.organization_id, + o.area, + o.source, + o.requirement, + o.actions_to_be_implemented, + o.regulator, + o.owner_id, + o.last_review_date, + o.due_date, + o.status, + o.snapshot_id, + o.source_id, + o.created_at, + o.updated_at, + o.tenant_id, + o.search_vector + FROM + obligations o + INNER JOIN + risks_obligations ro ON o.id = ro.obligation_id + WHERE + ro.risk_id = @risk_id +) +SELECT + id, + organization_id, + area, + source, + requirement, + actions_to_be_implemented, + regulator, + owner_id, + last_review_date, + due_date, + status, + snapshot_id, + source_id, + created_at, + updated_at +FROM + obls +WHERE %s + AND %s + AND %s +` + + q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"risk_id": riskID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, filter.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query obligations: %w", err) + } + + obligations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Obligation]) + if err != nil { + return fmt.Errorf("cannot collect obligations: %w", err) + } + + *os = obligations + + return nil +} + func (o *Obligation) Insert( ctx context.Context, conn pg.Conn, @@ -220,7 +341,6 @@ INSERT INTO obligations ( id, tenant_id, organization_id, - reference_id, area, source, requirement, @@ -238,7 +358,6 @@ INSERT INTO obligations ( @id, @tenant_id, @organization_id, - @reference_id, @area, @source, @requirement, @@ -259,7 +378,6 @@ INSERT INTO obligations ( "id": o.ID, "tenant_id": scope.GetTenantID(), "organization_id": o.OrganizationID, - "reference_id": o.ReferenceID, "area": o.Area, "source": o.Source, "requirement": o.Requirement, @@ -290,7 +408,6 @@ func (o *Obligation) Update( ) error { q := ` UPDATE obligations SET - reference_id = @reference_id, area = @area, source = @source, requirement = @requirement, @@ -311,7 +428,6 @@ WHERE args := pgx.StrictNamedArgs{ "id": o.ID, - "reference_id": o.ReferenceID, "area": o.Area, "source": o.Source, "requirement": o.Requirement, @@ -367,7 +483,6 @@ INSERT INTO obligations ( snapshot_id, source_id, organization_id, - reference_id, area, source, requirement, @@ -386,7 +501,6 @@ SELECT @snapshot_id, o.id, o.organization_id, - o.reference_id, o.area, o.source, o.requirement, diff --git a/pkg/coredata/obligation_order_field.go b/pkg/coredata/obligation_order_field.go index e3b422020..c2ea2ac3f 100644 --- a/pkg/coredata/obligation_order_field.go +++ b/pkg/coredata/obligation_order_field.go @@ -25,7 +25,6 @@ const ( ObligationOrderFieldLastReviewDate ObligationOrderField = "LAST_REVIEW_DATE" ObligationOrderFieldDueDate ObligationOrderField = "DUE_DATE" ObligationOrderFieldStatus ObligationOrderField = "STATUS" - ObligationOrderFieldReferenceId ObligationOrderField = "REFERENCE_ID" ) func (p ObligationOrderField) Column() string { @@ -46,8 +45,7 @@ func (p *ObligationOrderField) UnmarshalText(text []byte) error { case string(ObligationOrderFieldCreatedAt), string(ObligationOrderFieldLastReviewDate), string(ObligationOrderFieldDueDate), - string(ObligationOrderFieldStatus), - string(ObligationOrderFieldReferenceId): + string(ObligationOrderFieldStatus): *p = ObligationOrderField(val) return nil } diff --git a/pkg/coredata/obligation_status.go b/pkg/coredata/obligation_status.go index ae91f59aa..47a57d71e 100644 --- a/pkg/coredata/obligation_status.go +++ b/pkg/coredata/obligation_status.go @@ -22,9 +22,9 @@ import ( type ObligationStatus string const ( - ObligationStatusOpen ObligationStatus = "OPEN" - ObligationStatusInProgress ObligationStatus = "IN_PROGRESS" - ObligationStatusClosed ObligationStatus = "CLOSED" + ObligationStatusNonCompliant ObligationStatus = "NON_COMPLIANT" + ObligationStatusPartiallyCompliant ObligationStatus = "PARTIALLY_COMPLIANT" + ObligationStatusCompliant ObligationStatus = "COMPLIANT" ) func (os ObligationStatus) String() string { @@ -43,12 +43,12 @@ func (os *ObligationStatus) Scan(value any) error { } switch s { - case "OPEN": - *os = ObligationStatusOpen - case "IN_PROGRESS": - *os = ObligationStatusInProgress - case "CLOSED": - *os = ObligationStatusClosed + case "NON_COMPLIANT": + *os = ObligationStatusNonCompliant + case "PARTIALLY_COMPLIANT": + *os = ObligationStatusPartiallyCompliant + case "COMPLIANT": + *os = ObligationStatusCompliant default: return fmt.Errorf("invalid ObligationStatus value: %q", s) } diff --git a/pkg/coredata/risk_obligation.go b/pkg/coredata/risk_obligation.go new file mode 100644 index 000000000..2a5d01c75 --- /dev/null +++ b/pkg/coredata/risk_obligation.go @@ -0,0 +1,99 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +import ( + "context" + "fmt" + "maps" + "time" + + "github.com/getprobo/probo/pkg/gid" + "github.com/jackc/pgx/v5" + "go.gearno.de/kit/pg" +) + +type ( + RiskObligation struct { + RiskID gid.GID `db:"risk_id"` + ObligationID gid.GID `db:"obligation_id"` + CreatedAt time.Time `db:"created_at"` + } + + RiskObligations []*RiskObligation +) + +func (ro RiskObligation) Insert( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +INSERT INTO risks_obligations ( + risk_id, + obligation_id, + tenant_id, + created_at +) VALUES ( + @risk_id, + @obligation_id, + @tenant_id, + @created_at +) +` + + args := pgx.StrictNamedArgs{ + "risk_id": ro.RiskID, + "obligation_id": ro.ObligationID, + "tenant_id": scope.GetTenantID(), + "created_at": ro.CreatedAt, + } + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot insert risk obligation: %w", err) + } + + return nil +} + +func (ro RiskObligation) Delete( + ctx context.Context, + conn pg.Conn, + scope Scoper, +) error { + q := ` +DELETE FROM risks_obligations +WHERE + %s + AND risk_id = @risk_id + AND obligation_id = @obligation_id +` + + q = fmt.Sprintf(q, scope.SQLFragment()) + + args := pgx.StrictNamedArgs{ + "risk_id": ro.RiskID, + "obligation_id": ro.ObligationID, + } + maps.Copy(args, scope.SQLArguments()) + + _, err := conn.Exec(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot delete risk obligation: %w", err) + } + + return nil +} diff --git a/pkg/probo/obligation_service.go b/pkg/probo/obligation_service.go index 4e820c3f5..2f2da46f5 100644 --- a/pkg/probo/obligation_service.go +++ b/pkg/probo/obligation_service.go @@ -32,7 +32,6 @@ type ObligationService struct { type ( CreateObligationRequest struct { OrganizationID gid.GID - ReferenceID string Area *string Source *string Requirement *string @@ -46,7 +45,6 @@ type ( UpdateObligationRequest struct { ID gid.GID - ReferenceID *string Area **string Source **string Requirement **string @@ -92,7 +90,6 @@ func (s *ObligationService) Create( obligation := &coredata.Obligation{ ID: gid.New(s.svc.scope.GetTenantID(), coredata.ObligationEntityType), OrganizationID: req.OrganizationID, - ReferenceID: req.ReferenceID, Area: req.Area, Source: req.Source, Requirement: req.Requirement, @@ -147,10 +144,6 @@ func (s *ObligationService) Update( return fmt.Errorf("cannot load obligation: %w", err) } - if req.ReferenceID != nil { - obligation.ReferenceID = *req.ReferenceID - } - if req.Area != nil { obligation.Area = *req.Area } @@ -284,3 +277,57 @@ func (s ObligationService) ListForOrganizationID( return page.NewPage(obligations, cursor), nil } + +func (s ObligationService) CountForRiskID( + ctx context.Context, + riskID gid.GID, + filter *coredata.ObligationFilter, +) (int, error) { + var count int + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) (err error) { + obligations := &coredata.Obligations{} + count, err = obligations.CountByRiskID(ctx, conn, s.svc.scope, riskID, filter) + if err != nil { + return fmt.Errorf("cannot count obligations: %w", err) + } + + return nil + }, + ) + + if err != nil { + return 0, err + } + + return count, nil +} + +func (s ObligationService) ListForRiskID( + ctx context.Context, + riskID gid.GID, + cursor *page.Cursor[coredata.ObligationOrderField], + filter *coredata.ObligationFilter, +) (*page.Page[*coredata.Obligation, coredata.ObligationOrderField], error) { + var obligations coredata.Obligations + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + err := obligations.LoadByRiskID(ctx, conn, s.svc.scope, riskID, cursor, filter) + if err != nil { + return fmt.Errorf("cannot load obligations: %w", err) + } + + return nil + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(obligations, cursor), nil +} diff --git a/pkg/probo/risk_service.go b/pkg/probo/risk_service.go index 0994708ca..888d81bd2 100644 --- a/pkg/probo/risk_service.go +++ b/pkg/probo/risk_service.go @@ -303,6 +303,76 @@ func (s RiskService) DeleteMeasureMapping( return risk, measure, nil } +func (s RiskService) CreateObligationMapping( + ctx context.Context, + riskID gid.GID, + obligationID gid.GID, +) (*coredata.Risk, *coredata.Obligation, error) { + risk := &coredata.Risk{} + obligation := &coredata.Obligation{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + if err := risk.LoadByID(ctx, conn, s.svc.scope, riskID); err != nil { + return fmt.Errorf("cannot load risk: %w", err) + } + + if err := obligation.LoadByID(ctx, conn, s.svc.scope, obligationID); err != nil { + return fmt.Errorf("cannot load obligation: %w", err) + } + + riskObligation := &coredata.RiskObligation{ + RiskID: risk.ID, + ObligationID: obligation.ID, + CreatedAt: time.Now(), + } + + return riskObligation.Insert(ctx, conn, s.svc.scope) + }, + ) + + if err != nil { + return nil, nil, fmt.Errorf("cannot create risk obligation mapping: %w", err) + } + + return risk, obligation, nil +} + +func (s RiskService) DeleteObligationMapping( + ctx context.Context, + riskID gid.GID, + obligationID gid.GID, +) (*coredata.Risk, *coredata.Obligation, error) { + riskObligation := &coredata.RiskObligation{} + risk := &coredata.Risk{} + obligation := &coredata.Obligation{} + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + if err := risk.LoadByID(ctx, conn, s.svc.scope, riskID); err != nil { + return fmt.Errorf("cannot load risk: %w", err) + } + + if err := obligation.LoadByID(ctx, conn, s.svc.scope, obligationID); err != nil { + return fmt.Errorf("cannot load obligation: %w", err) + } + + riskObligation.RiskID = risk.ID + riskObligation.ObligationID = obligation.ID + + return riskObligation.Delete(ctx, conn, s.svc.scope) + }, + ) + + if err != nil { + return nil, nil, fmt.Errorf("cannot delete risk obligation mapping: %w", err) + } + + return risk, obligation, nil +} + func (s RiskService) Create( ctx context.Context, req CreateRiskRequest, diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index 6e5c91f4f..6549b9e42 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -169,17 +169,17 @@ enum NonconformityStatus enum ObligationStatus @goModel(model: "github.com/getprobo/probo/pkg/coredata.ObligationStatus") { - OPEN + NON_COMPLIANT @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusOpen" + value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusNonCompliant" ) - IN_PROGRESS + PARTIALLY_COMPLIANT @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusInProgress" + value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusPartiallyCompliant" ) - CLOSED + COMPLIANT @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusClosed" + value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusCompliant" ) } @@ -1044,10 +1044,6 @@ enum ObligationOrderField @goEnum( value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldCreatedAt" ) - REFERENCE_ID - @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldReferenceId" - ) LAST_REVIEW_DATE @goEnum( value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldLastReviewDate" @@ -2004,6 +2000,15 @@ type Risk implements Node { filter: ControlFilter ): ControlConnection! @goField(forceResolver: true) + obligations( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ObligationOrder + filter: ObligationFilter + ): ObligationConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -2056,7 +2061,6 @@ type Obligation implements Node { snapshotId: ID sourceId: ID organization: Organization! @goField(forceResolver: true) - referenceId: String! area: String source: String requirement: String @@ -2669,6 +2673,13 @@ type Mutation { input: DeleteRiskDocumentMappingInput! ): DeleteRiskDocumentMappingPayload! + createRiskObligationMapping( + input: CreateRiskObligationMappingInput! + ): CreateRiskObligationMappingPayload! + deleteRiskObligationMapping( + input: DeleteRiskObligationMappingInput! + ): DeleteRiskObligationMappingPayload! + # Evidence mutations requestEvidence(input: RequestEvidenceInput!): RequestEvidencePayload! fulfillEvidence(input: FulfillEvidenceInput!): FulfillEvidencePayload! @@ -3187,6 +3198,16 @@ input DeleteRiskDocumentMappingInput { documentId: ID! } +input CreateRiskObligationMappingInput { + riskId: ID! + obligationId: ID! +} + +input DeleteRiskObligationMappingInput { + riskId: ID! + obligationId: ID! +} + input RequestEvidenceInput { taskId: ID! name: String! @@ -3392,7 +3413,6 @@ input DeleteNonconformityInput { input CreateObligationInput { organizationId: ID! - referenceId: String! area: String source: String requirement: String @@ -3406,7 +3426,6 @@ input CreateObligationInput { input UpdateObligationInput { id: ID! - referenceId: String area: String source: String requirement: String @@ -3734,6 +3753,16 @@ type DeleteRiskDocumentMappingPayload { deletedDocumentId: ID! } +type CreateRiskObligationMappingPayload { + riskEdge: RiskEdge! + obligationEdge: ObligationEdge! +} + +type DeleteRiskObligationMappingPayload { + deletedRiskId: ID! + deletedObligationId: ID! +} + type RequestEvidencePayload { evidenceEdge: EvidenceEdge! } diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index c4cd41bb3..8b846e24d 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -349,6 +349,11 @@ type ComplexityRoot struct { RiskEdge func(childComplexity int) int } + CreateRiskObligationMappingPayload struct { + ObligationEdge func(childComplexity int) int + RiskEdge func(childComplexity int) int + } + CreateRiskPayload struct { RiskEdge func(childComplexity int) int } @@ -502,6 +507,11 @@ type ComplexityRoot struct { DeletedRiskID func(childComplexity int) int } + DeleteRiskObligationMappingPayload struct { + DeletedObligationID func(childComplexity int) int + DeletedRiskID func(childComplexity int) int + } + DeleteRiskPayload struct { DeletedRiskID func(childComplexity int) int } @@ -758,6 +768,7 @@ type ComplexityRoot struct { CreateRisk func(childComplexity int, input types.CreateRiskInput) int CreateRiskDocumentMapping func(childComplexity int, input types.CreateRiskDocumentMappingInput) int CreateRiskMeasureMapping func(childComplexity int, input types.CreateRiskMeasureMappingInput) int + CreateRiskObligationMapping func(childComplexity int, input types.CreateRiskObligationMappingInput) int CreateSnapshot func(childComplexity int, input types.CreateSnapshotInput) int CreateTask func(childComplexity int, input types.CreateTaskInput) int CreateTrustCenterAccess func(childComplexity int, input types.CreateTrustCenterAccessInput) int @@ -789,6 +800,7 @@ type ComplexityRoot struct { DeleteRisk func(childComplexity int, input types.DeleteRiskInput) int DeleteRiskDocumentMapping func(childComplexity int, input types.DeleteRiskDocumentMappingInput) int DeleteRiskMeasureMapping func(childComplexity int, input types.DeleteRiskMeasureMappingInput) int + DeleteRiskObligationMapping func(childComplexity int, input types.DeleteRiskObligationMappingInput) int DeleteSnapshot func(childComplexity int, input types.DeleteSnapshotInput) int DeleteTask func(childComplexity int, input types.DeleteTaskInput) int DeleteTrustCenterAccess func(childComplexity int, input types.DeleteTrustCenterAccessInput) int @@ -885,7 +897,6 @@ type ComplexityRoot struct { LastReviewDate func(childComplexity int) int Organization func(childComplexity int) int Owner func(childComplexity int) int - ReferenceID func(childComplexity int) int Regulator func(childComplexity int) int Requirement func(childComplexity int) int SnapshotID func(childComplexity int) int @@ -1059,6 +1070,7 @@ type ComplexityRoot struct { Measures func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) int Name func(childComplexity int) int Note func(childComplexity int) int + Obligations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) int Organization func(childComplexity int) int Owner func(childComplexity int) int ResidualImpact func(childComplexity int) int @@ -1663,6 +1675,8 @@ type MutationResolver interface { DeleteRiskMeasureMapping(ctx context.Context, input types.DeleteRiskMeasureMappingInput) (*types.DeleteRiskMeasureMappingPayload, error) CreateRiskDocumentMapping(ctx context.Context, input types.CreateRiskDocumentMappingInput) (*types.CreateRiskDocumentMappingPayload, error) DeleteRiskDocumentMapping(ctx context.Context, input types.DeleteRiskDocumentMappingInput) (*types.DeleteRiskDocumentMappingPayload, error) + CreateRiskObligationMapping(ctx context.Context, input types.CreateRiskObligationMappingInput) (*types.CreateRiskObligationMappingPayload, error) + DeleteRiskObligationMapping(ctx context.Context, input types.DeleteRiskObligationMappingInput) (*types.DeleteRiskObligationMappingPayload, error) RequestEvidence(ctx context.Context, input types.RequestEvidenceInput) (*types.RequestEvidencePayload, error) FulfillEvidence(ctx context.Context, input types.FulfillEvidenceInput) (*types.FulfillEvidencePayload, error) DeleteEvidence(ctx context.Context, input types.DeleteEvidenceInput) (*types.DeleteEvidencePayload, error) @@ -1783,6 +1797,7 @@ type RiskResolver interface { Measures(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) (*types.MeasureConnection, error) Documents(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) (*types.DocumentConnection, error) Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) + Obligations(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) (*types.ObligationConnection, error) } type RiskConnectionResolver interface { TotalCount(ctx context.Context, obj *types.RiskConnection) (int, error) @@ -2730,6 +2745,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.CreateRiskMeasureMappingPayload.RiskEdge(childComplexity), true + case "CreateRiskObligationMappingPayload.obligationEdge": + if e.complexity.CreateRiskObligationMappingPayload.ObligationEdge == nil { + break + } + + return e.complexity.CreateRiskObligationMappingPayload.ObligationEdge(childComplexity), true + + case "CreateRiskObligationMappingPayload.riskEdge": + if e.complexity.CreateRiskObligationMappingPayload.RiskEdge == nil { + break + } + + return e.complexity.CreateRiskObligationMappingPayload.RiskEdge(childComplexity), true + case "CreateRiskPayload.riskEdge": if e.complexity.CreateRiskPayload.RiskEdge == nil { break @@ -3092,6 +3121,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.DeleteRiskMeasureMappingPayload.DeletedRiskID(childComplexity), true + case "DeleteRiskObligationMappingPayload.deletedObligationId": + if e.complexity.DeleteRiskObligationMappingPayload.DeletedObligationID == nil { + break + } + + return e.complexity.DeleteRiskObligationMappingPayload.DeletedObligationID(childComplexity), true + + case "DeleteRiskObligationMappingPayload.deletedRiskId": + if e.complexity.DeleteRiskObligationMappingPayload.DeletedRiskID == nil { + break + } + + return e.complexity.DeleteRiskObligationMappingPayload.DeletedRiskID(childComplexity), true + case "DeleteRiskPayload.deletedRiskId": if e.complexity.DeleteRiskPayload.DeletedRiskID == nil { break @@ -4264,6 +4307,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Mutation.CreateRiskMeasureMapping(childComplexity, args["input"].(types.CreateRiskMeasureMappingInput)), true + case "Mutation.createRiskObligationMapping": + if e.complexity.Mutation.CreateRiskObligationMapping == nil { + break + } + + args, err := ec.field_Mutation_createRiskObligationMapping_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.CreateRiskObligationMapping(childComplexity, args["input"].(types.CreateRiskObligationMappingInput)), true + case "Mutation.createSnapshot": if e.complexity.Mutation.CreateSnapshot == nil { break @@ -4636,6 +4691,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Mutation.DeleteRiskMeasureMapping(childComplexity, args["input"].(types.DeleteRiskMeasureMappingInput)), true + case "Mutation.deleteRiskObligationMapping": + if e.complexity.Mutation.DeleteRiskObligationMapping == nil { + break + } + + args, err := ec.field_Mutation_deleteRiskObligationMapping_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteRiskObligationMapping(childComplexity, args["input"].(types.DeleteRiskObligationMappingInput)), true + case "Mutation.deleteSnapshot": if e.complexity.Mutation.DeleteSnapshot == nil { break @@ -5504,13 +5571,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Obligation.Owner(childComplexity), true - case "Obligation.referenceId": - if e.complexity.Obligation.ReferenceID == nil { - break - } - - return e.complexity.Obligation.ReferenceID(childComplexity), true - case "Obligation.regulator": if e.complexity.Obligation.Regulator == nil { break @@ -6433,6 +6493,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Risk.Note(childComplexity), true + case "Risk.obligations": + if e.complexity.Risk.Obligations == nil { + break + } + + args, err := ec.field_Risk_obligations_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Risk.Obligations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ObligationOrderBy), args["filter"].(*types.ObligationFilter)), true + case "Risk.organization": if e.complexity.Risk.Organization == nil { break @@ -8125,6 +8197,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputCreateRiskDocumentMappingInput, ec.unmarshalInputCreateRiskInput, ec.unmarshalInputCreateRiskMeasureMappingInput, + ec.unmarshalInputCreateRiskObligationMappingInput, ec.unmarshalInputCreateSnapshotInput, ec.unmarshalInputCreateTaskInput, ec.unmarshalInputCreateTrustCenterAccessInput, @@ -8158,6 +8231,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputDeleteRiskDocumentMappingInput, ec.unmarshalInputDeleteRiskInput, ec.unmarshalInputDeleteRiskMeasureMappingInput, + ec.unmarshalInputDeleteRiskObligationMappingInput, ec.unmarshalInputDeleteSnapshotInput, ec.unmarshalInputDeleteTaskInput, ec.unmarshalInputDeleteTrustCenterAccessInput, @@ -8514,17 +8588,17 @@ enum NonconformityStatus enum ObligationStatus @goModel(model: "github.com/getprobo/probo/pkg/coredata.ObligationStatus") { - OPEN + NON_COMPLIANT @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusOpen" + value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusNonCompliant" ) - IN_PROGRESS + PARTIALLY_COMPLIANT @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusInProgress" + value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusPartiallyCompliant" ) - CLOSED + COMPLIANT @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusClosed" + value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusCompliant" ) } @@ -9389,10 +9463,6 @@ enum ObligationOrderField @goEnum( value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldCreatedAt" ) - REFERENCE_ID - @goEnum( - value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldReferenceId" - ) LAST_REVIEW_DATE @goEnum( value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldLastReviewDate" @@ -10349,6 +10419,15 @@ type Risk implements Node { filter: ControlFilter ): ControlConnection! @goField(forceResolver: true) + obligations( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ObligationOrder + filter: ObligationFilter + ): ObligationConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -10401,7 +10480,6 @@ type Obligation implements Node { snapshotId: ID sourceId: ID organization: Organization! @goField(forceResolver: true) - referenceId: String! area: String source: String requirement: String @@ -11014,6 +11092,13 @@ type Mutation { input: DeleteRiskDocumentMappingInput! ): DeleteRiskDocumentMappingPayload! + createRiskObligationMapping( + input: CreateRiskObligationMappingInput! + ): CreateRiskObligationMappingPayload! + deleteRiskObligationMapping( + input: DeleteRiskObligationMappingInput! + ): DeleteRiskObligationMappingPayload! + # Evidence mutations requestEvidence(input: RequestEvidenceInput!): RequestEvidencePayload! fulfillEvidence(input: FulfillEvidenceInput!): FulfillEvidencePayload! @@ -11532,6 +11617,16 @@ input DeleteRiskDocumentMappingInput { documentId: ID! } +input CreateRiskObligationMappingInput { + riskId: ID! + obligationId: ID! +} + +input DeleteRiskObligationMappingInput { + riskId: ID! + obligationId: ID! +} + input RequestEvidenceInput { taskId: ID! name: String! @@ -11737,7 +11832,6 @@ input DeleteNonconformityInput { input CreateObligationInput { organizationId: ID! - referenceId: String! area: String source: String requirement: String @@ -11751,7 +11845,6 @@ input CreateObligationInput { input UpdateObligationInput { id: ID! - referenceId: String area: String source: String requirement: String @@ -12079,6 +12172,16 @@ type DeleteRiskDocumentMappingPayload { deletedDocumentId: ID! } +type CreateRiskObligationMappingPayload { + riskEdge: RiskEdge! + obligationEdge: ObligationEdge! +} + +type DeleteRiskObligationMappingPayload { + deletedRiskId: ID! + deletedObligationId: ID! +} + type RequestEvidencePayload { evidenceEdge: EvidenceEdge! } @@ -14850,6 +14953,29 @@ func (ec *executionContext) field_Mutation_createRiskMeasureMapping_argsInput( return zeroVal, nil } +func (ec *executionContext) field_Mutation_createRiskObligationMapping_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mutation_createRiskObligationMapping_argsInput(ctx, rawArgs) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} +func (ec *executionContext) field_Mutation_createRiskObligationMapping_argsInput( + ctx context.Context, + rawArgs map[string]any, +) (types.CreateRiskObligationMappingInput, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + if tmp, ok := rawArgs["input"]; ok { + return ec.unmarshalNCreateRiskObligationMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateRiskObligationMappingInput(ctx, tmp) + } + + var zeroVal types.CreateRiskObligationMappingInput + return zeroVal, nil +} + func (ec *executionContext) field_Mutation_createRisk_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -15563,6 +15689,29 @@ func (ec *executionContext) field_Mutation_deleteRiskMeasureMapping_argsInput( return zeroVal, nil } +func (ec *executionContext) field_Mutation_deleteRiskObligationMapping_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mutation_deleteRiskObligationMapping_argsInput(ctx, rawArgs) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} +func (ec *executionContext) field_Mutation_deleteRiskObligationMapping_argsInput( + ctx context.Context, + rawArgs map[string]any, +) (types.DeleteRiskObligationMappingInput, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + if tmp, ok := rawArgs["input"]; ok { + return ec.unmarshalNDeleteRiskObligationMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteRiskObligationMappingInput(ctx, tmp) + } + + var zeroVal types.DeleteRiskObligationMappingInput + return zeroVal, nil +} + func (ec *executionContext) field_Mutation_deleteRisk_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -19185,6 +19334,119 @@ func (ec *executionContext) field_Risk_measures_argsFilter( return zeroVal, nil } +func (ec *executionContext) field_Risk_obligations_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Risk_obligations_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Risk_obligations_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Risk_obligations_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Risk_obligations_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Risk_obligations_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + arg5, err := ec.field_Risk_obligations_argsFilter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["filter"] = arg5 + return args, nil +} +func (ec *executionContext) field_Risk_obligations_argsFirst( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("first")) + if tmp, ok := rawArgs["first"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Risk_obligations_argsAfter( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("after")) + if tmp, ok := rawArgs["after"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Risk_obligations_argsLast( + ctx context.Context, + rawArgs map[string]any, +) (*int, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("last")) + if tmp, ok := rawArgs["last"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Risk_obligations_argsBefore( + ctx context.Context, + rawArgs map[string]any, +) (*page.CursorKey, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("before")) + if tmp, ok := rawArgs["before"]; ok { + return ec.unmarshalOCursorKey2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋpageᚐCursorKey(ctx, tmp) + } + + var zeroVal *page.CursorKey + return zeroVal, nil +} + +func (ec *executionContext) field_Risk_obligations_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.ObligationOrderBy, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalOObligationOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐObligationOrderBy(ctx, tmp) + } + + var zeroVal *types.ObligationOrderBy + return zeroVal, nil +} + +func (ec *executionContext) field_Risk_obligations_argsFilter( + ctx context.Context, + rawArgs map[string]any, +) (*types.ObligationFilter, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + if tmp, ok := rawArgs["filter"]; ok { + return ec.unmarshalOObligationFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐObligationFilter(ctx, tmp) + } + + var zeroVal *types.ObligationFilter + return zeroVal, nil +} + func (ec *executionContext) field_Snapshot_controls_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -26171,6 +26433,106 @@ func (ec *executionContext) fieldContext_CreateRiskMeasureMappingPayload_measure return fc, nil } +func (ec *executionContext) _CreateRiskObligationMappingPayload_riskEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreateRiskObligationMappingPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CreateRiskObligationMappingPayload_riskEdge(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.RiskEdge, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.RiskEdge) + fc.Result = res + return ec.marshalNRiskEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐRiskEdge(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CreateRiskObligationMappingPayload_riskEdge(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CreateRiskObligationMappingPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cursor": + return ec.fieldContext_RiskEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_RiskEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type RiskEdge", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _CreateRiskObligationMappingPayload_obligationEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreateRiskObligationMappingPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CreateRiskObligationMappingPayload_obligationEdge(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.ObligationEdge, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.ObligationEdge) + fc.Result = res + return ec.marshalNObligationEdge2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐObligationEdge(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CreateRiskObligationMappingPayload_obligationEdge(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CreateRiskObligationMappingPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cursor": + return ec.fieldContext_ObligationEdge_cursor(ctx, field) + case "node": + return ec.fieldContext_ObligationEdge_node(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ObligationEdge", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _CreateRiskPayload_riskEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreateRiskPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_CreateRiskPayload_riskEdge(ctx, field) if err != nil { @@ -28629,6 +28991,94 @@ func (ec *executionContext) fieldContext_DeleteRiskMeasureMappingPayload_deleted return fc, nil } +func (ec *executionContext) _DeleteRiskObligationMappingPayload_deletedRiskId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteRiskObligationMappingPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeleteRiskObligationMappingPayload_deletedRiskId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeletedRiskID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(gid.GID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_DeleteRiskObligationMappingPayload_deletedRiskId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "DeleteRiskObligationMappingPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _DeleteRiskObligationMappingPayload_deletedObligationId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteRiskObligationMappingPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeleteRiskObligationMappingPayload_deletedObligationId(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return obj.DeletedObligationID, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(gid.GID) + fc.Result = res + return ec.marshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_DeleteRiskObligationMappingPayload_deletedObligationId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "DeleteRiskObligationMappingPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type ID does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _DeleteRiskPayload_deletedRiskId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteRiskPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DeleteRiskPayload_deletedRiskId(ctx, field) if err != nil { @@ -37833,6 +38283,128 @@ func (ec *executionContext) fieldContext_Mutation_deleteRiskDocumentMapping(ctx return fc, nil } +func (ec *executionContext) _Mutation_createRiskObligationMapping(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_createRiskObligationMapping(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().CreateRiskObligationMapping(rctx, fc.Args["input"].(types.CreateRiskObligationMappingInput)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.CreateRiskObligationMappingPayload) + fc.Result = res + return ec.marshalNCreateRiskObligationMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateRiskObligationMappingPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_createRiskObligationMapping(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 "riskEdge": + return ec.fieldContext_CreateRiskObligationMappingPayload_riskEdge(ctx, field) + case "obligationEdge": + return ec.fieldContext_CreateRiskObligationMappingPayload_obligationEdge(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CreateRiskObligationMappingPayload", 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_createRiskObligationMapping_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_deleteRiskObligationMapping(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteRiskObligationMapping(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Mutation().DeleteRiskObligationMapping(rctx, fc.Args["input"].(types.DeleteRiskObligationMappingInput)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.DeleteRiskObligationMappingPayload) + fc.Result = res + return ec.marshalNDeleteRiskObligationMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteRiskObligationMappingPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_deleteRiskObligationMapping(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 "deletedRiskId": + return ec.fieldContext_DeleteRiskObligationMappingPayload_deletedRiskId(ctx, field) + case "deletedObligationId": + return ec.fieldContext_DeleteRiskObligationMappingPayload_deletedObligationId(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeleteRiskObligationMappingPayload", 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_deleteRiskObligationMapping_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_requestEvidence(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_requestEvidence(ctx, field) if err != nil { @@ -42389,50 +42961,6 @@ func (ec *executionContext) fieldContext_Obligation_organization(_ context.Conte return fc, nil } -func (ec *executionContext) _Obligation_referenceId(ctx context.Context, field graphql.CollectedField, obj *types.Obligation) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Obligation_referenceId(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { - ctx = rctx // use context from middleware stack in children - return obj.ReferenceID, nil - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(string) - fc.Result = res - return ec.marshalNString2string(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_Obligation_referenceId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "Obligation", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return nil, errors.New("field of type String does not have child fields") - }, - } - return fc, nil -} - func (ec *executionContext) _Obligation_area(ctx context.Context, field graphql.CollectedField, obj *types.Obligation) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Obligation_area(ctx, field) if err != nil { @@ -43157,8 +43685,6 @@ func (ec *executionContext) fieldContext_ObligationEdge_node(_ context.Context, return ec.fieldContext_Obligation_sourceId(ctx, field) case "organization": return ec.fieldContext_Obligation_organization(ctx, field) - case "referenceId": - return ec.fieldContext_Obligation_referenceId(ctx, field) case "area": return ec.fieldContext_Obligation_area(ctx, field) case "source": @@ -48894,6 +49420,69 @@ func (ec *executionContext) fieldContext_Risk_controls(ctx context.Context, fiel return fc, nil } +func (ec *executionContext) _Risk_obligations(ctx context.Context, field graphql.CollectedField, obj *types.Risk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Risk_obligations(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Risk().Obligations(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ObligationOrderBy), fc.Args["filter"].(*types.ObligationFilter)) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*types.ObligationConnection) + fc.Result = res + return ec.marshalNObligationConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐObligationConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Risk_obligations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Risk", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "totalCount": + return ec.fieldContext_ObligationConnection_totalCount(ctx, field) + case "edges": + return ec.fieldContext_ObligationConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_ObligationConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ObligationConnection", 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_Risk_obligations_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Risk_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Risk) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Risk_createdAt(ctx, field) if err != nil { @@ -49249,6 +49838,8 @@ func (ec *executionContext) fieldContext_RiskEdge_node(_ context.Context, field return ec.fieldContext_Risk_documents(ctx, field) case "controls": return ec.fieldContext_Risk_controls(ctx, field) + case "obligations": + return ec.fieldContext_Risk_obligations(ctx, field) case "createdAt": return ec.fieldContext_Risk_createdAt(ctx, field) case "updatedAt": @@ -53556,8 +54147,6 @@ func (ec *executionContext) fieldContext_UpdateObligationPayload_obligation(_ co return ec.fieldContext_Obligation_sourceId(ctx, field) case "organization": return ec.fieldContext_Obligation_organization(ctx, field) - case "referenceId": - return ec.fieldContext_Obligation_referenceId(ctx, field) case "area": return ec.fieldContext_Obligation_area(ctx, field) case "source": @@ -53918,6 +54507,8 @@ func (ec *executionContext) fieldContext_UpdateRiskPayload_risk(_ context.Contex return ec.fieldContext_Risk_documents(ctx, field) case "controls": return ec.fieldContext_Risk_controls(ctx, field) + case "obligations": + return ec.fieldContext_Risk_obligations(ctx, field) case "createdAt": return ec.fieldContext_Risk_createdAt(ctx, field) case "updatedAt": @@ -64039,7 +64630,7 @@ func (ec *executionContext) unmarshalInputCreateObligationInput(ctx context.Cont asMap[k] = v } - fieldsInOrder := [...]string{"organizationId", "referenceId", "area", "source", "requirement", "actionsToBeImplemented", "regulator", "ownerId", "lastReviewDate", "dueDate", "status"} + fieldsInOrder := [...]string{"organizationId", "area", "source", "requirement", "actionsToBeImplemented", "regulator", "ownerId", "lastReviewDate", "dueDate", "status"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -64053,13 +64644,6 @@ func (ec *executionContext) unmarshalInputCreateObligationInput(ctx context.Cont return it, err } it.OrganizationID = data - case "referenceId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("referenceId")) - data, err := ec.unmarshalNString2string(ctx, v) - if err != nil { - return it, err - } - it.ReferenceID = data case "area": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("area")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -64529,6 +65113,40 @@ func (ec *executionContext) unmarshalInputCreateRiskMeasureMappingInput(ctx cont return it, nil } +func (ec *executionContext) unmarshalInputCreateRiskObligationMappingInput(ctx context.Context, obj any) (types.CreateRiskObligationMappingInput, error) { + var it types.CreateRiskObligationMappingInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"riskId", "obligationId"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "riskId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("riskId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.RiskID = data + case "obligationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("obligationId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.ObligationID = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputCreateSnapshotInput(ctx context.Context, obj any) (types.CreateSnapshotInput, error) { var it types.CreateSnapshotInput asMap := map[string]any{} @@ -65798,6 +66416,40 @@ func (ec *executionContext) unmarshalInputDeleteRiskMeasureMappingInput(ctx cont return it, nil } +func (ec *executionContext) unmarshalInputDeleteRiskObligationMappingInput(ctx context.Context, obj any) (types.DeleteRiskObligationMappingInput, error) { + var it types.DeleteRiskObligationMappingInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"riskId", "obligationId"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "riskId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("riskId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.RiskID = data + case "obligationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("obligationId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.ObligationID = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputDeleteSnapshotInput(ctx context.Context, obj any) (types.DeleteSnapshotInput, error) { var it types.DeleteSnapshotInput asMap := map[string]any{} @@ -67992,7 +68644,7 @@ func (ec *executionContext) unmarshalInputUpdateObligationInput(ctx context.Cont asMap[k] = v } - fieldsInOrder := [...]string{"id", "referenceId", "area", "source", "requirement", "actionsToBeImplemented", "regulator", "ownerId", "lastReviewDate", "dueDate", "status"} + fieldsInOrder := [...]string{"id", "area", "source", "requirement", "actionsToBeImplemented", "regulator", "ownerId", "lastReviewDate", "dueDate", "status"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -68006,13 +68658,6 @@ func (ec *executionContext) unmarshalInputUpdateObligationInput(ctx context.Cont return it, err } it.ID = data - case "referenceId": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("referenceId")) - data, err := ec.unmarshalOString2ᚖstring(ctx, v) - if err != nil { - return it, err - } - it.ReferenceID = data case "area": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("area")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -72459,6 +73104,50 @@ func (ec *executionContext) _CreateRiskMeasureMappingPayload(ctx context.Context return out } +var createRiskObligationMappingPayloadImplementors = []string{"CreateRiskObligationMappingPayload"} + +func (ec *executionContext) _CreateRiskObligationMappingPayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreateRiskObligationMappingPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, createRiskObligationMappingPayloadImplementors) + + 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("CreateRiskObligationMappingPayload") + case "riskEdge": + out.Values[i] = ec._CreateRiskObligationMappingPayload_riskEdge(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "obligationEdge": + out.Values[i] = ec._CreateRiskObligationMappingPayload_obligationEdge(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 createRiskPayloadImplementors = []string{"CreateRiskPayload"} func (ec *executionContext) _CreateRiskPayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreateRiskPayload) graphql.Marshaler { @@ -73991,6 +74680,50 @@ func (ec *executionContext) _DeleteRiskMeasureMappingPayload(ctx context.Context return out } +var deleteRiskObligationMappingPayloadImplementors = []string{"DeleteRiskObligationMappingPayload"} + +func (ec *executionContext) _DeleteRiskObligationMappingPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteRiskObligationMappingPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteRiskObligationMappingPayloadImplementors) + + 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("DeleteRiskObligationMappingPayload") + case "deletedRiskId": + out.Values[i] = ec._DeleteRiskObligationMappingPayload_deletedRiskId(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deletedObligationId": + out.Values[i] = ec._DeleteRiskObligationMappingPayload_deletedObligationId(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 deleteRiskPayloadImplementors = []string{"DeleteRiskPayload"} func (ec *executionContext) _DeleteRiskPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteRiskPayload) graphql.Marshaler { @@ -76977,6 +77710,20 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "createRiskObligationMapping": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_createRiskObligationMapping(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deleteRiskObligationMapping": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_deleteRiskObligationMapping(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "requestEvidence": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_requestEvidence(ctx, field) @@ -77756,11 +78503,6 @@ func (ec *executionContext) _Obligation(ctx context.Context, sel ast.SelectionSe } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "referenceId": - out.Values[i] = ec._Obligation_referenceId(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) - } case "area": out.Values[i] = ec._Obligation_area(ctx, field, obj) case "source": @@ -79951,6 +80693,42 @@ func (ec *executionContext) _Risk(ctx context.Context, sel ast.SelectionSet, obj continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "obligations": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Risk_obligations(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "createdAt": out.Values[i] = ec._Risk_createdAt(ctx, field, obj) @@ -87236,6 +88014,25 @@ func (ec *executionContext) marshalNCreateRiskMeasureMappingPayload2ᚖgithubᚗ return ec._CreateRiskMeasureMappingPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNCreateRiskObligationMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateRiskObligationMappingInput(ctx context.Context, v any) (types.CreateRiskObligationMappingInput, error) { + res, err := ec.unmarshalInputCreateRiskObligationMappingInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCreateRiskObligationMappingPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateRiskObligationMappingPayload(ctx context.Context, sel ast.SelectionSet, v types.CreateRiskObligationMappingPayload) graphql.Marshaler { + return ec._CreateRiskObligationMappingPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCreateRiskObligationMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateRiskObligationMappingPayload(ctx context.Context, sel ast.SelectionSet, v *types.CreateRiskObligationMappingPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._CreateRiskObligationMappingPayload(ctx, sel, v) +} + func (ec *executionContext) marshalNCreateRiskPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateRiskPayload(ctx context.Context, sel ast.SelectionSet, v types.CreateRiskPayload) graphql.Marshaler { return ec._CreateRiskPayload(ctx, sel, &v) } @@ -88061,6 +88858,25 @@ func (ec *executionContext) marshalNDeleteRiskMeasureMappingPayload2ᚖgithubᚗ return ec._DeleteRiskMeasureMappingPayload(ctx, sel, v) } +func (ec *executionContext) unmarshalNDeleteRiskObligationMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteRiskObligationMappingInput(ctx context.Context, v any) (types.DeleteRiskObligationMappingInput, error) { + res, err := ec.unmarshalInputDeleteRiskObligationMappingInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDeleteRiskObligationMappingPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteRiskObligationMappingPayload(ctx context.Context, sel ast.SelectionSet, v types.DeleteRiskObligationMappingPayload) graphql.Marshaler { + return ec._DeleteRiskObligationMappingPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteRiskObligationMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteRiskObligationMappingPayload(ctx context.Context, sel ast.SelectionSet, v *types.DeleteRiskObligationMappingPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._DeleteRiskObligationMappingPayload(ctx, sel, v) +} + func (ec *executionContext) marshalNDeleteRiskPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteRiskPayload(ctx context.Context, sel ast.SelectionSet, v types.DeleteRiskPayload) graphql.Marshaler { return ec._DeleteRiskPayload(ctx, sel, &v) } @@ -89561,14 +90377,12 @@ func (ec *executionContext) marshalNObligationOrderField2githubᚗcomᚋgetprobo var ( unmarshalNObligationOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐObligationOrderField = map[string]coredata.ObligationOrderField{ "CREATED_AT": coredata.ObligationOrderFieldCreatedAt, - "REFERENCE_ID": coredata.ObligationOrderFieldReferenceId, "LAST_REVIEW_DATE": coredata.ObligationOrderFieldLastReviewDate, "DUE_DATE": coredata.ObligationOrderFieldDueDate, "STATUS": coredata.ObligationOrderFieldStatus, } marshalNObligationOrderField2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐObligationOrderField = map[coredata.ObligationOrderField]string{ coredata.ObligationOrderFieldCreatedAt: "CREATED_AT", - coredata.ObligationOrderFieldReferenceId: "REFERENCE_ID", coredata.ObligationOrderFieldLastReviewDate: "LAST_REVIEW_DATE", coredata.ObligationOrderFieldDueDate: "DUE_DATE", coredata.ObligationOrderFieldStatus: "STATUS", @@ -89594,14 +90408,14 @@ func (ec *executionContext) marshalNObligationStatus2githubᚗcomᚋgetproboᚋp var ( unmarshalNObligationStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐObligationStatus = map[string]coredata.ObligationStatus{ - "OPEN": coredata.ObligationStatusOpen, - "IN_PROGRESS": coredata.ObligationStatusInProgress, - "CLOSED": coredata.ObligationStatusClosed, + "NON_COMPLIANT": coredata.ObligationStatusNonCompliant, + "PARTIALLY_COMPLIANT": coredata.ObligationStatusPartiallyCompliant, + "COMPLIANT": coredata.ObligationStatusCompliant, } marshalNObligationStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐObligationStatus = map[coredata.ObligationStatus]string{ - coredata.ObligationStatusOpen: "OPEN", - coredata.ObligationStatusInProgress: "IN_PROGRESS", - coredata.ObligationStatusClosed: "CLOSED", + coredata.ObligationStatusNonCompliant: "NON_COMPLIANT", + coredata.ObligationStatusPartiallyCompliant: "PARTIALLY_COMPLIANT", + coredata.ObligationStatusCompliant: "COMPLIANT", } ) @@ -93878,14 +94692,14 @@ func (ec *executionContext) marshalOObligationStatus2ᚖgithubᚗcomᚋgetprobo var ( unmarshalOObligationStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐObligationStatus = map[string]coredata.ObligationStatus{ - "OPEN": coredata.ObligationStatusOpen, - "IN_PROGRESS": coredata.ObligationStatusInProgress, - "CLOSED": coredata.ObligationStatusClosed, + "NON_COMPLIANT": coredata.ObligationStatusNonCompliant, + "PARTIALLY_COMPLIANT": coredata.ObligationStatusPartiallyCompliant, + "COMPLIANT": coredata.ObligationStatusCompliant, } marshalOObligationStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐObligationStatus = map[coredata.ObligationStatus]string{ - coredata.ObligationStatusOpen: "OPEN", - coredata.ObligationStatusInProgress: "IN_PROGRESS", - coredata.ObligationStatusClosed: "CLOSED", + coredata.ObligationStatusNonCompliant: "NON_COMPLIANT", + coredata.ObligationStatusPartiallyCompliant: "PARTIALLY_COMPLIANT", + coredata.ObligationStatusCompliant: "COMPLIANT", } ) diff --git a/pkg/server/api/console/v1/types/obligation.go b/pkg/server/api/console/v1/types/obligation.go index 1743bb403..c663919fd 100644 --- a/pkg/server/api/console/v1/types/obligation.go +++ b/pkg/server/api/console/v1/types/obligation.go @@ -60,7 +60,6 @@ func NewObligation(cr *coredata.Obligation) *Obligation { ID: cr.ID, SnapshotID: cr.SnapshotID, SourceID: cr.SourceID, - ReferenceID: cr.ReferenceID, Area: cr.Area, Source: cr.Source, Requirement: cr.Requirement, diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index 1a3428908..deff67dd4 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -401,7 +401,6 @@ type CreateNonconformityPayload struct { type CreateObligationInput struct { OrganizationID gid.GID `json:"organizationId"` - ReferenceID string `json:"referenceId"` Area *string `json:"area,omitempty"` Source *string `json:"source,omitempty"` Requirement *string `json:"requirement,omitempty"` @@ -497,6 +496,16 @@ type CreateRiskMeasureMappingPayload struct { MeasureEdge *MeasureEdge `json:"measureEdge"` } +type CreateRiskObligationMappingInput struct { + RiskID gid.GID `json:"riskId"` + ObligationID gid.GID `json:"obligationId"` +} + +type CreateRiskObligationMappingPayload struct { + RiskEdge *RiskEdge `json:"riskEdge"` + ObligationEdge *ObligationEdge `json:"obligationEdge"` +} + type CreateRiskPayload struct { RiskEdge *RiskEdge `json:"riskEdge"` } @@ -828,6 +837,16 @@ type DeleteRiskMeasureMappingPayload struct { DeletedRiskID gid.GID `json:"deletedRiskId"` } +type DeleteRiskObligationMappingInput struct { + RiskID gid.GID `json:"riskId"` + ObligationID gid.GID `json:"obligationId"` +} + +type DeleteRiskObligationMappingPayload struct { + DeletedRiskID gid.GID `json:"deletedRiskId"` + DeletedObligationID gid.GID `json:"deletedObligationId"` +} + type DeleteRiskPayload struct { DeletedRiskID gid.GID `json:"deletedRiskId"` } @@ -1187,7 +1206,6 @@ type Obligation struct { SnapshotID *gid.GID `json:"snapshotId,omitempty"` SourceID *gid.GID `json:"sourceId,omitempty"` Organization *Organization `json:"organization"` - ReferenceID string `json:"referenceId"` Area *string `json:"area,omitempty"` Source *string `json:"source,omitempty"` Requirement *string `json:"requirement,omitempty"` @@ -1409,6 +1427,7 @@ type Risk struct { Measures *MeasureConnection `json:"measures"` Documents *DocumentConnection `json:"documents"` Controls *ControlConnection `json:"controls"` + Obligations *ObligationConnection `json:"obligations"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } @@ -1687,7 +1706,6 @@ type UpdateNonconformityPayload struct { type UpdateObligationInput struct { ID gid.GID `json:"id"` - ReferenceID *string `json:"referenceId,omitempty"` Area *string `json:"area,omitempty"` Source *string `json:"source,omitempty"` Requirement *string `json:"requirement,omitempty"` diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 2857d282d..c23be7aee 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -2209,6 +2209,36 @@ func (r *mutationResolver) DeleteRiskDocumentMapping(ctx context.Context, input }, nil } +// CreateRiskObligationMapping is the resolver for the createRiskObligationMapping field. +func (r *mutationResolver) CreateRiskObligationMapping(ctx context.Context, input types.CreateRiskObligationMappingInput) (*types.CreateRiskObligationMappingPayload, error) { + prb := r.ProboService(ctx, input.RiskID.TenantID()) + + risk, obligation, err := prb.Risks.CreateObligationMapping(ctx, input.RiskID, input.ObligationID) + if err != nil { + panic(fmt.Errorf("cannot create risk obligation mapping: %w", err)) + } + + return &types.CreateRiskObligationMappingPayload{ + RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt), + ObligationEdge: types.NewObligationEdge(obligation, coredata.ObligationOrderFieldCreatedAt), + }, nil +} + +// DeleteRiskObligationMapping is the resolver for the deleteRiskObligationMapping field. +func (r *mutationResolver) DeleteRiskObligationMapping(ctx context.Context, input types.DeleteRiskObligationMappingInput) (*types.DeleteRiskObligationMappingPayload, error) { + prb := r.ProboService(ctx, input.RiskID.TenantID()) + + risk, obligation, err := prb.Risks.DeleteObligationMapping(ctx, input.RiskID, input.ObligationID) + if err != nil { + panic(fmt.Errorf("cannot delete risk obligation mapping: %w", err)) + } + + return &types.DeleteRiskObligationMappingPayload{ + DeletedRiskID: risk.ID, + DeletedObligationID: obligation.ID, + }, nil +} + // RequestEvidence is the resolver for the requestEvidence field. func (r *mutationResolver) RequestEvidence(ctx context.Context, input types.RequestEvidenceInput) (*types.RequestEvidencePayload, error) { prb := r.ProboService(ctx, input.TaskID.TenantID()) @@ -3118,7 +3148,6 @@ func (r *mutationResolver) CreateObligation(ctx context.Context, input types.Cre req := probo.CreateObligationRequest{ OrganizationID: input.OrganizationID, - ReferenceID: input.ReferenceID, Area: input.Area, Source: input.Source, Requirement: input.Requirement, @@ -3146,7 +3175,6 @@ func (r *mutationResolver) UpdateObligation(ctx context.Context, input types.Upd req := probo.UpdateObligationRequest{ ID: input.ID, - ReferenceID: input.ReferenceID, Area: &input.Area, Source: &input.Source, Requirement: &input.Requirement, @@ -3478,6 +3506,17 @@ func (r *obligationConnectionResolver) TotalCount(ctx context.Context, obj *type panic(fmt.Errorf("cannot count obligations: %w", err)) } return count, nil + case *riskResolver: + obligationFilter := coredata.NewObligationFilter(nil) + if obj.Filter != nil { + obligationFilter = coredata.NewObligationFilter(&obj.Filter.SnapshotID) + } + + count, err := prb.Obligations.CountForRiskID(ctx, obj.ParentID, obligationFilter) + if err != nil { + panic(fmt.Errorf("cannot count risk obligations: %w", err)) + } + return count, nil } return 0, fmt.Errorf("unsupported resolver: %T", obj.Resolver) @@ -4388,6 +4427,36 @@ func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int return types.NewControlConnection(page, r, obj.ID, filters), nil } +// Obligations is the resolver for the obligations field. +func (r *riskResolver) Obligations(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) (*types.ObligationConnection, error) { + prb := r.ProboService(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ObligationOrderField]{ + Field: coredata.ObligationOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ObligationOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + var obligationFilter = coredata.NewObligationFilter(nil) + if filter != nil { + obligationFilter = coredata.NewObligationFilter(&filter.SnapshotID) + } + + page, err := prb.Obligations.ListForRiskID(ctx, obj.ID, cursor, obligationFilter) + if err != nil { + panic(fmt.Errorf("cannot list risk obligations: %w", err)) + } + + return types.NewObligationConnection(page, r, obj.ID, filter), nil +} + // TotalCount is the resolver for the totalCount field. func (r *riskConnectionResolver) TotalCount(ctx context.Context, obj *types.RiskConnection) (int, error) { prb := r.ProboService(ctx, obj.ParentID.TenantID())