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>
This commit is contained in:
Cursor Agent
2026-07-20 15:41:29 +00:00
parent a2b29b3d80
commit 0870e1dd15
4 changed files with 46 additions and 192 deletions

View File

@@ -20,80 +20,10 @@
package accessreview
import (
"errors"
"fmt"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
)
import "errors"
var (
ErrCampaignNoScopeSources = errors.New("cannot start campaign: no scope sources configured")
ErrCampaignInvalidStatus = errors.New("campaign status does not allow this operation")
ErrCampaignNotDraft = errors.New("campaign must be in draft status")
ErrCampaignSourceOrganizationMismatch = errors.New("access source does not belong to the same organization")
)
type CampaignInvalidStatusError struct {
Operation string
Status coredata.AccessReviewCampaignStatus
Expected coredata.AccessReviewCampaignStatus
}
func (e *CampaignInvalidStatusError) Error() string {
switch e.Operation {
case "add scope source", "remove scope source":
return fmt.Sprintf(
"cannot %s: campaign status is %s, expected %s",
e.Operation,
e.Status,
e.Expected,
)
default:
return fmt.Sprintf(
"cannot %s campaign: status is %s, expected %s",
e.Operation,
e.Status,
e.Expected,
)
}
}
func (e *CampaignInvalidStatusError) Is(target error) bool {
return target == ErrCampaignInvalidStatus
}
type CampaignSourceOrganizationMismatchError struct {
Operation string
SourceID gid.GID
}
func (e *CampaignSourceOrganizationMismatchError) Error() string {
switch e.Operation {
case "create":
return fmt.Sprintf(
"cannot create campaign: access source %s does not belong to the same organization",
e.SourceID,
)
case "update":
return fmt.Sprintf(
"cannot update campaign: access source %s does not belong to the same organization",
e.SourceID,
)
default:
return fmt.Sprintf(
"cannot add scope source: access source %q does not belong to the same organization",
e.SourceID,
)
}
}
func (e *CampaignSourceOrganizationMismatchError) Is(target error) bool {
return target == ErrCampaignSourceOrganizationMismatch
}
func IsCampaignClientError(err error) bool {
return errors.Is(err, ErrCampaignNoScopeSources) ||
errors.Is(err, ErrCampaignInvalidStatus) ||
errors.Is(err, ErrCampaignSourceOrganizationMismatch)
}