Remove deprecated SOA code

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-02-17 13:55:56 +01:00
parent e9263fb42f
commit 40e4db9278
50 changed files with 285 additions and 2669 deletions

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<35c2c66dca0c895c7abca2b0f4917a68>>
* @generated SignedSource<<10bdb7f9b009009a66d93605d3d671c4>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,15 +10,12 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type ControlStatus = "EXCLUDED" | "INCLUDED";
export type CreateControlInput = {
bestPractice: boolean;
description?: string | null | undefined;
exclusionJustification?: string | null | undefined;
frameworkId: string;
name: string;
sectionTitle: string;
status: ControlStatus;
};
export type FrameworkControlDialogCreateMutation$variables = {
connections: ReadonlyArray<string>;
@@ -169,20 +166,6 @@ return {
"name": "sectionTitle",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "exclusionJustification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -218,12 +201,12 @@ return {
]
},
"params": {
"cacheID": "81df1f9c65937d3e387edffc6e9a28ac",
"cacheID": "51536092f1e2e37d1499182357306138",
"id": null,
"metadata": {},
"name": "FrameworkControlDialogCreateMutation",
"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 status\n exclusionJustification\n bestPractice\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 bestPractice\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<3a62e5f931a990d83caca02ab629b0c0>>
* @generated SignedSource<<43181b3f35ce7df5f54b19bf82eeaad0>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,16 +9,13 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type ControlStatus = "EXCLUDED" | "INCLUDED";
import { FragmentRefs } from "relay-runtime";
export type FrameworkControlDialogFragment$data = {
readonly bestPractice: boolean;
readonly description: string | null | undefined;
readonly exclusionJustification: string | null | undefined;
readonly id: string;
readonly name: string;
readonly sectionTitle: string;
readonly status: ControlStatus;
readonly " $fragmentType": "FrameworkControlDialogFragment";
};
export type FrameworkControlDialogFragment$key = {
@@ -60,20 +57,6 @@ const node: ReaderFragment = {
"name": "sectionTitle",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "exclusionJustification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -86,6 +69,6 @@ const node: ReaderFragment = {
"abstractKey": null
};
(node as any).hash = "481f75ce8c525c1fa12517ef173ad868";
(node as any).hash = "3ca9ebd60cc24f1080a594bf4ca0b928";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<7ee3c72e16fa86d6c006c004e4f93c95>>
* @generated SignedSource<<791e18651a3911865d78b730f268f489>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,15 +10,12 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type ControlStatus = "EXCLUDED" | "INCLUDED";
export type UpdateControlInput = {
bestPractice?: boolean | null | undefined;
description?: string | null | undefined;
exclusionJustification?: string | null | undefined;
id: string;
name?: string | null | undefined;
sectionTitle?: string | null | undefined;
status?: ControlStatus | null | undefined;
};
export type FrameworkControlDialogUpdateMutation$variables = {
input: UpdateControlInput;
@@ -138,20 +135,6 @@ return {
"name": "sectionTitle",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "exclusionJustification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -168,12 +151,12 @@ return {
]
},
"params": {
"cacheID": "374c69b33dd6e94dec75829fe0777f58",
"cacheID": "a44a5055f8167fa3754f4f389cee598f",
"id": null,
"metadata": {},
"name": "FrameworkControlDialogUpdateMutation",
"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 status\n exclusionJustification\n bestPractice\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 bestPractice\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<3db987ede3a3aa1dfa570e76a00ef224>>
* @generated SignedSource<<b78ad7244ad348d2622288743eff7a9b>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,24 +9,20 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type ControlStatus = "EXCLUDED" | "INCLUDED";
import { FragmentRefs } from "relay-runtime";
export type FrameworkDetailPageFragment$data = {
readonly canCreateControl: boolean;
readonly canDelete: boolean;
readonly canExport: boolean;
readonly canGenerateSOA: boolean;
readonly canUpdate: boolean;
readonly controls: {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly bestPractice: boolean;
readonly exclusionJustification: string | null | undefined;
readonly id: string;
readonly name: string;
readonly sectionTitle: string;
readonly status: ControlStatus;
};
}>;
};
@@ -141,19 +137,6 @@ return {
"name": "permission",
"storageKey": "permission(action:\"core:control:create\")"
},
{
"alias": "canGenerateSOA",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "core:framework:generate-state-of-applicability"
}
],
"kind": "ScalarField",
"name": "permission",
"storageKey": "permission(action:\"core:framework:generate-state-of-applicability\")"
},
{
"alias": null,
"args": null,
@@ -213,20 +196,6 @@ return {
"storageKey": null
},
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "exclusionJustification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -261,6 +230,6 @@ return {
};
})();
(node as any).hash = "a3eb8b9d80c69d7faa83eae88e3b986b";
(node as any).hash = "f6eba8f00d1e491f79bc201851b8c69d";
export default node;

View File

