From d0fc5cd439d13adb1daecee08dde7f425d920186 Mon Sep 17 00:00:00 2001 From: gearnode Date: Mon, 31 Mar 2025 22:01:09 +0200 Subject: [PATCH] Add mapping between control and mitigation Signed-off-by: gearnode --- .../frameworks/FrameworkView.tsx | 2 +- .../frameworks/controls/ControlView.tsx | 770 ++++++++- ...CreateMitigationMappingMutation.graphql.ts | 93 + ...DeleteMitigationMappingMutation.graphql.ts | 93 + ...ntrolViewLinkedMitigationsQuery.graphql.ts | 280 +++ ...iewOrganizationMitigationsQuery.graphql.ts | 280 +++ .../mitigations/MitigationView.tsx | 575 ++++++- ...iewCreateControlMappingMutation.graphql.ts | 93 + ...iewDeleteControlMappingMutation.graphql.ts | 93 + .../MitigationViewFrameworksQuery.graphql.ts | 364 ++++ ...tigationViewLinkedControlsQuery.graphql.ts | 262 +++ pkg/coredata/control.go | 61 +- pkg/coredata/control_mitigation.go | 4 +- pkg/coredata/mitigation.go | 179 +- pkg/probo/control_service.go | 61 + pkg/probo/mitigation_service.go | 21 + pkg/server/api/console/v1/schema.graphql | 60 + pkg/server/api/console/v1/schema/schema.go | 1496 ++++++++++++++++- pkg/server/api/console/v1/types/types.go | 48 +- pkg/server/api/console/v1/v1_resolver.go | 96 ++ 20 files changed, 4775 insertions(+), 156 deletions(-) create mode 100644 apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewCreateMitigationMappingMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewDeleteMitigationMappingMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewLinkedMitigationsQuery.graphql.ts create mode 100644 apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewOrganizationMitigationsQuery.graphql.ts create mode 100644 apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewCreateControlMappingMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewDeleteControlMappingMutation.graphql.ts create mode 100644 apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewFrameworksQuery.graphql.ts create mode 100644 apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewLinkedControlsQuery.graphql.ts diff --git a/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx b/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx index 278dd77a7..ae6c84b3e 100644 --- a/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx +++ b/apps/console/src/pages/organizations/frameworks/FrameworkView.tsx @@ -137,7 +137,7 @@ function FrameworkViewContent({
+ + {/* Control Description */} + {control.description && ( +
{control.description}
+ )} + + {/* Security Measures Section */} +
+ {/* Mitigation Mapping Dialog */} + + + + Link Security Measures to Control + + Search and select security measures to link to this control. + This helps track which security measures address this control. + + + +
+
+
+ + setMitigationSearchQuery(e.target.value)} + className="w-full pl-10" + /> +
+
+
+ +
+
+ +
+ {isLoadingMitigations ? ( +
+ + Loading security measures... +
+ ) : ( +
+ {filteredMitigations().length === 0 ? ( +
+ No security measures found. Try adjusting your search or + select a different category. +
+ ) : ( + + + + + + + + + + + {filteredMitigations().map( + (mitigation: MitigationNode) => { + const isLinked = isMitigationLinked( + mitigation.id + ); + return ( + + + + + + + ); + } + )} + +
Name + Importance + State + Actions +
+
+ {mitigation.name} +
+ {mitigation.description && ( +
+ {mitigation.description} +
+ )} +
+
+ {formatImportance(mitigation.importance)} +
+
+
+ {formatState(mitigation.state)} +
+
+ {isLinked ? ( + + ) : ( + + )} +
+ )} +
+ )} +
+ + + + +
+
+ + {/* Linked Mitigations List */} +
+
+

+ Security measures +

+ +
+ + {isLoadingMitigations ? ( +
+ + Loading security measures... +
+ ) : linkedMitigationsData?.control?.mitigations?.edges && + linkedMitigationsData.control.mitigations.edges.length > 0 ? ( +
+ + + + + + + + + + + {getLinkedMitigations().map( + (mitigation: MitigationNode) => ( + + + + + + + ) + )} + +
NameImportanceState + Actions +
+
{mitigation.name}
+ {mitigation.description && ( +
+ {mitigation.description} +
+ )} +
+
+ {formatImportance(mitigation.importance)} +
+
+
+ {formatState(mitigation.state)} +
+
+
+ + +
+
+
+ ) : ( +
+ No security measures linked to this control yet. Click + "Link Security Measures" to connect some. +
+ )} +
diff --git a/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewCreateMitigationMappingMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewCreateMitigationMappingMutation.graphql.ts new file mode 100644 index 000000000..6ae7302e3 --- /dev/null +++ b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewCreateMitigationMappingMutation.graphql.ts @@ -0,0 +1,93 @@ +/** + * @generated SignedSource<<02b5ad33ae08fb81731cf27c34f31ccf>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type CreateControlMappingInput = { + controlId: string; + mitigationId: string; +}; +export type ControlViewCreateMitigationMappingMutation$variables = { + input: CreateControlMappingInput; +}; +export type ControlViewCreateMitigationMappingMutation$data = { + readonly createControlMapping: { + readonly success: boolean; + }; +}; +export type ControlViewCreateMitigationMappingMutation = { + response: ControlViewCreateMitigationMappingMutation$data; + variables: ControlViewCreateMitigationMappingMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "CreateControlMappingPayload", + "kind": "LinkedField", + "name": "createControlMapping", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "success", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ControlViewCreateMitigationMappingMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ControlViewCreateMitigationMappingMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "1d95423714868543cd1ae0867f7274f5", + "id": null, + "metadata": {}, + "name": "ControlViewCreateMitigationMappingMutation", + "operationKind": "mutation", + "text": "mutation ControlViewCreateMitigationMappingMutation(\n $input: CreateControlMappingInput!\n) {\n createControlMapping(input: $input) {\n success\n }\n}\n" + } +}; +})(); + +(node as any).hash = "caa36b4928fc747295d0b3cb8c90f786"; + +export default node; diff --git a/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewDeleteMitigationMappingMutation.graphql.ts b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewDeleteMitigationMappingMutation.graphql.ts new file mode 100644 index 000000000..0c5513cec --- /dev/null +++ b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewDeleteMitigationMappingMutation.graphql.ts @@ -0,0 +1,93 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type DeleteControlMappingInput = { + controlId: string; + mitigationId: string; +}; +export type ControlViewDeleteMitigationMappingMutation$variables = { + input: DeleteControlMappingInput; +}; +export type ControlViewDeleteMitigationMappingMutation$data = { + readonly deleteControlMapping: { + readonly success: boolean; + }; +}; +export type ControlViewDeleteMitigationMappingMutation = { + response: ControlViewDeleteMitigationMappingMutation$data; + variables: ControlViewDeleteMitigationMappingMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "DeleteControlMappingPayload", + "kind": "LinkedField", + "name": "deleteControlMapping", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "success", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ControlViewDeleteMitigationMappingMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ControlViewDeleteMitigationMappingMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "49d66f6dddce3b6c7a82334e36d9b464", + "id": null, + "metadata": {}, + "name": "ControlViewDeleteMitigationMappingMutation", + "operationKind": "mutation", + "text": "mutation ControlViewDeleteMitigationMappingMutation(\n $input: DeleteControlMappingInput!\n) {\n deleteControlMapping(input: $input) {\n success\n }\n}\n" + } +}; +})(); + +(node as any).hash = "6f954d3c3c2b38b68a0be5f51de3b935"; + +export default node; diff --git a/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewLinkedMitigationsQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewLinkedMitigationsQuery.graphql.ts new file mode 100644 index 000000000..be60b1442 --- /dev/null +++ b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewLinkedMitigationsQuery.graphql.ts @@ -0,0 +1,280 @@ +/** + * @generated SignedSource<<5a15ae55cd27e88ee96715ba785e412d>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type MitigationImportance = "ADVANCED" | "MANDATORY" | "PREFERRED"; +export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED"; +export type ControlViewLinkedMitigationsQuery$variables = { + controlId: string; +}; +export type ControlViewLinkedMitigationsQuery$data = { + readonly control: { + readonly id: string; + readonly mitigations?: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly category: string; + readonly description: string; + readonly id: string; + readonly importance: MitigationImportance; + readonly name: string; + readonly state: MitigationState; + }; + }>; + }; + }; +}; +export type ControlViewLinkedMitigationsQuery = { + response: ControlViewLinkedMitigationsQuery$data; + variables: ControlViewLinkedMitigationsQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "controlId" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "controlId" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v4 = [ + { + "alias": null, + "args": null, + "concreteType": "MitigationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Mitigation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "category", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "importance", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "state", + "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 + } + ], + "storageKey": null + } +], +v5 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ControlViewLinkedMitigationsQuery", + "selections": [ + { + "alias": "control", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": "mitigations", + "args": null, + "concreteType": "MitigationConnection", + "kind": "LinkedField", + "name": "__Control__mitigations_connection", + "plural": false, + "selections": (v4/*: any*/), + "storageKey": null + } + ], + "type": "Control", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ControlViewLinkedMitigationsQuery", + "selections": [ + { + "alias": "control", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v5/*: any*/), + "concreteType": "MitigationConnection", + "kind": "LinkedField", + "name": "mitigations", + "plural": false, + "selections": (v4/*: any*/), + "storageKey": "mitigations(first:100)" + }, + { + "alias": null, + "args": (v5/*: any*/), + "filters": null, + "handle": "connection", + "key": "Control__mitigations", + "kind": "LinkedHandle", + "name": "mitigations" + } + ], + "type": "Control", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "909a089ca2452cc35928267d86d1bb7b", + "id": null, + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "control", + "mitigations" + ] + } + ] + }, + "name": "ControlViewLinkedMitigationsQuery", + "operationKind": "query", + "text": "query ControlViewLinkedMitigationsQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n mitigations(first: 100) {\n edges {\n node {\n id\n name\n description\n category\n importance\n state\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "c89fb8a01e18c89d2ae310f4c7a2b522"; + +export default node; diff --git a/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewOrganizationMitigationsQuery.graphql.ts b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewOrganizationMitigationsQuery.graphql.ts new file mode 100644 index 000000000..fbfee19ef --- /dev/null +++ b/apps/console/src/pages/organizations/frameworks/controls/__generated__/ControlViewOrganizationMitigationsQuery.graphql.ts @@ -0,0 +1,280 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type MitigationImportance = "ADVANCED" | "MANDATORY" | "PREFERRED"; +export type MitigationState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED"; +export type ControlViewOrganizationMitigationsQuery$variables = { + organizationId: string; +}; +export type ControlViewOrganizationMitigationsQuery$data = { + readonly organization: { + readonly id: string; + readonly mitigations?: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly category: string; + readonly description: string; + readonly id: string; + readonly importance: MitigationImportance; + readonly name: string; + readonly state: MitigationState; + }; + }>; + }; + }; +}; +export type ControlViewOrganizationMitigationsQuery = { + response: ControlViewOrganizationMitigationsQuery$data; + variables: ControlViewOrganizationMitigationsQuery$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 = [ + { + "alias": null, + "args": null, + "concreteType": "MitigationEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Mitigation", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "category", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "importance", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "state", + "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 + } + ], + "storageKey": null + } +], +v5 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ControlViewOrganizationMitigationsQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": "mitigations", + "args": null, + "concreteType": "MitigationConnection", + "kind": "LinkedField", + "name": "__Organization__mitigations_connection", + "plural": false, + "selections": (v4/*: any*/), + "storageKey": null + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ControlViewOrganizationMitigationsQuery", + "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": (v5/*: any*/), + "concreteType": "MitigationConnection", + "kind": "LinkedField", + "name": "mitigations", + "plural": false, + "selections": (v4/*: any*/), + "storageKey": "mitigations(first:100)" + }, + { + "alias": null, + "args": (v5/*: any*/), + "filters": null, + "handle": "connection", + "key": "Organization__mitigations", + "kind": "LinkedHandle", + "name": "mitigations" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "f5c0ad1c64f05306ed6c23a5ad4d649e", + "id": null, + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "organization", + "mitigations" + ] + } + ] + }, + "name": "ControlViewOrganizationMitigationsQuery", + "operationKind": "query", + "text": "query ControlViewOrganizationMitigationsQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n mitigations(first: 100) {\n edges {\n node {\n id\n name\n description\n category\n importance\n state\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "5c14bf02367f0b6e5ccc633b7a77d1aa"; + +export default node; diff --git a/apps/console/src/pages/organizations/mitigations/MitigationView.tsx b/apps/console/src/pages/organizations/mitigations/MitigationView.tsx index f347c09ab..82ea1723f 100644 --- a/apps/console/src/pages/organizations/mitigations/MitigationView.tsx +++ b/apps/console/src/pages/organizations/mitigations/MitigationView.tsx @@ -34,6 +34,8 @@ import { UserMinus, User, Link2, + Search, + Link as LinkIcon, } from "lucide-react"; import { Card, CardContent } from "@/components/ui/card"; import { useToast } from "@/hooks/use-toast"; @@ -58,6 +60,7 @@ import { SelectContent, SelectItem, SelectTrigger, + SelectValue, } from "@/components/ui/select"; import ReactMarkdown from "react-markdown"; import { Textarea } from "@/components/ui/textarea"; @@ -70,6 +73,7 @@ import { SheetTitle, SheetClose, } from "@/components/ui/sheet"; +import { Badge } from "@/components/ui/badge"; import { PageTemplate } from "@/components/PageTemplate"; import { MitigationViewSkeleton } from "./MitigationPage"; @@ -83,6 +87,10 @@ import { MitigationViewUnassignTaskMutation as MitigationViewUnassignTaskMutatio import { MitigationViewUpdateMitigationStateMutation as MitigationViewUpdateMitigationStateMutationType } from "./__generated__/MitigationViewUpdateMitigationStateMutation.graphql"; import { MitigationViewQuery as MitigationViewQueryType } from "./__generated__/MitigationViewQuery.graphql"; import { MitigationViewOrganizationQuery$data } from "./__generated__/MitigationViewOrganizationQuery.graphql"; +import { MitigationViewFrameworksQuery$data } from "./__generated__/MitigationViewFrameworksQuery.graphql"; +import { MitigationViewLinkedControlsQuery$data } from "./__generated__/MitigationViewLinkedControlsQuery.graphql"; +import { MitigationViewCreateControlMappingMutation$data } from "./__generated__/MitigationViewCreateControlMappingMutation.graphql"; +import { MitigationViewDeleteControlMappingMutation$data } from "./__generated__/MitigationViewDeleteControlMappingMutation.graphql"; // Function to format ISO8601 duration to human-readable format const formatDuration = (isoDuration: string): string => { @@ -325,6 +333,75 @@ const organizationQuery = graphql` } `; +// New queries and mutations for Control Mapping +const frameworksQuery = graphql` + query MitigationViewFrameworksQuery($organizationId: ID!) { + organization: node(id: $organizationId) { + id + ... on Organization { + frameworks(first: 100) @connection(key: "Organization__frameworks") { + edges { + node { + id + name + controls(first: 100) @connection(key: "Framework__controls") { + edges { + node { + id + referenceId + name + description + } + } + } + } + } + } + } + } + } +`; + +const linkedControlsQuery = graphql` + query MitigationViewLinkedControlsQuery($mitigationId: ID!) { + mitigation: node(id: $mitigationId) { + id + ... on Mitigation { + controls(first: 100) @connection(key: "Mitigation__controls") { + edges { + node { + id + referenceId + name + description + } + } + } + } + } + } +`; + +const createControlMappingMutation = graphql` + mutation MitigationViewCreateControlMappingMutation( + $input: CreateControlMappingInput! + ) { + createControlMapping(input: $input) { + success + } + } +`; + +const deleteControlMappingMutation = graphql` + mutation MitigationViewDeleteControlMappingMutation( + $input: DeleteControlMappingInput! + ) { + deleteControlMapping(input: $input) { + success + } + } +`; + function MitigationViewContent({ queryRef, }: { @@ -347,25 +424,55 @@ function MitigationViewContent({ const [organizationData, setOrganizationData] = useState(null); + // Control mapping state + const [isControlMappingDialogOpen, setIsControlMappingDialogOpen] = + useState(false); + const [frameworksData, setFrameworksData] = useState(null); + const [linkedControlsData, setLinkedControlsData] = useState( + null + ); + const [controlSearchQuery, setControlSearchQuery] = useState(""); + const [selectedFrameworkId, setSelectedFrameworkId] = useState( + null + ); + const [isLoadingControls, setIsLoadingControls] = useState(false); + const [isLinkingControl, setIsLinkingControl] = useState(false); + const [isUnlinkingControl, setIsUnlinkingControl] = useState(false); + + // Create mutation hooks for control mapping + const [commitCreateControlMapping] = useMutation( + createControlMappingMutation + ); + const [commitDeleteControlMapping] = useMutation( + deleteControlMappingMutation + ); + useEffect(() => { if (organizationId) { - fetchQuery(environment, organizationQuery, { - organizationId, - }) - .toPromise() - .then((response) => { - setOrganizationData(response as MitigationViewOrganizationQuery$data); - }) - .catch((error) => { - console.error("Error fetching organization data:", error); - toast({ - title: "Error", - description: "Failed to load people data", - variant: "destructive", - }); - }); + fetchQuery(environment, organizationQuery, { organizationId }).subscribe({ + next: (data) => { + setOrganizationData(data); + }, + error: (error) => { + console.error("Error fetching organization:", error); + }, + }); } - }, [organizationId, environment, toast]); + }, [environment, organizationId]); + + // Load linked controls when component mounts + useEffect(() => { + if (mitigationId) { + fetchQuery(environment, linkedControlsQuery, { mitigationId }).subscribe({ + next: (data) => { + setLinkedControlsData(data); + }, + error: (error) => { + console.error("Error fetching linked controls:", error); + }, + }); + } + }, [environment, mitigationId]); const formatImportance = (importance: string | undefined): string => { if (!importance) return ""; @@ -1123,9 +1230,9 @@ function MitigationViewContent({ // Update SheetContent to handle closing const handleCloseTaskPanel = () => { + setSelectedTask(null); setIsTaskPanelOpen(false); - - // Remove the task ID from URL parameters when closing + // Remove taskId from URL when panel is closed searchParams.delete("taskId"); setSearchParams(searchParams); }; @@ -1242,6 +1349,227 @@ function MitigationViewContent({ [parseISODuration] ); + // Control mapping functions + const loadFrameworksAndControls = useCallback(() => { + if (!organizationId || !mitigationId) return; + + setIsLoadingControls(true); + + // Fetch all frameworks and their controls + fetchQuery(environment, frameworksQuery, { organizationId }).subscribe({ + next: (data: any) => { + setFrameworksData(data); + if ( + data?.organization?.frameworks?.edges?.length > 0 && + !selectedFrameworkId + ) { + // Select the first framework by default if none is selected + setSelectedFrameworkId(data.organization.frameworks.edges[0].node.id); + } + }, + complete: () => { + // Fetch already linked controls for this mitigation + fetchQuery(environment, linkedControlsQuery, { + mitigationId, + }).subscribe({ + next: (data: any) => { + setLinkedControlsData(data); + setIsLoadingControls(false); + }, + error: (error) => { + console.error("Error fetching linked controls:", error); + setIsLoadingControls(false); + toast({ + title: "Error", + description: "Failed to load linked controls.", + variant: "destructive", + }); + }, + }); + }, + error: (error) => { + console.error("Error fetching frameworks:", error); + setIsLoadingControls(false); + toast({ + title: "Error", + description: "Failed to load frameworks and controls.", + variant: "destructive", + }); + }, + }); + }, [environment, mitigationId, organizationId, selectedFrameworkId, toast]); + + const getControls = useCallback(() => { + if (!frameworksData?.organization?.frameworks?.edges) return []; + + // Get controls from the selected framework + const frameworks = frameworksData.organization.frameworks.edges; + if (selectedFrameworkId) { + const selectedFramework = frameworks.find( + (edge: any) => edge.node.id === selectedFrameworkId + ); + + if (selectedFramework?.node?.controls?.edges) { + return selectedFramework.node.controls.edges.map( + (edge: any) => edge.node + ); + } + } + + // If no framework is selected or it doesn't have controls, return controls from all frameworks + return frameworks.flatMap((framework: any) => + framework.node.controls.edges.map((edge: any) => edge.node) + ); + }, [frameworksData, selectedFrameworkId]); + + const getLinkedControls = useCallback(() => { + if (!linkedControlsData?.mitigation?.controls?.edges) return []; + return linkedControlsData.mitigation.controls.edges.map( + (edge) => edge.node + ); + }, [linkedControlsData]); + + const isControlLinked = useCallback( + (controlId: string) => { + const linkedControls = getLinkedControls(); + return linkedControls.some((control: any) => control.id === controlId); + }, + [getLinkedControls] + ); + + const handleLinkControl = useCallback( + (controlId: string) => { + if (!mitigationId) return; + + setIsLinkingControl(true); + + commitCreateControlMapping({ + variables: { + input: { + controlId, + mitigationId, + }, + }, + onCompleted: (_, errors) => { + setIsLinkingControl(false); + + if (errors) { + console.error("Error linking control:", errors); + toast({ + title: "Error", + description: "Failed to link control. Please try again.", + variant: "destructive", + }); + return; + } + + // Refresh linked controls data + fetchQuery(environment, linkedControlsQuery, { + mitigationId, + }).subscribe({ + next: (data: any) => { + setLinkedControlsData(data); + }, + error: (error) => { + console.error("Error refreshing linked controls:", error); + }, + }); + + toast({ + title: "Success", + description: "Control successfully linked to mitigation.", + }); + }, + onError: (error) => { + setIsLinkingControl(false); + console.error("Error linking control:", error); + toast({ + title: "Error", + description: "Failed to link control. Please try again.", + variant: "destructive", + }); + }, + }); + }, + [commitCreateControlMapping, environment, mitigationId, toast] + ); + + const handleUnlinkControl = useCallback( + (controlId: string) => { + if (!mitigationId) return; + + setIsUnlinkingControl(true); + + commitDeleteControlMapping({ + variables: { + input: { + controlId, + mitigationId, + }, + }, + onCompleted: (_, errors) => { + setIsUnlinkingControl(false); + + if (errors) { + console.error("Error unlinking control:", errors); + toast({ + title: "Error", + description: "Failed to unlink control. Please try again.", + variant: "destructive", + }); + return; + } + + // Refresh linked controls data + fetchQuery(environment, linkedControlsQuery, { + mitigationId, + }).subscribe({ + next: (data: any) => { + setLinkedControlsData(data); + }, + error: (error) => { + console.error("Error refreshing linked controls:", error); + }, + }); + + toast({ + title: "Success", + description: "Control successfully unlinked from mitigation.", + }); + }, + onError: (error) => { + setIsUnlinkingControl(false); + console.error("Error unlinking control:", error); + toast({ + title: "Error", + description: "Failed to unlink control. Please try again.", + variant: "destructive", + }); + }, + }); + }, + [commitDeleteControlMapping, environment, mitigationId, toast] + ); + + const handleOpenControlMappingDialog = useCallback(() => { + loadFrameworksAndControls(); + setIsControlMappingDialogOpen(true); + }, [loadFrameworksAndControls]); + + const filteredControls = useCallback(() => { + const controls = getControls(); + if (!controlSearchQuery) return controls; + + const lowerQuery = controlSearchQuery.toLowerCase(); + return controls.filter( + (control: any) => + control.referenceId.toLowerCase().includes(lowerQuery) || + control.name.toLowerCase().includes(lowerQuery) || + (control.description && + control.description.toLowerCase().includes(lowerQuery)) + ); + }, [controlSearchQuery, getControls]); + return ( + {/* Control Mapping Section */} +
+
+

