jsonapi-resources 0.2.0 → 0.3.0.pre1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +5 -2
- data/Gemfile +3 -1
- data/README.md +52 -13
- data/jsonapi-resources.gemspec +1 -1
- data/lib/jsonapi-resources.rb +1 -0
- data/lib/jsonapi/association.rb +1 -9
- data/lib/jsonapi/error_codes.rb +1 -0
- data/lib/jsonapi/exceptions.rb +9 -5
- data/lib/jsonapi/formatter.rb +9 -18
- data/lib/jsonapi/paginator.rb +4 -15
- data/lib/jsonapi/request.rb +26 -42
- data/lib/jsonapi/resource.rb +35 -45
- data/lib/jsonapi/resource_controller.rb +6 -32
- data/lib/jsonapi/resource_serializer.rb +62 -33
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/routing_ext.rb +4 -4
- data/test/config/database.yml +2 -1
- data/test/controllers/controller_test.rb +200 -160
- data/test/fixtures/active_record.rb +44 -201
- data/test/fixtures/book_comments.yml +11 -0
- data/test/fixtures/books.yml +6 -0
- data/test/fixtures/comments.yml +17 -0
- data/test/fixtures/comments_tags.yml +20 -0
- data/test/fixtures/expense_entries.yml +13 -0
- data/test/fixtures/facts.yml +11 -0
- data/test/fixtures/iso_currencies.yml +17 -0
- data/test/fixtures/people.yml +24 -0
- data/test/fixtures/posts.yml +96 -0
- data/test/fixtures/posts_tags.yml +59 -0
- data/test/fixtures/preferences.yml +18 -0
- data/test/fixtures/sections.yml +8 -0
- data/test/fixtures/tags.yml +39 -0
- data/test/helpers/hash_helpers.rb +0 -7
- data/test/integration/requests/request_test.rb +86 -28
- data/test/integration/routes/routes_test.rb +14 -25
- data/test/test_helper.rb +41 -17
- data/test/unit/jsonapi_request/jsonapi_request_test.rb +152 -0
- data/test/unit/operation/operations_processor_test.rb +13 -2
- data/test/unit/resource/resource_test.rb +68 -13
- data/test/unit/serializer/serializer_test.rb +328 -220
- metadata +33 -6
- data/lib/jsonapi/resource_for.rb +0 -29
@@ -0,0 +1,24 @@
|
|
1
|
+
a:
|
2
|
+
id: 1
|
3
|
+
name: Joe Author
|
4
|
+
email: joe@xyz.fake
|
5
|
+
date_joined: <%= DateTime.parse('2013-08-07 20:25:00 UTC +00:00') %>
|
6
|
+
preferences_id: 1
|
7
|
+
|
8
|
+
b:
|
9
|
+
id: 2
|
10
|
+
name: Fred Reader
|
11
|
+
email: fred@xyz.fake
|
12
|
+
date_joined: <%= DateTime.parse('2013-10-31 20:25:00 UTC +00:00') %>
|
13
|
+
|
14
|
+
c:
|
15
|
+
id: 3
|
16
|
+
name: Lazy Author
|
17
|
+
email: lazy@xyz.fake
|
18
|
+
date_joined: <%= DateTime.parse('2013-10-31 21:25:00 UTC +00:00') %>
|
19
|
+
|
20
|
+
d:
|
21
|
+
id: 4
|
22
|
+
name: Tag Crazy Author
|
23
|
+
email: taggy@xyz.fake
|
24
|
+
date_joined: <%= DateTime.parse('2013-11-30 4:20:00 UTC +00:00') %>
|
@@ -0,0 +1,96 @@
|
|
1
|
+
post_1:
|
2
|
+
id: 1
|
3
|
+
title: New post
|
4
|
+
body: A body!!!
|
5
|
+
author_id: 1
|
6
|
+
|
7
|
+
post_2:
|
8
|
+
id: 2
|
9
|
+
title: JR Solves your serialization woes!
|
10
|
+
body: Use JR
|
11
|
+
author_id: 1
|
12
|
+
section_id: 2
|
13
|
+
|
14
|
+
post_3:
|
15
|
+
id: 3
|
16
|
+
title: Update This Later
|
17
|
+
body: AAAA
|
18
|
+
author_id: 3
|
19
|
+
|
20
|
+
post_4:
|
21
|
+
id: 4
|
22
|
+
title: Delete This Later - Single
|
23
|
+
body: AAAA
|
24
|
+
author_id: 3
|
25
|
+
|
26
|
+
post_5:
|
27
|
+
id: 5
|
28
|
+
title: Delete This Later - Multiple1
|
29
|
+
body: AAAA
|
30
|
+
author_id: 3
|
31
|
+
|
32
|
+
post_6:
|
33
|
+
id: 6
|
34
|
+
title: Delete This Later - Multiple2
|
35
|
+
body: AAAA
|
36
|
+
author_id: 3
|
37
|
+
|
38
|
+
post_7:
|
39
|
+
id: 7
|
40
|
+
title: Delete This Later - Single2
|
41
|
+
body: AAAA
|
42
|
+
author_id: 3
|
43
|
+
|
44
|
+
post_8:
|
45
|
+
id: 8
|
46
|
+
title: Delete This Later - Multiple2-1
|
47
|
+
body: AAAA
|
48
|
+
author_id: 3
|
49
|
+
|
50
|
+
post_9:
|
51
|
+
id: 9
|
52
|
+
title: Delete This Later - Multiple2-2
|
53
|
+
body: AAAA
|
54
|
+
author_id: 3
|
55
|
+
|
56
|
+
post_10:
|
57
|
+
id: 10
|
58
|
+
title: Update This Later - Multiple
|
59
|
+
body: AAAA
|
60
|
+
author_id: 3
|
61
|
+
|
62
|
+
post_11:
|
63
|
+
id: 11
|
64
|
+
title: JR How To
|
65
|
+
body: Use JR to write API apps
|
66
|
+
author_id: 1
|
67
|
+
|
68
|
+
post_12:
|
69
|
+
id: 12
|
70
|
+
title: Tagged up post 1
|
71
|
+
body: AAAA
|
72
|
+
author_id: 4
|
73
|
+
|
74
|
+
post_13:
|
75
|
+
id: 13
|
76
|
+
title: Tagged up post 2
|
77
|
+
body: BBBB
|
78
|
+
author_id: 4
|
79
|
+
|
80
|
+
post_14:
|
81
|
+
id: 14
|
82
|
+
title: A First Post
|
83
|
+
body: A First Post!!!!!!!!!
|
84
|
+
author_id: 3
|
85
|
+
|
86
|
+
post_15:
|
87
|
+
id: 15
|
88
|
+
title: AAAA First Post
|
89
|
+
body: First!!!!!!!!!
|
90
|
+
author_id: 3
|
91
|
+
|
92
|
+
post_16:
|
93
|
+
id: 16
|
94
|
+
title: SDFGH
|
95
|
+
body: Not First!!!!
|
96
|
+
author_id: 3
|
@@ -0,0 +1,59 @@
|
|
1
|
+
post_1_short:
|
2
|
+
post_id: 1
|
3
|
+
tag_id: 1
|
4
|
+
|
5
|
+
post_1_whiny:
|
6
|
+
post_id: 1
|
7
|
+
tag_id: 2
|
8
|
+
|
9
|
+
post_1_grumpy:
|
10
|
+
post_id: 1
|
11
|
+
tag_id: 3
|
12
|
+
|
13
|
+
post_2_jr:
|
14
|
+
post_id: 2
|
15
|
+
tag_id: 5
|
16
|
+
|
17
|
+
post_11_jr:
|
18
|
+
post_id: 11
|
19
|
+
tag_id: 5
|
20
|
+
|
21
|
+
post_12_silly:
|
22
|
+
post_id: 12
|
23
|
+
tag_id: 6
|
24
|
+
|
25
|
+
post_12_sleepy:
|
26
|
+
post_id: 12
|
27
|
+
tag_id: 7
|
28
|
+
|
29
|
+
post_12_goofy:
|
30
|
+
post_id: 12
|
31
|
+
tag_id: 8
|
32
|
+
|
33
|
+
post_12_wacky:
|
34
|
+
post_id: 12
|
35
|
+
tag_id: 9
|
36
|
+
|
37
|
+
post_13_silly:
|
38
|
+
post_id: 13
|
39
|
+
tag_id: 6
|
40
|
+
|
41
|
+
post_13_sleepy:
|
42
|
+
post_id: 13
|
43
|
+
tag_id: 7
|
44
|
+
|
45
|
+
post_13_goofy:
|
46
|
+
post_id: 13
|
47
|
+
tag_id: 8
|
48
|
+
|
49
|
+
post_13_wacky:
|
50
|
+
post_id: 13
|
51
|
+
tag_id: 9
|
52
|
+
|
53
|
+
post_14_whiny:
|
54
|
+
post_id: 14
|
55
|
+
tag_id: 2
|
56
|
+
|
57
|
+
post_14_grumpy:
|
58
|
+
post_id: 14
|
59
|
+
tag_id: 3
|
@@ -0,0 +1,39 @@
|
|
1
|
+
short_tag:
|
2
|
+
id: 1
|
3
|
+
name: short
|
4
|
+
|
5
|
+
whiny_tag:
|
6
|
+
id: 2
|
7
|
+
name: whiny
|
8
|
+
|
9
|
+
grumpy_tag:
|
10
|
+
id: 3
|
11
|
+
name: grumpy
|
12
|
+
|
13
|
+
happy_tag:
|
14
|
+
id: 4
|
15
|
+
name: happy
|
16
|
+
|
17
|
+
jr_tag:
|
18
|
+
id: 5
|
19
|
+
name: JR
|
20
|
+
|
21
|
+
silly_tag:
|
22
|
+
id: 6
|
23
|
+
name: silly
|
24
|
+
|
25
|
+
sleepy_tag:
|
26
|
+
id: 7
|
27
|
+
name: sleepy
|
28
|
+
|
29
|
+
goofy_tag:
|
30
|
+
id: 8
|
31
|
+
name: goofy
|
32
|
+
|
33
|
+
wacky_tag:
|
34
|
+
id: 9
|
35
|
+
name: wacky
|
36
|
+
|
37
|
+
bad_tag:
|
38
|
+
id: 10
|
39
|
+
name: bad
|
@@ -1,15 +1,8 @@
|
|
1
1
|
module Helpers
|
2
2
|
module HashHelpers
|
3
|
-
# :nocov:
|
4
|
-
def assert_hash_contains(exp, act, msg = nil)
|
5
|
-
msg = message(msg, '') { diff exp, act }
|
6
|
-
assert(matches_hash?(exp, act), msg)
|
7
|
-
end
|
8
|
-
|
9
3
|
def assert_hash_equals(exp, act, msg = nil)
|
10
4
|
msg = message(msg, '') { diff exp, act }
|
11
5
|
assert(matches_hash?(exp, act, {exact: true}), msg)
|
12
6
|
end
|
13
|
-
# :nocov:
|
14
7
|
end
|
15
8
|
end
|
@@ -68,22 +68,31 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
68
68
|
JSONAPI.configuration.route_format = :camelized_route
|
69
69
|
get '/api/v4/expenseEntries/1/links/isoCurrency'
|
70
70
|
assert_equal 200, status
|
71
|
-
assert_hash_equals({'
|
71
|
+
assert_hash_equals({'links' => {
|
72
|
+
'self' => 'http://www.example.com/api/v4/expenseEntries/1/links/isoCurrency',
|
73
|
+
'related' => 'http://www.example.com/api/v4/expenseEntries/1/isoCurrency'
|
74
|
+
},
|
75
|
+
'data' => {
|
72
76
|
'type' => 'isoCurrencies',
|
73
|
-
'id' => 'USD'
|
74
|
-
|
75
|
-
|
77
|
+
'id' => 'USD'
|
78
|
+
}
|
79
|
+
}, json_response)
|
76
80
|
end
|
77
81
|
|
78
82
|
def test_put_single_without_content_type
|
79
83
|
put '/posts/3',
|
80
84
|
{
|
81
85
|
'data' => {
|
82
|
-
'
|
83
|
-
|
86
|
+
'linkage' => {
|
87
|
+
'type' => 'posts',
|
88
|
+
'id' => '3',
|
89
|
+
},
|
84
90
|
'title' => 'A great new Post',
|
85
91
|
'links' => {
|
86
|
-
'tags' =>
|
92
|
+
'tags' => [
|
93
|
+
{type: 'tags', id: 3},
|
94
|
+
{type: 'tags', id: 4}
|
95
|
+
]
|
87
96
|
}
|
88
97
|
}
|
89
98
|
}.to_json, "CONTENT_TYPE" => "application/json"
|
@@ -99,7 +108,10 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
99
108
|
'id' => '3',
|
100
109
|
'title' => 'A great new Post',
|
101
110
|
'links' => {
|
102
|
-
'tags' =>
|
111
|
+
'tags' => [
|
112
|
+
{type: 'tags', id: 3},
|
113
|
+
{type: 'tags', id: 4}
|
114
|
+
]
|
103
115
|
}
|
104
116
|
}
|
105
117
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -113,7 +125,10 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
113
125
|
'posts' => {
|
114
126
|
'title' => 'A great new Post',
|
115
127
|
'links' => {
|
116
|
-
'tags' => [
|
128
|
+
'tags' => [
|
129
|
+
{type: 'tags', id: 3},
|
130
|
+
{type: 'tags', id: 4}
|
131
|
+
]
|
117
132
|
}
|
118
133
|
}
|
119
134
|
}.to_json, "CONTENT_TYPE" => "application/json"
|
@@ -139,12 +154,19 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
139
154
|
|
140
155
|
def test_update_association_without_content_type
|
141
156
|
ruby = Section.find_by(name: 'ruby')
|
142
|
-
|
157
|
+
patch '/posts/3/links/section', { 'sections' => {type: 'sections', id: ruby.id.to_s }}.to_json
|
143
158
|
|
144
159
|
assert_equal 415, status
|
145
160
|
end
|
146
161
|
|
147
|
-
def
|
162
|
+
def test_patch_update_association_has_one
|
163
|
+
ruby = Section.find_by(name: 'ruby')
|
164
|
+
patch '/posts/3/links/section', { 'data' => {type: 'sections', id: ruby.id.to_s }}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
165
|
+
|
166
|
+
assert_equal 204, status
|
167
|
+
end
|
168
|
+
|
169
|
+
def test_put_update_association_has_one
|
148
170
|
ruby = Section.find_by(name: 'ruby')
|
149
171
|
put '/posts/3/links/section', { 'data' => {type: 'sections', id: ruby.id.to_s }}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
150
172
|
|
@@ -169,7 +191,29 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
169
191
|
'id' => '3',
|
170
192
|
'title' => 'A great new Post',
|
171
193
|
'links' => {
|
172
|
-
'tags' =>
|
194
|
+
'tags' => [
|
195
|
+
{type: 'tags', id: 3},
|
196
|
+
{type: 'tags', id: 4}
|
197
|
+
]
|
198
|
+
}
|
199
|
+
}
|
200
|
+
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
201
|
+
|
202
|
+
assert_match JSONAPI::MEDIA_TYPE, headers['Content-Type']
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_patch_content_type
|
206
|
+
patch '/posts/3',
|
207
|
+
{
|
208
|
+
'data' => {
|
209
|
+
'type' => 'posts',
|
210
|
+
'id' => '3',
|
211
|
+
'title' => 'A great new Post',
|
212
|
+
'links' => {
|
213
|
+
'tags' => [
|
214
|
+
{type: 'tags', id: 3},
|
215
|
+
{type: 'tags', id: 4}
|
216
|
+
]
|
173
217
|
}
|
174
218
|
}
|
175
219
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -246,7 +290,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
246
290
|
assert_equal 200, status
|
247
291
|
assert_equal 2, json_response['data'].size
|
248
292
|
assert_equal 'http://www.example.com/api/v2/books/1/book_comments',
|
249
|
-
json_response['data'][
|
293
|
+
json_response['data'][1]['links']['book_comments']['related']
|
250
294
|
end
|
251
295
|
|
252
296
|
def test_pagination_related_resources_data
|
@@ -255,7 +299,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
255
299
|
get '/api/v2/books/1/book_comments?page[limit]=10'
|
256
300
|
assert_equal 200, status
|
257
301
|
assert_equal 10, json_response['data'].size
|
258
|
-
assert_equal 'This is comment 9 on book
|
302
|
+
assert_equal 'This is comment 9 on book 1.', json_response['data'][9]['body']
|
259
303
|
end
|
260
304
|
|
261
305
|
def test_pagination_related_resources_data_includes
|
@@ -264,7 +308,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
264
308
|
get '/api/v2/books/1/book_comments?page[limit]=10&include=author,book'
|
265
309
|
assert_equal 200, status
|
266
310
|
assert_equal 10, json_response['data'].size
|
267
|
-
assert_equal 'This is comment 9 on book
|
311
|
+
assert_equal 'This is comment 9 on book 1.', json_response['data'][9]['body']
|
268
312
|
end
|
269
313
|
|
270
314
|
def test_flow_self
|
@@ -284,7 +328,13 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
284
328
|
|
285
329
|
get post_1['links']['author']['self']
|
286
330
|
assert_equal 200, status
|
287
|
-
assert_hash_equals(json_response, {
|
331
|
+
assert_hash_equals(json_response, {
|
332
|
+
'links' => {
|
333
|
+
'self' => 'http://www.example.com/posts/1/links/author',
|
334
|
+
'related' => 'http://www.example.com/posts/1/author'
|
335
|
+
},
|
336
|
+
'data' => {type: 'people', id: '1'}
|
337
|
+
})
|
288
338
|
end
|
289
339
|
|
290
340
|
def test_flow_link_has_many_self_link
|
@@ -295,21 +345,26 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
295
345
|
get post_1['links']['tags']['self']
|
296
346
|
assert_equal 200, status
|
297
347
|
assert_hash_equals(json_response,
|
298
|
-
{
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
348
|
+
{
|
349
|
+
'links' => {
|
350
|
+
'self' => 'http://www.example.com/posts/1/links/tags',
|
351
|
+
'related' => 'http://www.example.com/posts/1/tags'
|
352
|
+
},
|
353
|
+
'data' => [
|
354
|
+
{type: 'tags', id: '1'},
|
355
|
+
{type: 'tags', id: '2'},
|
356
|
+
{type: 'tags', id: '3'}
|
357
|
+
]
|
303
358
|
})
|
304
359
|
end
|
305
360
|
|
306
361
|
def test_flow_link_has_many_self_link_put
|
307
362
|
get '/posts'
|
308
363
|
assert_equal 200, status
|
309
|
-
post_1 = json_response['data'][
|
364
|
+
post_1 = json_response['data'][4]
|
310
365
|
|
311
366
|
post post_1['links']['tags']['self'],
|
312
|
-
{'data' => {'type' => 'tags', '
|
367
|
+
{'data' => [{'type' => 'tags', 'id' => '10'}]}.to_json,
|
313
368
|
"CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
314
369
|
|
315
370
|
assert_equal 204, status
|
@@ -317,11 +372,14 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
317
372
|
get post_1['links']['tags']['self']
|
318
373
|
assert_equal 200, status
|
319
374
|
assert_hash_equals(json_response,
|
320
|
-
{
|
321
|
-
'
|
322
|
-
|
323
|
-
|
324
|
-
|
375
|
+
{
|
376
|
+
'links' => {
|
377
|
+
'self' => 'http://www.example.com/posts/5/links/tags',
|
378
|
+
'related' => 'http://www.example.com/posts/5/tags'
|
379
|
+
},
|
380
|
+
'data' => [
|
381
|
+
{type: 'tags', id: '10'}
|
382
|
+
]
|
325
383
|
})
|
326
384
|
end
|
327
385
|
|