Module: ActiveCall::Api

Extended by:
ActiveSupport::Concern
Defined in:
lib/active_call/api/version.rb,
lib/active_call/api.rb

Defined Under Namespace

Modules: Attributes

Constant Summary collapse

VERSION =
'0.1.5'

Instance Method Summary collapse

Instance Method Details

#bad_gateway?Boolean

Returns:

  • (Boolean)


295
296
297
# File 'lib/active_call/api.rb', line 295

def bad_gateway?
  response.status == 502
end

#bad_request?Boolean

The methods below determine what type of error gets added to the errors object.

service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=bad_request, options={}>]>

When using ‘.call!`, they map to the `exception_mapping` above, so `bad_request?` maps to `bad_request`.

exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=bad_request, options={}>]>

These methods can be overridden to add more rules when an API does not respond with the relevant HTTP status code.

A common occurrence is when an API returns an HTTP status code of 400 with an error message in the body for anything related to client errors, sometimes even for a resource that could not be found.

It is not required to override any of these methods since all 4xx and 5xx errors add a ‘client_error` or `server_error` type to the errors object, respectively.

While not required, handling specific errors based on their actual meaning makes for a happier development experience.

You have access to the full ‘Farady::Response` object set to the `response` attribute, so you can use `response.status` and `response.body` to determine the type of error.

Perhaps the API does not always respond with a 422 HTTP status code for unprocessable entity requests or a 404 HTTP status for resources not found.

class YourGem::BaseService < ActiveCall::Base
  ...

  def not_found?
    response.status == 404 || (response.status == 400 && response.body['error_code'] == 'not_found')
  end

  def unprocessable_entity?
    response.status == 422 || (response.status == 400 && response.body['error_code'] == 'not_processable')
  end

Returns:

  • (Boolean)


243
244
245
# File 'lib/active_call/api.rb', line 243

def bad_request?
  response.status == 400
end

#conflict?Boolean

Returns:

  • (Boolean)


271
272
273
# File 'lib/active_call/api.rb', line 271

def conflict?
  response.status == 409
end

#connectionObject

Subclasses must implement a ‘connection` method to hold a `Faraday::Connection` object.

This connection instance will then be used in the ‘call` methods of the individual service objects.

Examples

class YourGem::BaseService < ActiveCall::Base
  config_accessor :api_key, default: ENV['API_KEY'], instance_writer: false
  config_accessor :logger, default: Logger.new($stdout), instance_writer: false

  def connection
    @_connection ||= Faraday.new do |conn|
      conn.url_prefix = 'https://example.com/api/v1'
      conn.request :authorization, 'X-API-Key', api_key
      conn.request :json
      conn.response :json
      conn.response :logger, logger, formatter: Faraday::Logging::ColorFormatter, prefix: { request: 'YourGem', response: 'YourGem' } do |logger|
        logger.filter(/(Authorization:).*"(.+)."/i, '\1 [FILTERED]')
      end
      conn.adapter Faraday.default_adapter
    end
  end

You can now create a REST API service object like so.

class YourGem::SomeResource::UpdateService < YourGem::BaseService
  attr_reader :id, :first_name, :last_name

  validates :id, :first_name, :last_name, presence: true

  def initialize(id:, first_name:, last_name:)
    @id         = id
    @first_name = first_name
    @last_name  = last_name
  end

  # PUT /api/v1/someresource/:id
  def call
    connection.put("someresource/#{id}", first_name: first_name, last_name: last_name)
  end
end


203
204
205
# File 'lib/active_call/api.rb', line 203

def connection
  raise NotImplementedError, 'Subclasses must implement a connection method. Must return a Faraday.new object.'
end

#forbidden?Boolean

Returns:

  • (Boolean)


251
252
253
# File 'lib/active_call/api.rb', line 251

def forbidden?
  response.status == 403
end

#gateway_timeout?Boolean

Returns:

  • (Boolean)


303
304
305
# File 'lib/active_call/api.rb', line 303

def gateway_timeout?
  response.status == 504
end

#gone?Boolean

Returns:

  • (Boolean)


275
276
277
# File 'lib/active_call/api.rb', line 275

def gone?
  response.status == 410
end

#internal_server_error?Boolean

Returns:

  • (Boolean)


287
288
289
# File 'lib/active_call/api.rb', line 287

def internal_server_error?
  response.status == 500
end

#not_acceptable?Boolean

Returns:

  • (Boolean)


259
260
261
# File 'lib/active_call/api.rb', line 259

def not_acceptable?
  response.status == 406
end

#not_found?Boolean

Returns:

  • (Boolean)


255
256
257
# File 'lib/active_call/api.rb', line 255

def not_found?
  response.status == 404
end

#not_implemented?Boolean

Returns:

  • (Boolean)


291
292
293
# File 'lib/active_call/api.rb', line 291

def not_implemented?
  response.status == 501
end

#proxy_authentication_required?Boolean

Returns:

  • (Boolean)


263
264
265
# File 'lib/active_call/api.rb', line 263

def proxy_authentication_required?
  response.status == 407
end

#request_timeout?Boolean

Returns:

  • (Boolean)


267
268
269
# File 'lib/active_call/api.rb', line 267

def request_timeout?
  response.status == 408
end

#service_unavailable?Boolean

Returns:

  • (Boolean)


299
300
301
# File 'lib/active_call/api.rb', line 299

def service_unavailable?
  response.status == 503
end

#too_many_requests?Boolean

Returns:

  • (Boolean)


283
284
285
# File 'lib/active_call/api.rb', line 283

def too_many_requests?
  response.status == 429
end

#unauthorized?Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/active_call/api.rb', line 247

def unauthorized?
  response.status == 401
end

#unprocessable_entity?Boolean

Returns:

  • (Boolean)


279
280
281
# File 'lib/active_call/api.rb', line 279

def unprocessable_entity?
  response.status == 422
end