Files
probo/packages/n8n-node/nodes/Probo/actions/document/publish.operation.ts
Sacha Al Himdani 04a34c9757 Require explicit approver_ids when publishing a document major version
The publish flow ignored a document's stored default approvers and only
requested approval when approver_ids were passed in the call, so a major
publish with no approver_ids silently published directly without routing
through the approval flow — there was no way to tell "caller forgot
approvers" (null) from "caller wants no approval" (empty).

Make approver_ids an explicit choice, enforced once in the service so it
covers every caller (console, MCP, n8n):
- major publish: approver_ids must be set; an empty list publishes
  directly, a non-empty list requests approval.
- minor publish: approver_ids must be omitted (approvers are ignored).

Validate this in PublishDocumentRequest.Validate(), update the console
publish dialog and the n8n publish node to honour the contract, document
it in the MCP tool spec, and cover it with e2e tests.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-11 11:41:30 +02:00

142 lines
3.6 KiB
TypeScript

// Copyright (c) 2025-2026 Probo Inc <hello@probo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
import type { INodeProperties, IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
import { proboApiRequest } from '../../GenericFunctions';
export const description: INodeProperties[] = [
{
displayName: 'Document ID',
name: 'documentId',
type: 'string',
displayOptions: {
show: {
resource: ['document'],
operation: ['publish'],
},
},
default: '',
description: 'The ID of the document',
required: true,
},
{
displayName: 'Minor',
name: 'minor',
type: 'boolean',
displayOptions: {
show: {
resource: ['document'],
operation: ['publish'],
},
},
default: false,
description: 'Whether to publish as a minor version. Approvers are ignored when set.',
},
{
displayName: 'Approver IDs',
name: 'approverIds',
type: 'string',
displayOptions: {
show: {
resource: ['document'],
operation: ['publish'],
minor: [false],
},
},
default: '',
description: 'Comma-separated list of approver profile IDs. Provide IDs to request approval; leave empty to publish the major version directly without approval.',
},
{
displayName: 'Changelog',
name: 'changelog',
type: 'string',
typeOptions: {
rows: 4,
},
displayOptions: {
show: {
resource: ['document'],
operation: ['publish'],
},
},
default: '',
description: 'The changelog for this version',
required: true,
},
];
export async function execute(
this: IExecuteFunctions,
itemIndex: number,
): Promise<INodeExecutionData> {
const documentId = this.getNodeParameter('documentId', itemIndex) as string;
const minor = this.getNodeParameter('minor', itemIndex, false) as boolean;
const approverIdsRaw = this.getNodeParameter('approverIds', itemIndex, '') as string;
const changelog = this.getNodeParameter('changelog', itemIndex) as string;
const query = `
mutation PublishDocument($input: PublishDocumentInput!) {
publishDocument(input: $input) {
document {
id
status
trustCenterVisibility
currentPublishedMajor
currentPublishedMinor
createdAt
updatedAt
}
documentVersion {
id
title
major
minor
status
content
changelog
classification
documentType
publishedAt
createdAt
updatedAt
}
approvalQuorum {
id
status
createdAt
updatedAt
}
}
}
`;
const input: Record<string, unknown> = { documentId, minor, changelog };
if (!minor) {
// A major publish must set approverIds explicitly: an empty list publishes
// directly without approval, a non-empty list requests approval. It must be
// omitted for a minor publish, which ignores approvers.
input.approverIds = approverIdsRaw
.split(',')
.map(id => id.trim())
.filter(Boolean);
}
const responseData = await proboApiRequest.call(this, query, { input });
return {
json: responseData,
pairedItem: { item: itemIndex },
};
}