Commit Graph

15 Commits

Author SHA1 Message Date
Cursor Agent
b229da0115 Block delete for in-progress access review campaigns
Reject deletion while a campaign is fetching sources so workers are
not racing a removed record. All other statuses remain deletable.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 16:02:26 +00:00
Cursor Agent
b156b0333a Allow deleting access review campaigns in any status
Drop the backend status gate on campaign delete and show delete in
the console whenever the user has delete permission, regardless of
whether the campaign is draft, in progress, or completed.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 16:02:26 +00:00
Cursor Agent
9c1cecfc1e Allow deleting completed access review campaigns
Completed campaigns were immutable because delete only accepted draft
or cancelled status. Extend deletion to completed records and surface
delete in the console for that status.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-29 16:02:26 +00:00
Cursor Agent
3f545652d5 Use business campaign rules instead of status errors
Introduce accessreview.Campaign with draft/deletable predicates and
operation-specific client errors. Drop errUnlessDraftCampaign and
status-to-sentinel switches in the service layer.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
5dca6bb883 Use struct campaign errors with campaign ID in message
Business-layer errors carry the access review campaign GID in
Error() and match GraphQL sentinels via Is().

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
352c551e53 Return campaign status sentinels without wrapping
Drop CampaignStatusError from errors.go and map coredata status to
client errors in the service layer only. Cancel on terminal statuses
uses the same sentinels instead of fmt status strings.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
c187ea7124 Remove CampaignStatusErrors slice
List campaign status sentinels explicitly at resolver AnyOf
call sites instead of a shared slice.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
f8dcb6c014 Add errorx.AnyOf for sentinel error checks
Centralize errors.Is OR chains in access review campaign resolvers
and expose CampaignStatusErrors for the four non-draft status
sentinels.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
b3e60da4d5 Use status-specific errors for non-draft campaigns
Replace ErrCampaignCannotStart and ErrCampaignCannotUpdate with
sentinels per campaign status, mapped through CampaignStatusError
and wrapped with operation-specific fmt.Errorf prefixes.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
3d606dd0e8 Replace ErrCampaignNotDraft with operation-specific errors
Use ErrCampaignCannotStart and ErrCampaignCannotUpdate with clear
actionable messages instead of a generic draft-status sentinel
wrapped in noisy fmt.Errorf chains.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
eabd0fb659 Rename ErrCampaignNoSourcesSelected to ErrCampaignMissingSources
The failure is about unconfigured campaign sources, not an empty UI
selection.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
eebc957d36 Treat invalid campaign sources as not found
Drop ErrCampaignSourceOrganizationMismatch. When a source ID is
missing or belongs to another organization, return
coredata.ErrResourceNotFound so clients get a generic not-found
response instead of leaking cross-organization details.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
1dcfa31dab Rename ErrCampaignNoScopeSources to ErrCampaignNoSourcesSelected
Align the sentinel name with the validation it represents.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
0870e1dd15 Use sentinel errors for campaign validation failures
Follow the cookiebanner pattern: grouped var Err* sentinels in the
service package, wrapped with fmt.Errorf where context is needed, and
explicit errors.Is checks in GraphQL resolvers.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00
Cursor Agent
a2b29b3d80 Use typed campaign errors instead of string matching
Introduce sentinel and structured errors for access review campaign
validation failures, and map them to INVALID in GraphQL resolvers via
errors.Is rather than matching error message prefixes.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-07-27 15:56:07 +00:00