Add document types filtering and rename ISMS to GOVERNANCE

Adds 5 new document types (PLAN, REGISTER, RECORD, REPORT, TEMPLATE), renames ISMS to GOVERNANCE, and implements type-based filtering across GraphQL, MCP, and frontend. Includes migration, enum updates, filter implementation with SQL array support, and frontend dropdown UI with Relay refetch pattern.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-19 11:24:45 +01:00
parent 2b08dea600
commit 4f54241382
15 changed files with 178 additions and 37 deletions

View File

@@ -25,6 +25,7 @@ type (
trustCenterVisibilities []TrustCenterVisibility
published *bool
userEmail *mail.Addr
documentTypes []DocumentType
}
)
@@ -55,6 +56,11 @@ func (f *DocumentFilter) WithUserEmail(userEmail *mail.Addr) *DocumentFilter {
return f
}
func (f *DocumentFilter) WithDocumentTypes(documentTypes []DocumentType) *DocumentFilter {
f.documentTypes = documentTypes
return f
}
func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
var visibilities []string
if f.trustCenterVisibilities != nil {
@@ -63,11 +69,21 @@ func (f *DocumentFilter) SQLArguments() pgx.NamedArgs {
visibilities[i] = v.String()
}
}
var documentTypes []string
if f.documentTypes != nil {
documentTypes = make([]string, len(f.documentTypes))
for i, dt := range f.documentTypes {
documentTypes[i] = dt.String()
}
}
return pgx.NamedArgs{
"query": f.query,
"trust_center_visibilities": visibilities,
"published": f.published,
"user_email": f.userEmail,
"document_types": documentTypes,
}
}
@@ -109,5 +125,11 @@ func (f *DocumentFilter) SQLFragment() string {
AND dvs.state IN ('REQUESTED', 'SIGNED')
)
END
AND
CASE
WHEN @document_types::document_type[] IS NOT NULL THEN
document_type = ANY(@document_types::document_type[])
ELSE TRUE
END
)`
}

View File

@@ -24,18 +24,28 @@ type (
)
const (
DocumentTypeOther DocumentType = "OTHER"
DocumentTypeISMS DocumentType = "ISMS"
DocumentTypePolicy DocumentType = "POLICY"
DocumentTypeProcedure DocumentType = "PROCEDURE"
DocumentTypeOther DocumentType = "OTHER"
DocumentTypeGovernance DocumentType = "GOVERNANCE"
DocumentTypePolicy DocumentType = "POLICY"
DocumentTypeProcedure DocumentType = "PROCEDURE"
DocumentTypePlan DocumentType = "PLAN"
DocumentTypeRegister DocumentType = "REGISTER"
DocumentTypeRecord DocumentType = "RECORD"
DocumentTypeReport DocumentType = "REPORT"
DocumentTypeTemplate DocumentType = "TEMPLATE"
)
func DocumentTypes() []DocumentType {
return []DocumentType{
DocumentTypeOther,
DocumentTypeISMS,
DocumentTypeGovernance,
DocumentTypePolicy,
DocumentTypeProcedure,
DocumentTypePlan,
DocumentTypeRegister,
DocumentTypeRecord,
DocumentTypeReport,
DocumentTypeTemplate,
}
}
@@ -49,12 +59,22 @@ func (dt *DocumentType) UnmarshalText(data []byte) error {
switch val {
case DocumentTypeOther.String():
*dt = DocumentTypeOther
case DocumentTypeISMS.String():
*dt = DocumentTypeISMS
case DocumentTypeGovernance.String():
*dt = DocumentTypeGovernance
case DocumentTypePolicy.String():
*dt = DocumentTypePolicy
case DocumentTypeProcedure.String():
*dt = DocumentTypeProcedure
case DocumentTypePlan.String():
*dt = DocumentTypePlan
case DocumentTypeRegister.String():
*dt = DocumentTypeRegister
case DocumentTypeRecord.String():
*dt = DocumentTypeRecord
case DocumentTypeReport.String():
*dt = DocumentTypeReport
case DocumentTypeTemplate.String():
*dt = DocumentTypeTemplate
default:
return fmt.Errorf("invalid DocumentType value: %q", val)
}

View File

@@ -0,0 +1,6 @@
ALTER TYPE document_type RENAME VALUE 'ISMS' TO 'GOVERNANCE';
ALTER TYPE document_type ADD VALUE 'PLAN';
ALTER TYPE document_type ADD VALUE 'REGISTER';
ALTER TYPE document_type ADD VALUE 'RECORD';
ALTER TYPE document_type ADD VALUE 'REPORT';
ALTER TYPE document_type ADD VALUE 'TEMPLATE';

View File

@@ -971,10 +971,18 @@ enum VendorCategory
enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
ISMS @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeISMS")
GOVERNANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeGovernance")
POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
PLAN @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePlan")
REGISTER
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRegister")
RECORD @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRecord")
REPORT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeReport")
TEMPLATE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeTemplate")
}
enum DocumentClassification
@@ -1571,6 +1579,7 @@ input ControlFilter {
input DocumentFilter {
query: String
documentTypes: [DocumentType!]
}
input MeasureFilter {

View File

@@ -567,7 +567,8 @@ func (r *controlResolver) Documents(ctx context.Context, obj *types.Control, fir
var documentFilter = coredata.NewDocumentFilter(nil)
if filter != nil {
documentFilter = coredata.NewDocumentFilter(filter.Query)
documentFilter = coredata.NewDocumentFilter(filter.Query).
WithDocumentTypes(filter.DocumentTypes)
}
page, err := prb.Documents.ListForControlID(ctx, obj.ID, cursor, documentFilter)
@@ -6477,7 +6478,8 @@ func (r *organizationResolver) Documents(ctx context.Context, obj *types.Organiz
var documentFilter = coredata.NewDocumentFilter(nil)
if filter != nil {
documentFilter = coredata.NewDocumentFilter(filter.Query)
documentFilter = coredata.NewDocumentFilter(filter.Query).
WithDocumentTypes(filter.DocumentTypes)
}
page, err := prb.Documents.ListByOrganizationID(ctx, obj.ID, cursor, documentFilter)
@@ -7844,7 +7846,8 @@ func (r *riskResolver) Documents(ctx context.Context, obj *types.Risk, first *in
var documentFilter = coredata.NewDocumentFilter(nil)
if filter != nil {
documentFilter = coredata.NewDocumentFilter(filter.Query)
documentFilter = coredata.NewDocumentFilter(filter.Query).
WithDocumentTypes(filter.DocumentTypes)
}
page, err := prb.Documents.ListForRiskID(ctx, obj.ID, cursor, documentFilter)

View File

@@ -2019,7 +2019,8 @@ func (r *Resolver) ListDocumentsTool(ctx context.Context, req *mcp.CallToolReque
query = input.Filter.Query
}
documentFilter = coredata.NewDocumentFilter(query)
documentFilter = coredata.NewDocumentFilter(query).
WithDocumentTypes(input.Filter.DocumentTypes)
}
docPage, err := prb.Documents.ListByOrganizationID(ctx, input.OrganizationID, cursor, documentFilter)

View File

@@ -5038,9 +5038,14 @@ components:
type: string
enum:
- OTHER
- ISMS
- GOVERNANCE
- POLICY
- PROCEDURE
- PLAN
- REGISTER
- RECORD
- REPORT
- TEMPLATE
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.DocumentType
DocumentClassification:
@@ -5314,6 +5319,11 @@ components:
items:
$ref: "#/components/schemas/TrustCenterVisibility"
description: Trust center visibilities
document_types:
type: array
items:
$ref: "#/components/schemas/DocumentType"
description: Document types
ListDocumentsOutput:
type: object

View File

@@ -52,10 +52,18 @@ type Organization implements Node {
enum DocumentType
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentType") {
OTHER @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeOther")
ISMS @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeISMS")
GOVERNANCE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeGovernance")
POLICY @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePolicy")
PROCEDURE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeProcedure")
PLAN @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypePlan")
REGISTER
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRegister")
RECORD @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeRecord")
REPORT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeReport")
TEMPLATE
@goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentTypeTemplate")
}
type Document implements Node @nda {

View File

@@ -177,7 +177,7 @@ func (s *DocumentService) exportPDFData(
switch document.DocumentType {
case coredata.DocumentTypePolicy:
classification = docgen.ClassificationConfidential
case coredata.DocumentTypeISMS:
case coredata.DocumentTypeGovernance:
classification = docgen.ClassificationSecret
}