@@ -175,6 +175,72 @@ WHERE %s
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controls) LoadByRiskID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
riskID gid.GID,
|
||||
cursor *page.Cursor[ControlOrderField],
|
||||
) error {
|
||||
q := `
|
||||
WITH ctrl AS (
|
||||
SELECT DISTINCT
|
||||
c.id,
|
||||
c.reference_id,
|
||||
c.framework_id,
|
||||
c.tenant_id,
|
||||
c.name,
|
||||
c.description,
|
||||
c.created_at,
|
||||
c.updated_at
|
||||
FROM
|
||||
controls c
|
||||
LEFT JOIN
|
||||
controls_policies cp ON c.id = cp.control_id
|
||||
LEFT JOIN
|
||||
risks_policies rp ON cp.policy_id = rp.policy_id
|
||||
LEFT JOIN
|
||||
controls_mitigations cm ON c.id = cm.control_id
|
||||
LEFT JOIN
|
||||
risks_mitigations rm ON (rm.mitigation_id = cm.mitigation_id)
|
||||
WHERE
|
||||
rp.risk_id = @risk_id OR rm.risk_id = @risk_id
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
reference_id,
|
||||
framework_id,
|
||||
tenant_id,
|
||||
name,
|
||||
description,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
ctrl
|
||||
WHERE %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.NamedArgs{"risk_id": riskID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query controls: %w", err)
|
||||
}
|
||||
|
||||
controls, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Control])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect controls: %w", err)
|
||||
}
|
||||
|
||||
*c = controls
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controls) LoadByFrameworkID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
|
||||
7
pkg/coredata/migrations/20250411T212500Z.sql
Normal file
7
pkg/coredata/migrations/20250411T212500Z.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
ALTER TABLE risks RENAME COLUMN probability TO likelihood;
|
||||
ALTER TABLE risks ADD COLUMN residual_likelihood float;
|
||||
ALTER TABLE risks ADD COLUMN residual_impact float;
|
||||
ALTER TABLE risks ALTER COLUMN likelihood TYPE float;
|
||||
ALTER TABLE risks ALTER COLUMN impact TYPE float;
|
||||
|
||||
ALTER TABLE risks_mitigations DROP COLUMN probability, DROP COLUMN impact;
|
||||
5
pkg/coredata/migrations/20250411T215600Z.sql
Normal file
5
pkg/coredata/migrations/20250411T215600Z.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
UPDATE risks SET residual_impact = impact WHERE residual_impact IS NULL;
|
||||
UPDATE risks SET residual_likelihood = likelihood WHERE residual_likelihood IS NULL;
|
||||
|
||||
ALTER TABLE risks ALTER COLUMN residual_impact SET NOT NULL;
|
||||
ALTER TABLE risks ALTER COLUMN residual_likelihood SET NOT NULL;
|
||||
2
pkg/coredata/migrations/20250411T225200Z.sql
Normal file
2
pkg/coredata/migrations/20250411T225200Z.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE risks RENAME COLUMN likelihood TO inherent_likelihood;
|
||||
ALTER TABLE risks RENAME COLUMN impact TO inherent_impact;
|
||||
7
pkg/coredata/migrations/20250411T233300Z.sql
Normal file
7
pkg/coredata/migrations/20250411T233300Z.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE risks_policies (
|
||||
risk_id TEXT NOT NULL REFERENCES risks(id),
|
||||
policy_id TEXT NOT NULL REFERENCES policies(id),
|
||||
tenant_id TEXT NOT NULL,
|
||||
created_at timestamp NOT NULL,
|
||||
PRIMARY KEY (risk_id, policy_id)
|
||||
);
|
||||
@@ -299,3 +299,66 @@ WHERE %s
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Policies) LoadByRiskID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
riskID gid.GID,
|
||||
cursor *page.Cursor[PolicyOrderField],
|
||||
) error {
|
||||
q := `
|
||||
WITH plcs AS (
|
||||
SELECT
|
||||
p.id,
|
||||
p.tenant_id,
|
||||
p.organization_id,
|
||||
p.owner_id,
|
||||
p.name,
|
||||
p.content,
|
||||
p.status,
|
||||
p.review_date,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM
|
||||
policies p
|
||||
INNER JOIN
|
||||
risks_policies rp ON p.id = rp.policy_id
|
||||
WHERE
|
||||
rp.risk_id = @risk_id
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
owner_id,
|
||||
name,
|
||||
content,
|
||||
status,
|
||||
review_date,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
plcs
|
||||
WHERE %s
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"risk_id": riskID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, cursor.SQLArguments())
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query policies: %w", err)
|
||||
}
|
||||
|
||||
policies, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Policy])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect policies: %w", err)
|
||||
}
|
||||
|
||||
*p = policies
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -28,14 +28,16 @@ import (
|
||||
|
||||
type (
|
||||
Risk struct {
|
||||
ID gid.GID
|
||||
OrganizationID gid.GID
|
||||
Name string
|
||||
Description string
|
||||
Probability float64
|
||||
Impact float64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
Name string `db:"name"`
|
||||
Description string `db:"description"`
|
||||
InherentLikelihood float64 `db:"inherent_likelihood"`
|
||||
InherentImpact float64 `db:"inherent_impact"`
|
||||
ResidualLikelihood float64 `db:"residual_likelihood"`
|
||||
ResidualImpact float64 `db:"residual_impact"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
}
|
||||
|
||||
Risks []*Risk
|
||||
@@ -50,6 +52,14 @@ func (r *Risk) CursorKey(orderBy RiskOrderField) page.CursorKey {
|
||||
panic(fmt.Sprintf("unsupported order by: %s", orderBy))
|
||||
}
|
||||
|
||||
func (r *Risk) InherentSeverity() float64 {
|
||||
return r.InherentLikelihood * r.InherentImpact
|
||||
}
|
||||
|
||||
func (r *Risk) ResidualSeverity() float64 {
|
||||
return r.ResidualLikelihood * r.ResidualImpact
|
||||
}
|
||||
|
||||
func (r *Risks) LoadByMitigationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -65,8 +75,10 @@ WITH rsks AS (
|
||||
r.tenant_id,
|
||||
r.name,
|
||||
r.description,
|
||||
r.probability,
|
||||
r.impact,
|
||||
r.inherent_likelihood,
|
||||
r.inherent_impact,
|
||||
r.residual_likelihood,
|
||||
r.residual_impact,
|
||||
r.created_at,
|
||||
r.updated_at
|
||||
FROM
|
||||
@@ -81,8 +93,10 @@ SELECT
|
||||
organization_id,
|
||||
name,
|
||||
description,
|
||||
probability,
|
||||
impact,
|
||||
inherent_likelihood,
|
||||
inherent_impact,
|
||||
residual_likelihood,
|
||||
residual_impact,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
@@ -123,8 +137,10 @@ SELECT
|
||||
organization_id,
|
||||
name,
|
||||
description,
|
||||
probability,
|
||||
impact,
|
||||
inherent_likelihood,
|
||||
inherent_impact,
|
||||
residual_likelihood,
|
||||
residual_impact,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM risks
|
||||
@@ -164,8 +180,10 @@ SELECT
|
||||
organization_id,
|
||||
name,
|
||||
description,
|
||||
probability,
|
||||
impact,
|
||||
inherent_likelihood,
|
||||
inherent_impact,
|
||||
residual_likelihood,
|
||||
residual_impact,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM risks
|
||||
@@ -199,20 +217,22 @@ func (r *Risk) Insert(
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO risks (id, tenant_id, organization_id, name, description, probability, impact, created_at, updated_at)
|
||||
VALUES (@id, @tenant_id, @organization_id, @name, @description, @probability, @impact, @created_at, @updated_at)
|
||||
INSERT INTO risks (id, tenant_id, organization_id, name, description, inherent_likelihood, inherent_impact, residual_likelihood, residual_impact, created_at, updated_at)
|
||||
VALUES (@id, @tenant_id, @organization_id, @name, @description, @inherent_likelihood, @inherent_impact, @residual_likelihood, @residual_impact, @created_at, @updated_at)
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"id": r.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"organization_id": r.OrganizationID,
|
||||
"name": r.Name,
|
||||
"description": r.Description,
|
||||
"probability": r.Probability,
|
||||
"impact": r.Impact,
|
||||
"created_at": r.CreatedAt,
|
||||
"updated_at": r.UpdatedAt,
|
||||
"id": r.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"organization_id": r.OrganizationID,
|
||||
"name": r.Name,
|
||||
"description": r.Description,
|
||||
"inherent_likelihood": r.InherentLikelihood,
|
||||
"inherent_impact": r.InherentImpact,
|
||||
"residual_likelihood": r.ResidualLikelihood,
|
||||
"residual_impact": r.ResidualImpact,
|
||||
"created_at": r.CreatedAt,
|
||||
"updated_at": r.UpdatedAt,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
@@ -229,20 +249,25 @@ UPDATE risks
|
||||
SET
|
||||
name = @name,
|
||||
description = @description,
|
||||
probability = @probability,
|
||||
impact = @impact,
|
||||
inherent_likelihood = @inherent_likelihood,
|
||||
inherent_impact = @inherent_impact,
|
||||
residual_likelihood = @residual_likelihood,
|
||||
residual_impact = @residual_impact,
|
||||
updated_at = @updated_at
|
||||
WHERE %s
|
||||
AND tenant_id = @tenant_id
|
||||
AND id = @risk_id
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"name": r.Name,
|
||||
"description": r.Description,
|
||||
"probability": r.Probability,
|
||||
"impact": r.Impact,
|
||||
"updated_at": r.UpdatedAt,
|
||||
"risk_id": r.ID,
|
||||
"name": r.Name,
|
||||
"description": r.Description,
|
||||
"inherent_likelihood": r.InherentLikelihood,
|
||||
"inherent_impact": r.InherentImpact,
|
||||
"residual_likelihood": r.ResidualLikelihood,
|
||||
"residual_impact": r.ResidualImpact,
|
||||
"updated_at": r.UpdatedAt,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
|
||||
@@ -31,8 +31,6 @@ type (
|
||||
MitigationID gid.GID `db:"mitigation_id"`
|
||||
TenantID gid.TenantID `db:"tenant_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
Probability float64 `db:"probability"`
|
||||
Impact float64 `db:"impact"`
|
||||
}
|
||||
|
||||
RiskMitigations []*RiskMitigation
|
||||
@@ -49,16 +47,12 @@ INSERT INTO
|
||||
risk_id,
|
||||
mitigation_id,
|
||||
tenant_id,
|
||||
probability,
|
||||
impact,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
@risk_id,
|
||||
@mitigation_id,
|
||||
@tenant_id,
|
||||
@probability,
|
||||
@impact,
|
||||
@created_at
|
||||
);
|
||||
`
|
||||
@@ -67,8 +61,6 @@ VALUES (
|
||||
"risk_id": rm.RiskID,
|
||||
"mitigation_id": rm.MitigationID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"probability": rm.Probability,
|
||||
"impact": rm.Impact,
|
||||
"created_at": rm.CreatedAt,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
|
||||
95
pkg/coredata/risk_policy.go
Normal file
95
pkg/coredata/risk_policy.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"time"
|
||||
|
||||
"github.com/getprobo/probo/pkg/gid"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"go.gearno.de/kit/pg"
|
||||
)
|
||||
|
||||
type (
|
||||
RiskPolicy struct {
|
||||
RiskID gid.GID `db:"risk_id"`
|
||||
PolicyID gid.GID `db:"policy_id"`
|
||||
TenantID gid.TenantID `db:"tenant_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
RiskPolicies []*RiskPolicy
|
||||
)
|
||||
|
||||
func (rp RiskPolicy) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO
|
||||
risks_policies (
|
||||
risk_id,
|
||||
policy_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
@risk_id,
|
||||
@policy_id,
|
||||
@tenant_id,
|
||||
@created_at
|
||||
);
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"risk_id": rp.RiskID,
|
||||
"policy_id": rp.PolicyID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"created_at": rp.CreatedAt,
|
||||
}
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
|
||||
func (rp RiskPolicy) Delete(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
DELETE
|
||||
FROM
|
||||
risks_policies
|
||||
WHERE
|
||||
%s
|
||||
AND risk_id = @risk_id
|
||||
AND policy_id = @policy_id;
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"risk_id": rp.RiskID,
|
||||
"policy_id": rp.PolicyID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
return err
|
||||
}
|
||||
@@ -177,7 +177,6 @@ func (s ControlService) DeletePolicyMapping(
|
||||
)
|
||||
}
|
||||
|
||||
// Create creates a new control
|
||||
func (s ControlService) Create(
|
||||
ctx context.Context,
|
||||
req CreateControlRequest,
|
||||
@@ -208,7 +207,6 @@ func (s ControlService) Create(
|
||||
return control, nil
|
||||
}
|
||||
|
||||
// Get retrieves a control by ID
|
||||
func (s ControlService) Get(
|
||||
ctx context.Context,
|
||||
controlID gid.GID,
|
||||
@@ -229,7 +227,6 @@ func (s ControlService) Get(
|
||||
return control, nil
|
||||
}
|
||||
|
||||
// Update updates an existing control
|
||||
func (s ControlService) Update(
|
||||
ctx context.Context,
|
||||
req UpdateControlRequest,
|
||||
@@ -254,7 +251,6 @@ func (s ControlService) Update(
|
||||
return control, nil
|
||||
}
|
||||
|
||||
// Delete removes a control
|
||||
func (s ControlService) Delete(
|
||||
ctx context.Context,
|
||||
controlID gid.GID,
|
||||
@@ -269,7 +265,6 @@ func (s ControlService) Delete(
|
||||
)
|
||||
}
|
||||
|
||||
// ListForFrameworkID retrieves all controls for a framework
|
||||
func (s ControlService) ListForFrameworkID(
|
||||
ctx context.Context,
|
||||
frameworkID gid.GID,
|
||||
@@ -297,41 +292,23 @@ func (s ControlService) ListForFrameworkID(
|
||||
return page.NewPage(controls, cursor), nil
|
||||
}
|
||||
|
||||
func (s ControlService) ConnectToMitigation(
|
||||
func (s ControlService) ListForRiskID(
|
||||
ctx context.Context,
|
||||
req ConnectControlToMitigationRequest,
|
||||
) error {
|
||||
now := time.Now()
|
||||
riskID gid.GID,
|
||||
cursor *page.Cursor[coredata.ControlOrderField],
|
||||
) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) {
|
||||
var controls coredata.Controls
|
||||
|
||||
controlMitigation := &coredata.ControlMitigation{
|
||||
ControlID: req.ControlID,
|
||||
MitigationID: req.MitigationID,
|
||||
TenantID: s.svc.scope.GetTenantID(),
|
||||
CreatedAt: now,
|
||||
}
|
||||
|
||||
return s.svc.pg.WithConn(
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return controlMitigation.Insert(ctx, conn, s.svc.scope)
|
||||
return controls.LoadByRiskID(ctx, conn, s.svc.scope, riskID, cursor)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// DisconnectFromMitigation removes the link between a control and a mitigation
|
||||
func (s ControlService) DisconnectFromMitigation(
|
||||
ctx context.Context,
|
||||
req DisconnectControlFromMitigationRequest,
|
||||
) error {
|
||||
controlMitigation := &coredata.ControlMitigation{
|
||||
ControlID: req.ControlID,
|
||||
MitigationID: req.MitigationID,
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot list controls: %w", err)
|
||||
}
|
||||
|
||||
return s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return controlMitigation.Delete(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
return page.NewPage(controls, cursor), nil
|
||||
}
|
||||
|
||||
@@ -209,3 +209,24 @@ func (s *PolicyService) ListForControlID(
|
||||
|
||||
return page.NewPage(policies, cursor), nil
|
||||
}
|
||||
|
||||
func (s *PolicyService) ListForRiskID(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
cursor *page.Cursor[coredata.PolicyOrderField],
|
||||
) (*page.Page[*coredata.Policy, coredata.PolicyOrderField], error) {
|
||||
var policies coredata.Policies
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return policies.LoadByRiskID(ctx, conn, s.svc.scope, riskID, cursor)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage(policies, cursor), nil
|
||||
}
|
||||
|
||||
@@ -31,19 +31,23 @@ type (
|
||||
}
|
||||
|
||||
CreateRiskRequest struct {
|
||||
OrganizationID gid.GID
|
||||
Name string
|
||||
Description string
|
||||
Probability float64
|
||||
Impact float64
|
||||
OrganizationID gid.GID
|
||||
Name string
|
||||
Description string
|
||||
InherentLikelihood float64
|
||||
InherentImpact float64
|
||||
ResidualLikelihood *float64
|
||||
ResidualImpact *float64
|
||||
}
|
||||
|
||||
UpdateRiskRequest struct {
|
||||
ID gid.GID
|
||||
Name *string
|
||||
Description *string
|
||||
Probability *float64
|
||||
Impact *float64
|
||||
ID gid.GID
|
||||
Name *string
|
||||
Description *string
|
||||
InherentLikelihood *float64
|
||||
InherentImpact *float64
|
||||
ResidualLikelihood *float64
|
||||
ResidualImpact *float64
|
||||
}
|
||||
)
|
||||
|
||||
@@ -68,20 +72,56 @@ func (s RiskService) ListForMitigationID(
|
||||
return page.NewPage(risks, cursor), nil
|
||||
}
|
||||
|
||||
func (s RiskService) CreateMapping(
|
||||
func (s RiskService) CreatePolicyMapping(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
policyID gid.GID,
|
||||
) error {
|
||||
riskPolicy := &coredata.RiskPolicy{
|
||||
RiskID: riskID,
|
||||
PolicyID: policyID,
|
||||
TenantID: s.svc.scope.GetTenantID(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
return s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return riskPolicy.Insert(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s RiskService) DeletePolicyMapping(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
policyID gid.GID,
|
||||
) error {
|
||||
riskPolicy := &coredata.RiskPolicy{
|
||||
RiskID: riskID,
|
||||
PolicyID: policyID,
|
||||
TenantID: s.svc.scope.GetTenantID(),
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
return s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
return riskPolicy.Delete(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func (s RiskService) CreateMitigationMapping(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
mitigationID gid.GID,
|
||||
probability float64,
|
||||
impact float64,
|
||||
) error {
|
||||
riskMitigation := &coredata.RiskMitigation{
|
||||
RiskID: riskID,
|
||||
MitigationID: mitigationID,
|
||||
TenantID: s.svc.scope.GetTenantID(),
|
||||
CreatedAt: time.Now(),
|
||||
Probability: probability,
|
||||
Impact: impact,
|
||||
}
|
||||
|
||||
return s.svc.pg.WithConn(
|
||||
@@ -92,7 +132,7 @@ func (s RiskService) CreateMapping(
|
||||
)
|
||||
}
|
||||
|
||||
func (s RiskService) DeleteMapping(
|
||||
func (s RiskService) DeleteMitigationMapping(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
mitigationID gid.GID,
|
||||
@@ -123,14 +163,24 @@ func (s RiskService) Create(
|
||||
}
|
||||
|
||||
risk := &coredata.Risk{
|
||||
ID: riskID,
|
||||
OrganizationID: req.OrganizationID,
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
Probability: req.Probability,
|
||||
Impact: req.Impact,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ID: riskID,
|
||||
OrganizationID: req.OrganizationID,
|
||||
Name: req.Name,
|
||||
Description: req.Description,
|
||||
InherentLikelihood: req.InherentLikelihood,
|
||||
InherentImpact: req.InherentImpact,
|
||||
ResidualLikelihood: req.InherentLikelihood,
|
||||
ResidualImpact: req.InherentImpact,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
||||
if req.ResidualLikelihood != nil {
|
||||
risk.ResidualLikelihood = *req.ResidualLikelihood
|
||||
}
|
||||
|
||||
if req.ResidualImpact != nil {
|
||||
risk.ResidualImpact = *req.ResidualImpact
|
||||
}
|
||||
|
||||
err = s.svc.pg.WithConn(
|
||||
@@ -188,12 +238,20 @@ func (s RiskService) Update(
|
||||
risk.Description = *req.Description
|
||||
}
|
||||
|
||||
if req.Probability != nil {
|
||||
risk.Probability = *req.Probability
|
||||
if req.InherentLikelihood != nil {
|
||||
risk.InherentLikelihood = *req.InherentLikelihood
|
||||
}
|
||||
|
||||
if req.Impact != nil {
|
||||
risk.Impact = *req.Impact
|
||||
if req.InherentImpact != nil {
|
||||
risk.InherentImpact = *req.InherentImpact
|
||||
}
|
||||
|
||||
if req.ResidualLikelihood != nil {
|
||||
risk.ResidualLikelihood = *req.ResidualLikelihood
|
||||
}
|
||||
|
||||
if req.ResidualImpact != nil {
|
||||
risk.ResidualImpact = *req.ResidualImpact
|
||||
}
|
||||
|
||||
risk.UpdatedAt = time.Now()
|
||||
|
||||
@@ -608,8 +608,12 @@ type Risk implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
probability: Float!
|
||||
impact: Float!
|
||||
inherentLikelihood: Float!
|
||||
inherentImpact: Float!
|
||||
inherentSeverity: Float!
|
||||
residualLikelihood: Float!
|
||||
residualImpact: Float!
|
||||
residualSeverity: Float!
|
||||
|
||||
mitigations(
|
||||
first: Int
|
||||
@@ -619,6 +623,22 @@ type Risk implements Node {
|
||||
orderBy: MitigationOrder
|
||||
): MitigationConnection! @goField(forceResolver: true)
|
||||
|
||||
policies(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: PolicyOrder
|
||||
): PolicyConnection! @goField(forceResolver: true)
|
||||
|
||||
controls(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: ControlOrder
|
||||
): ControlConnection! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -831,8 +851,19 @@ type Mutation {
|
||||
createRisk(input: CreateRiskInput!): CreateRiskPayload!
|
||||
updateRisk(input: UpdateRiskInput!): UpdateRiskPayload!
|
||||
deleteRisk(input: DeleteRiskInput!): DeleteRiskPayload!
|
||||
createRiskMapping(input: CreateRiskMappingInput!): CreateRiskMappingPayload!
|
||||
deleteRiskMapping(input: DeleteRiskMappingInput!): DeleteRiskMappingPayload!
|
||||
createRiskMitigationMapping(
|
||||
input: CreateRiskMitigationMappingInput!
|
||||
): CreateRiskMitigationMappingPayload!
|
||||
deleteRiskMitigationMapping(
|
||||
input: DeleteRiskMitigationMappingInput!
|
||||
): DeleteRiskMitigationMappingPayload!
|
||||
|
||||
createRiskPolicyMapping(
|
||||
input: CreateRiskPolicyMappingInput!
|
||||
): CreateRiskPolicyMappingPayload!
|
||||
deleteRiskPolicyMapping(
|
||||
input: DeleteRiskPolicyMappingInput!
|
||||
): DeleteRiskPolicyMappingPayload!
|
||||
|
||||
# Evidence mutations
|
||||
requestEvidence(input: RequestEvidenceInput!): RequestEvidencePayload!
|
||||
@@ -1033,34 +1064,46 @@ input CreateRiskInput {
|
||||
organizationId: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
probability: Float!
|
||||
impact: Float!
|
||||
inherentLikelihood: Float!
|
||||
inherentImpact: Float!
|
||||
residualLikelihood: Float
|
||||
residualImpact: Float
|
||||
}
|
||||
|
||||
input UpdateRiskInput {
|
||||
id: ID!
|
||||
name: String
|
||||
description: String
|
||||
probability: Float
|
||||
impact: Float
|
||||
inherentLikelihood: Float
|
||||
inherentImpact: Float
|
||||
residualLikelihood: Float
|
||||
residualImpact: Float
|
||||
}
|
||||
|
||||
input DeleteRiskInput {
|
||||
riskId: ID!
|
||||
}
|
||||
|
||||
input CreateRiskMappingInput {
|
||||
input CreateRiskMitigationMappingInput {
|
||||
riskId: ID!
|
||||
mitigationId: ID!
|
||||
probability: Float!
|
||||
impact: Float!
|
||||
}
|
||||
|
||||
input DeleteRiskMappingInput {
|
||||
input DeleteRiskMitigationMappingInput {
|
||||
riskId: ID!
|
||||
mitigationId: ID!
|
||||
}
|
||||
|
||||
input CreateRiskPolicyMappingInput {
|
||||
riskId: ID!
|
||||
policyId: ID!
|
||||
}
|
||||
|
||||
input DeleteRiskPolicyMappingInput {
|
||||
riskId: ID!
|
||||
policyId: ID!
|
||||
}
|
||||
|
||||
input RequestEvidenceInput {
|
||||
taskId: ID!
|
||||
name: String!
|
||||
@@ -1250,11 +1293,19 @@ type DeleteRiskPayload {
|
||||
deletedRiskId: ID!
|
||||
}
|
||||
|
||||
type CreateRiskMappingPayload {
|
||||
type CreateRiskMitigationMappingPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type DeleteRiskMappingPayload {
|
||||
type DeleteRiskMitigationMappingPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type CreateRiskPolicyMappingPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type DeleteRiskPolicyMappingPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,12 +45,16 @@ func NewRiskEdge(r *coredata.Risk, orderBy coredata.RiskOrderField) *RiskEdge {
|
||||
|
||||
func NewRisk(r *coredata.Risk) *Risk {
|
||||
return &Risk{
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
Description: r.Description,
|
||||
Probability: r.Probability,
|
||||
Impact: r.Impact,
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
ID: r.ID,
|
||||
Name: r.Name,
|
||||
Description: r.Description,
|
||||
InherentLikelihood: r.InherentLikelihood,
|
||||
InherentImpact: r.InherentImpact,
|
||||
InherentSeverity: r.InherentSeverity(),
|
||||
ResidualLikelihood: r.ResidualLikelihood,
|
||||
ResidualImpact: r.ResidualImpact,
|
||||
ResidualSeverity: r.ResidualSeverity(),
|
||||
CreatedAt: r.CreatedAt,
|
||||
UpdatedAt: r.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,21 +147,21 @@ type CreatePolicyPayload struct {
|
||||
}
|
||||
|
||||
type CreateRiskInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Probability float64 `json:"probability"`
|
||||
Impact float64 `json:"impact"`
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
InherentLikelihood float64 `json:"inherentLikelihood"`
|
||||
InherentImpact float64 `json:"inherentImpact"`
|
||||
ResidualLikelihood *float64 `json:"residualLikelihood,omitempty"`
|
||||
ResidualImpact *float64 `json:"residualImpact,omitempty"`
|
||||
}
|
||||
|
||||
type CreateRiskMappingInput struct {
|
||||
type CreateRiskMitigationMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
MitigationID gid.GID `json:"mitigationId"`
|
||||
Probability float64 `json:"probability"`
|
||||
Impact float64 `json:"impact"`
|
||||
}
|
||||
|
||||
type CreateRiskMappingPayload struct {
|
||||
type CreateRiskMitigationMappingPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
@@ -169,6 +169,15 @@ type CreateRiskPayload struct {
|
||||
RiskEdge *RiskEdge `json:"riskEdge"`
|
||||
}
|
||||
|
||||
type CreateRiskPolicyMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
PolicyID gid.GID `json:"policyId"`
|
||||
}
|
||||
|
||||
type CreateRiskPolicyMappingPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type CreateTaskInput struct {
|
||||
MitigationID gid.GID `json:"mitigationId"`
|
||||
Name string `json:"name"`
|
||||
@@ -269,12 +278,12 @@ type DeleteRiskInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
}
|
||||
|
||||
type DeleteRiskMappingInput struct {
|
||||
type DeleteRiskMitigationMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
MitigationID gid.GID `json:"mitigationId"`
|
||||
}
|
||||
|
||||
type DeleteRiskMappingPayload struct {
|
||||
type DeleteRiskMitigationMappingPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
@@ -282,6 +291,15 @@ type DeleteRiskPayload struct {
|
||||
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||
}
|
||||
|
||||
type DeleteRiskPolicyMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
PolicyID gid.GID `json:"policyId"`
|
||||
}
|
||||
|
||||
type DeleteRiskPolicyMappingPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type DeleteTaskInput struct {
|
||||
TaskID gid.GID `json:"taskId"`
|
||||
}
|
||||
@@ -536,14 +554,20 @@ type RequestEvidencePayload struct {
|
||||
}
|
||||
|
||||
type Risk struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Probability float64 `json:"probability"`
|
||||
Impact float64 `json:"impact"`
|
||||
Mitigations *MitigationConnection `json:"mitigations"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
InherentLikelihood float64 `json:"inherentLikelihood"`
|
||||
InherentImpact float64 `json:"inherentImpact"`
|
||||
InherentSeverity float64 `json:"inherentSeverity"`
|
||||
ResidualLikelihood float64 `json:"residualLikelihood"`
|
||||
ResidualImpact float64 `json:"residualImpact"`
|
||||
ResidualSeverity float64 `json:"residualSeverity"`
|
||||
Mitigations *MitigationConnection `json:"mitigations"`
|
||||
Policies *PolicyConnection `json:"policies"`
|
||||
Controls *ControlConnection `json:"controls"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (Risk) IsNode() {}
|
||||
@@ -656,11 +680,13 @@ type UpdatePolicyPayload struct {
|
||||
}
|
||||
|
||||
type UpdateRiskInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Probability *float64 `json:"probability,omitempty"`
|
||||
Impact *float64 `json:"impact,omitempty"`
|
||||
ID gid.GID `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
InherentLikelihood *float64 `json:"inherentLikelihood,omitempty"`
|
||||
InherentImpact *float64 `json:"inherentImpact,omitempty"`
|
||||
ResidualLikelihood *float64 `json:"residualLikelihood,omitempty"`
|
||||
ResidualImpact *float64 `json:"residualImpact,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateRiskPayload struct {
|
||||
|
||||
@@ -723,11 +723,13 @@ func (r *mutationResolver) CreateRisk(ctx context.Context, input types.CreateRis
|
||||
risk, err := svc.Risks.Create(
|
||||
ctx,
|
||||
probo.CreateRiskRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
Probability: input.Probability,
|
||||
Impact: input.Impact,
|
||||
OrganizationID: input.OrganizationID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
InherentLikelihood: input.InherentLikelihood,
|
||||
InherentImpact: input.InherentImpact,
|
||||
ResidualLikelihood: input.ResidualLikelihood,
|
||||
ResidualImpact: input.ResidualImpact,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -746,11 +748,13 @@ func (r *mutationResolver) UpdateRisk(ctx context.Context, input types.UpdateRis
|
||||
risk, err := svc.Risks.Update(
|
||||
ctx,
|
||||
probo.UpdateRiskRequest{
|
||||
ID: input.ID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
Probability: input.Probability,
|
||||
Impact: input.Impact,
|
||||
ID: input.ID,
|
||||
Name: input.Name,
|
||||
Description: input.Description,
|
||||
InherentLikelihood: input.InherentLikelihood,
|
||||
InherentImpact: input.InherentImpact,
|
||||
ResidualLikelihood: input.ResidualLikelihood,
|
||||
ResidualImpact: input.ResidualImpact,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -776,30 +780,58 @@ func (r *mutationResolver) DeleteRisk(ctx context.Context, input types.DeleteRis
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateRiskMapping is the resolver for the createRiskMapping field.
|
||||
func (r *mutationResolver) CreateRiskMapping(ctx context.Context, input types.CreateRiskMappingInput) (*types.CreateRiskMappingPayload, error) {
|
||||
// CreateRiskMitigationMapping is the resolver for the createRiskMitigationMapping field.
|
||||
func (r *mutationResolver) CreateRiskMitigationMapping(ctx context.Context, input types.CreateRiskMitigationMappingInput) (*types.CreateRiskMitigationMappingPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||
|
||||
err := svc.Risks.CreateMapping(ctx, input.RiskID, input.MitigationID, input.Probability, input.Impact)
|
||||
err := svc.Risks.CreateMitigationMapping(ctx, input.RiskID, input.MitigationID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create risk mapping: %w", err))
|
||||
panic(fmt.Errorf("cannot create risk mitigation mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateRiskMappingPayload{
|
||||
return &types.CreateRiskMitigationMappingPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteRiskMapping is the resolver for the deleteRiskMapping field.
|
||||
func (r *mutationResolver) DeleteRiskMapping(ctx context.Context, input types.DeleteRiskMappingInput) (*types.DeleteRiskMappingPayload, error) {
|
||||
// DeleteRiskMitigationMapping is the resolver for the deleteRiskMitigationMapping field.
|
||||
func (r *mutationResolver) DeleteRiskMitigationMapping(ctx context.Context, input types.DeleteRiskMitigationMappingInput) (*types.DeleteRiskMitigationMappingPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||
|
||||
err := svc.Risks.DeleteMapping(ctx, input.RiskID, input.MitigationID)
|
||||
err := svc.Risks.DeleteMitigationMapping(ctx, input.RiskID, input.MitigationID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot delete risk mapping: %w", err))
|
||||
panic(fmt.Errorf("cannot delete risk mitigation mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.DeleteRiskMappingPayload{
|
||||
return &types.DeleteRiskMitigationMappingPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateRiskPolicyMapping is the resolver for the createRiskPolicyMapping field.
|
||||
func (r *mutationResolver) CreateRiskPolicyMapping(ctx context.Context, input types.CreateRiskPolicyMappingInput) (*types.CreateRiskPolicyMappingPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||
|
||||
err := svc.Risks.CreatePolicyMapping(ctx, input.RiskID, input.PolicyID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create risk policy mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateRiskPolicyMappingPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteRiskPolicyMapping is the resolver for the deleteRiskPolicyMapping field.
|
||||
func (r *mutationResolver) DeleteRiskPolicyMapping(ctx context.Context, input types.DeleteRiskPolicyMappingInput) (*types.DeleteRiskPolicyMappingPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||
|
||||
err := svc.Risks.DeletePolicyMapping(ctx, input.RiskID, input.PolicyID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot delete risk policy mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.DeleteRiskPolicyMappingPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -1336,6 +1368,56 @@ func (r *riskResolver) Mitigations(ctx context.Context, obj *types.Risk, first *
|
||||
return types.NewMitigationConnection(page), nil
|
||||
}
|
||||
|
||||
// Policies is the resolver for the policies field.
|
||||
func (r *riskResolver) Policies(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.PolicyOrderBy) (*types.PolicyConnection, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.PolicyOrderField]{
|
||||
Field: coredata.PolicyOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.PolicyOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := svc.Policies.ListForRiskID(ctx, obj.ID, cursor)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list risk policies: %w", err))
|
||||
}
|
||||
|
||||
return types.NewPolicyConnection(page), nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.ControlOrderField]{
|
||||
Field: coredata.ControlOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.ControlOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
page, err := svc.Controls.ListForRiskID(ctx, obj.ID, cursor)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list risk controls: %w", err))
|
||||
}
|
||||
|
||||
return types.NewControlConnection(page), nil
|
||||
}
|
||||
|
||||
// AssignedTo is the resolver for the assignedTo field.
|
||||
func (r *taskResolver) AssignedTo(ctx context.Context, obj *types.Task) (*types.People, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, obj.ID.TenantID())
|
||||
@@ -1509,3 +1591,36 @@ type taskResolver struct{ *Resolver }
|
||||
type vendorResolver struct{ *Resolver }
|
||||
type vendorComplianceReportResolver struct{ *Resolver }
|
||||
type viewerResolver struct{ *Resolver }
|
||||
|
||||
// !!! WARNING !!!
|
||||
// The code below was going to be deleted when updating resolvers. It has been copied here so you have
|
||||
// one last chance to move it out of harms way if you want. There are two reasons this happens:
|
||||
// - When renaming or deleting a resolver the old code will be put in here. You can safely delete
|
||||
// it when you're done.
|
||||
// - You have helper methods in this file. Move them out to keep these resolver files clean.
|
||||
/*
|
||||
func (r *mutationResolver) CreateRiskControlMapping(ctx context.Context, input types.CreateRiskControlMappingInput) (*types.CreateRiskControlMappingPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||
|
||||
err := svc.Risks.CreateControlMapping(ctx, input.RiskID, input.ControlID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create risk control mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateRiskControlMappingPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
func (r *mutationResolver) DeleteRiskControlMapping(ctx context.Context, input types.DeleteRiskControlMappingInput) (*types.DeleteRiskControlMappingPayload, error) {
|
||||
svc := r.GetTenantServiceIfAuthorized(ctx, input.RiskID.TenantID())
|
||||
|
||||
err := svc.Risks.DeleteControlMapping(ctx, input.RiskID, input.ControlID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot delete risk control mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.DeleteRiskControlMappingPayload{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user