@@ -1,95 +0,0 @@
/**
* @generated SignedSource<<b7c54e98eb3ba12bf4619530b45fdbab>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation$variables = {
frameworkId: string;
};
export type FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation$data = {
readonly generateFrameworkStateOfApplicability: {
readonly data: string;
};
};
export type FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation = {
response: FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation$data;
variables: FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "frameworkId"
}
],
v1 = [
{
"alias": null,
"args": [
{
"fields": [
{
"kind": "Variable",
"name": "frameworkId",
"variableName": "frameworkId"
}
],
"kind": "ObjectValue",
"name": "input"
}
],
"concreteType": "GenerateFrameworkStateOfApplicabilityPayload",
"kind": "LinkedField",
"name": "generateFrameworkStateOfApplicability",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "data",
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "80185d474f3a9c5d20f97a387b219511",
"id": null,
"metadata": {},
"name": "FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation",
"operationKind": "mutation",
"text": "mutation FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation(\n $frameworkId: ID!\n) {\n generateFrameworkStateOfApplicability(input: {frameworkId: $frameworkId}) {\n data\n }\n}\n"
}
};
})();
(node as any).hash = "eac70164d676cb8e19d7fe7f06b2b817";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<0c1816374b8fe7e0c0c79e9013aeb2b4>>
* @generated SignedSource<<72da3c2c267ec4fd2da7aee87f38c51b>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,7 +10,6 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type ControlStatus = "EXCLUDED" | "INCLUDED";
export type FrameworkGraphControlNodeQuery$variables = {
controlId: string;
};
@@ -47,7 +46,6 @@ export type FrameworkGraphControlNodeQuery$data = {
};
}>;
};
readonly exclusionJustification?: string | null | undefined;
readonly id?: string;
readonly measures?: {
readonly __id: string;
@@ -78,7 +76,6 @@ export type FrameworkGraphControlNodeQuery$data = {
};
}>;
};
readonly status?: ControlStatus;
readonly " $fragmentSpreads": FragmentRefs<"FrameworkControlDialogFragment">;
};
};
@@ -131,20 +128,6 @@ v5 = {
"storageKey": null
},
v6 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
v7 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "exclusionJustification",
"storageKey": null
},
v8 = {
"alias": "canUpdate",
"args": [
{
@@ -157,7 +140,7 @@ v8 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:update\")"
},
v9 = {
v7 = {
"alias": "canDelete",
"args": [
{
@@ -170,7 +153,7 @@ v9 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:delete\")"
},
v10 = {
v8 = {
"alias": "canCreateMeasureMapping",
"args": [
{
@@ -183,7 +166,7 @@ v10 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:create-measure-mapping\")"
},
v11 = {
v9 = {
"alias": "canDeleteMeasureMapping",
"args": [
{
@@ -196,7 +179,7 @@ v11 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:delete-measure-mapping\")"
},
v12 = {
v10 = {
"alias": "canCreateDocumentMapping",
"args": [
{
@@ -209,7 +192,7 @@ v12 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:create-document-mapping\")"
},
v13 = {
v11 = {
"alias": "canDeleteDocumentMapping",
"args": [
{
@@ -222,7 +205,7 @@ v13 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:delete-document-mapping\")"
},
v14 = {
v12 = {
"alias": "canCreateAuditMapping",
"args": [
{
@@ -235,7 +218,7 @@ v14 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:create-audit-mapping\")"
},
v15 = {
v13 = {
"alias": "canDeleteAuditMapping",
"args": [
{
@@ -248,7 +231,7 @@ v15 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:delete-audit-mapping\")"
},
v16 = {
v14 = {
"alias": "canCreateSnapshotMapping",
"args": [
{
@@ -261,7 +244,7 @@ v16 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:create-snapshot-mapping\")"
},
v17 = {
v15 = {
"alias": "canDeleteSnapshotMapping",
"args": [
{
@@ -274,7 +257,7 @@ v17 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:delete-snapshot-mapping\")"
},
v18 = {
v16 = {
"alias": "canCreateObligationMapping",
"args": [
{
@@ -287,7 +270,7 @@ v18 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:create-obligation-mapping\")"
},
v19 = {
v17 = {
"alias": "canDeleteObligationMapping",
"args": [
{
@@ -300,21 +283,21 @@ v19 = {
"name": "permission",
"storageKey": "permission(action:\"core:control:delete-obligation-mapping\")"
},
v20 = {
v18 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v21 = {
v19 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v22 = {
v20 = {
"alias": null,
"args": null,
"concreteType": "PageInfo",
@@ -339,7 +322,7 @@ v22 = {
],
"storageKey": null
},
v23 = {
v21 = {
"kind": "ClientExtension",
"selections": [
{
@@ -351,26 +334,33 @@ v23 = {
}
]
},
v24 = [
v22 = [
{
"kind": "Literal",
"name": "first",
"value": 100
}
],
v25 = {
v23 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "state",
"storageKey": null
},
v26 = {
v24 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "createdAt",
"storageKey": null
},
v25 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
};
return {
"fragment": {
@@ -406,8 +396,6 @@ return {
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
(v19/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -443,16 +431,16 @@ return {
"kind": "FragmentSpread",
"name": "LinkedMeasuresCardFragment"
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": null
},
@@ -486,16 +474,16 @@ return {
"kind": "FragmentSpread",
"name": "LinkedDocumentsCardFragment"
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": null
},
@@ -529,16 +517,16 @@ return {
"kind": "FragmentSpread",
"name": "LinkedAuditsCardFragment"
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": null
},
@@ -572,16 +560,16 @@ return {
"kind": "FragmentSpread",
"name": "LinkedObligationsCardFragment"
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": null
},
@@ -615,16 +603,16 @@ return {
"kind": "FragmentSpread",
"name": "LinkedSnapshotsCardFragment"
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": null
}
@@ -653,7 +641,7 @@ return {
"name": "node",
"plural": false,
"selections": [
(v20/*: any*/),
(v18/*: any*/),
(v2/*: any*/),
{
"kind": "InlineFragment",
@@ -673,8 +661,6 @@ return {
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
(v19/*: any*/),
{
"alias": null,
"args": null,
@@ -684,7 +670,7 @@ return {
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"concreteType": "MeasureConnection",
"kind": "LinkedField",
"name": "measures",
@@ -708,23 +694,23 @@ return {
"selections": [
(v2/*: any*/),
(v3/*: any*/),
(v25/*: any*/),
(v20/*: any*/)
(v23/*: any*/),
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": "measures(first:100)"
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"filters": null,
"handle": "connection",
"key": "FrameworkGraphControl_measures",
@@ -733,7 +719,7 @@ return {
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"concreteType": "DocumentConnection",
"kind": "LinkedField",
"name": "documents",
@@ -763,7 +749,7 @@ return {
"name": "title",
"storageKey": null
},
(v26/*: any*/),
(v24/*: any*/),
{
"alias": null,
"args": null,
@@ -802,7 +788,7 @@ return {
"plural": false,
"selections": [
(v2/*: any*/),
(v6/*: any*/)
(v25/*: any*/)
],
"storageKey": null
}
@@ -812,22 +798,22 @@ return {
],
"storageKey": "versions(first:1)"
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": "documents(first:100)"
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"filters": null,
"handle": "connection",
"key": "FrameworkGraphControl_documents",
@@ -836,7 +822,7 @@ return {
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"concreteType": "AuditConnection",
"kind": "LinkedField",
"name": "audits",
@@ -860,8 +846,8 @@ return {
"selections": [
(v2/*: any*/),
(v3/*: any*/),
(v26/*: any*/),
(v25/*: any*/),
(v24/*: any*/),
(v23/*: any*/),
{
"alias": null,
"args": null,
@@ -889,22 +875,22 @@ return {
],
"storageKey": null
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": "audits(first:100)"
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"filters": null,
"handle": "connection",
"key": "FrameworkGraphControl_audits",
@@ -913,7 +899,7 @@ return {
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"concreteType": "ObligationConnection",
"kind": "LinkedField",
"name": "obligations",
@@ -957,7 +943,7 @@ return {
"name": "source",
"storageKey": null
},
(v6/*: any*/),
(v25/*: any*/),
{
"alias": null,
"args": null,
@@ -977,22 +963,22 @@ return {
],
"storageKey": null
},
(v20/*: any*/)
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": "obligations(first:100)"
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"filters": null,
"handle": "connection",
"key": "FrameworkGraphControl_obligations",
@@ -1001,7 +987,7 @@ return {
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"concreteType": "SnapshotConnection",
"kind": "LinkedField",
"name": "snapshots",
@@ -1033,23 +1019,23 @@ return {
"name": "type",
"storageKey": null
},
(v26/*: any*/),
(v20/*: any*/)
(v24/*: any*/),
(v18/*: any*/)
],
"storageKey": null
},
(v21/*: any*/)
(v19/*: any*/)
],
"storageKey": null
},
(v22/*: any*/),
(v23/*: any*/)
(v20/*: any*/),
(v21/*: any*/)
],
"storageKey": "snapshots(first:100)"
},
{
"alias": null,
"args": (v24/*: any*/),
"args": (v22/*: any*/),
"filters": null,
"handle": "connection",
"key": "FrameworkGraphControl_snapshots",
@@ -1066,7 +1052,7 @@ return {
]
},
"params": {
"cacheID": "759f31a603a2a7d2b2ad1bfcbe1f24bd",
"cacheID": "d177e11d82fa12c1622578238707b173",
"id": null,
"metadata": {
"connection": [
@@ -1119,11 +1105,11 @@ return {
},
"name": "FrameworkGraphControlNodeQuery",
"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 status\n exclusionJustification\n canUpdate: permission(action: \"core:control:update\")\n canDelete: permission(action: \"core:control:delete\")\n canCreateMeasureMapping: permission(action: \"core:control:create-measure-mapping\")\n canDeleteMeasureMapping: permission(action: \"core:control:delete-measure-mapping\")\n canCreateDocumentMapping: permission(action: \"core:control:create-document-mapping\")\n canDeleteDocumentMapping: permission(action: \"core:control:delete-document-mapping\")\n canCreateAuditMapping: permission(action: \"core:control:create-audit-mapping\")\n canDeleteAuditMapping: permission(action: \"core:control:delete-audit-mapping\")\n canCreateSnapshotMapping: permission(action: \"core:control:create-snapshot-mapping\")\n canDeleteSnapshotMapping: permission(action: \"core:control:delete-snapshot-mapping\")\n canCreateObligationMapping: permission(action: \"core:control:create-obligation-mapping\")\n canDeleteObligationMapping: permission(action: \"core:control:delete-obligation-mapping\")\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 audits(first: 100) {\n edges {\n node {\n id\n ...LinkedAuditsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n obligations(first: 100) {\n edges {\n node {\n id\n ...LinkedObligationsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n snapshots(first: 100) {\n edges {\n node {\n id\n ...LinkedSnapshotsCardFragment\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 bestPractice\n}\n\nfragment LinkedAuditsCardFragment on Audit {\n id\n name\n createdAt\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n\nfragment LinkedObligationsCardFragment on Obligation {\n id\n requirement\n area\n source\n status\n owner {\n fullName\n id\n }\n}\n\nfragment LinkedSnapshotsCardFragment on Snapshot {\n id\n name\n description\n type\n createdAt\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 canUpdate: permission(action: \"core:control:update\")\n canDelete: permission(action: \"core:control:delete\")\n canCreateMeasureMapping: permission(action: \"core:control:create-measure-mapping\")\n canDeleteMeasureMapping: permission(action: \"core:control:delete-measure-mapping\")\n canCreateDocumentMapping: permission(action: \"core:control:create-document-mapping\")\n canDeleteDocumentMapping: permission(action: \"core:control:delete-document-mapping\")\n canCreateAuditMapping: permission(action: \"core:control:create-audit-mapping\")\n canDeleteAuditMapping: permission(action: \"core:control:delete-audit-mapping\")\n canCreateSnapshotMapping: permission(action: \"core:control:create-snapshot-mapping\")\n canDeleteSnapshotMapping: permission(action: \"core:control:delete-snapshot-mapping\")\n canCreateObligationMapping: permission(action: \"core:control:create-obligation-mapping\")\n canDeleteObligationMapping: permission(action: \"core:control:delete-obligation-mapping\")\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 audits(first: 100) {\n edges {\n node {\n id\n ...LinkedAuditsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n obligations(first: 100) {\n edges {\n node {\n id\n ...LinkedObligationsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n snapshots(first: 100) {\n edges {\n node {\n id\n ...LinkedSnapshotsCardFragment\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 bestPractice\n}\n\nfragment LinkedAuditsCardFragment on Audit {\n id\n name\n createdAt\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n\nfragment LinkedObligationsCardFragment on Obligation {\n id\n requirement\n area\n source\n status\n owner {\n fullName\n id\n }\n}\n\nfragment LinkedSnapshotsCardFragment on Snapshot {\n id\n name\n description\n type\n createdAt\n}\n"
}
};
})();
(node as any).hash = "d320f2f2ee2e7f9b9f398dd1ff46ac2b";
(node as any).hash = "c5a03208206812d371f5133c8963a710";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<a493a708fe8353db1159647a455582ad>>
* @generated SignedSource<<4716483592ffb7c9bb7d96ea56503734>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -189,19 +189,6 @@ return {
"name": "permission",
"storageKey": "permission(action:\"core:control:create\")"
},
{
"alias": "canGenerateSOA",
"args": [
{
"kind": "Literal",
"name": "action",
"value": "core:framework:generate-state-of-applicability"
}
],
"kind": "ScalarField",
"name": "permission",
"storageKey": "permission(action:\"core:framework:generate-state-of-applicability\")"
},
{
"alias": null,
"args": null,
@@ -262,20 +249,6 @@ return {
"storageKey": null
},
(v3/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "exclusionJustification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -314,12 +287,12 @@ return {
]
},
"params": {
"cacheID": "58485d98106169267addab7729095131",
"cacheID": "9cb1289702b4dd6c83330a0ef84297d6",
"id": null,
"metadata": {},
"name": "FrameworkGraphNodeQuery",
"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 lightLogoURL\n darkLogoURL\n canExport: permission(action: \"core:franework:export\")\n canUpdate: permission(action: \"core:framework:update\")\n canDelete: permission(action: \"core:framework:delete\")\n canCreateControl: permission(action: \"core:control:create\")\n canGenerateSOA: permission(action: \"core:framework:generate-state-of-applicability\")\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 bestPractice\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 lightLogoURL\n darkLogoURL\n canExport: permission(action: \"core:franework:export\")\n canUpdate: permission(action: \"core:framework:update\")\n canDelete: permission(action: \"core:framework:delete\")\n canCreateControl: permission(action: \"core:control:create\")\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 bestPractice\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -101,8 +101,6 @@ export const frameworkControlNodeQuery = graphql`
name
sectionTitle
description
status
exclusionJustification
canUpdate: permission(action: "core:control:update")
canDelete: permission(action: "core:control:delete")
canCreateMeasureMapping: permission(

View File

@@ -336,22 +336,7 @@ export default function FrameworkControlPage({ queryRef }: Props) {
</div>
</div>
{control.status === "EXCLUDED" && (
<div className="bg-danger border border-border-danger rounded-lg p-4">
<div className="flex items-center gap-2 mb-2">
<div className="font-medium text-txt-danger">
{__("This control is excluded")}
</div>
</div>
<div className="text-sm">
<strong>{__("Justification:")}</strong>
{" "}
{control.exclusionJustification
|| __("No justification provided")}
</div>
</div>
)}
<div className={control.status === "EXCLUDED" ? "opacity-60" : ""}>
<div>
<div className="text-base mb-1">{control.name}</div>
{control.description && (
<div className="text-sm text-txt-secondary mb-4 whitespace-pre-wrap">

View File

@@ -21,7 +21,6 @@ import { ConnectionHandler, graphql } from "relay-runtime";
import type { FrameworkDetailPageExportFrameworkMutation } from "#/__generated__/core/FrameworkDetailPageExportFrameworkMutation.graphql";
import type { FrameworkDetailPageFragment$key } from "#/__generated__/core/FrameworkDetailPageFragment.graphql";
import type { FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation } from "#/__generated__/core/FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation.graphql";
import type { FrameworkGraphNodeQuery } from "#/__generated__/core/FrameworkGraphNodeQuery.graphql";
import {
connectionListKey,
@@ -45,7 +44,6 @@ const frameworkDetailFragment = graphql`
canUpdate: permission(action: "core:framework:update")
canDelete: permission(action: "core:framework:delete")
canCreateControl: permission(action: "core:control:create")
canGenerateSOA: permission(action: "core:framework:generate-state-of-applicability")
organization {
name
}
@@ -59,8 +57,6 @@ const frameworkDetailFragment = graphql`
id
sectionTitle
name
status
exclusionJustification
bestPractice
}
}
@@ -68,18 +64,6 @@ const frameworkDetailFragment = graphql`
}
`;
const generateFrameworkStateOfApplicabilityMutation = graphql`
mutation FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation(
$frameworkId: ID!
) {
generateFrameworkStateOfApplicability(
input: { frameworkId: $frameworkId }
) {
data
}
}
`;
const exportFrameworkMutation = graphql`
mutation FrameworkDetailPageExportFrameworkMutation($frameworkId: ID!) {
exportFramework(input: { frameworkId: $frameworkId }) {
@@ -116,17 +100,6 @@ export default function FrameworkDetailPage(props: Props) {
framework,
ConnectionHandler.getConnectionID(organizationId, connectionListKey),
);
const [generateFrameworkStateOfApplicability]
= useMutationWithToasts<FrameworkDetailPageGenerateFrameworkStateOfApplicabilityMutation>(
generateFrameworkStateOfApplicabilityMutation,
{
errorMessage:
"Failed to generate framework state of applicability",
successMessage:
"Framework state of applicability generated successfully",
},
);
const [exportFramework]
= useMutationWithToasts<FrameworkDetailPageExportFrameworkMutation>(
exportFrameworkMutation,
@@ -154,7 +127,7 @@ export default function FrameworkDetailPage(props: Props) {
);
}
const hasAnyAction = framework.canExport || framework.canDelete || framework.canGenerateSOA;
const hasAnyAction = framework.canExport || framework.canDelete;
return (
<div className="space-y-6">
@@ -178,35 +151,6 @@ export default function FrameworkDetailPage(props: Props) {
)}
{hasAnyAction && (
<ActionDropdown variant="secondary">
{framework.canGenerateSOA
&& (
<DropdownItem
variant="primary"
onClick={() => {
void generateFrameworkStateOfApplicability({
variables: { frameworkId: framework.id },
onCompleted: (data) => {
if (
data
.generateFrameworkStateOfApplicability
?.data
) {
const link
= window.document.createElement("a");
link.href
= data.generateFrameworkStateOfApplicability.data;
link.download = `${framework.organization.name}-${framework.name}-SOA.xlsx`;
window.document.body.appendChild(link);
link.click();
window.document.body.removeChild(link);
}
},
});
}}
>
{__("Download SOA")}
</DropdownItem>
)}
{framework.canExport
&& (
<DropdownItem
@@ -245,7 +189,6 @@ export default function FrameworkDetailPage(props: Props) {
key={control.id}
id={control.sectionTitle}
description={control.name}
excluded={control.status === "EXCLUDED"}
to={`/organizations/${organizationId}/frameworks/${framework.id}/controls/${control.id}`}
active={selectedControl?.id === control.id}
/>

View File

@@ -33,8 +33,6 @@ const controlFragment = graphql`
name
description
sectionTitle
status
exclusionJustification
bestPractice
}
`;
@@ -64,31 +62,12 @@ const updateMutation = graphql`
}
`;
const schema = z
.object({
name: z.string(),
description: z.string().optional().nullable(),
sectionTitle: z.string(),
status: z.enum(["INCLUDED", "EXCLUDED"]),
exclusionJustification: z.string().optional(),
bestPractice: z.boolean(),
})
.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"],
},
);
const schema = z.object({
name: z.string(),
description: z.string().optional().nullable(),
sectionTitle: z.string(),
bestPractice: z.boolean(),
});
export function FrameworkControlDialog(props: Props) {
const { __ } = useTranslate();
@@ -111,9 +90,6 @@ export function FrameworkControlDialog(props: Props) {
name: frameworkControl?.name ?? "",
description: frameworkControl?.description ?? "",
sectionTitle: frameworkControl?.sectionTitle ?? "",
status: frameworkControl?.status ?? "INCLUDED",
exclusionJustification:
frameworkControl?.exclusionJustification ?? "",
bestPractice: frameworkControl?.bestPractice ?? true,
}),
[frameworkControl],
@@ -141,11 +117,6 @@ export function FrameworkControlDialog(props: Props) {
description: data.description || null,
sectionTitle: data.sectionTitle,
bestPractice: data.bestPractice,
status: data.status,
exclusionJustification:
data.status === "EXCLUDED"
? data.exclusionJustification
: null,
},
},
});
@@ -159,11 +130,6 @@ export function FrameworkControlDialog(props: Props) {
description: data.description || null,
sectionTitle: data.sectionTitle,
bestPractice: data.bestPractice ?? true,
status: data.status,
exclusionJustification:
data.status === "EXCLUDED"
? data.exclusionJustification
: null,
},
connections: [props.connectionId!],
},

View File

@@ -39,7 +39,6 @@ func TestControl_Create(t *testing.T) {
id
name
sectionTitle
status
}
}
}
@@ -53,7 +52,6 @@ func TestControl_Create(t *testing.T) {
ID string `json:"id"`
Name string `json:"name"`
SectionTitle string `json:"sectionTitle"`
Status string `json:"status"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
@@ -65,7 +63,6 @@ func TestControl_Create(t *testing.T) {
"sectionTitle": "A.5",
"name": "Information Security Policies",
"description": "Policies for information security",
"status": "INCLUDED",
"bestPractice": true,
},
}, &result)
@@ -75,52 +72,6 @@ func TestControl_Create(t *testing.T) {
assert.NotEmpty(t, control.ID)
assert.Equal(t, "Information Security Policies", control.Name)
assert.Equal(t, "A.5", control.SectionTitle)
assert.Equal(t, "INCLUDED", control.Status)
})
t.Run("with excluded status and justification", func(t *testing.T) {
query := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
status
exclusionJustification
}
}
}
}
`
var result struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
Status string `json:"status"`
ExclusionJustification *string `json:"exclusionJustification"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"sectionTitle": "A.10",
"name": "Cryptography",
"description": "Cryptography controls",
"status": "EXCLUDED",
"exclusionJustification": "Not applicable - no cryptographic data processing",
"bestPractice": false,
},
}, &result)
require.NoError(t, err)
control := result.CreateControl.ControlEdge.Node
assert.Equal(t, "EXCLUDED", control.Status)
assert.Equal(t, "Not applicable - no cryptographic data processing", *control.ExclusionJustification)
})
}
@@ -167,46 +118,6 @@ func TestControl_Update(t *testing.T) {
assert.Equal(t, "Updated Control Name", result.UpdateControl.Control.Name)
})
t.Run("changes status from INCLUDED to EXCLUDED", func(t *testing.T) {
statusTestControlID := factory.CreateControl(owner, frameworkID, factory.Attrs{
"name": "Status Change Control",
"status": "INCLUDED",
})
query := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
status
exclusionJustification
}
}
}
`
var result struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
Status string `json:"status"`
ExclusionJustification *string `json:"exclusionJustification"`
} `json:"control"`
} `json:"updateControl"`
}
err := owner.Execute(query, map[string]any{
"input": map[string]any{
"id": statusTestControlID,
"status": "EXCLUDED",
"exclusionJustification": "No physical assets in scope",
},
}, &result)
require.NoError(t, err)
assert.Equal(t, "EXCLUDED", result.UpdateControl.Control.Status)
assert.Equal(t, "No physical assets in scope", *result.UpdateControl.Control.ExclusionJustification)
})
}
func TestControl_Delete(t *testing.T) {
@@ -357,7 +268,6 @@ func TestControl_RequiredFields(t *testing.T) {
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
},
@@ -370,7 +280,6 @@ func TestControl_RequiredFields(t *testing.T) {
"frameworkId": frameworkID,
"description": "Test",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
},
@@ -383,20 +292,6 @@ func TestControl_RequiredFields(t *testing.T) {
"frameworkId": frameworkID,
"name": "Test Control",
"description": "Test",
"status": "INCLUDED",
"bestPractice": true,
},
},
wantError: true,
},
{
name: "Missing status should fail",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"bestPractice": true,
},
},
@@ -409,7 +304,6 @@ func TestControl_RequiredFields(t *testing.T) {
"frameworkId": frameworkID,
"name": "Test Control",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
},
@@ -423,21 +317,6 @@ func TestControl_RequiredFields(t *testing.T) {
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"status": "INCLUDED",
},
},
wantError: true,
},
{
name: "Invalid status enum should fail",
variables: map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Test Control",
"description": "Test",
"sectionTitle": "Section 1",
"status": "INVALID_STATUS",
"bestPractice": true,
},
},
wantError: true,
@@ -524,7 +403,6 @@ func TestControl_OmittableDescription(t *testing.T) {
"name": "Omittable Test Control",
"description": "Initial description",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
}, &createResult)
@@ -689,7 +567,6 @@ func TestControl_SubResolvers(t *testing.T) {
"name": "SubResolver Test Control",
"description": "Test description",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
}, &controlResult)
@@ -832,152 +709,3 @@ func TestControl_SubResolvers(t *testing.T) {
assert.GreaterOrEqual(t, len(result.Node.Measures.Edges), 1)
})
}
func TestControl_ExclusionJustification(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
// Create framework
createFrameworkQuery := `
mutation CreateFramework($input: CreateFrameworkInput!) {
createFramework(input: $input) {
frameworkEdge {
node {
id
}
}
}
}
`
var frameworkResult struct {
CreateFramework struct {
FrameworkEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"frameworkEdge"`
} `json:"createFramework"`
}
err := owner.Execute(createFrameworkQuery, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"name": fmt.Sprintf("Exclusion Test %d", time.Now().UnixNano()),
},
}, &frameworkResult)
require.NoError(t, err)
frameworkID := frameworkResult.CreateFramework.FrameworkEdge.Node.ID
t.Run("EXCLUDED status should have exclusionJustification", func(t *testing.T) {
createControlQuery := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
status
exclusionJustification
}
}
}
}
`
var result struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
Status string `json:"status"`
ExclusionJustification string `json:"exclusionJustification"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
err := owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "Excluded Control",
"description": "Test",
"sectionTitle": "Section 1",
"status": "EXCLUDED",
"exclusionJustification": "Not applicable to our business",
"bestPractice": false,
},
}, &result)
require.NoError(t, err)
assert.Equal(t, "EXCLUDED", result.CreateControl.ControlEdge.Node.Status)
assert.Equal(t, "Not applicable to our business", result.CreateControl.ControlEdge.Node.ExclusionJustification)
})
t.Run("Change status from INCLUDED to EXCLUDED", func(t *testing.T) {
// Create included control first
createControlQuery := `
mutation CreateControl($input: CreateControlInput!) {
createControl(input: $input) {
controlEdge {
node {
id
}
}
}
}
`
var createResult struct {
CreateControl struct {
ControlEdge struct {
Node struct {
ID string `json:"id"`
} `json:"node"`
} `json:"controlEdge"`
} `json:"createControl"`
}
err := owner.Execute(createControlQuery, map[string]any{
"input": map[string]any{
"frameworkId": frameworkID,
"name": "To Be Excluded Control",
"description": "Test",
"sectionTitle": "Section 2",
"status": "INCLUDED",
"bestPractice": true,
},
}, &createResult)
require.NoError(t, err)
// Update to excluded
updateControlQuery := `
mutation UpdateControl($input: UpdateControlInput!) {
updateControl(input: $input) {
control {
id
status
exclusionJustification
}
}
}
`
var updateResult struct {
UpdateControl struct {
Control struct {
ID string `json:"id"`
Status string `json:"status"`
ExclusionJustification string `json:"exclusionJustification"`
} `json:"control"`
} `json:"updateControl"`
}
err = owner.Execute(updateControlQuery, map[string]any{
"input": map[string]any{
"id": createResult.CreateControl.ControlEdge.Node.ID,
"status": "EXCLUDED",
"exclusionJustification": "Decided to exclude",
},
}, &updateResult)
require.NoError(t, err)
})
}

