Upgrade to Pro — share decks privately, control downloads, hide ads and more …

My API Client

Ryz310
July 03, 2020

My API Client

My API Client is a framework of Web API Client.
Provides features error handling, retrying, pagination and so on.

See also: https://github.com/ryz310/my_api_client

Ryz310

July 03, 2020
Tweet

More Decks by Ryz310

Other Decks in Programming

Transcript

  1. WHAT IS WHAT IS ? ? A framework of Web

    API Client. Provides features error handling, retrying, pagination and so on. https://github.com/ryz310/my_api_client
  2. class ExampleApiClient < MyApiClient::Base endpoint 'https://example.com/common' # GET https://example.com/common/path/to/resource?key=value def

    get_resource query = { key: 'value' } get 'path/to/resource', headers: headers, query: query end private def headers { 'Content-Type': 'application/json;charset=UTF-8' } end end api_client = ExampleApiClient.new response = api_client.get_resource # => { message: 'Hello world!' }
  3. TESTING WITH RSPEC TESTING WITH RSPEC RSpec.describe ExampleApiClient, type: :api_client

    do let(:api_client) { described_class.new } let(:headers) do { 'Content-Type': 'application/json;charset=UTF-8' } end describe '#get_resource' do subject(:api_request) { api_client.get_resource } it do expect { api_request } .to request_to(:get, 'https://example.com/common/path/to/ .with(headers: headers, query: { key: 'value' }) end end ExampleApiClient #get_resource is expected to request to "GET https://example.com/common/pat
  4. HANDLE ERROR BY STATUS CODE HANDLE ERROR BY STATUS CODE

    class ExampleApiClient < MyApiClient::Base endpoint 'https://example.com/common' error_handling status_code: 404, raise: MyErrors::NotFound # GET https://example.com/common/path/to/resource?key=value def get_resource query = { key: 'value' } get 'path/to/resource', headers: headers, query: query end # ... end api_client = ExampleApiClient.new begin response = api_client.get_resource rescue MyErrors::NotFound puts 'The resource was not found.' end
  5. TESTING WITH RSPEC TESTING WITH RSPEC RSpec.describe ExampleApiClient, type: :api_client

    do let(:api_client) { described_class.new } let(:headers) do { 'Content-Type': 'application/json;charset=UTF-8' } end describe '#get_resource' do subject(:api_request) { api_client.get_resource } context 'when received status code is 404' do it do expect { api_request } .to be_handled_as_an_error(MyErrors::NotFound) .when_receive(status_code: 404) end ExampleApiClient #get_resource when received status code is 404 is expected to be handled as MyErrors::NotFound
  6. HANDLE ERROR BY RESPONSE BODY HANDLE ERROR BY RESPONSE BODY

    error_handling json: { '$.error.message': /You requested error co raise: MyErrors::ErrorCodeOther error_handling json: { '$.error.code': :zero? }, raise: MyErrors::ErrorCode00 error_handling json: { '$.error.code': 10 }, raise: MyErrors::ErrorCode10 error_handling json: { '$.error.code': 20..29 }, raise: MyErrors::ErrorCode2x error_handling json: { '$.error.code': 30 }, status_code: 400, raise: MyErrors::ErrorCode30 { "error": { "code": 10, "message": "You requested error code: 10" } }
  7. TESTING WITH RSPEC TESTING WITH RSPEC RSpec.describe ExampleApiClient, type: :api_client

    do let(:api_client) { described_class.new } let(:headers) do { 'Content-Type': 'application/json;charset=UTF-8' } end describe '#get_resource' do subject(:api_request) { api_client.get_resource } context 'when received error code is 10' do let(:response_body) do { error: { code: 10, message: "You requested error code: 10" ExampleApiClient #get_resource when received error code is 10 is expected to be handled as MyErrors::ErrorCode10
  8. Returns arbitrary response without real HTTP connection. api_client = stub_api_client(

    ExampleApiClient, get_resource: { message: 'Hello world!' } ) response = api_client.get_resource # => { message: 'Hello world!' }
  9. stub_api_client_all( ExampleApiClient, get_resource: { message: 'Hello world!' } ) api_client

    = ExampleApiClient.new # => It returns a mock instance. response = api_client.get_resource # => { message: 'Hello world!' }
  10. WITH LAMBDA EXPRESSION WITH LAMBDA EXPRESSION api_client = stub_api_client( ExampleApiClient,

    get_resource: ->(name) { { message: "Hello #{name}!" } } ) response = api_client.get_resource('John') # => { message: 'Hello John!' }
  11. TO RAISE ARBITRARY ERROR TO RAISE ARBITRARY ERROR api_client =

    stub_api_client( ExampleApiClient, get_resource: { raise: MyErrors::NotFound } ) begin response = api_client.get_resource rescue MyErrors::NotFound # You can raise arbitrary error. end
  12. DEMO DEMO $ curl GET $MY_API_ENDPOINT/pagination | jq . {

    "links": { "next": "https://{...}/pagination?page=2" }, "page": 1 } $ bin/console irb(main):001:0> api_client = MyPaginationApiClient.new irb(main):002:0> api_client.pagination irb(main):003:0> api_client.pagination.first irb(main):004:0> api_client.pagination.take(3).eager.map(&:itself
  13. api_client = MyPaginationApiClient.new api_client.pagination # => #<Enumerator::Lazy: #<Enumerator: #<Enumerator::Generator:0x api_client.pagination.first

    # => { # :links=>{ # :next=>"https://{...}/dev/pagination?page=2" # }, # :page=>1 # } api_client.pagination.take(3).eager.map(&:itself) # => [ # {
  14. IS… IS… A framework of Web API Client. Provides features:

    error handling test stub retrying pagination and so on…