Controls

+ +
+ + {/* Control Mapping Dialog */} + + + + Map Mitigation to Controls + + Search and select controls to link to this mitigation. This + helps track which controls are addressed by this mitigation. + + + +
+
+
+ + setControlSearchQuery(e.target.value)} + className="w-full pl-10" + /> +
+
+ +
+ +
+
+ +
+ {isLoadingControls ? ( +
+ + Loading controls... +
+ ) : ( +
+ {filteredControls().length === 0 ? ( +
+ No controls found. Try adjusting your search or select a + different framework. +
+ ) : ( + filteredControls().map((control: any) => { + const isLinked = isControlLinked(control.id); + return ( + +
+
+
+
+
+ {control.referenceId} +
+ {isLinked && ( + + Linked + + )} +
+

{control.name}

+ {control.description && ( +

+ {control.description} +

+ )} +
+
+ {isLinked ? ( + + ) : ( + + )} +
+
+
+
+ ); + }) + )} +
+ )} +
+ + + + +
+
+ + {/* Linked Controls List */} + + + {linkedControlsData?.mitigation?.controls?.edges?.length > 0 ? ( +
+ {getLinkedControls().map((control: any) => ( + +
+
+
+ {control.referenceId} +
+
+

{control.name}

+ {control.description && ( +

+ {control.description} +

+ )} +
+ +
+
+
+ ))} +
+ ) : ( +
+ No controls linked to this mitigation yet. Click "Map to + Controls" to link controls. +
+ )} +
+
+
+

