@@ -100,6 +112,8 @@ type RowProps = {
function RiskRow(props: RowProps) {
const { __ } = useTranslate();
const { risk, connectionId, organizationId } = props;
+ const { snapshotId } = useParams<{ snapshotId?: string }>();
+ const isSnapshotMode = Boolean(snapshotId);
const [deleteRisk] = useDeleteRiskMutation();
const confirm = useConfirm();
const onDelete = () => {
@@ -125,14 +139,21 @@ function RiskRow(props: RowProps) {
);
};
const formDialogRef = useDialogRef();
+
+ const riskUrl = isSnapshotMode && snapshotId
+ ? `/organizations/${organizationId}/snapshots/${snapshotId}/risks/${risk.id}/overview`
+ : `/organizations/${organizationId}/risks/${risk.id}/overview`;
+
return (
<>
-
-
+ {!isSnapshotMode && (
+
+ )}
+
| {risk.name} |
{risk.category} |
{getTreatment(__, risk.treatment)} |
@@ -143,22 +164,24 @@ function RiskRow(props: RowProps) {
-
- formDialogRef.current?.open()}
- >
- {__("Edit")}
-
+ {!isSnapshotMode && (
+
+ formDialogRef.current?.open()}
+ >
+ {__("Edit")}
+
-
- {__("Delete")}
-
-
+
+ {__("Delete")}
+
+
+ )}
|
>
diff --git a/apps/console/src/routes/riskRoutes.ts b/apps/console/src/routes/riskRoutes.ts
index 8b93ef30a..990e4de0c 100644
--- a/apps/console/src/routes/riskRoutes.ts
+++ b/apps/console/src/routes/riskRoutes.ts
@@ -14,7 +14,20 @@ export const riskRoutes = [
path: "risks",
fallback: RisksPageSkeleton,
queryLoader: ({ organizationId }) =>
- loadQuery(relayEnvironment, risksQuery, { organizationId }),
+ loadQuery(relayEnvironment, risksQuery, {
+ organizationId,
+ snapshotId: null
+ }),
+ Component: lazy(() => import("/pages/organizations/risks/RisksPage")),
+ },
+ {
+ path: "snapshots/:snapshotId/risks",
+ fallback: RisksPageSkeleton,
+ queryLoader: ({ organizationId, snapshotId }) =>
+ loadQuery(relayEnvironment, risksQuery, {
+ organizationId,
+ snapshotId
+ }),
Component: lazy(() => import("/pages/organizations/risks/RisksPage")),
},
{
@@ -61,4 +74,27 @@ export const riskRoutes = [
},
],
},
+ {
+ path: "snapshots/:snapshotId/risks/:riskId",
+ fallback: PageSkeleton,
+ queryLoader: ({ riskId }) =>
+ loadQuery(relayEnvironment, riskNodeQuery, { riskId }),
+ Component: lazy(() => import("/pages/organizations/risks/RiskDetailPage")),
+ children: [
+ {
+ path: "",
+ loader: () => {
+ throw redirect("overview");
+ },
+ Component: Fragment,
+ },
+ {
+ path: "overview",
+ fallback: LinkCardSkeleton,
+ Component: lazy(
+ () => import("/pages/organizations/risks/tabs/RiskOverviewTab.tsx")
+ ),
+ },
+ ],
+ },
] satisfies AppRoute[];
diff --git a/packages/helpers/src/snapshots.ts b/packages/helpers/src/snapshots.ts
index 3cb628424..21bad5ca1 100644
--- a/packages/helpers/src/snapshots.ts
+++ b/packages/helpers/src/snapshots.ts
@@ -1,6 +1,7 @@
type Translator = (s: string) => string;
export const snapshotTypes = [
+ "RISKS",
"VENDORS",
"ASSETS",
"DATA",
@@ -39,6 +40,8 @@ export function getSnapshotTypeLabel(__: Translator, type: string | null | undef
export function getSnapshotTypeUrlPath(type?: string): string {
switch (type) {
+ case "RISKS":
+ return "/risks";
case "VENDORS":
return "/vendors";
case "ASSETS":
diff --git a/pkg/coredata/migrations/20250902T093819Z.sql b/pkg/coredata/migrations/20250902T093819Z.sql
new file mode 100644
index 000000000..6e1a6b210
--- /dev/null
+++ b/pkg/coredata/migrations/20250902T093819Z.sql
@@ -0,0 +1,11 @@
+ALTER TABLE risks ADD COLUMN snapshot_id TEXT;
+ALTER TABLE risks ADD COLUMN source_id TEXT;
+
+ALTER TABLE risks ADD CONSTRAINT risks_snapshot_id_fkey
+ FOREIGN KEY (snapshot_id)
+ REFERENCES snapshots(id)
+ ON UPDATE CASCADE
+ ON DELETE CASCADE;
+
+ALTER TABLE risks ADD CONSTRAINT risks_source_id_snapshot_id_key
+ UNIQUE (source_id, snapshot_id);
diff --git a/pkg/coredata/risk.go b/pkg/coredata/risk.go
index acf66cc95..0114f58e2 100644
--- a/pkg/coredata/risk.go
+++ b/pkg/coredata/risk.go
@@ -42,11 +42,17 @@ type (
ResidualLikelihood int `db:"residual_likelihood"`
ResidualImpact int `db:"residual_impact"`
ResidualRiskScore int `db:"residual_risk_score"`
+ SnapshotID *gid.GID `db:"snapshot_id"`
+ SourceID *gid.GID `db:"source_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
Risks []*Risk
+
+ RiskSnapshotter interface {
+ InsertRiskSnapshots(ctx context.Context, conn pg.Conn, scope Scoper, organizationID, snapshotID gid.GID) error
+ }
)
func (r *Risk) CursorKey(orderBy RiskOrderField) page.CursorKey {
@@ -79,7 +85,9 @@ func (r *Risks) CountByMeasureID(
WITH rsks AS (
SELECT
r.id,
- r.tenant_id
+ r.tenant_id,
+ r.search_vector,
+ r.snapshot_id
FROM
risks r
INNER JOIN
@@ -136,6 +144,8 @@ WITH rsks AS (
r.residual_likelihood,
r.residual_impact,
r.residual_risk_score,
+ r.snapshot_id,
+ r.source_id,
r.search_vector,
r.created_at,
r.updated_at
@@ -161,6 +171,8 @@ SELECT
residual_likelihood,
residual_impact,
residual_risk_score,
+ snapshot_id,
+ source_id,
created_at,
updated_at
FROM
@@ -246,6 +258,8 @@ SELECT
residual_impact,
residual_risk_score,
category,
+ snapshot_id,
+ source_id,
created_at,
updated_at
FROM risks
@@ -298,6 +312,8 @@ SELECT
residual_likelihood,
residual_impact,
residual_risk_score,
+ snapshot_id,
+ source_id,
created_at,
updated_at
FROM risks
@@ -378,6 +394,7 @@ SET
updated_at = @updated_at
WHERE %s
AND id = @risk_id
+ AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -408,7 +425,7 @@ func (r *Risk) Delete(
riskID gid.GID,
) error {
q := `
-DELETE FROM risks WHERE %s AND id = @id
+DELETE FROM risks WHERE %s AND id = @id AND snapshot_id IS NULL
`
q = fmt.Sprintf(q, scope.SQLFragment())
@@ -430,7 +447,9 @@ func (r *Risks) CountByDocumentID(
WITH rsks AS (
SELECT
r.id,
- r.tenant_id
+ r.tenant_id,
+ r.search_vector,
+ r.snapshot_id
FROM
risks r
INNER JOIN
@@ -460,3 +479,78 @@ WHERE %s
return count, nil
}
+
+func (r Risks) Snapshot(ctx context.Context, conn pg.Conn, scope Scoper, organizationID, snapshotID gid.GID) error {
+ if err := r.InsertRiskSnapshots(ctx, conn, scope, organizationID, snapshotID); err != nil {
+ return fmt.Errorf("cannot create risk snapshots: %w", err)
+ }
+
+ return nil
+}
+
+func (r Risks) InsertRiskSnapshots(
+ ctx context.Context,
+ conn pg.Conn,
+ scope Scoper,
+ organizationID gid.GID,
+ snapshotID gid.GID,
+) error {
+ query := `
+INSERT INTO risks (
+ tenant_id,
+ id,
+ snapshot_id,
+ source_id,
+ organization_id,
+ name,
+ description,
+ category,
+ treatment,
+ note,
+ owner_id,
+ inherent_likelihood,
+ inherent_impact,
+ residual_likelihood,
+ residual_impact,
+ created_at,
+ updated_at
+)
+SELECT
+ @tenant_id,
+ generate_gid(decode_base64_unpadded(@tenant_id), @risk_entity_type),
+ @snapshot_id,
+ r.id,
+ r.organization_id,
+ r.name,
+ r.description,
+ r.category,
+ r.treatment,
+ r.note,
+ r.owner_id,
+ r.inherent_likelihood,
+ r.inherent_impact,
+ r.residual_likelihood,
+ r.residual_impact,
+ r.created_at,
+ r.updated_at
+FROM risks r
+WHERE %s AND organization_id = @organization_id AND snapshot_id IS NULL
+ `
+
+ query = fmt.Sprintf(query, scope.SQLFragment())
+
+ args := pgx.StrictNamedArgs{
+ "tenant_id": scope.GetTenantID(),
+ "snapshot_id": snapshotID,
+ "organization_id": organizationID,
+ "risk_entity_type": RiskEntityType,
+ }
+ maps.Copy(args, scope.SQLArguments())
+
+ _, err := conn.Exec(ctx, query, args)
+ if err != nil {
+ return fmt.Errorf("cannot insert risk snapshots: %w", err)
+ }
+
+ return nil
+}
diff --git a/pkg/coredata/risk_filter.go b/pkg/coredata/risk_filter.go
index 0017b7266..b4f75ef82 100644
--- a/pkg/coredata/risk_filter.go
+++ b/pkg/coredata/risk_filter.go
@@ -15,36 +15,62 @@
package coredata
import (
+ "github.com/getprobo/probo/pkg/gid"
"github.com/jackc/pgx/v5"
)
type (
RiskFilter struct {
- query *string
+ query *string
+ snapshotID **gid.GID
}
)
-func NewRiskFilter(query *string) *RiskFilter {
+func NewRiskFilter(query *string, snapshotID **gid.GID) *RiskFilter {
return &RiskFilter{
- query: query,
+ query: query,
+ snapshotID: snapshotID,
}
}
-func (f *RiskFilter) SQLArguments() pgx.NamedArgs {
- return pgx.NamedArgs{
+func (f *RiskFilter) SQLArguments() pgx.StrictNamedArgs {
+ args := pgx.StrictNamedArgs{
"query": f.query,
}
+
+ if f.snapshotID == nil {
+ args["has_snapshot_filter"] = false
+ args["filter_snapshot_id"] = nil
+ } else if *f.snapshotID == nil {
+ args["has_snapshot_filter"] = true
+ args["filter_snapshot_id"] = nil
+ } else {
+ args["has_snapshot_filter"] = true
+ args["filter_snapshot_id"] = **f.snapshotID
+ }
+
+ return args
}
func (f *RiskFilter) SQLFragment() string {
- if f.query == nil || *f.query == "" {
- return "TRUE"
- }
-
return `
- search_vector @@ (
- SELECT to_tsquery('simple', string_agg(lexeme || ':*', ' & '))
- FROM unnest(regexp_split_to_array(trim(@query), '\s+')) AS lexeme
- )
- `
+(
+ CASE
+ WHEN @query::text IS NOT NULL AND @query::text != '' THEN
+ search_vector @@ (
+ SELECT to_tsquery('simple', string_agg(lexeme || ':*', ' & '))
+ FROM unnest(regexp_split_to_array(trim(@query), '\s+')) AS lexeme
+ )
+ ELSE TRUE
+ END
+ AND
+ CASE
+ WHEN @has_snapshot_filter::boolean = false THEN TRUE
+ WHEN @has_snapshot_filter::boolean = true AND @filter_snapshot_id::text IS NOT NULL THEN
+ snapshot_id = @filter_snapshot_id::text
+ WHEN @has_snapshot_filter::boolean = true AND @filter_snapshot_id::text IS NULL THEN
+ snapshot_id IS NULL
+ ELSE TRUE
+ END
+)`
}
diff --git a/pkg/coredata/snapshottable.go b/pkg/coredata/snapshottable.go
index 6aed421ee..afd9392dd 100644
--- a/pkg/coredata/snapshottable.go
+++ b/pkg/coredata/snapshottable.go
@@ -30,6 +30,8 @@ func GetSnapshottable(snapshotType SnapshotsType) (Snapshottable, error) {
switch snapshotType {
case SnapshotsTypeAssets:
return Assets{}, nil
+ case SnapshotsTypeRisks:
+ return Risks{}, nil
case SnapshotsTypeData:
return Data{}, nil
case SnapshotsTypeNonConformityRegistries:
diff --git a/pkg/probo/framework_service.go b/pkg/probo/framework_service.go
index 80958a0d6..b27ba92e8 100644
--- a/pkg/probo/framework_service.go
+++ b/pkg/probo/framework_service.go
@@ -364,12 +364,13 @@ func (s FrameworkService) StateOfApplicability(ctx context.Context, frameworkID
for _, measure := range measures {
risks := coredata.Risks{}
+ var nilSnapshotID *gid.GID = nil
risksCount, err := risks.CountByMeasureID(
ctx,
conn,
s.svc.scope,
measure.ID,
- coredata.NewRiskFilter(nil),
+ coredata.NewRiskFilter(nil, &nilSnapshotID),
)
if err != nil {
return fmt.Errorf("cannot count risks: %w", err)
@@ -405,12 +406,13 @@ func (s FrameworkService) StateOfApplicability(ctx context.Context, frameworkID
for _, document := range documents {
risks := coredata.Risks{}
+ var nilSnapshotID *gid.GID = nil
risksCount, err := risks.CountByDocumentID(
ctx,
conn,
s.svc.scope,
document.ID,
- coredata.NewRiskFilter(nil),
+ coredata.NewRiskFilter(nil, &nilSnapshotID),
)
if err != nil {
return fmt.Errorf("cannot count risks: %w", err)
diff --git a/pkg/probo/risk_service.go b/pkg/probo/risk_service.go
index b3d4f1e11..0994708ca 100644
--- a/pkg/probo/risk_service.go
+++ b/pkg/probo/risk_service.go
@@ -186,7 +186,6 @@ func (s RiskService) CreateDocumentMapping(
riskDocument := &coredata.RiskDocument{
RiskID: risk.ID,
DocumentID: document.ID,
- TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
}
@@ -254,7 +253,6 @@ func (s RiskService) CreateMeasureMapping(
riskMeasure := &coredata.RiskMeasure{
RiskID: risk.ID,
MeasureID: measure.ID,
- TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
}
@@ -291,7 +289,6 @@ func (s RiskService) DeleteMeasureMapping(
riskMeasure := &coredata.RiskMeasure{
RiskID: riskID,
MeasureID: measureID,
- TenantID: s.svc.scope.GetTenantID(),
CreatedAt: time.Now(),
}
diff --git a/pkg/server/api/console/v1/schema.graphql b/pkg/server/api/console/v1/schema.graphql
index 172cb496b..5b835ca01 100644
--- a/pkg/server/api/console/v1/schema.graphql
+++ b/pkg/server/api/console/v1/schema.graphql
@@ -1100,6 +1100,7 @@ input MeasureFilter {
input RiskFilter {
query: String
+ snapshotId: ID
}
input PeopleFilter {
@@ -1236,7 +1237,7 @@ type Organization implements Node {
last: Int
before: CursorKey
orderBy: RiskOrder
- filter: RiskFilter
+ filter: RiskFilter = { snapshotId: null }
): RiskConnection! @goField(forceResolver: true)
tasks(
@@ -1664,6 +1665,7 @@ type Document implements Node {
type Risk implements Node {
id: ID!
+ snapshotId: ID
name: String!
description: String!
category: String!
diff --git a/pkg/server/api/console/v1/schema/schema.go b/pkg/server/api/console/v1/schema/schema.go
index a27a4dcb8..8905074af 100644
--- a/pkg/server/api/console/v1/schema/schema.go
+++ b/pkg/server/api/console/v1/schema/schema.go
@@ -1027,6 +1027,7 @@ type ComplexityRoot struct {
ResidualImpact func(childComplexity int) int
ResidualLikelihood func(childComplexity int) int
ResidualRiskScore func(childComplexity int) int
+ SnapshotID func(childComplexity int) int
Treatment func(childComplexity int) int
UpdatedAt func(childComplexity int) int
}
@@ -6224,6 +6225,13 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin
return e.complexity.Risk.ResidualRiskScore(childComplexity), true
+ case "Risk.snapshotId":
+ if e.complexity.Risk.SnapshotID == nil {
+ break
+ }
+
+ return e.complexity.Risk.SnapshotID(childComplexity), true
+
case "Risk.treatment":
if e.complexity.Risk.Treatment == nil {
break
@@ -9061,6 +9069,7 @@ input MeasureFilter {
input RiskFilter {
query: String
+ snapshotId: ID
}
input PeopleFilter {
@@ -9197,7 +9206,7 @@ type Organization implements Node {
last: Int
before: CursorKey
orderBy: RiskOrder
- filter: RiskFilter
+ filter: RiskFilter = { snapshotId: null }
): RiskConnection! @goField(forceResolver: true)
tasks(
@@ -9625,6 +9634,7 @@ type Document implements Node {
type Risk implements Node {
id: ID!
+ snapshotId: ID
name: String!
description: String!
category: String!
@@ -45984,6 +45994,47 @@ func (ec *executionContext) fieldContext_Risk_id(_ context.Context, field graphq
return fc, nil
}
+func (ec *executionContext) _Risk_snapshotId(ctx context.Context, field graphql.CollectedField, obj *types.Risk) (ret graphql.Marshaler) {
+ fc, err := ec.fieldContext_Risk_snapshotId(ctx, field)
+ if err != nil {
+ return graphql.Null
+ }
+ ctx = graphql.WithFieldContext(ctx, fc)
+ defer func() {
+ if r := recover(); r != nil {
+ ec.Error(ctx, ec.Recover(ctx, r))
+ ret = graphql.Null
+ }
+ }()
+ resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
+ ctx = rctx // use context from middleware stack in children
+ return obj.SnapshotID, nil
+ })
+ if err != nil {
+ ec.Error(ctx, err)
+ return graphql.Null
+ }
+ if resTmp == nil {
+ return graphql.Null
+ }
+ res := resTmp.(*gid.GID)
+ fc.Result = res
+ return ec.marshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, field.Selections, res)
+}
+
+func (ec *executionContext) fieldContext_Risk_snapshotId(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
+ fc = &graphql.FieldContext{
+ Object: "Risk",
+ Field: field,
+ IsMethod: false,
+ IsResolver: false,
+ Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
+ return nil, errors.New("field of type ID does not have child fields")
+ },
+ }
+ return fc, nil
+}
+
func (ec *executionContext) _Risk_name(ctx context.Context, field graphql.CollectedField, obj *types.Risk) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_Risk_name(ctx, field)
if err != nil {
@@ -47135,6 +47186,8 @@ func (ec *executionContext) fieldContext_RiskEdge_node(_ context.Context, field
switch field.Name {
case "id":
return ec.fieldContext_Risk_id(ctx, field)
+ case "snapshotId":
+ return ec.fieldContext_Risk_snapshotId(ctx, field)
case "name":
return ec.fieldContext_Risk_name(ctx, field)
case "description":
@@ -51015,6 +51068,8 @@ func (ec *executionContext) fieldContext_UpdateRiskPayload_risk(_ context.Contex
switch field.Name {
case "id":
return ec.fieldContext_Risk_id(ctx, field)
+ case "snapshotId":
+ return ec.fieldContext_Risk_snapshotId(ctx, field)
case "name":
return ec.fieldContext_Risk_name(ctx, field)
case "description":
@@ -63958,7 +64013,7 @@ func (ec *executionContext) unmarshalInputRiskFilter(ctx context.Context, obj an
asMap[k] = v
}
- fieldsInOrder := [...]string{"query"}
+ fieldsInOrder := [...]string{"query", "snapshotId"}
for _, k := range fieldsInOrder {
v, ok := asMap[k]
if !ok {
@@ -63972,6 +64027,13 @@ func (ec *executionContext) unmarshalInputRiskFilter(ctx context.Context, obj an
return it, err
}
it.Query = data
+ case "snapshotId":
+ ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("snapshotId"))
+ data, err := ec.unmarshalOID2ᚖgithubᚗcomᚋgetproboᚋproboᚋpkgᚋgidᚐGID(ctx, v)
+ if err != nil {
+ return it, err
+ }
+ it.SnapshotID = data
}
}
@@ -76125,6 +76187,8 @@ func (ec *executionContext) _Risk(ctx context.Context, sel ast.SelectionSet, obj
if out.Values[i] == graphql.Null {
atomic.AddUint32(&out.Invalids, 1)
}
+ case "snapshotId":
+ out.Values[i] = ec._Risk_snapshotId(ctx, field, obj)
case "name":
out.Values[i] = ec._Risk_name(ctx, field, obj)
if out.Values[i] == graphql.Null {
diff --git a/pkg/server/api/console/v1/types/risk.go b/pkg/server/api/console/v1/types/risk.go
index 784bb8fba..34dd971ed 100644
--- a/pkg/server/api/console/v1/types/risk.go
+++ b/pkg/server/api/console/v1/types/risk.go
@@ -67,6 +67,7 @@ func NewRisk(r *coredata.Risk) *Risk {
return &Risk{
ID: r.ID,
Name: r.Name,
+ SnapshotID: r.SnapshotID,
Description: r.Description,
Treatment: r.Treatment,
InherentLikelihood: r.InherentLikelihood,
diff --git a/pkg/server/api/console/v1/types/types.go b/pkg/server/api/console/v1/types/types.go
index 8b8209104..cfd6e5b37 100644
--- a/pkg/server/api/console/v1/types/types.go
+++ b/pkg/server/api/console/v1/types/types.go
@@ -1336,6 +1336,7 @@ type RequestSignaturePayload struct {
type Risk struct {
ID gid.GID `json:"id"`
+ SnapshotID *gid.GID `json:"snapshotId,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Category string `json:"category"`
@@ -1365,7 +1366,8 @@ type RiskEdge struct {
}
type RiskFilter struct {
- Query *string `json:"query,omitempty"`
+ Query *string `json:"query,omitempty"`
+ SnapshotID *gid.GID `json:"snapshotId,omitempty"`
}
type SendSigningNotificationsInput struct {
diff --git a/pkg/server/api/console/v1/v1_resolver.go b/pkg/server/api/console/v1/v1_resolver.go
index 5a9c0f22c..ad6d0aadb 100644
--- a/pkg/server/api/console/v1/v1_resolver.go
+++ b/pkg/server/api/console/v1/v1_resolver.go
@@ -1034,9 +1034,9 @@ func (r *measureResolver) Risks(ctx context.Context, obj *types.Measure, first *
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- var riskFilter = coredata.NewRiskFilter(nil)
+ var riskFilter = coredata.NewRiskFilter(nil, nil)
if filter != nil {
- riskFilter = coredata.NewRiskFilter(filter.Query)
+ riskFilter = coredata.NewRiskFilter(filter.Query, &filter.SnapshotID)
}
page, err := prb.Risks.ListForMeasureID(ctx, obj.ID, cursor, riskFilter)
@@ -3595,9 +3595,9 @@ func (r *organizationResolver) Risks(ctx context.Context, obj *types.Organizatio
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
- var riskFilter = coredata.NewRiskFilter(nil)
+ var riskFilter = coredata.NewRiskFilter(nil, nil)
if filter != nil {
- riskFilter = coredata.NewRiskFilter(filter.Query)
+ riskFilter = coredata.NewRiskFilter(filter.Query, &filter.SnapshotID)
}
page, err := prb.Risks.ListForOrganizationID(ctx, obj.ID, cursor, riskFilter)