Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ language: en-US
early_access: true
enable_free_tier: true

tone_instructions: "Be concise and direct. Focus on bugs, security vulnerabilities, breaking changes to the public surface, and cross-language parity gaps. Skip nitpicks and style suggestions — each language has its own linter (biome, ruff, rubocop, golangci-lint, php-cs-fixer, gradle warnings). Only comment on code changed in this PR."
tone_instructions: "Be concise. Focus on bugs, security issues, breaking public-surface changes, and cross-language parity gaps. Skip style/nitpicks — each language has its own linter. Only comment on code changed in this PR."

reviews:
profile: chill
Expand Down
3 changes: 3 additions & 0 deletions bin/check_parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,15 @@ def check_language(lang_cfg: dict, manifest: dict) -> LanguageReport:
source = collect_source(lang_dir, lang_cfg["extensions"])
case = lang_cfg["method_case"]
suffix = lang_cfg["class_suffix"]
async_suffix = lang_cfg.get("async_suffix", "")

# --- methods ---
for version, vcfg in manifest["versions"].items():
for resource, rcfg in vcfg["resources"].items():
for method in rcfg["methods"]:
spellings = method_variants(method, case)
if async_suffix:
spellings = spellings + [s + async_suffix for s in spellings]
if not any(re.search(rf"\b{re.escape(s)}\b", source) for s in spellings):
report.missing_methods.append(f"{version}.{resource}.{method}")

Expand Down
20 changes: 20 additions & 0 deletions go/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,26 @@ var (
ErrTLS = errors.New("tesote: tls error")
ErrConfig = errors.New("tesote: config error")
ErrEndpointRemoved = errors.New("tesote: endpoint removed")
ErrAccountNotFound = errors.New("tesote: account not found")
ErrTransactionNotFound = errors.New("tesote: transaction not found")
ErrSyncSessionNotFound = errors.New("tesote: sync session not found")
ErrPaymentMethodNotFound = errors.New("tesote: payment method not found")
ErrTransactionOrderNotFound = errors.New("tesote: transaction order not found")
ErrBatchNotFound = errors.New("tesote: batch not found")
ErrBankConnectionNotFound = errors.New("tesote: bank connection not found")
ErrInvalidCursor = errors.New("tesote: invalid cursor")
ErrInvalidCount = errors.New("tesote: invalid count")
ErrInvalidLimit = errors.New("tesote: invalid limit")
ErrInvalidQuery = errors.New("tesote: invalid query")
ErrMissingDateRange = errors.New("tesote: missing date range")
ErrSyncInProgress = errors.New("tesote: sync in progress")
ErrSyncRateLimitExceeded = errors.New("tesote: sync rate limit exceeded")
ErrBankUnderMaintenance = errors.New("tesote: bank under maintenance")
ErrValidation = errors.New("tesote: validation error")
ErrInvalidOrderState = errors.New("tesote: invalid order state")
ErrBankSubmission = errors.New("tesote: bank submission error")
ErrBatchValidation = errors.New("tesote: batch validation error")
ErrInternal = errors.New("tesote: internal error")
)

