Consolidate document draft management into updateDocument

Replace the three separate draft mutations (createDraftDocumentVersion,
updateDocumentVersion, deleteDraftDocumentVersion) with automatic draft
lifecycle management inside updateDocument. The backend now auto-creates
a draft when a published document is edited, updates the existing draft
on subsequent edits, and auto-deletes the draft when content reverts to
match the published version.

A new deleteDocumentDraft mutation provides explicit draft deletion.

Backend:
- Merge version-level fields (content, title, classification,
  documentType) into UpdateDocumentRequest
- Convert CreateDraft, UpdateVersion, DeleteDraft into private
  transaction helpers called from Update
- Update returns (*Document, *DocumentVersion, error) with the version
  present only when a draft exists

Frontend:
- Remove all create/update/delete draft mutations from components
- Auto-save via updateDocument with layout refetch on draft status
  transitions while preserving editor cursor (data-generation key)
- Title, type, and classification editable on published versions
  (backend auto-creates draft)
- Forms use react-hook-form values option to stay synced with Relay
  fragment data across draft/publish transitions

API surface (GraphQL, MCP, CLI, n8n) updated consistently:
- Removed: createDraftDocumentVersion, updateDocumentVersion,
  deleteDraftDocumentVersion
- Added: deleteDocumentDraft (document-level)
- Updated: updateDocument accepts content, classification, documentType

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-04-14 14:37:24 +02:00
parent 74d7d3ff25
commit 03708d45c3
21 changed files with 1054 additions and 1087 deletions

View File

@@ -2115,10 +2115,23 @@ func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequ
defaultApproverIDs = &input.DefaultApproverIds
}
document, err := svc.Documents.Update(
var content *string
if input.Content != nil {
c, err := markdownToProseMirrorJSON(*input.Content)
if err != nil {
panic(fmt.Errorf("cannot convert markdown to prosemirror: %w", err))
}
content = &c
}
document, documentVersion, _, err := svc.Documents.Update(
ctx,
probo.UpdateDocumentRequest{
DocumentID: input.ID,
Title: input.Title,
Content: content,
Classification: input.Classification,
DocumentType: input.DocumentType,
TrustCenterVisibility: input.TrustCenterVisibility,
DefaultApproverIDs: defaultApproverIDs,
},
@@ -2127,9 +2140,15 @@ func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequ
panic(fmt.Errorf("cannot update document: %w", err))
}
return nil, types.UpdateDocumentOutput{
output := types.UpdateDocumentOutput{
Document: types.NewDocument(document),
}, nil
}
if documentVersion != nil {
output.DocumentVersion = types.NewDocumentVersion(documentVersion)
}
return nil, output, nil
}
func (r *Resolver) ListDocumentVersionsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListDocumentVersionsInput) (*mcp.CallToolResult, types.ListDocumentVersionsOutput, error) {
@@ -2172,72 +2191,6 @@ func (r *Resolver) GetDocumentVersionTool(ctx context.Context, req *mcp.CallTool
}, nil
}
func (r *Resolver) CreateDraftDocumentVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.CreateDraftDocumentVersionInput) (*mcp.CallToolResult, types.CreateDraftDocumentVersionOutput, error) {
r.MustAuthorize(ctx, input.DocumentID, probo.ActionDocumentDraftVersionCreate)
svc := r.ProboService(ctx, input.DocumentID)
draftVersion, err := svc.Documents.CreateDraft(ctx, input.DocumentID)
if err != nil {
panic(fmt.Errorf("cannot create draft document version: %w", err))
}
if input.Content != nil {
content, err := markdownToProseMirrorJSON(*input.Content)
if err != nil {
panic(fmt.Errorf("cannot convert markdown to prosemirror: %w", err))
}
draftVersion, err = svc.Documents.UpdateVersion(
ctx,
probo.UpdateDocumentVersionRequest{
ID: draftVersion.ID,
Content: &content,
},
)
if err != nil {
panic(fmt.Errorf("cannot update draft document version content: %w", err))
}
}
return nil, types.CreateDraftDocumentVersionOutput{
DocumentVersion: types.NewDocumentVersion(draftVersion),
}, nil
}
func (r *Resolver) UpdateDocumentVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateDocumentVersionInput) (*mcp.CallToolResult, types.UpdateDocumentVersionOutput, error) {
r.MustAuthorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionUpdate)
svc := r.ProboService(ctx, input.DocumentVersionID)
var content *string
if input.Content != nil {
c, err := markdownToProseMirrorJSON(*input.Content)
if err != nil {
panic(fmt.Errorf("cannot convert markdown to prosemirror: %w", err))
}
content = &c
}
documentVersion, err := svc.Documents.UpdateVersion(
ctx,
probo.UpdateDocumentVersionRequest{
ID: input.DocumentVersionID,
Title: input.Title,
Content: content,
Classification: input.Classification,
DocumentType: input.DocumentType,
},
)
if err != nil {
panic(fmt.Errorf("cannot update document version: %w", err))
}
return nil, types.UpdateDocumentVersionOutput{
DocumentVersion: types.NewDocumentVersion(documentVersion),
}, nil
}
func (r *Resolver) ListDocumentVersionSignaturesTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListDocumentVersionSignaturesInput) (*mcp.CallToolResult, types.ListDocumentVersionSignaturesOutput, error) {
r.MustAuthorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionSignatureList)
@@ -2312,21 +2265,6 @@ func (r *Resolver) RequestDocumentVersionSignatureTool(ctx context.Context, req
}, nil
}
func (r *Resolver) DeleteDraftDocumentVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteDraftDocumentVersionInput) (*mcp.CallToolResult, types.DeleteDraftDocumentVersionOutput, error) {
r.MustAuthorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionDeleteDraft)
svc := r.ProboService(ctx, input.DocumentVersionID)
err := svc.Documents.DeleteDraft(ctx, input.DocumentVersionID)
if err != nil {
panic(fmt.Errorf("cannot delete draft document version: %w", err))
}
return nil, types.DeleteDraftDocumentVersionOutput{
DeletedDocumentVersionID: input.DocumentVersionID,
}, nil
}
func (r *Resolver) DeleteDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteDocumentInput) (*mcp.CallToolResult, types.DeleteDocumentOutput, error) {
r.MustAuthorize(ctx, input.DocumentID, probo.ActionDocumentDelete)
@@ -3971,3 +3909,18 @@ func (r *Resolver) SendSigningNotificationsTool(ctx context.Context, req *mcp.Ca
Success: true,
}, nil
}
func (r *Resolver) DeleteDocumentDraftTool(ctx context.Context, req *mcp.CallToolRequest, input *types.DeleteDocumentDraftInput) (*mcp.CallToolResult, types.DeleteDocumentDraftOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionDocumentDeleteDraft)
svc := r.ProboService(ctx, input.ID)
document, err := svc.Documents.DeleteDraft(ctx, input.ID)
if err != nil {
return nil, types.DeleteDocumentDraftOutput{}, fmt.Errorf("cannot delete document draft: %w", err)
}
return nil, types.DeleteDocumentDraftOutput{
Document: types.NewDocument(document),
}, nil
}

