Add control fulltext search

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-06-07 12:02:16 -07:00
parent 032144e04b
commit 67cc9d9ddc
9 changed files with 296 additions and 46 deletions

View File

@@ -64,6 +64,7 @@ func (c *Controls) LoadByDocumentID(
scope Scoper,
documentID gid.GID,
cursor *page.Cursor[ControlOrderField],
filter *ControlFilter,
) error {
q := `
WITH ctrl AS (
@@ -96,11 +97,13 @@ FROM
ctrl
WHERE %s
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"document_id": documentID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
@@ -124,6 +127,7 @@ func (c *Controls) LoadByMeasureID(
scope Scoper,
measureID gid.GID,
cursor *page.Cursor[ControlOrderField],
filter *ControlFilter,
) error {
q := `
WITH ctrl AS (
@@ -156,11 +160,14 @@ FROM
ctrl
WHERE %s
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"measure_id": measureID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
@@ -183,6 +190,7 @@ func (c *Controls) LoadByRiskID(
scope Scoper,
riskID gid.GID,
cursor *page.Cursor[ControlOrderField],
filter *ControlFilter,
) error {
q := `
WITH ctrl AS (
@@ -221,11 +229,13 @@ FROM
ctrl
WHERE %s
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"risk_id": riskID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
@@ -249,6 +259,7 @@ func (c *Controls) LoadByFrameworkID(
scope Scoper,
frameworkID gid.GID,
cursor *page.Cursor[ControlOrderField],
filter *ControlFilter,
) error {
q := `
SELECT
@@ -266,11 +277,14 @@ WHERE
%s
AND framework_id = @framework_id
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"framework_id": frameworkID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
@@ -293,6 +307,7 @@ func (c *Controls) LoadByOrganizationID(
scope Scoper,
organizationID gid.GID,
cursor *page.Cursor[ControlOrderField],
filter *ControlFilter,
) error {
q := `
WITH ctrl AS (
@@ -325,11 +340,14 @@ FROM
ctrl
WHERE %s
AND %s
AND %s
`
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
args := pgx.NamedArgs{"organization_id": organizationID}
maps.Copy(args, scope.SQLArguments())
maps.Copy(args, filter.SQLArguments())
maps.Copy(args, cursor.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2025 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 coredata
import (
"github.com/jackc/pgx/v5"
)
type (
ControlFilter struct {
query *string
}
)
func NewControlFilter(query *string) *ControlFilter {
return &ControlFilter{
query: query,
}
}
func (f *ControlFilter) SQLArguments() pgx.StrictNamedArgs {
return pgx.StrictNamedArgs{
"query": f.query,
}
}
func (f *ControlFilter) SQLFragment() string {
if f.query == nil || *f.query == "" {
return "TRUE"
}
return "search_vector @@ websearch_to_tsquery('simple', @query)"
}

View File

@@ -0,0 +1,10 @@
ALTER TABLE controls ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
to_tsvector('simple',
COALESCE(section_title, '') || ' ' ||
COALESCE(name, '') || ' ' ||
COALESCE(description, '')
)
) STORED;
CREATE INDEX controls_search_idx ON controls USING gin(search_vector);

View File

@@ -60,6 +60,7 @@ func (s ControlService) ListForDocumentID(
ctx context.Context,
documentID gid.GID,
cursor *page.Cursor[coredata.ControlOrderField],
filter *coredata.ControlFilter,
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
var controls coredata.Controls
document := &coredata.Document{}
@@ -71,7 +72,7 @@ func (s ControlService) ListForDocumentID(
return fmt.Errorf("cannot load document: %w", err)
}
return controls.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor)
return controls.LoadByDocumentID(ctx, conn, s.svc.scope, documentID, cursor, filter)
},
)
@@ -86,6 +87,7 @@ func (s ControlService) ListForMeasureID(
ctx context.Context,
measureID gid.GID,
cursor *page.Cursor[coredata.ControlOrderField],
filter *coredata.ControlFilter,
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
var controls coredata.Controls
measure := &coredata.Measure{}
@@ -97,7 +99,7 @@ func (s ControlService) ListForMeasureID(
return fmt.Errorf("cannot load measure: %w", err)
}
return controls.LoadByMeasureID(ctx, conn, s.svc.scope, measureID, cursor)
return controls.LoadByMeasureID(ctx, conn, s.svc.scope, measureID, cursor, filter)
},
)
@@ -356,6 +358,7 @@ func (s ControlService) ListForFrameworkID(
ctx context.Context,
frameworkID gid.GID,
cursor *page.Cursor[coredata.ControlOrderField],
filter *coredata.ControlFilter,
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
var controls coredata.Controls
framework := &coredata.Framework{}
@@ -373,6 +376,7 @@ func (s ControlService) ListForFrameworkID(
s.svc.scope,
framework.ID,
cursor,
filter,
)
},
)
@@ -388,6 +392,7 @@ func (s ControlService) ListForOrganizationID(
ctx context.Context,
organizationID gid.GID,
cursor *page.Cursor[coredata.ControlOrderField],
filter *coredata.ControlFilter,
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
var controls coredata.Controls
organization := &coredata.Organization{}
@@ -405,6 +410,7 @@ func (s ControlService) ListForOrganizationID(
s.svc.scope,
organization.ID,
cursor,
filter,
)
},
)
@@ -420,6 +426,7 @@ func (s ControlService) ListForRiskID(
ctx context.Context,
riskID gid.GID,
cursor *page.Cursor[coredata.ControlOrderField],
filter *coredata.ControlFilter,
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
var controls coredata.Controls
risk := &coredata.Risk{}
@@ -431,7 +438,7 @@ func (s ControlService) ListForRiskID(
return fmt.Errorf("cannot load risk: %w", err)
}
return controls.LoadByRiskID(ctx, conn, s.svc.scope, risk.ID, cursor)
return controls.LoadByRiskID(ctx, conn, s.svc.scope, risk.ID, cursor, filter)
},
)

