jsonapi-resources 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +274 -102
- data/jsonapi-resources.gemspec +1 -0
- data/lib/jsonapi-resources.rb +15 -0
- data/lib/jsonapi/active_record_operations_processor.rb +21 -10
- data/lib/jsonapi/acts_as_resource_controller.rb +175 -0
- data/lib/jsonapi/configuration.rb +11 -0
- data/lib/jsonapi/error_codes.rb +7 -4
- data/lib/jsonapi/exceptions.rb +23 -15
- data/lib/jsonapi/formatter.rb +5 -5
- data/lib/jsonapi/include_directives.rb +67 -0
- data/lib/jsonapi/operation.rb +185 -65
- data/lib/jsonapi/operation_result.rb +38 -5
- data/lib/jsonapi/operation_results.rb +33 -0
- data/lib/jsonapi/operations_processor.rb +49 -9
- data/lib/jsonapi/paginator.rb +31 -17
- data/lib/jsonapi/request.rb +347 -163
- data/lib/jsonapi/resource.rb +159 -56
- data/lib/jsonapi/resource_controller.rb +1 -234
- data/lib/jsonapi/resource_serializer.rb +55 -69
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +87 -0
- data/lib/jsonapi/routing_ext.rb +17 -11
- data/test/controllers/controller_test.rb +602 -326
- data/test/fixtures/active_record.rb +96 -6
- data/test/fixtures/line_items.yml +7 -1
- data/test/fixtures/numeros_telefone.yml +3 -0
- data/test/fixtures/purchase_orders.yml +6 -0
- data/test/integration/requests/request_test.rb +129 -60
- data/test/integration/routes/routes_test.rb +17 -17
- data/test/test_helper.rb +23 -5
- data/test/unit/jsonapi_request/jsonapi_request_test.rb +48 -0
- data/test/unit/operation/operations_processor_test.rb +242 -54
- data/test/unit/resource/resource_test.rb +108 -2
- data/test/unit/serializer/include_directives_test.rb +108 -0
- data/test/unit/serializer/response_document_test.rb +61 -0
- data/test/unit/serializer/serializer_test.rb +679 -520
- metadata +26 -2
@@ -3,6 +3,7 @@ require 'jsonapi-resources'
|
|
3
3
|
|
4
4
|
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
5
5
|
inflect.uncountable 'preferences'
|
6
|
+
inflect.irregular 'numero_telefone', 'numeros_telefone'
|
6
7
|
end
|
7
8
|
|
8
9
|
### DATABASE
|
@@ -156,6 +157,16 @@ ActiveRecord::Schema.define do
|
|
156
157
|
create_table :hair_cuts, force: true do |t|
|
157
158
|
t.string :style
|
158
159
|
end
|
160
|
+
|
161
|
+
create_table :numeros_telefone, force: true do |t|
|
162
|
+
t.string :numero_telefone
|
163
|
+
t.timestamps null: false
|
164
|
+
end
|
165
|
+
|
166
|
+
create_table :categories, force: true do |t|
|
167
|
+
t.string :name
|
168
|
+
t.string :status, limit: 10
|
169
|
+
end
|
159
170
|
end
|
160
171
|
|
161
172
|
### MODELS
|
@@ -211,6 +222,16 @@ class Planet < ActiveRecord::Base
|
|
211
222
|
belongs_to :planet_type
|
212
223
|
|
213
224
|
has_and_belongs_to_many :tags, join_table: :planets_tags
|
225
|
+
|
226
|
+
# Test model callback cancelling save
|
227
|
+
before_save :check_not_pluto
|
228
|
+
|
229
|
+
def check_not_pluto
|
230
|
+
# Pluto can't be a planet, so cancel the save
|
231
|
+
if name.downcase == 'pluto'
|
232
|
+
return false
|
233
|
+
end
|
234
|
+
end
|
214
235
|
end
|
215
236
|
|
216
237
|
class PlanetType < ActiveRecord::Base
|
@@ -242,6 +263,7 @@ class Breed
|
|
242
263
|
@id = id
|
243
264
|
end
|
244
265
|
@name = name
|
266
|
+
@errors = ActiveModel::Errors.new(self)
|
245
267
|
end
|
246
268
|
|
247
269
|
attr_accessor :id, :name
|
@@ -250,7 +272,18 @@ class Breed
|
|
250
272
|
$breed_data.remove(@id)
|
251
273
|
end
|
252
274
|
|
253
|
-
def
|
275
|
+
def valid?
|
276
|
+
@errors.clear
|
277
|
+
if name.is_a?(String) && name.length > 0
|
278
|
+
return true
|
279
|
+
else
|
280
|
+
@errors.set(:name, ["can't be blank"])
|
281
|
+
return false
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
def errors
|
286
|
+
@errors
|
254
287
|
end
|
255
288
|
end
|
256
289
|
|
@@ -304,6 +337,12 @@ class LineItem < ActiveRecord::Base
|
|
304
337
|
belongs_to :purchase_order
|
305
338
|
end
|
306
339
|
|
340
|
+
class NumeroTelefone < ActiveRecord::Base
|
341
|
+
end
|
342
|
+
|
343
|
+
class Category < ActiveRecord::Base
|
344
|
+
end
|
345
|
+
|
307
346
|
### PORO Data - don't do this in a production app
|
308
347
|
$breed_data = BreedData.new
|
309
348
|
$breed_data.add(Breed.new(0, 'persian'))
|
@@ -311,6 +350,19 @@ $breed_data.add(Breed.new(1, 'siamese'))
|
|
311
350
|
$breed_data.add(Breed.new(2, 'sphinx'))
|
312
351
|
$breed_data.add(Breed.new(3, 'to_delete'))
|
313
352
|
|
353
|
+
### OperationsProcessor
|
354
|
+
class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
|
355
|
+
after_find_operation do
|
356
|
+
|
357
|
+
count = @operation.resource_klass.find_count(@operation.resource_klass.verify_filters(@operation.filters, @context),
|
358
|
+
context: @context,
|
359
|
+
include_directives: @operation.include_directives,
|
360
|
+
sort_criteria: @operation.sort_criteria)
|
361
|
+
|
362
|
+
@operation_meta[:total_records] = count
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
314
366
|
### CONTROLLERS
|
315
367
|
class AuthorsController < JSONAPI::ResourceController
|
316
368
|
end
|
@@ -318,7 +370,8 @@ end
|
|
318
370
|
class PeopleController < JSONAPI::ResourceController
|
319
371
|
end
|
320
372
|
|
321
|
-
class PostsController <
|
373
|
+
class PostsController < ActionController::Base
|
374
|
+
include JSONAPI::ActsAsResourceController
|
322
375
|
end
|
323
376
|
|
324
377
|
class CommentsController < JSONAPI::ResourceController
|
@@ -342,6 +395,9 @@ end
|
|
342
395
|
class FactsController < JSONAPI::ResourceController
|
343
396
|
end
|
344
397
|
|
398
|
+
class CategoriesController < JSONAPI::ResourceController
|
399
|
+
end
|
400
|
+
|
345
401
|
### CONTROLLERS
|
346
402
|
module Api
|
347
403
|
module V1
|
@@ -351,7 +407,8 @@ module Api
|
|
351
407
|
class PeopleController < JSONAPI::ResourceController
|
352
408
|
end
|
353
409
|
|
354
|
-
class PostsController <
|
410
|
+
class PostsController < ActionController::Base
|
411
|
+
include JSONAPI::ActsAsResourceController
|
355
412
|
end
|
356
413
|
|
357
414
|
class TagsController < JSONAPI::ResourceController
|
@@ -459,6 +516,11 @@ module Api
|
|
459
516
|
class OrderFlagsController < JSONAPI::ResourceController
|
460
517
|
end
|
461
518
|
end
|
519
|
+
|
520
|
+
module V8
|
521
|
+
class NumerosTelefoneController < JSONAPI::ResourceController
|
522
|
+
end
|
523
|
+
end
|
462
524
|
end
|
463
525
|
|
464
526
|
### RESOURCES
|
@@ -492,6 +554,8 @@ class CommentResource < JSONAPI::Resource
|
|
492
554
|
has_one :post
|
493
555
|
has_one :author, class_name: 'Person'
|
494
556
|
has_many :tags
|
557
|
+
|
558
|
+
filters :body
|
495
559
|
end
|
496
560
|
|
497
561
|
class TagResource < JSONAPI::Resource
|
@@ -555,11 +619,11 @@ class PostResource < JSONAPI::Resource
|
|
555
619
|
filters :title, :author, :tags, :comments
|
556
620
|
filters :id, :ids
|
557
621
|
|
558
|
-
def self.
|
622
|
+
def self.updatable_fields(context)
|
559
623
|
super(context) - [:author, :subject]
|
560
624
|
end
|
561
625
|
|
562
|
-
def self.
|
626
|
+
def self.creatable_fields(context)
|
563
627
|
super(context) - [:subject]
|
564
628
|
end
|
565
629
|
|
@@ -643,6 +707,11 @@ class BreedResource < JSONAPI::Resource
|
|
643
707
|
def self.find_by_key(id, options = {})
|
644
708
|
BreedResource.new($breed_data.breeds[id.to_i], options[:context])
|
645
709
|
end
|
710
|
+
|
711
|
+
def _save
|
712
|
+
super
|
713
|
+
return :accepted
|
714
|
+
end
|
646
715
|
end
|
647
716
|
|
648
717
|
class PlanetResource < JSONAPI::Resource
|
@@ -696,6 +765,10 @@ class FactResource < JSONAPI::Resource
|
|
696
765
|
attribute :cool
|
697
766
|
end
|
698
767
|
|
768
|
+
class CategoryResource < JSONAPI::Resource
|
769
|
+
filter :status, default: 'active'
|
770
|
+
end
|
771
|
+
|
699
772
|
module Api
|
700
773
|
module V1
|
701
774
|
class WriterResource < JSONAPI::Resource
|
@@ -782,6 +855,10 @@ module Api
|
|
782
855
|
class BookResource < Api::V2::BookResource
|
783
856
|
paginator :paged
|
784
857
|
end
|
858
|
+
|
859
|
+
class BookCommentResource < Api::V2::BookCommentResource
|
860
|
+
paginator :paged
|
861
|
+
end
|
785
862
|
end
|
786
863
|
end
|
787
864
|
|
@@ -866,6 +943,12 @@ module Api
|
|
866
943
|
OrderFlagResource = V6::OrderFlagResource.dup
|
867
944
|
LineItemResource = V6::LineItemResource.dup
|
868
945
|
end
|
946
|
+
|
947
|
+
module V8
|
948
|
+
class NumeroTelefoneResource < JSONAPI::Resource
|
949
|
+
attribute :numero_telefone
|
950
|
+
end
|
951
|
+
end
|
869
952
|
end
|
870
953
|
|
871
954
|
warn 'start testing Name Collisions'
|
@@ -896,10 +979,17 @@ saturn = Planet.create(name: 'Satern',
|
|
896
979
|
description: 'Saturn is the sixth planet from the Sun and the second largest planet in the Solar System, after Jupiter.',
|
897
980
|
planet_type_id: planetoid.id)
|
898
981
|
titan = Moon.create(name:'Titan', description: 'Best known of the Saturn moons.', planet_id: saturn.id)
|
899
|
-
|
982
|
+
makemake = Planet.create(name: 'Makemake', description: 'A small planetoid in the Kuiperbelt.', planet_type_id: planetoid.id)
|
900
983
|
uranus = Planet.create(name: 'Uranus', description: 'Insert adolescent jokes here.', planet_type_id: gas_giant.id)
|
901
984
|
jupiter = Planet.create(name: 'Jupiter', description: 'A gas giant.', planet_type_id: gas_giant.id)
|
902
985
|
betax = Planet.create(name: 'Beta X', description: 'Newly discovered Planet X', planet_type_id: unknown.id)
|
903
986
|
betay = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Y', planet_type_id: unknown.id)
|
904
987
|
betaz = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Z', planet_type_id: unknown.id)
|
905
988
|
betaw = Planet.create(name: 'Beta W', description: 'Newly discovered Planet W')
|
989
|
+
Category.create(name: 'Category A', status: 'active')
|
990
|
+
Category.create(name: 'Category B', status: 'active')
|
991
|
+
Category.create(name: 'Category C', status: 'active')
|
992
|
+
Category.create(name: 'Category D', status: 'inactive')
|
993
|
+
Category.create(name: 'Category E', status: 'inactive')
|
994
|
+
Category.create(name: 'Category F', status: 'inactive')
|
995
|
+
Category.create(name: 'Category G', status: 'inactive')
|
@@ -1,8 +1,6 @@
|
|
1
1
|
require File.expand_path('../../../test_helper', __FILE__)
|
2
|
-
require File.expand_path('../../../fixtures/active_record', __FILE__)
|
3
2
|
|
4
3
|
class RequestTest < ActionDispatch::IntegrationTest
|
5
|
-
|
6
4
|
def setup
|
7
5
|
JSONAPI.configuration.json_key_format = :underscored_key
|
8
6
|
JSONAPI.configuration.route_format = :underscored_route
|
@@ -18,6 +16,11 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
18
16
|
assert_equal 200, status
|
19
17
|
end
|
20
18
|
|
19
|
+
def test_get_inflected_resource
|
20
|
+
get '/api/v8/numeros_telefone'
|
21
|
+
assert_equal 200, status
|
22
|
+
end
|
23
|
+
|
21
24
|
def test_get_nested_has_one
|
22
25
|
get '/posts/1/author'
|
23
26
|
assert_equal 200, status
|
@@ -45,7 +48,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
45
48
|
get '/iso_currencies?filter[country_name]=Canada'
|
46
49
|
assert_equal 200, status
|
47
50
|
assert_equal 1, json_response['data'].size
|
48
|
-
assert_equal 'Canada', json_response['data'][0]['country_name']
|
51
|
+
assert_equal 'Canada', json_response['data'][0]['attributes']['country_name']
|
49
52
|
end
|
50
53
|
|
51
54
|
def test_get_camelized_key_filtered
|
@@ -53,7 +56,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
53
56
|
get '/iso_currencies?filter[countryName]=Canada'
|
54
57
|
assert_equal 200, status
|
55
58
|
assert_equal 1, json_response['data'].size
|
56
|
-
assert_equal 'Canada', json_response['data'][0]['countryName']
|
59
|
+
assert_equal 'Canada', json_response['data'][0]['attributes']['countryName']
|
57
60
|
end
|
58
61
|
|
59
62
|
def test_get_camelized_route_and_key_filtered
|
@@ -61,16 +64,16 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
61
64
|
get '/api/v4/isoCurrencies?filter[countryName]=Canada'
|
62
65
|
assert_equal 200, status
|
63
66
|
assert_equal 1, json_response['data'].size
|
64
|
-
assert_equal 'Canada', json_response['data'][0]['countryName']
|
67
|
+
assert_equal 'Canada', json_response['data'][0]['attributes']['countryName']
|
65
68
|
end
|
66
69
|
|
67
70
|
def test_get_camelized_route_and_links
|
68
71
|
JSONAPI.configuration.json_key_format = :camelized_key
|
69
72
|
JSONAPI.configuration.route_format = :camelized_route
|
70
|
-
get '/api/v4/expenseEntries/1/
|
73
|
+
get '/api/v4/expenseEntries/1/relationships/isoCurrency'
|
71
74
|
assert_equal 200, status
|
72
75
|
assert_hash_equals({'links' => {
|
73
|
-
'self' => 'http://www.example.com/api/v4/expenseEntries/1/
|
76
|
+
'self' => 'http://www.example.com/api/v4/expenseEntries/1/relationships/isoCurrency',
|
74
77
|
'related' => 'http://www.example.com/api/v4/expenseEntries/1/isoCurrency'
|
75
78
|
},
|
76
79
|
'data' => {
|
@@ -88,7 +91,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
88
91
|
'type' => 'posts',
|
89
92
|
'id' => '3',
|
90
93
|
},
|
91
|
-
'
|
94
|
+
'attributes' => {
|
95
|
+
'title' => 'A great new Post'
|
96
|
+
},
|
92
97
|
'links' => {
|
93
98
|
'tags' => [
|
94
99
|
{type: 'tags', id: 3},
|
@@ -107,10 +112,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
107
112
|
'data' => {
|
108
113
|
'type' => 'posts',
|
109
114
|
'id' => '3',
|
110
|
-
'
|
111
|
-
|
115
|
+
'attributes' => {
|
116
|
+
'title' => 'A great new Post'
|
117
|
+
},
|
118
|
+
'relationships' => {
|
112
119
|
'tags' => {
|
113
|
-
'
|
120
|
+
'data' => [
|
114
121
|
{type: 'tags', id: 3},
|
115
122
|
{type: 'tags', id: 4}
|
116
123
|
]
|
@@ -126,10 +133,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
126
133
|
post '/posts',
|
127
134
|
{
|
128
135
|
'posts' => {
|
129
|
-
'
|
130
|
-
|
136
|
+
'attributes' => {
|
137
|
+
'title' => 'A great new Post'
|
138
|
+
},
|
139
|
+
'relationships' => {
|
131
140
|
'tags' => {
|
132
|
-
'
|
141
|
+
'data' => [
|
133
142
|
{type: 'tags', id: 3},
|
134
143
|
{type: 'tags', id: 4}
|
135
144
|
]
|
@@ -146,10 +155,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
146
155
|
{
|
147
156
|
'data' => {
|
148
157
|
'type' => 'posts',
|
149
|
-
'
|
150
|
-
|
151
|
-
|
152
|
-
|
158
|
+
'attributes' => {
|
159
|
+
'title' => 'A great new Post',
|
160
|
+
'body' => 'JSONAPIResources is the greatest thing since unsliced bread.'
|
161
|
+
},
|
162
|
+
'relationships' => {
|
163
|
+
'author' => {'data' => {type: 'people', id: '3'}}
|
153
164
|
}
|
154
165
|
}
|
155
166
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -176,9 +187,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
176
187
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
177
188
|
|
178
189
|
assert_equal 201, status
|
179
|
-
assert_nil json_response['data']['body']
|
180
|
-
assert_nil json_response['data']['
|
181
|
-
assert_nil json_response['data']['
|
190
|
+
assert_nil json_response['data']['attributes']['body']
|
191
|
+
assert_nil json_response['data']['relationships']['post']['data']
|
192
|
+
assert_nil json_response['data']['relationships']['author']['data']
|
182
193
|
end
|
183
194
|
|
184
195
|
def test_post_single_minimal_invalid
|
@@ -194,21 +205,21 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
194
205
|
|
195
206
|
def test_update_association_without_content_type
|
196
207
|
ruby = Section.find_by(name: 'ruby')
|
197
|
-
patch '/posts/3/
|
208
|
+
patch '/posts/3/relationships/section', { 'data' => {type: 'sections', id: ruby.id.to_s }}.to_json
|
198
209
|
|
199
210
|
assert_equal 415, status
|
200
211
|
end
|
201
212
|
|
202
213
|
def test_patch_update_association_has_one
|
203
214
|
ruby = Section.find_by(name: 'ruby')
|
204
|
-
patch '/posts/3/
|
215
|
+
patch '/posts/3/relationships/section', { 'data' => {type: 'sections', id: ruby.id.to_s }}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
205
216
|
|
206
217
|
assert_equal 204, status
|
207
218
|
end
|
208
219
|
|
209
220
|
def test_put_update_association_has_one
|
210
221
|
ruby = Section.find_by(name: 'ruby')
|
211
|
-
put '/posts/3/
|
222
|
+
put '/posts/3/relationships/section', { 'data' => {type: 'sections', id: ruby.id.to_s }}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
212
223
|
|
213
224
|
assert_equal 204, status
|
214
225
|
end
|
@@ -217,14 +228,14 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
217
228
|
# Comments are acts_as_set=false so PUT/PATCH should respond with 403
|
218
229
|
|
219
230
|
rogue = Comment.find_by(body: 'Rogue Comment Here')
|
220
|
-
patch '/posts/5/
|
231
|
+
patch '/posts/5/relationships/comments', { 'data' => [{type: 'comments', id: rogue.id.to_s }]}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
221
232
|
|
222
233
|
assert_equal 403, status
|
223
234
|
end
|
224
235
|
|
225
236
|
def test_post_update_association_has_many
|
226
237
|
rogue = Comment.find_by(body: 'Rogue Comment Here')
|
227
|
-
post '/posts/5/
|
238
|
+
post '/posts/5/relationships/comments', { 'data' => [{type: 'comments', id: rogue.id.to_s }]}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
228
239
|
|
229
240
|
assert_equal 204, status
|
230
241
|
end
|
@@ -233,7 +244,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
233
244
|
# Comments are acts_as_set=false so PUT/PATCH should respond with 403. Note: JR currently treats PUT and PATCH as equivalent
|
234
245
|
|
235
246
|
rogue = Comment.find_by(body: 'Rogue Comment Here')
|
236
|
-
put '/posts/5/
|
247
|
+
put '/posts/5/relationships/comments', { 'data' => [{type: 'comments', id: rogue.id.to_s }]}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
237
248
|
|
238
249
|
assert_equal 403, status
|
239
250
|
end
|
@@ -254,10 +265,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
254
265
|
'data' => {
|
255
266
|
'type' => 'posts',
|
256
267
|
'id' => '3',
|
257
|
-
'
|
258
|
-
|
268
|
+
'attributes' => {
|
269
|
+
'title' => 'A great new Post'
|
270
|
+
},
|
271
|
+
'relationships' => {
|
259
272
|
'tags' => {
|
260
|
-
'
|
273
|
+
'data' => [
|
261
274
|
{type: 'tags', id: 3},
|
262
275
|
{type: 'tags', id: 4}
|
263
276
|
]
|
@@ -275,10 +288,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
275
288
|
'data' => {
|
276
289
|
'type' => 'posts',
|
277
290
|
'id' => '3',
|
278
|
-
'
|
279
|
-
|
291
|
+
'attributes' => {
|
292
|
+
'title' => 'A great new Post'
|
293
|
+
},
|
294
|
+
'relationships' => {
|
280
295
|
'tags' => {
|
281
|
-
'
|
296
|
+
'data' => [
|
282
297
|
{type: 'tags', id: 3},
|
283
298
|
{type: 'tags', id: 4}
|
284
299
|
]
|
@@ -295,9 +310,11 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
295
310
|
{
|
296
311
|
'data' => {
|
297
312
|
'type' => 'posts',
|
298
|
-
'
|
299
|
-
|
300
|
-
|
313
|
+
'attributes' => {
|
314
|
+
'title' => 'A great new Post'
|
315
|
+
},
|
316
|
+
'relationships' => {
|
317
|
+
'author' => {'data' => {type: 'people', id: '3'}}
|
301
318
|
}
|
302
319
|
}
|
303
320
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -328,7 +345,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
328
345
|
get '/api/v2/books'
|
329
346
|
assert_equal 200, status
|
330
347
|
assert_equal JSONAPI.configuration.default_page_size, json_response['data'].size
|
331
|
-
assert_equal 'Book 0', json_response['data'][0]['title']
|
348
|
+
assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
|
332
349
|
end
|
333
350
|
|
334
351
|
def test_pagination_offset_style_offset
|
@@ -336,7 +353,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
336
353
|
get '/api/v2/books?page[offset]=50'
|
337
354
|
assert_equal 200, status
|
338
355
|
assert_equal JSONAPI.configuration.default_page_size, json_response['data'].size
|
339
|
-
assert_equal 'Book 50', json_response['data'][0]['title']
|
356
|
+
assert_equal 'Book 50', json_response['data'][0]['attributes']['title']
|
340
357
|
end
|
341
358
|
|
342
359
|
def test_pagination_offset_style_offset_limit
|
@@ -344,7 +361,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
344
361
|
get '/api/v2/books?page[offset]=50&page[limit]=20'
|
345
362
|
assert_equal 200, status
|
346
363
|
assert_equal 20, json_response['data'].size
|
347
|
-
assert_equal 'Book 50', json_response['data'][0]['title']
|
364
|
+
assert_equal 'Book 50', json_response['data'][0]['attributes']['title']
|
348
365
|
end
|
349
366
|
|
350
367
|
def test_pagination_offset_bad_param
|
@@ -359,7 +376,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
359
376
|
assert_equal 200, status
|
360
377
|
assert_equal 2, json_response['data'].size
|
361
378
|
assert_equal 'http://www.example.com/api/v2/books/1/book_comments',
|
362
|
-
json_response['data'][1]['
|
379
|
+
json_response['data'][1]['relationships']['book_comments']['links']['related']
|
363
380
|
end
|
364
381
|
|
365
382
|
def test_pagination_related_resources_data
|
@@ -368,7 +385,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
368
385
|
get '/api/v2/books/1/book_comments?page[limit]=10'
|
369
386
|
assert_equal 200, status
|
370
387
|
assert_equal 10, json_response['data'].size
|
371
|
-
assert_equal 'This is comment 9 on book 1.', json_response['data'][9]['body']
|
388
|
+
assert_equal 'This is comment 9 on book 1.', json_response['data'][9]['attributes']['body']
|
372
389
|
end
|
373
390
|
|
374
391
|
def test_pagination_related_resources_data_includes
|
@@ -377,7 +394,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
377
394
|
get '/api/v2/books/1/book_comments?page[limit]=10&include=author,book'
|
378
395
|
assert_equal 200, status
|
379
396
|
assert_equal 10, json_response['data'].size
|
380
|
-
assert_equal 'This is comment 9 on book 1.', json_response['data'][9]['body']
|
397
|
+
assert_equal 'This is comment 9 on book 1.', json_response['data'][9]['attributes']['body']
|
381
398
|
end
|
382
399
|
|
383
400
|
def test_flow_self
|
@@ -395,11 +412,11 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
395
412
|
assert_equal 200, status
|
396
413
|
post_1 = json_response['data'][0]
|
397
414
|
|
398
|
-
get post_1['
|
415
|
+
get post_1['relationships']['author']['links']['self']
|
399
416
|
assert_equal 200, status
|
400
417
|
assert_hash_equals(json_response, {
|
401
418
|
'links' => {
|
402
|
-
'self' => 'http://www.example.com/posts/1/
|
419
|
+
'self' => 'http://www.example.com/posts/1/relationships/author',
|
403
420
|
'related' => 'http://www.example.com/posts/1/author'
|
404
421
|
},
|
405
422
|
'data' => {type: 'people', id: '1'}
|
@@ -411,12 +428,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
411
428
|
assert_equal 200, status
|
412
429
|
post_1 = json_response['data'][0]
|
413
430
|
|
414
|
-
get post_1['
|
431
|
+
get post_1['relationships']['tags']['links']['self']
|
415
432
|
assert_equal 200, status
|
416
433
|
assert_hash_equals(json_response,
|
417
434
|
{
|
418
435
|
'links' => {
|
419
|
-
'self' => 'http://www.example.com/posts/1/
|
436
|
+
'self' => 'http://www.example.com/posts/1/relationships/tags',
|
420
437
|
'related' => 'http://www.example.com/posts/1/tags'
|
421
438
|
},
|
422
439
|
'data' => [
|
@@ -432,18 +449,18 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
432
449
|
assert_equal 200, status
|
433
450
|
post_1 = json_response['data'][4]
|
434
451
|
|
435
|
-
post post_1['
|
452
|
+
post post_1['relationships']['tags']['links']['self'],
|
436
453
|
{'data' => [{'type' => 'tags', 'id' => '10'}]}.to_json,
|
437
454
|
"CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
438
455
|
|
439
456
|
assert_equal 204, status
|
440
457
|
|
441
|
-
get post_1['
|
458
|
+
get post_1['relationships']['tags']['links']['self']
|
442
459
|
assert_equal 200, status
|
443
460
|
assert_hash_equals(json_response,
|
444
461
|
{
|
445
462
|
'links' => {
|
446
|
-
'self' => 'http://www.example.com/posts/5/
|
463
|
+
'self' => 'http://www.example.com/posts/5/relationships/tags',
|
447
464
|
'related' => 'http://www.example.com/posts/5/tags'
|
448
465
|
},
|
449
466
|
'data' => [
|
@@ -499,7 +516,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
499
516
|
post '/api/v6/purchase-orders',
|
500
517
|
{
|
501
518
|
'data' => {
|
502
|
-
'
|
519
|
+
'attributes' => {
|
520
|
+
'delivery-name' => 'ASDFG Corp'
|
521
|
+
},
|
503
522
|
'type' => 'purchase-orders'
|
504
523
|
}
|
505
524
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -513,7 +532,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
513
532
|
post '/api/v6/purchase-orders',
|
514
533
|
{
|
515
534
|
'data' => {
|
516
|
-
'
|
535
|
+
'attributes' => {
|
536
|
+
'delivery_name' => 'ASDFG Corp'
|
537
|
+
},
|
517
538
|
'type' => 'purchase_orders'
|
518
539
|
}
|
519
540
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -527,7 +548,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
527
548
|
post '/api/v7/purchase_orders',
|
528
549
|
{
|
529
550
|
'data' => {
|
530
|
-
'
|
551
|
+
'attributes' => {
|
552
|
+
'delivery-name' => 'ASDFG Corp'
|
553
|
+
},
|
531
554
|
'type' => 'purchase-orders'
|
532
555
|
}
|
533
556
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -541,7 +564,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
541
564
|
post '/api/v6/purchase-orders',
|
542
565
|
{
|
543
566
|
'data' => {
|
544
|
-
'
|
567
|
+
'attributes' => {
|
568
|
+
'delivery_name' => 'ASDFG Corp'
|
569
|
+
},
|
545
570
|
'type' => 'purchase-orders'
|
546
571
|
}
|
547
572
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -556,7 +581,9 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
556
581
|
{
|
557
582
|
'data' => {
|
558
583
|
'id' => '1',
|
559
|
-
'
|
584
|
+
'attributes' => {
|
585
|
+
'delivery-name' => 'ASDFG Corp'
|
586
|
+
},
|
560
587
|
'type' => 'purchase-orders'
|
561
588
|
}
|
562
589
|
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
@@ -572,10 +599,12 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
572
599
|
'data' => {
|
573
600
|
'id' => '1',
|
574
601
|
'type' => 'line-items',
|
575
|
-
'
|
576
|
-
|
602
|
+
'attributes' => {
|
603
|
+
'item-cost' => '23.57'
|
604
|
+
},
|
605
|
+
'relationships' => {
|
577
606
|
'purchase-order' => {
|
578
|
-
'
|
607
|
+
'data' => {'type' => 'purchase-orders', 'id' => '2'}
|
579
608
|
}
|
580
609
|
}
|
581
610
|
}
|
@@ -592,15 +621,15 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
592
621
|
'data' => {
|
593
622
|
'id' => '2',
|
594
623
|
'type' => 'purchase-orders',
|
595
|
-
'
|
624
|
+
'relationships' => {
|
596
625
|
'line-items' => {
|
597
|
-
'
|
626
|
+
'data' => [
|
598
627
|
{'type' => 'line-items', 'id' => '3'},
|
599
628
|
{'type' => 'line-items', 'id' => '4'}
|
600
629
|
]
|
601
630
|
},
|
602
631
|
'order-flags' => {
|
603
|
-
'
|
632
|
+
'data' => [
|
604
633
|
{'type' => 'order-flags', 'id' => '1'},
|
605
634
|
{'type' => 'order-flags', 'id' => '2'}
|
606
635
|
]
|
@@ -611,4 +640,44 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
611
640
|
|
612
641
|
assert_equal 200, status
|
613
642
|
end
|
643
|
+
|
644
|
+
def test_post_has_many_link
|
645
|
+
JSONAPI.configuration.route_format = :dasherized_route
|
646
|
+
JSONAPI.configuration.json_key_format = :dasherized_key
|
647
|
+
post '/api/v6/purchase-orders/3/relationships/line-items',
|
648
|
+
{
|
649
|
+
'data' => [
|
650
|
+
{'type' => 'line-items', 'id' => '3'},
|
651
|
+
{'type' => 'line-items', 'id' => '4'}
|
652
|
+
]
|
653
|
+
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
654
|
+
|
655
|
+
assert_equal 204, status
|
656
|
+
end
|
657
|
+
|
658
|
+
def test_patch_has_many_link
|
659
|
+
JSONAPI.configuration.route_format = :dasherized_route
|
660
|
+
JSONAPI.configuration.json_key_format = :dasherized_key
|
661
|
+
patch '/api/v6/purchase-orders/3/relationships/order-flags',
|
662
|
+
{
|
663
|
+
'data' => [
|
664
|
+
{'type' => 'order-flags', 'id' => '1'},
|
665
|
+
{'type' => 'order-flags', 'id' => '2'}
|
666
|
+
]
|
667
|
+
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
668
|
+
|
669
|
+
assert_equal 204, status
|
670
|
+
end
|
671
|
+
|
672
|
+
def test_patch_has_one
|
673
|
+
JSONAPI.configuration.route_format = :dasherized_route
|
674
|
+
JSONAPI.configuration.json_key_format = :dasherized_key
|
675
|
+
patch '/api/v6/line-items/5/relationships/purchase-order',
|
676
|
+
{
|
677
|
+
'data' => {'type' => 'purchase-orders', 'id' => '3'}
|
678
|
+
}.to_json, "CONTENT_TYPE" => JSONAPI::MEDIA_TYPE
|
679
|
+
|
680
|
+
assert_equal 204, status
|
681
|
+
end
|
682
|
+
|
614
683
|
end
|