Move document classification from document to document version
Classification now lives exclusively on DocumentVersion. The field is removed from the Document model, all SQL queries, GraphQL Document type, SignableDocument type, UpdateDocumentInput, and MCP Document schema. New documents still accept classification in CreateDocumentInput, applied to the first version. New drafts inherit classification from the previous version. PDF generation uses the version classification. The drawer allows editing classification on draft versions via the updateDocumentVersion mutation. Classification is read-only on published versions. Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -30,18 +30,17 @@ import (
|
||||
|
||||
type (
|
||||
Document struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Title string `db:"title"`
|
||||
DocumentType DocumentType `db:"document_type"`
|
||||
Classification DocumentClassification `db:"classification"`
|
||||
CurrentPublishedMajor *int `db:"current_published_major"`
|
||||
CurrentPublishedMinor *int `db:"current_published_minor"`
|
||||
TrustCenterVisibility TrustCenterVisibility `db:"trust_center_visibility"`
|
||||
Status DocumentStatus `db:"status"`
|
||||
ArchivedAt *time.Time `db:"archived_at"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Title string `db:"title"`
|
||||
DocumentType DocumentType `db:"document_type"`
|
||||
CurrentPublishedMajor *int `db:"current_published_major"`
|
||||
CurrentPublishedMinor *int `db:"current_published_minor"`
|
||||
TrustCenterVisibility TrustCenterVisibility `db:"trust_center_visibility"`
|
||||
Status DocumentStatus `db:"status"`
|
||||
ArchivedAt *time.Time `db:"archived_at"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
Documents []*Document
|
||||
@@ -128,7 +127,6 @@ SELECT
|
||||
organization_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -182,7 +180,6 @@ SELECT
|
||||
organization_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -237,7 +234,6 @@ SELECT
|
||||
organization_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -321,7 +317,6 @@ SELECT
|
||||
organization_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -374,7 +369,6 @@ SELECT
|
||||
organization_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -441,7 +435,6 @@ SELECT
|
||||
organization_id,
|
||||
COALESCE(published_title, title) AS title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -491,7 +484,6 @@ INSERT INTO
|
||||
organization_id,
|
||||
title,
|
||||
document_type,
|
||||
classification,
|
||||
current_published_major,
|
||||
current_published_minor,
|
||||
trust_center_visibility,
|
||||
@@ -506,7 +498,6 @@ VALUES (
|
||||
@organization_id,
|
||||
@title,
|
||||
@document_type,
|
||||
@classification,
|
||||
@current_published_major,
|
||||
@current_published_minor,
|
||||
@trust_center_visibility,
|
||||
@@ -523,7 +514,6 @@ VALUES (
|
||||
"organization_id": p.OrganizationID,
|
||||
"title": p.Title,
|
||||
"document_type": p.DocumentType,
|
||||
"classification": p.Classification,
|
||||
"current_published_major": p.CurrentPublishedMajor,
|
||||
"current_published_minor": p.CurrentPublishedMinor,
|
||||
"trust_center_visibility": p.TrustCenterVisibility,
|
||||
@@ -586,7 +576,6 @@ SET
|
||||
current_published_major = @current_published_major,
|
||||
current_published_minor = @current_published_minor,
|
||||
document_type = @document_type,
|
||||
classification = @classification,
|
||||
trust_center_visibility = @trust_center_visibility,
|
||||
status = @status,
|
||||
archived_at = @archived_at,
|
||||
@@ -605,7 +594,6 @@ WHERE
|
||||
"current_published_major": p.CurrentPublishedMajor,
|
||||
"current_published_minor": p.CurrentPublishedMinor,
|
||||
"document_type": p.DocumentType,
|
||||
"classification": p.Classification,
|
||||
"trust_center_visibility": p.TrustCenterVisibility,
|
||||
"status": p.Status,
|
||||
"archived_at": p.ArchivedAt,
|
||||
@@ -678,7 +666,6 @@ SELECT
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
scoped_documents.current_published_major,
|
||||
scoped_documents.current_published_minor,
|
||||
scoped_documents.trust_center_visibility,
|
||||
@@ -770,7 +757,6 @@ SELECT
|
||||
scoped_documents.organization_id,
|
||||
scoped_documents.title,
|
||||
scoped_documents.document_type,
|
||||
scoped_documents.classification,
|
||||
scoped_documents.current_published_major,
|
||||
scoped_documents.current_published_minor,
|
||||
scoped_documents.trust_center_visibility,
|
||||
|
||||
2
pkg/coredata/migrations/20260328T120000Z.sql
Normal file
2
pkg/coredata/migrations/20260328T120000Z.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
-- TODO: drop the classification column from documents.
|
||||
ALTER TABLE documents ALTER COLUMN classification SET DEFAULT 'SECRET';
|
||||
@@ -85,14 +85,14 @@ type (
|
||||
UpdateDocumentRequest struct {
|
||||
DocumentID gid.GID
|
||||
Title *string
|
||||
Classification *coredata.DocumentClassification
|
||||
DocumentType *coredata.DocumentType
|
||||
TrustCenterVisibility *coredata.TrustCenterVisibility
|
||||
}
|
||||
|
||||
UpdateDocumentVersionRequest struct {
|
||||
ID gid.GID
|
||||
Content string
|
||||
ID gid.GID
|
||||
Content *string
|
||||
Classification *coredata.DocumentClassification
|
||||
}
|
||||
|
||||
RequestSignatureRequest struct {
|
||||
@@ -133,7 +133,6 @@ func (udr *UpdateDocumentRequest) Validate() error {
|
||||
|
||||
v.Check(udr.DocumentID, "document_id", validator.Required(), validator.GID(coredata.DocumentEntityType))
|
||||
v.Check(udr.Title, "title", validator.SafeTextNoNewLine(TitleMaxLength))
|
||||
v.Check(udr.Classification, "classification", validator.OneOfSlice(coredata.DocumentClassifications()))
|
||||
v.Check(udr.DocumentType, "document_type", validator.OneOfSlice(coredata.DocumentTypes()))
|
||||
v.Check(udr.TrustCenterVisibility, "trust_center_visibility", validator.OneOfSlice(coredata.TrustCenterVisibilities()))
|
||||
|
||||
@@ -144,7 +143,8 @@ func (udvr *UpdateDocumentVersionRequest) Validate() error {
|
||||
v := validator.New()
|
||||
|
||||
v.Check(udvr.ID, "id", validator.Required(), validator.GID(coredata.DocumentVersionEntityType))
|
||||
v.Check(udvr.Content, "content", validator.Required(), validator.NotEmpty(), validator.MaxLen(documentMaxLength))
|
||||
v.Check(udvr.Content, "content", validator.NotEmpty(), validator.MaxLen(documentMaxLength))
|
||||
v.Check(udvr.Classification, "classification", validator.OneOfSlice(coredata.DocumentClassifications()))
|
||||
|
||||
return v.Error()
|
||||
}
|
||||
@@ -509,7 +509,6 @@ func (s *DocumentService) Create(
|
||||
Title: req.Title,
|
||||
DocumentType: req.DocumentType,
|
||||
TrustCenterVisibility: coredata.TrustCenterVisibilityNone,
|
||||
Classification: req.Classification,
|
||||
Status: coredata.DocumentStatusActive,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
@@ -763,8 +762,12 @@ func (s *DocumentService) UpdateVersion(
|
||||
}
|
||||
|
||||
documentVersion.Title = document.Title
|
||||
documentVersion.Classification = document.Classification
|
||||
documentVersion.Content = req.Content
|
||||
if req.Content != nil {
|
||||
documentVersion.Content = *req.Content
|
||||
}
|
||||
if req.Classification != nil {
|
||||
documentVersion.Classification = *req.Classification
|
||||
}
|
||||
documentVersion.UpdatedAt = time.Now()
|
||||
|
||||
if err := documentVersion.Update(ctx, conn, s.svc.scope); err != nil {
|
||||
@@ -1002,7 +1005,7 @@ func (s *DocumentService) CreateDraft(
|
||||
draftVersion.Title = document.Title
|
||||
draftVersion.Major = latestVersion.Major
|
||||
draftVersion.Minor = latestVersion.Minor + 1
|
||||
draftVersion.Classification = document.Classification
|
||||
draftVersion.Classification = latestVersion.Classification
|
||||
draftVersion.Content = latestVersion.Content
|
||||
draftVersion.Status = coredata.DocumentVersionStatusDraft
|
||||
draftVersion.CreatedAt = now
|
||||
@@ -1533,10 +1536,6 @@ func (s *DocumentService) Update(
|
||||
document.Title = *req.Title
|
||||
}
|
||||
|
||||
if req.Classification != nil {
|
||||
document.Classification = *req.Classification
|
||||
}
|
||||
|
||||
if req.DocumentType != nil {
|
||||
document.DocumentType = *req.DocumentType
|
||||
}
|
||||
@@ -1559,7 +1558,6 @@ func (s *DocumentService) Update(
|
||||
err := draftVersion.LoadLatestVersion(ctx, tx, s.svc.scope, req.DocumentID)
|
||||
if err == nil && draftVersion.Status == coredata.DocumentVersionStatusDraft {
|
||||
draftVersion.Title = document.Title
|
||||
draftVersion.Classification = document.Classification
|
||||
draftVersion.UpdatedAt = now
|
||||
|
||||
if err := draftVersion.Update(ctx, tx, s.svc.scope); err != nil {
|
||||
@@ -1934,7 +1932,7 @@ func exportDocumentPDF(
|
||||
}
|
||||
|
||||
classification := docgen.ClassificationSecret
|
||||
switch document.Classification {
|
||||
switch version.Classification {
|
||||
case coredata.DocumentClassificationPublic:
|
||||
classification = docgen.ClassificationPublic
|
||||
case coredata.DocumentClassificationInternal:
|
||||
|
||||
@@ -2440,7 +2440,6 @@ type Document implements Node {
|
||||
title: String!
|
||||
description: String
|
||||
documentType: DocumentType!
|
||||
classification: DocumentClassification!
|
||||
currentPublishedMajor: Int
|
||||
currentPublishedMinor: Int
|
||||
trustCenterVisibility: TrustCenterVisibility!
|
||||
@@ -2481,7 +2480,6 @@ type EmployeeDocument
|
||||
title: String!
|
||||
description: String
|
||||
documentType: DocumentType!
|
||||
classification: DocumentClassification!
|
||||
signed: Boolean @goField(forceResolver: true)
|
||||
approvalState: DocumentVersionApprovalDecisionState @goField(forceResolver: true)
|
||||
|
||||
@@ -2505,6 +2503,7 @@ type EmployeeDocumentVersion
|
||||
major: Int!
|
||||
minor: Int!
|
||||
status: DocumentVersionStatus!
|
||||
classification: DocumentClassification!
|
||||
signed: Boolean! @goField(forceResolver: true)
|
||||
approvalDecision: DocumentVersionApprovalDecision @goField(forceResolver: true)
|
||||
publishedAt: Datetime
|
||||
@@ -4482,7 +4481,6 @@ input UpdateDocumentInput {
|
||||
title: String
|
||||
content: String
|
||||
documentType: DocumentType
|
||||
classification: DocumentClassification
|
||||
trustCenterVisibility: TrustCenterVisibility
|
||||
}
|
||||
|
||||
@@ -5738,7 +5736,8 @@ input DeleteDraftDocumentVersionInput {
|
||||
|
||||
input UpdateDocumentVersionInput {
|
||||
documentVersionId: ID!
|
||||
content: String!
|
||||
content: String
|
||||
classification: DocumentClassification
|
||||
}
|
||||
|
||||
input CancelSignatureRequestInput {
|
||||
|
||||
@@ -81,7 +81,6 @@ func NewDocument(document *coredata.Document) *Document {
|
||||
ID: document.OrganizationID,
|
||||
},
|
||||
DocumentType: document.DocumentType,
|
||||
Classification: document.Classification,
|
||||
CurrentPublishedMajor: document.CurrentPublishedMajor,
|
||||
CurrentPublishedMinor: document.CurrentPublishedMinor,
|
||||
TrustCenterVisibility: document.TrustCenterVisibility,
|
||||
|
||||
@@ -42,13 +42,12 @@ type (
|
||||
}
|
||||
|
||||
EmployeeDocument struct {
|
||||
ID gid.GID
|
||||
Title string
|
||||
Description *string
|
||||
DocumentType coredata.DocumentType
|
||||
Classification coredata.DocumentClassification
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
ID gid.GID
|
||||
Title string
|
||||
Description *string
|
||||
DocumentType coredata.DocumentType
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
|
||||
FilterMode EmployeeDocumentFilterMode
|
||||
}
|
||||
@@ -69,6 +68,7 @@ type (
|
||||
Major int
|
||||
Minor int
|
||||
Status coredata.DocumentVersionStatus
|
||||
Classification coredata.DocumentClassification
|
||||
PublishedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
|
||||
@@ -1664,6 +1664,7 @@ func (r *employeeDocumentResolver) Versions(ctx context.Context, obj *types.Empl
|
||||
Major: v.Major,
|
||||
Minor: v.Minor,
|
||||
Status: v.Status,
|
||||
Classification: v.Classification,
|
||||
PublishedAt: v.PublishedAt,
|
||||
CreatedAt: v.CreatedAt,
|
||||
UpdatedAt: v.UpdatedAt,
|
||||
@@ -4669,7 +4670,6 @@ func (r *mutationResolver) UpdateDocument(ctx context.Context, input types.Updat
|
||||
probo.UpdateDocumentRequest{
|
||||
DocumentID: input.ID,
|
||||
Title: input.Title,
|
||||
Classification: input.Classification,
|
||||
DocumentType: input.DocumentType,
|
||||
TrustCenterVisibility: input.TrustCenterVisibility,
|
||||
},
|
||||
@@ -5410,8 +5410,9 @@ func (r *mutationResolver) UpdateDocumentVersion(ctx context.Context, input type
|
||||
documentVersion, err := prb.Documents.UpdateVersion(
|
||||
ctx,
|
||||
probo.UpdateDocumentVersionRequest{
|
||||
ID: input.DocumentVersionID,
|
||||
Content: input.Content,
|
||||
ID: input.DocumentVersionID,
|
||||
Content: input.Content,
|
||||
Classification: input.Classification,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -10243,13 +10244,12 @@ func (r *viewerResolver) SignableDocuments(ctx context.Context, obj *types.Viewe
|
||||
employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data))
|
||||
for i, doc := range documentsPage.Data {
|
||||
employeeDocuments[i] = &types.EmployeeDocument{
|
||||
ID: doc.ID,
|
||||
Title: doc.Title,
|
||||
DocumentType: doc.DocumentType,
|
||||
Classification: doc.Classification,
|
||||
CreatedAt: doc.CreatedAt,
|
||||
UpdatedAt: doc.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeSignature,
|
||||
ID: doc.ID,
|
||||
Title: doc.Title,
|
||||
DocumentType: doc.DocumentType,
|
||||
CreatedAt: doc.CreatedAt,
|
||||
UpdatedAt: doc.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeSignature,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10280,13 +10280,12 @@ func (r *viewerResolver) SignableDocument(ctx context.Context, obj *types.Viewer
|
||||
}
|
||||
|
||||
return &types.EmployeeDocument{
|
||||
ID: document.ID,
|
||||
Title: document.Title,
|
||||
DocumentType: document.DocumentType,
|
||||
Classification: document.Classification,
|
||||
CreatedAt: document.CreatedAt,
|
||||
UpdatedAt: document.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeSignature,
|
||||
ID: document.ID,
|
||||
Title: document.Title,
|
||||
DocumentType: document.DocumentType,
|
||||
CreatedAt: document.CreatedAt,
|
||||
UpdatedAt: document.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeSignature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -10324,13 +10323,12 @@ func (r *viewerResolver) ApprovableDocuments(ctx context.Context, obj *types.Vie
|
||||
employeeDocuments := make([]*types.EmployeeDocument, len(documentsPage.Data))
|
||||
for i, doc := range documentsPage.Data {
|
||||
employeeDocuments[i] = &types.EmployeeDocument{
|
||||
ID: doc.ID,
|
||||
Title: doc.Title,
|
||||
DocumentType: doc.DocumentType,
|
||||
Classification: doc.Classification,
|
||||
CreatedAt: doc.CreatedAt,
|
||||
UpdatedAt: doc.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeApproval,
|
||||
ID: doc.ID,
|
||||
Title: doc.Title,
|
||||
DocumentType: doc.DocumentType,
|
||||
CreatedAt: doc.CreatedAt,
|
||||
UpdatedAt: doc.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeApproval,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10361,13 +10359,12 @@ func (r *viewerResolver) ApprovableDocument(ctx context.Context, obj *types.View
|
||||
}
|
||||
|
||||
return &types.EmployeeDocument{
|
||||
ID: document.ID,
|
||||
Title: document.Title,
|
||||
DocumentType: document.DocumentType,
|
||||
Classification: document.Classification,
|
||||
CreatedAt: document.CreatedAt,
|
||||
UpdatedAt: document.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeApproval,
|
||||
ID: document.ID,
|
||||
Title: document.Title,
|
||||
DocumentType: document.DocumentType,
|
||||
CreatedAt: document.CreatedAt,
|
||||
UpdatedAt: document.UpdatedAt,
|
||||
FilterMode: types.EmployeeDocumentFilterModeApproval,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2106,7 +2106,6 @@ func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequ
|
||||
probo.UpdateDocumentRequest{
|
||||
DocumentID: input.ID,
|
||||
Title: input.Title,
|
||||
Classification: input.Classification,
|
||||
DocumentType: input.DocumentType,
|
||||
TrustCenterVisibility: input.TrustCenterVisibility,
|
||||
},
|
||||
@@ -2183,8 +2182,9 @@ func (r *Resolver) UpdateDocumentVersionTool(ctx context.Context, req *mcp.CallT
|
||||
documentVersion, err := svc.Documents.UpdateVersion(
|
||||
ctx,
|
||||
probo.UpdateDocumentVersionRequest{
|
||||
ID: input.DocumentVersionID,
|
||||
Content: input.Content,
|
||||
ID: input.DocumentVersionID,
|
||||
Content: input.Content,
|
||||
Classification: input.Classification,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -5186,7 +5186,6 @@ components:
|
||||
- organization_id
|
||||
- title
|
||||
- document_type
|
||||
- classification
|
||||
- trust_center_visibility
|
||||
- status
|
||||
- created_at
|
||||
@@ -5204,9 +5203,6 @@ components:
|
||||
document_type:
|
||||
$ref: "#/components/schemas/DocumentType"
|
||||
description: Document type
|
||||
classification:
|
||||
$ref: "#/components/schemas/DocumentClassification"
|
||||
description: Document classification
|
||||
current_published_major:
|
||||
type:
|
||||
- integer
|
||||
@@ -5459,9 +5455,6 @@ components:
|
||||
title:
|
||||
type: string
|
||||
description: Document title
|
||||
classification:
|
||||
$ref: "#/components/schemas/DocumentClassification"
|
||||
description: Document classification
|
||||
document_type:
|
||||
$ref: "#/components/schemas/DocumentType"
|
||||
description: Document type
|
||||
@@ -5580,7 +5573,6 @@ components:
|
||||
type: object
|
||||
required:
|
||||
- document_version_id
|
||||
- content
|
||||
properties:
|
||||
document_version_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
@@ -5588,6 +5580,9 @@ components:
|
||||
content:
|
||||
type: string
|
||||
description: Document content
|
||||
classification:
|
||||
$ref: "#/components/schemas/DocumentClassification"
|
||||
description: Document classification
|
||||
|
||||
UpdateDocumentVersionOutput:
|
||||
type: object
|
||||
|
||||
@@ -25,7 +25,6 @@ func NewDocument(d *coredata.Document) *Document {
|
||||
OrganizationID: d.OrganizationID,
|
||||
Title: d.Title,
|
||||
DocumentType: d.DocumentType,
|
||||
Classification: d.Classification,
|
||||
CurrentPublishedMajor: d.CurrentPublishedMajor,
|
||||
CurrentPublishedMinor: d.CurrentPublishedMinor,
|
||||
TrustCenterVisibility: d.TrustCenterVisibility,
|
||||
|
||||
@@ -219,12 +219,14 @@ func (s *DocumentService) exportPDFData(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
classification := docgen.ClassificationInternal
|
||||
switch document.DocumentType {
|
||||
case coredata.DocumentTypePolicy:
|
||||
classification := docgen.ClassificationSecret
|
||||
switch version.Classification {
|
||||
case coredata.DocumentClassificationPublic:
|
||||
classification = docgen.ClassificationPublic
|
||||
case coredata.DocumentClassificationInternal:
|
||||
classification = docgen.ClassificationInternal
|
||||
case coredata.DocumentClassificationConfidential:
|
||||
classification = docgen.ClassificationConfidential
|
||||
case coredata.DocumentTypeGovernance:
|
||||
classification = docgen.ClassificationSecret
|
||||
}
|
||||
|
||||
horizontalLogoBase64 := ""
|
||||
|
||||
Reference in New Issue
Block a user