Add document approval workflow
Introduce a complete approval system for document publishing. Document versions can now require approval from selected reviewers before being published, with automatic publishing once all approvers have approved. - Add approval quorum and decision tables with backfill migration - Implement request approval, approve, and reject flows with electronic signature support for approve decisions - Add employee approvals page with dedicated tab and pending approvals view - Add changelog field to publish and request approval flows - Pre-select previous version's approvers in the publish dialog - Show quorum approvers in document list with 100 approver hard limit - Expose approval workflow through GraphQL, MCP, and CLI - Remove legacy default approvers feature entirely - Add comprehensive e2e test coverage for approval workflows Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -1623,7 +1623,7 @@ input ApplicabilityStatementOrder
|
||||
}
|
||||
|
||||
input DocumentVersionFilter {
|
||||
status: DocumentVersionStatus
|
||||
statuses: [DocumentVersionStatus!]
|
||||
}
|
||||
|
||||
# Input Types for Filtering
|
||||
@@ -2441,13 +2441,6 @@ type Document implements Node {
|
||||
classification: DocumentClassification!
|
||||
currentPublishedVersion: Int
|
||||
trustCenterVisibility: TrustCenterVisibility!
|
||||
approvers(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: ProfileOrder
|
||||
): ProfileConnection! @goField(forceResolver: true)
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
|
||||
versions(
|
||||
@@ -2477,16 +2470,17 @@ type Document implements Node {
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type SignableDocument
|
||||
type EmployeeDocument
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SignableDocument"
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocument"
|
||||
) {
|
||||
id: ID!
|
||||
title: String!
|
||||
description: String
|
||||
documentType: DocumentType!
|
||||
classification: DocumentClassification!
|
||||
signed: Boolean! @goField(forceResolver: true)
|
||||
signed: Boolean @goField(forceResolver: true)
|
||||
approvalState: DocumentVersionApprovalDecisionState @goField(forceResolver: true)
|
||||
|
||||
versions(
|
||||
first: Int
|
||||
@@ -2494,13 +2488,26 @@ type SignableDocument
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentVersionOrder
|
||||
filter: DocumentVersionFilter
|
||||
): DocumentVersionConnection! @goField(forceResolver: true)
|
||||
): EmployeeDocumentVersionConnection! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type EmployeeDocumentVersion
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentVersion"
|
||||
) {
|
||||
id: ID!
|
||||
version: Int!
|
||||
status: DocumentVersionStatus!
|
||||
signed: Boolean! @goField(forceResolver: true)
|
||||
approvalDecision: DocumentVersionApprovalDecision @goField(forceResolver: true)
|
||||
publishedAt: Datetime
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type Meeting implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
@@ -2952,9 +2959,20 @@ type Viewer {
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentOrder
|
||||
): SignableDocumentConnection! @goField(forceResolver: true)
|
||||
): EmployeeDocumentConnection! @goField(forceResolver: true)
|
||||
|
||||
signableDocument(id: ID!): SignableDocument @goField(forceResolver: true)
|
||||
signableDocument(id: ID!): EmployeeDocument @goField(forceResolver: true)
|
||||
|
||||
approvableDocuments(
|
||||
organizationId: ID!
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentOrder
|
||||
): EmployeeDocumentConnection! @goField(forceResolver: true)
|
||||
|
||||
approvableDocument(id: ID!): EmployeeDocument @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type TrustCenterConnection {
|
||||
@@ -3226,20 +3244,36 @@ type EvidenceEdge {
|
||||
node: Evidence!
|
||||
}
|
||||
|
||||
type SignableDocumentConnection
|
||||
type EmployeeDocumentConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SignableDocumentConnection"
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentConnection"
|
||||
) {
|
||||
edges: [SignableDocumentEdge!]!
|
||||
edges: [EmployeeDocumentEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type SignableDocumentEdge
|
||||
type EmployeeDocumentEdge
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.SignableDocumentEdge"
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentEdge"
|
||||
) {
|
||||
cursor: CursorKey!
|
||||
node: SignableDocument!
|
||||
node: EmployeeDocument!
|
||||
}
|
||||
|
||||
type EmployeeDocumentVersionConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentVersionConnection"
|
||||
) {
|
||||
edges: [EmployeeDocumentVersionEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type EmployeeDocumentVersionEdge
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.EmployeeDocumentVersionEdge"
|
||||
) {
|
||||
cursor: CursorKey!
|
||||
node: EmployeeDocumentVersion!
|
||||
}
|
||||
|
||||
type DocumentConnection
|
||||
@@ -3762,6 +3796,9 @@ type Mutation {
|
||||
bulkPublishDocumentVersions(
|
||||
input: BulkPublishDocumentVersionsInput!
|
||||
): BulkPublishDocumentVersionsPayload!
|
||||
requestDocumentVersionApproval(
|
||||
input: RequestDocumentVersionApprovalInput!
|
||||
): RequestDocumentVersionApprovalPayload!
|
||||
bulkDeleteDocuments(
|
||||
input: BulkDeleteDocumentsInput!
|
||||
): BulkDeleteDocumentsPayload!
|
||||
@@ -3797,6 +3834,18 @@ type Mutation {
|
||||
input: CancelSignatureRequestInput!
|
||||
): CancelSignatureRequestPayload!
|
||||
signDocument(input: SignDocumentInput!): SignDocumentPayload!
|
||||
addDocumentVersionApprover(
|
||||
input: AddDocumentVersionApproverInput!
|
||||
): AddDocumentVersionApproverPayload!
|
||||
removeDocumentVersionApprover(
|
||||
input: RemoveDocumentVersionApproverInput!
|
||||
): RemoveDocumentVersionApproverPayload!
|
||||
approveDocumentVersion(
|
||||
input: ApproveDocumentVersionInput!
|
||||
): ApproveDocumentVersionPayload!
|
||||
rejectDocumentVersion(
|
||||
input: RejectDocumentVersionInput!
|
||||
): RejectDocumentVersionPayload!
|
||||
|
||||
exportDocumentVersionPDF(
|
||||
input: ExportDocumentVersionPDFInput!
|
||||
@@ -4409,7 +4458,6 @@ input CreateDocumentInput {
|
||||
organizationId: ID!
|
||||
title: String!
|
||||
content: String!
|
||||
approverIds: [ID!]!
|
||||
documentType: DocumentType!
|
||||
classification: DocumentClassification!
|
||||
trustCenterVisibility: TrustCenterVisibility
|
||||
@@ -4419,7 +4467,6 @@ input UpdateDocumentInput {
|
||||
id: ID!
|
||||
title: String
|
||||
content: String
|
||||
approverIds: [ID!]
|
||||
documentType: DocumentType
|
||||
classification: DocumentClassification
|
||||
trustCenterVisibility: TrustCenterVisibility
|
||||
@@ -5324,6 +5371,14 @@ type DocumentVersion implements Node {
|
||||
filter: DocumentVersionSignatureFilter
|
||||
): DocumentVersionSignatureConnection! @goField(forceResolver: true)
|
||||
|
||||
approvalQuorums(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentVersionApprovalQuorumOrder
|
||||
): DocumentVersionApprovalQuorumConnection! @goField(forceResolver: true)
|
||||
|
||||
signed: Boolean! @goField(forceResolver: true)
|
||||
|
||||
publishedAt: Datetime
|
||||
@@ -5398,6 +5453,170 @@ type DocumentVersionSignature implements Node {
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
enum DocumentVersionApprovalDecisionState
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionState"
|
||||
) {
|
||||
PENDING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStatePending"
|
||||
)
|
||||
APPROVED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateApproved"
|
||||
)
|
||||
REJECTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionStateRejected"
|
||||
)
|
||||
}
|
||||
|
||||
enum DocumentVersionApprovalDecisionOrderField
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionOrderField"
|
||||
) {
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalDecisionOrderFieldCreatedAt"
|
||||
)
|
||||
}
|
||||
|
||||
enum DocumentVersionApprovalQuorumStatus
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatus"
|
||||
) {
|
||||
PENDING
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusPending"
|
||||
)
|
||||
APPROVED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusApproved"
|
||||
)
|
||||
REJECTED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumStatusRejected"
|
||||
)
|
||||
}
|
||||
|
||||
enum DocumentVersionApprovalQuorumOrderField
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumOrderField"
|
||||
) {
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.DocumentVersionApprovalQuorumOrderFieldCreatedAt"
|
||||
)
|
||||
}
|
||||
|
||||
input DocumentVersionApprovalQuorumOrder {
|
||||
field: DocumentVersionApprovalQuorumOrderField!
|
||||
direction: OrderDirection!
|
||||
}
|
||||
|
||||
type DocumentVersionApprovalQuorumConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionApprovalQuorumConnection"
|
||||
) {
|
||||
edges: [DocumentVersionApprovalQuorumEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type DocumentVersionApprovalQuorumEdge {
|
||||
cursor: CursorKey!
|
||||
node: DocumentVersionApprovalQuorum!
|
||||
}
|
||||
|
||||
type DocumentVersionApprovalQuorum implements Node {
|
||||
id: ID!
|
||||
documentVersion: DocumentVersion! @goField(forceResolver: true)
|
||||
status: DocumentVersionApprovalQuorumStatus!
|
||||
decisions(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: DocumentVersionApprovalDecisionOrder
|
||||
filter: DocumentVersionApprovalDecisionFilter
|
||||
): DocumentVersionApprovalDecisionConnection! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
input DocumentVersionApprovalDecisionFilter {
|
||||
states: [DocumentVersionApprovalDecisionState!]
|
||||
}
|
||||
|
||||
input DocumentVersionApprovalDecisionOrder {
|
||||
field: DocumentVersionApprovalDecisionOrderField!
|
||||
direction: OrderDirection!
|
||||
}
|
||||
|
||||
type DocumentVersionApprovalDecisionConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.DocumentVersionApprovalDecisionConnection"
|
||||
) {
|
||||
edges: [DocumentVersionApprovalDecisionEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type DocumentVersionApprovalDecisionEdge {
|
||||
cursor: CursorKey!
|
||||
node: DocumentVersionApprovalDecision!
|
||||
}
|
||||
|
||||
type DocumentVersionApprovalDecision implements Node {
|
||||
id: ID!
|
||||
quorum: DocumentVersionApprovalQuorum! @goField(forceResolver: true)
|
||||
documentVersion: DocumentVersion! @goField(forceResolver: true)
|
||||
approver: Profile! @goField(forceResolver: true)
|
||||
state: DocumentVersionApprovalDecisionState!
|
||||
comment: String
|
||||
decidedAt: Datetime
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
permission(action: String!): Boolean! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
input ApproveDocumentVersionInput {
|
||||
documentVersionId: ID!
|
||||
comment: String
|
||||
}
|
||||
|
||||
type ApproveDocumentVersionPayload {
|
||||
approvalDecision: DocumentVersionApprovalDecision!
|
||||
}
|
||||
|
||||
input RejectDocumentVersionInput {
|
||||
documentVersionId: ID!
|
||||
comment: String
|
||||
}
|
||||
|
||||
type RejectDocumentVersionPayload {
|
||||
approvalDecision: DocumentVersionApprovalDecision!
|
||||
}
|
||||
|
||||
input AddDocumentVersionApproverInput {
|
||||
documentVersionId: ID!
|
||||
approverId: ID!
|
||||
}
|
||||
|
||||
type AddDocumentVersionApproverPayload {
|
||||
approvalDecisionEdge: DocumentVersionApprovalDecisionEdge!
|
||||
}
|
||||
|
||||
input RemoveDocumentVersionApproverInput {
|
||||
approvalDecisionId: ID!
|
||||
}
|
||||
|
||||
type RemoveDocumentVersionApproverPayload {
|
||||
deletedApprovalDecisionId: ID!
|
||||
documentVersion: DocumentVersion!
|
||||
}
|
||||
|
||||
input RequestSignatureInput {
|
||||
documentVersionId: ID!
|
||||
signatoryId: ID!
|
||||
@@ -5416,16 +5635,6 @@ type BulkRequestSignaturesPayload {
|
||||
documentVersionSignatureEdges: [DocumentVersionSignatureEdge!]!
|
||||
}
|
||||
|
||||
input BulkPublishDocumentVersionsInput {
|
||||
documentIds: [ID!]!
|
||||
changelog: String!
|
||||
}
|
||||
|
||||
type BulkPublishDocumentVersionsPayload {
|
||||
documentVersionEdges: [DocumentVersionEdge!]!
|
||||
documentEdges: [DocumentEdge!]!
|
||||
}
|
||||
|
||||
input BulkDeleteDocumentsInput {
|
||||
documentIds: [ID!]!
|
||||
}
|
||||
@@ -5461,14 +5670,34 @@ type BulkExportDocumentsPayload {
|
||||
exportJobId: ID!
|
||||
}
|
||||
|
||||
input RequestDocumentVersionApprovalInput {
|
||||
documentId: ID!
|
||||
approverIds: [ID!]!
|
||||
changelog: String
|
||||
}
|
||||
|
||||
type RequestDocumentVersionApprovalPayload {
|
||||
approvalQuorum: DocumentVersionApprovalQuorum!
|
||||
}
|
||||
|
||||
input PublishDocumentVersionInput {
|
||||
documentId: ID!
|
||||
changelog: String
|
||||
}
|
||||
|
||||
type PublishDocumentVersionPayload {
|
||||
documentVersion: DocumentVersion!
|
||||
document: Document!
|
||||
documentVersion: DocumentVersion!
|
||||
}
|
||||
|
||||
input BulkPublishDocumentVersionsInput {
|
||||
documentIds: [ID!]!
|
||||
changelog: String!
|
||||
}
|
||||
|
||||
type BulkPublishDocumentVersionsPayload {
|
||||
documentVersions: [DocumentVersion!]!
|
||||
documents: [Document!]!
|
||||
}
|
||||
|
||||
type CreateDraftDocumentVersionPayload {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
DocumentVersionApprovalDecisionOrderBy OrderBy[coredata.DocumentVersionApprovalDecisionOrderField]
|
||||
|
||||
DocumentVersionApprovalDecisionConnection struct {
|
||||
TotalCount int
|
||||
Edges []*DocumentVersionApprovalDecisionEdge
|
||||
PageInfo PageInfo
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
Filters *coredata.DocumentVersionApprovalDecisionFilter
|
||||
}
|
||||
)
|
||||
|
||||
func NewDocumentVersionApprovalDecisionConnection(
|
||||
page *page.Page[*coredata.DocumentVersionApprovalDecision, coredata.DocumentVersionApprovalDecisionOrderField],
|
||||
parentType any,
|
||||
parentID gid.GID,
|
||||
filter *coredata.DocumentVersionApprovalDecisionFilter,
|
||||
) *DocumentVersionApprovalDecisionConnection {
|
||||
edges := make([]*DocumentVersionApprovalDecisionEdge, len(page.Data))
|
||||
for i, decision := range page.Data {
|
||||
edges[i] = NewDocumentVersionApprovalDecisionEdge(decision, page.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &DocumentVersionApprovalDecisionConnection{
|
||||
Edges: edges,
|
||||
PageInfo: *NewPageInfo(page),
|
||||
|
||||
Resolver: parentType,
|
||||
ParentID: parentID,
|
||||
Filters: filter,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocumentVersionApprovalDecisionEdge(decision *coredata.DocumentVersionApprovalDecision, orderBy coredata.DocumentVersionApprovalDecisionOrderField) *DocumentVersionApprovalDecisionEdge {
|
||||
return &DocumentVersionApprovalDecisionEdge{
|
||||
Cursor: decision.CursorKey(orderBy),
|
||||
Node: NewDocumentVersionApprovalDecision(decision),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocumentVersionApprovalDecision(decision *coredata.DocumentVersionApprovalDecision) *DocumentVersionApprovalDecision {
|
||||
return &DocumentVersionApprovalDecision{
|
||||
Quorum: &DocumentVersionApprovalQuorum{
|
||||
ID: decision.QuorumID,
|
||||
},
|
||||
Approver: &Profile{
|
||||
ID: decision.ApproverID,
|
||||
},
|
||||
ID: decision.ID,
|
||||
State: decision.State,
|
||||
Comment: decision.Comment,
|
||||
DecidedAt: decision.DecidedAt,
|
||||
CreatedAt: decision.CreatedAt,
|
||||
UpdatedAt: decision.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
DocumentVersionApprovalQuorumConnection struct {
|
||||
TotalCount int
|
||||
Edges []*DocumentVersionApprovalQuorumEdge
|
||||
PageInfo PageInfo
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
}
|
||||
)
|
||||
|
||||
func NewDocumentVersionApprovalQuorumConnection(
|
||||
page *page.Page[*coredata.DocumentVersionApprovalQuorum, coredata.DocumentVersionApprovalQuorumOrderField],
|
||||
parentType any,
|
||||
parentID gid.GID,
|
||||
) *DocumentVersionApprovalQuorumConnection {
|
||||
edges := make([]*DocumentVersionApprovalQuorumEdge, len(page.Data))
|
||||
for i, quorum := range page.Data {
|
||||
edges[i] = NewDocumentVersionApprovalQuorumEdge(quorum, page.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &DocumentVersionApprovalQuorumConnection{
|
||||
Edges: edges,
|
||||
PageInfo: *NewPageInfo(page),
|
||||
|
||||
Resolver: parentType,
|
||||
ParentID: parentID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocumentVersionApprovalQuorumEdge(
|
||||
quorum *coredata.DocumentVersionApprovalQuorum,
|
||||
orderBy coredata.DocumentVersionApprovalQuorumOrderField,
|
||||
) *DocumentVersionApprovalQuorumEdge {
|
||||
return &DocumentVersionApprovalQuorumEdge{
|
||||
Cursor: quorum.CursorKey(orderBy),
|
||||
Node: NewDocumentVersionApprovalQuorum(quorum),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocumentVersionApprovalQuorum(quorum *coredata.DocumentVersionApprovalQuorum) *DocumentVersionApprovalQuorum {
|
||||
return &DocumentVersionApprovalQuorum{
|
||||
ID: quorum.ID,
|
||||
DocumentVersion: &DocumentVersion{
|
||||
ID: quorum.VersionID,
|
||||
},
|
||||
Status: quorum.Status,
|
||||
CreatedAt: quorum.CreatedAt,
|
||||
UpdatedAt: quorum.UpdatedAt,
|
||||
}
|
||||
}
|
||||
141
pkg/server/api/console/v1/types/employee_document.go
Normal file
141
pkg/server/api/console/v1/types/employee_document.go
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type EmployeeDocumentFilterMode int
|
||||
|
||||
const (
|
||||
EmployeeDocumentFilterModeSignature EmployeeDocumentFilterMode = iota
|
||||
EmployeeDocumentFilterModeApproval
|
||||
)
|
||||
|
||||
type (
|
||||
EmployeeDocumentConnection struct {
|
||||
Edges []*EmployeeDocumentEdge
|
||||
PageInfo *PageInfo
|
||||
}
|
||||
|
||||
EmployeeDocumentEdge struct {
|
||||
Cursor page.CursorKey
|
||||
Node *EmployeeDocument
|
||||
}
|
||||
|
||||
EmployeeDocument struct {
|
||||
ID gid.GID
|
||||
Title string
|
||||
Description *string
|
||||
DocumentType coredata.DocumentType
|
||||
Classification coredata.DocumentClassification
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
|
||||
FilterMode EmployeeDocumentFilterMode
|
||||
}
|
||||
|
||||
EmployeeDocumentVersionConnection struct {
|
||||
Edges []*EmployeeDocumentVersionEdge
|
||||
PageInfo *PageInfo
|
||||
}
|
||||
|
||||
EmployeeDocumentVersionEdge struct {
|
||||
Cursor page.CursorKey
|
||||
Node *EmployeeDocumentVersion
|
||||
}
|
||||
|
||||
EmployeeDocumentVersion struct {
|
||||
ID gid.GID
|
||||
OrganizationID gid.GID
|
||||
Version int
|
||||
Status coredata.DocumentVersionStatus
|
||||
PublishedAt *time.Time
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
)
|
||||
|
||||
func NewEmployeeDocumentConnection(
|
||||
p *page.Page[*EmployeeDocument, coredata.DocumentOrderField],
|
||||
) *EmployeeDocumentConnection {
|
||||
var edges = make([]*EmployeeDocumentEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewEmployeeDocumentEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &EmployeeDocumentConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewEmployeeDocumentEdge(document *EmployeeDocument, orderBy coredata.DocumentOrderField) *EmployeeDocumentEdge {
|
||||
return &EmployeeDocumentEdge{
|
||||
Cursor: document.CursorKey(orderBy),
|
||||
Node: document,
|
||||
}
|
||||
}
|
||||
|
||||
func (d EmployeeDocument) CursorKey(orderBy coredata.DocumentOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case coredata.DocumentOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(d.ID, d.CreatedAt)
|
||||
case coredata.DocumentOrderFieldTitle:
|
||||
return page.NewCursorKey(d.ID, d.Title)
|
||||
case coredata.DocumentOrderFieldDocumentType:
|
||||
return page.NewCursorKey(d.ID, d.DocumentType)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func NewEmployeeDocumentVersionConnection(
|
||||
p *page.Page[*EmployeeDocumentVersion, coredata.DocumentVersionOrderField],
|
||||
) *EmployeeDocumentVersionConnection {
|
||||
var edges = make([]*EmployeeDocumentVersionEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewEmployeeDocumentVersionEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &EmployeeDocumentVersionConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewEmployeeDocumentVersionEdge(version *EmployeeDocumentVersion, orderBy coredata.DocumentVersionOrderField) *EmployeeDocumentVersionEdge {
|
||||
return &EmployeeDocumentVersionEdge{
|
||||
Cursor: version.CursorKey(orderBy),
|
||||
Node: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (v EmployeeDocumentVersion) CursorKey(orderBy coredata.DocumentVersionOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case coredata.DocumentVersionOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(v.ID, v.CreatedAt)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (c) 2025-2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
SignableDocumentConnection struct {
|
||||
Edges []*SignableDocumentEdge
|
||||
PageInfo *PageInfo
|
||||
}
|
||||
|
||||
SignableDocumentEdge struct {
|
||||
Cursor page.CursorKey
|
||||
Node *SignableDocument
|
||||
}
|
||||
|
||||
SignableDocument struct {
|
||||
ID gid.GID
|
||||
Title string
|
||||
Description *string
|
||||
DocumentType coredata.DocumentType
|
||||
Classification coredata.DocumentClassification
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
)
|
||||
|
||||
func (SignableDocument) IsNode() {}
|
||||
func (d SignableDocument) GetID() gid.GID { return d.ID }
|
||||
|
||||
func NewSignableDocumentConnection(
|
||||
p *page.Page[*SignableDocument, coredata.DocumentOrderField],
|
||||
) *SignableDocumentConnection {
|
||||
var edges = make([]*SignableDocumentEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewSignableDocumentEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &SignableDocumentConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
}
|
||||
}
|
||||
|
||||
func NewSignableDocumentEdge(document *SignableDocument, orderBy coredata.DocumentOrderField) *SignableDocumentEdge {
|
||||
return &SignableDocumentEdge{
|
||||
Cursor: document.CursorKey(orderBy),
|
||||
Node: document,
|
||||
}
|
||||
}
|
||||
|
||||
func (d SignableDocument) CursorKey(orderBy coredata.DocumentOrderField) page.CursorKey {
|
||||
switch orderBy {
|
||||
case coredata.DocumentOrderFieldCreatedAt:
|
||||
return page.NewCursorKey(d.ID, d.CreatedAt)
|
||||
case coredata.DocumentOrderFieldTitle:
|
||||
return page.NewCursorKey(d.ID, d.Title)
|
||||
case coredata.DocumentOrderFieldDocumentType:
|
||||
return page.NewCursorKey(d.ID, d.DocumentType)
|
||||
}
|
||||
|
||||
panic("unsupported order by")
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package mcp_v1
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func allApproversCursor() *page.Cursor[coredata.MembershipProfileOrderField] {
|
||||
return page.NewCursor(
|
||||
100,
|
||||
nil,
|
||||
page.Head,
|
||||
page.OrderBy[coredata.MembershipProfileOrderField]{
|
||||
Field: coredata.MembershipProfileOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func profileIDs(p *page.Page[*coredata.MembershipProfile, coredata.MembershipProfileOrderField]) []gid.GID {
|
||||
ids := make([]gid.GID, len(p.Data))
|
||||
for i, profile := range p.Data {
|
||||
ids[i] = profile.ID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
@@ -1685,16 +1685,7 @@ func (r *Resolver) ListControlDocumentsTool(ctx context.Context, req *mcp.CallTo
|
||||
return nil, types.ListControlDocumentsOutput{}, fmt.Errorf("failed to list control documents: %w", err)
|
||||
}
|
||||
|
||||
approverIDsMap := make(map[gid.GID][]gid.GID)
|
||||
for _, d := range docPage.Data {
|
||||
approverPage, err := prb.Documents.ListApprovers(ctx, d.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
return nil, types.ListControlDocumentsOutput{}, fmt.Errorf("failed to list document approvers: %w", err)
|
||||
}
|
||||
approverIDsMap[d.ID] = profileIDs(approverPage)
|
||||
}
|
||||
|
||||
return nil, types.NewListControlDocumentsOutput(docPage, approverIDsMap), nil
|
||||
return nil, types.NewListControlDocumentsOutput(docPage), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) ListControlAuditsTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ListControlAuditsInput) (*mcp.CallToolResult, types.ListControlAuditsOutput, error) {
|
||||
@@ -2059,16 +2050,7 @@ func (r *Resolver) ListDocumentsTool(ctx context.Context, req *mcp.CallToolReque
|
||||
panic(fmt.Errorf("cannot list organization documents: %w", err))
|
||||
}
|
||||
|
||||
approverIDsMap := make(map[gid.GID][]gid.GID)
|
||||
for _, d := range docPage.Data {
|
||||
approverPage, err := prb.Documents.ListApprovers(ctx, d.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document approvers: %w", err))
|
||||
}
|
||||
approverIDsMap[d.ID] = profileIDs(approverPage)
|
||||
}
|
||||
|
||||
return nil, types.NewListDocumentsOutput(docPage, approverIDsMap), nil
|
||||
return nil, types.NewListDocumentsOutput(docPage), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) GetDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetDocumentInput) (*mcp.CallToolResult, types.GetDocumentOutput, error) {
|
||||
@@ -2081,13 +2063,8 @@ func (r *Resolver) GetDocumentTool(ctx context.Context, req *mcp.CallToolRequest
|
||||
panic(fmt.Errorf("cannot get document: %w", err))
|
||||
}
|
||||
|
||||
approverPage, err := prb.Documents.ListApprovers(ctx, input.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document approvers: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.GetDocumentOutput{
|
||||
Document: types.NewDocument(document, profileIDs(approverPage)),
|
||||
Document: types.NewDocument(document),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2107,7 +2084,6 @@ func (r *Resolver) AddDocumentTool(ctx context.Context, req *mcp.CallToolRequest
|
||||
OrganizationID: input.OrganizationID,
|
||||
Title: input.Title,
|
||||
Content: input.Content,
|
||||
ApproverIDs: input.ApproverIds,
|
||||
Classification: input.Classification,
|
||||
DocumentType: input.DocumentType,
|
||||
TrustCenterVisibility: trustCenterVisibility,
|
||||
@@ -2117,7 +2093,7 @@ func (r *Resolver) AddDocumentTool(ctx context.Context, req *mcp.CallToolRequest
|
||||
panic(fmt.Errorf("cannot create document: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.NewAddDocumentOutput(document, documentVersion, input.ApproverIds, input.ApproverIds), nil
|
||||
return nil, types.NewAddDocumentOutput(document, documentVersion), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UpdateDocumentInput) (*mcp.CallToolResult, types.UpdateDocumentOutput, error) {
|
||||
@@ -2130,7 +2106,6 @@ func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequ
|
||||
probo.UpdateDocumentRequest{
|
||||
DocumentID: input.ID,
|
||||
Title: input.Title,
|
||||
ApproverIDs: input.ApproverIds,
|
||||
Classification: input.Classification,
|
||||
DocumentType: input.DocumentType,
|
||||
TrustCenterVisibility: input.TrustCenterVisibility,
|
||||
@@ -2140,13 +2115,8 @@ func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequ
|
||||
panic(fmt.Errorf("cannot update document: %w", err))
|
||||
}
|
||||
|
||||
approverPage, err := svc.Documents.ListApprovers(ctx, input.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document approvers: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.UpdateDocumentOutput{
|
||||
Document: types.NewDocument(document, profileIDs(approverPage)),
|
||||
Document: types.NewDocument(document),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2172,16 +2142,7 @@ func (r *Resolver) ListDocumentVersionsTool(ctx context.Context, req *mcp.CallTo
|
||||
panic(fmt.Errorf("cannot list document versions: %w", err))
|
||||
}
|
||||
|
||||
approverIDsMap := make(map[gid.GID][]gid.GID)
|
||||
for _, v := range versionPage.Data {
|
||||
approverPage, err := svc.Documents.ListVersionApprovers(ctx, v.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document version approvers: %w", err))
|
||||
}
|
||||
approverIDsMap[v.ID] = profileIDs(approverPage)
|
||||
}
|
||||
|
||||
return nil, types.NewListDocumentVersionsOutput(versionPage, approverIDsMap), nil
|
||||
return nil, types.NewListDocumentVersionsOutput(versionPage), nil
|
||||
}
|
||||
|
||||
func (r *Resolver) GetDocumentVersionTool(ctx context.Context, req *mcp.CallToolRequest, input *types.GetDocumentVersionInput) (*mcp.CallToolResult, types.GetDocumentVersionOutput, error) {
|
||||
@@ -2194,13 +2155,8 @@ func (r *Resolver) GetDocumentVersionTool(ctx context.Context, req *mcp.CallTool
|
||||
panic(fmt.Errorf("cannot get document version: %w", err))
|
||||
}
|
||||
|
||||
approverPage, err := svc.Documents.ListVersionApprovers(ctx, input.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document version approvers: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.GetDocumentVersionOutput{
|
||||
DocumentVersion: types.NewDocumentVersion(version, profileIDs(approverPage)),
|
||||
DocumentVersion: types.NewDocumentVersion(version),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2214,13 +2170,8 @@ func (r *Resolver) CreateDraftDocumentVersionTool(ctx context.Context, req *mcp.
|
||||
panic(fmt.Errorf("cannot create draft document version: %w", err))
|
||||
}
|
||||
|
||||
approverPage, err := svc.Documents.ListVersionApprovers(ctx, draftVersion.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document version approvers: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.CreateDraftDocumentVersionOutput{
|
||||
DocumentVersion: types.NewDocumentVersion(draftVersion, profileIDs(approverPage)),
|
||||
DocumentVersion: types.NewDocumentVersion(draftVersion),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2240,13 +2191,8 @@ func (r *Resolver) UpdateDocumentVersionTool(ctx context.Context, req *mcp.CallT
|
||||
panic(fmt.Errorf("cannot update document version: %w", err))
|
||||
}
|
||||
|
||||
versionApproverPage, err := svc.Documents.ListVersionApprovers(ctx, documentVersion.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document version approvers: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.UpdateDocumentVersionOutput{
|
||||
DocumentVersion: types.NewDocumentVersion(documentVersion, profileIDs(versionApproverPage)),
|
||||
DocumentVersion: types.NewDocumentVersion(documentVersion),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2262,19 +2208,9 @@ func (r *Resolver) PublishDocumentVersionTool(ctx context.Context, req *mcp.Call
|
||||
panic(fmt.Errorf("cannot publish document version: %w", err))
|
||||
}
|
||||
|
||||
docApproverPage, err := svc.Documents.ListApprovers(ctx, document.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document approvers: %w", err))
|
||||
}
|
||||
|
||||
versionApproverPage, err := svc.Documents.ListVersionApprovers(ctx, documentVersion.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list document version approvers: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.PublishDocumentVersionOutput{
|
||||
Document: types.NewDocument(document, profileIDs(docApproverPage)),
|
||||
DocumentVersion: types.NewDocumentVersion(documentVersion, profileIDs(versionApproverPage)),
|
||||
Document: types.NewDocument(document),
|
||||
DocumentVersion: types.NewDocumentVersion(documentVersion),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -3243,13 +3179,8 @@ func (r *Resolver) ArchiveDocumentTool(ctx context.Context, req *mcp.CallToolReq
|
||||
return nil, types.ArchiveDocumentOutput{}, fmt.Errorf("cannot archive document: %w", err)
|
||||
}
|
||||
|
||||
approverPage, err := svc.Documents.ListApprovers(ctx, input.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
return nil, types.ArchiveDocumentOutput{}, fmt.Errorf("cannot list document approvers: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.ArchiveDocumentOutput{
|
||||
Document: types.NewDocument(document, profileIDs(approverPage)),
|
||||
Document: types.NewDocument(document),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -3263,13 +3194,8 @@ func (r *Resolver) UnarchiveDocumentTool(ctx context.Context, req *mcp.CallToolR
|
||||
return nil, types.UnarchiveDocumentOutput{}, fmt.Errorf("cannot unarchive document: %w", err)
|
||||
}
|
||||
|
||||
approverPage, err := svc.Documents.ListApprovers(ctx, input.ID, allApproversCursor())
|
||||
if err != nil {
|
||||
return nil, types.UnarchiveDocumentOutput{}, fmt.Errorf("cannot list document approvers: %w", err)
|
||||
}
|
||||
|
||||
return nil, types.UnarchiveDocumentOutput{
|
||||
Document: types.NewDocument(document, profileIDs(approverPage)),
|
||||
Document: types.NewDocument(document),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -3374,3 +3300,27 @@ func (r *Resolver) GetAuditLogEntryTool(ctx context.Context, req *mcp.CallToolRe
|
||||
AuditLogEntry: types.NewAuditLogEntry(entry),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) RequestDocumentVersionApprovalTool(ctx context.Context, req *mcp.CallToolRequest, input *types.RequestDocumentVersionApprovalInput) (*mcp.CallToolResult, types.RequestDocumentVersionApprovalOutput, error) {
|
||||
r.MustAuthorize(ctx, input.DocumentID, probo.ActionDocumentVersionRequestApproval)
|
||||
|
||||
svc := r.ProboService(ctx, input.DocumentID)
|
||||
|
||||
quorum, err := svc.DocumentApprovals.RequestApproval(ctx, probo.RequestApprovalRequest{
|
||||
DocumentID: input.DocumentID,
|
||||
ApproverIDs: input.ApproverIds,
|
||||
Changelog: input.Changelog,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot request document version approval: %w", err))
|
||||
}
|
||||
|
||||
documentVersion, err := svc.Documents.GetVersion(ctx, quorum.VersionID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot get document version: %w", err))
|
||||
}
|
||||
|
||||
return nil, types.RequestDocumentVersionApprovalOutput{
|
||||
DocumentVersion: types.NewDocumentVersion(documentVersion),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -5184,7 +5184,6 @@ components:
|
||||
required:
|
||||
- id
|
||||
- organization_id
|
||||
- approver_ids
|
||||
- title
|
||||
- document_type
|
||||
- classification
|
||||
@@ -5199,11 +5198,6 @@ components:
|
||||
organization_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Organization ID
|
||||
approver_ids:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Approver IDs
|
||||
title:
|
||||
type: string
|
||||
description: Document title
|
||||
@@ -5246,7 +5240,6 @@ components:
|
||||
- organization_id
|
||||
- document_id
|
||||
- title
|
||||
- approver_ids
|
||||
- version_number
|
||||
- classification
|
||||
- content
|
||||
@@ -5267,11 +5260,6 @@ components:
|
||||
title:
|
||||
type: string
|
||||
description: Document version title
|
||||
approver_ids:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Approver IDs
|
||||
version_number:
|
||||
type: integer
|
||||
description: Version number
|
||||
@@ -5418,7 +5406,6 @@ components:
|
||||
- organization_id
|
||||
- title
|
||||
- content
|
||||
- approver_ids
|
||||
- classification
|
||||
- document_type
|
||||
properties:
|
||||
@@ -5431,11 +5418,6 @@ components:
|
||||
content:
|
||||
type: string
|
||||
description: Document content
|
||||
approver_ids:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Approver IDs
|
||||
classification:
|
||||
$ref: "#/components/schemas/DocumentClassification"
|
||||
description: Document classification
|
||||
@@ -5468,11 +5450,6 @@ components:
|
||||
title:
|
||||
type: string
|
||||
description: Document title
|
||||
approver_ids:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Approver IDs
|
||||
classification:
|
||||
$ref: "#/components/schemas/DocumentClassification"
|
||||
description: Document classification
|
||||
@@ -5639,7 +5616,7 @@ components:
|
||||
description: Document ID
|
||||
changelog:
|
||||
type: string
|
||||
description: Changelog
|
||||
description: Changelog for this version
|
||||
|
||||
PublishDocumentVersionOutput:
|
||||
type: object
|
||||
@@ -5652,6 +5629,32 @@ components:
|
||||
document_version:
|
||||
$ref: "#/components/schemas/DocumentVersion"
|
||||
|
||||
RequestDocumentVersionApprovalInput:
|
||||
type: object
|
||||
required:
|
||||
- document_id
|
||||
- approver_ids
|
||||
properties:
|
||||
document_id:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Document ID
|
||||
approver_ids:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/GID"
|
||||
description: Approver profile IDs
|
||||
changelog:
|
||||
type: string
|
||||
description: Changelog for this version
|
||||
|
||||
RequestDocumentVersionApprovalOutput:
|
||||
type: object
|
||||
required:
|
||||
- document_version
|
||||
properties:
|
||||
document_version:
|
||||
$ref: "#/components/schemas/DocumentVersion"
|
||||
|
||||
DeleteDocumentInput:
|
||||
type: object
|
||||
required:
|
||||
@@ -7476,6 +7479,14 @@ tools:
|
||||
$ref: "#/components/schemas/PublishDocumentVersionInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/PublishDocumentVersionOutput"
|
||||
- name: requestDocumentVersionApproval
|
||||
description: Request approval for a document version
|
||||
hints:
|
||||
readonly: false
|
||||
inputSchema:
|
||||
$ref: "#/components/schemas/RequestDocumentVersionApprovalInput"
|
||||
outputSchema:
|
||||
$ref: "#/components/schemas/RequestDocumentVersionApprovalOutput"
|
||||
- name: deleteDocument
|
||||
description: Delete a document
|
||||
hints:
|
||||
|
||||
@@ -16,15 +16,13 @@ package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
func NewDocument(d *coredata.Document, approverIDs []gid.GID) *Document {
|
||||
func NewDocument(d *coredata.Document) *Document {
|
||||
return &Document{
|
||||
ID: d.ID,
|
||||
OrganizationID: d.OrganizationID,
|
||||
ApproverIds: approverIDs,
|
||||
Title: d.Title,
|
||||
DocumentType: d.DocumentType,
|
||||
Classification: d.Classification,
|
||||
@@ -37,10 +35,10 @@ func NewDocument(d *coredata.Document, approverIDs []gid.GID) *Document {
|
||||
}
|
||||
}
|
||||
|
||||
func NewListControlDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata.DocumentOrderField], approverIDsMap map[gid.GID][]gid.GID) ListControlDocumentsOutput {
|
||||
func NewListControlDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata.DocumentOrderField]) ListControlDocumentsOutput {
|
||||
documents := make([]*Document, 0, len(documentPage.Data))
|
||||
for _, d := range documentPage.Data {
|
||||
documents = append(documents, NewDocument(d, approverIDsMap[d.ID]))
|
||||
documents = append(documents, NewDocument(d))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
@@ -55,10 +53,10 @@ func NewListControlDocumentsOutput(documentPage *page.Page[*coredata.Document, c
|
||||
}
|
||||
}
|
||||
|
||||
func NewListDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata.DocumentOrderField], approverIDsMap map[gid.GID][]gid.GID) ListDocumentsOutput {
|
||||
func NewListDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata.DocumentOrderField]) ListDocumentsOutput {
|
||||
documents := make([]*Document, 0, len(documentPage.Data))
|
||||
for _, d := range documentPage.Data {
|
||||
documents = append(documents, NewDocument(d, approverIDsMap[d.ID]))
|
||||
documents = append(documents, NewDocument(d))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
@@ -73,20 +71,19 @@ func NewListDocumentsOutput(documentPage *page.Page[*coredata.Document, coredata
|
||||
}
|
||||
}
|
||||
|
||||
func NewAddDocumentOutput(doc *coredata.Document, docVersion *coredata.DocumentVersion, docApproverIDs []gid.GID, versionApproverIDs []gid.GID) AddDocumentOutput {
|
||||
func NewAddDocumentOutput(doc *coredata.Document, docVersion *coredata.DocumentVersion) AddDocumentOutput {
|
||||
return AddDocumentOutput{
|
||||
Document: NewDocument(doc, docApproverIDs),
|
||||
DocumentVersion: NewDocumentVersion(docVersion, versionApproverIDs),
|
||||
Document: NewDocument(doc),
|
||||
DocumentVersion: NewDocumentVersion(docVersion),
|
||||
}
|
||||
}
|
||||
|
||||
func NewDocumentVersion(dv *coredata.DocumentVersion, approverIDs []gid.GID) *DocumentVersion {
|
||||
func NewDocumentVersion(dv *coredata.DocumentVersion) *DocumentVersion {
|
||||
return &DocumentVersion{
|
||||
ID: dv.ID,
|
||||
OrganizationID: dv.OrganizationID,
|
||||
DocumentID: dv.DocumentID,
|
||||
Title: dv.Title,
|
||||
ApproverIds: approverIDs,
|
||||
VersionNumber: dv.VersionNumber,
|
||||
Classification: dv.Classification,
|
||||
Content: dv.Content,
|
||||
@@ -98,10 +95,10 @@ func NewDocumentVersion(dv *coredata.DocumentVersion, approverIDs []gid.GID) *Do
|
||||
}
|
||||
}
|
||||
|
||||
func NewListDocumentVersionsOutput(versionPage *page.Page[*coredata.DocumentVersion, coredata.DocumentVersionOrderField], approverIDsMap map[gid.GID][]gid.GID) ListDocumentVersionsOutput {
|
||||
func NewListDocumentVersionsOutput(versionPage *page.Page[*coredata.DocumentVersion, coredata.DocumentVersionOrderField]) ListDocumentVersionsOutput {
|
||||
versions := make([]*DocumentVersion, 0, len(versionPage.Data))
|
||||
for _, v := range versionPage.Data {
|
||||
versions = append(versions, NewDocumentVersion(v, approverIDsMap[v.ID]))
|
||||
versions = append(versions, NewDocumentVersion(v))
|
||||
}
|
||||
|
||||
var nextCursor *page.CursorKey
|
||||
|
||||
Reference in New Issue
Block a user