// RequestSummary is the redacted request snapshot attached to every error.
Expand Down
188 changes: 188 additions & 0 deletions go/errors_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,191 @@ func (e *ServiceUnavailableError) Is(target error) bool { return target == ErrSe

// Unwrap exposes the embedded *APIError.
func (e *ServiceUnavailableError) Unwrap() error { return e.APIError }

// AccountNotFoundError is raised on 404 ACCOUNT_NOT_FOUND.
type AccountNotFoundError struct{ *APIError }

// Is matches the ErrAccountNotFound sentinel.
func (e *AccountNotFoundError) Is(target error) bool { return target == ErrAccountNotFound }

// Unwrap exposes the embedded *APIError.
func (e *AccountNotFoundError) Unwrap() error { return e.APIError }

// TransactionNotFoundError is raised on 404 TRANSACTION_NOT_FOUND.
type TransactionNotFoundError struct{ *APIError }

// Is matches the ErrTransactionNotFound sentinel.
func (e *TransactionNotFoundError) Is(target error) bool { return target == ErrTransactionNotFound }

// Unwrap exposes the embedded *APIError.
func (e *TransactionNotFoundError) Unwrap() error { return e.APIError }

// SyncSessionNotFoundError is raised on 404 SYNC_SESSION_NOT_FOUND.
type SyncSessionNotFoundError struct{ *APIError }

// Is matches the ErrSyncSessionNotFound sentinel.
func (e *SyncSessionNotFoundError) Is(target error) bool { return target == ErrSyncSessionNotFound }

// Unwrap exposes the embedded *APIError.
func (e *SyncSessionNotFoundError) Unwrap() error { return e.APIError }

// PaymentMethodNotFoundError is raised on 404 PAYMENT_METHOD_NOT_FOUND.
type PaymentMethodNotFoundError struct{ *APIError }

// Is matches the ErrPaymentMethodNotFound sentinel.
func (e *PaymentMethodNotFoundError) Is(target error) bool {
return target == ErrPaymentMethodNotFound
}

// Unwrap exposes the embedded *APIError.
func (e *PaymentMethodNotFoundError) Unwrap() error { return e.APIError }

// TransactionOrderNotFoundError is raised on 404 TRANSACTION_ORDER_NOT_FOUND.
type TransactionOrderNotFoundError struct{ *APIError }

// Is matches the ErrTransactionOrderNotFound sentinel.
func (e *TransactionOrderNotFoundError) Is(target error) bool {
return target == ErrTransactionOrderNotFound
}

// Unwrap exposes the embedded *APIError.
func (e *TransactionOrderNotFoundError) Unwrap() error { return e.APIError }

// BatchNotFoundError is raised on 404 BATCH_NOT_FOUND.
type BatchNotFoundError struct{ *APIError }

// Is matches the ErrBatchNotFound sentinel.
func (e *BatchNotFoundError) Is(target error) bool { return target == ErrBatchNotFound }

// Unwrap exposes the embedded *APIError.
func (e *BatchNotFoundError) Unwrap() error { return e.APIError }

// BankConnectionNotFoundError is raised on 404 BANK_CONNECTION_NOT_FOUND.
type BankConnectionNotFoundError struct{ *APIError }

// Is matches the ErrBankConnectionNotFound sentinel.
func (e *BankConnectionNotFoundError) Is(target error) bool {
return target == ErrBankConnectionNotFound
}

// Unwrap exposes the embedded *APIError.
func (e *BankConnectionNotFoundError) Unwrap() error { return e.APIError }

// InvalidCursorError is raised on 422 INVALID_CURSOR.
type InvalidCursorError struct{ *APIError }

// Is matches the ErrInvalidCursor sentinel.
func (e *InvalidCursorError) Is(target error) bool { return target == ErrInvalidCursor }

// Unwrap exposes the embedded *APIError.
func (e *InvalidCursorError) Unwrap() error { return e.APIError }

// InvalidCountError is raised on 422 INVALID_COUNT.
type InvalidCountError struct{ *APIError }

// Is matches the ErrInvalidCount sentinel.
func (e *InvalidCountError) Is(target error) bool { return target == ErrInvalidCount }

// Unwrap exposes the embedded *APIError.
func (e *InvalidCountError) Unwrap() error { return e.APIError }

// InvalidLimitError is raised on 422 INVALID_LIMIT.
type InvalidLimitError struct{ *APIError }

// Is matches the ErrInvalidLimit sentinel.
func (e *InvalidLimitError) Is(target error) bool { return target == ErrInvalidLimit }

// Unwrap exposes the embedded *APIError.
func (e *InvalidLimitError) Unwrap() error { return e.APIError }

// InvalidQueryError is raised on 422 INVALID_QUERY.
type InvalidQueryError struct{ *APIError }

// Is matches the ErrInvalidQuery sentinel.
func (e *InvalidQueryError) Is(target error) bool { return target == ErrInvalidQuery }

// Unwrap exposes the embedded *APIError.
func (e *InvalidQueryError) Unwrap() error { return e.APIError }

// MissingDateRangeError is raised on 422 MISSING_DATE_RANGE.
type MissingDateRangeError struct{ *APIError }

// Is matches the ErrMissingDateRange sentinel.
func (e *MissingDateRangeError) Is(target error) bool { return target == ErrMissingDateRange }

// Unwrap exposes the embedded *APIError.
func (e *MissingDateRangeError) Unwrap() error { return e.APIError }

// SyncInProgressError is raised on 409 SYNC_IN_PROGRESS.
type SyncInProgressError struct{ *APIError }

// Is matches the ErrSyncInProgress sentinel.
func (e *SyncInProgressError) Is(target error) bool { return target == ErrSyncInProgress }

// Unwrap exposes the embedded *APIError.
func (e *SyncInProgressError) Unwrap() error { return e.APIError }

// SyncRateLimitExceededError is raised on 429 SYNC_RATE_LIMIT_EXCEEDED.
type SyncRateLimitExceededError struct{ *APIError }

// Is matches the ErrSyncRateLimitExceeded sentinel.
func (e *SyncRateLimitExceededError) Is(target error) bool {
return target == ErrSyncRateLimitExceeded
}

// Unwrap exposes the embedded *APIError.
func (e *SyncRateLimitExceededError) Unwrap() error { return e.APIError }

// BankUnderMaintenanceError is raised on 503 BANK_UNDER_MAINTENANCE.
type BankUnderMaintenanceError struct{ *APIError }

// Is matches the ErrBankUnderMaintenance sentinel.
func (e *BankUnderMaintenanceError) Is(target error) bool { return target == ErrBankUnderMaintenance }

// Unwrap exposes the embedded *APIError.
func (e *BankUnderMaintenanceError) Unwrap() error { return e.APIError }

// ValidationError is raised on 400 VALIDATION_ERROR.
type ValidationError struct{ *APIError }

// Is matches the ErrValidation sentinel.
func (e *ValidationError) Is(target error) bool { return target == ErrValidation }

// Unwrap exposes the embedded *APIError.
func (e *ValidationError) Unwrap() error { return e.APIError }

// InvalidOrderStateError is raised on 409 INVALID_ORDER_STATE.
type InvalidOrderStateError struct{ *APIError }

// Is matches the ErrInvalidOrderState sentinel.
func (e *InvalidOrderStateError) Is(target error) bool { return target == ErrInvalidOrderState }

// Unwrap exposes the embedded *APIError.
func (e *InvalidOrderStateError) Unwrap() error { return e.APIError }

// BankSubmissionError is raised on 422 BANK_SUBMISSION_ERROR.
type BankSubmissionError struct{ *APIError }

// Is matches the ErrBankSubmission sentinel.
func (e *BankSubmissionError) Is(target error) bool { return target == ErrBankSubmission }

// Unwrap exposes the embedded *APIError.
func (e *BankSubmissionError) Unwrap() error { return e.APIError }

// BatchValidationError is raised on 400 BATCH_VALIDATION_ERROR.
type BatchValidationError struct{ *APIError }

// Is matches the ErrBatchValidation sentinel.
func (e *BatchValidationError) Is(target error) bool { return target == ErrBatchValidation }

// Unwrap exposes the embedded *APIError.
func (e *BatchValidationError) Unwrap() error { return e.APIError }

// InternalError is raised on 500 INTERNAL_ERROR.
type InternalError struct{ *APIError }

// Is matches the ErrInternal sentinel.
func (e *InternalError) Is(target error) bool { return target == ErrInternal }

// Unwrap exposes the embedded *APIError.
func (e *InternalError) Unwrap() error { return e.APIError }
46 changes: 46 additions & 0 deletions go/errors_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,46 @@ func wrapTyped(base *APIError) error {
return &InvalidDateRangeError{APIError: base}
case "RATE_LIMIT_EXCEEDED":
return &RateLimitExceededError{APIError: base}
case "ACCOUNT_NOT_FOUND":
return &AccountNotFoundError{APIError: base}
case "TRANSACTION_NOT_FOUND":
return &TransactionNotFoundError{APIError: base}
case "SYNC_SESSION_NOT_FOUND":
return &SyncSessionNotFoundError{APIError: base}
case "PAYMENT_METHOD_NOT_FOUND":
return &PaymentMethodNotFoundError{APIError: base}
case "TRANSACTION_ORDER_NOT_FOUND":
return &TransactionOrderNotFoundError{APIError: base}
case "BATCH_NOT_FOUND":
return &BatchNotFoundError{APIError: base}
case "BANK_CONNECTION_NOT_FOUND":
return &BankConnectionNotFoundError{APIError: base}
case "INVALID_CURSOR":
return &InvalidCursorError{APIError: base}
case "INVALID_COUNT":
return &InvalidCountError{APIError: base}
case "INVALID_LIMIT":
return &InvalidLimitError{APIError: base}
case "INVALID_QUERY":
return &InvalidQueryError{APIError: base}
case "MISSING_DATE_RANGE":
return &MissingDateRangeError{APIError: base}
case "SYNC_IN_PROGRESS":
return &SyncInProgressError{APIError: base}
case "SYNC_RATE_LIMIT_EXCEEDED":
return &SyncRateLimitExceededError{APIError: base}
case "BANK_UNDER_MAINTENANCE":
return &BankUnderMaintenanceError{APIError: base}
case "VALIDATION_ERROR":
return &ValidationError{APIError: base}
case "INVALID_ORDER_STATE":
return &InvalidOrderStateError{APIError: base}
case "BANK_SUBMISSION_ERROR":
return &BankSubmissionError{APIError: base}
case "BATCH_VALIDATION_ERROR":
return &BatchValidationError{APIError: base}
case "INTERNAL_ERROR":
return &InternalError{APIError: base}
}
switch base.HTTPStatus {
case http.StatusUnauthorized:
Expand All @@ -98,16 +138,22 @@ func wrapTyped(base *APIError) error {

func fallbackCode(status int) string {
switch status {
case http.StatusBadRequest:
return "VALIDATION_ERROR"
case http.StatusUnauthorized:
return "UNAUTHORIZED"
case http.StatusForbidden:
return "FORBIDDEN"
case http.StatusNotFound:
return "NOT_FOUND"
case http.StatusConflict:
return "MUTATION_CONFLICT"
case http.StatusUnprocessableEntity:
return "UNPROCESSABLE_CONTENT"
case http.StatusTooManyRequests:
return "RATE_LIMIT_EXCEEDED"
case http.StatusInternalServerError:
return "INTERNAL_ERROR"
case http.StatusServiceUnavailable:
return "SERVICE_UNAVAILABLE"
}
Expand Down
Loading
Loading