Skip to content
Matt Muller edited this page Apr 12, 2024 · 10 revisions

The Config class is a struct that contains configuration options used by the Client. Config resolves default values using default provider chains defined for each configuration option and validates provided values against expected types and other defined constraints. Config classes have a 1:1 mapping with a Smithy service shape.

@railsJson
@title("High Score Sample Rails Service")
service HighScoreService {
    version: "2021-02-15",
    resources: [HighScore]
}

Usage

A Config object is created, resolved and validated when creating a Client. Config has default values which can be sourced from multiple locations. Config will also validate that the provided values are of expected types. For example, :endpoint is validated to be a String.

client = HighScoreService::Client.new(endpoint: 'http://127.0.0.1:3000')
# => #<HighScoreService::Client ... >

client.config
# => #<struct HighScoreService::Config ... >

The client's Config instance is immutable after validation, but individual values may mutate their own internal state. For example, an Identity Provider may resolve and cache an identity internally.

client.config.endpoint = 'http://127.0.0.1:8080
# => Cannot modify frozen HighScoreService::Config

Plugins

All Plugins have access to the Config instance and may modify values during client initialization and operation invocation.

# Plugin (callable) that changes the configured endpoint
plugin = proc { |config| config.endpoint = 'http://127.0.0.1:8080' }
# => #<Proc ... >

client = HighScoreService.new(
  endpoint: '127.0.0.1:3000',
  plugins: Hearth::PluginList.new([plugin])
)
# => #<HighScoreService::Client ... >

client.list_high_scores
# <uses endpoint from plugin>
# => #<Hearth::Output @data=... >

Config resolution and lifecycle

Client initialization

When a Client is initialized, the following steps occur:

  1. Options hash is passed into the Client's #initialize method.
  2. Any provided Interceptors are removed (to be added later to ensure correct interceptor ordering with plugins)
  3. a Config instance is created using the options. Hearth's Hearth::Config::Resolver is used to resolve the Config's values using the defined defaults (a chain of default values providers defined for each configuration option). Any option with a value explicitly provided (including nil) will be used as is.
  4. Class level Plugins are called and may mutate the resolved Config.
  5. Plugins from provided options are called and may mutate the resolved Config.
  6. The Config instance is then validated - checking ruby types are correct and any other constraints that have been defined for each option.
  7. config is frozen, preventing any additional mutation.

Operation invocation

It is possible to override config options set on the client during operation invocation. Its also possible to provide plugins to the operation which may also mutate the config used for the operation:

client.list_high_scores(
  {} # operation parameters
  {
    endpoint: 'http://127.0.0.1:8080',
    plugins: [my_operation_plugin],
    interceptors: [my_operation_interceptor]
  } # operation specific config overrides
)

The Config used during operation invocation is resolved by:

  1. Removing plugins and interceptors.
  2. Create a new, operation local Config by merging the client's config with options provided to the operation method.
  3. Operation plugins are called in order, mutating the operation local config.
  4. Interceptors provided to the operation are appended.
  5. The operation config is validated and then frozen.