From 46c60685595748002e11e39f2880950cc3c9ed79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Wed, 20 May 2026 09:44:00 +0400 Subject: [PATCH] Apply coredata rules: idempotent deletes and constraint checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delete methods no longer check RowsAffected — deletes are idempotent. PgError handlers now check both error code and constraint name to avoid misattributing violations. Also migrated remaining errors.As patterns to errors.AsType. Signed-off-by: Émile Ré --- pkg/coredata/access_review_campaign.go | 6 +----- pkg/coredata/applicability_statement.go | 11 +++-------- pkg/coredata/compliance_framework.go | 5 ++--- pkg/coredata/connector.go | 6 +----- .../document_version_approval_decision.go | 5 ++--- pkg/coredata/document_version_approval_quorum.go | 5 ++--- pkg/coredata/mailing_list_subscriber.go | 8 ++------ pkg/coredata/mailing_list_update.go | 6 +----- pkg/coredata/membership.go | 9 ++------- pkg/coredata/membership_profile.go | 8 ++------ pkg/coredata/oauth2_consent.go | 2 +- pkg/coredata/risk_assessment.go | 2 +- pkg/coredata/risk_assessment_node.go | 2 +- pkg/coredata/risk_assessment_process.go | 2 +- pkg/coredata/risk_assessment_scenario.go | 2 +- pkg/coredata/risk_assessment_scenario_risk.go | 2 +- pkg/coredata/risk_assessment_scenario_threat.go | 2 +- pkg/coredata/risk_assessment_scope.go | 2 +- pkg/coredata/risk_assessment_threat.go | 2 +- pkg/coredata/scim_bridge.go | 6 +----- pkg/coredata/statement_of_applicability.go | 16 +++++----------- pkg/coredata/webhook_subscription.go | 6 +----- 22 files changed, 34 insertions(+), 81 deletions(-) diff --git a/pkg/coredata/access_review_campaign.go b/pkg/coredata/access_review_campaign.go index dab2f9944..5ddf72031 100644 --- a/pkg/coredata/access_review_campaign.go +++ b/pkg/coredata/access_review_campaign.go @@ -233,15 +233,11 @@ WHERE %s AND id = @id args := pgx.StrictNamedArgs{"id": c.ID} maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete access_review_campaign: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/applicability_statement.go b/pkg/coredata/applicability_statement.go index 86d601f2a..939cf2d7d 100644 --- a/pkg/coredata/applicability_statement.go +++ b/pkg/coredata/applicability_statement.go @@ -250,9 +250,8 @@ VALUES ( _, err := conn.Exec(ctx, q, args) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "states_of_applicability_contr_state_of_applicability_id_con_key" { return ErrResourceAlreadyExists } } @@ -382,15 +381,11 @@ WHERE args := pgx.StrictNamedArgs{"id": applicabilityStatementID} maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete applicability statement: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/compliance_framework.go b/pkg/coredata/compliance_framework.go index c71b5866f..d121cf1af 100644 --- a/pkg/coredata/compliance_framework.go +++ b/pkg/coredata/compliance_framework.go @@ -212,9 +212,8 @@ RETURNING rank; err := conn.QueryRow(ctx, q, args).Scan(&c.Rank) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "compliance_frameworks_trust_center_id_framework_id_key" { return ErrResourceAlreadyExists } } diff --git a/pkg/coredata/connector.go b/pkg/coredata/connector.go index ba3d8394b..a240a6180 100644 --- a/pkg/coredata/connector.go +++ b/pkg/coredata/connector.go @@ -294,15 +294,11 @@ WHERE %s AND id = @id args := pgx.StrictNamedArgs{"id": c.ID} maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete connector: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/document_version_approval_decision.go b/pkg/coredata/document_version_approval_decision.go index e30cfe6da..058f49059 100644 --- a/pkg/coredata/document_version_approval_decision.go +++ b/pkg/coredata/document_version_approval_decision.go @@ -305,9 +305,8 @@ INSERT INTO document_version_approval_decisions ( _, err := conn.Exec(ctx, q, args) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "document_version_approval_decisions_quorum_id_approver_id_key" { return ErrResourceAlreadyExists } } diff --git a/pkg/coredata/document_version_approval_quorum.go b/pkg/coredata/document_version_approval_quorum.go index 847798d16..177a9a496 100644 --- a/pkg/coredata/document_version_approval_quorum.go +++ b/pkg/coredata/document_version_approval_quorum.go @@ -290,9 +290,8 @@ INSERT INTO document_version_approval_quorums ( _, err := conn.Exec(ctx, query, args) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "document_one_pending_quorum_idx" { return ErrResourceAlreadyExists } } diff --git a/pkg/coredata/mailing_list_subscriber.go b/pkg/coredata/mailing_list_subscriber.go index b078d70bb..ab6e79b5b 100644 --- a/pkg/coredata/mailing_list_subscriber.go +++ b/pkg/coredata/mailing_list_subscriber.go @@ -214,7 +214,7 @@ VALUES ( } if _, err := conn.Exec(ctx, q, args); err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "mailing_list_subscribers_mailing_list_id_email_key" { return ErrResourceAlreadyExists } @@ -278,15 +278,11 @@ WHERE args := pgx.StrictNamedArgs{"mailing_list_subscriber_id": cns.ID} maps.Copy(args, scope.SQLArguments()) - tag, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete mailing list subscriber: %w", err) } - if tag.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/mailing_list_update.go b/pkg/coredata/mailing_list_update.go index ab4b51934..bc0510221 100644 --- a/pkg/coredata/mailing_list_update.go +++ b/pkg/coredata/mailing_list_update.go @@ -158,15 +158,11 @@ WHERE } maps.Copy(args, scope.SQLArguments()) - tag, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete mailing list update: %w", err) } - if tag.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/membership.go b/pkg/coredata/membership.go index cd2e6f09e..d1d445bde 100644 --- a/pkg/coredata/membership.go +++ b/pkg/coredata/membership.go @@ -138,8 +138,7 @@ VALUES ( result, err := conn.Exec(ctx, query, args) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "authz_memberships_user_id_organization_id_key" { return ErrResourceAlreadyExists } @@ -339,15 +338,11 @@ WHERE } maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, query, args) + _, err := conn.Exec(ctx, query, args) if err != nil { return fmt.Errorf("cannot delete membership: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/membership_profile.go b/pkg/coredata/membership_profile.go index a61d7eb29..84fd86e04 100644 --- a/pkg/coredata/membership_profile.go +++ b/pkg/coredata/membership_profile.go @@ -1219,7 +1219,7 @@ VALUES ( _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "idx_profiles_identity_id_organization_id" { return ErrResourceAlreadyExists } @@ -1409,14 +1409,10 @@ WHERE args := pgx.StrictNamedArgs{"profile_id": profileID} maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete profile: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/oauth2_consent.go b/pkg/coredata/oauth2_consent.go index 4a4cac90d..bbd98a12c 100644 --- a/pkg/coredata/oauth2_consent.go +++ b/pkg/coredata/oauth2_consent.go @@ -367,7 +367,7 @@ INSERT INTO iam_oauth2_consents ( _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "iam_oauth2_consents_pkey" { return ErrResourceAlreadyExists } diff --git a/pkg/coredata/risk_assessment.go b/pkg/coredata/risk_assessment.go index 0bd508806..4a869a1c7 100644 --- a/pkg/coredata/risk_assessment.go +++ b/pkg/coredata/risk_assessment.go @@ -191,7 +191,7 @@ VALUES (@id, @tenant_id, @organization_id, @name, @description, @created_at, @up _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessments_pkey" { return ErrResourceAlreadyExists } return fmt.Errorf("cannot insert risk assessment: %w", err) diff --git a/pkg/coredata/risk_assessment_node.go b/pkg/coredata/risk_assessment_node.go index 92ab24ef4..394edb768 100644 --- a/pkg/coredata/risk_assessment_node.go +++ b/pkg/coredata/risk_assessment_node.go @@ -242,7 +242,7 @@ INSERT INTO risk_assessment_nodes ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + 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) diff --git a/pkg/coredata/risk_assessment_process.go b/pkg/coredata/risk_assessment_process.go index 1dd23a262..b739cedd8 100644 --- a/pkg/coredata/risk_assessment_process.go +++ b/pkg/coredata/risk_assessment_process.go @@ -248,7 +248,7 @@ INSERT INTO risk_assessment_processes ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + 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) diff --git a/pkg/coredata/risk_assessment_scenario.go b/pkg/coredata/risk_assessment_scenario.go index 80247a4e3..b2483f5ba 100644 --- a/pkg/coredata/risk_assessment_scenario.go +++ b/pkg/coredata/risk_assessment_scenario.go @@ -351,7 +351,7 @@ INSERT INTO risk_assessment_scenarios ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_scenarios_pkey" { return ErrResourceAlreadyExists } return fmt.Errorf("cannot insert risk scenario: %w", err) diff --git a/pkg/coredata/risk_assessment_scenario_risk.go b/pkg/coredata/risk_assessment_scenario_risk.go index 653472416..b376bf6c2 100644 --- a/pkg/coredata/risk_assessment_scenario_risk.go +++ b/pkg/coredata/risk_assessment_scenario_risk.go @@ -60,7 +60,7 @@ INSERT INTO risk_assessment_scenario_risks ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + 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) diff --git a/pkg/coredata/risk_assessment_scenario_threat.go b/pkg/coredata/risk_assessment_scenario_threat.go index df3d8448e..3d4a60954 100644 --- a/pkg/coredata/risk_assessment_scenario_threat.go +++ b/pkg/coredata/risk_assessment_scenario_threat.go @@ -60,7 +60,7 @@ INSERT INTO risk_assessment_scenario_threats ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + 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) diff --git a/pkg/coredata/risk_assessment_scope.go b/pkg/coredata/risk_assessment_scope.go index 926751f09..c9e224ea7 100644 --- a/pkg/coredata/risk_assessment_scope.go +++ b/pkg/coredata/risk_assessment_scope.go @@ -196,7 +196,7 @@ INSERT INTO risk_assessment_scopes ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" && pgErr.ConstraintName == "risk_assessment_scopes_pkey" { return ErrResourceAlreadyExists } return fmt.Errorf("cannot insert risk assessment scope: %w", err) diff --git a/pkg/coredata/risk_assessment_threat.go b/pkg/coredata/risk_assessment_threat.go index 8860b6d83..fa4de6d95 100644 --- a/pkg/coredata/risk_assessment_threat.go +++ b/pkg/coredata/risk_assessment_threat.go @@ -248,7 +248,7 @@ INSERT INTO risk_assessment_threats ( } _, err := conn.Exec(ctx, q, args) if err != nil { - if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok && pgErr.Code == "23505" { + 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) diff --git a/pkg/coredata/scim_bridge.go b/pkg/coredata/scim_bridge.go index a2eb442f3..10c7097f6 100644 --- a/pkg/coredata/scim_bridge.go +++ b/pkg/coredata/scim_bridge.go @@ -436,14 +436,10 @@ WHERE args := pgx.StrictNamedArgs{"id": s.ID} maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete scim_bridge: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/statement_of_applicability.go b/pkg/coredata/statement_of_applicability.go index f2cc08133..6cd0915eb 100644 --- a/pkg/coredata/statement_of_applicability.go +++ b/pkg/coredata/statement_of_applicability.go @@ -227,9 +227,8 @@ VALUES ( _, err := conn.Exec(ctx, q, args) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "statements_of_applicability_document_id_key" { return ErrResourceAlreadyExists } } @@ -268,9 +267,8 @@ WHERE result, err := conn.Exec(ctx, q, args) if err != nil { - var pgErr *pgconn.PgError - if errors.As(err, &pgErr) { - if pgErr.Code == "23505" { + if pgErr, ok := errors.AsType[*pgconn.PgError](err); ok { + if pgErr.Code == "23505" && pgErr.ConstraintName == "statements_of_applicability_document_id_key" { return ErrResourceAlreadyExists } } @@ -303,15 +301,11 @@ WHERE } maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete statement_of_applicability: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil } diff --git a/pkg/coredata/webhook_subscription.go b/pkg/coredata/webhook_subscription.go index ab7d15ffc..32050081d 100644 --- a/pkg/coredata/webhook_subscription.go +++ b/pkg/coredata/webhook_subscription.go @@ -402,14 +402,10 @@ WHERE %s } maps.Copy(args, scope.SQLArguments()) - result, err := conn.Exec(ctx, q, args) + _, err := conn.Exec(ctx, q, args) if err != nil { return fmt.Errorf("cannot delete webhook subscription: %w", err) } - if result.RowsAffected() == 0 { - return ErrResourceNotFound - } - return nil }