Add type to document

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-05-30 09:15:49 -07:00
parent e1b4079e1f
commit 2391b19db3
17 changed files with 7433 additions and 6257 deletions

View File

@@ -45,6 +45,13 @@ import {
import PeopleSelector from "@/components/PeopleSelector";
import type { PeopleSelector_organization$key } from "@/components/__generated__/PeopleSelector_organization.graphql";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const documentListViewQuery = graphql`
query DocumentListViewQuery($organizationId: ID!) {
@@ -64,6 +71,7 @@ const documentListViewQuery = graphql`
id
title
description
documentType
currentPublishedVersion
createdAt
updatedAt
@@ -229,6 +237,17 @@ function DocumentTableRow({
</span>
</div>
</td>
<td className="py-4 px-6">
<Badge
className={`font-medium border-0 py-0 px-[6px] h-5 text-xs rounded-md bg-secondary-bg text-tertiary`}
>
{document.documentType === "POLICY"
? "Policy"
: document.documentType === "ISMS"
? "ISMS"
: "Other"}
</Badge>
</td>
<td className="py-4 px-6">
<span className="text-sm text-primary">
{document.owner?.fullName || "Unassigned"}
@@ -306,6 +325,7 @@ function CreateDocumentModal({
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const [ownerId, setOwnerId] = useState<string | null>(null);
const [documentType, setDocumentType] = useState<"OTHER" | "ISMS" | "POLICY">("POLICY");
const [createDocument, isCreating] =
useMutation<DocumentListViewCreateMutation>(createDocumentMutation);
@@ -341,6 +361,7 @@ function CreateDocumentModal({
title,
content,
ownerId,
documentType,
};
createDocument({
@@ -443,7 +464,9 @@ function CreateDocumentModal({
></h1>
</div>
<Textarea
placeholder="This Privacy Document outlines how NovaSoft collects, uses, and protects personal information provided by users of its services. By accessing or using our platform, you agree to the collection and use of information in accordance with this document..."
placeholder="
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur euismod ligula pretium elit rhoncus, ut tincidunt ligula luctus. Sed pulvinar porta blandit. Praesent odio ligula, tristique a rutrum non, imperdiet ut urna. Aenean at metus vitae diam lacinia auctor. Nullam fermentum volutpat magna. Curabitur tincidunt mauris sit amet velit lacinia..."
className="min-h-[300px] border-none resize-none p-0 focus-visible:ring-0 focus-visible:ring-offset-0"
value={content}
onChange={(e) => setContent(e.target.value)}
@@ -454,14 +477,28 @@ function CreateDocumentModal({
<div className="flex flex-col gap-4">
<div className="flex justify-between items-center py-2 border-t border-[rgba(2,42,2,0.08)]">
<span className="text-sm font-medium text-tertiary">
Status
</span>
<span className="text-sm font-medium text-tertiary">Status</span>
<div className="bg-[rgba(0,39,0,0.05)] py-1.5 px-2 rounded-lg">
<span className="text-sm font-medium">Draft</span>
</div>
</div>
<div className="flex justify-between items-center py-2 border-t border-[rgba(2,42,2,0.08)]">
<span className="text-sm font-medium text-tertiary">Document Type</span>
<div className="relative">
<Select value={documentType} onValueChange={(value: "OTHER" | "ISMS" | "POLICY") => setDocumentType(value)}>
<SelectTrigger className="w-[180px]">
<SelectValue placeholder="Select type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="POLICY">Policy</SelectItem>
<SelectItem value="ISMS">ISMS</SelectItem>
<SelectItem value="OTHER">Other</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="flex justify-between items-center py-2 border-t border-[rgba(2,42,2,0.08)]">
<span className="text-sm font-medium text-tertiary">Owner</span>
<div className="relative">
@@ -604,6 +641,12 @@ function DocumentListViewContent({
<ChevronDown className="h-3 w-3 text-quaternary" />
</div>
</th>
<th className="py-3 px-6 text-xs font-medium text-tertiary border-b border-low-b">
<div className="flex items-center gap-1">
Type
<ChevronDown className="h-3 w-3 text-quaternary" />
</div>
</th>
<th className="py-3 px-6 text-xs font-medium text-tertiary border-b border-low-b">
<div className="flex items-center gap-1">
Owner

View File

@@ -16,6 +16,7 @@ import {
MoreHorizontal,
X,
FileSignature,
User,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
@@ -23,6 +24,9 @@ import { PageTemplate } from "@/components/PageTemplate";
import type { ShowDocumentViewQuery } from "./__generated__/ShowDocumentViewQuery.graphql";
import { ShowDocumentViewPublishMutation } from "./__generated__/ShowDocumentViewPublishMutation.graphql";
import { ShowDocumentViewCreateDraftMutation } from "./__generated__/ShowDocumentViewCreateDraftMutation.graphql";
import { ShowDocumentViewUpdateDocumentMutation } from "./__generated__/ShowDocumentViewUpdateDocumentMutation.graphql";
import { ShowDocumentViewUpdateDocumentTypeMutation } from "./__generated__/ShowDocumentViewUpdateDocumentTypeMutation.graphql";
import type { DocumentType } from "./__generated__/DocumentListViewCreateMutation.graphql";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { ShowDocumentViewSkeleton } from "./ShowDocumentPage";
@@ -47,12 +51,15 @@ import rehypeRaw from "rehype-raw";
import { createRoot } from "react-dom/client";
import { documentVersionsFragment } from "./SignaturesModal";
import type { SignaturesModal_documentVersions$key } from "./__generated__/SignaturesModal_documentVersions.graphql";
import PeopleSelector from "@/components/PeopleSelector";
import { Label } from "@/components/ui/label";
const documentViewQuery = graphql`
query ShowDocumentViewQuery($documentId: ID!, $organizationId: ID!) {
organization: node(id: $organizationId) {
... on Organization {
name
...PeopleSelector_organization
}
}
@@ -64,6 +71,7 @@ const documentViewQuery = graphql`
createdAt
updatedAt
currentPublishedVersion
documentType
owner {
id
fullName
@@ -130,6 +138,22 @@ const createDraftDocumentVersionMutation = graphql`
}
`;
const updateDocumentMutation = graphql`
mutation ShowDocumentViewUpdateDocumentMutation($input: UpdateDocumentInput!) {
updateDocument(input: $input) {
document {
id
documentType
owner {
id
fullName
primaryEmailAddress
}
}
}
}
`;
function ShowDocumentContent({
queryRef,
}: {
@@ -150,6 +174,8 @@ function ShowDocumentContent({
const [isDeleting, setIsDeleting] = useState(false);
const [isVersionHistoryOpen, setIsVersionHistoryOpen] = useState(false);
const [isSignaturesModalOpen, setIsSignaturesModalOpen] = useState(false);
const [isEditingOwner, setIsEditingOwner] = useState(false);
const [isEditingType, setIsEditingType] = useState(false);
const printContentRef = useRef<HTMLDivElement>(null);
const [publishDraft, isPublishInFlight] =
@@ -158,12 +184,96 @@ function ShowDocumentContent({
useMutation<ShowDocumentViewCreateDraftMutation>(
createDraftDocumentVersionMutation,
);
const [updateDocument, isUpdatingDocument] =
useMutation<ShowDocumentViewUpdateDocumentMutation>(updateDocumentMutation);
const latestVersionEdge = documentValue.latestVersion?.edges[0];
const latestVersionNode = latestVersionEdge?.node;
const isDraft = latestVersionNode?.status === "DRAFT";
// Handle owner update
const handleOwnerUpdate = useCallback((newOwnerId: string) => {
if (!documentValue.id) return;
updateDocument({
variables: {
input: {
id: documentValue.id,
title: documentValue.title,
content: documentValue.latestVersion?.edges[0]?.node?.content || '',
ownerId: newOwnerId,
},
},
onCompleted: (_, errors) => {
if (errors) {
toast({
title: "Error updating owner",
description: errors[0]?.message || "An unknown error occurred",
variant: "destructive",
});
return;
}
toast({
title: "Success",
description: "Document owner updated successfully",
});
setIsEditingOwner(false);
// Reload the query to refresh the data
loadQuery({ documentId: documentValue.id, organizationId: organizationId! });
},
onError: (error) => {
toast({
title: "Error updating owner",
description: error.message || "An unknown error occurred",
variant: "destructive",
});
},
});
}, [documentValue.id, updateDocument, toast, loadQuery, organizationId]);
// Handle document type update
const handleTypeUpdate = useCallback((newType: DocumentType) => {
if (!documentValue.id) return;
updateDocument({
variables: {
input: {
id: documentValue.id,
documentType: newType,
},
},
onCompleted: (_, errors) => {
if (errors) {
toast({
title: "Error updating document type",
description: errors[0]?.message || "An unknown error occurred",
variant: "destructive",
});
return;
}
toast({
title: "Success",
description: "Document type updated successfully",
});
setIsEditingType(false);
// Reload the query to refresh the data
loadQuery({ documentId: documentValue.id, organizationId: organizationId! });
},
onError: (error) => {
toast({
title: "Error updating document type",
description: error.message || "An unknown error occurred",
variant: "destructive",
});
},
});
}, [documentValue.id, updateDocument, toast, loadQuery, organizationId]);
useEffect(() => {
// No need to update selectedVersion state since we're using the VersionHistoryModal component
}, []);
@@ -560,7 +670,77 @@ function ShowDocumentContent({
<span className="font-medium">Published Date:</span> {latestVersionNode.status === "PUBLISHED" ? formatDate(latestVersionNode.publishedAt || "") : "Not yet published"}
</div>
<div>
<span className="font-medium">Owner:</span> {documentValue.owner?.fullName || "Unknown"}
<div className="flex items-center gap-2">
<span className="font-medium">Owner:</span>
{isEditingOwner ? (
<div className="flex items-center gap-2">
<div className="w-[350px]">
<PeopleSelector
organizationRef={data.organization}
selectedPersonId={documentValue.owner?.id || ""}
onSelect={handleOwnerUpdate}
placeholder="Select document owner"
required={true}
/>
</div>
<Button
variant="outline"
size="sm"
onClick={() => setIsEditingOwner(false)}
disabled={isUpdatingDocument}
>
Cancel
</Button>
</div>
) : (
<div className="flex items-center gap-2">
<span>{documentValue.owner?.fullName || "Unknown"}</span>
<Button
variant="outline"
size="sm"
onClick={() => setIsEditingOwner(true)}
>
Change
</Button>
</div>
)}
</div>
</div>
<div>
<div className="flex items-center gap-2">
<span className="font-medium">Document Type:</span>
{isEditingType ? (
<div className="flex items-center gap-2">
<select
className="h-9 w-[200px] rounded-md border border-input bg-background px-3 py-1 text-sm shadow-sm transition-colors"
value={ documentValue.documentType }
onChange={(e) => handleTypeUpdate(e.target.value as DocumentType)}
>
<option value="POLICY">Policy</option>
<option value="ISMS">ISMS</option>
<option value="OTHER">Other</option>
</select>
<Button
variant="outline"
size="sm"
onClick={() => setIsEditingType(false)}
>
Cancel
</Button>
</div>
) : (
<div className="flex items-center gap-2">
<span>{ documentValue.documentType === "ISMS" ? "ISMS" : documentValue.documentType === "POLICY" ? "Policy" : "Other" }</span>
<Button
variant="outline"
size="sm"
onClick={() => setIsEditingType(true)}
>
Change
</Button>
</div>
)}
</div>
</div>
<div>
<span className="font-medium">Last Modified:</span> {formatDate(latestVersionNode.updatedAt)}

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<095491df1cce828cc3ec9ba9f2ad4183>>
* @generated SignedSource<<83ccc4aaa1b2380bfc185c4a6dd8806a>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,8 +9,10 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type CreateDocumentInput = {
content: string;
documentType: DocumentType;
organizationId: string;
ownerId: string;
title: string;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<4ae1fa075cd3c3fd2ae9d63e3ec0ae02>>
* @generated SignedSource<<2a0020cb72b5e7296b156bfd4d10cd35>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -11,6 +11,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
export type DocumentListViewQuery$variables = {
organizationId: string;
@@ -23,6 +24,7 @@ export type DocumentListViewQuery$data = {
readonly createdAt: string;
readonly currentPublishedVersion: number | null | undefined;
readonly description: string;
readonly documentType: DocumentType;
readonly id: string;
readonly owner: {
readonly fullName: string;
@@ -195,6 +197,13 @@ v11 = [
"name": "description",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -519,7 +528,7 @@ return {
]
},
"params": {
"cacheID": "6b91a4a77bedb9f66abeaeec8c1c5535",
"cacheID": "fae30dd5b946e2e95ddc19c2956cdf3f",
"id": null,
"metadata": {
"connection": [
@@ -536,11 +545,11 @@ return {
},
"name": "DocumentListViewQuery",
"operationKind": "query",
"text": "query DocumentListViewQuery(\n $organizationId: ID!\n) {\n viewer {\n user {\n id\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...PeopleSelector_organization\n documents(first: 50, orderBy: {field: TITLE, direction: ASC}) {\n edges {\n node {\n id\n title\n description\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
"text": "query DocumentListViewQuery(\n $organizationId: ID!\n) {\n viewer {\n user {\n id\n }\n id\n }\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n ...PeopleSelector_organization\n documents(first: 50, orderBy: {field: TITLE, direction: ASC}) {\n edges {\n node {\n id\n title\n description\n documentType\n currentPublishedVersion\n createdAt\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n updatedAt\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n id\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n"
}
};
})();
(node as any).hash = "df33668bcdfe69a122a8846bb1d66df1";
(node as any).hash = "4ed6ed2633b4c113bc88e19ac1db96b5";
export default node;

View File

@@ -0,0 +1,114 @@
/**
* @generated SignedSource<<6501e136d1171aaca8238084a7606a9e>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type UpdateDocumentVersionInput = {
content: string;
documentVersionId: string;
};
export type EditDocumentViewVersionMutation$variables = {
input: UpdateDocumentVersionInput;
};
export type EditDocumentViewVersionMutation$data = {
readonly updateDocumentVersion: {
readonly documentVersion: {
readonly content: string;
readonly id: string;
};
};
};
export type EditDocumentViewVersionMutation = {
response: EditDocumentViewVersionMutation$data;
variables: EditDocumentViewVersionMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "UpdateDocumentVersionPayload",
"kind": "LinkedField",
"name": "updateDocumentVersion",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "DocumentVersion",
"kind": "LinkedField",
"name": "documentVersion",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "content",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "EditDocumentViewVersionMutation",
"selections": (v1/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "EditDocumentViewVersionMutation",
"selections": (v1/*: any*/)
},
"params": {
"cacheID": "aa2a1c45efb2771987bf777971585e92",
"id": null,
"metadata": {},
"name": "EditDocumentViewVersionMutation",
"operationKind": "mutation",
"text": "mutation EditDocumentViewVersionMutation(\n $input: UpdateDocumentVersionInput!\n) {\n updateDocumentVersion(input: $input) {\n documentVersion {\n id\n content\n }\n }\n}\n"
}
};
})();
(node as any).hash = "656a8d74e00bb9e15e42987775bb1d14";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<f7c8f2df8a102d1bd15cb053d146a8b1>>
* @generated SignedSource<<bf950636055ed83dd68cd6e513a4d83e>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -11,6 +11,7 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type ShowDocumentViewQuery$variables = {
documentId: string;
organizationId: string;
@@ -20,6 +21,7 @@ export type ShowDocumentViewQuery$data = {
readonly createdAt?: string;
readonly currentPublishedVersion?: number | null | undefined;
readonly description?: string;
readonly documentType?: DocumentType;
readonly id: string;
readonly latestVersion?: {
readonly edges: ReadonlyArray<{
@@ -49,6 +51,7 @@ export type ShowDocumentViewQuery$data = {
};
readonly organization: {
readonly name?: string;
readonly " $fragmentSpreads": FragmentRefs<"PeopleSelector_organization">;
};
};
export type ShowDocumentViewQuery = {
@@ -77,18 +80,11 @@ v1 = [
}
],
v2 = {
"kind": "InlineFragment",
"selections": [
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
}
],
"type": "Organization",
"abstractKey": null
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
v3 = [
{
@@ -143,10 +139,24 @@ v10 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"name": "documentType",
"storageKey": null
},
v11 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
v12 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
},
v13 = {
"alias": null,
"args": null,
"concreteType": "People",
@@ -155,86 +165,124 @@ v11 = {
"plural": false,
"selections": [
(v4/*: any*/),
(v10/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
(v11/*: any*/),
(v12/*: any*/)
],
"storageKey": null
},
v12 = [
v14 = [
{
"kind": "Literal",
"name": "first",
"value": 1
}
],
v13 = {
v15 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "version",
"storageKey": null
},
v14 = {
v16 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "status",
"storageKey": null
},
v15 = {
v17 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "content",
"storageKey": null
},
v16 = {
v18 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "changelog",
"storageKey": null
},
v17 = {
v19 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "publishedAt",
"storageKey": null
},
v18 = {
v20 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v19 = [
(v10/*: any*/),
v21 = {
"kind": "Literal",
"name": "first",
"value": 100
},
v22 = [
(v21/*: any*/),
{
"kind": "Literal",
"name": "orderBy",
"value": {
"direction": "ASC",
"field": "FULL_NAME"
}
}
],
v23 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v24 = {
"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
}
],
"storageKey": null
},
v25 = [
(v11/*: any*/),
(v4/*: any*/)
],
v20 = {
v26 = {
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "publishedBy",
"plural": false,
"selections": (v19/*: any*/),
"selections": (v25/*: any*/),
"storageKey": null
},
v21 = [
{
"kind": "Literal",
"name": "first",
"value": 100
}
v27 = [
(v21/*: any*/)
];
return {
"fragment": {
@@ -251,7 +299,19 @@ return {
"name": "node",
"plural": false,
"selections": [
(v2/*: any*/)
{
"kind": "InlineFragment",
"selections": [
(v2/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
"name": "PeopleSelector_organization"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
},
@@ -272,7 +332,8 @@ return {
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v11/*: any*/),
(v10/*: any*/),
(v13/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -285,7 +346,7 @@ return {
},
{
"alias": "latestVersion",
"args": (v12/*: any*/),
"args": (v14/*: any*/),
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
@@ -308,11 +369,11 @@ return {
"plural": false,
"selections": [
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
(v19/*: any*/),
{
"alias": null,
"args": null,
@@ -321,7 +382,7 @@ return {
"name": "publishedBy",
"plural": false,
"selections": [
(v10/*: any*/)
(v11/*: any*/)
],
"storageKey": null
},
@@ -361,9 +422,66 @@ return {
"name": "node",
"plural": false,
"selections": [
(v18/*: any*/),
(v2/*: any*/),
(v4/*: any*/)
(v20/*: any*/),
(v4/*: any*/),
{
"kind": "InlineFragment",
"selections": [
(v2/*: any*/),
{
"alias": null,
"args": (v22/*: any*/),
"concreteType": "PeopleConnection",
"kind": "LinkedField",
"name": "peoples",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "PeopleEdge",
"kind": "LinkedField",
"name": "edges",
"plural": true,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "node",
"plural": false,
"selections": [
(v4/*: any*/),
(v11/*: any*/),
(v12/*: any*/),
(v20/*: any*/)
],
"storageKey": null
},
(v23/*: any*/)
],
"storageKey": null
},
(v24/*: any*/)
],
"storageKey": "peoples(first:100,orderBy:{\"direction\":\"ASC\",\"field\":\"FULL_NAME\"})"
},
{
"alias": null,
"args": (v22/*: any*/),
"filters": [
"orderBy"
],
"handle": "connection",
"key": "PeopleSelector_organization_peoples",
"kind": "LinkedHandle",
"name": "peoples"
}
],
"type": "Organization",
"abstractKey": null
}
],
"storageKey": null
},
@@ -375,7 +493,7 @@ return {
"name": "node",
"plural": false,
"selections": [
(v18/*: any*/),
(v20/*: any*/),
(v4/*: any*/),
{
"kind": "InlineFragment",
@@ -385,7 +503,8 @@ return {
(v7/*: any*/),
(v8/*: any*/),
(v9/*: any*/),
(v11/*: any*/),
(v10/*: any*/),
(v13/*: any*/),
{
"alias": "documentVersions",
"args": [
@@ -417,14 +536,14 @@ return {
"plural": false,
"selections": [
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v17/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v19/*: any*/),
(v8/*: any*/),
(v20/*: any*/),
(v26/*: any*/),
{
"alias": null,
"args": (v21/*: any*/),
"args": (v27/*: any*/),
"concreteType": "DocumentVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
@@ -475,7 +594,7 @@ return {
"kind": "LinkedField",
"name": "signedBy",
"plural": false,
"selections": (v19/*: any*/),
"selections": (v25/*: any*/),
"storageKey": null
},
{
@@ -485,54 +604,24 @@ return {
"kind": "LinkedField",
"name": "requestedBy",
"plural": false,
"selections": (v19/*: any*/),
"selections": (v25/*: any*/),
"storageKey": null
},
(v18/*: any*/)
(v20/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
}
(v23/*: any*/)
],
"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
}
],
"storageKey": null
}
(v24/*: any*/)
],
"storageKey": "signatures(first:100)"
},
{
"alias": null,
"args": (v21/*: any*/),
"args": (v27/*: any*/),
"filters": null,
"handle": "connection",
"key": "SignaturesModal_documentVersions_signatures",
@@ -579,13 +668,13 @@ return {
"plural": false,
"selections": [
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v18/*: any*/),
(v19/*: any*/),
(v8/*: any*/),
(v20/*: any*/)
(v26/*: any*/)
],
"storageKey": null
}
@@ -597,7 +686,7 @@ return {
},
{
"alias": "latestVersion",
"args": (v12/*: any*/),
"args": (v14/*: any*/),
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
@@ -620,12 +709,12 @@ return {
"plural": false,
"selections": [
(v4/*: any*/),
(v13/*: any*/),
(v14/*: any*/),
(v15/*: any*/),
(v16/*: any*/),
(v17/*: any*/),
(v20/*: any*/),
(v18/*: any*/),
(v19/*: any*/),
(v26/*: any*/),
(v7/*: any*/),
(v8/*: any*/)
],
@@ -647,16 +736,16 @@ return {
]
},
"params": {
"cacheID": "16dd0ba75fa5a1339b59fa3e6bb9a6be",
"cacheID": "a1b25325fb4a764d130253f86435efcb",
"id": null,
"metadata": {},
"name": "ShowDocumentViewQuery",
"operationKind": "query",
"text": "query ShowDocumentViewQuery(\n $documentId: ID!\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n name\n }\n id\n }\n node(id: $documentId) {\n __typename\n id\n ... on Document {\n title\n description\n createdAt\n updatedAt\n currentPublishedVersion\n owner {\n id\n fullName\n primaryEmailAddress\n }\n ...SignaturesModal_documentVersions\n ...VersionHistoryModal_documentVersions\n latestVersion: versions(first: 1) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n publishedBy {\n fullName\n id\n }\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n}\n\nfragment SignaturesModal_documentVersions on Document {\n title\n documentVersions: versions(first: 10) {\n edges {\n node {\n id\n version\n status\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n id\n }\n requestedBy {\n fullName\n id\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment VersionHistoryModal_documentVersions on Document {\n title\n owner {\n fullName\n id\n }\n versionHistory: versions(first: 20) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n }\n }\n }\n}\n"
"text": "query ShowDocumentViewQuery(\n $documentId: ID!\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n ... on Organization {\n name\n ...PeopleSelector_organization\n }\n id\n }\n node(id: $documentId) {\n __typename\n id\n ... on Document {\n title\n description\n createdAt\n updatedAt\n currentPublishedVersion\n documentType\n owner {\n id\n fullName\n primaryEmailAddress\n }\n ...SignaturesModal_documentVersions\n ...VersionHistoryModal_documentVersions\n latestVersion: versions(first: 1) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n publishedBy {\n fullName\n id\n }\n createdAt\n updatedAt\n }\n }\n }\n }\n }\n}\n\nfragment PeopleSelector_organization on Organization {\n id\n peoples(first: 100, orderBy: {direction: ASC, field: FULL_NAME}) {\n edges {\n node {\n id\n fullName\n primaryEmailAddress\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment SignaturesModal_documentVersions on Document {\n title\n documentVersions: versions(first: 10) {\n edges {\n node {\n id\n version\n status\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n signatures(first: 100) {\n edges {\n node {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n id\n }\n requestedBy {\n fullName\n id\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n }\n }\n }\n}\n\nfragment VersionHistoryModal_documentVersions on Document {\n title\n owner {\n fullName\n id\n }\n versionHistory: versions(first: 20) {\n edges {\n node {\n id\n version\n status\n content\n changelog\n publishedAt\n updatedAt\n publishedBy {\n fullName\n id\n }\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "df52189b2fac349ac8588c518a4fce55";
(node as any).hash = "96c84ddc6ada95da2302068bae68f825";
export default node;

View File

@@ -0,0 +1,151 @@
/**
* @generated SignedSource<<f884c800d6c27056d14eec073a481151>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type UpdateDocumentInput = {
content?: string | null | undefined;
createdBy?: string | null | undefined;
documentType?: DocumentType | null | undefined;
id: string;
ownerId?: string | null | undefined;
title?: string | null | undefined;
};
export type ShowDocumentViewUpdateDocumentMutation$variables = {
input: UpdateDocumentInput;
};
export type ShowDocumentViewUpdateDocumentMutation$data = {
readonly updateDocument: {
readonly document: {
readonly documentType: DocumentType;
readonly id: string;
readonly owner: {
readonly fullName: string;
readonly id: string;
readonly primaryEmailAddress: string;
};
};
};
};
export type ShowDocumentViewUpdateDocumentMutation = {
response: ShowDocumentViewUpdateDocumentMutation$data;
variables: ShowDocumentViewUpdateDocumentMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v2 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "UpdateDocumentPayload",
"kind": "LinkedField",
"name": "updateDocument",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Document",
"kind": "LinkedField",
"name": "document",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowDocumentViewUpdateDocumentMutation",
"selections": (v2/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowDocumentViewUpdateDocumentMutation",
"selections": (v2/*: any*/)
},
"params": {
"cacheID": "62cd235bd1903d80012364fbc3358054",
"id": null,
"metadata": {},
"name": "ShowDocumentViewUpdateDocumentMutation",
"operationKind": "mutation",
"text": "mutation ShowDocumentViewUpdateDocumentMutation(\n $input: UpdateDocumentInput!\n) {\n updateDocument(input: $input) {\n document {\n id\n documentType\n owner {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "094ff9193be9f023152588380f5114b5";
export default node;

View File

@@ -0,0 +1,151 @@
/**
* @generated SignedSource<<9752f2c7d1c495c0c21482fb6d881c9e>>
* @lightSyntaxTransform
* @nogrep
*/
/* tslint:disable */
/* eslint-disable */
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DocumentType = "ISMS" | "OTHER" | "POLICY";
export type UpdateDocumentInput = {
content?: string | null | undefined;
createdBy?: string | null | undefined;
documentType?: DocumentType | null | undefined;
id: string;
ownerId?: string | null | undefined;
title?: string | null | undefined;
};
export type ShowDocumentViewUpdateDocumentTypeMutation$variables = {
input: UpdateDocumentInput;
};
export type ShowDocumentViewUpdateDocumentTypeMutation$data = {
readonly updateDocument: {
readonly document: {
readonly documentType: DocumentType;
readonly id: string;
readonly owner: {
readonly fullName: string;
readonly id: string;
readonly primaryEmailAddress: string;
};
};
};
};
export type ShowDocumentViewUpdateDocumentTypeMutation = {
response: ShowDocumentViewUpdateDocumentTypeMutation$data;
variables: ShowDocumentViewUpdateDocumentTypeMutation$variables;
};
const node: ConcreteRequest = (function(){
var v0 = [
{
"defaultValue": null,
"kind": "LocalArgument",
"name": "input"
}
],
v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "id",
"storageKey": null
},
v2 = [
{
"alias": null,
"args": [
{
"kind": "Variable",
"name": "input",
"variableName": "input"
}
],
"concreteType": "UpdateDocumentPayload",
"kind": "LinkedField",
"name": "updateDocument",
"plural": false,
"selections": [
{
"alias": null,
"args": null,
"concreteType": "Document",
"kind": "LinkedField",
"name": "document",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v1/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "primaryEmailAddress",
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
],
"storageKey": null
}
];
return {
"fragment": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Fragment",
"metadata": null,
"name": "ShowDocumentViewUpdateDocumentTypeMutation",
"selections": (v2/*: any*/),
"type": "Mutation",
"abstractKey": null
},
"kind": "Request",
"operation": {
"argumentDefinitions": (v0/*: any*/),
"kind": "Operation",
"name": "ShowDocumentViewUpdateDocumentTypeMutation",
"selections": (v2/*: any*/)
},
"params": {
"cacheID": "4904ae0647cf1c01126766096adf2b4b",
"id": null,
"metadata": {},
"name": "ShowDocumentViewUpdateDocumentTypeMutation",
"operationKind": "mutation",
"text": "mutation ShowDocumentViewUpdateDocumentTypeMutation(\n $input: UpdateDocumentInput!\n) {\n updateDocument(input: $input) {\n document {\n id\n documentType\n owner {\n id\n fullName\n primaryEmailAddress\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "5d04382a57a6020b7e8c80e615e1b32a";
export default node;

View File

@@ -28,13 +28,14 @@ import (
type (
Document struct {
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
OwnerID gid.GID `db:"owner_id"`
Title string `db:"title"`
CurrentPublishedVersion *int `db:"current_published_version"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
OrganizationID gid.GID `db:"organization_id"`
OwnerID gid.GID `db:"owner_id"`
Title string `db:"title"`
DocumentType DocumentType `db:"document_type"`
CurrentPublishedVersion *int `db:"current_published_version"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Documents []*Document
@@ -63,6 +64,7 @@ SELECT
organization_id,
owner_id,
title,
document_type,
current_published_version,
created_at,
updated_at
@@ -107,6 +109,7 @@ SELECT
organization_id,
owner_id,
title,
document_type,
current_published_version,
created_at,
updated_at
@@ -152,6 +155,7 @@ INSERT INTO
organization_id,
owner_id,
title,
document_type,
current_published_version,
created_at,
updated_at
@@ -162,6 +166,7 @@ VALUES (
@organization_id,
@owner_id,
@title,
@document_type,
@current_published_version,
@created_at,
@updated_at
@@ -174,6 +179,7 @@ VALUES (
"organization_id": p.OrganizationID,
"owner_id": p.OwnerID,
"title": p.Title,
"document_type": p.DocumentType,
"current_published_version": p.CurrentPublishedVersion,
"created_at": p.CreatedAt,
"updated_at": p.UpdatedAt,
@@ -212,6 +218,7 @@ SET
title = @title,
current_published_version = @current_published_version,
owner_id = @owner_id,
document_type = @document_type,
updated_at = @updated_at
WHERE %s
AND id = @document_id
@@ -224,6 +231,7 @@ WHERE %s
"title": p.Title,
"current_published_version": p.CurrentPublishedVersion,
"owner_id": p.OwnerID,
"document_type": p.DocumentType,
}
maps.Copy(args, scope.SQLArguments())
@@ -250,6 +258,7 @@ WITH plcs AS (
p.organization_id,
p.owner_id,
p.title,
p.document_type,
p.current_published_version,
p.created_at,
p.updated_at
@@ -265,6 +274,7 @@ SELECT
organization_id,
owner_id,
title,
document_type,
current_published_version,
created_at,
updated_at
@@ -309,6 +319,7 @@ WITH plcs AS (
p.organization_id,
p.owner_id,
p.title,
p.document_type,
p.current_published_version,
p.created_at,
p.updated_at
@@ -324,6 +335,7 @@ SELECT
organization_id,
owner_id,
title,
document_type,
current_published_version,
created_at,
updated_at

View File

@@ -0,0 +1,68 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package coredata
import (
"database/sql/driver"
"fmt"
)
type (
DocumentType string
)
const (
DocumentTypeOther DocumentType = "OTHER"
DocumentTypeISMS DocumentType = "ISMS"
DocumentTypePolicy DocumentType = "POLICY"
)
func (dt DocumentType) MarshalText() ([]byte, error) {
return []byte(dt.String()), nil
}
func (dt *DocumentType) UnmarshalText(data []byte) error {
val := string(data)
switch val {
case DocumentTypeOther.String():
*dt = DocumentTypeOther
case DocumentTypeISMS.String():
*dt = DocumentTypeISMS
case DocumentTypePolicy.String():
*dt = DocumentTypePolicy
default:
return fmt.Errorf("invalid DocumentType value: %q", val)
}
return nil
}
func (dt DocumentType) String() string {
return string(dt)
}
func (dt *DocumentType) Scan(value any) error {
val, ok := value.(string)
if !ok {
return fmt.Errorf("invalid scan source for DocumentType, expected string got %T", value)
}
return dt.UnmarshalText([]byte(val))
}
func (dt DocumentType) Value() (driver.Value, error) {
return dt.String(), nil
}

View File

@@ -0,0 +1,3 @@
CREATE TYPE document_type AS ENUM ('OTHER', 'ISMS', 'POLICY');
ALTER TABLE documents ADD COLUMN document_type document_type NOT NULL DEFAULT 'POLICY';

View File

@@ -25,6 +25,7 @@ type (
Content string
OwnerID gid.GID
CreatedBy gid.GID
DocumentType coredata.DocumentType
}
UpdateDocumentVersionRequest struct {
@@ -131,10 +132,11 @@ func (s *DocumentService) Create(
people := &coredata.People{}
document := &coredata.Document{
ID: documentID,
Title: req.Title,
CreatedAt: now,
UpdatedAt: now,
ID: documentID,
Title: req.Title,
DocumentType: req.DocumentType,
CreatedAt: now,
UpdatedAt: now,
}
documentVersion := &coredata.DocumentVersion{
@@ -632,3 +634,47 @@ func (s *DocumentService) ListForRiskID(
return page.NewPage(documents, cursor), nil
}
func (s *DocumentService) Update(
ctx context.Context,
documentID gid.GID,
newOwnerID *gid.GID,
documentType *coredata.DocumentType,
) (*coredata.Document, error) {
document := &coredata.Document{}
people := &coredata.People{}
now := time.Now()
err := s.svc.pg.WithTx(
ctx,
func(tx pg.Conn) error {
if err := document.LoadByID(ctx, tx, s.svc.scope, documentID); err != nil {
return fmt.Errorf("cannot load document %q: %w", documentID, err)
}
if newOwnerID != nil {
if err := people.LoadByID(ctx, tx, s.svc.scope, *newOwnerID); err != nil {
return fmt.Errorf("cannot load new owner %q: %w", *newOwnerID, err)
}
document.OwnerID = *newOwnerID
}
if documentType != nil {
document.DocumentType = *documentType
}
document.UpdatedAt = now
if err := document.Update(ctx, tx, s.svc.scope); err != nil {
return fmt.Errorf("cannot update document: %w", err)
}
return nil
},
)
if err != nil {
return nil, err
}
return document, nil
}

View File

@@ -337,6 +337,12 @@ enum VendorCategory @goModel(model: "github.com/getprobo/probo/pkg/coredata.Vend
VERSION_CONTROL @goEnum(value: "github.com/getprobo/probo/pkg/coredata.VendorCategoryVersionControl")
}
enum DocumentType @goModel(model: "github.com/getprobo/probo/pkg/coredata.DocumentType") {
OTHER @goEnum(value: "github.com/getprobo/probo/pkg/coredata.DocumentTypeOther")
ISMS @goEnum(value: "github.com/getprobo/probo/pkg/coredata.DocumentTypeISMS")
POLICY @goEnum(value: "github.com/getprobo/probo/pkg/coredata.DocumentTypePolicy")
}
# Order Input Types
input UserOrder
@goModel(
@@ -752,6 +758,7 @@ type Document implements Node {
id: ID!
title: String!
description: String!
documentType: DocumentType!
currentPublishedVersion: Int
owner: People! @goField(forceResolver: true)
organization: Organization! @goField(forceResolver: true)
@@ -1096,6 +1103,7 @@ type Mutation {
# Document mutations
createDocument(input: CreateDocumentInput!): CreateDocumentPayload!
updateDocument(input: UpdateDocumentInput!): UpdateDocumentPayload!
deleteDocument(input: DeleteDocumentInput!): DeleteDocumentPayload!
publishDocumentVersion(
input: PublishDocumentVersionInput!
@@ -1396,6 +1404,7 @@ input CreateDocumentInput {
title: String!
content: String!
ownerId: ID!
documentType: DocumentType!
}
input UpdateDocumentInput {
@@ -1404,6 +1413,7 @@ input UpdateDocumentInput {
content: String
ownerId: ID
createdBy: ID
documentType: DocumentType
}
input DeleteDocumentInput {

File diff suppressed because it is too large Load Diff

View File

@@ -46,6 +46,7 @@ func NewDocument(document *coredata.Document) *Document {
return &Document{
ID: document.ID,
Title: document.Title,
DocumentType: document.DocumentType,
CurrentPublishedVersion: document.CurrentPublishedVersion,
CreatedAt: document.CreatedAt,
UpdatedAt: document.UpdatedAt,

View File

@@ -73,15 +73,15 @@ type ConnectorOrder struct {
}
type Control struct {
ID gid.GID `json:"id"`
ReferenceID string `json:"referenceId"`
Name string `json:"name"`
Description string `json:"description"`
Framework *Framework `json:"framework"`
Measures *MeasureConnection `json:"measures"`
Documents *DocumentConnection `json:"documents"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID gid.GID `json:"id"`
ReferenceID string `json:"referenceId"`
Name string `json:"name"`
Description string `json:"description"`
Framework *Framework `json:"framework"`
Measures *MeasureConnection `json:"measures"`
Documents *DocumentConnection `json:"documents"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (Control) IsNode() {}
@@ -97,6 +97,16 @@ type ControlEdge struct {
Node *Control `json:"node"`
}
type CreateControlDocumentMappingInput struct {
ControlID gid.GID `json:"controlId"`
DocumentID gid.GID `json:"documentId"`
}
type CreateControlDocumentMappingPayload struct {
ControlEdge *ControlEdge `json:"controlEdge"`
DocumentEdge *DocumentEdge `json:"documentEdge"`
}
type CreateControlMeasureMappingInput struct {
ControlID gid.GID `json:"controlId"`
MeasureID gid.GID `json:"measureId"`
@@ -107,14 +117,17 @@ type CreateControlMeasureMappingPayload struct {
MeasureEdge *MeasureEdge `json:"measureEdge"`
}
type CreateControlDocumentMappingInput struct {
ControlID gid.GID `json:"controlId"`
DocumentID gid.GID `json:"documentId"`
type CreateDocumentInput struct {
OrganizationID gid.GID `json:"organizationId"`
Title string `json:"title"`
Content string `json:"content"`
OwnerID gid.GID `json:"ownerId"`
DocumentType coredata.DocumentType `json:"documentType"`
}
type CreateControlDocumentMappingPayload struct {
ControlEdge *ControlEdge `json:"controlEdge"`
DocumentEdge *DocumentEdge `json:"documentEdge"`
type CreateDocumentPayload struct {
DocumentEdge *DocumentEdge `json:"documentEdge"`
DocumentVersionEdge *DocumentVersionEdge `json:"documentVersionEdge"`
}
type CreateDraftDocumentVersionInput struct {
@@ -182,16 +195,14 @@ type CreatePeoplePayload struct {
PeopleEdge *PeopleEdge `json:"peopleEdge"`
}
type CreateDocumentInput struct {
OrganizationID gid.GID `json:"organizationId"`
Title string `json:"title"`
Content string `json:"content"`
OwnerID gid.GID `json:"ownerId"`
type CreateRiskDocumentMappingInput struct {
RiskID gid.GID `json:"riskId"`
DocumentID gid.GID `json:"documentId"`
}
type CreateDocumentPayload struct {
DocumentEdge *DocumentEdge `json:"documentEdge"`
DocumentVersionEdge *DocumentVersionEdge `json:"documentVersionEdge"`
type CreateRiskDocumentMappingPayload struct {
RiskEdge *RiskEdge `json:"riskEdge"`
DocumentEdge *DocumentEdge `json:"documentEdge"`
}
type CreateRiskInput struct {
@@ -222,16 +233,6 @@ type CreateRiskPayload struct {
RiskEdge *RiskEdge `json:"riskEdge"`
}
type CreateRiskDocumentMappingInput struct {
RiskID gid.GID `json:"riskId"`
DocumentID gid.GID `json:"documentId"`
}
type CreateRiskDocumentMappingPayload struct {
RiskEdge *RiskEdge `json:"riskEdge"`
DocumentEdge *DocumentEdge `json:"documentEdge"`
}
type CreateTaskInput struct {
OrganizationID gid.GID `json:"organizationId"`
MeasureID *gid.GID `json:"measureId,omitempty"`
@@ -284,6 +285,16 @@ type CreateVendorRiskAssessmentPayload struct {
VendorRiskAssessmentEdge *VendorRiskAssessmentEdge `json:"vendorRiskAssessmentEdge"`
}
type DeleteControlDocumentMappingInput struct {
ControlID gid.GID `json:"controlId"`
DocumentID gid.GID `json:"documentId"`
}
type DeleteControlDocumentMappingPayload struct {
DeletedControlID gid.GID `json:"deletedControlId"`
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
type DeleteControlMeasureMappingInput struct {
ControlID gid.GID `json:"controlId"`
MeasureID gid.GID `json:"measureId"`
@@ -294,14 +305,12 @@ type DeleteControlMeasureMappingPayload struct {
DeletedMeasureID gid.GID `json:"deletedMeasureId"`
}
type DeleteControlDocumentMappingInput struct {
ControlID gid.GID `json:"controlId"`
DocumentID gid.GID `json:"documentId"`
type DeleteDocumentInput struct {
DocumentID gid.GID `json:"documentId"`
}
type DeleteControlDocumentMappingPayload struct {
DeletedControlID gid.GID `json:"deletedControlId"`
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
type DeleteDocumentPayload struct {
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
type DeleteEvidenceInput struct {
@@ -344,11 +353,13 @@ type DeletePeoplePayload struct {
DeletedPeopleID gid.GID `json:"deletedPeopleId"`
}
type DeleteDocumentInput struct {
type DeleteRiskDocumentMappingInput struct {
RiskID gid.GID `json:"riskId"`
DocumentID gid.GID `json:"documentId"`
}
type DeleteDocumentPayload struct {
type DeleteRiskDocumentMappingPayload struct {
DeletedRiskID gid.GID `json:"deletedRiskId"`
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
@@ -370,16 +381,6 @@ type DeleteRiskPayload struct {
DeletedRiskID gid.GID `json:"deletedRiskId"`
}
type DeleteRiskDocumentMappingInput struct {
RiskID gid.GID `json:"riskId"`
DocumentID gid.GID `json:"documentId"`
}
type DeleteRiskDocumentMappingPayload struct {
DeletedRiskID gid.GID `json:"deletedRiskId"`
DeletedDocumentID gid.GID `json:"deletedDocumentId"`
}
type DeleteTaskInput struct {
TaskID gid.GID `json:"taskId"`
}
@@ -404,6 +405,94 @@ type DeleteVendorPayload struct {
DeletedVendorID gid.GID `json:"deletedVendorId"`
}
type Document struct {
ID gid.GID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
DocumentType coredata.DocumentType `json:"documentType"`
CurrentPublishedVersion *int `json:"currentPublishedVersion,omitempty"`
Owner *People `json:"owner"`
Organization *Organization `json:"organization"`
Versions *DocumentVersionConnection `json:"versions"`
Controls *ControlConnection `json:"controls"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (Document) IsNode() {}
func (this Document) GetID() gid.GID { return this.ID }
type DocumentConnection struct {
Edges []*DocumentEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type DocumentEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *Document `json:"node"`
}
type DocumentVersion struct {
ID gid.GID `json:"id"`
Document *Document `json:"document"`
Status coredata.DocumentStatus `json:"status"`
Version int `json:"version"`
Content string `json:"content"`
Changelog string `json:"changelog"`
Signatures *DocumentVersionSignatureConnection `json:"signatures"`
PublishedBy *People `json:"publishedBy,omitempty"`
PublishedAt *time.Time `json:"publishedAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (DocumentVersion) IsNode() {}
func (this DocumentVersion) GetID() gid.GID { return this.ID }
type DocumentVersionConnection struct {
Edges []*DocumentVersionEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type DocumentVersionEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *DocumentVersion `json:"node"`
}
type DocumentVersionFilter struct {
Status *coredata.DocumentStatus `json:"status,omitempty"`
}
type DocumentVersionSignature struct {
ID gid.GID `json:"id"`
DocumentVersion *DocumentVersion `json:"documentVersion"`
State coredata.DocumentVersionSignatureState `json:"state"`
SignedBy *People `json:"signedBy"`
SignedAt *time.Time `json:"signedAt,omitempty"`
RequestedAt time.Time `json:"requestedAt"`
RequestedBy *People `json:"requestedBy"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (DocumentVersionSignature) IsNode() {}
func (this DocumentVersionSignature) GetID() gid.GID { return this.ID }
type DocumentVersionSignatureConnection struct {
Edges []*DocumentVersionSignatureEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type DocumentVersionSignatureEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *DocumentVersionSignature `json:"node"`
}
type DocumentVersionSignatureOrder struct {
Field coredata.DocumentVersionSignatureOrderField `json:"field"`
Direction page.OrderDirection `json:"direction"`
}
type Evidence struct {
ID gid.GID `json:"id"`
FileURL *string `json:"fileUrl,omitempty"`
@@ -542,7 +631,7 @@ type Organization struct {
Frameworks *FrameworkConnection `json:"frameworks"`
Vendors *VendorConnection `json:"vendors"`
Peoples *PeopleConnection `json:"peoples"`
Documents *DocumentConnection `json:"documents"`
Documents *DocumentConnection `json:"documents"`
Measures *MeasureConnection `json:"measures"`
Risks *RiskConnection `json:"risks"`
Tasks *TaskConnection `json:"tasks"`
@@ -601,93 +690,6 @@ type PeopleEdge struct {
Node *People `json:"node"`
}
type Document struct {
ID gid.GID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
CurrentPublishedVersion *int `json:"currentPublishedVersion,omitempty"`
Owner *People `json:"owner"`
Organization *Organization `json:"organization"`
Versions *DocumentVersionConnection `json:"versions"`
Controls *ControlConnection `json:"controls"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (Document) IsNode() {}
func (this Document) GetID() gid.GID { return this.ID }
type DocumentConnection struct {
Edges []*DocumentEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type DocumentEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *Document `json:"node"`
}
type DocumentVersion struct {
ID gid.GID `json:"id"`
Document *Document `json:"document"`
Status coredata.DocumentStatus `json:"status"`
Version int `json:"version"`
Content string `json:"content"`
Changelog string `json:"changelog"`
Signatures *DocumentVersionSignatureConnection `json:"signatures"`
PublishedBy *People `json:"publishedBy,omitempty"`
PublishedAt *time.Time `json:"publishedAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (DocumentVersion) IsNode() {}
func (this DocumentVersion) GetID() gid.GID { return this.ID }
type DocumentVersionConnection struct {
Edges []*DocumentVersionEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type DocumentVersionEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *DocumentVersion `json:"node"`
}
type DocumentVersionFilter struct {
Status *coredata.DocumentStatus `json:"status,omitempty"`
}
type DocumentVersionSignature struct {
ID gid.GID `json:"id"`
DocumentVersion *DocumentVersion `json:"documentVersion"`
State coredata.DocumentVersionSignatureState `json:"state"`
SignedBy *People `json:"signedBy"`
SignedAt *time.Time `json:"signedAt,omitempty"`
RequestedAt time.Time `json:"requestedAt"`
RequestedBy *People `json:"requestedBy"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (DocumentVersionSignature) IsNode() {}
func (this DocumentVersionSignature) GetID() gid.GID { return this.ID }
type DocumentVersionSignatureConnection struct {
Edges []*DocumentVersionSignatureEdge `json:"edges"`
PageInfo *PageInfo `json:"pageInfo"`
}
type DocumentVersionSignatureEdge struct {
Cursor page.CursorKey `json:"cursor"`
Node *DocumentVersionSignature `json:"node"`
}
type DocumentVersionSignatureOrder struct {
Field coredata.DocumentVersionSignatureOrderField `json:"field"`
Direction page.OrderDirection `json:"direction"`
}
type PublishDocumentVersionInput struct {
DocumentID gid.GID `json:"documentId"`
}
@@ -722,7 +724,7 @@ type RequestEvidencePayload struct {
type RequestSignatureInput struct {
DocumentVersionID gid.GID `json:"documentVersionId"`
SignatoryID gid.GID `json:"signatoryId"`
SignatoryID gid.GID `json:"signatoryId"`
}
type RequestSignaturePayload struct {
@@ -745,7 +747,7 @@ type Risk struct {
Owner *People `json:"owner,omitempty"`
Organization *Organization `json:"organization"`
Measures *MeasureConnection `json:"measures"`
Documents *DocumentConnection `json:"documents"`
Documents *DocumentConnection `json:"documents"`
Controls *ControlConnection `json:"controls"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
@@ -812,6 +814,28 @@ type UnassignTaskPayload struct {
Task *Task `json:"task"`
}
type UpdateDocumentInput struct {
ID gid.GID `json:"id"`
Title *string `json:"title,omitempty"`
Content *string `json:"content,omitempty"`
OwnerID *gid.GID `json:"ownerId,omitempty"`
CreatedBy *gid.GID `json:"createdBy,omitempty"`
DocumentType *coredata.DocumentType `json:"documentType,omitempty"`
}
type UpdateDocumentPayload struct {
Document *Document `json:"document"`
}
type UpdateDocumentVersionInput struct {
DocumentVersionID gid.GID `json:"documentVersionId"`
Content string `json:"content"`
}
type UpdateDocumentVersionPayload struct {
DocumentVersion *DocumentVersion `json:"documentVersion"`
}
type UpdateFrameworkInput struct {
ID gid.GID `json:"id"`
Name *string `json:"name,omitempty"`
@@ -859,27 +883,6 @@ type UpdatePeoplePayload struct {
People *People `json:"people"`
}
type UpdateDocumentInput struct {
ID gid.GID `json:"id"`
Title *string `json:"title,omitempty"`
Content *string `json:"content,omitempty"`
OwnerID *gid.GID `json:"ownerId,omitempty"`
CreatedBy *gid.GID `json:"createdBy,omitempty"`
}
type UpdateDocumentPayload struct {
Document *Document `json:"document"`
}
type UpdateDocumentVersionInput struct {
DocumentVersionID gid.GID `json:"documentVersionId"`
Content string `json:"content"`
}
type UpdateDocumentVersionPayload struct {
DocumentVersion *DocumentVersion `json:"documentVersion"`
}
type UpdateRiskInput struct {
ID gid.GID `json:"id"`
Name *string `json:"name,omitempty"`

View File

@@ -87,6 +87,205 @@ func (r *controlResolver) Documents(ctx context.Context, obj *types.Control, fir
return types.NewDocumentConnection(page), nil
}
// Owner is the resolver for the owner field.
func (r *documentResolver) Owner(ctx context.Context, obj *types.Document) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
document, err := svc.Documents.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document: %w", err))
}
// Get the owner
owner, err := svc.Peoples.Get(ctx, document.OwnerID)
if err != nil {
panic(fmt.Errorf("cannot get owner: %w", err))
}
return types.NewPeople(owner), nil
}
// Organization is the resolver for the organization field.
func (r *documentResolver) Organization(ctx context.Context, obj *types.Document) (*types.Organization, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
document, err := svc.Documents.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document: %w", err))
}
organization, err := svc.Organizations.Get(ctx, document.OrganizationID)
if err != nil {
panic(fmt.Errorf("cannot get organization: %w", err))
}
return types.NewOrganization(organization), nil
}
// Versions is the resolver for the versions field.
func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentVersionOrderField]{
Field: coredata.DocumentVersionOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DocumentVersionOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Documents.ListVersions(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list document versions: %w", err))
}
return types.NewDocumentVersionConnection(page), nil
}
// Controls is the resolver for the controls field.
func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, 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)
page, err := svc.Controls.ListForDocumentID(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list document controls: %w", err))
}
return types.NewControlConnection(page), nil
}
// Document is the resolver for the document field.
func (r *documentVersionResolver) Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersion, err := svc.Documents.GetVersion(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
document, err := svc.Documents.Get(ctx, documentVersion.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot get document: %w", err))
}
return types.NewDocument(document), nil
}
// Signatures is the resolver for the signatures field.
func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.DocumentVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionSignatureOrder) (*types.DocumentVersionSignatureConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: coredata.DocumentVersionSignatureOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Documents.ListSignatures(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list document version signatures: %w", err))
}
return types.NewDocumentVersionSignatureConnection(page), nil
}
// PublishedBy is the resolver for the publishedBy field.
func (r *documentVersionResolver) PublishedBy(ctx context.Context, obj *types.DocumentVersion) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersion, err := svc.Documents.GetVersion(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
if documentVersion.PublishedBy == nil {
return nil, nil
}
people, err := svc.Peoples.Get(ctx, *documentVersion.PublishedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
return types.NewPeople(people), nil
}
// DocumentVersion is the resolver for the documentVersion field.
func (r *documentVersionSignatureResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionSignature) (*types.DocumentVersion, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
documentVersion, err := svc.Documents.GetVersion(ctx, documentVersionSignature.DocumentVersionID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
return types.NewDocumentVersion(documentVersion), nil
}
// SignedBy is the resolver for the signedBy field.
func (r *documentVersionSignatureResolver) SignedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
people, err := svc.Peoples.Get(ctx, documentVersionSignature.SignedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
return types.NewPeople(people), nil
}
// RequestedBy is the resolver for the requestedBy field.
func (r *documentVersionSignatureResolver) RequestedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
people, err := svc.Peoples.Get(ctx, documentVersionSignature.RequestedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
return types.NewPeople(people), nil
}
// FileURL is the resolver for the fileUrl field.
func (r *evidenceResolver) FileURL(ctx context.Context, obj *types.Evidence) (*string, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
@@ -1153,6 +1352,7 @@ func (r *mutationResolver) CreateDocument(ctx context.Context, input types.Creat
ctx,
probo.CreateDocumentRequest{
OrganizationID: input.OrganizationID,
DocumentType: input.DocumentType,
Title: input.Title,
OwnerID: input.OwnerID,
Content: input.Content,
@@ -1169,6 +1369,26 @@ func (r *mutationResolver) CreateDocument(ctx context.Context, input types.Creat
}, nil
}
// UpdateDocument is the resolver for the updateDocument field.
func (r *mutationResolver) UpdateDocument(ctx context.Context, input types.UpdateDocumentInput) (*types.UpdateDocumentPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.ID.TenantID())
document, err := svc.Documents.Update(
ctx,
input.ID,
input.OwnerID,
input.DocumentType,
)
if err != nil {
return nil, fmt.Errorf("cannot update document: %w", err)
}
return &types.UpdateDocumentPayload{
Document: types.NewDocument(document),
}, nil
}
// DeleteDocument is the resolver for the deleteDocument field.
func (r *mutationResolver) DeleteDocument(ctx context.Context, input types.DeleteDocumentInput) (*types.DeleteDocumentPayload, error) {
svc := GetTenantService(ctx, r.proboSvc, input.DocumentID.TenantID())
@@ -1567,205 +1787,6 @@ func (r *organizationResolver) Tasks(ctx context.Context, obj *types.Organizatio
return types.NewTaskConnection(page), nil
}
// Owner is the resolver for the owner field.
func (r *documentResolver) Owner(ctx context.Context, obj *types.Document) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
document, err := svc.Documents.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document: %w", err))
}
// Get the owner
owner, err := svc.Peoples.Get(ctx, document.OwnerID)
if err != nil {
panic(fmt.Errorf("cannot get owner: %w", err))
}
return types.NewPeople(owner), nil
}
// Organization is the resolver for the organization field.
func (r *documentResolver) Organization(ctx context.Context, obj *types.Document) (*types.Organization, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
document, err := svc.Documents.Get(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document: %w", err))
}
organization, err := svc.Organizations.Get(ctx, document.OrganizationID)
if err != nil {
panic(fmt.Errorf("cannot get organization: %w", err))
}
return types.NewOrganization(organization), nil
}
// Versions is the resolver for the versions field.
func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentVersionOrderField]{
Field: coredata.DocumentVersionOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DocumentVersionOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Documents.ListVersions(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list document versions: %w", err))
}
return types.NewDocumentVersionConnection(page), nil
}
// Controls is the resolver for the controls field.
func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, 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)
page, err := svc.Controls.ListForDocumentID(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list document controls: %w", err))
}
return types.NewControlConnection(page), nil
}
// Document is the resolver for the document field.
func (r *documentVersionResolver) Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersion, err := svc.Documents.GetVersion(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
document, err := svc.Documents.Get(ctx, documentVersion.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot get document: %w", err))
}
return types.NewDocument(document), nil
}
// Signatures is the resolver for the signatures field.
func (r *documentVersionResolver) Signatures(ctx context.Context, obj *types.DocumentVersion, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionSignatureOrder) (*types.DocumentVersionSignatureConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: coredata.DocumentVersionSignatureOrderFieldCreatedAt,
Direction: page.OrderDirectionDesc,
}
if orderBy != nil {
pageOrderBy = page.OrderBy[coredata.DocumentVersionSignatureOrderField]{
Field: orderBy.Field,
Direction: orderBy.Direction,
}
}
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Documents.ListSignatures(ctx, obj.ID, cursor)
if err != nil {
panic(fmt.Errorf("cannot list document version signatures: %w", err))
}
return types.NewDocumentVersionSignatureConnection(page), nil
}
// PublishedBy is the resolver for the publishedBy field.
func (r *documentVersionResolver) PublishedBy(ctx context.Context, obj *types.DocumentVersion) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersion, err := svc.Documents.GetVersion(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
if documentVersion.PublishedBy == nil {
return nil, nil
}
people, err := svc.Peoples.Get(ctx, *documentVersion.PublishedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
return types.NewPeople(people), nil
}
// DocumentVersion is the resolver for the documentVersion field.
func (r *documentVersionSignatureResolver) DocumentVersion(ctx context.Context, obj *types.DocumentVersionSignature) (*types.DocumentVersion, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
documentVersion, err := svc.Documents.GetVersion(ctx, documentVersionSignature.DocumentVersionID)
if err != nil {
panic(fmt.Errorf("cannot get document version: %w", err))
}
return types.NewDocumentVersion(documentVersion), nil
}
// SignedBy is the resolver for the signedBy field.
func (r *documentVersionSignatureResolver) SignedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
people, err := svc.Peoples.Get(ctx, documentVersionSignature.SignedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
return types.NewPeople(people), nil
}
// RequestedBy is the resolver for the requestedBy field.
func (r *documentVersionSignatureResolver) RequestedBy(ctx context.Context, obj *types.DocumentVersionSignature) (*types.People, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
documentVersionSignature, err := svc.Documents.GetVersionSignature(ctx, obj.ID)
if err != nil {
panic(fmt.Errorf("cannot get document version signature: %w", err))
}
people, err := svc.Peoples.Get(ctx, documentVersionSignature.RequestedBy)
if err != nil {
panic(fmt.Errorf("cannot get people: %w", err))
}
return types.NewPeople(people), nil
}
// Node is the resolver for the node field.
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
svc := GetTenantService(ctx, r.proboSvc, id.TenantID())
@@ -2268,6 +2289,19 @@ func (r *viewerResolver) Organizations(ctx context.Context, obj *types.Viewer, f
// Control returns schema.ControlResolver implementation.
func (r *Resolver) Control() schema.ControlResolver { return &controlResolver{r} }
// Document returns schema.DocumentResolver implementation.
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
// DocumentVersion returns schema.DocumentVersionResolver implementation.
func (r *Resolver) DocumentVersion() schema.DocumentVersionResolver {
return &documentVersionResolver{r}
}
// DocumentVersionSignature returns schema.DocumentVersionSignatureResolver implementation.
func (r *Resolver) DocumentVersionSignature() schema.DocumentVersionSignatureResolver {
return &documentVersionSignatureResolver{r}
}
// Evidence returns schema.EvidenceResolver implementation.
func (r *Resolver) Evidence() schema.EvidenceResolver { return &evidenceResolver{r} }
@@ -2283,19 +2317,6 @@ func (r *Resolver) Mutation() schema.MutationResolver { return &mutationResolver
// Organization returns schema.OrganizationResolver implementation.
func (r *Resolver) Organization() schema.OrganizationResolver { return &organizationResolver{r} }
// Document returns schema.DocumentResolver implementation.
func (r *Resolver) Document() schema.DocumentResolver { return &documentResolver{r} }
// DocumentVersion returns schema.DocumentVersionResolver implementation.
func (r *Resolver) DocumentVersion() schema.DocumentVersionResolver {
return &documentVersionResolver{r}
}
// DocumentVersionSignature returns schema.DocumentVersionSignatureResolver implementation.
func (r *Resolver) DocumentVersionSignature() schema.DocumentVersionSignatureResolver {
return &documentVersionSignatureResolver{r}
}
// Query returns schema.QueryResolver implementation.
func (r *Resolver) Query() schema.QueryResolver { return &queryResolver{r} }
@@ -2325,14 +2346,14 @@ func (r *Resolver) VendorRiskAssessment() schema.VendorRiskAssessmentResolver {
func (r *Resolver) Viewer() schema.ViewerResolver { return &viewerResolver{r} }
type controlResolver struct{ *Resolver }
type documentResolver struct{ *Resolver }
type documentVersionResolver struct{ *Resolver }
type documentVersionSignatureResolver struct{ *Resolver }
type evidenceResolver struct{ *Resolver }
type frameworkResolver struct{ *Resolver }
type measureResolver struct{ *Resolver }
type mutationResolver struct{ *Resolver }
type organizationResolver struct{ *Resolver }
type documentResolver struct{ *Resolver }
type documentVersionResolver struct{ *Resolver }
type documentVersionSignatureResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type riskResolver struct{ *Resolver }
type taskResolver struct{ *Resolver }