Remove platform tenancy from SXCHNGE customer BFF (#34)

This commit is contained in:
Stephan D committed 2026-09-12 14:00:14 +02:00
1 parent 7062672a87
commit 981e84a4b4
244 files changed
+651 -17130

No files matched your search

-25
View File
@@ -1,25 +0,0 @@
package merrors
var ErrAccountingInvalidPosting = Define("accounting.invalid_posting")
var ErrAccountingUnbalancedPosting = Define("accounting.unbalanced_posting")
var ErrAccountingAssetMismatch = Define("accounting.asset_mismatch")
var ErrAccountingAccountNotFound = Define("accounting.account_not_found")
var ErrAccountingAccountInactive = Define("accounting.account_inactive")
var ErrAccountingInvalidAmount = Define("accounting.invalid_amount")
var ErrAccountingInvalidIdempotency = Define("accounting.invalid_idempotency")
var ErrAccountingIdempotencyConflict = Define("accounting.idempotency_conflict")
var ErrAccountingStorageFailed = Define("accounting.storage_failed")
var ErrAccountingDuplicateKey = Define("accounting.duplicate_key")
var ErrAccountingOptimisticConflict = Define("accounting.optimistic_conflict")
var ErrAccountingInvalidStorageState = Define("accounting.invalid_storage_state")
-20
View File
@@ -1,20 +0,0 @@
package merrors
import "strings"
const ReasonAMLSourceIdempotencyConflict = "AML_SOURCE_IDEMPOTENCY_CONFLICT"
var ErrAMLSourceIdempotencyConflict = Define("aml.source_idempotency_conflict")
func AMLSourceIdempotencyConflict(msg string) error {
msg = strings.TrimSpace(msg)
if msg == "" {
msg = ErrAMLSourceIdempotencyConflict.Error()
}
return classifiedError{
base: ErrDataConflict,
code: ErrAMLSourceIdempotencyConflict,
msg: msg,
reason: ReasonAMLSourceIdempotencyConflict,
}
}
-21
View File
@@ -1,21 +0,0 @@
package merrors
import (
"errors"
"testing"
)
func TestAMLSourceIdempotencyConflictReasonRoundTrip(t *testing.T) {
err := AMLSourceIdempotencyConflict("key is bound to another source mutation")
if !errors.Is(err, ErrDataConflict) || !errors.Is(err, ErrAMLSourceIdempotencyConflict) {
t.Fatalf("AML source idempotency conflict classification mismatch: %v", err)
}
reason, ok := Reason(err)
if !ok || reason != ReasonAMLSourceIdempotencyConflict {
t.Fatalf("AML source idempotency reason mismatch: reason=%q ok=%t", reason, ok)
}
restored, ok := FromReason(reason, err.Error())
if !ok || !errors.Is(restored, ErrAMLSourceIdempotencyConflict) {
t.Fatalf("AML source idempotency reason did not round trip: %v", restored)
}
}
-21
View File
@@ -1,21 +0,0 @@
package merrors
import "strings"
const (
ReasonChainExecutionSigningUnavailable = "chain_execution.signing_unavailable"
ReasonChainExecutionUnsupportedCapability = "chain_execution.unsupported_capability"
ReasonChainExecutionInsufficientBalance = "chain_execution.insufficient_balance"
ReasonChainExecutionInsufficientNativeResources = "chain_execution.insufficient_native_resources"
ReasonChainExecutionDestinationIneligible = "chain_execution.destination_ineligible"
)
// ChainExecutionPrecondition creates a typed, reason-bearing permanent
// preparation failure. Callers can classify it with both errors.Is and Reason.
func ChainExecutionPrecondition(reason, message string) error {
return classifiedError{
base: ErrFailedPrecondition,
msg: strings.TrimSpace(message),
reason: strings.TrimSpace(reason),
}
}
@@ -1,17 +0,0 @@
package merrors
import (
"errors"
"testing"
)
func TestChainExecutionPreconditionCarriesClassAndReason(t *testing.T) {
err := ChainExecutionPrecondition(ReasonChainExecutionInsufficientBalance, "source balance is insufficient")
if !errors.Is(err, ErrFailedPrecondition) {
t.Fatal("expected failed-precondition classification")
}
reason, ok := Reason(err)
if !ok || reason != ReasonChainExecutionInsufficientBalance {
t.Fatalf("unexpected reason: %q", reason)
}
}
-44
View File
@@ -1,44 +0,0 @@
package merrors
import "strings"
const (
ReasonDocumentsReportOwned = "DOCUMENTS_REPORT_OWNED"
ReasonDocumentsQuotationExpired = "DOCUMENTS_QUOTATION_EXPIRED"
)
var ErrDocumentsReportOwned = Define("documents.report_owned")
var ErrDocumentsQuotationExpired = Define("documents.quotation_expired")
// DocumentsReportOwned reports that an active or settled payout owns a
// report, so callers must not mutate its document lifecycle.
func DocumentsReportOwned(msg string) error {
msg = strings.TrimSpace(msg)
if msg == "" {
msg = "report is owned by a payout"
}
return classifiedError{
base: ErrDataConflict,
code: ErrDocumentsReportOwned,
msg: msg,
reason: ReasonDocumentsReportOwned,
publicMessage: true,
}
}
// DocumentsQuotationExpired reports that a payout registry still references
// a quotation which can no longer be executed. A fresh quotation is required.
func DocumentsQuotationExpired(msg string) error {
msg = strings.TrimSpace(msg)
if msg == "" {
msg = "the payout quotation has expired; request a new quotation"
}
return classifiedError{
base: ErrFailedPrecondition,
code: ErrDocumentsQuotationExpired,
msg: msg,
reason: ReasonDocumentsQuotationExpired,
publicMessage: true,
}
}
-37
View File
@@ -1,37 +0,0 @@
package merrors
import (
"errors"
"testing"
)
func TestDocumentsReportOwnedReasonRoundTrip(t *testing.T) {
err := DocumentsReportOwned("report has an active payout")
if !errors.Is(err, ErrDataConflict) || !errors.Is(err, ErrDocumentsReportOwned) {
t.Fatalf("DocumentsReportOwned() classification = %v", err)
}
reason, ok := Reason(err)
if !ok || reason != ReasonDocumentsReportOwned {
t.Fatalf("Reason() = %q, %t", reason, ok)
}
restored, ok := FromReason(reason, "report has an active payout")
if !ok || !errors.Is(restored, ErrDataConflict) || !errors.Is(restored, ErrDocumentsReportOwned) {
t.Fatalf("FromReason() = %v, %t", restored, ok)
}
}
func TestDocumentsQuotationExpiredReasonRoundTrip(t *testing.T) {
err := DocumentsQuotationExpired("quotation expired")
if !errors.Is(err, ErrFailedPrecondition) || !errors.Is(err, ErrDocumentsQuotationExpired) {
t.Fatalf("DocumentsQuotationExpired() classification = %v", err)
}
reason, ok := Reason(err)
if !ok || reason != ReasonDocumentsQuotationExpired {
t.Fatalf("Reason() = %q, %t", reason, ok)
}
restored, ok := FromReason(reason, "quotation expired")
if !ok || !errors.Is(restored, ErrFailedPrecondition) ||
!errors.Is(restored, ErrDocumentsQuotationExpired) {
t.Fatalf("FromReason() = %v, %t", restored, ok)
}
}
-6
View File
@@ -223,12 +223,6 @@ func Unauthorizedf(format string, args ...any) error {
return Unauthorized(fmt.Sprintf(format, args...))
}
var ErrNoMessagingTopic = Define("messagingTopicError")
func NoMessagingTopic(topic string) error {
return Wrapf(ErrNoMessagingTopic, "messaging topic '%s' not found", topic)
}
func InvalidArgumentWrap(err error, msg string, argumentNames ...string) error {
return wrapError(ErrInvalidArg, invalidArgumentMessage(msg, argumentNames...), err)
}
-65
View File
@@ -1,65 +0,0 @@
package merrors
import "strings"
const (
ReasonLedgerIdempotencyConflict = "LEDGER_IDEMPOTENCY_CONFLICT"
ReasonLedgerCommandIdentityMismatch = "LEDGER_COMMAND_IDENTITY_MISMATCH"
ReasonLedgerAccessViolation = "LEDGER_ACCESS_VIOLATION"
ReasonAccountNotEligible = "ACCOUNT_NOT_ELIGIBLE"
)
var ErrLedgerIdempotencyConflict = Define("ledger.idempotency_conflict")
var ErrLedgerCommandIdentityMismatch = Define("ledger.command_identity_mismatch")
var ErrLedgerAccessViolation = Define("ledger.access_violation")
var ErrAccountNotEligible = Define("account_not_eligible")
func LedgerIdempotencyConflict(msg string) error {
return classifiedError{
base: ErrDataConflict,
code: ErrLedgerIdempotencyConflict,
msg: ledgerErrorMessage(msg, ErrLedgerIdempotencyConflict),
reason: ReasonLedgerIdempotencyConflict,
}
}
func LedgerCommandIdentityMismatch(msg string) error {
return classifiedError{
base: ErrDataConflict,
code: ErrLedgerCommandIdentityMismatch,
msg: ledgerErrorMessage(msg, ErrLedgerCommandIdentityMismatch),
reason: ReasonLedgerCommandIdentityMismatch,
}
}
func LedgerAccessViolation(msg string) error {
return classifiedError{
base: ErrFailedPrecondition,
code: ErrLedgerAccessViolation,
msg: ledgerErrorMessage(msg, ErrLedgerAccessViolation),
reason: ReasonLedgerAccessViolation,
}
}
func AccountNotEligible(msg string) error {
return classifiedError{
base: ErrFailedPrecondition,
code: ErrAccountNotEligible,
msg: ledgerErrorMessage(msg, ErrAccountNotEligible),
reason: ReasonAccountNotEligible,
}
}
func ledgerErrorMessage(msg string, fallback error) string {
msg = strings.TrimSpace(msg)
if msg != "" {
return msg
}
if fallback == nil {
return ""
}
return fallback.Error()
}
-48
View File
@@ -1,48 +0,0 @@
package merrors
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
)
func TestLedgerReasonRoundTrip(t *testing.T) {
err := LedgerIdempotencyConflict("conflicting command")
reason, ok := Reason(err)
require.True(t, ok)
require.Equal(t, ReasonLedgerIdempotencyConflict, reason)
restored, ok := FromReason(reason, "conflicting command")
require.True(t, ok)
require.True(t, errors.Is(restored, ErrLedgerIdempotencyConflict))
require.True(t, errors.Is(restored, ErrDataConflict))
}
func TestAccountNotEligibleReasonRoundTrip(t *testing.T) {
err := AccountNotEligible("account is frozen")
reason, ok := Reason(err)
require.True(t, ok)
require.Equal(t, ReasonAccountNotEligible, reason)
require.True(t, errors.Is(err, ErrAccountNotEligible))
require.True(t, errors.Is(err, ErrFailedPrecondition))
restored, ok := FromReason(reason, "account is frozen")
require.True(t, ok)
require.True(t, errors.Is(restored, ErrAccountNotEligible))
require.True(t, errors.Is(restored, ErrFailedPrecondition))
}
func TestLedgerAccessViolationReasonRoundTrip(t *testing.T) {
err := LedgerAccessViolation("from_account: account does not belong to organization")
reason, ok := Reason(err)
require.True(t, ok)
require.Equal(t, ReasonLedgerAccessViolation, reason)
require.True(t, errors.Is(err, ErrLedgerAccessViolation))
require.True(t, errors.Is(err, ErrFailedPrecondition))
restored, ok := FromReason(reason, "from_account: account does not belong to organization")
require.True(t, ok)
require.True(t, errors.Is(restored, ErrLedgerAccessViolation))
require.True(t, errors.Is(restored, ErrFailedPrecondition))
}
-19
View File
@@ -1,19 +0,0 @@
package merrors
var ErrLimitsRuntimeInvalidRecord = Define("limits.runtime.invalid_record")
var ErrLimitsRuntimeCASConflict = Define("limits.runtime.cas_conflict")
var ErrLimitsRuntimeDuplicateStateKey = Define("limits.runtime.duplicate_state_key")
var ErrLimitsRuntimeDuplicateStateRef = Define("limits.runtime.duplicate_state_ref")
var ErrLimitsRuntimeDuplicateReservationRef = Define("limits.runtime.duplicate_reservation_ref")
var ErrLimitsRuntimeDuplicateReservationOperation = Define("limits.runtime.duplicate_reservation_operation")
var ErrLimitsRuntimeIdentityConflict = Define("limits.runtime.identity_conflict")
var ErrLimitsRuntimeDataConflict = Define("limits.runtime.data_conflict")
var ErrLimitsRuntimeRepositoryRequired = Define("limits.runtime.repository_required")
-66
View File
@@ -1,66 +0,0 @@
package merrors
import "strings"
// Reasons for organization provisioning outcomes. They travel to callers in
// gRPC error details so every outcome stays distinguishable regardless of the
// status code it shares with others.
const (
ReasonOrganizationProvisioningInProgress = "organization.provisioning_in_progress"
ReasonOrganizationAlreadyActive = "organization.already_active"
ReasonOrganizationProvisioningBlocked = "organization.provisioning_blocked"
ReasonOrganizationOwnerMismatch = "organization.owner_mismatch"
ReasonOrganizationNotOperatorProvisioned = "organization.not_operator_provisioned"
)
// ErrOrganizationProvisioningInProgress means another attempt currently holds
// the provisioning lease of the organization.
var ErrOrganizationProvisioningInProgress = Define(ReasonOrganizationProvisioningInProgress)
// ErrOrganizationAlreadyActive means the organization finished provisioning already.
var ErrOrganizationAlreadyActive = Define(ReasonOrganizationAlreadyActive)
// ErrOrganizationProvisioningBlocked means a failed signup compensated the
// organization and it requires manual recovery before it can be activated.
var ErrOrganizationProvisioningBlocked = Define(ReasonOrganizationProvisioningBlocked)
// ErrOrganizationOwnerMismatch means the organization is being provisioned for
// a different account than the one supplied.
var ErrOrganizationOwnerMismatch = Define(ReasonOrganizationOwnerMismatch)
// ErrOrganizationNotOperatorProvisioned means the organization was created through signup:
// organizations created through general signup are never finished by someone in backoffice.
var ErrOrganizationNotOperatorProvisioned = Define(ReasonOrganizationNotOperatorProvisioned)
func OrganizationProvisioningInProgress(msg string) error {
return organizationError(ErrOrganizationProvisioningInProgress, ReasonOrganizationProvisioningInProgress, msg)
}
func OrganizationAlreadyActive(msg string) error {
return organizationError(ErrOrganizationAlreadyActive, ReasonOrganizationAlreadyActive, msg)
}
func OrganizationProvisioningBlocked(msg string) error {
return organizationError(ErrOrganizationProvisioningBlocked, ReasonOrganizationProvisioningBlocked, msg)
}
func OrganizationOwnerMismatch(msg string) error {
return organizationError(ErrOrganizationOwnerMismatch, ReasonOrganizationOwnerMismatch, msg)
}
func OrganizationNotOperatorProvisioned(msg string) error {
return organizationError(ErrOrganizationNotOperatorProvisioned, ReasonOrganizationNotOperatorProvisioned, msg)
}
func organizationError(code error, reason, msg string) error {
msg = strings.TrimSpace(msg)
if msg == "" && code != nil {
msg = code.Error()
}
return classifiedError{
base: ErrDataConflict,
code: code,
msg: msg,
reason: reason,
}
}
-9
View File
@@ -1,9 +0,0 @@
package merrors
var ErrPaymasterPaused = Define("paymaster_paused")
var ErrControlWalletInsufficientNativeBalance = Define("control_wallet_insufficient_native_balance")
var ErrPaymasterControlDurableWalletFailure = Define("paymaster_control_durable_wallet_failure")
var ErrPaymasterControlKeyActivityMismatch = Define("paymaster_control_key_activity_mismatch")
-47
View File
@@ -1,47 +0,0 @@
package merrors
import "strings"
const ReasonPaymentExecutionNotAccepted = "PAYMENT_EXECUTION_NOT_ACCEPTED"
var ErrPaymentExecutionNotAccepted = Define("payment.execution_not_accepted")
type paymentExecutionNotAcceptedError struct {
classifiedError
cause error
}
func (e paymentExecutionNotAcceptedError) Unwrap() []error {
causes := e.classifiedError.Unwrap()
if e.cause != nil {
causes = append(causes, e.cause)
}
return causes
}
// PaymentExecutionNotAccepted reports that the orchestrator proved an
// execution request did not create or reuse any payment. Callers may release
// request-side reservations only for this outcome; ordinary transport and
// internal errors have an ambiguous ownership result.
func PaymentExecutionNotAccepted(msg string) error {
return PaymentExecutionNotAcceptedWrap(nil, msg)
}
// PaymentExecutionNotAcceptedWrap adds the definitive rejection
// classification while retaining the underlying cause for diagnostics.
func PaymentExecutionNotAcceptedWrap(cause error, msg string) error {
msg = strings.TrimSpace(msg)
if msg == "" {
msg = "payment execution was not accepted before a payment was created"
}
return paymentExecutionNotAcceptedError{
classifiedError: classifiedError{
base: ErrFailedPrecondition,
code: ErrPaymentExecutionNotAccepted,
msg: msg,
reason: ReasonPaymentExecutionNotAccepted,
publicMessage: true,
},
cause: cause,
}
}
@@ -1,26 +0,0 @@
package merrors
import (
"errors"
"testing"
)
func TestPaymentExecutionNotAcceptedPreservesClassificationAndCause(t *testing.T) {
cause := ServiceUnavailable("orchestrator unavailable")
err := PaymentExecutionNotAcceptedWrap(cause, "execution was rejected")
if !errors.Is(err, ErrFailedPrecondition) ||
!errors.Is(err, ErrPaymentExecutionNotAccepted) ||
!errors.Is(err, cause) {
t.Fatalf("PaymentExecutionNotAcceptedWrap() classification = %v", err)
}
reason, ok := Reason(err)
if !ok || reason != ReasonPaymentExecutionNotAccepted {
t.Fatalf("PaymentExecutionNotAcceptedWrap() reason = %q, %t", reason, ok)
}
restored, ok := FromReason(reason, err.Error())
if !ok || !errors.Is(restored, ErrPaymentExecutionNotAccepted) ||
!errors.Is(restored, ErrFailedPrecondition) {
t.Fatalf("FromReason() = %v, %t", restored, ok)
}
}
-32
View File
@@ -1,32 +0,0 @@
package merrors
const (
QuotationFeeConfigurationMissingCode = "quotation_fee_configuration_missing"
QuotationFeeConfigurationMissingDetails = "quotation fee configuration is missing"
QuotationReplayMissingCode = "quotation_replay_missing"
QuotationReplayMissingDetails = "the expected payment quotation is no longer available"
)
var ErrQuotationFeeConfigurationMissing = Define(QuotationFeeConfigurationMissingCode)
var ErrQuotationReplayMissing = Define(QuotationReplayMissingCode)
func QuotationFeeConfigurationMissing() error {
return classifiedError{
base: ErrFailedPrecondition,
code: ErrQuotationFeeConfigurationMissing,
msg: QuotationFeeConfigurationMissingDetails,
reason: QuotationFeeConfigurationMissingCode,
publicMessage: true,
}
}
func QuotationReplayMissing() error {
return classifiedError{
base: ErrNoData,
code: ErrQuotationReplayMissing,
msg: QuotationReplayMissingDetails,
reason: QuotationReplayMissingCode,
publicMessage: true,
}
}
-44
View File
@@ -1,44 +0,0 @@
package merrors
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
)
func TestQuotationFeeConfigurationMissingReasonRoundTrip(t *testing.T) {
err := QuotationFeeConfigurationMissing()
require.True(t, errors.Is(err, ErrFailedPrecondition))
require.True(t, errors.Is(err, ErrQuotationFeeConfigurationMissing))
reason, ok := Reason(err)
require.True(t, ok)
require.Equal(t, QuotationFeeConfigurationMissingCode, reason)
require.Equal(t, QuotationFeeConfigurationMissingDetails, err.Error())
restored, ok := FromReason(reason, "ignored message")
require.True(t, ok)
require.True(t, errors.Is(restored, ErrFailedPrecondition))
require.True(t, errors.Is(restored, ErrQuotationFeeConfigurationMissing))
require.Equal(t, QuotationFeeConfigurationMissingDetails, restored.Error())
}
func TestQuotationReplayMissingReasonRoundTrip(t *testing.T) {
err := QuotationReplayMissing()
require.True(t, errors.Is(err, ErrNoData))
require.True(t, errors.Is(err, ErrQuotationReplayMissing))
reason, ok := Reason(err)
require.True(t, ok)
require.Equal(t, QuotationReplayMissingCode, reason)
require.Equal(t, QuotationReplayMissingDetails, err.Error())
restored, ok := FromReason(reason, "ignored message")
require.True(t, ok)
require.True(t, errors.Is(restored, ErrNoData))
require.True(t, errors.Is(restored, ErrQuotationReplayMissing))
require.Equal(t, QuotationReplayMissingDetails, restored.Error())
}
+1 -87
View File
@@ -15,91 +15,5 @@ func Reason(err error) (string, bool) {
return reason, true
}
}
switch {
case errors.Is(err, ErrAMLSourceIdempotencyConflict):
return ReasonAMLSourceIdempotencyConflict, true
case errors.Is(err, ErrLedgerIdempotencyConflict):
return ReasonLedgerIdempotencyConflict, true
case errors.Is(err, ErrLedgerCommandIdentityMismatch):
return ReasonLedgerCommandIdentityMismatch, true
case errors.Is(err, ErrLedgerAccessViolation):
return ReasonLedgerAccessViolation, true
case errors.Is(err, ErrAccountNotEligible):
return ReasonAccountNotEligible, true
case errors.Is(err, ErrQuotationFeeConfigurationMissing):
return QuotationFeeConfigurationMissingCode, true
case errors.Is(err, ErrQuotationReplayMissing):
return QuotationReplayMissingCode, true
default:
return "", false
}
}
func FromReason(reason string, msg string) (error, bool) {
switch strings.TrimSpace(reason) {
case ReasonRouteNotFound:
return RouteNotFound(msg), true
case ReasonRouteAmbiguous:
return RouteAmbiguous(msg), true
case ReasonRouteDisabled:
return RouteDisabled(msg), true
case ReasonRouteInvalidSource:
return RouteInvalidSource(msg), true
case ReasonRouteDestinationNotFound:
return RouteDestinationNotFound(msg), true
case ReasonRouteDestinationInactive:
return RouteDestinationInactive(msg), true
case ReasonRouteDestinationCurrencyMismatch:
return RouteDestinationCurrencyMismatch(msg), true
case ReasonRouteDestinationOrgMismatch:
return RouteDestinationOrgMismatch(msg), true
case ReasonRouteDestinationAmbiguous:
return RouteDestinationAmbiguous(msg), true
case ReasonRouteDestinationOwnerMismatch:
return RouteDestinationOwnerMismatch(msg), true
case ReasonRouteActiveSourceConflict:
return RouteActiveSourceConflict(msg), true
case ReasonRouteDestinationTypeMismatch:
return RouteDestinationTypeMismatch(msg), true
case ReasonRouteDestinationRoleMismatch:
return RouteDestinationRoleMismatch(msg), true
case ReasonRouteIdempotencyConflict:
return RouteIdempotencyConflict(msg), true
case ReasonRouteVersionConflict:
return RouteVersionConflict(msg), true
case ReasonRouteInvalidState:
return RouteInvalidState(msg), true
case ReasonAMLSourceIdempotencyConflict:
return AMLSourceIdempotencyConflict(msg), true
case ReasonLedgerIdempotencyConflict:
return LedgerIdempotencyConflict(msg), true
case ReasonLedgerCommandIdentityMismatch:
return LedgerCommandIdentityMismatch(msg), true
case ReasonLedgerAccessViolation:
return LedgerAccessViolation(msg), true
case ReasonAccountNotEligible:
return AccountNotEligible(msg), true
case ReasonDocumentsReportOwned:
return DocumentsReportOwned(msg), true
case ReasonDocumentsQuotationExpired:
return DocumentsQuotationExpired(msg), true
case ReasonPaymentExecutionNotAccepted:
return PaymentExecutionNotAccepted(msg), true
case QuotationFeeConfigurationMissingCode:
return QuotationFeeConfigurationMissing(), true
case QuotationReplayMissingCode:
return QuotationReplayMissing(), true
case ReasonOrganizationProvisioningInProgress:
return OrganizationProvisioningInProgress(msg), true
case ReasonOrganizationAlreadyActive:
return OrganizationAlreadyActive(msg), true
case ReasonOrganizationProvisioningBlocked:
return OrganizationProvisioningBlocked(msg), true
case ReasonOrganizationOwnerMismatch:
return OrganizationOwnerMismatch(msg), true
case ReasonOrganizationNotOperatorProvisioned:
return OrganizationNotOperatorProvisioned(msg), true
default:
return nil, false
}
return "", false
}
-127
View File
@@ -1,127 +0,0 @@
package merrors
import "strings"
const routeErrorCodePrefix = "ROUTE_ERROR_CODE_"
type routeMetadataError struct {
classifiedError
routeCode string
}
func (e routeMetadataError) ErrorInfoMetadata() map[string]string {
return map[string]string{"route_error_code": e.routeCode}
}
const (
ReasonRouteNotFound = "ROUTE_NOT_FOUND"
ReasonRouteAmbiguous = "ROUTE_AMBIGUOUS"
ReasonRouteDisabled = "ROUTE_DISABLED"
ReasonRouteInvalidSource = "ROUTE_INVALID_SOURCE"
ReasonRouteDestinationNotFound = "ROUTE_DESTINATION_NOT_FOUND"
ReasonRouteDestinationInactive = "ROUTE_DESTINATION_INACTIVE"
ReasonRouteDestinationCurrencyMismatch = "ROUTE_DESTINATION_CURRENCY_MISMATCH"
ReasonRouteDestinationOrgMismatch = "ROUTE_DESTINATION_ORG_MISMATCH"
ReasonRouteDestinationAmbiguous = "ROUTE_DESTINATION_AMBIGUOUS"
ReasonRouteDestinationOwnerMismatch = "ROUTE_DESTINATION_OWNER_MISMATCH"
ReasonRouteActiveSourceConflict = "ROUTE_ACTIVE_SOURCE_CONFLICT"
ReasonRouteDestinationTypeMismatch = "ROUTE_DESTINATION_TYPE_MISMATCH"
ReasonRouteDestinationRoleMismatch = "ROUTE_DESTINATION_ROLE_MISMATCH"
ReasonRouteIdempotencyConflict = "ROUTE_IDEMPOTENCY_CONFLICT"
ReasonRouteVersionConflict = "ROUTE_VERSION_CONFLICT"
ReasonRouteInvalidState = "ROUTE_INVALID_STATE"
)
var (
ErrRouteNotFound = Define("route.not_found")
ErrRouteAmbiguous = Define("route.ambiguous")
ErrRouteDisabled = Define("route.disabled")
ErrRouteInvalidSource = Define("route.invalid_source")
ErrRouteDestinationNotFound = Define("route.destination_not_found")
ErrRouteDestinationInactive = Define("route.destination_inactive")
ErrRouteDestinationCurrencyMismatch = Define("route.destination_currency_mismatch")
ErrRouteDestinationOrgMismatch = Define("route.destination_org_mismatch")
ErrRouteDestinationAmbiguous = Define("route.destination_ambiguous")
ErrRouteDestinationOwnerMismatch = Define("route.destination_owner_mismatch")
ErrRouteActiveSourceConflict = Define("route.active_source_conflict")
ErrRouteDestinationTypeMismatch = Define("route.destination_type_mismatch")
ErrRouteDestinationRoleMismatch = Define("route.destination_role_mismatch")
ErrRouteIdempotencyConflict = Define("route.idempotency_conflict")
ErrRouteVersionConflict = Define("route.version_conflict")
ErrRouteInvalidState = Define("route.invalid_state")
)
func RouteNotFound(msg string) error {
return newRouteClassifiedError(ErrNoData, ErrRouteNotFound, ReasonRouteNotFound, msg)
}
func RouteAmbiguous(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteAmbiguous, ReasonRouteAmbiguous, msg)
}
func RouteDisabled(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDisabled, ReasonRouteDisabled, msg)
}
func RouteInvalidSource(msg string) error {
return newRouteClassifiedError(ErrInvalidArg, ErrRouteInvalidSource, ReasonRouteInvalidSource, msg)
}
func RouteDestinationNotFound(msg string) error {
return newRouteClassifiedError(ErrNoData, ErrRouteDestinationNotFound, ReasonRouteDestinationNotFound, msg)
}
func RouteDestinationInactive(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationInactive, ReasonRouteDestinationInactive, msg)
}
func RouteDestinationCurrencyMismatch(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationCurrencyMismatch, ReasonRouteDestinationCurrencyMismatch, msg)
}
func RouteDestinationOrgMismatch(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationOrgMismatch, ReasonRouteDestinationOrgMismatch, msg)
}
func RouteDestinationAmbiguous(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationAmbiguous, ReasonRouteDestinationAmbiguous, msg)
}
func RouteDestinationOwnerMismatch(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationOwnerMismatch, ReasonRouteDestinationOwnerMismatch, msg)
}
func RouteActiveSourceConflict(msg string) error {
return newRouteClassifiedError(ErrDataConflict, ErrRouteActiveSourceConflict, ReasonRouteActiveSourceConflict, msg)
}
func RouteDestinationTypeMismatch(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationTypeMismatch, ReasonRouteDestinationTypeMismatch, msg)
}
func RouteDestinationRoleMismatch(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteDestinationRoleMismatch, ReasonRouteDestinationRoleMismatch, msg)
}
func RouteIdempotencyConflict(msg string) error {
return newRouteClassifiedError(ErrDataConflict, ErrRouteIdempotencyConflict, ReasonRouteIdempotencyConflict, msg)
}
func RouteVersionConflict(msg string) error {
return newRouteClassifiedError(ErrDataConflict, ErrRouteVersionConflict, ReasonRouteVersionConflict, msg)
}
func RouteInvalidState(msg string) error {
return newRouteClassifiedError(ErrFailedPrecondition, ErrRouteInvalidState, ReasonRouteInvalidState, msg)
}
func newRouteClassifiedError(base, code error, reason, msg string) error {
msg = strings.TrimSpace(msg)
if msg == "" && code != nil {
msg = code.Error()
}
return routeMetadataError{
classifiedError: classifiedError{base: base, code: code, msg: msg, reason: reason, publicMessage: true},
routeCode: routeErrorCodePrefix + reason,
}
}
-52
View File
@@ -1,52 +0,0 @@
package merrors
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
)
func TestRouteReasonsRoundTrip(t *testing.T) {
tests := []struct {
name string
create func(string) error
code error
base error
reason string
}{
{name: "not found", create: RouteNotFound, code: ErrRouteNotFound, base: ErrNoData, reason: ReasonRouteNotFound},
{name: "ambiguous", create: RouteAmbiguous, code: ErrRouteAmbiguous, base: ErrFailedPrecondition, reason: ReasonRouteAmbiguous},
{name: "disabled", create: RouteDisabled, code: ErrRouteDisabled, base: ErrFailedPrecondition, reason: ReasonRouteDisabled},
{name: "invalid source", create: RouteInvalidSource, code: ErrRouteInvalidSource, base: ErrInvalidArg, reason: ReasonRouteInvalidSource},
{name: "destination not found", create: RouteDestinationNotFound, code: ErrRouteDestinationNotFound, base: ErrNoData, reason: ReasonRouteDestinationNotFound},
{name: "destination inactive", create: RouteDestinationInactive, code: ErrRouteDestinationInactive, base: ErrFailedPrecondition, reason: ReasonRouteDestinationInactive},
{name: "destination currency mismatch", create: RouteDestinationCurrencyMismatch, code: ErrRouteDestinationCurrencyMismatch, base: ErrFailedPrecondition, reason: ReasonRouteDestinationCurrencyMismatch},
{name: "destination organization mismatch", create: RouteDestinationOrgMismatch, code: ErrRouteDestinationOrgMismatch, base: ErrFailedPrecondition, reason: ReasonRouteDestinationOrgMismatch},
{name: "destination ambiguous", create: RouteDestinationAmbiguous, code: ErrRouteDestinationAmbiguous, base: ErrFailedPrecondition, reason: ReasonRouteDestinationAmbiguous},
{name: "destination owner mismatch", create: RouteDestinationOwnerMismatch, code: ErrRouteDestinationOwnerMismatch, base: ErrFailedPrecondition, reason: ReasonRouteDestinationOwnerMismatch},
{name: "active source conflict", create: RouteActiveSourceConflict, code: ErrRouteActiveSourceConflict, base: ErrDataConflict, reason: ReasonRouteActiveSourceConflict},
{name: "destination type mismatch", create: RouteDestinationTypeMismatch, code: ErrRouteDestinationTypeMismatch, base: ErrFailedPrecondition, reason: ReasonRouteDestinationTypeMismatch},
{name: "destination role mismatch", create: RouteDestinationRoleMismatch, code: ErrRouteDestinationRoleMismatch, base: ErrFailedPrecondition, reason: ReasonRouteDestinationRoleMismatch},
{name: "idempotency conflict", create: RouteIdempotencyConflict, code: ErrRouteIdempotencyConflict, base: ErrDataConflict, reason: ReasonRouteIdempotencyConflict},
{name: "version conflict", create: RouteVersionConflict, code: ErrRouteVersionConflict, base: ErrDataConflict, reason: ReasonRouteVersionConflict},
{name: "invalid state", create: RouteInvalidState, code: ErrRouteInvalidState, base: ErrFailedPrecondition, reason: ReasonRouteInvalidState},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.create("route error")
require.Equal(t, "route error", err.Error())
reason, ok := Reason(err)
require.True(t, ok)
require.Equal(t, tt.reason, reason)
require.True(t, errors.Is(err, tt.code))
require.True(t, errors.Is(err, tt.base))
restored, ok := FromReason(reason, "route error")
require.True(t, ok)
require.True(t, errors.Is(restored, tt.code))
require.True(t, errors.Is(restored, tt.base))
})
}
}
@@ -1,78 +0,0 @@
package merrors
import (
"errors"
"strings"
)
const (
SignupEmailPolicyCode = "signup_email_rejected"
SignupEmailPolicyReasonInvalidEmail = "invalid_email"
SignupEmailPolicyReasonDisposableDomain = "disposable_domain"
SignupEmailPolicyReasonFreeDomain = "free_domain"
)
var ErrSignupEmailPolicyRejected = Define(SignupEmailPolicyCode)
type SignupEmailPolicyError struct {
reason string
domain string
}
func NormalizeSignupEmailPolicyReason(reason string) string {
switch strings.TrimSpace(reason) {
case SignupEmailPolicyReasonInvalidEmail:
return SignupEmailPolicyReasonInvalidEmail
case SignupEmailPolicyReasonDisposableDomain:
return SignupEmailPolicyReasonDisposableDomain
case SignupEmailPolicyReasonFreeDomain:
return SignupEmailPolicyReasonFreeDomain
default:
return SignupEmailPolicyReasonInvalidEmail
}
}
func IsSignupEmailPolicyReason(reason string) bool {
return strings.TrimSpace(reason) == NormalizeSignupEmailPolicyReason(reason)
}
func (e *SignupEmailPolicyError) Error() string {
if e == nil || e.reason == "" {
return SignupEmailPolicyCode
}
return SignupEmailPolicyCode + ": " + e.reason
}
func (e *SignupEmailPolicyError) Unwrap() error {
return ErrSignupEmailPolicyRejected
}
func (e *SignupEmailPolicyError) Reason() string {
if e == nil {
return ""
}
return strings.TrimSpace(e.reason)
}
func (e *SignupEmailPolicyError) Domain() string {
if e == nil {
return ""
}
return strings.TrimSpace(e.domain)
}
func SignupEmailPolicyRejected(reason, domain string) error {
return &SignupEmailPolicyError{
reason: NormalizeSignupEmailPolicyReason(reason),
domain: strings.TrimSpace(domain),
}
}
func AsSignupEmailPolicyError(err error) (*SignupEmailPolicyError, bool) {
var policyErr *SignupEmailPolicyError
if errors.As(err, &policyErr) && policyErr != nil {
return policyErr, true
}
return nil, false
}
@@ -1,52 +0,0 @@
package merrors
import (
"errors"
"testing"
)
func TestSignupEmailPolicyError(t *testing.T) {
err := SignupEmailPolicyRejected(SignupEmailPolicyReasonFreeDomain, "gmail.com")
policyErr, ok := AsSignupEmailPolicyError(err)
if !ok {
t.Fatalf("expected signup email policy error")
}
if policyErr.Reason() != SignupEmailPolicyReasonFreeDomain {
t.Fatalf("reason mismatch: got=%q", policyErr.Reason())
}
if policyErr.Domain() != "gmail.com" {
t.Fatalf("domain mismatch: got=%q", policyErr.Domain())
}
if !errors.Is(err, ErrSignupEmailPolicyRejected) {
t.Fatalf("expected signup email policy sentinel")
}
reason, ok := Reason(err)
if !ok {
t.Fatalf("expected generic reason")
}
if reason != SignupEmailPolicyReasonFreeDomain {
t.Fatalf("generic reason mismatch: got=%q", reason)
}
}
func TestSignupEmailPolicyReasonNormalization(t *testing.T) {
for _, reason := range []string{
SignupEmailPolicyReasonInvalidEmail,
SignupEmailPolicyReasonDisposableDomain,
SignupEmailPolicyReasonFreeDomain,
} {
if !IsSignupEmailPolicyReason(reason) {
t.Fatalf("expected supported reason: %q", reason)
}
}
err := SignupEmailPolicyRejected("unexpected_reason", "")
policyErr, ok := AsSignupEmailPolicyError(err)
if !ok {
t.Fatalf("expected signup email policy error")
}
if policyErr.Reason() != SignupEmailPolicyReasonInvalidEmail {
t.Fatalf("unexpected reasons must not leak: got=%q", policyErr.Reason())
}
}
-15
View File
@@ -1,15 +0,0 @@
package merrors
var ErrTreasuryInvalidArgument = Define("treasury.monitoring.invalid_argument")
var ErrTreasuryConfiguration = Define("treasury.monitoring.configuration_error")
var ErrTreasuryNotFound = Define("treasury.monitoring.not_found")
var ErrTreasuryOutOfOrder = Define("treasury.monitoring.out_of_order")
var ErrTreasuryNegativeBalance = Define("treasury.monitoring.negative_balance")
var ErrTreasuryAlreadyReversed = Define("treasury.monitoring.movement_already_reversed")
var ErrTreasuryProjectionMismatch = Define("treasury.monitoring.projection_mismatch")