diff --git a/.golangci.yml b/.golangci.yml index 02f576b91..60937a32b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -18,3 +18,6 @@ linters: default: default enable: - after-block + exclusions: + paths: + - node_modules diff --git a/GNUmakefile b/GNUmakefile index 1931efb1f..a0aedfe63 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -333,6 +333,15 @@ genmodels: ## Refresh LLM model registry from OpenRouter help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +.PHONY: fix +fix: fix-go ## Auto-fix Go code + +.PHONY: fix-go +fix-go: generate embed ## Auto-fix Go code (format, go fix, lint --fix) + gofmt -w apps cmd packages pkg e2e + $(GO_BASE) fix -omitzero=false ./apps/... ./cmd/... ./packages/... ./pkg/... ./e2e/... + $(GOLINTCMD) run --fix ./... + .PHONY: fmt fmt: fmt-go ## Format Go code diff --git a/e2e/console/risk_assessment_test.go b/e2e/console/risk_assessment_test.go index b0f9ae5a6..689068107 100644 --- a/e2e/console/risk_assessment_test.go +++ b/e2e/console/risk_assessment_test.go @@ -40,6 +40,7 @@ func TestRiskAssessment_Create(t *testing.T) { } `json:"riskAssessmentEdge"` } `json:"createRiskAssessment"` } + err := owner.Execute(` mutation($input: CreateRiskAssessmentInput!) { createRiskAssessment(input: $input) { @@ -81,6 +82,7 @@ func TestRiskAssessment_Delete(t *testing.T) { ID string `json:"id"` } `json:"node"` } + err = owner.Execute(`query($id: ID!) { node(id: $id) { ... on RiskAssessmentScope { id } } }`, map[string]any{"id": scopeID}, &result) testutil.AssertNodeNotAccessible(t, err, result.Node == nil, "RiskAssessmentScope") @@ -111,6 +113,7 @@ func TestRiskAssessmentScope_CRUD(t *testing.T) { } `json:"scopes"` } `json:"node"` } + err := owner.Execute(` query($id: ID!) { node(id: $id) { @@ -150,6 +153,7 @@ func TestRiskAssessmentNode_Create(t *testing.T) { } `json:"riskAssessmentNodeEdge"` } `json:"createRiskAssessmentNode"` } + err := owner.Execute(` mutation($input: CreateRiskAssessmentNodeInput!) { createRiskAssessmentNode(input: $input) { @@ -191,6 +195,7 @@ func TestRiskAssessmentProcess_Create(t *testing.T) { } `json:"riskAssessmentProcessEdge"` } `json:"createRiskAssessmentProcess"` } + err := owner.Execute(` mutation($input: CreateRiskAssessmentProcessInput!) { createRiskAssessmentProcess(input: $input) { @@ -232,6 +237,7 @@ func TestRiskAssessmentThreat_Create(t *testing.T) { } `json:"riskAssessmentThreatEdge"` } `json:"createRiskAssessmentThreat"` } + err := owner.Execute(` mutation($input: CreateRiskAssessmentThreatInput!) { createRiskAssessmentThreat(input: $input) { @@ -269,6 +275,7 @@ func TestRiskAssessmentScenario_Create(t *testing.T) { } `json:"riskAssessmentScenarioEdge"` } `json:"createRiskAssessmentScenario"` } + err := owner.Execute(` mutation($input: CreateRiskAssessmentScenarioInput!) { createRiskAssessmentScenario(input: $input) { @@ -314,6 +321,7 @@ func TestRiskAssessmentScenario_ListViaRisk(t *testing.T) { } `json:"scenarios"` } `json:"node"` } + err := owner.Execute(` query($id: ID!) { node(id: $id) { @@ -355,6 +363,7 @@ func TestRiskAssessmentScenario_ListViaScope(t *testing.T) { } `json:"scenarios"` } `json:"node"` } + err := owner.Execute(` query($id: ID!) { node(id: $id) { @@ -388,6 +397,7 @@ func TestRiskAssessment_Update(t *testing.T) { } `json:"riskAssessment"` } `json:"updateRiskAssessment"` } + err := owner.Execute(` mutation($input: UpdateRiskAssessmentInput!) { updateRiskAssessment(input: $input) { @@ -423,6 +433,7 @@ func TestRiskAssessmentScope_Update(t *testing.T) { } `json:"riskAssessmentScope"` } `json:"updateRiskAssessmentScope"` } + err := owner.Execute(` mutation($input: UpdateRiskAssessmentScopeInput!) { updateRiskAssessmentScope(input: $input) { @@ -457,6 +468,7 @@ func TestRiskAssessmentNode_Update(t *testing.T) { } `json:"riskAssessmentNode"` } `json:"updateRiskAssessmentNode"` } + err := owner.Execute(` mutation($input: UpdateRiskAssessmentNodeInput!) { updateRiskAssessmentNode(input: $input) { @@ -494,6 +506,7 @@ func TestRiskAssessmentProcess_Update(t *testing.T) { } `json:"riskAssessmentProcess"` } `json:"updateRiskAssessmentProcess"` } + err := owner.Execute(` mutation($input: UpdateRiskAssessmentProcessInput!) { updateRiskAssessmentProcess(input: $input) { @@ -531,6 +544,7 @@ func TestRiskAssessmentThreat_Update(t *testing.T) { } `json:"riskAssessmentThreat"` } `json:"updateRiskAssessmentThreat"` } + err := owner.Execute(` mutation($input: UpdateRiskAssessmentThreatInput!) { updateRiskAssessmentThreat(input: $input) { @@ -567,6 +581,7 @@ func TestRiskAssessmentScenario_Update(t *testing.T) { } `json:"riskAssessmentScenario"` } `json:"updateRiskAssessmentScenario"` } + err := owner.Execute(` mutation($input: UpdateRiskAssessmentScenarioInput!) { updateRiskAssessmentScenario(input: $input) { @@ -609,6 +624,7 @@ func TestRiskAssessmentScenario_LinkUnlinkThreat(t *testing.T) { } `json:"threats"` } `json:"node"` } + err := owner.Execute(` query($id: ID!) { node(id: $id) { @@ -665,6 +681,7 @@ func TestRiskAssessmentScenario_LinkUnlinkRisk(t *testing.T) { } `json:"risks"` } `json:"node"` } + err := owner.Execute(` query($id: ID!) { node(id: $id) { @@ -735,6 +752,7 @@ func TestRiskAssessment_RBAC(t *testing.T) { Name string `json:"name"` } `json:"node"` } + err := viewer.Execute(` query($id: ID!) { node(id: $id) { ... on RiskAssessment { id name } } } `, map[string]any{"id": raID}, &result) @@ -755,6 +773,7 @@ func TestRiskAssessment_TenantIsolation(t *testing.T) { ID string `json:"id"` } `json:"node"` } + err := owner2.Execute(` query($id: ID!) { node(id: $id) { ... on RiskAssessment { id } } } `, map[string]any{"id": raID}, &result) diff --git a/e2e/internal/factory/factory.go b/e2e/internal/factory/factory.go index b651cae0c..c7ef7e350 100644 --- a/e2e/internal/factory/factory.go +++ b/e2e/internal/factory/factory.go @@ -1467,10 +1467,12 @@ func ReportDetectedResources(c *testutil.Client, bannerID string, count int) { func CreateRiskAssessment(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() + var a Attrs if len(attrs) > 0 { a = attrs[0] } + const query = ` mutation($input: CreateRiskAssessmentInput!) { createRiskAssessment(input: $input) { @@ -1478,6 +1480,7 @@ func CreateRiskAssessment(c *testutil.Client, attrs ...Attrs) string { } } ` + input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("Risk Assessment")), @@ -1485,6 +1488,7 @@ func CreateRiskAssessment(c *testutil.Client, attrs ...Attrs) string { if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } + var result struct { CreateRiskAssessment struct { RiskAssessmentEdge struct { @@ -1494,17 +1498,21 @@ func CreateRiskAssessment(c *testutil.Client, attrs ...Attrs) string { } `json:"riskAssessmentEdge"` } `json:"createRiskAssessment"` } + err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessment mutation failed") + return result.CreateRiskAssessment.RiskAssessmentEdge.Node.ID } func CreateRiskAssessmentScope(c *testutil.Client, riskAssessmentID string, attrs ...Attrs) string { c.T.Helper() + var a Attrs if len(attrs) > 0 { a = attrs[0] } + const query = ` mutation($input: CreateRiskAssessmentScopeInput!) { createRiskAssessmentScope(input: $input) { @@ -1512,10 +1520,12 @@ func CreateRiskAssessmentScope(c *testutil.Client, riskAssessmentID string, attr } } ` + input := map[string]any{ "riskAssessmentId": riskAssessmentID, "name": a.getString("name", SafeName("Scope")), } + var result struct { CreateRiskAssessmentScope struct { RiskAssessmentScopeEdge struct { @@ -1525,17 +1535,21 @@ func CreateRiskAssessmentScope(c *testutil.Client, riskAssessmentID string, attr } `json:"riskAssessmentScopeEdge"` } `json:"createRiskAssessmentScope"` } + err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentScope mutation failed") + return result.CreateRiskAssessmentScope.RiskAssessmentScopeEdge.Node.ID } func CreateRiskAssessmentNode(c *testutil.Client, scopeID string, attrs ...Attrs) string { c.T.Helper() + var a Attrs if len(attrs) > 0 { a = attrs[0] } + const query = ` mutation($input: CreateRiskAssessmentNodeInput!) { createRiskAssessmentNode(input: $input) { @@ -1543,11 +1557,13 @@ func CreateRiskAssessmentNode(c *testutil.Client, scopeID string, attrs ...Attrs } } ` + input := map[string]any{ "riskAssessmentScopeId": scopeID, "nodeType": a.getString("nodeType", "ASSET"), "name": a.getString("name", SafeName("Node")), } + var result struct { CreateRiskAssessmentNode struct { RiskAssessmentNodeEdge struct { @@ -1557,17 +1573,21 @@ func CreateRiskAssessmentNode(c *testutil.Client, scopeID string, attrs ...Attrs } `json:"riskAssessmentNodeEdge"` } `json:"createRiskAssessmentNode"` } + err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentNode mutation failed") + return result.CreateRiskAssessmentNode.RiskAssessmentNodeEdge.Node.ID } func CreateRiskAssessmentProcess(c *testutil.Client, scopeID, sourceNodeID, targetNodeID string, attrs ...Attrs) string { c.T.Helper() + var a Attrs if len(attrs) > 0 { a = attrs[0] } + const query = ` mutation($input: CreateRiskAssessmentProcessInput!) { createRiskAssessmentProcess(input: $input) { @@ -1575,12 +1595,14 @@ func CreateRiskAssessmentProcess(c *testutil.Client, scopeID, sourceNodeID, targ } } ` + input := map[string]any{ "riskAssessmentScopeId": scopeID, "sourceNodeId": sourceNodeID, "targetNodeId": targetNodeID, "name": a.getString("name", SafeName("Process")), } + var result struct { CreateRiskAssessmentProcess struct { RiskAssessmentProcessEdge struct { @@ -1590,17 +1612,21 @@ func CreateRiskAssessmentProcess(c *testutil.Client, scopeID, sourceNodeID, targ } `json:"riskAssessmentProcessEdge"` } `json:"createRiskAssessmentProcess"` } + err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentProcess mutation failed") + return result.CreateRiskAssessmentProcess.RiskAssessmentProcessEdge.Node.ID } func CreateRiskAssessmentThreat(c *testutil.Client, scopeID, processID string, attrs ...Attrs) string { c.T.Helper() + var a Attrs if len(attrs) > 0 { a = attrs[0] } + const query = ` mutation($input: CreateRiskAssessmentThreatInput!) { createRiskAssessmentThreat(input: $input) { @@ -1608,12 +1634,14 @@ func CreateRiskAssessmentThreat(c *testutil.Client, scopeID, processID string, a } } ` + input := map[string]any{ "riskAssessmentScopeId": scopeID, "processId": processID, "name": a.getString("name", SafeName("Threat")), "category": a.getString("category", "Confidentiality"), } + var result struct { CreateRiskAssessmentThreat struct { RiskAssessmentThreatEdge struct { @@ -1623,17 +1651,21 @@ func CreateRiskAssessmentThreat(c *testutil.Client, scopeID, processID string, a } `json:"riskAssessmentThreatEdge"` } `json:"createRiskAssessmentThreat"` } + err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentThreat mutation failed") + return result.CreateRiskAssessmentThreat.RiskAssessmentThreatEdge.Node.ID } func CreateRiskAssessmentScenario(c *testutil.Client, scopeID string, attrs ...Attrs) string { c.T.Helper() + var a Attrs if len(attrs) > 0 { a = attrs[0] } + const query = ` mutation($input: CreateRiskAssessmentScenarioInput!) { createRiskAssessmentScenario(input: $input) { @@ -1641,6 +1673,7 @@ func CreateRiskAssessmentScenario(c *testutil.Client, scopeID string, attrs ...A } } ` + input := map[string]any{ "riskAssessmentScopeId": scopeID, "name": a.getString("name", SafeName("Scenario")), @@ -1648,6 +1681,7 @@ func CreateRiskAssessmentScenario(c *testutil.Client, scopeID string, attrs ...A if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } + var result struct { CreateRiskAssessmentScenario struct { RiskAssessmentScenarioEdge struct { @@ -1657,13 +1691,16 @@ func CreateRiskAssessmentScenario(c *testutil.Client, scopeID string, attrs ...A } `json:"riskAssessmentScenarioEdge"` } `json:"createRiskAssessmentScenario"` } + err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentScenario mutation failed") + return result.CreateRiskAssessmentScenario.RiskAssessmentScenarioEdge.Node.ID } func LinkRiskAssessmentScenarioThreat(c *testutil.Client, scenarioID, threatID string) { c.T.Helper() + const query = ` mutation($input: LinkRiskAssessmentScenarioThreatInput!) { linkRiskAssessmentScenarioThreat(input: $input) { @@ -1671,6 +1708,7 @@ func LinkRiskAssessmentScenarioThreat(c *testutil.Client, scenarioID, threatID s } } ` + _, err := c.Do(query, map[string]any{ "input": map[string]any{ "riskAssessmentScenarioId": scenarioID, @@ -1682,6 +1720,7 @@ func LinkRiskAssessmentScenarioThreat(c *testutil.Client, scenarioID, threatID s func LinkRiskAssessmentScenarioRisk(c *testutil.Client, scenarioID, riskID string) { c.T.Helper() + const query = ` mutation($input: LinkRiskAssessmentScenarioRiskInput!) { linkRiskAssessmentScenarioRisk(input: $input) { @@ -1689,6 +1728,7 @@ func LinkRiskAssessmentScenarioRisk(c *testutil.Client, scenarioID, riskID strin } } ` + _, err := c.Do(query, map[string]any{ "input": map[string]any{ "riskAssessmentScenarioId": scenarioID, diff --git a/pkg/accessreview/drivers/heroku.go b/pkg/accessreview/drivers/heroku.go index 6e950172a..5277997e7 100644 --- a/pkg/accessreview/drivers/heroku.go +++ b/pkg/accessreview/drivers/heroku.go @@ -76,6 +76,7 @@ func (d *HerokuDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error if err != nil { return nil, fmt.Errorf("cannot build heroku members URL: %w", err) } + rangeHeader := "" for range maxPaginationPages { diff --git a/pkg/coredata/risk_assessment.go b/pkg/coredata/risk_assessment.go index 976b70911..15c0aa52e 100644 --- a/pkg/coredata/risk_assessment.go +++ b/pkg/coredata/risk_assessment.go @@ -59,6 +59,7 @@ func (ra *RiskAssessment) AuthorizationAttributes(ctx context.Context, conn pg.Q if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } + return nil, fmt.Errorf("cannot query risk assessment authorization attributes: %w", err) } @@ -192,6 +193,7 @@ VALUES (@id, @tenant_id, @organization_id, @name, @description, @created_at, @up if err != nil { return fmt.Errorf("cannot insert risk assessment: %w", err) } + return nil } @@ -246,5 +248,6 @@ DELETE FROM risk_assessments WHERE %s AND id = @id maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/coredata/risk_assessment_node.go b/pkg/coredata/risk_assessment_node.go index 394edb768..84b4b1f2a 100644 --- a/pkg/coredata/risk_assessment_node.go +++ b/pkg/coredata/risk_assessment_node.go @@ -49,6 +49,7 @@ func (n *RiskAssessmentNode) CursorKey(orderBy RiskAssessmentNodeOrderField) pag case RiskAssessmentNodeOrderFieldName: return page.CursorKey{ID: n.ID, Value: n.Name} } + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } @@ -60,6 +61,7 @@ func (n *RiskAssessmentNode) AuthorizationAttributes(ctx context.Context, conn p if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } + return nil, fmt.Errorf("cannot query risk assessment node authorization attributes: %w", err) } @@ -98,11 +100,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk assessment nodes: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentNode]) if err != nil { return fmt.Errorf("cannot collect risk assessment nodes: %w", err) } + *ns = results + return nil } @@ -137,11 +142,14 @@ ORDER BY if err != nil { return fmt.Errorf("cannot query risk assessment nodes: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentNode]) if err != nil { return fmt.Errorf("cannot collect risk assessment nodes: %w", err) } + *ns = results + return nil } @@ -169,6 +177,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk assessment nodes: %w", err) } + return count, nil } @@ -197,14 +206,18 @@ LIMIT 1; if err != nil { return fmt.Errorf("cannot query risk assessment node: %w", err) } + result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[RiskAssessmentNode]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } + return fmt.Errorf("cannot collect risk assessment node: %w", err) } + *n = result + return nil } @@ -240,13 +253,16 @@ INSERT INTO risk_assessment_nodes ( "created_at": n.CreatedAt, "updated_at": n.UpdatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_nodes_unique_name" { return ErrResourceAlreadyExists } + return fmt.Errorf("cannot insert risk assessment node: %w", err) } + return nil } @@ -274,9 +290,11 @@ WHERE if err != nil { return fmt.Errorf("cannot update risk assessment node: %w", err) } + if result.RowsAffected() == 0 { return ErrResourceNotFound } + return nil } @@ -292,5 +310,6 @@ WHERE maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/coredata/risk_assessment_process.go b/pkg/coredata/risk_assessment_process.go index b739cedd8..387d27d85 100644 --- a/pkg/coredata/risk_assessment_process.go +++ b/pkg/coredata/risk_assessment_process.go @@ -50,6 +50,7 @@ func (p *RiskAssessmentProcess) CursorKey(orderBy RiskAssessmentProcessOrderFiel case RiskAssessmentProcessOrderFieldName: return page.CursorKey{ID: p.ID, Value: p.Name} } + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } @@ -61,6 +62,7 @@ func (p *RiskAssessmentProcess) AuthorizationAttributes(ctx context.Context, con if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } + return nil, fmt.Errorf("cannot query risk assessment process authorization attributes: %w", err) } @@ -100,11 +102,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk assessment processes: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentProcess]) if err != nil { return fmt.Errorf("cannot collect risk assessment processes: %w", err) } + *ps = results + return nil } @@ -140,11 +145,14 @@ ORDER BY if err != nil { return fmt.Errorf("cannot query risk assessment processes: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentProcess]) if err != nil { return fmt.Errorf("cannot collect risk assessment processes: %w", err) } + *ps = results + return nil } @@ -172,6 +180,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk assessment processes: %w", err) } + return count, nil } @@ -196,18 +205,23 @@ LIMIT 1; q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) + rows, err := conn.Query(ctx, q, args) if err != nil { return fmt.Errorf("cannot query risk assessment process: %w", err) } + result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[RiskAssessmentProcess]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } + return fmt.Errorf("cannot collect risk assessment process: %w", err) } + *p = result + return nil } @@ -246,13 +260,16 @@ INSERT INTO risk_assessment_processes ( "created_at": p.CreatedAt, "updated_at": p.UpdatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_processes_unique_name" { return ErrResourceAlreadyExists } + return fmt.Errorf("cannot insert risk assessment process: %w", err) } + return nil } @@ -277,13 +294,16 @@ WHERE "updated_at": p.UpdatedAt, } maps.Copy(args, scope.SQLArguments()) + result, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot update risk assessment process: %w", err) } + if result.RowsAffected() == 0 { return ErrResourceNotFound } + return nil } @@ -298,5 +318,6 @@ WHERE args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/coredata/risk_assessment_scenario.go b/pkg/coredata/risk_assessment_scenario.go index 503af0066..483e9334a 100644 --- a/pkg/coredata/risk_assessment_scenario.go +++ b/pkg/coredata/risk_assessment_scenario.go @@ -48,6 +48,7 @@ func (s *RiskAssessmentScenario) CursorKey(orderBy RiskAssessmentScenarioOrderFi case RiskAssessmentScenarioOrderFieldName: return page.CursorKey{ID: s.ID, Value: s.Name} } + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } @@ -59,6 +60,7 @@ func (s *RiskAssessmentScenario) AuthorizationAttributes(ctx context.Context, co if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } + return nil, fmt.Errorf("cannot query risk scenario authorization attributes: %w", err) } @@ -97,11 +99,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk scenarios: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentScenario]) if err != nil { return fmt.Errorf("cannot collect risk scenarios: %w", err) } + *ss = results + return nil } @@ -128,6 +133,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk scenarios: %w", err) } + return count, nil } @@ -172,11 +178,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk scenarios: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentScenario]) if err != nil { return fmt.Errorf("cannot collect risk scenarios: %w", err) } + *ss = results + return nil } @@ -212,6 +221,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk scenarios: %w", err) } + return count, nil } @@ -247,11 +257,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk scenarios: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentScenario]) if err != nil { return fmt.Errorf("cannot collect risk scenarios: %w", err) } + *ss = results + return nil } @@ -278,6 +291,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk scenarios: %w", err) } + return count, nil } @@ -301,18 +315,23 @@ LIMIT 1 q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) + rows, err := conn.Query(ctx, q, args) if err != nil { return fmt.Errorf("cannot query risk scenario: %w", err) } + result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[RiskAssessmentScenario]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } + return fmt.Errorf("cannot collect risk scenario: %w", err) } + *s = result + return nil } @@ -348,10 +367,12 @@ INSERT INTO risk_assessment_scenarios ( "created_at": s.CreatedAt, "updated_at": s.UpdatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot insert risk scenario: %w", err) } + return nil } @@ -369,13 +390,16 @@ WHERE q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{"id": s.ID, "name": s.Name, "description": s.Description, "updated_at": s.UpdatedAt} maps.Copy(args, scope.SQLArguments()) + result, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot update risk scenario: %w", err) } + if result.RowsAffected() == 0 { return ErrResourceNotFound } + return nil } @@ -390,5 +414,6 @@ WHERE args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/coredata/risk_assessment_scenario_risk.go b/pkg/coredata/risk_assessment_scenario_risk.go index b376bf6c2..67b2ea1cb 100644 --- a/pkg/coredata/risk_assessment_scenario_risk.go +++ b/pkg/coredata/risk_assessment_scenario_risk.go @@ -58,13 +58,16 @@ INSERT INTO risk_assessment_scenario_risks ( "risk_id": sr.RiskID, "created_at": sr.CreatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_scenario_risks_pkey" { return ErrResourceAlreadyExists } + return fmt.Errorf("cannot insert risk scenario risk: %w", err) } + return nil } @@ -83,6 +86,7 @@ WHERE } maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } @@ -137,11 +141,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk scenario risks: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Risk]) if err != nil { return fmt.Errorf("cannot collect risk scenario risks: %w", err) } + *rs = results + return nil } @@ -177,5 +184,6 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk scenario risks: %w", err) } + return count, nil } diff --git a/pkg/coredata/risk_assessment_scenario_threat.go b/pkg/coredata/risk_assessment_scenario_threat.go index 3d4a60954..c581a5d59 100644 --- a/pkg/coredata/risk_assessment_scenario_threat.go +++ b/pkg/coredata/risk_assessment_scenario_threat.go @@ -58,13 +58,16 @@ INSERT INTO risk_assessment_scenario_threats ( "risk_assessment_threat_id": st.RiskAssessmentThreatID, "created_at": st.CreatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_scenario_threats_pkey" { return ErrResourceAlreadyExists } + return fmt.Errorf("cannot insert risk scenario threat: %w", err) } + return nil } @@ -83,6 +86,7 @@ WHERE } maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } @@ -128,11 +132,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk scenario threats: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentThreat]) if err != nil { return fmt.Errorf("cannot collect risk scenario threats: %w", err) } + *ts = results + return nil } @@ -168,5 +175,6 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk scenario threats: %w", err) } + return count, nil } diff --git a/pkg/coredata/risk_assessment_scope.go b/pkg/coredata/risk_assessment_scope.go index ca1137036..9eac303db 100644 --- a/pkg/coredata/risk_assessment_scope.go +++ b/pkg/coredata/risk_assessment_scope.go @@ -47,6 +47,7 @@ func (s *RiskAssessmentScope) CursorKey(orderBy RiskAssessmentScopeOrderField) p case RiskAssessmentScopeOrderFieldName: return page.CursorKey{ID: s.ID, Value: s.Name} } + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } @@ -58,6 +59,7 @@ func (s *RiskAssessmentScope) AuthorizationAttributes(ctx context.Context, conn if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } + return nil, fmt.Errorf("cannot query risk assessment scope authorization attributes: %w", err) } @@ -95,11 +97,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk assessment scopes: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentScope]) if err != nil { return fmt.Errorf("cannot collect risk assessment scopes: %w", err) } + *ss = results + return nil } @@ -127,6 +132,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk assessment scopes: %w", err) } + return count, nil } @@ -149,18 +155,23 @@ LIMIT 1 q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) + rows, err := conn.Query(ctx, q, args) if err != nil { return fmt.Errorf("cannot query risk assessment scope: %w", err) } + result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[RiskAssessmentScope]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } + return fmt.Errorf("cannot collect risk assessment scope: %w", err) } + *s = result + return nil } @@ -193,10 +204,12 @@ INSERT INTO risk_assessment_scopes ( "created_at": s.CreatedAt, "updated_at": s.UpdatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot insert risk assessment scope: %w", err) } + return nil } @@ -213,13 +226,16 @@ WHERE q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{"id": s.ID, "name": s.Name, "updated_at": s.UpdatedAt} maps.Copy(args, scope.SQLArguments()) + result, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot update risk assessment scope: %w", err) } + if result.RowsAffected() == 0 { return ErrResourceNotFound } + return nil } @@ -234,5 +250,6 @@ WHERE args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/coredata/risk_assessment_threat.go b/pkg/coredata/risk_assessment_threat.go index fa4de6d95..a7c569e54 100644 --- a/pkg/coredata/risk_assessment_threat.go +++ b/pkg/coredata/risk_assessment_threat.go @@ -50,6 +50,7 @@ func (t *RiskAssessmentThreat) CursorKey(orderBy RiskAssessmentThreatOrderField) case RiskAssessmentThreatOrderFieldName: return page.CursorKey{ID: t.ID, Value: t.Name} } + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } @@ -61,6 +62,7 @@ func (t *RiskAssessmentThreat) AuthorizationAttributes(ctx context.Context, conn if errors.Is(err, pgx.ErrNoRows) { return nil, ErrResourceNotFound } + return nil, fmt.Errorf("cannot query risk assessment threat authorization attributes: %w", err) } @@ -100,11 +102,14 @@ WHERE if err != nil { return fmt.Errorf("cannot query risk threats: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentThreat]) if err != nil { return fmt.Errorf("cannot collect risk threats: %w", err) } + *ts = results + return nil } @@ -140,11 +145,14 @@ ORDER BY if err != nil { return fmt.Errorf("cannot query risk threats: %w", err) } + results, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[RiskAssessmentThreat]) if err != nil { return fmt.Errorf("cannot collect risk threats: %w", err) } + *ts = results + return nil } @@ -172,6 +180,7 @@ WHERE if err := conn.QueryRow(ctx, q, args).Scan(&count); err != nil { return 0, fmt.Errorf("cannot count risk threats: %w", err) } + return count, nil } @@ -196,18 +205,23 @@ LIMIT 1; q = fmt.Sprintf(q, scope.SQLFragment()) args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) + rows, err := conn.Query(ctx, q, args) if err != nil { return fmt.Errorf("cannot query risk threat: %w", err) } + result, err := pgx.CollectExactlyOneRow(rows, pgx.RowToStructByName[RiskAssessmentThreat]) if err != nil { if errors.Is(err, pgx.ErrNoRows) { return ErrResourceNotFound } + return fmt.Errorf("cannot collect risk threat: %w", err) } + *t = result + return nil } @@ -246,13 +260,16 @@ INSERT INTO risk_assessment_threats ( "created_at": t.CreatedAt, "updated_at": t.UpdatedAt, } + _, err := conn.Exec(ctx, q, args) if err != nil { if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_threats_unique_name" { return ErrResourceAlreadyExists } + return fmt.Errorf("cannot insert risk threat: %w", err) } + return nil } @@ -277,13 +294,16 @@ WHERE "updated_at": t.UpdatedAt, } maps.Copy(args, scope.SQLArguments()) + result, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot update risk threat: %w", err) } + if result.RowsAffected() == 0 { return ErrResourceNotFound } + return nil } @@ -298,5 +318,6 @@ WHERE args := pgx.StrictNamedArgs{"id": id} maps.Copy(args, scope.SQLArguments()) _, err := conn.Exec(ctx, q, args) + return err } diff --git a/pkg/riskmanagement/mermaid.go b/pkg/riskmanagement/mermaid.go index 2302f4261..c2bba404c 100644 --- a/pkg/riskmanagement/mermaid.go +++ b/pkg/riskmanagement/mermaid.go @@ -35,12 +35,15 @@ func (s *Service) BuildScopeMermaidChart(ctx context.Context, scope coredata.Sco if err := nodes.LoadAllByRiskAssessmentScopeID(ctx, conn, scope, scopeID); err != nil { return fmt.Errorf("cannot load nodes: %w", err) } + if err := processes.LoadAllByRiskAssessmentScopeID(ctx, conn, scope, scopeID); err != nil { return fmt.Errorf("cannot load processes: %w", err) } + if err := threats.LoadAllByRiskAssessmentScopeID(ctx, conn, scope, scopeID); err != nil { return fmt.Errorf("cannot load threats: %w", err) } + return nil }) if err != nil { @@ -75,10 +78,12 @@ func buildScopeMermaidChart( for _, p := range processes { src, srcOK := nodeAlias[p.SourceNodeID] + dst, dstOK := nodeAlias[p.TargetNodeID] if !srcOK || !dstOK { continue } + fmt.Fprintf(&b, " %s -- \"%s\" --> %s\n", src, escapeMermaidLabel(p.Name), dst) } @@ -92,10 +97,12 @@ func buildScopeMermaidChart( if !ok { continue } + targetAlias, ok := nodeAlias[target] if !ok { continue } + tid := fmt.Sprintf("t%d", i) label := escapeMermaidLabel(fmt.Sprintf("%s (%s)", t.Name, t.Category)) fmt.Fprintf(&b, " %s{{\"%s\"}}\n", tid, label) @@ -114,6 +121,7 @@ func buildScopeMermaidChart( func mermaidNodeShape(t coredata.RiskAssessmentNodeType, id, name string) string { label := `"` + escapeMermaidLabel(name) + `"` + switch t { case coredata.RiskAssessmentNodeTypeEntity: return fmt.Sprintf("%s([%s])", id, label) diff --git a/pkg/riskmanagement/service.go b/pkg/riskmanagement/service.go index 2eec0e29f..4be7a749f 100644 --- a/pkg/riskmanagement/service.go +++ b/pkg/riskmanagement/service.go @@ -140,6 +140,7 @@ func (r *CreateRiskAssessmentRequest) Validate() error { v.Check(r.OrganizationID, "organization_id", validator.Required(), validator.GID(coredata.OrganizationEntityType)) v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + return v.Error() } @@ -148,6 +149,7 @@ func (r *UpdateRiskAssessmentRequest) Validate() error { v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.RiskAssessmentEntityType)) v.Check(r.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + return v.Error() } @@ -155,6 +157,7 @@ func (r *CreateRiskAssessmentScopeRequest) Validate() error { v := validator.New() v.Check(r.RiskAssessmentID, "risk_assessment_id", validator.Required(), validator.GID(coredata.RiskAssessmentEntityType)) v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) + return v.Error() } @@ -162,6 +165,7 @@ func (r *UpdateRiskAssessmentScopeRequest) Validate() error { v := validator.New() v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.RiskAssessmentScopeEntityType)) v.Check(r.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) + return v.Error() } @@ -170,6 +174,7 @@ func (r *CreateRiskAssessmentNodeRequest) Validate() error { v.Check(r.RiskAssessmentScopeID, "risk_assessment_scope_id", validator.Required(), validator.GID(coredata.RiskAssessmentScopeEntityType)) v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.NodeType, "node_type", validator.Required(), validator.OneOfSlice(coredata.RiskAssessmentNodeTypes())) + return v.Error() } @@ -178,6 +183,7 @@ func (r *UpdateRiskAssessmentNodeRequest) Validate() error { v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.RiskAssessmentNodeEntityType)) v.Check(r.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.NodeType, "node_type", validator.OneOfSlice(coredata.RiskAssessmentNodeTypes())) + return v.Error() } @@ -187,6 +193,7 @@ func (r *CreateRiskAssessmentProcessRequest) Validate() error { v.Check(r.SourceNodeID, "source_node_id", validator.Required(), validator.GID(coredata.RiskAssessmentNodeEntityType)) v.Check(r.TargetNodeID, "target_node_id", validator.Required(), validator.GID(coredata.RiskAssessmentNodeEntityType)) v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) + return v.Error() } @@ -196,6 +203,7 @@ func (r *UpdateRiskAssessmentProcessRequest) Validate() error { v.Check(r.SourceNodeID, "source_node_id", validator.GID(coredata.RiskAssessmentNodeEntityType)) v.Check(r.TargetNodeID, "target_node_id", validator.GID(coredata.RiskAssessmentNodeEntityType)) v.Check(r.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) + return v.Error() } @@ -205,6 +213,7 @@ func (r *CreateRiskAssessmentThreatRequest) Validate() error { v.Check(r.ProcessID, "process_id", validator.Required(), validator.GID(coredata.RiskAssessmentProcessEntityType)) v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.Category, "category", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) + return v.Error() } @@ -214,6 +223,7 @@ func (r *UpdateRiskAssessmentThreatRequest) Validate() error { v.Check(r.ProcessID, "process_id", validator.GID(coredata.RiskAssessmentProcessEntityType)) v.Check(r.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.Category, "category", validator.SafeTextNoNewLine(TitleMaxLength)) + return v.Error() } @@ -222,6 +232,7 @@ func (r *CreateRiskAssessmentScenarioRequest) Validate() error { v.Check(r.RiskAssessmentScopeID, "risk_assessment_scope_id", validator.Required(), validator.GID(coredata.RiskAssessmentScopeEntityType)) v.Check(r.Name, "name", validator.Required(), validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + return v.Error() } @@ -229,6 +240,7 @@ func (r *LinkRiskAssessmentScenarioThreatRequest) Validate() error { v := validator.New() v.Check(r.RiskAssessmentScenarioID, "risk_scenario_id", validator.Required(), validator.GID(coredata.RiskAssessmentScenarioEntityType)) v.Check(r.ThreatID, "threat_id", validator.Required(), validator.GID(coredata.RiskAssessmentThreatEntityType)) + return v.Error() } @@ -236,6 +248,7 @@ func (r *UnlinkRiskAssessmentScenarioThreatRequest) Validate() error { v := validator.New() v.Check(r.RiskAssessmentScenarioID, "risk_scenario_id", validator.Required(), validator.GID(coredata.RiskAssessmentScenarioEntityType)) v.Check(r.ThreatID, "threat_id", validator.Required(), validator.GID(coredata.RiskAssessmentThreatEntityType)) + return v.Error() } @@ -243,6 +256,7 @@ func (r *LinkRiskAssessmentScenarioRiskRequest) Validate() error { v := validator.New() v.Check(r.RiskAssessmentScenarioID, "risk_scenario_id", validator.Required(), validator.GID(coredata.RiskAssessmentScenarioEntityType)) v.Check(r.RiskID, "risk_id", validator.Required(), validator.GID(coredata.RiskEntityType)) + return v.Error() } @@ -250,6 +264,7 @@ func (r *UnlinkRiskAssessmentScenarioRiskRequest) Validate() error { v := validator.New() v.Check(r.RiskAssessmentScenarioID, "risk_scenario_id", validator.Required(), validator.GID(coredata.RiskAssessmentScenarioEntityType)) v.Check(r.RiskID, "risk_id", validator.Required(), validator.GID(coredata.RiskEntityType)) + return v.Error() } @@ -258,6 +273,7 @@ func (r *UpdateRiskAssessmentScenarioRequest) Validate() error { v.Check(r.ID, "id", validator.Required(), validator.GID(coredata.RiskAssessmentScenarioEntityType)) v.Check(r.Name, "name", validator.SafeTextNoNewLine(TitleMaxLength)) v.Check(r.Description, "description", validator.SafeText(ContentMaxLength)) + return v.Error() } @@ -282,29 +298,34 @@ func (s *Service) Create(ctx context.Context, scope coredata.Scoper, req CreateR if err := ra.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot insert risk assessment: %w", err) } + return nil }, ) if err != nil { return nil, err } + return ra, nil } func (s *Service) Get(ctx context.Context, scope coredata.Scoper, id gid.GID) (*coredata.RiskAssessment, error) { ra := &coredata.RiskAssessment{} + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := ra.LoadByID(ctx, conn, scope, id); err != nil { return fmt.Errorf("cannot load risk assessment: %w", err) } + return nil }, ) if err != nil { return nil, err } + return ra, nil } @@ -314,28 +335,34 @@ func (s *Service) Update(ctx context.Context, scope coredata.Scoper, req UpdateR } ra := &coredata.RiskAssessment{} + err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { if err := ra.LoadByID(ctx, tx, scope, req.ID); err != nil { return fmt.Errorf("cannot load risk assessment: %w", err) } + if req.Name != nil { ra.Name = *req.Name } + if req.Description != nil { ra.Description = *req.Description } + ra.UpdatedAt = time.Now() if err := ra.Update(ctx, tx, scope); err != nil { return fmt.Errorf("cannot update risk assessment: %w", err) } + return nil }, ) if err != nil { return nil, err } + return ra, nil } @@ -347,6 +374,7 @@ func (s *Service) Delete(ctx context.Context, scope coredata.Scoper, id gid.GID) if err := ra.Delete(ctx, tx, scope, id); err != nil { return fmt.Errorf("cannot delete risk assessment: %w", err) } + return nil }, ) @@ -359,18 +387,21 @@ func (s *Service) ListForOrganizationID( cursor *page.Cursor[coredata.RiskAssessmentOrderField], ) (*page.Page[*coredata.RiskAssessment, coredata.RiskAssessmentOrderField], error) { var results coredata.RiskAssessments + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByOrganizationID(ctx, conn, scope, organizationID, cursor); err != nil { return fmt.Errorf("cannot list risk assessments: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } @@ -380,20 +411,24 @@ func (s *Service) CountForOrganizationID( organizationID gid.GID, ) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ras := &coredata.RiskAssessments{} + count, err = ras.CountByOrganizationID(ctx, conn, scope, organizationID) if err != nil { return fmt.Errorf("cannot count risk assessments: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -418,33 +453,39 @@ func (s *Service) CreateScope(ctx context.Context, scope coredata.Scoper, req Cr if err := ra.LoadByID(ctx, tx, scope, req.RiskAssessmentID); err != nil { return fmt.Errorf("cannot load risk assessment: %w", err) } + raScope.OrganizationID = ra.OrganizationID if err := raScope.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot insert risk assessment scope: %w", err) } + return nil }, ) if err != nil { return nil, err } + return raScope, nil } func (s *Service) GetScope(ctx context.Context, scope coredata.Scoper, id gid.GID) (*coredata.RiskAssessmentScope, error) { raScope := &coredata.RiskAssessmentScope{} + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := raScope.LoadByID(ctx, conn, scope, id); err != nil { return fmt.Errorf("cannot load risk assessment scope: %w", err) } + return nil }, ) if err != nil { return nil, err } + return raScope, nil } @@ -454,25 +495,30 @@ func (s *Service) UpdateScope(ctx context.Context, scope coredata.Scoper, req Up } raScope := &coredata.RiskAssessmentScope{} + err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { if err := raScope.LoadByID(ctx, tx, scope, req.ID); err != nil { return fmt.Errorf("cannot load risk assessment scope: %w", err) } + if req.Name != nil { raScope.Name = *req.Name } + raScope.UpdatedAt = time.Now() if err := raScope.Update(ctx, tx, scope); err != nil { return fmt.Errorf("cannot update risk assessment scope: %w", err) } + return nil }, ) if err != nil { return nil, err } + return raScope, nil } @@ -484,6 +530,7 @@ func (s *Service) DeleteScope(ctx context.Context, scope coredata.Scoper, id gid if err := raScope.Delete(ctx, tx, scope, id); err != nil { return fmt.Errorf("cannot delete risk assessment scope: %w", err) } + return nil }, ) @@ -496,37 +543,44 @@ func (s *Service) ListScopesForRiskAssessmentID( cursor *page.Cursor[coredata.RiskAssessmentScopeOrderField], ) (*page.Page[*coredata.RiskAssessmentScope, coredata.RiskAssessmentScopeOrderField], error) { var results coredata.RiskAssessmentScopes + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByRiskAssessmentID(ctx, conn, scope, riskAssessmentID, cursor); err != nil { return fmt.Errorf("cannot list risk assessment scopes: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountScopesForRiskAssessmentID(ctx context.Context, scope coredata.Scoper, riskAssessmentID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ss := &coredata.RiskAssessmentScopes{} + count, err = ss.CountByRiskAssessmentID(ctx, conn, scope, riskAssessmentID) if err != nil { return fmt.Errorf("cannot count risk assessment scopes: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -552,33 +606,39 @@ func (s *Service) CreateNode(ctx context.Context, scope coredata.Scoper, req Cre if err := raScope.LoadByID(ctx, tx, scope, req.RiskAssessmentScopeID); err != nil { return fmt.Errorf("cannot load risk assessment scope: %w", err) } + node.OrganizationID = raScope.OrganizationID if err := node.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot insert risk assessment node: %w", err) } + return nil }, ) if err != nil { return nil, err } + return node, nil } func (s *Service) GetNode(ctx context.Context, scope coredata.Scoper, id gid.GID) (*coredata.RiskAssessmentNode, error) { node := &coredata.RiskAssessmentNode{} + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := node.LoadByID(ctx, conn, scope, id); err != nil { return fmt.Errorf("cannot load risk assessment node: %w", err) } + return nil }, ) if err != nil { return nil, err } + return node, nil } @@ -588,28 +648,34 @@ func (s *Service) UpdateNode(ctx context.Context, scope coredata.Scoper, req Upd } node := &coredata.RiskAssessmentNode{} + err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { if err := node.LoadByID(ctx, tx, scope, req.ID); err != nil { return fmt.Errorf("cannot load risk assessment node: %w", err) } + if req.Name != nil { node.Name = *req.Name } + if req.NodeType != nil { node.NodeType = *req.NodeType } + node.UpdatedAt = time.Now() if err := node.Update(ctx, tx, scope); err != nil { return fmt.Errorf("cannot update risk assessment node: %w", err) } + return nil }, ) if err != nil { return nil, err } + return node, nil } @@ -621,6 +687,7 @@ func (s *Service) DeleteNode(ctx context.Context, scope coredata.Scoper, id gid. if err := node.Delete(ctx, tx, scope, id); err != nil { return fmt.Errorf("cannot delete risk assessment node: %w", err) } + return nil }, ) @@ -633,37 +700,44 @@ func (s *Service) ListNodesForScopeID( cursor *page.Cursor[coredata.RiskAssessmentNodeOrderField], ) (*page.Page[*coredata.RiskAssessmentNode, coredata.RiskAssessmentNodeOrderField], error) { var results coredata.RiskAssessmentNodes + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByRiskAssessmentScopeID(ctx, conn, scope, scopeID, cursor); err != nil { return fmt.Errorf("cannot list risk assessment nodes: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountNodesForScopeID(ctx context.Context, scope coredata.Scoper, scopeID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ns := &coredata.RiskAssessmentNodes{} + count, err = ns.CountByRiskAssessmentScopeID(ctx, conn, scope, scopeID) if err != nil { return fmt.Errorf("cannot count risk assessment nodes: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -690,39 +764,48 @@ func (s *Service) CreateProcess(ctx context.Context, scope coredata.Scoper, req if err := raScope.LoadByID(ctx, tx, scope, req.RiskAssessmentScopeID); err != nil { return fmt.Errorf("cannot load risk assessment scope: %w", err) } + process.OrganizationID = raScope.OrganizationID + if err := s.assertNodeInScope(ctx, tx, scope, req.SourceNodeID, req.RiskAssessmentScopeID, "source_node_id"); err != nil { return err } + if err := s.assertNodeInScope(ctx, tx, scope, req.TargetNodeID, req.RiskAssessmentScopeID, "target_node_id"); err != nil { return err } + if err := process.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot insert risk assessment process: %w", err) } + return nil }, ) if err != nil { return nil, err } + return process, nil } func (s *Service) GetProcess(ctx context.Context, scope coredata.Scoper, id gid.GID) (*coredata.RiskAssessmentProcess, error) { process := &coredata.RiskAssessmentProcess{} + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := process.LoadByID(ctx, conn, scope, id); err != nil { return fmt.Errorf("cannot load risk assessment process: %w", err) } + return nil }, ) if err != nil { return nil, err } + return process, nil } @@ -732,37 +815,46 @@ func (s *Service) UpdateProcess(ctx context.Context, scope coredata.Scoper, req } process := &coredata.RiskAssessmentProcess{} + err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { if err := process.LoadByID(ctx, tx, scope, req.ID); err != nil { return fmt.Errorf("cannot load risk assessment process: %w", err) } + if req.SourceNodeID != nil { if err := s.assertNodeInScope(ctx, tx, scope, *req.SourceNodeID, process.RiskAssessmentScopeID, "source_node_id"); err != nil { return err } + process.SourceNodeID = *req.SourceNodeID } + if req.TargetNodeID != nil { if err := s.assertNodeInScope(ctx, tx, scope, *req.TargetNodeID, process.RiskAssessmentScopeID, "target_node_id"); err != nil { return err } + process.TargetNodeID = *req.TargetNodeID } + if req.Name != nil { process.Name = *req.Name } + process.UpdatedAt = time.Now() if err := process.Update(ctx, tx, scope); err != nil { return fmt.Errorf("cannot update risk assessment process: %w", err) } + return nil }, ) if err != nil { return nil, err } + return process, nil } @@ -774,6 +866,7 @@ func (s *Service) DeleteProcess(ctx context.Context, scope coredata.Scoper, id g if err := process.Delete(ctx, tx, scope, id); err != nil { return fmt.Errorf("cannot delete risk assessment process: %w", err) } + return nil }, ) @@ -786,37 +879,44 @@ func (s *Service) ListProcessesForScopeID( cursor *page.Cursor[coredata.RiskAssessmentProcessOrderField], ) (*page.Page[*coredata.RiskAssessmentProcess, coredata.RiskAssessmentProcessOrderField], error) { var results coredata.RiskAssessmentProcesses + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByRiskAssessmentScopeID(ctx, conn, scope, scopeID, cursor); err != nil { return fmt.Errorf("cannot list risk assessment processes: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountProcessesForScopeID(ctx context.Context, scope coredata.Scoper, scopeID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ps := &coredata.RiskAssessmentProcesses{} + count, err = ps.CountByRiskAssessmentScopeID(ctx, conn, scope, scopeID) if err != nil { return fmt.Errorf("cannot count risk assessment processes: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -843,36 +943,44 @@ func (s *Service) CreateThreat(ctx context.Context, scope coredata.Scoper, req C if err := raScope.LoadByID(ctx, tx, scope, req.RiskAssessmentScopeID); err != nil { return fmt.Errorf("cannot load risk assessment scope: %w", err) } + threat.OrganizationID = raScope.OrganizationID + if err := s.assertProcessInScope(ctx, tx, scope, req.ProcessID, req.RiskAssessmentScopeID, "process_id"); err != nil { return err } + if err := threat.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot insert risk threat: %w", err) } + return nil }, ) if err != nil { return nil, err } + return threat, nil } func (s *Service) GetThreat(ctx context.Context, scope coredata.Scoper, id gid.GID) (*coredata.RiskAssessmentThreat, error) { threat := &coredata.RiskAssessmentThreat{} + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := threat.LoadByID(ctx, conn, scope, id); err != nil { return fmt.Errorf("cannot load risk threat: %w", err) } + return nil }, ) if err != nil { return nil, err } + return threat, nil } @@ -882,34 +990,42 @@ func (s *Service) UpdateThreat(ctx context.Context, scope coredata.Scoper, req U } threat := &coredata.RiskAssessmentThreat{} + err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { if err := threat.LoadByID(ctx, tx, scope, req.ID); err != nil { return fmt.Errorf("cannot load risk threat: %w", err) } + if req.ProcessID != nil { if err := s.assertProcessInScope(ctx, tx, scope, *req.ProcessID, threat.RiskAssessmentScopeID, "process_id"); err != nil { return err } + threat.ProcessID = *req.ProcessID } + if req.Name != nil { threat.Name = *req.Name } + if req.Category != nil { threat.Category = *req.Category } + threat.UpdatedAt = time.Now() if err := threat.Update(ctx, tx, scope); err != nil { return fmt.Errorf("cannot update risk threat: %w", err) } + return nil }, ) if err != nil { return nil, err } + return threat, nil } @@ -921,6 +1037,7 @@ func (s *Service) DeleteThreat(ctx context.Context, scope coredata.Scoper, id gi if err := threat.Delete(ctx, tx, scope, id); err != nil { return fmt.Errorf("cannot delete risk threat: %w", err) } + return nil }, ) @@ -933,37 +1050,44 @@ func (s *Service) ListThreatsForScopeID( cursor *page.Cursor[coredata.RiskAssessmentThreatOrderField], ) (*page.Page[*coredata.RiskAssessmentThreat, coredata.RiskAssessmentThreatOrderField], error) { var results coredata.RiskAssessmentThreats + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByRiskAssessmentScopeID(ctx, conn, scope, scopeID, cursor); err != nil { return fmt.Errorf("cannot list risk threats: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountThreatsForScopeID(ctx context.Context, scope coredata.Scoper, scopeID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ts := &coredata.RiskAssessmentThreats{} + count, err = ts.CountByRiskAssessmentScopeID(ctx, conn, scope, scopeID) if err != nil { return fmt.Errorf("cannot count risk threats: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -989,33 +1113,39 @@ func (s *Service) CreateScenario(ctx context.Context, scope coredata.Scoper, req if err := raScope.LoadByID(ctx, tx, scope, req.RiskAssessmentScopeID); err != nil { return fmt.Errorf("cannot load risk assessment scope: %w", err) } + scenario.OrganizationID = raScope.OrganizationID if err := scenario.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot insert risk scenario: %w", err) } + return nil }, ) if err != nil { return nil, err } + return scenario, nil } func (s *Service) GetScenario(ctx context.Context, scope coredata.Scoper, id gid.GID) (*coredata.RiskAssessmentScenario, error) { scenario := &coredata.RiskAssessmentScenario{} + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := scenario.LoadByID(ctx, conn, scope, id); err != nil { return fmt.Errorf("cannot load risk scenario: %w", err) } + return nil }, ) if err != nil { return nil, err } + return scenario, nil } @@ -1025,28 +1155,34 @@ func (s *Service) UpdateScenario(ctx context.Context, scope coredata.Scoper, req } scenario := &coredata.RiskAssessmentScenario{} + err := s.pg.WithTx( ctx, func(ctx context.Context, tx pg.Tx) error { if err := scenario.LoadByID(ctx, tx, scope, req.ID); err != nil { return fmt.Errorf("cannot load risk scenario: %w", err) } + if req.Name != nil { scenario.Name = *req.Name } + if req.Description != nil { scenario.Description = *req.Description } + scenario.UpdatedAt = time.Now() if err := scenario.Update(ctx, tx, scope); err != nil { return fmt.Errorf("cannot update risk scenario: %w", err) } + return nil }, ) if err != nil { return nil, err } + return scenario, nil } @@ -1058,6 +1194,7 @@ func (s *Service) DeleteScenario(ctx context.Context, scope coredata.Scoper, id if err := scenario.Delete(ctx, tx, scope, id); err != nil { return fmt.Errorf("cannot delete risk scenario: %w", err) } + return nil }, ) @@ -1070,18 +1207,21 @@ func (s *Service) ListScenariosForOrganizationID( cursor *page.Cursor[coredata.RiskAssessmentScenarioOrderField], ) (*page.Page[*coredata.RiskAssessmentScenario, coredata.RiskAssessmentScenarioOrderField], error) { var results coredata.RiskAssessmentScenarios + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByOrganizationID(ctx, conn, scope, organizationID, cursor); err != nil { return fmt.Errorf("cannot list risk scenarios: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } @@ -1091,20 +1231,24 @@ func (s *Service) CountScenariosForOrganizationID( organizationID gid.GID, ) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ss := &coredata.RiskAssessmentScenarios{} + count, err = ss.CountByOrganizationID(ctx, conn, scope, organizationID) if err != nil { return fmt.Errorf("cannot count risk scenarios: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -1115,37 +1259,44 @@ func (s *Service) ListScenariosForRiskID( cursor *page.Cursor[coredata.RiskAssessmentScenarioOrderField], ) (*page.Page[*coredata.RiskAssessmentScenario, coredata.RiskAssessmentScenarioOrderField], error) { var results coredata.RiskAssessmentScenarios + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByRiskID(ctx, conn, scope, riskID, cursor); err != nil { return fmt.Errorf("cannot list risk scenarios: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountScenariosForRiskID(ctx context.Context, scope coredata.Scoper, riskID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ss := &coredata.RiskAssessmentScenarios{} + count, err = ss.CountByRiskID(ctx, conn, scope, riskID) if err != nil { return fmt.Errorf("cannot count risk scenarios: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -1156,37 +1307,44 @@ func (s *Service) ListScenariosForScopeID( cursor *page.Cursor[coredata.RiskAssessmentScenarioOrderField], ) (*page.Page[*coredata.RiskAssessmentScenario, coredata.RiskAssessmentScenarioOrderField], error) { var results coredata.RiskAssessmentScenarios + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByRiskAssessmentScopeID(ctx, conn, scope, scopeID, cursor); err != nil { return fmt.Errorf("cannot list risk scenarios: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountScenariosForScopeID(ctx context.Context, scope coredata.Scoper, scopeID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ss := &coredata.RiskAssessmentScenarios{} + count, err = ss.CountByRiskAssessmentScopeID(ctx, conn, scope, scopeID) if err != nil { return fmt.Errorf("cannot count risk scenarios: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -1202,10 +1360,12 @@ func (s *Service) LinkScenarioThreat(ctx context.Context, scope coredata.Scoper, if err := scenario.LoadByID(ctx, tx, scope, req.RiskAssessmentScenarioID); err != nil { return fmt.Errorf("cannot load risk scenario: %w", err) } + threat := coredata.RiskAssessmentThreat{} if err := threat.LoadByID(ctx, tx, scope, req.ThreatID); err != nil { return fmt.Errorf("cannot load threat: %w", err) } + if scenario.OrganizationID != threat.OrganizationID { return validator.ValidationErrors{{ Field: "threat_id", @@ -1213,6 +1373,7 @@ func (s *Service) LinkScenarioThreat(ctx context.Context, scope coredata.Scoper, Message: "threat and scenario must belong to the same organization", }} } + link := &coredata.RiskAssessmentScenarioThreat{ RiskAssessmentScenarioID: req.RiskAssessmentScenarioID, RiskAssessmentThreatID: req.ThreatID, @@ -1221,6 +1382,7 @@ func (s *Service) LinkScenarioThreat(ctx context.Context, scope coredata.Scoper, if err := link.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot link scenario threat: %w", err) } + return nil }, ) @@ -1241,6 +1403,7 @@ func (s *Service) UnlinkScenarioThreat(ctx context.Context, scope coredata.Scope if err := link.Delete(ctx, tx, scope); err != nil { return fmt.Errorf("cannot unlink scenario threat: %w", err) } + return nil }, ) @@ -1258,10 +1421,12 @@ func (s *Service) LinkScenarioRisk(ctx context.Context, scope coredata.Scoper, r if err := scenario.LoadByID(ctx, tx, scope, req.RiskAssessmentScenarioID); err != nil { return fmt.Errorf("cannot load risk scenario: %w", err) } + risk := coredata.Risk{} if err := risk.LoadByID(ctx, tx, scope, req.RiskID); err != nil { return fmt.Errorf("cannot load risk: %w", err) } + if scenario.OrganizationID != risk.OrganizationID { return validator.ValidationErrors{{ Field: "risk_id", @@ -1269,6 +1434,7 @@ func (s *Service) LinkScenarioRisk(ctx context.Context, scope coredata.Scoper, r Message: "risk and scenario must belong to the same organization", }} } + link := &coredata.RiskAssessmentScenarioRisk{ RiskAssessmentScenarioID: req.RiskAssessmentScenarioID, RiskID: req.RiskID, @@ -1277,6 +1443,7 @@ func (s *Service) LinkScenarioRisk(ctx context.Context, scope coredata.Scoper, r if err := link.Insert(ctx, tx, scope); err != nil { return fmt.Errorf("cannot link scenario risk: %w", err) } + return nil }, ) @@ -1297,6 +1464,7 @@ func (s *Service) UnlinkScenarioRisk(ctx context.Context, scope coredata.Scoper, if err := link.Delete(ctx, tx, scope); err != nil { return fmt.Errorf("cannot unlink scenario risk: %w", err) } + return nil }, ) @@ -1309,37 +1477,44 @@ func (s *Service) ListThreatsForScenarioID( cursor *page.Cursor[coredata.RiskAssessmentThreatOrderField], ) (*page.Page[*coredata.RiskAssessmentThreat, coredata.RiskAssessmentThreatOrderField], error) { var results coredata.RiskAssessmentThreats + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByScenarioID(ctx, conn, scope, scenarioID, cursor); err != nil { return fmt.Errorf("cannot list scenario threats: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountThreatsForScenarioID(ctx context.Context, scope coredata.Scoper, scenarioID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { ts := &coredata.RiskAssessmentThreats{} + count, err = ts.CountByScenarioID(ctx, conn, scope, scenarioID) if err != nil { return fmt.Errorf("cannot count scenario threats: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -1350,37 +1525,44 @@ func (s *Service) ListRisksForScenarioID( cursor *page.Cursor[coredata.RiskOrderField], ) (*page.Page[*coredata.Risk, coredata.RiskOrderField], error) { var results coredata.Risks + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) error { if err := results.LoadByScenarioID(ctx, conn, scope, scenarioID, cursor); err != nil { return fmt.Errorf("cannot list scenario risks: %w", err) } + return nil }, ) if err != nil { return nil, err } + return page.NewPage(results, cursor), nil } func (s *Service) CountRisksForScenarioID(ctx context.Context, scope coredata.Scoper, scenarioID gid.GID) (int, error) { var count int + err := s.pg.WithConn( ctx, func(ctx context.Context, conn pg.Querier) (err error) { rs := &coredata.Risks{} + count, err = rs.CountByScenarioID(ctx, conn, scope, scenarioID) if err != nil { return fmt.Errorf("cannot count scenario risks: %w", err) } + return nil }, ) if err != nil { return 0, err } + return count, nil } @@ -1400,6 +1582,7 @@ func (s *Service) assertNodeInScope( Message: "node not found", }} } + if node.RiskAssessmentScopeID != scopeID { return validator.ValidationErrors{{ Field: field, @@ -1407,6 +1590,7 @@ func (s *Service) assertNodeInScope( Message: "node does not belong to this scope", }} } + return nil } @@ -1426,6 +1610,7 @@ func (s *Service) assertProcessInScope( Message: "process not found", }} } + if process.RiskAssessmentScopeID != scopeID { return validator.ValidationErrors{{ Field: field, @@ -1433,5 +1618,6 @@ func (s *Service) assertProcessInScope( Message: "process does not belong to this scope", }} } + return nil } diff --git a/pkg/server/api/connect/v1/session_resolvers.go b/pkg/server/api/connect/v1/session_resolvers.go index a103770a4..ebc4ccb33 100644 --- a/pkg/server/api/connect/v1/session_resolvers.go +++ b/pkg/server/api/connect/v1/session_resolvers.go @@ -402,6 +402,7 @@ func (r *mutationResolver) AssumeOrganizationSession(ctx context.Context, input } r.logger.ErrorCtx(ctx, "cannot assume organization session", log.Error(err)) + return nil, gqlutils.Internal(ctx) } diff --git a/pkg/server/api/console/v1/base_resolvers.go b/pkg/server/api/console/v1/base_resolvers.go index f05eb3d65..25a342537 100644 --- a/pkg/server/api/console/v1/base_resolvers.go +++ b/pkg/server/api/console/v1/base_resolvers.go @@ -124,60 +124,72 @@ func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error action = probo.ActionRiskAssessmentGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { scope := coredata.NewScopeFromObjectID(id) + ra, err := r.riskManagement.Get(ctx, scope, id) if err != nil { return nil, err } + return types.NewRiskAssessment(ra), nil } case coredata.RiskAssessmentNodeEntityType: action = probo.ActionRiskAssessmentNodeGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { scope := coredata.NewScopeFromObjectID(id) + n, err := r.riskManagement.GetNode(ctx, scope, id) if err != nil { return nil, err } + return types.NewRiskAssessmentNode(n), nil } case coredata.RiskAssessmentProcessEntityType: action = probo.ActionRiskAssessmentProcessGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { scope := coredata.NewScopeFromObjectID(id) + p, err := r.riskManagement.GetProcess(ctx, scope, id) if err != nil { return nil, err } + return types.NewRiskAssessmentProcess(p), nil } case coredata.RiskAssessmentThreatEntityType: action = probo.ActionRiskAssessmentThreatGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { scope := coredata.NewScopeFromObjectID(id) + t, err := r.riskManagement.GetThreat(ctx, scope, id) if err != nil { return nil, err } + return types.NewRiskAssessmentThreat(t), nil } case coredata.RiskAssessmentScopeEntityType: action = probo.ActionRiskAssessmentScopeGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { scope := coredata.NewScopeFromObjectID(id) + s, err := r.riskManagement.GetScope(ctx, scope, id) if err != nil { return nil, err } + return types.NewRiskAssessmentScope(s), nil } case coredata.RiskAssessmentScenarioEntityType: action = probo.ActionRiskAssessmentScenarioGet loadNode = func(ctx context.Context, id gid.GID) (types.Node, error) { scope := coredata.NewScopeFromObjectID(id) + s, err := r.riskManagement.GetScenario(ctx, scope, id) if err != nil { return nil, err } + return types.NewRiskAssessmentScenario(s), nil } case coredata.ThirdPartyComplianceReportEntityType: diff --git a/pkg/server/api/console/v1/risk_assessment_resolvers.go b/pkg/server/api/console/v1/risk_assessment_resolvers.go index 22c6e766e..9354f8886 100644 --- a/pkg/server/api/console/v1/risk_assessment_resolvers.go +++ b/pkg/server/api/console/v1/risk_assessment_resolvers.go @@ -27,7 +27,9 @@ func (r *mutationResolver) CreateRiskAssessment(ctx context.Context, input types if err := r.authorize(ctx, input.OrganizationID, probo.ActionRiskAssessmentCreate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.OrganizationID) + ra, err := r.riskManagement.Create( ctx, scope, @@ -41,12 +43,16 @@ func (r *mutationResolver) CreateRiskAssessment(ctx context.Context, input types if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot create risk assessment", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.CreateRiskAssessmentPayload{ RiskAssessmentEdge: types.NewRiskAssessmentConnectionEdge(ra, coredata.RiskAssessmentOrderFieldCreatedAt), }, nil @@ -57,7 +63,9 @@ func (r *mutationResolver) UpdateRiskAssessment(ctx context.Context, input types if err := r.authorize(ctx, input.ID, probo.ActionRiskAssessmentUpdate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.ID) + ra, err := r.riskManagement.Update( ctx, scope, @@ -71,9 +79,12 @@ func (r *mutationResolver) UpdateRiskAssessment(ctx context.Context, input types if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot update risk assessment", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.UpdateRiskAssessmentPayload{RiskAssessment: types.NewRiskAssessment(ra)}, nil } @@ -82,14 +93,18 @@ func (r *mutationResolver) DeleteRiskAssessment(ctx context.Context, input types if err := r.authorize(ctx, input.RiskAssessmentID, probo.ActionRiskAssessmentDelete); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentID) if err := r.riskManagement.Delete(ctx, scope, input.RiskAssessmentID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot delete risk assessment", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.DeleteRiskAssessmentPayload{DeletedRiskAssessmentID: input.RiskAssessmentID}, nil } @@ -98,7 +113,9 @@ func (r *mutationResolver) CreateRiskAssessmentScope(ctx context.Context, input if err := r.authorize(ctx, input.RiskAssessmentID, probo.ActionRiskAssessmentScopeCreate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentID) + raScope, err := r.riskManagement.CreateScope( ctx, scope, @@ -111,12 +128,16 @@ func (r *mutationResolver) CreateRiskAssessmentScope(ctx context.Context, input if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot create risk assessment scope", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.CreateRiskAssessmentScopePayload{ RiskAssessmentScopeEdge: types.NewRiskAssessmentScopeConnectionEdge(raScope, coredata.RiskAssessmentScopeOrderFieldCreatedAt), }, nil @@ -127,7 +148,9 @@ func (r *mutationResolver) UpdateRiskAssessmentScope(ctx context.Context, input if err := r.authorize(ctx, input.ID, probo.ActionRiskAssessmentScopeUpdate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.ID) + raScope, err := r.riskManagement.UpdateScope( ctx, scope, @@ -140,9 +163,12 @@ func (r *mutationResolver) UpdateRiskAssessmentScope(ctx context.Context, input if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot update risk assessment scope", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.UpdateRiskAssessmentScopePayload{RiskAssessmentScope: types.NewRiskAssessmentScope(raScope)}, nil } @@ -151,14 +177,18 @@ func (r *mutationResolver) DeleteRiskAssessmentScope(ctx context.Context, input if err := r.authorize(ctx, input.RiskAssessmentScopeID, probo.ActionRiskAssessmentScopeDelete); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScopeID) if err := r.riskManagement.DeleteScope(ctx, scope, input.RiskAssessmentScopeID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot delete risk assessment scope", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.DeleteRiskAssessmentScopePayload{DeletedRiskAssessmentScopeID: input.RiskAssessmentScopeID}, nil } @@ -167,7 +197,9 @@ func (r *mutationResolver) CreateRiskAssessmentNode(ctx context.Context, input t if err := r.authorize(ctx, input.RiskAssessmentScopeID, probo.ActionRiskAssessmentNodeCreate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScopeID) + node, err := r.riskManagement.CreateNode( ctx, scope, @@ -181,12 +213,16 @@ func (r *mutationResolver) CreateRiskAssessmentNode(ctx context.Context, input t if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot create risk assessment node", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.CreateRiskAssessmentNodePayload{ RiskAssessmentNodeEdge: &types.RiskAssessmentNodeConnectionEdge{ Cursor: node.CursorKey(coredata.RiskAssessmentNodeOrderFieldCreatedAt), @@ -200,7 +236,9 @@ func (r *mutationResolver) UpdateRiskAssessmentNode(ctx context.Context, input t if err := r.authorize(ctx, input.ID, probo.ActionRiskAssessmentNodeUpdate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.ID) + node, err := r.riskManagement.UpdateNode( ctx, scope, @@ -214,9 +252,12 @@ func (r *mutationResolver) UpdateRiskAssessmentNode(ctx context.Context, input t if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot update risk assessment node", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.UpdateRiskAssessmentNodePayload{RiskAssessmentNode: types.NewRiskAssessmentNode(node)}, nil } @@ -225,14 +266,18 @@ func (r *mutationResolver) DeleteRiskAssessmentNode(ctx context.Context, input t if err := r.authorize(ctx, input.RiskAssessmentNodeID, probo.ActionRiskAssessmentNodeDelete); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentNodeID) if err := r.riskManagement.DeleteNode(ctx, scope, input.RiskAssessmentNodeID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot delete risk assessment node", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.DeleteRiskAssessmentNodePayload{DeletedRiskAssessmentNodeID: input.RiskAssessmentNodeID}, nil } @@ -241,7 +286,9 @@ func (r *mutationResolver) CreateRiskAssessmentProcess(ctx context.Context, inpu if err := r.authorize(ctx, input.RiskAssessmentScopeID, probo.ActionRiskAssessmentProcessCreate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScopeID) + process, err := r.riskManagement.CreateProcess( ctx, scope, @@ -256,12 +303,16 @@ func (r *mutationResolver) CreateRiskAssessmentProcess(ctx context.Context, inpu if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot create risk assessment process", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.CreateRiskAssessmentProcessPayload{ RiskAssessmentProcessEdge: &types.RiskAssessmentProcessConnectionEdge{ Cursor: process.CursorKey(coredata.RiskAssessmentProcessOrderFieldCreatedAt), @@ -275,7 +326,9 @@ func (r *mutationResolver) UpdateRiskAssessmentProcess(ctx context.Context, inpu if err := r.authorize(ctx, input.ID, probo.ActionRiskAssessmentProcessUpdate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.ID) + process, err := r.riskManagement.UpdateProcess( ctx, scope, @@ -290,9 +343,12 @@ func (r *mutationResolver) UpdateRiskAssessmentProcess(ctx context.Context, inpu if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot update risk assessment process", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.UpdateRiskAssessmentProcessPayload{RiskAssessmentProcess: types.NewRiskAssessmentProcess(process)}, nil } @@ -301,14 +357,18 @@ func (r *mutationResolver) DeleteRiskAssessmentProcess(ctx context.Context, inpu if err := r.authorize(ctx, input.RiskAssessmentProcessID, probo.ActionRiskAssessmentProcessDelete); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentProcessID) if err := r.riskManagement.DeleteProcess(ctx, scope, input.RiskAssessmentProcessID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot delete risk assessment process", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.DeleteRiskAssessmentProcessPayload{DeletedRiskAssessmentProcessID: input.RiskAssessmentProcessID}, nil } @@ -317,7 +377,9 @@ func (r *mutationResolver) CreateRiskAssessmentThreat(ctx context.Context, input if err := r.authorize(ctx, input.RiskAssessmentScopeID, probo.ActionRiskAssessmentThreatCreate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScopeID) + threat, err := r.riskManagement.CreateThreat( ctx, scope, @@ -332,12 +394,16 @@ func (r *mutationResolver) CreateRiskAssessmentThreat(ctx context.Context, input if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot create risk threat", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.CreateRiskAssessmentThreatPayload{ RiskAssessmentThreatEdge: &types.RiskAssessmentThreatConnectionEdge{ Cursor: threat.CursorKey(coredata.RiskAssessmentThreatOrderFieldCreatedAt), @@ -351,7 +417,9 @@ func (r *mutationResolver) UpdateRiskAssessmentThreat(ctx context.Context, input if err := r.authorize(ctx, input.ID, probo.ActionRiskAssessmentThreatUpdate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.ID) + threat, err := r.riskManagement.UpdateThreat( ctx, scope, @@ -366,9 +434,12 @@ func (r *mutationResolver) UpdateRiskAssessmentThreat(ctx context.Context, input if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot update risk threat", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.UpdateRiskAssessmentThreatPayload{RiskAssessmentThreat: types.NewRiskAssessmentThreat(threat)}, nil } @@ -377,14 +448,18 @@ func (r *mutationResolver) DeleteRiskAssessmentThreat(ctx context.Context, input if err := r.authorize(ctx, input.RiskAssessmentThreatID, probo.ActionRiskAssessmentThreatDelete); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentThreatID) if err := r.riskManagement.DeleteThreat(ctx, scope, input.RiskAssessmentThreatID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot delete risk threat", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.DeleteRiskAssessmentThreatPayload{DeletedRiskAssessmentThreatID: input.RiskAssessmentThreatID}, nil } @@ -393,7 +468,9 @@ func (r *mutationResolver) CreateRiskAssessmentScenario(ctx context.Context, inp if err := r.authorize(ctx, input.RiskAssessmentScopeID, probo.ActionRiskAssessmentScenarioCreate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScopeID) + scenario, err := r.riskManagement.CreateScenario( ctx, scope, @@ -407,12 +484,16 @@ func (r *mutationResolver) CreateRiskAssessmentScenario(ctx context.Context, inp if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot create risk scenario", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.CreateRiskAssessmentScenarioPayload{ RiskAssessmentScenarioEdge: &types.RiskAssessmentScenarioConnectionEdge{ Cursor: scenario.CursorKey(coredata.RiskAssessmentScenarioOrderFieldCreatedAt), @@ -426,7 +507,9 @@ func (r *mutationResolver) UpdateRiskAssessmentScenario(ctx context.Context, inp if err := r.authorize(ctx, input.ID, probo.ActionRiskAssessmentScenarioUpdate); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.ID) + scenario, err := r.riskManagement.UpdateScenario( ctx, scope, @@ -440,9 +523,12 @@ func (r *mutationResolver) UpdateRiskAssessmentScenario(ctx context.Context, inp if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot update risk scenario", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.UpdateRiskAssessmentScenarioPayload{RiskAssessmentScenario: types.NewRiskAssessmentScenario(scenario)}, nil } @@ -451,14 +537,18 @@ func (r *mutationResolver) DeleteRiskAssessmentScenario(ctx context.Context, inp if err := r.authorize(ctx, input.RiskAssessmentScenarioID, probo.ActionRiskAssessmentScenarioDelete); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScenarioID) if err := r.riskManagement.DeleteScenario(ctx, scope, input.RiskAssessmentScenarioID); err != nil { if errors.Is(err, coredata.ErrResourceNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot delete risk scenario", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return &types.DeleteRiskAssessmentScenarioPayload{DeletedRiskAssessmentScenarioID: input.RiskAssessmentScenarioID}, nil } @@ -467,6 +557,7 @@ func (r *mutationResolver) LinkRiskAssessmentScenarioThreat(ctx context.Context, if err := r.authorize(ctx, input.RiskAssessmentScenarioID, probo.ActionRiskAssessmentScenarioThreatLink); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScenarioID) if err := r.riskManagement.LinkScenarioThreat( ctx, @@ -479,17 +570,22 @@ func (r *mutationResolver) LinkRiskAssessmentScenarioThreat(ctx context.Context, if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot link risk scenario threat", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + scenario, err := r.riskManagement.GetScenario(ctx, scope, input.RiskAssessmentScenarioID) if err != nil { r.logger.ErrorCtx(ctx, "cannot load risk scenario", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &types.LinkRiskAssessmentScenarioThreatPayload{RiskAssessmentScenario: types.NewRiskAssessmentScenario(scenario)}, nil } @@ -498,6 +594,7 @@ func (r *mutationResolver) UnlinkRiskAssessmentScenarioThreat(ctx context.Contex if err := r.authorize(ctx, input.RiskAssessmentScenarioID, probo.ActionRiskAssessmentScenarioThreatUnlink); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScenarioID) if err := r.riskManagement.UnlinkScenarioThreat( ctx, @@ -510,14 +607,18 @@ func (r *mutationResolver) UnlinkRiskAssessmentScenarioThreat(ctx context.Contex if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot unlink risk scenario threat", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + scenario, err := r.riskManagement.GetScenario(ctx, scope, input.RiskAssessmentScenarioID) if err != nil { r.logger.ErrorCtx(ctx, "cannot load risk scenario", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &types.UnlinkRiskAssessmentScenarioThreatPayload{RiskAssessmentScenario: types.NewRiskAssessmentScenario(scenario)}, nil } @@ -526,6 +627,7 @@ func (r *mutationResolver) LinkRiskAssessmentScenarioRisk(ctx context.Context, i if err := r.authorize(ctx, input.RiskAssessmentScenarioID, probo.ActionRiskAssessmentScenarioRiskLink); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScenarioID) if err := r.riskManagement.LinkScenarioRisk( ctx, @@ -538,18 +640,24 @@ func (r *mutationResolver) LinkRiskAssessmentScenarioRisk(ctx context.Context, i if errors.Is(err, coredata.ErrResourceAlreadyExists) { return nil, gqlutils.Conflict(ctx, err) } + if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot link risk scenario risk", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + scenario, err := r.riskManagement.GetScenario(ctx, scope, input.RiskAssessmentScenarioID) if err != nil { r.logger.ErrorCtx(ctx, "cannot load risk scenario", log.Error(err)) return nil, gqlutils.Internal(ctx) } + s := types.NewRiskAssessmentScenario(scenario) + return &types.LinkRiskAssessmentScenarioRiskPayload{ RiskAssessmentScenario: s, RiskAssessmentScenarioEdge: &types.RiskAssessmentScenarioConnectionEdge{ @@ -564,6 +672,7 @@ func (r *mutationResolver) UnlinkRiskAssessmentScenarioRisk(ctx context.Context, if err := r.authorize(ctx, input.RiskAssessmentScenarioID, probo.ActionRiskAssessmentScenarioRiskUnlink); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(input.RiskAssessmentScenarioID) if err := r.riskManagement.UnlinkScenarioRisk( ctx, @@ -576,14 +685,18 @@ func (r *mutationResolver) UnlinkRiskAssessmentScenarioRisk(ctx context.Context, if validationErrors, ok := errors.AsType[validator.ValidationErrors](err); ok { return nil, gqlutils.InvalidValidationErrors(ctx, validationErrors) } + r.logger.ErrorCtx(ctx, "cannot unlink risk scenario risk", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + scenario, err := r.riskManagement.GetScenario(ctx, scope, input.RiskAssessmentScenarioID) if err != nil { r.logger.ErrorCtx(ctx, "cannot load risk scenario", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &types.UnlinkRiskAssessmentScenarioRiskPayload{ RiskAssessmentScenario: types.NewRiskAssessmentScenario(scenario), DeletedRiskAssessmentScenarioID: input.RiskAssessmentScenarioID, @@ -595,15 +708,20 @@ func (r *riskAssessmentResolver) Organization(ctx context.Context, obj *types.Ri if err := r.authorize(ctx, obj.ID, probo.ActionOrganizationGet); err != nil { return nil, err } + loaders := dataloader.FromContext(ctx) + organization, err := loaders.Organization.Load(ctx, obj.Organization.ID) if err != nil { if errors.Is(err, coredata.ErrResourceNotFound) || errors.Is(err, dataloadgen.ErrNotFound) { return nil, gqlutils.NotFound(ctx, err) } + r.logger.ErrorCtx(ctx, "cannot get organization", log.Error(err)) + return nil, gqlutils.Internal(ctx) } + return types.NewOrganization(organization), nil } @@ -612,7 +730,9 @@ func (r *riskAssessmentResolver) Scopes(ctx context.Context, obj *types.RiskAsse if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentScopeList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentScopeOrderField]{ Field: coredata.RiskAssessmentScopeOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -620,12 +740,15 @@ func (r *riskAssessmentResolver) Scopes(ctx context.Context, obj *types.RiskAsse if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentScopeOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListScopesForRiskAssessmentID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list risk assessment scopes", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentScopeConnection(p, r, obj.ID), nil } @@ -639,12 +762,15 @@ func (r *riskAssessmentConnectionResolver) TotalCount(ctx context.Context, obj * if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskAssessmentList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ParentID) + count, err := r.riskManagement.CountForOrganizationID(ctx, scope, obj.ParentID) if err != nil { r.logger.ErrorCtx(ctx, "cannot count risk assessments", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil } @@ -653,12 +779,15 @@ func (r *riskAssessmentNodeConnectionResolver) TotalCount(ctx context.Context, o if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskAssessmentNodeList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ParentID) + count, err := r.riskManagement.CountNodesForScopeID(ctx, scope, obj.ParentID) if err != nil { r.logger.ErrorCtx(ctx, "cannot count risk assessment nodes", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil } @@ -667,12 +796,15 @@ func (r *riskAssessmentProcessConnectionResolver) TotalCount(ctx context.Context if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskAssessmentProcessList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ParentID) + count, err := r.riskManagement.CountProcessesForScopeID(ctx, scope, obj.ParentID) if err != nil { r.logger.ErrorCtx(ctx, "cannot count risk assessment processes", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil } @@ -681,12 +813,15 @@ func (r *riskAssessmentScenarioResolver) Scope(ctx context.Context, obj *types.R if err := r.authorize(ctx, obj.RiskAssessmentScopeID, probo.ActionRiskAssessmentScopeGet); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.RiskAssessmentScopeID) + raScope, err := r.riskManagement.GetScope(ctx, scope, obj.RiskAssessmentScopeID) if err != nil { r.logger.ErrorCtx(ctx, "cannot load risk assessment scope", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentScope(raScope), nil } @@ -695,7 +830,9 @@ func (r *riskAssessmentScenarioResolver) Threats(ctx context.Context, obj *types if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentThreatList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentThreatOrderField]{ Field: coredata.RiskAssessmentThreatOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -703,12 +840,15 @@ func (r *riskAssessmentScenarioResolver) Threats(ctx context.Context, obj *types if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentThreatOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListThreatsForScenarioID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list scenario threats", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentThreatConnection(p, r, obj.ID), nil } @@ -717,7 +857,9 @@ func (r *riskAssessmentScenarioResolver) Risks(ctx context.Context, obj *types.R if err := r.authorize(ctx, obj.ID, probo.ActionRiskList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskOrderField]{ Field: coredata.RiskOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -725,12 +867,15 @@ func (r *riskAssessmentScenarioResolver) Risks(ctx context.Context, obj *types.R if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListRisksForScenarioID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list scenario risks", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskConnection(p, r, obj.ID, nil), nil } @@ -739,6 +884,7 @@ func (r *riskAssessmentScenarioConnectionResolver) TotalCount(ctx context.Contex if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskAssessmentScenarioList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ParentID) switch obj.Resolver.(type) { @@ -748,6 +894,7 @@ func (r *riskAssessmentScenarioConnectionResolver) TotalCount(ctx context.Contex r.logger.ErrorCtx(ctx, "cannot count risk scenarios", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil case *organizationResolver: count, err := r.riskManagement.CountScenariosForOrganizationID(ctx, scope, obj.ParentID) @@ -755,6 +902,7 @@ func (r *riskAssessmentScenarioConnectionResolver) TotalCount(ctx context.Contex r.logger.ErrorCtx(ctx, "cannot count risk scenarios", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil default: count, err := r.riskManagement.CountScenariosForRiskID(ctx, scope, obj.ParentID) @@ -762,6 +910,7 @@ func (r *riskAssessmentScenarioConnectionResolver) TotalCount(ctx context.Contex r.logger.ErrorCtx(ctx, "cannot count risk scenarios", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil } } @@ -771,7 +920,9 @@ func (r *riskAssessmentScopeResolver) Nodes(ctx context.Context, obj *types.Risk if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentNodeList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentNodeOrderField]{ Field: coredata.RiskAssessmentNodeOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -779,12 +930,15 @@ func (r *riskAssessmentScopeResolver) Nodes(ctx context.Context, obj *types.Risk if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentNodeOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListNodesForScopeID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list risk assessment nodes", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentNodeConnection(p, r, obj.ID), nil } @@ -793,7 +947,9 @@ func (r *riskAssessmentScopeResolver) Processes(ctx context.Context, obj *types. if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentProcessList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentProcessOrderField]{ Field: coredata.RiskAssessmentProcessOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -801,12 +957,15 @@ func (r *riskAssessmentScopeResolver) Processes(ctx context.Context, obj *types. if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentProcessOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListProcessesForScopeID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list risk assessment processes", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentProcessConnection(p, r, obj.ID), nil } @@ -815,7 +974,9 @@ func (r *riskAssessmentScopeResolver) Threats(ctx context.Context, obj *types.Ri if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentThreatList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentThreatOrderField]{ Field: coredata.RiskAssessmentThreatOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -823,12 +984,15 @@ func (r *riskAssessmentScopeResolver) Threats(ctx context.Context, obj *types.Ri if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentThreatOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListThreatsForScopeID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list risk threats", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentThreatConnection(p, r, obj.ID), nil } @@ -837,7 +1001,9 @@ func (r *riskAssessmentScopeResolver) Scenarios(ctx context.Context, obj *types. if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentScenarioList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentScenarioOrderField]{ Field: coredata.RiskAssessmentScenarioOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -845,12 +1011,15 @@ func (r *riskAssessmentScopeResolver) Scenarios(ctx context.Context, obj *types. if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentScenarioOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListScenariosForScopeID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list risk scenarios", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentScenarioConnection(p, r, obj.ID), nil } @@ -859,12 +1028,15 @@ func (r *riskAssessmentScopeResolver) MermaidChart(ctx context.Context, obj *typ if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentScopeGet); err != nil { return "", err } + scope := coredata.NewScopeFromObjectID(obj.ID) + chart, err := r.riskManagement.BuildScopeMermaidChart(ctx, scope, obj.ID) if err != nil { r.logger.ErrorCtx(ctx, "cannot build risk assessment scope mermaid chart", log.Error(err)) return "", gqlutils.Internal(ctx) } + return chart, nil } @@ -873,12 +1045,15 @@ func (r *riskAssessmentScopeConnectionResolver) TotalCount(ctx context.Context, if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskAssessmentScopeList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ParentID) + count, err := r.riskManagement.CountScopesForRiskAssessmentID(ctx, scope, obj.ParentID) if err != nil { r.logger.ErrorCtx(ctx, "cannot count risk assessment scopes", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil } @@ -887,6 +1062,7 @@ func (r *riskAssessmentThreatConnectionResolver) TotalCount(ctx context.Context, if err := r.authorize(ctx, obj.ParentID, probo.ActionRiskAssessmentThreatList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ParentID) switch obj.Resolver.(type) { @@ -896,6 +1072,7 @@ func (r *riskAssessmentThreatConnectionResolver) TotalCount(ctx context.Context, r.logger.ErrorCtx(ctx, "cannot count scenario threats", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil default: count, err := r.riskManagement.CountThreatsForScopeID(ctx, scope, obj.ParentID) @@ -903,6 +1080,7 @@ func (r *riskAssessmentThreatConnectionResolver) TotalCount(ctx context.Context, r.logger.ErrorCtx(ctx, "cannot count risk threats", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return &count, nil } } diff --git a/pkg/server/api/console/v1/risk_resolvers.go b/pkg/server/api/console/v1/risk_resolvers.go index fb33defb5..421d47f03 100644 --- a/pkg/server/api/console/v1/risk_resolvers.go +++ b/pkg/server/api/console/v1/risk_resolvers.go @@ -463,7 +463,9 @@ func (r *riskResolver) Scenarios(ctx context.Context, obj *types.Risk, first *in if err := r.authorize(ctx, obj.ID, probo.ActionRiskAssessmentScenarioList); err != nil { return nil, err } + scope := coredata.NewScopeFromObjectID(obj.ID) + pageOrderBy := page.OrderBy[coredata.RiskAssessmentScenarioOrderField]{ Field: coredata.RiskAssessmentScenarioOrderFieldCreatedAt, Direction: page.OrderDirectionDesc, @@ -471,12 +473,15 @@ func (r *riskResolver) Scenarios(ctx context.Context, obj *types.Risk, first *in if orderBy != nil { pageOrderBy = page.OrderBy[coredata.RiskAssessmentScenarioOrderField]{Field: orderBy.Field, Direction: orderBy.Direction} } + cursor := types.NewCursor(first, after, last, before, pageOrderBy) + p, err := r.riskManagement.ListScenariosForRiskID(ctx, scope, obj.ID, cursor) if err != nil { r.logger.ErrorCtx(ctx, "cannot list risk scenarios", log.Error(err)) return nil, gqlutils.Internal(ctx) } + return types.NewRiskAssessmentScenarioConnection(p, r, obj.ID), nil } @@ -512,11 +517,13 @@ func (r *riskConnectionResolver) TotalCount(ctx context.Context, obj *types.Risk return count, nil case *riskAssessmentScenarioResolver: scope := coredata.NewScopeFromObjectID(obj.ParentID) + count, err := r.riskManagement.CountRisksForScenarioID(ctx, scope, obj.ParentID) if err != nil { r.logger.ErrorCtx(ctx, "cannot count scenario risks", log.Error(err)) return 0, gqlutils.Internal(ctx) } + return count, nil } diff --git a/pkg/server/api/console/v1/types/risk_assessment.go b/pkg/server/api/console/v1/types/risk_assessment.go index 597a54071..a38c16c90 100644 --- a/pkg/server/api/console/v1/types/risk_assessment.go +++ b/pkg/server/api/console/v1/types/risk_assessment.go @@ -44,6 +44,7 @@ func NewRiskAssessmentConnection( Node: NewRiskAssessment(p.Data[i]), } } + return &RiskAssessmentConnection{ Edges: edges, PageInfo: *NewPageInfo(p), diff --git a/pkg/server/api/console/v1/types/risk_assessment_node.go b/pkg/server/api/console/v1/types/risk_assessment_node.go index b7444c98e..9284d856e 100644 --- a/pkg/server/api/console/v1/types/risk_assessment_node.go +++ b/pkg/server/api/console/v1/types/risk_assessment_node.go @@ -44,6 +44,7 @@ func NewRiskAssessmentNodeConnection( Node: NewRiskAssessmentNode(p.Data[i]), } } + return &RiskAssessmentNodeConnection{ Edges: edges, PageInfo: *NewPageInfo(p), diff --git a/pkg/server/api/console/v1/types/risk_assessment_process.go b/pkg/server/api/console/v1/types/risk_assessment_process.go index a129a5057..bb38b5c42 100644 --- a/pkg/server/api/console/v1/types/risk_assessment_process.go +++ b/pkg/server/api/console/v1/types/risk_assessment_process.go @@ -44,6 +44,7 @@ func NewRiskAssessmentProcessConnection( Node: NewRiskAssessmentProcess(p.Data[i]), } } + return &RiskAssessmentProcessConnection{ Edges: edges, PageInfo: *NewPageInfo(p), diff --git a/pkg/server/api/console/v1/types/risk_assessment_scenario.go b/pkg/server/api/console/v1/types/risk_assessment_scenario.go index eba9226c3..7a2ad7f65 100644 --- a/pkg/server/api/console/v1/types/risk_assessment_scenario.go +++ b/pkg/server/api/console/v1/types/risk_assessment_scenario.go @@ -44,6 +44,7 @@ func NewRiskAssessmentScenarioConnection( Node: NewRiskAssessmentScenario(p.Data[i]), } } + return &RiskAssessmentScenarioConnection{ Edges: edges, PageInfo: *NewPageInfo(p), diff --git a/pkg/server/api/console/v1/types/risk_assessment_scope.go b/pkg/server/api/console/v1/types/risk_assessment_scope.go index 7afb35507..cd4895405 100644 --- a/pkg/server/api/console/v1/types/risk_assessment_scope.go +++ b/pkg/server/api/console/v1/types/risk_assessment_scope.go @@ -44,6 +44,7 @@ func NewRiskAssessmentScopeConnection( Node: NewRiskAssessmentScope(p.Data[i]), } } + return &RiskAssessmentScopeConnection{ Edges: edges, PageInfo: *NewPageInfo(p), diff --git a/pkg/server/api/console/v1/types/risk_assessment_threat.go b/pkg/server/api/console/v1/types/risk_assessment_threat.go index ee42d1371..9674a090e 100644 --- a/pkg/server/api/console/v1/types/risk_assessment_threat.go +++ b/pkg/server/api/console/v1/types/risk_assessment_threat.go @@ -44,6 +44,7 @@ func NewRiskAssessmentThreatConnection( Node: NewRiskAssessmentThreat(p.Data[i]), } } + return &RiskAssessmentThreatConnection{ Edges: edges, PageInfo: *NewPageInfo(p),