mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 16:01:30 +00:00
refactor(handler,repo): replace strings.Contains error dispatch with typed sentinels (S-2)
Closes one 2026-04-24 audit finding (P2):
- cat-s6-efc7f6f6bd50: 30 strings.Contains(err.Error(), ...) sites
in internal/api/handler/ — brittle to repository-layer message
changes, untyped against the actual failure mode.
Approach (Option B from prompt design notes):
- New typed sentinels in internal/repository/errors.go:
ErrNotFound, ErrForeignKeyConstraint
IsForeignKeyError(err) helper (the only place substring
matching at the lib/pq boundary is allowed; isolates the
DB-driver string knowledge to one function).
- New typed sentinel in internal/domain/errors.go:
ErrValidation (reserved for future per-entity validation
wrappers; not yet used by all handlers).
- 49 sites in internal/repository/postgres/*.go updated to wrap
sql.ErrNoRows-derived errors via fmt.Errorf("...: %w",
repository.ErrNotFound).
- 18 not-found handler sites + 2 FK-constraint handler sites
refactored to errors.Is(err, repository.ErrNotFound) /
repository.IsForeignKeyError(err).
- 23 inline `fmt.Errorf("X not found")` test fixtures across
handler tests rewrapped to wrap repository.ErrNotFound.
- test_utils.go::ErrMockNotFound rewrapped to wrap
repository.ErrNotFound; renewal_policy.go closure docblock
updated to reflect the new convention.
- integration test mockJobRepository.Get wraps repository.ErrNotFound.
CI regression guardrail:
- .github/workflows/ci.yml::"Forbidden strings.Contains(err.Error())
regression guard (S-2)" greps for the three patterns ("not found",
"violates foreign key", "RESTRICT") under internal/api/handler/
and fails the build on regression.
Verification:
- go build ./... — clean
- go vet ./... — clean
- go test ./... -short -count=1 — all packages pass (handler +
repository + service + integration)
- golangci-lint v2.11.4 run ./... — 0 issues
- S-2 guardrail dry-run on post-fix tree → empty (good)
- All sibling guardrails (S-1, G-3, D-1+D-2, B-1, L-1, H-1, C-1, F-1, P-1) pass
Audit findings closed:
- cat-s6-efc7f6f6bd50 (P2)
Deferred follow-ups:
- 6 domain-specific substring patterns still inline in handlers
("cannot approve", "cannot reject", "cannot be parsed",
"no certificates found", "challenge password", "invalid"/
"required" validation chains in profiles + agent_groups). Each
needs its own typed sentinel, scoped per service. Documented
by the S-2 CI guardrail's allowlist for closure-comments only.
- Per-entity not-found sentinels (Option A — ErrCertificateNotFound,
ErrAgentNotFound, etc.) deferred. Generic ErrNotFound covers the
current dispatch needs; per-entity precision would let handlers
return entity-aware error bodies without a domain.Type field,
but not blocking.
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// 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")
|
||||
|
||||
// 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")
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -73,7 +74,7 @@ func (r *AgentRepository) Get(ctx context.Context, id string) (*domain.Agent, er
|
||||
agent, err := scanAgent(row)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("agent not found")
|
||||
return nil, fmt.Errorf("agent not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query agent: %w", err)
|
||||
}
|
||||
@@ -170,7 +171,7 @@ func (r *AgentRepository) Update(ctx context.Context, agent *domain.Agent) error
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("agent not found")
|
||||
return fmt.Errorf("agent not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -190,7 +191,7 @@ func (r *AgentRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("agent not found")
|
||||
return fmt.Errorf("agent not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -237,7 +238,7 @@ func (r *AgentRepository) UpdateHeartbeat(ctx context.Context, id string, metada
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("agent not found")
|
||||
return fmt.Errorf("agent not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -259,7 +260,7 @@ func (r *AgentRepository) GetByAPIKey(ctx context.Context, keyHash string) (*dom
|
||||
agent, err := scanAgent(row)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("agent not found")
|
||||
return nil, fmt.Errorf("agent not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query agent: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -50,7 +51,7 @@ func (r *AgentGroupRepository) Get(ctx context.Context, id string) (*domain.Agen
|
||||
err := row.Scan(&g.ID, &g.Name, &g.Description, &g.MatchOS, &g.MatchArchitecture,
|
||||
&g.MatchIPCIDR, &g.MatchVersion, &g.Enabled, &g.CreatedAt, &g.UpdatedAt)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("agent group not found: %s", id)
|
||||
return nil, fmt.Errorf("agent group not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get agent group: %w", err)
|
||||
@@ -84,7 +85,7 @@ func (r *AgentGroupRepository) Update(ctx context.Context, group *domain.AgentGr
|
||||
}
|
||||
rows, _ := result.RowsAffected()
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("agent group not found: %s", group.ID)
|
||||
return fmt.Errorf("agent group not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -97,7 +98,7 @@ func (r *AgentGroupRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
rows, _ := result.RowsAffected()
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("agent group not found: %s", id)
|
||||
return fmt.Errorf("agent group not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ func (r *CertificateRepository) Get(ctx context.Context, id string) (*domain.Man
|
||||
cert, err := r.scanCertificate(ctx, row)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("certificate not found")
|
||||
return nil, fmt.Errorf("certificate not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query certificate: %w", err)
|
||||
}
|
||||
@@ -397,7 +397,7 @@ func (r *CertificateRepository) Update(ctx context.Context, cert *domain.Managed
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("certificate not found")
|
||||
return fmt.Errorf("certificate not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -419,7 +419,7 @@ func (r *CertificateRepository) Archive(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("certificate not found")
|
||||
return fmt.Errorf("certificate not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -62,7 +62,7 @@ func (r *DiscoveryRepository) GetScan(ctx context.Context, id string) (*domain.D
|
||||
&scan.ScanDurationMs, &scan.StartedAt, &scan.CompletedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("discovery scan not found: %s", id)
|
||||
return nil, fmt.Errorf("discovery scan not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get discovery scan: %w", err)
|
||||
@@ -190,7 +190,7 @@ func (r *DiscoveryRepository) GetDiscovered(ctx context.Context, id string) (*do
|
||||
&cert.CreatedAt, &cert.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("discovered certificate not found: %s", id)
|
||||
return nil, fmt.Errorf("discovered certificate not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get discovered certificate: %w", err)
|
||||
@@ -317,7 +317,7 @@ func (r *DiscoveryRepository) UpdateDiscoveredStatus(ctx context.Context, id str
|
||||
}
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("discovered certificate not found: %s", id)
|
||||
return fmt.Errorf("discovered certificate not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (r *HealthCheckRepository) Get(ctx context.Context, id string) (*domain.End
|
||||
&check.CreatedAt, &check.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("health check not found: %s", id)
|
||||
return nil, fmt.Errorf("health check not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get health check: %w", err)
|
||||
@@ -299,7 +299,7 @@ func (r *HealthCheckRepository) GetByEndpoint(ctx context.Context, endpoint stri
|
||||
&check.CreatedAt, &check.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("health check not found for endpoint: %s", endpoint)
|
||||
return nil, fmt.Errorf("health check not found for endpoint: %w", repository.ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get health check by endpoint: %w", err)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -69,7 +70,7 @@ func (r *IssuerRepository) Get(ctx context.Context, id string) (*domain.Issuer,
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("issuer not found")
|
||||
return nil, fmt.Errorf("issuer not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query issuer: %w", err)
|
||||
}
|
||||
@@ -169,7 +170,7 @@ func (r *IssuerRepository) Update(ctx context.Context, issuer *domain.Issuer) er
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("issuer not found")
|
||||
return fmt.Errorf("issuer not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -189,7 +190,7 @@ func (r *IssuerRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("issuer not found")
|
||||
return fmt.Errorf("issuer not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -62,7 +63,7 @@ func (r *JobRepository) Get(ctx context.Context, id string) (*domain.Job, error)
|
||||
job, err := scanJob(row)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("job not found")
|
||||
return nil, fmt.Errorf("job not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query job: %w", err)
|
||||
}
|
||||
@@ -123,7 +124,7 @@ func (r *JobRepository) Update(ctx context.Context, job *domain.Job) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("job not found")
|
||||
return fmt.Errorf("job not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -143,7 +144,7 @@ func (r *JobRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("job not found")
|
||||
return fmt.Errorf("job not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -232,7 +233,7 @@ func (r *JobRepository) UpdateStatus(ctx context.Context, id string, status doma
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("job not found")
|
||||
return fmt.Errorf("job not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -68,7 +69,7 @@ func (r *NetworkScanRepository) Get(ctx context.Context, id string) (*domain.Net
|
||||
&target.CreatedAt, &target.UpdatedAt,
|
||||
)
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("network scan target not found: %s", id)
|
||||
return nil, fmt.Errorf("network scan target not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get network scan target: %w", err)
|
||||
@@ -117,7 +118,7 @@ func (r *NetworkScanRepository) Update(ctx context.Context, target *domain.Netwo
|
||||
}
|
||||
rows, _ := result.RowsAffected()
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("network scan target not found: %s", target.ID)
|
||||
return fmt.Errorf("network scan target not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -130,7 +131,7 @@ func (r *NetworkScanRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
rows, _ := result.RowsAffected()
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("network scan target not found: %s", id)
|
||||
return fmt.Errorf("network scan target not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ func (r *NotificationRepository) UpdateStatus(ctx context.Context, id string, st
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("notification not found")
|
||||
return fmt.Errorf("notification not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -336,7 +336,7 @@ func (r *NotificationRepository) RecordFailedAttempt(ctx context.Context, id str
|
||||
// Same "not found" error shape as UpdateStatus above. The scheduler
|
||||
// logs-and-continues on this so a concurrently-deleted row doesn't
|
||||
// break the sweep.
|
||||
return fmt.Errorf("notification not found")
|
||||
return fmt.Errorf("notification not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -368,7 +368,7 @@ func (r *NotificationRepository) MarkAsDead(ctx context.Context, id string, last
|
||||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||||
}
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("notification not found")
|
||||
return fmt.Errorf("notification not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -405,7 +405,7 @@ func (r *NotificationRepository) Requeue(ctx context.Context, id string) error {
|
||||
return fmt.Errorf("failed to get rows affected: %w", err)
|
||||
}
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("notification not found")
|
||||
return fmt.Errorf("notification not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -61,7 +62,7 @@ func (r *OwnerRepository) Get(ctx context.Context, id string) (*domain.Owner, er
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("owner not found")
|
||||
return nil, fmt.Errorf("owner not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query owner: %w", err)
|
||||
}
|
||||
@@ -110,7 +111,7 @@ func (r *OwnerRepository) Update(ctx context.Context, owner *domain.Owner) error
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("owner not found")
|
||||
return fmt.Errorf("owner not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -130,7 +131,7 @@ func (r *OwnerRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("owner not found")
|
||||
return fmt.Errorf("owner not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -63,7 +63,7 @@ func (r *PolicyRepository) GetRule(ctx context.Context, id string) (*domain.Poli
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("policy rule not found")
|
||||
return nil, fmt.Errorf("policy rule not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query policy rule: %w", err)
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func (r *PolicyRepository) UpdateRule(ctx context.Context, rule *domain.PolicyRu
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("policy rule not found")
|
||||
return fmt.Errorf("policy rule not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -134,7 +134,7 @@ func (r *PolicyRepository) DeleteRule(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("policy rule not found")
|
||||
return fmt.Errorf("policy rule not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
@@ -64,7 +65,7 @@ func (r *ProfileRepository) Get(ctx context.Context, id string) (*domain.Certifi
|
||||
p, err := scanProfile(row)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("profile not found")
|
||||
return nil, fmt.Errorf("profile not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query profile: %w", err)
|
||||
}
|
||||
@@ -159,7 +160,7 @@ func (r *ProfileRepository) Update(ctx context.Context, profile *domain.Certific
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("profile not found")
|
||||
return fmt.Errorf("profile not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -178,7 +179,7 @@ func (r *ProfileRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("profile not found")
|
||||
return fmt.Errorf("profile not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -72,7 +72,7 @@ func (r *RenewalPolicyRepository) Get(ctx context.Context, id string) (*domain.R
|
||||
policy, err := scanRenewalPolicy(row)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, fmt.Errorf("renewal policy not found: %s", id)
|
||||
return nil, fmt.Errorf("renewal policy not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query renewal policy: %w", err)
|
||||
}
|
||||
@@ -252,7 +252,7 @@ func (r *RenewalPolicyRepository) Update(ctx context.Context, id string, policy
|
||||
updated, err := scanRenewalPolicy(row)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return fmt.Errorf("renewal policy not found: %s", id)
|
||||
return fmt.Errorf("renewal policy not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
if isUniqueViolation(err) {
|
||||
return repository.ErrRenewalPolicyDuplicateName
|
||||
@@ -283,7 +283,7 @@ func (r *RenewalPolicyRepository) Delete(ctx context.Context, id string) error {
|
||||
return fmt.Errorf("failed to read RowsAffected for delete: %w", err)
|
||||
}
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("renewal policy not found: %s", id)
|
||||
return fmt.Errorf("renewal policy not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -136,7 +137,7 @@ func (r *RevocationRepository) MarkIssuerNotified(ctx context.Context, id string
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("revocation not found")
|
||||
return fmt.Errorf("revocation not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -89,7 +90,7 @@ func (r *TargetRepository) Get(ctx context.Context, id string) (*domain.Deployme
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("target not found")
|
||||
return nil, fmt.Errorf("target not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query target: %w", err)
|
||||
}
|
||||
@@ -174,7 +175,7 @@ func (r *TargetRepository) Update(ctx context.Context, target *domain.Deployment
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("target not found")
|
||||
return fmt.Errorf("target not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -194,7 +195,7 @@ func (r *TargetRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("target not found")
|
||||
return fmt.Errorf("target not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"github.com/shankar0123/certctl/internal/repository"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -61,7 +62,7 @@ func (r *TeamRepository) Get(ctx context.Context, id string) (*domain.Team, erro
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, fmt.Errorf("team not found")
|
||||
return nil, fmt.Errorf("team not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to query team: %w", err)
|
||||
}
|
||||
@@ -108,7 +109,7 @@ func (r *TeamRepository) Update(ctx context.Context, team *domain.Team) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("team not found")
|
||||
return fmt.Errorf("team not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -128,7 +129,7 @@ func (r *TeamRepository) Delete(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("team not found")
|
||||
return fmt.Errorf("team not found: %w", repository.ErrNotFound)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user