View File

@@ -81,7 +81,6 @@ func TestControlMeasureMapping_CreateDelete(t *testing.T) {
"name": "Control for Mapping",
"description": "Test control for mapping",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
}, &createControlResult)
@@ -361,7 +360,6 @@ func TestControlDocumentMapping_CreateDelete(t *testing.T) {
"name": "Control for Document Mapping",
"description": "Test control",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
}, &createControlResult)
@@ -503,7 +501,6 @@ func TestControlAuditMapping_CreateDelete(t *testing.T) {
"name": "Control for Audit Mapping",
"description": "Test control",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
}, &createControlResult)
@@ -641,7 +638,6 @@ func TestControlSnapshotMapping_CreateDelete(t *testing.T) {
"name": "Control for Snapshot Mapping",
"description": "Test control",
"sectionTitle": "Section 1",
"status": "INCLUDED",
"bestPractice": true,
},
}, &createControlResult)

View File

@@ -384,7 +384,7 @@ func TestRBAC(t *testing.T) {
client: owner,
query: createControlMutation,
variables: func() map[string]any {
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Owner"), "status": "INCLUDED", "bestPractice": true}}
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Owner"), "bestPractice": true}}
},
shouldAllow: true,
},
@@ -394,7 +394,7 @@ func TestRBAC(t *testing.T) {
client: admin,
query: createControlMutation,
variables: func() map[string]any {
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Admin"), "status": "INCLUDED", "bestPractice": true}}
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Admin"), "bestPractice": true}}
},
shouldAllow: true,
},
@@ -404,7 +404,7 @@ func TestRBAC(t *testing.T) {
client: viewer,
query: createControlMutation,
variables: func() map[string]any {
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Viewer"), "status": "INCLUDED", "bestPractice": true}}
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Viewer"), "bestPractice": true}}
},
shouldAllow: false,
},

View File

@@ -205,7 +205,6 @@ func CreateControl(c *testutil.Client, frameworkID string, attrs ...Attrs) strin
"name": a.getString("name", SafeName("Control")),
"description": a.getString("description", "Test control description"),
"sectionTitle": a.getString("sectionTitle", fmt.Sprintf("Section %s", gofakeit.LetterN(3))),
"status": a.getString("status", "INCLUDED"),
"bestPractice": a.getBool("bestPractice", true),
}

6
go.mod
View File

