Skip to content

Generate a typed error hierarchy from the ErrorCode enum #3

Description

@lineoffligbot

Today every failure raises HTTP::StatusError from http.rb's :raise_error
feature. The parsed body is reachable via e.response.parse, but it is untyped,
undocumented, and the same class covers all 50 documented failure modes.

Amazon Ads publishes a closed enum, components/schemas/ErrorCode, with 50
values and a description for each:

ACTION_NOT_SUPPORTED, ACTIVE_RESOURCE_LIMIT_EXCEEDED, ARCHIVED_PARENT_CANNOT_CREATE,
ARCHIVED_RESOURCE_CANNOT_EDIT, BAD_REQUEST, CONFLICT, CONTENT_TOO_LARGE,
DUPLICATE_FIELD_VALUE_FOUND, FIELD_VALUE_IS_INVALID, FORBIDDEN, INTERNAL_ERROR,
NOT_FOUND, PAYMENT_ISSUE, PRODUCT_INELIGIBLE, RESOURCE_ID_NOT_FOUND,
TOO_MANY_REQUESTS, UNSUPPORTED_MARKETPLACE, ...

Because the enum is closed we can generate the classes at build time rather
than defining them at runtime:

module AmazonAds
  class Error < StandardError
    attr_reader :response
    def status = response&.status&.code
    def deconstruct_keys(keys) = { status:, code: }
  end

  module Errors
    # Archived resources cannot be edited.
    class ArchivedResourceCannotEdit < Error; end
    # There have been too many requests, please slow down your call rate.
    class TooManyRequests < Error; end
    # ...
  end
end

Generating beats runtime const_set here: the constants exist before the first
failure, they are greppable, they get RBS types, and each carries Amazon's own
description as a doc comment.

Implementation notes

  • Envelope shapes differ across our specs. amazon_ads.json and
    marketing_stream.json use {code, message}; reporting.json uses
    {code, detail}. A builder needs to handle both.
  • With retries: n, http.rb raises HTTP::OutOfRetriesError instead. That needs
    catching and rebuilding into the same hierarchy.
  • Unknown or missing codes should fall back to the base Error rather than
    raising during error construction.

Prior art

Peddler does this dynamically (lib/peddler/error/builder.rb) because SP-API
error codes are open-ended strings. We have a fixed enum, so we get to do the
better version.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions