Files
shankar0123 21aeed4f4e legal: addlicense headers + normalize legacy variants (Phase 0 RED-4)
Phase 0 closure (Path B2, post-rewrite):

addlicense sweep — adds the canonical certctl LLC copyright + BUSL-1.1
SPDX header to every production Go file. Template:

  // Copyright 2026 certctl LLC. All rights reserved.
  // SPDX-License-Identifier: BUSL-1.1

Coverage: 338 / 338 production Go files (cmd/ + internal/, excluding
*_test.go and **/testdata/**). Pre-sweep coverage was 22 / 338 (6.5%);
post-sweep is 338 / 338 (100%).

Normalized 22 pre-existing legacy headers (`// Copyright (c) certctl`
+ `// SPDX-License-Identifier: BSL-1.1`) and 1 file using a
`Certctl Contributors` attribution. The legacy SPDX ID `BSL-1.1`
is non-standard; the official SPDX identifier for Business Source
License 1.1 is `BUSL-1.1` (capital U). All 338 files now share the
canonical form.

Generated via:
  addlicense -c "certctl LLC" -y 2026 \
    -f cowork/legal/copyright-header.tpl \
    -ignore '**/testdata/**' -ignore '**/*_test.go' \
    cmd/ internal/

Verification:
  find cmd internal -name '*.go' -not -name '*_test.go' \
    -not -path '*/testdata/*' \
    -exec grep -L '^// Copyright 2026 certctl LLC' {} \; | wc -l

  Returns: 0

gofmt clean. Header additions are comments only, no compile impact.

Closes: cowork/certctl-architecture-diligence-audit.html#fix-RED-4
2026-05-13 21:23:35 +00:00

79 lines
3.6 KiB
Go

// Copyright 2026 certctl LLC. All rights reserved.
// SPDX-License-Identifier: BUSL-1.1
// Package repository defines the repository-layer error sentinels that
// handlers map to HTTP status codes via errors.Is.
//
// S-2 closure (cat-s6-efc7f6f6bd50): pre-S-2 every handler-side
// not-found dispatch was a `strings.Contains(err.Error(), "not found")`
// site (30+ across internal/api/handler/*.go), brittle to any
// repository-layer message change and untyped against the actual
// failure mode. Post-S-2 the dispatch is type-checked: repositories
// wrap sql.ErrNoRows via fmt.Errorf("...: %w", repository.ErrNotFound)
// and FK constraint violations via repository.ErrForeignKeyConstraint;
// handlers consume via errors.Is. The substring matching is preserved
// at the lib/pq boundary inside `errors.go::isFKError` because the
// PostgreSQL driver returns un-typed *pq.Error values whose codes are
// the canonical signal — but it's confined to one helper rather than
// scattered across every handler file. See unified-audit.md
// cat-s6-efc7f6f6bd50 for the closure rationale.
package repository
import (
"errors"
"strings"
)
// ErrNotFound is the canonical sentinel for repository methods that
// return after sql.ErrNoRows (or its wrapped form). Handlers that
// surface a 404 should `errors.Is(err, repository.ErrNotFound)`
// rather than substring-match.
var ErrNotFound = errors.New("repository: row not found")
// ErrAlreadyExists is the canonical sentinel for postgres unique-
// constraint (SQLSTATE 23505) violations bubbling up from an INSERT
// (or partial-unique INSERT, like Rank 7's idx_approval_pending_per_job
// which enforces "at most one pending approval per job"). Handlers
// that surface a 409 Conflict should
// `errors.Is(err, repository.ErrAlreadyExists)`.
//
// The repo also reuses ErrAlreadyExists for "row is already terminal"
// state-transition attempts (e.g., Approve called on an already-
// approved request) — semantically the same "you're trying to create
// a state that already exists" failure mode.
var ErrAlreadyExists = errors.New("repository: row already exists")
// ErrForeignKeyConstraint is the canonical sentinel for PostgreSQL
// FK / RESTRICT violations bubbling up from a DELETE or UPDATE.
// Handlers that surface a 409 Conflict should
// `errors.Is(err, repository.ErrForeignKeyConstraint)`.
//
// The B-1 closure introduced ErrRenewalPolicyInUse as the per-entity
// FK sentinel for renewal_policies; future per-entity FK sentinels
// (ErrIssuerInUse, ErrTeamInUse, ErrOwnerInUse) can wrap this generic
// one via fmt.Errorf("...: %w", ErrForeignKeyConstraint) so handlers
// can choose between generic-409 and entity-specific 409 dispatch.
var ErrForeignKeyConstraint = errors.New("repository: foreign key constraint violation")
// IsForeignKeyError detects PostgreSQL FK violation errors from the
// lib/pq driver via the canonical error-text patterns it emits. The
// substring matching is intentionally confined to this helper —
// callers should use this once at the repo layer to wrap into the
// typed ErrForeignKeyConstraint sentinel, then handlers consume via
// errors.Is.
//
// Patterns recognised:
// - "violates foreign key constraint" (the standard PG message)
// - "violates restrict" / "RESTRICT" (DELETE blocked by ON DELETE RESTRICT)
//
// Returns false for nil err so callers can defensively chain it.
func IsForeignKeyError(err error) bool {
if err == nil {
return false
}
msg := err.Error()
return strings.Contains(msg, "violates foreign key") ||
strings.Contains(msg, "RESTRICT") ||
strings.Contains(msg, "violates restrict")
}