Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

configure mapping outside Faraday #137

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lib/spyke/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@ module Http

included do
class_attribute :connection, instance_accessor: false

class_attribute :json_mapping
self.json_mapping = { data_key: :data, metadata_key: :metadata, errors_key: :errors }
end

module ClassMethods
def spyke_json_mapping(map)
self.json_mapping.merge!(map)
end

METHODS.each do |method|
define_method(method) do
new_instance_or_collection_from_result scoped_request(method)
Expand All @@ -23,7 +30,7 @@ def request(method, path, params = {})
ActiveSupport::Notifications.instrument('request.spyke', method: method) do |payload|
response = send_request(method, path, params)
payload[:url], payload[:status] = response.env.url, response.status
Result.new_from_response(response)
Result.new_from_response_body(response.body, **json_mapping)
end
end

Expand Down
29 changes: 12 additions & 17 deletions lib/spyke/result.rb
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
module Spyke
class Result
attr_reader :body
attr_reader :data, :metadata, :errors

def self.new_from_response(response)
new(response.body)
def self.new_from_response_body(body, data_key:, metadata_key:, errors_key:)
body = HashWithIndifferentAccess.new(body.presence)
new(
data: body[data_key],
metadata: body[metadata_key],
errors: body[errors_key]
)
end

def initialize(body)
@body = HashWithIndifferentAccess.new(body)
end

def data
body[:data]
end

def metadata
body[:metadata] || {}
end

def errors
body[:errors] || []
def initialize(data:, metadata: nil, errors: nil)
@data = data
@metadata = metadata || {}
@errors = errors || []
end
end
end
8 changes: 4 additions & 4 deletions test/orm_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_reload
end

def test_404
stub_request(:get, 'http://sushi.com/recipes/1').to_return(status: 404, body: { message: 'Not found' }.to_json)
stub_request(:get, 'http://sushi.com/recipes/1').to_return_json({ body: { message: 'Not found' } }, status: 404)

assert_raises(ResourceNotFound) { Recipe.find(1) }
assert_raises(ResourceNotFound) { Recipe.find(nil) }
Expand Down Expand Up @@ -185,14 +185,14 @@ def test_scoped_destroy_class_method_without_param
end

def test_relative_uris
previous = Spyke::Base.connection.url_prefix
Spyke::Base.connection.url_prefix = 'http://sushi.com/api/v2/'
previous = Api.connection.url_prefix
Api.connection.url_prefix = 'http://sushi.com/api/v2/'

endpoint = stub_request(:get, 'http://sushi.com/api/v2/recipes')
Recipe.all.to_a
assert_requested endpoint

Spyke::Base.connection.url_prefix = previous
Api.connection.url_prefix = previous
end

def test_custom_primary_key_on_collection
Expand Down
50 changes: 19 additions & 31 deletions test/support/fixtures.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
require 'multi_json'

# Dummy api
class JSONParser < Faraday::Middleware
def on_complete(env)
json = MultiJson.load(env.body, symbolize_keys: true)
env.body = {
data: json[:result],
metadata: json[:metadata],
errors: json[:errors]
}
rescue MultiJson::ParseError => exception
env.body = { errors: { base: [ error: exception.message ] } }
class Api < Spyke::Base
self.connection = Faraday.new(url: 'http://sushi.com') do |faraday|
faraday.request :multipart
faraday.request :json
faraday.response :json
faraday.adapter Faraday.default_adapter
end
end

Spyke::Base.connection = Faraday.new(url: 'http://sushi.com') do |faraday|
faraday.request :multipart
faraday.request :json
faraday.use JSONParser
faraday.adapter Faraday.default_adapter
spyke_json_mapping data_key: :result
end

# Test classes
class Recipe < Spyke::Base
class Recipe < Api
has_many :groups
has_many :gallery_images, class_name: 'Image'
has_one :image
Expand Down Expand Up @@ -62,7 +50,7 @@ def before_update_callback; end
def before_save_callback; end
end

class Image < Spyke::Base
class Image < Api
method_for :create, :put
attributes :description, :caption
end
Expand All @@ -80,7 +68,7 @@ class RecipeImage < Image
include_root_in_json false
end

class Group < Spyke::Base
class Group < Api
has_many :ingredients, uri: nil
has_many :featured_ingredients, uri: 'featured_ingredients?filter[group_id]=:group_id', class_name: "Ingredient"
accepts_nested_attributes_for :ingredients
Expand All @@ -93,20 +81,20 @@ def self.build_default
end
end

class Ingredient < Spyke::Base
class Ingredient < Api
uri 'recipes/:recipe_id/ingredients/(:id)'
end

class User < Spyke::Base
class User < Api
self.primary_key = :uuid
has_many :recipes
end

class Photo < Spyke::Base
class Photo < Api
uri 'images/photos/(:id)'
end

class Comment < Spyke::Base
class Comment < Api
belongs_to :user
has_many :users
scope :approved, -> { where(comment_approved: true) }
Expand All @@ -115,7 +103,7 @@ class Comment < Spyke::Base

class OtherApi < Spyke::Base
self.connection = Faraday.new(url: 'http://sashimi.com') do |faraday|
faraday.use JSONParser
faraday.response :json
faraday.adapter Faraday.default_adapter
end
end
Expand Down Expand Up @@ -145,22 +133,22 @@ def suggestions
end

module Cookbook
class Tip < Spyke::Base
class Tip < Api
uri 'tips/(:id)'
has_many :likes, class_name: 'Cookbook::Like'
has_many :favorites
has_many :votes
has_many :photos, class_name: 'Photo'
end

class Like < Spyke::Base
class Like < Api
belongs_to :tip
end

class Favorite < Spyke::Base
class Favorite < Api
end

class Photo < Spyke::Base
class Photo < Api
include_root_in_json :foto
end
end
Expand Down
3 changes: 2 additions & 1 deletion test/support/webmock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

class WebMock::RequestStub
def to_return_json(hash, options = {})
options[:body] = MultiJson.dump(hash)
options[:body] = hash.to_json
options[:headers] = { content_type: 'application/json' }
to_return(options)
end
end
Expand Down