Update obligations
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
26
pkg/coredata/migrations/20250923T120205Z.sql
Normal file
26
pkg/coredata/migrations/20250923T120205Z.sql
Normal file
@@ -0,0 +1,26 @@
|
||||
ALTER TYPE obligations_status RENAME VALUE 'OPEN' TO 'NON_COMPLIANT';
|
||||
ALTER TYPE obligations_status RENAME VALUE 'IN_PROGRESS' TO 'PARTIALLY_COMPLIANT';
|
||||
ALTER TYPE obligations_status RENAME VALUE 'CLOSED' TO 'COMPLIANT';
|
||||
|
||||
ALTER TABLE obligations DROP COLUMN reference_id;
|
||||
|
||||
CREATE TABLE risks_obligations (
|
||||
risk_id TEXT NOT NULL REFERENCES risks(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
obligation_id TEXT NOT NULL REFERENCES obligations(id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
tenant_id TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
PRIMARY KEY (risk_id, obligation_id)
|
||||
);
|
||||
|
||||
ALTER TABLE obligations ADD COLUMN search_vector tsvector
|
||||
GENERATED ALWAYS AS (
|
||||
to_tsvector('simple',
|
||||
COALESCE(requirement, '') || ' ' ||
|
||||
COALESCE(area, '') || ' ' ||
|
||||
COALESCE(source, '') || ' ' ||
|
||||
COALESCE(regulator, '') || ' ' ||
|
||||
COALESCE(actions_to_be_implemented, '')
|
||||
)
|
||||
) STORED;
|
||||
|
||||
CREATE INDEX obligations_search_idx ON obligations USING gin(search_vector);
|
||||
@@ -30,7 +30,6 @@ type (
|
||||
Obligation struct {
|
||||
ID gid.GID `db:"id"`
|
||||
OrganizationID gid.GID `db:"organization_id"`
|
||||
ReferenceID string `db:"reference_id"`
|
||||
Area *string `db:"area"`
|
||||
Source *string `db:"source"`
|
||||
Requirement *string `db:"requirement"`
|
||||
@@ -59,8 +58,6 @@ func (o *Obligation) CursorKey(field ObligationOrderField) page.CursorKey {
|
||||
return page.NewCursorKey(o.ID, o.DueDate)
|
||||
case ObligationOrderFieldStatus:
|
||||
return page.NewCursorKey(o.ID, o.Status)
|
||||
case ObligationOrderFieldReferenceId:
|
||||
return page.NewCursorKey(o.ID, o.ReferenceID)
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported order by: %s", field))
|
||||
@@ -78,7 +75,6 @@ SELECT
|
||||
organization_id,
|
||||
snapshot_id,
|
||||
source_id,
|
||||
reference_id,
|
||||
area,
|
||||
source,
|
||||
requirement,
|
||||
@@ -153,6 +149,52 @@ WHERE
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (os *Obligations) CountByRiskID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
riskID gid.GID,
|
||||
filter *ObligationFilter,
|
||||
) (int, error) {
|
||||
q := `
|
||||
WITH obls AS (
|
||||
SELECT
|
||||
o.id,
|
||||
o.tenant_id,
|
||||
o.snapshot_id,
|
||||
o.search_vector
|
||||
FROM
|
||||
obligations o
|
||||
INNER JOIN
|
||||
risks_obligations ro ON o.id = ro.obligation_id
|
||||
WHERE
|
||||
ro.risk_id = @risk_id
|
||||
)
|
||||
SELECT
|
||||
COUNT(id)
|
||||
FROM
|
||||
obls
|
||||
WHERE %s
|
||||
AND %s
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"risk_id": riskID}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
maps.Copy(args, filter.SQLArguments())
|
||||
|
||||
row := conn.QueryRow(ctx, q, args)
|
||||
|
||||
var count int
|
||||
err := row.Scan(&count)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot count obligations: %w", err)
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (os *Obligations) LoadByOrganizationID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -165,7 +207,6 @@ func (os *Obligations) LoadByOrganizationID(
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
reference_id,
|
||||
area,
|
||||
source,
|
||||
requirement,
|
||||
@@ -210,6 +251,86 @@ WHERE
|
||||
return nil
|
||||
}
|
||||
|
||||
func (os *Obligations) LoadByRiskID(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
riskID gid.GID,
|
||||
cursor *page.Cursor[ObligationOrderField],
|
||||
filter *ObligationFilter,
|
||||
) error {
|
||||
q := `
|
||||
WITH obls AS (
|
||||
SELECT
|
||||
o.id,
|
||||
o.organization_id,
|
||||
o.area,
|
||||
o.source,
|
||||
o.requirement,
|
||||
o.actions_to_be_implemented,
|
||||
o.regulator,
|
||||
o.owner_id,
|
||||
o.last_review_date,
|
||||
o.due_date,
|
||||
o.status,
|
||||
o.snapshot_id,
|
||||
o.source_id,
|
||||
o.created_at,
|
||||
o.updated_at,
|
||||
o.tenant_id,
|
||||
o.search_vector
|
||||
FROM
|
||||
obligations o
|
||||
INNER JOIN
|
||||
risks_obligations ro ON o.id = ro.obligation_id
|
||||
WHERE
|
||||
ro.risk_id = @risk_id
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
organization_id,
|
||||
area,
|
||||
source,
|
||||
requirement,
|
||||
actions_to_be_implemented,
|
||||
regulator,
|
||||
owner_id,
|
||||
last_review_date,
|
||||
due_date,
|
||||
status,
|
||||
snapshot_id,
|
||||
source_id,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
obls
|
||||
WHERE %s
|
||||
AND %s
|
||||
AND %s
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), filter.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{"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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query obligations: %w", err)
|
||||
}
|
||||
|
||||
obligations, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Obligation])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect obligations: %w", err)
|
||||
}
|
||||
|
||||
*os = obligations
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Obligation) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
@@ -220,7 +341,6 @@ INSERT INTO obligations (
|
||||
id,
|
||||
tenant_id,
|
||||
organization_id,
|
||||
reference_id,
|
||||
area,
|
||||
source,
|
||||
requirement,
|
||||
@@ -238,7 +358,6 @@ INSERT INTO obligations (
|
||||
@id,
|
||||
@tenant_id,
|
||||
@organization_id,
|
||||
@reference_id,
|
||||
@area,
|
||||
@source,
|
||||
@requirement,
|
||||
@@ -259,7 +378,6 @@ INSERT INTO obligations (
|
||||
"id": o.ID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"organization_id": o.OrganizationID,
|
||||
"reference_id": o.ReferenceID,
|
||||
"area": o.Area,
|
||||
"source": o.Source,
|
||||
"requirement": o.Requirement,
|
||||
@@ -290,7 +408,6 @@ func (o *Obligation) Update(
|
||||
) error {
|
||||
q := `
|
||||
UPDATE obligations SET
|
||||
reference_id = @reference_id,
|
||||
area = @area,
|
||||
source = @source,
|
||||
requirement = @requirement,
|
||||
@@ -311,7 +428,6 @@ WHERE
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"id": o.ID,
|
||||
"reference_id": o.ReferenceID,
|
||||
"area": o.Area,
|
||||
"source": o.Source,
|
||||
"requirement": o.Requirement,
|
||||
@@ -367,7 +483,6 @@ INSERT INTO obligations (
|
||||
snapshot_id,
|
||||
source_id,
|
||||
organization_id,
|
||||
reference_id,
|
||||
area,
|
||||
source,
|
||||
requirement,
|
||||
@@ -386,7 +501,6 @@ SELECT
|
||||
@snapshot_id,
|
||||
o.id,
|
||||
o.organization_id,
|
||||
o.reference_id,
|
||||
o.area,
|
||||
o.source,
|
||||
o.requirement,
|
||||
|
||||
@@ -25,7 +25,6 @@ const (
|
||||
ObligationOrderFieldLastReviewDate ObligationOrderField = "LAST_REVIEW_DATE"
|
||||
ObligationOrderFieldDueDate ObligationOrderField = "DUE_DATE"
|
||||
ObligationOrderFieldStatus ObligationOrderField = "STATUS"
|
||||
ObligationOrderFieldReferenceId ObligationOrderField = "REFERENCE_ID"
|
||||
)
|
||||
|
||||
func (p ObligationOrderField) Column() string {
|
||||
@@ -46,8 +45,7 @@ func (p *ObligationOrderField) UnmarshalText(text []byte) error {
|
||||
case string(ObligationOrderFieldCreatedAt),
|
||||
string(ObligationOrderFieldLastReviewDate),
|
||||
string(ObligationOrderFieldDueDate),
|
||||
string(ObligationOrderFieldStatus),
|
||||
string(ObligationOrderFieldReferenceId):
|
||||
string(ObligationOrderFieldStatus):
|
||||
*p = ObligationOrderField(val)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import (
|
||||
type ObligationStatus string
|
||||
|
||||
const (
|
||||
ObligationStatusOpen ObligationStatus = "OPEN"
|
||||
ObligationStatusInProgress ObligationStatus = "IN_PROGRESS"
|
||||
ObligationStatusClosed ObligationStatus = "CLOSED"
|
||||
ObligationStatusNonCompliant ObligationStatus = "NON_COMPLIANT"
|
||||
ObligationStatusPartiallyCompliant ObligationStatus = "PARTIALLY_COMPLIANT"
|
||||
ObligationStatusCompliant ObligationStatus = "COMPLIANT"
|
||||
)
|
||||
|
||||
func (os ObligationStatus) String() string {
|
||||
@@ -43,12 +43,12 @@ func (os *ObligationStatus) Scan(value any) error {
|
||||
}
|
||||
|
||||
switch s {
|
||||
case "OPEN":
|
||||
*os = ObligationStatusOpen
|
||||
case "IN_PROGRESS":
|
||||
*os = ObligationStatusInProgress
|
||||
case "CLOSED":
|
||||
*os = ObligationStatusClosed
|
||||
case "NON_COMPLIANT":
|
||||
*os = ObligationStatusNonCompliant
|
||||
case "PARTIALLY_COMPLIANT":
|
||||
*os = ObligationStatusPartiallyCompliant
|
||||
case "COMPLIANT":
|
||||
*os = ObligationStatusCompliant
|
||||
default:
|
||||
return fmt.Errorf("invalid ObligationStatus value: %q", s)
|
||||
}
|
||||
|
||||
99
pkg/coredata/risk_obligation.go
Normal file
99
pkg/coredata/risk_obligation.go
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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 (
|
||||
RiskObligation struct {
|
||||
RiskID gid.GID `db:"risk_id"`
|
||||
ObligationID gid.GID `db:"obligation_id"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
}
|
||||
|
||||
RiskObligations []*RiskObligation
|
||||
)
|
||||
|
||||
func (ro RiskObligation) Insert(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
INSERT INTO risks_obligations (
|
||||
risk_id,
|
||||
obligation_id,
|
||||
tenant_id,
|
||||
created_at
|
||||
) VALUES (
|
||||
@risk_id,
|
||||
@obligation_id,
|
||||
@tenant_id,
|
||||
@created_at
|
||||
)
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"risk_id": ro.RiskID,
|
||||
"obligation_id": ro.ObligationID,
|
||||
"tenant_id": scope.GetTenantID(),
|
||||
"created_at": ro.CreatedAt,
|
||||
}
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot insert risk obligation: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ro RiskObligation) Delete(
|
||||
ctx context.Context,
|
||||
conn pg.Conn,
|
||||
scope Scoper,
|
||||
) error {
|
||||
q := `
|
||||
DELETE FROM risks_obligations
|
||||
WHERE
|
||||
%s
|
||||
AND risk_id = @risk_id
|
||||
AND obligation_id = @obligation_id
|
||||
`
|
||||
|
||||
q = fmt.Sprintf(q, scope.SQLFragment())
|
||||
|
||||
args := pgx.StrictNamedArgs{
|
||||
"risk_id": ro.RiskID,
|
||||
"obligation_id": ro.ObligationID,
|
||||
}
|
||||
maps.Copy(args, scope.SQLArguments())
|
||||
|
||||
_, err := conn.Exec(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot delete risk obligation: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -32,7 +32,6 @@ type ObligationService struct {
|
||||
type (
|
||||
CreateObligationRequest struct {
|
||||
OrganizationID gid.GID
|
||||
ReferenceID string
|
||||
Area *string
|
||||
Source *string
|
||||
Requirement *string
|
||||
@@ -46,7 +45,6 @@ type (
|
||||
|
||||
UpdateObligationRequest struct {
|
||||
ID gid.GID
|
||||
ReferenceID *string
|
||||
Area **string
|
||||
Source **string
|
||||
Requirement **string
|
||||
@@ -92,7 +90,6 @@ func (s *ObligationService) Create(
|
||||
obligation := &coredata.Obligation{
|
||||
ID: gid.New(s.svc.scope.GetTenantID(), coredata.ObligationEntityType),
|
||||
OrganizationID: req.OrganizationID,
|
||||
ReferenceID: req.ReferenceID,
|
||||
Area: req.Area,
|
||||
Source: req.Source,
|
||||
Requirement: req.Requirement,
|
||||
@@ -147,10 +144,6 @@ func (s *ObligationService) Update(
|
||||
return fmt.Errorf("cannot load obligation: %w", err)
|
||||
}
|
||||
|
||||
if req.ReferenceID != nil {
|
||||
obligation.ReferenceID = *req.ReferenceID
|
||||
}
|
||||
|
||||
if req.Area != nil {
|
||||
obligation.Area = *req.Area
|
||||
}
|
||||
@@ -284,3 +277,57 @@ func (s ObligationService) ListForOrganizationID(
|
||||
|
||||
return page.NewPage(obligations, cursor), nil
|
||||
}
|
||||
|
||||
func (s ObligationService) CountForRiskID(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
filter *coredata.ObligationFilter,
|
||||
) (int, error) {
|
||||
var count int
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) (err error) {
|
||||
obligations := &coredata.Obligations{}
|
||||
count, err = obligations.CountByRiskID(ctx, conn, s.svc.scope, riskID, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot count obligations: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s ObligationService) ListForRiskID(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
cursor *page.Cursor[coredata.ObligationOrderField],
|
||||
filter *coredata.ObligationFilter,
|
||||
) (*page.Page[*coredata.Obligation, coredata.ObligationOrderField], error) {
|
||||
var obligations coredata.Obligations
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
err := obligations.LoadByRiskID(ctx, conn, s.svc.scope, riskID, cursor, filter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot load obligations: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return page.NewPage(obligations, cursor), nil
|
||||
}
|
||||
|
||||
@@ -303,6 +303,76 @@ func (s RiskService) DeleteMeasureMapping(
|
||||
return risk, measure, nil
|
||||
}
|
||||
|
||||
func (s RiskService) CreateObligationMapping(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
obligationID gid.GID,
|
||||
) (*coredata.Risk, *coredata.Obligation, error) {
|
||||
risk := &coredata.Risk{}
|
||||
obligation := &coredata.Obligation{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := risk.LoadByID(ctx, conn, s.svc.scope, riskID); err != nil {
|
||||
return fmt.Errorf("cannot load risk: %w", err)
|
||||
}
|
||||
|
||||
if err := obligation.LoadByID(ctx, conn, s.svc.scope, obligationID); err != nil {
|
||||
return fmt.Errorf("cannot load obligation: %w", err)
|
||||
}
|
||||
|
||||
riskObligation := &coredata.RiskObligation{
|
||||
RiskID: risk.ID,
|
||||
ObligationID: obligation.ID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
return riskObligation.Insert(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot create risk obligation mapping: %w", err)
|
||||
}
|
||||
|
||||
return risk, obligation, nil
|
||||
}
|
||||
|
||||
func (s RiskService) DeleteObligationMapping(
|
||||
ctx context.Context,
|
||||
riskID gid.GID,
|
||||
obligationID gid.GID,
|
||||
) (*coredata.Risk, *coredata.Obligation, error) {
|
||||
riskObligation := &coredata.RiskObligation{}
|
||||
risk := &coredata.Risk{}
|
||||
obligation := &coredata.Obligation{}
|
||||
|
||||
err := s.svc.pg.WithConn(
|
||||
ctx,
|
||||
func(conn pg.Conn) error {
|
||||
if err := risk.LoadByID(ctx, conn, s.svc.scope, riskID); err != nil {
|
||||
return fmt.Errorf("cannot load risk: %w", err)
|
||||
}
|
||||
|
||||
if err := obligation.LoadByID(ctx, conn, s.svc.scope, obligationID); err != nil {
|
||||
return fmt.Errorf("cannot load obligation: %w", err)
|
||||
}
|
||||
|
||||
riskObligation.RiskID = risk.ID
|
||||
riskObligation.ObligationID = obligation.ID
|
||||
|
||||
return riskObligation.Delete(ctx, conn, s.svc.scope)
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("cannot delete risk obligation mapping: %w", err)
|
||||
}
|
||||
|
||||
return risk, obligation, nil
|
||||
}
|
||||
|
||||
func (s RiskService) Create(
|
||||
ctx context.Context,
|
||||
req CreateRiskRequest,
|
||||
|
||||
@@ -169,17 +169,17 @@ enum NonconformityStatus
|
||||
|
||||
enum ObligationStatus
|
||||
@goModel(model: "github.com/getprobo/probo/pkg/coredata.ObligationStatus") {
|
||||
OPEN
|
||||
NON_COMPLIANT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusOpen"
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusNonCompliant"
|
||||
)
|
||||
IN_PROGRESS
|
||||
PARTIALLY_COMPLIANT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusInProgress"
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusPartiallyCompliant"
|
||||
)
|
||||
CLOSED
|
||||
COMPLIANT
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusClosed"
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationStatusCompliant"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1044,10 +1044,6 @@ enum ObligationOrderField
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldCreatedAt"
|
||||
)
|
||||
REFERENCE_ID
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldReferenceId"
|
||||
)
|
||||
LAST_REVIEW_DATE
|
||||
@goEnum(
|
||||
value: "github.com/getprobo/probo/pkg/coredata.ObligationOrderFieldLastReviewDate"
|
||||
@@ -2004,6 +2000,15 @@ type Risk implements Node {
|
||||
filter: ControlFilter
|
||||
): ControlConnection! @goField(forceResolver: true)
|
||||
|
||||
obligations(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: ObligationOrder
|
||||
filter: ObligationFilter
|
||||
): ObligationConnection! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -2056,7 +2061,6 @@ type Obligation implements Node {
|
||||
snapshotId: ID
|
||||
sourceId: ID
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
referenceId: String!
|
||||
area: String
|
||||
source: String
|
||||
requirement: String
|
||||
@@ -2669,6 +2673,13 @@ type Mutation {
|
||||
input: DeleteRiskDocumentMappingInput!
|
||||
): DeleteRiskDocumentMappingPayload!
|
||||
|
||||
createRiskObligationMapping(
|
||||
input: CreateRiskObligationMappingInput!
|
||||
): CreateRiskObligationMappingPayload!
|
||||
deleteRiskObligationMapping(
|
||||
input: DeleteRiskObligationMappingInput!
|
||||
): DeleteRiskObligationMappingPayload!
|
||||
|
||||
# Evidence mutations
|
||||
requestEvidence(input: RequestEvidenceInput!): RequestEvidencePayload!
|
||||
fulfillEvidence(input: FulfillEvidenceInput!): FulfillEvidencePayload!
|
||||
@@ -3187,6 +3198,16 @@ input DeleteRiskDocumentMappingInput {
|
||||
documentId: ID!
|
||||
}
|
||||
|
||||
input CreateRiskObligationMappingInput {
|
||||
riskId: ID!
|
||||
obligationId: ID!
|
||||
}
|
||||
|
||||
input DeleteRiskObligationMappingInput {
|
||||
riskId: ID!
|
||||
obligationId: ID!
|
||||
}
|
||||
|
||||
input RequestEvidenceInput {
|
||||
taskId: ID!
|
||||
name: String!
|
||||
@@ -3392,7 +3413,6 @@ input DeleteNonconformityInput {
|
||||
|
||||
input CreateObligationInput {
|
||||
organizationId: ID!
|
||||
referenceId: String!
|
||||
area: String
|
||||
source: String
|
||||
requirement: String
|
||||
@@ -3406,7 +3426,6 @@ input CreateObligationInput {
|
||||
|
||||
input UpdateObligationInput {
|
||||
id: ID!
|
||||
referenceId: String
|
||||
area: String
|
||||
source: String
|
||||
requirement: String
|
||||
@@ -3734,6 +3753,16 @@ type DeleteRiskDocumentMappingPayload {
|
||||
deletedDocumentId: ID!
|
||||
}
|
||||
|
||||
type CreateRiskObligationMappingPayload {
|
||||
riskEdge: RiskEdge!
|
||||
obligationEdge: ObligationEdge!
|
||||
}
|
||||
|
||||
type DeleteRiskObligationMappingPayload {
|
||||
deletedRiskId: ID!
|
||||
deletedObligationId: ID!
|
||||
}
|
||||
|
||||
type RequestEvidencePayload {
|
||||
evidenceEdge: EvidenceEdge!
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -60,7 +60,6 @@ func NewObligation(cr *coredata.Obligation) *Obligation {
|
||||
ID: cr.ID,
|
||||
SnapshotID: cr.SnapshotID,
|
||||
SourceID: cr.SourceID,
|
||||
ReferenceID: cr.ReferenceID,
|
||||
Area: cr.Area,
|
||||
Source: cr.Source,
|
||||
Requirement: cr.Requirement,
|
||||
|
||||
@@ -401,7 +401,6 @@ type CreateNonconformityPayload struct {
|
||||
|
||||
type CreateObligationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
ReferenceID string `json:"referenceId"`
|
||||
Area *string `json:"area,omitempty"`
|
||||
Source *string `json:"source,omitempty"`
|
||||
Requirement *string `json:"requirement,omitempty"`
|
||||
@@ -497,6 +496,16 @@ type CreateRiskMeasureMappingPayload struct {
|
||||
MeasureEdge *MeasureEdge `json:"measureEdge"`
|
||||
}
|
||||
|
||||
type CreateRiskObligationMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
ObligationID gid.GID `json:"obligationId"`
|
||||
}
|
||||
|
||||
type CreateRiskObligationMappingPayload struct {
|
||||
RiskEdge *RiskEdge `json:"riskEdge"`
|
||||
ObligationEdge *ObligationEdge `json:"obligationEdge"`
|
||||
}
|
||||
|
||||
type CreateRiskPayload struct {
|
||||
RiskEdge *RiskEdge `json:"riskEdge"`
|
||||
}
|
||||
@@ -828,6 +837,16 @@ type DeleteRiskMeasureMappingPayload struct {
|
||||
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||
}
|
||||
|
||||
type DeleteRiskObligationMappingInput struct {
|
||||
RiskID gid.GID `json:"riskId"`
|
||||
ObligationID gid.GID `json:"obligationId"`
|
||||
}
|
||||
|
||||
type DeleteRiskObligationMappingPayload struct {
|
||||
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||
DeletedObligationID gid.GID `json:"deletedObligationId"`
|
||||
}
|
||||
|
||||
type DeleteRiskPayload struct {
|
||||
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||
}
|
||||
@@ -1187,7 +1206,6 @@ type Obligation struct {
|
||||
SnapshotID *gid.GID `json:"snapshotId,omitempty"`
|
||||
SourceID *gid.GID `json:"sourceId,omitempty"`
|
||||
Organization *Organization `json:"organization"`
|
||||
ReferenceID string `json:"referenceId"`
|
||||
Area *string `json:"area,omitempty"`
|
||||
Source *string `json:"source,omitempty"`
|
||||
Requirement *string `json:"requirement,omitempty"`
|
||||
@@ -1409,6 +1427,7 @@ type Risk struct {
|
||||
Measures *MeasureConnection `json:"measures"`
|
||||
Documents *DocumentConnection `json:"documents"`
|
||||
Controls *ControlConnection `json:"controls"`
|
||||
Obligations *ObligationConnection `json:"obligations"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -1687,7 +1706,6 @@ type UpdateNonconformityPayload struct {
|
||||
|
||||
type UpdateObligationInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
ReferenceID *string `json:"referenceId,omitempty"`
|
||||
Area *string `json:"area,omitempty"`
|
||||
Source *string `json:"source,omitempty"`
|
||||
Requirement *string `json:"requirement,omitempty"`
|
||||
|
||||
@@ -2209,6 +2209,36 @@ func (r *mutationResolver) DeleteRiskDocumentMapping(ctx context.Context, input
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateRiskObligationMapping is the resolver for the createRiskObligationMapping field.
|
||||
func (r *mutationResolver) CreateRiskObligationMapping(ctx context.Context, input types.CreateRiskObligationMappingInput) (*types.CreateRiskObligationMappingPayload, error) {
|
||||
prb := r.ProboService(ctx, input.RiskID.TenantID())
|
||||
|
||||
risk, obligation, err := prb.Risks.CreateObligationMapping(ctx, input.RiskID, input.ObligationID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot create risk obligation mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.CreateRiskObligationMappingPayload{
|
||||
RiskEdge: types.NewRiskEdge(risk, coredata.RiskOrderFieldCreatedAt),
|
||||
ObligationEdge: types.NewObligationEdge(obligation, coredata.ObligationOrderFieldCreatedAt),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeleteRiskObligationMapping is the resolver for the deleteRiskObligationMapping field.
|
||||
func (r *mutationResolver) DeleteRiskObligationMapping(ctx context.Context, input types.DeleteRiskObligationMappingInput) (*types.DeleteRiskObligationMappingPayload, error) {
|
||||
prb := r.ProboService(ctx, input.RiskID.TenantID())
|
||||
|
||||
risk, obligation, err := prb.Risks.DeleteObligationMapping(ctx, input.RiskID, input.ObligationID)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot delete risk obligation mapping: %w", err))
|
||||
}
|
||||
|
||||
return &types.DeleteRiskObligationMappingPayload{
|
||||
DeletedRiskID: risk.ID,
|
||||
DeletedObligationID: obligation.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RequestEvidence is the resolver for the requestEvidence field.
|
||||
func (r *mutationResolver) RequestEvidence(ctx context.Context, input types.RequestEvidenceInput) (*types.RequestEvidencePayload, error) {
|
||||
prb := r.ProboService(ctx, input.TaskID.TenantID())
|
||||
@@ -3118,7 +3148,6 @@ func (r *mutationResolver) CreateObligation(ctx context.Context, input types.Cre
|
||||
|
||||
req := probo.CreateObligationRequest{
|
||||
OrganizationID: input.OrganizationID,
|
||||
ReferenceID: input.ReferenceID,
|
||||
Area: input.Area,
|
||||
Source: input.Source,
|
||||
Requirement: input.Requirement,
|
||||
@@ -3146,7 +3175,6 @@ func (r *mutationResolver) UpdateObligation(ctx context.Context, input types.Upd
|
||||
|
||||
req := probo.UpdateObligationRequest{
|
||||
ID: input.ID,
|
||||
ReferenceID: input.ReferenceID,
|
||||
Area: &input.Area,
|
||||
Source: &input.Source,
|
||||
Requirement: &input.Requirement,
|
||||
@@ -3478,6 +3506,17 @@ func (r *obligationConnectionResolver) TotalCount(ctx context.Context, obj *type
|
||||
panic(fmt.Errorf("cannot count obligations: %w", err))
|
||||
}
|
||||
return count, nil
|
||||
case *riskResolver:
|
||||
obligationFilter := coredata.NewObligationFilter(nil)
|
||||
if obj.Filter != nil {
|
||||
obligationFilter = coredata.NewObligationFilter(&obj.Filter.SnapshotID)
|
||||
}
|
||||
|
||||
count, err := prb.Obligations.CountForRiskID(ctx, obj.ParentID, obligationFilter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot count risk obligations: %w", err))
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unsupported resolver: %T", obj.Resolver)
|
||||
@@ -4388,6 +4427,36 @@ func (r *riskResolver) Controls(ctx context.Context, obj *types.Risk, first *int
|
||||
return types.NewControlConnection(page, r, obj.ID, filters), nil
|
||||
}
|
||||
|
||||
// Obligations is the resolver for the obligations field.
|
||||
func (r *riskResolver) Obligations(ctx context.Context, obj *types.Risk, first *int, after *page.CursorKey, last *int, before *page.CursorKey, orderBy *types.ObligationOrderBy, filter *types.ObligationFilter) (*types.ObligationConnection, error) {
|
||||
prb := r.ProboService(ctx, obj.ID.TenantID())
|
||||
|
||||
pageOrderBy := page.OrderBy[coredata.ObligationOrderField]{
|
||||
Field: coredata.ObligationOrderFieldCreatedAt,
|
||||
Direction: page.OrderDirectionDesc,
|
||||
}
|
||||
if orderBy != nil {
|
||||
pageOrderBy = page.OrderBy[coredata.ObligationOrderField]{
|
||||
Field: orderBy.Field,
|
||||
Direction: orderBy.Direction,
|
||||
}
|
||||
}
|
||||
|
||||
cursor := types.NewCursor(first, after, last, before, pageOrderBy)
|
||||
|
||||
var obligationFilter = coredata.NewObligationFilter(nil)
|
||||
if filter != nil {
|
||||
obligationFilter = coredata.NewObligationFilter(&filter.SnapshotID)
|
||||
}
|
||||
|
||||
page, err := prb.Obligations.ListForRiskID(ctx, obj.ID, cursor, obligationFilter)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("cannot list risk obligations: %w", err))
|
||||
}
|
||||
|
||||
return types.NewObligationConnection(page, r, obj.ID, filter), nil
|
||||
}
|
||||
|
||||
// TotalCount is the resolver for the totalCount field.
|
||||
func (r *riskConnectionResolver) TotalCount(ctx context.Context, obj *types.RiskConnection) (int, error) {
|
||||
prb := r.ProboService(ctx, obj.ParentID.TenantID())
|
||||
|
||||
Reference in New Issue
Block a user