lib/buckybox/api.rb in buckybox-api-1.6.2 vs lib/buckybox/api.rb in buckybox-api-1.7.0
- old
+ new
@@ -1,51 +1,65 @@
require "cgi"
-require "httparty"
require "crazy_money"
-require "super_recursive_open_struct"
+require "hashie/mash"
+require "oj"
+require "typhoeus"
module BuckyBox
class API
ResponseError = Class.new(Exception) # generic error
- NotFoundError = Class.new(Exception)
+ NotFoundError = Class.new(ResponseError)
ENDPOINTS = {
production: "https://api.buckybox.com/v1",
staging: "https://api-staging.buckybox.com/v1",
development: "http://api.buckybox.local:3000/v1",
- test: "http://api.buckybox.local:3000/v1",
+ test: "https://api.buckybox.com/v1",
}.freeze
+ class Response < Hashie::Mash
+ def initialize(hash)
+ unless hash.is_a?(Hash)
+ raise ArgumentError, "#{hash.inspect} must be a Hash"
+ end
+
+ super(hash, nil) do |object, key|
+ raise NoMethodError, "undefined method `#{key}' for #{object}"
+ end
+ end
+ end
+
class CachedResponse
- attr_reader :response, :cached_at
+ attr_reader :response
def initialize(response)
@response, @cached_at = response, epoch
end
def expired?
- epoch - cached_at > 60 # NOTE: cache responses for 60 seconds
+ epoch - @cached_at > 60 # NOTE: cache responses for 60 seconds
end
private def epoch
Time.now.utc.to_i
end
end
- include HTTParty
- format :json
- base_uri ENDPOINTS.fetch(ENV.fetch("RAILS_ENV", "production").to_sym)
+ def self.fixtures_path
+ File.expand_path("../../../fixtures", __FILE__)
+ end
def initialize(headers)
- self.class.headers(headers)
+ @headers = headers.freeze
+ @endpoint = ENDPOINTS.fetch(ENV.fetch("RAILS_ENV", :production).to_sym)
end
- def boxes(params = {embed: "images"}, options = {})
+ def boxes(params = { embed: "images" }, options = {})
query :get, "/boxes", params, options, price: CrazyMoney
end
- def box(id, params = {embed: "extras,images,box_items"}, options = {})
+ def box(id, params = { embed: "extras,images,box_items" }, options = {})
query :get, "/boxes/#{id}", params, options, price: CrazyMoney
end
def delivery_services(params = {}, options = {})
query :get, "/delivery_services", params, options, fee: CrazyMoney
@@ -70,13 +84,13 @@
def authenticate_customer(params = {}, options = {})
query :post, "/customers/sign_in", params, options
end
def create_or_update_customer(json_customer)
- customer = JSON.parse(json_customer)
+ customer = Oj.load(json_customer)
- if customer['id']
+ if customer["id"]
query :put, "/customers/#{customer['id']}", json_customer # TODO: replace by :patch
else
query :post, "/customers", json_customer
end
end
@@ -89,51 +103,70 @@
@cache = nil
end
private
- def check_response!(response)
- unless [200, 201].include? response.code
- message = response.parsed_response["message"] || response.parsed_response
- message = "Error #{response.code} - #{message}"
-
- raise exception_type(response.code), message
+ def handle_error!(response)
+ parsed_response = parse_response(response)
+ message = if parsed_response
+ parsed_response["message"] || parsed_response
+ else
+ "Empty response"
end
+
+ message = "Error #{response.code} - #{message}"
+
+ raise exception_type(response.code), message
end
+ def parse_response(response)
+ Oj.load(response.body)
+ end
+
def exception_type(http_code)
{
404 => NotFoundError,
}.fetch(http_code, ResponseError)
end
- def query(type, uri, params = {}, options = {}, types = {})
- options = {
- as_object: true
- }.merge(options)
+ def query(method, path, params = {}, options = {}, types = {})
+ options = { as_object: true }.merge(options)
+ hash = query_cache(method, path, params, types)
- hash = query_cache(type, uri, params, types)
-
if options[:as_object]
- SuperRecursiveOpenStruct.new(hash)
+ if hash.is_a?(Array)
+ hash.map { |item| Response.new(item) }
+ else
+ Response.new(hash)
+ end
else
hash
end.freeze
end
- def query_cache(type, uri, params, types)
- query_fresh = -> {
- params_key = (type == :get ? :query : :body)
- response = self.class.public_send(type, uri, params_key => params)
- check_response!(response)
- parsed_response = response.parsed_response
+ def query_cache(method, path, params, types)
+ uri = [@endpoint, path].join
+
+ query_fresh = lambda do
+ params_key = (method == :get ? :params : :body)
+
+ response = Typhoeus::Request.new(
+ uri,
+ headers: @headers,
+ method: method,
+ params_key => params,
+ accept_encoding: "gzip",
+ ).run
+
+ handle_error!(response) unless response.success?
+ parsed_response = parse_response(response)
add_types(parsed_response, types)
- }
+ end
- if type == :get # NOTE: only cache GET method
+ if method == :get # NOTE: only cache GET method
@cache ||= {}
- cache_key = [self.class.headers.hash, uri, to_query(params)].join
+ cache_key = [@headers.hash, uri, to_query(params)].join
cached_response = @cache[cache_key]
if cached_response && !cached_response.expired?
cached_response.response
else
@@ -161,10 +194,10 @@
end
end
def to_query(hash)
if hash.empty?
- ''
+ ""
else
hash.map do |key, value|
"#{CGI.escape(key.to_s)}=#{CGI.escape(value)}"
end.sort!.join("&")
end