@@ -26,7 +26,6 @@ require (
github.com/scim2/filter-parser/v2 v2.2.0
github.com/stretchr/testify v1.11.1
github.com/vektah/gqlparser/v2 v2.5.31
github.com/xuri/excelize/v2 v2.10.0
github.com/yuin/goldmark v1.7.16
go.gearno.de/crypto/uuid v0.1.1-0.20251208105319-3f587312a712
go.gearno.de/kit v0.1.1
@@ -109,8 +108,6 @@ require (
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.19.2 // indirect
github.com/richardlehane/mscfb v1.0.6 // indirect
github.com/richardlehane/msoleps v1.0.6 // indirect
github.com/russellhaering/goxmldsig v1.5.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/spf13/cobra v1.10.1 // indirect
@@ -120,10 +117,7 @@ require (
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/tiendc/go-deepcopy v1.7.2 // indirect
github.com/urfave/cli/v3 v3.6.1 // indirect
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
go.gearno.de/x/panicf v0.1.1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect

12
go.sum
View File

@@ -205,10 +205,6 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
github.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8=
github.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw=
@@ -245,18 +241,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=
github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k=
github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=

View File

@@ -44,30 +44,6 @@ export const description: INodeProperties[] = [
description: 'The name of the control',
required: true,
},
{
displayName: 'Status',
name: 'status',
type: 'options',
displayOptions: {
show: {
resource: ['control'],
operation: ['create'],
},
},
options: [
{
name: 'Included',
value: 'INCLUDED',
},
{
name: 'Excluded',
value: 'EXCLUDED',
},
],
default: 'INCLUDED',
description: 'The status of the control',
required: true,
},
{
displayName: 'Description',
name: 'description',
@@ -81,19 +57,6 @@ export const description: INodeProperties[] = [
default: '',
description: 'The description of the control',
},
{
displayName: 'Exclusion Justification',
name: 'exclusionJustification',
type: 'string',
displayOptions: {
show: {
resource: ['control'],
operation: ['create'],
},
},
default: '',
description: 'The justification for excluding the control',
},
];
export async function execute(
@@ -103,9 +66,7 @@ export async function execute(
const frameworkId = this.getNodeParameter('frameworkId', itemIndex) as string;
const sectionTitle = this.getNodeParameter('sectionTitle', itemIndex) as string;
const name = this.getNodeParameter('name', itemIndex) as string;
const status = this.getNodeParameter('status', itemIndex) as string;
const description = this.getNodeParameter('description', itemIndex, '') as string;
const exclusionJustification = this.getNodeParameter('exclusionJustification', itemIndex, '') as string;
const query = `
mutation CreateControl($input: CreateControlInput!) {
@@ -116,8 +77,6 @@ export async function execute(
sectionTitle
name
description
status
exclusionJustification
createdAt
updatedAt
}
@@ -131,9 +90,8 @@ export async function execute(
frameworkId,
sectionTitle,
name,
status,
bestPractice: true,
...(description && { description }),
...(exclusionJustification && { exclusionJustification }),
},
};
@@ -144,4 +102,3 @@ export async function execute(
pairedItem: { item: itemIndex },
};
}

View File

@@ -32,8 +32,6 @@ export async function execute(
sectionTitle
name
description
status
exclusionJustification
createdAt
updatedAt
}

View File

@@ -67,8 +67,6 @@ export async function execute(
sectionTitle
name
description
status
exclusionJustification
createdAt
updatedAt
}

View File

@@ -55,42 +55,6 @@ export const description: INodeProperties[] = [
default: '',
description: 'The description of the control',
},
{
displayName: 'Status',
name: 'status',
type: 'options',
displayOptions: {
show: {
resource: ['control'],
operation: ['update'],
},
},
options: [
{
name: 'Included',
value: 'INCLUDED',
},
{
name: 'Excluded',
value: 'EXCLUDED',
},
],
default: 'INCLUDED',
description: 'The status of the control',
},
{
displayName: 'Exclusion Justification',
name: 'exclusionJustification',
type: 'string',
displayOptions: {
show: {
resource: ['control'],
operation: ['update'],
},
},
default: '',
description: 'The justification for excluding the control',
},
];
export async function execute(
@@ -101,8 +65,6 @@ export async function execute(
const sectionTitle = this.getNodeParameter('sectionTitle', itemIndex, '') as string;
const name = this.getNodeParameter('name', itemIndex, '') as string;
const description = this.getNodeParameter('description', itemIndex, '') as string;
const status = this.getNodeParameter('status', itemIndex, '') as string;
const exclusionJustification = this.getNodeParameter('exclusionJustification', itemIndex, '') as string;
const query = `
mutation UpdateControl($input: UpdateControlInput!) {
@@ -112,8 +74,6 @@ export async function execute(
sectionTitle
name
description
status
exclusionJustification
createdAt
updatedAt
}
@@ -125,8 +85,6 @@ export async function execute(
if (sectionTitle) input.sectionTitle = sectionTitle;
if (name) input.name = name;
if (description) input.description = description;
if (status) input.status = status;
if (exclusionJustification) input.exclusionJustification = exclusionJustification;
const responseData = await proboApiRequest.call(this, query, { input });
@@ -135,4 +93,3 @@ export async function execute(
pairedItem: { item: itemIndex },
};
}

View File

@@ -6,7 +6,6 @@ type Props = {
active?: boolean;
id: string;
description?: string;
excluded?: boolean;
to: string;
} & HTMLAttributes<HTMLAnchorElement>;
@@ -27,39 +26,19 @@ const classNames = tv({
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: {
active: false,
excluded: false,
},
});
export function ControlItem({ active, id, description, excluded, to, ...props }: Props) {
export function ControlItem({ active, id, description, to, ...props }: Props) {
const {
wrapper,
id: idCls,
description: descriptionCls,
} = classNames({
active,
excluded,
});
const ref = useRef<HTMLAnchorElement>(null);

View File

@@ -30,17 +30,15 @@ import (
type (
Control struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
SectionTitle string `db:"section_title"`
FrameworkID gid.GID `db:"framework_id"`
Name string `db:"name"`
Description *string `db:"description"`
Status ControlStatus `db:"status"`
ExclusionJustification *string `db:"exclusion_justification"`
BestPractice bool `db:"best_practice"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
SectionTitle string `db:"section_title"`
FrameworkID gid.GID `db:"framework_id"`
Name string `db:"name"`
Description *string `db:"description"`
BestPractice bool `db:"best_practice"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Controls []*Control
@@ -132,8 +130,6 @@ WITH ctrl AS (
c.tenant_id,
c.name,
c.description,
c.status,
c.exclusion_justification,
c.best_practice,
c.created_at,
c.updated_at,
@@ -152,8 +148,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -245,8 +239,6 @@ WITH ctrl AS (
c.tenant_id,
c.name,
c.description,
c.status,
c.exclusion_justification,
c.best_practice,
c.created_at,
c.updated_at,
@@ -265,8 +257,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -364,8 +354,6 @@ WITH ctrl AS (
c.tenant_id,
c.name,
c.description,
c.status,
c.exclusion_justification,
c.best_practice,
c.created_at,
c.updated_at,
@@ -390,8 +378,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -471,8 +457,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -567,8 +551,6 @@ WITH ctrl AS (
c.tenant_id,
c.name,
c.description,
c.status,
c.exclusion_justification,
c.best_practice,
c.created_at,
c.updated_at,
@@ -587,8 +569,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -635,8 +615,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -685,8 +663,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -739,8 +715,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -783,8 +757,6 @@ INSERT INTO
section_title,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -797,8 +769,6 @@ VALUES (
@section_title,
@name,
@description,
@status,
@exclusion_justification,
@best_practice,
@created_at,
@updated_at
@@ -806,18 +776,16 @@ VALUES (
`
args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(),
"control_id": c.ID,
"organization_id": c.OrganizationID,
"framework_id": c.FrameworkID,
"section_title": c.SectionTitle,
"name": c.Name,
"description": c.Description,
"status": c.Status,
"exclusion_justification": c.ExclusionJustification,
"best_practice": c.BestPractice,
"created_at": c.CreatedAt,
"updated_at": c.UpdatedAt,
"tenant_id": scope.GetTenantID(),
"control_id": c.ID,
"organization_id": c.OrganizationID,
"framework_id": c.FrameworkID,
"section_title": c.SectionTitle,
"name": c.Name,
"description": c.Description,
"best_practice": c.BestPractice,
"created_at": c.CreatedAt,
"updated_at": c.UpdatedAt,
}
_, err := conn.Exec(ctx, q, args)
@@ -866,8 +834,6 @@ UPDATE controls SET
name = @name,
description = @description,
section_title = @section_title,
status = @status,
exclusion_justification = @exclusion_justification,
best_practice = @best_practice,
updated_at = @updated_at
WHERE %s
@@ -876,14 +842,12 @@ WHERE %s
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{
"control_id": c.ID,
"name": c.Name,
"description": c.Description,
"section_title": c.SectionTitle,
"status": c.Status,
"exclusion_justification": c.ExclusionJustification,
"best_practice": c.BestPractice,
"updated_at": c.UpdatedAt,
"control_id": c.ID,
"name": c.Name,
"description": c.Description,
"section_title": c.SectionTitle,
"best_practice": c.BestPractice,
"updated_at": c.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())
@@ -963,8 +927,6 @@ WITH ctrl AS (
c.tenant_id,
c.name,
c.description,
c.status,
c.exclusion_justification,
c.best_practice,
c.created_at,
c.updated_at,
@@ -983,8 +945,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at
@@ -1077,8 +1037,6 @@ WITH ctrl AS (
c.tenant_id,
c.name,
c.description,
c.status,
c.exclusion_justification,
c.best_practice,
c.created_at,
c.updated_at,
@@ -1097,8 +1055,6 @@ SELECT
organization_id,
name,
description,
status,
exclusion_justification,
best_practice,
created_at,
updated_at

View File

@@ -1,59 +0,0 @@
// 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 ControlStatuses() []ControlStatus {
return []ControlStatus{
ControlStatusIncluded,
ControlStatusExcluded,
}
}
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
}

View File

@@ -88,7 +88,7 @@ func (states DocumentVersionSignatureStates) Value() (driver.Value, error) {
if i > 0 {
result.WriteString(",")
}
result.WriteString(fmt.Sprintf("%q", state.String()))
fmt.Fprintf(&result, "%q", state.String())
}
result.WriteString("}")
return result.String(), nil

View File

@@ -21,7 +21,7 @@ import (
)
type (
InvitationStatus string
InvitationStatus string
InvitationStatuses []InvitationStatus
)
@@ -74,7 +74,7 @@ func (statuses InvitationStatuses) Value() (driver.Value, error) {
if i > 0 {
result.WriteString(",")
}
result.WriteString(fmt.Sprintf("%q", status.String()))
fmt.Fprintf(&result, "%q", status.String())
}
result.WriteString("}")
return result.String(), nil

View File

@@ -0,0 +1,6 @@
ALTER TABLE controls ALTER COLUMN status DROP NOT NULL;
-- TODO: drop columns and type once all code references are fully removed
-- ALTER TABLE controls DROP COLUMN status;
-- ALTER TABLE controls DROP COLUMN exclusion_justification;
-- DROP TYPE control_status;

View File

@@ -105,8 +105,7 @@ const (
ActionFrameworkCreate = "core:framework:create"
ActionFrameworkUpdate = "core:framework:update"
ActionFrameworkDelete = "core:framework:delete"
ActionFrameworkStateOfApplicabilityGenerate = "core:framework:generate-state-of-applicability"
ActionFrameworkExport = "core:framework:export"
ActionFrameworkExport = "core:framework:export"
ActionFrameworkImport = "core:framework:import"
// Control actions

View File

@@ -32,24 +32,20 @@ type (
}
CreateControlRequest struct {
ID gid.GID
FrameworkID gid.GID
Name string
Description *string
SectionTitle string
Status *coredata.ControlStatus
ExclusionJustification *string
BestPractice bool
ID gid.GID
FrameworkID gid.GID
Name string
Description *string
SectionTitle string
BestPractice bool
}
UpdateControlRequest struct {
ID gid.GID
Name *string
Description **string
SectionTitle *string
Status *coredata.ControlStatus
ExclusionJustification *string
BestPractice *bool
ID gid.GID
Name *string
Description **string
SectionTitle *string
BestPractice *bool
}
)
@@ -60,8 +56,6 @@ func (ccr *CreateControlRequest) Validate() error {
v.Check(ccr.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(ccr.Description, "description", validator.Required(), validator.SafeText(ContentMaxLength))
v.Check(ccr.SectionTitle, "section_title", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(ccr.Status, "status", validator.Required(), validator.OneOfSlice(coredata.ControlStatuses()))
v.Check(ccr.ExclusionJustification, "exclusion_justification", validator.SafeText(TitleMaxLength))
return v.Error()
}
@@ -73,8 +67,6 @@ func (ucr *UpdateControlRequest) Validate() error {
v.Check(ucr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(ucr.Description, "description", validator.SafeText(ContentMaxLength))
v.Check(ucr.SectionTitle, "section_title", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(ucr.Status, "status", validator.OneOfSlice(coredata.ControlStatuses()))
v.Check(ucr.ExclusionJustification, "exclusion_justification", validator.SafeText(TitleMaxLength))
return v.Error()
}
@@ -839,16 +831,14 @@ func (s ControlService) Create(
framework := &coredata.Framework{}
control := &coredata.Control{
ID: gid.New(s.svc.scope.GetTenantID(), coredata.ControlEntityType),
FrameworkID: req.FrameworkID,
Name: req.Name,
Description: req.Description,
SectionTitle: req.SectionTitle,
Status: *req.Status,
ExclusionJustification: req.ExclusionJustification,
BestPractice: req.BestPractice,
CreatedAt: now,
UpdatedAt: now,
ID: gid.New(s.svc.scope.GetTenantID(), coredata.ControlEntityType),
FrameworkID: req.FrameworkID,
Name: req.Name,
Description: req.Description,
SectionTitle: req.SectionTitle,
BestPractice: req.BestPractice,
CreatedAt: now,
UpdatedAt: now,
}
err := s.svc.pg.WithTx(
@@ -921,14 +911,6 @@ func (s ControlService) Update(
control.SectionTitle = *req.SectionTitle
}
if req.Status != nil {
control.Status = *req.Status
}
if req.ExclusionJustification != nil {
control.ExclusionJustification = req.ExclusionJustification
}
if req.BestPractice != nil {
control.BestPractice = *req.BestPractice
}

View File

@@ -36,12 +36,10 @@ import (
"go.probo.inc/probo/pkg/mail"
"go.probo.inc/probo/pkg/page"
"go.probo.inc/probo/pkg/slug"
"go.probo.inc/probo/pkg/soagen"
"go.probo.inc/probo/pkg/validator"
)
const (
maxStateOfApplicabilityLimit = 10_000
frameworkExportEmailExpiresIn = 24 * time.Hour
)
@@ -584,7 +582,6 @@ func (s FrameworkService) Import(
SectionTitle: control.ID,
Name: control.Name,
Description: &description,
Status: coredata.ControlStatusIncluded,
BestPractice: bestPractice,
CreatedAt: now,
UpdatedAt: now,
@@ -605,182 +602,6 @@ func (s FrameworkService) Import(
return framework, nil
}
func (s FrameworkService) StateOfApplicability(ctx context.Context, frameworkID gid.GID) ([]byte, error) {
rows := []soagen.SOARowData{}
err := s.svc.pg.WithTx(
ctx,
func(conn pg.Conn) error {
framework := &coredata.Framework{}
if err := framework.LoadByID(ctx, conn, s.svc.scope, frameworkID); err != nil {
return fmt.Errorf("cannot load framework: %w", err)
}
controls := coredata.Controls{}
err := controls.LoadByFrameworkID(
ctx,
conn,
s.svc.scope,
frameworkID,
page.NewCursor(
maxStateOfApplicabilityLimit,
nil,
page.Head,
page.OrderBy[coredata.ControlOrderField]{
Field: coredata.ControlOrderFieldSectionTitle,
Direction: page.OrderDirectionAsc,
},
),
coredata.NewControlFilter(nil),
)
if err != nil {
return fmt.Errorf("cannot load controls: %w", err)
}
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{
SectionTitle: control.SectionTitle,
ControlName: control.Name,
Applicability: applicability,
ExclusionJustification: exclusionJustification,
Regulatory: ref.Ref(false),
Contractual: ref.Ref(false),
BestPractice: bestPractice,
RiskAssessment: ref.Ref(false),
SecurityMeasures: []string{},
}
if control.Status == coredata.ControlStatusExcluded {
rows = append(rows, row)
continue
}
measures := coredata.Measures{}
err = measures.LoadByControlID(
ctx,
conn,
s.svc.scope,
control.ID,
page.NewCursor(
maxStateOfApplicabilityLimit,
nil,
page.Head,
page.OrderBy[coredata.MeasureOrderField]{
Field: coredata.MeasureOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
},
),
coredata.NewMeasureFilter(nil, nil),
)
if err != nil {
return fmt.Errorf("cannot load measures: %w", err)
}
for _, measure := range measures {
risks := coredata.Risks{}
var nilSnapshotID *gid.GID = nil
risksCount, err := risks.CountByMeasureID(
ctx,
conn,
s.svc.scope,
measure.ID,
coredata.NewRiskFilter(nil, &nilSnapshotID),
)
if err != nil {
return fmt.Errorf("cannot count risks: %w", err)
}
if risksCount > 0 {
row.RiskAssessment = ref.Ref(true)
}
row.SecurityMeasures = append(row.SecurityMeasures, measure.Name)
}
documents := coredata.Documents{}
err = documents.LoadByControlID(
ctx,
conn,
s.svc.scope,
control.ID,
page.NewCursor(
0,
nil,
page.Head,
page.OrderBy[coredata.DocumentOrderField]{
Field: coredata.DocumentOrderFieldCreatedAt,
Direction: page.OrderDirectionAsc,
},
),
coredata.NewDocumentFilter(nil),
)
if err != nil {
return fmt.Errorf("cannot load documents: %w", err)
}
for _, document := range documents {
risks := coredata.Risks{}
var nilSnapshotID *gid.GID = nil
risksCount, err := risks.CountByDocumentID(
ctx,
conn,
s.svc.scope,
document.ID,
coredata.NewRiskFilter(nil, &nilSnapshotID),
)
if err != nil {
return fmt.Errorf("cannot count risks: %w", err)
}
if risksCount > 0 {
row.RiskAssessment = ref.Ref(true)
}
row.SecurityMeasures = append(row.SecurityMeasures, document.Title)
}
rows = append(rows, row)
}
return nil
},
)
if err != nil {
return nil, err
}
output, err := soagen.GenerateExcel(
soagen.SOAData{
Rows: rows,
},
)
if err != nil {
return nil, fmt.Errorf("cannot generate Excel file: %w", err)
}
return output, nil
}
func (s FrameworkService) SendExportEmail(
ctx context.Context,
fileID gid.GID,

View File

@@ -1030,14 +1030,6 @@ enum DataClassification
)
}
enum ControlStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.ControlStatus") {
INCLUDED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ControlStatusIncluded")
EXCLUDED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ControlStatusExcluded")
}
enum AuditOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.AuditOrderField") {
CREATED_AT
@@ -2051,8 +2043,6 @@ type Control implements Node {
sectionTitle: String!
name: String!
description: String
status: ControlStatus!
exclusionJustification: String
bestPractice: Boolean!
regulatory: Boolean! @goField(forceResolver: true)
contractual: Boolean! @goField(forceResolver: true)
@@ -3287,9 +3277,6 @@ type Mutation {
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
importFramework(input: ImportFrameworkInput!): ImportFrameworkPayload!
deleteFramework(input: DeleteFrameworkInput!): DeleteFrameworkPayload!
generateFrameworkStateOfApplicability(
input: GenerateFrameworkStateOfApplicabilityInput!
): GenerateFrameworkStateOfApplicabilityPayload!
exportFramework(input: ExportFrameworkInput!): ExportFrameworkPayload!
# Control mutations
createControl(input: CreateControlInput!): CreateControlPayload!
@@ -3576,14 +3563,6 @@ type Mutation {
}
# Input Types
input GenerateFrameworkStateOfApplicabilityInput {
frameworkId: ID!
}
type GenerateFrameworkStateOfApplicabilityPayload {
data: String!
}
input UpdateOrganizationContextInput {
organizationId: ID!
summary: String @goField(omittable: true)
@@ -4146,8 +4125,6 @@ input CreateControlInput {
sectionTitle: String!
name: String!
description: String
status: ControlStatus!
exclusionJustification: String
bestPractice: Boolean!
}
@@ -4156,8 +4133,6 @@ input UpdateControlInput {
sectionTitle: String
name: String
description: String @goField(omittable: true)
status: ControlStatus
exclusionJustification: String
bestPractice: Boolean
}

View File

@@ -258,26 +258,24 @@ type ComplexityRoot struct {
}
Control struct {
Audits func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditOrderBy) int
BestPractice func(childComplexity int) int
Contractual func(childComplexity int) int
CreatedAt 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
ExclusionJustification func(childComplexity int) int
Framework func(childComplexity int) int
ID 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
Name func(childComplexity int) int
Obligations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) int
Organization func(childComplexity int) int
Permission func(childComplexity int, action string) int
Regulatory func(childComplexity int) int
RiskAssessment func(childComplexity int) int
SectionTitle func(childComplexity int) int
Snapshots func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SnapshotOrderBy) int
Status func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Audits func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditOrderBy) int
BestPractice func(childComplexity int) int
Contractual func(childComplexity int) int
CreatedAt 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
Framework func(childComplexity int) int
ID 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
Name func(childComplexity int) int
Obligations func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) int
Organization func(childComplexity int) int
Permission func(childComplexity int, action string) int
Regulatory func(childComplexity int) int
RiskAssessment func(childComplexity int) int
SectionTitle func(childComplexity int) int
Snapshots func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.SnapshotOrderBy) int
UpdatedAt func(childComplexity int) int
}
ControlConnection struct {
@@ -873,10 +871,6 @@ type ComplexityRoot struct {
Changelog func(childComplexity int) int
}
GenerateFrameworkStateOfApplicabilityPayload struct {
Data func(childComplexity int) int
}
GetTrustCenterFilePayload struct {
TrustCenterFile func(childComplexity int) int
}
@@ -1034,7 +1028,6 @@ type ComplexityRoot struct {
ExportStateOfApplicabilityPDF func(childComplexity int, input types.ExportStateOfApplicabilityPDFInput) int
ExportTransferImpactAssessmentsPDF func(childComplexity int, input types.ExportTransferImpactAssessmentsPDFInput) int
GenerateDocumentChangelog func(childComplexity int, input types.GenerateDocumentChangelogInput) int
GenerateFrameworkStateOfApplicability func(childComplexity int, input types.GenerateFrameworkStateOfApplicabilityInput) int
GetTrustCenterFile func(childComplexity int, input types.GetTrustCenterFileInput) int
ImportFramework func(childComplexity int, input types.ImportFrameworkInput) int
ImportMeasure func(childComplexity int, input types.ImportMeasureInput) int
@@ -2164,7 +2157,6 @@ type MutationResolver interface {
UpdateFramework(ctx context.Context, input types.UpdateFrameworkInput) (*types.UpdateFrameworkPayload, error)
ImportFramework(ctx context.Context, input types.ImportFrameworkInput) (*types.ImportFrameworkPayload, error)
DeleteFramework(ctx context.Context, input types.DeleteFrameworkInput) (*types.DeleteFrameworkPayload, error)
GenerateFrameworkStateOfApplicability(ctx context.Context, input types.GenerateFrameworkStateOfApplicabilityInput) (*types.GenerateFrameworkStateOfApplicabilityPayload, error)
ExportFramework(ctx context.Context, input types.ExportFrameworkInput) (*types.ExportFrameworkPayload, error)
CreateControl(ctx context.Context, input types.CreateControlInput) (*types.CreateControlPayload, error)
UpdateControl(ctx context.Context, input types.UpdateControlInput) (*types.UpdateControlPayload, error)
@@ -3104,12 +3096,6 @@ 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
case "Control.exclusionJustification":
if e.complexity.Control.ExclusionJustification == nil {
break
}
return e.complexity.Control.ExclusionJustification(childComplexity), true
case "Control.framework":
if e.complexity.Control.Framework == nil {
break
@@ -3196,12 +3182,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.complexity.Control.Snapshots(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.SnapshotOrderBy)), true
case "Control.status":
if e.complexity.Control.Status == nil {
break
}
return e.complexity.Control.Status(childComplexity), true
case "Control.updatedAt":
if e.complexity.Control.UpdatedAt == nil {
break
@@ -4887,13 +4867,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.GenerateDocumentChangelogPayload.Changelog(childComplexity), true
case "GenerateFrameworkStateOfApplicabilityPayload.data":
if e.complexity.GenerateFrameworkStateOfApplicabilityPayload.Data == nil {
break
}
return e.complexity.GenerateFrameworkStateOfApplicabilityPayload.Data(childComplexity), true
case "GetTrustCenterFilePayload.trustCenterFile":
if e.complexity.GetTrustCenterFilePayload.TrustCenterFile == nil {
break
@@ -6182,17 +6155,6 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
}
return e.complexity.Mutation.GenerateDocumentChangelog(childComplexity, args["input"].(types.GenerateDocumentChangelogInput)), true
case "Mutation.generateFrameworkStateOfApplicability":
if e.complexity.Mutation.GenerateFrameworkStateOfApplicability == nil {
break
}
args, err := ec.field_Mutation_generateFrameworkStateOfApplicability_args(ctx, rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Mutation.GenerateFrameworkStateOfApplicability(childComplexity, args["input"].(types.GenerateFrameworkStateOfApplicabilityInput)), true
case "Mutation.getTrustCenterFile":
if e.complexity.Mutation.GetTrustCenterFile == nil {
break
@@ -10419,7 +10381,6 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputExportTransferImpactAssessmentsPDFInput,
ec.unmarshalInputFrameworkOrder,
ec.unmarshalInputGenerateDocumentChangelogInput,
ec.unmarshalInputGenerateFrameworkStateOfApplicabilityInput,
ec.unmarshalInputGetTrustCenterFileInput,
ec.unmarshalInputImportFrameworkInput,
ec.unmarshalInputImportMeasureInput,
@@ -11627,14 +11588,6 @@ enum DataClassification
)
}
enum ControlStatus
@goModel(model: "go.probo.inc/probo/pkg/coredata.ControlStatus") {
INCLUDED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ControlStatusIncluded")
EXCLUDED
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ControlStatusExcluded")
}
enum AuditOrderField
@goModel(model: "go.probo.inc/probo/pkg/coredata.AuditOrderField") {
CREATED_AT
@@ -12648,8 +12601,6 @@ type Control implements Node {
sectionTitle: String!
name: String!
description: String
status: ControlStatus!
exclusionJustification: String
bestPractice: Boolean!
regulatory: Boolean! @goField(forceResolver: true)
contractual: Boolean! @goField(forceResolver: true)
@@ -13884,9 +13835,6 @@ type Mutation {
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
importFramework(input: ImportFrameworkInput!): ImportFrameworkPayload!
deleteFramework(input: DeleteFrameworkInput!): DeleteFrameworkPayload!
generateFrameworkStateOfApplicability(
input: GenerateFrameworkStateOfApplicabilityInput!
): GenerateFrameworkStateOfApplicabilityPayload!
exportFramework(input: ExportFrameworkInput!): ExportFrameworkPayload!
# Control mutations
createControl(input: CreateControlInput!): CreateControlPayload!
@@ -14173,14 +14121,6 @@ type Mutation {
}
# Input Types
input GenerateFrameworkStateOfApplicabilityInput {
frameworkId: ID!
}
type GenerateFrameworkStateOfApplicabilityPayload {
data: String!
}
input UpdateOrganizationContextInput {
organizationId: ID!
summary: String @goField(omittable: true)
@@ -14743,8 +14683,6 @@ input CreateControlInput {
sectionTitle: String!
name: String!
description: String
status: ControlStatus!
exclusionJustification: String
bestPractice: Boolean!
}
@@ -14753,8 +14691,6 @@ input UpdateControlInput {
sectionTitle: String
name: String
description: String @goField(omittable: true)
status: ControlStatus
exclusionJustification: String
bestPractice: Boolean
}
@@ -17835,17 +17771,6 @@ func (ec *executionContext) field_Mutation_generateDocumentChangelog_args(ctx co
return args, nil
}
func (ec *executionContext) field_Mutation_generateFrameworkStateOfApplicability_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", ec.unmarshalNGenerateFrameworkStateOfApplicabilityInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐGenerateFrameworkStateOfApplicabilityInput)
if err != nil {
return nil, err
}
args["input"] = arg0
return args, nil
}
func (ec *executionContext) field_Mutation_getTrustCenterFile_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -20211,10 +20136,6 @@ func (ec *executionContext) fieldContext_ApplicabilityStatement_control(_ contex
return ec.fieldContext_Control_name(ctx, field)
case "description":
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 "bestPractice":
return ec.fieldContext_Control_bestPractice(ctx, field)
case "regulatory":
@@ -23225,64 +23146,6 @@ func (ec *executionContext) fieldContext_Control_description(_ context.Context,
return fc, nil
}
func (ec *executionContext) _Control_status(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_Control_status,
func(ctx context.Context) (any, error) {
return obj.Status, nil
},
nil,
ec.marshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus,
true,
true,
)
}
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) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_Control_exclusionJustification,
func(ctx context.Context) (any, error) {
return obj.ExclusionJustification, nil
},
nil,
ec.marshalOString2ᚖstring,
true,
false,
)
}
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_bestPractice(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -23960,10 +23823,6 @@ 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 "status":
return ec.fieldContext_Control_status(ctx, field)
case "exclusionJustification":
return ec.fieldContext_Control_exclusionJustification(ctx, field)
case "bestPractice":
return ec.fieldContext_Control_bestPractice(ctx, field)
case "regulatory":
@@ -32598,35 +32457,6 @@ func (ec *executionContext) fieldContext_GenerateDocumentChangelogPayload_change
return fc, nil
}
func (ec *executionContext) _GenerateFrameworkStateOfApplicabilityPayload_data(ctx context.Context, field graphql.CollectedField, obj *types.GenerateFrameworkStateOfApplicabilityPayload) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_GenerateFrameworkStateOfApplicabilityPayload_data,
func(ctx context.Context) (any, error) {
return obj.Data, nil
},
nil,
ec.marshalNString2string,
true,
true,
)
}
func (ec *executionContext) fieldContext_GenerateFrameworkStateOfApplicabilityPayload_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "GenerateFrameworkStateOfApplicabilityPayload",
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) _GetTrustCenterFilePayload_trustCenterFile(ctx context.Context, field graphql.CollectedField, obj *types.GetTrustCenterFilePayload) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -35189,51 +35019,6 @@ func (ec *executionContext) fieldContext_Mutation_deleteFramework(ctx context.Co
return fc, nil
}
func (ec *executionContext) _Mutation_generateFrameworkStateOfApplicability(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
ec.OperationContext,
field,
ec.fieldContext_Mutation_generateFrameworkStateOfApplicability,
func(ctx context.Context) (any, error) {
fc := graphql.GetFieldContext(ctx)
return ec.resolvers.Mutation().GenerateFrameworkStateOfApplicability(ctx, fc.Args["input"].(types.GenerateFrameworkStateOfApplicabilityInput))
},
nil,
ec.marshalNGenerateFrameworkStateOfApplicabilityPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐGenerateFrameworkStateOfApplicabilityPayload,
true,
true,
)
}
func (ec *executionContext) fieldContext_Mutation_generateFrameworkStateOfApplicability(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 "data":
return ec.fieldContext_GenerateFrameworkStateOfApplicabilityPayload_data(ctx, field)
}
return nil, fmt.Errorf("no field named %q was found under type GenerateFrameworkStateOfApplicabilityPayload", 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_generateFrameworkStateOfApplicability_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
ec.Error(ctx, err)
return fc, err
}
return fc, nil
}
func (ec *executionContext) _Mutation_exportFramework(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
return graphql.ResolveField(
ctx,
@@ -53944,10 +53729,6 @@ func (ec *executionContext) fieldContext_UpdateControlPayload_control(_ context.
return ec.fieldContext_Control_name(ctx, field)
case "description":
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 "bestPractice":
return ec.fieldContext_Control_bestPractice(ctx, field)
case "regulatory":
@@ -63141,7 +62922,7 @@ func (ec *executionContext) unmarshalInputCreateControlInput(ctx context.Context
asMap[k] = v
}
fieldsInOrder := [...]string{"frameworkId", "sectionTitle", "name", "description", "status", "exclusionJustification", "bestPractice"}
fieldsInOrder := [...]string{"frameworkId", "sectionTitle", "name", "description", "bestPractice"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -63176,20 +62957,6 @@ func (ec *executionContext) unmarshalInputCreateControlInput(ctx context.Context
return it, err
}
it.Description = data
case "status":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status"))
data, err := ec.unmarshalNControlStatus2goᚗproboᚗincᚋ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
case "bestPractice":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("bestPractice"))
data, err := ec.unmarshalNBoolean2bool(ctx, v)
@@ -66924,33 +66691,6 @@ func (ec *executionContext) unmarshalInputGenerateDocumentChangelogInput(ctx con
return it, nil
}
func (ec *executionContext) unmarshalInputGenerateFrameworkStateOfApplicabilityInput(ctx context.Context, obj any) (types.GenerateFrameworkStateOfApplicabilityInput, error) {
var it types.GenerateFrameworkStateOfApplicabilityInput
asMap := map[string]any{}
for k, v := range obj.(map[string]any) {
asMap[k] = v
}
fieldsInOrder := [...]string{"frameworkId"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
case "frameworkId":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("frameworkId"))
data, err := ec.unmarshalNID2goᚗproboᚗincᚋproboᚋpkgᚋgidᚐGID(ctx, v)
if err != nil {
return it, err
}
it.FrameworkID = data
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputGetTrustCenterFileInput(ctx context.Context, obj any) (types.GetTrustCenterFileInput, error) {
var it types.GetTrustCenterFileInput
asMap := map[string]any{}
@@ -68231,7 +67971,7 @@ func (ec *executionContext) unmarshalInputUpdateControlInput(ctx context.Context
asMap[k] = v
}
fieldsInOrder := [...]string{"id", "sectionTitle", "name", "description", "status", "exclusionJustification", "bestPractice"}
fieldsInOrder := [...]string{"id", "sectionTitle", "name", "description", "bestPractice"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -68266,20 +68006,6 @@ func (ec *executionContext) unmarshalInputUpdateControlInput(ctx context.Context
return it, err
}
it.Description = graphql.OmittableOf(data)
case "status":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("status"))
data, err := ec.unmarshalOControlStatus2ᚖgoᚗproboᚗincᚋ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
case "bestPractice":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("bestPractice"))
data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v)
@@ -72533,13 +72259,6 @@ func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet,
}
case "description":
out.Values[i] = ec._Control_description(ctx, field, obj)
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 "bestPractice":
out.Values[i] = ec._Control_bestPractice(ctx, field, obj)
if out.Values[i] == graphql.Null {
@@ -79243,45 +78962,6 @@ func (ec *executionContext) _GenerateDocumentChangelogPayload(ctx context.Contex
return out
}
var generateFrameworkStateOfApplicabilityPayloadImplementors = []string{"GenerateFrameworkStateOfApplicabilityPayload"}
func (ec *executionContext) _GenerateFrameworkStateOfApplicabilityPayload(ctx context.Context, sel ast.SelectionSet, obj *types.GenerateFrameworkStateOfApplicabilityPayload) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, generateFrameworkStateOfApplicabilityPayloadImplementors)
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("GenerateFrameworkStateOfApplicabilityPayload")
case "data":
out.Values[i] = ec._GenerateFrameworkStateOfApplicabilityPayload_data(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 getTrustCenterFilePayloadImplementors = []string{"GetTrustCenterFilePayload"}
func (ec *executionContext) _GetTrustCenterFilePayload(ctx context.Context, sel ast.SelectionSet, obj *types.GetTrustCenterFilePayload) graphql.Marshaler {
@@ -80277,13 +79957,6 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "generateFrameworkStateOfApplicability":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_generateFrameworkStateOfApplicability(ctx, field)
})
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "exportFramework":
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
return ec._Mutation_exportFramework(ctx, field)
@@ -93049,34 +92722,6 @@ var (
}
)
func (ec *executionContext) unmarshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx context.Context, v any) (coredata.ControlStatus, error) {
tmp, err := graphql.UnmarshalString(v)
res := unmarshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus[tmp]
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus(ctx context.Context, sel ast.SelectionSet, v coredata.ControlStatus) graphql.Marshaler {
_ = sel
res := graphql.MarshalString(marshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus[v])
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
}
return res
}
var (
unmarshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus = map[string]coredata.ControlStatus{
"INCLUDED": coredata.ControlStatusIncluded,
"EXCLUDED": coredata.ControlStatusExcluded,
}
marshalNControlStatus2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus = map[coredata.ControlStatus]string{
coredata.ControlStatusIncluded: "INCLUDED",
coredata.ControlStatusExcluded: "EXCLUDED",
}
)
func (ec *executionContext) unmarshalNCountryCode2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐCountryCode(ctx context.Context, v any) (coredata.CountryCode, error) {
tmp, err := graphql.UnmarshalString(v)
res := unmarshalNCountryCode2goᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐCountryCode[tmp]
@@ -96474,25 +96119,6 @@ func (ec *executionContext) marshalNGenerateDocumentChangelogPayload2ᚖgoᚗpro
return ec._GenerateDocumentChangelogPayload(ctx, sel, v)
}
func (ec *executionContext) unmarshalNGenerateFrameworkStateOfApplicabilityInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐGenerateFrameworkStateOfApplicabilityInput(ctx context.Context, v any) (types.GenerateFrameworkStateOfApplicabilityInput, error) {
res, err := ec.unmarshalInputGenerateFrameworkStateOfApplicabilityInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNGenerateFrameworkStateOfApplicabilityPayload2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐGenerateFrameworkStateOfApplicabilityPayload(ctx context.Context, sel ast.SelectionSet, v types.GenerateFrameworkStateOfApplicabilityPayload) graphql.Marshaler {
return ec._GenerateFrameworkStateOfApplicabilityPayload(ctx, sel, &v)
}
func (ec *executionContext) marshalNGenerateFrameworkStateOfApplicabilityPayload2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐGenerateFrameworkStateOfApplicabilityPayload(ctx context.Context, sel ast.SelectionSet, v *types.GenerateFrameworkStateOfApplicabilityPayload) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._GenerateFrameworkStateOfApplicabilityPayload(ctx, sel, v)
}
func (ec *executionContext) unmarshalNGetTrustCenterFileInput2goᚗproboᚗincᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐGetTrustCenterFileInput(ctx context.Context, v any) (types.GetTrustCenterFileInput, error) {
res, err := ec.unmarshalInputGetTrustCenterFileInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err)
@@ -101595,36 +101221,6 @@ func (ec *executionContext) unmarshalOControlOrder2ᚖgoᚗproboᚗincᚋprobo
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalOControlStatus2ᚖgoᚗproboᚗincᚋ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ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus[tmp]
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalOControlStatus2ᚖgoᚗproboᚗincᚋ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ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus[*v])
return res
}
var (
unmarshalOControlStatus2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus = map[string]coredata.ControlStatus{
"INCLUDED": coredata.ControlStatusIncluded,
"EXCLUDED": coredata.ControlStatusExcluded,
}
marshalOControlStatus2ᚖgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐControlStatus = map[coredata.ControlStatus]string{
coredata.ControlStatusIncluded: "INCLUDED",
coredata.ControlStatusExcluded: "EXCLUDED",
}
)
func (ec *executionContext) unmarshalOCountryCode2ᚕgoᚗproboᚗincᚋproboᚋpkgᚋcoredataᚐCountryCodeᚄ(ctx context.Context, v any) ([]coredata.CountryCode, error) {
if v == nil {
return nil, nil

View File

@@ -57,13 +57,11 @@ func NewControl(control *coredata.Control) *Control {
Framework: &Framework{
ID: control.FrameworkID,
},
SectionTitle: control.SectionTitle,
Name: control.Name,
Description: control.Description,
Status: control.Status,
ExclusionJustification: control.ExclusionJustification,
BestPractice: control.BestPractice,
CreatedAt: control.CreatedAt,
UpdatedAt: control.UpdatedAt,
SectionTitle: control.SectionTitle,
Name: control.Name,
Description: control.Description,
BestPractice: control.BestPractice,
CreatedAt: control.CreatedAt,
UpdatedAt: control.UpdatedAt,
}
}

View File

@@ -179,26 +179,24 @@ type ContinualImprovementFilter struct {
}
type Control struct {
ID gid.GID `json:"id"`
Organization *Organization `json:"organization,omitempty"`
SectionTitle string `json:"sectionTitle"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Status coredata.ControlStatus `json:"status"`
ExclusionJustification *string `json:"exclusionJustification,omitempty"`
BestPractice bool `json:"bestPractice"`
Regulatory bool `json:"regulatory"`
Contractual bool `json:"contractual"`
RiskAssessment bool `json:"riskAssessment"`
Framework *Framework `json:"framework"`
Measures *MeasureConnection `json:"measures"`
Documents *DocumentConnection `json:"documents"`
Audits *AuditConnection `json:"audits"`
Obligations *ObligationConnection `json:"obligations"`
Snapshots *SnapshotConnection `json:"snapshots"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Permission bool `json:"permission"`
ID gid.GID `json:"id"`
Organization *Organization `json:"organization,omitempty"`
SectionTitle string `json:"sectionTitle"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
BestPractice bool `json:"bestPractice"`
Regulatory bool `json:"regulatory"`
Contractual bool `json:"contractual"`
RiskAssessment bool `json:"riskAssessment"`
Framework *Framework `json:"framework"`
Measures *MeasureConnection `json:"measures"`
Documents *DocumentConnection `json:"documents"`
Audits *AuditConnection `json:"audits"`
Obligations *ObligationConnection `json:"obligations"`
Snapshots *SnapshotConnection `json:"snapshots"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Permission bool `json:"permission"`
}
func (Control) IsNode() {}
@@ -288,13 +286,11 @@ type CreateControlDocumentMappingPayload struct {
}
type CreateControlInput struct {
FrameworkID gid.GID `json:"frameworkId"`
SectionTitle string `json:"sectionTitle"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
Status coredata.ControlStatus `json:"status"`
ExclusionJustification *string `json:"exclusionJustification,omitempty"`
BestPractice bool `json:"bestPractice"`
FrameworkID gid.GID `json:"frameworkId"`
SectionTitle string `json:"sectionTitle"`
Name string `json:"name"`
Description *string `json:"description,omitempty"`
BestPractice bool `json:"bestPractice"`
}
type CreateControlMeasureMappingInput struct {
@@ -1334,14 +1330,6 @@ type GenerateDocumentChangelogPayload struct {
Changelog string `json:"changelog"`
}
type GenerateFrameworkStateOfApplicabilityInput struct {
FrameworkID gid.GID `json:"frameworkId"`
}
type GenerateFrameworkStateOfApplicabilityPayload struct {
Data string `json:"data"`
}
type GetTrustCenterFileInput struct {
ID gid.GID `json:"id"`
}
@@ -1994,13 +1982,11 @@ type UpdateContinualImprovementPayload struct {
}
type UpdateControlInput struct {
ID gid.GID `json:"id"`
SectionTitle *string `json:"sectionTitle,omitempty"`
Name *string `json:"name,omitempty"`
Description graphql.Omittable[*string] `json:"description,omitempty"`
Status *coredata.ControlStatus `json:"status,omitempty"`
ExclusionJustification *string `json:"exclusionJustification,omitempty"`
BestPractice *bool `json:"bestPractice,omitempty"`
ID gid.GID `json:"id"`
SectionTitle *string `json:"sectionTitle,omitempty"`
Name *string `json:"name,omitempty"`
Description graphql.Omittable[*string] `json:"description,omitempty"`
BestPractice *bool `json:"bestPractice,omitempty"`
}
type UpdateControlPayload struct {

View File

@@ -2511,28 +2511,6 @@ func (r *mutationResolver) DeleteFramework(ctx context.Context, input types.Dele
}, nil
}
// GenerateFrameworkStateOfApplicability is the resolver for the generateFrameworkStateOfApplicability field.
func (r *mutationResolver) GenerateFrameworkStateOfApplicability(ctx context.Context, input types.GenerateFrameworkStateOfApplicabilityInput) (*types.GenerateFrameworkStateOfApplicabilityPayload, error) {
if err := r.authorize(ctx, input.FrameworkID, probo.ActionFrameworkStateOfApplicabilityGenerate); err != nil {
return nil, err
}
prb := r.ProboService(ctx, input.FrameworkID.TenantID())
soa, err := prb.Frameworks.StateOfApplicability(ctx, input.FrameworkID)
if err != nil {
// TODO no panic use gqlutils.InternalError
panic(fmt.Errorf("cannot generate framework SOA: %w", err))
}
return &types.GenerateFrameworkStateOfApplicabilityPayload{
Data: fmt.Sprintf(
"data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64,%s",
base64.StdEncoding.EncodeToString(soa),
),
}, nil
}
// ExportFramework is the resolver for the exportFramework field.
func (r *mutationResolver) ExportFramework(ctx context.Context, input types.ExportFrameworkInput) (*types.ExportFrameworkPayload, error) {
if err := r.authorize(ctx, input.FrameworkID, probo.ActionFrameworkExport); err != nil {
@@ -2569,13 +2547,11 @@ func (r *mutationResolver) CreateControl(ctx context.Context, input types.Create
control, err := prb.Controls.Create(
ctx,
probo.CreateControlRequest{
FrameworkID: input.FrameworkID,
Name: input.Name,
Description: input.Description,
SectionTitle: input.SectionTitle,
Status: &input.Status,
ExclusionJustification: input.ExclusionJustification,
BestPractice: input.BestPractice,
FrameworkID: input.FrameworkID,
Name: input.Name,
Description: input.Description,
SectionTitle: input.SectionTitle,
BestPractice: input.BestPractice,
},
)
if err != nil {
@@ -2603,13 +2579,11 @@ func (r *mutationResolver) UpdateControl(ctx context.Context, input types.Update
control, err := prb.Controls.Update(
ctx,
probo.UpdateControlRequest{
ID: input.ID,
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
SectionTitle: input.SectionTitle,
Status: input.Status,
ExclusionJustification: input.ExclusionJustification,
BestPractice: input.BestPractice,
ID: input.ID,
Name: input.Name,
Description: gqlutils.UnwrapOmittable(input.Description),
SectionTitle: input.SectionTitle,
BestPractice: input.BestPractice,
},
)

View File

@@ -1182,12 +1182,10 @@ func (r *Resolver) AddControlTool(ctx context.Context, req *mcp.CallToolRequest,
control, err := svc.Controls.Create(
ctx,
probo.CreateControlRequest{
FrameworkID: input.FrameworkID,
Name: input.Name,
Description: input.Description,
SectionTitle: input.SectionTitle,
Status: input.Status,
ExclusionJustification: input.ExclusionJustification,
FrameworkID: input.FrameworkID,
Name: input.Name,
Description: input.Description,
SectionTitle: input.SectionTitle,
},
)
if err != nil {
@@ -1207,12 +1205,10 @@ func (r *Resolver) UpdateControlTool(ctx context.Context, req *mcp.CallToolReque
control, err := svc.Controls.Update(
ctx,
probo.UpdateControlRequest{
ID: input.ID,
Name: input.Name,
Description: UnwrapOmittable(input.Description),
SectionTitle: input.SectionTitle,
Status: input.Status,
ExclusionJustification: input.ExclusionJustification,
ID: input.ID,
Name: input.Name,
Description: UnwrapOmittable(input.Description),
SectionTitle: input.SectionTitle,
},
)
if err != nil {

View File

@@ -2758,13 +2758,6 @@ components:
audit:
$ref: "#/components/schemas/Audit"
ControlStatus:
type: string
enum:
- INCLUDED
- EXCLUDED
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlStatus
ControlOrderField:
type: string
enum:
@@ -2793,7 +2786,6 @@ components:
- framework_id
- section_title
- name
- status
- created_at
- updated_at
properties:
@@ -2817,14 +2809,6 @@ components:
- string
- "null"
description: Control description
status:
$ref: "#/components/schemas/ControlStatus"
description: Control status
exclusion_justification:
type:
- string
- "null"
description: Exclusion justification
created_at:
type: string
format: date-time
@@ -2914,12 +2898,6 @@ components:
description:
type: string
description: Control description
status:
$ref: "#/components/schemas/ControlStatus"
description: Control status
exclusion_justification:
type: string
description: Exclusion justification
AddControlOutput:
type: object
@@ -2947,16 +2925,6 @@ components:
type: ["string", "null"]
description: Control description
go.probo.inc/mcpgen/omittable: true
status:
anyOf:
- $ref: "#/components/schemas/ControlStatus"
description: Control status
- type: "null"
description: No status
description: Control status
exclusion_justification:
type: string
description: Exclusion justification
UpdateControlOutput:
type: object

View File

@@ -20,16 +20,14 @@ import (
func NewControl(c *coredata.Control) *Control {
return &Control{
ID: c.ID,
OrganizationID: c.OrganizationID,
SectionTitle: c.SectionTitle,
FrameworkID: c.FrameworkID,
Name: c.Name,
Description: c.Description,
Status: c.Status,
ExclusionJustification: c.ExclusionJustification,
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
ID: c.ID,
OrganizationID: c.OrganizationID,
SectionTitle: c.SectionTitle,
FrameworkID: c.FrameworkID,
Name: c.Name,
Description: c.Description,
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
}
}

View File

@@ -21,8 +21,8 @@ var (
AddAuditToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"audit":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"description":"Audit name"},"organization_id":{"type":"string","format":"string"},"state":{"type":"string","enum":["NOT_STARTED","IN_PROGRESS","COMPLETED","REJECTED","OUTDATED"]},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"},"valid_from":{"description":"Valid from date","format":"date-time"},"valid_until":{"description":"Valid until date","format":"date-time"}},"required":["id","organization_id","framework_id","state","trust_center_visibility","created_at","updated_at"]}},"required":["audit"]}`)
AddContinualImprovementToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"description":{"type":"string","description":"Description"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"priority":{"description":"Priority","anyOf":[{"type":"string","enum":["LOW","MEDIUM","HIGH"]},{"type":"null","description":"No priority"}]},"reference_id":{"type":"string","description":"Reference ID"},"source":{"type":"string","description":"Source"},"status":{"description":"Status","anyOf":[{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},{"type":"null","description":"No status"}]},"target_date":{"type":"string","description":"Target date","format":"date-time"}},"required":["organization_id","reference_id","owner_id"]}`)
AddContinualImprovementToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"continual_improvement":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Description"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"reference_id":{"type":"string","description":"Reference ID"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"target_date":{"description":"Target date","format":"date-time"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","reference_id","owner_id","status","priority","created_at","updated_at"]}},"required":["continual_improvement"]}`)
AddControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"description":{"type":"string","description":"Control description"},"exclusion_justification":{"type":"string","description":"Exclusion justification"},"framework_id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"status":{"type":"string","enum":["INCLUDED","EXCLUDED"]}},"required":["organization_id","framework_id","section_title","name"]}`)
AddControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"exclusion_justification":{"description":"Exclusion justification"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"status":{"type":"string","enum":["INCLUDED","EXCLUDED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","status","created_at","updated_at"]}},"required":["control"]}`)
AddControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"description":{"type":"string","description":"Control description"},"framework_id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"}},"required":["organization_id","framework_id","section_title","name"]}`)
AddControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","created_at","updated_at"]}},"required":["control"]}`)
AddDatumToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"data_classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"name":{"type":"string","description":"Datum name"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"vendor_ids":{"type":"array","items":{"type":"string","format":"string"},"description":"Vendor IDs"}},"required":["organization_id","name","data_classification","owner_id"]}`)
AddDatumToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"datum":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"data_classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Datum name"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","data_classification","owner_id","created_at","updated_at"]}},"required":["datum"]}`)
AddDocumentToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"approver_ids":{"type":"array","items":{"type":"string","format":"string"},"description":"Approver IDs"},"classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"content":{"type":"string","description":"Document content"},"document_type":{"type":"string","enum":["OTHER","ISMS","POLICY","PROCEDURE"]},"organization_id":{"type":"string","format":"string"},"title":{"type":"string","description":"Document title"},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]}},"required":["organization_id","title","content","approver_ids","classification","document_type"]}`)
@@ -54,11 +54,11 @@ var (
DeleteDraftDocumentVersionToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"document_version_id":{"type":"string","format":"string"}},"required":["document_version_id"]}`)
DeleteDraftDocumentVersionToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"deleted_document_version_id":{"type":"string","format":"string"}},"required":["deleted_document_version_id"]}`)
DeleteMeetingToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
DeleteTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
DeleteTaskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"deleted_task_id":{"type":"string","format":"string"}},"required":["deleted_task_id"]}`)
DeleteMeetingToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"deleted_meeting_id":{"type":"string","format":"string"}},"required":["deleted_meeting_id"]}`)
DeleteRiskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
DeleteRiskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"deleted_risk_id":{"type":"string","format":"string"}},"required":["deleted_risk_id"]}`)
DeleteTaskToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
DeleteTaskToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"deleted_task_id":{"type":"string","format":"string"}},"required":["deleted_task_id"]}`)
GetAssetToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
GetAssetToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"asset":{"type":"object","properties":{"amount":{"type":"integer","description":"Asset amount"},"asset_type":{"type":"string","enum":["PHYSICAL","VIRTUAL"]},"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"data_types_stored":{"type":"string","description":"Data types stored"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Asset name"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","amount","owner_id","asset_type","data_types_stored","created_at","updated_at"]}},"required":["asset"]}`)
GetAuditToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
@@ -66,7 +66,7 @@ var (
GetContinualImprovementToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
GetContinualImprovementToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"continual_improvement":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Description"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"reference_id":{"type":"string","description":"Reference ID"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"target_date":{"description":"Target date","format":"date-time"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","reference_id","owner_id","status","priority","created_at","updated_at"]}},"required":["continual_improvement"]}`)
GetControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
GetControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"exclusion_justification":{"description":"Exclusion justification"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"status":{"type":"string","enum":["INCLUDED","EXCLUDED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","status","created_at","updated_at"]}},"required":["control"]}`)
GetControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","created_at","updated_at"]}},"required":["control"]}`)
GetDatumToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
GetDatumToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"datum":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"data_classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Datum name"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","data_classification","owner_id","created_at","updated_at"]}},"required":["datum"]}`)
GetDocumentToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"id":{"type":"string","format":"string"}},"required":["id"]}`)
@@ -108,7 +108,7 @@ var (
ListContinualImprovementsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","REFERENCE_ID","TARGET_DATE","STATUS","PRIORITY"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`)
ListContinualImprovementsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"continual_improvements":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Description"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"reference_id":{"type":"string","description":"Reference ID"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"target_date":{"description":"Target date","format":"date-time"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","reference_id","owner_id","status","priority","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["continual_improvements"]}`)
ListControlsToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"query":{"type":"string","description":"Search query"}}},"framework_id":{"type":"string","format":"string"},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","SECTION_TITLE"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`)
ListControlsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"controls":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"exclusion_justification":{"description":"Exclusion justification"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"status":{"type":"string","enum":["INCLUDED","EXCLUDED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","status","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["controls"]}`)
ListControlsToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"controls":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["controls"]}`)
ListDataToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"snapshot_id":{"type":"string","format":"string"}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","NAME","DATA_CLASSIFICATION"]}},"required":["field","direction"]},"organization_id":{"type":"string","format":"string"},"size":{"type":"integer","description":"Page size"}},"required":["organization_id"]}`)
ListDataToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"data_classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Datum name"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","data_classification","owner_id","created_at","updated_at"]}},"next_cursor":{"type":"string","format":"string"}},"required":["data"]}`)
ListDocumentVersionSignaturesToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"cursor":{"type":"string","format":"string"},"document_version_id":{"type":"string","format":"string"},"filter":{"type":"object","properties":{"active_contract":{"type":"boolean","description":"Signatory contract status"},"states":{"type":"array","items":{"type":"string","enum":["REQUESTED","SIGNED"]},"description":"Signature states"}}},"order_by":{"type":"object","properties":{"direction":{"type":"string","enum":["ASC","DESC"]},"field":{"type":"string","enum":["CREATED_AT","SIGNED_AT"]}},"required":["field","direction"]},"size":{"type":"integer","description":"Page size"}},"required":["document_version_id"]}`)
@@ -163,8 +163,8 @@ var (
UpdateAuditToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"audit":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"description":"Audit name"},"organization_id":{"type":"string","format":"string"},"state":{"type":"string","enum":["NOT_STARTED","IN_PROGRESS","COMPLETED","REJECTED","OUTDATED"]},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"},"valid_from":{"description":"Valid from date","format":"date-time"},"valid_until":{"description":"Valid until date","format":"date-time"}},"required":["id","organization_id","framework_id","state","trust_center_visibility","created_at","updated_at"]}},"required":["audit"]}`)
UpdateContinualImprovementToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"description":{"description":"Description"},"id":{"type":"string","format":"string"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"priority":{"description":"Priority","anyOf":[{"type":"string","enum":["LOW","MEDIUM","HIGH"]},{"type":"null","description":"No priority"}]},"reference_id":{"type":"string","description":"Reference ID"},"source":{"description":"Source"},"status":{"description":"Status","anyOf":[{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},{"type":"null","description":"No status"}]},"target_date":{"description":"Target date","format":"date-time"}},"required":["id"]}`)
UpdateContinualImprovementToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"continual_improvement":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Description"},"id":{"type":"string","format":"string"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"priority":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"reference_id":{"type":"string","description":"Reference ID"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"source":{"description":"Source"},"source_id":{"description":"Source ID"},"status":{"type":"string","enum":["OPEN","IN_PROGRESS","CLOSED"]},"target_date":{"description":"Target date","format":"date-time"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","reference_id","owner_id","status","priority","created_at","updated_at"]}},"required":["continual_improvement"]}`)
UpdateControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"description":{"description":"Control description"},"exclusion_justification":{"type":"string","description":"Exclusion justification"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"section_title":{"type":"string","description":"Section title"},"status":{"description":"Control status","anyOf":[{"type":"string","enum":["INCLUDED","EXCLUDED"]},{"type":"null","description":"No status"}]}},"required":["id"]}`)
UpdateControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"exclusion_justification":{"description":"Exclusion justification"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"status":{"type":"string","enum":["INCLUDED","EXCLUDED"]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","status","created_at","updated_at"]}},"required":["control"]}`)
UpdateControlToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"description":{"description":"Control description"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"section_title":{"type":"string","description":"Section title"}},"required":["id"]}`)
UpdateControlToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"control":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"description":{"description":"Control description"},"framework_id":{"type":"string","format":"string"},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Control name"},"organization_id":{"type":"string","format":"string"},"section_title":{"type":"string","description":"Section title"},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","framework_id","section_title","name","created_at","updated_at"]}},"required":["control"]}`)
UpdateDatumToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"data_classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Datum name"},"owner_id":{"description":"Owner ID","anyOf":[{"type":"string","format":"string"},{"type":"null"}]},"vendor_ids":{"type":"array","items":{"type":"string","format":"string"},"description":"Vendor IDs"}},"required":["id"]}`)
UpdateDatumToolOutputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"datum":{"type":"object","properties":{"created_at":{"type":"string","description":"Creation timestamp","format":"date-time"},"data_classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"id":{"type":"string","format":"string"},"name":{"type":"string","description":"Datum name"},"organization_id":{"type":"string","format":"string"},"owner_id":{"type":"string","format":"string"},"snapshot_id":{"description":"Snapshot ID","anyOf":[{"type":"string","format":"string"},{"type":"null","description":"No snapshot"}]},"updated_at":{"type":"string","description":"Update timestamp","format":"date-time"}},"required":["id","organization_id","name","data_classification","owner_id","created_at","updated_at"]}},"required":["datum"]}`)
UpdateDocumentToolInputSchema = mcp.MustUnmarshalSchema(`{"type":"object","properties":{"approver_ids":{"type":"array","items":{"type":"string","format":"string"},"description":"Approver IDs"},"classification":{"type":"string","enum":["PUBLIC","INTERNAL","CONFIDENTIAL","SECRET"]},"document_type":{"type":"string","enum":["OTHER","ISMS","POLICY","PROCEDURE"]},"id":{"type":"string","format":"string"},"title":{"type":"string","description":"Document title"},"trust_center_visibility":{"type":"string","enum":["NONE","PRIVATE","PUBLIC"]}},"required":["id"]}`)
@@ -564,8 +564,6 @@ type AddContinualImprovementOutput struct {
type AddControlInput struct {
// Control description
Description *string `json:"description,omitempty"`
// Exclusion justification
ExclusionJustification *string `json:"exclusion_justification,omitempty"`
// Framework ID
FrameworkID gid.GID `json:"framework_id"`
// Control name
@@ -574,8 +572,6 @@ type AddControlInput struct {
OrganizationID gid.GID `json:"organization_id"`
// Section title
SectionTitle string `json:"section_title"`
// Control status
Status *coredata.ControlStatus `json:"status,omitempty"`
}
// AddControlOutput represents the schema
@@ -975,8 +971,6 @@ type Control struct {
CreatedAt time.Time `json:"created_at"`
// Control description
Description *string `json:"description,omitempty"`
// Exclusion justification
ExclusionJustification *string `json:"exclusion_justification,omitempty"`
// Framework ID
FrameworkID gid.GID `json:"framework_id"`
// Control ID
@@ -987,8 +981,6 @@ type Control struct {
OrganizationID gid.GID `json:"organization_id"`
// Section title
SectionTitle string `json:"section_title"`
// Control status
Status coredata.ControlStatus `json:"status"`
// Update timestamp
UpdatedAt time.Time `json:"updated_at"`
}
@@ -1088,6 +1080,18 @@ type DeleteRiskOutput struct {
DeletedRiskID gid.GID `json:"deleted_risk_id"`
}
// DeleteTaskInput represents the schema
type DeleteTaskInput struct {
// Task ID
ID gid.GID `json:"id"`
}
// DeleteTaskOutput represents the schema
type DeleteTaskOutput struct {
// Deleted task ID
DeletedTaskID gid.GID `json:"deleted_task_id"`
}
// Document represents the schema
type Document struct {
// Approver IDs
@@ -2171,18 +2175,6 @@ type UnassignTaskOutput struct {
Task *Task `json:"task"`
}
// DeleteTaskInput represents the schema
type DeleteTaskInput struct {
// Task ID
ID gid.GID `json:"id"`
}
// DeleteTaskOutput represents the schema
type DeleteTaskOutput struct {
// Deleted task ID
DeletedTaskID gid.GID `json:"deleted_task_id"`
}
// UnlinkControlAuditInput represents the schema
type UnlinkControlAuditInput struct {
// Audit ID
@@ -2304,16 +2296,12 @@ type UpdateContinualImprovementOutput struct {
type UpdateControlInput struct {
// Control description
Description mcp.Omittable[*string] `json:"description,omitempty"`
// Exclusion justification
ExclusionJustification *string `json:"exclusion_justification,omitempty"`
// Control ID
ID gid.GID `json:"id"`
// Control name
Name *string `json:"name,omitempty"`
// Section title
SectionTitle *string `json:"section_title,omitempty"`
// Control status
Status *coredata.ControlStatus `json:"status,omitempty"`
}
// UpdateControlOutput represents the schema

View File

@@ -1,56 +0,0 @@
// 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 soagen
import "github.com/xuri/excelize/v2"
type Applicability struct {
Value string
IsApplicable bool
}
func NewApplicability(value string, isApplicable bool) Applicability {
return Applicability{
Value: value,
IsApplicable: isApplicable,
}
}
func (a Applicability) String() string {
return a.Value
}
func (a Applicability) MarshalExcel() ExcelValue {
color := "FFB6C1" // Light red for not applicable
if a.IsApplicable {
color = "90EE90" // Light green for applicable
}
return ExcelValue{
Value: a.Value,
Style: &excelize.Style{
Border: []excelize.Border{
{Type: "left", Color: "000000", Style: 1},
{Type: "top", Color: "000000", Style: 1},
{Type: "bottom", Color: "000000", Style: 1},
{Type: "right", Color: "000000", Style: 1},
},
Fill: excelize.Fill{Type: "pattern", Color: []string{color}, Pattern: 1},
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
},
Validation: []string{"Yes", "No"},
Width: 12,
}
}

View File

@@ -1,28 +0,0 @@
// 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 soagen
import "github.com/xuri/excelize/v2"
type ExcelValue struct {
Value interface{}
Style *excelize.Style
Validation []string
Width float64
}
type ExcelMarshaler interface {
MarshalExcel() ExcelValue
}

View File

@@ -1,101 +0,0 @@
// 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 soagen
type FieldConfiguration struct {
Field string
Columns []string
FilterColumns []string // Specific columns that should have filters (subset of Columns)
Width []float64 // Width for each column
DefaultWidth float64 // Default width if not specified
HasFilter bool // Whether to enable auto-filter
}
var (
soaFieldConfigs = []FieldConfiguration{
{
Field: "SectionTitle",
Columns: []string{"A"},
FilterColumns: []string{},
Width: []float64{12},
DefaultWidth: 12,
HasFilter: false,
},
{
Field: "ControlName",
Columns: []string{"B"},
FilterColumns: []string{},
Width: []float64{35},
DefaultWidth: 35,
HasFilter: false,
},
{
Field: "Applicability",
Columns: []string{"C"},
FilterColumns: []string{"C"},
Width: []float64{12},
DefaultWidth: 12,
HasFilter: true,
},
{
Field: "ExclusionJustification",
Columns: []string{"D"},
FilterColumns: []string{},
Width: []float64{25},
DefaultWidth: 25,
HasFilter: false,
},
{
Field: "Regulatory",
Columns: []string{"E"},
FilterColumns: []string{},
Width: []float64{12},
DefaultWidth: 12,
HasFilter: false,
},
{
Field: "Contractual",
Columns: []string{"F"},
FilterColumns: []string{},
Width: []float64{12},
DefaultWidth: 12,
HasFilter: false,
},
{
Field: "BestPractice",
Columns: []string{"G"},
FilterColumns: []string{},
Width: []float64{12},
DefaultWidth: 12,
HasFilter: false,
},
{
Field: "RiskAssessment",
Columns: []string{"H"},
FilterColumns: []string{},
Width: []float64{12},
DefaultWidth: 12,
HasFilter: false,
},
{
Field: "SecurityMeasures",
Columns: []string{"I"},
FilterColumns: []string{},
Width: []float64{40},
DefaultWidth: 40,
HasFilter: false,
},
}
)

View File

@@ -1,319 +0,0 @@
// 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 soagen
import (
"fmt"
"strings"
"github.com/xuri/excelize/v2"
)
func GenerateSOAExcel(data SOAData) ([]byte, error) {
f := excelize.NewFile()
defer func() { _ = f.Close() }()
sheetName := "State of Applicability"
_, err := f.NewSheet(sheetName)
if err != nil {
return nil, fmt.Errorf("cannot create sheet: %w", err)
}
if err := f.DeleteSheet("Sheet1"); err != nil {
return nil, fmt.Errorf("cannot delete sheet: %w", err)
}
if err := setupSOAHeader(f, sheetName); err != nil {
return nil, fmt.Errorf("cannot setup Excel header: %w", err)
}
if err := populateSOAData(f, sheetName, data.Rows); err != nil {
return nil, fmt.Errorf("cannot populate Excel data: %w", err)
}
if err := applySOAFinalFormatting(f, sheetName, len(data.Rows)); err != nil {
return nil, fmt.Errorf("cannot apply final formatting: %w", err)
}
buf, err := f.WriteToBuffer()
if err != nil {
return nil, fmt.Errorf("cannot write Excel file to buffer: %w", err)
}
return buf.Bytes(), nil
}
func setupSOAHeader(f *excelize.File, sheetName string) error {
headerStyle, err := f.NewStyle(getHeaderStyle())
if err != nil {
return err
}
cellStyle, err := f.NewStyle(getCellStyle())
if err != nil {
return err
}
return applyHeaderLayout(f, sheetName, getSOAHeaderLayout(), headerStyle, cellStyle)
}
func populateSOAData(f *excelize.File, sheetName string, rows []SOARowData) error {
dataStartRow := 8
for i, rowData := range rows {
isFirstRow := i == 0
filterColumns, err := writeSOARow(f, sheetName, dataStartRow+i, rowData, isFirstRow)
if err != nil {
return fmt.Errorf("cannot write row %d: %w", dataStartRow+i, err)
}
if isFirstRow && len(filterColumns) > 0 {
if err := applySOAAutoFilter(f, sheetName, filterColumns); err != nil {
return fmt.Errorf("cannot apply auto filter: %w", err)
}
}
}
return nil
}
func writeSOARow(f *excelize.File, sheetName string, row int, data SOARowData, isFirstRow bool) ([]string, error) {
var allFilterColumns []string
fields := []struct {
name string
value interface{}
}{
{"SectionTitle", data.SectionTitle},
{"ControlName", data.ControlName},
{"Applicability", data.Applicability},
{"Regulatory", data.Regulatory},
{"Contractual", data.Contractual},
{"BestPractice", data.BestPractice},
{"RiskAssessment", data.RiskAssessment},
{"ExclusionJustification", data.ExclusionJustification},
{"SecurityMeasures", data.SecurityMeasures},
}
for _, field := range fields {
fieldConfig := getSOAFieldConfig(field.name)
if fieldConfig == nil {
continue
}
filterColumns, err := processField(f, sheetName, row, field.value, *fieldConfig, isFirstRow)
if err != nil {
return nil, fmt.Errorf("cannot write field %s: %w", field.name, err)
}
allFilterColumns = append(allFilterColumns, filterColumns...)
}
return allFilterColumns, nil
}
func getSOAFieldConfig(fieldName string) *FieldConfiguration {
for _, config := range soaFieldConfigs {
if config.Field == fieldName {
return &config
}
}
return nil
}
func applySOAAutoFilter(f *excelize.File, sheetName string, filterColumns []string) error {
if len(filterColumns) == 0 {
return nil
}
firstCol, lastCol := filterColumns[0], filterColumns[0]
for _, col := range filterColumns {
if col < firstCol {
firstCol = col
}
if col > lastCol {
lastCol = col
}
}
filterRange := fmt.Sprintf("%s7:%s1000", firstCol, lastCol)
return f.AutoFilter(sheetName, filterRange, []excelize.AutoFilterOptions{})
}
func applySOAFinalFormatting(f *excelize.File, sheetName string, dataRowCount int) error {
if dataRowCount > 0 {
headerRowHeight := 30.0
_ = f.SetRowHeight(sheetName, 6, headerRowHeight)
_ = f.SetRowHeight(sheetName, 7, headerRowHeight)
return f.SetPanes(sheetName, &excelize.Panes{
Freeze: true,
XSplit: 0,
YSplit: 7,
TopLeftCell: "A8",
})
}
return nil
}
func processField(f *excelize.File, sheetName string, row int, value interface{}, config FieldConfiguration, isFirstRow bool) ([]string, error) {
switch v := value.(type) {
case ExcelMarshaler:
marshaler := v
excelValue := marshaler.MarshalExcel()
styleID, err := createCellStyle(f, excelValue.Style)
if err != nil {
return nil, err
}
return writeSingleColumnField(f, sheetName, row, excelValue, &config, styleID, isFirstRow)
case string:
if len(config.Columns) == 0 {
return []string{}, nil
}
col := config.Columns[0]
cellRef := fmt.Sprintf("%s%d", col, row)
if err := f.SetCellValue(sheetName, cellRef, v); err != nil {
return nil, fmt.Errorf("cannot set cell value: %w", err)
}
textStyleID, err := createCellStyle(f, getTextStyle())
if err != nil {
return nil, fmt.Errorf("cannot create text style: %w", err)
}
if err := f.SetCellStyle(sheetName, cellRef, cellRef, textStyleID); err != nil {
return nil, fmt.Errorf("cannot set cell style: %w", err)
}
if isFirstRow {
width := config.DefaultWidth
if len(config.Width) > 0 {
width = config.Width[0]
}
if err := f.SetColWidth(sheetName, col, col, width); err != nil {
return nil, fmt.Errorf("cannot set column width: %w", err)
}
}
if config.HasFilter {
if len(config.FilterColumns) > 0 {
return config.FilterColumns, nil
}
return []string{col}, nil
}
return []string{}, nil
case []string:
var formattedLines []string
for _, item := range v {
lines := strings.Split(item, "\n")
for _, line := range lines {
if strings.TrimSpace(line) != "" {
formattedLines = append(formattedLines, "• "+strings.TrimSpace(line))
}
}
}
joinedValue := strings.Join(formattedLines, "\n")
if len(config.Columns) == 0 {
return []string{}, nil
}
col := config.Columns[0]
cellRef := fmt.Sprintf("%s%d", col, row)
if err := f.SetCellValue(sheetName, cellRef, joinedValue); err != nil {
return nil, fmt.Errorf("cannot set cell value: %w", err)
}
textStyleID, err := createCellStyle(f, getTextStyle())
if err != nil {
return nil, fmt.Errorf("cannot create text style: %w", err)
}
if err := f.SetCellStyle(sheetName, cellRef, cellRef, textStyleID); err != nil {
return nil, fmt.Errorf("cannot set cell style: %w", err)
}
if isFirstRow {
width := config.DefaultWidth
if len(config.Width) > 0 {
width = config.Width[0]
}
if err := f.SetColWidth(sheetName, col, col, width); err != nil {
return nil, fmt.Errorf("cannot set column width: %w", err)
}
}
if config.HasFilter {
if len(config.FilterColumns) > 0 {
return config.FilterColumns, nil
}
return []string{col}, nil
}
return []string{}, nil
case *bool:
yesNoValue := boolToYesNo(v)
excelValue := yesNoValue.MarshalExcel()
styleID, err := createCellStyle(f, excelValue.Style)
if err != nil {
return nil, err
}
return writeSingleColumnField(f, sheetName, row, excelValue, &config, styleID, isFirstRow)
default:
return nil, fmt.Errorf("no handler found for field type: %T", value)
}
}
func GenerateExcel(data SOAData) ([]byte, error) {
return GenerateSOAExcel(data)
}
func writeSingleColumnField(f *excelize.File, sheetName string, row int, excelValue ExcelValue, colDef *FieldConfiguration, styleID int, isFirstRow bool) ([]string, error) {
if len(colDef.Columns) == 0 {
return []string{}, nil
}
col := colDef.Columns[0]
cellRef := fmt.Sprintf("%s%d", col, row)
_ = f.SetCellValue(sheetName, cellRef, excelValue.Value)
_ = f.SetCellStyle(sheetName, cellRef, cellRef, styleID)
if isFirstRow {
if err := applyDataValidation(f, sheetName, col, row, excelValue.Validation); err != nil {
return nil, err
}
if err := setColumnWidth(f, sheetName, col, excelValue.Width); err != nil {
return nil, err
}
}
if colDef.HasFilter {
if len(colDef.FilterColumns) > 0 {
return colDef.FilterColumns, nil
}
return []string{col}, nil
}
return []string{}, nil
}

View File

@@ -1,134 +0,0 @@
// 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 soagen
import (
"fmt"
"time"
"github.com/xuri/excelize/v2"
)
type HeaderCell struct {
Cell string
Value string
Style string // "header" or "cell"
}
type MergedCell struct {
StartCell string
EndCell string
}
type HeaderLayout struct {
InfoHeader []HeaderCell
InfoData []HeaderCell
MainHeader []HeaderCell
SubHeader []HeaderCell
Merges []MergedCell
}
func getSOAHeaderLayout() HeaderLayout {
return HeaderLayout{
InfoHeader: []HeaderCell{
{"A2", "Version", "header"},
{"C2", "Date", "header"},
{"D2", "Comment", "header"},
{"E2", "Author", "header"},
{"F2", "Approver", "header"},
},
InfoData: []HeaderCell{
{"A3", "1.0", "cell"},
{"C3", time.Now().Format("01/02/2006"), "cell"},
{"D3", "Initial SoA", "cell"},
{"E3", "System Admin", "cell"},
{"F3", "Security Manager", "cell"},
},
MainHeader: []HeaderCell{
{"A6", "Control", "header"},
{"B6", "Control name", "header"},
{"C6", "Applicability", "header"},
{"D6", "Justification for exclusion", "header"},
{"E6", "Justification for inclusion", "header"},
{"F6", "Justification for inclusion", "header"},
{"G6", "Justification for inclusion", "header"},
{"H6", "Justification for inclusion", "header"},
{"I6", "List of security measure or policy", "header"},
},
SubHeader: []HeaderCell{
{"A7", "Control", "header"},
{"B7", "Control name", "header"},
{"C7", "Applicability", "header"},
{"D7", "Justification for exclusion", "header"},
{"E7", "Regulatory", "header"},
{"F7", "Contractual", "header"},
{"G7", "Best practice", "header"},
{"H7", "Risk assessment", "header"},
{"I7", "List of security measure or policy", "header"},
},
Merges: []MergedCell{
{"A6", "A7"},
{"B6", "B7"},
{"C6", "C7"},
{"D6", "D7"},
{"E6", "H6"},
{"I6", "I7"},
},
}
}
func applyHeaderLayout(f *excelize.File, sheetName string, layout HeaderLayout, headerStyle, cellStyle int) error {
if err := applyCells(f, sheetName, layout.InfoHeader, headerStyle, cellStyle); err != nil {
return err
}
if err := applyCells(f, sheetName, layout.InfoData, headerStyle, cellStyle); err != nil {
return err
}
if err := applyCells(f, sheetName, layout.MainHeader, headerStyle, cellStyle); err != nil {
return err
}
if err := applyCells(f, sheetName, layout.SubHeader, headerStyle, cellStyle); err != nil {
return err
}
for _, merge := range layout.Merges {
if err := f.MergeCell(sheetName, merge.StartCell, merge.EndCell); err != nil {
return err
}
}
return nil
}
func applyCells(f *excelize.File, sheetName string, cells []HeaderCell, headerStyle, cellStyle int) error {
for _, cell := range cells {
if err := f.SetCellValue(sheetName, cell.Cell, cell.Value); err != nil {
return fmt.Errorf("cannot set cell value: %w", err)
}
style := cellStyle
if cell.Style == "header" {
style = headerStyle
}
if err := f.SetCellStyle(sheetName, cell.Cell, cell.Cell, style); err != nil {
return fmt.Errorf("cannot set cell style: %w", err)
}
}
return nil
}

View File

@@ -1,33 +0,0 @@
// 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 soagen
// SOARowData represents a single row in the State of Applicability Excel
type SOARowData struct {
SectionTitle string
ControlName string
Applicability Applicability
Regulatory *bool
Contractual *bool
BestPractice *bool
RiskAssessment *bool
ExclusionJustification string
SecurityMeasures []string
}
// SOAData contains all the data needed for State of Applicability generation
type SOAData struct {
Rows []SOARowData
}

View File

@@ -1,95 +0,0 @@
// 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 soagen
import (
"fmt"
"github.com/xuri/excelize/v2"
)
// getTextStyle returns the standard text style for string fields
func getTextStyle() *excelize.Style {
return &excelize.Style{
Border: []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "top", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
},
Alignment: &excelize.Alignment{Horizontal: "left", Vertical: "center", WrapText: true},
}
}
// getHeaderStyle returns the standard header style
func getHeaderStyle() *excelize.Style {
return &excelize.Style{
Font: &excelize.Font{Bold: true, Size: 10, Color: "#000000"},
Fill: excelize.Fill{Type: "pattern", Color: []string{"#D9D9D9"}, Pattern: 1},
Border: []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "top", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
},
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
}
}
// getCellStyle returns the standard cell style
func getCellStyle() *excelize.Style {
return &excelize.Style{
Border: []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "top", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
},
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
}
}
func createCellStyle(f *excelize.File, style *excelize.Style) (int, error) {
styleID, err := f.NewStyle(style)
if err != nil {
return 0, fmt.Errorf("cannot create style: %w", err)
}
return styleID, nil
}
func applyDataValidation(f *excelize.File, sheetName, col string, row int, validation []string) error {
if len(validation) == 0 {
return nil
}
dv := excelize.NewDataValidation(true)
dv.Sqref = fmt.Sprintf("%s%d:%s1000", col, row, col) // Apply to reasonable range
_ = dv.SetDropList(validation)
dv.SetError(excelize.DataValidationErrorStyleStop, "Invalid Input", "Please select from the dropdown list.")
if err := f.AddDataValidation(sheetName, dv); err != nil {
return fmt.Errorf("cannot add data validation: %w", err)
}
return nil
}
func setColumnWidth(f *excelize.File, sheetName, col string, width float64) error {
if width > 0 {
if err := f.SetColWidth(sheetName, col, col, width); err != nil {
return fmt.Errorf("cannot set column width: %w", err)
}
}
return nil
}

View File

@@ -1,60 +0,0 @@
// 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 soagen
import "github.com/xuri/excelize/v2"
// YesNo represents a YES/NO/Empty value
type YesNo string
const (
Yes YesNo = "YES"
No YesNo = "NO"
Empty YesNo = ""
)
// String returns the string representation of YesNo
func (yn YesNo) String() string {
return string(yn)
}
// MarshalExcel implements ExcelMarshaler for YesNo
func (yn YesNo) MarshalExcel() ExcelValue {
return ExcelValue{
Value: yn.String(),
Style: &excelize.Style{
Border: []excelize.Border{
{Type: "left", Color: "#000000", Style: 1},
{Type: "top", Color: "#000000", Style: 1},
{Type: "bottom", Color: "#000000", Style: 1},
{Type: "right", Color: "#000000", Style: 1},
},
Alignment: &excelize.Alignment{Horizontal: "center", Vertical: "center", WrapText: true},
},
Validation: []string{string(Yes), string(No), string(Empty)},
Width: 12,
}
}
// boolToYesNo converts *bool to YesNo type for Excel output
func boolToYesNo(b *bool) YesNo {
if b == nil {
return Empty
}
if *b {
return Yes
}
return No
}

View File

@@ -154,8 +154,7 @@ func TestValidationErrors_Methods(t *testing.T) {
first := errors.First()
if first == nil {
t.Fatal("expected first error")
}
if first.Field != "email" {
} else if first.Field != "email" {
t.Errorf("expected first field to be 'email', got '%s'", first.Field)
}
})

View File

@@ -40,8 +40,7 @@ func TestMinItems(t *testing.T) {
err := MinItems(2)(&items)
if err == nil {
t.Fatal("expected validation error")
}
if err.Code != ErrorCodeOutOfRange {
} else if err.Code != ErrorCodeOutOfRange {
t.Errorf("expected error code %s, got %s", ErrorCodeOutOfRange, err.Code)
}
})
@@ -85,8 +84,7 @@ func TestMaxItems(t *testing.T) {
err := MaxItems(2)(&items)
if err == nil {
t.Fatal("expected validation error")
}
if err.Code != ErrorCodeOutOfRange {
} else if err.Code != ErrorCodeOutOfRange {
t.Errorf("expected error code %s, got %s", ErrorCodeOutOfRange, err.Code)
}
})
@@ -114,8 +112,7 @@ func TestUniqueItems(t *testing.T) {
err := UniqueItems()(&items)
if err == nil {
t.Fatal("expected validation error")
}
if err.Code != ErrorCodeInvalidFormat {
} else if err.Code != ErrorCodeInvalidFormat {
t.Errorf("expected error code %s, got %s", ErrorCodeInvalidFormat, err.Code)
}
})
@@ -157,12 +154,13 @@ func TestUniqueItems(t *testing.T) {
err := UniqueItems()(&items)
if err == nil {
t.Fatal("expected validation error for non-comparable type")
}
if err.Code != ErrorCodeInvalidFormat {
t.Errorf("expected error code %s, got %s", ErrorCodeInvalidFormat, err.Code)
}
if err.Message != "cannot validate uniqueness for non-comparable types" {
t.Errorf("unexpected error message: %s", err.Message)
} else {
if err.Code != ErrorCodeInvalidFormat {
t.Errorf("expected error code %s, got %s", ErrorCodeInvalidFormat, err.Code)
}
if err.Message != "cannot validate uniqueness for non-comparable types" {
t.Errorf("unexpected error message: %s", err.Message)
}
}
})
@@ -171,8 +169,7 @@ func TestUniqueItems(t *testing.T) {
err := UniqueItems()(&items)
if err == nil {
t.Fatal("expected validation error for non-comparable type")
}
if err.Code != ErrorCodeInvalidFormat {
} else if err.Code != ErrorCodeInvalidFormat {
t.Errorf("expected error code %s, got %s", ErrorCodeInvalidFormat, err.Code)
}
})
@@ -185,8 +182,7 @@ func TestUniqueItems(t *testing.T) {
err := UniqueItems()(&items)
if err == nil {
t.Fatal("expected validation error for non-comparable type")
}
if err.Code != ErrorCodeInvalidFormat {
} else if err.Code != ErrorCodeInvalidFormat {
t.Errorf("expected error code %s, got %s", ErrorCodeInvalidFormat, err.Code)
}
})
@@ -212,8 +208,7 @@ func TestUniqueItems(t *testing.T) {
err := UniqueItems()(&items)
if err == nil {
t.Fatal("expected validation error for duplicate comparable structs")
}
if err.Code != ErrorCodeInvalidFormat {
} else if err.Code != ErrorCodeInvalidFormat {
t.Errorf("expected error code %s, got %s", ErrorCodeInvalidFormat, err.Code)
}
})

