Skip to content

Commit

Permalink
Allow custom parser
Browse files Browse the repository at this point in the history
Paves the way for returning what's in Amazon's Open API models
  • Loading branch information
hakanensari committed Oct 7, 2024
1 parent 7109a59 commit 5748ac3
Show file tree
Hide file tree
Showing 7 changed files with 377 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

### Added

- Allow custom parser
- Marketplace.id and Marketplace.ids shorthands

## [3.0.0] - 2024-10-04
Expand Down
17 changes: 16 additions & 1 deletion lib/peddler/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require "peddler/endpoint"
require "peddler/error"
require "peddler/marketplace"
require "peddler/response_decorator"
require "peddler/version"

module Peddler
Expand All @@ -13,6 +14,11 @@ class API
class CannotSandbox < StandardError; end
class MustSandbox < StandardError; end

class << self
# @return [#call]
attr_accessor :parser
end

# @return [Peddler::Endpoint]
attr_reader :endpoint

Expand Down Expand Up @@ -122,10 +128,19 @@ def meter(rate_limit)
raise error if error
end

response
ResponseDecorator.decorate(response, parser:)
end
end

# @param [#call]
attr_writer :parser

# @!attribute [r]
# @return [#call]
def parser
@parser || self.class.parser
end

private

def user_agent
Expand Down
46 changes: 46 additions & 0 deletions lib/peddler/response_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

require "delegate"
require "forwardable"

module Peddler
# Wraps HTTP::Response to allow custom parsing
class ResponseDecorator < SimpleDelegator
extend Forwardable

# @!method dig(*key)
# Delegates to the Hash returned by {Response#to_h} to extract a nested
# value specified by the sequence of keys
#
# @param [String] key one or more keys
# @see https://ruby-doc.org/core/Hash.html#method-i-dig
def_delegator :to_h, :dig

class << self
# Decorates an HTTP::Response
#
# @param [HTTP::Response] response
# @param [nil, #call] parser (if any)
# @return [ResponseDecorator]
def decorate(response, parser: nil)
new(response).tap do |decorator|
decorator.parser = parser
end
end
end

# @return [#call]
attr_accessor :parser

def parse
parser ? parser.call(__getobj__) : __getobj__.parse
end

# Converts the response body to a Hash
#
# @return [Hash]
def to_h
(parser && parser.respond_to?(:to_h) ? parser : parse).to_h
end
end
end
42 changes: 42 additions & 0 deletions test/peddler/custom_parser_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# frozen_string_literal: true

require "helper"

require "peddler/api/reports_2021_06_30"

module Peddler
class CustomParserTest < Minitest::Test
include FeatureHelpers

def test_custom_parser_on_instance
api.parser = custom_parser
res = api.get_report("1234567")

assert_nil(api_class.parser)
assert(api.parser)
assert(res.dig(:reportId))
end

def test_custom_parser_on_class
klass = Class.new(api_class)
klass.parser = custom_parser
access_token = request_access_token(grantless: false)
api = klass.new(aws_region, access_token)
res = api.get_report("1234567")

assert_nil(api_class.parser)
assert(klass.parser)
assert(res.dig(:reportId))
end

private

def custom_parser
->(response) { JSON.parse(response, symbolize_names: true) }
end

def api_class
API::Reports20210630
end
end
end
70 changes: 70 additions & 0 deletions test/peddler/response_decorator_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

require "helper"
require "peddler/response_decorator"

module Peddler
class ResponseDecoratorTest < Minitest::Test
def test_parses
decorator = ResponseDecorator.decorate(response)

assert_equal(payload, decorator.parse)
end

def test_to_h
decorator = ResponseDecorator.decorate(response)

assert_equal(payload, decorator.to_h)
end

def test_dig
decorator = ResponseDecorator.decorate(response)

assert(decorator.dig("foo"))
end

def test_parses_with_custom_parser
decorator = ResponseDecorator.decorate(
response, parser: ->(response) { JSON.parse(response, symbolize_names: true) }
)

assert_equal(payload_with_symbolized_keys, decorator.parse)
end

def test_to_h_with_custom_parser
decorator = ResponseDecorator.decorate(
response, parser: ->(response) { JSON.parse(response, symbolize_names: true) }
)

assert_equal(payload_with_symbolized_keys, decorator.to_h)
end

def test_dig_with_custom_parser
decorator = ResponseDecorator.decorate(
response, parser: ->(response) { JSON.parse(response, symbolize_names: true) }
)

assert(decorator.dig(:foo))
end

private

def response
HTTP::Response.new(
body: JSON.dump(payload),
headers: { "Content-Type" => "application/json" },
status: nil,
version: nil,
request: nil,
)
end

def payload
{ "foo" => "bar" }
end

def payload_with_symbolized_keys
payload.transform_keys(&:to_sym)
end
end
end

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5748ac3

Please sign in to comment.