Rename State of Applicability to Statement of Applicability

Rename the entity across the full stack: database table
(states_of_applicability → statements_of_applicability), Go model,
GraphQL types, MCP specification, CLI commands, frontend components,
routes, and display labels. Includes a migration to rename the table
and its foreign key column.

Widen sidebar from 260px to 280px to fit the longer label.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-04-09 13:06:06 +02:00
parent 050073ab3c
commit 5fd580e995
48 changed files with 978 additions and 957 deletions

View File

@@ -34,15 +34,15 @@ import { useEffect, useMemo, useState } from "react";
import { useFragment } from "react-relay"; import { useFragment } from "react-relay";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import type { LinkedStatesOfApplicabilityCardFragment$key } from "#/__generated__/core/LinkedStatesOfApplicabilityCardFragment.graphql"; import type { LinkedStatementsOfApplicabilityCardFragment$key } from "#/__generated__/core/LinkedStatementsOfApplicabilityCardFragment.graphql";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
import { LinkedStatesOfApplicabilityDialog } from "./LinkedStatesOfApplicabilityDialog"; import { LinkedStatementsOfApplicabilityDialog } from "./LinkedStatementsOfApplicabilityDialog";
const linkedStateOfApplicabilityFragment = graphql` const linkedStatementOfApplicabilityFragment = graphql`
fragment LinkedStatesOfApplicabilityCardFragment on ApplicabilityStatement { fragment LinkedStatementsOfApplicabilityCardFragment on ApplicabilityStatement {
id id
stateOfApplicability { statementOfApplicability {
id id
name name
} }
@@ -57,7 +57,7 @@ const linkedStateOfApplicabilityFragment = graphql`
type AttachMutation<Params> = (p: { type AttachMutation<Params> = (p: {
variables: { variables: {
input: { input: {
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
applicability: boolean; applicability: boolean;
justification: string | null; justification: string | null;
} & Params; } & Params;
@@ -68,7 +68,7 @@ type AttachMutation<Params> = (p: {
type DetachMutation = (p: { type DetachMutation = (p: {
variables: { variables: {
input: { input: {
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
controlId: string; controlId: string;
}; };
connections: string[]; connections: string[];
@@ -76,7 +76,7 @@ type DetachMutation = (p: {
}) => void; }) => void;
type Props<Params> = { type Props<Params> = {
statesOfApplicability: readonly (LinkedStatesOfApplicabilityCardFragment$key & { statementsOfApplicability: readonly (LinkedStatementsOfApplicabilityCardFragment$key & {
id: string; id: string;
})[]; })[];
params: Params; params: Params;
@@ -88,7 +88,7 @@ type Props<Params> = {
readOnly?: boolean; readOnly?: boolean;
}; };
export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) { export function LinkedStatementsOfApplicabilityCard<Params>(props: Props<Params>) {
const { __ } = useTranslate(); const { __ } = useTranslate();
const [limit, setLimit] = useState<number | null>( const [limit, setLimit] = useState<number | null>(
@@ -96,30 +96,30 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
); );
const [linkedInfo, setLinkedInfo] = useState< const [linkedInfo, setLinkedInfo] = useState<
{ stateOfApplicabilityId: string; controlId: string }[] { statementOfApplicabilityId: string; controlId: string }[]
>([]); >([]);
const statesOfApplicability = useMemo(() => { const statementsOfApplicability = useMemo(() => {
return limit return limit
? props.statesOfApplicability.slice(0, limit) ? props.statementsOfApplicability.slice(0, limit)
: props.statesOfApplicability; : props.statementsOfApplicability;
}, [props.statesOfApplicability, limit]); }, [props.statementsOfApplicability, limit]);
const showMoreButton const showMoreButton
= limit !== null && props.statesOfApplicability.length > limit; = limit !== null && props.statementsOfApplicability.length > limit;
const variant = props.variant ?? "table"; const variant = props.variant ?? "table";
const linkedData = linkedInfo; const linkedData = linkedInfo;
const onAttach = ( const onAttach = (
stateOfApplicabilityId: string, statementOfApplicabilityId: string,
applicability: boolean, applicability: boolean,
justification: string | null, justification: string | null,
) => { ) => {
props.onAttach({ props.onAttach({
variables: { variables: {
input: { input: {
stateOfApplicabilityId, statementOfApplicabilityId,
applicability, applicability,
justification, justification,
...props.params, ...props.params,
@@ -129,11 +129,11 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
}); });
}; };
const onDetach = (stateOfApplicabilityId: string, controlId: string) => { const onDetach = (statementOfApplicabilityId: string, controlId: string) => {
props.onDetach({ props.onDetach({
variables: { variables: {
input: { input: {
stateOfApplicabilityId, statementOfApplicabilityId,
controlId, controlId,
}, },
connections: [props.connectionId], connections: [props.connectionId],
@@ -145,7 +145,7 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
return ( return (
<Wrapper padded className="space-y-[10px]"> <Wrapper padded className="space-y-[10px]">
{props.statesOfApplicability.map((soa, idx) => ( {props.statementsOfApplicability.map((soa, idx) => (
<LinkedInfoExtractor <LinkedInfoExtractor
key={idx} key={idx}
fragment={soa} fragment={soa}
@@ -153,8 +153,8 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
setLinkedInfo((prev) => { setLinkedInfo((prev) => {
const exists = prev.some( const exists = prev.some(
p => p =>
p.stateOfApplicabilityId p.statementOfApplicabilityId
=== info.stateOfApplicabilityId === info.statementOfApplicabilityId
&& p.controlId === info.controlId, && p.controlId === info.controlId,
); );
return exists ? prev : [...prev, info]; return exists ? prev : [...prev, info];
@@ -165,20 +165,20 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
{variant === "card" && ( {variant === "card" && (
<div className="flex justify-between"> <div className="flex justify-between">
<div className="text-lg font-semibold"> <div className="text-lg font-semibold">
{__("States of Applicability")} {__("Statements of Applicability")}
</div> </div>
{!props.readOnly && ( {!props.readOnly && (
<LinkedStatesOfApplicabilityDialog <LinkedStatementsOfApplicabilityDialog
connectionId={props.connectionId} connectionId={props.connectionId}
disabled={props.disabled} disabled={props.disabled}
linkedStatesOfApplicability={linkedData} linkedStatementsOfApplicability={linkedData}
onLink={onAttach} onLink={onAttach}
onUnlink={onDetach} onUnlink={onDetach}
> >
<Button variant="tertiary" icon={IconPlusLarge}> <Button variant="tertiary" icon={IconPlusLarge}>
{__("Link state of applicability")} {__("Link statement of applicability")}
</Button> </Button>
</LinkedStatesOfApplicabilityDialog> </LinkedStatementsOfApplicabilityDialog>
)} )}
</div> </div>
)} )}
@@ -192,36 +192,36 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{statesOfApplicability.length === 0 && ( {statementsOfApplicability.length === 0 && (
<Tr> <Tr>
<Td <Td
colSpan={props.readOnly ? 3 : 4} colSpan={props.readOnly ? 3 : 4}
className="text-center text-txt-secondary" className="text-center text-txt-secondary"
> >
{__("No states of applicability linked")} {__("No statements of applicability linked")}
</Td> </Td>
</Tr> </Tr>
)} )}
{statesOfApplicability.map(soa => ( {statementsOfApplicability.map(soa => (
<StateOfApplicabilityRow <StatementOfApplicabilityRow
key={soa.id} key={soa.id}
stateOfApplicability={soa} statementOfApplicability={soa}
onClick={onDetach} onClick={onDetach}
readOnly={props.readOnly} readOnly={props.readOnly}
/> />
))} ))}
{variant === "table" && !props.readOnly && ( {variant === "table" && !props.readOnly && (
<LinkedStatesOfApplicabilityDialog <LinkedStatementsOfApplicabilityDialog
connectionId={props.connectionId} connectionId={props.connectionId}
disabled={props.disabled} disabled={props.disabled}
linkedStatesOfApplicability={linkedData} linkedStatementsOfApplicability={linkedData}
onLink={onAttach} onLink={onAttach}
onUnlink={onDetach} onUnlink={onDetach}
> >
<TrButton colspan={4} icon={IconPlusLarge}> <TrButton colspan={4} icon={IconPlusLarge}>
{__("Link state of applicability")} {__("Link statement of applicability")}
</TrButton> </TrButton>
</LinkedStatesOfApplicabilityDialog> </LinkedStatementsOfApplicabilityDialog>
)} )}
</Tbody> </Tbody>
</Table> </Table>
@@ -233,7 +233,7 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
> >
{sprintf( {sprintf(
__("Show %d more"), __("Show %d more"),
props.statesOfApplicability.length - limit, props.statementsOfApplicability.length - limit,
)} )}
</Button> </Button>
)} )}
@@ -242,48 +242,48 @@ export function LinkedStatesOfApplicabilityCard<Params>(props: Props<Params>) {
} }
function LinkedInfoExtractor(props: { function LinkedInfoExtractor(props: {
fragment: LinkedStatesOfApplicabilityCardFragment$key; fragment: LinkedStatementsOfApplicabilityCardFragment$key;
onExtracted: (info: { onExtracted: (info: {
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
controlId: string; controlId: string;
}) => void; }) => void;
}) { }) {
const { onExtracted, fragment } = props; const { onExtracted, fragment } = props;
const data = useFragment( const data = useFragment(
linkedStateOfApplicabilityFragment, linkedStatementOfApplicabilityFragment,
fragment, fragment,
); );
useEffect(() => { useEffect(() => {
onExtracted({ onExtracted({
stateOfApplicabilityId: data.stateOfApplicability.id, statementOfApplicabilityId: data.statementOfApplicability.id,
controlId: data.control.id, controlId: data.control.id,
}); });
}, [data.stateOfApplicability.id, data.control.id, onExtracted]); }, [data.statementOfApplicability.id, data.control.id, onExtracted]);
return null; return null;
} }
function StateOfApplicabilityRow(props: { function StatementOfApplicabilityRow(props: {
stateOfApplicability: LinkedStatesOfApplicabilityCardFragment$key & { statementOfApplicability: LinkedStatementsOfApplicabilityCardFragment$key & {
id: string; id: string;
}; };
onClick: (stateOfApplicabilityId: string, controlId: string) => void; onClick: (statementOfApplicabilityId: string, controlId: string) => void;
readOnly?: boolean; readOnly?: boolean;
}) { }) {
const soa = useFragment( const soa = useFragment(
linkedStateOfApplicabilityFragment, linkedStatementOfApplicabilityFragment,
props.stateOfApplicability, props.statementOfApplicability,
); );
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const { __ } = useTranslate(); const { __ } = useTranslate();
return ( return (
<Tr <Tr
to={`/organizations/${organizationId}/states-of-applicability/${soa.stateOfApplicability.id}`} to={`/organizations/${organizationId}/statements-of-applicability/${soa.statementOfApplicability.id}`}
> >
<Td>{soa.stateOfApplicability.name}</Td> <Td>{soa.statementOfApplicability.name}</Td>
<Td> <Td>
<Badge variant={soa.applicability ? "success" : "danger"}> <Badge variant={soa.applicability ? "success" : "danger"}>
{soa.applicability {soa.applicability
@@ -298,7 +298,7 @@ function StateOfApplicabilityRow(props: {
variant="secondary" variant="secondary"
onClick={() => onClick={() =>
props.onClick( props.onClick(
soa.stateOfApplicability.id, soa.statementOfApplicability.id,
soa.control.id, soa.control.id,
)} )}
icon={IconTrashCan} icon={IconTrashCan}

View File

@@ -27,14 +27,14 @@ import { Suspense, useRef, useState } from "react";
import { useLazyLoadQuery } from "react-relay"; import { useLazyLoadQuery } from "react-relay";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import type { LinkedStatesOfApplicabilityDialogQuery } from "#/__generated__/core/LinkedStatesOfApplicabilityDialogQuery.graphql"; import type { LinkedStatementsOfApplicabilityDialogQuery } from "#/__generated__/core/LinkedStatementsOfApplicabilityDialogQuery.graphql";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
const query = graphql` const query = graphql`
query LinkedStatesOfApplicabilityDialogQuery($organizationId: ID!) { query LinkedStatementsOfApplicabilityDialogQuery($organizationId: ID!) {
organization: node(id: $organizationId) { organization: node(id: $organizationId) {
... on Organization { ... on Organization {
statesOfApplicability(first: 100) { statementsOfApplicability(first: 100) {
edges { edges {
node { node {
id id
@@ -48,7 +48,7 @@ const query = graphql`
`; `;
type LinkedSOAInfo = { type LinkedSOAInfo = {
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
controlId: string; controlId: string;
}; };
@@ -56,16 +56,16 @@ type Props = {
children: ReactNode; children: ReactNode;
connectionId: string; connectionId: string;
disabled?: boolean; disabled?: boolean;
linkedStatesOfApplicability: readonly LinkedSOAInfo[]; linkedStatementsOfApplicability: readonly LinkedSOAInfo[];
onLink: ( onLink: (
stateOfApplicabilityId: string, statementOfApplicabilityId: string,
applicability: boolean, applicability: boolean,
justification: string | null, justification: string | null,
) => void; ) => void;
onUnlink: (stateOfApplicabilityId: string, controlId: string) => void; onUnlink: (statementOfApplicabilityId: string, controlId: string) => void;
}; };
export function LinkedStatesOfApplicabilityDialog({ export function LinkedStatementsOfApplicabilityDialog({
children, children,
...props ...props
}: Props) { }: Props) {
@@ -75,10 +75,10 @@ export function LinkedStatesOfApplicabilityDialog({
<Dialog <Dialog
ref={dialogRef} ref={dialogRef}
trigger={children} trigger={children}
title="Link State of Applicability" title="Link Statement of Applicability"
> >
<Suspense fallback={<div>Loading...</div>}> <Suspense fallback={<div>Loading...</div>}>
<LinkedStatesOfApplicabilityDialogContent <LinkedStatementsOfApplicabilityDialogContent
{...props} {...props}
onClose={() => dialogRef.current?.close()} onClose={() => dialogRef.current?.close()}
/> />
@@ -87,7 +87,7 @@ export function LinkedStatesOfApplicabilityDialog({
); );
} }
function LinkedStatesOfApplicabilityDialogContent( function LinkedStatementsOfApplicabilityDialogContent(
props: Omit<Props, "children"> & { onClose: () => void }, props: Omit<Props, "children"> & { onClose: () => void },
) { ) {
const { __ } = useTranslate(); const { __ } = useTranslate();
@@ -99,7 +99,7 @@ function LinkedStatesOfApplicabilityDialogContent(
const [applicability, setApplicability] = useState(true); const [applicability, setApplicability] = useState(true);
const [justification, setJustification] = useState(""); const [justification, setJustification] = useState("");
const data = useLazyLoadQuery<LinkedStatesOfApplicabilityDialogQuery>( const data = useLazyLoadQuery<LinkedStatementsOfApplicabilityDialogQuery>(
query, query,
{ {
organizationId, organizationId,
@@ -108,18 +108,18 @@ function LinkedStatesOfApplicabilityDialogContent(
); );
const linkedSOAIds = new Set( const linkedSOAIds = new Set(
props.linkedStatesOfApplicability.map( props.linkedStatementsOfApplicability.map(
soa => soa.stateOfApplicabilityId, soa => soa.statementOfApplicabilityId,
), ),
); );
const linkedSOAMap = new Map( const linkedSOAMap = new Map(
props.linkedStatesOfApplicability.map(soa => [ props.linkedStatementsOfApplicability.map(soa => [
soa.stateOfApplicabilityId, soa.statementOfApplicabilityId,
soa, soa,
]), ]),
); );
const statesOfApplicability const statementsOfApplicability
= data.organization?.statesOfApplicability?.edges.map( = data.organization?.statementsOfApplicability?.edges.map(
edge => edge.node, edge => edge.node,
) ?? []; ) ?? [];
@@ -140,11 +140,11 @@ function LinkedStatesOfApplicabilityDialogContent(
} }
}; };
const handleUnlink = (stateOfApplicabilityId: string) => { const handleUnlink = (statementOfApplicabilityId: string) => {
const linkedSOA = linkedSOAMap.get(stateOfApplicabilityId); const linkedSOA = linkedSOAMap.get(statementOfApplicabilityId);
if (linkedSOA) { if (linkedSOA) {
props.onUnlink( props.onUnlink(
linkedSOA.stateOfApplicabilityId, linkedSOA.statementOfApplicabilityId,
linkedSOA.controlId, linkedSOA.controlId,
); );
} }
@@ -153,15 +153,15 @@ function LinkedStatesOfApplicabilityDialogContent(
return ( return (
<> <>
<DialogContent padded className="space-y-4"> <DialogContent padded className="space-y-4">
{statesOfApplicability.length === 0 {statementsOfApplicability.length === 0
? ( ? (
<div className="flex flex-col items-center justify-center py-12 text-center"> <div className="flex flex-col items-center justify-center py-12 text-center">
<div className="text-txt-secondary text-base mb-2"> <div className="text-txt-secondary text-base mb-2">
{__("No states of applicability available")} {__("No statements of applicability available")}
</div> </div>
<div className="text-txt-tertiary text-sm"> <div className="text-txt-tertiary text-sm">
{__( {__(
"Create a state of applicability first to link it to this control", "Create a statement of applicability first to link it to this control",
)} )}
</div> </div>
</div> </div>
@@ -170,9 +170,9 @@ function LinkedStatesOfApplicabilityDialogContent(
? ( ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="text-sm font-medium mb-2"> <div className="text-sm font-medium mb-2">
{__("Select a state of applicability:")} {__("Select a statement of applicability:")}
</div> </div>
{statesOfApplicability.map((soa) => { {statementsOfApplicability.map((soa) => {
const isLinked = linkedSOAIds.has(soa.id); const isLinked = linkedSOAIds.has(soa.id);
return ( return (
<div <div

View File

@@ -64,8 +64,8 @@ const fragment = graphql`
canListSnapshots: permission(action: "core:snapshot:list") canListSnapshots: permission(action: "core:snapshot:list")
canGetTrustCenter: permission(action: "core:trust-center:get") canGetTrustCenter: permission(action: "core:trust-center:get")
canUpdateOrganization: permission(action: "iam:organization:update") canUpdateOrganization: permission(action: "iam:organization:update")
canListStatesOfApplicability: permission( canListStatementsOfApplicability: permission(
action: "core:state-of-applicability:list" action: "core:statement-of-applicability:list"
) )
canListAccessReviewCampaigns: permission( canListAccessReviewCampaigns: permission(
action: "core:access-review-campaign:list" action: "core:access-review-campaign:list"
@@ -183,11 +183,11 @@ export function Sidebar(props: { fKey: SidebarFragment$key }) {
to={`${prefix}/processing-activities`} to={`${prefix}/processing-activities`}
/> />
)} )}
{organization.canListStatesOfApplicability && ( {organization.canListStatementsOfApplicability && (
<SidebarItem <SidebarItem
label={__("States of Applicability")} label={__("Statements of Applicability")}
icon={IconPageCheck} icon={IconPageCheck}
to={`${prefix}/states-of-applicability`} to={`${prefix}/statements-of-applicability`}
/> />
)} )}
{organization.canListRightsRequests && ( {organization.canListRightsRequests && (

View File

@@ -42,56 +42,56 @@ import {
import { useNavigate, useParams } from "react-router"; import { useNavigate, useParams } from "react-router";
import { z } from "zod"; import { z } from "zod";
import type { StateOfApplicabilityDetailPageDeleteMutation } from "#/__generated__/core/StateOfApplicabilityDetailPageDeleteMutation.graphql"; import type { StatementOfApplicabilityDetailPageDeleteMutation } from "#/__generated__/core/StatementOfApplicabilityDetailPageDeleteMutation.graphql";
import type { StateOfApplicabilityDetailPageExportMutation } from "#/__generated__/core/StateOfApplicabilityDetailPageExportMutation.graphql"; import type { StatementOfApplicabilityDetailPageExportMutation } from "#/__generated__/core/StatementOfApplicabilityDetailPageExportMutation.graphql";
import type { StateOfApplicabilityDetailPageQuery } from "#/__generated__/core/StateOfApplicabilityDetailPageQuery.graphql"; import type { StatementOfApplicabilityDetailPageQuery } from "#/__generated__/core/StatementOfApplicabilityDetailPageQuery.graphql";
import type { StateOfApplicabilityDetailPageUpdateMutation } from "#/__generated__/core/StateOfApplicabilityDetailPageUpdateMutation.graphql"; import type { StatementOfApplicabilityDetailPageUpdateMutation } from "#/__generated__/core/StatementOfApplicabilityDetailPageUpdateMutation.graphql";
import { PeopleSelectField } from "#/components/form/PeopleSelectField"; import { PeopleSelectField } from "#/components/form/PeopleSelectField";
import { SnapshotBanner } from "#/components/SnapshotBanner"; import { SnapshotBanner } from "#/components/SnapshotBanner";
import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useFormWithSchema } from "#/hooks/useFormWithSchema";
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
import StateOfApplicabilityControlsTab from "./tabs/StateOfApplicabilityControlsTab"; import StatementOfApplicabilityControlsTab from "./tabs/StatementOfApplicabilityControlsTab";
export const stateOfApplicabilityDetailPageQuery = graphql` export const statementOfApplicabilityDetailPageQuery = graphql`
query StateOfApplicabilityDetailPageQuery($stateOfApplicabilityId: ID!) { query StatementOfApplicabilityDetailPageQuery($statementOfApplicabilityId: ID!) {
node(id: $stateOfApplicabilityId) { node(id: $statementOfApplicabilityId) {
... on StateOfApplicability { ... on StatementOfApplicability {
id id
name name
snapshotId snapshotId
createdAt createdAt
updatedAt updatedAt
canUpdate: permission(action: "core:state-of-applicability:update") canUpdate: permission(action: "core:statement-of-applicability:update")
canDelete: permission(action: "core:state-of-applicability:delete") canDelete: permission(action: "core:statement-of-applicability:delete")
canExport: permission(action: "core:state-of-applicability:export") canExport: permission(action: "core:statement-of-applicability:export")
owner { owner {
id id
fullName fullName
} }
...StateOfApplicabilityControlsTabFragment ...StatementOfApplicabilityControlsTabFragment
} }
} }
} }
`; `;
const exportMutation = graphql` const exportMutation = graphql`
mutation StateOfApplicabilityDetailPageExportMutation( mutation StatementOfApplicabilityDetailPageExportMutation(
$input: ExportStateOfApplicabilityPDFInput! $input: ExportStatementOfApplicabilityPDFInput!
) { ) {
exportStateOfApplicabilityPDF(input: $input) { exportStatementOfApplicabilityPDF(input: $input) {
data data
} }
} }
`; `;
const updateMutation = graphql` const updateMutation = graphql`
mutation StateOfApplicabilityDetailPageUpdateMutation( mutation StatementOfApplicabilityDetailPageUpdateMutation(
$input: UpdateStateOfApplicabilityInput! $input: UpdateStatementOfApplicabilityInput!
) { ) {
updateStateOfApplicability(input: $input) { updateStatementOfApplicability(input: $input) {
stateOfApplicability { statementOfApplicability {
id id
name name
sourceId sourceId
@@ -108,74 +108,75 @@ const updateMutation = graphql`
`; `;
const deleteMutation = graphql` const deleteMutation = graphql`
mutation StateOfApplicabilityDetailPageDeleteMutation( mutation StatementOfApplicabilityDetailPageDeleteMutation(
$input: DeleteStateOfApplicabilityInput! $input: DeleteStatementOfApplicabilityInput!
$connections: [ID!]! $connections: [ID!]!
) { ) {
deleteStateOfApplicability(input: $input) { deleteStatementOfApplicability(input: $input) {
deletedStateOfApplicabilityId @deleteEdge(connections: $connections) deletedStatementOfApplicabilityId @deleteEdge(connections: $connections)
} }
} }
`; `;
const StateOfApplicabilityConnectionKey = "StatesOfApplicabilityPage_statesOfApplicability"; const StatementOfApplicabilityConnectionKey = "StatementsOfApplicabilityPage_statementsOfApplicability";
type Props = { type Props = {
queryRef: PreloadedQuery<StateOfApplicabilityDetailPageQuery>; queryRef: PreloadedQuery<StatementOfApplicabilityDetailPageQuery>;
}; };
export default function StateOfApplicabilityDetailPage(props: Props) { export default function StatementOfApplicabilityDetailPage(props: Props) {
const { stateOfApplicabilityId, snapshotId } = useParams<{ const { statementOfApplicabilityId, snapshotId } = useParams<{
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
snapshotId?: string; snapshotId?: string;
}>(); }>();
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const data = usePreloadedQuery(stateOfApplicabilityDetailPageQuery, props.queryRef); const data = usePreloadedQuery(statementOfApplicabilityDetailPageQuery, props.queryRef);
const stateOfApplicability = data.node; const statementOfApplicability = data.node;
const { __ } = useTranslate(); const { __ } = useTranslate();
const navigate = useNavigate(); const navigate = useNavigate();
const isSnapshotMode = Boolean(snapshotId); const isSnapshotMode = Boolean(snapshotId);
const confirm = useConfirm(); const confirm = useConfirm();
const { toast } = useToast(); const { toast } = useToast();
if (!stateOfApplicabilityId || !stateOfApplicability) { if (!statementOfApplicabilityId || !statementOfApplicability) {
throw new Error( throw new Error(
"Cannot load state of applicability detail page without stateOfApplicabilityId parameter", "Cannot load statement of applicability detail page without statementOfApplicabilityId parameter",
); );
} }
validateSnapshotConsistency(stateOfApplicability, snapshotId); validateSnapshotConsistency(statementOfApplicability, snapshotId);
const connectionId = ConnectionHandler.getConnectionID( const connectionId = ConnectionHandler.getConnectionID(
organizationId, organizationId,
StateOfApplicabilityConnectionKey, StatementOfApplicabilityConnectionKey,
{ filter: { snapshotId: snapshotId ?? null } }, { filter: { snapshotId: snapshotId ?? null } },
); );
const [deleteStateOfApplicability] = useMutation<StateOfApplicabilityDetailPageDeleteMutation>(deleteMutation); const [deleteStatementOfApplicability]
= useMutation<StatementOfApplicabilityDetailPageDeleteMutation>(deleteMutation);
const handleDelete = () => { const handleDelete = () => {
if (!stateOfApplicability.id || !stateOfApplicability.name) { if (!statementOfApplicability.id || !statementOfApplicability.name) {
return alert(__("Failed to delete state of applicability: missing id or name")); return alert(__("Failed to delete statement of applicability: missing id or name"));
} }
confirm( confirm(
() => () =>
promisifyMutation(deleteStateOfApplicability)({ promisifyMutation(deleteStatementOfApplicability)({
variables: { variables: {
input: { input: {
stateOfApplicabilityId: stateOfApplicability.id!, statementOfApplicabilityId: statementOfApplicability.id!,
}, },
connections: [connectionId], connections: [connectionId],
}, },
}) })
.then(() => { .then(() => {
void navigate(`/organizations/${organizationId}/states-of-applicability`); void navigate(`/organizations/${organizationId}/statements-of-applicability`);
}) })
.catch((error) => { .catch((error) => {
toast({ toast({
title: __("Error"), title: __("Error"),
description: formatError( description: formatError(
__("Failed to delete state of applicability"), __("Failed to delete statement of applicability"),
error as GraphQLError, error as GraphQLError,
), ),
variant: "error", variant: "error",
@@ -186,52 +187,53 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
__( __(
"This will permanently delete \"%s\". This action cannot be undone.", "This will permanently delete \"%s\". This action cannot be undone.",
), ),
stateOfApplicability.name, statementOfApplicability.name,
), ),
}, },
); );
}; };
usePageTitle(stateOfApplicability.name || __("State of Applicability")); usePageTitle(statementOfApplicability.name || __("Statement of Applicability"));
const [isEditingName, setIsEditingName] = useState(false); const [isEditingName, setIsEditingName] = useState(false);
const [isEditingOwner, setIsEditingOwner] = useState(false); const [isEditingOwner, setIsEditingOwner] = useState(false);
const [updateStateOfApplicability, isUpdating] = useMutationWithToasts<StateOfApplicabilityDetailPageUpdateMutation>( const [updateStatementOfApplicability, isUpdating]
updateMutation, = useMutationWithToasts<StatementOfApplicabilityDetailPageUpdateMutation>(
{ updateMutation,
successMessage: __("State of Applicability updated successfully."), {
errorMessage: __("Failed to update State of Applicability"), successMessage: __("Statement of Applicability updated successfully."),
}, errorMessage: __("Failed to update Statement of Applicability"),
); },
);
const canUpdate = !isSnapshotMode && stateOfApplicability.canUpdate; const canUpdate = !isSnapshotMode && statementOfApplicability.canUpdate;
const canDelete = !isSnapshotMode && stateOfApplicability.canDelete; const canDelete = !isSnapshotMode && statementOfApplicability.canDelete;
const [exportStateOfApplicabilityPDF, isExporting] const [exportStatementOfApplicabilityPDF, isExporting]
= useMutationWithToasts<StateOfApplicabilityDetailPageExportMutation>( = useMutationWithToasts<StatementOfApplicabilityDetailPageExportMutation>(
exportMutation, exportMutation,
{ {
successMessage: __( successMessage: __(
"State of Applicability exported successfully.", "Statement of Applicability exported successfully.",
), ),
errorMessage: __("Failed to export State of Applicability"), errorMessage: __("Failed to export Statement of Applicability"),
}, },
); );
const handleExport = async () => { const handleExport = async () => {
if (!stateOfApplicability.id) return; if (!statementOfApplicability.id) return;
await exportStateOfApplicabilityPDF({ await exportStatementOfApplicabilityPDF({
variables: { variables: {
input: { input: {
stateOfApplicabilityId: stateOfApplicability.id, statementOfApplicabilityId: statementOfApplicability.id,
}, },
}, },
onCompleted: (data) => { onCompleted: (data) => {
if (data.exportStateOfApplicabilityPDF?.data) { if (data.exportStatementOfApplicabilityPDF?.data) {
const link = window.document.createElement("a"); const link = window.document.createElement("a");
link.href = data.exportStateOfApplicabilityPDF.data; link.href = data.exportStatementOfApplicabilityPDF.data;
link.download = `${stateOfApplicability.name || "state-of-applicability"}.pdf`; link.download = `${statementOfApplicability.name || "statement-of-applicability"}.pdf`;
window.document.body.appendChild(link); window.document.body.appendChild(link);
link.click(); link.click();
window.document.body.removeChild(link); window.document.body.removeChild(link);
@@ -254,7 +256,7 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
reset: resetName, reset: resetName,
} = useFormWithSchema(nameSchema, { } = useFormWithSchema(nameSchema, {
defaultValues: { defaultValues: {
name: stateOfApplicability.name || "", name: statementOfApplicability.name || "",
}, },
}); });
@@ -264,17 +266,17 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
reset: resetOwner, reset: resetOwner,
} = useFormWithSchema(ownerSchema, { } = useFormWithSchema(ownerSchema, {
defaultValues: { defaultValues: {
ownerId: stateOfApplicability.owner?.id || "", ownerId: statementOfApplicability.owner?.id || "",
}, },
}); });
const handleUpdateName = handleSubmitName(async (data) => { const handleUpdateName = handleSubmitName(async (data) => {
if (!stateOfApplicability.id) return; if (!statementOfApplicability.id) return;
await updateStateOfApplicability({ await updateStatementOfApplicability({
variables: { variables: {
input: { input: {
id: stateOfApplicability.id, id: statementOfApplicability.id,
name: data.name, name: data.name,
}, },
}, },
@@ -286,12 +288,12 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
}); });
const handleUpdateOwner = handleSubmitOwner(async (data) => { const handleUpdateOwner = handleSubmitOwner(async (data) => {
if (!stateOfApplicability.id) return; if (!statementOfApplicability.id) return;
await updateStateOfApplicability({ await updateStatementOfApplicability({
variables: { variables: {
input: { input: {
id: stateOfApplicability.id, id: statementOfApplicability.id,
ownerId: data.ownerId, ownerId: data.ownerId,
}, },
}, },
@@ -305,20 +307,20 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
const handleCancelNameEdit = () => { const handleCancelNameEdit = () => {
setIsEditingName(false); setIsEditingName(false);
resetName({ resetName({
name: stateOfApplicability.name || "", name: statementOfApplicability.name || "",
}); });
}; };
const handleCancelOwnerEdit = () => { const handleCancelOwnerEdit = () => {
setIsEditingOwner(false); setIsEditingOwner(false);
resetOwner({ resetOwner({
ownerId: stateOfApplicability.owner?.id || "", ownerId: statementOfApplicability.owner?.id || "",
}); });
}; };
const listUrl = snapshotId const listUrl = snapshotId
? `/organizations/${organizationId}/snapshots/${snapshotId}/states-of-applicability` ? `/organizations/${organizationId}/snapshots/${snapshotId}/statements-of-applicability`
: `/organizations/${organizationId}/states-of-applicability`; : `/organizations/${organizationId}/statements-of-applicability`;
return ( return (
<div className="space-y-6"> <div className="space-y-6">
@@ -326,13 +328,13 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
<Breadcrumb <Breadcrumb
items={[ items={[
{ {
label: __("States of Applicability"), label: __("Statements of Applicability"),
to: listUrl, to: listUrl,
}, },
{ {
label: label:
stateOfApplicability.name statementOfApplicability.name
|| __("State of Applicability detail"), || __("Statement of Applicability detail"),
}, },
]} ]}
/> />
@@ -371,7 +373,7 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
) )
: ( : (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span>{stateOfApplicability.name || ""}</span> <span>{statementOfApplicability.name || ""}</span>
{canUpdate && ( {canUpdate && (
<Button <Button
variant="quaternary" variant="quaternary"
@@ -383,7 +385,7 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
) )
} }
> >
{stateOfApplicability.canExport && ( {statementOfApplicability.canExport && (
<Button <Button
variant="secondary" variant="secondary"
icon={IconArrowDown} icon={IconArrowDown}
@@ -445,7 +447,7 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
: ( : (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="text-sm text-txt-primary"> <div className="text-sm text-txt-primary">
{stateOfApplicability.owner {statementOfApplicability.owner
?.fullName || "-"} ?.fullName || "-"}
</div> </div>
{canUpdate && ( {canUpdate && (
@@ -466,7 +468,7 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
{__("Created at")} {__("Created at")}
</div> </div>
<div className="text-sm text-txt-primary"> <div className="text-sm text-txt-primary">
{formatDate(stateOfApplicability.createdAt)} {formatDate(statementOfApplicability.createdAt)}
</div> </div>
</div> </div>
<div> <div>
@@ -474,21 +476,21 @@ export default function StateOfApplicabilityDetailPage(props: Props) {
{__("Updated at")} {__("Updated at")}
</div> </div>
<div className="text-sm text-txt-primary"> <div className="text-sm text-txt-primary">
{formatDate(stateOfApplicability.updatedAt)} {formatDate(statementOfApplicability.updatedAt)}
</div> </div>
</div> </div>
</div> </div>
</Card> </Card>
</div> </div>
{stateOfApplicability.id && ( {statementOfApplicability.id && (
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-base font-medium"> <h2 className="text-base font-medium">
{__("Statements")} {__("Statements")}
</h2> </h2>
<StateOfApplicabilityControlsTab <StatementOfApplicabilityControlsTab
stateOfApplicability={ statementOfApplicability={
stateOfApplicability as typeof stateOfApplicability & { statementOfApplicability as typeof statementOfApplicability & {
id: string; id: string;
} }
} }

View File

@@ -16,21 +16,21 @@ import { Suspense, useEffect } from "react";
import { useQueryLoader } from "react-relay"; import { useQueryLoader } from "react-relay";
import { useParams } from "react-router"; import { useParams } from "react-router";
import type { StateOfApplicabilityDetailPageQuery } from "#/__generated__/core/StateOfApplicabilityDetailPageQuery.graphql"; import type { StatementOfApplicabilityDetailPageQuery } from "#/__generated__/core/StatementOfApplicabilityDetailPageQuery.graphql";
import { PageSkeleton } from "#/components/skeletons/PageSkeleton"; import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
import StateOfApplicabilityDetailPage, { stateOfApplicabilityDetailPageQuery } from "./StateOfApplicabilityDetailPage"; import StatementOfApplicabilityDetailPage, { statementOfApplicabilityDetailPageQuery } from "./StatementOfApplicabilityDetailPage";
export default function StateOfApplicabilityDetailPageLoader() { export default function StatementOfApplicabilityDetailPageLoader() {
const { stateOfApplicabilityId } = useParams<{ stateOfApplicabilityId: string }>(); const { statementOfApplicabilityId } = useParams<{ statementOfApplicabilityId: string }>();
const [queryRef, loadQuery] const [queryRef, loadQuery]
= useQueryLoader<StateOfApplicabilityDetailPageQuery>(stateOfApplicabilityDetailPageQuery); = useQueryLoader<StatementOfApplicabilityDetailPageQuery>(statementOfApplicabilityDetailPageQuery);
useEffect(() => { useEffect(() => {
if (stateOfApplicabilityId) { if (statementOfApplicabilityId) {
loadQuery({ stateOfApplicabilityId }); loadQuery({ statementOfApplicabilityId });
} }
}, [loadQuery, stateOfApplicabilityId]); }, [loadQuery, statementOfApplicabilityId]);
if (!queryRef) { if (!queryRef) {
return <PageSkeleton />; return <PageSkeleton />;
@@ -38,7 +38,7 @@ export default function StateOfApplicabilityDetailPageLoader() {
return ( return (
<Suspense fallback={<PageSkeleton />}> <Suspense fallback={<PageSkeleton />}>
<StateOfApplicabilityDetailPage queryRef={queryRef} /> <StatementOfApplicabilityDetailPage queryRef={queryRef} />
</Suspense> </Suspense>
); );
} }

View File

@@ -34,86 +34,86 @@ import {
} from "react-relay"; } from "react-relay";
import { useParams } from "react-router"; import { useParams } from "react-router";
import type { StatesOfApplicabilityPageFragment$key } from "#/__generated__/core/StatesOfApplicabilityPageFragment.graphql"; import type { StatementsOfApplicabilityPageFragment$key } from "#/__generated__/core/StatementsOfApplicabilityPageFragment.graphql";
import type { StatesOfApplicabilityPagePaginationQuery } from "#/__generated__/core/StatesOfApplicabilityPagePaginationQuery.graphql"; import type { StatementsOfApplicabilityPagePaginationQuery } from "#/__generated__/core/StatementsOfApplicabilityPagePaginationQuery.graphql";
import type { StatesOfApplicabilityPageQuery } from "#/__generated__/core/StatesOfApplicabilityPageQuery.graphql"; import type { StatementsOfApplicabilityPageQuery } from "#/__generated__/core/StatementsOfApplicabilityPageQuery.graphql";
import { SnapshotBanner } from "#/components/SnapshotBanner"; import { SnapshotBanner } from "#/components/SnapshotBanner";
import { StateOfApplicabilityRow } from "./_components/StateOfApplicabilityRow"; import { StatementOfApplicabilityRow } from "./_components/StatementOfApplicabilityRow";
import { CreateStateOfApplicabilityDialog } from "./dialogs/CreateStateOfApplicabilityDialog"; import { CreateStatementOfApplicabilityDialog } from "./dialogs/CreateStatementOfApplicabilityDialog";
export const statesOfApplicabilityPageQuery = graphql` export const statementsOfApplicabilityPageQuery = graphql`
query StatesOfApplicabilityPageQuery($organizationId: ID!) { query StatementsOfApplicabilityPageQuery($organizationId: ID!) {
organization: node(id: $organizationId) { organization: node(id: $organizationId) {
__typename __typename
... on Organization { ... on Organization {
id id
canCreateStateOfApplicability: permission(action: "core:state-of-applicability:create") canCreateStatementOfApplicability: permission(action: "core:statement-of-applicability:create")
...StatesOfApplicabilityPageFragment ...StatementsOfApplicabilityPageFragment
} }
} }
} }
`; `;
const paginatedFragment = graphql` const paginatedFragment = graphql`
fragment StatesOfApplicabilityPageFragment on Organization fragment StatementsOfApplicabilityPageFragment on Organization
@refetchable(queryName: "StatesOfApplicabilityPagePaginationQuery") @refetchable(queryName: "StatementsOfApplicabilityPagePaginationQuery")
@argumentDefinitions( @argumentDefinitions(
first: { type: "Int", defaultValue: 50 } first: { type: "Int", defaultValue: 50 }
order: { order: {
type: "StateOfApplicabilityOrder" type: "StatementOfApplicabilityOrder"
defaultValue: { direction: DESC, field: CREATED_AT } defaultValue: { direction: DESC, field: CREATED_AT }
} }
after: { type: "CursorKey", defaultValue: null } after: { type: "CursorKey", defaultValue: null }
before: { type: "CursorKey", defaultValue: null } before: { type: "CursorKey", defaultValue: null }
last: { type: "Int", defaultValue: null } last: { type: "Int", defaultValue: null }
filter: { type: "StateOfApplicabilityFilter", defaultValue: { snapshotId: null } } filter: { type: "StatementOfApplicabilityFilter", defaultValue: { snapshotId: null } }
) { ) {
statesOfApplicability( statementsOfApplicability(
first: $first first: $first
after: $after after: $after
last: $last last: $last
before: $before before: $before
orderBy: $order orderBy: $order
filter: $filter filter: $filter
) @connection(key: "StatesOfApplicabilityPage_statesOfApplicability", filters: ["filter"]) { ) @connection(key: "StatementsOfApplicabilityPage_statementsOfApplicability", filters: ["filter"]) {
__id __id
edges { edges {
node { node {
id id
...StateOfApplicabilityRowFragment ...StatementOfApplicabilityRowFragment
} }
} }
} }
} }
`; `;
export default function StatesOfApplicabilityPage({ export default function StatementsOfApplicabilityPage({
queryRef, queryRef,
}: { }: {
queryRef: PreloadedQuery<StatesOfApplicabilityPageQuery>; queryRef: PreloadedQuery<StatementsOfApplicabilityPageQuery>;
}) { }) {
const { __ } = useTranslate(); const { __ } = useTranslate();
const { snapshotId } = useParams<{ snapshotId?: string }>(); const { snapshotId } = useParams<{ snapshotId?: string }>();
const isSnapshotMode = Boolean(snapshotId); const isSnapshotMode = Boolean(snapshotId);
usePageTitle(__("States of Applicability")); usePageTitle(__("Statements of Applicability"));
const { organization } = usePreloadedQuery(statesOfApplicabilityPageQuery, queryRef); const { organization } = usePreloadedQuery(statementsOfApplicabilityPageQuery, queryRef);
if (organization.__typename !== "Organization") { if (organization.__typename !== "Organization") {
throw new Error("Organization not found"); throw new Error("Organization not found");
} }
const { const {
data: { statesOfApplicability }, data: { statementsOfApplicability },
loadNext, loadNext,
hasNext, hasNext,
refetch, refetch,
isLoadingNext, isLoadingNext,
} = usePaginationFragment< } = usePaginationFragment<
StatesOfApplicabilityPagePaginationQuery, StatementsOfApplicabilityPagePaginationQuery,
StatesOfApplicabilityPageFragment$key StatementsOfApplicabilityPageFragment$key
>(paginatedFragment, organization); >(paginatedFragment, organization);
useEffect(() => { useEffect(() => {
@@ -129,24 +129,24 @@ export default function StatesOfApplicabilityPage({
<div className="space-y-6"> <div className="space-y-6">
{snapshotId && <SnapshotBanner snapshotId={snapshotId} />} {snapshotId && <SnapshotBanner snapshotId={snapshotId} />}
<PageHeader <PageHeader
title={__("States of Applicability")} title={__("Statements of Applicability")}
description={__( description={__(
"Manage states of applicability for your organization's frameworks.", "Manage statements of applicability for your organization's frameworks.",
)} )}
> >
{!isSnapshotMode {!isSnapshotMode
&& organization.canCreateStateOfApplicability && ( && organization.canCreateStatementOfApplicability && (
<CreateStateOfApplicabilityDialog <CreateStatementOfApplicabilityDialog
connectionId={statesOfApplicability.__id} connectionId={statementsOfApplicability.__id}
> >
<Button icon={IconPlusLarge}> <Button icon={IconPlusLarge}>
{__("Add state of applicability")} {__("Add statement of applicability")}
</Button> </Button>
</CreateStateOfApplicabilityDialog> </CreateStatementOfApplicabilityDialog>
)} )}
</PageHeader> </PageHeader>
{statesOfApplicability && statesOfApplicability.edges.length > 0 {statementsOfApplicability && statementsOfApplicability.edges.length > 0
? ( ? (
<Card> <Card>
<Table> <Table>
@@ -158,11 +158,11 @@ export default function StatesOfApplicabilityPage({
</Tr> </Tr>
</Thead> </Thead>
<Tbody> <Tbody>
{statesOfApplicability.edges.map(edge => ( {statementsOfApplicability.edges.map(edge => (
<StateOfApplicabilityRow <StatementOfApplicabilityRow
key={edge.node.id} key={edge.node.id}
fKey={edge.node} fKey={edge.node}
connectionId={statesOfApplicability.__id} connectionId={statementsOfApplicability.__id}
/> />
))} ))}
</Tbody> </Tbody>
@@ -187,11 +187,11 @@ export default function StatesOfApplicabilityPage({
<Card padded> <Card padded>
<div className="text-center py-12"> <div className="text-center py-12">
<h3 className="text-lg font-semibold mb-2"> <h3 className="text-lg font-semibold mb-2">
{__("No states of applicability yet")} {__("No statements of applicability yet")}
</h3> </h3>
<p className="text-txt-tertiary mb-4"> <p className="text-txt-tertiary mb-4">
{__( {__(
"Create your first state of applicability to get started.", "Create your first statement of applicability to get started.",
)} )}
</p> </p>
</div> </div>

View File

@@ -15,16 +15,16 @@
import { Suspense, useEffect } from "react"; import { Suspense, useEffect } from "react";
import { useQueryLoader } from "react-relay"; import { useQueryLoader } from "react-relay";
import type { StatesOfApplicabilityPageQuery } from "#/__generated__/core/StatesOfApplicabilityPageQuery.graphql"; import type { StatementsOfApplicabilityPageQuery } from "#/__generated__/core/StatementsOfApplicabilityPageQuery.graphql";
import { PageSkeleton } from "#/components/skeletons/PageSkeleton"; import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
import StatesOfApplicabilityPage, { statesOfApplicabilityPageQuery } from "./StatesOfApplicabilityPage"; import StatementsOfApplicabilityPage, { statementsOfApplicabilityPageQuery } from "./StatementsOfApplicabilityPage";
export default function StatesOfApplicabilityPageLoader() { export default function StatementsOfApplicabilityPageLoader() {
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const [queryRef, loadQuery] const [queryRef, loadQuery]
= useQueryLoader<StatesOfApplicabilityPageQuery>(statesOfApplicabilityPageQuery); = useQueryLoader<StatementsOfApplicabilityPageQuery>(statementsOfApplicabilityPageQuery);
useEffect(() => { useEffect(() => {
loadQuery({ organizationId }); loadQuery({ organizationId });
@@ -36,7 +36,7 @@ export default function StatesOfApplicabilityPageLoader() {
return ( return (
<Suspense fallback={<PageSkeleton />}> <Suspense fallback={<PageSkeleton />}>
<StatesOfApplicabilityPage queryRef={queryRef} /> <StatementsOfApplicabilityPage queryRef={queryRef} />
</Suspense> </Suspense>
); );
} }

View File

@@ -27,16 +27,16 @@ import { useFragment, useMutation } from "react-relay";
import { useParams } from "react-router"; import { useParams } from "react-router";
import { graphql } from "relay-runtime"; import { graphql } from "relay-runtime";
import type { StateOfApplicabilityRowDeleteMutation } from "#/__generated__/core/StateOfApplicabilityRowDeleteMutation.graphql"; import type { StatementOfApplicabilityRowDeleteMutation } from "#/__generated__/core/StatementOfApplicabilityRowDeleteMutation.graphql";
import type { StateOfApplicabilityRowFragment$key } from "#/__generated__/core/StateOfApplicabilityRowFragment.graphql"; import type { StatementOfApplicabilityRowFragment$key } from "#/__generated__/core/StatementOfApplicabilityRowFragment.graphql";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
const fragment = graphql` const fragment = graphql`
fragment StateOfApplicabilityRowFragment on StateOfApplicability { fragment StatementOfApplicabilityRowFragment on StatementOfApplicability {
id id
name name
createdAt createdAt
canDelete: permission(action: "core:state-of-applicability:delete") canDelete: permission(action: "core:statement-of-applicability:delete")
statementsInfo: applicabilityStatements { statementsInfo: applicabilityStatements {
totalCount totalCount
} }
@@ -44,22 +44,22 @@ const fragment = graphql`
`; `;
const deleteMutation = graphql` const deleteMutation = graphql`
mutation StateOfApplicabilityRowDeleteMutation( mutation StatementOfApplicabilityRowDeleteMutation(
$input: DeleteStateOfApplicabilityInput! $input: DeleteStatementOfApplicabilityInput!
$connections: [ID!]! $connections: [ID!]!
) { ) {
deleteStateOfApplicability(input: $input) { deleteStatementOfApplicability(input: $input) {
deletedStateOfApplicabilityId @deleteEdge(connections: $connections) deletedStatementOfApplicabilityId @deleteEdge(connections: $connections)
} }
} }
`; `;
type Props = { type Props = {
fKey: StateOfApplicabilityRowFragment$key; fKey: StatementOfApplicabilityRowFragment$key;
connectionId: string; connectionId: string;
}; };
export function StateOfApplicabilityRow({ fKey, connectionId }: Props) { export function StatementOfApplicabilityRow({ fKey, connectionId }: Props) {
const { __ } = useTranslate(); const { __ } = useTranslate();
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const { snapshotId } = useParams<{ snapshotId?: string }>(); const { snapshotId } = useParams<{ snapshotId?: string }>();
@@ -67,21 +67,21 @@ export function StateOfApplicabilityRow({ fKey, connectionId }: Props) {
const { toast } = useToast(); const { toast } = useToast();
const isSnapshotMode = Boolean(snapshotId); const isSnapshotMode = Boolean(snapshotId);
const stateOfApplicability = useFragment(fragment, fKey); const statementOfApplicability = useFragment(fragment, fKey);
const canDelete = !isSnapshotMode && stateOfApplicability.canDelete; const canDelete = !isSnapshotMode && statementOfApplicability.canDelete;
const [deleteStateOfApplicability] = useMutation<StateOfApplicabilityRowDeleteMutation>(deleteMutation); const [deleteStatementOfApplicability] = useMutation<StatementOfApplicabilityRowDeleteMutation>(deleteMutation);
const handleDelete = () => { const handleDelete = () => {
if (!stateOfApplicability.id || !stateOfApplicability.name) { if (!statementOfApplicability.id || !statementOfApplicability.name) {
return alert(__("Failed to delete state of applicability: missing id or name")); return alert(__("Failed to delete statement of applicability: missing id or name"));
} }
confirm( confirm(
() => () =>
promisifyMutation(deleteStateOfApplicability)({ promisifyMutation(deleteStatementOfApplicability)({
variables: { variables: {
input: { input: {
stateOfApplicabilityId: stateOfApplicability.id, statementOfApplicabilityId: statementOfApplicability.id,
}, },
connections: [connectionId], connections: [connectionId],
}, },
@@ -89,7 +89,7 @@ export function StateOfApplicabilityRow({ fKey, connectionId }: Props) {
toast({ toast({
title: __("Error"), title: __("Error"),
description: formatError( description: formatError(
__("Failed to delete state of applicability"), __("Failed to delete statement of applicability"),
error as GraphQLError, error as GraphQLError,
), ),
variant: "error", variant: "error",
@@ -100,25 +100,25 @@ export function StateOfApplicabilityRow({ fKey, connectionId }: Props) {
__( __(
"This will permanently delete \"%s\". This action cannot be undone.", "This will permanently delete \"%s\". This action cannot be undone.",
), ),
stateOfApplicability.name, statementOfApplicability.name,
), ),
}, },
); );
}; };
const detailUrl = snapshotId const detailUrl = snapshotId
? `/organizations/${organizationId}/snapshots/${snapshotId}/states-of-applicability/${stateOfApplicability.id}` ? `/organizations/${organizationId}/snapshots/${snapshotId}/statements-of-applicability/${statementOfApplicability.id}`
: `/organizations/${organizationId}/states-of-applicability/${stateOfApplicability.id}`; : `/organizations/${organizationId}/statements-of-applicability/${statementOfApplicability.id}`;
return ( return (
<Tr to={detailUrl}> <Tr to={detailUrl}>
<Td>{stateOfApplicability.name}</Td> <Td>{statementOfApplicability.name}</Td>
<Td> <Td>
<time dateTime={stateOfApplicability.createdAt}> <time dateTime={statementOfApplicability.createdAt}>
{formatDate(stateOfApplicability.createdAt)} {formatDate(statementOfApplicability.createdAt)}
</time> </time>
</Td> </Td>
<Td>{stateOfApplicability.statementsInfo?.totalCount ?? 0}</Td> <Td>{statementOfApplicability.statementsInfo?.totalCount ?? 0}</Td>
{canDelete && ( {canDelete && (
<Td noLink width={50} className="text-end"> <Td noLink width={50} className="text-end">
<ActionDropdown> <ActionDropdown>

View File

@@ -38,9 +38,9 @@ import type { AddApplicabilityStatementDialogQuery } from "#/__generated__/core/
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
const query = graphql` const query = graphql`
query AddApplicabilityStatementDialogQuery($stateOfApplicabilityId: ID!, $organizationId: ID!) { query AddApplicabilityStatementDialogQuery($statementOfApplicabilityId: ID!, $organizationId: ID!) {
stateOfApplicability: node(id: $stateOfApplicabilityId) { statementOfApplicability: node(id: $statementOfApplicabilityId) {
... on StateOfApplicability { ... on StatementOfApplicability {
id id
applicabilityStatements(first: 10000) { applicabilityStatements(first: 10000) {
edges { edges {
@@ -138,7 +138,7 @@ const updateApplicabilityStatementMutation = graphql`
`; `;
export type AddApplicabilityStatementDialogRef = { export type AddApplicabilityStatementDialogRef = {
open: (stateOfApplicabilityId: string, organizationId: string, connectionId: string) => void; open: (statementOfApplicabilityId: string, organizationId: string, connectionId: string) => void;
}; };
type ControlWithStatement = { type ControlWithStatement = {
@@ -154,11 +154,11 @@ type ControlWithStatement = {
function ControlRow({ function ControlRow({
control, control,
stateOfApplicabilityId, statementOfApplicabilityId,
connectionId, connectionId,
}: { }: {
control: ControlWithStatement; control: ControlWithStatement;
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
connectionId: string; connectionId: string;
}) { }) {
const { __ } = useTranslate(); const { __ } = useTranslate();
@@ -228,7 +228,7 @@ function ControlRow({
await createApplicabilityStatement({ await createApplicabilityStatement({
variables: { variables: {
input: { input: {
stateOfApplicabilityId, statementOfApplicabilityId,
controlId: control.controlId, controlId: control.controlId,
applicability: true, applicability: true,
justification: null, justification: null,
@@ -263,7 +263,7 @@ function ControlRow({
await createApplicabilityStatement({ await createApplicabilityStatement({
variables: { variables: {
input: { input: {
stateOfApplicabilityId, statementOfApplicabilityId,
controlId: control.controlId, controlId: control.controlId,
applicability: false, applicability: false,
justification: justification || null, justification: justification || null,
@@ -333,11 +333,11 @@ function ControlRow({
} }
function AddApplicabilityStatementDialogContent({ function AddApplicabilityStatementDialogContent({
stateOfApplicabilityId, statementOfApplicabilityId,
organizationId, organizationId,
connectionId, connectionId,
}: { }: {
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
organizationId: string; organizationId: string;
connectionId: string; connectionId: string;
}) { }) {
@@ -346,7 +346,7 @@ function AddApplicabilityStatementDialogContent({
const [collapsedFrameworks, setCollapsedFrameworks] = useState<Set<string>>(new Set()); const [collapsedFrameworks, setCollapsedFrameworks] = useState<Set<string>>(new Set());
const data = useLazyLoadQuery<AddApplicabilityStatementDialogQuery>( const data = useLazyLoadQuery<AddApplicabilityStatementDialogQuery>(
query, query,
{ stateOfApplicabilityId, organizationId }, { statementOfApplicabilityId, organizationId },
{ fetchPolicy: "store-or-network" }, { fetchPolicy: "store-or-network" },
); );
@@ -355,7 +355,7 @@ function AddApplicabilityStatementDialogContent({
string, string,
{ id: string; applicability: boolean; justification: string | null } { id: string; applicability: boolean; justification: string | null }
>(); >();
data.stateOfApplicability?.applicabilityStatements?.edges.forEach((edge) => { data.statementOfApplicability?.applicabilityStatements?.edges.forEach((edge) => {
map.set(edge.node.control.id, { map.set(edge.node.control.id, {
id: edge.node.id, id: edge.node.id,
applicability: edge.node.applicability, applicability: edge.node.applicability,
@@ -363,7 +363,7 @@ function AddApplicabilityStatementDialogContent({
}); });
}); });
return map; return map;
}, [data.stateOfApplicability?.applicabilityStatements]); }, [data.statementOfApplicability?.applicabilityStatements]);
const allControls = useMemo(() => { const allControls = useMemo(() => {
return (data.organization?.controls?.edges || []).map((edge) => { return (data.organization?.controls?.edges || []).map((edge) => {
@@ -459,7 +459,7 @@ function AddApplicabilityStatementDialogContent({
<ControlRow <ControlRow
key={control.controlId} key={control.controlId}
control={control} control={control}
stateOfApplicabilityId={stateOfApplicabilityId} statementOfApplicabilityId={statementOfApplicabilityId}
connectionId={connectionId} connectionId={connectionId}
/> />
))} ))}
@@ -488,7 +488,7 @@ export const AddApplicabilityStatementDialog = forwardRef<
({ onClose }, ref) => { ({ onClose }, ref) => {
const { __ } = useTranslate(); const { __ } = useTranslate();
const dialogRef = useDialogRef(); const dialogRef = useDialogRef();
const [stateOfApplicabilityId, setStateOfApplicabilityId] = useState<string | null>(null); const [statementOfApplicabilityId, setStatementOfApplicabilityId] = useState<string | null>(null);
const [organizationId, setOrganizationId] = useState<string | null>(null); const [organizationId, setOrganizationId] = useState<string | null>(null);
const [connectionId, setConnectionId] = useState<string | null>(null); const [connectionId, setConnectionId] = useState<string | null>(null);
@@ -496,7 +496,7 @@ export const AddApplicabilityStatementDialog = forwardRef<
ref, ref,
() => ({ () => ({
open: (soaId: string, orgId: string, connId: string) => { open: (soaId: string, orgId: string, connId: string) => {
setStateOfApplicabilityId(soaId); setStatementOfApplicabilityId(soaId);
setOrganizationId(orgId); setOrganizationId(orgId);
setConnectionId(connId); setConnectionId(connId);
dialogRef.current?.open(); dialogRef.current?.open();
@@ -506,7 +506,7 @@ export const AddApplicabilityStatementDialog = forwardRef<
); );
const handleClose = () => { const handleClose = () => {
setStateOfApplicabilityId(null); setStatementOfApplicabilityId(null);
setOrganizationId(null); setOrganizationId(null);
setConnectionId(null); setConnectionId(null);
onClose?.(); onClose?.();
@@ -518,12 +518,12 @@ export const AddApplicabilityStatementDialog = forwardRef<
className="max-w-3xl" className="max-w-3xl"
title={( title={(
<Breadcrumb <Breadcrumb
items={[__("States of Applicability"), __("Add Statement")]} items={[__("Statements of Applicability"), __("Add Statement")]}
/> />
)} )}
onClose={handleClose} onClose={handleClose}
> >
{stateOfApplicabilityId && organizationId && connectionId {statementOfApplicabilityId && organizationId && connectionId
? ( ? (
<Suspense <Suspense
fallback={( fallback={(
@@ -536,7 +536,7 @@ export const AddApplicabilityStatementDialog = forwardRef<
)} )}
> >
<AddApplicabilityStatementDialogContent <AddApplicabilityStatementDialogContent
stateOfApplicabilityId={stateOfApplicabilityId} statementOfApplicabilityId={statementOfApplicabilityId}
organizationId={organizationId} organizationId={organizationId}
connectionId={connectionId} connectionId={connectionId}
/> />

View File

@@ -28,19 +28,19 @@ import { graphql } from "react-relay";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { z } from "zod"; import { z } from "zod";
import type { CreateStateOfApplicabilityDialogMutation } from "#/__generated__/core/CreateStateOfApplicabilityDialogMutation.graphql"; import type { CreateStatementOfApplicabilityDialogMutation } from "#/__generated__/core/CreateStatementOfApplicabilityDialogMutation.graphql";
import { PeopleSelectField } from "#/components/form/PeopleSelectField"; import { PeopleSelectField } from "#/components/form/PeopleSelectField";
import { useFormWithSchema } from "#/hooks/useFormWithSchema"; import { useFormWithSchema } from "#/hooks/useFormWithSchema";
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
const createMutation = graphql` const createMutation = graphql`
mutation CreateStateOfApplicabilityDialogMutation( mutation CreateStatementOfApplicabilityDialogMutation(
$input: CreateStateOfApplicabilityInput! $input: CreateStatementOfApplicabilityInput!
$connections: [ID!]! $connections: [ID!]!
) { ) {
createStateOfApplicability(input: $input) { createStatementOfApplicability(input: $input) {
stateOfApplicabilityEdge @prependEdge(connections: $connections) { statementOfApplicabilityEdge @prependEdge(connections: $connections) {
node { node {
id id
name name
@@ -48,8 +48,8 @@ const createMutation = graphql`
snapshotId snapshotId
createdAt createdAt
updatedAt updatedAt
canDelete: permission(action: "core:state-of-applicability:delete") canDelete: permission(action: "core:statement-of-applicability:delete")
...StateOfApplicabilityRowFragment ...StatementOfApplicabilityRowFragment
} }
} }
} }
@@ -66,7 +66,7 @@ const schema = z.object({
ownerId: z.string().min(1), ownerId: z.string().min(1),
}); });
export function CreateStateOfApplicabilityDialog({ export function CreateStatementOfApplicabilityDialog({
children, children,
connectionId, connectionId,
}: Props) { }: Props) {
@@ -84,19 +84,19 @@ export function CreateStateOfApplicabilityDialog({
); );
const ref = useDialogRef(); const ref = useDialogRef();
const [createStateOfApplicability, isCreating] const [createStatementOfApplicability, isCreating]
= useMutationWithToasts<CreateStateOfApplicabilityDialogMutation>( = useMutationWithToasts<CreateStatementOfApplicabilityDialogMutation>(
createMutation, createMutation,
{ {
successMessage: __( successMessage: __(
"State of applicability created successfully.", "Statement of applicability created successfully.",
), ),
errorMessage: __("Failed to create state of applicability"), errorMessage: __("Failed to create statement of applicability"),
}, },
); );
const onSubmit = async (data: z.infer<typeof schema>) => { const onSubmit = async (data: z.infer<typeof schema>) => {
await createStateOfApplicability({ await createStatementOfApplicability({
variables: { variables: {
input: { input: {
name: data.name, name: data.name,
@@ -108,11 +108,11 @@ export function CreateStateOfApplicabilityDialog({
onCompleted: (response) => { onCompleted: (response) => {
reset(); reset();
ref.current?.close(); ref.current?.close();
const stateOfApplicabilityId const statementOfApplicabilityId
= response.createStateOfApplicability.stateOfApplicabilityEdge = response.createStatementOfApplicability.statementOfApplicabilityEdge
.node.id; .node.id;
void navigate( void navigate(
`/organizations/${organizationId}/states-of-applicability/${stateOfApplicabilityId}`, `/organizations/${organizationId}/statements-of-applicability/${statementOfApplicabilityId}`,
); );
}, },
}); });
@@ -125,8 +125,8 @@ export function CreateStateOfApplicabilityDialog({
title={( title={(
<Breadcrumb <Breadcrumb
items={[ items={[
__("States of Applicability"), __("Statements of Applicability"),
__("New State of Applicability"), __("New Statement of Applicability"),
]} ]}
/> />
)} )}

View File

@@ -121,7 +121,7 @@ export const EditControlDialog = forwardRef<EditControlDialogRef>((_props, ref)
ref={dialogRef} ref={dialogRef}
className="max-w-lg" className="max-w-lg"
title={ title={
<Breadcrumb items={[__("States of Applicability"), __("Edit Statement")]} /> <Breadcrumb items={[__("Statements of Applicability"), __("Edit Statement")]} />
} }
> >
{control {control

View File

@@ -38,9 +38,9 @@ import type { LinkControlDialogQuery } from "#/__generated__/core/LinkControlDia
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
const linkControlQuery = graphql` const linkControlQuery = graphql`
query LinkControlDialogQuery($stateOfApplicabilityId: ID!, $organizationId: ID!) { query LinkControlDialogQuery($statementOfApplicabilityId: ID!, $organizationId: ID!) {
stateOfApplicability: node(id: $stateOfApplicabilityId) { statementOfApplicability: node(id: $statementOfApplicabilityId) {
... on StateOfApplicability { ... on StatementOfApplicability {
id id
applicabilityStatements(first: 10000) { applicabilityStatements(first: 10000) {
edges { edges {
@@ -103,7 +103,7 @@ const unlinkControlMutation = graphql`
`; `;
export type LinkControlDialogRef = { export type LinkControlDialogRef = {
open: (stateOfApplicabilityId: string, organizationId: string, onUpdate?: () => void) => void; open: (statementOfApplicabilityId: string, organizationId: string, onUpdate?: () => void) => void;
}; };
type Control = { type Control = {
@@ -119,12 +119,12 @@ type Control = {
function ControlRow({ function ControlRow({
control, control,
stateOfApplicabilityId, statementOfApplicabilityId,
isLinked, isLinked,
onUpdate, onUpdate,
}: { }: {
control: Control; control: Control;
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
isLinked: boolean; isLinked: boolean;
onUpdate?: () => void; onUpdate?: () => void;
}) { }) {
@@ -169,7 +169,7 @@ function ControlRow({
await linkMutate({ await linkMutate({
variables: { variables: {
input: { input: {
stateOfApplicabilityId, statementOfApplicabilityId,
controlId: control.controlId, controlId: control.controlId,
applicability: true, applicability: true,
justification: null, justification: null,
@@ -189,7 +189,7 @@ function ControlRow({
await linkMutate({ await linkMutate({
variables: { variables: {
input: { input: {
stateOfApplicabilityId, statementOfApplicabilityId,
controlId: control.controlId, controlId: control.controlId,
applicability: false, applicability: false,
justification: justification || null, justification: justification || null,
@@ -259,11 +259,11 @@ function ControlRow({
} }
function LinkControlDialogContent({ function LinkControlDialogContent({
stateOfApplicabilityId, statementOfApplicabilityId,
organizationId, organizationId,
onUpdate, onUpdate,
}: { }: {
stateOfApplicabilityId: string; statementOfApplicabilityId: string;
organizationId: string; organizationId: string;
onUpdate?: () => void; onUpdate?: () => void;
}) { }) {
@@ -272,10 +272,10 @@ function LinkControlDialogContent({
const [collapsedFrameworks, setCollapsedFrameworks] = useState<Set<string>>(new Set()); const [collapsedFrameworks, setCollapsedFrameworks] = useState<Set<string>>(new Set());
const data = useLazyLoadQuery<LinkControlDialogQuery>( const data = useLazyLoadQuery<LinkControlDialogQuery>(
linkControlQuery, linkControlQuery,
{ stateOfApplicabilityId, organizationId }, { statementOfApplicabilityId, organizationId },
{ fetchPolicy: "store-or-network" }, { fetchPolicy: "store-or-network" },
) as { ) as {
stateOfApplicability: { statementOfApplicability: {
id: string; id: string;
applicabilityStatements?: { applicabilityStatements?: {
edges: Array<{ edges: Array<{
@@ -309,7 +309,7 @@ function LinkControlDialogContent({
// Build a map of control ID -> applicability statement // Build a map of control ID -> applicability statement
const applicabilityMap = useMemo(() => { const applicabilityMap = useMemo(() => {
const map = new Map<string, { id: string; applicability: boolean; justification: string | null }>(); const map = new Map<string, { id: string; applicability: boolean; justification: string | null }>();
data.stateOfApplicability?.applicabilityStatements?.edges.forEach((edge) => { data.statementOfApplicability?.applicabilityStatements?.edges.forEach((edge) => {
map.set(edge.node.control.id, { map.set(edge.node.control.id, {
id: edge.node.id, id: edge.node.id,
applicability: edge.node.applicability, applicability: edge.node.applicability,
@@ -317,7 +317,7 @@ function LinkControlDialogContent({
}); });
}); });
return map; return map;
}, [data.stateOfApplicability?.applicabilityStatements]); }, [data.statementOfApplicability?.applicabilityStatements]);
// Merge controls with applicability info // Merge controls with applicability info
const allControls = useMemo(() => { const allControls = useMemo(() => {
@@ -410,7 +410,7 @@ function LinkControlDialogContent({
<ControlRow <ControlRow
key={control.controlId} key={control.controlId}
control={control} control={control}
stateOfApplicabilityId={stateOfApplicabilityId} statementOfApplicabilityId={statementOfApplicabilityId}
isLinked={control.applicabilityStatementId !== null} isLinked={control.applicabilityStatementId !== null}
onUpdate={onUpdate} onUpdate={onUpdate}
/> />
@@ -431,13 +431,13 @@ function LinkControlDialogContent({
export const LinkControlDialog = forwardRef<LinkControlDialogRef>((_props, ref) => { export const LinkControlDialog = forwardRef<LinkControlDialogRef>((_props, ref) => {
const { __ } = useTranslate(); const { __ } = useTranslate();
const dialogRef = useDialogRef(); const dialogRef = useDialogRef();
const [stateOfApplicabilityId, setStateOfApplicabilityId] = useState<string | null>(null); const [statementOfApplicabilityId, setStatementOfApplicabilityId] = useState<string | null>(null);
const [organizationId, setOrganizationId] = useState<string | null>(null); const [organizationId, setOrganizationId] = useState<string | null>(null);
const [onUpdateCallback, setOnUpdateCallback] = useState<(() => void) | undefined>(undefined); const [onUpdateCallback, setOnUpdateCallback] = useState<(() => void) | undefined>(undefined);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
open: (soaId: string, orgId: string, callback?: () => void) => { open: (soaId: string, orgId: string, callback?: () => void) => {
setStateOfApplicabilityId(soaId); setStatementOfApplicabilityId(soaId);
setOrganizationId(orgId); setOrganizationId(orgId);
setOnUpdateCallback(() => callback); setOnUpdateCallback(() => callback);
dialogRef.current?.open(); dialogRef.current?.open();
@@ -445,7 +445,7 @@ export const LinkControlDialog = forwardRef<LinkControlDialogRef>((_props, ref)
}), [dialogRef]); }), [dialogRef]);
const handleClose = () => { const handleClose = () => {
setStateOfApplicabilityId(null); setStatementOfApplicabilityId(null);
setOrganizationId(null); setOrganizationId(null);
setOnUpdateCallback(undefined); setOnUpdateCallback(undefined);
}; };
@@ -456,12 +456,12 @@ export const LinkControlDialog = forwardRef<LinkControlDialogRef>((_props, ref)
className="max-w-3xl" className="max-w-3xl"
title={( title={(
<Breadcrumb <Breadcrumb
items={[__("States of Applicability"), __("Add Controls")]} items={[__("Statements of Applicability"), __("Add Controls")]}
/> />
)} )}
onClose={handleClose} onClose={handleClose}
> >
{stateOfApplicabilityId && organizationId {statementOfApplicabilityId && organizationId
? ( ? (
<Suspense <Suspense
fallback={( fallback={(
@@ -471,7 +471,7 @@ export const LinkControlDialog = forwardRef<LinkControlDialogRef>((_props, ref)
)} )}
> >
<LinkControlDialogContent <LinkControlDialogContent
stateOfApplicabilityId={stateOfApplicabilityId} statementOfApplicabilityId={statementOfApplicabilityId}
organizationId={organizationId} organizationId={organizationId}
onUpdate={onUpdateCallback} onUpdate={onUpdateCallback}
/> />

View File

@@ -32,7 +32,7 @@ import { Suspense, useCallback, useRef } from "react";
import { graphql, useFragment, useRelayEnvironment } from "react-relay"; import { graphql, useFragment, useRelayEnvironment } from "react-relay";
import { fetchQuery } from "relay-runtime"; import { fetchQuery } from "relay-runtime";
import type { StateOfApplicabilityControlsTabFragment$key } from "#/__generated__/core/StateOfApplicabilityControlsTabFragment.graphql"; import type { StatementOfApplicabilityControlsTabFragment$key } from "#/__generated__/core/StatementOfApplicabilityControlsTabFragment.graphql";
import { useMutationWithToasts } from "#/hooks/useMutationWithToasts"; import { useMutationWithToasts } from "#/hooks/useMutationWithToasts";
import { useOrganizationId } from "#/hooks/useOrganizationId"; import { useOrganizationId } from "#/hooks/useOrganizationId";
@@ -46,17 +46,17 @@ import {
} from "../dialogs/EditControlDialog"; } from "../dialogs/EditControlDialog";
const refetchStatementsQuery = graphql` const refetchStatementsQuery = graphql`
query StateOfApplicabilityControlsTabRefetchQuery($stateOfApplicabilityId: ID!) { query StatementOfApplicabilityControlsTabRefetchQuery($statementOfApplicabilityId: ID!) {
node(id: $stateOfApplicabilityId) { node(id: $statementOfApplicabilityId) {
... on StateOfApplicability { ... on StatementOfApplicability {
...StateOfApplicabilityControlsTabFragment ...StatementOfApplicabilityControlsTabFragment
} }
} }
} }
`; `;
export const controlsFragment = graphql` export const controlsFragment = graphql`
fragment StateOfApplicabilityControlsTabFragment on StateOfApplicability { fragment StatementOfApplicabilityControlsTabFragment on StatementOfApplicability {
id id
organization { organization {
id id
@@ -73,7 +73,7 @@ export const controlsFragment = graphql`
) )
applicabilityStatements(first: 1000, orderBy: { direction: ASC, field: CONTROL_SECTION_TITLE }) applicabilityStatements(first: 1000, orderBy: { direction: ASC, field: CONTROL_SECTION_TITLE })
@connection(key: "StateOfApplicabilityControlsTab_applicabilityStatements") { @connection(key: "StatementOfApplicabilityControlsTab_applicabilityStatements") {
__id __id
edges { edges {
node { node {
@@ -105,7 +105,7 @@ export const controlsFragment = graphql`
`; `;
const deleteApplicabilityStatementMutation = graphql` const deleteApplicabilityStatementMutation = graphql`
mutation StateOfApplicabilityControlsTabDeleteMutation( mutation StatementOfApplicabilityControlsTabDeleteMutation(
$input: DeleteApplicabilityStatementInput! $input: DeleteApplicabilityStatementInput!
$connections: [ID!]! $connections: [ID!]!
) { ) {
@@ -115,17 +115,17 @@ const deleteApplicabilityStatementMutation = graphql`
} }
`; `;
export default function StateOfApplicabilityControlsTab({ export default function StatementOfApplicabilityControlsTab({
stateOfApplicability, statementOfApplicability,
isSnapshotMode = false, isSnapshotMode = false,
}: { }: {
stateOfApplicability: StateOfApplicabilityControlsTabFragment$key & { statementOfApplicability: StatementOfApplicabilityControlsTabFragment$key & {
id: string; id: string;
}; };
isSnapshotMode?: boolean; isSnapshotMode?: boolean;
}) { }) {
const { __ } = useTranslate(); const { __ } = useTranslate();
const data = useFragment(controlsFragment, stateOfApplicability); const data = useFragment(controlsFragment, statementOfApplicability);
const environment = useRelayEnvironment(); const environment = useRelayEnvironment();
const organizationId = useOrganizationId(); const organizationId = useOrganizationId();
const addStatementDialogRef = useRef<AddApplicabilityStatementDialogRef>(null); const addStatementDialogRef = useRef<AddApplicabilityStatementDialogRef>(null);
@@ -134,7 +134,7 @@ export default function StateOfApplicabilityControlsTab({
const handleDialogClose = useCallback(() => { const handleDialogClose = useCallback(() => {
if (!data.id) return; if (!data.id) return;
fetchQuery(environment, refetchStatementsQuery, { fetchQuery(environment, refetchStatementsQuery, {
stateOfApplicabilityId: data.id, statementOfApplicabilityId: data.id,
}).subscribe({}); }).subscribe({});
}, [environment, data.id]); }, [environment, data.id]);

View File

@@ -45,7 +45,7 @@ import { processingActivityRoutes } from "./routes/processingActivityRoutes";
import { rightsRequestRoutes } from "./routes/rightsRequestRoutes"; import { rightsRequestRoutes } from "./routes/rightsRequestRoutes";
import { riskRoutes } from "./routes/riskRoutes"; import { riskRoutes } from "./routes/riskRoutes";
import { snapshotsRoutes } from "./routes/snapshotsRoutes"; import { snapshotsRoutes } from "./routes/snapshotsRoutes";
import { statesOfApplicabilityRoutes } from "./routes/statesOfApplicabilityRoutes"; import { statementsOfApplicabilityRoutes } from "./routes/statementsOfApplicabilityRoutes";
import { taskRoutes } from "./routes/taskRoutes"; import { taskRoutes } from "./routes/taskRoutes";
import { vendorRoutes } from "./routes/vendorRoutes"; import { vendorRoutes } from "./routes/vendorRoutes";
@@ -288,7 +288,7 @@ const routes = [
...obligationRoutes, ...obligationRoutes,
...rightsRequestRoutes, ...rightsRequestRoutes,
...processingActivityRoutes, ...processingActivityRoutes,
...statesOfApplicabilityRoutes, ...statementsOfApplicabilityRoutes,
...accessReviewRoutes, ...accessReviewRoutes,
...compliancePageRoutes, ...compliancePageRoutes,
...snapshotsRoutes, ...snapshotsRoutes,

View File

@@ -17,33 +17,33 @@ import type { AppRoute } from "@probo/routes";
import { PageSkeleton } from "#/components/skeletons/PageSkeleton"; import { PageSkeleton } from "#/components/skeletons/PageSkeleton";
export const statesOfApplicabilityRoutes = [ export const statementsOfApplicabilityRoutes = [
{ {
path: "states-of-applicability", path: "statements-of-applicability",
Fallback: PageSkeleton, Fallback: PageSkeleton,
Component: lazy( Component: lazy(
() => import("#/pages/organizations/states-of-applicability/StatesOfApplicabilityPageLoader"), () => import("#/pages/organizations/statements-of-applicability/StatementsOfApplicabilityPageLoader"),
), ),
}, },
{ {
path: "snapshots/:snapshotId/states-of-applicability", path: "snapshots/:snapshotId/statements-of-applicability",
Fallback: PageSkeleton, Fallback: PageSkeleton,
Component: lazy( Component: lazy(
() => import("#/pages/organizations/states-of-applicability/StatesOfApplicabilityPageLoader"), () => import("#/pages/organizations/statements-of-applicability/StatementsOfApplicabilityPageLoader"),
), ),
}, },
{ {
path: "states-of-applicability/:stateOfApplicabilityId", path: "statements-of-applicability/:statementOfApplicabilityId",
Fallback: PageSkeleton, Fallback: PageSkeleton,
Component: lazy( Component: lazy(
() => import("#/pages/organizations/states-of-applicability/StateOfApplicabilityDetailPageLoader"), () => import("#/pages/organizations/statements-of-applicability/StatementOfApplicabilityDetailPageLoader"),
), ),
}, },
{ {
path: "snapshots/:snapshotId/states-of-applicability/:stateOfApplicabilityId", path: "snapshots/:snapshotId/statements-of-applicability/:statementOfApplicabilityId",
Fallback: PageSkeleton, Fallback: PageSkeleton,
Component: lazy( Component: lazy(
() => import("#/pages/organizations/states-of-applicability/StateOfApplicabilityDetailPageLoader"), () => import("#/pages/organizations/statements-of-applicability/StatementOfApplicabilityDetailPageLoader"),
), ),
}, },
] satisfies AppRoute[]; ] satisfies AppRoute[];

View File

@@ -22,7 +22,7 @@ export const snapshotTypes = [
"FINDINGS", "FINDINGS",
"OBLIGATIONS", "OBLIGATIONS",
"PROCESSING_ACTIVITIES", "PROCESSING_ACTIVITIES",
"STATES_OF_APPLICABILITY", "STATEMENTS_OF_APPLICABILITY",
] as const; ] as const;
export function getSnapshotTypeLabel(__: Translator, type: string | null | undefined) { export function getSnapshotTypeLabel(__: Translator, type: string | null | undefined) {
@@ -47,8 +47,8 @@ export function getSnapshotTypeLabel(__: Translator, type: string | null | undef
return __("Obligations"); return __("Obligations");
case "PROCESSING_ACTIVITIES": case "PROCESSING_ACTIVITIES":
return __("Processing Activities"); return __("Processing Activities");
case "STATES_OF_APPLICABILITY": case "STATEMENTS_OF_APPLICABILITY":
return __("States of Applicability"); return __("Statements of Applicability");
default: default:
return __("Unknown"); return __("Unknown");
} }
@@ -72,8 +72,8 @@ export function getSnapshotTypeUrlPath(type?: string): string {
return "/obligations"; return "/obligations";
case "PROCESSING_ACTIVITIES": case "PROCESSING_ACTIVITIES":
return "/processing-activities"; return "/processing-activities";
case "STATES_OF_APPLICABILITY": case "STATEMENTS_OF_APPLICABILITY":
return "/states-of-applicability"; return "/statements-of-applicability";
default: default:
return ""; return "";
} }

View File

@@ -47,7 +47,7 @@ export function Sidebar({ children }: PropsWithChildren) {
<aside <aside
className={clsx( className={clsx(
"border-r border-border-solid relative pt-16 flex-none", "border-r border-border-solid relative pt-16 flex-none",
open ? "px-4 w-[260px]" : "px-2", open ? "px-4 w-[280px]" : "px-2",
)} )}
> >
{children} {children}

View File

@@ -24,9 +24,9 @@ import (
) )
const createMutation = ` const createMutation = `
mutation($input: CreateStateOfApplicabilityInput!) { mutation($input: CreateStatementOfApplicabilityInput!) {
createStateOfApplicability(input: $input) { createStatementOfApplicability(input: $input) {
stateOfApplicabilityEdge { statementOfApplicabilityEdge {
node { node {
id id
name name
@@ -38,15 +38,15 @@ mutation($input: CreateStateOfApplicabilityInput!) {
` `
type createResponse struct { type createResponse struct {
CreateStateOfApplicability struct { CreateStatementOfApplicability struct {
StateOfApplicabilityEdge struct { StatementOfApplicabilityEdge struct {
Node struct { Node struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CreatedAt string `json:"createdAt"` CreatedAt string `json:"createdAt"`
} `json:"node"` } `json:"node"`
} `json:"stateOfApplicabilityEdge"` } `json:"statementOfApplicabilityEdge"`
} `json:"createStateOfApplicability"` } `json:"createStatementOfApplicability"`
} }
func NewCmdCreate(f *cmdutil.Factory) *cobra.Command { func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
@@ -107,7 +107,7 @@ func NewCmdCreate(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("cannot parse response: %w", err) return fmt.Errorf("cannot parse response: %w", err)
} }
s := resp.CreateStateOfApplicability.StateOfApplicabilityEdge.Node s := resp.CreateStatementOfApplicability.StatementOfApplicabilityEdge.Node
_, _ = fmt.Fprintf( _, _ = fmt.Fprintf(
f.IOStreams.Out, f.IOStreams.Out,
"Created statement of applicability %s (%s)\n", "Created statement of applicability %s (%s)\n",

View File

@@ -24,9 +24,9 @@ import (
) )
const deleteMutation = ` const deleteMutation = `
mutation($input: DeleteStateOfApplicabilityInput!) { mutation($input: DeleteStatementOfApplicabilityInput!) {
deleteStateOfApplicability(input: $input) { deleteStatementOfApplicability(input: $input) {
deletedStateOfApplicabilityId deletedStatementOfApplicabilityId
} }
} }
` `
@@ -78,7 +78,7 @@ func NewCmdDelete(f *cmdutil.Factory) *cobra.Command {
deleteMutation, deleteMutation,
map[string]any{ map[string]any{
"input": map[string]any{ "input": map[string]any{
"stateOfApplicabilityId": args[0], "statementOfApplicabilityId": args[0],
}, },
}, },
) )

View File

@@ -24,11 +24,11 @@ import (
) )
const listQuery = ` const listQuery = `
query($id: ID!, $first: Int, $after: CursorKey, $orderBy: StateOfApplicabilityOrder) { query($id: ID!, $first: Int, $after: CursorKey, $orderBy: StatementOfApplicabilityOrder) {
node(id: $id) { node(id: $id) {
__typename __typename
... on Organization { ... on Organization {
statesOfApplicability(first: $first, after: $after, orderBy: $orderBy) { statementsOfApplicability(first: $first, after: $after, orderBy: $orderBy) {
totalCount totalCount
edges { edges {
node { node {
@@ -48,7 +48,7 @@ query($id: ID!, $first: Int, $after: CursorKey, $orderBy: StateOfApplicabilityOr
} }
` `
type stateOfApplicability struct { type statementOfApplicability struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
CreatedAt string `json:"createdAt"` CreatedAt string `json:"createdAt"`
@@ -118,11 +118,11 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
listQuery, listQuery,
variables, variables,
flagLimit, flagLimit,
func(data json.RawMessage) (*api.Connection[stateOfApplicability], error) { func(data json.RawMessage) (*api.Connection[statementOfApplicability], error) {
var resp struct { var resp struct {
Node *struct { Node *struct {
Typename string `json:"__typename"` Typename string `json:"__typename"`
StatesOfApplicability api.Connection[stateOfApplicability] `json:"statesOfApplicability"` StatementsOfApplicability api.Connection[statementOfApplicability] `json:"statementsOfApplicability"`
} `json:"node"` } `json:"node"`
} }
if err := json.Unmarshal(data, &resp); err != nil { if err := json.Unmarshal(data, &resp); err != nil {
@@ -134,7 +134,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if resp.Node.Typename != "Organization" { if resp.Node.Typename != "Organization" {
return nil, fmt.Errorf("expected Organization node, got %s", resp.Node.Typename) return nil, fmt.Errorf("expected Organization node, got %s", resp.Node.Typename)
} }
return &resp.Node.StatesOfApplicability, nil return &resp.Node.StatementsOfApplicability, nil
}, },
) )
if err != nil { if err != nil {
@@ -143,7 +143,7 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if *flagOutput == cmdutil.OutputJSON { if *flagOutput == cmdutil.OutputJSON {
if soas == nil { if soas == nil {
soas = []stateOfApplicability{} soas = []statementOfApplicability{}
} }
return cmdutil.PrintJSON(f.IOStreams.Out, soas) return cmdutil.PrintJSON(f.IOStreams.Out, soas)
} }

View File

@@ -130,9 +130,9 @@ func NewCmdAdd(f *cmdutil.Factory) *cobra.Command {
) )
input := map[string]any{ input := map[string]any{
"stateOfApplicabilityId": flagSoA, "statementOfApplicabilityId": flagSoA,
"controlId": flagControl, "controlId": flagControl,
"applicability": flagApplicable, "applicability": flagApplicable,
} }
if flagJustification != "" { if flagJustification != "" {

View File

@@ -27,7 +27,7 @@ const listQuery = `
query($id: ID!, $first: Int, $after: CursorKey, $orderBy: ApplicabilityStatementOrder) { query($id: ID!, $first: Int, $after: CursorKey, $orderBy: ApplicabilityStatementOrder) {
node(id: $id) { node(id: $id) {
__typename __typename
... on StateOfApplicability { ... on StatementOfApplicability {
applicabilityStatements(first: $first, after: $after, orderBy: $orderBy) { applicabilityStatements(first: $first, after: $after, orderBy: $orderBy) {
totalCount totalCount
edges { edges {
@@ -130,8 +130,8 @@ func NewCmdList(f *cmdutil.Factory) *cobra.Command {
if resp.Node == nil { if resp.Node == nil {
return nil, fmt.Errorf("statement of applicability %s not found", args[0]) return nil, fmt.Errorf("statement of applicability %s not found", args[0])
} }
if resp.Node.Typename != "StateOfApplicability" { if resp.Node.Typename != "StatementOfApplicability" {
return nil, fmt.Errorf("expected StateOfApplicability node, got %s", resp.Node.Typename) return nil, fmt.Errorf("expected StatementOfApplicability node, got %s", resp.Node.Typename)
} }
return &resp.Node.ApplicabilityStatements, nil return &resp.Node.ApplicabilityStatements, nil
}, },

View File

@@ -24,9 +24,9 @@ import (
) )
const updateMutation = ` const updateMutation = `
mutation($input: UpdateStateOfApplicabilityInput!) { mutation($input: UpdateStatementOfApplicabilityInput!) {
updateStateOfApplicability(input: $input) { updateStatementOfApplicability(input: $input) {
stateOfApplicability { statementOfApplicability {
id id
name name
} }
@@ -35,12 +35,12 @@ mutation($input: UpdateStateOfApplicabilityInput!) {
` `
type updateResponse struct { type updateResponse struct {
UpdateStateOfApplicability struct { UpdateStatementOfApplicability struct {
StateOfApplicability struct { StatementOfApplicability struct {
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
} `json:"stateOfApplicability"` } `json:"statementOfApplicability"`
} `json:"updateStateOfApplicability"` } `json:"updateStatementOfApplicability"`
} }
func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command { func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
@@ -103,7 +103,7 @@ func NewCmdUpdate(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("cannot parse response: %w", err) return fmt.Errorf("cannot parse response: %w", err)
} }
s := resp.UpdateStateOfApplicability.StateOfApplicability s := resp.UpdateStatementOfApplicability.StatementOfApplicability
_, _ = fmt.Fprintf( _, _ = fmt.Fprintf(
f.IOStreams.Out, f.IOStreams.Out,
"Updated statement of applicability %s (%s)\n", "Updated statement of applicability %s (%s)\n",

View File

@@ -28,7 +28,7 @@ const viewQuery = `
query($id: ID!) { query($id: ID!) {
node(id: $id) { node(id: $id) {
__typename __typename
... on StateOfApplicability { ... on StatementOfApplicability {
id id
name name
owner { owner {
@@ -104,8 +104,8 @@ func NewCmdView(f *cmdutil.Factory) *cobra.Command {
return fmt.Errorf("statement of applicability %s not found", args[0]) return fmt.Errorf("statement of applicability %s not found", args[0])
} }
if resp.Node.Typename != "StateOfApplicability" { if resp.Node.Typename != "StatementOfApplicability" {
return fmt.Errorf("expected StateOfApplicability node, got %s", resp.Node.Typename) return fmt.Errorf("expected StatementOfApplicability node, got %s", resp.Node.Typename)
} }
if *flagOutput == cmdutil.OutputJSON { if *flagOutput == cmdutil.OutputJSON {

View File

@@ -30,15 +30,15 @@ import (
type ( type (
ApplicabilityStatement struct { ApplicabilityStatement struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
StateOfApplicabilityID gid.GID `db:"state_of_applicability_id"` StatementOfApplicabilityID gid.GID `db:"statement_of_applicability_id"`
ControlID gid.GID `db:"control_id"` ControlID gid.GID `db:"control_id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
SnapshotID *gid.GID `db:"snapshot_id"` SnapshotID *gid.GID `db:"snapshot_id"`
Applicability bool `db:"applicability"` Applicability bool `db:"applicability"`
Justification *string `db:"justification"` Justification *string `db:"justification"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
// Ordering only. // Ordering only.
SectionTitle string `db:"section_title"` SectionTitle string `db:"section_title"`
@@ -82,7 +82,7 @@ func (sac *ApplicabilityStatement) LoadByID(
WITH stmt AS ( WITH stmt AS (
SELECT SELECT
a.id, a.id,
a.state_of_applicability_id, a.statement_of_applicability_id,
a.control_id, a.control_id,
a.organization_id, a.organization_id,
a.snapshot_id, a.snapshot_id,
@@ -104,7 +104,7 @@ WITH stmt AS (
) )
SELECT SELECT
id, id,
state_of_applicability_id, statement_of_applicability_id,
control_id, control_id,
organization_id, organization_id,
snapshot_id, snapshot_id,
@@ -142,25 +142,25 @@ LIMIT 1;
return nil return nil
} }
func (sac *ApplicabilityStatement) LoadByStateOfApplicabilityIDAndControlID( func (sac *ApplicabilityStatement) LoadByStatementOfApplicabilityIDAndControlID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
controlID gid.GID, controlID gid.GID,
) error { ) error {
q := ` q := `
WITH current_soa AS ( WITH current_soa AS (
SELECT id SELECT id
FROM states_of_applicability FROM statements_of_applicability
WHERE WHERE
%s %s
AND id = @state_of_applicability_id AND id = @statement_of_applicability_id
AND snapshot_id IS NULL AND snapshot_id IS NULL
) )
SELECT SELECT
soac.id, soac.id,
soac.state_of_applicability_id, soac.statement_of_applicability_id,
soac.control_id, soac.control_id,
soac.organization_id, soac.organization_id,
soac.snapshot_id, soac.snapshot_id,
@@ -172,7 +172,7 @@ SELECT
FROM FROM
applicability_statements soac applicability_statements soac
INNER JOIN INNER JOIN
current_soa ON soac.state_of_applicability_id = current_soa.id current_soa ON soac.statement_of_applicability_id = current_soa.id
INNER JOIN INNER JOIN
controls c ON c.id = soac.control_id controls c ON c.id = soac.control_id
INNER JOIN INNER JOIN
@@ -184,8 +184,8 @@ LIMIT 1;
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"state_of_applicability_id": stateOfApplicabilityID, "statement_of_applicability_id": statementOfApplicabilityID,
"control_id": controlID, "control_id": controlID,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
@@ -215,7 +215,7 @@ func (sac *ApplicabilityStatement) Insert(
INSERT INTO INSERT INTO
applicability_statements ( applicability_statements (
id, id,
state_of_applicability_id, statement_of_applicability_id,
control_id, control_id,
organization_id, organization_id,
tenant_id, tenant_id,
@@ -227,7 +227,7 @@ INSERT INTO
) )
VALUES ( VALUES (
@id, @id,
@state_of_applicability_id, @statement_of_applicability_id,
@control_id, @control_id,
@organization_id, @organization_id,
@tenant_id, @tenant_id,
@@ -240,16 +240,16 @@ VALUES (
` `
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"id": sac.ID, "id": sac.ID,
"state_of_applicability_id": sac.StateOfApplicabilityID, "statement_of_applicability_id": sac.StatementOfApplicabilityID,
"control_id": sac.ControlID, "control_id": sac.ControlID,
"organization_id": sac.OrganizationID, "organization_id": sac.OrganizationID,
"tenant_id": scope.GetTenantID(), "tenant_id": scope.GetTenantID(),
"snapshot_id": sac.SnapshotID, "snapshot_id": sac.SnapshotID,
"applicability": sac.Applicability, "applicability": sac.Applicability,
"justification": sac.Justification, "justification": sac.Justification,
"created_at": sac.CreatedAt, "created_at": sac.CreatedAt,
"updated_at": sac.UpdatedAt, "updated_at": sac.UpdatedAt,
} }
_, err := conn.Exec(ctx, q, args) _, err := conn.Exec(ctx, q, args)
@@ -280,17 +280,17 @@ SET
updated_at = @updated_at updated_at = @updated_at
WHERE WHERE
%s %s
AND state_of_applicability_id = @state_of_applicability_id AND statement_of_applicability_id = @statement_of_applicability_id
AND control_id = @control_id AND control_id = @control_id
` `
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"state_of_applicability_id": sac.StateOfApplicabilityID, "statement_of_applicability_id": sac.StatementOfApplicabilityID,
"control_id": sac.ControlID, "control_id": sac.ControlID,
"applicability": sac.Applicability, "applicability": sac.Applicability,
"justification": sac.Justification, "justification": sac.Justification,
"updated_at": sac.UpdatedAt, "updated_at": sac.UpdatedAt,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
@@ -347,20 +347,20 @@ func (sac *ApplicabilityStatement) Delete(
q := ` q := `
WITH current_soa AS ( WITH current_soa AS (
SELECT id SELECT id
FROM states_of_applicability FROM statements_of_applicability
WHERE WHERE
%s %s
AND id = @state_of_applicability_id AND id = @statement_of_applicability_id
AND snapshot_id IS NULL AND snapshot_id IS NULL
) )
DELETE FROM applicability_statements DELETE FROM applicability_statements
WHERE state_of_applicability_id IN (SELECT id FROM current_soa) WHERE statement_of_applicability_id IN (SELECT id FROM current_soa)
AND control_id = @control_id; AND control_id = @control_id;
` `
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"state_of_applicability_id": sac.StateOfApplicabilityID, "statement_of_applicability_id": sac.StatementOfApplicabilityID,
"control_id": sac.ControlID, "control_id": sac.ControlID,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
@@ -398,18 +398,18 @@ WHERE
return nil return nil
} }
func (sacs *ApplicabilityStatements) LoadByStateOfApplicabilityID( func (sacs *ApplicabilityStatements) LoadByStatementOfApplicabilityID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
cursor *page.Cursor[ApplicabilityStatementOrderField], cursor *page.Cursor[ApplicabilityStatementOrderField],
) error { ) error {
q := ` q := `
WITH stmt AS ( WITH stmt AS (
SELECT SELECT
a.id, a.id,
a.state_of_applicability_id, a.statement_of_applicability_id,
a.control_id, a.control_id,
a.organization_id, a.organization_id,
a.snapshot_id, a.snapshot_id,
@@ -427,11 +427,11 @@ WITH stmt AS (
frameworks f ON f.id = c.framework_id frameworks f ON f.id = c.framework_id
WHERE WHERE
a.%[1]s a.%[1]s
AND a.state_of_applicability_id = @state_of_applicability_id AND a.statement_of_applicability_id = @statement_of_applicability_id
) )
SELECT SELECT
id, id,
state_of_applicability_id, statement_of_applicability_id,
control_id, control_id,
organization_id, organization_id,
snapshot_id, snapshot_id,
@@ -448,7 +448,7 @@ WHERE
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{ args := pgx.NamedArgs{
"state_of_applicability_id": stateOfApplicabilityID, "statement_of_applicability_id": statementOfApplicabilityID,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
maps.Copy(args, cursor.SQLArguments()) maps.Copy(args, cursor.SQLArguments())
@@ -467,11 +467,11 @@ WHERE
return nil return nil
} }
func (sacs *ApplicabilityStatements) CountByStateOfApplicabilityID( func (sacs *ApplicabilityStatements) CountByStatementOfApplicabilityID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
) (int, error) { ) (int, error) {
q := ` q := `
SELECT SELECT
@@ -480,11 +480,11 @@ FROM
applicability_statements applicability_statements
WHERE WHERE
%s %s
AND state_of_applicability_id = @state_of_applicability_id; AND statement_of_applicability_id = @statement_of_applicability_id;
` `
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.NamedArgs{"state_of_applicability_id": stateOfApplicabilityID} args := pgx.NamedArgs{"statement_of_applicability_id": statementOfApplicabilityID}
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
var count int var count int
@@ -506,7 +506,7 @@ func (sacs *ApplicabilityStatements) LoadByControlID(
WITH soac_ctrl AS ( WITH soac_ctrl AS (
SELECT SELECT
soac.id, soac.id,
soac.state_of_applicability_id, soac.statement_of_applicability_id,
soac.control_id, soac.control_id,
soac.organization_id, soac.organization_id,
soac.snapshot_id, soac.snapshot_id,
@@ -519,7 +519,7 @@ WITH soac_ctrl AS (
FROM FROM
applicability_statements soac applicability_statements soac
INNER JOIN INNER JOIN
states_of_applicability soa ON soac.state_of_applicability_id = soa.id statements_of_applicability soa ON soac.statement_of_applicability_id = soa.id
INNER JOIN INNER JOIN
controls c ON c.id = soac.control_id controls c ON c.id = soac.control_id
INNER JOIN INNER JOIN
@@ -531,7 +531,7 @@ WITH soac_ctrl AS (
) )
SELECT SELECT
id, id,
state_of_applicability_id, statement_of_applicability_id,
control_id, control_id,
organization_id, organization_id,
snapshot_id, snapshot_id,

View File

@@ -97,8 +97,8 @@ func ResourceTypeName(entityType uint16) string {
return "TransferImpactAssessment" return "TransferImpactAssessment"
case RightsRequestEntityType: case RightsRequestEntityType:
return "RightsRequest" return "RightsRequest"
case StateOfApplicabilityEntityType: case StatementOfApplicabilityEntityType:
return "StateOfApplicability" return "StatementOfApplicability"
case ApplicabilityStatementEntityType: case ApplicabilityStatementEntityType:
return "ApplicabilityStatement" return "ApplicabilityStatement"
case WebhookSubscriptionEntityType: case WebhookSubscriptionEntityType:

View File

@@ -1041,11 +1041,11 @@ WHERE %s
return nil return nil
} }
func (c *Controls) CountByStateOfApplicabilityID( func (c *Controls) CountByStatementOfApplicabilityID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
filter *ControlFilter, filter *ControlFilter,
) (int, error) { ) (int, error) {
q := ` q := `
@@ -1059,7 +1059,7 @@ WITH ctrl AS (
INNER JOIN INNER JOIN
applicability_statements soac ON c.id = soac.control_id applicability_statements soac ON c.id = soac.control_id
WHERE WHERE
soac.state_of_applicability_id = @state_of_applicability_id soac.statement_of_applicability_id = @statement_of_applicability_id
) )
SELECT SELECT
COUNT(id) COUNT(id)
@@ -1070,7 +1070,7 @@ WHERE %s
` `
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
args := pgx.NamedArgs{"state_of_applicability_id": stateOfApplicabilityID} args := pgx.NamedArgs{"statement_of_applicability_id": statementOfApplicabilityID}
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments()) maps.Copy(args, filter.SQLArguments())

View File

@@ -72,7 +72,7 @@ const (
DataProtectionImpactAssessmentEntityType uint16 = 46 DataProtectionImpactAssessmentEntityType uint16 = 46
TransferImpactAssessmentEntityType uint16 = 47 TransferImpactAssessmentEntityType uint16 = 47
RightsRequestEntityType uint16 = 48 RightsRequestEntityType uint16 = 48
StateOfApplicabilityEntityType uint16 = 49 StatementOfApplicabilityEntityType uint16 = 49
ApplicabilityStatementEntityType uint16 = 50 ApplicabilityStatementEntityType uint16 = 50
MembershipProfileEntityType uint16 = 51 MembershipProfileEntityType uint16 = 51
SCIMConfigurationEntityType uint16 = 52 SCIMConfigurationEntityType uint16 = 52
@@ -194,8 +194,8 @@ func NewEntityFromID(id gid.GID) (any, bool) {
return &TransferImpactAssessment{ID: id}, true return &TransferImpactAssessment{ID: id}, true
case RightsRequestEntityType: case RightsRequestEntityType:
return &RightsRequest{ID: id}, true return &RightsRequest{ID: id}, true
case StateOfApplicabilityEntityType: case StatementOfApplicabilityEntityType:
return &StateOfApplicability{ID: id}, true return &StatementOfApplicability{ID: id}, true
case ApplicabilityStatementEntityType: case ApplicabilityStatementEntityType:
return &ApplicabilityStatement{ID: id}, true return &ApplicabilityStatement{ID: id}, true
case MembershipProfileEntityType: case MembershipProfileEntityType:

View File

@@ -0,0 +1,19 @@
-- Copyright (c) 2025-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.
-- Rename states_of_applicability to statements_of_applicability
ALTER TABLE states_of_applicability RENAME TO statements_of_applicability;
ALTER TABLE applicability_statements RENAME COLUMN state_of_applicability_id TO statement_of_applicability_id;
ALTER TYPE snapshots_type RENAME VALUE 'STATES_OF_APPLICABILITY' TO 'STATEMENTS_OF_APPLICABILITY';

View File

@@ -24,14 +24,14 @@ type (
) )
const ( const (
SnapshotsTypeRisks SnapshotsType = "RISKS" SnapshotsTypeRisks SnapshotsType = "RISKS"
SnapshotsTypeVendors SnapshotsType = "VENDORS" SnapshotsTypeVendors SnapshotsType = "VENDORS"
SnapshotsTypeAssets SnapshotsType = "ASSETS" SnapshotsTypeAssets SnapshotsType = "ASSETS"
SnapshotsTypeData SnapshotsType = "DATA" SnapshotsTypeData SnapshotsType = "DATA"
SnapshotsTypeFindings SnapshotsType = "FINDINGS" SnapshotsTypeFindings SnapshotsType = "FINDINGS"
SnapshotsTypeObligations SnapshotsType = "OBLIGATIONS" SnapshotsTypeObligations SnapshotsType = "OBLIGATIONS"
SnapshotsTypeProcessingActivities SnapshotsType = "PROCESSING_ACTIVITIES" SnapshotsTypeProcessingActivities SnapshotsType = "PROCESSING_ACTIVITIES"
SnapshotsTypeStatesOfApplicability SnapshotsType = "STATES_OF_APPLICABILITY" SnapshotsTypeStatementsOfApplicability SnapshotsType = "STATEMENTS_OF_APPLICABILITY"
) )
func SnapshotsTypes() []SnapshotsType { func SnapshotsTypes() []SnapshotsType {
@@ -43,7 +43,7 @@ func SnapshotsTypes() []SnapshotsType {
SnapshotsTypeFindings, SnapshotsTypeFindings,
SnapshotsTypeObligations, SnapshotsTypeObligations,
SnapshotsTypeProcessingActivities, SnapshotsTypeProcessingActivities,
SnapshotsTypeStatesOfApplicability, SnapshotsTypeStatementsOfApplicability,
} }
} }
@@ -77,8 +77,8 @@ func (st *SnapshotsType) Scan(value any) error {
*st = SnapshotsTypeObligations *st = SnapshotsTypeObligations
case SnapshotsTypeProcessingActivities.String(): case SnapshotsTypeProcessingActivities.String():
*st = SnapshotsTypeProcessingActivities *st = SnapshotsTypeProcessingActivities
case SnapshotsTypeStatesOfApplicability.String(): case SnapshotsTypeStatementsOfApplicability.String():
*st = SnapshotsTypeStatesOfApplicability *st = SnapshotsTypeStatementsOfApplicability
default: default:
return fmt.Errorf("invalid SnapshotsType value: %q", s) return fmt.Errorf("invalid SnapshotsType value: %q", s)
} }

View File

@@ -42,8 +42,8 @@ func GetSnapshottable(snapshotType SnapshotsType) (Snapshottable, error) {
return ProcessingActivities{}, nil return ProcessingActivities{}, nil
case SnapshotsTypeVendors: case SnapshotsTypeVendors:
return Vendors{}, nil return Vendors{}, nil
case SnapshotsTypeStatesOfApplicability: case SnapshotsTypeStatementsOfApplicability:
return StatesOfApplicability{}, nil return StatementsOfApplicability{}, nil
default: default:
return nil, fmt.Errorf("unsupported snapshot type: %s", snapshotType) return nil, fmt.Errorf("unsupported snapshot type: %s", snapshotType)
} }

View File

@@ -29,7 +29,7 @@ import (
) )
type ( type (
StateOfApplicability struct { StatementOfApplicability struct {
ID gid.GID `db:"id"` ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"` OrganizationID gid.GID `db:"organization_id"`
Name string `db:"name"` Name string `db:"name"`
@@ -40,39 +40,39 @@ type (
UpdatedAt time.Time `db:"updated_at"` UpdatedAt time.Time `db:"updated_at"`
} }
StatesOfApplicability []*StateOfApplicability StatementsOfApplicability []*StatementOfApplicability
) )
func (s StateOfApplicability) CursorKey(orderBy StateOfApplicabilityOrderField) page.CursorKey { func (s StatementOfApplicability) CursorKey(orderBy StatementOfApplicabilityOrderField) page.CursorKey {
switch orderBy { switch orderBy {
case StateOfApplicabilityOrderFieldCreatedAt: case StatementOfApplicabilityOrderFieldCreatedAt:
return page.NewCursorKey(s.ID, s.CreatedAt) return page.NewCursorKey(s.ID, s.CreatedAt)
case StateOfApplicabilityOrderFieldName: case StatementOfApplicabilityOrderFieldName:
return page.NewCursorKey(s.ID, s.Name) return page.NewCursorKey(s.ID, s.Name)
} }
panic(fmt.Sprintf("unsupported order by: %s", orderBy)) panic(fmt.Sprintf("unsupported order by: %s", orderBy))
} }
func (s *StateOfApplicability) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) { func (s *StatementOfApplicability) AuthorizationAttributes(ctx context.Context, conn pg.Querier) (map[string]string, error) {
q := `SELECT organization_id FROM states_of_applicability WHERE id = $1 LIMIT 1;` q := `SELECT organization_id FROM statements_of_applicability WHERE id = $1 LIMIT 1;`
var organizationID gid.GID var organizationID gid.GID
if err := conn.QueryRow(ctx, q, s.ID).Scan(&organizationID); err != nil { if err := conn.QueryRow(ctx, q, s.ID).Scan(&organizationID); err != nil {
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrResourceNotFound return nil, ErrResourceNotFound
} }
return nil, fmt.Errorf("cannot query state of applicability authorization attributes: %w", err) return nil, fmt.Errorf("cannot query statement of applicability authorization attributes: %w", err)
} }
return map[string]string{"organization_id": organizationID.String()}, nil return map[string]string{"organization_id": organizationID.String()}, nil
} }
func (s *StateOfApplicability) LoadByID( func (s *StatementOfApplicability) LoadByID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
) error { ) error {
q := ` q := `
SELECT SELECT
@@ -85,43 +85,43 @@ SELECT
created_at, created_at,
updated_at updated_at
FROM FROM
states_of_applicability statements_of_applicability
WHERE WHERE
%s %s
AND id = @state_of_applicability_id AND id = @statement_of_applicability_id
LIMIT 1; LIMIT 1;
` `
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{"state_of_applicability_id": stateOfApplicabilityID} args := pgx.StrictNamedArgs{"statement_of_applicability_id": statementOfApplicabilityID}
maps.Copy(args, scope.SQLArguments()) 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 states_of_applicability: %w", err) return fmt.Errorf("cannot query statements_of_applicability: %w", err)
} }
stateOfApplicability, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[StateOfApplicability]) statementOfApplicability, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[StatementOfApplicability])
if err != nil { if err != nil {
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
return ErrResourceNotFound return ErrResourceNotFound
} }
return fmt.Errorf("cannot collect state_of_applicability: %w", err) return fmt.Errorf("cannot collect statement_of_applicability: %w", err)
} }
*s = stateOfApplicability *s = statementOfApplicability
return nil return nil
} }
func (s *StatesOfApplicability) LoadByOrganizationID( func (s *StatementsOfApplicability) LoadByOrganizationID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
organizationID gid.GID, organizationID gid.GID,
cursor *page.Cursor[StateOfApplicabilityOrderField], cursor *page.Cursor[StatementOfApplicabilityOrderField],
filter *StateOfApplicabilityFilter, filter *StatementOfApplicabilityFilter,
) error { ) error {
q := ` q := `
SELECT SELECT
@@ -134,7 +134,7 @@ SELECT
created_at, created_at,
updated_at updated_at
FROM FROM
states_of_applicability statements_of_applicability
WHERE WHERE
%s %s
AND organization_id = @organization_id AND organization_id = @organization_id
@@ -150,30 +150,30 @@ WHERE
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 states_of_applicability: %w", err) return fmt.Errorf("cannot query statements_of_applicability: %w", err)
} }
statesOfApplicability, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[StateOfApplicability]) statementsOfApplicability, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[StatementOfApplicability])
if err != nil { if err != nil {
return fmt.Errorf("cannot collect states_of_applicability: %w", err) return fmt.Errorf("cannot collect statements_of_applicability: %w", err)
} }
*s = statesOfApplicability *s = statementsOfApplicability
return nil return nil
} }
func (s *StatesOfApplicability) CountByOrganizationID( func (s *StatementsOfApplicability) CountByOrganizationID(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
organizationID gid.GID, organizationID gid.GID,
filter *StateOfApplicabilityFilter, filter *StatementOfApplicabilityFilter,
) (int, error) { ) (int, error) {
q := ` q := `
SELECT SELECT
COUNT(*) COUNT(*)
FROM FROM
states_of_applicability statements_of_applicability
WHERE WHERE
%s %s
AND organization_id = @organization_id AND organization_id = @organization_id
@@ -190,20 +190,20 @@ WHERE
row := conn.QueryRow(ctx, q, args) row := conn.QueryRow(ctx, q, args)
var count int var count int
if err := row.Scan(&count); err != nil { if err := row.Scan(&count); err != nil {
return 0, fmt.Errorf("cannot count states_of_applicability: %w", err) return 0, fmt.Errorf("cannot count statements_of_applicability: %w", err)
} }
return count, nil return count, nil
} }
func (s *StateOfApplicability) Insert( func (s *StatementOfApplicability) Insert(
ctx context.Context, ctx context.Context,
conn pg.Tx, conn pg.Tx,
scope Scoper, scope Scoper,
) error { ) error {
q := ` q := `
INSERT INTO INSERT INTO
states_of_applicability ( statements_of_applicability (
tenant_id, tenant_id,
id, id,
organization_id, organization_id,
@@ -216,7 +216,7 @@ INSERT INTO
) )
VALUES ( VALUES (
@tenant_id, @tenant_id,
@state_of_applicability_id, @statement_of_applicability_id,
@organization_id, @organization_id,
@name, @name,
@source_id, @source_id,
@@ -228,15 +228,15 @@ VALUES (
` `
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(), "tenant_id": scope.GetTenantID(),
"state_of_applicability_id": s.ID, "statement_of_applicability_id": s.ID,
"organization_id": s.OrganizationID, "organization_id": s.OrganizationID,
"name": s.Name, "name": s.Name,
"source_id": s.SourceID, "source_id": s.SourceID,
"snapshot_id": s.SnapshotID, "snapshot_id": s.SnapshotID,
"owner_profile_id": s.OwnerID, "owner_profile_id": s.OwnerID,
"created_at": s.CreatedAt, "created_at": s.CreatedAt,
"updated_at": s.UpdatedAt, "updated_at": s.UpdatedAt,
} }
_, err := conn.Exec(ctx, q, args) _, err := conn.Exec(ctx, q, args)
@@ -247,35 +247,35 @@ VALUES (
return ErrResourceAlreadyExists return ErrResourceAlreadyExists
} }
} }
return fmt.Errorf("cannot insert state_of_applicability: %w", err) return fmt.Errorf("cannot insert statement_of_applicability: %w", err)
} }
return nil return nil
} }
func (s *StateOfApplicability) Update( func (s *StatementOfApplicability) Update(
ctx context.Context, ctx context.Context,
conn pg.Tx, conn pg.Tx,
scope Scoper, scope Scoper,
) error { ) error {
q := ` q := `
UPDATE states_of_applicability UPDATE statements_of_applicability
SET SET
name = @name, name = @name,
owner_profile_id = @owner_profile_id, owner_profile_id = @owner_profile_id,
updated_at = @updated_at updated_at = @updated_at
WHERE WHERE
%s %s
AND id = @state_of_applicability_id AND id = @statement_of_applicability_id
` `
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"state_of_applicability_id": s.ID, "statement_of_applicability_id": s.ID,
"name": s.Name, "name": s.Name,
"owner_profile_id": s.OwnerID, "owner_profile_id": s.OwnerID,
"updated_at": s.UpdatedAt, "updated_at": s.UpdatedAt,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
@@ -287,7 +287,7 @@ WHERE
return ErrResourceAlreadyExists return ErrResourceAlreadyExists
} }
} }
return fmt.Errorf("cannot update state_of_applicability: %w", err) return fmt.Errorf("cannot update statement_of_applicability: %w", err)
} }
if result.RowsAffected() == 0 { if result.RowsAffected() == 0 {
@@ -297,27 +297,27 @@ WHERE
return nil return nil
} }
func (s *StateOfApplicability) Delete( func (s *StatementOfApplicability) Delete(
ctx context.Context, ctx context.Context,
conn pg.Tx, conn pg.Tx,
scope Scoper, scope Scoper,
) error { ) error {
q := ` q := `
DELETE FROM states_of_applicability DELETE FROM statements_of_applicability
WHERE WHERE
%s %s
AND id = @state_of_applicability_id AND id = @statement_of_applicability_id
` `
q = fmt.Sprintf(q, scope.SQLFragment()) q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"state_of_applicability_id": s.ID, "statement_of_applicability_id": s.ID,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
result, err := conn.Exec(ctx, q, args) result, err := conn.Exec(ctx, q, args)
if err != nil { if err != nil {
return fmt.Errorf("cannot delete state_of_applicability: %w", err) return fmt.Errorf("cannot delete statement_of_applicability: %w", err)
} }
if result.RowsAffected() == 0 { if result.RowsAffected() == 0 {
@@ -327,19 +327,19 @@ WHERE
return nil return nil
} }
func (soas StatesOfApplicability) Snapshot(ctx context.Context, conn pg.Tx, scope Scoper, organizationID, snapshotID gid.GID) error { func (soas StatementsOfApplicability) Snapshot(ctx context.Context, conn pg.Tx, scope Scoper, organizationID, snapshotID gid.GID) error {
if err := soas.insertStateOfApplicabilitySnapshots(ctx, conn, scope, organizationID, snapshotID); err != nil { if err := soas.insertStatementOfApplicabilitySnapshots(ctx, conn, scope, organizationID, snapshotID); err != nil {
return fmt.Errorf("cannot insert state_of_applicability snapshots: %w", err) return fmt.Errorf("cannot insert statement_of_applicability snapshots: %w", err)
} }
if err := soas.insertStateOfApplicabilityControlSnapshots(ctx, conn, scope, organizationID, snapshotID); err != nil { if err := soas.insertStatementOfApplicabilityControlSnapshots(ctx, conn, scope, organizationID, snapshotID); err != nil {
return fmt.Errorf("cannot insert state_of_applicability_control snapshots: %w", err) return fmt.Errorf("cannot insert statement_of_applicability_control snapshots: %w", err)
} }
return nil return nil
} }
func (soas StatesOfApplicability) insertStateOfApplicabilitySnapshots( func (soas StatementsOfApplicability) insertStatementOfApplicabilitySnapshots(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
@@ -347,7 +347,7 @@ func (soas StatesOfApplicability) insertStateOfApplicabilitySnapshots(
snapshotID gid.GID, snapshotID gid.GID,
) error { ) error {
query := ` query := `
INSERT INTO states_of_applicability ( INSERT INTO statements_of_applicability (
id, id,
tenant_id, tenant_id,
organization_id, organization_id,
@@ -359,7 +359,7 @@ INSERT INTO states_of_applicability (
updated_at updated_at
) )
SELECT SELECT
generate_gid(decode_base64_unpadded(@tenant_id), @state_of_applicability_entity_type), generate_gid(decode_base64_unpadded(@tenant_id), @statement_of_applicability_entity_type),
@tenant_id, @tenant_id,
soa.organization_id, soa.organization_id,
soa.name, soa.name,
@@ -368,7 +368,7 @@ SELECT
soa.owner_profile_id, soa.owner_profile_id,
soa.created_at, soa.created_at,
soa.updated_at soa.updated_at
FROM states_of_applicability soa FROM statements_of_applicability soa
WHERE WHERE
%s %s
AND soa.organization_id = @organization_id AND soa.organization_id = @organization_id
@@ -378,22 +378,22 @@ WHERE
query = fmt.Sprintf(query, scope.SQLFragment()) query = fmt.Sprintf(query, scope.SQLFragment())
args := pgx.StrictNamedArgs{ args := pgx.StrictNamedArgs{
"tenant_id": scope.GetTenantID(), "tenant_id": scope.GetTenantID(),
"snapshot_id": snapshotID, "snapshot_id": snapshotID,
"organization_id": organizationID, "organization_id": organizationID,
"state_of_applicability_entity_type": StateOfApplicabilityEntityType, "statement_of_applicability_entity_type": StatementOfApplicabilityEntityType,
} }
maps.Copy(args, scope.SQLArguments()) maps.Copy(args, scope.SQLArguments())
_, err := conn.Exec(ctx, query, args) _, err := conn.Exec(ctx, query, args)
if err != nil { if err != nil {
return fmt.Errorf("cannot insert state_of_applicability snapshots: %w", err) return fmt.Errorf("cannot insert statement_of_applicability snapshots: %w", err)
} }
return nil return nil
} }
func (soas StatesOfApplicability) insertStateOfApplicabilityControlSnapshots( func (soas StatementsOfApplicability) insertStatementOfApplicabilityControlSnapshots(
ctx context.Context, ctx context.Context,
conn pg.Querier, conn pg.Querier,
scope Scoper, scope Scoper,
@@ -403,7 +403,7 @@ func (soas StatesOfApplicability) insertStateOfApplicabilityControlSnapshots(
query := ` query := `
WITH source_soa AS ( WITH source_soa AS (
SELECT id, organization_id SELECT id, organization_id
FROM states_of_applicability FROM statements_of_applicability
WHERE WHERE
%s %s
AND organization_id = @organization_id AND organization_id = @organization_id
@@ -411,12 +411,12 @@ WITH source_soa AS (
), ),
snapshot_soa AS ( snapshot_soa AS (
SELECT id, source_id SELECT id, source_id
FROM states_of_applicability FROM statements_of_applicability
WHERE snapshot_id = @snapshot_id WHERE snapshot_id = @snapshot_id
) )
INSERT INTO applicability_statements ( INSERT INTO applicability_statements (
id, id,
state_of_applicability_id, statement_of_applicability_id,
control_id, control_id,
organization_id, organization_id,
tenant_id, tenant_id,
@@ -439,7 +439,7 @@ SELECT
soac.updated_at soac.updated_at
FROM applicability_statements soac FROM applicability_statements soac
INNER JOIN source_soa INNER JOIN source_soa
ON soac.state_of_applicability_id = source_soa.id ON soac.statement_of_applicability_id = source_soa.id
INNER JOIN snapshot_soa INNER JOIN snapshot_soa
ON snapshot_soa.source_id = source_soa.id ON snapshot_soa.source_id = source_soa.id
WHERE soac.snapshot_id IS NULL WHERE soac.snapshot_id IS NULL
@@ -457,7 +457,7 @@ WHERE soac.snapshot_id IS NULL
_, err := conn.Exec(ctx, query, args) _, err := conn.Exec(ctx, query, args)
if err != nil { if err != nil {
return fmt.Errorf("cannot insert state_of_applicability_control snapshots: %w", err) return fmt.Errorf("cannot insert statement_of_applicability_control snapshots: %w", err)
} }
return nil return nil

View File

@@ -20,18 +20,18 @@ import (
) )
type ( type (
StateOfApplicabilityFilter struct { StatementOfApplicabilityFilter struct {
snapshotID **gid.GID snapshotID **gid.GID
} }
) )
func NewStateOfApplicabilityFilter(snapshotID **gid.GID) *StateOfApplicabilityFilter { func NewStatementOfApplicabilityFilter(snapshotID **gid.GID) *StatementOfApplicabilityFilter {
return &StateOfApplicabilityFilter{ return &StatementOfApplicabilityFilter{
snapshotID: snapshotID, snapshotID: snapshotID,
} }
} }
func (f *StateOfApplicabilityFilter) SQLArguments() pgx.StrictNamedArgs { func (f *StatementOfApplicabilityFilter) SQLArguments() pgx.StrictNamedArgs {
args := pgx.StrictNamedArgs{} args := pgx.StrictNamedArgs{}
if f.snapshotID == nil { if f.snapshotID == nil {
@@ -48,7 +48,7 @@ func (f *StateOfApplicabilityFilter) SQLArguments() pgx.StrictNamedArgs {
return args return args
} }
func (f *StateOfApplicabilityFilter) SQLFragment() string { func (f *StatementOfApplicabilityFilter) SQLFragment() string {
return ` return `
CASE CASE
WHEN @has_snapshot_filter::boolean = false THEN TRUE WHEN @has_snapshot_filter::boolean = false THEN TRUE

View File

@@ -19,44 +19,44 @@ import (
) )
type ( type (
StateOfApplicabilityOrderField string StatementOfApplicabilityOrderField string
) )
const ( const (
StateOfApplicabilityOrderFieldName StateOfApplicabilityOrderField = "NAME" StatementOfApplicabilityOrderFieldName StatementOfApplicabilityOrderField = "NAME"
StateOfApplicabilityOrderFieldCreatedAt StateOfApplicabilityOrderField = "CREATED_AT" StatementOfApplicabilityOrderFieldCreatedAt StatementOfApplicabilityOrderField = "CREATED_AT"
) )
func (s StateOfApplicabilityOrderField) Column() string { func (s StatementOfApplicabilityOrderField) Column() string {
switch s { switch s {
case StateOfApplicabilityOrderFieldName: case StatementOfApplicabilityOrderFieldName:
return "name" return "name"
case StateOfApplicabilityOrderFieldCreatedAt: case StatementOfApplicabilityOrderFieldCreatedAt:
return "created_at" return "created_at"
} }
panic(fmt.Sprintf("unsupported order by: %s", s)) panic(fmt.Sprintf("unsupported order by: %s", s))
} }
func (s StateOfApplicabilityOrderField) String() string { func (s StatementOfApplicabilityOrderField) String() string {
return string(s) return string(s)
} }
func (s StateOfApplicabilityOrderField) IsValid() bool { func (s StatementOfApplicabilityOrderField) IsValid() bool {
switch s { switch s {
case StateOfApplicabilityOrderFieldName, StateOfApplicabilityOrderFieldCreatedAt: case StatementOfApplicabilityOrderFieldName, StatementOfApplicabilityOrderFieldCreatedAt:
return true return true
} }
return false return false
} }
func (s StateOfApplicabilityOrderField) MarshalText() ([]byte, error) { func (s StatementOfApplicabilityOrderField) MarshalText() ([]byte, error) {
return []byte(s.String()), nil return []byte(s.String()), nil
} }
func (s *StateOfApplicabilityOrderField) UnmarshalText(text []byte) error { func (s *StatementOfApplicabilityOrderField) UnmarshalText(text []byte) error {
*s = StateOfApplicabilityOrderField(text) *s = StatementOfApplicabilityOrderField(text)
if !s.IsValid() { if !s.IsValid() {
return fmt.Errorf("%s is not a valid StateOfApplicabilityOrderField", string(text)) return fmt.Errorf("%s is not a valid StatementOfApplicabilityOrderField", string(text))
} }
return nil return nil
} }

View File

@@ -187,7 +187,7 @@ var (
transferImpactAssessmentsTemplate = template.Must(template.New("transferImpactAssessments").Funcs(templateFuncs).Parse(transferImpactAssessmentsTemplateContent)) transferImpactAssessmentsTemplate = template.Must(template.New("transferImpactAssessments").Funcs(templateFuncs).Parse(transferImpactAssessmentsTemplateContent))
stateOfApplicabilityTemplate = template.Must(template.New("state-of-applicability").Funcs(templateFuncs).Parse(soaTemplateContent)) statementOfApplicabilityTemplate = template.Must(template.New("statement-of-applicability").Funcs(templateFuncs).Parse(soaTemplateContent))
) )
type ( type (
@@ -279,7 +279,7 @@ type (
SupplementaryMeasures *string SupplementaryMeasures *string
} }
StateOfApplicabilityData struct { StatementOfApplicabilityData struct {
Title string Title string
OrganizationName string OrganizationName string
CreatedAt time.Time CreatedAt time.Time
@@ -382,9 +382,9 @@ func RenderTransferImpactAssessmentsTableHTML(data TransferImpactAssessmentTable
return buf.Bytes(), nil return buf.Bytes(), nil
} }
func RenderStateOfApplicabilityHTML(data StateOfApplicabilityData) ([]byte, error) { func RenderStatementOfApplicabilityHTML(data StatementOfApplicabilityData) ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
if err := stateOfApplicabilityTemplate.Execute(&buf, data); err != nil { if err := statementOfApplicabilityTemplate.Execute(&buf, data); err != nil {
return nil, fmt.Errorf("cannot execute SOA template: %w", err) return nil, fmt.Errorf("cannot execute SOA template: %w", err)
} }

View File

@@ -3,7 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>State of Applicability</title> <title>Statement of Applicability</title>
<style> <style>
@page { @page {
size: A4 landscape; size: A4 landscape;
@@ -254,7 +254,7 @@
{{- end}} {{- end}}
</div> </div>
<h1 class="export-title">State of Applicability</h1> <h1 class="export-title">Statement of Applicability</h1>
<h2 class="export-subtitle">{{.Title}}</h2> <h2 class="export-subtitle">{{.Title}}</h2>
<div class="document-meta"> <div class="document-meta">
@@ -283,7 +283,7 @@
<div class="purpose-section"> <div class="purpose-section">
<div class="purpose-title">1. Purpose</div> <div class="purpose-title">1. Purpose</div>
<div class="purpose-text"> <div class="purpose-text">
This document provides a comprehensive overview of the state of applicability for controls within the organization. This document provides a comprehensive overview of the statement of applicability for controls within the organization.
It serves as a record of which controls are applicable or not applicable to the organization, along with their It serves as a record of which controls are applicable or not applicable to the organization, along with their
relationships to regulatory requirements, contractual obligations, risk assessments, and best practices. relationships to regulatory requirements, contractual obligations, risk assessments, and best practices.
</div> </div>

View File

@@ -340,13 +340,13 @@ const (
ActionRightsRequestUpdate = "core:rights-request:update" ActionRightsRequestUpdate = "core:rights-request:update"
ActionRightsRequestDelete = "core:rights-request:delete" ActionRightsRequestDelete = "core:rights-request:delete"
// StateOfApplicability actions // StatementOfApplicability actions
ActionStateOfApplicabilityList = "core:state-of-applicability:list" ActionStatementOfApplicabilityList = "core:statement-of-applicability:list"
ActionStateOfApplicabilityGet = "core:state-of-applicability:get" ActionStatementOfApplicabilityGet = "core:statement-of-applicability:get"
ActionStateOfApplicabilityCreate = "core:state-of-applicability:create" ActionStatementOfApplicabilityCreate = "core:statement-of-applicability:create"
ActionStateOfApplicabilityUpdate = "core:state-of-applicability:update" ActionStatementOfApplicabilityUpdate = "core:statement-of-applicability:update"
ActionStateOfApplicabilityDelete = "core:state-of-applicability:delete" ActionStatementOfApplicabilityDelete = "core:statement-of-applicability:delete"
ActionStateOfApplicabilityExport = "core:state-of-applicability:export" ActionStatementOfApplicabilityExport = "core:statement-of-applicability:export"
ActionApplicabilityStatementGet = "core:applicability-statement:get" ActionApplicabilityStatementGet = "core:applicability-statement:get"
ActionApplicabilityStatementList = "core:applicability-statement:list" ActionApplicabilityStatementList = "core:applicability-statement:list"

View File

@@ -816,9 +816,9 @@ func (s ControlService) ListForSnapshotID(
return page.NewPage([]*coredata.Control(controls), cursor), nil return page.NewPage([]*coredata.Control(controls), cursor), nil
} }
func (s ControlService) CountForStateOfApplicabilityID( func (s ControlService) CountForStatementOfApplicabilityID(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
filter *coredata.ControlFilter, filter *coredata.ControlFilter,
) (int, error) { ) (int, error) {
var count int var count int
@@ -827,7 +827,7 @@ func (s ControlService) CountForStateOfApplicabilityID(
ctx, ctx,
func(ctx context.Context, conn pg.Querier) (err error) { func(ctx context.Context, conn pg.Querier) (err error) {
controls := &coredata.Controls{} controls := &coredata.Controls{}
count, err = controls.CountByStateOfApplicabilityID(ctx, conn, s.svc.scope, stateOfApplicabilityID, filter) count, err = controls.CountByStatementOfApplicabilityID(ctx, conn, s.svc.scope, statementOfApplicabilityID, filter)
if err != nil { if err != nil {
return fmt.Errorf("cannot count controls: %w", err) return fmt.Errorf("cannot count controls: %w", err)
} }

View File

@@ -145,7 +145,7 @@ var ViewerPolicy = policy.NewPolicy(
ActionFileGet, ActionFileDownloadUrl, ActionFileGet, ActionFileDownloadUrl,
ActionSlackConnectionList, ActionConnectorList, ActionSlackConnectionList, ActionConnectorList,
ActionRightsRequestGet, ActionRightsRequestList, ActionRightsRequestGet, ActionRightsRequestList,
ActionStateOfApplicabilityGet, ActionStateOfApplicabilityList, ActionStatementOfApplicabilityGet, ActionStatementOfApplicabilityList,
ActionApplicabilityStatementGet, ActionApplicabilityStatementList, ActionApplicabilityStatementGet, ActionApplicabilityStatementList,
ActionWebhookSubscriptionGet, ActionWebhookSubscriptionList, ActionWebhookSubscriptionGet, ActionWebhookSubscriptionList,
ActionAccessReviewCampaignGet, ActionAccessReviewCampaignList, ActionAccessReviewCampaignGet, ActionAccessReviewCampaignList,
@@ -222,7 +222,7 @@ var AuditorPolicy = policy.NewPolicy(
ActionTransferImpactAssessmentGet, ActionTransferImpactAssessmentList, ActionTransferImpactAssessmentGet, ActionTransferImpactAssessmentList,
ActionSnapshotGet, ActionSnapshotList, ActionSnapshotGet, ActionSnapshotList,
ActionFileGet, ActionFileDownloadUrl, ActionFileGet, ActionFileDownloadUrl,
ActionStateOfApplicabilityGet, ActionStateOfApplicabilityList, ActionStatementOfApplicabilityGet, ActionStatementOfApplicabilityList,
ActionApplicabilityStatementGet, ActionApplicabilityStatementList, ActionApplicabilityStatementGet, ActionApplicabilityStatementList,
).WithSID("entity-read-access").When(organizationCondition), ).WithSID("entity-read-access").When(organizationCondition),
@@ -236,7 +236,7 @@ var AuditorPolicy = policy.NewPolicy(
).WithSID("employee-document-access").When(organizationCondition), ).WithSID("employee-document-access").When(organizationCondition),
policy.Allow( policy.Allow(
ActionStateOfApplicabilityExport, ActionStatementOfApplicabilityExport,
).WithSID("soa-export").When(organizationCondition), ).WithSID("soa-export").When(organizationCondition),
).WithDescription("Read-only probo access for auditors (excludes internal/employee content)") ).WithDescription("Read-only probo access for auditors (excludes internal/employee content)")

View File

@@ -117,7 +117,7 @@ type (
ProcessingActivities *ProcessingActivityService ProcessingActivities *ProcessingActivityService
DataProtectionImpactAssessments *DataProtectionImpactAssessmentService DataProtectionImpactAssessments *DataProtectionImpactAssessmentService
TransferImpactAssessments *TransferImpactAssessmentService TransferImpactAssessments *TransferImpactAssessmentService
StatesOfApplicability *StateOfApplicabilityService StatementsOfApplicability *StatementOfApplicabilityService
Files *FileService Files *FileService
CustomDomains *CustomDomainService CustomDomains *CustomDomainService
SlackMessages *slack.SlackMessageService SlackMessages *slack.SlackMessageService
@@ -283,7 +283,7 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
svc: tenantService, svc: tenantService,
html2pdfConverter: s.html2pdfConverter, html2pdfConverter: s.html2pdfConverter,
} }
tenantService.StatesOfApplicability = &StateOfApplicabilityService{ tenantService.StatementsOfApplicability = &StatementOfApplicabilityService{
svc: tenantService, svc: tenantService,
html2pdfConverter: s.html2pdfConverter, html2pdfConverter: s.html2pdfConverter,
} }

View File

@@ -29,26 +29,26 @@ import (
"go.probo.inc/probo/pkg/validator" "go.probo.inc/probo/pkg/validator"
) )
type StateOfApplicabilityService struct { type StatementOfApplicabilityService struct {
svc *TenantService svc *TenantService
html2pdfConverter *html2pdf.Converter html2pdfConverter *html2pdf.Converter
} }
type ( type (
CreateStateOfApplicabilityRequest struct { CreateStatementOfApplicabilityRequest struct {
OrganizationID gid.GID OrganizationID gid.GID
Name string Name string
OwnerID gid.GID OwnerID gid.GID
} }
UpdateStateOfApplicabilityRequest struct { UpdateStatementOfApplicabilityRequest struct {
StateOfApplicabilityID gid.GID StatementOfApplicabilityID gid.GID
Name *string Name *string
OwnerID *gid.GID OwnerID *gid.GID
} }
) )
func (csr *CreateStateOfApplicabilityRequest) Validate() error { func (csr *CreateStatementOfApplicabilityRequest) Validate() error {
v := validator.New() v := validator.New()
v.Check(csr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType)) v.Check(csr.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType))
@@ -58,23 +58,23 @@ func (csr *CreateStateOfApplicabilityRequest) Validate() error {
return v.Error() return v.Error()
} }
func (usr *UpdateStateOfApplicabilityRequest) Validate() error { func (usr *UpdateStatementOfApplicabilityRequest) Validate() error {
v := validator.New() v := validator.New()
v.Check(usr.StateOfApplicabilityID, "state_of_applicability_id", validator.Required(), validator.GID(coredata.StateOfApplicabilityEntityType)) v.Check(usr.StatementOfApplicabilityID, "statement_of_applicability_id", validator.Required(), validator.GID(coredata.StatementOfApplicabilityEntityType))
v.Check(usr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(usr.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength))
v.Check(usr.OwnerID, "owner_id", validator.GID(coredata.MembershipProfileEntityType)) v.Check(usr.OwnerID, "owner_id", validator.GID(coredata.MembershipProfileEntityType))
return v.Error() return v.Error()
} }
func (s StateOfApplicabilityService) ListForOrganizationID( func (s StatementOfApplicabilityService) ListForOrganizationID(
ctx context.Context, ctx context.Context,
organizationID gid.GID, organizationID gid.GID,
cursor *page.Cursor[coredata.StateOfApplicabilityOrderField], cursor *page.Cursor[coredata.StatementOfApplicabilityOrderField],
filter *coredata.StateOfApplicabilityFilter, filter *coredata.StatementOfApplicabilityFilter,
) (*page.Page[*coredata.StateOfApplicability, coredata.StateOfApplicabilityOrderField], error) { ) (*page.Page[*coredata.StatementOfApplicability, coredata.StatementOfApplicabilityOrderField], error) {
var statesOfApplicability coredata.StatesOfApplicability var statementsOfApplicability coredata.StatementsOfApplicability
organization := &coredata.Organization{} organization := &coredata.Organization{}
err := s.svc.pg.WithConn( err := s.svc.pg.WithConn(
@@ -84,7 +84,7 @@ func (s StateOfApplicabilityService) ListForOrganizationID(
return fmt.Errorf("cannot load organization: %w", err) return fmt.Errorf("cannot load organization: %w", err)
} }
err := statesOfApplicability.LoadByOrganizationID( err := statementsOfApplicability.LoadByOrganizationID(
ctx, ctx,
conn, conn,
s.svc.scope, s.svc.scope,
@@ -93,7 +93,7 @@ func (s StateOfApplicabilityService) ListForOrganizationID(
filter, filter,
) )
if err != nil { if err != nil {
return fmt.Errorf("cannot load states_of_applicability: %w", err) return fmt.Errorf("cannot load statements_of_applicability: %w", err)
} }
return nil return nil
@@ -104,23 +104,23 @@ func (s StateOfApplicabilityService) ListForOrganizationID(
return nil, err return nil, err
} }
return page.NewPage(statesOfApplicability, cursor), nil return page.NewPage(statementsOfApplicability, cursor), nil
} }
func (s StateOfApplicabilityService) CountForOrganizationID( func (s StatementOfApplicabilityService) CountForOrganizationID(
ctx context.Context, ctx context.Context,
organizationID gid.GID, organizationID gid.GID,
filter *coredata.StateOfApplicabilityFilter, filter *coredata.StatementOfApplicabilityFilter,
) (int, error) { ) (int, error) {
var count int var count int
err := s.svc.pg.WithConn( err := s.svc.pg.WithConn(
ctx, ctx,
func(ctx context.Context, conn pg.Querier) (err error) { func(ctx context.Context, conn pg.Querier) (err error) {
statesOfApplicability := &coredata.StatesOfApplicability{} statementsOfApplicability := &coredata.StatementsOfApplicability{}
count, err = statesOfApplicability.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter) count, err = statementsOfApplicability.CountByOrganizationID(ctx, conn, s.svc.scope, organizationID, filter)
if err != nil { if err != nil {
return fmt.Errorf("cannot count states_of_applicability: %w", err) return fmt.Errorf("cannot count statements_of_applicability: %w", err)
} }
return nil return nil
@@ -134,16 +134,16 @@ func (s StateOfApplicabilityService) CountForOrganizationID(
return count, nil return count, nil
} }
func (s StateOfApplicabilityService) Get( func (s StatementOfApplicabilityService) Get(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
) (*coredata.StateOfApplicability, error) { ) (*coredata.StatementOfApplicability, error) {
stateOfApplicability := &coredata.StateOfApplicability{} statementOfApplicability := &coredata.StatementOfApplicability{}
err := s.svc.pg.WithConn( err := s.svc.pg.WithConn(
ctx, ctx,
func(ctx context.Context, conn pg.Querier) error { func(ctx context.Context, conn pg.Querier) error {
return stateOfApplicability.LoadByID(ctx, conn, s.svc.scope, stateOfApplicabilityID) return statementOfApplicability.LoadByID(ctx, conn, s.svc.scope, statementOfApplicabilityID)
}, },
) )
@@ -151,13 +151,13 @@ func (s StateOfApplicabilityService) Get(
return nil, err return nil, err
} }
return stateOfApplicability, nil return statementOfApplicability, nil
} }
func (s StateOfApplicabilityService) Create( func (s StatementOfApplicabilityService) Create(
ctx context.Context, ctx context.Context,
req CreateStateOfApplicabilityRequest, req CreateStatementOfApplicabilityRequest,
) (*coredata.StateOfApplicability, error) { ) (*coredata.StatementOfApplicability, error) {
if err := req.Validate(); err != nil { if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err) return nil, fmt.Errorf("invalid request: %w", err)
} }
@@ -175,9 +175,9 @@ func (s StateOfApplicabilityService) Create(
return nil, fmt.Errorf("cannot load organization: %w", err) return nil, fmt.Errorf("cannot load organization: %w", err)
} }
stateOfApplicabilityID := gid.New(organization.ID.TenantID(), coredata.StateOfApplicabilityEntityType) statementOfApplicabilityID := gid.New(organization.ID.TenantID(), coredata.StatementOfApplicabilityEntityType)
stateOfApplicability := &coredata.StateOfApplicability{ statementOfApplicability := &coredata.StatementOfApplicability{
ID: stateOfApplicabilityID, ID: statementOfApplicabilityID,
OrganizationID: organization.ID, OrganizationID: organization.ID,
Name: req.Name, Name: req.Name,
OwnerID: req.OwnerID, OwnerID: req.OwnerID,
@@ -188,8 +188,8 @@ func (s StateOfApplicabilityService) Create(
err = s.svc.pg.WithTx( err = s.svc.pg.WithTx(
ctx, ctx,
func(ctx context.Context, conn pg.Tx) error { func(ctx context.Context, conn pg.Tx) error {
if err := stateOfApplicability.Insert(ctx, conn, s.svc.scope); err != nil { if err := statementOfApplicability.Insert(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot insert state_of_applicability: %w", err) return fmt.Errorf("cannot insert statement_of_applicability: %w", err)
} }
return nil return nil
@@ -200,37 +200,37 @@ func (s StateOfApplicabilityService) Create(
return nil, err return nil, err
} }
return stateOfApplicability, nil return statementOfApplicability, nil
} }
func (s StateOfApplicabilityService) Update( func (s StatementOfApplicabilityService) Update(
ctx context.Context, ctx context.Context,
req UpdateStateOfApplicabilityRequest, req UpdateStatementOfApplicabilityRequest,
) (*coredata.StateOfApplicability, error) { ) (*coredata.StatementOfApplicability, error) {
if err := req.Validate(); err != nil { if err := req.Validate(); err != nil {
return nil, fmt.Errorf("invalid request: %w", err) return nil, fmt.Errorf("invalid request: %w", err)
} }
stateOfApplicability := &coredata.StateOfApplicability{} statementOfApplicability := &coredata.StatementOfApplicability{}
err := s.svc.pg.WithTx( err := s.svc.pg.WithTx(
ctx, ctx,
func(ctx context.Context, conn pg.Tx) error { func(ctx context.Context, conn pg.Tx) error {
if err := stateOfApplicability.LoadByID(ctx, conn, s.svc.scope, req.StateOfApplicabilityID); err != nil { if err := statementOfApplicability.LoadByID(ctx, conn, s.svc.scope, req.StatementOfApplicabilityID); err != nil {
return fmt.Errorf("cannot load state_of_applicability: %w", err) return fmt.Errorf("cannot load statement_of_applicability: %w", err)
} }
if req.Name != nil { if req.Name != nil {
stateOfApplicability.Name = *req.Name statementOfApplicability.Name = *req.Name
} }
if req.OwnerID != nil { if req.OwnerID != nil {
stateOfApplicability.OwnerID = *req.OwnerID statementOfApplicability.OwnerID = *req.OwnerID
} }
stateOfApplicability.UpdatedAt = time.Now() statementOfApplicability.UpdatedAt = time.Now()
if err := stateOfApplicability.Update(ctx, conn, s.svc.scope); err != nil { if err := statementOfApplicability.Update(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot update state_of_applicability: %w", err) return fmt.Errorf("cannot update statement_of_applicability: %w", err)
} }
return nil return nil
@@ -241,24 +241,24 @@ func (s StateOfApplicabilityService) Update(
return nil, err return nil, err
} }
return stateOfApplicability, nil return statementOfApplicability, nil
} }
func (s StateOfApplicabilityService) Delete( func (s StatementOfApplicabilityService) Delete(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
) error { ) error {
stateOfApplicability := &coredata.StateOfApplicability{ID: stateOfApplicabilityID} statementOfApplicability := &coredata.StatementOfApplicability{ID: statementOfApplicabilityID}
err := s.svc.pg.WithTx( err := s.svc.pg.WithTx(
ctx, ctx,
func(ctx context.Context, conn pg.Tx) error { func(ctx context.Context, conn pg.Tx) error {
if err := stateOfApplicability.LoadByID(ctx, conn, s.svc.scope, stateOfApplicabilityID); err != nil { if err := statementOfApplicability.LoadByID(ctx, conn, s.svc.scope, statementOfApplicabilityID); err != nil {
return fmt.Errorf("cannot load state_of_applicability: %w", err) return fmt.Errorf("cannot load statement_of_applicability: %w", err)
} }
if err := stateOfApplicability.Delete(ctx, conn, s.svc.scope); err != nil { if err := statementOfApplicability.Delete(ctx, conn, s.svc.scope); err != nil {
return fmt.Errorf("cannot delete state_of_applicability: %w", err) return fmt.Errorf("cannot delete statement_of_applicability: %w", err)
} }
return nil return nil
@@ -272,7 +272,7 @@ func (s StateOfApplicabilityService) Delete(
return nil return nil
} }
func (s StateOfApplicabilityService) GetApplicabilityStatement( func (s StatementOfApplicabilityService) GetApplicabilityStatement(
ctx context.Context, ctx context.Context,
applicabilityStatementID gid.GID, applicabilityStatementID gid.GID,
) (*coredata.ApplicabilityStatement, error) { ) (*coredata.ApplicabilityStatement, error) {
@@ -291,9 +291,9 @@ func (s StateOfApplicabilityService) GetApplicabilityStatement(
return applicabilityStatement, nil return applicabilityStatement, nil
} }
func (s StateOfApplicabilityService) ListApplicabilityStatements( func (s StatementOfApplicabilityService) ListApplicabilityStatements(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
cursor *page.Cursor[coredata.ApplicabilityStatementOrderField], cursor *page.Cursor[coredata.ApplicabilityStatementOrderField],
) (*page.Page[*coredata.ApplicabilityStatement, coredata.ApplicabilityStatementOrderField], error) { ) (*page.Page[*coredata.ApplicabilityStatement, coredata.ApplicabilityStatementOrderField], error) {
var statements coredata.ApplicabilityStatements var statements coredata.ApplicabilityStatements
@@ -301,7 +301,7 @@ func (s StateOfApplicabilityService) ListApplicabilityStatements(
err := s.svc.pg.WithConn( err := s.svc.pg.WithConn(
ctx, ctx,
func(ctx context.Context, conn pg.Querier) error { func(ctx context.Context, conn pg.Querier) error {
if err := statements.LoadByStateOfApplicabilityID(ctx, conn, s.svc.scope, stateOfApplicabilityID, cursor); err != nil { if err := statements.LoadByStatementOfApplicabilityID(ctx, conn, s.svc.scope, statementOfApplicabilityID, cursor); err != nil {
return fmt.Errorf("cannot load applicability statements: %w", err) return fmt.Errorf("cannot load applicability statements: %w", err)
} }
return nil return nil
@@ -315,9 +315,9 @@ func (s StateOfApplicabilityService) ListApplicabilityStatements(
return page.NewPage(statements, cursor), nil return page.NewPage(statements, cursor), nil
} }
func (s StateOfApplicabilityService) CountApplicabilityStatements( func (s StatementOfApplicabilityService) CountApplicabilityStatements(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
) (int, error) { ) (int, error) {
var count int var count int
@@ -325,7 +325,7 @@ func (s StateOfApplicabilityService) CountApplicabilityStatements(
ctx, ctx,
func(ctx context.Context, conn pg.Querier) (err error) { func(ctx context.Context, conn pg.Querier) (err error) {
statements := &coredata.ApplicabilityStatements{} statements := &coredata.ApplicabilityStatements{}
count, err = statements.CountByStateOfApplicabilityID(ctx, conn, s.svc.scope, stateOfApplicabilityID) count, err = statements.CountByStatementOfApplicabilityID(ctx, conn, s.svc.scope, statementOfApplicabilityID)
if err != nil { if err != nil {
return fmt.Errorf("cannot count applicability statements: %w", err) return fmt.Errorf("cannot count applicability statements: %w", err)
} }
@@ -340,35 +340,35 @@ func (s StateOfApplicabilityService) CountApplicabilityStatements(
return count, nil return count, nil
} }
func (s StateOfApplicabilityService) CreateApplicabilityStatement( func (s StatementOfApplicabilityService) CreateApplicabilityStatement(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
controlID gid.GID, controlID gid.GID,
applicability bool, applicability bool,
justification *string, justification *string,
) (*coredata.ApplicabilityStatement, error) { ) (*coredata.ApplicabilityStatement, error) {
var ( var (
stateOfApplicability = &coredata.StateOfApplicability{} statementOfApplicability = &coredata.StatementOfApplicability{}
applicabilityStatement = &coredata.ApplicabilityStatement{} applicabilityStatement = &coredata.ApplicabilityStatement{}
now = time.Now() now = time.Now()
) )
err := s.svc.pg.WithTx( err := s.svc.pg.WithTx(
ctx, ctx,
func(ctx context.Context, conn pg.Tx) error { func(ctx context.Context, conn pg.Tx) error {
if err := stateOfApplicability.LoadByID(ctx, conn, s.svc.scope, stateOfApplicabilityID); err != nil { if err := statementOfApplicability.LoadByID(ctx, conn, s.svc.scope, statementOfApplicabilityID); err != nil {
return fmt.Errorf("cannot load state of applicability: %w", err) return fmt.Errorf("cannot load statement of applicability: %w", err)
} }
applicabilityStatement = &coredata.ApplicabilityStatement{ applicabilityStatement = &coredata.ApplicabilityStatement{
ID: gid.New(s.svc.scope.GetTenantID(), coredata.ApplicabilityStatementEntityType), ID: gid.New(s.svc.scope.GetTenantID(), coredata.ApplicabilityStatementEntityType),
StateOfApplicabilityID: stateOfApplicabilityID, StatementOfApplicabilityID: statementOfApplicabilityID,
ControlID: controlID, ControlID: controlID,
OrganizationID: stateOfApplicability.OrganizationID, OrganizationID: statementOfApplicability.OrganizationID,
Applicability: applicability, Applicability: applicability,
Justification: justification, Justification: justification,
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
} }
if err := applicabilityStatement.Insert(ctx, conn, s.svc.scope); err != nil { if err := applicabilityStatement.Insert(ctx, conn, s.svc.scope); err != nil {
@@ -385,7 +385,7 @@ func (s StateOfApplicabilityService) CreateApplicabilityStatement(
return applicabilityStatement, nil return applicabilityStatement, nil
} }
func (s StateOfApplicabilityService) UpdateApplicabilityStatement( func (s StatementOfApplicabilityService) UpdateApplicabilityStatement(
ctx context.Context, ctx context.Context,
applicabilityStatementID gid.GID, applicabilityStatementID gid.GID,
applicability bool, applicability bool,
@@ -414,7 +414,7 @@ func (s StateOfApplicabilityService) UpdateApplicabilityStatement(
return applicabilityStatement, nil return applicabilityStatement, nil
} }
func (s StateOfApplicabilityService) DeleteApplicabilityStatement( func (s StatementOfApplicabilityService) DeleteApplicabilityStatement(
ctx context.Context, ctx context.Context,
applicabilityStatementID gid.GID, applicabilityStatementID gid.GID,
) error { ) error {
@@ -428,7 +428,7 @@ func (s StateOfApplicabilityService) DeleteApplicabilityStatement(
) )
} }
func (s StateOfApplicabilityService) ListControlLinks( func (s StatementOfApplicabilityService) ListControlLinks(
ctx context.Context, ctx context.Context,
controlID gid.GID, controlID gid.GID,
cursor *page.Cursor[coredata.ApplicabilityStatementOrderField], cursor *page.Cursor[coredata.ApplicabilityStatementOrderField],
@@ -445,27 +445,27 @@ func (s StateOfApplicabilityService) ListControlLinks(
return page.NewPage(controls, cursor), nil return page.NewPage(controls, cursor), nil
} }
func (s StateOfApplicabilityService) ExportPDF( func (s StatementOfApplicabilityService) ExportPDF(
ctx context.Context, ctx context.Context,
stateOfApplicabilityID gid.GID, statementOfApplicabilityID gid.GID,
) ([]byte, error) { ) ([]byte, error) {
var documentData docgen.StateOfApplicabilityData var documentData docgen.StatementOfApplicabilityData
err := s.svc.pg.WithConn( err := s.svc.pg.WithConn(
ctx, ctx,
func(ctx context.Context, conn pg.Querier) error { func(ctx context.Context, conn pg.Querier) error {
stateOfApplicability := &coredata.StateOfApplicability{} statementOfApplicability := &coredata.StatementOfApplicability{}
if err := stateOfApplicability.LoadByID(ctx, conn, s.svc.scope, stateOfApplicabilityID); err != nil { if err := statementOfApplicability.LoadByID(ctx, conn, s.svc.scope, statementOfApplicabilityID); err != nil {
return fmt.Errorf("cannot load state of applicability: %w", err) return fmt.Errorf("cannot load statement of applicability: %w", err)
} }
organization := &coredata.Organization{} organization := &coredata.Organization{}
if err := organization.LoadByID(ctx, conn, s.svc.scope, stateOfApplicability.OrganizationID); err != nil { if err := organization.LoadByID(ctx, conn, s.svc.scope, statementOfApplicability.OrganizationID); err != nil {
return fmt.Errorf("cannot load organization: %w", err) return fmt.Errorf("cannot load organization: %w", err)
} }
owner := &coredata.MembershipProfile{} owner := &coredata.MembershipProfile{}
if err := owner.LoadByID(ctx, conn, s.svc.scope, stateOfApplicability.OwnerID); err != nil { if err := owner.LoadByID(ctx, conn, s.svc.scope, statementOfApplicability.OwnerID); err != nil {
return fmt.Errorf("cannot load owner profile: %w", err) return fmt.Errorf("cannot load owner profile: %w", err)
} }
@@ -480,16 +480,16 @@ func (s StateOfApplicabilityService) ExportPDF(
Direction: page.OrderDirectionAsc, Direction: page.OrderDirectionAsc,
}, },
) )
if err := applicabilityStatements.LoadByStateOfApplicabilityID(ctx, conn, s.svc.scope, stateOfApplicabilityID, cursor); err != nil { if err := applicabilityStatements.LoadByStatementOfApplicabilityID(ctx, conn, s.svc.scope, statementOfApplicabilityID, cursor); err != nil {
return fmt.Errorf("cannot load applicability statements: %w", err) return fmt.Errorf("cannot load applicability statements: %w", err)
} }
if len(applicabilityStatements) == 0 { if len(applicabilityStatements) == 0 {
// No linked controls, skip loading additional data // No linked controls, skip loading additional data
documentData = docgen.StateOfApplicabilityData{ documentData = docgen.StatementOfApplicabilityData{
Title: stateOfApplicability.Name, Title: statementOfApplicability.Name,
OrganizationName: organization.Name, OrganizationName: organization.Name,
CreatedAt: stateOfApplicability.CreatedAt, CreatedAt: statementOfApplicability.CreatedAt,
TotalControls: 0, TotalControls: 0,
FrameworkGroups: []docgen.FrameworkControlGroup{}, FrameworkGroups: []docgen.FrameworkControlGroup{},
} }
@@ -602,19 +602,19 @@ func (s StateOfApplicabilityService) ExportPDF(
} }
var snapshots coredata.Snapshots var snapshots coredata.Snapshots
snapshotType := coredata.SnapshotsTypeStatesOfApplicability snapshotType := coredata.SnapshotsTypeStatementsOfApplicability
var version int var version int
var publishedAt time.Time var publishedAt time.Time
if stateOfApplicability.SnapshotID != nil { if statementOfApplicability.SnapshotID != nil {
snapshot := &coredata.Snapshot{} snapshot := &coredata.Snapshot{}
if err := snapshot.LoadByID(ctx, conn, s.svc.scope, *stateOfApplicability.SnapshotID); err != nil { if err := snapshot.LoadByID(ctx, conn, s.svc.scope, *statementOfApplicability.SnapshotID); err != nil {
return fmt.Errorf("cannot load snapshot: %w", err) return fmt.Errorf("cannot load snapshot: %w", err)
} }
publishedAt = snapshot.CreatedAt publishedAt = snapshot.CreatedAt
snapshotFilter := coredata.NewSnapshotFilter(&snapshotType).WithBeforeDate(&snapshot.CreatedAt) snapshotFilter := coredata.NewSnapshotFilter(&snapshotType).WithBeforeDate(&snapshot.CreatedAt)
snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, stateOfApplicability.OrganizationID, snapshotFilter) snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, statementOfApplicability.OrganizationID, snapshotFilter)
if err != nil { if err != nil {
return fmt.Errorf("cannot count states of applicability snapshots: %w", err) return fmt.Errorf("cannot count states of applicability snapshots: %w", err)
} }
@@ -622,7 +622,7 @@ func (s StateOfApplicabilityService) ExportPDF(
} else { } else {
publishedAt = time.Now() publishedAt = time.Now()
snapshotFilter := coredata.NewSnapshotFilter(&snapshotType) snapshotFilter := coredata.NewSnapshotFilter(&snapshotType)
snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, stateOfApplicability.OrganizationID, snapshotFilter) snapshotCount, err := snapshots.CountByOrganizationID(ctx, conn, s.svc.scope, statementOfApplicability.OrganizationID, snapshotFilter)
if err != nil { if err != nil {
return fmt.Errorf("cannot count states of applicability snapshots: %w", err) return fmt.Errorf("cannot count states of applicability snapshots: %w", err)
} }
@@ -641,10 +641,10 @@ func (s StateOfApplicabilityService) ExportPDF(
} }
} }
documentData = docgen.StateOfApplicabilityData{ documentData = docgen.StatementOfApplicabilityData{
Title: stateOfApplicability.Name, Title: statementOfApplicability.Name,
OrganizationName: organization.Name, OrganizationName: organization.Name,
CreatedAt: stateOfApplicability.CreatedAt, CreatedAt: statementOfApplicability.CreatedAt,
TotalControls: len(applicabilityStatements), TotalControls: len(applicabilityStatements),
FrameworkGroups: frameworkGroups, FrameworkGroups: frameworkGroups,
CompanyHorizontalLogoBase64: horizontalLogoBase64, CompanyHorizontalLogoBase64: horizontalLogoBase64,
@@ -661,7 +661,7 @@ func (s StateOfApplicabilityService) ExportPDF(
return nil, err return nil, err
} }
htmlData, err := docgen.RenderStateOfApplicabilityHTML(documentData) htmlData, err := docgen.RenderStatementOfApplicabilityHTML(documentData)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot render HTML: %w", err) return nil, fmt.Errorf("cannot render HTML: %w", err)
} }

View File

@@ -1398,9 +1398,9 @@ enum SnapshotsType
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeProcessingActivities" value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeProcessingActivities"
) )
STATES_OF_APPLICABILITY STATEMENTS_OF_APPLICABILITY
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeStatesOfApplicability" value: "go.probo.inc/probo/pkg/coredata.SnapshotsTypeStatementsOfApplicability"
) )
} }
@@ -1690,7 +1690,7 @@ input DatumFilter {
snapshotId: ID snapshotId: ID
} }
input StateOfApplicabilityFilter { input StatementOfApplicabilityFilter {
snapshotId: ID snapshotId: ID
} }
@@ -1955,14 +1955,14 @@ type Organization implements Node {
orderBy: MeetingOrder orderBy: MeetingOrder
): MeetingConnection! @goField(forceResolver: true) ): MeetingConnection! @goField(forceResolver: true)
statesOfApplicability( statementsOfApplicability(
first: Int first: Int
after: CursorKey after: CursorKey
last: Int last: Int
before: CursorKey before: CursorKey
orderBy: StateOfApplicabilityOrder orderBy: StatementOfApplicabilityOrder
filter: StateOfApplicabilityFilter = { snapshotId: null } filter: StatementOfApplicabilityFilter = { snapshotId: null }
): StateOfApplicabilityConnection! @goField(forceResolver: true) ): StatementOfApplicabilityConnection! @goField(forceResolver: true)
measureCategories: [String!]! @goField(forceResolver: true) measureCategories: [String!]! @goField(forceResolver: true)
@@ -2756,7 +2756,7 @@ type WebhookEventEdge {
node: WebhookEvent! node: WebhookEvent!
} }
type StateOfApplicability implements Node { type StatementOfApplicability implements Node {
id: ID! id: ID!
name: String! name: String!
sourceId: ID sourceId: ID
@@ -2793,7 +2793,7 @@ type ApplicabilityStatementEdge {
type ApplicabilityStatement implements Node { type ApplicabilityStatement implements Node {
id: ID! id: ID!
stateOfApplicability: StateOfApplicability! @goField(forceResolver: true) statementOfApplicability: StatementOfApplicability! @goField(forceResolver: true)
control: Control! @goField(forceResolver: true) control: Control! @goField(forceResolver: true)
applicability: Boolean! applicability: Boolean!
justification: String! justification: String!
@@ -3427,18 +3427,18 @@ type MeetingEdge {
node: Meeting! node: Meeting!
} }
type StateOfApplicabilityConnection type StatementOfApplicabilityConnection
@goModel( @goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StateOfApplicabilityConnection" model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StatementOfApplicabilityConnection"
) { ) {
totalCount: Int! @goField(forceResolver: true) totalCount: Int! @goField(forceResolver: true)
edges: [StateOfApplicabilityEdge!]! edges: [StatementOfApplicabilityEdge!]!
pageInfo: PageInfo! pageInfo: PageInfo!
} }
type StateOfApplicabilityEdge { type StatementOfApplicabilityEdge {
cursor: CursorKey! cursor: CursorKey!
node: StateOfApplicability! node: StatementOfApplicability!
} }
@@ -3906,19 +3906,19 @@ type Mutation {
deleteWebhookSubscription( deleteWebhookSubscription(
input: DeleteWebhookSubscriptionInput! input: DeleteWebhookSubscriptionInput!
): DeleteWebhookSubscriptionPayload! ): DeleteWebhookSubscriptionPayload!
# StateOfApplicability mutations # StatementOfApplicability mutations
createStateOfApplicability( createStatementOfApplicability(
input: CreateStateOfApplicabilityInput! input: CreateStatementOfApplicabilityInput!
): CreateStateOfApplicabilityPayload! ): CreateStatementOfApplicabilityPayload!
updateStateOfApplicability( updateStatementOfApplicability(
input: UpdateStateOfApplicabilityInput! input: UpdateStatementOfApplicabilityInput!
): UpdateStateOfApplicabilityPayload! ): UpdateStatementOfApplicabilityPayload!
deleteStateOfApplicability( deleteStatementOfApplicability(
input: DeleteStateOfApplicabilityInput! input: DeleteStatementOfApplicabilityInput!
): DeleteStateOfApplicabilityPayload! ): DeleteStatementOfApplicabilityPayload!
exportStateOfApplicabilityPDF( exportStatementOfApplicabilityPDF(
input: ExportStateOfApplicabilityPDFInput! input: ExportStatementOfApplicabilityPDFInput!
): ExportStateOfApplicabilityPDFPayload! ): ExportStatementOfApplicabilityPDFPayload!
publishMajorDocumentVersion( publishMajorDocumentVersion(
input: PublishMajorDocumentVersionInput! input: PublishMajorDocumentVersionInput!
): PublishDocumentVersionPayload! ): PublishDocumentVersionPayload!
@@ -4491,7 +4491,7 @@ input DeleteControlDocumentMappingInput {
} }
input CreateApplicabilityStatementInput { input CreateApplicabilityStatementInput {
stateOfApplicabilityId: ID! statementOfApplicabilityId: ID!
controlId: ID! controlId: ID!
applicability: Boolean! applicability: Boolean!
justification: String justification: String
@@ -4735,13 +4735,13 @@ input DeleteMeetingInput {
meetingId: ID! meetingId: ID!
} }
input CreateStateOfApplicabilityInput { input CreateStatementOfApplicabilityInput {
organizationId: ID! organizationId: ID!
name: String! name: String!
ownerId: ID! ownerId: ID!
} }
input UpdateStateOfApplicabilityInput { input UpdateStatementOfApplicabilityInput {
id: ID! id: ID!
name: String name: String
ownerId: ID ownerId: ID
@@ -4753,37 +4753,37 @@ input ApplicabilityStatementInput {
justification: String justification: String
} }
input DeleteStateOfApplicabilityInput { input DeleteStatementOfApplicabilityInput {
stateOfApplicabilityId: ID! statementOfApplicabilityId: ID!
} }
input ExportStateOfApplicabilityPDFInput { input ExportStatementOfApplicabilityPDFInput {
stateOfApplicabilityId: ID! statementOfApplicabilityId: ID!
} }
type ExportStateOfApplicabilityPDFPayload { type ExportStatementOfApplicabilityPDFPayload {
data: String! data: String!
} }
input StateOfApplicabilityOrder input StatementOfApplicabilityOrder
@goModel( @goModel(
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StateOfApplicabilityOrderBy" model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StatementOfApplicabilityOrderBy"
) { ) {
direction: OrderDirection! direction: OrderDirection!
field: StateOfApplicabilityOrderField! field: StatementOfApplicabilityOrderField!
} }
enum StateOfApplicabilityOrderField enum StatementOfApplicabilityOrderField
@goModel( @goModel(
model: "go.probo.inc/probo/pkg/coredata.StateOfApplicabilityOrderField" model: "go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderField"
) { ) {
NAME NAME
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.StateOfApplicabilityOrderFieldName" value: "go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderFieldName"
) )
CREATED_AT CREATED_AT
@goEnum( @goEnum(
value: "go.probo.inc/probo/pkg/coredata.StateOfApplicabilityOrderFieldCreatedAt" value: "go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderFieldCreatedAt"
) )
} }
input CreateControlInput { input CreateControlInput {
@@ -5496,16 +5496,16 @@ type DeleteWebhookSubscriptionPayload {
deletedWebhookSubscriptionId: ID! deletedWebhookSubscriptionId: ID!
} }
type CreateStateOfApplicabilityPayload { type CreateStatementOfApplicabilityPayload {
stateOfApplicabilityEdge: StateOfApplicabilityEdge! statementOfApplicabilityEdge: StatementOfApplicabilityEdge!
} }
type UpdateStateOfApplicabilityPayload { type UpdateStatementOfApplicabilityPayload {
stateOfApplicability: StateOfApplicability! statementOfApplicability: StatementOfApplicability!
} }
type DeleteStateOfApplicabilityPayload { type DeleteStatementOfApplicabilityPayload {
deletedStateOfApplicabilityId: ID! deletedStatementOfApplicabilityId: ID!
} }
input VendorRiskAssessmentOrder { input VendorRiskAssessmentOrder {

View File

@@ -70,8 +70,8 @@ func NewApplicabilityStatement(as *coredata.ApplicabilityStatement) *Applicabili
return &ApplicabilityStatement{ return &ApplicabilityStatement{
ID: as.ID, ID: as.ID,
StateOfApplicability: &StateOfApplicability{ StatementOfApplicability: &StatementOfApplicability{
ID: as.StateOfApplicabilityID, ID: as.StatementOfApplicabilityID,
}, },
Control: &Control{ Control: &Control{
ID: as.ControlID, ID: as.ControlID,

View File

@@ -21,32 +21,32 @@ import (
) )
type ( type (
StateOfApplicabilityOrderBy OrderBy[coredata.StateOfApplicabilityOrderField] StatementOfApplicabilityOrderBy OrderBy[coredata.StatementOfApplicabilityOrderField]
StateOfApplicabilityConnection struct { StatementOfApplicabilityConnection struct {
TotalCount int TotalCount int
Edges []*StateOfApplicabilityEdge Edges []*StatementOfApplicabilityEdge
PageInfo PageInfo PageInfo PageInfo
Resolver any Resolver any
ParentID gid.GID ParentID gid.GID
Filters *coredata.StateOfApplicabilityFilter Filters *coredata.StatementOfApplicabilityFilter
} }
) )
func NewStateOfApplicabilityConnection( func NewStatementOfApplicabilityConnection(
p *page.Page[*coredata.StateOfApplicability, coredata.StateOfApplicabilityOrderField], p *page.Page[*coredata.StatementOfApplicability, coredata.StatementOfApplicabilityOrderField],
parentType any, parentType any,
parentID gid.GID, parentID gid.GID,
filters *coredata.StateOfApplicabilityFilter, filters *coredata.StatementOfApplicabilityFilter,
) *StateOfApplicabilityConnection { ) *StatementOfApplicabilityConnection {
var edges = make([]*StateOfApplicabilityEdge, len(p.Data)) var edges = make([]*StatementOfApplicabilityEdge, len(p.Data))
for i := range edges { for i := range edges {
edges[i] = NewStateOfApplicabilityEdge(p.Data[i], p.Cursor.OrderBy.Field) edges[i] = NewStatementOfApplicabilityEdge(p.Data[i], p.Cursor.OrderBy.Field)
} }
return &StateOfApplicabilityConnection{ return &StatementOfApplicabilityConnection{
Edges: edges, Edges: edges,
PageInfo: *NewPageInfo(p), PageInfo: *NewPageInfo(p),
@@ -56,15 +56,15 @@ func NewStateOfApplicabilityConnection(
} }
} }
func NewStateOfApplicabilityEdge(soa *coredata.StateOfApplicability, orderBy coredata.StateOfApplicabilityOrderField) *StateOfApplicabilityEdge { func NewStatementOfApplicabilityEdge(soa *coredata.StatementOfApplicability, orderBy coredata.StatementOfApplicabilityOrderField) *StatementOfApplicabilityEdge {
return &StateOfApplicabilityEdge{ return &StatementOfApplicabilityEdge{
Cursor: soa.CursorKey(orderBy), Cursor: soa.CursorKey(orderBy),
Node: NewStateOfApplicability(soa), Node: NewStatementOfApplicability(soa),
} }
} }
func NewStateOfApplicability(soa *coredata.StateOfApplicability) *StateOfApplicability { func NewStatementOfApplicability(soa *coredata.StatementOfApplicability) *StatementOfApplicability {
return &StateOfApplicability{ return &StatementOfApplicability{
ID: soa.ID, ID: soa.ID,
Organization: &Organization{ Organization: &Organization{
ID: soa.OrganizationID, ID: soa.OrganizationID,

View File

@@ -543,21 +543,21 @@ func (r *accessSourceConnectionResolver) TotalCount(ctx context.Context, obj *ty
panic(fmt.Errorf("unsupported resolver: %T", obj.Resolver)) panic(fmt.Errorf("unsupported resolver: %T", obj.Resolver))
} }
// StateOfApplicability is the resolver for the stateOfApplicability field. // StatementOfApplicability is the resolver for the statementOfApplicability field.
func (r *applicabilityStatementResolver) StateOfApplicability(ctx context.Context, obj *types.ApplicabilityStatement) (*types.StateOfApplicability, error) { func (r *applicabilityStatementResolver) StatementOfApplicability(ctx context.Context, obj *types.ApplicabilityStatement) (*types.StatementOfApplicability, error) {
if err := r.authorize(ctx, obj.StateOfApplicability.ID, probo.ActionStateOfApplicabilityGet); err != nil { if err := r.authorize(ctx, obj.StatementOfApplicability.ID, probo.ActionStatementOfApplicabilityGet); err != nil {
return nil, err return nil, err
} }
prb := r.ProboService(ctx, obj.StateOfApplicability.ID.TenantID()) prb := r.ProboService(ctx, obj.StatementOfApplicability.ID.TenantID())
soa, err := prb.StatesOfApplicability.Get(ctx, obj.StateOfApplicability.ID) soa, err := prb.StatementsOfApplicability.Get(ctx, obj.StatementOfApplicability.ID)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot get state of applicability", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot get statement of applicability", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
return types.NewStateOfApplicability(soa), nil return types.NewStatementOfApplicability(soa), nil
} }
// Control is the resolver for the control field. // Control is the resolver for the control field.
@@ -595,8 +595,8 @@ func (r *applicabilityStatementConnectionResolver) TotalCount(ctx context.Contex
prb := r.ProboService(ctx, obj.ParentID.TenantID()) prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) { switch obj.Resolver.(type) {
case *stateOfApplicabilityResolver: case *statementOfApplicabilityResolver:
count, err := prb.StatesOfApplicability.CountApplicabilityStatements(ctx, obj.ParentID) count, err := prb.StatementsOfApplicability.CountApplicabilityStatements(ctx, obj.ParentID)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot count applicability statements", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot count applicability statements", log.Error(err))
return 0, gqlutils.Internal(ctx) return 0, gqlutils.Internal(ctx)
@@ -1289,8 +1289,8 @@ func (r *controlConnectionResolver) TotalCount(ctx context.Context, obj *types.C
return 0, gqlutils.Internal(ctx) return 0, gqlutils.Internal(ctx)
} }
return count, nil return count, nil
case *stateOfApplicabilityResolver: case *statementOfApplicabilityResolver:
count, err := prb.Controls.CountForStateOfApplicabilityID(ctx, obj.ParentID, obj.Filters) count, err := prb.Controls.CountForStatementOfApplicabilityID(ctx, obj.ParentID, obj.Filters)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot count controls", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot count controls", log.Error(err))
return 0, gqlutils.Internal(ctx) return 0, gqlutils.Internal(ctx)
@@ -4440,9 +4440,9 @@ func (r *mutationResolver) CreateApplicabilityStatement(ctx context.Context, inp
return nil, err return nil, err
} }
prb := r.ProboService(ctx, input.StateOfApplicabilityID.TenantID()) prb := r.ProboService(ctx, input.StatementOfApplicabilityID.TenantID())
applicabilityStatement, err := prb.StatesOfApplicability.CreateApplicabilityStatement(ctx, input.StateOfApplicabilityID, input.ControlID, input.Applicability, input.Justification) applicabilityStatement, err := prb.StatementsOfApplicability.CreateApplicabilityStatement(ctx, input.StatementOfApplicabilityID, input.ControlID, input.Applicability, input.Justification)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot create applicability statement", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot create applicability statement", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
@@ -4461,7 +4461,7 @@ func (r *mutationResolver) UpdateApplicabilityStatement(ctx context.Context, inp
prb := r.ProboService(ctx, input.ApplicabilityStatementID.TenantID()) prb := r.ProboService(ctx, input.ApplicabilityStatementID.TenantID())
applicabilityStatement, err := prb.StatesOfApplicability.UpdateApplicabilityStatement(ctx, input.ApplicabilityStatementID, input.Applicability, input.Justification) applicabilityStatement, err := prb.StatementsOfApplicability.UpdateApplicabilityStatement(ctx, input.ApplicabilityStatementID, input.Applicability, input.Justification)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot update applicability statement", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot update applicability statement", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
@@ -4480,7 +4480,7 @@ func (r *mutationResolver) DeleteApplicabilityStatement(ctx context.Context, inp
prb := r.ProboService(ctx, input.ApplicabilityStatementID.TenantID()) prb := r.ProboService(ctx, input.ApplicabilityStatementID.TenantID())
err := prb.StatesOfApplicability.DeleteApplicabilityStatement(ctx, input.ApplicabilityStatementID) err := prb.StatementsOfApplicability.DeleteApplicabilityStatement(ctx, input.ApplicabilityStatementID)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete applicability statement", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot delete applicability statement", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
@@ -5526,17 +5526,17 @@ func (r *mutationResolver) DeleteWebhookSubscription(ctx context.Context, input
}, nil }, nil
} }
// CreateStateOfApplicability is the resolver for the createStateOfApplicability field. // CreateStatementOfApplicability is the resolver for the createStatementOfApplicability field.
func (r *mutationResolver) CreateStateOfApplicability(ctx context.Context, input types.CreateStateOfApplicabilityInput) (*types.CreateStateOfApplicabilityPayload, error) { func (r *mutationResolver) CreateStatementOfApplicability(ctx context.Context, input types.CreateStatementOfApplicabilityInput) (*types.CreateStatementOfApplicabilityPayload, error) {
if err := r.authorize(ctx, input.OrganizationID, probo.ActionStateOfApplicabilityCreate); err != nil { if err := r.authorize(ctx, input.OrganizationID, probo.ActionStatementOfApplicabilityCreate); err != nil {
return nil, err return nil, err
} }
prb := r.ProboService(ctx, input.OrganizationID.TenantID()) prb := r.ProboService(ctx, input.OrganizationID.TenantID())
stateOfApplicability, err := prb.StatesOfApplicability.Create( statementOfApplicability, err := prb.StatementsOfApplicability.Create(
ctx, ctx,
probo.CreateStateOfApplicabilityRequest{ probo.CreateStatementOfApplicabilityRequest{
OrganizationID: input.OrganizationID, OrganizationID: input.OrganizationID,
Name: input.Name, Name: input.Name,
OwnerID: input.OwnerID, OwnerID: input.OwnerID,
@@ -5549,18 +5549,18 @@ func (r *mutationResolver) CreateStateOfApplicability(ctx context.Context, input
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
} }
r.logger.ErrorCtx(ctx, "cannot create state_of_applicability", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot create statement_of_applicability", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
return &types.CreateStateOfApplicabilityPayload{ return &types.CreateStatementOfApplicabilityPayload{
StateOfApplicabilityEdge: types.NewStateOfApplicabilityEdge(stateOfApplicability, coredata.StateOfApplicabilityOrderFieldCreatedAt), StatementOfApplicabilityEdge: types.NewStatementOfApplicabilityEdge(statementOfApplicability, coredata.StatementOfApplicabilityOrderFieldCreatedAt),
}, nil }, nil
} }
// UpdateStateOfApplicability is the resolver for the updateStateOfApplicability field. // UpdateStatementOfApplicability is the resolver for the updateStatementOfApplicability field.
func (r *mutationResolver) UpdateStateOfApplicability(ctx context.Context, input types.UpdateStateOfApplicabilityInput) (*types.UpdateStateOfApplicabilityPayload, error) { func (r *mutationResolver) UpdateStatementOfApplicability(ctx context.Context, input types.UpdateStatementOfApplicabilityInput) (*types.UpdateStatementOfApplicabilityPayload, error) {
if err := r.authorize(ctx, input.ID, probo.ActionStateOfApplicabilityUpdate); err != nil { if err := r.authorize(ctx, input.ID, probo.ActionStatementOfApplicabilityUpdate); err != nil {
return nil, err return nil, err
} }
@@ -5571,12 +5571,12 @@ func (r *mutationResolver) UpdateStateOfApplicability(ctx context.Context, input
name = input.Name name = input.Name
} }
stateOfApplicability, err := prb.StatesOfApplicability.Update( statementOfApplicability, err := prb.StatementsOfApplicability.Update(
ctx, ctx,
probo.UpdateStateOfApplicabilityRequest{ probo.UpdateStatementOfApplicabilityRequest{
StateOfApplicabilityID: input.ID, StatementOfApplicabilityID: input.ID,
Name: name, Name: name,
OwnerID: input.OwnerID, OwnerID: input.OwnerID,
}, },
) )
if err != nil { if err != nil {
@@ -5586,52 +5586,52 @@ func (r *mutationResolver) UpdateStateOfApplicability(ctx context.Context, input
if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok {
return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors)
} }
r.logger.ErrorCtx(ctx, "cannot update state_of_applicability", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot update statement_of_applicability", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
return &types.UpdateStateOfApplicabilityPayload{ return &types.UpdateStatementOfApplicabilityPayload{
StateOfApplicability: types.NewStateOfApplicability(stateOfApplicability), StatementOfApplicability: types.NewStatementOfApplicability(statementOfApplicability),
}, nil }, nil
} }
// DeleteStateOfApplicability is the resolver for the deleteStateOfApplicability field. // DeleteStatementOfApplicability is the resolver for the deleteStatementOfApplicability field.
func (r *mutationResolver) DeleteStateOfApplicability(ctx context.Context, input types.DeleteStateOfApplicabilityInput) (*types.DeleteStateOfApplicabilityPayload, error) { func (r *mutationResolver) DeleteStatementOfApplicability(ctx context.Context, input types.DeleteStatementOfApplicabilityInput) (*types.DeleteStatementOfApplicabilityPayload, error) {
if err := r.authorize(ctx, input.StateOfApplicabilityID, probo.ActionStateOfApplicabilityDelete); err != nil { if err := r.authorize(ctx, input.StatementOfApplicabilityID, probo.ActionStatementOfApplicabilityDelete); err != nil {
return nil, err return nil, err
} }
prb := r.ProboService(ctx, input.StateOfApplicabilityID.TenantID()) prb := r.ProboService(ctx, input.StatementOfApplicabilityID.TenantID())
err := prb.StatesOfApplicability.Delete(ctx, input.StateOfApplicabilityID) err := prb.StatementsOfApplicability.Delete(ctx, input.StatementOfApplicabilityID)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot delete state_of_applicability", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot delete statement_of_applicability", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
return &types.DeleteStateOfApplicabilityPayload{ return &types.DeleteStatementOfApplicabilityPayload{
DeletedStateOfApplicabilityID: input.StateOfApplicabilityID, DeletedStatementOfApplicabilityID: input.StatementOfApplicabilityID,
}, nil }, nil
} }
// ExportStateOfApplicabilityPDF is the resolver for the exportStateOfApplicabilityPDF field. // ExportStatementOfApplicabilityPDF is the resolver for the exportStatementOfApplicabilityPDF field.
func (r *mutationResolver) ExportStateOfApplicabilityPDF(ctx context.Context, input types.ExportStateOfApplicabilityPDFInput) (*types.ExportStateOfApplicabilityPDFPayload, error) { func (r *mutationResolver) ExportStatementOfApplicabilityPDF(ctx context.Context, input types.ExportStatementOfApplicabilityPDFInput) (*types.ExportStatementOfApplicabilityPDFPayload, error) {
if err := r.authorize(ctx, input.StateOfApplicabilityID, probo.ActionStateOfApplicabilityExport); err != nil { if err := r.authorize(ctx, input.StatementOfApplicabilityID, probo.ActionStatementOfApplicabilityExport); err != nil {
return nil, err return nil, err
} }
prb := r.ProboService(ctx, input.StateOfApplicabilityID.TenantID()) prb := r.ProboService(ctx, input.StatementOfApplicabilityID.TenantID())
pdfData, err := prb.StatesOfApplicability.ExportPDF(ctx, input.StateOfApplicabilityID) pdfData, err := prb.StatementsOfApplicability.ExportPDF(ctx, input.StatementOfApplicabilityID)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot export state of applicability PDF", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot export statement of applicability PDF", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
base64Data := base64.StdEncoding.EncodeToString(pdfData) base64Data := base64.StdEncoding.EncodeToString(pdfData)
dataURI := fmt.Sprintf("data:application/pdf;base64,%s", base64Data) dataURI := fmt.Sprintf("data:application/pdf;base64,%s", base64Data)
return &types.ExportStateOfApplicabilityPDFPayload{ return &types.ExportStatementOfApplicabilityPDFPayload{
Data: dataURI, Data: dataURI,
}, nil }, nil
} }
@@ -8522,20 +8522,20 @@ func (r *organizationResolver) Meetings(ctx context.Context, obj *types.Organiza
return types.NewMeetingConnection(page, r, obj.ID), nil return types.NewMeetingConnection(page, r, obj.ID), nil
} }
// StatesOfApplicability is the resolver for the statesOfApplicability field. // StatementsOfApplicability is the resolver for the statementsOfApplicability field.
func (r *organizationResolver) StatesOfApplicability(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.StateOfApplicabilityOrderBy, filter *types.StateOfApplicabilityFilter) (*types.StateOfApplicabilityConnection, error) { func (r *organizationResolver) StatementsOfApplicability(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.StatementOfApplicabilityOrderBy, filter *types.StatementOfApplicabilityFilter) (*types.StatementOfApplicabilityConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionStateOfApplicabilityList); err != nil { if err := r.authorize(ctx, obj.ID, probo.ActionStatementOfApplicabilityList); err != nil {
return nil, err return nil, err
} }
prb := r.ProboService(ctx, obj.ID.TenantID()) prb := r.ProboService(ctx, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.StateOfApplicabilityOrderField]{ pageOrderBy := page.OrderBy[coredata.StatementOfApplicabilityOrderField]{
Field: coredata.StateOfApplicabilityOrderFieldCreatedAt, Field: coredata.StatementOfApplicabilityOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc, Direction: page.OrderDirectionDesc,
} }
if orderBy != nil { if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.StateOfApplicabilityOrderField]{ pageOrderBy = page.OrderBy[coredata.StatementOfApplicabilityOrderField]{
Field: orderBy.Field, Field: orderBy.Field,
Direction: orderBy.Direction, Direction: orderBy.Direction,
} }
@@ -8543,18 +8543,18 @@ func (r *organizationResolver) StatesOfApplicability(ctx context.Context, obj *t
cursor := types.NewCursor(first, after, last, before, pageOrderBy) cursor := types.NewCursor(first, after, last, before, pageOrderBy)
var stateOfApplicabilityFilter = coredata.NewStateOfApplicabilityFilter(nil) var statementOfApplicabilityFilter = coredata.NewStatementOfApplicabilityFilter(nil)
if filter != nil { if filter != nil {
stateOfApplicabilityFilter = coredata.NewStateOfApplicabilityFilter(&filter.SnapshotID) statementOfApplicabilityFilter = coredata.NewStatementOfApplicabilityFilter(&filter.SnapshotID)
} }
page, err := prb.StatesOfApplicability.ListForOrganizationID(ctx, obj.ID, cursor, stateOfApplicabilityFilter) page, err := prb.StatementsOfApplicability.ListForOrganizationID(ctx, obj.ID, cursor, statementOfApplicabilityFilter)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot list organization states_of_applicability", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot list organization statements_of_applicability", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
} }
return types.NewStateOfApplicabilityConnection(page, r, obj.ID, stateOfApplicabilityFilter), nil return types.NewStatementOfApplicabilityConnection(page, r, obj.ID, statementOfApplicabilityFilter), nil
} }
// MeasureCategories is the resolver for the measureCategories field. // MeasureCategories is the resolver for the measureCategories field.
@@ -9695,14 +9695,14 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error
} }
return types.NewRightsRequest(rightsRequest), nil return types.NewRightsRequest(rightsRequest), nil
} }
case coredata.StateOfApplicabilityEntityType: case coredata.StatementOfApplicabilityEntityType:
action = probo.ActionStateOfApplicabilityGet action = probo.ActionStatementOfApplicabilityGet
loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) {
stateOfApplicability, err := prb.StatesOfApplicability.Get(ctx, id) statementOfApplicability, err := prb.StatementsOfApplicability.Get(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return types.NewStateOfApplicability(stateOfApplicability), nil return types.NewStatementOfApplicability(statementOfApplicability), nil
} }
case coredata.WebhookSubscriptionEntityType: case coredata.WebhookSubscriptionEntityType:
action = probo.ActionWebhookSubscriptionGet action = probo.ActionWebhookSubscriptionGet
@@ -10191,7 +10191,7 @@ func (r *snapshotConnectionResolver) TotalCount(ctx context.Context, obj *types.
} }
// Organization is the resolver for the organization field. // Organization is the resolver for the organization field.
func (r *stateOfApplicabilityResolver) Organization(ctx context.Context, obj *types.StateOfApplicability) (*types.Organization, error) { func (r *statementOfApplicabilityResolver) Organization(ctx context.Context, obj *types.StatementOfApplicability) (*types.Organization, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil {
return nil, err return nil, err
} }
@@ -10211,7 +10211,7 @@ func (r *stateOfApplicabilityResolver) Organization(ctx context.Context, obj *ty
} }
// Owner is the resolver for the owner field. // Owner is the resolver for the owner field.
func (r *stateOfApplicabilityResolver) Owner(ctx context.Context, obj *types.StateOfApplicability) (*types.Profile, error) { func (r *statementOfApplicabilityResolver) Owner(ctx context.Context, obj *types.StatementOfApplicability) (*types.Profile, error) {
if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil { if err := r.authorize(ctx, obj.ID, iam.ActionMembershipProfileGet); err != nil {
return nil, err return nil, err
} }
@@ -10231,7 +10231,7 @@ func (r *stateOfApplicabilityResolver) Owner(ctx context.Context, obj *types.Sta
} }
// ApplicabilityStatements is the resolver for the applicabilityStatements field. // ApplicabilityStatements is the resolver for the applicabilityStatements field.
func (r *stateOfApplicabilityResolver) ApplicabilityStatements(ctx context.Context, obj *types.StateOfApplicability, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ApplicabilityStatementOrderBy) (*types.ApplicabilityStatementConnection, error) { func (r *statementOfApplicabilityResolver) ApplicabilityStatements(ctx context.Context, obj *types.StatementOfApplicability, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ApplicabilityStatementOrderBy) (*types.ApplicabilityStatementConnection, error) {
if err := r.authorize(ctx, obj.ID, probo.ActionApplicabilityStatementList); err != nil { if err := r.authorize(ctx, obj.ID, probo.ActionApplicabilityStatementList); err != nil {
return nil, err return nil, err
} }
@@ -10251,7 +10251,7 @@ func (r *stateOfApplicabilityResolver) ApplicabilityStatements(ctx context.Conte
cursor := types.NewCursor(first, after, last, before, pageOrderBy) cursor := types.NewCursor(first, after, last, before, pageOrderBy)
p, err := prb.StatesOfApplicability.ListApplicabilityStatements(ctx, obj.ID, cursor) p, err := prb.StatementsOfApplicability.ListApplicabilityStatements(ctx, obj.ID, cursor)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot list applicability statements", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot list applicability statements", log.Error(err))
return nil, gqlutils.Internal(ctx) return nil, gqlutils.Internal(ctx)
@@ -10261,19 +10261,19 @@ func (r *stateOfApplicabilityResolver) ApplicabilityStatements(ctx context.Conte
} }
// Permission is the resolver for the permission field. // Permission is the resolver for the permission field.
func (r *stateOfApplicabilityResolver) Permission(ctx context.Context, obj *types.StateOfApplicability, action string) (bool, error) { func (r *statementOfApplicabilityResolver) Permission(ctx context.Context, obj *types.StatementOfApplicability, action string) (bool, error) {
return r.Resolver.Permission(ctx, obj, action) return r.Resolver.Permission(ctx, obj, action)
} }
// TotalCount is the resolver for the totalCount field. // TotalCount is the resolver for the totalCount field.
func (r *stateOfApplicabilityConnectionResolver) TotalCount(ctx context.Context, obj *types.StateOfApplicabilityConnection) (int, error) { func (r *statementOfApplicabilityConnectionResolver) TotalCount(ctx context.Context, obj *types.StatementOfApplicabilityConnection) (int, error) {
prb := r.ProboService(ctx, obj.ParentID.TenantID()) prb := r.ProboService(ctx, obj.ParentID.TenantID())
switch obj.Resolver.(type) { switch obj.Resolver.(type) {
case *organizationResolver: case *organizationResolver:
count, err := prb.StatesOfApplicability.CountForOrganizationID(ctx, obj.ParentID, obj.Filters) count, err := prb.StatementsOfApplicability.CountForOrganizationID(ctx, obj.ParentID, obj.Filters)
if err != nil { if err != nil {
r.logger.ErrorCtx(ctx, "cannot count states_of_applicability", log.Error(err)) r.logger.ErrorCtx(ctx, "cannot count statements_of_applicability", log.Error(err))
return 0, gqlutils.Internal(ctx) return 0, gqlutils.Internal(ctx)
} }
return count, nil return count, nil
@@ -12081,14 +12081,14 @@ func (r *Resolver) SnapshotConnection() schema.SnapshotConnectionResolver {
return &snapshotConnectionResolver{r} return &snapshotConnectionResolver{r}
} }
// StateOfApplicability returns schema.StateOfApplicabilityResolver implementation. // StatementOfApplicability returns schema.StatementOfApplicabilityResolver implementation.
func (r *Resolver) StateOfApplicability() schema.StateOfApplicabilityResolver { func (r *Resolver) StatementOfApplicability() schema.StatementOfApplicabilityResolver {
return &stateOfApplicabilityResolver{r} return &statementOfApplicabilityResolver{r}
} }
// StateOfApplicabilityConnection returns schema.StateOfApplicabilityConnectionResolver implementation. // StatementOfApplicabilityConnection returns schema.StatementOfApplicabilityConnectionResolver implementation.
func (r *Resolver) StateOfApplicabilityConnection() schema.StateOfApplicabilityConnectionResolver { func (r *Resolver) StatementOfApplicabilityConnection() schema.StatementOfApplicabilityConnectionResolver {
return &stateOfApplicabilityConnectionResolver{r} return &statementOfApplicabilityConnectionResolver{r}
} }
// Task returns schema.TaskResolver implementation. // Task returns schema.TaskResolver implementation.
@@ -12267,8 +12267,8 @@ type riskConnectionResolver struct{ *Resolver }
type slackConnectionResolver struct{ *Resolver } type slackConnectionResolver struct{ *Resolver }
type snapshotResolver struct{ *Resolver } type snapshotResolver struct{ *Resolver }
type snapshotConnectionResolver struct{ *Resolver } type snapshotConnectionResolver struct{ *Resolver }
type stateOfApplicabilityResolver struct{ *Resolver } type statementOfApplicabilityResolver struct{ *Resolver }
type stateOfApplicabilityConnectionResolver struct{ *Resolver } type statementOfApplicabilityConnectionResolver struct{ *Resolver }
type taskResolver struct{ *Resolver } type taskResolver struct{ *Resolver }
type taskConnectionResolver struct{ *Resolver } type taskConnectionResolver struct{ *Resolver }
type transferImpactAssessmentResolver struct{ *Resolver } type transferImpactAssessmentResolver struct{ *Resolver }

View File

@@ -2844,17 +2844,17 @@ func (r *Resolver) DeleteDataProtectionImpactAssessmentTool(ctx context.Context,
}, nil }, nil
} }
func (r *Resolver) ListStatesOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListStatesOfApplicabilityInput) (*mcp.CallToolResult, types.ListStatesOfApplicabilityOutput, error) { func (r *Resolver) ListStatementsOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListStatementsOfApplicabilityInput) (*mcp.CallToolResult, types.ListStatementsOfApplicabilityOutput, error) {
r.MustAuthorize(ctx, input.OrganizationID, probo.ActionStateOfApplicabilityList) r.MustAuthorize(ctx, input.OrganizationID, probo.ActionStatementOfApplicabilityList)
prb := r.ProboService(ctx, input.OrganizationID) prb := r.ProboService(ctx, input.OrganizationID)
pageOrderBy := page.OrderBy[coredata.StateOfApplicabilityOrderField]{ pageOrderBy := page.OrderBy[coredata.StatementOfApplicabilityOrderField]{
Field: coredata.StateOfApplicabilityOrderFieldCreatedAt, Field: coredata.StatementOfApplicabilityOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc, Direction: page.OrderDirectionDesc,
} }
if input.OrderBy != nil { if input.OrderBy != nil {
pageOrderBy = page.OrderBy[coredata.StateOfApplicabilityOrderField]{ pageOrderBy = page.OrderBy[coredata.StatementOfApplicabilityOrderField]{
Field: input.OrderBy.Field, Field: input.OrderBy.Field,
Direction: input.OrderBy.Direction, Direction: input.OrderBy.Direction,
} }
@@ -2862,107 +2862,107 @@ func (r *Resolver) ListStatesOfApplicabilityTool(ctx context.Context, req *mcp.C
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
pg, err := prb.StatesOfApplicability.ListForOrganizationID(ctx, input.OrganizationID, cursor, coredata.NewStateOfApplicabilityFilter(nil)) pg, err := prb.StatementsOfApplicability.ListForOrganizationID(ctx, input.OrganizationID, cursor, coredata.NewStatementOfApplicabilityFilter(nil))
if err != nil { if err != nil {
return nil, types.ListStatesOfApplicabilityOutput{}, fmt.Errorf("failed to list states of applicability: %w", err) return nil, types.ListStatementsOfApplicabilityOutput{}, fmt.Errorf("failed to list statements of applicability: %w", err)
} }
return nil, types.NewListStatesOfApplicabilityOutput(pg), nil return nil, types.NewListStatementsOfApplicabilityOutput(pg), nil
} }
func (r *Resolver) GetStateOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetStateOfApplicabilityInput) (*mcp.CallToolResult, types.GetStateOfApplicabilityOutput, error) { func (r *Resolver) GetStatementOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetStatementOfApplicabilityInput) (*mcp.CallToolResult, types.GetStatementOfApplicabilityOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionStateOfApplicabilityGet) r.MustAuthorize(ctx, input.ID, probo.ActionStatementOfApplicabilityGet)
prb := r.ProboService(ctx, input.ID) prb := r.ProboService(ctx, input.ID)
soa, err := prb.StatesOfApplicability.Get(ctx, input.ID) soa, err := prb.StatementsOfApplicability.Get(ctx, input.ID)
if err != nil { if err != nil {
return nil, types.GetStateOfApplicabilityOutput{}, fmt.Errorf("failed to get state of applicability: %w", err) return nil, types.GetStatementOfApplicabilityOutput{}, fmt.Errorf("failed to get statement of applicability: %w", err)
} }
return nil, types.GetStateOfApplicabilityOutput{ return nil, types.GetStatementOfApplicabilityOutput{
StateOfApplicability: types.NewStateOfApplicability(soa), StatementOfApplicability: types.NewStatementOfApplicability(soa),
}, nil }, nil
} }
func (r *Resolver) AddStateOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddStateOfApplicabilityInput) (*mcp.CallToolResult, types.AddStateOfApplicabilityOutput, error) { func (r *Resolver) AddStatementOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddStatementOfApplicabilityInput) (*mcp.CallToolResult, types.AddStatementOfApplicabilityOutput, error) {
r.MustAuthorize(ctx, input.OrganizationID, probo.ActionStateOfApplicabilityCreate) r.MustAuthorize(ctx, input.OrganizationID, probo.ActionStatementOfApplicabilityCreate)
svc := r.ProboService(ctx, input.OrganizationID) svc := r.ProboService(ctx, input.OrganizationID)
soa, err := svc.StatesOfApplicability.Create(ctx, probo.CreateStateOfApplicabilityRequest{ soa, err := svc.StatementsOfApplicability.Create(ctx, probo.CreateStatementOfApplicabilityRequest{
OrganizationID: input.OrganizationID, OrganizationID: input.OrganizationID,
Name: input.Name, Name: input.Name,
OwnerID: input.OwnerID, OwnerID: input.OwnerID,
}) })
if err != nil { if err != nil {
return nil, types.AddStateOfApplicabilityOutput{}, fmt.Errorf("failed to create state of applicability: %w", err) return nil, types.AddStatementOfApplicabilityOutput{}, fmt.Errorf("failed to create statement of applicability: %w", err)
} }
return nil, types.AddStateOfApplicabilityOutput{ return nil, types.AddStatementOfApplicabilityOutput{
StateOfApplicability: types.NewStateOfApplicability(soa), StatementOfApplicability: types.NewStatementOfApplicability(soa),
}, nil }, nil
} }
func (r *Resolver) UpdateStateOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateStateOfApplicabilityInput) (*mcp.CallToolResult, types.UpdateStateOfApplicabilityOutput, error) { func (r *Resolver) UpdateStatementOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateStatementOfApplicabilityInput) (*mcp.CallToolResult, types.UpdateStatementOfApplicabilityOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionStateOfApplicabilityUpdate) r.MustAuthorize(ctx, input.ID, probo.ActionStatementOfApplicabilityUpdate)
svc := r.ProboService(ctx, input.ID) svc := r.ProboService(ctx, input.ID)
soa, err := svc.StatesOfApplicability.Update(ctx, probo.UpdateStateOfApplicabilityRequest{ soa, err := svc.StatementsOfApplicability.Update(ctx, probo.UpdateStatementOfApplicabilityRequest{
StateOfApplicabilityID: input.ID, StatementOfApplicabilityID: input.ID,
Name: input.Name, Name: input.Name,
OwnerID: input.OwnerID, OwnerID: input.OwnerID,
}) })
if err != nil { if err != nil {
return nil, types.UpdateStateOfApplicabilityOutput{}, fmt.Errorf("failed to update state of applicability: %w", err) return nil, types.UpdateStatementOfApplicabilityOutput{}, fmt.Errorf("failed to update statement of applicability: %w", err)
} }
return nil, types.UpdateStateOfApplicabilityOutput{ return nil, types.UpdateStatementOfApplicabilityOutput{
StateOfApplicability: types.NewStateOfApplicability(soa), StatementOfApplicability: types.NewStatementOfApplicability(soa),
}, nil }, nil
} }
func (r *Resolver) DeleteStateOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteStateOfApplicabilityInput) (*mcp.CallToolResult, types.DeleteStateOfApplicabilityOutput, error) { func (r *Resolver) DeleteStatementOfApplicabilityTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteStatementOfApplicabilityInput) (*mcp.CallToolResult, types.DeleteStatementOfApplicabilityOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionStateOfApplicabilityDelete) r.MustAuthorize(ctx, input.ID, probo.ActionStatementOfApplicabilityDelete)
svc := r.ProboService(ctx, input.ID) svc := r.ProboService(ctx, input.ID)
err := svc.StatesOfApplicability.Delete(ctx, input.ID) err := svc.StatementsOfApplicability.Delete(ctx, input.ID)
if err != nil { if err != nil {
return nil, types.DeleteStateOfApplicabilityOutput{}, fmt.Errorf("failed to delete state of applicability: %w", err) return nil, types.DeleteStatementOfApplicabilityOutput{}, fmt.Errorf("failed to delete statement of applicability: %w", err)
} }
return nil, types.DeleteStateOfApplicabilityOutput{ return nil, types.DeleteStatementOfApplicabilityOutput{
DeletedStateOfApplicabilityID: input.ID, DeletedStatementOfApplicabilityID: input.ID,
}, nil }, nil
} }
func (r *Resolver) ExportStateOfApplicabilityPDFTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ExportStateOfApplicabilityPDFInput) (*mcp.CallToolResult, types.ExportStateOfApplicabilityPDFOutput, error) { func (r *Resolver) ExportStatementOfApplicabilityPDFTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ExportStatementOfApplicabilityPDFInput) (*mcp.CallToolResult, types.ExportStatementOfApplicabilityPDFOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionStateOfApplicabilityExport) r.MustAuthorize(ctx, input.ID, probo.ActionStatementOfApplicabilityExport)
svc := r.ProboService(ctx, input.ID) svc := r.ProboService(ctx, input.ID)
soa, err := svc.StatesOfApplicability.Get(ctx, input.ID) soa, err := svc.StatementsOfApplicability.Get(ctx, input.ID)
if err != nil { if err != nil {
return nil, types.ExportStateOfApplicabilityPDFOutput{}, fmt.Errorf("failed to get state of applicability: %w", err) return nil, types.ExportStatementOfApplicabilityPDFOutput{}, fmt.Errorf("failed to get statement of applicability: %w", err)
} }
pdfData, err := svc.StatesOfApplicability.ExportPDF(ctx, input.ID) pdfData, err := svc.StatementsOfApplicability.ExportPDF(ctx, input.ID)
if err != nil { if err != nil {
return nil, types.ExportStateOfApplicabilityPDFOutput{}, fmt.Errorf("failed to export state of applicability PDF: %w", err) return nil, types.ExportStatementOfApplicabilityPDFOutput{}, fmt.Errorf("failed to export statement of applicability PDF: %w", err)
} }
return nil, types.ExportStateOfApplicabilityPDFOutput{ return nil, types.ExportStatementOfApplicabilityPDFOutput{
PdfBase64: base64.StdEncoding.EncodeToString(pdfData), PdfBase64: base64.StdEncoding.EncodeToString(pdfData),
Filename: soa.Name + ".pdf", Filename: soa.Name + ".pdf",
}, nil }, nil
} }
func (r *Resolver) ListApplicabilityStatementsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListApplicabilityStatementsInput) (*mcp.CallToolResult, types.ListApplicabilityStatementsOutput, error) { func (r *Resolver) ListApplicabilityStatementsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListApplicabilityStatementsInput) (*mcp.CallToolResult, types.ListApplicabilityStatementsOutput, error) {
r.MustAuthorize(ctx, input.StateOfApplicabilityID, probo.ActionApplicabilityStatementList) r.MustAuthorize(ctx, input.StatementOfApplicabilityID, probo.ActionApplicabilityStatementList)
prb := r.ProboService(ctx, input.StateOfApplicabilityID) prb := r.ProboService(ctx, input.StatementOfApplicabilityID)
pageOrderBy := page.OrderBy[coredata.ApplicabilityStatementOrderField]{ pageOrderBy := page.OrderBy[coredata.ApplicabilityStatementOrderField]{
Field: coredata.ApplicabilityStatementOrderFieldControlSectionTitle, Field: coredata.ApplicabilityStatementOrderFieldControlSectionTitle,
@@ -2977,7 +2977,7 @@ func (r *Resolver) ListApplicabilityStatementsTool(ctx context.Context, req *mcp
cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy) cursor := types.NewCursor(input.Size, input.Cursor, pageOrderBy)
pg, err := prb.StatesOfApplicability.ListApplicabilityStatements(ctx, input.StateOfApplicabilityID, cursor) pg, err := prb.StatementsOfApplicability.ListApplicabilityStatements(ctx, input.StatementOfApplicabilityID, cursor)
if err != nil { if err != nil {
return nil, types.ListApplicabilityStatementsOutput{}, fmt.Errorf("failed to list applicability statements: %w", err) return nil, types.ListApplicabilityStatementsOutput{}, fmt.Errorf("failed to list applicability statements: %w", err)
} }
@@ -2990,7 +2990,7 @@ func (r *Resolver) GetApplicabilityStatementTool(ctx context.Context, req *mcp.C
prb := r.ProboService(ctx, input.ID) prb := r.ProboService(ctx, input.ID)
stmt, err := prb.StatesOfApplicability.GetApplicabilityStatement(ctx, input.ID) stmt, err := prb.StatementsOfApplicability.GetApplicabilityStatement(ctx, input.ID)
if err != nil { if err != nil {
return nil, types.GetApplicabilityStatementOutput{}, fmt.Errorf("failed to get applicability statement: %w", err) return nil, types.GetApplicabilityStatementOutput{}, fmt.Errorf("failed to get applicability statement: %w", err)
} }
@@ -3001,13 +3001,13 @@ func (r *Resolver) GetApplicabilityStatementTool(ctx context.Context, req *mcp.C
} }
func (r *Resolver) AddApplicabilityStatementTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddApplicabilityStatementInput) (*mcp.CallToolResult, types.AddApplicabilityStatementOutput, error) { func (r *Resolver) AddApplicabilityStatementTool(ctx context.Context, req *mcp.CallToolRequest, input *types.AddApplicabilityStatementInput) (*mcp.CallToolResult, types.AddApplicabilityStatementOutput, error) {
r.MustAuthorize(ctx, input.StateOfApplicabilityID, probo.ActionApplicabilityStatementCreate) r.MustAuthorize(ctx, input.StatementOfApplicabilityID, probo.ActionApplicabilityStatementCreate)
svc := r.ProboService(ctx, input.StateOfApplicabilityID) svc := r.ProboService(ctx, input.StatementOfApplicabilityID)
stmt, err := svc.StatesOfApplicability.CreateApplicabilityStatement( stmt, err := svc.StatementsOfApplicability.CreateApplicabilityStatement(
ctx, ctx,
input.StateOfApplicabilityID, input.StatementOfApplicabilityID,
input.ControlID, input.ControlID,
input.Applicability, input.Applicability,
input.Justification, input.Justification,
@@ -3026,7 +3026,7 @@ func (r *Resolver) UpdateApplicabilityStatementTool(ctx context.Context, req *mc
svc := r.ProboService(ctx, input.ID) svc := r.ProboService(ctx, input.ID)
stmt, err := svc.StatesOfApplicability.UpdateApplicabilityStatement( stmt, err := svc.StatementsOfApplicability.UpdateApplicabilityStatement(
ctx, ctx,
input.ID, input.ID,
input.Applicability, input.Applicability,
@@ -3046,7 +3046,7 @@ func (r *Resolver) DeleteApplicabilityStatementTool(ctx context.Context, req *mc
svc := r.ProboService(ctx, input.ID) svc := r.ProboService(ctx, input.ID)
err := svc.StatesOfApplicability.DeleteApplicabilityStatement(ctx, input.ID) err := svc.StatementsOfApplicability.DeleteApplicabilityStatement(ctx, input.ID)
if err != nil { if err != nil {
return nil, types.DeleteApplicabilityStatementOutput{}, fmt.Errorf("failed to delete applicability statement: %w", err) return nil, types.DeleteApplicabilityStatementOutput{}, fmt.Errorf("failed to delete applicability statement: %w", err)
} }

View File

@@ -6076,27 +6076,27 @@ components:
$ref: "#/components/schemas/Profile" $ref: "#/components/schemas/Profile"
description: List of attendee profiles description: List of attendee profiles
StateOfApplicabilityOrderField: StatementOfApplicabilityOrderField:
type: string type: string
enum: enum:
- CREATED_AT - CREATED_AT
- NAME - NAME
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.StateOfApplicabilityOrderField go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.StatementOfApplicabilityOrderField
StateOfApplicabilityOrderBy: StatementOfApplicabilityOrderBy:
type: object type: object
required: required:
- field - field
- direction - direction
properties: properties:
field: field:
$ref: "#/components/schemas/StateOfApplicabilityOrderField" $ref: "#/components/schemas/StatementOfApplicabilityOrderField"
description: State of applicability order field description: Statement of applicability order field
direction: direction:
$ref: "#/components/schemas/OrderDirection" $ref: "#/components/schemas/OrderDirection"
description: Order direction description: Order direction
StateOfApplicability: StatementOfApplicability:
type: object type: object
required: required:
- id - id
@@ -6108,13 +6108,13 @@ components:
properties: properties:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
organization_id: organization_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Organization ID description: Organization ID
name: name:
type: string type: string
description: State of applicability name description: Statement of applicability name
owner_id: owner_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Owner profile ID description: Owner profile ID
@@ -6132,7 +6132,7 @@ components:
format: date-time format: date-time
description: Update timestamp description: Update timestamp
ListStatesOfApplicabilityInput: ListStatementsOfApplicabilityInput:
type: object type: object
required: required:
- organization_id - organization_id
@@ -6141,7 +6141,7 @@ components:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Organization ID description: Organization ID
order_by: order_by:
$ref: "#/components/schemas/StateOfApplicabilityOrderBy" $ref: "#/components/schemas/StatementOfApplicabilityOrderBy"
description: Order by description: Order by
size: size:
type: integer type: integer
@@ -6150,37 +6150,37 @@ components:
$ref: "#/components/schemas/CursorKey" $ref: "#/components/schemas/CursorKey"
description: Page cursor description: Page cursor
ListStatesOfApplicabilityOutput: ListStatementsOfApplicabilityOutput:
type: object type: object
required: required:
- states_of_applicability - statements_of_applicability
properties: properties:
next_cursor: next_cursor:
$ref: "#/components/schemas/CursorKey" $ref: "#/components/schemas/CursorKey"
description: Next cursor description: Next cursor
states_of_applicability: statements_of_applicability:
type: array type: array
items: items:
$ref: "#/components/schemas/StateOfApplicability" $ref: "#/components/schemas/StatementOfApplicability"
GetStateOfApplicabilityInput: GetStatementOfApplicabilityInput:
type: object type: object
required: required:
- id - id
properties: properties:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
GetStateOfApplicabilityOutput: GetStatementOfApplicabilityOutput:
type: object type: object
required: required:
- state_of_applicability - statement_of_applicability
properties: properties:
state_of_applicability: statement_of_applicability:
$ref: "#/components/schemas/StateOfApplicability" $ref: "#/components/schemas/StatementOfApplicability"
AddStateOfApplicabilityInput: AddStatementOfApplicabilityInput:
type: object type: object
required: required:
- organization_id - organization_id
@@ -6192,70 +6192,70 @@ components:
description: Organization ID description: Organization ID
name: name:
type: string type: string
description: State of applicability name description: Statement of applicability name
owner_id: owner_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Owner profile ID description: Owner profile ID
AddStateOfApplicabilityOutput: AddStatementOfApplicabilityOutput:
type: object type: object
required: required:
- state_of_applicability - statement_of_applicability
properties: properties:
state_of_applicability: statement_of_applicability:
$ref: "#/components/schemas/StateOfApplicability" $ref: "#/components/schemas/StatementOfApplicability"
UpdateStateOfApplicabilityInput: UpdateStatementOfApplicabilityInput:
type: object type: object
required: required:
- id - id
properties: properties:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
name: name:
type: string type: string
description: State of applicability name description: Statement of applicability name
owner_id: owner_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Owner profile ID description: Owner profile ID
UpdateStateOfApplicabilityOutput: UpdateStatementOfApplicabilityOutput:
type: object type: object
required: required:
- state_of_applicability - statement_of_applicability
properties: properties:
state_of_applicability: statement_of_applicability:
$ref: "#/components/schemas/StateOfApplicability" $ref: "#/components/schemas/StatementOfApplicability"
DeleteStateOfApplicabilityInput: DeleteStatementOfApplicabilityInput:
type: object type: object
required: required:
- id - id
properties: properties:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
DeleteStateOfApplicabilityOutput: DeleteStatementOfApplicabilityOutput:
type: object type: object
required: required:
- deleted_state_of_applicability_id - deleted_statement_of_applicability_id
properties: properties:
deleted_state_of_applicability_id: deleted_statement_of_applicability_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Deleted state of applicability ID description: Deleted statement of applicability ID
ExportStateOfApplicabilityPDFInput: ExportStatementOfApplicabilityPDFInput:
type: object type: object
required: required:
- id - id
properties: properties:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
ExportStateOfApplicabilityPDFOutput: ExportStatementOfApplicabilityPDFOutput:
type: object type: object
required: required:
- pdf_base64 - pdf_base64
@@ -6292,7 +6292,7 @@ components:
type: object type: object
required: required:
- id - id
- state_of_applicability_id - statement_of_applicability_id
- control_id - control_id
- organization_id - organization_id
- applicability - applicability
@@ -6302,9 +6302,9 @@ components:
id: id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Applicability statement ID description: Applicability statement ID
state_of_applicability_id: statement_of_applicability_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
control_id: control_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Control ID description: Control ID
@@ -6336,11 +6336,11 @@ components:
ListApplicabilityStatementsInput: ListApplicabilityStatementsInput:
type: object type: object
required: required:
- state_of_applicability_id - statement_of_applicability_id
properties: properties:
state_of_applicability_id: statement_of_applicability_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
order_by: order_by:
$ref: "#/components/schemas/ApplicabilityStatementOrderBy" $ref: "#/components/schemas/ApplicabilityStatementOrderBy"
description: Order by description: Order by
@@ -6384,13 +6384,13 @@ components:
AddApplicabilityStatementInput: AddApplicabilityStatementInput:
type: object type: object
required: required:
- state_of_applicability_id - statement_of_applicability_id
- control_id - control_id
- applicability - applicability
properties: properties:
state_of_applicability_id: statement_of_applicability_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: State of applicability ID description: Statement of applicability ID
control_id: control_id:
$ref: "#/components/schemas/GID" $ref: "#/components/schemas/GID"
description: Control ID description: Control ID
@@ -8526,60 +8526,60 @@ tools:
$ref: "#/components/schemas/ListMeetingAttendeesInput" $ref: "#/components/schemas/ListMeetingAttendeesInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/ListMeetingAttendeesOutput" $ref: "#/components/schemas/ListMeetingAttendeesOutput"
- name: listStatesOfApplicability - name: listStatementsOfApplicability
description: List all states of applicability for the organization description: List all statements of applicability for the organization
hints: hints:
readonly: true readonly: true
idempotent: true idempotent: true
inputSchema: inputSchema:
$ref: "#/components/schemas/ListStatesOfApplicabilityInput" $ref: "#/components/schemas/ListStatementsOfApplicabilityInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/ListStatesOfApplicabilityOutput" $ref: "#/components/schemas/ListStatementsOfApplicabilityOutput"
- name: getStateOfApplicability - name: getStatementOfApplicability
description: Get a state of applicability by ID description: Get a statement of applicability by ID
hints: hints:
readonly: true readonly: true
idempotent: true idempotent: true
inputSchema: inputSchema:
$ref: "#/components/schemas/GetStateOfApplicabilityInput" $ref: "#/components/schemas/GetStatementOfApplicabilityInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/GetStateOfApplicabilityOutput" $ref: "#/components/schemas/GetStatementOfApplicabilityOutput"
- name: addStateOfApplicability - name: addStatementOfApplicability
description: Add a new state of applicability to the organization description: Add a new statement of applicability to the organization
hints: hints:
readonly: false readonly: false
inputSchema: inputSchema:
$ref: "#/components/schemas/AddStateOfApplicabilityInput" $ref: "#/components/schemas/AddStatementOfApplicabilityInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/AddStateOfApplicabilityOutput" $ref: "#/components/schemas/AddStatementOfApplicabilityOutput"
- name: updateStateOfApplicability - name: updateStatementOfApplicability
description: Update an existing state of applicability description: Update an existing statement of applicability
hints: hints:
readonly: false readonly: false
inputSchema: inputSchema:
$ref: "#/components/schemas/UpdateStateOfApplicabilityInput" $ref: "#/components/schemas/UpdateStatementOfApplicabilityInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/UpdateStateOfApplicabilityOutput" $ref: "#/components/schemas/UpdateStatementOfApplicabilityOutput"
- name: deleteStateOfApplicability - name: deleteStatementOfApplicability
description: Delete a state of applicability description: Delete a statement of applicability
hints: hints:
readonly: false readonly: false
destructive: true destructive: true
inputSchema: inputSchema:
$ref: "#/components/schemas/DeleteStateOfApplicabilityInput" $ref: "#/components/schemas/DeleteStatementOfApplicabilityInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/DeleteStateOfApplicabilityOutput" $ref: "#/components/schemas/DeleteStatementOfApplicabilityOutput"
- name: exportStateOfApplicabilityPDF - name: exportStatementOfApplicabilityPDF
description: Export a state of applicability as a PDF document description: Export a statement of applicability as a PDF document
hints: hints:
readonly: true readonly: true
idempotent: true idempotent: true
inputSchema: inputSchema:
$ref: "#/components/schemas/ExportStateOfApplicabilityPDFInput" $ref: "#/components/schemas/ExportStatementOfApplicabilityPDFInput"
outputSchema: outputSchema:
$ref: "#/components/schemas/ExportStateOfApplicabilityPDFOutput" $ref: "#/components/schemas/ExportStatementOfApplicabilityPDFOutput"
- name: listApplicabilityStatements - name: listApplicabilityStatements
description: List all applicability statements for a state of applicability description: List all applicability statements for a statement of applicability
hints: hints:
readonly: true readonly: true
idempotent: true idempotent: true
@@ -8597,7 +8597,7 @@ tools:
outputSchema: outputSchema:
$ref: "#/components/schemas/GetApplicabilityStatementOutput" $ref: "#/components/schemas/GetApplicabilityStatementOutput"
- name: addApplicabilityStatement - name: addApplicabilityStatement
description: Add a control to a state of applicability with an applicability decision description: Add a control to a statement of applicability with an applicability decision
hints: hints:
readonly: false readonly: false
inputSchema: inputSchema:
@@ -8613,7 +8613,7 @@ tools:
outputSchema: outputSchema:
$ref: "#/components/schemas/UpdateApplicabilityStatementOutput" $ref: "#/components/schemas/UpdateApplicabilityStatementOutput"
- name: deleteApplicabilityStatement - name: deleteApplicabilityStatement
description: Delete an applicability statement from a state of applicability description: Delete an applicability statement from a statement of applicability
hints: hints:
readonly: false readonly: false
destructive: true destructive: true

View File

@@ -18,8 +18,8 @@ import (
"go.probo.inc/probo/pkg/page" "go.probo.inc/probo/pkg/page"
) )
func NewStateOfApplicability(s *coredata.StateOfApplicability) *StateOfApplicability { func NewStatementOfApplicability(s *coredata.StatementOfApplicability) *StatementOfApplicability {
return &StateOfApplicability{ return &StatementOfApplicability{
ID: s.ID, ID: s.ID,
OrganizationID: s.OrganizationID, OrganizationID: s.OrganizationID,
Name: s.Name, Name: s.Name,
@@ -30,33 +30,33 @@ func NewStateOfApplicability(s *coredata.StateOfApplicability) *StateOfApplicabi
} }
} }
func NewListStatesOfApplicabilityOutput(pg *page.Page[*coredata.StateOfApplicability, coredata.StateOfApplicabilityOrderField]) ListStatesOfApplicabilityOutput { func NewListStatementsOfApplicabilityOutput(pg *page.Page[*coredata.StatementOfApplicability, coredata.StatementOfApplicabilityOrderField]) ListStatementsOfApplicabilityOutput {
items := make([]*StateOfApplicability, 0, len(pg.Data)) items := make([]*StatementOfApplicability, 0, len(pg.Data))
for _, v := range pg.Data { for _, v := range pg.Data {
items = append(items, NewStateOfApplicability(v)) items = append(items, NewStatementOfApplicability(v))
} }
var nextCursor *page.CursorKey var nextCursor *page.CursorKey
if len(pg.Data) > 0 { if len(pg.Data) > 0 {
cursorKey := pg.Data[len(pg.Data)-1].CursorKey(pg.Cursor.OrderBy.Field) cursorKey := pg.Data[len(pg.Data)-1].CursorKey(pg.Cursor.OrderBy.Field)
nextCursor = &cursorKey nextCursor = &cursorKey
} }
return ListStatesOfApplicabilityOutput{ return ListStatementsOfApplicabilityOutput{
NextCursor: nextCursor, NextCursor: nextCursor,
StatesOfApplicability: items, StatementsOfApplicability: items,
} }
} }
func NewApplicabilityStatement(a *coredata.ApplicabilityStatement) *ApplicabilityStatement { func NewApplicabilityStatement(a *coredata.ApplicabilityStatement) *ApplicabilityStatement {
return &ApplicabilityStatement{ return &ApplicabilityStatement{
ID: a.ID, ID: a.ID,
StateOfApplicabilityID: a.StateOfApplicabilityID, StatementOfApplicabilityID: a.StatementOfApplicabilityID,
ControlID: a.ControlID, ControlID: a.ControlID,
OrganizationID: a.OrganizationID, OrganizationID: a.OrganizationID,
SnapshotID: a.SnapshotID, SnapshotID: a.SnapshotID,
Applicability: a.Applicability, Applicability: a.Applicability,
Justification: a.Justification, Justification: a.Justification,
CreatedAt: a.CreatedAt, CreatedAt: a.CreatedAt,
UpdatedAt: a.UpdatedAt, UpdatedAt: a.UpdatedAt,
} }
} }