Skip to content

Commit 1c7dc4c

Browse files
committed
Improve error message when server returns invalid data
As discussed in #121, the error message you get when a web server returns non-json hal data or the Content-Type is incorrect is rather unclear on what happened. This commit adds a check when creating a Resource that the received representation is something that makes sense. This turns the error message for an example such as `Hyperclient.new('http://www.google.com/').foo` from ``` /lib/hyperclient/collection.rb:78:in `method_missing': undefined method `fetch' for #<String:0x005654b3898628> (NoMethodError) from /lib/hyperclient/resource.rb:87:in `block in method_missing' from /lib/hyperclient/resource.rb:85:in `each' from /lib/hyperclient/resource.rb:85:in `method_missing' from /lib/hyperclient/link.rb:129:in `method_missing' from example.rb:3:in `<main>' ``` to ``` /lib/hyperclient/resource.rb:90:in `validate': Invalid representation for resource (got String, expected Hash). Is your web server returning JSON HAL data with a 'Content-Type: application/hal+json' header? (Hyperclient::InvalidRepresentationError) from /lib/hyperclient/resource.rb:43:in `initialize' from /lib/hyperclient/link.rb:179:in `new' from /lib/hyperclient/link.rb:179:in `http_method' from /lib/hyperclient/link.rb:89:in `_get' from /lib/hyperclient/link.rb:84:in `_resource' from /lib/hyperclient/link.rb:128:in `method_missing' from example.rb:3:in `<main>' ``` e.g. we now validate immediately after the request, and provide meaningful information and even a hint towards the possibly-missing header.
1 parent 7fcdcfc commit 1c7dc4c

File tree

3 files changed

+35
-2
lines changed

3 files changed

+35
-2
lines changed

features/steps/default_config.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Spinach::Features::DefaultConfig < Spinach::FeatureSteps
1212
end
1313

1414
step 'I send some data to the API' do
15-
stub_request(:post, 'http://api.example.org/posts')
15+
stub_request(:post, 'http://api.example.org/posts').to_return(headers: { 'Content-Type' => 'application/hal+json' })
1616
assert_equal 200, api._links.posts._post(title: 'My first blog post')._response.status
1717
end
1818

lib/hyperclient/resource.rb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
require 'forwardable'
22

33
module Hyperclient
4+
# Public: Exception that is raised when passing in invalid representation data
5+
# for the resource.
6+
class InvalidRepresentationError < ArgumentError
7+
attr_reader :representation
8+
9+
def initialize(error_description, representation)
10+
super(error_description)
11+
@representation = representation
12+
end
13+
end
14+
415
# Public: Represents a resource from your API. Its responsability is to
516
# ease the way you access its attributes, links and embedded resources.
617
class Resource
@@ -29,8 +40,9 @@ class Resource
2940
# representation - The hash with the HAL representation of the Resource.
3041
# entry_point - The EntryPoint object to inject the configutation.
3142
def initialize(representation, entry_point, response = nil)
32-
representation = representation ? representation.dup : {}
43+
representation = validate(representation)
3344
links = representation['_links'] || {}
45+
3446
@_links = LinkCollection.new(links, links['curies'], entry_point)
3547
@_embedded = ResourceCollection.new(representation['_embedded'], entry_point)
3648
@_attributes = Attributes.new(representation)
@@ -68,6 +80,21 @@ def fetch(key, *args)
6880

6981
private
7082

83+
# Internal: Ensures the received representation is a valid Hash-lookalike.
84+
def validate(representation)
85+
return {} unless representation
86+
87+
if representation.respond_to?(:to_hash)
88+
representation.to_hash.dup
89+
else
90+
raise InvalidRepresentationError.new(
91+
"Invalid representation for resource (got #{representation.class}, expected Hash). " \
92+
"Is your web server returning JSON HAL data with a 'Content-Type: application/hal+json' header?",
93+
representation
94+
)
95+
end
96+
end
97+
7198
# Internal: Returns the self Link of the Resource. Used to handle the HTTP
7299
# methods.
73100
def _self_link

test/hyperclient/resource_test.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ module Hyperclient
4040

4141
resource._response.body.must_equal body
4242
end
43+
44+
describe 'with an invalid representation' do
45+
it 'raises an InvalidRepresentationError' do
46+
proc { Resource.new('invalid representation data', entry_point) }.must_raise InvalidRepresentationError
47+
end
48+
end
4349
end
4450

4551
describe '_links' do

0 commit comments

Comments
 (0)