Add contols audits
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
201
apps/console/src/components/audits/LinkedAuditsCard.tsx
Normal file
201
apps/console/src/components/audits/LinkedAuditsCard.tsx
Normal file
@@ -0,0 +1,201 @@
|
||||
import { graphql } from "relay-runtime";
|
||||
import {
|
||||
Card,
|
||||
IconPlusLarge,
|
||||
Button,
|
||||
Tr,
|
||||
Td,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Th,
|
||||
IconChevronDown,
|
||||
IconTrashCan,
|
||||
Badge,
|
||||
TrButton,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import type { LinkedAuditsCardFragment$key } from "./__generated__/LinkedAuditsCardFragment.graphql";
|
||||
import { useFragment } from "react-relay";
|
||||
import { useMemo, useState } from "react";
|
||||
import { sprintf, getAuditStateVariant } from "@probo/helpers";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { LinkedAuditsDialog } from "./LinkedAuditsDialog";
|
||||
import clsx from "clsx";
|
||||
|
||||
const linkedAuditFragment = graphql`
|
||||
fragment LinkedAuditsCardFragment on Audit {
|
||||
id
|
||||
name
|
||||
createdAt
|
||||
state
|
||||
validFrom
|
||||
validUntil
|
||||
framework {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Mutation<Params> = (p: {
|
||||
variables: {
|
||||
input: {
|
||||
auditId: string;
|
||||
} & Params;
|
||||
connections: string[];
|
||||
};
|
||||
}) => void;
|
||||
|
||||
type Props<Params> = {
|
||||
audits: (LinkedAuditsCardFragment$key & { id: string })[];
|
||||
params: Params;
|
||||
disabled?: boolean;
|
||||
connectionId: string;
|
||||
onAttach: Mutation<Params>;
|
||||
onDetach: Mutation<Params>;
|
||||
variant?: "card" | "table";
|
||||
};
|
||||
|
||||
export function LinkedAuditsCard<Params>(props: Props<Params>) {
|
||||
const { __ } = useTranslate();
|
||||
const [limit, setLimit] = useState<number | null>(4);
|
||||
const audits = useMemo(() => {
|
||||
return limit ? props.audits.slice(0, limit) : props.audits;
|
||||
}, [props.audits, limit]);
|
||||
const showMoreButton = limit !== null && props.audits.length > limit;
|
||||
const variant = props.variant ?? "table";
|
||||
|
||||
const onAttach = (auditId: string) => {
|
||||
props.onAttach({
|
||||
variables: {
|
||||
input: {
|
||||
auditId,
|
||||
...props.params,
|
||||
},
|
||||
connections: [props.connectionId],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const onDetach = (auditId: string) => {
|
||||
props.onDetach({
|
||||
variables: {
|
||||
input: {
|
||||
auditId,
|
||||
...props.params,
|
||||
},
|
||||
connections: [props.connectionId],
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const Wrapper = variant === "card" ? Card : "div";
|
||||
|
||||
return (
|
||||
<Wrapper padded className="space-y-[10px]">
|
||||
{variant === "card" && (
|
||||
<div className="flex justify-between">
|
||||
<div className="text-lg font-semibold">{__("Audits")}</div>
|
||||
<LinkedAuditsDialog
|
||||
disabled={props.disabled}
|
||||
linkedAudits={props.audits}
|
||||
onLink={onAttach}
|
||||
onUnlink={onDetach}
|
||||
>
|
||||
<Button variant="tertiary" icon={IconPlusLarge}>
|
||||
{__("Link audit")}
|
||||
</Button>
|
||||
</LinkedAuditsDialog>
|
||||
</div>
|
||||
)}
|
||||
<Table className={clsx(variant === "card" && "bg-invert")}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{__("Name")}</Th>
|
||||
<Th>{__("State")}</Th>
|
||||
<Th></Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{audits.length === 0 && (
|
||||
<Tr>
|
||||
<Td colSpan={3} className="text-center text-txt-secondary">
|
||||
{__("No audits linked")}
|
||||
</Td>
|
||||
</Tr>
|
||||
)}
|
||||
{audits.map((audit) => (
|
||||
<AuditRow
|
||||
key={audit.id}
|
||||
audit={audit}
|
||||
onClick={onDetach}
|
||||
/>
|
||||
))}
|
||||
{variant === "table" && (
|
||||
<LinkedAuditsDialog
|
||||
disabled={props.disabled}
|
||||
linkedAudits={props.audits}
|
||||
onLink={onAttach}
|
||||
onUnlink={onDetach}
|
||||
>
|
||||
<TrButton colspan={3} icon={IconPlusLarge}>
|
||||
{__("Link audit")}
|
||||
</TrButton>
|
||||
</LinkedAuditsDialog>
|
||||
)}
|
||||
</Tbody>
|
||||
</Table>
|
||||
{showMoreButton && (
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={() => setLimit(null)}
|
||||
className="mt-3 mx-auto"
|
||||
icon={IconChevronDown}
|
||||
>
|
||||
{sprintf(__("Show %s more"), props.audits.length - limit)}
|
||||
</Button>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
function AuditRow(props: {
|
||||
audit: LinkedAuditsCardFragment$key & { id: string };
|
||||
onClick: (auditId: string) => void;
|
||||
}) {
|
||||
const audit = useFragment(linkedAuditFragment, props.audit);
|
||||
const organizationId = useOrganizationId();
|
||||
const { __ } = useTranslate();
|
||||
|
||||
return (
|
||||
<Tr to={`/organizations/${organizationId}/audits/${audit.id}`}>
|
||||
<Td>
|
||||
<div className="flex flex-col">
|
||||
<div className="font-medium">
|
||||
{audit.framework?.name}
|
||||
</div>
|
||||
{audit.name && (
|
||||
<div className="text-sm text-txt-secondary">
|
||||
{audit.name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<Badge color={getAuditStateVariant(audit.state)}>
|
||||
{audit.state.replace(/_/g, " ")}
|
||||
</Badge>
|
||||
</Td>
|
||||
<Td noLink width={50} className="text-end">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => props.onClick(audit.id)}
|
||||
icon={IconTrashCan}
|
||||
>
|
||||
{__("Unlink")}
|
||||
</Button>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
}
|
||||
195
apps/console/src/components/audits/LinkedAuditsDialog.tsx
Normal file
195
apps/console/src/components/audits/LinkedAuditsDialog.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
Badge,
|
||||
IconMagnifyingGlass,
|
||||
IconPlusLarge,
|
||||
IconTrashCan,
|
||||
InfiniteScrollTrigger,
|
||||
Input,
|
||||
Spinner,
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { getAuditStateVariant } from "@probo/helpers";
|
||||
import { Suspense, useMemo, useState, type ReactNode } from "react";
|
||||
import { graphql } from "relay-runtime";
|
||||
import { useLazyLoadQuery, usePaginationFragment } from "react-relay";
|
||||
import type { LinkedAuditsDialogQuery } from "./__generated__/LinkedAuditsDialogQuery.graphql";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import type { NodeOf } from "/types";
|
||||
import type {
|
||||
LinkedAuditsDialogFragment$data,
|
||||
LinkedAuditsDialogFragment$key,
|
||||
} from "./__generated__/LinkedAuditsDialogFragment.graphql";
|
||||
|
||||
const auditsQuery = graphql`
|
||||
query LinkedAuditsDialogQuery($organizationId: ID!) {
|
||||
organization: node(id: $organizationId) {
|
||||
id
|
||||
... on Organization {
|
||||
...LinkedAuditsDialogFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const auditsFragment = graphql`
|
||||
fragment LinkedAuditsDialogFragment on Organization
|
||||
@refetchable(queryName: "LinkedAuditsDialogQuery_fragment")
|
||||
@argumentDefinitions(
|
||||
first: { type: "Int", defaultValue: 20 }
|
||||
order: { type: "AuditOrder", defaultValue: null }
|
||||
after: { type: "CursorKey", defaultValue: null }
|
||||
before: { type: "CursorKey", defaultValue: null }
|
||||
last: { type: "Int", defaultValue: null }
|
||||
) {
|
||||
audits(
|
||||
first: $first
|
||||
after: $after
|
||||
last: $last
|
||||
before: $before
|
||||
orderBy: $order
|
||||
) @connection(key: "LinkedAuditsDialogQuery_audits") {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
state
|
||||
validFrom
|
||||
validUntil
|
||||
framework {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
disabled?: boolean;
|
||||
linkedAudits?: { id: string }[];
|
||||
onLink: (auditId: string) => void;
|
||||
onUnlink: (auditId: string) => void;
|
||||
};
|
||||
|
||||
export function LinkedAuditsDialog({ children, ...props }: Props) {
|
||||
const { __ } = useTranslate();
|
||||
|
||||
return (
|
||||
<Dialog trigger={children} title={__("Link audits")}>
|
||||
<DialogContent>
|
||||
<Suspense fallback={<Spinner centered />}>
|
||||
<LinkedAuditsDialogContent {...props} />
|
||||
</Suspense>
|
||||
</DialogContent>
|
||||
<DialogFooter exitLabel={__("Close")} />
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function LinkedAuditsDialogContent(props: Omit<Props, "children">) {
|
||||
const organizationId = useOrganizationId();
|
||||
const query = useLazyLoadQuery<LinkedAuditsDialogQuery>(auditsQuery, {
|
||||
organizationId,
|
||||
});
|
||||
const { data, loadNext, hasNext, isLoadingNext } = usePaginationFragment(
|
||||
auditsFragment,
|
||||
query.organization as LinkedAuditsDialogFragment$key
|
||||
);
|
||||
const { __ } = useTranslate();
|
||||
const [search, setSearch] = useState("");
|
||||
const audits = data.audits?.edges?.map((edge) => edge.node) ?? [];
|
||||
const linkedIds = useMemo(() => {
|
||||
return new Set(props.linkedAudits?.map((a) => a.id) ?? []);
|
||||
}, [props.linkedAudits]);
|
||||
|
||||
const filteredAudits = useMemo(() => {
|
||||
return audits.filter((audit) =>
|
||||
(audit.name || "").toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
}, [audits, search]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center gap-2 sticky top-0 relative py-4 bg-linear-to-b from-50% from-level-2 to-level-2/0 px-6">
|
||||
<Input
|
||||
icon={IconMagnifyingGlass}
|
||||
placeholder={__("Search audits...")}
|
||||
onValueChange={setSearch}
|
||||
/>
|
||||
</div>
|
||||
<div className="divide-y divide-border-low">
|
||||
{filteredAudits.map((audit) => (
|
||||
<AuditRow
|
||||
key={audit.id}
|
||||
audit={audit}
|
||||
linkedAudits={linkedIds}
|
||||
onLink={props.onLink}
|
||||
onUnlink={props.onUnlink}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
))}
|
||||
{hasNext && (
|
||||
<InfiniteScrollTrigger
|
||||
loading={isLoadingNext}
|
||||
onView={() => loadNext(20)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
type Audit = NodeOf<LinkedAuditsDialogFragment$data["audits"]>;
|
||||
|
||||
type RowProps = {
|
||||
audit: Audit;
|
||||
linkedAudits: Set<string>;
|
||||
disabled?: boolean;
|
||||
onLink: (auditId: string) => void;
|
||||
onUnlink: (auditId: string) => void;
|
||||
};
|
||||
|
||||
function AuditRow(props: RowProps) {
|
||||
const { __ } = useTranslate();
|
||||
|
||||
const isLinked = props.linkedAudits.has(props.audit.id);
|
||||
const onClick = isLinked ? props.onUnlink : props.onLink;
|
||||
const IconComponent = isLinked ? IconTrashCan : IconPlusLarge;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="py-4 flex items-center gap-4 hover:bg-subtle cursor-pointer px-6 w-full h-[100px]"
|
||||
onClick={() => onClick(props.audit.id)}
|
||||
>
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
<div className="font-medium">
|
||||
{props.audit.framework?.name}
|
||||
</div>
|
||||
{props.audit.name && (
|
||||
<div className="text-sm text-txt-secondary">
|
||||
{props.audit.name}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Badge color={getAuditStateVariant(props.audit.state)}>
|
||||
{props.audit.state.replace(/_/g, " ")}
|
||||
</Badge>
|
||||
<Button
|
||||
disabled={props.disabled}
|
||||
className="ml-auto"
|
||||
variant={isLinked ? "secondary" : "primary"}
|
||||
asChild
|
||||
>
|
||||
<span>
|
||||
<IconComponent size={16} /> {isLinked ? __("Unlink") : __("Link")}
|
||||
</span>
|
||||
</Button>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
104
apps/console/src/components/audits/__generated__/LinkedAuditsCardFragment.graphql.ts
generated
Normal file
104
apps/console/src/components/audits/__generated__/LinkedAuditsCardFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* @generated SignedSource<<eb9ce92b73884420f07e71096665149f>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type AuditState = "COMPLETED" | "IN_PROGRESS" | "NOT_STARTED" | "OUTDATED" | "REJECTED";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type LinkedAuditsCardFragment$data = {
|
||||
readonly createdAt: any;
|
||||
readonly framework: {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly name: string | null | undefined;
|
||||
readonly state: AuditState;
|
||||
readonly validFrom: any | null | undefined;
|
||||
readonly validUntil: any | null | undefined;
|
||||
readonly " $fragmentType": "LinkedAuditsCardFragment";
|
||||
};
|
||||
export type LinkedAuditsCardFragment$key = {
|
||||
readonly " $data"?: LinkedAuditsCardFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsCardFragment">;
|
||||
};
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "LinkedAuditsCardFragment",
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validFrom",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validUntil",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Framework",
|
||||
"kind": "LinkedField",
|
||||
"name": "framework",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Audit",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "9ff9367a1f0668a8537bdd5bf5c7233d";
|
||||
|
||||
export default node;
|
||||
257
apps/console/src/components/audits/__generated__/LinkedAuditsDialogFragment.graphql.ts
generated
Normal file
257
apps/console/src/components/audits/__generated__/LinkedAuditsDialogFragment.graphql.ts
generated
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* @generated SignedSource<<72efdb68c08c793ff724c9a16d91d29a>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ReaderFragment } from 'relay-runtime';
|
||||
export type AuditState = "COMPLETED" | "IN_PROGRESS" | "NOT_STARTED" | "OUTDATED" | "REJECTED";
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type LinkedAuditsDialogFragment$data = {
|
||||
readonly audits: {
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly framework: {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly name: string | null | undefined;
|
||||
readonly state: AuditState;
|
||||
readonly validFrom: any | null | undefined;
|
||||
readonly validUntil: any | null | undefined;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly id: string;
|
||||
readonly " $fragmentType": "LinkedAuditsDialogFragment";
|
||||
};
|
||||
export type LinkedAuditsDialogFragment$key = {
|
||||
readonly " $data"?: LinkedAuditsDialogFragment$data;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsDialogFragment">;
|
||||
};
|
||||
|
||||
import LinkedAuditsDialogQuery_fragment_graphql from './LinkedAuditsDialogQuery_fragment.graphql';
|
||||
|
||||
const node: ReaderFragment = (function(){
|
||||
var v0 = [
|
||||
"audits"
|
||||
],
|
||||
v1 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"argumentDefinitions": [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "before"
|
||||
},
|
||||
{
|
||||
"defaultValue": 20,
|
||||
"kind": "LocalArgument",
|
||||
"name": "first"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "last"
|
||||
},
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "order"
|
||||
}
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": {
|
||||
"connection": [
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "bidirectional",
|
||||
"path": (v0/*: any*/)
|
||||
}
|
||||
],
|
||||
"refetch": {
|
||||
"connection": {
|
||||
"forward": {
|
||||
"count": "first",
|
||||
"cursor": "after"
|
||||
},
|
||||
"backward": {
|
||||
"count": "last",
|
||||
"cursor": "before"
|
||||
},
|
||||
"path": (v0/*: any*/)
|
||||
},
|
||||
"fragmentPathInResult": [
|
||||
"node"
|
||||
],
|
||||
"operation": LinkedAuditsDialogQuery_fragment_graphql,
|
||||
"identifierInfo": {
|
||||
"identifierField": "id",
|
||||
"identifierQueryVariableName": "id"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "LinkedAuditsDialogFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "audits",
|
||||
"args": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "orderBy",
|
||||
"variableName": "order"
|
||||
}
|
||||
],
|
||||
"concreteType": "AuditConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__LinkedAuditsDialogQuery_audits_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validFrom",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validUntil",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Framework",
|
||||
"kind": "LinkedField",
|
||||
"name": "framework",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d58fca9b4fb36a24ded1858b632f5acf";
|
||||
|
||||
export default node;
|
||||
273
apps/console/src/components/audits/__generated__/LinkedAuditsDialogQuery.graphql.ts
generated
Normal file
273
apps/console/src/components/audits/__generated__/LinkedAuditsDialogQuery.graphql.ts
generated
Normal file
@@ -0,0 +1,273 @@
|
||||
/**
|
||||
* @generated SignedSource<<1f9d92c4803d49c216fef6447a59c779>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type LinkedAuditsDialogQuery$variables = {
|
||||
organizationId: string;
|
||||
};
|
||||
export type LinkedAuditsDialogQuery$data = {
|
||||
readonly organization: {
|
||||
readonly id: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsDialogFragment">;
|
||||
};
|
||||
};
|
||||
export type LinkedAuditsDialogQuery = {
|
||||
response: LinkedAuditsDialogQuery$data;
|
||||
variables: LinkedAuditsDialogQuery$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = [
|
||||
{
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "organizationId"
|
||||
}
|
||||
],
|
||||
v1 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "organizationId"
|
||||
}
|
||||
],
|
||||
v2 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = [
|
||||
{
|
||||
"kind": "Literal",
|
||||
"name": "first",
|
||||
"value": 20
|
||||
}
|
||||
],
|
||||
v5 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "LinkedAuditsDialogQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "LinkedAuditsDialogFragment"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
"kind": "Operation",
|
||||
"name": "LinkedAuditsDialogQuery",
|
||||
"selections": [
|
||||
{
|
||||
"alias": "organization",
|
||||
"args": (v1/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"concreteType": "AuditConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "audits",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v5/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validFrom",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validUntil",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Framework",
|
||||
"kind": "LinkedField",
|
||||
"name": "framework",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": "audits(first:20)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v4/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "LinkedAuditsDialogQuery_audits",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "audits"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "0daa6dc9bebd2677cc0c36e819631f9c",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "LinkedAuditsDialogQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query LinkedAuditsDialogQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ... on Organization {\n ...LinkedAuditsDialogFragment\n }\n }\n}\n\nfragment LinkedAuditsDialogFragment on Organization {\n audits(first: 20) {\n edges {\n node {\n id\n name\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "a6f87b771862cc24fee101cc924a57b2";
|
||||
|
||||
export default node;
|
||||
346
apps/console/src/components/audits/__generated__/LinkedAuditsDialogQuery_fragment.graphql.ts
generated
Normal file
346
apps/console/src/components/audits/__generated__/LinkedAuditsDialogQuery_fragment.graphql.ts
generated
Normal file
@@ -0,0 +1,346 @@
|
||||
/**
|
||||
* @generated SignedSource<<64f01ce9caaee3040df3df201bb608f8>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type AuditOrderField = "CREATED_AT" | "STATE" | "VALID_FROM" | "VALID_UNTIL";
|
||||
export type OrderDirection = "ASC" | "DESC";
|
||||
export type AuditOrder = {
|
||||
direction: OrderDirection;
|
||||
field: AuditOrderField;
|
||||
};
|
||||
export type LinkedAuditsDialogQuery_fragment$variables = {
|
||||
after?: any | null | undefined;
|
||||
before?: any | null | undefined;
|
||||
first?: number | null | undefined;
|
||||
id: string;
|
||||
last?: number | null | undefined;
|
||||
order?: AuditOrder | null | undefined;
|
||||
};
|
||||
export type LinkedAuditsDialogQuery_fragment$data = {
|
||||
readonly node: {
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsDialogFragment">;
|
||||
};
|
||||
};
|
||||
export type LinkedAuditsDialogQuery_fragment = {
|
||||
response: LinkedAuditsDialogQuery_fragment$data;
|
||||
variables: LinkedAuditsDialogQuery_fragment$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "after"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "before"
|
||||
},
|
||||
v2 = {
|
||||
"defaultValue": 20,
|
||||
"kind": "LocalArgument",
|
||||
"name": "first"
|
||||
},
|
||||
v3 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "id"
|
||||
},
|
||||
v4 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "last"
|
||||
},
|
||||
v5 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "order"
|
||||
},
|
||||
v6 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "id",
|
||||
"variableName": "id"
|
||||
}
|
||||
],
|
||||
v7 = {
|
||||
"kind": "Variable",
|
||||
"name": "after",
|
||||
"variableName": "after"
|
||||
},
|
||||
v8 = {
|
||||
"kind": "Variable",
|
||||
"name": "before",
|
||||
"variableName": "before"
|
||||
},
|
||||
v9 = {
|
||||
"kind": "Variable",
|
||||
"name": "first",
|
||||
"variableName": "first"
|
||||
},
|
||||
v10 = {
|
||||
"kind": "Variable",
|
||||
"name": "last",
|
||||
"variableName": "last"
|
||||
},
|
||||
v11 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "__typename",
|
||||
"storageKey": null
|
||||
},
|
||||
v12 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v13 = [
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "orderBy",
|
||||
"variableName": "order"
|
||||
}
|
||||
],
|
||||
v14 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "LinkedAuditsDialogQuery_fragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"args": [
|
||||
(v7/*: any*/),
|
||||
(v8/*: any*/),
|
||||
(v9/*: any*/),
|
||||
(v10/*: any*/),
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "order",
|
||||
"variableName": "order"
|
||||
}
|
||||
],
|
||||
"kind": "FragmentSpread",
|
||||
"name": "LinkedAuditsDialogFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Query",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/),
|
||||
(v2/*: any*/),
|
||||
(v4/*: any*/),
|
||||
(v5/*: any*/),
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "LinkedAuditsDialogQuery_fragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v6/*: any*/),
|
||||
"concreteType": null,
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v11/*: any*/),
|
||||
(v12/*: any*/),
|
||||
{
|
||||
"kind": "InlineFragment",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v13/*: any*/),
|
||||
"concreteType": "AuditConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "audits",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v12/*: any*/),
|
||||
(v14/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validFrom",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validUntil",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Framework",
|
||||
"kind": "LinkedField",
|
||||
"name": "framework",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v12/*: any*/),
|
||||
(v14/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v11/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "cursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "PageInfo",
|
||||
"kind": "LinkedField",
|
||||
"name": "pageInfo",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "endCursor",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasNextPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "hasPreviousPage",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "startCursor",
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v13/*: any*/),
|
||||
"filters": [
|
||||
"orderBy"
|
||||
],
|
||||
"handle": "connection",
|
||||
"key": "LinkedAuditsDialogQuery_audits",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "audits"
|
||||
}
|
||||
],
|
||||
"type": "Organization",
|
||||
"abstractKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "26f1cad4cab5157a2d9e10d4b89114ec",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "LinkedAuditsDialogQuery_fragment",
|
||||
"operationKind": "query",
|
||||
"text": "query LinkedAuditsDialogQuery_fragment(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 20\n $last: Int = null\n $order: AuditOrder = null\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...LinkedAuditsDialogFragment_16fISc\n id\n }\n}\n\nfragment LinkedAuditsDialogFragment_16fISc on Organization {\n audits(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n name\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d58fca9b4fb36a24ded1858b632f5acf";
|
||||
|
||||
export default node;
|
||||
@@ -120,6 +120,16 @@ export const frameworkControlNodeQuery = graphql`
|
||||
}
|
||||
}
|
||||
}
|
||||
audits(first: 100)
|
||||
@connection(key: "FrameworkGraphControl_audits") {
|
||||
__id
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...LinkedAuditsCardFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* @generated SignedSource<<95d2adf687d1cae6938863ba004db9a8>>
|
||||
* @generated SignedSource<<dec38386ca0015f6c77121c2740fa406>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
@@ -16,6 +16,15 @@ export type FrameworkGraphControlNodeQuery$variables = {
|
||||
};
|
||||
export type FrameworkGraphControlNodeQuery$data = {
|
||||
readonly node: {
|
||||
readonly audits?: {
|
||||
readonly __id: string;
|
||||
readonly edges: ReadonlyArray<{
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsCardFragment">;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
readonly description?: string;
|
||||
readonly documents?: {
|
||||
readonly __id: string;
|
||||
@@ -162,7 +171,21 @@ v12 = [
|
||||
"name": "first",
|
||||
"value": 100
|
||||
}
|
||||
];
|
||||
],
|
||||
v13 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
v14 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": (v0/*: any*/),
|
||||
@@ -277,6 +300,49 @@ return {
|
||||
(v11/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": "audits",
|
||||
"args": null,
|
||||
"concreteType": "AuditConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "__FrameworkGraphControl_audits_connection",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "LinkedAuditsCardFragment"
|
||||
},
|
||||
(v8/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v9/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Control",
|
||||
@@ -339,13 +405,7 @@ return {
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
(v13/*: any*/),
|
||||
(v8/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
@@ -400,13 +460,7 @@ return {
|
||||
"name": "title",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
(v14/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
@@ -476,6 +530,83 @@ return {
|
||||
"key": "FrameworkGraphControl_documents",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "documents"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v12/*: any*/),
|
||||
"concreteType": "AuditConnection",
|
||||
"kind": "LinkedField",
|
||||
"name": "audits",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "edges",
|
||||
"plural": true,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/),
|
||||
(v14/*: any*/),
|
||||
(v13/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validFrom",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validUntil",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Framework",
|
||||
"kind": "LinkedField",
|
||||
"name": "framework",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v2/*: any*/),
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v8/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v9/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
(v10/*: any*/),
|
||||
(v11/*: any*/)
|
||||
],
|
||||
"storageKey": "audits(first:100)"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v12/*: any*/),
|
||||
"filters": null,
|
||||
"handle": "connection",
|
||||
"key": "FrameworkGraphControl_audits",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "audits"
|
||||
}
|
||||
],
|
||||
"type": "Control",
|
||||
@@ -487,7 +618,7 @@ return {
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "ed223aa33ef15ba2e170e8aeae990f8e",
|
||||
"cacheID": "20a0b80a6b0d796951c7f8c6eca41d3c",
|
||||
"id": null,
|
||||
"metadata": {
|
||||
"connection": [
|
||||
@@ -508,16 +639,25 @@ return {
|
||||
"node",
|
||||
"documents"
|
||||
]
|
||||
},
|
||||
{
|
||||
"count": null,
|
||||
"cursor": null,
|
||||
"direction": "forward",
|
||||
"path": [
|
||||
"node",
|
||||
"audits"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "FrameworkGraphControlNodeQuery",
|
||||
"operationKind": "query",
|
||||
"text": "query FrameworkGraphControlNodeQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n sectionTitle\n description\n status\n exclusionJustification\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n status\n exclusionJustification\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n"
|
||||
"text": "query FrameworkGraphControlNodeQuery(\n $controlId: ID!\n) {\n node(id: $controlId) {\n __typename\n ... on Control {\n id\n name\n sectionTitle\n description\n status\n exclusionJustification\n ...FrameworkControlDialogFragment\n measures(first: 100) {\n edges {\n node {\n id\n ...LinkedMeasuresCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n documents(first: 100) {\n edges {\n node {\n id\n ...LinkedDocumentsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n audits(first: 100) {\n edges {\n node {\n id\n ...LinkedAuditsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment FrameworkControlDialogFragment on Control {\n id\n name\n description\n sectionTitle\n status\n exclusionJustification\n}\n\nfragment LinkedAuditsCardFragment on Audit {\n id\n name\n createdAt\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n}\n\nfragment LinkedDocumentsCardFragment on Document {\n id\n title\n createdAt\n documentType\n versions(first: 1) {\n edges {\n node {\n id\n status\n }\n }\n }\n}\n\nfragment LinkedMeasuresCardFragment on Measure {\n id\n name\n state\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "d5c8f5ce17bd227c83fec31d63a82262";
|
||||
(node as any).hash = "ac5240126dfbf4882ba2b111e865045d";
|
||||
|
||||
export default node;
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from "@probo/ui";
|
||||
import { useTranslate } from "@probo/i18n";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { FrameworkLogo } from "/components/FrameworkLogo";
|
||||
import { ControlledField } from "/components/form/ControlledField";
|
||||
import { useFormWithSchema } from "/hooks/useFormWithSchema";
|
||||
import z from "zod";
|
||||
@@ -135,7 +136,10 @@ export default function AuditDetailsPage(props: Props) {
|
||||
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="text-2xl">{auditEntry.framework?.name}</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<FrameworkLogo name={auditEntry.framework?.name || ""} />
|
||||
<div className="text-2xl">{auditEntry.framework?.name}</div>
|
||||
</div>
|
||||
<Badge variant={getAuditStateVariant(auditEntry.state || "NOT_STARTED")}>
|
||||
{getAuditStateLabel(__, auditEntry.state || "NOT_STARTED")}
|
||||
</Badge>
|
||||
|
||||
@@ -17,6 +17,7 @@ import { LinkedMeasuresCard } from "/components/measures/LinkedMeasuresCard";
|
||||
import { useNavigate, useOutletContext } from "react-router";
|
||||
import { useOrganizationId } from "/hooks/useOrganizationId";
|
||||
import { LinkedDocumentsCard } from "/components/documents/LinkedDocumentsCard";
|
||||
import { LinkedAuditsCard } from "/components/audits/LinkedAuditsCard";
|
||||
import { FrameworkControlDialog } from "./dialogs/FrameworkControlDialog";
|
||||
import { promisifyMutation } from "@probo/helpers";
|
||||
import type { FrameworkGraphControlNodeQuery } from "/hooks/graph/__generated__/FrameworkGraphControlNodeQuery.graphql";
|
||||
@@ -77,6 +78,33 @@ const detachDocumentMutation = graphql`
|
||||
}
|
||||
`;
|
||||
|
||||
const attachAuditMutation = graphql`
|
||||
mutation FrameworkControlPageAttachAuditMutation(
|
||||
$input: CreateControlAuditMappingInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
createControlAuditMapping(input: $input) {
|
||||
auditEdge @prependEdge(connections: $connections) {
|
||||
node {
|
||||
id
|
||||
...LinkedAuditsCardFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const detachAuditMutation = graphql`
|
||||
mutation FrameworkControlPageDetachAuditMutation(
|
||||
$input: DeleteControlAuditMappingInput!
|
||||
$connections: [ID!]!
|
||||
) {
|
||||
deleteControlAuditMapping(input: $input) {
|
||||
deletedAuditId @deleteEdge(connections: $connections)
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const deleteControlMutation = graphql`
|
||||
mutation FrameworkControlPageDeleteControlMutation(
|
||||
$input: DeleteControlInput!
|
||||
@@ -118,6 +146,8 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
const [attachDocument, isAttachingDocument] = useMutation(
|
||||
attachDocumentMutation
|
||||
);
|
||||
const [detachAudit, isDetachingAudit] = useMutation(detachAuditMutation);
|
||||
const [attachAudit, isAttachingAudit] = useMutation(attachAuditMutation);
|
||||
const [deleteControl] = useMutation(deleteControlMutation);
|
||||
|
||||
const onDelete = () => {
|
||||
@@ -204,7 +234,16 @@ export default function FrameworkControlPage({ queryRef }: Props) {
|
||||
onAttach={attachDocument}
|
||||
onDetach={detachDocument}
|
||||
disabled={isAttachingDocument || isDetachingDocument}
|
||||
/>
|
||||
/>
|
||||
<LinkedAuditsCard
|
||||
variant="card"
|
||||
audits={control.audits?.edges.map((edge) => edge.node) ?? []}
|
||||
params={{ controlId: control.id }}
|
||||
connectionId={control.audits?.__id!}
|
||||
onAttach={attachAudit}
|
||||
onDetach={detachAudit}
|
||||
disabled={isAttachingAudit || isDetachingAudit}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/**
|
||||
* @generated SignedSource<<c1f987833f73f19a66f044b28688f1dd>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
import { FragmentRefs } from "relay-runtime";
|
||||
export type CreateControlAuditMappingInput = {
|
||||
auditId: string;
|
||||
controlId: string;
|
||||
};
|
||||
export type FrameworkControlPageAttachAuditMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: CreateControlAuditMappingInput;
|
||||
};
|
||||
export type FrameworkControlPageAttachAuditMutation$data = {
|
||||
readonly createControlAuditMapping: {
|
||||
readonly auditEdge: {
|
||||
readonly node: {
|
||||
readonly id: string;
|
||||
readonly " $fragmentSpreads": FragmentRefs<"LinkedAuditsCardFragment">;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
export type FrameworkControlPageAttachAuditMutation = {
|
||||
response: FrameworkControlPageAttachAuditMutation$data;
|
||||
variables: FrameworkControlPageAttachAuditMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "id",
|
||||
"storageKey": null
|
||||
},
|
||||
v4 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "name",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FrameworkControlPageAttachAuditMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateControlAuditMappingPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createControlAuditMapping",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "auditEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"args": null,
|
||||
"kind": "FragmentSpread",
|
||||
"name": "LinkedAuditsCardFragment"
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "FrameworkControlPageAttachAuditMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "CreateControlAuditMappingPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "createControlAuditMapping",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "AuditEdge",
|
||||
"kind": "LinkedField",
|
||||
"name": "auditEdge",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Audit",
|
||||
"kind": "LinkedField",
|
||||
"name": "node",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "createdAt",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "state",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validFrom",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "validUntil",
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"concreteType": "Framework",
|
||||
"kind": "LinkedField",
|
||||
"name": "framework",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
(v4/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "prependEdge",
|
||||
"key": "",
|
||||
"kind": "LinkedHandle",
|
||||
"name": "auditEdge",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "23afa68a91d9ae6630123a4ff693a0ec",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FrameworkControlPageAttachAuditMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation FrameworkControlPageAttachAuditMutation(\n $input: CreateControlAuditMappingInput!\n) {\n createControlAuditMapping(input: $input) {\n auditEdge {\n node {\n id\n ...LinkedAuditsCardFragment\n }\n }\n }\n}\n\nfragment LinkedAuditsCardFragment on Audit {\n id\n name\n createdAt\n state\n validFrom\n validUntil\n framework {\n id\n name\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "0a9f52be5fb551ec55667b7d777391de";
|
||||
|
||||
export default node;
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @generated SignedSource<<f3f6c7f7f6e09f53b111e39ce4176b1c>>
|
||||
* @lightSyntaxTransform
|
||||
* @nogrep
|
||||
*/
|
||||
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { ConcreteRequest } from 'relay-runtime';
|
||||
export type DeleteControlAuditMappingInput = {
|
||||
auditId: string;
|
||||
controlId: string;
|
||||
};
|
||||
export type FrameworkControlPageDetachAuditMutation$variables = {
|
||||
connections: ReadonlyArray<string>;
|
||||
input: DeleteControlAuditMappingInput;
|
||||
};
|
||||
export type FrameworkControlPageDetachAuditMutation$data = {
|
||||
readonly deleteControlAuditMapping: {
|
||||
readonly deletedAuditId: string;
|
||||
};
|
||||
};
|
||||
export type FrameworkControlPageDetachAuditMutation = {
|
||||
response: FrameworkControlPageDetachAuditMutation$data;
|
||||
variables: FrameworkControlPageDetachAuditMutation$variables;
|
||||
};
|
||||
|
||||
const node: ConcreteRequest = (function(){
|
||||
var v0 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "connections"
|
||||
},
|
||||
v1 = {
|
||||
"defaultValue": null,
|
||||
"kind": "LocalArgument",
|
||||
"name": "input"
|
||||
},
|
||||
v2 = [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "input",
|
||||
"variableName": "input"
|
||||
}
|
||||
],
|
||||
v3 = {
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"kind": "ScalarField",
|
||||
"name": "deletedAuditId",
|
||||
"storageKey": null
|
||||
};
|
||||
return {
|
||||
"fragment": {
|
||||
"argumentDefinitions": [
|
||||
(v0/*: any*/),
|
||||
(v1/*: any*/)
|
||||
],
|
||||
"kind": "Fragment",
|
||||
"metadata": null,
|
||||
"name": "FrameworkControlPageDetachAuditMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteControlAuditMappingPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteControlAuditMapping",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/)
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
],
|
||||
"type": "Mutation",
|
||||
"abstractKey": null
|
||||
},
|
||||
"kind": "Request",
|
||||
"operation": {
|
||||
"argumentDefinitions": [
|
||||
(v1/*: any*/),
|
||||
(v0/*: any*/)
|
||||
],
|
||||
"kind": "Operation",
|
||||
"name": "FrameworkControlPageDetachAuditMutation",
|
||||
"selections": [
|
||||
{
|
||||
"alias": null,
|
||||
"args": (v2/*: any*/),
|
||||
"concreteType": "DeleteControlAuditMappingPayload",
|
||||
"kind": "LinkedField",
|
||||
"name": "deleteControlAuditMapping",
|
||||
"plural": false,
|
||||
"selections": [
|
||||
(v3/*: any*/),
|
||||
{
|
||||
"alias": null,
|
||||
"args": null,
|
||||
"filters": null,
|
||||
"handle": "deleteEdge",
|
||||
"key": "",
|
||||
"kind": "ScalarHandle",
|
||||
"name": "deletedAuditId",
|
||||
"handleArgs": [
|
||||
{
|
||||
"kind": "Variable",
|
||||
"name": "connections",
|
||||
"variableName": "connections"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"storageKey": null
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"cacheID": "0ec6a285e6ffd0057602e30ef8411e16",
|
||||
"id": null,
|
||||
"metadata": {},
|
||||
"name": "FrameworkControlPageDetachAuditMutation",
|
||||
"operationKind": "mutation",
|
||||
"text": "mutation FrameworkControlPageDetachAuditMutation(\n $input: DeleteControlAuditMappingInput!\n) {\n deleteControlAuditMapping(input: $input) {\n deletedAuditId\n }\n}\n"
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
(node as any).hash = "3c800764c7d5801bd228e8a61625bd75";
|
||||
|
||||
export default node;
|
||||
@@ -314,3 +314,109 @@ WHERE
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Audits) CountByControlID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
controlID gid.GID,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH audits_by_control AS (
|
||||
SELECT
|
||||
a.id,
|
||||
a.tenant_id
|
||||
FROM
|
||||
audits a
|
||||
INNER JOIN
|
||||
controls_audits ca ON a.id = ca.audit_id
|
||||
WHERE
|
||||
ca.control_id = @control_id
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
audits_by_control
|
||||
WHERE %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"control_id": controlID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
row := conn.QueryRow(ctx, q, args)
|
||||
|
||||
var count int
|
||||
if err := row.Scan(&count); err != nil {
|
||||
return 0, fmt.Errorf("cannot scan count: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (a *Audits) LoadByControlID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
controlID gid.GID,
|
||||
cursor *page.Cursor[AuditOrderField],
|
||||
) error {
|
||||
q := `
|
||||
WITH audits_by_control AS (
|
||||
SELECT
|
||||
a.id,
|
||||
a.tenant_id,
|
||||
a.name,
|
||||
a.organization_id,
|
||||
a.framework_id,
|
||||
a.report_id,
|
||||
a.valid_from,
|
||||
a.valid_until,
|
||||
a.state,
|
||||
a.show_on_trust_center,
|
||||
a.created_at,
|
||||
a.updated_at
|
||||
FROM
|
||||
audits a
|
||||
INNER JOIN
|
||||
controls_audits ca ON a.id = ca.audit_id
|
||||
WHERE
|
||||
ca.control_id = @control_id
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
organization_id,
|
||||
framework_id,
|
||||
report_id,
|
||||
valid_from,
|
||||
valid_until,
|
||||
state,
|
||||
show_on_trust_center,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
audits_by_control
|
||||
WHERE %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"control_id": controlID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query audits: %w", err)
|
||||
}
|
||||
|
||||
audits, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Audit])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect audits: %w", err)
|
||||
}
|
||||
|
||||
*a = audits
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -826,3 +826,114 @@ RETURNING
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controls) CountByAuditID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
auditID gid.GID,
|
||||
filter *ControlFilter,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH ctrl AS (
|
||||
SELECT
|
||||
c.id,
|
||||
c.tenant_id,
|
||||
c.search_vector
|
||||
FROM
|
||||
controls c
|
||||
INNER JOIN
|
||||
controls_audits ca ON c.id = ca.control_id
|
||||
WHERE
|
||||
ca.audit_id = @audit_id
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
ctrl
|
||||
WHERE %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"audit_id": auditID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
row := conn.QueryRow(ctx, q, args)
|
||||
|
||||
var count int
|
||||
if err := row.Scan(&count); err != nil {
|
||||
return 0, fmt.Errorf("cannot scan count: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (c *Controls) LoadByAuditID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
auditID gid.GID,
|
||||
cursor *page.Cursor[ControlOrderField],
|
||||
filter *ControlFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH ctrl AS (
|
||||
SELECT
|
||||
c.id,
|
||||
c.section_title,
|
||||
c.framework_id,
|
||||
c.tenant_id,
|
||||
c.name,
|
||||
c.description,
|
||||
c.status,
|
||||
c.exclusion_justification,
|
||||
c.created_at,
|
||||
c.updated_at,
|
||||
c.search_vector
|
||||
FROM
|
||||
controls c
|
||||
INNER JOIN
|
||||
controls_audits ca ON c.id = ca.control_id
|
||||
WHERE
|
||||
ca.audit_id = @audit_id
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
section_title,
|
||||
framework_id,
|
||||
tenant_id,
|
||||
name,
|
||||
description,
|
||||
status,
|
||||
exclusion_justification,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
ctrl
|
||||
WHERE %s
|
||||
AND %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"audit_id": auditID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query controls: %w", err)
|
||||
}
|
||||
|
||||
controls, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Control])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect controls: %w", err)
|
||||
}
|
||||
|
||||
*c = controls
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
168
pkg/coredata/control_audit.go
Normal file
168
pkg/coredata/control_audit.go
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package coredata
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/getprobo/probo/pkg/gid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
)
|
||||
|
||||
type (
|
||||
ControlAudit struct {
|
||||
ControlID gid.GID `db:"control_id"`
|
||||
AuditID gid.GID `db:"audit_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
ControlAudits []*ControlAudit
|
||||
)
|
||||
|
||||
func (ca ControlAudit) Upsert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO
|
||||
controls_audits (
|
||||
control_id,
|
||||
audit_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
@control_id,
|
||||
@audit_id,
|
||||
@tenant_id,
|
||||
@created_at
|
||||
)
|
||||
ON CONFLICT (control_id, audit_id) DO NOTHING;
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"control_id": ca.ControlID,
|
||||
"audit_id": ca.AuditID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"created_at": ca.CreatedAt,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (ca ControlAudit) Delete(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
controlID gid.GID,
|
||||
auditID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
DELETE
|
||||
FROM
|
||||
controls_audits
|
||||
WHERE
|
||||
%s
|
||||
AND control_id = @control_id
|
||||
AND audit_id = @audit_id;
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"control_id": controlID,
|
||||
"audit_id": auditID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cas *ControlAudits) LoadByControlID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
controlID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
control_id,
|
||||
audit_id,
|
||||
created_at
|
||||
FROM
|
||||
controls_audits
|
||||
WHERE
|
||||
%s
|
||||
AND control_id = @control_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"control_id": controlID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query control_audits: %w", err)
|
||||
}
|
||||
|
||||
controlAudits, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ControlAudit])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect control_audits: %w", err)
|
||||
}
|
||||
|
||||
*cas = controlAudits
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cas *ControlAudits) LoadByAuditID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
auditID gid.GID,
|
||||
) error {
|
||||
q := `
|
||||
SELECT
|
||||
control_id,
|
||||
audit_id,
|
||||
created_at
|
||||
FROM
|
||||
controls_audits
|
||||
WHERE
|
||||
%s
|
||||
AND audit_id = @audit_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"audit_id": auditID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query control_audits: %w", err)
|
||||
}
|
||||
|
||||
controlAudits, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[ControlAudit])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect control_audits: %w", err)
|
||||
}
|
||||
|
||||
*cas = controlAudits
|
||||
return nil
|
||||
}
|
||||
7
pkg/coredata/migrations/20250814T123441Z.sql
Normal file
7
pkg/coredata/migrations/20250814T123441Z.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE controls_audits (
|
||||
control_id TEXT NOT NULL REFERENCES controls(id),
|
||||
audit_id TEXT NOT NULL REFERENCES audits(id),
|
||||
tenant_id TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
PRIMARY KEY (control_id, audit_id)
|
||||
);
|
||||
@@ -346,3 +346,34 @@ func (s AuditService) DeleteReport(
|
||||
|
||||
return audit, nil
|
||||
}
|
||||
|
||||
func (s AuditService) ListForControlID(
|
||||
ctx context.Context,
|
||||
controlID gid.GID,
|
||||
cursor *page.Cursor[coredata.AuditOrderField],
|
||||
) (*page.Page[*coredata.Audit, coredata.AuditOrderField], error) {
|
||||
var audits coredata.Audits
|
||||
control := &coredata.Control{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := control.LoadByID(ctx, conn, s.svc.scope, controlID); err != nil {
|
||||
return fmt.Errorf("cannot load control: %w", err)
|
||||
}
|
||||
|
||||
err := audits.LoadByControlID(ctx, conn, s.svc.scope, control.ID, cursor)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load audits: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage(audits, cursor), nil
|
||||
}
|
||||
|
||||
@@ -492,6 +492,111 @@ func (s ControlService) DeleteDocumentMapping(
|
||||
return control, document, nil
|
||||
}
|
||||
|
||||
func (s ControlService) CreateAuditMapping(
|
||||
ctx context.Context,
|
||||
controlID gid.GID,
|
||||
auditID gid.GID,
|
||||
) (*coredata.Control, *coredata.Audit, error) {
|
||||
controlAudit := &coredata.ControlAudit{
|
||||
ControlID: controlID,
|
||||
AuditID: auditID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
control := &coredata.Control{}
|
||||
audit := &coredata.Audit{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := control.LoadByID(ctx, conn, s.svc.scope, controlID); err != nil {
|
||||
return fmt.Errorf("cannot load control: %w", err)
|
||||
}
|
||||
|
||||
if err := audit.LoadByID(ctx, conn, s.svc.scope, auditID); err != nil {
|
||||
return fmt.Errorf("cannot load audit: %w", err)
|
||||
}
|
||||
|
||||
if err := controlAudit.Upsert(ctx, conn, s.svc.scope); err != nil {
|
||||
return fmt.Errorf("cannot create control audit mapping: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return control, audit, nil
|
||||
}
|
||||
|
||||
func (s ControlService) DeleteAuditMapping(
|
||||
ctx context.Context,
|
||||
controlID gid.GID,
|
||||
auditID gid.GID,
|
||||
) (*coredata.Control, *coredata.Audit, error) {
|
||||
control := &coredata.Control{}
|
||||
audit := &coredata.Audit{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := control.LoadByID(ctx, conn, s.svc.scope, controlID); err != nil {
|
||||
return fmt.Errorf("cannot load control: %w", err)
|
||||
}
|
||||
|
||||
if err := audit.LoadByID(ctx, conn, s.svc.scope, auditID); err != nil {
|
||||
return fmt.Errorf("cannot load audit: %w", err)
|
||||
}
|
||||
|
||||
controlAudit := &coredata.ControlAudit{}
|
||||
if err := controlAudit.Delete(ctx, conn, s.svc.scope, control.ID, audit.ID); err != nil {
|
||||
return fmt.Errorf("cannot delete control audit mapping: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot delete control audit mapping: %w", err)
|
||||
}
|
||||
|
||||
return control, audit, nil
|
||||
}
|
||||
|
||||
func (s ControlService) ListForAuditID(
|
||||
ctx context.Context,
|
||||
auditID gid.GID,
|
||||
cursor *page.Cursor[coredata.ControlOrderField],
|
||||
filter *coredata.ControlFilter,
|
||||
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
|
||||
var controls coredata.Controls
|
||||
audit := &coredata.Audit{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := audit.LoadByID(ctx, conn, s.svc.scope, auditID); err != nil {
|
||||
return fmt.Errorf("cannot load audit: %w", err)
|
||||
}
|
||||
if err := controls.LoadByAuditID(ctx, conn, s.svc.scope, auditID, cursor, filter); err != nil {
|
||||
return fmt.Errorf("cannot load controls: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage([]*coredata.Control(controls), cursor), nil
|
||||
}
|
||||
|
||||
func (s ControlService) Create(
|
||||
ctx context.Context,
|
||||
req CreateControlRequest,
|
||||
|
||||
@@ -1076,6 +1076,14 @@ type Control implements Node {
|
||||
filter: DocumentFilter
|
||||
): DocumentConnection! @goField(forceResolver: true)
|
||||
|
||||
audits(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: AuditOrder
|
||||
): AuditConnection! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -1257,6 +1265,16 @@ type Audit implements Node {
|
||||
report: Report @goField(forceResolver: true)
|
||||
reportUrl: String @goField(forceResolver: true)
|
||||
state: AuditState!
|
||||
|
||||
controls(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: ControlOrder
|
||||
filter: ControlFilter
|
||||
): ControlConnection! @goField(forceResolver: true)
|
||||
|
||||
showOnTrustCenter: Boolean!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
@@ -1636,6 +1654,12 @@ type Mutation {
|
||||
deleteControlDocumentMapping(
|
||||
input: DeleteControlDocumentMappingInput!
|
||||
): DeleteControlDocumentMappingPayload!
|
||||
createControlAuditMapping(
|
||||
input: CreateControlAuditMappingInput!
|
||||
): CreateControlAuditMappingPayload!
|
||||
deleteControlAuditMapping(
|
||||
input: DeleteControlAuditMappingInput!
|
||||
): DeleteControlAuditMappingPayload!
|
||||
|
||||
# Task mutations
|
||||
createTask(input: CreateTaskInput!): CreateTaskPayload!
|
||||
@@ -1987,6 +2011,16 @@ input DeleteControlDocumentMappingInput {
|
||||
documentId: ID!
|
||||
}
|
||||
|
||||
input CreateControlAuditMappingInput {
|
||||
controlId: ID!
|
||||
auditId: ID!
|
||||
}
|
||||
|
||||
input DeleteControlAuditMappingInput {
|
||||
controlId: ID!
|
||||
auditId: ID!
|
||||
}
|
||||
|
||||
input CreateRiskInput {
|
||||
organizationId: ID!
|
||||
name: String!
|
||||
@@ -2352,6 +2386,16 @@ type DeleteControlDocumentMappingPayload {
|
||||
deletedDocumentId: ID!
|
||||
}
|
||||
|
||||
type CreateControlAuditMappingPayload {
|
||||
controlEdge: ControlEdge!
|
||||
auditEdge: AuditEdge!
|
||||
}
|
||||
|
||||
type DeleteControlAuditMappingPayload {
|
||||
deletedControlId: ID!
|
||||
deletedAuditId: ID!
|
||||
}
|
||||
|
||||
type CreateRiskPayload {
|
||||
riskEdge: RiskEdge!
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -66,6 +66,7 @@ type Audit struct {
|
||||
Report *Report `json:"report,omitempty"`
|
||||
ReportURL *string `json:"reportUrl,omitempty"`
|
||||
State coredata.AuditState `json:"state"`
|
||||
Controls *ControlConnection `json:"controls"`
|
||||
ShowOnTrustCenter bool `json:"showOnTrustCenter"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
@@ -150,6 +151,7 @@ type Control struct {
|
||||
Framework *Framework `json:"framework"`
|
||||
Measures *MeasureConnection `json:"measures"`
|
||||
Documents *DocumentConnection `json:"documents"`
|
||||
Audits *AuditConnection `json:"audits"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -194,6 +196,16 @@ type CreateAuditPayload struct {
|
||||
AuditEdge *AuditEdge `json:"auditEdge"`
|
||||
}
|
||||
|
||||
type CreateControlAuditMappingInput struct {
|
||||
ControlID gid.GID `json:"controlId"`
|
||||
AuditID gid.GID `json:"auditId"`
|
||||
}
|
||||
|
||||
type CreateControlAuditMappingPayload struct {
|
||||
ControlEdge *ControlEdge `json:"controlEdge"`
|
||||
AuditEdge *AuditEdge `json:"auditEdge"`
|
||||
}
|
||||
|
||||
type CreateControlDocumentMappingInput struct {
|
||||
ControlID gid.GID `json:"controlId"`
|
||||
DocumentID gid.GID `json:"documentId"`
|
||||
@@ -473,6 +485,16 @@ type DeleteAuditReportPayload struct {
|
||||
Audit *Audit `json:"audit"`
|
||||
}
|
||||
|
||||
type DeleteControlAuditMappingInput struct {
|
||||
ControlID gid.GID `json:"controlId"`
|
||||
AuditID gid.GID `json:"auditId"`
|
||||
}
|
||||
|
||||
type DeleteControlAuditMappingPayload struct {
|
||||
DeletedControlID gid.GID `json:"deletedControlId"`
|
||||
DeletedAuditID gid.GID `json:"deletedAuditId"`
|
||||
}
|
||||
|
||||
type DeleteControlDocumentMappingInput struct {
|
||||
ControlID gid.GID `json:"controlId"`
|
||||
DocumentID gid.GID `json:"documentId"`
|
||||
|
||||
@@ -179,6 +179,36 @@ func (r *auditResolver) ReportURL(ctx context.Context, obj *types.Audit) (*strin
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// Controls is the resolver for the controls field.
|
||||
func (r *auditResolver) Controls(ctx context.Context, obj *types.Audit, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
|
||||
Field: coredata.ControlOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
var controlFilter = coredata.NewControlFilter(nil)
|
||||
if filter != nil {
|
||||
controlFilter = coredata.NewControlFilter(filter.Query)
|
||||
}
|
||||
|
||||
page, err := prb.Controls.ListForAuditID(ctx, obj.ID, cursor, controlFilter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot list audit controls: %w", err)
|
||||
}
|
||||
|
||||
return types.NewControlConnection(page, r, obj.ID, controlFilter), nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
func (r *auditConnectionResolver) TotalCount(ctx context.Context, obj *types.AuditConnection) (int, error) {
|
||||
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
||||
@@ -267,6 +297,31 @@ func (r *controlResolver) Documents(ctx context.Context, obj *types.Control, fir
|
||||
return types.NewDocumentConnection(page, r, obj.ID, documentFilter), nil
|
||||
}
|
||||
|
||||
// Audits is the resolver for the audits field.
|
||||
func (r *controlResolver) Audits(ctx context.Context, obj *types.Control, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AuditOrderBy) (*types.AuditConnection, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.AuditOrderField]{
|
||||
Field: coredata.AuditOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.AuditOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := prb.Audits.ListForControlID(ctx, obj.ID, cursor)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot list control audits: %w", err)
|
||||
}
|
||||
|
||||
return types.NewAuditConnection(page, r, obj.ID), nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
func (r *controlConnectionResolver) TotalCount(ctx context.Context, obj *types.ControlConnection) (int, error) {
|
||||
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
||||
@@ -1598,6 +1653,36 @@ func (r *mutationResolver) DeleteControlDocumentMapping(ctx context.Context, inp
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateControlAuditMapping is the resolver for the createControlAuditMapping field.
|
||||
func (r *mutationResolver) CreateControlAuditMapping(ctx context.Context, input types.CreateControlAuditMappingInput) (*types.CreateControlAuditMappingPayload, error) {
|
||||
prb := r.ProboService(ctx, input.AuditID.TenantID())
|
||||
|
||||
control, audit, err := prb.Controls.CreateAuditMapping(ctx, input.ControlID, input.AuditID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create control audit mapping: %w", err)
|
||||
}
|
||||
|
||||
return &types.CreateControlAuditMappingPayload{
|
||||
ControlEdge: types.NewControlEdge(control, coredata.ControlOrderFieldCreatedAt),
|
||||
AuditEdge: types.NewAuditEdge(audit, coredata.AuditOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteControlAuditMapping is the resolver for the deleteControlAuditMapping field.
|
||||
func (r *mutationResolver) DeleteControlAuditMapping(ctx context.Context, input types.DeleteControlAuditMappingInput) (*types.DeleteControlAuditMappingPayload, error) {
|
||||
prb := r.ProboService(ctx, input.AuditID.TenantID())
|
||||
|
||||
control, audit, err := prb.Controls.DeleteAuditMapping(ctx, input.ControlID, input.AuditID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot delete control audit mapping: %w", err)
|
||||
}
|
||||
|
||||
return &types.DeleteControlAuditMappingPayload{
|
||||
DeletedControlID: control.ID,
|
||||
DeletedAuditID: audit.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateTask is the resolver for the createTask field.
|
||||
func (r *mutationResolver) CreateTask(ctx context.Context, input types.CreateTaskInput) (*types.CreateTaskPayload, error) {
|
||||
prb := r.ProboService(ctx, input.MeasureID.TenantID())
|
||||
|
||||
Reference in New Issue
Block a user