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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>