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

The client class is the public interface used for interacting with the service. The client will have methods for each operation defined by the API. Clients are 1:1 with a Smithy service shape and its public methods are 1:1 with Smithy operation shapes.

Initialization

The initialize method takes a hash of config options and creates a new Client instance. Any plugins applied to the Client or passed into initialize are called. Config options are validated using the Config class.

def initialize(options = {})
  @config = initialize_config(options)
  # other instance state
end

Global Plugins

The client exposes a class method that allows for appending plugins to every instance of Client.

# @api private
@plugins = Hearth::PluginList.new

# @return [Hearth::PluginList]
def self.plugins
  @plugins
end

Operation Methods

Each Smithy operation shape maps to one operation method in the Client. These operation methods use a static Middleware stack. The middleware stack handles building the request and returning the response data or an error. Streaming operations take a block, and the Hearth::HTTP::Response object will take an output stream.

def my_operation(params = {}, options = {}, &block)
  response_body = output_stream(options, &block)
  config = operation_config(options)
  input = Params::MyOperationInput.build(params, context: 'params')
  stack = MyService::Middleware::MyOperation.build(config, @stubs)
  context = Hearth::Context.new(
    request: Hearth::HTTP::Request.new(uri: URI('')),
    response: Hearth::HTTP::Response.new(body: response_body),
    logger: config.logger,
    operation_name: :my_operation,
    interceptors: config.interceptors
  )
  context.logger.info("[#{context.invocation_id}] [#{self.class}#my_operation] params: #{params}, options: #{options}")
  output = stack.run(input, context)
  if output.error
    context.logger.error("[#{context.invocation_id}] [#{self.class}#my_operation] #{output.error} (#{output.error.class})")
    raise output.error
  end
  context.logger.info("[#{context.invocation_id}] [#{self.class}#my_operation] #{output.data}")
  output
end

Operation methods take two positional hash parameters, one for API parameters and the other for operation config overrides. However, generated SDKs also support passing params as the operations input type (e.g. an instance of Types::Input) allowing users to build input using structured classes.

# normal param usage
client.my_operation(param1: p1, param2: p2)

# structured param usage
my_operation_input = Types::MyOperationInput.new(param1: p1, param2: p2)
client.my_operation(my_operation_input)

# overriding client options
client.operation(
  { param1: p1, param2: p2 },
  { endpoint: 'new.endpoint.com' }
)