From cf1dadc0b5b07da35a0cd7d2cc6cbfaa51cc7970 Mon Sep 17 00:00:00 2001 From: Sacha Al Himdani Date: Mon, 16 Mar 2026 19:24:34 +0100 Subject: [PATCH] Add implemented state and justification to controls Introduce `implemented` enum (IMPLEMENTED/NOT_IMPLEMENTED) and `not_implemented_justification` (nullable text) fields on the Control entity across all API surfaces (GraphQL, MCP, CLI), database, frontend, and SOA export. The database stores implementation state as a PostgreSQL enum `control_implementation_state`. Controls default to IMPLEMENTED during migration. The SOA list and PDF export show implementation status alongside applicability, with "-" for non-applicable controls. Justification columns are renamed for clarity: "Justification for non-applicability" and "Justification for non-implementation". Signed-off-by: Sacha Al Himdani --- .../console/src/hooks/graph/FrameworkGraph.ts | 3 + .../frameworks/FrameworkControlPage.tsx | 24 +++ .../dialogs/FrameworkControlDialog.tsx | 50 ++++-- .../AddApplicabilityStatementDialog.tsx | 2 + .../tabs/StateOfApplicabilityControlsTab.tsx | 147 +++++++----------- e2e/console/control_test.go | 21 +++ e2e/console/mapping_test.go | 4 + e2e/console/rbac_test.go | 6 +- e2e/internal/factory/factory.go | 15 ++ pkg/cmd/control/create/create.go | 38 +++-- pkg/cmd/control/update/update.go | 40 +++-- pkg/coredata/control.go | 92 ++++++++--- pkg/coredata/control_implementation_state.go | 66 ++++++++ pkg/coredata/migrations/20260316T120000Z.sql | 5 + pkg/docgen/generator.go | 26 ++-- pkg/docgen/soa_template.html | 72 +++++++-- pkg/probo/control_service.go | 80 +++++++--- pkg/probo/framework_service.go | 38 +++-- pkg/probo/state_of_applicability_service.go | 20 ++- pkg/server/api/console/v1/schema.graphql | 20 +++ pkg/server/api/console/v1/types/control.go | 14 +- pkg/server/api/console/v1/v1_resolver.go | 24 +-- pkg/server/api/mcp/v1/schema.resolvers.go | 28 +++- pkg/server/api/mcp/v1/specification.yaml | 42 +++++ pkg/server/api/mcp/v1/types/control.go | 19 ++- 25 files changed, 663 insertions(+), 233 deletions(-) create mode 100644 pkg/coredata/control_implementation_state.go create mode 100644 pkg/coredata/migrations/20260316T120000Z.sql diff --git a/apps/console/src/hooks/graph/FrameworkGraph.ts b/apps/console/src/hooks/graph/FrameworkGraph.ts index fed43088e..c384cd5cd 100644 --- a/apps/console/src/hooks/graph/FrameworkGraph.ts +++ b/apps/console/src/hooks/graph/FrameworkGraph.ts @@ -103,6 +103,9 @@ export const frameworkControlNodeQuery = graphql` name sectionTitle description + bestPractice + implemented + notImplementedJustification canUpdate: permission(action: "core:control:update") canDelete: permission(action: "core:control:delete") canCreateMeasureMapping: permission( diff --git a/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx b/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx index 186a73c5b..88021e882 100644 --- a/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx +++ b/apps/console/src/pages/organizations/frameworks/FrameworkControlPage.tsx @@ -3,7 +3,9 @@ import { promisifyMutation } from "@probo/helpers"; import { useTranslate } from "@probo/i18n"; import { ActionDropdown, + Badge, Button, + Card, DropdownItem, IconPencil, IconTrashCan, @@ -354,6 +356,28 @@ export default function FrameworkControlPage({ queryRef }: Props) { {control.description} )} + +
+
+ {__("Best Practice")} + + {control.bestPractice ? __("Yes") : __("No")} + +
+
+ {__("Implemented")} + + {control.implemented === "IMPLEMENTED" ? __("Implemented") : __("Not Implemented")} + +
+ {control.implemented === "NOT_IMPLEMENTED" && control.notImplementedJustification && ( +
+ {__("Justification for non-implementation")} +
{control.notImplementedJustification}
+
+ )} +
+
) => { if (frameworkControl) { - // Update the control await mutate({ variables: { input: { @@ -117,11 +123,12 @@ export function FrameworkControlDialog(props: Props) { description: data.description || null, sectionTitle: data.sectionTitle, bestPractice: data.bestPractice, + implemented: data.implemented, + notImplementedJustification: data.implemented === "IMPLEMENTED" ? null : (data.notImplementedJustification || null), }, }, }); } else { - // Create a new control await mutate({ variables: { input: { @@ -130,6 +137,8 @@ export function FrameworkControlDialog(props: Props) { description: data.description || null, sectionTitle: data.sectionTitle, bestPractice: data.bestPractice ?? true, + implemented: data.implemented ?? "IMPLEMENTED", + notImplementedJustification: data.implemented === "IMPLEMENTED" ? null : (data.notImplementedJustification || null), }, connections: [props.connectionId!], }, @@ -167,7 +176,7 @@ export function FrameworkControlDialog(props: Props) { id="title" required variant="title" - placeholder={__("Document title")} + placeholder={__("Control name")} {...register("name")} />