@@ -97,6 +97,8 @@ export const frameworkControlNodeQuery = graphql`
|
|||||||
name
|
name
|
||||||
sectionTitle
|
sectionTitle
|
||||||
description
|
description
|
||||||
|
status
|
||||||
|
exclusionJustification
|
||||||
...FrameworkControlDialogFragment
|
...FrameworkControlDialogFragment
|
||||||
measures(first: 100)
|
measures(first: 100)
|
||||||
@connection(key: "FrameworkGraphControl_measures") {
|
@connection(key: "FrameworkGraphControl_measures") {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<9972eafa19f49d7beea0458c5b9c06d9>>
|
* @generated SignedSource<<95d2adf687d1cae6938863ba004db9a8>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
import { FragmentRefs } from "relay-runtime";
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type ControlStatus = "EXCLUDED" | "INCLUDED";
|
||||||
export type FrameworkGraphControlNodeQuery$variables = {
|
export type FrameworkGraphControlNodeQuery$variables = {
|
||||||
controlId: string;
|
controlId: string;
|
||||||
};
|
};
|
||||||
@@ -25,6 +26,7 @@ export type FrameworkGraphControlNodeQuery$data = {
|
|||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
readonly exclusionJustification?: string | null | undefined;
|
||||||
readonly id?: string;
|
readonly id?: string;
|
||||||
readonly measures?: {
|
readonly measures?: {
|
||||||
readonly __id: string;
|
readonly __id: string;
|
||||||
@@ -37,6 +39,7 @@ export type FrameworkGraphControlNodeQuery$data = {
|
|||||||
};
|
};
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
readonly sectionTitle?: string;
|
readonly sectionTitle?: string;
|
||||||
|
readonly status?: ControlStatus;
|
||||||
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
|
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -92,17 +95,31 @@ v6 = {
|
|||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "__typename",
|
"name": "status",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v7 = {
|
v7 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "cursor",
|
"name": "exclusionJustification",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v8 = {
|
v8 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "__typename",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v9 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "cursor",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v10 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "PageInfo",
|
"concreteType": "PageInfo",
|
||||||
@@ -127,7 +144,7 @@ v8 = {
|
|||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v9 = {
|
v11 = {
|
||||||
"kind": "ClientExtension",
|
"kind": "ClientExtension",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
@@ -139,7 +156,7 @@ v9 = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
v10 = [
|
v12 = [
|
||||||
{
|
{
|
||||||
"kind": "Literal",
|
"kind": "Literal",
|
||||||
"name": "first",
|
"name": "first",
|
||||||
@@ -168,6 +185,8 @@ return {
|
|||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
|
(v6/*: any*/),
|
||||||
|
(v7/*: any*/),
|
||||||
{
|
{
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "FragmentSpread",
|
"kind": "FragmentSpread",
|
||||||
@@ -203,16 +222,16 @@ return {
|
|||||||
"kind": "FragmentSpread",
|
"kind": "FragmentSpread",
|
||||||
"name": "LinkedMeasuresCardFragment"
|
"name": "LinkedMeasuresCardFragment"
|
||||||
},
|
},
|
||||||
(v6/*: any*/)
|
(v8/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v7/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v8/*: any*/),
|
(v10/*: any*/),
|
||||||
(v9/*: any*/)
|
(v11/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
@@ -246,16 +265,16 @@ return {
|
|||||||
"kind": "FragmentSpread",
|
"kind": "FragmentSpread",
|
||||||
"name": "LinkedDocumentsCardFragment"
|
"name": "LinkedDocumentsCardFragment"
|
||||||
},
|
},
|
||||||
(v6/*: any*/)
|
(v8/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v7/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v8/*: any*/),
|
(v10/*: any*/),
|
||||||
(v9/*: any*/)
|
(v11/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -284,7 +303,7 @@ return {
|
|||||||
"name": "node",
|
"name": "node",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v6/*: any*/),
|
(v8/*: any*/),
|
||||||
(v2/*: any*/),
|
(v2/*: any*/),
|
||||||
{
|
{
|
||||||
"kind": "InlineFragment",
|
"kind": "InlineFragment",
|
||||||
@@ -292,9 +311,11 @@ return {
|
|||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
|
(v6/*: any*/),
|
||||||
|
(v7/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v10/*: any*/),
|
"args": (v12/*: any*/),
|
||||||
"concreteType": "MeasureConnection",
|
"concreteType": "MeasureConnection",
|
||||||
"kind": "LinkedField",
|
"kind": "LinkedField",
|
||||||
"name": "measures",
|
"name": "measures",
|
||||||
@@ -325,22 +346,22 @@ return {
|
|||||||
"name": "state",
|
"name": "state",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v6/*: any*/)
|
(v8/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v7/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v8/*: any*/),
|
(v10/*: any*/),
|
||||||
(v9/*: any*/)
|
(v11/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": "measures(first:100)"
|
"storageKey": "measures(first:100)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v10/*: any*/),
|
"args": (v12/*: any*/),
|
||||||
"filters": null,
|
"filters": null,
|
||||||
"handle": "connection",
|
"handle": "connection",
|
||||||
"key": "FrameworkGraphControl_measures",
|
"key": "FrameworkGraphControl_measures",
|
||||||
@@ -349,7 +370,7 @@ return {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v10/*: any*/),
|
"args": (v12/*: any*/),
|
||||||
"concreteType": "DocumentConnection",
|
"concreteType": "DocumentConnection",
|
||||||
"kind": "LinkedField",
|
"kind": "LinkedField",
|
||||||
"name": "documents",
|
"name": "documents",
|
||||||
@@ -424,13 +445,7 @@ return {
|
|||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v2/*: any*/),
|
(v2/*: any*/),
|
||||||
{
|
(v6/*: any*/)
|
||||||
"alias": null,
|
|
||||||
"args": null,
|
|
||||||
"kind": "ScalarField",
|
|
||||||
"name": "status",
|
|
||||||
"storageKey": null
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -440,22 +455,22 @@ return {
|
|||||||
],
|
],
|
||||||
"storageKey": "versions(first:1)"
|
"storageKey": "versions(first:1)"
|
||||||
},
|
},
|
||||||
(v6/*: any*/)
|
(v8/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v7/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v8/*: any*/),
|
(v10/*: any*/),
|
||||||
(v9/*: any*/)
|
(v11/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": "documents(first:100)"
|
"storageKey": "documents(first:100)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v10/*: any*/),
|
"args": (v12/*: any*/),
|
||||||
"filters": null,
|
"filters": null,
|
||||||
"handle": "connection",
|
"handle": "connection",
|
||||||
"key": "FrameworkGraphControl_documents",
|
"key": "FrameworkGraphControl_documents",
|
||||||
@@ -472,7 +487,7 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "165ee5f4a3579e54fb7acafd6fd7ab12",
|
"cacheID": "ed223aa33ef15ba2e170e8aeae990f8e",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"connection": [
|
"connection": [
|
||||||
@@ -498,11 +513,11 @@ return {
|
|||||||
},
|
},
|
||||||
"name": "FrameworkGraphControlNodeQuery",
|
"name": "FrameworkGraphControlNodeQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query FrameworkGraphControlNodeQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n sectionTitle\n description\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n"
|
"text": "query FrameworkGraphControlNodeQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n sectionTitle\n description\n status\n exclusionJustification\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n status\n exclusionJustification\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "a2b0b5449af380ea2843aab6ce187b75";
|
(node as any).hash = "d5c8f5ce17bd227c83fec31d63a82262";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<f1a5a3800adf762d96c4e257075a0f48>>
|
* @generated SignedSource<<8901b5c750861080551542d26c2681d6>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -123,6 +123,19 @@ return {
|
|||||||
"name": "description",
|
"name": "description",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Organization",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "organization",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v3/*: any*/),
|
||||||
|
(v2/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": [
|
"args": [
|
||||||
@@ -169,7 +182,21 @@ return {
|
|||||||
"name": "sectionTitle",
|
"name": "sectionTitle",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v3/*: any*/)
|
(v3/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "exclusionJustification",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -201,12 +228,12 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "cf02a37946ff1ec0d7bb2b9a24f26c2a",
|
"cacheID": "29d6f638feb1bb633169abcfe083f5d2",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "FrameworkGraphNodeQuery",
|
"name": "FrameworkGraphNodeQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query FrameworkGraphNodeQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n ...FrameworkDetailPageFragment\n }\n id\n }\n}\n\nfragment FrameworkDetailPageFragment on Framework {\n id\n name\n description\n controls(first: 250, orderBy: {field: SECTION_TITLE, direction: ASC}) {\n edges {\n node {\n id\n sectionTitle\n name\n }\n }\n }\n}\n"
|
"text": "query FrameworkGraphNodeQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n ... on Framework {\n id\n name\n ...FrameworkDetailPageFragment\n }\n id\n }\n}\n\nfragment FrameworkDetailPageFragment on Framework {\n id\n name\n description\n organization {\n name\n id\n }\n controls(first: 250, orderBy: {field: SECTION_TITLE, direction: ASC}) {\n edges {\n node {\n id\n sectionTitle\n name\n status\n exclusionJustification\n }\n }\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -146,8 +146,10 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="flex justify-between">
|
<div className="flex justify-between">
|
||||||
<div className="text-xl font-medium px-[6px] py-[2px] border border-border-low rounded-lg w-max bg-active mb-3">
|
<div className="flex items-center gap-3">
|
||||||
{control.sectionTitle}
|
<div className="text-xl font-medium px-[6px] py-[2px] border border-border-low rounded-lg w-max bg-active mb-3">
|
||||||
|
{control.sectionTitle}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<FrameworkControlDialog
|
<FrameworkControlDialog
|
||||||
@@ -171,25 +173,39 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-base">{control.name}</div>
|
{control.status === "EXCLUDED" && (
|
||||||
<LinkedMeasuresCard
|
<div className="bg-danger border border-border-danger rounded-lg p-4">
|
||||||
variant="card"
|
<div className="flex items-center gap-2 mb-2">
|
||||||
measures={control.measures?.edges.map((edge) => edge.node) ?? []}
|
<div className="font-medium text-txt-danger">
|
||||||
params={{ controlId: control.id }}
|
{__("This control is excluded")}
|
||||||
connectionId={control.measures?.__id!}
|
</div>
|
||||||
onAttach={attachMeasure}
|
</div>
|
||||||
onDetach={detachMeasure}
|
<div className="text-sm">
|
||||||
disabled={isAttachingMeasure || isDetachingMeasure}
|
<strong>{__("Justification:")}</strong> {control.exclusionJustification || __("No justification provided")}
|
||||||
/>
|
</div>
|
||||||
<LinkedDocumentsCard
|
</div>
|
||||||
variant="card"
|
)}
|
||||||
documents={control.documents?.edges.map((edge) => edge.node) ?? []}
|
<div className={control.status === "EXCLUDED" ? "opacity-60" : ""}>
|
||||||
params={{ controlId: control.id }}
|
<div className="text-base">{control.name}</div>
|
||||||
connectionId={control.documents?.__id!}
|
<LinkedMeasuresCard
|
||||||
onAttach={attachDocument}
|
variant="card"
|
||||||
onDetach={detachDocument}
|
measures={control.measures?.edges.map((edge) => edge.node) ?? []}
|
||||||
disabled={isAttachingDocument || isDetachingDocument}
|
params={{ controlId: control.id }}
|
||||||
|
connectionId={control.measures?.__id!}
|
||||||
|
onAttach={attachMeasure}
|
||||||
|
onDetach={detachMeasure}
|
||||||
|
disabled={isAttachingMeasure || isDetachingMeasure}
|
||||||
|
/>
|
||||||
|
<LinkedDocumentsCard
|
||||||
|
variant="card"
|
||||||
|
documents={control.documents?.edges.map((edge) => edge.node) ?? []}
|
||||||
|
params={{ controlId: control.id }}
|
||||||
|
connectionId={control.documents?.__id!}
|
||||||
|
onAttach={attachDocument}
|
||||||
|
onDetach={detachDocument}
|
||||||
|
disabled={isAttachingDocument || isDetachingDocument}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,9 @@ const frameworkDetailFragment = graphql`
|
|||||||
id
|
id
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
|
organization {
|
||||||
|
name
|
||||||
|
}
|
||||||
controls(first: 250, orderBy: { field: SECTION_TITLE, direction: ASC }) {
|
controls(first: 250, orderBy: { field: SECTION_TITLE, direction: ASC }) {
|
||||||
__id
|
__id
|
||||||
edges {
|
edges {
|
||||||
@@ -43,6 +46,8 @@ const frameworkDetailFragment = graphql`
|
|||||||
id
|
id
|
||||||
sectionTitle
|
sectionTitle
|
||||||
name
|
name
|
||||||
|
status
|
||||||
|
exclusionJustification
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +145,7 @@ export default function FrameworkDetailPage(props: Props) {
|
|||||||
if (data.generateFrameworkStateOfApplicability?.data) {
|
if (data.generateFrameworkStateOfApplicability?.data) {
|
||||||
const link = window.document.createElement("a");
|
const link = window.document.createElement("a");
|
||||||
link.href = data.generateFrameworkStateOfApplicability.data;
|
link.href = data.generateFrameworkStateOfApplicability.data;
|
||||||
link.download = `${framework.name}-SOA.xlsx`;
|
link.download = `${framework.organization.name}-${framework.name}-SOA.xlsx`;
|
||||||
window.document.body.appendChild(link);
|
window.document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
window.document.body.removeChild(link);
|
window.document.body.removeChild(link);
|
||||||
@@ -169,6 +174,7 @@ export default function FrameworkDetailPage(props: Props) {
|
|||||||
key={control.id}
|
key={control.id}
|
||||||
id={control.sectionTitle}
|
id={control.sectionTitle}
|
||||||
description={control.name}
|
description={control.name}
|
||||||
|
excluded={control.status === "EXCLUDED"}
|
||||||
to={`/organizations/${organizationId}/frameworks/${framework.id}/controls/${control.id}`}
|
to={`/organizations/${organizationId}/frameworks/${framework.id}/controls/${control.id}`}
|
||||||
active={selectedControl?.id === control.id}
|
active={selectedControl?.id === control.id}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<d3c93020e1d7d125fc2055a650dd62e3>>
|
* @generated SignedSource<<c6e55821a322dceae5408ac3898cfa48>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -9,21 +9,27 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ReaderFragment } from 'relay-runtime';
|
import { ReaderFragment } from 'relay-runtime';
|
||||||
|
export type ControlStatus = "EXCLUDED" | "INCLUDED";
|
||||||
import { FragmentRefs } from "relay-runtime";
|
import { FragmentRefs } from "relay-runtime";
|
||||||
export type FrameworkDetailPageFragment$data = {
|
export type FrameworkDetailPageFragment$data = {
|
||||||
readonly controls: {
|
readonly controls: {
|
||||||
readonly __id: string;
|
readonly __id: string;
|
||||||
readonly edges: ReadonlyArray<{
|
readonly edges: ReadonlyArray<{
|
||||||
readonly node: {
|
readonly node: {
|
||||||
|
readonly exclusionJustification: string | null | undefined;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly sectionTitle: string;
|
readonly sectionTitle: string;
|
||||||
|
readonly status: ControlStatus;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
readonly description: string;
|
readonly description: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
|
readonly organization: {
|
||||||
|
readonly name: string;
|
||||||
|
};
|
||||||
readonly " $fragmentType": "FrameworkDetailPageFragment";
|
readonly " $fragmentType": "FrameworkDetailPageFragment";
|
||||||
};
|
};
|
||||||
export type FrameworkDetailPageFragment$key = {
|
export type FrameworkDetailPageFragment$key = {
|
||||||
@@ -61,6 +67,18 @@ return {
|
|||||||
"name": "description",
|
"name": "description",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"concreteType": "Organization",
|
||||||
|
"kind": "LinkedField",
|
||||||
|
"name": "organization",
|
||||||
|
"plural": false,
|
||||||
|
"selections": [
|
||||||
|
(v1/*: any*/)
|
||||||
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": [
|
"args": [
|
||||||
@@ -107,7 +125,21 @@ return {
|
|||||||
"name": "sectionTitle",
|
"name": "sectionTitle",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v1/*: any*/)
|
(v1/*: any*/),
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "exclusionJustification",
|
||||||
|
"storageKey": null
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -135,6 +167,6 @@ return {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "ad59b1c61a01fdcb5afbf0ee920a46bb";
|
(node as any).hash = "c31fd5c0f675d2524f736d69c4ebebe3";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
DialogFooter,
|
DialogFooter,
|
||||||
Input,
|
Input,
|
||||||
Textarea,
|
Textarea,
|
||||||
|
Option,
|
||||||
useDialogRef,
|
useDialogRef,
|
||||||
} from "@probo/ui";
|
} from "@probo/ui";
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
@@ -16,6 +17,7 @@ import type { FrameworkControlDialogFragment$key } from "./__generated__/Framewo
|
|||||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
|
||||||
|
import { ControlledSelect } from "/components/form/ControlledField";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -30,6 +32,8 @@ const controlFragment = graphql`
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
sectionTitle
|
sectionTitle
|
||||||
|
status
|
||||||
|
exclusionJustification
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -62,6 +66,16 @@ const schema = z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
description: z.string(),
|
description: z.string(),
|
||||||
sectionTitle: z.string(),
|
sectionTitle: z.string(),
|
||||||
|
status: z.enum(["INCLUDED", "EXCLUDED"]),
|
||||||
|
exclusionJustification: z.string().optional(),
|
||||||
|
}).refine((data) => {
|
||||||
|
if (data.status === "EXCLUDED") {
|
||||||
|
return data.exclusionJustification && data.exclusionJustification.trim().length > 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}, {
|
||||||
|
message: "Exclusion justification is required when status is excluded",
|
||||||
|
path: ["exclusionJustification"],
|
||||||
});
|
});
|
||||||
|
|
||||||
export function FrameworkControlDialog(props: Props) {
|
export function FrameworkControlDialog(props: Props) {
|
||||||
@@ -77,14 +91,19 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
successMessage: __("Control created successfully."),
|
successMessage: __("Control created successfully."),
|
||||||
errorMessage: __("Failed to create control. Please try again."),
|
errorMessage: __("Failed to create control. Please try again."),
|
||||||
});
|
});
|
||||||
const { control, handleSubmit, register, reset } = useFormWithSchema(schema, {
|
const { control, handleSubmit, register, reset, watch } = useFormWithSchema(schema, {
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: frameworkControl?.name ?? "",
|
name: frameworkControl?.name ?? "",
|
||||||
description: frameworkControl?.description ?? "",
|
description: frameworkControl?.description ?? "",
|
||||||
sectionTitle: frameworkControl?.sectionTitle ?? "",
|
sectionTitle: frameworkControl?.sectionTitle ?? "",
|
||||||
|
status: frameworkControl?.status ?? "INCLUDED",
|
||||||
|
exclusionJustification: frameworkControl?.exclusionJustification ?? "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const statusValue = watch("status");
|
||||||
|
const showExclusionJustification = statusValue === "EXCLUDED";
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async (data) => {
|
const onSubmit = handleSubmit(async (data) => {
|
||||||
if (frameworkControl) {
|
if (frameworkControl) {
|
||||||
// Update the control
|
// Update the control
|
||||||
@@ -95,6 +114,8 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
description: data.description,
|
description: data.description,
|
||||||
sectionTitle: data.sectionTitle,
|
sectionTitle: data.sectionTitle,
|
||||||
|
status: data.status,
|
||||||
|
exclusionJustification: data.status === "EXCLUDED" ? data.exclusionJustification : null,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -107,6 +128,8 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
description: data.description,
|
description: data.description,
|
||||||
sectionTitle: data.sectionTitle,
|
sectionTitle: data.sectionTitle,
|
||||||
|
status: data.status,
|
||||||
|
exclusionJustification: data.status === "EXCLUDED" ? data.exclusionJustification : null,
|
||||||
},
|
},
|
||||||
connections: [props.connectionId!],
|
connections: [props.connectionId!],
|
||||||
},
|
},
|
||||||
@@ -148,11 +171,26 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
<Textarea
|
<Textarea
|
||||||
id="content"
|
id="content"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
required
|
|
||||||
autogrow
|
autogrow
|
||||||
placeholder={__("Add description")}
|
placeholder={__("Add description")}
|
||||||
{...register("description")}
|
{...register("description")}
|
||||||
/>
|
/>
|
||||||
|
<ControlledSelect
|
||||||
|
control={control}
|
||||||
|
name="status"
|
||||||
|
placeholder={__("Select status")}
|
||||||
|
>
|
||||||
|
<Option value="INCLUDED">{__("Included")}</Option>
|
||||||
|
<Option value="EXCLUDED">{__("Excluded")}</Option>
|
||||||
|
</ControlledSelect>
|
||||||
|
{showExclusionJustification && (
|
||||||
|
<Input
|
||||||
|
id="exclusionJustification"
|
||||||
|
variant="bordered"
|
||||||
|
placeholder={__("Reason for exclusion")}
|
||||||
|
{...register("exclusionJustification")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<Button type="submit" disabled={isMutating}>
|
<Button type="submit" disabled={isMutating}>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<3a14264d0b2b45f62b29aaefcf7e2c7f>>
|
* @generated SignedSource<<5c976aa26b87b4f77dfcfeba71d3bbe3>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -10,11 +10,14 @@
|
|||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
import { FragmentRefs } from "relay-runtime";
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type ControlStatus = "EXCLUDED" | "INCLUDED";
|
||||||
export type CreateControlInput = {
|
export type CreateControlInput = {
|
||||||
description: string;
|
description: string;
|
||||||
|
exclusionJustification?: string | null | undefined;
|
||||||
frameworkId: string;
|
frameworkId: string;
|
||||||
name: string;
|
name: string;
|
||||||
sectionTitle: string;
|
sectionTitle: string;
|
||||||
|
status?: ControlStatus | null | undefined;
|
||||||
};
|
};
|
||||||
export type FrameworkControlDialogCreateMutation$variables = {
|
export type FrameworkControlDialogCreateMutation$variables = {
|
||||||
connections: ReadonlyArray<string>;
|
connections: ReadonlyArray<string>;
|
||||||
@@ -164,6 +167,20 @@ return {
|
|||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "sectionTitle",
|
"name": "sectionTitle",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "exclusionJustification",
|
||||||
|
"storageKey": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -193,12 +210,12 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "87e5bd454a1adfe2f67ecb006e21e115",
|
"cacheID": "3df2d5a0a51c774ccf711423e947093d",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "FrameworkControlDialogCreateMutation",
|
"name": "FrameworkControlDialogCreateMutation",
|
||||||
"operationKind": "mutation",
|
"operationKind": "mutation",
|
||||||
"text": "mutation FrameworkControlDialogCreateMutation(\n $input: CreateControlInput!\n) {\n createControl(input: $input) {\n controlEdge {\n node {\n ...FrameworkControlDialogFragment\n id\n }\n }\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n"
|
"text": "mutation FrameworkControlDialogCreateMutation(\n $input: CreateControlInput!\n) {\n createControl(input: $input) {\n controlEdge {\n node {\n ...FrameworkControlDialogFragment\n id\n }\n }\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n status\n exclusionJustification\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<6052cc5cfc8407e61eaa1a842e976a90>>
|
* @generated SignedSource<<8b52b1e3fd91d74821361d9ae652d9bd>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -9,12 +9,15 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ReaderFragment } from 'relay-runtime';
|
import { ReaderFragment } from 'relay-runtime';
|
||||||
|
export type ControlStatus = "EXCLUDED" | "INCLUDED";
|
||||||
import { FragmentRefs } from "relay-runtime";
|
import { FragmentRefs } from "relay-runtime";
|
||||||
export type FrameworkControlDialogFragment$data = {
|
export type FrameworkControlDialogFragment$data = {
|
||||||
readonly description: string;
|
readonly description: string;
|
||||||
|
readonly exclusionJustification: string | null | undefined;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly sectionTitle: string;
|
readonly sectionTitle: string;
|
||||||
|
readonly status: ControlStatus;
|
||||||
readonly " $fragmentType": "FrameworkControlDialogFragment";
|
readonly " $fragmentType": "FrameworkControlDialogFragment";
|
||||||
};
|
};
|
||||||
export type FrameworkControlDialogFragment$key = {
|
export type FrameworkControlDialogFragment$key = {
|
||||||
@@ -55,12 +58,26 @@ const node: ReaderFragment = {
|
|||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "sectionTitle",
|
"name": "sectionTitle",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "exclusionJustification",
|
||||||
|
"storageKey": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"type": "Control",
|
"type": "Control",
|
||||||
"abstractKey": null
|
"abstractKey": null
|
||||||
};
|
};
|
||||||
|
|
||||||
(node as any).hash = "ff1a543d6a2225d9bacfe7db3b142f1a";
|
(node as any).hash = "856a8302abf15737b6a4d4316b77f8cc";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<250267db99194edcb6fa20053d1d82df>>
|
* @generated SignedSource<<7d04994d5cabbbfc12f82754e8da0d83>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -10,11 +10,14 @@
|
|||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
import { FragmentRefs } from "relay-runtime";
|
import { FragmentRefs } from "relay-runtime";
|
||||||
|
export type ControlStatus = "EXCLUDED" | "INCLUDED";
|
||||||
export type UpdateControlInput = {
|
export type UpdateControlInput = {
|
||||||
description?: string | null | undefined;
|
description?: string | null | undefined;
|
||||||
|
exclusionJustification?: string | null | undefined;
|
||||||
id: string;
|
id: string;
|
||||||
name?: string | null | undefined;
|
name?: string | null | undefined;
|
||||||
sectionTitle?: string | null | undefined;
|
sectionTitle?: string | null | undefined;
|
||||||
|
status?: ControlStatus | null | undefined;
|
||||||
};
|
};
|
||||||
export type FrameworkControlDialogUpdateMutation$variables = {
|
export type FrameworkControlDialogUpdateMutation$variables = {
|
||||||
input: UpdateControlInput;
|
input: UpdateControlInput;
|
||||||
@@ -133,6 +136,20 @@ return {
|
|||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "sectionTitle",
|
"name": "sectionTitle",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "status",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "exclusionJustification",
|
||||||
|
"storageKey": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -143,12 +160,12 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "442b248ff35d4448c66052906ea5fff1",
|
"cacheID": "cee79ce9185400bad478453c44930dd6",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "FrameworkControlDialogUpdateMutation",
|
"name": "FrameworkControlDialogUpdateMutation",
|
||||||
"operationKind": "mutation",
|
"operationKind": "mutation",
|
||||||
"text": "mutation FrameworkControlDialogUpdateMutation(\n $input: UpdateControlInput!\n) {\n updateControl(input: $input) {\n control {\n ...FrameworkControlDialogFragment\n id\n }\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n}\n"
|
"text": "mutation FrameworkControlDialogUpdateMutation(\n $input: UpdateControlInput!\n) {\n updateControl(input: $input) {\n control {\n ...FrameworkControlDialogFragment\n id\n }\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n status\n exclusionJustification\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ type Props = {
|
|||||||
active?: boolean;
|
active?: boolean;
|
||||||
id: string;
|
id: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
excluded?: boolean;
|
||||||
to: string;
|
to: string;
|
||||||
} & HTMLAttributes<HTMLAnchorElement>;
|
} & HTMLAttributes<HTMLAnchorElement>;
|
||||||
|
|
||||||
@@ -26,19 +27,39 @@ const classNames = tv({
|
|||||||
id: "bg-highlight",
|
id: "bg-highlight",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
excluded: {
|
||||||
|
true: {
|
||||||
|
wrapper: "opacity-60",
|
||||||
|
},
|
||||||
|
false: {
|
||||||
|
wrapper: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
compoundVariants: [
|
||||||
|
{
|
||||||
|
active: true,
|
||||||
|
excluded: true,
|
||||||
|
class: {
|
||||||
|
wrapper: "bg-tertiary-pressed opacity-60",
|
||||||
|
id: "bg-active",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
active: false,
|
active: false,
|
||||||
|
excluded: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export function ControlItem({ active, id, description, to, ...props }: Props) {
|
export function ControlItem({ active, id, description, excluded, to, ...props }: Props) {
|
||||||
const {
|
const {
|
||||||
wrapper,
|
wrapper,
|
||||||
id: idCls,
|
id: idCls,
|
||||||
description: descriptionCls,
|
description: descriptionCls,
|
||||||
} = classNames({
|
} = classNames({
|
||||||
active,
|
active,
|
||||||
|
excluded,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ref = useRef<HTMLAnchorElement>(null);
|
const ref = useRef<HTMLAnchorElement>(null);
|
||||||
|
|||||||
@@ -28,22 +28,26 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Control struct {
|
Control struct {
|
||||||
ID gid.GID `db:"id"`
|
ID gid.GID `db:"id"`
|
||||||
SectionTitle string `db:"section_title"`
|
SectionTitle string `db:"section_title"`
|
||||||
TenantID gid.TenantID `db:"tenant_id"`
|
TenantID gid.TenantID `db:"tenant_id"`
|
||||||
FrameworkID gid.GID `db:"framework_id"`
|
FrameworkID gid.GID `db:"framework_id"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
Description string `db:"description"`
|
Description string `db:"description"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
Status ControlStatus `db:"status"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
ExclusionJustification *string `db:"exclusion_justification"`
|
||||||
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
Controls []*Control
|
Controls []*Control
|
||||||
|
|
||||||
UpdateControlParams struct {
|
UpdateControlParams struct {
|
||||||
Name *string
|
Name *string
|
||||||
Description *string
|
Description *string
|
||||||
SectionTitle *string
|
SectionTitle *string
|
||||||
|
Status *ControlStatus
|
||||||
|
ExclusionJustification *string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -117,6 +121,8 @@ WITH ctrl AS (
|
|||||||
c.tenant_id,
|
c.tenant_id,
|
||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
|
c.status,
|
||||||
|
c.exclusion_justification,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
c.updated_at,
|
c.updated_at,
|
||||||
c.search_vector
|
c.search_vector
|
||||||
@@ -134,6 +140,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -223,6 +231,8 @@ WITH ctrl AS (
|
|||||||
c.tenant_id,
|
c.tenant_id,
|
||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
|
c.status,
|
||||||
|
c.exclusion_justification,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
c.updated_at,
|
c.updated_at,
|
||||||
c.search_vector
|
c.search_vector
|
||||||
@@ -240,6 +250,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -335,6 +347,8 @@ WITH ctrl AS (
|
|||||||
c.tenant_id,
|
c.tenant_id,
|
||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
|
c.status,
|
||||||
|
c.exclusion_justification,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
c.updated_at,
|
c.updated_at,
|
||||||
c.search_vector
|
c.search_vector
|
||||||
@@ -358,6 +372,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -400,8 +416,8 @@ SELECT
|
|||||||
COUNT(id)
|
COUNT(id)
|
||||||
FROM
|
FROM
|
||||||
controls
|
controls
|
||||||
WHERE %s
|
WHERE %s
|
||||||
AND framework_id = @framework_id
|
AND framework_id = @framework_id
|
||||||
AND %s
|
AND %s
|
||||||
`
|
`
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||||
@@ -436,6 +452,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -528,6 +546,8 @@ WITH ctrl AS (
|
|||||||
c.tenant_id,
|
c.tenant_id,
|
||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
|
c.status,
|
||||||
|
c.exclusion_justification,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
c.updated_at,
|
c.updated_at,
|
||||||
c.search_vector
|
c.search_vector
|
||||||
@@ -545,6 +565,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -590,6 +612,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -633,6 +657,8 @@ SELECT
|
|||||||
tenant_id,
|
tenant_id,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
FROM
|
FROM
|
||||||
@@ -675,6 +701,8 @@ INSERT INTO
|
|||||||
section_title,
|
section_title,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
)
|
)
|
||||||
@@ -685,20 +713,24 @@ VALUES (
|
|||||||
@section_title,
|
@section_title,
|
||||||
@name,
|
@name,
|
||||||
@description,
|
@description,
|
||||||
|
@status,
|
||||||
|
@exclusion_justification,
|
||||||
@created_at,
|
@created_at,
|
||||||
@updated_at
|
@updated_at
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"tenant_id": scope.GetTenantID(),
|
"tenant_id": scope.GetTenantID(),
|
||||||
"control_id": c.ID,
|
"control_id": c.ID,
|
||||||
"framework_id": c.FrameworkID,
|
"framework_id": c.FrameworkID,
|
||||||
"section_title": c.SectionTitle,
|
"section_title": c.SectionTitle,
|
||||||
"name": c.Name,
|
"name": c.Name,
|
||||||
"description": c.Description,
|
"description": c.Description,
|
||||||
"created_at": c.CreatedAt,
|
"status": c.Status,
|
||||||
"updated_at": c.UpdatedAt,
|
"exclusion_justification": c.ExclusionJustification,
|
||||||
|
"created_at": c.CreatedAt,
|
||||||
|
"updated_at": c.UpdatedAt,
|
||||||
}
|
}
|
||||||
_, err := conn.Exec(ctx, q, args)
|
_, err := conn.Exec(ctx, q, args)
|
||||||
return err
|
return err
|
||||||
@@ -737,6 +769,8 @@ UPDATE controls SET
|
|||||||
name = COALESCE(@name, name),
|
name = COALESCE(@name, name),
|
||||||
description = COALESCE(@description, description),
|
description = COALESCE(@description, description),
|
||||||
section_title = COALESCE(@section_title, section_title),
|
section_title = COALESCE(@section_title, section_title),
|
||||||
|
status = COALESCE(@status, status),
|
||||||
|
exclusion_justification = COALESCE(@exclusion_justification, exclusion_justification),
|
||||||
updated_at = @updated_at
|
updated_at = @updated_at
|
||||||
WHERE %s
|
WHERE %s
|
||||||
AND id = @control_id
|
AND id = @control_id
|
||||||
@@ -747,15 +781,19 @@ RETURNING
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
section_title,
|
section_title,
|
||||||
|
status,
|
||||||
|
exclusion_justification,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at
|
updated_at
|
||||||
`
|
`
|
||||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||||
|
|
||||||
args := pgx.StrictNamedArgs{
|
args := pgx.StrictNamedArgs{
|
||||||
"control_id": c.ID,
|
"control_id": c.ID,
|
||||||
"section_title": params.SectionTitle,
|
"section_title": params.SectionTitle,
|
||||||
"updated_at": time.Now(),
|
"status": params.Status,
|
||||||
|
"exclusion_justification": params.ExclusionJustification,
|
||||||
|
"updated_at": time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.Name != nil {
|
if params.Name != nil {
|
||||||
@@ -765,6 +803,13 @@ RETURNING
|
|||||||
args["description"] = *params.Description
|
args["description"] = *params.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.Status != nil {
|
||||||
|
args["status"] = *params.Status
|
||||||
|
}
|
||||||
|
if params.ExclusionJustification != nil {
|
||||||
|
args["exclusion_justification"] = *params.ExclusionJustification
|
||||||
|
}
|
||||||
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
rows, err := conn.Query(ctx, q, args)
|
||||||
|
|||||||
52
pkg/coredata/control_status.go
Normal file
52
pkg/coredata/control_status.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package coredata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ControlStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControlStatusIncluded ControlStatus = "INCLUDED"
|
||||||
|
ControlStatusExcluded ControlStatus = "EXCLUDED"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cs ControlStatus) String() string {
|
||||||
|
return string(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ControlStatus) Scan(value interface{}) error {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case string:
|
||||||
|
switch v {
|
||||||
|
case "INCLUDED":
|
||||||
|
*cs = ControlStatusIncluded
|
||||||
|
case "EXCLUDED":
|
||||||
|
*cs = ControlStatusExcluded
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid ControlStatus value: %q", v)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type for ControlStatus: %T", value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs ControlStatus) Value() (driver.Value, error) {
|
||||||
|
return cs.String(), nil
|
||||||
|
}
|
||||||
5
pkg/coredata/migrations/20250717T122222Z.sql
Normal file
5
pkg/coredata/migrations/20250717T122222Z.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
CREATE TYPE control_status AS ENUM ('INCLUDED', 'EXCLUDED');
|
||||||
|
|
||||||
|
ALTER TABLE controls ADD COLUMN status control_status NOT NULL DEFAULT 'INCLUDED';
|
||||||
|
ALTER TABLE controls ALTER COLUMN status DROP DEFAULT;
|
||||||
|
ALTER TABLE controls ADD COLUMN exclusion_justification TEXT;
|
||||||
@@ -39,10 +39,12 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpdateControlRequest struct {
|
UpdateControlRequest struct {
|
||||||
ID gid.GID
|
ID gid.GID
|
||||||
Name *string
|
Name *string
|
||||||
Description *string
|
Description *string
|
||||||
SectionTitle *string
|
SectionTitle *string
|
||||||
|
Status *coredata.ControlStatus
|
||||||
|
ExclusionJustification *string
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectControlToMitigationRequest struct {
|
ConnectControlToMitigationRequest struct {
|
||||||
@@ -551,9 +553,11 @@ func (s ControlService) Update(
|
|||||||
req UpdateControlRequest,
|
req UpdateControlRequest,
|
||||||
) (*coredata.Control, error) {
|
) (*coredata.Control, error) {
|
||||||
params := coredata.UpdateControlParams{
|
params := coredata.UpdateControlParams{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
SectionTitle: req.SectionTitle,
|
SectionTitle: req.SectionTitle,
|
||||||
|
Status: req.Status,
|
||||||
|
ExclusionJustification: req.ExclusionJustification,
|
||||||
}
|
}
|
||||||
|
|
||||||
control := &coredata.Control{ID: req.ID}
|
control := &coredata.Control{ID: req.ID}
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ func (s FrameworkService) Import(
|
|||||||
SectionTitle: control.ID,
|
SectionTitle: control.ID,
|
||||||
Name: control.Name,
|
Name: control.Name,
|
||||||
Description: control.Description,
|
Description: control.Description,
|
||||||
|
Status: coredata.ControlStatusIncluded,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
@@ -306,18 +307,41 @@ func (s FrameworkService) StateOfApplicability(ctx context.Context, frameworkID
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, control := range controls {
|
for _, control := range controls {
|
||||||
|
exclusionJustification := ""
|
||||||
|
if control.Status == coredata.ControlStatusExcluded {
|
||||||
|
if control.ExclusionJustification == nil {
|
||||||
|
return fmt.Errorf("exclusion justification is required for excluded controls")
|
||||||
|
}
|
||||||
|
exclusionJustification = *control.ExclusionJustification
|
||||||
|
}
|
||||||
|
|
||||||
|
applicability := soagen.NewApplicability("Yes", true)
|
||||||
|
if control.Status == coredata.ControlStatusExcluded {
|
||||||
|
applicability = soagen.NewApplicability("No", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
bestPractice := ref.Ref(true)
|
||||||
|
if control.Status == coredata.ControlStatusExcluded {
|
||||||
|
bestPractice = ref.Ref(false)
|
||||||
|
}
|
||||||
|
|
||||||
row := soagen.SOARowData{
|
row := soagen.SOARowData{
|
||||||
SectionTitle: control.SectionTitle,
|
SectionTitle: control.SectionTitle,
|
||||||
ControlName: control.Name,
|
ControlName: control.Name,
|
||||||
Applicability: soagen.NewApplicability("Yes", true),
|
Applicability: applicability,
|
||||||
JustificationExclusion: "",
|
ExclusionJustification: exclusionJustification,
|
||||||
Regulatory: ref.Ref(false),
|
Regulatory: ref.Ref(false),
|
||||||
Contractual: ref.Ref(false),
|
Contractual: ref.Ref(false),
|
||||||
BestPractice: ref.Ref(true),
|
BestPractice: bestPractice,
|
||||||
RiskAssessment: ref.Ref(false),
|
RiskAssessment: ref.Ref(false),
|
||||||
SecurityMeasures: []string{},
|
SecurityMeasures: []string{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if control.Status == coredata.ControlStatusExcluded {
|
||||||
|
rows = append(rows, row)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
measures := coredata.Measures{}
|
measures := coredata.Measures{}
|
||||||
err = measures.LoadByControlID(
|
err = measures.LoadByControlID(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@@ -509,6 +509,18 @@ enum DataClassification
|
|||||||
value: "github.com/getprobo/probo/pkg/coredata.DataClassificationSecret"
|
value: "github.com/getprobo/probo/pkg/coredata.DataClassificationSecret"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ControlStatus
|
||||||
|
@goModel(model: "github.com/getprobo/probo/pkg/coredata.ControlStatus") {
|
||||||
|
INCLUDED
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlStatusIncluded"
|
||||||
|
)
|
||||||
|
EXCLUDED
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlStatusExcluded"
|
||||||
|
)
|
||||||
|
}
|
||||||
# Input Types
|
# Input Types
|
||||||
input UserOrder
|
input UserOrder
|
||||||
@goModel(
|
@goModel(
|
||||||
@@ -859,6 +871,8 @@ type Control implements Node {
|
|||||||
sectionTitle: String!
|
sectionTitle: String!
|
||||||
name: String!
|
name: String!
|
||||||
description: String!
|
description: String!
|
||||||
|
status: ControlStatus!
|
||||||
|
exclusionJustification: String
|
||||||
|
|
||||||
framework: Framework! @goField(forceResolver: true)
|
framework: Framework! @goField(forceResolver: true)
|
||||||
|
|
||||||
@@ -1747,6 +1761,8 @@ input CreateControlInput {
|
|||||||
sectionTitle: String!
|
sectionTitle: String!
|
||||||
name: String!
|
name: String!
|
||||||
description: String!
|
description: String!
|
||||||
|
status: ControlStatus
|
||||||
|
exclusionJustification: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateControlInput {
|
input UpdateControlInput {
|
||||||
@@ -1754,6 +1770,8 @@ input UpdateControlInput {
|
|||||||
sectionTitle: String
|
sectionTitle: String
|
||||||
name: String
|
name: String
|
||||||
description: String
|
description: String
|
||||||
|
status: ControlStatus
|
||||||
|
exclusionJustification: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteControlInput {
|
input DeleteControlInput {
|
||||||
|
|||||||
@@ -147,15 +147,17 @@ type ComplexityRoot struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Control struct {
|
Control struct {
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
Description func(childComplexity int) int
|
Description func(childComplexity int) int
|
||||||
Documents func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) int
|
Documents func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy, filter *types.DocumentFilter) int
|
||||||
Framework func(childComplexity int) int
|
ExclusionJustification func(childComplexity int) int
|
||||||
ID func(childComplexity int) int
|
Framework func(childComplexity int) int
|
||||||
Measures func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) int
|
ID func(childComplexity int) int
|
||||||
Name func(childComplexity int) int
|
Measures func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy, filter *types.MeasureFilter) int
|
||||||
SectionTitle func(childComplexity int) int
|
Name func(childComplexity int) int
|
||||||
UpdatedAt func(childComplexity int) int
|
SectionTitle func(childComplexity int) int
|
||||||
|
Status func(childComplexity int) int
|
||||||
|
UpdatedAt func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
ControlConnection struct {
|
ControlConnection struct {
|
||||||
@@ -1390,6 +1392,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
|
|
||||||
return e.complexity.Control.Documents(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.DocumentOrderBy), args["filter"].(*types.DocumentFilter)), true
|
return e.complexity.Control.Documents(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.DocumentOrderBy), args["filter"].(*types.DocumentFilter)), true
|
||||||
|
|
||||||
|
case "Control.exclusionJustification":
|
||||||
|
if e.complexity.Control.ExclusionJustification == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Control.ExclusionJustification(childComplexity), true
|
||||||
|
|
||||||
case "Control.framework":
|
case "Control.framework":
|
||||||
if e.complexity.Control.Framework == nil {
|
if e.complexity.Control.Framework == nil {
|
||||||
break
|
break
|
||||||
@@ -1430,6 +1439,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
|
|||||||
|
|
||||||
return e.complexity.Control.SectionTitle(childComplexity), true
|
return e.complexity.Control.SectionTitle(childComplexity), true
|
||||||
|
|
||||||
|
case "Control.status":
|
||||||
|
if e.complexity.Control.Status == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Control.Status(childComplexity), true
|
||||||
|
|
||||||
case "Control.updatedAt":
|
case "Control.updatedAt":
|
||||||
if e.complexity.Control.UpdatedAt == nil {
|
if e.complexity.Control.UpdatedAt == nil {
|
||||||
break
|
break
|
||||||
@@ -5450,6 +5466,18 @@ enum DataClassification
|
|||||||
value: "github.com/getprobo/probo/pkg/coredata.DataClassificationSecret"
|
value: "github.com/getprobo/probo/pkg/coredata.DataClassificationSecret"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ControlStatus
|
||||||
|
@goModel(model: "github.com/getprobo/probo/pkg/coredata.ControlStatus") {
|
||||||
|
INCLUDED
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlStatusIncluded"
|
||||||
|
)
|
||||||
|
EXCLUDED
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlStatusExcluded"
|
||||||
|
)
|
||||||
|
}
|
||||||
# Input Types
|
# Input Types
|
||||||
input UserOrder
|
input UserOrder
|
||||||
@goModel(
|
@goModel(
|
||||||
@@ -5800,6 +5828,8 @@ type Control implements Node {
|
|||||||
sectionTitle: String!
|
sectionTitle: String!
|
||||||
name: String!
|
name: String!
|
||||||
description: String!
|
description: String!
|
||||||
|
status: ControlStatus!
|
||||||
|
exclusionJustification: String
|
||||||
|
|
||||||
framework: Framework! @goField(forceResolver: true)
|
framework: Framework! @goField(forceResolver: true)
|
||||||
|
|
||||||
@@ -6688,6 +6718,8 @@ input CreateControlInput {
|
|||||||
sectionTitle: String!
|
sectionTitle: String!
|
||||||
name: String!
|
name: String!
|
||||||
description: String!
|
description: String!
|
||||||
|
status: ControlStatus
|
||||||
|
exclusionJustification: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateControlInput {
|
input UpdateControlInput {
|
||||||
@@ -6695,6 +6727,8 @@ input UpdateControlInput {
|
|||||||
sectionTitle: String
|
sectionTitle: String
|
||||||
name: String
|
name: String
|
||||||
description: String
|
description: String
|
||||||
|
status: ControlStatus
|
||||||
|
exclusionJustification: String
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteControlInput {
|
input DeleteControlInput {
|
||||||
@@ -14015,6 +14049,91 @@ func (ec *executionContext) fieldContext_Control_description(_ context.Context,
|
|||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Control_status(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||||
|
fc, err := ec.fieldContext_Control_status(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.Status, 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.(coredata.ControlStatus)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_Control_status(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "Control",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
return nil, errors.New("field of type ControlStatus does not have child fields")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Control_exclusionJustification(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||||
|
fc, err := ec.fieldContext_Control_exclusionJustification(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.ExclusionJustification, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
if resTmp == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
res := resTmp.(*string)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_Control_exclusionJustification(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "Control",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: false,
|
||||||
|
IsResolver: false,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
return nil, errors.New("field of type String does not have child fields")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Control_framework(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Control_framework(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Control_framework(ctx, field)
|
fc, err := ec.fieldContext_Control_framework(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -14528,6 +14647,10 @@ func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, fie
|
|||||||
return ec.fieldContext_Control_name(ctx, field)
|
return ec.fieldContext_Control_name(ctx, field)
|
||||||
case "description":
|
case "description":
|
||||||
return ec.fieldContext_Control_description(ctx, field)
|
return ec.fieldContext_Control_description(ctx, field)
|
||||||
|
case "status":
|
||||||
|
return ec.fieldContext_Control_status(ctx, field)
|
||||||
|
case "exclusionJustification":
|
||||||
|
return ec.fieldContext_Control_exclusionJustification(ctx, field)
|
||||||
case "framework":
|
case "framework":
|
||||||
return ec.fieldContext_Control_framework(ctx, field)
|
return ec.fieldContext_Control_framework(ctx, field)
|
||||||
case "measures":
|
case "measures":
|
||||||
@@ -31252,6 +31375,10 @@ func (ec *executionContext) fieldContext_UpdateControlPayload_control(_ context.
|
|||||||
return ec.fieldContext_Control_name(ctx, field)
|
return ec.fieldContext_Control_name(ctx, field)
|
||||||
case "description":
|
case "description":
|
||||||
return ec.fieldContext_Control_description(ctx, field)
|
return ec.fieldContext_Control_description(ctx, field)
|
||||||
|
case "status":
|
||||||
|
return ec.fieldContext_Control_status(ctx, field)
|
||||||
|
case "exclusionJustification":
|
||||||
|
return ec.fieldContext_Control_exclusionJustification(ctx, field)
|
||||||
case "framework":
|
case "framework":
|
||||||
return ec.fieldContext_Control_framework(ctx, field)
|
return ec.fieldContext_Control_framework(ctx, field)
|
||||||
case "measures":
|
case "measures":
|
||||||
@@ -37986,7 +38113,7 @@ func (ec *executionContext) unmarshalInputCreateControlInput(ctx context.Context
|
|||||||
asMap[k] = v
|
asMap[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldsInOrder := [...]string{"frameworkId", "sectionTitle", "name", "description"}
|
fieldsInOrder := [...]string{"frameworkId", "sectionTitle", "name", "description", "status", "exclusionJustification"}
|
||||||
for _, k := range fieldsInOrder {
|
for _, k := range fieldsInOrder {
|
||||||
v, ok := asMap[k]
|
v, ok := asMap[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -38021,6 +38148,20 @@ func (ec *executionContext) unmarshalInputCreateControlInput(ctx context.Context
|
|||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
it.Description = data
|
it.Description = data
|
||||||
|
case "status":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status"))
|
||||||
|
data, err := ec.unmarshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.Status = data
|
||||||
|
case "exclusionJustification":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("exclusionJustification"))
|
||||||
|
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.ExclusionJustification = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40368,7 +40509,7 @@ func (ec *executionContext) unmarshalInputUpdateControlInput(ctx context.Context
|
|||||||
asMap[k] = v
|
asMap[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldsInOrder := [...]string{"id", "sectionTitle", "name", "description"}
|
fieldsInOrder := [...]string{"id", "sectionTitle", "name", "description", "status", "exclusionJustification"}
|
||||||
for _, k := range fieldsInOrder {
|
for _, k := range fieldsInOrder {
|
||||||
v, ok := asMap[k]
|
v, ok := asMap[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -40403,6 +40544,20 @@ func (ec *executionContext) unmarshalInputUpdateControlInput(ctx context.Context
|
|||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
it.Description = data
|
it.Description = data
|
||||||
|
case "status":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status"))
|
||||||
|
data, err := ec.unmarshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.Status = data
|
||||||
|
case "exclusionJustification":
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("exclusionJustification"))
|
||||||
|
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
it.ExclusionJustification = data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42241,6 +42396,13 @@ func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet,
|
|||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
atomic.AddUint32(&out.Invalids, 1)
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
}
|
}
|
||||||
|
case "status":
|
||||||
|
out.Values[i] = ec._Control_status(ctx, field, obj)
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
atomic.AddUint32(&out.Invalids, 1)
|
||||||
|
}
|
||||||
|
case "exclusionJustification":
|
||||||
|
out.Values[i] = ec._Control_exclusionJustification(ctx, field, obj)
|
||||||
case "framework":
|
case "framework":
|
||||||
field := field
|
field := field
|
||||||
|
|
||||||
@@ -51248,6 +51410,34 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx context.Context, v any) (coredata.ControlStatus, error) {
|
||||||
|
tmp, err := graphql.UnmarshalString(v)
|
||||||
|
res := unmarshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus[tmp]
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx context.Context, sel ast.SelectionSet, v coredata.ControlStatus) graphql.Marshaler {
|
||||||
|
_ = sel
|
||||||
|
res := graphql.MarshalString(marshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus[v])
|
||||||
|
if res == graphql.Null {
|
||||||
|
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||||
|
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
unmarshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus = map[string]coredata.ControlStatus{
|
||||||
|
"INCLUDED": coredata.ControlStatusIncluded,
|
||||||
|
"EXCLUDED": coredata.ControlStatusExcluded,
|
||||||
|
}
|
||||||
|
marshalNControlStatus2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus = map[coredata.ControlStatus]string{
|
||||||
|
coredata.ControlStatusIncluded: "INCLUDED",
|
||||||
|
coredata.ControlStatusExcluded: "EXCLUDED",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNCreateAssetInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateAssetInput(ctx context.Context, v any) (types.CreateAssetInput, error) {
|
func (ec *executionContext) unmarshalNCreateAssetInput2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐCreateAssetInput(ctx context.Context, v any) (types.CreateAssetInput, error) {
|
||||||
res, err := ec.unmarshalInputCreateAssetInput(ctx, v)
|
res, err := ec.unmarshalInputCreateAssetInput(ctx, v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
@@ -55014,6 +55204,36 @@ func (ec *executionContext) unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋ
|
|||||||
return &res, graphql.ErrorOnPath(ctx, err)
|
return &res, graphql.ErrorOnPath(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx context.Context, v any) (*coredata.ControlStatus, error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
tmp, err := graphql.UnmarshalString(v)
|
||||||
|
res := unmarshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus[tmp]
|
||||||
|
return &res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx context.Context, sel ast.SelectionSet, v *coredata.ControlStatus) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
_ = sel
|
||||||
|
_ = ctx
|
||||||
|
res := graphql.MarshalString(marshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus[*v])
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
unmarshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus = map[string]coredata.ControlStatus{
|
||||||
|
"INCLUDED": coredata.ControlStatusIncluded,
|
||||||
|
"EXCLUDED": coredata.ControlStatusExcluded,
|
||||||
|
}
|
||||||
|
marshalOControlStatus2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐControlStatus = map[coredata.ControlStatus]string{
|
||||||
|
coredata.ControlStatusIncluded: "INCLUDED",
|
||||||
|
coredata.ControlStatusExcluded: "EXCLUDED",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOCriticityLevel2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐCriticityLevel(ctx context.Context, v any) (*coredata.CriticityLevel, error) {
|
func (ec *executionContext) unmarshalOCriticityLevel2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐCriticityLevel(ctx context.Context, v any) (*coredata.CriticityLevel, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|||||||
@@ -65,11 +65,13 @@ func NewControlEdge(c *coredata.Control, orderBy coredata.ControlOrderField) *Co
|
|||||||
|
|
||||||
func NewControl(c *coredata.Control) *Control {
|
func NewControl(c *coredata.Control) *Control {
|
||||||
return &Control{
|
return &Control{
|
||||||
ID: c.ID,
|
ID: c.ID,
|
||||||
SectionTitle: c.SectionTitle,
|
SectionTitle: c.SectionTitle,
|
||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
Description: c.Description,
|
Description: c.Description,
|
||||||
CreatedAt: c.CreatedAt,
|
Status: c.Status,
|
||||||
UpdatedAt: c.UpdatedAt,
|
ExclusionJustification: c.ExclusionJustification,
|
||||||
|
CreatedAt: c.CreatedAt,
|
||||||
|
UpdatedAt: c.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,15 +118,17 @@ type ConnectorOrder struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Control struct {
|
type Control struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
SectionTitle string `json:"sectionTitle"`
|
SectionTitle string `json:"sectionTitle"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Framework *Framework `json:"framework"`
|
Status coredata.ControlStatus `json:"status"`
|
||||||
Measures *MeasureConnection `json:"measures"`
|
ExclusionJustification *string `json:"exclusionJustification,omitempty"`
|
||||||
Documents *DocumentConnection `json:"documents"`
|
Framework *Framework `json:"framework"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
Measures *MeasureConnection `json:"measures"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
Documents *DocumentConnection `json:"documents"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Control) IsNode() {}
|
func (Control) IsNode() {}
|
||||||
@@ -167,10 +169,12 @@ type CreateControlDocumentMappingPayload struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreateControlInput struct {
|
type CreateControlInput struct {
|
||||||
FrameworkID gid.GID `json:"frameworkId"`
|
FrameworkID gid.GID `json:"frameworkId"`
|
||||||
SectionTitle string `json:"sectionTitle"`
|
SectionTitle string `json:"sectionTitle"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
Status *coredata.ControlStatus `json:"status,omitempty"`
|
||||||
|
ExclusionJustification *string `json:"exclusionJustification,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateControlMeasureMappingInput struct {
|
type CreateControlMeasureMappingInput struct {
|
||||||
@@ -948,10 +952,12 @@ type UpdateAssetPayload struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UpdateControlInput struct {
|
type UpdateControlInput struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
SectionTitle *string `json:"sectionTitle,omitempty"`
|
SectionTitle *string `json:"sectionTitle,omitempty"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
Status *coredata.ControlStatus `json:"status,omitempty"`
|
||||||
|
ExclusionJustification *string `json:"exclusionJustification,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateControlPayload struct {
|
type UpdateControlPayload struct {
|
||||||
|
|||||||
@@ -1220,10 +1220,12 @@ func (r *mutationResolver) UpdateControl(ctx context.Context, input types.Update
|
|||||||
prb := r.ProboService(ctx, input.ID.TenantID())
|
prb := r.ProboService(ctx, input.ID.TenantID())
|
||||||
|
|
||||||
control, err := prb.Controls.Update(ctx, probo.UpdateControlRequest{
|
control, err := prb.Controls.Update(ctx, probo.UpdateControlRequest{
|
||||||
ID: input.ID,
|
ID: input.ID,
|
||||||
Name: input.Name,
|
Name: input.Name,
|
||||||
Description: input.Description,
|
Description: input.Description,
|
||||||
SectionTitle: input.SectionTitle,
|
SectionTitle: input.SectionTitle,
|
||||||
|
Status: input.Status,
|
||||||
|
ExclusionJustification: input.ExclusionJustification,
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ var (
|
|||||||
HasFilter: true,
|
HasFilter: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Field: "JustificationExclusion",
|
Field: "ExclusionJustification",
|
||||||
Columns: []string{"D"},
|
Columns: []string{"D"},
|
||||||
FilterColumns: []string{},
|
FilterColumns: []string{},
|
||||||
Width: []float64{25},
|
Width: []float64{25},
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ func writeSOARow(f *excelize.File, sheetName string, row int, data SOARowData, i
|
|||||||
{"Contractual", data.Contractual},
|
{"Contractual", data.Contractual},
|
||||||
{"BestPractice", data.BestPractice},
|
{"BestPractice", data.BestPractice},
|
||||||
{"RiskAssessment", data.RiskAssessment},
|
{"RiskAssessment", data.RiskAssessment},
|
||||||
{"JustificationExclusion", data.JustificationExclusion},
|
{"ExclusionJustification", data.ExclusionJustification},
|
||||||
{"SecurityMeasures", data.SecurityMeasures},
|
{"SecurityMeasures", data.SecurityMeasures},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ type SOARowData struct {
|
|||||||
Contractual *bool
|
Contractual *bool
|
||||||
BestPractice *bool
|
BestPractice *bool
|
||||||
RiskAssessment *bool
|
RiskAssessment *bool
|
||||||
JustificationExclusion string
|
ExclusionJustification string
|
||||||
SecurityMeasures []string
|
SecurityMeasures []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user