Replace implemented column with CMMI maturity level
Drop the boolean implemented/not-implemented state in favor of a mandatory CMMI maturity level enum (NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING) stored as a Postgres enum type. The migration backfills existing rows (NOT_IMPLEMENTED → NONE, IMPLEMENTED → INITIAL), makes the column NOT NULL, and drops the old implemented column and its enum type. - maturityLevel is required on CreateControlInput and non-nullable (!) in the GraphQL schema - CLI displays human-readable CMMI labels instead of raw enum tokens - SOA table and published document use a single Maturity column in place of the old Implemented + Maturity columns - Remove ControlImplementationState type and all implemented references across backend, frontend, CLI, MCP, n8n, and E2E tests Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -19,14 +19,11 @@ import {
|
|||||||
import { useTranslate } from "@probo/i18n";
|
import { useTranslate } from "@probo/i18n";
|
||||||
import { Option } from "@probo/ui";
|
import { Option } from "@probo/ui";
|
||||||
|
|
||||||
export const MATURITY_LEVEL_UNSET = "__unset__" as const;
|
|
||||||
|
|
||||||
export function ControlMaturityLevelOptions() {
|
export function ControlMaturityLevelOptions() {
|
||||||
const { __ } = useTranslate();
|
const { __ } = useTranslate();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Option value={MATURITY_LEVEL_UNSET}>{__("Not set")}</Option>
|
|
||||||
{controlMaturityLevels.map(level => (
|
{controlMaturityLevels.map(level => (
|
||||||
<Option key={level} value={level}>
|
<Option key={level} value={level}>
|
||||||
{getControlMaturityLevelLabel(__, level)}
|
{getControlMaturityLevelLabel(__, level)}
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ export const frameworkControlNodeQuery = graphql`
|
|||||||
sectionTitle
|
sectionTitle
|
||||||
description
|
description
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
canUpdate: permission(action: "core:control:update")
|
canUpdate: permission(action: "core:control:update")
|
||||||
|
|||||||
@@ -383,25 +383,17 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm text-txt-secondary">{__("Implemented")}</span>
|
<span className="text-sm text-txt-secondary">{__("Maturity level")}</span>
|
||||||
<Badge variant={control.implemented === "IMPLEMENTED" ? "success" : "warning"} size="sm">
|
<Badge variant="neutral" size="sm">
|
||||||
{control.implemented === "IMPLEMENTED" ? __("Implemented") : __("Not Implemented")}
|
{getControlMaturityLevelLabel(__, control.maturityLevel ?? "NONE")}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
{control.implemented === "NOT_IMPLEMENTED" && control.notImplementedJustification && (
|
{control.maturityLevel === "NONE" && control.notImplementedJustification && (
|
||||||
<div>
|
<div>
|
||||||
<span className="text-xs text-txt-secondary">{__("Justification for non-implementation")}</span>
|
<span className="text-xs text-txt-secondary">{__("Justification for non-implementation")}</span>
|
||||||
<div className="text-sm mt-0.5 whitespace-pre-wrap">{control.notImplementedJustification}</div>
|
<div className="text-sm mt-0.5 whitespace-pre-wrap">{control.notImplementedJustification}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="text-sm text-txt-secondary">{__("Maturity level")}</span>
|
|
||||||
<Badge variant="neutral" size="sm">
|
|
||||||
{control.maturityLevel
|
|
||||||
? getControlMaturityLevelLabel(__, control.maturityLevel)
|
|
||||||
: __("Not set")}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
|
|||||||
@@ -32,10 +32,7 @@ import { graphql } from "relay-runtime";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import type { FrameworkControlDialogFragment$key } from "#/__generated__/core/FrameworkControlDialogFragment.graphql";
|
import type { FrameworkControlDialogFragment$key } from "#/__generated__/core/FrameworkControlDialogFragment.graphql";
|
||||||
import {
|
import { ControlMaturityLevelOptions } from "#/components/form/ControlMaturityLevelOptions";
|
||||||
ControlMaturityLevelOptions,
|
|
||||||
MATURITY_LEVEL_UNSET,
|
|
||||||
} from "#/components/form/ControlMaturityLevelOptions";
|
|
||||||
import { useFormWithSchema } from "#/hooks/useFormWithSchema";
|
import { useFormWithSchema } from "#/hooks/useFormWithSchema";
|
||||||
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
|
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
|
||||||
|
|
||||||
@@ -53,7 +50,6 @@ const controlFragment = graphql`
|
|||||||
description
|
description
|
||||||
sectionTitle
|
sectionTitle
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
}
|
}
|
||||||
@@ -89,10 +85,7 @@ const schema = z.object({
|
|||||||
description: z.string().optional().nullable(),
|
description: z.string().optional().nullable(),
|
||||||
sectionTitle: z.string(),
|
sectionTitle: z.string(),
|
||||||
bestPractice: z.boolean(),
|
bestPractice: z.boolean(),
|
||||||
implemented: z.enum(["IMPLEMENTED", "NOT_IMPLEMENTED"]),
|
|
||||||
notImplementedJustification: z.string().optional().nullable(),
|
|
||||||
maturityLevel: z.enum([
|
maturityLevel: z.enum([
|
||||||
MATURITY_LEVEL_UNSET,
|
|
||||||
"NONE",
|
"NONE",
|
||||||
"INITIAL",
|
"INITIAL",
|
||||||
"MANAGED",
|
"MANAGED",
|
||||||
@@ -100,6 +93,7 @@ const schema = z.object({
|
|||||||
"QUANTITATIVELY_MANAGED",
|
"QUANTITATIVELY_MANAGED",
|
||||||
"OPTIMIZING",
|
"OPTIMIZING",
|
||||||
]),
|
]),
|
||||||
|
notImplementedJustification: z.string().optional().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export function FrameworkControlDialog(props: Props) {
|
export function FrameworkControlDialog(props: Props) {
|
||||||
@@ -124,9 +118,8 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
description: frameworkControl?.description ?? "",
|
description: frameworkControl?.description ?? "",
|
||||||
sectionTitle: frameworkControl?.sectionTitle ?? "",
|
sectionTitle: frameworkControl?.sectionTitle ?? "",
|
||||||
bestPractice: frameworkControl?.bestPractice ?? true,
|
bestPractice: frameworkControl?.bestPractice ?? true,
|
||||||
implemented: frameworkControl?.implemented ?? "IMPLEMENTED",
|
maturityLevel: frameworkControl?.maturityLevel ?? "INITIAL",
|
||||||
notImplementedJustification: frameworkControl?.notImplementedJustification ?? "",
|
notImplementedJustification: frameworkControl?.notImplementedJustification ?? "",
|
||||||
maturityLevel: frameworkControl?.maturityLevel ?? MATURITY_LEVEL_UNSET,
|
|
||||||
}),
|
}),
|
||||||
[frameworkControl],
|
[frameworkControl],
|
||||||
);
|
);
|
||||||
@@ -141,14 +134,9 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
}, [defaultValues, reset]);
|
}, [defaultValues, reset]);
|
||||||
|
|
||||||
const bestPracticeValue = watch("bestPractice");
|
const bestPracticeValue = watch("bestPractice");
|
||||||
const implementedValue = watch("implemented");
|
|
||||||
const maturityLevelValue = watch("maturityLevel");
|
const maturityLevelValue = watch("maturityLevel");
|
||||||
|
|
||||||
const onSubmit = async (data: z.infer<typeof schema>) => {
|
const onSubmit = async (data: z.infer<typeof schema>) => {
|
||||||
const maturityLevel = data.maturityLevel === MATURITY_LEVEL_UNSET
|
|
||||||
? null
|
|
||||||
: data.maturityLevel;
|
|
||||||
|
|
||||||
if (frameworkControl) {
|
if (frameworkControl) {
|
||||||
await mutate({
|
await mutate({
|
||||||
variables: {
|
variables: {
|
||||||
@@ -158,9 +146,8 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
description: data.description || null,
|
description: data.description || null,
|
||||||
sectionTitle: data.sectionTitle,
|
sectionTitle: data.sectionTitle,
|
||||||
bestPractice: data.bestPractice,
|
bestPractice: data.bestPractice,
|
||||||
implemented: data.implemented,
|
maturityLevel: data.maturityLevel,
|
||||||
notImplementedJustification: data.implemented === "IMPLEMENTED" ? null : (data.notImplementedJustification || null),
|
notImplementedJustification: data.maturityLevel === "NONE" ? (data.notImplementedJustification || null) : null,
|
||||||
maturityLevel,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -173,9 +160,8 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
description: data.description || null,
|
description: data.description || null,
|
||||||
sectionTitle: data.sectionTitle,
|
sectionTitle: data.sectionTitle,
|
||||||
bestPractice: data.bestPractice ?? true,
|
bestPractice: data.bestPractice ?? true,
|
||||||
implemented: data.implemented ?? "IMPLEMENTED",
|
maturityLevel: data.maturityLevel,
|
||||||
notImplementedJustification: data.implemented === "IMPLEMENTED" ? null : (data.notImplementedJustification || null),
|
notImplementedJustification: data.maturityLevel === "NONE" ? (data.notImplementedJustification || null) : null,
|
||||||
maturityLevel,
|
|
||||||
},
|
},
|
||||||
connections: [props.connectionId!],
|
connections: [props.connectionId!],
|
||||||
},
|
},
|
||||||
@@ -232,23 +218,6 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
/>
|
/>
|
||||||
<span className="text-sm">{__("Best Practice")}</span>
|
<span className="text-sm">{__("Best Practice")}</span>
|
||||||
</label>
|
</label>
|
||||||
<label className="flex items-center gap-2 cursor-pointer">
|
|
||||||
<Checkbox
|
|
||||||
checked={implementedValue === "IMPLEMENTED"}
|
|
||||||
onChange={checked =>
|
|
||||||
setValue("implemented", checked ? "IMPLEMENTED" : "NOT_IMPLEMENTED")}
|
|
||||||
/>
|
|
||||||
<span className="text-sm">{__("Implemented")}</span>
|
|
||||||
</label>
|
|
||||||
{implementedValue === "NOT_IMPLEMENTED" && (
|
|
||||||
<Textarea
|
|
||||||
id="notImplementedJustification"
|
|
||||||
variant="ghost"
|
|
||||||
autogrow
|
|
||||||
placeholder={__("Justification for non-implementation")}
|
|
||||||
{...register("notImplementedJustification")}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-sm">{__("Maturity level")}</span>
|
<span className="text-sm">{__("Maturity level")}</span>
|
||||||
<Select
|
<Select
|
||||||
@@ -260,6 +229,15 @@ export function FrameworkControlDialog(props: Props) {
|
|||||||
<ControlMaturityLevelOptions />
|
<ControlMaturityLevelOptions />
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
{maturityLevelValue === "NONE" && (
|
||||||
|
<Textarea
|
||||||
|
id="notImplementedJustification"
|
||||||
|
variant="ghost"
|
||||||
|
autogrow
|
||||||
|
placeholder={__("Justification for non-implementation")}
|
||||||
|
{...register("notImplementedJustification")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ const createApplicabilityStatementMutation = graphql`
|
|||||||
sectionTitle
|
sectionTitle
|
||||||
name
|
name
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
regulatory
|
regulatory
|
||||||
contractual
|
contractual
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ export const controlsFragment = graphql`
|
|||||||
sectionTitle
|
sectionTitle
|
||||||
name
|
name
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
regulatory
|
regulatory
|
||||||
@@ -151,7 +150,6 @@ export default function StatementOfApplicabilityControlsTab({
|
|||||||
applicability: edge.node.applicability,
|
applicability: edge.node.applicability,
|
||||||
justification: edge.node.justification,
|
justification: edge.node.justification,
|
||||||
bestPractice: edge.node.control.bestPractice,
|
bestPractice: edge.node.control.bestPractice,
|
||||||
implemented: edge.node.control.implemented,
|
|
||||||
notImplementedJustification: edge.node.control.notImplementedJustification,
|
notImplementedJustification: edge.node.control.notImplementedJustification,
|
||||||
maturityLevel: edge.node.control.maturityLevel,
|
maturityLevel: edge.node.control.maturityLevel,
|
||||||
regulatory: edge.node.control.regulatory,
|
regulatory: edge.node.control.regulatory,
|
||||||
@@ -226,7 +224,6 @@ export default function StatementOfApplicabilityControlsTab({
|
|||||||
<Th className="w-[9%]">{__("Framework")}</Th>
|
<Th className="w-[9%]">{__("Framework")}</Th>
|
||||||
<Th className="w-[17%]">{__("Control")}</Th>
|
<Th className="w-[17%]">{__("Control")}</Th>
|
||||||
<Th className="w-[12%]">{__("Applicability")}</Th>
|
<Th className="w-[12%]">{__("Applicability")}</Th>
|
||||||
<Th className="w-[12%]">{__("Implemented")}</Th>
|
|
||||||
<Th className="w-[14%]">{__("Maturity")}</Th>
|
<Th className="w-[14%]">{__("Maturity")}</Th>
|
||||||
<Th className="w-[7%]">{__("Regulatory")}</Th>
|
<Th className="w-[7%]">{__("Regulatory")}</Th>
|
||||||
<Th className="w-[7%]">{__("Contractual")}</Th>
|
<Th className="w-[7%]">{__("Contractual")}</Th>
|
||||||
@@ -241,7 +238,7 @@ export default function StatementOfApplicabilityControlsTab({
|
|||||||
{linkedControls.length === 0 && (
|
{linkedControls.length === 0 && (
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td
|
<Td
|
||||||
colSpan={canUpdate || canDelete ? 10 : 9}
|
colSpan={canUpdate || canDelete ? 9 : 8}
|
||||||
className="text-center text-txt-secondary py-12"
|
className="text-center text-txt-secondary py-12"
|
||||||
>
|
>
|
||||||
{__("No controls linked")}
|
{__("No controls linked")}
|
||||||
@@ -293,12 +290,12 @@ export default function StatementOfApplicabilityControlsTab({
|
|||||||
: (
|
: (
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Badge
|
<Badge
|
||||||
variant={control.implemented === "IMPLEMENTED" ? "success" : "danger"}
|
variant={control.maturityLevel !== "NONE" ? "success" : "neutral"}
|
||||||
size="sm"
|
size="sm"
|
||||||
>
|
>
|
||||||
{control.implemented === "IMPLEMENTED" ? __("Yes") : __("No")}
|
{getControlMaturityLevelLabel(__, control.maturityLevel)}
|
||||||
</Badge>
|
</Badge>
|
||||||
{control.implemented === "NOT_IMPLEMENTED" && control.notImplementedJustification && (
|
{control.maturityLevel === "NONE" && control.notImplementedJustification && (
|
||||||
<p className="text-xs text-txt-secondary break-words">
|
<p className="text-xs text-txt-secondary break-words">
|
||||||
{control.notImplementedJustification}
|
{control.notImplementedJustification}
|
||||||
</p>
|
</p>
|
||||||
@@ -306,17 +303,6 @@ export default function StatementOfApplicabilityControlsTab({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
|
||||||
{control.applicability === false
|
|
||||||
? <span className="text-txt-tertiary">-</span>
|
|
||||||
: (
|
|
||||||
<Badge variant="neutral" size="sm">
|
|
||||||
{control.maturityLevel
|
|
||||||
? getControlMaturityLevelLabel(__, control.maturityLevel)
|
|
||||||
: __("Not set")}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</Td>
|
|
||||||
<Td>
|
<Td>
|
||||||
{control.applicability === false
|
{control.applicability === false
|
||||||
? <span className="text-txt-tertiary">-</span>
|
? <span className="text-txt-tertiary">-</span>
|
||||||
|
|||||||
@@ -370,7 +370,7 @@ type snapshotControl struct {
|
|||||||
applicability bool
|
applicability bool
|
||||||
justification *string
|
justification *string
|
||||||
bestPractice bool
|
bestPractice bool
|
||||||
implemented string
|
maturityLevel string
|
||||||
notImplementedJustification *string
|
notImplementedJustification *string
|
||||||
hasLegal bool
|
hasLegal bool
|
||||||
hasContractual bool
|
hasContractual bool
|
||||||
@@ -403,7 +403,7 @@ SELECT
|
|||||||
stmt.applicability,
|
stmt.applicability,
|
||||||
stmt.justification,
|
stmt.justification,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
c.maturity_level,
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1 FROM controls_obligations co
|
SELECT 1 FROM controls_obligations co
|
||||||
@@ -442,7 +442,7 @@ ORDER BY f.name, c.section_title;
|
|||||||
&sc.applicability,
|
&sc.applicability,
|
||||||
&sc.justification,
|
&sc.justification,
|
||||||
&sc.bestPractice,
|
&sc.bestPractice,
|
||||||
&sc.implemented,
|
&sc.maturityLevel,
|
||||||
&sc.notImplementedJustification,
|
&sc.notImplementedJustification,
|
||||||
&sc.hasLegal,
|
&sc.hasLegal,
|
||||||
&sc.hasContractual,
|
&sc.hasContractual,
|
||||||
@@ -458,17 +458,13 @@ ORDER BY f.name, c.section_title;
|
|||||||
justification = *sc.justification
|
justification = *sc.justification
|
||||||
}
|
}
|
||||||
|
|
||||||
implemented := "-"
|
maturityLevel := "-"
|
||||||
if applicable {
|
if applicable {
|
||||||
if sc.implemented == "IMPLEMENTED" {
|
maturityLevel = docgen.MaturityLabel(coredata.ControlMaturityLevel(sc.maturityLevel))
|
||||||
implemented = "Yes"
|
|
||||||
} else {
|
|
||||||
implemented = "No"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notImplJustification := "-"
|
notImplJustification := "-"
|
||||||
if applicable && sc.implemented != "IMPLEMENTED" && sc.notImplementedJustification != nil {
|
if applicable && sc.maturityLevel == "NONE" && sc.notImplementedJustification != nil {
|
||||||
notImplJustification = *sc.notImplementedJustification
|
notImplJustification = *sc.notImplementedJustification
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,7 +485,7 @@ ORDER BY f.name, c.section_title;
|
|||||||
ControlName: sc.controlName,
|
ControlName: sc.controlName,
|
||||||
Applicability: docgen.BoolLabel(applicable),
|
Applicability: docgen.BoolLabel(applicable),
|
||||||
Justification: justification,
|
Justification: justification,
|
||||||
Implemented: implemented,
|
MaturityLevel: maturityLevel,
|
||||||
NotImplJustification: notImplJustification,
|
NotImplJustification: notImplJustification,
|
||||||
Regulatory: regulatory,
|
Regulatory: regulatory,
|
||||||
Contractual: contractual,
|
Contractual: contractual,
|
||||||
|
|||||||
@@ -59,12 +59,12 @@ func TestControl_Create(t *testing.T) {
|
|||||||
|
|
||||||
err := owner.Execute(query, map[string]any{
|
err := owner.Execute(query, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"sectionTitle": "A.5",
|
"sectionTitle": "A.5",
|
||||||
"name": "Information Security Policies",
|
"name": "Information Security Policies",
|
||||||
"description": "Policies for information security",
|
"description": "Policies for information security",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &result)
|
}, &result)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -266,11 +266,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
|||||||
name: "Missing frameworkId should fail",
|
name: "Missing frameworkId should fail",
|
||||||
variables: map[string]any{
|
variables: map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"name": "Test Control",
|
"name": "Test Control",
|
||||||
"description": "Test",
|
"description": "Test",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantError: true,
|
wantError: true,
|
||||||
@@ -279,11 +279,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
|||||||
name: "Missing name should fail",
|
name: "Missing name should fail",
|
||||||
variables: map[string]any{
|
variables: map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"description": "Test",
|
"description": "Test",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantError: true,
|
wantError: true,
|
||||||
@@ -292,11 +292,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
|||||||
name: "Missing sectionTitle should fail",
|
name: "Missing sectionTitle should fail",
|
||||||
variables: map[string]any{
|
variables: map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Test Control",
|
"name": "Test Control",
|
||||||
"description": "Test",
|
"description": "Test",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantError: true,
|
wantError: true,
|
||||||
@@ -305,11 +305,11 @@ func TestControl_RequiredFields(t *testing.T) {
|
|||||||
name: "Missing description should fail (required field)",
|
name: "Missing description should fail (required field)",
|
||||||
variables: map[string]any{
|
variables: map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Test Control",
|
"name": "Test Control",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantError: true,
|
wantError: true,
|
||||||
@@ -318,17 +318,17 @@ func TestControl_RequiredFields(t *testing.T) {
|
|||||||
name: "Missing bestPractice should fail",
|
name: "Missing bestPractice should fail",
|
||||||
variables: map[string]any{
|
variables: map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Test Control",
|
"name": "Test Control",
|
||||||
"description": "Test",
|
"description": "Test",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantError: true,
|
wantError: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Missing implemented should fail",
|
name: "Missing maturityLevel should fail",
|
||||||
variables: map[string]any{
|
variables: map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
@@ -418,12 +418,12 @@ func TestControl_OmittableDescription(t *testing.T) {
|
|||||||
|
|
||||||
err = owner.Execute(createControlQuery, map[string]any{
|
err = owner.Execute(createControlQuery, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Omittable Test Control",
|
"name": "Omittable Test Control",
|
||||||
"description": "Initial description",
|
"description": "Initial description",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &createResult)
|
}, &createResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -555,8 +555,8 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
CreateControl struct {
|
CreateControl struct {
|
||||||
ControlEdge struct {
|
ControlEdge struct {
|
||||||
Node struct {
|
Node struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
MaturityLevel *string `json:"maturityLevel"`
|
MaturityLevel string `json:"maturityLevel"`
|
||||||
} `json:"node"`
|
} `json:"node"`
|
||||||
} `json:"controlEdge"`
|
} `json:"controlEdge"`
|
||||||
} `json:"createControl"`
|
} `json:"createControl"`
|
||||||
@@ -565,26 +565,26 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
type updateResult struct {
|
type updateResult struct {
|
||||||
UpdateControl struct {
|
UpdateControl struct {
|
||||||
Control struct {
|
Control struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
MaturityLevel *string `json:"maturityLevel"`
|
MaturityLevel string `json:"maturityLevel"`
|
||||||
} `json:"control"`
|
} `json:"control"`
|
||||||
} `json:"updateControl"`
|
} `json:"updateControl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("create without maturityLevel returns null", func(t *testing.T) {
|
t.Run("create with INITIAL maturityLevel", func(t *testing.T) {
|
||||||
var res createResult
|
var res createResult
|
||||||
err := owner.Execute(createControlQuery, map[string]any{
|
err := owner.Execute(createControlQuery, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"sectionTitle": "M.1",
|
"sectionTitle": "M.1",
|
||||||
"name": "Control without maturity",
|
"name": "Control with initial maturity",
|
||||||
"description": "control without maturity description",
|
"description": "control with initial maturity description",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &res)
|
}, &res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Nil(t, res.CreateControl.ControlEdge.Node.MaturityLevel)
|
assert.Equal(t, "INITIAL", res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("create with maturityLevel persists value", func(t *testing.T) {
|
t.Run("create with maturityLevel persists value", func(t *testing.T) {
|
||||||
@@ -596,25 +596,23 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
"name": "Control with maturity",
|
"name": "Control with maturity",
|
||||||
"description": "control with maturity description",
|
"description": "control with maturity description",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
|
||||||
"maturityLevel": "DEFINED",
|
"maturityLevel": "DEFINED",
|
||||||
},
|
},
|
||||||
}, &res)
|
}, &res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, res.CreateControl.ControlEdge.Node.MaturityLevel)
|
assert.Equal(t, "DEFINED", res.CreateControl.ControlEdge.Node.MaturityLevel)
|
||||||
assert.Equal(t, "DEFINED", *res.CreateControl.ControlEdge.Node.MaturityLevel)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update lifecycle: set, change, clear, omit", func(t *testing.T) {
|
t.Run("update lifecycle: set, change, omit", func(t *testing.T) {
|
||||||
var created createResult
|
var created createResult
|
||||||
err := owner.Execute(createControlQuery, map[string]any{
|
err := owner.Execute(createControlQuery, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"sectionTitle": "M.3",
|
"sectionTitle": "M.3",
|
||||||
"name": "Lifecycle control",
|
"name": "Lifecycle control",
|
||||||
"description": "lifecycle control description",
|
"description": "lifecycle control description",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &created)
|
}, &created)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -629,8 +627,7 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, &setRes)
|
}, &setRes)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, setRes.UpdateControl.Control.MaturityLevel)
|
assert.Equal(t, "INITIAL", setRes.UpdateControl.Control.MaturityLevel)
|
||||||
assert.Equal(t, "INITIAL", *setRes.UpdateControl.Control.MaturityLevel)
|
|
||||||
|
|
||||||
// change
|
// change
|
||||||
var changeRes updateResult
|
var changeRes updateResult
|
||||||
@@ -641,30 +638,9 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, &changeRes)
|
}, &changeRes)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, changeRes.UpdateControl.Control.MaturityLevel)
|
assert.Equal(t, "OPTIMIZING", changeRes.UpdateControl.Control.MaturityLevel)
|
||||||
assert.Equal(t, "OPTIMIZING", *changeRes.UpdateControl.Control.MaturityLevel)
|
|
||||||
|
|
||||||
// clear (explicit null)
|
|
||||||
var clearRes updateResult
|
|
||||||
err = owner.Execute(updateControlQuery, map[string]any{
|
|
||||||
"input": map[string]any{
|
|
||||||
"id": controlID,
|
|
||||||
"maturityLevel": nil,
|
|
||||||
},
|
|
||||||
}, &clearRes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Nil(t, clearRes.UpdateControl.Control.MaturityLevel)
|
|
||||||
|
|
||||||
// set again, then omit field on next update -> stays unchanged
|
|
||||||
var setAgain updateResult
|
|
||||||
err = owner.Execute(updateControlQuery, map[string]any{
|
|
||||||
"input": map[string]any{
|
|
||||||
"id": controlID,
|
|
||||||
"maturityLevel": "MANAGED",
|
|
||||||
},
|
|
||||||
}, &setAgain)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
|
// omit field on next update -> stays unchanged
|
||||||
var omitRes updateResult
|
var omitRes updateResult
|
||||||
err = owner.Execute(updateControlQuery, map[string]any{
|
err = owner.Execute(updateControlQuery, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
@@ -673,8 +649,7 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, &omitRes)
|
}, &omitRes)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, omitRes.UpdateControl.Control.MaturityLevel)
|
assert.Equal(t, "OPTIMIZING", omitRes.UpdateControl.Control.MaturityLevel)
|
||||||
assert.Equal(t, "MANAGED", *omitRes.UpdateControl.Control.MaturityLevel)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid maturityLevel is rejected", func(t *testing.T) {
|
t.Run("invalid maturityLevel is rejected", func(t *testing.T) {
|
||||||
@@ -686,7 +661,6 @@ func TestControl_MaturityLevel(t *testing.T) {
|
|||||||
"name": "Bad maturity",
|
"name": "Bad maturity",
|
||||||
"description": "bad maturity description",
|
"description": "bad maturity description",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
|
||||||
"maturityLevel": "BOGUS",
|
"maturityLevel": "BOGUS",
|
||||||
},
|
},
|
||||||
}, &res)
|
}, &res)
|
||||||
@@ -755,12 +729,12 @@ func TestControl_SubResolvers(t *testing.T) {
|
|||||||
|
|
||||||
err = owner.Execute(createControlQuery, map[string]any{
|
err = owner.Execute(createControlQuery, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "SubResolver Test Control",
|
"name": "SubResolver Test Control",
|
||||||
"description": "Test description",
|
"description": "Test description",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &controlResult)
|
}, &controlResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -78,12 +78,12 @@ func TestControlMeasureMapping_CreateDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`, map[string]any{
|
`, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Control for Mapping",
|
"name": "Control for Mapping",
|
||||||
"description": "Test control for mapping",
|
"description": "Test control for mapping",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &createControlResult)
|
}, &createControlResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -358,12 +358,12 @@ func TestControlDocumentMapping_CreateDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`, map[string]any{
|
`, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Control for Document Mapping",
|
"name": "Control for Document Mapping",
|
||||||
"description": "Test control",
|
"description": "Test control",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &createControlResult)
|
}, &createControlResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -498,12 +498,12 @@ func TestControlAuditMapping_CreateDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`, map[string]any{
|
`, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Control for Audit Mapping",
|
"name": "Control for Audit Mapping",
|
||||||
"description": "Test control",
|
"description": "Test control",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &createControlResult)
|
}, &createControlResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -636,12 +636,12 @@ func TestControlSnapshotMapping_CreateDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
`, map[string]any{
|
`, map[string]any{
|
||||||
"input": map[string]any{
|
"input": map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": "Control for Snapshot Mapping",
|
"name": "Control for Snapshot Mapping",
|
||||||
"description": "Test control",
|
"description": "Test control",
|
||||||
"sectionTitle": "Section 1",
|
"sectionTitle": "Section 1",
|
||||||
"bestPractice": true,
|
"bestPractice": true,
|
||||||
"implemented": "IMPLEMENTED",
|
"maturityLevel": "INITIAL",
|
||||||
},
|
},
|
||||||
}, &createControlResult)
|
}, &createControlResult)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -446,7 +446,7 @@ func TestRBAC(t *testing.T) {
|
|||||||
client: owner,
|
client: owner,
|
||||||
query: createControlMutation,
|
query: createControlMutation,
|
||||||
variables: func() map[string]any {
|
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"), "bestPractice": true, "implemented": "IMPLEMENTED"}}
|
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Owner"), "bestPractice": true, "maturityLevel": "INITIAL"}}
|
||||||
},
|
},
|
||||||
shouldAllow: true,
|
shouldAllow: true,
|
||||||
},
|
},
|
||||||
@@ -456,7 +456,7 @@ func TestRBAC(t *testing.T) {
|
|||||||
client: admin,
|
client: admin,
|
||||||
query: createControlMutation,
|
query: createControlMutation,
|
||||||
variables: func() map[string]any {
|
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"), "bestPractice": true, "implemented": "IMPLEMENTED"}}
|
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Admin"), "bestPractice": true, "maturityLevel": "INITIAL"}}
|
||||||
},
|
},
|
||||||
shouldAllow: true,
|
shouldAllow: true,
|
||||||
},
|
},
|
||||||
@@ -466,7 +466,7 @@ func TestRBAC(t *testing.T) {
|
|||||||
client: viewer,
|
client: viewer,
|
||||||
query: createControlMutation,
|
query: createControlMutation,
|
||||||
variables: func() map[string]any {
|
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"), "bestPractice": true, "implemented": "IMPLEMENTED"}}
|
return map[string]any{"input": map[string]any{"frameworkId": frameworkID, "name": factory.SafeName("Control"), "description": "Test", "sectionTitle": factory.SafeName("Section Viewer"), "bestPractice": true, "maturityLevel": "INITIAL"}}
|
||||||
},
|
},
|
||||||
shouldAllow: false,
|
shouldAllow: false,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -248,12 +248,12 @@ func CreateControl(c *testutil.Client, frameworkID string, attrs ...Attrs) strin
|
|||||||
`
|
`
|
||||||
|
|
||||||
input := map[string]any{
|
input := map[string]any{
|
||||||
"frameworkId": frameworkID,
|
"frameworkId": frameworkID,
|
||||||
"name": a.getString("name", SafeName("Control")),
|
"name": a.getString("name", SafeName("Control")),
|
||||||
"description": a.getString("description", "Test control description"),
|
"description": a.getString("description", "Test control description"),
|
||||||
"sectionTitle": a.getString("sectionTitle", fmt.Sprintf("Section %s", gofakeit.LetterN(3))),
|
"sectionTitle": a.getString("sectionTitle", fmt.Sprintf("Section %s", gofakeit.LetterN(3))),
|
||||||
"bestPractice": a.getBool("bestPractice", true),
|
"bestPractice": a.getBool("bestPractice", true),
|
||||||
"implemented": a.getString("implemented", "IMPLEMENTED"),
|
"maturityLevel": a.getString("maturityLevel", "INITIAL"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if justification := a.getStringPtr("notImplementedJustification"); justification != nil {
|
if justification := a.getStringPtr("notImplementedJustification"); justification != nil {
|
||||||
@@ -502,8 +502,8 @@ func (b *ControlBuilder) WithBestPractice(bestPractice bool) *ControlBuilder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *ControlBuilder) WithImplemented(implemented string) *ControlBuilder {
|
func (b *ControlBuilder) WithMaturityLevel(maturityLevel string) *ControlBuilder {
|
||||||
b.attrs["implemented"] = implemented
|
b.attrs["maturityLevel"] = maturityLevel
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export async function execute(
|
|||||||
sectionTitle
|
sectionTitle
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
createdAt
|
createdAt
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ export async function execute(
|
|||||||
sectionTitle
|
sectionTitle
|
||||||
name
|
name
|
||||||
description
|
description
|
||||||
implemented
|
|
||||||
maturityLevel
|
maturityLevel
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ mutation($input: CreateControlInput!) {
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
}
|
}
|
||||||
@@ -60,9 +59,8 @@ type createResponse struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
BestPractice bool `json:"bestPractice"`
|
BestPractice bool `json:"bestPractice"`
|
||||||
Implemented string `json:"implemented"`
|
|
||||||
NotImplementedJustification *string `json:"notImplementedJustification"`
|
NotImplementedJustification *string `json:"notImplementedJustification"`
|
||||||
MaturityLevel *string `json:"maturityLevel"`
|
MaturityLevel string `json:"maturityLevel"`
|
||||||
} `json:"node"`
|
} `json:"node"`
|
||||||
} `json:"controlEdge"`
|
} `json:"controlEdge"`
|
||||||
} `json:"createControl"`
|
} `json:"createControl"`
|
||||||
@@ -75,16 +73,15 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
flagName string
|
flagName string
|
||||||
flagDescription string
|
flagDescription string
|
||||||
flagBestPractice bool
|
flagBestPractice bool
|
||||||
flagNotImplemented bool
|
|
||||||
flagNotImplementedJustification string
|
|
||||||
flagMaturityLevel string
|
flagMaturityLevel string
|
||||||
|
flagNotImplementedJustification string
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "create",
|
Use: "create",
|
||||||
Short: "Create a new control",
|
Short: "Create a new control",
|
||||||
Example: ` # Create a control
|
Example: ` # Create a control
|
||||||
prb control create --framework FW_ID --section-title "A.5" --name "Information security policies"`,
|
prb control create --framework FW_ID --section-title "A.5" --name "Information security policies" --maturity-level INITIAL`,
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cfg, err := f.Config()
|
cfg, err := f.Config()
|
||||||
@@ -105,34 +102,26 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
cmdutil.TokenRefreshOption(cfg, host, hc),
|
cmdutil.TokenRefreshOption(cfg, host, hc),
|
||||||
)
|
)
|
||||||
|
|
||||||
implemented := "IMPLEMENTED"
|
if err := cmdutil.ValidateEnum("maturity-level", flagMaturityLevel, maturityLevelValues); err != nil {
|
||||||
if flagNotImplemented {
|
return err
|
||||||
implemented = "NOT_IMPLEMENTED"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input := map[string]any{
|
input := map[string]any{
|
||||||
"frameworkId": flagFramework,
|
"frameworkId": flagFramework,
|
||||||
"sectionTitle": flagSectionTitle,
|
"sectionTitle": flagSectionTitle,
|
||||||
"name": flagName,
|
"name": flagName,
|
||||||
"bestPractice": flagBestPractice,
|
"bestPractice": flagBestPractice,
|
||||||
"implemented": implemented,
|
"maturityLevel": flagMaturityLevel,
|
||||||
}
|
}
|
||||||
|
|
||||||
if flagDescription != "" {
|
if flagDescription != "" {
|
||||||
input["description"] = flagDescription
|
input["description"] = flagDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
if flagNotImplemented && flagNotImplementedJustification != "" {
|
if flagMaturityLevel == "NONE" && flagNotImplementedJustification != "" {
|
||||||
input["notImplementedJustification"] = flagNotImplementedJustification
|
input["notImplementedJustification"] = flagNotImplementedJustification
|
||||||
}
|
}
|
||||||
|
|
||||||
if flagMaturityLevel != "" {
|
|
||||||
if err := cmdutil.ValidateEnum("maturity-level", flagMaturityLevel, maturityLevelValues); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
input["maturityLevel"] = flagMaturityLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := client.Do(
|
data, err := client.Do(
|
||||||
createMutation,
|
createMutation,
|
||||||
map[string]any{"input": input},
|
map[string]any{"input": input},
|
||||||
@@ -163,9 +152,8 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
cmd.Flags().StringVar(&flagName, "name", "", "Control name (required)")
|
cmd.Flags().StringVar(&flagName, "name", "", "Control name (required)")
|
||||||
cmd.Flags().StringVar(&flagDescription, "description", "", "Control description")
|
cmd.Flags().StringVar(&flagDescription, "description", "", "Control description")
|
||||||
cmd.Flags().BoolVar(&flagBestPractice, "best-practice", false, "Mark as best practice")
|
cmd.Flags().BoolVar(&flagBestPractice, "best-practice", false, "Mark as best practice")
|
||||||
cmd.Flags().BoolVar(&flagNotImplemented, "not-implemented", false, "Mark as not implemented")
|
cmd.Flags().StringVar(&flagMaturityLevel, "maturity-level", "INITIAL", "CMMI maturity level (NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING)")
|
||||||
cmd.Flags().StringVar(&flagNotImplementedJustification, "not-implemented-justification", "", "Justification for non-implementation")
|
cmd.Flags().StringVar(&flagNotImplementedJustification, "not-implemented-justification", "", "Justification when maturity level is NONE")
|
||||||
cmd.Flags().StringVar(&flagMaturityLevel, "maturity-level", "", "CMMI maturity level (NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING)")
|
|
||||||
|
|
||||||
_ = cmd.MarkFlagRequired("framework")
|
_ = cmd.MarkFlagRequired("framework")
|
||||||
_ = cmd.MarkFlagRequired("section-title")
|
_ = cmd.MarkFlagRequired("section-title")
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.probo.inc/probo/pkg/cli/api"
|
"go.probo.inc/probo/pkg/cli/api"
|
||||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||||
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
|
"go.probo.inc/probo/pkg/docgen"
|
||||||
)
|
)
|
||||||
|
|
||||||
const listQuery = `
|
const listQuery = `
|
||||||
@@ -37,7 +39,6 @@ query($id: ID!, $first: Int, $after: CursorKey, $orderBy: ControlOrder, $filter:
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
maturityLevel
|
maturityLevel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,8 +58,7 @@ type control struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
BestPractice bool `json:"bestPractice"`
|
BestPractice bool `json:"bestPractice"`
|
||||||
Implemented string `json:"implemented"`
|
MaturityLevel string `json:"maturityLevel"`
|
||||||
MaturityLevel *string `json:"maturityLevel"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
||||||
@@ -167,16 +167,12 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
|
|||||||
if c.BestPractice {
|
if c.BestPractice {
|
||||||
bp = "Yes"
|
bp = "Yes"
|
||||||
}
|
}
|
||||||
maturity := "-"
|
|
||||||
if c.MaturityLevel != nil {
|
|
||||||
maturity = *c.MaturityLevel
|
|
||||||
}
|
|
||||||
rows = append(rows, []string{
|
rows = append(rows, []string{
|
||||||
c.ID,
|
c.ID,
|
||||||
c.SectionTitle,
|
c.SectionTitle,
|
||||||
c.Name,
|
c.Name,
|
||||||
bp,
|
bp,
|
||||||
maturity,
|
docgen.MaturityLabel(coredata.ControlMaturityLevel(c.MaturityLevel)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ mutation($input: UpdateControlInput!) {
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
}
|
}
|
||||||
@@ -48,9 +47,8 @@ type updateResponse struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
BestPractice bool `json:"bestPractice"`
|
BestPractice bool `json:"bestPractice"`
|
||||||
Implemented string `json:"implemented"`
|
|
||||||
NotImplementedJustification *string `json:"notImplementedJustification"`
|
NotImplementedJustification *string `json:"notImplementedJustification"`
|
||||||
MaturityLevel *string `json:"maturityLevel"`
|
MaturityLevel string `json:"maturityLevel"`
|
||||||
} `json:"control"`
|
} `json:"control"`
|
||||||
} `json:"updateControl"`
|
} `json:"updateControl"`
|
||||||
}
|
}
|
||||||
@@ -70,9 +68,8 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
flagName string
|
flagName string
|
||||||
flagDescription string
|
flagDescription string
|
||||||
flagBestPractice bool
|
flagBestPractice bool
|
||||||
flagNotImplemented bool
|
|
||||||
flagNotImplementedJustification string
|
|
||||||
flagMaturityLevel string
|
flagMaturityLevel string
|
||||||
|
flagNotImplementedJustification string
|
||||||
)
|
)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
@@ -118,12 +115,11 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
if cmd.Flags().Changed("best-practice") {
|
if cmd.Flags().Changed("best-practice") {
|
||||||
input["bestPractice"] = flagBestPractice
|
input["bestPractice"] = flagBestPractice
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("not-implemented") {
|
if cmd.Flags().Changed("maturity-level") {
|
||||||
if flagNotImplemented {
|
if err := cmdutil.ValidateEnum("maturity-level", flagMaturityLevel, maturityLevelValues); err != nil {
|
||||||
input["implemented"] = "NOT_IMPLEMENTED"
|
return err
|
||||||
} else {
|
|
||||||
input["implemented"] = "IMPLEMENTED"
|
|
||||||
}
|
}
|
||||||
|
input["maturityLevel"] = flagMaturityLevel
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("not-implemented-justification") {
|
if cmd.Flags().Changed("not-implemented-justification") {
|
||||||
if flagNotImplementedJustification == "" {
|
if flagNotImplementedJustification == "" {
|
||||||
@@ -132,16 +128,6 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
input["notImplementedJustification"] = flagNotImplementedJustification
|
input["notImplementedJustification"] = flagNotImplementedJustification
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cmd.Flags().Changed("maturity-level") {
|
|
||||||
if flagMaturityLevel == "" {
|
|
||||||
input["maturityLevel"] = nil
|
|
||||||
} else {
|
|
||||||
if err := cmdutil.ValidateEnum("maturity-level", flagMaturityLevel, maturityLevelValues); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
input["maturityLevel"] = flagMaturityLevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(input) == 1 {
|
if len(input) == 1 {
|
||||||
return fmt.Errorf("at least one field must be specified for update")
|
return fmt.Errorf("at least one field must be specified for update")
|
||||||
@@ -176,9 +162,8 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
|
|||||||
cmd.Flags().StringVar(&flagName, "name", "", "Control name")
|
cmd.Flags().StringVar(&flagName, "name", "", "Control name")
|
||||||
cmd.Flags().StringVar(&flagDescription, "description", "", "Control description")
|
cmd.Flags().StringVar(&flagDescription, "description", "", "Control description")
|
||||||
cmd.Flags().BoolVar(&flagBestPractice, "best-practice", false, "Mark as best practice")
|
cmd.Flags().BoolVar(&flagBestPractice, "best-practice", false, "Mark as best practice")
|
||||||
cmd.Flags().BoolVar(&flagNotImplemented, "not-implemented", false, "Mark as not implemented")
|
cmd.Flags().StringVar(&flagMaturityLevel, "maturity-level", "", "CMMI maturity level (NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING)")
|
||||||
cmd.Flags().StringVar(&flagNotImplementedJustification, "not-implemented-justification", "", "Justification for non-implementation")
|
cmd.Flags().StringVar(&flagNotImplementedJustification, "not-implemented-justification", "", "Justification when maturity level is NONE")
|
||||||
cmd.Flags().StringVar(&flagMaturityLevel, "maturity-level", "", "CMMI maturity level (NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING). Empty string clears the value.")
|
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ import (
|
|||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"go.probo.inc/probo/pkg/cli/api"
|
"go.probo.inc/probo/pkg/cli/api"
|
||||||
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
"go.probo.inc/probo/pkg/cmd/cmdutil"
|
||||||
|
"go.probo.inc/probo/pkg/coredata"
|
||||||
|
"go.probo.inc/probo/pkg/docgen"
|
||||||
)
|
)
|
||||||
|
|
||||||
const viewQuery = `
|
const viewQuery = `
|
||||||
@@ -34,7 +36,6 @@ query($id: ID!) {
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
bestPractice
|
bestPractice
|
||||||
implemented
|
|
||||||
notImplementedJustification
|
notImplementedJustification
|
||||||
maturityLevel
|
maturityLevel
|
||||||
framework {
|
framework {
|
||||||
@@ -56,9 +57,8 @@ type viewResponse struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
BestPractice bool `json:"bestPractice"`
|
BestPractice bool `json:"bestPractice"`
|
||||||
Implemented string `json:"implemented"`
|
|
||||||
NotImplementedJustification *string `json:"notImplementedJustification"`
|
NotImplementedJustification *string `json:"notImplementedJustification"`
|
||||||
MaturityLevel *string `json:"maturityLevel"`
|
MaturityLevel string `json:"maturityLevel"`
|
||||||
Framework struct {
|
Framework struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@@ -144,17 +144,11 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
|
|||||||
bp = "Yes"
|
bp = "Yes"
|
||||||
}
|
}
|
||||||
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Best Practice:"), bp)
|
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Best Practice:"), bp)
|
||||||
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Implemented:"), c.Implemented)
|
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Maturity:"), docgen.MaturityLabel(coredata.ControlMaturityLevel(c.MaturityLevel)))
|
||||||
if c.Implemented == "NOT_IMPLEMENTED" && c.NotImplementedJustification != nil && *c.NotImplementedJustification != "" {
|
if c.MaturityLevel == "NONE" && c.NotImplementedJustification != nil && *c.NotImplementedJustification != "" {
|
||||||
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Justification:"), *c.NotImplementedJustification)
|
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Justification:"), *c.NotImplementedJustification)
|
||||||
}
|
}
|
||||||
|
|
||||||
maturity := "Not set"
|
|
||||||
if c.MaturityLevel != nil {
|
|
||||||
maturity = *c.MaturityLevel
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Maturity:"), maturity)
|
|
||||||
|
|
||||||
_, _ = fmt.Fprintln(out)
|
_, _ = fmt.Fprintln(out)
|
||||||
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Created:"), cmdutil.FormatTime(c.CreatedAt))
|
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Created:"), cmdutil.FormatTime(c.CreatedAt))
|
||||||
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Updated:"), cmdutil.FormatTime(c.UpdatedAt))
|
_, _ = fmt.Fprintf(out, "%s%s\n", label.Render("Updated:"), cmdutil.FormatTime(c.UpdatedAt))
|
||||||
|
|||||||
@@ -30,18 +30,17 @@ import (
|
|||||||
|
|
||||||
type (
|
type (
|
||||||
Control struct {
|
Control struct {
|
||||||
ID gid.GID `db:"id"`
|
ID gid.GID `db:"id"`
|
||||||
OrganizationID gid.GID `db:"organization_id"`
|
OrganizationID gid.GID `db:"organization_id"`
|
||||||
SectionTitle string `db:"section_title"`
|
SectionTitle string `db:"section_title"`
|
||||||
FrameworkID gid.GID `db:"framework_id"`
|
FrameworkID gid.GID `db:"framework_id"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
Description *string `db:"description"`
|
Description *string `db:"description"`
|
||||||
BestPractice bool `db:"best_practice"`
|
BestPractice bool `db:"best_practice"`
|
||||||
Implemented ControlImplementationState `db:"implemented"`
|
NotImplementedJustification *string `db:"not_implemented_justification"`
|
||||||
NotImplementedJustification *string `db:"not_implemented_justification"`
|
MaturityLevel ControlMaturityLevel `db:"maturity_level"`
|
||||||
MaturityLevel *ControlMaturityLevel `db:"maturity_level"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
UpdatedAt time.Time `db:"updated_at"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Controls []*Control
|
Controls []*Control
|
||||||
@@ -134,7 +133,6 @@ WITH ctrl AS (
|
|||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
c.maturity_level,
|
c.maturity_level,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
@@ -155,7 +153,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -249,7 +246,6 @@ WITH ctrl AS (
|
|||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
c.maturity_level,
|
c.maturity_level,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
@@ -270,7 +266,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -370,7 +365,6 @@ WITH ctrl AS (
|
|||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
c.maturity_level,
|
c.maturity_level,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
@@ -397,7 +391,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -479,7 +472,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -576,7 +568,6 @@ WITH ctrl AS (
|
|||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
c.maturity_level,
|
c.maturity_level,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
@@ -597,7 +588,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -646,7 +636,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -697,7 +686,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -747,7 +735,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -794,7 +781,6 @@ INSERT INTO
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -809,7 +795,6 @@ VALUES (
|
|||||||
@name,
|
@name,
|
||||||
@description,
|
@description,
|
||||||
@best_practice,
|
@best_practice,
|
||||||
@implemented,
|
|
||||||
@not_implemented_justification,
|
@not_implemented_justification,
|
||||||
@maturity_level,
|
@maturity_level,
|
||||||
@created_at,
|
@created_at,
|
||||||
@@ -826,7 +811,6 @@ VALUES (
|
|||||||
"name": c.Name,
|
"name": c.Name,
|
||||||
"description": c.Description,
|
"description": c.Description,
|
||||||
"best_practice": c.BestPractice,
|
"best_practice": c.BestPractice,
|
||||||
"implemented": c.Implemented,
|
|
||||||
"not_implemented_justification": c.NotImplementedJustification,
|
"not_implemented_justification": c.NotImplementedJustification,
|
||||||
"maturity_level": c.MaturityLevel,
|
"maturity_level": c.MaturityLevel,
|
||||||
"created_at": c.CreatedAt,
|
"created_at": c.CreatedAt,
|
||||||
@@ -880,7 +864,6 @@ UPDATE controls SET
|
|||||||
description = @description,
|
description = @description,
|
||||||
section_title = @section_title,
|
section_title = @section_title,
|
||||||
best_practice = @best_practice,
|
best_practice = @best_practice,
|
||||||
implemented = @implemented,
|
|
||||||
not_implemented_justification = @not_implemented_justification,
|
not_implemented_justification = @not_implemented_justification,
|
||||||
maturity_level = @maturity_level,
|
maturity_level = @maturity_level,
|
||||||
updated_at = @updated_at
|
updated_at = @updated_at
|
||||||
@@ -895,7 +878,6 @@ WHERE %s
|
|||||||
"description": c.Description,
|
"description": c.Description,
|
||||||
"section_title": c.SectionTitle,
|
"section_title": c.SectionTitle,
|
||||||
"best_practice": c.BestPractice,
|
"best_practice": c.BestPractice,
|
||||||
"implemented": c.Implemented,
|
|
||||||
"not_implemented_justification": c.NotImplementedJustification,
|
"not_implemented_justification": c.NotImplementedJustification,
|
||||||
"maturity_level": c.MaturityLevel,
|
"maturity_level": c.MaturityLevel,
|
||||||
"updated_at": c.UpdatedAt,
|
"updated_at": c.UpdatedAt,
|
||||||
@@ -936,7 +918,6 @@ WITH ctrl AS (
|
|||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
c.maturity_level,
|
c.maturity_level,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
@@ -957,7 +938,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -1009,7 +989,6 @@ WITH ctrl AS (
|
|||||||
c.name,
|
c.name,
|
||||||
c.description,
|
c.description,
|
||||||
c.best_practice,
|
c.best_practice,
|
||||||
c.implemented,
|
|
||||||
c.not_implemented_justification,
|
c.not_implemented_justification,
|
||||||
c.maturity_level,
|
c.maturity_level,
|
||||||
c.created_at,
|
c.created_at,
|
||||||
@@ -1030,7 +1009,6 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
best_practice,
|
best_practice,
|
||||||
implemented,
|
|
||||||
not_implemented_justification,
|
not_implemented_justification,
|
||||||
maturity_level,
|
maturity_level,
|
||||||
created_at,
|
created_at,
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
// Copyright (c) 2026 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 (
|
|
||||||
ControlImplementationState string
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ControlImplementationStateImplemented ControlImplementationState = "IMPLEMENTED"
|
|
||||||
ControlImplementationStateNotImplemented ControlImplementationState = "NOT_IMPLEMENTED"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (s ControlImplementationState) IsValid() bool {
|
|
||||||
switch s {
|
|
||||||
case ControlImplementationStateImplemented, ControlImplementationStateNotImplemented:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ControlImplementationState) String() string {
|
|
||||||
return string(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ControlImplementationState) MarshalText() ([]byte, error) {
|
|
||||||
return []byte(s.String()), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ControlImplementationState) UnmarshalText(data []byte) error {
|
|
||||||
val := ControlImplementationState(data)
|
|
||||||
if !val.IsValid() {
|
|
||||||
return fmt.Errorf("invalid ControlImplementationState value: %q", string(data))
|
|
||||||
}
|
|
||||||
*s = val
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ControlImplementationState) Scan(value any) error {
|
|
||||||
val, ok := value.(string)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid scan source for ControlImplementationState, expected string got %T", value)
|
|
||||||
}
|
|
||||||
return s.UnmarshalText([]byte(val))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s ControlImplementationState) Value() (driver.Value, error) {
|
|
||||||
return s.String(), nil
|
|
||||||
}
|
|
||||||
@@ -32,6 +32,17 @@ const (
|
|||||||
ControlMaturityLevelOptimizing ControlMaturityLevel = "OPTIMIZING"
|
ControlMaturityLevelOptimizing ControlMaturityLevel = "OPTIMIZING"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ControlMaturityLevels() []ControlMaturityLevel {
|
||||||
|
return []ControlMaturityLevel{
|
||||||
|
ControlMaturityLevelNone,
|
||||||
|
ControlMaturityLevelInitial,
|
||||||
|
ControlMaturityLevelManaged,
|
||||||
|
ControlMaturityLevelDefined,
|
||||||
|
ControlMaturityLevelQuantitativelyManaged,
|
||||||
|
ControlMaturityLevelOptimizing,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (l ControlMaturityLevel) IsValid() bool {
|
func (l ControlMaturityLevel) IsValid() bool {
|
||||||
switch l {
|
switch l {
|
||||||
case ControlMaturityLevelNone,
|
case ControlMaturityLevelNone,
|
||||||
|
|||||||
@@ -12,4 +12,25 @@
|
|||||||
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
-- PERFORMANCE OF THIS SOFTWARE.
|
-- PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
ALTER TABLE controls ADD COLUMN maturity_level TEXT;
|
CREATE TYPE control_maturity_level AS ENUM (
|
||||||
|
'NONE',
|
||||||
|
'INITIAL',
|
||||||
|
'MANAGED',
|
||||||
|
'DEFINED',
|
||||||
|
'QUANTITATIVELY_MANAGED',
|
||||||
|
'OPTIMIZING'
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE controls ADD COLUMN maturity_level control_maturity_level;
|
||||||
|
|
||||||
|
UPDATE controls SET maturity_level = CASE
|
||||||
|
WHEN implemented = 'NOT_IMPLEMENTED' THEN 'NONE'::control_maturity_level
|
||||||
|
ELSE 'INITIAL'::control_maturity_level
|
||||||
|
END;
|
||||||
|
|
||||||
|
ALTER TABLE controls ALTER COLUMN maturity_level SET NOT NULL;
|
||||||
|
ALTER TABLE controls ALTER COLUMN implemented DROP NOT NULL;
|
||||||
|
|
||||||
|
-- TODO: drop column and type in a future migration
|
||||||
|
-- ALTER TABLE controls DROP COLUMN implemented;
|
||||||
|
-- DROP TYPE control_implementation_state;
|
||||||
|
|||||||
@@ -289,9 +289,8 @@ type (
|
|||||||
ControlName string
|
ControlName string
|
||||||
Applicability string
|
Applicability string
|
||||||
Justification string
|
Justification string
|
||||||
Implemented string
|
|
||||||
NotImplJustification string
|
|
||||||
MaturityLevel string
|
MaturityLevel string
|
||||||
|
NotImplJustification string
|
||||||
Regulatory string
|
Regulatory string
|
||||||
Contractual string
|
Contractual string
|
||||||
BestPractice string
|
BestPractice string
|
||||||
@@ -321,11 +320,8 @@ func BoolLabel(v bool) string {
|
|||||||
return "No"
|
return "No"
|
||||||
}
|
}
|
||||||
|
|
||||||
func MaturityLabel(l *coredata.ControlMaturityLevel) string {
|
func MaturityLabel(l coredata.ControlMaturityLevel) string {
|
||||||
if l == nil {
|
switch l {
|
||||||
return "Not set"
|
|
||||||
}
|
|
||||||
switch *l {
|
|
||||||
case coredata.ControlMaturityLevelNone:
|
case coredata.ControlMaturityLevelNone:
|
||||||
return "0 - None"
|
return "0 - None"
|
||||||
case coredata.ControlMaturityLevelInitial:
|
case coredata.ControlMaturityLevelInitial:
|
||||||
|
|||||||
@@ -38,9 +38,8 @@ type (
|
|||||||
Description *string
|
Description *string
|
||||||
SectionTitle string
|
SectionTitle string
|
||||||
BestPractice bool
|
BestPractice bool
|
||||||
Implemented coredata.ControlImplementationState
|
MaturityLevel coredata.ControlMaturityLevel
|
||||||
NotImplementedJustification *string
|
NotImplementedJustification *string
|
||||||
MaturityLevel *coredata.ControlMaturityLevel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateControlRequest struct {
|
UpdateControlRequest struct {
|
||||||
@@ -49,9 +48,8 @@ type (
|
|||||||
Description **string
|
Description **string
|
||||||
SectionTitle *string
|
SectionTitle *string
|
||||||
BestPractice *bool
|
BestPractice *bool
|
||||||
Implemented *coredata.ControlImplementationState
|
MaturityLevel *coredata.ControlMaturityLevel
|
||||||
NotImplementedJustification **string
|
NotImplementedJustification **string
|
||||||
MaturityLevel **coredata.ControlMaturityLevel
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,30 +63,12 @@ func (ccr *CreateControlRequest) Validate() error {
|
|||||||
v.Check(ccr.NotImplementedJustification, "not_implemented_justification", validator.SafeText(ContentMaxLength))
|
v.Check(ccr.NotImplementedJustification, "not_implemented_justification", validator.SafeText(ContentMaxLength))
|
||||||
|
|
||||||
v.Check(
|
v.Check(
|
||||||
ccr.Implemented,
|
ccr.MaturityLevel,
|
||||||
"implemented",
|
"maturity_level",
|
||||||
validator.Required(),
|
validator.Required(),
|
||||||
validator.OneOfSlice([]string{
|
validator.OneOfSlice(coredata.ControlMaturityLevels()),
|
||||||
string(coredata.ControlImplementationStateImplemented),
|
|
||||||
string(coredata.ControlImplementationStateNotImplemented),
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if ccr.MaturityLevel != nil {
|
|
||||||
v.Check(
|
|
||||||
*ccr.MaturityLevel,
|
|
||||||
"maturity_level",
|
|
||||||
validator.OneOfSlice([]string{
|
|
||||||
string(coredata.ControlMaturityLevelNone),
|
|
||||||
string(coredata.ControlMaturityLevelInitial),
|
|
||||||
string(coredata.ControlMaturityLevelManaged),
|
|
||||||
string(coredata.ControlMaturityLevelDefined),
|
|
||||||
string(coredata.ControlMaturityLevelQuantitativelyManaged),
|
|
||||||
string(coredata.ControlMaturityLevelOptimizing),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.Error()
|
return v.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,27 +80,12 @@ func (ucr *UpdateControlRequest) Validate() error {
|
|||||||
v.Check(ucr.Description, "description", validator.SafeText(ContentMaxLength))
|
v.Check(ucr.Description, "description", validator.SafeText(ContentMaxLength))
|
||||||
v.Check(ucr.SectionTitle, "section_title", validator.SafeTextNoNewLine(TitleMaxLength))
|
v.Check(ucr.SectionTitle, "section_title", validator.SafeTextNoNewLine(TitleMaxLength))
|
||||||
v.Check(ucr.NotImplementedJustification, "not_implemented_justification", validator.SafeText(ContentMaxLength))
|
v.Check(ucr.NotImplementedJustification, "not_implemented_justification", validator.SafeText(ContentMaxLength))
|
||||||
v.Check(
|
|
||||||
ucr.Implemented,
|
|
||||||
"implemented",
|
|
||||||
validator.OneOfSlice([]string{
|
|
||||||
string(coredata.ControlImplementationStateImplemented),
|
|
||||||
string(coredata.ControlImplementationStateNotImplemented),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
if ucr.MaturityLevel != nil && *ucr.MaturityLevel != nil {
|
if ucr.MaturityLevel != nil {
|
||||||
v.Check(
|
v.Check(
|
||||||
**ucr.MaturityLevel,
|
*ucr.MaturityLevel,
|
||||||
"maturity_level",
|
"maturity_level",
|
||||||
validator.OneOfSlice([]string{
|
validator.OneOfSlice(coredata.ControlMaturityLevels()),
|
||||||
string(coredata.ControlMaturityLevelNone),
|
|
||||||
string(coredata.ControlMaturityLevelInitial),
|
|
||||||
string(coredata.ControlMaturityLevelManaged),
|
|
||||||
string(coredata.ControlMaturityLevelDefined),
|
|
||||||
string(coredata.ControlMaturityLevelQuantitativelyManaged),
|
|
||||||
string(coredata.ControlMaturityLevelOptimizing),
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,7 +852,7 @@ func (s ControlService) Create(
|
|||||||
framework := &coredata.Framework{}
|
framework := &coredata.Framework{}
|
||||||
|
|
||||||
notImplementedJustification := req.NotImplementedJustification
|
notImplementedJustification := req.NotImplementedJustification
|
||||||
if req.Implemented == coredata.ControlImplementationStateImplemented {
|
if req.MaturityLevel != coredata.ControlMaturityLevelNone {
|
||||||
notImplementedJustification = nil
|
notImplementedJustification = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -898,9 +863,8 @@ func (s ControlService) Create(
|
|||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
SectionTitle: req.SectionTitle,
|
SectionTitle: req.SectionTitle,
|
||||||
BestPractice: req.BestPractice,
|
BestPractice: req.BestPractice,
|
||||||
Implemented: req.Implemented,
|
|
||||||
NotImplementedJustification: notImplementedJustification,
|
|
||||||
MaturityLevel: req.MaturityLevel,
|
MaturityLevel: req.MaturityLevel,
|
||||||
|
NotImplementedJustification: notImplementedJustification,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
@@ -1007,21 +971,17 @@ func (s ControlService) Update(
|
|||||||
control.BestPractice = *req.BestPractice
|
control.BestPractice = *req.BestPractice
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.Implemented != nil {
|
if req.MaturityLevel != nil {
|
||||||
control.Implemented = *req.Implemented
|
control.MaturityLevel = *req.MaturityLevel
|
||||||
if *req.Implemented == coredata.ControlImplementationStateImplemented {
|
if *req.MaturityLevel != coredata.ControlMaturityLevelNone {
|
||||||
control.NotImplementedJustification = nil
|
control.NotImplementedJustification = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.NotImplementedJustification != nil && control.Implemented == coredata.ControlImplementationStateNotImplemented {
|
if req.NotImplementedJustification != nil && control.MaturityLevel == coredata.ControlMaturityLevelNone {
|
||||||
control.NotImplementedJustification = *req.NotImplementedJustification
|
control.NotImplementedJustification = *req.NotImplementedJustification
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.MaturityLevel != nil {
|
|
||||||
control.MaturityLevel = *req.MaturityLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
control.UpdatedAt = time.Now()
|
control.UpdatedAt = time.Now()
|
||||||
|
|
||||||
return control.Update(ctx, conn, s.svc.scope)
|
return control.Update(ctx, conn, s.svc.scope)
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ type (
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
BestPractice *bool `json:"best_practice,omitempty"`
|
BestPractice *bool `json:"best_practice,omitempty"`
|
||||||
Implemented string `json:"implemented,omitempty"`
|
|
||||||
NotImplementedJustification *string `json:"not_implemented_justification,omitempty"`
|
NotImplementedJustification *string `json:"not_implemented_justification,omitempty"`
|
||||||
MaturityLevel *string `json:"maturity_level,omitempty"`
|
MaturityLevel *string `json:"maturity_level,omitempty"`
|
||||||
} `json:"controls"`
|
} `json:"controls"`
|
||||||
@@ -605,21 +604,17 @@ func (s FrameworkService) Import(
|
|||||||
if control.BestPractice != nil {
|
if control.BestPractice != nil {
|
||||||
bestPractice = *control.BestPractice
|
bestPractice = *control.BestPractice
|
||||||
}
|
}
|
||||||
implemented := coredata.ControlImplementationState(control.Implemented)
|
maturityLevel := coredata.ControlMaturityLevelInitial
|
||||||
if !implemented.IsValid() {
|
|
||||||
implemented = coredata.ControlImplementationStateImplemented
|
|
||||||
}
|
|
||||||
var notImplementedJustification *string
|
|
||||||
if implemented == coredata.ControlImplementationStateNotImplemented {
|
|
||||||
notImplementedJustification = control.NotImplementedJustification
|
|
||||||
}
|
|
||||||
var maturityLevel *coredata.ControlMaturityLevel
|
|
||||||
if control.MaturityLevel != nil {
|
if control.MaturityLevel != nil {
|
||||||
ml := coredata.ControlMaturityLevel(*control.MaturityLevel)
|
ml := coredata.ControlMaturityLevel(*control.MaturityLevel)
|
||||||
if ml.IsValid() {
|
if ml.IsValid() {
|
||||||
maturityLevel = &ml
|
maturityLevel = ml
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var notImplementedJustification *string
|
||||||
|
if maturityLevel == coredata.ControlMaturityLevelNone {
|
||||||
|
notImplementedJustification = control.NotImplementedJustification
|
||||||
|
}
|
||||||
control := &coredata.Control{
|
control := &coredata.Control{
|
||||||
ID: controlID,
|
ID: controlID,
|
||||||
FrameworkID: frameworkID,
|
FrameworkID: frameworkID,
|
||||||
@@ -628,9 +623,8 @@ func (s FrameworkService) Import(
|
|||||||
Name: control.Name,
|
Name: control.Name,
|
||||||
Description: &description,
|
Description: &description,
|
||||||
BestPractice: bestPractice,
|
BestPractice: bestPractice,
|
||||||
Implemented: implemented,
|
|
||||||
NotImplementedJustification: notImplementedJustification,
|
|
||||||
MaturityLevel: maturityLevel,
|
MaturityLevel: maturityLevel,
|
||||||
|
NotImplementedJustification: notImplementedJustification,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -289,17 +289,8 @@ func (s *GeneratedDocumentService) buildStatementOfApplicabilityDocumentData(
|
|||||||
justification = *stmt.Justification
|
justification = *stmt.Justification
|
||||||
}
|
}
|
||||||
|
|
||||||
implemented := "-"
|
|
||||||
if applicable {
|
|
||||||
if control.Implemented == coredata.ControlImplementationStateImplemented {
|
|
||||||
implemented = "Yes"
|
|
||||||
} else {
|
|
||||||
implemented = "No"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notImplJustification := "-"
|
notImplJustification := "-"
|
||||||
if applicable && control.Implemented != coredata.ControlImplementationStateImplemented && control.NotImplementedJustification != nil {
|
if applicable && control.MaturityLevel == coredata.ControlMaturityLevelNone && control.NotImplementedJustification != nil {
|
||||||
notImplJustification = *control.NotImplementedJustification
|
notImplJustification = *control.NotImplementedJustification
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,9 +319,8 @@ func (s *GeneratedDocumentService) buildStatementOfApplicabilityDocumentData(
|
|||||||
ControlName: control.Name,
|
ControlName: control.Name,
|
||||||
Applicability: docgen.BoolLabel(applicable),
|
Applicability: docgen.BoolLabel(applicable),
|
||||||
Justification: justification,
|
Justification: justification,
|
||||||
Implemented: implemented,
|
|
||||||
NotImplJustification: notImplJustification,
|
|
||||||
MaturityLevel: maturityLevel,
|
MaturityLevel: maturityLevel,
|
||||||
|
NotImplJustification: notImplJustification,
|
||||||
Regulatory: regulatory,
|
Regulatory: regulatory,
|
||||||
Contractual: contractual,
|
Contractual: contractual,
|
||||||
BestPractice: bestPractice,
|
BestPractice: bestPractice,
|
||||||
|
|||||||
@@ -26,9 +26,8 @@
|
|||||||
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Control", "marks": [{ "type": "bold" }] }] }] },
|
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Control", "marks": [{ "type": "bold" }] }] }] },
|
||||||
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [70] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Applicability", "marks": [{ "type": "bold" }] }] }] },
|
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [70] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Applicability", "marks": [{ "type": "bold" }] }] }] },
|
||||||
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [130] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Justification for non-applicability", "marks": [{ "type": "bold" }] }] }] },
|
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [130] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Justification for non-applicability", "marks": [{ "type": "bold" }] }] }] },
|
||||||
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [70] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Implemented", "marks": [{ "type": "bold" }] }] }] },
|
|
||||||
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [110] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Justification for non-implementation", "marks": [{ "type": "bold" }] }] }] },
|
|
||||||
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [90] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Maturity", "marks": [{ "type": "bold" }] }] }] },
|
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [90] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Maturity", "marks": [{ "type": "bold" }] }] }] },
|
||||||
|
{ "type": "tableHeader", "attrs": { "colspan": 1, "rowspan": 2, "colwidth": [110] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Justification for non-implementation", "marks": [{ "type": "bold" }] }] }] },
|
||||||
{ "type": "tableHeader", "attrs": { "colspan": 4, "rowspan": 1 }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Justification for inclusion", "marks": [{ "type": "bold" }] }] }] }
|
{ "type": "tableHeader", "attrs": { "colspan": 4, "rowspan": 1 }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Justification for inclusion", "marks": [{ "type": "bold" }] }] }] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -48,9 +47,8 @@
|
|||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json (printf "[%s] " .ControlSection)}}, "marks": [{ "type": "code" }] }, { "type": "text", "text": {{json .ControlName}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [250] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json (printf "[%s] " .ControlSection)}}, "marks": [{ "type": "code" }] }, { "type": "text", "text": {{json .ControlName}} }] }] },
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [70] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Applicability}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [70] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Applicability}} }] }] },
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [130] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Justification}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [130] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Justification}} }] }] },
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [70] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Implemented}} }] }] },
|
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [110] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .NotImplJustification}} }] }] },
|
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [90] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .MaturityLevel}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [90] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .MaturityLevel}} }] }] },
|
||||||
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [110] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .NotImplJustification}} }] }] },
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [60] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Regulatory}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [60] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Regulatory}} }] }] },
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [60] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Contractual}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [60] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .Contractual}} }] }] },
|
||||||
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [60] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .BestPractice}} }] }] },
|
{ "type": "tableCell", "attrs": { "colspan": 1, "rowspan": 1, "colwidth": [60] }, "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": {{json .BestPractice}} }] }] },
|
||||||
@@ -104,28 +102,6 @@
|
|||||||
"type": "paragraph",
|
"type": "paragraph",
|
||||||
"content": [{ "type": "text", "text": "Provides the rationale when a control is not applicable. This field is empty for applicable controls." }]
|
"content": [{ "type": "text", "text": "Provides the rationale when a control is not applicable. This field is empty for applicable controls." }]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"attrs": { "level": 3 },
|
|
||||||
"content": [{ "type": "text", "text": "Implemented" }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "bulletList",
|
|
||||||
"content": [
|
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Yes: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The control has been implemented by the organization." }] }] },
|
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "No: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "The control has not been implemented (with justification provided)." }] }] },
|
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "-: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Not applicable (control is not applicable)." }] }] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "heading",
|
|
||||||
"attrs": { "level": 3 },
|
|
||||||
"content": [{ "type": "text", "text": "Justification for non-implementation" }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "paragraph",
|
|
||||||
"content": [{ "type": "text", "text": "Provides the rationale when a control is not implemented. This field is empty for implemented controls or when the control is not applicable." }]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "heading",
|
"type": "heading",
|
||||||
"attrs": { "level": 3 },
|
"attrs": { "level": 3 },
|
||||||
@@ -144,10 +120,18 @@
|
|||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "3 - Defined: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "documented, standardized and integrated into the organization." }] }] },
|
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "3 - Defined: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "documented, standardized and integrated into the organization." }] }] },
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "4 - Quantitatively Managed: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "measured, controlled with metrics and statistical objectives." }] }] },
|
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "4 - Quantitatively Managed: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "measured, controlled with metrics and statistical objectives." }] }] },
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "5 - Optimizing: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "continuous improvement based on quantitative analysis." }] }] },
|
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "5 - Optimizing: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "continuous improvement based on quantitative analysis." }] }] },
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "Not set: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "the maturity level has not yet been assessed." }] }] },
|
|
||||||
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "-: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Not applicable (control is not applicable)." }] }] }
|
{ "type": "listItem", "content": [{ "type": "paragraph", "content": [{ "type": "text", "text": "-: ", "marks": [{ "type": "bold" }] }, { "type": "text", "text": "Not applicable (control is not applicable)." }] }] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "heading",
|
||||||
|
"attrs": { "level": 3 },
|
||||||
|
"content": [{ "type": "text", "text": "Justification for non-implementation" }]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "paragraph",
|
||||||
|
"content": [{ "type": "text", "text": "Provides the rationale when a control has a maturity level of 0 - None. This field is empty for controls with higher maturity levels or when the control is not applicable." }]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "heading",
|
"type": "heading",
|
||||||
"attrs": { "level": 3 },
|
"attrs": { "level": 3 },
|
||||||
|
|||||||
@@ -415,9 +415,8 @@ func (r *mutationResolver) CreateControl(ctx context.Context, input types.Create
|
|||||||
Description: input.Description,
|
Description: input.Description,
|
||||||
SectionTitle: input.SectionTitle,
|
SectionTitle: input.SectionTitle,
|
||||||
BestPractice: input.BestPractice,
|
BestPractice: input.BestPractice,
|
||||||
Implemented: input.Implemented,
|
|
||||||
NotImplementedJustification: input.NotImplementedJustification,
|
|
||||||
MaturityLevel: input.MaturityLevel,
|
MaturityLevel: input.MaturityLevel,
|
||||||
|
NotImplementedJustification: input.NotImplementedJustification,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -453,9 +452,8 @@ func (r *mutationResolver) UpdateControl(ctx context.Context, input types.Update
|
|||||||
Description: gqlutils.UnwrapOmittable(input.Description),
|
Description: gqlutils.UnwrapOmittable(input.Description),
|
||||||
SectionTitle: input.SectionTitle,
|
SectionTitle: input.SectionTitle,
|
||||||
BestPractice: input.BestPractice,
|
BestPractice: input.BestPractice,
|
||||||
Implemented: input.Implemented,
|
MaturityLevel: input.MaturityLevel,
|
||||||
NotImplementedJustification: gqlutils.UnwrapOmittable(input.NotImplementedJustification),
|
NotImplementedJustification: gqlutils.UnwrapOmittable(input.NotImplementedJustification),
|
||||||
MaturityLevel: gqlutils.UnwrapOmittable(input.MaturityLevel),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,3 @@
|
|||||||
enum ControlImplementationState
|
|
||||||
@goModel(
|
|
||||||
model: "go.probo.inc/probo/pkg/coredata.ControlImplementationState"
|
|
||||||
) {
|
|
||||||
IMPLEMENTED
|
|
||||||
@goEnum(
|
|
||||||
value: "go.probo.inc/probo/pkg/coredata.ControlImplementationStateImplemented"
|
|
||||||
)
|
|
||||||
NOT_IMPLEMENTED
|
|
||||||
@goEnum(
|
|
||||||
value: "go.probo.inc/probo/pkg/coredata.ControlImplementationStateNotImplemented"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ControlMaturityLevel
|
enum ControlMaturityLevel
|
||||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.ControlMaturityLevel") {
|
@goModel(model: "go.probo.inc/probo/pkg/coredata.ControlMaturityLevel") {
|
||||||
NONE
|
NONE
|
||||||
@@ -113,9 +99,8 @@ type Control implements Node {
|
|||||||
name: String!
|
name: String!
|
||||||
description: String
|
description: String
|
||||||
bestPractice: Boolean!
|
bestPractice: Boolean!
|
||||||
implemented: ControlImplementationState!
|
|
||||||
notImplementedJustification: String
|
notImplementedJustification: String
|
||||||
maturityLevel: ControlMaturityLevel
|
maturityLevel: ControlMaturityLevel!
|
||||||
regulatory: Boolean! @goField(forceResolver: true)
|
regulatory: Boolean! @goField(forceResolver: true)
|
||||||
contractual: Boolean! @goField(forceResolver: true)
|
contractual: Boolean! @goField(forceResolver: true)
|
||||||
riskAssessment: Boolean! @goField(forceResolver: true)
|
riskAssessment: Boolean! @goField(forceResolver: true)
|
||||||
@@ -307,9 +292,8 @@ input CreateControlInput {
|
|||||||
name: String!
|
name: String!
|
||||||
description: String
|
description: String
|
||||||
bestPractice: Boolean!
|
bestPractice: Boolean!
|
||||||
implemented: ControlImplementationState!
|
maturityLevel: ControlMaturityLevel!
|
||||||
notImplementedJustification: String
|
notImplementedJustification: String
|
||||||
maturityLevel: ControlMaturityLevel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input UpdateControlInput {
|
input UpdateControlInput {
|
||||||
@@ -318,9 +302,8 @@ input UpdateControlInput {
|
|||||||
name: String
|
name: String
|
||||||
description: String @goField(omittable: true)
|
description: String @goField(omittable: true)
|
||||||
bestPractice: Boolean
|
bestPractice: Boolean
|
||||||
implemented: ControlImplementationState
|
maturityLevel: ControlMaturityLevel
|
||||||
notImplementedJustification: String @goField(omittable: true)
|
notImplementedJustification: String @goField(omittable: true)
|
||||||
maturityLevel: ControlMaturityLevel @goField(omittable: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input DeleteControlInput {
|
input DeleteControlInput {
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ func NewControl(control *coredata.Control) *Control {
|
|||||||
Name: control.Name,
|
Name: control.Name,
|
||||||
Description: control.Description,
|
Description: control.Description,
|
||||||
BestPractice: control.BestPractice,
|
BestPractice: control.BestPractice,
|
||||||
Implemented: control.Implemented,
|
|
||||||
NotImplementedJustification: control.NotImplementedJustification,
|
NotImplementedJustification: control.NotImplementedJustification,
|
||||||
MaturityLevel: control.MaturityLevel,
|
MaturityLevel: control.MaturityLevel,
|
||||||
CreatedAt: control.CreatedAt,
|
CreatedAt: control.CreatedAt,
|
||||||
|
|||||||
@@ -1473,12 +1473,6 @@ func (r *Resolver) AddControlTool(ctx context.Context, req *mcp.CallToolRequest,
|
|||||||
|
|
||||||
svc := r.ProboService(ctx, input.FrameworkID)
|
svc := r.ProboService(ctx, input.FrameworkID)
|
||||||
|
|
||||||
var maturityLevel *coredata.ControlMaturityLevel
|
|
||||||
if input.MaturityLevel != nil {
|
|
||||||
v := coredata.ControlMaturityLevel(*input.MaturityLevel)
|
|
||||||
maturityLevel = &v
|
|
||||||
}
|
|
||||||
|
|
||||||
control, err := svc.Controls.Create(
|
control, err := svc.Controls.Create(
|
||||||
ctx,
|
ctx,
|
||||||
probo.CreateControlRequest{
|
probo.CreateControlRequest{
|
||||||
@@ -1487,9 +1481,8 @@ func (r *Resolver) AddControlTool(ctx context.Context, req *mcp.CallToolRequest,
|
|||||||
Description: input.Description,
|
Description: input.Description,
|
||||||
SectionTitle: input.SectionTitle,
|
SectionTitle: input.SectionTitle,
|
||||||
BestPractice: input.BestPractice,
|
BestPractice: input.BestPractice,
|
||||||
Implemented: coredata.ControlImplementationState(input.Implemented),
|
MaturityLevel: coredata.ControlMaturityLevel(input.MaturityLevel),
|
||||||
NotImplementedJustification: input.NotImplementedJustification,
|
NotImplementedJustification: input.NotImplementedJustification,
|
||||||
MaturityLevel: maturityLevel,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1506,20 +1499,10 @@ func (r *Resolver) UpdateControlTool(ctx context.Context, req *mcp.CallToolReque
|
|||||||
|
|
||||||
svc := r.ProboService(ctx, input.ID)
|
svc := r.ProboService(ctx, input.ID)
|
||||||
|
|
||||||
var implemented *coredata.ControlImplementationState
|
var maturityLevel *coredata.ControlMaturityLevel
|
||||||
if input.Implemented != nil {
|
if input.MaturityLevel != nil {
|
||||||
v := coredata.ControlImplementationState(*input.Implemented)
|
v := coredata.ControlMaturityLevel(*input.MaturityLevel)
|
||||||
implemented = &v
|
maturityLevel = &v
|
||||||
}
|
|
||||||
|
|
||||||
var maturityLevel **coredata.ControlMaturityLevel
|
|
||||||
if rawMaturity := UnwrapOmittable(input.MaturityLevel); rawMaturity != nil {
|
|
||||||
var inner *coredata.ControlMaturityLevel
|
|
||||||
if *rawMaturity != nil {
|
|
||||||
v := coredata.ControlMaturityLevel(**rawMaturity)
|
|
||||||
inner = &v
|
|
||||||
}
|
|
||||||
maturityLevel = &inner
|
|
||||||
}
|
}
|
||||||
|
|
||||||
control, err := svc.Controls.Update(
|
control, err := svc.Controls.Update(
|
||||||
@@ -1530,9 +1513,8 @@ func (r *Resolver) UpdateControlTool(ctx context.Context, req *mcp.CallToolReque
|
|||||||
Description: UnwrapOmittable(input.Description),
|
Description: UnwrapOmittable(input.Description),
|
||||||
SectionTitle: input.SectionTitle,
|
SectionTitle: input.SectionTitle,
|
||||||
BestPractice: input.BestPractice,
|
BestPractice: input.BestPractice,
|
||||||
Implemented: implemented,
|
|
||||||
NotImplementedJustification: UnwrapOmittable(input.NotImplementedJustification),
|
|
||||||
MaturityLevel: maturityLevel,
|
MaturityLevel: maturityLevel,
|
||||||
|
NotImplementedJustification: UnwrapOmittable(input.NotImplementedJustification),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -4212,7 +4212,7 @@ components:
|
|||||||
- section_title
|
- section_title
|
||||||
- name
|
- name
|
||||||
- best_practice
|
- best_practice
|
||||||
- implemented
|
- maturity_level
|
||||||
- created_at
|
- created_at
|
||||||
- updated_at
|
- updated_at
|
||||||
properties:
|
properties:
|
||||||
@@ -4239,23 +4239,16 @@ components:
|
|||||||
best_practice:
|
best_practice:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether control is a best practice
|
description: Whether control is a best practice
|
||||||
implemented:
|
maturity_level:
|
||||||
type: string
|
type: string
|
||||||
enum: [IMPLEMENTED, NOT_IMPLEMENTED]
|
enum: [NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING]
|
||||||
description: Control implementation state
|
description: CMMI 0-5 maturity level of the control
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlImplementationState
|
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlMaturityLevel
|
||||||
not_implemented_justification:
|
not_implemented_justification:
|
||||||
type:
|
type:
|
||||||
- string
|
- string
|
||||||
- "null"
|
- "null"
|
||||||
description: Justification for non-implementation
|
description: Justification for non-implementation
|
||||||
maturity_level:
|
|
||||||
type:
|
|
||||||
- string
|
|
||||||
- "null"
|
|
||||||
enum: [NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING, null]
|
|
||||||
description: CMMI 0-5 maturity level of the control
|
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlMaturityLevel
|
|
||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
@@ -4330,7 +4323,7 @@ components:
|
|||||||
- section_title
|
- section_title
|
||||||
- name
|
- name
|
||||||
- best_practice
|
- best_practice
|
||||||
- implemented
|
- maturity_level
|
||||||
properties:
|
properties:
|
||||||
organization_id:
|
organization_id:
|
||||||
$ref: "#/components/schemas/GID"
|
$ref: "#/components/schemas/GID"
|
||||||
@@ -4350,23 +4343,16 @@ components:
|
|||||||
best_practice:
|
best_practice:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether control is a best practice
|
description: Whether control is a best practice
|
||||||
implemented:
|
maturity_level:
|
||||||
type: string
|
type: string
|
||||||
enum: [IMPLEMENTED, NOT_IMPLEMENTED]
|
enum: [NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING]
|
||||||
description: Control implementation state
|
description: CMMI 0-5 maturity level of the control
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlImplementationState
|
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlMaturityLevel
|
||||||
not_implemented_justification:
|
not_implemented_justification:
|
||||||
type:
|
type:
|
||||||
- string
|
- string
|
||||||
- "null"
|
- "null"
|
||||||
description: Justification for non-implementation
|
description: Justification for non-implementation
|
||||||
maturity_level:
|
|
||||||
type:
|
|
||||||
- string
|
|
||||||
- "null"
|
|
||||||
enum: [NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING, null]
|
|
||||||
description: CMMI 0-5 maturity level of the control
|
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlMaturityLevel
|
|
||||||
|
|
||||||
AddControlOutput:
|
AddControlOutput:
|
||||||
type: object
|
type: object
|
||||||
@@ -4397,21 +4383,15 @@ components:
|
|||||||
best_practice:
|
best_practice:
|
||||||
type: boolean
|
type: boolean
|
||||||
description: Whether control is a best practice
|
description: Whether control is a best practice
|
||||||
implemented:
|
maturity_level:
|
||||||
type: string
|
type: string
|
||||||
enum: [IMPLEMENTED, NOT_IMPLEMENTED]
|
enum: [NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING]
|
||||||
description: Control implementation state
|
description: CMMI 0-5 maturity level of the control
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlImplementationState
|
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlMaturityLevel
|
||||||
not_implemented_justification:
|
not_implemented_justification:
|
||||||
type: ["string", "null"]
|
type: ["string", "null"]
|
||||||
description: Justification for non-implementation
|
description: Justification for non-implementation
|
||||||
go.probo.inc/mcpgen/omittable: true
|
go.probo.inc/mcpgen/omittable: true
|
||||||
maturity_level:
|
|
||||||
type: ["string", "null"]
|
|
||||||
enum: [NONE, INITIAL, MANAGED, DEFINED, QUANTITATIVELY_MANAGED, OPTIMIZING, null]
|
|
||||||
description: CMMI 0-5 maturity level of the control
|
|
||||||
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.ControlMaturityLevel
|
|
||||||
go.probo.inc/mcpgen/omittable: true
|
|
||||||
|
|
||||||
UpdateControlOutput:
|
UpdateControlOutput:
|
||||||
type: object
|
type: object
|
||||||
|
|||||||
@@ -19,12 +19,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewControl(c *coredata.Control) *Control {
|
func NewControl(c *coredata.Control) *Control {
|
||||||
var maturityLevel *string
|
|
||||||
if c.MaturityLevel != nil {
|
|
||||||
s := string(*c.MaturityLevel)
|
|
||||||
maturityLevel = &s
|
|
||||||
}
|
|
||||||
|
|
||||||
return &Control{
|
return &Control{
|
||||||
ID: c.ID,
|
ID: c.ID,
|
||||||
OrganizationID: c.OrganizationID,
|
OrganizationID: c.OrganizationID,
|
||||||
@@ -33,9 +27,8 @@ func NewControl(c *coredata.Control) *Control {
|
|||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
Description: c.Description,
|
Description: c.Description,
|
||||||
BestPractice: c.BestPractice,
|
BestPractice: c.BestPractice,
|
||||||
Implemented: ControlImplemented(c.Implemented),
|
|
||||||
NotImplementedJustification: c.NotImplementedJustification,
|
NotImplementedJustification: c.NotImplementedJustification,
|
||||||
MaturityLevel: maturityLevel,
|
MaturityLevel: ControlMaturityLevel(c.MaturityLevel),
|
||||||
CreatedAt: c.CreatedAt,
|
CreatedAt: c.CreatedAt,
|
||||||
UpdatedAt: c.UpdatedAt,
|
UpdatedAt: c.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user