@@ -64,6 +64,7 @@ const controlOverviewPageQuery = graphql`
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
state
|
state
|
||||||
|
importance
|
||||||
category
|
category
|
||||||
tasks(first: 100) @connection(key: "ControlOverviewPage_tasks") {
|
tasks(first: 100) @connection(key: "ControlOverviewPage_tasks") {
|
||||||
__id
|
__id
|
||||||
@@ -195,6 +196,60 @@ function ControlOverviewPageContent({
|
|||||||
const { organizationId, frameworkId, controlId } = useParams();
|
const { organizationId, frameworkId, controlId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const environment = useRelayEnvironment();
|
const environment = useRelayEnvironment();
|
||||||
|
|
||||||
|
const formatImportance = (importance: string | undefined): string => {
|
||||||
|
if (!importance) return "";
|
||||||
|
|
||||||
|
const upperImportance = importance.toUpperCase();
|
||||||
|
|
||||||
|
if (upperImportance === "MANDATORY") return "Mandatory";
|
||||||
|
if (upperImportance === "PREFERRED") return "Preferred";
|
||||||
|
if (upperImportance === "ADVANCED") return "Advanced";
|
||||||
|
|
||||||
|
const formatted = importance.toLowerCase();
|
||||||
|
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatState = (state: string | undefined): string => {
|
||||||
|
if (!state) return "";
|
||||||
|
|
||||||
|
const upperState = state.toUpperCase();
|
||||||
|
|
||||||
|
if (upperState === "NOT_STARTED") return "Not Started";
|
||||||
|
if (upperState === "IN_PROGRESS") return "In Progress";
|
||||||
|
if (upperState === "NOT_APPLICABLE") return "Not Applicable";
|
||||||
|
if (upperState === "IMPLEMENTED") return "Implemented";
|
||||||
|
|
||||||
|
// Fallback for any other states
|
||||||
|
const formatted = state.toLowerCase();
|
||||||
|
return formatted.charAt(0).toUpperCase() + formatted.slice(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStateColor = (state: string | undefined): string => {
|
||||||
|
if (!state) return "bg-gray-100 text-gray-800";
|
||||||
|
|
||||||
|
const upperState = state.toUpperCase();
|
||||||
|
|
||||||
|
if (upperState === "NOT_STARTED") return "bg-gray-100 text-gray-800";
|
||||||
|
if (upperState === "IN_PROGRESS") return "bg-blue-100 text-blue-800";
|
||||||
|
if (upperState === "NOT_APPLICABLE") return "bg-purple-100 text-purple-800";
|
||||||
|
if (upperState === "IMPLEMENTED") return "bg-green-100 text-green-800";
|
||||||
|
|
||||||
|
return "bg-gray-100 text-gray-800";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getImportanceColor = (importance: string | undefined): string => {
|
||||||
|
if (!importance) return "bg-gray-100 text-gray-800";
|
||||||
|
|
||||||
|
const upperImportance = importance.toUpperCase();
|
||||||
|
|
||||||
|
if (upperImportance === "MANDATORY") return "bg-red-100 text-red-800";
|
||||||
|
if (upperImportance === "PREFERRED") return "bg-orange-100 text-orange-800";
|
||||||
|
if (upperImportance === "ADVANCED") return "bg-blue-100 text-blue-800";
|
||||||
|
|
||||||
|
return "bg-gray-100 text-gray-800";
|
||||||
|
};
|
||||||
|
|
||||||
const [updateTask] =
|
const [updateTask] =
|
||||||
useMutation<ControlOverviewPageUpdateTaskStateMutationType>(
|
useMutation<ControlOverviewPageUpdateTaskStateMutationType>(
|
||||||
updateTaskStateMutation
|
updateTaskStateMutation
|
||||||
@@ -542,7 +597,6 @@ function ControlOverviewPageContent({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to format file size
|
|
||||||
const formatFileSize = (bytes: number) => {
|
const formatFileSize = (bytes: number) => {
|
||||||
if (bytes === 0) return "0 Bytes";
|
if (bytes === 0) return "0 Bytes";
|
||||||
const k = 1024;
|
const k = 1024;
|
||||||
@@ -551,7 +605,6 @@ function ControlOverviewPageContent({
|
|||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to format date
|
|
||||||
const formatDate = (dateString: string) => {
|
const formatDate = (dateString: string) => {
|
||||||
const date = new Date(dateString);
|
const date = new Date(dateString);
|
||||||
return date.toLocaleDateString("en-US", {
|
return date.toLocaleDateString("en-US", {
|
||||||
@@ -681,40 +734,25 @@ function ControlOverviewPageContent({
|
|||||||
<Button variant="outline" size="sm" onClick={handleEditControl}>
|
<Button variant="outline" size="sm" onClick={handleEditControl}>
|
||||||
Edit Control
|
Edit Control
|
||||||
</Button>
|
</Button>
|
||||||
<div className="bg-green-100 text-green-800 px-3 py-1 rounded-full text-sm">
|
<div
|
||||||
30 min
|
className={`${getStateColor(
|
||||||
|
data.control.state
|
||||||
|
)} px-3 py-1 rounded-full text-sm`}
|
||||||
|
>
|
||||||
|
{formatState(data.control.state)}
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-100 text-gray-800 px-3 py-1 rounded-full text-sm">
|
<div
|
||||||
Mandatory
|
className={`${getImportanceColor(
|
||||||
|
data.control.importance
|
||||||
|
)} px-3 py-1 rounded-full text-sm`}
|
||||||
|
>
|
||||||
|
{formatImportance(data.control.importance)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-600 max-w-3xl">{data.control.description}</p>
|
<p className="text-gray-600 max-w-3xl">{data.control.description}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card className="bg-gray-50 border border-gray-200">
|
|
||||||
<CardContent className="p-6">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div
|
|
||||||
className={`w-4 h-4 rounded-full bg-white flex items-center justify-center border border-gray-200`}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`w-2 h-2 rounded-full ${
|
|
||||||
data.control.state === "IMPLEMENTED"
|
|
||||||
? "bg-green-500"
|
|
||||||
: "bg-gray-300"
|
|
||||||
}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm text-gray-700">
|
|
||||||
{data.control.state === "IMPLEMENTED"
|
|
||||||
? "Validated"
|
|
||||||
: "Not validated"}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h2 className="text-xl font-semibold">Tasks</h2>
|
<h2 className="text-xl font-semibold">Tasks</h2>
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ const FrameworkOverviewPageQuery = graphql`
|
|||||||
description
|
description
|
||||||
state
|
state
|
||||||
category
|
category
|
||||||
|
importance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -209,7 +210,10 @@ function FrameworkOverviewPageContent({
|
|||||||
30 min
|
30 min
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-[#2A2A2A] text-white px-2 py-1 rounded-full text-xs">
|
<div className="bg-[#2A2A2A] text-white px-2 py-1 rounded-full text-xs">
|
||||||
Mandatory
|
{
|
||||||
|
controlCards[hoveredCard]?.controls[hoveredControl]
|
||||||
|
?.importance
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MoveUpRight className="w-4 h-4 cursor-pointer hover:text-[#A3E635] transition-colors" />
|
<MoveUpRight className="w-4 h-4 cursor-pointer hover:text-[#A3E635] transition-colors" />
|
||||||
|
|||||||
@@ -23,10 +23,11 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
import type { UpdateControlPageUpdateControlMutation as UpdateControlPageUpdateControlMutationType } from "./__generated__/UpdateControlPageUpdateControlMutation.graphql";
|
||||||
// Type imports will be available after Relay compiler runs
|
import type {
|
||||||
// import type { UpdateControlPageQuery as UpdateControlPageQueryType } from "./__generated__/UpdateControlPageQuery.graphql";
|
ControlState,
|
||||||
// import type { UpdateControlPageUpdateControlMutation as UpdateControlPageUpdateControlMutationType } from "./__generated__/UpdateControlPageUpdateControlMutation.graphql";
|
ControlImportance,
|
||||||
|
} from "./__generated__/UpdateControlPageUpdateControlMutation.graphql";
|
||||||
|
|
||||||
const updateControlMutation = graphql`
|
const updateControlMutation = graphql`
|
||||||
mutation UpdateControlPageUpdateControlMutation($input: UpdateControlInput!) {
|
mutation UpdateControlPageUpdateControlMutation($input: UpdateControlInput!) {
|
||||||
@@ -36,6 +37,7 @@ const updateControlMutation = graphql`
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
category
|
category
|
||||||
|
importance
|
||||||
state
|
state
|
||||||
version
|
version
|
||||||
}
|
}
|
||||||
@@ -51,6 +53,7 @@ const updateControlQuery = graphql`
|
|||||||
name
|
name
|
||||||
description
|
description
|
||||||
category
|
category
|
||||||
|
importance
|
||||||
state
|
state
|
||||||
version
|
version
|
||||||
}
|
}
|
||||||
@@ -98,7 +101,7 @@ function EditableField({
|
|||||||
}
|
}
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full resize-none",
|
"w-full resize-none",
|
||||||
required && !value && "border-red-500",
|
required && !value && "border-red-500"
|
||||||
)}
|
)}
|
||||||
placeholder={`Enter ${label.toLowerCase()}`}
|
placeholder={`Enter ${label.toLowerCase()}`}
|
||||||
rows={4}
|
rows={4}
|
||||||
@@ -135,6 +138,7 @@ function UpdateControlPageContent({
|
|||||||
description: "",
|
description: "",
|
||||||
category: "",
|
category: "",
|
||||||
state: "",
|
state: "",
|
||||||
|
importance: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -144,12 +148,15 @@ function UpdateControlPageContent({
|
|||||||
description: data.node.description || "",
|
description: data.node.description || "",
|
||||||
category: data.node.category || "",
|
category: data.node.category || "",
|
||||||
state: data.node.state || "",
|
state: data.node.state || "",
|
||||||
|
importance: data.node.importance || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [data.node]);
|
}, [data.node]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
const [commit, isInFlight] =
|
||||||
const [commit, isInFlight] = useMutation<any>(updateControlMutation);
|
useMutation<UpdateControlPageUpdateControlMutationType>(
|
||||||
|
updateControlMutation
|
||||||
|
);
|
||||||
|
|
||||||
const handleFieldChange = (field: keyof typeof formData, value: string) => {
|
const handleFieldChange = (field: keyof typeof formData, value: string) => {
|
||||||
setFormData((prev) => ({
|
setFormData((prev) => ({
|
||||||
@@ -161,7 +168,7 @@ function UpdateControlPageContent({
|
|||||||
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
navigate(
|
navigate(
|
||||||
`/organizations/${organizationId}/frameworks/${frameworkId}/controls/${controlId}`,
|
`/organizations/${organizationId}/frameworks/${frameworkId}/controls/${controlId}`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -185,7 +192,8 @@ function UpdateControlPageContent({
|
|||||||
name?: string;
|
name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
category?: string;
|
category?: string;
|
||||||
state?: string;
|
state?: ControlState;
|
||||||
|
importance?: ControlImportance;
|
||||||
} = {
|
} = {
|
||||||
id: controlId!,
|
id: controlId!,
|
||||||
expectedVersion: data.node.version,
|
expectedVersion: data.node.version,
|
||||||
@@ -201,7 +209,10 @@ function UpdateControlPageContent({
|
|||||||
input.category = formData.category;
|
input.category = formData.category;
|
||||||
}
|
}
|
||||||
if (editedFields.has("state")) {
|
if (editedFields.has("state")) {
|
||||||
input.state = formData.state;
|
input.state = formData.state as ControlState;
|
||||||
|
}
|
||||||
|
if (editedFields.has("importance")) {
|
||||||
|
input.importance = formData.importance as ControlImportance;
|
||||||
}
|
}
|
||||||
|
|
||||||
commit({
|
commit({
|
||||||
@@ -224,7 +235,7 @@ function UpdateControlPageContent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
navigate(
|
navigate(
|
||||||
`/organizations/${organizationId}/frameworks/${frameworkId}/controls/${controlId}`,
|
`/organizations/${organizationId}/frameworks/${frameworkId}/controls/${controlId}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onError(error) {
|
onError(error) {
|
||||||
@@ -273,6 +284,27 @@ function UpdateControlPageContent({
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="importance" className="text-sm font-medium">
|
||||||
|
Importance
|
||||||
|
</Label>
|
||||||
|
<Select
|
||||||
|
value={formData.importance}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
handleFieldChange("importance", value)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select importance" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="MANDATORY">Mandatory</SelectItem>
|
||||||
|
<SelectItem value="PREFERRED">Preferred</SelectItem>
|
||||||
|
<SelectItem value="ADVANCED">Advanced</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="state" className="text-sm font-medium">
|
<Label htmlFor="state" className="text-sm font-medium">
|
||||||
State
|
State
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<b26c1748d5c4ae346ebbe98ea97b7459>>
|
* @generated SignedSource<<5f28c9407a05834be16d66330e268caa>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
export type ControlImportance = "ADVANCED" | "MANDATORY" | "PREFERRED";
|
||||||
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
||||||
export type EvidenceState = "EXPIRED" | "INVALID" | "VALID";
|
export type EvidenceState = "EXPIRED" | "INVALID" | "VALID";
|
||||||
export type TaskState = "DONE" | "TODO";
|
export type TaskState = "DONE" | "TODO";
|
||||||
@@ -20,6 +21,7 @@ export type ControlOverviewPageQuery$data = {
|
|||||||
readonly category?: string;
|
readonly category?: string;
|
||||||
readonly description?: string;
|
readonly description?: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
readonly importance?: ControlImportance;
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
readonly state?: ControlState;
|
readonly state?: ControlState;
|
||||||
readonly tasks?: {
|
readonly tasks?: {
|
||||||
@@ -101,31 +103,38 @@ v6 = {
|
|||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "category",
|
"name": "importance",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v7 = {
|
v7 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "version",
|
"name": "category",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v8 = {
|
v8 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "__typename",
|
"name": "version",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v9 = {
|
v9 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "cursor",
|
"name": "__typename",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v10 = {
|
v10 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "cursor",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v11 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"concreteType": "PageInfo",
|
"concreteType": "PageInfo",
|
||||||
@@ -150,7 +159,7 @@ v10 = {
|
|||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v11 = {
|
v12 = {
|
||||||
"kind": "ClientExtension",
|
"kind": "ClientExtension",
|
||||||
"selections": [
|
"selections": [
|
||||||
{
|
{
|
||||||
@@ -162,7 +171,7 @@ v11 = {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
v12 = [
|
v13 = [
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -209,25 +218,25 @@ v12 = [
|
|||||||
"name": "createdAt",
|
"name": "createdAt",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v8/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
(v9/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v10/*: any*/),
|
(v10/*: any*/)
|
||||||
(v11/*: any*/)
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v11/*: any*/),
|
||||||
|
(v12/*: any*/)
|
||||||
],
|
],
|
||||||
v13 = [
|
v14 = [
|
||||||
{
|
{
|
||||||
"kind": "Literal",
|
"kind": "Literal",
|
||||||
"name": "first",
|
"name": "first",
|
||||||
"value": 100
|
"value": 100
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
v14 = [
|
v15 = [
|
||||||
{
|
{
|
||||||
"kind": "Literal",
|
"kind": "Literal",
|
||||||
"name": "first",
|
"name": "first",
|
||||||
@@ -257,6 +266,7 @@ return {
|
|||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v6/*: any*/),
|
(v6/*: any*/),
|
||||||
|
(v7/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": "tasks",
|
"alias": "tasks",
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -285,7 +295,7 @@ return {
|
|||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v7/*: any*/),
|
(v8/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": "evidences",
|
"alias": "evidences",
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -293,19 +303,19 @@ return {
|
|||||||
"kind": "LinkedField",
|
"kind": "LinkedField",
|
||||||
"name": "__ControlOverviewPage_evidences_connection",
|
"name": "__ControlOverviewPage_evidences_connection",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": (v12/*: any*/),
|
"selections": (v13/*: any*/),
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
(v8/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v9/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v10/*: any*/),
|
(v10/*: any*/)
|
||||||
(v11/*: any*/)
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v11/*: any*/),
|
||||||
|
(v12/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
}
|
}
|
||||||
@@ -334,7 +344,7 @@ return {
|
|||||||
"name": "node",
|
"name": "node",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": [
|
"selections": [
|
||||||
(v8/*: any*/),
|
(v9/*: any*/),
|
||||||
(v2/*: any*/),
|
(v2/*: any*/),
|
||||||
{
|
{
|
||||||
"kind": "InlineFragment",
|
"kind": "InlineFragment",
|
||||||
@@ -343,9 +353,10 @@ return {
|
|||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v6/*: any*/),
|
(v6/*: any*/),
|
||||||
|
(v7/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v13/*: any*/),
|
"args": (v14/*: any*/),
|
||||||
"concreteType": "TaskConnection",
|
"concreteType": "TaskConnection",
|
||||||
"kind": "LinkedField",
|
"kind": "LinkedField",
|
||||||
"name": "tasks",
|
"name": "tasks",
|
||||||
@@ -371,42 +382,42 @@ return {
|
|||||||
(v3/*: any*/),
|
(v3/*: any*/),
|
||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v7/*: any*/),
|
(v8/*: any*/),
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v14/*: any*/),
|
"args": (v15/*: any*/),
|
||||||
"concreteType": "EvidenceConnection",
|
"concreteType": "EvidenceConnection",
|
||||||
"kind": "LinkedField",
|
"kind": "LinkedField",
|
||||||
"name": "evidences",
|
"name": "evidences",
|
||||||
"plural": false,
|
"plural": false,
|
||||||
"selections": (v12/*: any*/),
|
"selections": (v13/*: any*/),
|
||||||
"storageKey": "evidences(first:50)"
|
"storageKey": "evidences(first:50)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v14/*: any*/),
|
"args": (v15/*: any*/),
|
||||||
"filters": null,
|
"filters": null,
|
||||||
"handle": "connection",
|
"handle": "connection",
|
||||||
"key": "ControlOverviewPage_evidences",
|
"key": "ControlOverviewPage_evidences",
|
||||||
"kind": "LinkedHandle",
|
"kind": "LinkedHandle",
|
||||||
"name": "evidences"
|
"name": "evidences"
|
||||||
},
|
},
|
||||||
(v8/*: any*/)
|
|
||||||
],
|
|
||||||
"storageKey": null
|
|
||||||
},
|
|
||||||
(v9/*: any*/)
|
(v9/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
(v10/*: any*/),
|
(v10/*: any*/)
|
||||||
(v11/*: any*/)
|
],
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
(v11/*: any*/),
|
||||||
|
(v12/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": "tasks(first:100)"
|
"storageKey": "tasks(first:100)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": (v13/*: any*/),
|
"args": (v14/*: any*/),
|
||||||
"filters": null,
|
"filters": null,
|
||||||
"handle": "connection",
|
"handle": "connection",
|
||||||
"key": "ControlOverviewPage_tasks",
|
"key": "ControlOverviewPage_tasks",
|
||||||
@@ -423,7 +434,7 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "c8fb4975f792904f5106fb8518406063",
|
"cacheID": "1dc4bdedef3b39fd2e441f5259f83d29",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"connection": [
|
"connection": [
|
||||||
@@ -446,11 +457,11 @@ return {
|
|||||||
},
|
},
|
||||||
"name": "ControlOverviewPageQuery",
|
"name": "ControlOverviewPageQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query ControlOverviewPageQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n name\n description\n state\n category\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n version\n evidences(first: 50) {\n edges {\n node {\n id\n mimeType\n filename\n size\n state\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
"text": "query ControlOverviewPageQuery(\n $controlId: ID!\n) {\n control: node(id: $controlId) {\n __typename\n id\n ... on Control {\n name\n description\n state\n importance\n category\n tasks(first: 100) {\n edges {\n node {\n id\n name\n description\n state\n version\n evidences(first: 50) {\n edges {\n node {\n id\n mimeType\n filename\n size\n state\n createdAt\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "bd552dffe1a33e64e693a9170074989f";
|
(node as any).hash = "ff1a734556fc0d56e90ddae161a93408";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<6f707328d181f9b362855a7b6023acc0>>
|
* @generated SignedSource<<f5c15295df1bbb71ef99c60ec2417464>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
export type ControlImportance = "ADVANCED" | "MANDATORY" | "PREFERRED";
|
||||||
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
||||||
export type FrameworkOverviewPageQuery$variables = {
|
export type FrameworkOverviewPageQuery$variables = {
|
||||||
frameworkId: string;
|
frameworkId: string;
|
||||||
@@ -21,6 +22,7 @@ export type FrameworkOverviewPageQuery$data = {
|
|||||||
readonly category: string;
|
readonly category: string;
|
||||||
readonly description: string;
|
readonly description: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
readonly importance: ControlImportance;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly state: ControlState;
|
readonly state: ControlState;
|
||||||
};
|
};
|
||||||
@@ -113,6 +115,13 @@ v6 = [
|
|||||||
"name": "category",
|
"name": "category",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "importance",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
(v5/*: any*/)
|
(v5/*: any*/)
|
||||||
],
|
],
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
@@ -252,7 +261,7 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "77f347f74c922dbf3c1dd0baf0e37efd",
|
"cacheID": "6a4fc22ad7657a6861027975b620b08e",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"connection": [
|
"connection": [
|
||||||
@@ -269,11 +278,11 @@ return {
|
|||||||
},
|
},
|
||||||
"name": "FrameworkOverviewPageQuery",
|
"name": "FrameworkOverviewPageQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query FrameworkOverviewPageQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n id\n ... on Framework {\n name\n description\n controls(first: 90) {\n edges {\n node {\n id\n name\n description\n state\n category\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
"text": "query FrameworkOverviewPageQuery(\n $frameworkId: ID!\n) {\n node(id: $frameworkId) {\n __typename\n id\n ... on Framework {\n name\n description\n controls(first: 90) {\n edges {\n node {\n id\n name\n description\n state\n category\n importance\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "de913888fcb5a475baf829a5773326a3";
|
(node as any).hash = "0a7d8e6c782dae1777ee687c768d0ad6";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<daddf46b2039f01b80967778f24dd7f4>>
|
* @generated SignedSource<<86f7062f21c125e478c5a7a58b495d6c>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
|
||||||
import { ConcreteRequest } from 'relay-runtime';
|
import { ConcreteRequest } from 'relay-runtime';
|
||||||
|
export type ControlImportance = "ADVANCED" | "MANDATORY" | "PREFERRED";
|
||||||
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
export type ControlState = "IMPLEMENTED" | "IN_PROGRESS" | "NOT_APPLICABLE" | "NOT_STARTED";
|
||||||
export type UpdateControlPageQuery$variables = {
|
export type UpdateControlPageQuery$variables = {
|
||||||
controlId: string;
|
controlId: string;
|
||||||
@@ -18,6 +19,7 @@ export type UpdateControlPageQuery$data = {
|
|||||||
readonly category?: string;
|
readonly category?: string;
|
||||||
readonly description?: string;
|
readonly description?: string;
|
||||||
readonly id?: string;
|
readonly id?: string;
|
||||||
|
readonly importance?: ControlImportance;
|
||||||
readonly name?: string;
|
readonly name?: string;
|
||||||
readonly state?: ControlState;
|
readonly state?: ControlState;
|
||||||
readonly version?: number;
|
readonly version?: number;
|
||||||
@@ -75,10 +77,17 @@ v6 = {
|
|||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
"name": "state",
|
"name": "importance",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
v7 = {
|
v7 = {
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "state",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
|
v8 = {
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
"kind": "ScalarField",
|
"kind": "ScalarField",
|
||||||
@@ -108,7 +117,8 @@ return {
|
|||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v6/*: any*/),
|
(v6/*: any*/),
|
||||||
(v7/*: any*/)
|
(v7/*: any*/),
|
||||||
|
(v8/*: any*/)
|
||||||
],
|
],
|
||||||
"type": "Control",
|
"type": "Control",
|
||||||
"abstractKey": null
|
"abstractKey": null
|
||||||
@@ -149,7 +159,8 @@ return {
|
|||||||
(v4/*: any*/),
|
(v4/*: any*/),
|
||||||
(v5/*: any*/),
|
(v5/*: any*/),
|
||||||
(v6/*: any*/),
|
(v6/*: any*/),
|
||||||
(v7/*: any*/)
|
(v7/*: any*/),
|
||||||
|
(v8/*: any*/)
|
||||||
],
|
],
|
||||||
"type": "Control",
|
"type": "Control",
|
||||||
"abstractKey": null
|
"abstractKey": null
|
||||||
@@ -160,16 +171,16 @@ return {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "c44526d8079e0c539d634841a1f14923",
|
"cacheID": "8b9ee499b6fc5726d6ce679205b59501",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "UpdateControlPageQuery",
|
"name": "UpdateControlPageQuery",
|
||||||
"operationKind": "query",
|
"operationKind": "query",
|
||||||
"text": "query UpdateControlPageQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n description\n category\n state\n version\n }\n id\n }\n}\n"
|
"text": "query UpdateControlPageQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n description\n category\n importance\n state\n version\n }\n id\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "d0a9cc6ebcb5b97ea57567edea1f7d5f";
|
(node as any).hash = "f3336cff42059e52e983e4cd9d6c2f1e";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* @generated SignedSource<<21747064508d0bb73d846aed7ed6b97a>>
|
* @generated SignedSource<<8eaa34f32a83e626da3331f2e0c6a7cf>>
|
||||||
* @lightSyntaxTransform
|
* @lightSyntaxTransform
|
||||||
* @nogrep
|
* @nogrep
|
||||||
*/
|
*/
|
||||||
@@ -29,6 +29,7 @@ export type UpdateControlPageUpdateControlMutation$data = {
|
|||||||
readonly category: string;
|
readonly category: string;
|
||||||
readonly description: string;
|
readonly description: string;
|
||||||
readonly id: string;
|
readonly id: string;
|
||||||
|
readonly importance: ControlImportance;
|
||||||
readonly name: string;
|
readonly name: string;
|
||||||
readonly state: ControlState;
|
readonly state: ControlState;
|
||||||
readonly version: number;
|
readonly version: number;
|
||||||
@@ -99,6 +100,13 @@ v1 = [
|
|||||||
"name": "category",
|
"name": "category",
|
||||||
"storageKey": null
|
"storageKey": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"alias": null,
|
||||||
|
"args": null,
|
||||||
|
"kind": "ScalarField",
|
||||||
|
"name": "importance",
|
||||||
|
"storageKey": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"alias": null,
|
"alias": null,
|
||||||
"args": null,
|
"args": null,
|
||||||
@@ -138,16 +146,16 @@ return {
|
|||||||
"selections": (v1/*: any*/)
|
"selections": (v1/*: any*/)
|
||||||
},
|
},
|
||||||
"params": {
|
"params": {
|
||||||
"cacheID": "1e9e2b8000a1b3061fcfda07260413ed",
|
"cacheID": "3d05aa40f4dba0b24241235c54b85c4f",
|
||||||
"id": null,
|
"id": null,
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"name": "UpdateControlPageUpdateControlMutation",
|
"name": "UpdateControlPageUpdateControlMutation",
|
||||||
"operationKind": "mutation",
|
"operationKind": "mutation",
|
||||||
"text": "mutation UpdateControlPageUpdateControlMutation(\n $input: UpdateControlInput!\n) {\n updateControl(input: $input) {\n control {\n id\n name\n description\n category\n state\n version\n }\n }\n}\n"
|
"text": "mutation UpdateControlPageUpdateControlMutation(\n $input: UpdateControlInput!\n) {\n updateControl(input: $input) {\n control {\n id\n name\n description\n category\n importance\n state\n version\n }\n }\n}\n"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
(node as any).hash = "861813b150cef2977f8d9455a9bcf4fa";
|
(node as any).hash = "0de0b5b9560213e95af60cc1732dee4c";
|
||||||
|
|
||||||
export default node;
|
export default node;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ type (
|
|||||||
Category string `db:"category"`
|
Category string `db:"category"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
Description string `db:"description"`
|
Description string `db:"description"`
|
||||||
|
Importance ControlImportance `db:"importance"`
|
||||||
State ControlState `db:"state"`
|
State ControlState `db:"state"`
|
||||||
ContentRef string `db:"content_ref"`
|
ContentRef string `db:"content_ref"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
@@ -49,6 +50,7 @@ type (
|
|||||||
Description *string
|
Description *string
|
||||||
Category *string
|
Category *string
|
||||||
State *ControlState
|
State *ControlState
|
||||||
|
Importance *ControlImportance
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,6 +72,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
state,
|
state,
|
||||||
|
importance,
|
||||||
content_ref,
|
content_ref,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
@@ -115,6 +118,7 @@ INSERT INTO
|
|||||||
framework_id,
|
framework_id,
|
||||||
category,
|
category,
|
||||||
name,
|
name,
|
||||||
|
importance,
|
||||||
state,
|
state,
|
||||||
description,
|
description,
|
||||||
content_ref,
|
content_ref,
|
||||||
@@ -128,6 +132,7 @@ VALUES (
|
|||||||
@framework_id,
|
@framework_id,
|
||||||
@category,
|
@category,
|
||||||
@name,
|
@name,
|
||||||
|
@importance,
|
||||||
@state,
|
@state,
|
||||||
@description,
|
@description,
|
||||||
@content_ref,
|
@content_ref,
|
||||||
@@ -149,6 +154,7 @@ VALUES (
|
|||||||
"created_at": c.CreatedAt,
|
"created_at": c.CreatedAt,
|
||||||
"updated_at": c.UpdatedAt,
|
"updated_at": c.UpdatedAt,
|
||||||
"state": c.State,
|
"state": c.State,
|
||||||
|
"importance": c.Importance,
|
||||||
}
|
}
|
||||||
_, err := conn.Exec(ctx, q, args)
|
_, err := conn.Exec(ctx, q, args)
|
||||||
return err
|
return err
|
||||||
@@ -169,6 +175,7 @@ SELECT
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
state,
|
state,
|
||||||
|
importance,
|
||||||
content_ref,
|
content_ref,
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
@@ -213,6 +220,7 @@ UPDATE controls SET
|
|||||||
description = COALESCE(@description, description),
|
description = COALESCE(@description, description),
|
||||||
category = COALESCE(@category, category),
|
category = COALESCE(@category, category),
|
||||||
state = COALESCE(@state, state),
|
state = COALESCE(@state, state),
|
||||||
|
importance = COALESCE(@importance, importance),
|
||||||
updated_at = @updated_at,
|
updated_at = @updated_at,
|
||||||
version = version + 1
|
version = version + 1
|
||||||
WHERE %s
|
WHERE %s
|
||||||
@@ -224,6 +232,7 @@ RETURNING
|
|||||||
category,
|
category,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
importance,
|
||||||
state,
|
state,
|
||||||
content_ref,
|
content_ref,
|
||||||
created_at,
|
created_at,
|
||||||
@@ -235,23 +244,16 @@ RETURNING
|
|||||||
args := pgx.NamedArgs{
|
args := pgx.NamedArgs{
|
||||||
"control_id": c.ID,
|
"control_id": c.ID,
|
||||||
"expected_version": params.ExpectedVersion,
|
"expected_version": params.ExpectedVersion,
|
||||||
|
"name": params.Name,
|
||||||
|
"description": params.Description,
|
||||||
|
"category": params.Category,
|
||||||
|
"state": params.State,
|
||||||
|
"importance": params.Importance,
|
||||||
"updated_at": time.Now(),
|
"updated_at": time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
maps.Copy(args, scope.SQLArguments())
|
||||||
|
|
||||||
if params.Name != nil {
|
|
||||||
args["name"] = *params.Name
|
|
||||||
}
|
|
||||||
if params.Description != nil {
|
|
||||||
args["description"] = *params.Description
|
|
||||||
}
|
|
||||||
if params.Category != nil {
|
|
||||||
args["category"] = *params.Category
|
|
||||||
}
|
|
||||||
|
|
||||||
maps.Copy(args, scope.SQLArguments())
|
|
||||||
|
|
||||||
rows, err := conn.Query(ctx, q, args)
|
rows, err := conn.Query(ctx, q, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot query controls: %w", err)
|
return fmt.Errorf("cannot query controls: %w", err)
|
||||||
|
|||||||
100
pkg/coredata/control_importance.go
Normal file
100
pkg/coredata/control_importance.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
package coredata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ControlImportance uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
ControlImportanceMandatory ControlImportance = iota
|
||||||
|
ControlImportancePreferred
|
||||||
|
ControlImportanceAdvanced
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i ControlImportance) String() string {
|
||||||
|
return []string{"MANDATORY", "PREFERRED", "ADVANCED"}[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ControlImportance) Scan(value interface{}) error {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case uint8:
|
||||||
|
*i = ControlImportance(v)
|
||||||
|
case string:
|
||||||
|
switch v {
|
||||||
|
case "MANDATORY":
|
||||||
|
*i = ControlImportanceMandatory
|
||||||
|
case "PREFERRED":
|
||||||
|
*i = ControlImportancePreferred
|
||||||
|
case "ADVANCED":
|
||||||
|
*i = ControlImportanceAdvanced
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid ControlImportance value: %q", v)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported type for ControlImportance: %T", value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ControlImportance) Value() (driver.Value, error) {
|
||||||
|
return i.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ControlImportance) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(i.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ControlImportance) UnmarshalJSON(data []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(data, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case "MANDATORY":
|
||||||
|
*i = ControlImportanceMandatory
|
||||||
|
case "PREFERRED":
|
||||||
|
*i = ControlImportancePreferred
|
||||||
|
case "ADVANCED":
|
||||||
|
*i = ControlImportanceAdvanced
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid ControlImportance value: %q", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *ControlImportance) UnmarshalText(text []byte) error {
|
||||||
|
var s string
|
||||||
|
if err := json.Unmarshal(text, &s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s {
|
||||||
|
case "MANDATORY":
|
||||||
|
*i = ControlImportanceMandatory
|
||||||
|
case "PREFERRED":
|
||||||
|
*i = ControlImportancePreferred
|
||||||
|
case "ADVANCED":
|
||||||
|
*i = ControlImportanceAdvanced
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid ControlImportance value: %q", s)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
pkg/coredata/migrations/20250312T180800Z.sql
Normal file
8
pkg/coredata/migrations/20250312T180800Z.sql
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
CREATE TYPE control_importance AS ENUM ('MANDATORY', 'PREFERRED', 'ADVANCED');
|
||||||
|
|
||||||
|
ALTER TABLE controls ADD COLUMN importance control_importance NOT NULL DEFAULT 'MANDATORY';
|
||||||
|
ALTER TABLE controls ALTER COLUMN importance DROP DEFAULT;
|
||||||
|
|
||||||
|
ALTER TABLE tasks ADD COLUMN time_estimate INTERVAL NOT NULL DEFAULT '00:30:00';
|
||||||
|
ALTER TABLE tasks ALTER COLUMN time_estimate DROP DEFAULT;
|
||||||
|
|
||||||
@@ -36,6 +36,7 @@ type (
|
|||||||
Description string
|
Description string
|
||||||
ContentRef string
|
ContentRef string
|
||||||
Category string
|
Category string
|
||||||
|
Importance coredata.ControlImportance
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateControlRequest struct {
|
UpdateControlRequest struct {
|
||||||
@@ -45,6 +46,7 @@ type (
|
|||||||
Description *string
|
Description *string
|
||||||
Category *string
|
Category *string
|
||||||
State *coredata.ControlState
|
State *coredata.ControlState
|
||||||
|
Importance *coredata.ControlImportance
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -78,6 +80,7 @@ func (s ControlService) Update(
|
|||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
Category: req.Category,
|
Category: req.Category,
|
||||||
State: req.State,
|
State: req.State,
|
||||||
|
Importance: req.Importance,
|
||||||
}
|
}
|
||||||
|
|
||||||
control := &coredata.Control{ID: req.ID}
|
control := &coredata.Control{ID: req.ID}
|
||||||
@@ -139,6 +142,7 @@ func (s ControlService) Create(
|
|||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
Category: req.Category,
|
Category: req.Category,
|
||||||
State: coredata.ControlStateNotStarted,
|
State: coredata.ControlStateNotStarted,
|
||||||
|
Importance: req.Importance,
|
||||||
ContentRef: req.ContentRef,
|
ContentRef: req.ContentRef,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
|
|||||||
@@ -70,6 +70,22 @@ enum PeopleKind
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ControlImportance
|
||||||
|
@goModel(model: "github.com/getprobo/probo/pkg/coredata.ControlImportance") {
|
||||||
|
MANDATORY
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlImportanceMandatory"
|
||||||
|
)
|
||||||
|
PREFERRED
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlImportancePreferred"
|
||||||
|
)
|
||||||
|
ADVANCED
|
||||||
|
@goEnum(
|
||||||
|
value: "github.com/getprobo/probo/pkg/coredata.ControlImportanceAdvanced"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type PageInfo {
|
type PageInfo {
|
||||||
hasNextPage: Boolean!
|
hasNextPage: Boolean!
|
||||||
hasPreviousPage: Boolean!
|
hasPreviousPage: Boolean!
|
||||||
@@ -216,6 +232,7 @@ type Control implements Node {
|
|||||||
name: String!
|
name: String!
|
||||||
description: String!
|
description: String!
|
||||||
state: ControlState!
|
state: ControlState!
|
||||||
|
importance: ControlImportance!
|
||||||
|
|
||||||
tasks(
|
tasks(
|
||||||
first: Int
|
first: Int
|
||||||
@@ -308,24 +325,35 @@ type Mutation {
|
|||||||
createVendor(input: CreateVendorInput!): CreateVendorPayload!
|
createVendor(input: CreateVendorInput!): CreateVendorPayload!
|
||||||
updateVendor(input: UpdateVendorInput!): UpdateVendorPayload!
|
updateVendor(input: UpdateVendorInput!): UpdateVendorPayload!
|
||||||
deleteVendor(input: DeleteVendorInput!): DeleteVendorPayload!
|
deleteVendor(input: DeleteVendorInput!): DeleteVendorPayload!
|
||||||
|
|
||||||
createPeople(input: CreatePeopleInput!): CreatePeoplePayload!
|
createPeople(input: CreatePeopleInput!): CreatePeoplePayload!
|
||||||
updatePeople(input: UpdatePeopleInput!): UpdatePeoplePayload!
|
updatePeople(input: UpdatePeopleInput!): UpdatePeoplePayload!
|
||||||
deletePeople(input: DeletePeopleInput!): DeletePeoplePayload!
|
deletePeople(input: DeletePeopleInput!): DeletePeoplePayload!
|
||||||
|
|
||||||
createOrganization(
|
createOrganization(
|
||||||
input: CreateOrganizationInput!
|
input: CreateOrganizationInput!
|
||||||
): CreateOrganizationPayload!
|
): CreateOrganizationPayload!
|
||||||
|
updateOrganization(
|
||||||
|
input: UpdateOrganizationInput!
|
||||||
|
): UpdateOrganizationPayload!
|
||||||
deleteOrganization(
|
deleteOrganization(
|
||||||
input: DeleteOrganizationInput!
|
input: DeleteOrganizationInput!
|
||||||
): DeleteOrganizationPayload!
|
): DeleteOrganizationPayload!
|
||||||
|
|
||||||
createTask(input: CreateTaskInput!): CreateTaskPayload!
|
createTask(input: CreateTaskInput!): CreateTaskPayload!
|
||||||
updateTask(input: UpdateTaskInput!): UpdateTaskPayload!
|
updateTask(input: UpdateTaskInput!): UpdateTaskPayload!
|
||||||
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
deleteTask(input: DeleteTaskInput!): DeleteTaskPayload!
|
||||||
|
|
||||||
createFramework(input: CreateFrameworkInput!): CreateFrameworkPayload!
|
createFramework(input: CreateFrameworkInput!): CreateFrameworkPayload!
|
||||||
createControl(input: CreateControlInput!): CreateControlPayload!
|
|
||||||
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
|
updateFramework(input: UpdateFrameworkInput!): UpdateFrameworkPayload!
|
||||||
|
importFramework(input: ImportFrameworkInput!): ImportFrameworkPayload!
|
||||||
|
|
||||||
|
createControl(input: CreateControlInput!): CreateControlPayload!
|
||||||
updateControl(input: UpdateControlInput!): UpdateControlPayload!
|
updateControl(input: UpdateControlInput!): UpdateControlPayload!
|
||||||
|
|
||||||
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
|
uploadEvidence(input: UploadEvidenceInput!): UploadEvidencePayload!
|
||||||
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
|
deleteEvidence(input: DeleteEvidenceInput!): DeleteEvidencePayload!
|
||||||
|
|
||||||
createPolicy(input: CreatePolicyInput!): CreatePolicyPayload!
|
createPolicy(input: CreatePolicyInput!): CreatePolicyPayload!
|
||||||
updatePolicy(input: UpdatePolicyInput!): UpdatePolicyPayload!
|
updatePolicy(input: UpdatePolicyInput!): UpdatePolicyPayload!
|
||||||
deletePolicy(input: DeletePolicyInput!): DeletePolicyPayload!
|
deletePolicy(input: DeletePolicyInput!): DeletePolicyPayload!
|
||||||
@@ -431,6 +459,12 @@ input CreateOrganizationInput {
|
|||||||
name: String!
|
name: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input UpdateOrganizationInput {
|
||||||
|
organizationId: ID!
|
||||||
|
name: String
|
||||||
|
logoUrl: String
|
||||||
|
}
|
||||||
|
|
||||||
input DeleteOrganizationInput {
|
input DeleteOrganizationInput {
|
||||||
organizationId: ID!
|
organizationId: ID!
|
||||||
}
|
}
|
||||||
@@ -439,6 +473,10 @@ type CreateOrganizationPayload {
|
|||||||
organizationEdge: OrganizationEdge!
|
organizationEdge: OrganizationEdge!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateOrganizationPayload {
|
||||||
|
organization: Organization!
|
||||||
|
}
|
||||||
|
|
||||||
type DeleteOrganizationPayload {
|
type DeleteOrganizationPayload {
|
||||||
deletedOrganizationId: ID!
|
deletedOrganizationId: ID!
|
||||||
}
|
}
|
||||||
@@ -483,6 +521,7 @@ input CreateControlInput {
|
|||||||
name: String!
|
name: String!
|
||||||
description: String!
|
description: String!
|
||||||
category: String!
|
category: String!
|
||||||
|
importance: ControlImportance!
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateControlPayload {
|
type CreateControlPayload {
|
||||||
@@ -508,6 +547,7 @@ input UpdateControlInput {
|
|||||||
description: String
|
description: String
|
||||||
category: String
|
category: String
|
||||||
state: ControlState
|
state: ControlState
|
||||||
|
importance: ControlImportance
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateControlPayload {
|
type UpdateControlPayload {
|
||||||
@@ -616,3 +656,12 @@ input ConfirmEmailInput {
|
|||||||
type ConfirmEmailPayload {
|
type ConfirmEmailPayload {
|
||||||
success: Boolean!
|
success: Boolean!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input ImportFrameworkInput {
|
||||||
|
organizationId: ID!
|
||||||
|
file: Upload!
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImportFrameworkPayload {
|
||||||
|
frameworkEdge: FrameworkEdge!
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,7 @@ func NewControl(c *coredata.Control) *Control {
|
|||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
Description: c.Description,
|
Description: c.Description,
|
||||||
State: c.State,
|
State: c.State,
|
||||||
|
Importance: c.Importance,
|
||||||
CreatedAt: c.CreatedAt,
|
CreatedAt: c.CreatedAt,
|
||||||
UpdatedAt: c.UpdatedAt,
|
UpdatedAt: c.UpdatedAt,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type Control struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
State coredata.ControlState `json:"state"`
|
State coredata.ControlState `json:"state"`
|
||||||
|
Importance coredata.ControlImportance `json:"importance"`
|
||||||
Tasks *TaskConnection `json:"tasks"`
|
Tasks *TaskConnection `json:"tasks"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
@@ -54,6 +55,7 @@ type CreateControlInput struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
|
Importance coredata.ControlImportance `json:"importance"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateControlPayload struct {
|
type CreateControlPayload struct {
|
||||||
@@ -225,6 +227,15 @@ type FrameworkEdge struct {
|
|||||||
Node *Framework `json:"node"`
|
Node *Framework `json:"node"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImportFrameworkInput struct {
|
||||||
|
OrganizationID gid.GID `json:"organizationId"`
|
||||||
|
File graphql.Upload `json:"file"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImportFrameworkPayload struct {
|
||||||
|
FrameworkEdge *FrameworkEdge `json:"frameworkEdge"`
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation struct {
|
type Mutation struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,6 +359,7 @@ type UpdateControlInput struct {
|
|||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
Category *string `json:"category,omitempty"`
|
Category *string `json:"category,omitempty"`
|
||||||
State *coredata.ControlState `json:"state,omitempty"`
|
State *coredata.ControlState `json:"state,omitempty"`
|
||||||
|
Importance *coredata.ControlImportance `json:"importance,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateControlPayload struct {
|
type UpdateControlPayload struct {
|
||||||
@@ -365,6 +377,16 @@ type UpdateFrameworkPayload struct {
|
|||||||
Framework *Framework `json:"framework"`
|
Framework *Framework `json:"framework"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateOrganizationInput struct {
|
||||||
|
OrganizationID gid.GID `json:"organizationId"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
LogoURL *string `json:"logoUrl,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateOrganizationPayload struct {
|
||||||
|
Organization *Organization `json:"organization"`
|
||||||
|
}
|
||||||
|
|
||||||
type UpdatePeopleInput struct {
|
type UpdatePeopleInput struct {
|
||||||
ID gid.GID `json:"id"`
|
ID gid.GID `json:"id"`
|
||||||
ExpectedVersion int `json:"expectedVersion"`
|
ExpectedVersion int `json:"expectedVersion"`
|
||||||
|
|||||||
@@ -321,6 +321,7 @@ func (r *mutationResolver) UpdateControl(ctx context.Context, input types.Update
|
|||||||
Description: input.Description,
|
Description: input.Description,
|
||||||
Category: input.Category,
|
Category: input.Category,
|
||||||
State: input.State,
|
State: input.State,
|
||||||
|
Importance: input.Importance,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot update control: %w", err)
|
return nil, fmt.Errorf("cannot update control: %w", err)
|
||||||
|
|||||||
Reference in New Issue
Block a user