Add document classification

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-10-22 16:23:30 +02:00
parent 36ef29299c
commit c7cabf23de
32 changed files with 891 additions and 229 deletions

2
.gitattributes vendored
View File

@@ -1 +1 @@
pkg/api/console/v1/v1_resolver.go linguist-generated=false
pkg/server/api/console/v1/v1_resolver.go linguist-generated=false

View File

@@ -0,0 +1,20 @@
import { useTranslate } from "@probo/i18n";
import { Option } from "@probo/ui";
import {
documentClassifications,
getDocumentClassificationLabel,
} from "@probo/helpers";
export function DocumentClassificationOptions() {
const { __ } = useTranslate();
return (
<>
{documentClassifications.map((classification) => (
<Option key={classification} value={classification}>
{getDocumentClassificationLabel(__, classification)}
</Option>
))}
</>
);
}

View File

@@ -6,12 +6,14 @@ export const documentSchema = z.object({
content: z.string(),
ownerId: z.string(),
documentType: z.enum(["OTHER", "ISMS", "POLICY", "PROCEDURE"]),
classification: z.enum(["PUBLIC", "INTERNAL", "CONFIDENTIAL", "SECRET"]),
});
export const useDocumentForm = () => {
return useFormWithSchema(documentSchema, {
defaultValues: {
documentType: "POLICY",
classification: "INTERNAL",
},
});
};

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<80c5f44dbb42b6501cd63d626d52518c>>
* @generated SignedSource<<42c3f270cfade27e94b4d0895a0641f5>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -161,6 +161,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -377,12 +384,12 @@ return {
]
},
"params": {
"cacheID": "e3e170d0a40aa1d0d4d07d9d414704ce",
"cacheID": "74570a2b122efea617c7e2559fb51053",
"id": null,
"metadata": {},
"name": "DocumentGraphListQuery",
"operationKind": "query",
"text": "query DocumentGraphListQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...DocumentsPageListFragment\n }\n}\n\nfragment DocumentsPageListFragment on Organization {\n documents(first: 50, orderBy: {field: TITLE, direction: ASC}) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "query DocumentGraphListQuery(\n $organizationId: ID!\n) {\n organization: node(id: $organizationId) {\n __typename\n id\n ...DocumentsPageListFragment\n }\n}\n\nfragment DocumentsPageListFragment on Organization {\n documents(first: 50, orderBy: {field: TITLE, direction: ASC}) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n classification\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<d4ad1b45197821a3ac5d59350a5646d8>>
* @generated SignedSource<<a39062565d1126fbcbee36865fa8c0fb>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -53,48 +53,68 @@ v3 = {
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
v5 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
},
v5 = [
v6 = {
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v3/*: any*/),
(v5/*: any*/)
],
"storageKey": null
},
v7 = [
{
"kind": "Literal",
"name": "first",
"value": 20
}
],
v6 = {
v8 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "name",
"storageKey": null
},
v7 = {
v9 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v8 = {
v10 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "endCursor",
"storageKey": null
},
v9 = {
v11 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "hasNextPage",
"storageKey": null
},
v10 = {
v12 = {
"kind": "ClientExtension",
"selections": [
{
@@ -106,14 +126,14 @@ v10 = {
}
]
},
v11 = [
v13 = [
{
"kind": "Literal",
"name": "first",
"value": 1000
}
],
v12 = {
v14 = {
"alias": null,
"args": null,
"concreteType": "PageInfo",
@@ -121,8 +141,8 @@ v12 = {
"name": "pageInfo",
"plural": false,
"selections": [
(v8/*: any*/),
(v9/*: any*/)
(v10/*: any*/),
(v11/*: any*/)
],
"storageKey": null
};
@@ -193,22 +213,11 @@ return {
"name": "documentType",
"storageKey": null
},
(v4/*: any*/),
(v6/*: any*/),
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/)
],
"storageKey": null
},
{
"alias": null,
"args": (v5/*: any*/),
"args": (v7/*: any*/),
"concreteType": "ControlConnection",
"kind": "LinkedField",
"name": "controls",
@@ -231,7 +240,7 @@ return {
"plural": false,
"selections": [
(v3/*: any*/),
(v6/*: any*/),
(v8/*: any*/),
{
"alias": null,
"args": null,
@@ -248,7 +257,7 @@ return {
"plural": false,
"selections": [
(v3/*: any*/),
(v6/*: any*/)
(v8/*: any*/)
],
"storageKey": null
},
@@ -256,7 +265,7 @@ return {
],
"storageKey": null
},
(v7/*: any*/)
(v9/*: any*/)
],
"storageKey": null
},
@@ -268,8 +277,8 @@ return {
"name": "pageInfo",
"plural": false,
"selections": [
(v8/*: any*/),
(v9/*: any*/),
(v10/*: any*/),
(v11/*: any*/),
{
"alias": null,
"args": null,
@@ -287,13 +296,13 @@ return {
],
"storageKey": null
},
(v10/*: any*/)
(v12/*: any*/)
],
"storageKey": "controls(first:20)"
},
{
"alias": null,
"args": (v5/*: any*/),
"args": (v7/*: any*/),
"filters": [
"orderBy",
"filter"
@@ -329,7 +338,7 @@ return {
},
{
"alias": null,
"args": (v5/*: any*/),
"args": (v7/*: any*/),
"concreteType": "DocumentVersionConnection",
"kind": "LinkedField",
"name": "versions",
@@ -387,9 +396,11 @@ return {
"name": "updatedAt",
"storageKey": null
},
(v4/*: any*/),
(v6/*: any*/),
{
"alias": null,
"args": (v11/*: any*/),
"args": (v13/*: any*/),
"concreteType": "DocumentVersionSignatureConnection",
"kind": "LinkedField",
"name": "signatures",
@@ -428,7 +439,7 @@ return {
"plural": false,
"selections": [
(v3/*: any*/),
(v4/*: any*/),
(v5/*: any*/),
{
"alias": null,
"args": null,
@@ -457,18 +468,18 @@ return {
],
"storageKey": null
},
(v7/*: any*/)
(v9/*: any*/)
],
"storageKey": null
},
(v12/*: any*/),
(v10/*: any*/)
(v14/*: any*/),
(v12/*: any*/)
],
"storageKey": "signatures(first:1000)"
},
{
"alias": null,
"args": (v11/*: any*/),
"args": (v13/*: any*/),
"filters": [
"filter"
],
@@ -479,7 +490,7 @@ return {
},
{
"alias": null,
"args": (v11/*: any*/),
"args": (v13/*: any*/),
"filters": [],
"handle": "connection",
"key": "DocumentDetailPage_signatures",
@@ -490,18 +501,18 @@ return {
],
"storageKey": null
},
(v7/*: any*/)
(v9/*: any*/)
],
"storageKey": null
},
(v12/*: any*/),
(v10/*: any*/)
(v14/*: any*/),
(v12/*: any*/)
],
"storageKey": "versions(first:20)"
},
{
"alias": null,
"args": (v5/*: any*/),
"args": (v7/*: any*/),
"filters": null,
"handle": "connection",
"key": "DocumentDetailPage_versions",
@@ -518,12 +529,12 @@ return {
]
},
"params": {
"cacheID": "4dd1a6c2a9117deb789984766f9c3b36",
"cacheID": "72db9b5c4f2a4cd076ebf4e45f0eb662",
"id": null,
"metadata": {},
"name": "DocumentGraphNodeQuery",
"operationKind": "query",
"text": "query DocumentGraphNodeQuery(\n $documentId: ID!\n) {\n node(id: $documentId) {\n __typename\n ... on Document {\n ...DocumentDetailPageDocumentFragment\n }\n id\n }\n}\n\nfragment DocumentControlsTabFragment on Document {\n id\n controls(first: 20) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment DocumentDetailPageDocumentFragment on Document {\n id\n title\n documentType\n owner {\n id\n fullName\n }\n ...DocumentControlsTabFragment\n controlsInfo: controls(first: 0) {\n totalCount\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n ...DocumentSignaturesTab_version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesTab_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentSignaturesTab_signature on DocumentVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n\nfragment DocumentSignaturesTab_version on DocumentVersion {\n id\n status\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n signedBy {\n id\n fullName\n primaryEmailAddress\n }\n ...DocumentSignaturesTab_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n"
"text": "query DocumentGraphNodeQuery(\n $documentId: ID!\n) {\n node(id: $documentId) {\n __typename\n ... on Document {\n ...DocumentDetailPageDocumentFragment\n }\n id\n }\n}\n\nfragment DocumentControlsTabFragment on Document {\n id\n controls(first: 20) {\n edges {\n node {\n id\n ...LinkedControlsCardFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n}\n\nfragment DocumentDetailPageDocumentFragment on Document {\n id\n title\n documentType\n classification\n owner {\n id\n fullName\n }\n ...DocumentControlsTabFragment\n controlsInfo: controls(first: 0) {\n totalCount\n }\n versions(first: 20) {\n edges {\n node {\n id\n content\n status\n publishedAt\n version\n updatedAt\n classification\n owner {\n id\n fullName\n }\n ...DocumentSignaturesTab_version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n signedBy {\n id\n }\n ...DocumentSignaturesTab_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment DocumentSignaturesTab_signature on DocumentVersionSignature {\n id\n state\n signedAt\n requestedAt\n signedBy {\n fullName\n primaryEmailAddress\n id\n }\n}\n\nfragment DocumentSignaturesTab_version on DocumentVersion {\n id\n status\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n signedBy {\n id\n fullName\n primaryEmailAddress\n }\n ...DocumentSignaturesTab_signature\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nfragment LinkedControlsCardFragment on Control {\n id\n name\n sectionTitle\n framework {\n id\n name\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<18b931f119e8ef68f016dfcdf76366cc>>
* @generated SignedSource<<383c2d6a9d25911bb5df8c148a8a3e51>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,9 +10,11 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type DocumentClassification = "CONFIDENTIAL" | "INTERNAL" | "PUBLIC" | "SECRET";
export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE";
export type TrustCenterVisibility = "NONE" | "PRIVATE" | "PUBLIC";
export type UpdateDocumentInput = {
classification?: DocumentClassification | null | undefined;
content?: string | null | undefined;
documentType?: DocumentType | null | undefined;
id: string;

View File

@@ -49,8 +49,10 @@ import { useOrganizationId } from "/hooks/useOrganizationId";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import {
getDocumentTypeLabel,
getDocumentClassificationLabel,
sprintf,
documentTypes,
documentClassifications,
formatDate,
} from "@probo/helpers";
import {
@@ -71,6 +73,7 @@ import clsx from "clsx";
import { PeopleSelectField } from "/components/form/PeopleSelectField";
import { ControlledField } from "/components/form/ControlledField";
import { DocumentTypeOptions } from "/components/form/DocumentTypeOptions";
import { DocumentClassificationOptions } from "/components/form/DocumentClassificationOptions";
import { z } from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
@@ -83,6 +86,7 @@ const documentFragment = graphql`
id
title
documentType
classification
owner {
id
fullName
@@ -101,6 +105,11 @@ const documentFragment = graphql`
publishedAt
version
updatedAt
classification
owner {
id
fullName
}
...DocumentSignaturesTab_version
signatures(first: 1000)
@connection(key: "DocumentDetailPage_signatures", filters: []) {
@@ -128,6 +137,7 @@ graphql`
title
description
documentType
classification
updatedAt
owner {
id
@@ -182,6 +192,7 @@ const updateDocumentMutation = graphql`
id
title
documentType
classification
owner {
id
fullName
@@ -195,6 +206,7 @@ const documentUpdateSchema = z.object({
title: z.string().min(1, "Title is required").max(255),
ownerId: z.string().min(1, "Owner is required"),
documentType: z.enum(documentTypes),
classification: z.enum(documentClassifications),
});
const UserEmailQuery = graphql`
@@ -221,6 +233,7 @@ export default function DocumentDetailPage(props: Props) {
const [isEditingTitle, setIsEditingTitle] = useState(false);
const [isEditingOwner, setIsEditingOwner] = useState(false);
const [isEditingType, setIsEditingType] = useState(false);
const [isEditingClassification, setIsEditingClassification] = useState(false);
const versions = document.versions.edges.map((edge) => edge.node);
const currentVersion =
document.versions.edges.find((v) => v.node.id === versionId)?.node ??
@@ -267,8 +280,9 @@ export default function DocumentDetailPage(props: Props) {
{
defaultValues: {
title: document.title,
ownerId: document.owner?.id || "",
ownerId: currentVersion.owner?.id || "",
documentType: document.documentType,
classification: currentVersion.classification,
},
}
);
@@ -325,6 +339,28 @@ export default function DocumentDetailPage(props: Props) {
});
};
const handleUpdateClassification = (data: {
classification: (typeof documentClassifications)[number];
}) => {
updateDocument({
variables: {
input: {
id: document.id,
classification: data.classification,
},
},
onSuccess: () => {
setIsEditingClassification(false);
loadQuery(
props.queryRef.environment,
documentNodeQuery,
props.queryRef.variables,
{ fetchPolicy: "network-only" }
);
},
});
};
const handlePublish = () => {
publishDocumentVersion({
variables: {
@@ -610,8 +646,8 @@ export default function DocumentDetailPage(props: Props) {
) : (
<ReadOnlyPropertyContent onEdit={() => setIsEditingOwner(true)}>
<Badge variant="highlight" size="md" className="gap-2">
<Avatar name={document.owner?.fullName ?? ""} />
{document.owner?.fullName}
<Avatar name={currentVersion.owner?.fullName ?? ""} />
{currentVersion.owner?.fullName}
</Badge>
</ReadOnlyPropertyContent>
)}
@@ -642,6 +678,34 @@ export default function DocumentDetailPage(props: Props) {
</ReadOnlyPropertyContent>
)}
</PropertyRow>
<PropertyRow label={__("Classification")}>
{isEditingClassification ? (
<EditablePropertyContent
onSave={handleSubmit(handleUpdateClassification)}
onCancel={() => {
setIsEditingClassification(false);
reset();
}}
disabled={isUpdatingDocument}
>
<ControlledField
name="classification"
control={control}
type="select"
>
<DocumentClassificationOptions />
</ControlledField>
</EditablePropertyContent>
) : (
<ReadOnlyPropertyContent
onEdit={() => setIsEditingClassification(true)}
>
<div className="text-sm text-txt-secondary">
{getDocumentClassificationLabel(__, currentVersion.classification)}
</div>
</ReadOnlyPropertyContent>
)}
</PropertyRow>
<PropertyRow label={__("Status")}>
<Badge
variant={isDraft ? "highlight" : "success"}

View File

@@ -41,7 +41,12 @@ import {
} from "/hooks/graph/DocumentGraph";
import type { DocumentsPageListFragment$key } from "./__generated__/DocumentsPageListFragment.graphql";
import { useList, usePageTitle } from "@probo/hooks";
import { sprintf, getDocumentTypeLabel, formatDate } from "@probo/helpers";
import {
sprintf,
getDocumentTypeLabel,
getDocumentClassificationLabel,
formatDate,
} from "@probo/helpers";
import { CreateDocumentDialog } from "./dialogs/CreateDocumentDialog";
import type { DocumentsPageRowFragment$key } from "./__generated__/DocumentsPageRowFragment.graphql";
import { SortableTable, SortableTh } from "/components/SortableTable";
@@ -223,6 +228,7 @@ export default function DocumentsPage(props: Props) {
<SortableTh field="DOCUMENT_TYPE" className="w-28">
{__("Type")}
</SortableTh>
<Th className="w-32">{__("Classification")}</Th>
<Th className="w-60">{__("Owner")}</Th>
<Th className="w-60">{__("Last update")}</Th>
<Th className="w-20">{__("Signatures")}</Th>
@@ -230,7 +236,7 @@ export default function DocumentsPage(props: Props) {
</Tr>
) : (
<Tr>
<Th colspan={9} compact>
<Th colspan={10} compact>
<div className="flex justify-between items-center h-8">
<div className="flex gap-2 items-center">
{sprintf(__("%s documents selected"), selection.length)} -
@@ -330,6 +336,7 @@ const rowFragment = graphql`
title
description
documentType
classification
updatedAt
owner {
id
@@ -424,6 +431,9 @@ function DocumentRow({
<Td className="w-28">
{getDocumentTypeLabel(__, document.documentType)}
</Td>
<Td className="w-32">
{getDocumentClassificationLabel(__, document.classification)}
</Td>
<Td className="w-60">
<div className="flex gap-2 items-center">
<Avatar name={document.owner?.fullName ?? ""} />

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<c39dd56e0397cd63ae1d2da95977694c>>
* @generated SignedSource<<3ba79250c6ba07fa44aca6eff28b3d9d>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,11 +9,13 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type DocumentClassification = "CONFIDENTIAL" | "INTERNAL" | "PUBLIC" | "SECRET";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type DocumentDetailPageDocumentFragment$data = {
readonly classification: DocumentClassification;
readonly controlsInfo: {
readonly totalCount: number;
};
@@ -28,8 +30,13 @@ export type DocumentDetailPageDocumentFragment$data = {
readonly __id: string;
readonly edges: ReadonlyArray<{
readonly node: {
readonly classification: DocumentClassification;
readonly content: string;
readonly id: string;
readonly owner: {
readonly fullName: string;
readonly id: string;
};
readonly publishedAt: any | null | undefined;
readonly signatures: {
readonly __id: string;
@@ -71,17 +78,43 @@ v1 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"name": "classification",
"storageKey": null
},
v2 = {
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"storageKey": null
},
v3 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "__typename",
"storageKey": null
},
v4 = {
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "cursor",
"storageKey": null
},
v3 = {
v5 = {
"alias": null,
"args": null,
"concreteType": "PageInfo",
@@ -106,7 +139,7 @@ v3 = {
],
"storageKey": null
},
v4 = {
v6 = {
"kind": "ClientExtension",
"selections": [
{
@@ -156,25 +189,8 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"concreteType": "People",
"kind": "LinkedField",
"name": "owner",
"plural": false,
"selections": [
(v0/*: any*/),
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "fullName",
"storageKey": null
}
],
"storageKey": null
},
(v1/*: any*/),
(v2/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -264,6 +280,8 @@ return {
"name": "updatedAt",
"storageKey": null
},
(v1/*: any*/),
(v2/*: any*/),
{
"args": null,
"kind": "FragmentSpread",
@@ -318,29 +336,29 @@ return {
"kind": "FragmentSpread",
"name": "DocumentSignaturesTab_signature"
},
(v1/*: any*/)
(v3/*: any*/)
],
"storageKey": null
},
(v2/*: any*/)
(v4/*: any*/)
],
"storageKey": null
},
(v3/*: any*/),
(v4/*: any*/)
(v5/*: any*/),
(v6/*: any*/)
],
"storageKey": null
},
(v1/*: any*/)
(v3/*: any*/)
],
"storageKey": null
},
(v2/*: any*/)
(v4/*: any*/)
],
"storageKey": null
},
(v3/*: any*/),
(v4/*: any*/)
(v5/*: any*/),
(v6/*: any*/)
],
"storageKey": null
}
@@ -350,6 +368,6 @@ return {
};
})();
(node as any).hash = "f1a7ec789a05b74cade1d3024119a844";
(node as any).hash = "e1418d5efeade64db32e9939132c03de";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<c802e03e4097ba191cb3738a0e45a4ed>>
* @generated SignedSource<<80564e817a10279d3bcbed4ad0ce8e27>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -135,6 +135,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -264,12 +271,12 @@ return {
]
},
"params": {
"cacheID": "e7734d4aece1b73c9bd85eddfbda2548",
"cacheID": "abf83afc77c45fa28de8d3c711633ec3",
"id": null,
"metadata": {},
"name": "DocumentDetailPagePublishMutation",
"operationKind": "mutation",
"text": "mutation DocumentDetailPagePublishMutation(\n $input: PublishDocumentVersionInput!\n) {\n publishDocumentVersion(input: $input) {\n document {\n id\n ...DocumentDetailPageRowFragment\n }\n }\n}\n\nfragment DocumentDetailPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "mutation DocumentDetailPagePublishMutation(\n $input: PublishDocumentVersionInput!\n) {\n publishDocumentVersion(input: $input) {\n document {\n id\n ...DocumentDetailPageRowFragment\n }\n }\n}\n\nfragment DocumentDetailPageRowFragment on Document {\n id\n title\n description\n documentType\n classification\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n signatures(first: 100) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<07388b39e4109439464531d46a890841>>
* @generated SignedSource<<f6a6c2f623522098263a3b556d3c3658>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,11 +9,13 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type DocumentClassification = "CONFIDENTIAL" | "INTERNAL" | "PUBLIC" | "SECRET";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type DocumentDetailPageRowFragment$data = {
readonly classification: DocumentClassification;
readonly description: string;
readonly documentType: DocumentType;
readonly id: string;
@@ -82,6 +84,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -208,6 +217,6 @@ return {
};
})();
(node as any).hash = "2144e456e031960d8ceefd334741831e";
(node as any).hash = "405e3b543fa517a3754d74b4f8c03d46";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<575a6c27726fb3e5457989b0d5a4edc2>>
* @generated SignedSource<<7c1bfb7a8cc7cc0f426348c57a17f7c9>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,9 +9,11 @@
// @ts-nocheck
import { ConcreteRequest } from 'relay-runtime';
export type DocumentClassification = "CONFIDENTIAL" | "INTERNAL" | "PUBLIC" | "SECRET";
export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE";
export type TrustCenterVisibility = "NONE" | "PRIVATE" | "PUBLIC";
export type UpdateDocumentInput = {
classification?: DocumentClassification | null | undefined;
content?: string | null | undefined;
documentType?: DocumentType | null | undefined;
id: string;
@@ -25,6 +27,7 @@ export type DocumentDetailPageUpdateMutation$variables = {
export type DocumentDetailPageUpdateMutation$data = {
readonly updateDocument: {
readonly document: {
readonly classification: DocumentClassification;
readonly documentType: DocumentType;
readonly id: string;
readonly owner: {
@@ -93,6 +96,13 @@ v2 = [
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -137,16 +147,16 @@ return {
"selections": (v2/*: any*/)
},
"params": {
"cacheID": "23268b13b11a1a56164dcc4cb96e24b3",
"cacheID": "865056a65394e413822f798f31351341",
"id": null,
"metadata": {},
"name": "DocumentDetailPageUpdateMutation",
"operationKind": "mutation",
"text": "mutation DocumentDetailPageUpdateMutation(\n $input: UpdateDocumentInput!\n) {\n updateDocument(input: $input) {\n document {\n id\n title\n documentType\n owner {\n id\n fullName\n }\n }\n }\n}\n"
"text": "mutation DocumentDetailPageUpdateMutation(\n $input: UpdateDocumentInput!\n) {\n updateDocument(input: $input) {\n document {\n id\n title\n documentType\n classification\n owner {\n id\n fullName\n }\n }\n }\n}\n"
}
};
})();
(node as any).hash = "507a2bd40b923d2064fdd864f16f29aa";
(node as any).hash = "bd6acddfc137a6bdce42628d05dc9ff6";
export default node;

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<739800753f29986463be3d9e4316a44a>>
* @generated SignedSource<<83dad18be7a9249bb99048ba14b6de41>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -236,6 +236,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -452,12 +459,12 @@ return {
]
},
"params": {
"cacheID": "ce2facebbd111ac1b3398933a5a06a05",
"cacheID": "2f5cd9919b919f4e9d80354cb26d3e9d",
"id": null,
"metadata": {},
"name": "DocumentsListQuery",
"operationKind": "query",
"text": "query DocumentsListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: DocumentOrder = {field: TITLE, direction: ASC}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...DocumentsPageListFragment_16fISc\n id\n }\n}\n\nfragment DocumentsPageListFragment_16fISc on Organization {\n documents(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "query DocumentsListQuery(\n $after: CursorKey = null\n $before: CursorKey = null\n $first: Int = 50\n $last: Int = null\n $order: DocumentOrder = {field: TITLE, direction: ASC}\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...DocumentsPageListFragment_16fISc\n id\n }\n}\n\nfragment DocumentsPageListFragment_16fISc on Organization {\n documents(first: $first, after: $after, last: $last, before: $before, orderBy: $order) {\n edges {\n node {\n id\n ...DocumentsPageRowFragment\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n classification\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<96486371decaf730cf0a559125086bd8>>
* @generated SignedSource<<e853bbeba41d33327ee6612c84e21297>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -9,11 +9,13 @@
// @ts-nocheck
import { ReaderFragment } from 'relay-runtime';
export type DocumentClassification = "CONFIDENTIAL" | "INTERNAL" | "PUBLIC" | "SECRET";
export type DocumentStatus = "DRAFT" | "PUBLISHED";
export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE";
export type DocumentVersionSignatureState = "REQUESTED" | "SIGNED";
import { FragmentRefs } from "relay-runtime";
export type DocumentsPageRowFragment$data = {
readonly classification: DocumentClassification;
readonly description: string;
readonly documentType: DocumentType;
readonly id: string;
@@ -83,6 +85,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -216,6 +225,6 @@ return {
};
})();
(node as any).hash = "4151906ef10da152d053c46af6b9f890";
(node as any).hash = "cd6d6bebffb9ed17a251aeef4b86f753";
export default node;

View File

@@ -21,6 +21,7 @@ import { PeopleSelectField } from "/components/form/PeopleSelectField";
import type { CreateDocumentDialogMutation } from "./__generated__/CreateDocumentDialogMutation.graphql";
import { ControlledField } from "/components/form/ControlledField";
import { DocumentTypeOptions } from "/components/form/DocumentTypeOptions";
import { DocumentClassificationOptions } from "/components/form/DocumentClassificationOptions";
type Props = {
trigger?: ReactNode;
@@ -125,6 +126,20 @@ export function CreateDocumentDialog({ trigger, connection }: Props) {
</ControlledField>
</PropertyRow>
<PropertyRow
id="classification"
label={__("Classification")}
error={errors.classification?.message}
>
<ControlledField
control={control}
name="classification"
type="select"
>
<DocumentClassificationOptions />
</ControlledField>
</PropertyRow>
<PropertyRow
id="ownerId"
label={__("Owner")}

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<429de5dec69b88e0abedd39ecac81ee3>>
* @generated SignedSource<<8f842e1d6a4c314a9bfba43097ac2795>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -10,9 +10,11 @@
import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type DocumentClassification = "CONFIDENTIAL" | "INTERNAL" | "PUBLIC" | "SECRET";
export type DocumentType = "ISMS" | "OTHER" | "POLICY" | "PROCEDURE";
export type TrustCenterVisibility = "NONE" | "PRIVATE" | "PUBLIC";
export type CreateDocumentInput = {
classification: DocumentClassification;
content: string;
documentType: DocumentType;
organizationId: string;
@@ -172,6 +174,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -327,12 +336,12 @@ return {
]
},
"params": {
"cacheID": "0a5f7934a498b696a268943176bdf910",
"cacheID": "319502472cd9600df506f084a7648571",
"id": null,
"metadata": {},
"name": "CreateDocumentDialogMutation",
"operationKind": "mutation",
"text": "mutation CreateDocumentDialogMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "mutation CreateDocumentDialogMutation(\n $input: CreateDocumentInput!\n) {\n createDocument(input: $input) {\n documentEdge {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n classification\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<18ebf050c9c9ec64b126385ad8b8dd7d>>
* @generated SignedSource<<bf6b6a8a1f99fd0bfcc4eda44bbfe352>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -156,6 +156,13 @@ return {
"name": "documentType",
"storageKey": null
},
{
"alias": null,
"args": null,
"kind": "ScalarField",
"name": "classification",
"storageKey": null
},
{
"alias": null,
"args": null,
@@ -295,12 +302,12 @@ return {
]
},
"params": {
"cacheID": "5c79c67dcab25c8ead714ff999b0e7cf",
"cacheID": "d3e85ba061ad3937ad4a3c6727eb76fa",
"id": null,
"metadata": {},
"name": "PublishDocumentsDialogMutation",
"operationKind": "mutation",
"text": "mutation PublishDocumentsDialogMutation(\n $input: BulkPublishDocumentVersionsInput!\n) {\n bulkPublishDocumentVersions(input: $input) {\n documentEdges {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
"text": "mutation PublishDocumentsDialogMutation(\n $input: BulkPublishDocumentVersionsInput!\n) {\n bulkPublishDocumentVersions(input: $input) {\n documentEdges {\n node {\n id\n ...DocumentsPageRowFragment\n }\n }\n }\n}\n\nfragment DocumentsPageRowFragment on Document {\n id\n title\n description\n documentType\n classification\n updatedAt\n owner {\n id\n fullName\n }\n versions(first: 1) {\n edges {\n node {\n id\n status\n version\n signatures(first: 1000) {\n edges {\n node {\n id\n state\n }\n }\n }\n }\n }\n }\n}\n"
}
};
})();

View File

@@ -14,3 +14,23 @@ export function getDocumentTypeLabel(__: Translator, type: string) {
return __("Procedure");
}
}
export const documentClassifications = [
"PUBLIC",
"INTERNAL",
"CONFIDENTIAL",
"SECRET",
] as const;
export function getDocumentClassificationLabel(__: Translator, classification: string) {
switch (classification) {
case "PUBLIC":
return __("Public");
case "INTERNAL":
return __("Internal");
case "CONFIDENTIAL":
return __("Confidential");
case "SECRET":
return __("Secret");
}
}

View File

@@ -20,7 +20,12 @@ export {
type CountryCode,
} from "./countries";
export { availableFrameworks } from "./frameworks";
export { getDocumentTypeLabel, documentTypes } from "./documents";
export {
getDocumentTypeLabel,
documentTypes,
getDocumentClassificationLabel,
documentClassifications,
} from "./documents";
export { getAssetTypeVariant } from "./assets";
export {
getSnapshotTypeLabel,

View File

@@ -0,0 +1,33 @@
import { Badge } from "../../Atoms/Badge/Badge";
import { getDocumentClassificationLabel } from "@probo/helpers";
import { useTranslate } from "@probo/i18n";
type Props = {
classification: string;
};
export function DocumentClassificationBadge({ classification }: Props) {
const { __ } = useTranslate();
// Choose badge variant based on classification level
const variant = (() => {
switch (classification) {
case "PUBLIC":
return "success" as const;
case "INTERNAL":
return "neutral" as const;
case "CONFIDENTIAL":
return "warning" as const;
case "SECRET":
return "danger" as const;
default:
return "neutral" as const;
}
})();
return (
<Badge variant={variant}>
{getDocumentClassificationLabel(__, classification)}
</Badge>
);
}

View File

@@ -66,6 +66,7 @@ export { MeasureBadge } from "./Molecules/Badge/MeasureBadge";
export { MeasureImplementation } from "./Molecules/MeasureImplementation/MeasureImplementation";
export { FrameworkSelector } from "./Molecules/FrameworkSelector/FrameworkSelector";
export { DocumentTypeBadge } from "./Molecules/Badge/DocumentTypeBadge";
export { DocumentClassificationBadge } from "./Molecules/Badge/DocumentClassificationBadge";
export { SentitivityOptions } from "./Molecules/Select/SentitivityOptions";
export { ImpactOptions } from "./Molecules/Select/ImpactOptions";
export { DurationPicker } from "./Molecules/DurationPicker/DurationPicker";

View File

@@ -28,15 +28,16 @@ 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"`
DocumentType DocumentType `db:"document_type"`
CurrentPublishedVersion *int `db:"current_published_version"`
TrustCenterVisibility TrustCenterVisibility `db:"trust_center_visibility"`
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"`
Classification DocumentClassification `db:"classification"`
CurrentPublishedVersion *int `db:"current_published_version"`
TrustCenterVisibility TrustCenterVisibility `db:"trust_center_visibility"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Documents []*Document
@@ -68,6 +69,7 @@ SELECT
owner_id,
title,
document_type,
classification,
current_published_version,
trust_center_visibility,
created_at,
@@ -150,6 +152,7 @@ SELECT
owner_id,
title,
document_type,
classification,
current_published_version,
trust_center_visibility,
created_at,
@@ -200,6 +203,7 @@ SELECT
owner_id,
title,
document_type,
classification,
current_published_version,
trust_center_visibility,
created_at,
@@ -249,6 +253,7 @@ INSERT INTO
owner_id,
title,
document_type,
classification,
current_published_version,
trust_center_visibility,
created_at,
@@ -261,6 +266,7 @@ VALUES (
@owner_id,
@title,
@document_type,
@classification,
@current_published_version,
@trust_center_visibility,
@created_at,
@@ -275,6 +281,7 @@ VALUES (
"owner_id": p.OwnerID,
"title": p.Title,
"document_type": p.DocumentType,
"classification": p.Classification,
"current_published_version": p.CurrentPublishedVersion,
"trust_center_visibility": p.TrustCenterVisibility,
"created_at": p.CreatedAt,
@@ -334,6 +341,7 @@ SET
current_published_version = @current_published_version,
owner_id = @owner_id,
document_type = @document_type,
classification = @classification,
trust_center_visibility = @trust_center_visibility,
updated_at = @updated_at
WHERE
@@ -350,6 +358,7 @@ WHERE
"current_published_version": p.CurrentPublishedVersion,
"owner_id": p.OwnerID,
"document_type": p.DocumentType,
"classification": p.Classification,
"trust_center_visibility": p.TrustCenterVisibility,
}
maps.Copy(args, scope.SQLArguments())
@@ -427,6 +436,7 @@ WITH plcs AS (
p.owner_id,
p.title,
p.document_type,
p.classification,
p.current_published_version,
p.trust_center_visibility,
p.created_at,
@@ -445,6 +455,7 @@ SELECT
owner_id,
title,
document_type,
classification,
current_published_version,
trust_center_visibility,
created_at,
@@ -543,6 +554,7 @@ WITH plcs AS (
p.owner_id,
p.title,
p.document_type,
p.classification,
p.current_published_version,
p.trust_center_visibility,
p.created_at,
@@ -562,6 +574,7 @@ SELECT
owner_id,
title,
document_type,
classification,
current_published_version,
trust_center_visibility,
created_at,

View File

@@ -0,0 +1,49 @@
package coredata
import (
"database/sql/driver"
"fmt"
)
type DocumentClassification string
const (
DocumentClassificationPublic DocumentClassification = "PUBLIC"
DocumentClassificationInternal DocumentClassification = "INTERNAL"
DocumentClassificationConfidential DocumentClassification = "CONFIDENTIAL"
DocumentClassificationSecret DocumentClassification = "SECRET"
)
func (dc DocumentClassification) String() string {
switch dc {
case DocumentClassificationPublic:
return "PUBLIC"
case DocumentClassificationInternal:
return "INTERNAL"
case DocumentClassificationConfidential:
return "CONFIDENTIAL"
case DocumentClassificationSecret:
return "SECRET"
}
panic(fmt.Errorf("invalid DocumentClassification value: %s", string(dc)))
}
// Scan implements the sql.Scanner interface for database deserialization.
func (dc *DocumentClassification) Scan(value interface{}) error {
if value == nil {
return nil
}
sv, ok := value.(string)
if !ok {
return fmt.Errorf("failed to scan DocumentClassification: %v", value)
}
*dc = DocumentClassification(sv)
return nil
}
// Value implements the driver.Valuer interface for database serialization.
func (dc DocumentClassification) Value() (driver.Value, error) {
return string(dc), nil
}

View File

@@ -28,17 +28,18 @@ import (
type (
DocumentVersion struct {
ID gid.GID `db:"id"`
DocumentID gid.GID `db:"document_id"`
Title string `db:"title"`
OwnerID gid.GID `db:"owner_id"`
VersionNumber int `db:"version_number"`
Content string `db:"content"`
Changelog string `db:"changelog"`
Status DocumentStatus `db:"status"`
PublishedAt *time.Time `db:"published_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ID gid.GID `db:"id"`
DocumentID gid.GID `db:"document_id"`
Title string `db:"title"`
OwnerID gid.GID `db:"owner_id"`
VersionNumber int `db:"version_number"`
Classification DocumentClassification `db:"classification"`
Content string `db:"content"`
Changelog string `db:"changelog"`
Status DocumentStatus `db:"status"`
PublishedAt *time.Time `db:"published_at"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
DocumentVersions []*DocumentVersion
@@ -58,6 +59,7 @@ SELECT
title,
owner_id,
version_number,
classification,
content,
changelog,
status,
@@ -116,6 +118,7 @@ SELECT
title,
owner_id,
version_number,
classification,
content,
changelog,
status,
@@ -165,6 +168,7 @@ INSERT INTO document_versions (
title,
owner_id,
version_number,
classification,
content,
changelog,
status,
@@ -178,6 +182,7 @@ VALUES (
@title,
@owner_id,
@version_number,
@classification,
@content,
@changelog,
@status,
@@ -192,6 +197,7 @@ VALUES (
"title": p.Title,
"owner_id": p.OwnerID,
"version_number": p.VersionNumber,
"classification": p.Classification,
"content": p.Content,
"changelog": p.Changelog,
"status": p.Status,
@@ -221,6 +227,7 @@ SELECT
title,
owner_id,
version_number,
classification,
content,
changelog,
status,
@@ -272,6 +279,7 @@ SELECT
title,
owner_id,
version_number,
classification,
content,
changelog,
status,
@@ -321,6 +329,7 @@ SELECT
title,
owner_id,
version_number,
classification,
content,
changelog,
status,
@@ -372,6 +381,7 @@ UPDATE document_versions SET
status = @status,
content = @content,
published_at = @published_at,
classification = @classification,
updated_at = @updated_at
WHERE %s
AND id = @document_version_id
@@ -387,6 +397,7 @@ WHERE %s
"status": p.Status,
"content": p.Content,
"published_at": p.PublishedAt,
"classification": p.Classification,
"updated_at": p.UpdatedAt,
}
maps.Copy(args, scope.SQLArguments())

View File

@@ -0,0 +1,12 @@
CREATE TYPE document_classification AS ENUM (
'PUBLIC',
'INTERNAL',
'CONFIDENTIAL',
'SECRET'
);
ALTER TABLE documents ADD COLUMN classification document_classification NOT NULL DEFAULT 'CONFIDENTIAL';
ALTER TABLE documents ALTER COLUMN classification DROP DEFAULT;
ALTER TABLE document_versions ADD COLUMN classification document_classification NOT NULL DEFAULT 'CONFIDENTIAL';
ALTER TABLE document_versions ALTER COLUMN classification DROP DEFAULT;

View File

@@ -45,10 +45,20 @@ type (
Title string
Content string
OwnerID gid.GID
Classification coredata.DocumentClassification
DocumentType coredata.DocumentType
TrustCenterVisibility *coredata.TrustCenterVisibility
}
UpdateDocumentRequest struct {
DocumentID gid.GID
Title *string
OwnerID *gid.GID
Classification *coredata.DocumentClassification
DocumentType *coredata.DocumentType
TrustCenterVisibility *coredata.TrustCenterVisibility
}
UpdateDocumentVersionRequest struct {
ID gid.GID
Content string
@@ -308,6 +318,7 @@ func (s *DocumentService) Create(
Title: req.Title,
DocumentType: req.DocumentType,
TrustCenterVisibility: coredata.TrustCenterVisibilityNone,
Classification: req.Classification,
CreatedAt: now,
UpdatedAt: now,
}
@@ -317,15 +328,16 @@ func (s *DocumentService) Create(
}
documentVersion := &coredata.DocumentVersion{
ID: documentVersionID,
DocumentID: documentID,
Title: req.Title,
OwnerID: req.OwnerID,
VersionNumber: 1,
Content: req.Content,
Status: coredata.DocumentStatusDraft,
CreatedAt: now,
UpdatedAt: now,
ID: documentVersionID,
DocumentID: documentID,
Title: req.Title,
OwnerID: req.OwnerID,
VersionNumber: 1,
Content: req.Content,
Status: coredata.DocumentStatusDraft,
Classification: req.Classification,
CreatedAt: now,
UpdatedAt: now,
}
err := s.svc.pg.WithTx(
@@ -563,6 +575,7 @@ func (s *DocumentService) UpdateVersion(
documentVersion.Title = document.Title
documentVersion.OwnerID = document.OwnerID
documentVersion.Classification = document.Classification
documentVersion.Content = req.Content
documentVersion.UpdatedAt = time.Now()
@@ -764,6 +777,7 @@ func (s *DocumentService) CreateDraft(
draftVersion.Title = document.Title
draftVersion.OwnerID = document.OwnerID
draftVersion.VersionNumber = latestVersion.VersionNumber + 1
draftVersion.Classification = document.Classification
draftVersion.Content = latestVersion.Content
draftVersion.Status = coredata.DocumentStatusDraft
draftVersion.CreatedAt = now
@@ -1108,11 +1122,7 @@ func (s *DocumentService) ListForRiskID(
func (s *DocumentService) Update(
ctx context.Context,
documentID gid.GID,
newOwnerID *gid.GID,
documentType *coredata.DocumentType,
title *string,
trustCenterVisibility *coredata.TrustCenterVisibility,
req UpdateDocumentRequest,
) (*coredata.Document, error) {
document := &coredata.Document{}
people := &coredata.People{}
@@ -1121,27 +1131,35 @@ func (s *DocumentService) Update(
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 err := document.LoadByID(ctx, tx, s.svc.scope, req.DocumentID); err != nil {
return fmt.Errorf("cannot load document %q: %w", req.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)
if req.Title != nil {
document.Title = *req.Title
}
if req.Classification != nil {
document.Classification = *req.Classification
}
if req.DocumentType != nil {
document.DocumentType = *req.DocumentType
}
if req.DocumentType != nil {
document.DocumentType = *req.DocumentType
}
if req.TrustCenterVisibility != nil {
document.TrustCenterVisibility = *req.TrustCenterVisibility
}
if req.OwnerID != nil {
if err := people.LoadByID(ctx, tx, s.svc.scope, *req.OwnerID); err != nil {
return fmt.Errorf("cannot load owner %q: %w", *req.OwnerID, err)
}
document.OwnerID = *newOwnerID
}
if documentType != nil {
document.DocumentType = *documentType
}
if title != nil {
document.Title = *title
}
if trustCenterVisibility != nil {
document.TrustCenterVisibility = *trustCenterVisibility
document.OwnerID = people.ID
}
document.UpdatedAt = now

View File

@@ -95,11 +95,17 @@ enum PeopleKind
enum InvitationStatus
@goModel(model: "github.com/getprobo/probo/pkg/coredata.InvitationStatus") {
PENDING
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusPending")
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusPending"
)
ACCEPTED
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusAccepted")
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusAccepted"
)
EXPIRED
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusExpired")
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusExpired"
)
}
enum DocumentStatus
@@ -961,6 +967,28 @@ enum DocumentType
)
}
enum DocumentClassification
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.DocumentClassification"
) {
PUBLIC
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationPublic"
)
INTERNAL
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationInternal"
)
CONFIDENTIAL
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationConfidential"
)
SECRET
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationSecret"
)
}
enum AssetType
@goModel(model: "github.com/getprobo/probo/pkg/coredata.AssetType") {
PHYSICAL
@@ -1220,7 +1248,9 @@ enum SnapshotOrderField
}
enum MembershipOrderField
@goModel(model: "github.com/getprobo/probo/pkg/coredata.MembershipOrderField") {
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.MembershipOrderField"
) {
FULL_NAME
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.MembershipOrderFieldFullName"
@@ -1240,7 +1270,9 @@ enum MembershipOrderField
}
enum InvitationOrderField
@goModel(model: "github.com/getprobo/probo/pkg/coredata.InvitationOrderField") {
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.InvitationOrderField"
) {
FULL_NAME
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationOrderFieldFullName"
@@ -2077,6 +2109,7 @@ type Document implements Node {
title: String!
description: String!
documentType: DocumentType!
classification: DocumentClassification!
currentPublishedVersion: Int
trustCenterVisibility: TrustCenterVisibility!
owner: People! @goField(forceResolver: true)
@@ -3540,6 +3573,7 @@ input CreateDocumentInput {
content: String!
ownerId: ID!
documentType: DocumentType!
classification: DocumentClassification!
trustCenterVisibility: TrustCenterVisibility
}
@@ -3549,6 +3583,7 @@ input UpdateDocumentInput {
content: String
ownerId: ID
documentType: DocumentType
classification: DocumentClassification
trustCenterVisibility: TrustCenterVisibility
}
@@ -4174,6 +4209,7 @@ type DocumentVersion implements Node {
content: String!
changelog: String!
title: String!
classification: DocumentClassification!
owner: People! @goField(forceResolver: true)
signatures(

View File

@@ -607,6 +607,7 @@ type ComplexityRoot struct {
}
Document struct {
Classification func(childComplexity int) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int
CreatedAt func(childComplexity int) int
CurrentPublishedVersion func(childComplexity int) int
@@ -633,18 +634,19 @@ type ComplexityRoot struct {
}
DocumentVersion struct {
Changelog func(childComplexity int) int
Content func(childComplexity int) int
CreatedAt func(childComplexity int) int
Document func(childComplexity int) int
ID func(childComplexity int) int
Owner func(childComplexity int) int
PublishedAt func(childComplexity int) int
Signatures func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionSignatureOrder, filter *types.DocumentVersionSignatureFilter) int
Status func(childComplexity int) int
Title func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Version func(childComplexity int) int
Changelog func(childComplexity int) int
Classification func(childComplexity int) int
Content func(childComplexity int) int
CreatedAt func(childComplexity int) int
Document func(childComplexity int) int
ID func(childComplexity int) int
Owner func(childComplexity int) int
PublishedAt func(childComplexity int) int
Signatures func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionSignatureOrder, filter *types.DocumentVersionSignatureFilter) int
Status func(childComplexity int) int
Title func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Version func(childComplexity int) int
}
DocumentVersionConnection struct {
@@ -3487,6 +3489,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.DeleteVendorServicePayload.DeletedVendorServiceID(childComplexity), true
case "Document.classification":
if e.complexity.Document.Classification == nil {
break
}
return e.complexity.Document.Classification(childComplexity), true
case "Document.controls":
if e.complexity.Document.Controls == nil {
break
@@ -3623,6 +3632,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.DocumentVersion.Changelog(childComplexity), true
case "DocumentVersion.classification":
if e.complexity.DocumentVersion.Classification == nil {
break
}
return e.complexity.DocumentVersion.Classification(childComplexity), true
case "DocumentVersion.content":
if e.complexity.DocumentVersion.Content == nil {
break
@@ -9146,11 +9162,17 @@ enum PeopleKind
enum InvitationStatus
@goModel(model: "github.com/getprobo/probo/pkg/coredata.InvitationStatus") {
PENDING
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusPending")
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusPending"
)
ACCEPTED
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusAccepted")
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusAccepted"
)
EXPIRED
@goEnum(value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusExpired")
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationStatusExpired"
)
}
enum DocumentStatus
@@ -10012,6 +10034,28 @@ enum DocumentType
)
}
enum DocumentClassification
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.DocumentClassification"
) {
PUBLIC
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationPublic"
)
INTERNAL
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationInternal"
)
CONFIDENTIAL
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationConfidential"
)
SECRET
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.DocumentClassificationSecret"
)
}
enum AssetType
@goModel(model: "github.com/getprobo/probo/pkg/coredata.AssetType") {
PHYSICAL
@@ -10271,7 +10315,9 @@ enum SnapshotOrderField
}
enum MembershipOrderField
@goModel(model: "github.com/getprobo/probo/pkg/coredata.MembershipOrderField") {
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.MembershipOrderField"
) {
FULL_NAME
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.MembershipOrderFieldFullName"
@@ -10291,7 +10337,9 @@ enum MembershipOrderField
}
enum InvitationOrderField
@goModel(model: "github.com/getprobo/probo/pkg/coredata.InvitationOrderField") {
@goModel(
model: "github.com/getprobo/probo/pkg/coredata.InvitationOrderField"
) {
FULL_NAME
@goEnum(
value: "github.com/getprobo/probo/pkg/coredata.InvitationOrderFieldFullName"
@@ -11128,6 +11176,7 @@ type Document implements Node {
title: String!
description: String!
documentType: DocumentType!
classification: DocumentClassification!
currentPublishedVersion: Int
trustCenterVisibility: TrustCenterVisibility!
owner: People! @goField(forceResolver: true)
@@ -12591,6 +12640,7 @@ input CreateDocumentInput {
content: String!
ownerId: ID!
documentType: DocumentType!
classification: DocumentClassification!
trustCenterVisibility: TrustCenterVisibility
}
@@ -12600,6 +12650,7 @@ input UpdateDocumentInput {
content: String
ownerId: ID
documentType: DocumentType
classification: DocumentClassification
trustCenterVisibility: TrustCenterVisibility
}
@@ -13225,6 +13276,7 @@ type DocumentVersion implements Node {
content: String!
changelog: String!
title: String!
classification: DocumentClassification!
owner: People! @goField(forceResolver: true)
signatures(
@@ -32063,6 +32115,50 @@ func (ec *executionContext) fieldContext_Document_documentType(_ context.Context
return fc, nil
}
func (ec *executionContext) _Document_classification(ctx context.Context, field graphql.CollectedField, obj *types.Document) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Document_classification(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return obj.Classification, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(coredata.DocumentClassification)
fc.Result = res
return ec.marshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_Document_classification(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "Document",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type DocumentClassification does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _Document_currentPublishedVersion(ctx context.Context, field graphql.CollectedField, obj *types.Document) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Document_currentPublishedVersion(ctx, field)
if err != nil {
@@ -32773,6 +32869,8 @@ func (ec *executionContext) fieldContext_DocumentEdge_node(_ context.Context, fi
return ec.fieldContext_Document_description(ctx, field)
case "documentType":
return ec.fieldContext_Document_documentType(ctx, field)
case "classification":
return ec.fieldContext_Document_classification(ctx, field)
case "currentPublishedVersion":
return ec.fieldContext_Document_currentPublishedVersion(ctx, field)
case "trustCenterVisibility":
@@ -32887,6 +32985,8 @@ func (ec *executionContext) fieldContext_DocumentVersion_document(_ context.Cont
return ec.fieldContext_Document_description(ctx, field)
case "documentType":
return ec.fieldContext_Document_documentType(ctx, field)
case "classification":
return ec.fieldContext_Document_classification(ctx, field)
case "currentPublishedVersion":
return ec.fieldContext_Document_currentPublishedVersion(ctx, field)
case "trustCenterVisibility":
@@ -33130,6 +33230,50 @@ func (ec *executionContext) fieldContext_DocumentVersion_title(_ context.Context
return fc, nil
}
func (ec *executionContext) _DocumentVersion_classification(ctx context.Context, field graphql.CollectedField, obj *types.DocumentVersion) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_DocumentVersion_classification(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return obj.Classification, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(coredata.DocumentClassification)
fc.Result = res
return ec.marshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_DocumentVersion_classification(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "DocumentVersion",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type DocumentClassification does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _DocumentVersion_owner(ctx context.Context, field graphql.CollectedField, obj *types.DocumentVersion) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_DocumentVersion_owner(ctx, field)
if err != nil {
@@ -33587,6 +33731,8 @@ func (ec *executionContext) fieldContext_DocumentVersionEdge_node(_ context.Cont
return ec.fieldContext_DocumentVersion_changelog(ctx, field)
case "title":
return ec.fieldContext_DocumentVersion_title(ctx, field)
case "classification":
return ec.fieldContext_DocumentVersion_classification(ctx, field)
case "owner":
return ec.fieldContext_DocumentVersion_owner(ctx, field)
case "signatures":
@@ -33701,6 +33847,8 @@ func (ec *executionContext) fieldContext_DocumentVersionSignature_documentVersio
return ec.fieldContext_DocumentVersion_changelog(ctx, field)
case "title":
return ec.fieldContext_DocumentVersion_title(ctx, field)
case "classification":
return ec.fieldContext_DocumentVersion_classification(ctx, field)
case "owner":
return ec.fieldContext_DocumentVersion_owner(ctx, field)
case "signatures":
@@ -51743,6 +51891,8 @@ func (ec *executionContext) fieldContext_PublishDocumentVersionPayload_documentV
return ec.fieldContext_DocumentVersion_changelog(ctx, field)
case "title":
return ec.fieldContext_DocumentVersion_title(ctx, field)
case "classification":
return ec.fieldContext_DocumentVersion_classification(ctx, field)
case "owner":
return ec.fieldContext_DocumentVersion_owner(ctx, field)
case "signatures":
@@ -51807,6 +51957,8 @@ func (ec *executionContext) fieldContext_PublishDocumentVersionPayload_document(
return ec.fieldContext_Document_description(ctx, field)
case "documentType":
return ec.fieldContext_Document_documentType(ctx, field)
case "classification":
return ec.fieldContext_Document_classification(ctx, field)
case "currentPublishedVersion":
return ec.fieldContext_Document_currentPublishedVersion(ctx, field)
case "trustCenterVisibility":
@@ -57128,6 +57280,8 @@ func (ec *executionContext) fieldContext_TrustCenterDocumentAccess_document(_ co
return ec.fieldContext_Document_description(ctx, field)
case "documentType":
return ec.fieldContext_Document_documentType(ctx, field)
case "classification":
return ec.fieldContext_Document_classification(ctx, field)
case "currentPublishedVersion":
return ec.fieldContext_Document_currentPublishedVersion(ctx, field)
case "trustCenterVisibility":
@@ -58597,6 +58751,8 @@ func (ec *executionContext) fieldContext_UpdateDocumentPayload_document(_ contex
return ec.fieldContext_Document_description(ctx, field)
case "documentType":
return ec.fieldContext_Document_documentType(ctx, field)
case "classification":
return ec.fieldContext_Document_classification(ctx, field)
case "currentPublishedVersion":
return ec.fieldContext_Document_currentPublishedVersion(ctx, field)
case "trustCenterVisibility":
@@ -58673,6 +58829,8 @@ func (ec *executionContext) fieldContext_UpdateDocumentVersionPayload_documentVe
return ec.fieldContext_DocumentVersion_changelog(ctx, field)
case "title":
return ec.fieldContext_DocumentVersion_title(ctx, field)
case "classification":
return ec.fieldContext_DocumentVersion_classification(ctx, field)
case "owner":
return ec.fieldContext_DocumentVersion_owner(ctx, field)
case "signatures":
@@ -69128,7 +69286,7 @@ func (ec *executionContext) unmarshalInputCreateDocumentInput(ctx context.Contex
asMap[k] = v
}
fieldsInOrder := [...]string{"organizationId", "title", "content", "ownerId", "documentType", "trustCenterVisibility"}
fieldsInOrder := [...]string{"organizationId", "title", "content", "ownerId", "documentType", "classification", "trustCenterVisibility"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -69170,6 +69328,13 @@ func (ec *executionContext) unmarshalInputCreateDocumentInput(ctx context.Contex
return it, err
}
it.DocumentType = data
case "classification":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("classification"))
data, err := ec.unmarshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx, v)
if err != nil {
return it, err
}
it.Classification = data
case "trustCenterVisibility":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("trustCenterVisibility"))
data, err := ec.unmarshalOTrustCenterVisibility2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐTrustCenterVisibility(ctx, v)
@@ -73421,7 +73586,7 @@ func (ec *executionContext) unmarshalInputUpdateDocumentInput(ctx context.Contex
asMap[k] = v
}
fieldsInOrder := [...]string{"id", "title", "content", "ownerId", "documentType", "trustCenterVisibility"}
fieldsInOrder := [...]string{"id", "title", "content", "ownerId", "documentType", "classification", "trustCenterVisibility"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -73463,6 +73628,13 @@ func (ec *executionContext) unmarshalInputUpdateDocumentInput(ctx context.Contex
return it, err
}
it.DocumentType = data
case "classification":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("classification"))
data, err := ec.unmarshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx, v)
if err != nil {
return it, err
}
it.Classification = data
case "trustCenterVisibility":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("trustCenterVisibility"))
data, err := ec.unmarshalOTrustCenterVisibility2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐTrustCenterVisibility(ctx, v)
@@ -80617,6 +80789,11 @@ func (ec *executionContext) _Document(ctx context.Context, sel ast.SelectionSet,
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "classification":
out.Values[i] = ec._Document_classification(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "currentPublishedVersion":
out.Values[i] = ec._Document_currentPublishedVersion(ctx, field, obj)
case "trustCenterVisibility":
@@ -81002,6 +81179,11 @@ func (ec *executionContext) _DocumentVersion(ctx context.Context, sel ast.Select
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "classification":
out.Values[i] = ec._DocumentVersion_classification(ctx, field, obj)
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
case "owner":
field := field
@@ -95590,6 +95772,38 @@ func (ec *executionContext) marshalNDocument2ᚖgithubᚗcomᚋgetproboᚋprobo
return ec._Document(ctx, sel, v)
}
func (ec *executionContext) unmarshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx context.Context, v any) (coredata.DocumentClassification, error) {
tmp, err := graphql.UnmarshalString(v)
res := unmarshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification[tmp]
return res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx context.Context, sel ast.SelectionSet, v coredata.DocumentClassification) graphql.Marshaler {
_ = sel
res := graphql.MarshalString(marshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification[v])
if res == graphql.Null {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
}
return res
}
var (
unmarshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification = map[string]coredata.DocumentClassification{
"PUBLIC": coredata.DocumentClassificationPublic,
"INTERNAL": coredata.DocumentClassificationInternal,
"CONFIDENTIAL": coredata.DocumentClassificationConfidential,
"SECRET": coredata.DocumentClassificationSecret,
}
marshalNDocumentClassification2githubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification = map[coredata.DocumentClassification]string{
coredata.DocumentClassificationPublic: "PUBLIC",
coredata.DocumentClassificationInternal: "INTERNAL",
coredata.DocumentClassificationConfidential: "CONFIDENTIAL",
coredata.DocumentClassificationSecret: "SECRET",
}
)
func (ec *executionContext) marshalNDocumentConnection2githubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDocumentConnection(ctx context.Context, sel ast.SelectionSet, v types.DocumentConnection) graphql.Marshaler {
return ec._DocumentConnection(ctx, sel, &v)
}
@@ -101170,6 +101384,40 @@ func (ec *executionContext) marshalODocument2ᚖgithubᚗcomᚋgetproboᚋprobo
return ec._Document(ctx, sel, v)
}
func (ec *executionContext) unmarshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx context.Context, v any) (*coredata.DocumentClassification, error) {
if v == nil {
return nil, nil
}
tmp, err := graphql.UnmarshalString(v)
res := unmarshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification[tmp]
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) marshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification(ctx context.Context, sel ast.SelectionSet, v *coredata.DocumentClassification) graphql.Marshaler {
if v == nil {
return graphql.Null
}
_ = sel
_ = ctx
res := graphql.MarshalString(marshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification[*v])
return res
}
var (
unmarshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification = map[string]coredata.DocumentClassification{
"PUBLIC": coredata.DocumentClassificationPublic,
"INTERNAL": coredata.DocumentClassificationInternal,
"CONFIDENTIAL": coredata.DocumentClassificationConfidential,
"SECRET": coredata.DocumentClassificationSecret,
}
marshalODocumentClassification2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋcoredataᚐDocumentClassification = map[coredata.DocumentClassification]string{
coredata.DocumentClassificationPublic: "PUBLIC",
coredata.DocumentClassificationInternal: "INTERNAL",
coredata.DocumentClassificationConfidential: "CONFIDENTIAL",
coredata.DocumentClassificationSecret: "SECRET",
}
)
func (ec *executionContext) unmarshalODocumentFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐDocumentFilter(ctx context.Context, v any) (*types.DocumentFilter, error) {
if v == nil {
return nil, nil

View File

@@ -78,6 +78,7 @@ func NewDocument(document *coredata.Document) *Document {
ID: document.ID,
Title: document.Title,
DocumentType: document.DocumentType,
Classification: document.Classification,
CurrentPublishedVersion: document.CurrentPublishedVersion,
TrustCenterVisibility: document.TrustCenterVisibility,
CreatedAt: document.CreatedAt,

View File

@@ -54,14 +54,15 @@ func NewDocumentVersionEdge(documentVersion *coredata.DocumentVersion, orderBy c
func NewDocumentVersion(documentVersion *coredata.DocumentVersion) *DocumentVersion {
return &DocumentVersion{
ID: documentVersion.ID,
Version: documentVersion.VersionNumber,
Title: documentVersion.Title,
Content: documentVersion.Content,
Status: documentVersion.Status,
PublishedAt: documentVersion.PublishedAt,
Changelog: documentVersion.Changelog,
CreatedAt: documentVersion.CreatedAt,
UpdatedAt: documentVersion.UpdatedAt,
ID: documentVersion.ID,
Version: documentVersion.VersionNumber,
Title: documentVersion.Title,
Content: documentVersion.Content,
Status: documentVersion.Status,
Classification: documentVersion.Classification,
PublishedAt: documentVersion.PublishedAt,
Changelog: documentVersion.Changelog,
CreatedAt: documentVersion.CreatedAt,
UpdatedAt: documentVersion.UpdatedAt,
}
}

View File

@@ -351,6 +351,7 @@ type CreateDocumentInput struct {
Content string `json:"content"`
OwnerID gid.GID `json:"ownerId"`
DocumentType coredata.DocumentType `json:"documentType"`
Classification coredata.DocumentClassification `json:"classification"`
TrustCenterVisibility *coredata.TrustCenterVisibility `json:"trustCenterVisibility,omitempty"`
}
@@ -1006,18 +1007,19 @@ type DeleteVendorServicePayload struct {
}
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"`
TrustCenterVisibility coredata.TrustCenterVisibility `json:"trustCenterVisibility"`
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"`
ID gid.GID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
DocumentType coredata.DocumentType `json:"documentType"`
Classification coredata.DocumentClassification `json:"classification"`
CurrentPublishedVersion *int `json:"currentPublishedVersion,omitempty"`
TrustCenterVisibility coredata.TrustCenterVisibility `json:"trustCenterVisibility"`
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() {}
@@ -1033,18 +1035,19 @@ type DocumentFilter struct {
}
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"`
Title string `json:"title"`
Owner *People `json:"owner"`
Signatures *DocumentVersionSignatureConnection `json:"signatures"`
PublishedAt *time.Time `json:"publishedAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
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"`
Title string `json:"title"`
Classification coredata.DocumentClassification `json:"classification"`
Owner *People `json:"owner"`
Signatures *DocumentVersionSignatureConnection `json:"signatures"`
PublishedAt *time.Time `json:"publishedAt,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
func (DocumentVersion) IsNode() {}
@@ -1789,12 +1792,13 @@ type UpdateDatumPayload struct {
}
type UpdateDocumentInput struct {
ID gid.GID `json:"id"`
Title *string `json:"title,omitempty"`
Content *string `json:"content,omitempty"`
OwnerID *gid.GID `json:"ownerId,omitempty"`
DocumentType *coredata.DocumentType `json:"documentType,omitempty"`
TrustCenterVisibility *coredata.TrustCenterVisibility `json:"trustCenterVisibility,omitempty"`
ID gid.GID `json:"id"`
Title *string `json:"title,omitempty"`
Content *string `json:"content,omitempty"`
OwnerID *gid.GID `json:"ownerId,omitempty"`
DocumentType *coredata.DocumentType `json:"documentType,omitempty"`
Classification *coredata.DocumentClassification `json:"classification,omitempty"`
TrustCenterVisibility *coredata.TrustCenterVisibility `json:"trustCenterVisibility,omitempty"`
}
type UpdateDocumentPayload struct {

View File

@@ -2566,11 +2566,14 @@ func (r *mutationResolver) UpdateDocument(ctx context.Context, input types.Updat
document, err := prb.Documents.Update(
ctx,
input.ID,
input.OwnerID,
input.DocumentType,
input.Title,
input.TrustCenterVisibility,
probo.UpdateDocumentRequest{
DocumentID: input.ID,
Title: input.Title,
OwnerID: input.OwnerID,
Classification: input.Classification,
DocumentType: input.DocumentType,
TrustCenterVisibility: input.TrustCenterVisibility,
},
)
if err != nil {