View File

@@ -103,8 +103,7 @@ func TestRequired(t *testing.T) {
err := Required()(&str)
if err == nil {
t.Fatal("expected validation error")
}
if err.Code != ErrorCodeRequired {
} else if err.Code != ErrorCodeRequired {
t.Errorf("expected error code %s, got %s", ErrorCodeRequired, err.Code)
}
})
@@ -193,8 +192,7 @@ func TestRequired(t *testing.T) {
err := Required()(slice)
if err == nil {
t.Fatal("expected validation error for empty []string slice")
}
if err.Code != ErrorCodeRequired {
} else if err.Code != ErrorCodeRequired {
t.Errorf("expected error code %s, got %s", ErrorCodeRequired, err.Code)
}
})
@@ -212,8 +210,7 @@ func TestRequired(t *testing.T) {
err := Required()(slice)
if err == nil {
t.Fatal("expected validation error for empty []int slice")
}
if err.Code != ErrorCodeRequired {
} else if err.Code != ErrorCodeRequired {
t.Errorf("expected error code %s, got %s", ErrorCodeRequired, err.Code)
}
})
@@ -234,8 +231,7 @@ func TestRequired(t *testing.T) {
err := Required()(slice)
if err == nil {
t.Fatal("expected validation error for empty custom type slice")
}
if err.Code != ErrorCodeRequired {
} else if err.Code != ErrorCodeRequired {
t.Errorf("expected error code %s, got %s", ErrorCodeRequired, err.Code)
}
})