Skip to content

Commit 12fd255

Browse files
authored
Merge branch 'JsonApiClient:master' into master
2 parents 9378d92 + 4277a86 commit 12fd255

File tree

12 files changed

+242
-29
lines changed

12 files changed

+242
-29
lines changed

CHANGELOG.md

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
## Unreleased
44

5+
## 1.21.0
6+
- [#395](https://github.com/JsonApiClient/json_api_client/pull/395) - relaxing faraday dependency to anything less than 2.0
7+
8+
## 1.20.0
9+
- [#389](https://github.com/JsonApiClient/json_api_client/pull/389) - adding `create!`, `update_attributes!`, `update!` methods
10+
11+
## 1.19.0
12+
- [#382](https://github.com/JsonApiClient/json_api_client/pull/382) - Add extra error classes to hande server errors.
13+
14+
- [#386](https://github.com/JsonApiClient/json_api_client/pull/386) - use HashWithIndifferentAccess
15+
516
## 1.18.0
617
- [#372](https://github.com/JsonApiClient/json_api_client/pull/372) - Fix handling of dashed-types associations correctly
718

@@ -96,20 +107,20 @@
96107

97108
## v1.6.3
98109

99-
- [#312](https://githup.com/JsonApiClient/json_api_client/pull/312) - Don't raise on `422`
110+
- [#312](https://github.com/JsonApiClient/json_api_client/pull/312) - Don't raise on `422`
100111

101112
## v1.6.2
102113

103-
- [#311](https://githup.com/JsonApiClient/json_api_client/pull/311) - Raise JsonApiClient::Errors::ClientError for unhandled 4xx responses
114+
- [#311](https://github.com/JsonApiClient/json_api_client/pull/311) - Raise JsonApiClient::Errors::ClientError for unhandled 4xx responses
104115

105116
## v1.6.1
106117

107-
- [#297](https://githup.com/JsonApiClient/json_api_client/pull/297) - Fix test_helper
108-
- [#298](https://githup.com/JsonApiClient/json_api_client/pull/298) - README update: arguments for custom connections run method
109-
- [#306](https://githup.com/JsonApiClient/json_api_client/pull/306) - README update: pagination override examples
110-
- [#307](https://githup.com/JsonApiClient/json_api_client/pull/307) - Symbolize params keys on model initialize
111-
- [#304](https://githup.com/JsonApiClient/json_api_client/pull/304) - Optional add default to changes
112-
- [#300](https://githup.com/JsonApiClient/json_api_client/pull/300) - Define methods for properties and associations getter/setter
118+
- [#297](https://github.com/JsonApiClient/json_api_client/pull/297) - Fix test_helper
119+
- [#298](https://github.com/JsonApiClient/json_api_client/pull/298) - README update: arguments for custom connections run method
120+
- [#306](https://github.com/JsonApiClient/json_api_client/pull/306) - README update: pagination override examples
121+
- [#307](https://github.com/JsonApiClient/json_api_client/pull/307) - Symbolize params keys on model initialize
122+
- [#304](https://github.com/JsonApiClient/json_api_client/pull/304) - Optional add default to changes
123+
- [#300](https://github.com/JsonApiClient/json_api_client/pull/300) - Define methods for properties and associations getter/setter
113124

114125
## v1.6.0
115126

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# JsonApiClient [![Build Status](https://travis-ci.org/JsonApiClient/json_api_client.png)](https://travis-ci.org/JsonApiClient/json_api_client) [![Code Climate](https://codeclimate.com/github/JsonApiClient/json_api_client.png)](https://codeclimate.com/github/JsonApiClient/json_api_client) [![Code Coverage](https://codeclimate.com/github/JsonApiClient/json_api_client/coverage.png)](https://codeclimate.com/github/JsonApiClient/json_api_client)
1+
# JsonApiClient [![Build Status](https://travis-ci.org/JsonApiClient/json_api_client.png?branch=master)](https://travis-ci.org/JsonApiClient/json_api_client) [![Code Climate](https://codeclimate.com/github/JsonApiClient/json_api_client.png)](https://codeclimate.com/github/JsonApiClient/json_api_client) [![Code Coverage](https://codeclimate.com/github/JsonApiClient/json_api_client/coverage.png)](https://codeclimate.com/github/JsonApiClient/json_api_client)
22

33
This gem is meant to help you build an API client for interacting with REST APIs as laid out by [http://jsonapi.org](http://jsonapi.org). It attempts to give you a query building framework that is easy to understand (it is similar to ActiveRecord scopes).
44

json_api_client.gemspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
1212
s.summary = 'Build client libraries compliant with specification defined by jsonapi.org'
1313

1414
s.add_dependency "activesupport", '>= 3.2.0'
15-
s.add_dependency "faraday", '>= 0.15.2', '< 1.2.0'
16-
s.add_dependency "faraday_middleware", '>= 0.9.0', '< 1.2.0'
15+
s.add_dependency "faraday", '>= 0.15.2', '< 2.0'
16+
s.add_dependency "faraday_middleware", '>= 0.9.0', '< 2.0'
1717
s.add_dependency "addressable", '~> 2.2'
1818
s.add_dependency "activemodel", '>= 3.2.0'
1919
s.add_dependency "rack", '>= 0.2'

lib/json_api_client/errors.rb

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,28 @@ class AccessDenied < ClientError
4444
class NotAuthorized < ClientError
4545
end
4646

47+
class NotFound < ClientError
48+
attr_reader :uri
49+
def initialize(uri)
50+
@uri = uri
51+
52+
msg = "Resource not found: #{uri.to_s}"
53+
super nil, msg
54+
end
55+
end
56+
57+
class RequestTimeout < ClientError
58+
end
59+
60+
class Conflict < ClientError
61+
def initialize(env, msg = 'Resource already exists')
62+
super env, msg
63+
end
64+
end
65+
66+
class TooManyRequests < ClientError
67+
end
68+
4769
class ConnectionError < ApiError
4870
end
4971

@@ -59,23 +81,16 @@ def initialize(env, msg = nil)
5981
end
6082
end
6183

62-
class Conflict < ServerError
63-
def initialize(env, msg = 'Resource already exists')
64-
super env, msg
65-
end
84+
class InternalServerError < ServerError
6685
end
6786

68-
class NotFound < ServerError
69-
attr_reader :uri
70-
def initialize(uri)
71-
@uri = uri
87+
class BadGateway < ServerError
88+
end
7289

73-
msg = "Couldn't find resource at: #{uri.to_s}"
74-
super nil, msg
75-
end
90+
class ServiceUnavailable < ServerError
7691
end
7792

78-
class InternalServerError < ServerError
93+
class GatewayTimeout < ServerError
7994
end
8095

8196
class UnexpectedStatus < ServerError
@@ -88,5 +103,16 @@ def initialize(code, uri)
88103
super nil, msg
89104
end
90105
end
106+
107+
class RecordNotSaved < ServerError
108+
attr_reader :record
109+
110+
def initialize(message = nil, record = nil)
111+
@record = record
112+
end
113+
def message
114+
"Record not saved"
115+
end
116+
end
91117
end
92118
end

lib/json_api_client/middleware/status.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,24 @@ def handle_status(code, env)
3838
raise Errors::AccessDenied, env
3939
when 404
4040
raise Errors::NotFound, env[:url]
41+
when 408
42+
raise Errors::RequestTimeout, env
4143
when 409
4244
raise Errors::Conflict, env
4345
when 422
4446
# Allow to proceed as resource errors will be populated
47+
when 429
48+
raise Errors::TooManyRequests, env
4549
when 400..499
4650
raise Errors::ClientError, env
4751
when 500
4852
raise Errors::InternalServerError, env
53+
when 502
54+
raise Errors::BadGateway, env
55+
when 503
56+
raise Errors::ServiceUnavailable, env
57+
when 504
58+
raise Errors::GatewayTimeout, env
4959
when 501..599
5060
raise Errors::ServerError, env
5161
else

lib/json_api_client/query/builder.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ def last
6767
end
6868

6969
def build(attrs = {})
70-
klass.new @path_params.merge(attrs.symbolize_keys)
70+
klass.new @path_params.merge(attrs.with_indifferent_access)
7171
end
7272

7373
def create(attrs = {})
74-
klass.create @path_params.merge(attrs.symbolize_keys)
74+
klass.create @path_params.merge(attrs.with_indifferent_access)
7575
end
7676

7777
def params

lib/json_api_client/resource.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,12 @@ def create(attributes = {})
172172
end
173173
end
174174

175+
def create!(attributes = {})
176+
new(attributes).tap do |resource|
177+
raise(Errors::RecordNotSaved.new("Failed to save the record", resource)) unless resource.save
178+
end
179+
end
180+
175181
# Within the given block, add these headers to all requests made by
176182
# the resource class
177183
#
@@ -348,7 +354,7 @@ def _build_connection(rebuild = false)
348354
#
349355
# @param params [Hash] Attributes, links, and relationships
350356
def initialize(params = {})
351-
params = params.symbolize_keys
357+
params = params.with_indifferent_access
352358
@persisted = nil
353359
@destroyed = nil
354360
self.links = self.class.linker.new(params.delete(:links) || {})
@@ -376,6 +382,11 @@ def update_attributes(attrs = {})
376382
save
377383
end
378384

385+
def update_attributes!(attrs = {})
386+
self.attributes = attrs
387+
save ? true : raise(Errors::RecordNotSaved.new("Failed to update the record", self))
388+
end
389+
379390
# Alias to update_attributes
380391
#
381392
# @param attrs [Hash] Attributes to update
@@ -384,6 +395,10 @@ def update(attrs = {})
384395
update_attributes(attrs)
385396
end
386397

398+
def update!(attrs = {})
399+
update_attributes!(attrs)
400+
end
401+
387402
# Mark the record as persisted
388403
def mark_as_persisted!
389404
@persisted = true
@@ -538,7 +553,7 @@ def reset_request_select!(*resource_types)
538553
end
539554

540555
def path_attributes
541-
_belongs_to_params.merge attributes.slice( self.class.primary_key ).symbolize_keys
556+
_belongs_to_params.merge attributes.slice( self.class.primary_key ).with_indifferent_access
542557
end
543558

544559
protected

lib/json_api_client/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module JsonApiClient
2-
VERSION = "1.18.0"
2+
VERSION = "1.21.0"
33
end

test/unit/creation_test.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,23 @@ def test_can_create_with_class_method
6666
assert_equal "Rails is Omakase", article.title
6767
end
6868

69+
def test_failed_create!
70+
stub_request(:post, "http://example.com/users")
71+
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
72+
errors: [
73+
{
74+
status: "400",
75+
title: "Error"
76+
}
77+
]
78+
}.to_json)
79+
80+
exception = assert_raises JsonApiClient::Errors::RecordNotSaved do
81+
User.create!(name: 'Hans')
82+
end
83+
assert_equal "Record not saved", exception.message
84+
end
85+
6986
def test_changed_attributes_empty_after_create_with_class_method
7087
stub_simple_creation
7188
article = Article.create({
@@ -206,7 +223,65 @@ def test_can_create_with_new_record_with_relationships_and_save
206223
assert article.persisted?
207224
assert_equal article.comments.length, 1
208225
assert_equal "1", article.id
226+
end
209227

228+
def test_can_create_with_new_record_with_associated_relationships_and_save
229+
stub_request(:post, "http://example.com/articles")
230+
.with(headers: {content_type: "application/vnd.api+json", accept: "application/vnd.api+json"}, body: {
231+
data: {
232+
type: "articles",
233+
relationships: {
234+
author: {
235+
data: {
236+
id: 1,
237+
type: "authors"
238+
}
239+
}
240+
},
241+
attributes: {
242+
title: "Rails is Omakase"
243+
}
244+
}
245+
}.to_json)
246+
.to_return(headers: {content_type: "application/vnd.api+json"}, body: {
247+
data: {
248+
type: "articles",
249+
id: "1",
250+
attributes: {
251+
title: "Rails is Omakase"
252+
},
253+
relationships: {
254+
author: {
255+
data: [
256+
{
257+
id: "1",
258+
type: "comments"
259+
}
260+
]
261+
}
262+
}
263+
},
264+
included: [
265+
{
266+
id: "1",
267+
type: "authors",
268+
}
269+
]
270+
}.to_json)
271+
272+
author_hash = {
273+
author: {
274+
data: {
275+
id: 1,
276+
type: 'authors'
277+
}
278+
}
279+
}
280+
article = Article.new({title: "Rails is Omakase", "relationships" => author_hash})
281+
282+
assert article.save
283+
assert article.persisted?
284+
assert_equal "1", article.id
210285
end
211286

212287
def test_correct_create_with_nil_attribute_value

test/unit/errors_test.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,51 @@ def test_not_authorized
9696
end
9797
end
9898

99+
def test_request_timeout
100+
stub_request(:get, "http://example.com/users")
101+
.to_return(headers: {content_type: "text/plain"}, status: 408, body: "request timeout")
102+
103+
assert_raises JsonApiClient::Errors::RequestTimeout do
104+
User.all
105+
end
106+
end
107+
108+
def test_too_many_requests
109+
stub_request(:get, "http://example.com/users")
110+
.to_return(headers: {content_type: "text/plain"}, status: 429, body: "too many requests")
111+
112+
assert_raises JsonApiClient::Errors::TooManyRequests do
113+
User.all
114+
end
115+
end
116+
117+
def test_bad_gateway
118+
stub_request(:get, "http://example.com/users")
119+
.to_return(headers: {content_type: "text/plain"}, status: 502, body: "bad gateway")
120+
121+
assert_raises JsonApiClient::Errors::BadGateway do
122+
User.all
123+
end
124+
end
125+
126+
def test_service_unavailable
127+
stub_request(:get, "http://example.com/users")
128+
.to_return(headers: {content_type: "text/plain"}, status: 503, body: "service unavailable")
129+
130+
assert_raises JsonApiClient::Errors::ServiceUnavailable do
131+
User.all
132+
end
133+
end
134+
135+
def test_gateway_timeout
136+
stub_request(:get, "http://example.com/users")
137+
.to_return(headers: {content_type: "text/plain"}, status: 504, body: "gateway timeout")
138+
139+
assert_raises JsonApiClient::Errors::GatewayTimeout do
140+
User.all
141+
end
142+
end
143+
99144
def test_errors_are_rescuable_by_default_rescue
100145
begin
101146
raise JsonApiClient::Errors::ApiError, "Something bad happened"

0 commit comments

Comments
 (0)