View File

@@ -302,7 +302,7 @@ func (s FrameworkService) ExportAudit(
)
controls := coredata.Controls{}
if err := controls.LoadByFrameworkID(ctx, conn, s.svc.scope, frameworkID, cursor); err != nil {
if err := controls.LoadByFrameworkID(ctx, conn, s.svc.scope, frameworkID, cursor, coredata.NewControlFilter(nil)); err != nil {
return fmt.Errorf("cannot load controls: %w", err)
}

View File

@@ -370,7 +370,7 @@ enum DatumOrderField @goModel(model: "github.com/getprobo/probo/pkg/coredata.Dat
DATA_SENSITIVITY @goEnum(value: "github.com/getprobo/probo/pkg/coredata.DatumOrderFieldDataSensitivity")
}
# Order Input Types
# Input Types
input UserOrder
@goModel(
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.UserOrderBy"
@@ -481,6 +481,11 @@ input DocumentVersionFilter {
status: DocumentStatus
}
# Input Types for Filtering
input ControlFilter {
query: String
}
# Core Types
type Organization implements Node {
id: ID!
@@ -517,6 +522,7 @@ type Organization implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
vendors(
@@ -687,6 +693,7 @@ type Framework implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -758,6 +765,7 @@ type Measure implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -830,6 +838,7 @@ type Document implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -875,6 +884,7 @@ type Risk implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!

View File

@@ -322,7 +322,7 @@ type ComplexityRoot struct {
}
Document struct {
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int
CreatedAt func(childComplexity int) int
CurrentPublishedVersion func(childComplexity int) int
Description func(childComplexity int) int
@@ -424,7 +424,7 @@ type ComplexityRoot struct {
}
Framework struct {
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int
CreatedAt func(childComplexity int) int
Description func(childComplexity int) int
ID func(childComplexity int) int
@@ -465,7 +465,7 @@ type ComplexityRoot struct {
Measure struct {
Category func(childComplexity int) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int
CreatedAt func(childComplexity int) int
Description func(childComplexity int) int
Evidences func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) int
@@ -561,7 +561,7 @@ type ComplexityRoot struct {
Organization struct {
Assets func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.AssetOrder) int
Connectors func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ConnectorOrder) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int
CreatedAt func(childComplexity int) int
Data func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DatumOrder) int
Documents func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) int
@@ -650,7 +650,7 @@ type ComplexityRoot struct {
Risk struct {
Category func(childComplexity int) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) int
Controls func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) int
CreatedAt func(childComplexity int) int
Description func(childComplexity int) int
Documents func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) int
@@ -908,7 +908,7 @@ type DocumentResolver interface {
Owner(ctx context.Context, obj *types.Document) (*types.People, error)
Organization(ctx context.Context, obj *types.Document) (*types.Organization, error)
Versions(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentVersionOrderBy, filter *types.DocumentVersionFilter) (*types.DocumentVersionConnection, error)
Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error)
Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error)
}
type DocumentVersionResolver interface {
Document(ctx context.Context, obj *types.DocumentVersion) (*types.Document, error)
@@ -932,13 +932,13 @@ type EvidenceResolver interface {
}
type FrameworkResolver interface {
Organization(ctx context.Context, obj *types.Framework) (*types.Organization, error)
Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error)
Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error)
}
type MeasureResolver interface {
Evidences(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.EvidenceOrderBy) (*types.EvidenceConnection, error)
Tasks(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.TaskOrderBy) (*types.TaskConnection, error)
Risks(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.RiskOrderBy) (*types.RiskConnection, error)
Controls(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error)
Controls(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error)
}
type MutationResolver interface {
CreateOrganization(ctx context.Context, input types.CreateOrganizationInput) (*types.CreateOrganizationPayload, error)
@@ -1015,7 +1015,7 @@ type OrganizationResolver interface {
Users(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.UserOrderBy) (*types.UserConnection, error)
Connectors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ConnectorOrder) (*types.ConnectorConnection, error)
Frameworks(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.FrameworkOrderBy) (*types.FrameworkConnection, error)
Controls(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error)
Controls(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error)
Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error)
Peoples(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PeopleOrderBy) (*types.PeopleConnection, error)
Documents(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.DocumentConnection, error)
@@ -1034,7 +1034,7 @@ type RiskResolver interface {
Organization(ctx context.Context, obj *types.Risk) (*types.Organization, error)
Measures(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.MeasureOrderBy) (*types.MeasureConnection, error)
Documents(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.DocumentOrderBy) (*types.DocumentConnection, error)
Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error)
Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error)
}
type TaskResolver interface {
AssignedTo(ctx context.Context, obj *types.Task) (*types.People, error)
@@ -1801,7 +1801,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
return e.complexity.Document.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true
return e.complexity.Document.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy), args["filter"].(*types.ControlFilter)), true
case "Document.createdAt":
if e.complexity.Document.CreatedAt == nil {
@@ -2257,7 +2257,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
return e.complexity.Framework.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true
return e.complexity.Framework.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy), args["filter"].(*types.ControlFilter)), true
case "Framework.createdAt":
if e.complexity.Framework.CreatedAt == nil {
@@ -2381,7 +2381,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
return e.complexity.Measure.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true
return e.complexity.Measure.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy), args["filter"].(*types.ControlFilter)), true
case "Measure.createdAt":
if e.complexity.Measure.CreatedAt == nil {
@@ -3339,7 +3339,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
return e.complexity.Organization.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true
return e.complexity.Organization.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy), args["filter"].(*types.ControlFilter)), true
case "Organization.createdAt":
if e.complexity.Organization.CreatedAt == nil {
@@ -3723,7 +3723,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return 0, false
}
return e.complexity.Risk.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy)), true
return e.complexity.Risk.Controls(childComplexity, args["first"].(*int), args["after"].(*page.CursorKey), args["last"].(*int), args["before"].(*page.CursorKey), args["orderBy"].(*types.ControlOrderBy), args["filter"].(*types.ControlFilter)), true
case "Risk.createdAt":
if e.complexity.Risk.CreatedAt == nil {
@@ -4643,6 +4643,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
ec.unmarshalInputAssignTaskInput,
ec.unmarshalInputConfirmEmailInput,
ec.unmarshalInputConnectorOrder,
ec.unmarshalInputControlFilter,
ec.unmarshalInputControlOrder,
ec.unmarshalInputCreateAssetInput,
ec.unmarshalInputCreateControlDocumentMappingInput,
@@ -5193,7 +5194,7 @@ enum DatumOrderField @goModel(model: "github.com/getprobo/probo/pkg/coredata.Dat
DATA_SENSITIVITY @goEnum(value: "github.com/getprobo/probo/pkg/coredata.DatumOrderFieldDataSensitivity")
}
# Order Input Types
# Input Types
input UserOrder
@goModel(
model: "github.com/getprobo/probo/pkg/server/api/console/v1/types.UserOrderBy"
@@ -5304,6 +5305,11 @@ input DocumentVersionFilter {
status: DocumentStatus
}
# Input Types for Filtering
input ControlFilter {
query: String
}
# Core Types
type Organization implements Node {
id: ID!
@@ -5340,6 +5346,7 @@ type Organization implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
vendors(
@@ -5510,6 +5517,7 @@ type Framework implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -5581,6 +5589,7 @@ type Measure implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -5653,6 +5662,7 @@ type Document implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -5698,6 +5708,7 @@ type Risk implements Node {
last: Int
before: CursorKey
orderBy: ControlOrder
filter: ControlFilter
): ControlConnection! @goField(forceResolver: true)
createdAt: Datetime!
@@ -7454,6 +7465,11 @@ func (ec *executionContext) field_Document_controls_args(ctx context.Context, ra
return nil, err
}
args["orderBy"] = arg4
arg5, err := ec.field_Document_controls_argsFilter(ctx, rawArgs)
if err != nil {
return nil, err
}
args["filter"] = arg5
return args, nil
}
func (ec *executionContext) field_Document_controls_argsFirst(
@@ -7521,6 +7537,19 @@ func (ec *executionContext) field_Document_controls_argsOrderBy(
return zeroVal, nil
}
func (ec *executionContext) field_Document_controls_argsFilter(
ctx context.Context,
rawArgs map[string]any,
) (*types.ControlFilter, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter"))
if tmp, ok := rawArgs["filter"]; ok {
return ec.unmarshalOControlFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlFilter(ctx, tmp)
}
var zeroVal *types.ControlFilter
return zeroVal, nil
}
func (ec *executionContext) field_Document_versions_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -7662,6 +7691,11 @@ func (ec *executionContext) field_Framework_controls_args(ctx context.Context, r
return nil, err
}
args["orderBy"] = arg4
arg5, err := ec.field_Framework_controls_argsFilter(ctx, rawArgs)
if err != nil {
return nil, err
}
args["filter"] = arg5
return args, nil
}
func (ec *executionContext) field_Framework_controls_argsFirst(
@@ -7729,6 +7763,19 @@ func (ec *executionContext) field_Framework_controls_argsOrderBy(
return zeroVal, nil
}
func (ec *executionContext) field_Framework_controls_argsFilter(
ctx context.Context,
rawArgs map[string]any,
) (*types.ControlFilter, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter"))
if tmp, ok := rawArgs["filter"]; ok {
return ec.unmarshalOControlFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlFilter(ctx, tmp)
}
var zeroVal *types.ControlFilter
return zeroVal, nil
}
func (ec *executionContext) field_Measure_controls_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -7757,6 +7804,11 @@ func (ec *executionContext) field_Measure_controls_args(ctx context.Context, raw
return nil, err
}
args["orderBy"] = arg4
arg5, err := ec.field_Measure_controls_argsFilter(ctx, rawArgs)
if err != nil {
return nil, err
}
args["filter"] = arg5
return args, nil
}
func (ec *executionContext) field_Measure_controls_argsFirst(
@@ -7824,6 +7876,19 @@ func (ec *executionContext) field_Measure_controls_argsOrderBy(
return zeroVal, nil
}
func (ec *executionContext) field_Measure_controls_argsFilter(
ctx context.Context,
rawArgs map[string]any,
) (*types.ControlFilter, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter"))
if tmp, ok := rawArgs["filter"]; ok {
return ec.unmarshalOControlFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlFilter(ctx, tmp)
}
var zeroVal *types.ControlFilter
return zeroVal, nil
}
func (ec *executionContext) field_Measure_evidences_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -9891,6 +9956,11 @@ func (ec *executionContext) field_Organization_controls_args(ctx context.Context
return nil, err
}
args["orderBy"] = arg4
arg5, err := ec.field_Organization_controls_argsFilter(ctx, rawArgs)
if err != nil {
return nil, err
}
args["filter"] = arg5
return args, nil
}
func (ec *executionContext) field_Organization_controls_argsFirst(
@@ -9958,6 +10028,19 @@ func (ec *executionContext) field_Organization_controls_argsOrderBy(
return zeroVal, nil
}
func (ec *executionContext) field_Organization_controls_argsFilter(
ctx context.Context,
rawArgs map[string]any,
) (*types.ControlFilter, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter"))
if tmp, ok := rawArgs["filter"]; ok {
return ec.unmarshalOControlFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlFilter(ctx, tmp)
}
var zeroVal *types.ControlFilter
return zeroVal, nil
}
func (ec *executionContext) field_Organization_data_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -10887,6 +10970,11 @@ func (ec *executionContext) field_Risk_controls_args(ctx context.Context, rawArg
return nil, err
}
args["orderBy"] = arg4
arg5, err := ec.field_Risk_controls_argsFilter(ctx, rawArgs)
if err != nil {
return nil, err
}
args["filter"] = arg5
return args, nil
}
func (ec *executionContext) field_Risk_controls_argsFirst(
@@ -10954,6 +11042,19 @@ func (ec *executionContext) field_Risk_controls_argsOrderBy(
return zeroVal, nil
}
func (ec *executionContext) field_Risk_controls_argsFilter(
ctx context.Context,
rawArgs map[string]any,
) (*types.ControlFilter, error) {
ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter"))
if tmp, ok := rawArgs["filter"]; ok {
return ec.unmarshalOControlFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlFilter(ctx, tmp)
}
var zeroVal *types.ControlFilter
return zeroVal, nil
}
func (ec *executionContext) field_Risk_documents_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) {
var err error
args := map[string]any{}
@@ -16991,7 +17092,7 @@ func (ec *executionContext) _Document_controls(ctx context.Context, field graphq
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Document().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy))
return ec.resolvers.Document().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy), fc.Args["filter"].(*types.ControlFilter))
})
if err != nil {
ec.Error(ctx, err)
@@ -19993,7 +20094,7 @@ func (ec *executionContext) _Framework_controls(ctx context.Context, field graph
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Framework().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy))
return ec.resolvers.Framework().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy), fc.Args["filter"].(*types.ControlFilter))
})
if err != nil {
ec.Error(ctx, err)
@@ -20991,7 +21092,7 @@ func (ec *executionContext) _Measure_controls(ctx context.Context, field graphql
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Measure().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy))
return ec.resolvers.Measure().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy), fc.Args["filter"].(*types.ControlFilter))
})
if err != nil {
ec.Error(ctx, err)
@@ -25700,7 +25801,7 @@ func (ec *executionContext) _Organization_controls(ctx context.Context, field gr
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Organization().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy))
return ec.resolvers.Organization().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy), fc.Args["filter"].(*types.ControlFilter))
})
if err != nil {
ec.Error(ctx, err)
@@ -28825,7 +28926,7 @@ func (ec *executionContext) _Risk_controls(ctx context.Context, field graphql.Co
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Risk().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy))
return ec.resolvers.Risk().Controls(rctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*page.CursorKey), fc.Args["last"].(*int), fc.Args["before"].(*page.CursorKey), fc.Args["orderBy"].(*types.ControlOrderBy), fc.Args["filter"].(*types.ControlFilter))
})
if err != nil {
ec.Error(ctx, err)
@@ -36823,6 +36924,33 @@ func (ec *executionContext) unmarshalInputConnectorOrder(ctx context.Context, ob
return it, nil
}
func (ec *executionContext) unmarshalInputControlFilter(ctx context.Context, obj any) (types.ControlFilter, error) {
var it types.ControlFilter
asMap := map[string]any{}
for k, v := range obj.(map[string]any) {
asMap[k] = v
}
fieldsInOrder := [...]string{"query"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
continue
}
switch k {
case "query":
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("query"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.Query = data
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputControlOrder(ctx context.Context, obj any) (types.ControlOrderBy, error) {
var it types.ControlOrderBy
asMap := map[string]any{}
@@ -53558,6 +53686,14 @@ func (ec *executionContext) unmarshalOConnectorOrder2ᚖgithubᚗcomᚋgetprobo
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalOControlFilter2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlFilter(ctx context.Context, v any) (*types.ControlFilter, error) {
if v == nil {
return nil, nil
}
res, err := ec.unmarshalInputControlFilter(ctx, v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalOControlOrder2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋserverᚋapiᚋconsoleᚋv1ᚋtypesᚐControlOrderBy(ctx context.Context, v any) (*types.ControlOrderBy, error) {
if v == nil {
return nil, nil

View File

@@ -143,6 +143,10 @@ type ControlEdge struct {
Node *Control `json:"node"`
}
type ControlFilter struct {
Query *string `json:"query,omitempty"`
}
type CreateAssetInput struct {
OrganizationID gid.GID `json:"organizationId"`
Name string `json:"name"`

View File

@@ -261,7 +261,7 @@ func (r *documentResolver) Versions(ctx context.Context, obj *types.Document, fi
}
// Controls is the resolver for the controls field.
func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
@@ -277,7 +277,12 @@ func (r *documentResolver) Controls(ctx context.Context, obj *types.Document, fi
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Controls.ListForDocumentID(ctx, obj.ID, cursor)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := svc.Controls.ListForDocumentID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
panic(fmt.Errorf("cannot list document controls: %w", err))
}
@@ -489,7 +494,7 @@ func (r *frameworkResolver) Organization(ctx context.Context, obj *types.Framewo
}
// Controls is the resolver for the controls field.
func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
@@ -505,7 +510,12 @@ func (r *frameworkResolver) Controls(ctx context.Context, obj *types.Framework,
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Controls.ListForFrameworkID(ctx, obj.ID, cursor)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := svc.Controls.ListForFrameworkID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
return nil, fmt.Errorf("cannot list controls: %w", err)
}
@@ -589,7 +599,7 @@ func (r *measureResolver) Risks(ctx context.Context, obj *types.Measure, first *
}
// Controls is the resolver for the controls field.
func (r *measureResolver) Controls(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
func (r *measureResolver) Controls(ctx context.Context, obj *types.Measure, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
@@ -605,7 +615,12 @@ func (r *measureResolver) Controls(ctx context.Context, obj *types.Measure, firs
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Controls.ListForMeasureID(ctx, obj.ID, cursor)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := svc.Controls.ListForMeasureID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
return nil, fmt.Errorf("cannot list measure controls: %w", err)
}
@@ -1974,7 +1989,7 @@ func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organi
}
// Controls is the resolver for the controls field.
func (r *organizationResolver) Controls(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
func (r *organizationResolver) Controls(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
@@ -1990,7 +2005,12 @@ func (r *organizationResolver) Controls(ctx context.Context, obj *types.Organiza
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Controls.ListForOrganizationID(ctx, obj.ID, cursor)
var controlFilter = coredata.NewControlFilter(nil)
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := svc.Controls.ListForOrganizationID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
return nil, fmt.Errorf("cannot list controls: %w", err)
}
@@ -1998,11 +2018,6 @@ func (r *organizationResolver) Controls(ctx context.Context, obj *types.Organiza
return types.NewControlConnection(page), nil
}
// // Controls is the resolver for the controls field.
// func (r *organizationResolver) Controls(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
// panic(fmt.Errorf("not implemented: Controls - controls"))
// }
// Vendors is the resolver for the vendors field.
func (r *organizationResolver) Vendors(ctx context.Context, obj *types.Organization, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.VendorOrderBy) (*types.VendorConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
@@ -2412,7 +2427,7 @@ func (r *riskResolver) Documents(ctx context.Context, obj *types.Risk, first *in
}
// Controls is the resolver for the controls field.
func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy) (*types.ControlConnection, error) {
func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ControlOrderBy, filter *types.ControlFilter) (*types.ControlConnection, error) {
svc := GetTenantService(ctx, r.proboSvc, obj.ID.TenantID())
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
@@ -2428,7 +2443,12 @@ func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
page, err := svc.Controls.ListForRiskID(ctx, obj.ID, cursor)
var controlFilter *coredata.ControlFilter
if filter != nil {
controlFilter = coredata.NewControlFilter(filter.Query)
}
page, err := svc.Controls.ListForRiskID(ctx, obj.ID, cursor, controlFilter)
if err != nil {
panic(fmt.Errorf("cannot list risk controls: %w", err))
}