RestAPIBuilder
A simple wrapper for rest-client aiming to make creation and testing of API clients easier.
Why?
RestClient is great, but after building a few API clients with it you will almost inevitably find yourself re-implementing certain basic things such as:
- Compiling and parsing basic JSON requests/responses
- Handling and extracting details from non-200 responses
- Creating testing interfaces for your API clients
This library's tries to solve these and similar issues by providing a set of helper methods to improve on rest-client features and an optional webmock testing interface for it.
Installation
gem install rest_api_builder
WebMock interface installation
Simply require webmock interface before your test, for example in your test_helper.rb:
# test_helper.rb
require "webmock"
require "rest_api_builder/webmock_request_expectations"
WebMock.enable!
# my_spec.rb
require 'test_helper'
describe 'my test' do
it 'performs a request' do
RestAPIBuilder::WebMockRequestExpectations.expect_execute(...).to_return(body: "hi!")
result = RestClient::Request.execute(...)
# some assertions
end
end
RestAPIBuilder::WebMockRequestExpectations expects that you have WebMock installed as a dependency.
Usage
require "rest_api_builder"
class MyRequest
include RestAPIBuilder
def execute(options)
handle_response do
RestClient::Request.execute(compose_request_options(**options))
end
end
def json_execute(options)
handle_json_response do
RestClient::Request.execute(compose_json_request_options(**options))
end
end
end
my_request = MyRequest.new
# Simple request:
response = my_request.execute(base_url: "example.com", method: :get)
response[:success] #=> true
response[:status] #=> 200
response[:body] #=> "<!doctype html>\n<html>..."
response[:headers] #=> {:accept_ranges=>"bytes", ...}
# Non-200 responses:
response = my_request.execute(base_url: "example.com", path: "/foo", method: :get)
response[:success] #=> false
response[:status] #=> 404
response[:body] #=> "<!doctype html>\n<html>..."
# JSON requests:
response = my_request.json_execute(base_url: "api.github.com", path: "/users/octocat/orgs", method: :get)
response[:success] #=> true
response[:body] #=> []
WebMock Expectations
require "rest_api_builder"
require "webmock"
require "rest_api_builder/webmock_request_expectations"
WebMock.disable_net_connect!
class MyRequest
include RestAPIBuilder
def execute(options)
handle_response do
RestClient::Request.execute(compose_request_options(**options))
end
end
def json_execute(options)
handle_json_response do
RestClient::Request.execute(compose_json_request_options(**options))
end
end
end
my_request = MyRequest.new
Expectations = RestAPIBuilder::WebMockRequestExpectations
# Simple expectation
Expectations.expect_execute(base_url: "test.com", method: :get)
response = my_request.execute(base_url: "test.com", method: :get)
response[:success] #=> true
response[:status] #=> 200
response[:body] #=> ''
response[:headers] #=> {}
# Specifying expectation details with WebMock::Request methods
Expectations
.expect_execute(base_url: "test.com", method: :get)
.to_return(status: 404, body: "not found")
response = my_request.execute(base_url: "test.com", method: :get)
response[:success] #=> false
response[:status] #=> 404
response[:body] #=> "not found"
# Specifying expectation details with :request and :response options
Expectations.expect_execute(
base_url: "test.com",
method: :post,
response: { body: 'hello' },
request: { body: { foo: "bar" } } # body will be matched partially using hash_including matcher
)
response = my_request.json_execute(base_url: "test.com", method: :post, body: { foo: "bar", bar: "baz" })
response[:success] #=> true
response[:body] #=> 'hello'
my_request.json_execute(base_url: "test.com", method: :post, body: {bar: "baz"}) # => Raises WebMock::NetConnectNotAllowedError
# Using #expect_json_execute
Expectations.expect_json_execute(
base_url: "test.com",
method: :get,
response: { body: {hi: 'hello'} }
)
response = my_request.execute(base_url: "test.com", method: :get)
response[:success] #=> true
response[:body] #=> "{\"hi\":\"hello\"}"
Request API
RestAPIBuilder#compose_request_options(options)
Composes request options that can be passed to RestClient::Request.execute.
Options:
- base_url: Base URL of the request. Required.
- method: HTTP method of the request(e.g :get, :post, :patch). Required.
- path: Path to be appended to the :base_url. Optional.
- body: Request Body. Optional.
- headers: Request Headers. Optional.
- query: Query hash to be appended to the resulting url. Optional.
RestAPIBuilder#compose_json_request_options(options)
Accepts same options as compose_request_options but will also:
- Add
Content-Type: 'application/json'to requestheaders - Convert request
bodyto JSON if it's present
RestAPIBuilder#handle_response(options, &block)
Executes given block, expecting to receive RestClient::Response as a result.\
Returns plain ruby hash with following keys: :success, :status, :body, :headers\
This will also gracefully handle non-200 responses, but will throw on any error without defined response(e.g server timeout)
Options:
- logger: A
Loggerinstance. If provided, will log response details as RestClient wont do this by default. Optional
RestAPIBuilder#handle_json_response(options, &block)
Same as #handle_response but will also attempt to decode response :body, returning it as is if a parsing error occurrs
RestAPIBuilder#handle_response_error(options, &block)
Low-level API, you can use this method if you want to work with the RestClient's responses directly without any conversions(e.g when using block_response or raw_response options of RestClient). This will handle errors in the same way as #handle_response does, but will not do anything else.\
Returns ruby hash with :success and :raw_response keys.
WebMockRequestExpectations API
RestAPIBuilder::WebMockRequestExpectations.expect_execute(options)
Defines a request expectation using WebMock's stub_request. Returns an instance of WebMock::RequestStub on which methods such as with, to_return, to_timeout can be called.
Options:
- base_url: Base URL of the request. Required.
- method: HTTP method of the request(e.g :get, :post, :patch). Required.
- path: Path to be appended to the :base_url. Optional.
- request: request details which will be passed to
WebMock::RequestStub#withif provided. Ifqueryorbodykeys are present and are hashes, they will be converted into WebMock'shash_includingmatcher. Optional - response: response details which will be passed to
WebMock::RequestStub#to_returnif provided. Optional
RestAPIBuilder::WebMockRequestExpectations.expect_json_execute(options)
A convenience shortcut for #json_execute which will convert request[:body] to JSON if it's present
License
MIT