View File

@@ -5517,6 +5517,18 @@ components:
id:
$ref: "#/components/schemas/GID"
description: Document ID
title:
type: string
description: Document title
content:
type: string
description: Document content in markdown format
classification:
$ref: "#/components/schemas/DocumentClassification"
description: Document classification
document_type:
$ref: "#/components/schemas/DocumentType"
description: Document type
trust_center_visibility:
$ref: "#/components/schemas/TrustCenterVisibility"
description: Trust center visibility
@@ -5527,6 +5539,25 @@ components:
description: Default approver profile IDs
UpdateDocumentOutput:
type: object
required:
- document
properties:
document:
$ref: "#/components/schemas/Document"
document_version:
$ref: "#/components/schemas/DocumentVersion"
DeleteDocumentDraftInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Document ID
DeleteDocumentDraftOutput:
type: object
required:
- document
@@ -5618,75 +5649,6 @@ components:
document_version:
$ref: "#/components/schemas/DocumentVersion"
CreateDraftDocumentVersionInput:
type: object
required:
- document_id
properties:
document_id:
$ref: "#/components/schemas/GID"
description: Document ID
content:
type: string
description: Document content in markdown format
CreateDraftDocumentVersionOutput:
type: object
description: Created draft; document_version.content is markdown
required:
- document_version
properties:
document_version:
$ref: "#/components/schemas/DocumentVersion"
UpdateDocumentVersionInput:
type: object
required:
- document_version_id
properties:
document_version_id:
$ref: "#/components/schemas/GID"
description: Document version ID
title:
type: string
description: Document version title
content:
type: string
description: Document content in markdown format
classification:
$ref: "#/components/schemas/DocumentClassification"
description: Document classification
document_type:
$ref: "#/components/schemas/DocumentType"
description: Document type
UpdateDocumentVersionOutput:
type: object
description: Updated draft; document_version.content is markdown
required:
- document_version
properties:
document_version:
$ref: "#/components/schemas/DocumentVersion"
DeleteDraftDocumentVersionInput:
type: object
required:
- document_version_id
properties:
document_version_id:
$ref: "#/components/schemas/GID"
description: Document version ID
DeleteDraftDocumentVersionOutput:
type: object
required:
- deleted_document_version_id
properties:
deleted_document_version_id:
$ref: "#/components/schemas/GID"
description: Deleted document version ID
PublishMajorDocumentVersionInput:
type: object
required:
@@ -8398,6 +8360,15 @@ tools:
$ref: "#/components/schemas/UpdateDocumentInput"
outputSchema:
$ref: "#/components/schemas/UpdateDocumentOutput"
- name: deleteDocumentDraft
description: Delete the latest draft version of a document, reverting to the last published version. Cannot delete the initial v0.1 draft.
hints:
readonly: false
destructive: true
inputSchema:
$ref: "#/components/schemas/DeleteDocumentDraftInput"
outputSchema:
$ref: "#/components/schemas/DeleteDocumentDraftOutput"
- name: archiveDocument
description: Archive a document to prevent further modifications
hints:
@@ -8432,30 +8403,6 @@ tools:
$ref: "#/components/schemas/GetDocumentVersionInput"
outputSchema:
$ref: "#/components/schemas/GetDocumentVersionOutput"
- name: createDraftDocumentVersion
description: Create a new draft version from the latest published version
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/CreateDraftDocumentVersionInput"
outputSchema:
$ref: "#/components/schemas/CreateDraftDocumentVersionOutput"
- name: updateDocumentVersion
description: Update an existing draft document version content
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/UpdateDocumentVersionInput"
outputSchema:
$ref: "#/components/schemas/UpdateDocumentVersionOutput"
- name: deleteDraftDocumentVersion
description: Delete a draft document version
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/DeleteDraftDocumentVersionInput"
outputSchema:
$ref: "#/components/schemas/DeleteDraftDocumentVersionOutput"
- name: publishMajorDocumentVersion
description: Publish a draft document version as a new major version
hints: