Redesign document approval flow
Replace the per-approver add/remove model with a quorum-based approval system. Documents now have default approvers that are pre-populated when requesting approval, and the publish dialog lets users adjust the list before submitting. Key changes: - Add PENDING_APPROVAL document version status with dedicated transitions - Introduce approval quorums with request/approve/reject/void lifecycle - Add default approvers per document (stored in document_default_approvers) with MERGE-based upsert for efficient sync - Add NoDuplicates validator for slice fields - Split ALTER TYPE ADD VALUE migrations into separate files (required by PostgreSQL when run inside transactions) - Use VOIDED consistently for both quorum status and decision state enums - Expose void/approve/reject through GraphQL and MCP, with e2e tests - Add approval management UI: publish dialog with approver selection, approval list with void support, and external approve/reject page Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
@@ -353,15 +353,49 @@ func TestDocumentVersion_RequestSignature(t *testing.T) {
|
||||
assert.Equal(t, signerProfileID, result.RequestSignature.DocumentVersionSignatureEdge.Node.SignedBy.ID)
|
||||
}
|
||||
|
||||
func createTestDocumentWithApprovers(t *testing.T, owner *testutil.Client, approverIDs []string) (docID string) {
|
||||
t.Helper()
|
||||
|
||||
var result struct {
|
||||
CreateDocument struct {
|
||||
DocumentEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"node"`
|
||||
} `json:"documentEdge"`
|
||||
} `json:"createDocument"`
|
||||
}
|
||||
|
||||
err := owner.Execute(`
|
||||
mutation($input: CreateDocumentInput!) {
|
||||
createDocument(input: $input) {
|
||||
documentEdge {
|
||||
node { id }
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"title": "Test Document With Approvers",
|
||||
"content": testutil.ProseMirrorTextDoc("Initial content"),
|
||||
"documentType": "POLICY",
|
||||
"classification": "INTERNAL",
|
||||
"defaultApproverIds": approverIDs,
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
return result.CreateDocument.DocumentEdge.Node.ID
|
||||
}
|
||||
|
||||
func TestDocumentVersion_BulkPublish(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
// Create multiple documents
|
||||
// Create multiple draft documents (no default approvers — should publish directly)
|
||||
docID1, _ := createTestDocument(t, owner)
|
||||
docID2, _ := createTestDocument(t, owner)
|
||||
approveTestDocument(t, owner, docID1)
|
||||
approveTestDocument(t, owner, docID2)
|
||||
|
||||
query := `
|
||||
mutation BulkPublishMajorDocumentVersions($input: BulkPublishDocumentVersionsInput!) {
|
||||
@@ -397,6 +431,170 @@ func TestDocumentVersion_BulkPublish(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentVersion_BulkPublishRequestsApproval(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
|
||||
// Create a document with default approvers
|
||||
docID := createTestDocumentWithApprovers(t, owner, []string{approverID})
|
||||
|
||||
query := `
|
||||
mutation BulkPublishMajorDocumentVersions($input: BulkPublishDocumentVersionsInput!) {
|
||||
bulkPublishMajorDocumentVersions(input: $input) {
|
||||
documentVersions {
|
||||
id
|
||||
status
|
||||
major
|
||||
minor
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var result struct {
|
||||
BulkPublishMajorDocumentVersions struct {
|
||||
DocumentVersions []struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Major int `json:"major"`
|
||||
Minor int `json:"minor"`
|
||||
} `json:"documentVersions"`
|
||||
} `json:"bulkPublishMajorDocumentVersions"`
|
||||
}
|
||||
|
||||
err := owner.Execute(query, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentIds": []string{docID},
|
||||
"changelog": "Needs approval",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, result.BulkPublishMajorDocumentVersions.DocumentVersions, 1)
|
||||
dv := result.BulkPublishMajorDocumentVersions.DocumentVersions[0]
|
||||
assert.Equal(t, "PENDING_APPROVAL", dv.Status)
|
||||
assert.Equal(t, 1, dv.Major)
|
||||
assert.Equal(t, 0, dv.Minor)
|
||||
}
|
||||
|
||||
func TestDocumentVersion_BulkPublishSkipsPendingApproval(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
|
||||
// Create a document with default approvers and bulk publish it (puts it in PENDING_APPROVAL)
|
||||
docID := createTestDocumentWithApprovers(t, owner, []string{approverID})
|
||||
|
||||
_, err := owner.Do(`
|
||||
mutation($input: BulkPublishDocumentVersionsInput!) {
|
||||
bulkPublishMajorDocumentVersions(input: $input) {
|
||||
documentVersions { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentIds": []string{docID},
|
||||
"changelog": "First approval request",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Bulk publish again — should skip the pending document and return empty
|
||||
var result struct {
|
||||
BulkPublishMajorDocumentVersions struct {
|
||||
DocumentVersions []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"documentVersions"`
|
||||
} `json:"bulkPublishMajorDocumentVersions"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
mutation($input: BulkPublishDocumentVersionsInput!) {
|
||||
bulkPublishMajorDocumentVersions(input: $input) {
|
||||
documentVersions { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentIds": []string{docID},
|
||||
"changelog": "Second attempt",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, result.BulkPublishMajorDocumentVersions.DocumentVersions)
|
||||
}
|
||||
|
||||
func TestDocumentVersion_BulkPublishMinorSkipsPendingApproval(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
// Create and publish a document first (need a published version for minor publish)
|
||||
docID, _ := createTestDocument(t, owner)
|
||||
approveTestDocument(t, owner, docID)
|
||||
|
||||
// Create a draft so we can publish minor
|
||||
_, err := owner.Do(`
|
||||
mutation($input: CreateDraftDocumentVersionInput!) {
|
||||
createDraftDocumentVersion(input: $input) {
|
||||
documentVersionEdge {
|
||||
node { id }
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentID": docID,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Request approval to put it in PENDING_APPROVAL
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
_, err = owner.Do(`
|
||||
mutation($input: RequestDocumentVersionApprovalInput!) {
|
||||
requestDocumentVersionApproval(input: $input) {
|
||||
approvalQuorum { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentId": docID,
|
||||
"approverIds": []string{approverID},
|
||||
"changelog": "Approval request",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Bulk publish minor — should skip the pending document
|
||||
var result struct {
|
||||
BulkPublishMinorDocumentVersions struct {
|
||||
DocumentVersions []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"documentVersions"`
|
||||
} `json:"bulkPublishMinorDocumentVersions"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
mutation($input: BulkPublishDocumentVersionsInput!) {
|
||||
bulkPublishMinorDocumentVersions(input: $input) {
|
||||
documentVersions { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentIds": []string{docID},
|
||||
"changelog": "Minor publish attempt",
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, result.BulkPublishMinorDocumentVersions.DocumentVersions)
|
||||
}
|
||||
|
||||
func TestDocumentVersion_BulkRequestSignatures(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
@@ -480,3 +678,468 @@ func TestDocumentVersion_BulkDelete(t *testing.T) {
|
||||
assert.Contains(t, result.BulkDeleteDocuments.DeletedDocumentIds, docID1)
|
||||
assert.Contains(t, result.BulkDeleteDocuments.DeletedDocumentIds, docID2)
|
||||
}
|
||||
|
||||
func TestDocumentVersion_VoidApproval(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
docID, _ := createTestDocument(t, owner)
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
|
||||
// Request approval
|
||||
_, err := owner.Do(`
|
||||
mutation($input: RequestDocumentVersionApprovalInput!) {
|
||||
requestDocumentVersionApproval(input: $input) {
|
||||
approvalQuorum { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentId": docID,
|
||||
"approverIds": []string{approverID},
|
||||
"changelog": "Test changelog",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get version ID and verify version bumped to 1.0
|
||||
var versionResult struct {
|
||||
Node struct {
|
||||
Versions struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Major int `json:"major"`
|
||||
Minor int `json:"minor"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"versions"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
query($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Document {
|
||||
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges { node { id status major minor } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{"id": docID}, &versionResult)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, versionResult.Node.Versions.Edges)
|
||||
assert.Equal(t, "PENDING_APPROVAL", versionResult.Node.Versions.Edges[0].Node.Status)
|
||||
assert.Equal(t, 1, versionResult.Node.Versions.Edges[0].Node.Major)
|
||||
assert.Equal(t, 0, versionResult.Node.Versions.Edges[0].Node.Minor)
|
||||
|
||||
versionID := versionResult.Node.Versions.Edges[0].Node.ID
|
||||
|
||||
// Void approval — version should revert to 0.1
|
||||
var voidResult struct {
|
||||
VoidDocumentVersionApproval struct {
|
||||
ApprovalQuorum struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
} `json:"approvalQuorum"`
|
||||
DocumentVersion struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Major int `json:"major"`
|
||||
Minor int `json:"minor"`
|
||||
} `json:"documentVersion"`
|
||||
} `json:"voidDocumentVersionApproval"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
mutation($input: VoidDocumentVersionApprovalInput!) {
|
||||
voidDocumentVersionApproval(input: $input) {
|
||||
approvalQuorum { id status }
|
||||
documentVersion { id status major minor }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentVersionId": versionID,
|
||||
},
|
||||
}, &voidResult)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "VOIDED", voidResult.VoidDocumentVersionApproval.ApprovalQuorum.Status)
|
||||
assert.Equal(t, "DRAFT", voidResult.VoidDocumentVersionApproval.DocumentVersion.Status)
|
||||
assert.Equal(t, 0, voidResult.VoidDocumentVersionApproval.DocumentVersion.Major)
|
||||
assert.Equal(t, 1, voidResult.VoidDocumentVersionApproval.DocumentVersion.Minor)
|
||||
|
||||
// Verify decisions are VOIDED after voiding
|
||||
var quorumResult struct {
|
||||
Node struct {
|
||||
Versions struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ApprovalQuorums struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
Decisions struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
State string `json:"state"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"decisions"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"approvalQuorums"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"versions"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
query($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Document {
|
||||
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges {
|
||||
node {
|
||||
approvalQuorums(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges {
|
||||
node {
|
||||
decisions(first: 100) {
|
||||
edges { node { state } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{"id": docID}, &quorumResult)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, quorumResult.Node.Versions.Edges)
|
||||
require.NotEmpty(t, quorumResult.Node.Versions.Edges[0].Node.ApprovalQuorums.Edges)
|
||||
decisions := quorumResult.Node.Versions.Edges[0].Node.ApprovalQuorums.Edges[0].Node.Decisions.Edges
|
||||
require.NotEmpty(t, decisions)
|
||||
for _, d := range decisions {
|
||||
assert.Equal(t, "VOIDED", d.Node.State, "decisions should be VOIDED after voiding")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDocumentVersion_RejectApproval(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
docID, _ := createTestDocument(t, owner)
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
|
||||
// Request approval — version should bump to 1.0
|
||||
_, err := owner.Do(`
|
||||
mutation($input: RequestDocumentVersionApprovalInput!) {
|
||||
requestDocumentVersionApproval(input: $input) {
|
||||
approvalQuorum { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentId": docID,
|
||||
"approverIds": []string{approverID},
|
||||
"changelog": "Test changelog",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get version ID
|
||||
var versionResult struct {
|
||||
Node struct {
|
||||
Versions struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
Status string `json:"status"`
|
||||
Major int `json:"major"`
|
||||
Minor int `json:"minor"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"versions"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
query($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Document {
|
||||
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges { node { id status major minor } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{"id": docID}, &versionResult)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, versionResult.Node.Versions.Edges)
|
||||
assert.Equal(t, "PENDING_APPROVAL", versionResult.Node.Versions.Edges[0].Node.Status)
|
||||
assert.Equal(t, 1, versionResult.Node.Versions.Edges[0].Node.Major)
|
||||
assert.Equal(t, 0, versionResult.Node.Versions.Edges[0].Node.Minor)
|
||||
|
||||
versionID := versionResult.Node.Versions.Edges[0].Node.ID
|
||||
|
||||
// Reject approval
|
||||
_, err = owner.Do(`
|
||||
mutation($input: RejectDocumentVersionInput!) {
|
||||
rejectDocumentVersion(input: $input) {
|
||||
approvalDecision { id state }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentVersionId": versionID,
|
||||
"comment": "Needs rework",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify version reverted to 0.1 DRAFT
|
||||
err = owner.Execute(`
|
||||
query($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Document {
|
||||
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges { node { id status major minor } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{"id": docID}, &versionResult)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, versionResult.Node.Versions.Edges)
|
||||
assert.Equal(t, "DRAFT", versionResult.Node.Versions.Edges[0].Node.Status)
|
||||
assert.Equal(t, 0, versionResult.Node.Versions.Edges[0].Node.Major)
|
||||
assert.Equal(t, 1, versionResult.Node.Versions.Edges[0].Node.Minor)
|
||||
|
||||
// Verify decisions are VOIDED after reject
|
||||
var quorumResult struct {
|
||||
Node struct {
|
||||
Versions struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
ApprovalQuorums struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
Decisions struct {
|
||||
Edges []struct {
|
||||
Node struct {
|
||||
State string `json:"state"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"decisions"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"approvalQuorums"`
|
||||
} `json:"node"`
|
||||
} `json:"edges"`
|
||||
} `json:"versions"`
|
||||
} `json:"node"`
|
||||
}
|
||||
|
||||
err = owner.Execute(`
|
||||
query($id: ID!) {
|
||||
node(id: $id) {
|
||||
... on Document {
|
||||
versions(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges {
|
||||
node {
|
||||
approvalQuorums(first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {
|
||||
edges {
|
||||
node {
|
||||
decisions(first: 100) {
|
||||
edges { node { state } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{"id": docID}, &quorumResult)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, quorumResult.Node.Versions.Edges)
|
||||
require.NotEmpty(t, quorumResult.Node.Versions.Edges[0].Node.ApprovalQuorums.Edges)
|
||||
decisions := quorumResult.Node.Versions.Edges[0].Node.ApprovalQuorums.Edges[0].Node.Decisions.Edges
|
||||
require.Len(t, decisions, 1)
|
||||
assert.Equal(t, "REJECTED", decisions[0].Node.State, "rejecting approver's decision should be REJECTED")
|
||||
}
|
||||
|
||||
func TestDocumentVersion_PublishBlockedWhenPendingApproval(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
docID, _ := createTestDocument(t, owner)
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
|
||||
// Request approval (puts version in PENDING_APPROVAL)
|
||||
_, err := owner.Do(`
|
||||
mutation($input: RequestDocumentVersionApprovalInput!) {
|
||||
requestDocumentVersionApproval(input: $input) {
|
||||
approvalQuorum { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentId": docID,
|
||||
"approverIds": []string{approverID},
|
||||
"changelog": "Test changelog",
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("publish major blocked", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := owner.Do(`
|
||||
mutation($input: PublishMajorDocumentVersionInput!) {
|
||||
publishMajorDocumentVersion(input: $input) {
|
||||
documentVersion { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentId": docID,
|
||||
"changelog": "Major release",
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("publish minor blocked", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
_, err := owner.Do(`
|
||||
mutation($input: PublishMinorDocumentVersionInput!) {
|
||||
publishMinorDocumentVersion(input: $input) {
|
||||
documentVersion { id }
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"documentId": docID,
|
||||
"changelog": "Minor release",
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDocument_DefaultApprovers(t *testing.T) {
|
||||
t.Parallel()
|
||||
owner := testutil.NewClient(t, testutil.RoleOwner)
|
||||
|
||||
approverID := getOwnerProfileID(t, owner)
|
||||
|
||||
t.Run("create document with default approvers", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var result struct {
|
||||
CreateDocument struct {
|
||||
DocumentEdge struct {
|
||||
Node struct {
|
||||
ID string `json:"id"`
|
||||
DefaultApprovers []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"defaultApprovers"`
|
||||
} `json:"node"`
|
||||
} `json:"documentEdge"`
|
||||
} `json:"createDocument"`
|
||||
}
|
||||
|
||||
err := owner.Execute(`
|
||||
mutation($input: CreateDocumentInput!) {
|
||||
createDocument(input: $input) {
|
||||
documentEdge {
|
||||
node {
|
||||
id
|
||||
defaultApprovers { id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"organizationId": owner.GetOrganizationID().String(),
|
||||
"title": "Doc With Approvers",
|
||||
"content": testutil.ProseMirrorTextDoc("Content"),
|
||||
"documentType": "POLICY",
|
||||
"classification": "INTERNAL",
|
||||
"defaultApproverIds": []string{approverID},
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, result.CreateDocument.DocumentEdge.Node.DefaultApprovers, 1)
|
||||
assert.Equal(t, approverID, result.CreateDocument.DocumentEdge.Node.DefaultApprovers[0].ID)
|
||||
})
|
||||
|
||||
t.Run("update document with default approvers", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
docID, _ := createTestDocument(t, owner)
|
||||
|
||||
var result struct {
|
||||
UpdateDocument struct {
|
||||
Document struct {
|
||||
ID string `json:"id"`
|
||||
DefaultApprovers []struct {
|
||||
ID string `json:"id"`
|
||||
} `json:"defaultApprovers"`
|
||||
} `json:"document"`
|
||||
} `json:"updateDocument"`
|
||||
}
|
||||
|
||||
err := owner.Execute(`
|
||||
mutation($input: UpdateDocumentInput!) {
|
||||
updateDocument(input: $input) {
|
||||
document {
|
||||
id
|
||||
defaultApprovers { id }
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"id": docID,
|
||||
"defaultApproverIds": []string{approverID},
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, result.UpdateDocument.Document.DefaultApprovers, 1)
|
||||
assert.Equal(t, approverID, result.UpdateDocument.Document.DefaultApprovers[0].ID)
|
||||
|
||||
// Clear approvers
|
||||
err = owner.Execute(`
|
||||
mutation($input: UpdateDocumentInput!) {
|
||||
updateDocument(input: $input) {
|
||||
document {
|
||||
id
|
||||
defaultApprovers { id }
|
||||
}
|
||||
}
|
||||
}
|
||||
`, map[string]any{
|
||||
"input": map[string]any{
|
||||
"id": docID,
|
||||
"defaultApproverIds": []string{},
|
||||
},
|
||||
}, &result)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, result.UpdateDocument.Document.DefaultApprovers)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user