Tasks

diff --git a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewCreateControlMappingMutation.graphql.ts b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewCreateControlMappingMutation.graphql.ts new file mode 100644 index 000000000..9391e4df0 --- /dev/null +++ b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewCreateControlMappingMutation.graphql.ts @@ -0,0 +1,93 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type CreateControlMappingInput = { + controlId: string; + mitigationId: string; +}; +export type MitigationViewCreateControlMappingMutation$variables = { + input: CreateControlMappingInput; +}; +export type MitigationViewCreateControlMappingMutation$data = { + readonly createControlMapping: { + readonly success: boolean; + }; +}; +export type MitigationViewCreateControlMappingMutation = { + response: MitigationViewCreateControlMappingMutation$data; + variables: MitigationViewCreateControlMappingMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "CreateControlMappingPayload", + "kind": "LinkedField", + "name": "createControlMapping", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "success", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "MitigationViewCreateControlMappingMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "MitigationViewCreateControlMappingMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "79290e466d2aa856113aae8a4e7a0035", + "id": null, + "metadata": {}, + "name": "MitigationViewCreateControlMappingMutation", + "operationKind": "mutation", + "text": "mutation MitigationViewCreateControlMappingMutation(\n $input: CreateControlMappingInput!\n) {\n createControlMapping(input: $input) {\n success\n }\n}\n" + } +}; +})(); + +(node as any).hash = "32a27dc5fdd06c261258e80db3db50fc"; + +export default node; diff --git a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewDeleteControlMappingMutation.graphql.ts b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewDeleteControlMappingMutation.graphql.ts new file mode 100644 index 000000000..04e5408e4 --- /dev/null +++ b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewDeleteControlMappingMutation.graphql.ts @@ -0,0 +1,93 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type DeleteControlMappingInput = { + controlId: string; + mitigationId: string; +}; +export type MitigationViewDeleteControlMappingMutation$variables = { + input: DeleteControlMappingInput; +}; +export type MitigationViewDeleteControlMappingMutation$data = { + readonly deleteControlMapping: { + readonly success: boolean; + }; +}; +export type MitigationViewDeleteControlMappingMutation = { + response: MitigationViewDeleteControlMappingMutation$data; + variables: MitigationViewDeleteControlMappingMutation$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "input" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "input", + "variableName": "input" + } + ], + "concreteType": "DeleteControlMappingPayload", + "kind": "LinkedField", + "name": "deleteControlMapping", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "success", + "storageKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "MitigationViewDeleteControlMappingMutation", + "selections": (v1/*: any*/), + "type": "Mutation", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "MitigationViewDeleteControlMappingMutation", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "824394c9cc09d7d428ad42835d3737ef", + "id": null, + "metadata": {}, + "name": "MitigationViewDeleteControlMappingMutation", + "operationKind": "mutation", + "text": "mutation MitigationViewDeleteControlMappingMutation(\n $input: DeleteControlMappingInput!\n) {\n deleteControlMapping(input: $input) {\n success\n }\n}\n" + } +}; +})(); + +(node as any).hash = "4d7bd098ed16e825abec0e5d996b06e9"; + +export default node; diff --git a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewFrameworksQuery.graphql.ts b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewFrameworksQuery.graphql.ts new file mode 100644 index 000000000..13c85de6f --- /dev/null +++ b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewFrameworksQuery.graphql.ts @@ -0,0 +1,364 @@ +/** + * @generated SignedSource<<641911581864265e2933d2d5f2705bfe>> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type MitigationViewFrameworksQuery$variables = { + organizationId: string; +}; +export type MitigationViewFrameworksQuery$data = { + readonly organization: { + readonly frameworks?: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly controls: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly description: string; + readonly id: string; + readonly name: string; + readonly referenceId: string; + }; + }>; + }; + readonly id: string; + readonly name: string; + }; + }>; + }; + readonly id: string; + }; +}; +export type MitigationViewFrameworksQuery = { + response: MitigationViewFrameworksQuery$data; + variables: MitigationViewFrameworksQuery$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": "name", + "storageKey": null +}, +v4 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null +}, +v6 = { + "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 +}, +v7 = [ + { + "alias": null, + "args": null, + "concreteType": "ControlEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Control", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "referenceId", + "storageKey": null + }, + (v3/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "storageKey": null + }, + (v4/*: any*/) + ], + "storageKey": null + }, + (v5/*: any*/) + ], + "storageKey": null + }, + (v6/*: any*/) +], +v8 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "MitigationViewFrameworksQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": "frameworks", + "args": null, + "concreteType": "FrameworkConnection", + "kind": "LinkedField", + "name": "__Organization__frameworks_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FrameworkEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Framework", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "alias": "controls", + "args": null, + "concreteType": "ControlConnection", + "kind": "LinkedField", + "name": "__Framework__controls_connection", + "plural": false, + "selections": (v7/*: any*/), + "storageKey": null + }, + (v4/*: any*/) + ], + "storageKey": null + }, + (v5/*: any*/) + ], + "storageKey": null + }, + (v6/*: any*/) + ], + "storageKey": null + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "MitigationViewFrameworksQuery", + "selections": [ + { + "alias": "organization", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v4/*: any*/), + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v8/*: any*/), + "concreteType": "FrameworkConnection", + "kind": "LinkedField", + "name": "frameworks", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FrameworkEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Framework", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + (v3/*: any*/), + { + "alias": null, + "args": (v8/*: any*/), + "concreteType": "ControlConnection", + "kind": "LinkedField", + "name": "controls", + "plural": false, + "selections": (v7/*: any*/), + "storageKey": "controls(first:100)" + }, + { + "alias": null, + "args": (v8/*: any*/), + "filters": null, + "handle": "connection", + "key": "Framework__controls", + "kind": "LinkedHandle", + "name": "controls" + }, + (v4/*: any*/) + ], + "storageKey": null + }, + (v5/*: any*/) + ], + "storageKey": null + }, + (v6/*: any*/) + ], + "storageKey": "frameworks(first:100)" + }, + { + "alias": null, + "args": (v8/*: any*/), + "filters": null, + "handle": "connection", + "key": "Organization__frameworks", + "kind": "LinkedHandle", + "name": "frameworks" + } + ], + "type": "Organization", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "34e6669a992e649351d4284a2d66c66a", + "id": null, + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": null + }, + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "organization", + "frameworks" + ] + } + ] + }, + "name": "MitigationViewFrameworksQuery", + "operationKind": "query", + "text": "query MitigationViewFrameworksQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n frameworks(first: 100) {\n edges {\n node {\n id\n name\n controls(first: 100) {\n edges {\n node {\n id\n referenceId\n name\n description\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "f4f9ed0abc3a0d36365801dcc60c4333"; + +export default node; diff --git a/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewLinkedControlsQuery.graphql.ts b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewLinkedControlsQuery.graphql.ts new file mode 100644 index 000000000..599713d53 --- /dev/null +++ b/apps/console/src/pages/organizations/mitigations/__generated__/MitigationViewLinkedControlsQuery.graphql.ts @@ -0,0 +1,262 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest } from 'relay-runtime'; +export type MitigationViewLinkedControlsQuery$variables = { + mitigationId: string; +}; +export type MitigationViewLinkedControlsQuery$data = { + readonly mitigation: { + readonly controls?: { + readonly edges: ReadonlyArray<{ + readonly node: { + readonly description: string; + readonly id: string; + readonly name: string; + readonly referenceId: string; + }; + }>; + }; + readonly id: string; + }; +}; +export type MitigationViewLinkedControlsQuery = { + response: MitigationViewLinkedControlsQuery$data; + variables: MitigationViewLinkedControlsQuery$variables; +}; + +const node: ConcreteRequest = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "mitigationId" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "mitigationId" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}, +v4 = [ + { + "alias": null, + "args": null, + "concreteType": "ControlEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Control", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "referenceId", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "description", + "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 + } + ], + "storageKey": null + } +], +v5 = [ + { + "kind": "Literal", + "name": "first", + "value": 100 + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "MitigationViewLinkedControlsQuery", + "selections": [ + { + "alias": "mitigation", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": "controls", + "args": null, + "concreteType": "ControlConnection", + "kind": "LinkedField", + "name": "__Mitigation__controls_connection", + "plural": false, + "selections": (v4/*: any*/), + "storageKey": null + } + ], + "type": "Mitigation", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "MitigationViewLinkedControlsQuery", + "selections": [ + { + "alias": "mitigation", + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + (v3/*: any*/), + (v2/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": (v5/*: any*/), + "concreteType": "ControlConnection", + "kind": "LinkedField", + "name": "controls", + "plural": false, + "selections": (v4/*: any*/), + "storageKey": "controls(first:100)" + }, + { + "alias": null, + "args": (v5/*: any*/), + "filters": null, + "handle": "connection", + "key": "Mitigation__controls", + "kind": "LinkedHandle", + "name": "controls" + } + ], + "type": "Mitigation", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "7429166728b5a233e15cb0f4cb33d15c", + "id": null, + "metadata": { + "connection": [ + { + "count": null, + "cursor": null, + "direction": "forward", + "path": [ + "mitigation", + "controls" + ] + } + ] + }, + "name": "MitigationViewLinkedControlsQuery", + "operationKind": "query", + "text": "query MitigationViewLinkedControlsQuery(\n $mitigationId: ID!\n) {\n mitigation: node(id: $mitigationId) {\n __typename\n id\n ... on Mitigation {\n controls(first: 100) {\n edges {\n node {\n id\n referenceId\n name\n description\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n" + } +}; +})(); + +(node as any).hash = "0748d068025e5f577ab56a8014ee38cc"; + +export default node; diff --git a/pkg/coredata/control.go b/pkg/coredata/control.go index 8d5a0a258..f520dd0fa 100644 --- a/pkg/coredata/control.go +++ b/pkg/coredata/control.go @@ -30,8 +30,8 @@ type ( Control struct { ID gid.GID `db:"id"` ReferenceID string `db:"reference_id"` - FrameworkID gid.GID `db:"framework_id"` TenantID gid.TenantID `db:"tenant_id"` + FrameworkID gid.GID `db:"framework_id"` Name string `db:"name"` Description string `db:"description"` CreatedAt time.Time `db:"created_at"` @@ -56,6 +56,65 @@ func (c Control) CursorKey(orderBy ControlOrderField) page.CursorKey { panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } +func (c *Controls) LoadByMitigationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + mitigationID gid.GID, + cursor *page.Cursor[ControlOrderField], +) error { + q := ` +WITH ctrl AS ( + SELECT + c.id, + c.reference_id, + c.framework_id, + c.tenant_id, + c.name, + c.description, + c.created_at, + c.updated_at + FROM + controls c + INNER JOIN + controls_mitigations cm ON c.id = cm.control_id + WHERE + cm.mitigation_id = @mitigation_id +) +SELECT + id, + reference_id, + framework_id, + tenant_id, + name, + description, + created_at, + updated_at +FROM + ctrl +WHERE %s + AND %s +` + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.NamedArgs{"mitigation_id": mitigationID} + maps.Copy(args, scope.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query controls: %w", err) + } + + controls, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Control]) + if err != nil { + return fmt.Errorf("cannot collect controls: %w", err) + } + + *c = controls + + return nil +} + func (c *Controls) LoadByFrameworkID( ctx context.Context, conn pg.Conn, diff --git a/pkg/coredata/control_mitigation.go b/pkg/coredata/control_mitigation.go index 92145b96b..0004a05d3 100644 --- a/pkg/coredata/control_mitigation.go +++ b/pkg/coredata/control_mitigation.go @@ -43,7 +43,7 @@ func (cm ControlMitigation) Insert( ) error { q := ` INSERT INTO - control_mitigations ( + controls_mitigations ( control_id, mitigation_id, tenant_id, @@ -75,7 +75,7 @@ func (cm ControlMitigation) Delete( q := ` DELETE FROM - control_mitigations + controls_mitigations WHERE %s AND control_id = @control_id diff --git a/pkg/coredata/mitigation.go b/pkg/coredata/mitigation.go index 274080c09..a16cc2d6b 100644 --- a/pkg/coredata/mitigation.go +++ b/pkg/coredata/mitigation.go @@ -30,6 +30,7 @@ import ( type ( Mitigation struct { ID gid.GID `db:"id"` + TenantID gid.TenantID `db:"tenant_id"` OrganizationID gid.GID `db:"organization_id"` Category string `db:"category"` Name string `db:"name"` @@ -39,7 +40,6 @@ type ( ContentRef string `db:"content_ref"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` - Version int `db:"version"` Standards []string `db:"standards"` } @@ -55,6 +55,123 @@ func (c Mitigation) CursorKey(orderBy MitigationOrderField) page.CursorKey { panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } +func (c *Mitigations) LoadByControlID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + controlID gid.GID, + cursor *page.Cursor[MitigationOrderField], +) error { + q := ` +WITH mtgtns AS ( + SELECT + m.id, + m.tenant_id, + m.organization_id, + m.category, + m.name, + m.description, + m.state, + m.importance, + m.content_ref, + m.created_at, + m.updated_at, + m.standards + FROM + mitigations m + INNER JOIN + controls_mitigations cm ON m.id = cm.mitigation_id + WHERE + cm.control_id = @control_id +) +SELECT + id, + tenant_id, + organization_id, + category, + name, + description, + state, + importance, + content_ref, + created_at, + updated_at, + standards +FROM + mtgtns +WHERE %s + AND %s +` + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"control_id": controlID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query mitigations: %w", err) + } + + mitigations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Mitigation]) + if err != nil { + return fmt.Errorf("cannot collect mitigations: %w", err) + } + + *c = mitigations + + return nil +} + +func (c *Mitigations) LoadByOrganizationID( + ctx context.Context, + conn pg.Conn, + scope Scoper, + organizationID gid.GID, + cursor *page.Cursor[MitigationOrderField], +) error { + q := ` +SELECT + id, + tenant_id, + organization_id, + category, + name, + description, + state, + importance, + content_ref, + created_at, + updated_at, + standards +FROM + mitigations +WHERE + %s + AND organization_id = @organization_id + AND %s +` + q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) + + args := pgx.StrictNamedArgs{"organization_id": organizationID} + maps.Copy(args, scope.SQLArguments()) + maps.Copy(args, cursor.SQLArguments()) + + rows, err := conn.Query(ctx, q, args) + if err != nil { + return fmt.Errorf("cannot query mitigations: %w", err) + } + + mitigations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Mitigation]) + if err != nil { + return fmt.Errorf("cannot collect mitigations: %w", err) + } + + *c = mitigations + + return nil +} + func (c *Mitigation) LoadByID( ctx context.Context, conn pg.Conn, @@ -64,6 +181,7 @@ func (c *Mitigation) LoadByID( q := ` SELECT id, + tenant_id, organization_id, category, name, @@ -73,8 +191,7 @@ SELECT content_ref, created_at, updated_at, - standards, - version + standards FROM mitigations WHERE @@ -122,8 +239,7 @@ INSERT INTO content_ref, created_at, updated_at, - standards, - version + standards ) VALUES ( @tenant_id, @@ -137,8 +253,7 @@ VALUES ( @content_ref, @created_at, @updated_at, - @standards, - @version + @standards ); ` @@ -148,7 +263,6 @@ VALUES ( "organization_id": c.OrganizationID, "category": c.Category, "name": c.Name, - "version": 0, "description": c.Description, "content_ref": c.ContentRef, "created_at": c.CreatedAt, @@ -161,55 +275,6 @@ VALUES ( return err } -func (c *Mitigations) LoadByOrganizationID( - ctx context.Context, - conn pg.Conn, - scope Scoper, - organizationID gid.GID, - cursor *page.Cursor[MitigationOrderField], -) error { - q := ` -SELECT - id, - organization_id, - category, - name, - description, - state, - importance, - content_ref, - created_at, - updated_at, - standards, - version -FROM - mitigations -WHERE - %s - AND organization_id = @organization_id - AND %s -` - q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) - - args := pgx.StrictNamedArgs{"organization_id": organizationID} - maps.Copy(args, scope.SQLArguments()) - maps.Copy(args, cursor.SQLArguments()) - - rows, err := conn.Query(ctx, q, args) - if err != nil { - return fmt.Errorf("cannot query mitigations: %w", err) - } - - mitigations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Mitigation]) - if err != nil { - return fmt.Errorf("cannot collect mitigations: %w", err) - } - - *c = mitigations - - return nil -} - func (c *Mitigation) Update( ctx context.Context, conn pg.Conn, diff --git a/pkg/probo/control_service.go b/pkg/probo/control_service.go index 7e6b0daf2..d7b8a3115 100644 --- a/pkg/probo/control_service.go +++ b/pkg/probo/control_service.go @@ -55,6 +55,67 @@ type ( } ) +func (s ControlService) ListForMitigationID( + ctx context.Context, + mitigationID gid.GID, + cursor *page.Cursor[coredata.ControlOrderField], +) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) { + var controls coredata.Controls + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controls.LoadByMitigationID(ctx, conn, s.svc.scope, mitigationID, cursor) + }, + ) + + if err != nil { + return nil, fmt.Errorf("cannot list controls: %w", err) + } + + return page.NewPage(controls, cursor), nil +} + +func (s ControlService) CreateMapping( + ctx context.Context, + controlID gid.GID, + mitigationID gid.GID, +) error { + controlMitigation := &coredata.ControlMitigation{ + ControlID: controlID, + MitigationID: mitigationID, + TenantID: s.svc.scope.GetTenantID(), + CreatedAt: time.Now(), + } + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controlMitigation.Insert(ctx, conn, s.svc.scope) + }, + ) +} + +func (s ControlService) DeleteMapping( + ctx context.Context, + controlID gid.GID, + mitigationID gid.GID, +) error { + controlMitigation := &coredata.ControlMitigation{ + ControlID: controlID, + MitigationID: mitigationID, + TenantID: s.svc.scope.GetTenantID(), + CreatedAt: time.Now(), + } + + return s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return controlMitigation.Delete(ctx, conn, s.svc.scope) + }, + ) +} + // Create creates a new control func (s ControlService) Create( ctx context.Context, diff --git a/pkg/probo/mitigation_service.go b/pkg/probo/mitigation_service.go index 5df221d20..027fc4cf8 100644 --- a/pkg/probo/mitigation_service.go +++ b/pkg/probo/mitigation_service.go @@ -57,6 +57,27 @@ type ( } ) +func (s MitigationService) ListForControlID( + ctx context.Context, + controlID gid.GID, + cursor *page.Cursor[coredata.MitigationOrderField], +) (*page.Page[*coredata.Mitigation, coredata.MitigationOrderField], error) { + var mitigations coredata.Mitigations + + err := s.svc.pg.WithConn( + ctx, + func(conn pg.Conn) error { + return mitigations.LoadByControlID(ctx, conn, s.svc.scope, controlID, cursor) + }, + ) + + if err != nil { + return nil, err + } + + return page.NewPage(mitigations, cursor), nil +} + func (s MitigationService) Get( ctx context.Context, mitigationID gid.GID, diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql index 7db7ee4af..9c42bed6f 100644 --- a/pkg/server/api/console/v1/schema.graphql +++ b/pkg/server/api/console/v1/schema.graphql @@ -441,6 +441,15 @@ type Control implements Node { referenceId: String! name: String! description: String! + + mitigations( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: MitigationOrder + ): MitigationConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -461,6 +470,22 @@ type Mitigation implements Node { orderBy: TaskOrder ): TaskConnection! @goField(forceResolver: true) + risks( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: RiskOrder + ): RiskConnection! @goField(forceResolver: true) + + controls( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ControlOrder + ): ControlConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -517,6 +542,15 @@ type Risk implements Node { description: String! probability: Float! impact: Float! + + controls( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ControlOrder + ): ControlConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -694,6 +728,14 @@ type Mutation { updateMitigation(input: UpdateMitigationInput!): UpdateMitigationPayload! importMitigation(input: ImportMitigationInput!): ImportMitigationPayload! + # Control mutations + createControlMapping( + input: CreateControlMappingInput! + ): CreateControlMappingPayload! + deleteControlMapping( + input: DeleteControlMappingInput! + ): DeleteControlMappingPayload! + # Task mutations createTask(input: CreateTaskInput!): CreateTaskPayload! updateTask(input: UpdateTaskInput!): UpdateTaskPayload! @@ -853,6 +895,16 @@ input UnassignTaskInput { taskId: ID! } +input CreateControlMappingInput { + controlId: ID! + mitigationId: ID! +} + +input DeleteControlMappingInput { + controlId: ID! + mitigationId: ID! +} + input CreateRiskInput { organizationId: ID! name: String! @@ -1008,6 +1060,14 @@ type UnassignTaskPayload { task: Task! } +type CreateControlMappingPayload { + success: Boolean! +} + +type DeleteControlMappingPayload { + success: Boolean! +} + type CreateRiskPayload { riskEdge: RiskEdge! } diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go index 83b471402..1237a3d35 100644 --- a/pkg/server/api/console/v1/schema/schema.go +++ b/pkg/server/api/console/v1/schema/schema.go @@ -42,6 +42,7 @@ type Config struct { } type ResolverRoot interface { + Control() ControlResolver Evidence() EvidenceResolver Framework() FrameworkResolver Mitigation() MitigationResolver @@ -49,6 +50,7 @@ type ResolverRoot interface { Organization() OrganizationResolver Policy() PolicyResolver Query() QueryResolver + Risk() RiskResolver Task() TaskResolver Viewer() ViewerResolver } @@ -69,6 +71,7 @@ type ComplexityRoot struct { CreatedAt func(childComplexity int) int Description func(childComplexity int) int ID func(childComplexity int) int + Mitigations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) int Name func(childComplexity int) int ReferenceID func(childComplexity int) int UpdatedAt func(childComplexity int) int @@ -84,6 +87,10 @@ type ComplexityRoot struct { Node func(childComplexity int) int } + CreateControlMappingPayload struct { + Success func(childComplexity int) int + } + CreateFrameworkPayload struct { FrameworkEdge func(childComplexity int) int } @@ -116,6 +123,10 @@ type ComplexityRoot struct { VendorEdge func(childComplexity int) int } + DeleteControlMappingPayload struct { + Success func(childComplexity int) int + } + DeleteEvidencePayload struct { DeletedEvidenceID func(childComplexity int) int } @@ -205,11 +216,13 @@ type ComplexityRoot struct { Mitigation struct { Category func(childComplexity int) int + Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int CreatedAt func(childComplexity int) int Description func(childComplexity int) int ID func(childComplexity int) int Importance func(childComplexity int) int Name func(childComplexity int) int + Risks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) int State func(childComplexity int) int Tasks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) int UpdatedAt func(childComplexity int) int @@ -226,38 +239,40 @@ type ComplexityRoot struct { } Mutation struct { - AssignTask func(childComplexity int, input types.AssignTaskInput) int - ConfirmEmail func(childComplexity int, input types.ConfirmEmailInput) int - CreateFramework func(childComplexity int, input types.CreateFrameworkInput) int - CreateMitigation func(childComplexity int, input types.CreateMitigationInput) int - CreateOrganization func(childComplexity int, input types.CreateOrganizationInput) int - CreatePeople func(childComplexity int, input types.CreatePeopleInput) int - CreatePolicy func(childComplexity int, input types.CreatePolicyInput) int - CreateRisk func(childComplexity int, input types.CreateRiskInput) int - CreateTask func(childComplexity int, input types.CreateTaskInput) int - CreateVendor func(childComplexity int, input types.CreateVendorInput) int - DeleteEvidence func(childComplexity int, input types.DeleteEvidenceInput) int - DeleteFramework func(childComplexity int, input types.DeleteFrameworkInput) int - DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int - DeletePeople func(childComplexity int, input types.DeletePeopleInput) int - DeletePolicy func(childComplexity int, input types.DeletePolicyInput) int - DeleteRisk func(childComplexity int, input types.DeleteRiskInput) int - DeleteTask func(childComplexity int, input types.DeleteTaskInput) int - DeleteVendor func(childComplexity int, input types.DeleteVendorInput) int - ImportFramework func(childComplexity int, input types.ImportFrameworkInput) int - ImportMitigation func(childComplexity int, input types.ImportMitigationInput) int - InviteUser func(childComplexity int, input types.InviteUserInput) int - RemoveUser func(childComplexity int, input types.RemoveUserInput) int - UnassignTask func(childComplexity int, input types.UnassignTaskInput) int - UpdateFramework func(childComplexity int, input types.UpdateFrameworkInput) int - UpdateMitigation func(childComplexity int, input types.UpdateMitigationInput) int - UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int - UpdatePeople func(childComplexity int, input types.UpdatePeopleInput) int - UpdatePolicy func(childComplexity int, input types.UpdatePolicyInput) int - UpdateRisk func(childComplexity int, input types.UpdateRiskInput) int - UpdateTask func(childComplexity int, input types.UpdateTaskInput) int - UpdateVendor func(childComplexity int, input types.UpdateVendorInput) int - UploadEvidence func(childComplexity int, input types.UploadEvidenceInput) int + AssignTask func(childComplexity int, input types.AssignTaskInput) int + ConfirmEmail func(childComplexity int, input types.ConfirmEmailInput) int + CreateControlMapping func(childComplexity int, input types.CreateControlMappingInput) int + CreateFramework func(childComplexity int, input types.CreateFrameworkInput) int + CreateMitigation func(childComplexity int, input types.CreateMitigationInput) int + CreateOrganization func(childComplexity int, input types.CreateOrganizationInput) int + CreatePeople func(childComplexity int, input types.CreatePeopleInput) int + CreatePolicy func(childComplexity int, input types.CreatePolicyInput) int + CreateRisk func(childComplexity int, input types.CreateRiskInput) int + CreateTask func(childComplexity int, input types.CreateTaskInput) int + CreateVendor func(childComplexity int, input types.CreateVendorInput) int + DeleteControlMapping func(childComplexity int, input types.DeleteControlMappingInput) int + DeleteEvidence func(childComplexity int, input types.DeleteEvidenceInput) int + DeleteFramework func(childComplexity int, input types.DeleteFrameworkInput) int + DeleteOrganization func(childComplexity int, input types.DeleteOrganizationInput) int + DeletePeople func(childComplexity int, input types.DeletePeopleInput) int + DeletePolicy func(childComplexity int, input types.DeletePolicyInput) int + DeleteRisk func(childComplexity int, input types.DeleteRiskInput) int + DeleteTask func(childComplexity int, input types.DeleteTaskInput) int + DeleteVendor func(childComplexity int, input types.DeleteVendorInput) int + ImportFramework func(childComplexity int, input types.ImportFrameworkInput) int + ImportMitigation func(childComplexity int, input types.ImportMitigationInput) int + InviteUser func(childComplexity int, input types.InviteUserInput) int + RemoveUser func(childComplexity int, input types.RemoveUserInput) int + UnassignTask func(childComplexity int, input types.UnassignTaskInput) int + UpdateFramework func(childComplexity int, input types.UpdateFrameworkInput) int + UpdateMitigation func(childComplexity int, input types.UpdateMitigationInput) int + UpdateOrganization func(childComplexity int, input types.UpdateOrganizationInput) int + UpdatePeople func(childComplexity int, input types.UpdatePeopleInput) int + UpdatePolicy func(childComplexity int, input types.UpdatePolicyInput) int + UpdateRisk func(childComplexity int, input types.UpdateRiskInput) int + UpdateTask func(childComplexity int, input types.UpdateTaskInput) int + UpdateVendor func(childComplexity int, input types.UpdateVendorInput) int + UploadEvidence func(childComplexity int, input types.UploadEvidenceInput) int } Organization struct { @@ -343,6 +358,7 @@ type ComplexityRoot struct { } Risk struct { + Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int CreatedAt func(childComplexity int) int Description func(childComplexity int) int ID func(childComplexity int) int @@ -479,6 +495,9 @@ type ComplexityRoot struct { } } +type ControlResolver interface { + Mitigations(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) (*types.MitigationConnection, error) +} type EvidenceResolver interface { FileURL(ctx context.Context, obj *types.Evidence) (*string, error) } @@ -487,6 +506,8 @@ type FrameworkResolver interface { } type MitigationResolver interface { Tasks(ctx context.Context, obj *types.Mitigation, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error) + Risks(ctx context.Context, obj *types.Mitigation, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) (*types.RiskConnection, error) + Controls(ctx context.Context, obj *types.Mitigation, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) } type MutationResolver interface { CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) @@ -508,6 +529,8 @@ type MutationResolver interface { CreateMitigation(ctx context.Context, input types.CreateMitigationInput) (*types.CreateMitigationPayload, error) UpdateMitigation(ctx context.Context, input types.UpdateMitigationInput) (*types.UpdateMitigationPayload, error) ImportMitigation(ctx context.Context, input types.ImportMitigationInput) (*types.ImportMitigationPayload, error) + CreateControlMapping(ctx context.Context, input types.CreateControlMappingInput) (*types.CreateControlMappingPayload, error) + DeleteControlMapping(ctx context.Context, input types.DeleteControlMappingInput) (*types.DeleteControlMappingPayload, error) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) UpdateTask(ctx context.Context, input types.UpdateTaskInput) (*types.UpdateTaskPayload, error) DeleteTask(ctx context.Context, input types.DeleteTaskInput) (*types.DeleteTaskPayload, error) @@ -539,6 +562,9 @@ type QueryResolver interface { Node(ctx context.Context, id gid.GID) (types.Node, error) Viewer(ctx context.Context) (*types.Viewer, error) } +type RiskResolver interface { + Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) +} type TaskResolver interface { AssignedTo(ctx context.Context, obj *types.Task) (*types.People, error) Evidences(ctx context.Context, obj *types.Task, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error) @@ -601,6 +627,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Control.ID(childComplexity), true + case "Control.mitigations": + if e.complexity.Control.Mitigations == nil { + break + } + + args, err := ec.field_Control_mitigations_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Control.Mitigations(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.MitigationOrderBy)), true + case "Control.name": if e.complexity.Control.Name == nil { break @@ -650,6 +688,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.ControlEdge.Node(childComplexity), true + case "CreateControlMappingPayload.success": + if e.complexity.CreateControlMappingPayload.Success == nil { + break + } + + return e.complexity.CreateControlMappingPayload.Success(childComplexity), true + case "CreateFrameworkPayload.frameworkEdge": if e.complexity.CreateFrameworkPayload.FrameworkEdge == nil { break @@ -706,6 +751,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.CreateVendorPayload.VendorEdge(childComplexity), true + case "DeleteControlMappingPayload.success": + if e.complexity.DeleteControlMappingPayload.Success == nil { + break + } + + return e.complexity.DeleteControlMappingPayload.Success(childComplexity), true + case "DeleteEvidencePayload.deletedEvidenceId": if e.complexity.DeleteEvidencePayload.DeletedEvidenceID == nil { break @@ -970,6 +1022,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mitigation.Category(childComplexity), true + case "Mitigation.controls": + if e.complexity.Mitigation.Controls == nil { + break + } + + args, err := ec.field_Mitigation_controls_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mitigation.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true + case "Mitigation.createdAt": if e.complexity.Mitigation.CreatedAt == nil { break @@ -1005,6 +1069,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mitigation.Name(childComplexity), true + case "Mitigation.risks": + if e.complexity.Mitigation.Risks == nil { + break + } + + args, err := ec.field_Mitigation_risks_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mitigation.Risks(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.RiskOrderBy)), true + case "Mitigation.state": if e.complexity.Mitigation.State == nil { break @@ -1083,6 +1159,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.ConfirmEmail(childComplexity, args["input"].(types.ConfirmEmailInput)), true + case "Mutation.createControlMapping": + if e.complexity.Mutation.CreateControlMapping == nil { + break + } + + args, err := ec.field_Mutation_createControlMapping_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.CreateControlMapping(childComplexity, args["input"].(types.CreateControlMappingInput)), true + case "Mutation.createFramework": if e.complexity.Mutation.CreateFramework == nil { break @@ -1179,6 +1267,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.CreateVendor(childComplexity, args["input"].(types.CreateVendorInput)), true + case "Mutation.deleteControlMapping": + if e.complexity.Mutation.DeleteControlMapping == nil { + break + } + + args, err := ec.field_Mutation_deleteControlMapping_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.DeleteControlMapping(childComplexity, args["input"].(types.DeleteControlMappingInput)), true + case "Mutation.deleteEvidence": if e.complexity.Mutation.DeleteEvidence == nil { break @@ -1805,6 +1905,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.RemoveUserPayload.Success(childComplexity), true + case "Risk.controls": + if e.complexity.Risk.Controls == nil { + break + } + + args, err := ec.field_Risk_controls_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Risk.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true + case "Risk.createdAt": if e.complexity.Risk.CreatedAt == nil { break @@ -2274,6 +2386,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputAssignTaskInput, ec.unmarshalInputConfirmEmailInput, ec.unmarshalInputControlOrder, + ec.unmarshalInputCreateControlMappingInput, ec.unmarshalInputCreateFrameworkInput, ec.unmarshalInputCreateMitigationInput, ec.unmarshalInputCreateOrganizationInput, @@ -2282,6 +2395,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputCreateRiskInput, ec.unmarshalInputCreateTaskInput, ec.unmarshalInputCreateVendorInput, + ec.unmarshalInputDeleteControlMappingInput, ec.unmarshalInputDeleteEvidenceInput, ec.unmarshalInputDeleteFrameworkInput, ec.unmarshalInputDeleteOrganizationInput, @@ -2854,6 +2968,15 @@ type Control implements Node { referenceId: String! name: String! description: String! + + mitigations( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: MitigationOrder + ): MitigationConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -2874,6 +2997,22 @@ type Mitigation implements Node { orderBy: TaskOrder ): TaskConnection! @goField(forceResolver: true) + risks( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: RiskOrder + ): RiskConnection! @goField(forceResolver: true) + + controls( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ControlOrder + ): ControlConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -2930,6 +3069,15 @@ type Risk implements Node { description: String! probability: Float! impact: Float! + + controls( + first: Int + after: CursorKey + last: Int + before: CursorKey + orderBy: ControlOrder + ): ControlConnection! @goField(forceResolver: true) + createdAt: Datetime! updatedAt: Datetime! } @@ -3107,6 +3255,14 @@ type Mutation { updateMitigation(input: UpdateMitigationInput!): UpdateMitigationPayload! importMitigation(input: ImportMitigationInput!): ImportMitigationPayload! + # Control mutations + createControlMapping( + input: CreateControlMappingInput! + ): CreateControlMappingPayload! + deleteControlMapping( + input: DeleteControlMappingInput! + ): DeleteControlMappingPayload! + # Task mutations createTask(input: CreateTaskInput!): CreateTaskPayload! updateTask(input: UpdateTaskInput!): UpdateTaskPayload! @@ -3266,6 +3422,16 @@ input UnassignTaskInput { taskId: ID! } +input CreateControlMappingInput { + controlId: ID! + mitigationId: ID! +} + +input DeleteControlMappingInput { + controlId: ID! + mitigationId: ID! +} + input CreateRiskInput { organizationId: ID! name: String! @@ -3421,6 +3587,14 @@ type UnassignTaskPayload { task: Task! } +type CreateControlMappingPayload { + success: Boolean! +} + +type DeleteControlMappingPayload { + success: Boolean! +} + type CreateRiskPayload { riskEdge: RiskEdge! } @@ -3472,6 +3646,101 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) // region ***************************** args.gotpl ***************************** +func (ec *executionContext) field_Control_mitigations_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Control_mitigations_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Control_mitigations_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Control_mitigations_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Control_mitigations_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Control_mitigations_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + return args, nil +} +func (ec *executionContext) field_Control_mitigations_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_Control_mitigations_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_Control_mitigations_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_Control_mitigations_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_Control_mitigations_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.MitigationOrderBy, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalOMitigationOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐMitigationOrderBy(ctx, tmp) + } + + var zeroVal *types.MitigationOrderBy + return zeroVal, nil +} + func (ec *executionContext) field_Framework_controls_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3567,6 +3836,196 @@ func (ec *executionContext) field_Framework_controls_argsOrderBy( return zeroVal, nil } +func (ec *executionContext) field_Mitigation_controls_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mitigation_controls_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Mitigation_controls_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Mitigation_controls_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Mitigation_controls_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Mitigation_controls_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + return args, nil +} +func (ec *executionContext) field_Mitigation_controls_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_Mitigation_controls_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_Mitigation_controls_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_Mitigation_controls_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_Mitigation_controls_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.ControlOrderBy, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlOrderBy(ctx, tmp) + } + + var zeroVal *types.ControlOrderBy + return zeroVal, nil +} + +func (ec *executionContext) field_Mitigation_risks_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mitigation_risks_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Mitigation_risks_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Mitigation_risks_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Mitigation_risks_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Mitigation_risks_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + return args, nil +} +func (ec *executionContext) field_Mitigation_risks_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_Mitigation_risks_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_Mitigation_risks_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_Mitigation_risks_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_Mitigation_risks_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.RiskOrderBy, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalORiskOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐRiskOrderBy(ctx, tmp) + } + + var zeroVal *types.RiskOrderBy + return zeroVal, nil +} + func (ec *executionContext) field_Mitigation_tasks_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3708,6 +4167,29 @@ func (ec *executionContext) field_Mutation_confirmEmail_argsInput( return zeroVal, nil } +func (ec *executionContext) field_Mutation_createControlMapping_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mutation_createControlMapping_argsInput(ctx, rawArgs) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} +func (ec *executionContext) field_Mutation_createControlMapping_argsInput( + ctx context.Context, + rawArgs map[string]any, +) (types.CreateControlMappingInput, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + if tmp, ok := rawArgs["input"]; ok { + return ec.unmarshalNCreateControlMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateControlMappingInput(ctx, tmp) + } + + var zeroVal types.CreateControlMappingInput + return zeroVal, nil +} + func (ec *executionContext) field_Mutation_createFramework_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3892,6 +4374,29 @@ func (ec *executionContext) field_Mutation_createVendor_argsInput( return zeroVal, nil } +func (ec *executionContext) field_Mutation_deleteControlMapping_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Mutation_deleteControlMapping_argsInput(ctx, rawArgs) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} +func (ec *executionContext) field_Mutation_deleteControlMapping_argsInput( + ctx context.Context, + rawArgs map[string]any, +) (types.DeleteControlMappingInput, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("input")) + if tmp, ok := rawArgs["input"]; ok { + return ec.unmarshalNDeleteControlMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteControlMappingInput(ctx, tmp) + } + + var zeroVal types.DeleteControlMappingInput + return zeroVal, nil +} + func (ec *executionContext) field_Mutation_deleteEvidence_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -5109,6 +5614,101 @@ func (ec *executionContext) field_Query_node_argsID( return zeroVal, nil } +func (ec *executionContext) field_Risk_controls_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := ec.field_Risk_controls_argsFirst(ctx, rawArgs) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := ec.field_Risk_controls_argsAfter(ctx, rawArgs) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := ec.field_Risk_controls_argsLast(ctx, rawArgs) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := ec.field_Risk_controls_argsBefore(ctx, rawArgs) + if err != nil { + return nil, err + } + args["before"] = arg3 + arg4, err := ec.field_Risk_controls_argsOrderBy(ctx, rawArgs) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 + return args, nil +} +func (ec *executionContext) field_Risk_controls_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_controls_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_controls_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_controls_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_controls_argsOrderBy( + ctx context.Context, + rawArgs map[string]any, +) (*types.ControlOrderBy, error) { + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("orderBy")) + if tmp, ok := rawArgs["orderBy"]; ok { + return ec.unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlOrderBy(ctx, tmp) + } + + var zeroVal *types.ControlOrderBy + return zeroVal, nil +} + func (ec *executionContext) field_Task_evidences_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -5683,6 +6283,67 @@ func (ec *executionContext) fieldContext_Control_description(_ context.Context, return fc, nil } +func (ec *executionContext) _Control_mitigations(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Control_mitigations(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.Control().Mitigations(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.MitigationOrderBy)) + }) + 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.MitigationConnection) + fc.Result = res + return ec.marshalNMitigationConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐMitigationConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Control_mitigations(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Control", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_MitigationConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_MitigationConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MitigationConnection", 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_Control_mitigations_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Control_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Control_createdAt(ctx, field) if err != nil { @@ -5966,6 +6627,8 @@ func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, fie return ec.fieldContext_Control_name(ctx, field) case "description": return ec.fieldContext_Control_description(ctx, field) + case "mitigations": + return ec.fieldContext_Control_mitigations(ctx, field) case "createdAt": return ec.fieldContext_Control_createdAt(ctx, field) case "updatedAt": @@ -5977,6 +6640,50 @@ func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, fie return fc, nil } +func (ec *executionContext) _CreateControlMappingPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.CreateControlMappingPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_CreateControlMappingPayload_success(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.Success, 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_CreateControlMappingPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "CreateControlMappingPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _CreateFrameworkPayload_frameworkEdge(ctx context.Context, field graphql.CollectedField, obj *types.CreateFrameworkPayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_CreateFrameworkPayload_frameworkEdge(ctx, field) if err != nil { @@ -6377,6 +7084,50 @@ func (ec *executionContext) fieldContext_CreateVendorPayload_vendorEdge(_ contex return fc, nil } +func (ec *executionContext) _DeleteControlMappingPayload_success(ctx context.Context, field graphql.CollectedField, obj *types.DeleteControlMappingPayload) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_DeleteControlMappingPayload_success(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.Success, 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.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_DeleteControlMappingPayload_success(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "DeleteControlMappingPayload", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type Boolean does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _DeleteEvidencePayload_deletedEvidenceId(ctx context.Context, field graphql.CollectedField, obj *types.DeleteEvidencePayload) (ret graphql.Marshaler) { fc, err := ec.fieldContext_DeleteEvidencePayload_deletedEvidenceId(ctx, field) if err != nil { @@ -8379,6 +9130,128 @@ func (ec *executionContext) fieldContext_Mitigation_tasks(ctx context.Context, f return fc, nil } +func (ec *executionContext) _Mitigation_risks(ctx context.Context, field graphql.CollectedField, obj *types.Mitigation) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mitigation_risks(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.Mitigation().Risks(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.RiskOrderBy)) + }) + 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.RiskConnection) + fc.Result = res + return ec.marshalNRiskConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐRiskConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mitigation_risks(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mitigation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_RiskConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_RiskConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type RiskConnection", 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_Mitigation_risks_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mitigation_controls(ctx context.Context, field graphql.CollectedField, obj *types.Mitigation) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mitigation_controls(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.Mitigation().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy)) + }) + 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.ControlConnection) + fc.Result = res + return ec.marshalNControlConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mitigation_controls(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mitigation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "edges": + return ec.fieldContext_ControlConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_ControlConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ControlConnection", 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_Mitigation_controls_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mitigation_createdAt(ctx context.Context, field graphql.CollectedField, obj *types.Mitigation) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mitigation_createdAt(ctx, field) if err != nil { @@ -8668,6 +9541,10 @@ func (ec *executionContext) fieldContext_MitigationEdge_node(_ context.Context, return ec.fieldContext_Mitigation_importance(ctx, field) case "tasks": return ec.fieldContext_Mitigation_tasks(ctx, field) + case "risks": + return ec.fieldContext_Mitigation_risks(ctx, field) + case "controls": + return ec.fieldContext_Mitigation_controls(ctx, field) case "createdAt": return ec.fieldContext_Mitigation_createdAt(ctx, field) case "updatedAt": @@ -9800,6 +10677,124 @@ func (ec *executionContext) fieldContext_Mutation_importMitigation(ctx context.C return fc, nil } +func (ec *executionContext) _Mutation_createControlMapping(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_createControlMapping(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().CreateControlMapping(rctx, fc.Args["input"].(types.CreateControlMappingInput)) + }) + 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.CreateControlMappingPayload) + fc.Result = res + return ec.marshalNCreateControlMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateControlMappingPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_createControlMapping(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 "success": + return ec.fieldContext_CreateControlMappingPayload_success(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CreateControlMappingPayload", 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_createControlMapping_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_deleteControlMapping(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_deleteControlMapping(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().DeleteControlMapping(rctx, fc.Args["input"].(types.DeleteControlMappingInput)) + }) + 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.DeleteControlMappingPayload) + fc.Result = res + return ec.marshalNDeleteControlMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteControlMappingPayload(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Mutation_deleteControlMapping(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 "success": + return ec.fieldContext_DeleteControlMappingPayload_success(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type DeleteControlMappingPayload", 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_deleteControlMapping_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Mutation_createTask(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_createTask(ctx, field) if err != nil { @@ -13192,6 +14187,67 @@ func (ec *executionContext) fieldContext_Risk_impact(_ context.Context, field gr return fc, nil } +func (ec *executionContext) _Risk_controls(ctx context.Context, field graphql.CollectedField, obj *types.Risk) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Risk_controls(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().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy)) + }) + 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.ControlConnection) + fc.Result = res + return ec.marshalNControlConnection2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlConnection(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Risk_controls(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 "edges": + return ec.fieldContext_ControlConnection_edges(ctx, field) + case "pageInfo": + return ec.fieldContext_ControlConnection_pageInfo(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type ControlConnection", 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_controls_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 { @@ -13477,6 +14533,8 @@ func (ec *executionContext) fieldContext_RiskEdge_node(_ context.Context, field return ec.fieldContext_Risk_probability(ctx, field) case "impact": return ec.fieldContext_Risk_impact(ctx, field) + case "controls": + return ec.fieldContext_Risk_controls(ctx, field) case "createdAt": return ec.fieldContext_Risk_createdAt(ctx, field) case "updatedAt": @@ -14386,6 +15444,10 @@ func (ec *executionContext) fieldContext_UpdateMitigationPayload_mitigation(_ co return ec.fieldContext_Mitigation_importance(ctx, field) case "tasks": return ec.fieldContext_Mitigation_tasks(ctx, field) + case "risks": + return ec.fieldContext_Mitigation_risks(ctx, field) + case "controls": + return ec.fieldContext_Mitigation_controls(ctx, field) case "createdAt": return ec.fieldContext_Mitigation_createdAt(ctx, field) case "updatedAt": @@ -14638,6 +15700,8 @@ func (ec *executionContext) fieldContext_UpdateRiskPayload_risk(_ context.Contex return ec.fieldContext_Risk_probability(ctx, field) case "impact": return ec.fieldContext_Risk_impact(ctx, field) + case "controls": + return ec.fieldContext_Risk_controls(ctx, field) case "createdAt": return ec.fieldContext_Risk_createdAt(ctx, field) case "updatedAt": @@ -18198,6 +19262,40 @@ func (ec *executionContext) unmarshalInputControlOrder(ctx context.Context, obj return it, nil } +func (ec *executionContext) unmarshalInputCreateControlMappingInput(ctx context.Context, obj any) (types.CreateControlMappingInput, error) { + var it types.CreateControlMappingInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"controlId", "mitigationId"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "controlId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("controlId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.ControlID = data + case "mitigationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("mitigationId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.MitigationID = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputCreateFrameworkInput(ctx context.Context, obj any) (types.CreateFrameworkInput, error) { var it types.CreateFrameworkInput asMap := map[string]any{} @@ -18638,6 +19736,40 @@ func (ec *executionContext) unmarshalInputCreateVendorInput(ctx context.Context, return it, nil } +func (ec *executionContext) unmarshalInputDeleteControlMappingInput(ctx context.Context, obj any) (types.DeleteControlMappingInput, error) { + var it types.DeleteControlMappingInput + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"controlId", "mitigationId"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "controlId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("controlId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.ControlID = data + case "mitigationId": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("mitigationId")) + data, err := ec.unmarshalNID2githubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v) + if err != nil { + return it, err + } + it.MitigationID = data + } + } + + return it, nil +} + func (ec *executionContext) unmarshalInputDeleteEvidenceInput(ctx context.Context, obj any) (types.DeleteEvidenceInput, error) { var it types.DeleteEvidenceInput asMap := map[string]any{} @@ -20073,32 +21205,68 @@ func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet, case "id": out.Values[i] = ec._Control_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "referenceId": out.Values[i] = ec._Control_referenceId(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "name": out.Values[i] = ec._Control_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "description": out.Values[i] = ec._Control_description(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "mitigations": + 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._Control_mitigations(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._Control_createdAt(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "updatedAt": out.Values[i] = ec._Control_updatedAt(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -20211,6 +21379,45 @@ func (ec *executionContext) _ControlEdge(ctx context.Context, sel ast.SelectionS return out } +var createControlMappingPayloadImplementors = []string{"CreateControlMappingPayload"} + +func (ec *executionContext) _CreateControlMappingPayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreateControlMappingPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, createControlMappingPayloadImplementors) + + 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("CreateControlMappingPayload") + case "success": + out.Values[i] = ec._CreateControlMappingPayload_success(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 createFrameworkPayloadImplementors = []string{"CreateFrameworkPayload"} func (ec *executionContext) _CreateFrameworkPayload(ctx context.Context, sel ast.SelectionSet, obj *types.CreateFrameworkPayload) graphql.Marshaler { @@ -20523,6 +21730,45 @@ func (ec *executionContext) _CreateVendorPayload(ctx context.Context, sel ast.Se return out } +var deleteControlMappingPayloadImplementors = []string{"DeleteControlMappingPayload"} + +func (ec *executionContext) _DeleteControlMappingPayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteControlMappingPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deleteControlMappingPayloadImplementors) + + 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("DeleteControlMappingPayload") + case "success": + out.Values[i] = ec._DeleteControlMappingPayload_success(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 deleteEvidencePayloadImplementors = []string{"DeleteEvidencePayload"} func (ec *executionContext) _DeleteEvidencePayload(ctx context.Context, sel ast.SelectionSet, obj *types.DeleteEvidencePayload) graphql.Marshaler { @@ -21413,6 +22659,78 @@ func (ec *executionContext) _Mitigation(ctx context.Context, sel ast.SelectionSe continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "risks": + 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._Mitigation_risks(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 "controls": + 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._Mitigation_controls(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._Mitigation_createdAt(ctx, field, obj) @@ -21687,6 +23005,20 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } + case "createControlMapping": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_createControlMapping(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deleteControlMapping": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_deleteControlMapping(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } case "createTask": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { return ec._Mutation_createTask(ctx, field) @@ -22770,37 +24102,73 @@ func (ec *executionContext) _Risk(ctx context.Context, sel ast.SelectionSet, obj case "id": out.Values[i] = ec._Risk_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "name": out.Values[i] = ec._Risk_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "description": out.Values[i] = ec._Risk_description(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "probability": out.Values[i] = ec._Risk_probability(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "impact": out.Values[i] = ec._Risk_impact(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "controls": + 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_controls(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) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "updatedAt": out.Values[i] = ec._Risk_updatedAt(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -24458,6 +25826,25 @@ var ( } ) +func (ec *executionContext) unmarshalNCreateControlMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateControlMappingInput(ctx context.Context, v any) (types.CreateControlMappingInput, error) { + res, err := ec.unmarshalInputCreateControlMappingInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCreateControlMappingPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateControlMappingPayload(ctx context.Context, sel ast.SelectionSet, v types.CreateControlMappingPayload) graphql.Marshaler { + return ec._CreateControlMappingPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCreateControlMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateControlMappingPayload(ctx context.Context, sel ast.SelectionSet, v *types.CreateControlMappingPayload) 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._CreateControlMappingPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNCreateFrameworkInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateFrameworkInput(ctx context.Context, v any) (types.CreateFrameworkInput, error) { res, err := ec.unmarshalInputCreateFrameworkInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) @@ -24640,6 +26027,25 @@ func (ec *executionContext) marshalNDatetime2timeᚐTime(ctx context.Context, se return res } +func (ec *executionContext) unmarshalNDeleteControlMappingInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteControlMappingInput(ctx context.Context, v any) (types.DeleteControlMappingInput, error) { + res, err := ec.unmarshalInputDeleteControlMappingInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDeleteControlMappingPayload2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteControlMappingPayload(ctx context.Context, sel ast.SelectionSet, v types.DeleteControlMappingPayload) graphql.Marshaler { + return ec._DeleteControlMappingPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNDeleteControlMappingPayload2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteControlMappingPayload(ctx context.Context, sel ast.SelectionSet, v *types.DeleteControlMappingPayload) 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._DeleteControlMappingPayload(ctx, sel, v) +} + func (ec *executionContext) unmarshalNDeleteEvidenceInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDeleteEvidenceInput(ctx context.Context, v any) (types.DeleteEvidenceInput, error) { res, err := ec.unmarshalInputDeleteEvidenceInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go index 29b61d959..72df53316 100644 --- a/pkg/server/api/console/v1/types/types.go +++ b/pkg/server/api/console/v1/types/types.go @@ -37,12 +37,13 @@ type ConfirmEmailPayload struct { } type Control struct { - ID gid.GID `json:"id"` - ReferenceID string `json:"referenceId"` - Name string `json:"name"` - Description string `json:"description"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID gid.GID `json:"id"` + ReferenceID string `json:"referenceId"` + Name string `json:"name"` + Description string `json:"description"` + Mitigations *MitigationConnection `json:"mitigations"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } func (Control) IsNode() {} @@ -58,6 +59,15 @@ type ControlEdge struct { Node *Control `json:"node"` } +type CreateControlMappingInput struct { + ControlID gid.GID `json:"controlId"` + MitigationID gid.GID `json:"mitigationId"` +} + +type CreateControlMappingPayload struct { + Success bool `json:"success"` +} + type CreateFrameworkInput struct { OrganizationID gid.GID `json:"organizationId"` Name string `json:"name"` @@ -154,6 +164,15 @@ type CreateVendorPayload struct { VendorEdge *VendorEdge `json:"vendorEdge"` } +type DeleteControlMappingInput struct { + ControlID gid.GID `json:"controlId"` + MitigationID gid.GID `json:"mitigationId"` +} + +type DeleteControlMappingPayload struct { + Success bool `json:"success"` +} + type DeleteEvidenceInput struct { EvidenceID gid.GID `json:"evidenceId"` } @@ -303,6 +322,8 @@ type Mitigation struct { State coredata.MitigationState `json:"state"` Importance coredata.MitigationImportance `json:"importance"` Tasks *TaskConnection `json:"tasks"` + Risks *RiskConnection `json:"risks"` + Controls *ControlConnection `json:"controls"` CreatedAt time.Time `json:"createdAt"` UpdatedAt time.Time `json:"updatedAt"` } @@ -423,13 +444,14 @@ type RemoveUserPayload struct { } type Risk struct { - ID gid.GID `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Probability float64 `json:"probability"` - Impact float64 `json:"impact"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` + ID gid.GID `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Probability float64 `json:"probability"` + Impact float64 `json:"impact"` + Controls *ControlConnection `json:"controls"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` } func (Risk) IsNode() {} diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go index 71c24bf51..cbeccf6b6 100644 --- a/pkg/server/api/console/v1/v1_resolver.go +++ b/pkg/server/api/console/v1/v1_resolver.go @@ -19,6 +19,31 @@ import ( "github.com/vektah/gqlparser/v2/gqlerror" ) +// Mitigations is the resolver for the mitigations field. +func (r *controlResolver) Mitigations(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MitigationOrderBy) (*types.MitigationConnection, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.MitigationOrderField]{ + Field: coredata.MitigationOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.MitigationOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := svc.Mitigations.ListForControlID(ctx, obj.ID, cursor) + if err != nil { + return nil, fmt.Errorf("cannot list mitigations: %w", err) + } + + return types.NewMitigationConnection(page), nil +} + // FileURL is the resolver for the fileUrl field. func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*string, error) { svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) @@ -86,6 +111,36 @@ func (r *mitigationResolver) Tasks(ctx context.Context, obj *types.Mitigation, f return types.NewTaskConnection(page), nil } +// Risks is the resolver for the risks field. +func (r *mitigationResolver) Risks(ctx context.Context, obj *types.Mitigation, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) (*types.RiskConnection, error) { + panic(fmt.Errorf("not implemented: Risks - risks")) +} + +// Controls is the resolver for the controls field. +func (r *mitigationResolver) Controls(ctx context.Context, obj *types.Mitigation, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) + + pageOrderBy := page.OrderBy[coredata.ControlOrderField]{ + Field: coredata.ControlOrderFieldCreatedAt, + Direction: page.OrderDirectionDesc, + } + if orderBy != nil { + pageOrderBy = page.OrderBy[coredata.ControlOrderField]{ + Field: orderBy.Field, + Direction: orderBy.Direction, + } + } + + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + + page, err := svc.Controls.ListForMitigationID(ctx, obj.ID, cursor) + if err != nil { + return nil, fmt.Errorf("cannot list mitigation controls: %w", err) + } + + return types.NewControlConnection(page), nil +} + // CreateOrganization is the resolver for the createOrganization field. func (r *mutationResolver) CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error) { svc := r.proboSvc.WithTenant(gid.NewTenantID()) @@ -446,6 +501,34 @@ func (r *mutationResolver) ImportMitigation(ctx context.Context, input types.Imp }, nil } +// CreateControlMapping is the resolver for the createControlMapping field. +func (r *mutationResolver) CreateControlMapping(ctx context.Context, input types.CreateControlMappingInput) (*types.CreateControlMappingPayload, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, input.MitigationID.TenantID()) + + err := svc.Controls.CreateMapping(ctx, input.ControlID, input.MitigationID) + if err != nil { + return nil, fmt.Errorf("cannot create control mapping: %w", err) + } + + return &types.CreateControlMappingPayload{ + Success: true, + }, nil +} + +// DeleteControlMapping is the resolver for the deleteControlMapping field. +func (r *mutationResolver) DeleteControlMapping(ctx context.Context, input types.DeleteControlMappingInput) (*types.DeleteControlMappingPayload, error) { + svc := r.GetTenantServiceIfAuthorized(ctx, input.MitigationID.TenantID()) + + err := svc.Controls.DeleteMapping(ctx, input.ControlID, input.MitigationID) + if err != nil { + return nil, fmt.Errorf("cannot delete control mapping: %w", err) + } + + return &types.DeleteControlMappingPayload{ + Success: true, + }, nil +} + // CreateTask is the resolver for the createTask field. func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) { svc := r.GetTenantServiceIfAuthorized(ctx, input.MitigationID.TenantID()) @@ -983,6 +1066,11 @@ func (r *queryResolver) Viewer(ctx context.Context) (*types.Viewer, error) { }, nil } +// Controls is the resolver for the controls field. +func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) { + panic(fmt.Errorf("not implemented: Controls - controls")) +} + // AssignedTo is the resolver for the assignedTo field. func (r *taskResolver) AssignedTo(ctx context.Context, obj *types.Task) (*types.People, error) { svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID()) @@ -1053,6 +1141,9 @@ func (r *viewerResolver) Organizations(ctx context.Context, obj *types.Viewer, f }, nil } +// Control returns schema.ControlResolver implementation. +func (r *Resolver) Control() schema.ControlResolver { return &controlResolver{r} } + // Evidence returns schema.EvidenceResolver implementation. func (r *Resolver) Evidence() schema.EvidenceResolver { return &evidenceResolver{r} } @@ -1074,12 +1165,16 @@ func (r *Resolver) Policy() schema.PolicyResolver { return &policyResolver{r} } // Query returns schema.QueryResolver implementation. func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} } +// Risk returns schema.RiskResolver implementation. +func (r *Resolver) Risk() schema.RiskResolver { return &riskResolver{r} } + // Task returns schema.TaskResolver implementation. func (r *Resolver) Task() schema.TaskResolver { return &taskResolver{r} } // Viewer returns schema.ViewerResolver implementation. func (r *Resolver) Viewer() schema.ViewerResolver { return &viewerResolver{r} } +type controlResolver struct{ *Resolver } type evidenceResolver struct{ *Resolver } type frameworkResolver struct{ *Resolver } type mitigationResolver struct{ *Resolver } @@ -1087,5 +1182,6 @@ type mutationResolver struct{ *Resolver } type organizationResolver struct{ *Resolver } type policyResolver struct{ *Resolver } type queryResolver struct{ *Resolver } +type riskResolver struct{ *Resolver } type taskResolver struct{ *Resolver } type viewerResolver struct{ *Resolver }