jsonapi-resources 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -65,8 +65,10 @@ module ActionDispatch
65
65
  res._associations.each do |association_name, association|
66
66
  if association.is_a?(JSONAPI::Association::HasMany)
67
67
  jsonapi_links(association_name)
68
+ jsonapi_related_resources(association_name)
68
69
  else
69
70
  jsonapi_link(association_name)
71
+ jsonapi_related_resource(association_name)
70
72
  end
71
73
  end
72
74
  end
@@ -95,22 +97,17 @@ module ActionDispatch
95
97
 
96
98
  if methods.include?(:show)
97
99
  match "links/#{formatted_association_name}", controller: res._type.to_s,
98
- action: 'show_association', association: link_type.to_s, via: [:get]
99
- end
100
-
101
- if methods.include?(:create)
102
- match "links/#{formatted_association_name}", controller: res._type.to_s,
103
- action: 'create_association', association: link_type.to_s, via: [:post]
100
+ action: 'show_association', association: link_type.to_s, via: [:get]
104
101
  end
105
102
 
106
103
  if methods.include?(:update)
107
104
  match "links/#{formatted_association_name}", controller: res._type.to_s,
108
- action: 'update_association', association: link_type.to_s, via: [:put]
105
+ action: 'update_association', association: link_type.to_s, via: [:put]
109
106
  end
110
107
 
111
108
  if methods.include?(:destroy)
112
109
  match "links/#{formatted_association_name}", controller: res._type.to_s,
113
- action: 'destroy_association', association: link_type.to_s, via: [:delete]
110
+ action: 'destroy_association', association: link_type.to_s, via: [:delete]
114
111
  end
115
112
  end
116
113
 
@@ -125,25 +122,54 @@ module ActionDispatch
125
122
 
126
123
  if methods.include?(:show)
127
124
  match "links/#{formatted_association_name}", controller: res._type.to_s,
128
- action: 'show_association', association: link_type.to_s, via: [:get]
125
+ action: 'show_association', association: link_type.to_s, via: [:get]
129
126
  end
130
127
 
131
128
  if methods.include?(:create)
132
129
  match "links/#{formatted_association_name}", controller: res._type.to_s,
133
- action: 'create_association', association: link_type.to_s, via: [:post]
130
+ action: 'create_association', association: link_type.to_s, via: [:post]
134
131
  end
135
132
 
136
133
  if methods.include?(:update) && res._association(link_type).acts_as_set
137
134
  match "links/#{formatted_association_name}", controller: res._type.to_s,
138
- action: 'update_association', association: link_type.to_s, via: [:put]
135
+ action: 'update_association', association: link_type.to_s, via: [:put]
139
136
  end
140
137
 
141
138
  if methods.include?(:destroy)
142
139
  match "links/#{formatted_association_name}/:keys", controller: res._type.to_s,
143
- action: 'destroy_association', association: link_type.to_s, via: [:delete]
140
+ action: 'destroy_association', association: link_type.to_s, via: [:delete]
144
141
  end
145
142
  end
146
143
 
144
+ def jsonapi_related_resource(*association)
145
+ source = JSONAPI::Resource.resource_for(resource_type_with_module_prefix)
146
+
147
+ association_name = association.first
148
+ association = source._associations[association_name]
149
+
150
+ formatted_association_name = format_route(association.name)
151
+ related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(association.name.pluralize))
152
+
153
+ match "#{formatted_association_name}", controller: related_resource._type.to_s,
154
+ association: association.name, source: resource_type_with_module_prefix(source._type),
155
+ action: 'get_related_resource', via: [:get]
156
+ end
157
+
158
+ def jsonapi_related_resources(*association)
159
+ source = JSONAPI::Resource.resource_for(resource_type_with_module_prefix)
160
+
161
+ association_name = association.first
162
+ association = source._associations[association_name]
163
+
164
+ formatted_association_name = format_route(association.name)
165
+ related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(association.name))
166
+
167
+ match "#{formatted_association_name}", controller: related_resource._type.to_s,
168
+ association: association.name, source: resource_type_with_module_prefix(source._type),
169
+ action: 'get_related_resources', via: [:get]
170
+ end
171
+
172
+ private
147
173
  def resource_type_with_module_prefix(resource = nil)
148
174
  resource_name = resource || @scope[:jsonapi_resource]
149
175
  [@scope[:module], resource_name].compact.collect(&:to_s).join("/")
@@ -9,198 +9,211 @@ class PostsControllerTest < ActionController::TestCase
9
9
  def test_index
10
10
  get :index
11
11
  assert_response :success
12
- assert json_response['posts'].is_a?(Array)
12
+ assert json_response['data'].is_a?(Array)
13
13
  end
14
14
 
15
15
  def test_index_filter_with_empty_result
16
- get :index, {title: 'post that does not exist'}
16
+ get :index, {filter: {title: 'post that does not exist'}}
17
17
  assert_response :success
18
- assert json_response['posts'].is_a?(Array)
19
- assert_equal 0, json_response['posts'].size
18
+ assert json_response['data'].is_a?(Array)
19
+ assert_equal 0, json_response['data'].size
20
20
  end
21
21
 
22
22
  def test_index_filter_by_id
23
- get :index, {id: '1'}
23
+ get :index, {filter: {id: '1'}}
24
24
  assert_response :success
25
- assert json_response['posts'].is_a?(Array)
25
+ assert json_response['data'].is_a?(Array)
26
+ assert_equal 1, json_response['data'].size
26
27
  end
27
28
 
28
29
  def test_index_filter_by_title
29
- get :index, {title: 'New post'}
30
+ get :index, {filter: {title: 'New post'}}
30
31
  assert_response :success
31
- assert json_response['posts'].is_a?(Array)
32
+ assert json_response['data'].is_a?(Array)
33
+ assert_equal 1, json_response['data'].size
32
34
  end
33
35
 
34
36
  def test_index_filter_by_ids
35
- get :index, {ids: '1,2'}
37
+ get :index, {filter: {ids: '1,2'}}
36
38
  assert_response :success
37
- assert json_response['posts'].is_a?(Array)
38
- assert_equal 2, json_response['posts'].size
39
+ assert json_response['data'].is_a?(Array)
40
+ assert_equal 2, json_response['data'].size
39
41
  end
40
42
 
41
43
  def test_index_filter_by_ids_and_include_related
42
- get :index, ids: '2', include: 'comments'
44
+ get :index, {filter: {id: '2'}, include: 'comments'}
43
45
  assert_response :success
44
- assert_equal 1, json_response['posts'].size
45
- assert_equal 1, json_response['linked']['comments'].size
46
+ assert_equal 1, json_response['data'].size
47
+ assert_equal 1, json_response['linked'].size
46
48
  end
47
49
 
48
50
  def test_index_filter_by_ids_and_include_related_different_type
49
- get :index, {ids: '1,2', include: 'author'}
51
+ get :index, {filter: {id: '1,2'}, include: 'author'}
50
52
  assert_response :success
51
- assert_equal 2, json_response['posts'].size
52
- assert_equal 1, json_response['linked']['people'].size
53
+ assert_equal 2, json_response['data'].size
54
+ assert_equal 1, json_response['linked'].size
53
55
  end
54
56
 
55
57
  def test_index_filter_by_ids_and_fields
56
- get :index, {ids: '1,2', 'fields' => 'id,title,author'}
58
+ get :index, {filter: {id: '1,2'}, 'fields' => 'id,title,author'}
57
59
  assert_response :success
58
- assert_equal 2, json_response['posts'].size
60
+ assert_equal 2, json_response['data'].size
59
61
 
60
- # id, title, links
61
- assert_equal 3, json_response['posts'][0].size
62
- assert json_response['posts'][0].has_key?('id')
63
- assert json_response['posts'][0].has_key?('title')
64
- assert json_response['posts'][0].has_key?('links')
62
+ # type, id, title, links
63
+ assert_equal 4, json_response['data'][0].size
64
+ assert json_response['data'][0].has_key?('type')
65
+ assert json_response['data'][0].has_key?('id')
66
+ assert json_response['data'][0].has_key?('title')
67
+ assert json_response['data'][0].has_key?('links')
65
68
  end
66
69
 
67
70
  def test_index_filter_by_ids_and_fields_specify_type
68
- get :index, {ids: '1,2', 'fields' => {'posts' => 'id,title,author'}}
71
+ get :index, {filter: {id: '1,2'}, 'fields' => {'posts' => 'id,title,author'}}
69
72
  assert_response :success
70
- assert_equal 2, json_response['posts'].size
73
+ assert_equal 2, json_response['data'].size
71
74
 
72
- # id, title, links
73
- assert_equal 3, json_response['posts'][0].size
74
- assert json_response['posts'][0].has_key?('id')
75
- assert json_response['posts'][0].has_key?('title')
76
- assert json_response['posts'][0].has_key?('links')
75
+ # type, id, title, links
76
+ assert_equal 4, json_response['data'][0].size
77
+ assert json_response['data'][0].has_key?('type')
78
+ assert json_response['data'][0].has_key?('id')
79
+ assert json_response['data'][0].has_key?('title')
80
+ assert json_response['data'][0].has_key?('links')
77
81
  end
78
82
 
79
83
  def test_index_filter_by_ids_and_fields_specify_unrelated_type
80
- get :index, {ids: '1,2', 'fields' => {'currencies' => 'code'}}
84
+ get :index, {filter: {id: '1,2'}, 'fields' => {'currencies' => 'code'}}
81
85
  assert_response :bad_request
82
86
  assert_match /currencies is not a valid resource./, json_response['errors'][0]['detail']
83
87
  end
84
88
 
85
89
  def test_index_filter_by_ids_and_fields_2
86
- get :index, {ids: '1,2', 'fields' => 'author'}
90
+ get :index, {filter: {id: '1,2'}, 'fields' => 'author'}
87
91
  assert_response :success
88
- assert_equal 2, json_response['posts'].size
92
+ assert_equal 2, json_response['data'].size
89
93
 
90
- # links
91
- assert_equal 1, json_response['posts'][0].size
92
- assert json_response['posts'][0].has_key?('links')
94
+ # links, id, type
95
+ assert_equal 3, json_response['data'][0].size
96
+ assert json_response['data'][0].has_key?('type')
97
+ assert json_response['data'][0].has_key?('id')
98
+ assert json_response['data'][0]['links'].has_key?('author')
93
99
  end
94
100
 
95
101
  def test_filter_association_single
96
- get :index, {tags: '5,1'}
102
+ get :index, {filter: {tags: '5,1'}}
97
103
  assert_response :success
98
- assert_equal 3, json_response['posts'].size
104
+ assert_equal 3, json_response['data'].size
99
105
  assert_match /New post/, response.body
100
106
  assert_match /JR Solves your serialization woes!/, response.body
101
107
  assert_match /JR How To/, response.body
102
108
  end
103
109
 
104
110
  def test_filter_associations_multiple
105
- get :index, {tags: '5,1', comments: '3'}
111
+ get :index, {filter: {tags: '5,1', comments: '3'}}
106
112
  assert_response :success
107
- assert_equal 1, json_response['posts'].size
113
+ assert_equal 1, json_response['data'].size
108
114
  assert_match /JR Solves your serialization woes!/, response.body
109
115
  end
110
116
 
111
117
  def test_filter_associations_multiple_not_found
112
- get :index, {tags: '1', comments: '3'}
118
+ get :index, {filter: {tags: '1', comments: '3'}}
113
119
  assert_response :success
114
- assert_equal 0, json_response['posts'].size
120
+ assert_equal 0, json_response['data'].size
115
121
  end
116
122
 
117
123
  def test_bad_filter
118
- get :index, {post_ids: '1,2'}
124
+ get :index, {filter: {post_ids: '1,2'}}
119
125
  assert_response :bad_request
120
126
  assert_match /post_ids is not allowed/, response.body
121
127
  end
122
128
 
123
129
  def test_bad_filter_value_not_integer_array
124
- get :index, {ids: 'asdfg'}
130
+ get :index, {filter: {id: 'asdfg'}}
125
131
  assert_response :bad_request
126
132
  assert_match /asdfg is not a valid value for id/, response.body
127
133
  end
128
134
 
129
135
  def test_bad_filter_value_not_integer
130
- get :index, {id: 'asdfg'}
136
+ get :index, {filter: {id: 'asdfg'}}
131
137
  assert_response :bad_request
132
138
  assert_match /asdfg is not a valid value for id/, response.body
133
139
  end
134
140
 
135
141
  def test_bad_filter_value_not_found_array
136
- get :index, {ids: '5412333'}
142
+ get :index, {filter: {id: '5412333'}}
137
143
  assert_response :not_found
138
144
  assert_match /5412333 could not be found/, response.body
139
145
  end
140
146
 
141
147
  def test_bad_filter_value_not_found
142
- get :index, {id: '5412333'}
148
+ get :index, {filter: {id: '5412333'}}
143
149
  assert_response :not_found
144
150
  assert_match /5412333 could not be found/, json_response['errors'][0]['detail']
145
151
  end
146
152
 
147
153
  def test_index_malformed_fields
148
- get :index, {ids: '1,2', 'fields' => 'posts'}
154
+ get :index, {filter: {id: '1,2'}, 'fields' => 'posts'}
149
155
  assert_response :bad_request
150
156
  assert_match /posts is not a valid field for posts./, json_response['errors'][0]['detail']
151
157
  end
152
158
 
153
159
  def test_field_not_supported
154
- get :index, {ids: '1,2', 'fields' => {'posts' => 'id,title,rank,author'}}
160
+ get :index, {filter: {id: '1,2'}, 'fields' => {'posts' => 'id,title,rank,author'}}
155
161
  assert_response :bad_request
156
162
  assert_match /rank is not a valid field for posts./, json_response['errors'][0]['detail']
157
163
  end
158
164
 
159
165
  def test_resource_not_supported
160
- get :index, {ids: '1,2', 'fields' => {'posters' => 'id,title'}}
166
+ get :index, {filter: {id: '1,2'}, 'fields' => {'posters' => 'id,title'}}
161
167
  assert_response :bad_request
162
168
  assert_match /posters is not a valid resource./, json_response['errors'][0]['detail']
163
169
  end
164
170
 
165
171
  def test_index_filter_on_association
166
- get :index, {author: '1'}
172
+ get :index, {filter: {author: '1'}}
167
173
  assert_response :success
168
- assert_equal 3, json_response['posts'].size
174
+ assert_equal 3, json_response['data'].size
169
175
  end
170
176
 
171
177
  def test_sorting_asc
172
- get :index, {sort: 'title'}
178
+ get :index, {sort: '+title'}
173
179
 
174
180
  assert_response :success
175
- assert_equal "Delete This Later - Multiple2-1", json_response['posts'][0]['title']
181
+ assert_equal "Delete This Later - Multiple2-1", json_response['data'][0]['title']
176
182
  end
177
183
 
178
184
  def test_sorting_desc
179
185
  get :index, {sort: '-title'}
180
186
 
181
187
  assert_response :success
182
- assert_equal "Update This Later - Multiple", json_response['posts'][0]['title']
188
+ assert_equal "Update This Later - Multiple", json_response['data'][0]['title']
183
189
  end
184
190
 
185
191
  def test_sorting_by_multiple_fields
186
- get :index, {sort: 'title,body'}
192
+ get :index, {sort: '+title,+body'}
187
193
 
188
194
  assert_response :success
189
- assert_equal '8', json_response['posts'][0]['id']
195
+ assert_equal '8', json_response['data'][0]['id']
190
196
  end
191
197
 
192
198
  def test_invalid_sort_param
193
- get :index, {sort: 'asdfg'}
199
+ get :index, {sort: '+asdfg'}
200
+
201
+ assert_response :bad_request
202
+ assert_match /asdfg is not a valid sort criteria for post/, response.body
203
+ end
204
+
205
+ def test_invalid_sort_param_missing_direction
206
+ get :index, {sort: 'title'}
194
207
 
195
208
  assert_response :bad_request
196
- assert_match /asdfg is not a valid sort param for post/, response.body
209
+ assert_match /title must start with a direction/, response.body
197
210
  end
198
211
 
199
212
  def test_excluded_sort_param
200
- get :index, {sort: 'id'}
213
+ get :index, {sort: '+id'}
201
214
 
202
215
  assert_response :bad_request
203
- assert_match /id is not a valid sort param for post/, response.body
216
+ assert_match /id is not a valid sort criteria for post/, response.body
204
217
  end
205
218
 
206
219
  # ToDo: test validating the parameter values
@@ -212,33 +225,30 @@ class PostsControllerTest < ActionController::TestCase
212
225
  def test_show_single
213
226
  get :show, {id: '1'}
214
227
  assert_response :success
215
- assert json_response['posts'].is_a?(Hash)
216
- assert_equal 'New post', json_response['posts']['title']
217
- assert_equal 'A body!!!', json_response['posts']['body']
218
- assert_equal ['1', '2', '3'], json_response['posts']['links']['tags']
219
- assert_equal ['1', '2'], json_response['posts']['links']['comments']
228
+ assert json_response['data'].is_a?(Hash)
229
+ assert_equal 'New post', json_response['data']['title']
230
+ assert_equal 'A body!!!', json_response['data']['body']
220
231
  assert_nil json_response['linked']
221
232
  end
222
233
 
223
234
  def test_show_single_with_includes
224
235
  get :show, {id: '1', include: 'comments'}
225
236
  assert_response :success
226
- assert json_response['posts'].is_a?(Hash)
227
- assert_equal 'New post', json_response['posts']['title']
228
- assert_equal 'A body!!!', json_response['posts']['body']
229
- assert_equal ['1', '2', '3'], json_response['posts']['links']['tags']
230
- assert_equal ['1', '2'], json_response['posts']['links']['comments']
231
- assert_equal 2, json_response['linked']['comments'].size
232
- assert_nil json_response['linked']['tags']
237
+ assert json_response['data'].is_a?(Hash)
238
+ assert_equal 'New post', json_response['data']['title']
239
+ assert_equal 'A body!!!', json_response['data']['body']
240
+ assert_nil json_response['data']['links']['tags']['ids']
241
+ assert_equal ['1', '2'], json_response['data']['links']['comments']['ids']
242
+ assert_equal 2, json_response['linked'].size
233
243
  end
234
244
 
235
245
  def test_show_single_with_fields
236
246
  get :show, {id: '1', fields: 'author'}
237
247
  assert_response :success
238
- assert json_response['posts'].is_a?(Hash)
239
- assert_nil json_response['posts']['title']
240
- assert_nil json_response['posts']['body']
241
- assert_equal '1', json_response['posts']['links']['author']
248
+ assert json_response['data'].is_a?(Hash)
249
+ assert_nil json_response['data']['title']
250
+ assert_nil json_response['data']['body']
251
+ assert_equal '1', json_response['data']['links']['author']['id']
242
252
  end
243
253
 
244
254
  def test_show_single_invalid_id_format
@@ -269,31 +279,33 @@ class PostsControllerTest < ActionController::TestCase
269
279
  set_content_type_header!
270
280
  post :create,
271
281
  {
272
- posts: {
282
+ data: {
283
+ type: 'posts',
273
284
  title: 'JR is Great',
274
285
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
275
286
  links: {
276
- author: 3
287
+ author: {type: 'people', id: '3'}
277
288
  }
278
289
  }
279
290
  }
280
291
 
281
292
  assert_response :created
282
- assert json_response['posts'].is_a?(Hash)
283
- assert_equal '3', json_response['posts']['links']['author']
284
- assert_equal 'JR is Great', json_response['posts']['title']
285
- assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['posts']['body']
293
+ assert json_response['data'].is_a?(Hash)
294
+ assert_equal '3', json_response['data']['links']['author']['id']
295
+ assert_equal 'JR is Great', json_response['data']['title']
296
+ assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['body']
286
297
  end
287
298
 
288
299
  def test_create_link_to_missing_object
289
300
  set_content_type_header!
290
301
  post :create,
291
302
  {
292
- posts: {
303
+ data: {
304
+ type: 'posts',
293
305
  title: 'JR is Great',
294
306
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
295
307
  links: {
296
- author: 304567
308
+ author: {type: 'people', id: '304567'}
297
309
  }
298
310
  }
299
311
  }
@@ -307,12 +319,13 @@ class PostsControllerTest < ActionController::TestCase
307
319
  set_content_type_header!
308
320
  post :create,
309
321
  {
310
- posts: {
322
+ data: {
323
+ type: 'posts',
311
324
  asdfg: 'aaaa',
312
325
  title: 'JR is Great',
313
326
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
314
327
  links: {
315
- author: 3
328
+ author: {type: 'people', id: '3'}
316
329
  }
317
330
  }
318
331
  }
@@ -325,7 +338,8 @@ class PostsControllerTest < ActionController::TestCase
325
338
  set_content_type_header!
326
339
  post :create,
327
340
  {
328
- posts: {
341
+ data: {
342
+ type: 'posts',
329
343
  title: 'JSONAPIResources is the greatest thing...',
330
344
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
331
345
  links: {
@@ -349,28 +363,30 @@ class PostsControllerTest < ActionController::TestCase
349
363
  set_content_type_header!
350
364
  post :create,
351
365
  {
352
- posts: [
366
+ data: [
353
367
  {
368
+ type: 'posts',
354
369
  title: 'JR is Great',
355
370
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
356
371
  links: {
357
- author: 3
372
+ author: {type: 'people', id: '3'}
358
373
  }
359
374
  },
360
375
  {
376
+ type: 'posts',
361
377
  title: 'Ember is Great',
362
378
  body: 'Ember is the greatest thing since unsliced bread.',
363
379
  links: {
364
- author: 3
380
+ author: {type: 'people', id: '3'}
365
381
  }
366
382
  }
367
383
  ]
368
384
  }
369
385
 
370
386
  assert_response :created
371
- assert json_response['posts'].is_a?(Array)
372
- assert_equal json_response['posts'].size, 2
373
- assert_equal json_response['posts'][0]['links']['author'], '3'
387
+ assert json_response['data'].is_a?(Array)
388
+ assert_equal json_response['data'].size, 2
389
+ assert_equal json_response['data'][0]['links']['author']['id'], '3'
374
390
  assert_match /JR is Great/, response.body
375
391
  assert_match /Ember is Great/, response.body
376
392
  end
@@ -379,19 +395,21 @@ class PostsControllerTest < ActionController::TestCase
379
395
  set_content_type_header!
380
396
  post :create,
381
397
  {
382
- posts: [
398
+ data: [
383
399
  {
400
+ type: 'posts',
384
401
  Title: 'JR is Great',
385
402
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
386
403
  links: {
387
- author: 3
404
+ author: {type: 'people', id: '3'}
388
405
  }
389
406
  },
390
407
  {
408
+ type: 'posts',
391
409
  title: 'Ember is Great',
392
410
  BODY: 'Ember is the greatest thing since unsliced bread.',
393
411
  links: {
394
- author: 3
412
+ author: {type: 'people', id: '3'}
395
413
  }
396
414
  }
397
415
  ]
@@ -405,28 +423,65 @@ class PostsControllerTest < ActionController::TestCase
405
423
  set_content_type_header!
406
424
  post :create,
407
425
  {
408
- posts_spelled_wrong: {
426
+ data_spelled_wrong: {
427
+ type: 'posts',
428
+ title: 'JR is Great',
429
+ body: 'JSONAPIResources is the greatest thing since unsliced bread.',
430
+ links: {
431
+ author: {type: 'people', id: '3'}
432
+ }
433
+ }
434
+ }
435
+
436
+ assert_response :bad_request
437
+ assert_match /The required parameter, data, is missing./, json_response['errors'][0]['detail']
438
+ end
439
+
440
+ def test_create_simple_wrong_type
441
+ set_content_type_header!
442
+ post :create,
443
+ {
444
+ data: {
445
+ type: 'posts_spelled_wrong',
446
+ title: 'JR is Great',
447
+ body: 'JSONAPIResources is the greatest thing since unsliced bread.',
448
+ links: {
449
+ author: {type: 'people', id: '3'}
450
+ }
451
+ }
452
+ }
453
+
454
+ assert_response :bad_request
455
+ assert_match /posts_spelled_wrong is not a valid resource./, json_response['errors'][0]['detail']
456
+ end
457
+
458
+ def test_create_simple_missing_type
459
+ set_content_type_header!
460
+ post :create,
461
+ {
462
+ data: {
409
463
  title: 'JR is Great',
410
464
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
411
465
  links: {
412
- author: 3
466
+ author: {type: 'people', id: '3'}
413
467
  }
414
468
  }
415
469
  }
416
470
 
417
471
  assert_response :bad_request
418
- assert_match /The required parameter, posts, is missing./, json_response['errors'][0]['detail']
472
+ assert_match /The required parameter, type, is missing./, json_response['errors'][0]['detail']
419
473
  end
420
474
 
421
475
  def test_create_simple_unpermitted_attributes
422
476
  set_content_type_header!
423
477
  post :create,
424
478
  {
425
- posts: {
479
+ data: {
480
+ type: 'posts',
426
481
  subject: 'JR is Great',
427
482
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
428
483
  links: {
429
- author: 3
484
+ author: {type: 'people', id: '3'}
430
485
  }
431
486
  }
432
487
  }
@@ -435,38 +490,61 @@ class PostsControllerTest < ActionController::TestCase
435
490
  assert_match /subject/, json_response['errors'][0]['detail']
436
491
  end
437
492
 
438
- def test_create_with_links
493
+ def test_create_with_links_has_many_type_ids
439
494
  set_content_type_header!
440
495
  post :create,
441
496
  {
442
- posts: {
497
+ data: {
498
+ type: 'posts',
443
499
  title: 'JR is Great',
444
500
  body: 'JSONAPIResources is the greatest thing since unsliced bread.',
445
501
  links: {
446
- author: 3,
447
- tags: [3, 4]
502
+ author: {type: 'people', id: '3'},
503
+ tags: {type: 'tags', ids: [3, 4]}
448
504
  }
449
505
  }
450
506
  }
451
507
 
452
508
  assert_response :created
453
- assert json_response['posts'].is_a?(Hash)
454
- assert_equal '3', json_response['posts']['links']['author']
455
- assert_equal 'JR is Great', json_response['posts']['title']
456
- assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['posts']['body']
457
- assert_equal ['3', '4'], json_response['posts']['links']['tags']
509
+ assert json_response['data'].is_a?(Hash)
510
+ assert_equal '3', json_response['data']['links']['author']['id']
511
+ assert_equal 'JR is Great', json_response['data']['title']
512
+ assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['body']
513
+ end
514
+
515
+ def test_create_with_links_has_many_array
516
+ set_content_type_header!
517
+ post :create,
518
+ {
519
+ data: {
520
+ type: 'posts',
521
+ title: 'JR is Great',
522
+ body: 'JSONAPIResources is the greatest thing since unsliced bread.',
523
+ links: {
524
+ author: {type: 'people', id: '3'},
525
+ tags: [{type: 'tags', id: '3'}, {type: 'tags', id: '4'}]
526
+ }
527
+ }
528
+ }
529
+
530
+ assert_response :created
531
+ assert json_response['data'].is_a?(Hash)
532
+ assert_equal '3', json_response['data']['links']['author']['id']
533
+ assert_equal 'JR is Great', json_response['data']['title']
534
+ assert_equal 'JSONAPIResources is the greatest thing since unsliced bread.', json_response['data']['body']
458
535
  end
459
536
 
460
537
  def test_create_with_links_include_and_fields
461
538
  set_content_type_header!
462
539
  post :create,
463
540
  {
464
- posts: {
541
+ data: {
542
+ type: 'posts',
465
543
  title: 'JR is Great!',
466
544
  body: 'JSONAPIResources is the greatest thing since unsliced bread!',
467
545
  links: {
468
- author: 3,
469
- tags: [3, 4]
546
+ author: {type: 'people', id: '3'},
547
+ tags: {type: 'tags', ids: [3, 4]}
470
548
  }
471
549
  },
472
550
  include: 'author,author.posts',
@@ -474,14 +552,10 @@ class PostsControllerTest < ActionController::TestCase
474
552
  }
475
553
 
476
554
  assert_response :created
477
- assert json_response['posts'].is_a?(Hash)
478
- assert_equal '3', json_response['posts']['links']['author']
479
- assert_equal 'JR is Great!', json_response['posts']['title']
480
- assert_equal nil, json_response['posts']['body']
481
- assert_equal nil, json_response['posts']['links']['tags']
482
- assert_not_nil json_response['linked']['posts']
483
- assert_not_nil json_response['linked']['people']
484
- assert_nil json_response['linked']['tags']
555
+ assert json_response['data'].is_a?(Hash)
556
+ assert_equal '3', json_response['data']['links']['author']['id']
557
+ assert_equal 'JR is Great!', json_response['data']['title']
558
+ assert_not_nil json_response['linked'].size
485
559
  end
486
560
 
487
561
  def test_update_with_links
@@ -491,22 +565,25 @@ class PostsControllerTest < ActionController::TestCase
491
565
  put :update,
492
566
  {
493
567
  id: 3,
494
- posts: {
568
+ data: {
569
+ id: '3',
570
+ type: 'posts',
495
571
  title: 'A great new Post',
496
572
  links: {
497
- section: javascript.id,
498
- tags: [3, 4]
573
+ section: {type: 'sections', id: "#{javascript.id}"},
574
+ tags: {type: 'tags', ids: [3, 4]}
499
575
  }
500
- }
576
+ },
577
+ include: 'tags'
501
578
  }
502
579
 
503
580
  assert_response :success
504
- assert json_response['posts'].is_a?(Hash)
505
- assert_equal '3', json_response['posts']['links']['author']
506
- assert_equal javascript.id.to_s, json_response['posts']['links']['section']
507
- assert_equal 'A great new Post', json_response['posts']['title']
508
- assert_equal 'AAAA', json_response['posts']['body']
509
- assert matches_array?(['3', '4'], json_response['posts']['links']['tags'])
581
+ assert json_response['data'].is_a?(Hash)
582
+ assert_equal '3', json_response['data']['links']['author']['id']
583
+ assert_equal javascript.id.to_s, json_response['data']['links']['section']['id']
584
+ assert_equal 'A great new Post', json_response['data']['title']
585
+ assert_equal 'AAAA', json_response['data']['body']
586
+ assert matches_array?(['3', '4'], json_response['data']['links']['tags']['ids'])
510
587
  end
511
588
 
512
589
  def test_update_remove_links
@@ -514,22 +591,25 @@ class PostsControllerTest < ActionController::TestCase
514
591
  put :update,
515
592
  {
516
593
  id: 3,
517
- posts: {
594
+ data: {
595
+ type: 'posts',
596
+ id: 3,
518
597
  title: 'A great new Post',
519
598
  links: {
520
599
  section: nil,
521
600
  tags: []
522
601
  }
523
- }
602
+ },
603
+ include: 'tags'
524
604
  }
525
605
 
526
606
  assert_response :success
527
- assert json_response['posts'].is_a?(Hash)
528
- assert_equal '3', json_response['posts']['links']['author']
529
- assert_equal nil, json_response['posts']['links']['section']
530
- assert_equal 'A great new Post', json_response['posts']['title']
531
- assert_equal 'AAAA', json_response['posts']['body']
532
- assert matches_array?([], json_response['posts']['links']['tags'])
607
+ assert json_response['data'].is_a?(Hash)
608
+ assert_equal '3', json_response['data']['links']['author']['id']
609
+ assert_equal nil, json_response['data']['links']['section']['id']
610
+ assert_equal 'A great new Post', json_response['data']['title']
611
+ assert_equal 'AAAA', json_response['data']['body']
612
+ assert matches_array?([], json_response['data']['links']['tags']['ids'])
533
613
  end
534
614
 
535
615
  def test_update_relationship_has_one
@@ -538,82 +618,148 @@ class PostsControllerTest < ActionController::TestCase
538
618
  post_object = Post.find(3)
539
619
  assert_not_equal ruby.id, post_object.section_id
540
620
 
541
- put :update_association, {post_id: 3, association: 'section', sections: ruby.id}
621
+ put :update_association, {post_id: 3, association: 'section', data: {type: 'sections', id: "#{ruby.id}"}}
542
622
 
543
623
  assert_response :no_content
544
624
  post_object = Post.find(3)
545
625
  assert_equal ruby.id, post_object.section_id
546
626
  end
547
627
 
548
- def test_update_relationship_has_one_singular_param
628
+ def test_update_relationship_has_one_invalid_links_hash_keys_ids
549
629
  set_content_type_header!
550
- ruby = Section.find_by(name: 'ruby')
551
- post_object = Post.find(3)
630
+ put :update_association, {post_id: 3, association: 'section', data: {types: 'sections', ids: 'foo'}}
631
+
632
+ assert_response :bad_request
633
+ assert_match /Invalid Links Object/, response.body
634
+ end
635
+
636
+ def test_update_relationship_has_one_invalid_links_hash_count
637
+ set_content_type_header!
638
+ put :update_association, {post_id: 3, association: 'section', data: {types: 'sections'}}
639
+
640
+ assert_response :bad_request
641
+ assert_match /Invalid Links Object/, response.body
642
+ end
552
643
 
553
- put :update_association, {post_id: 3, association: 'section', section: ruby.id}
644
+ def test_update_relationship_has_one_invalid_links_hash_keys_type_mismatch
645
+ set_content_type_header!
646
+ put :update_association, {post_id: 3, association: 'section', data: {type: 'comment', id: '3'}}
554
647
 
555
648
  assert_response :bad_request
649
+ assert_match /Type Mismatch/, response.body
556
650
  end
557
651
 
558
- def test_update_relationship_has_one_singular_param_relation_nil
652
+ def test_update_nil_has_many_links
653
+ set_content_type_header!
654
+ put :update,
655
+ {
656
+ id: 3,
657
+ data: {
658
+ type: 'posts',
659
+ id: 3,
660
+ links: {
661
+ tags: nil
662
+ }
663
+ }
664
+ }
665
+
666
+ assert_response :bad_request
667
+ assert_match /Invalid Links Object/, response.body
668
+ end
669
+
670
+ def test_update_bad_hash_has_many_links
671
+ set_content_type_header!
672
+ put :update,
673
+ {
674
+ id: 3,
675
+ data: {
676
+ type: 'posts',
677
+ id: 3,
678
+ links: {
679
+ tags: {typ: 'bad link', idd: 'as'}
680
+ }
681
+ }
682
+ }
683
+
684
+ assert_response :bad_request
685
+ assert_match /Invalid Links Object/, response.body
686
+ end
687
+
688
+ def test_update_other_has_many_links
689
+ set_content_type_header!
690
+ put :update,
691
+ {
692
+ id: 3,
693
+ data: {
694
+ type: 'posts',
695
+ id: 3,
696
+ links: {
697
+ tags: 'bad link'
698
+ }
699
+ }
700
+ }
701
+
702
+ assert_response :bad_request
703
+ assert_match /Invalid Links Object/, response.body
704
+ end
705
+
706
+ def test_update_relationship_has_one_singular_param_id_nil
559
707
  set_content_type_header!
560
708
  ruby = Section.find_by(name: 'ruby')
561
709
  post_object = Post.find(3)
562
- post_object.section_id = nil
710
+ post_object.section_id = ruby.id
563
711
  post_object.save!
564
712
 
565
- put :update_association, {post_id: 3, association: 'section', sections: ruby.id}
713
+ put :update_association, {post_id: 3, association: 'section', data: {type: 'sections', id: nil}}
566
714
 
567
715
  assert_response :no_content
568
716
  post_object = Post.find(3)
569
- assert_equal ruby.id, post_object.section_id
717
+ assert_equal nil, post_object.section_id
570
718
  end
571
719
 
572
- def test_create_relationship_has_one_singular_param_relation_nil
720
+ def test_remove_relationship_has_one
573
721
  set_content_type_header!
574
722
  ruby = Section.find_by(name: 'ruby')
575
723
  post_object = Post.find(3)
576
- post_object.section_id = nil
724
+ post_object.section_id = ruby.id
577
725
  post_object.save!
578
726
 
579
- post :create_association, {post_id: 3, association: 'section', sections: ruby.id}
727
+ put :destroy_association, {post_id: 3, association: 'section'}
580
728
 
581
729
  assert_response :no_content
582
730
  post_object = Post.find(3)
583
- assert_equal ruby.id, post_object.section_id
731
+ assert_equal nil, post_object.section_id
584
732
  end
585
733
 
586
- def test_create_relationship_has_one_singular_param_relation_not_nil
734
+ def test_update_relationship_has_one_singular_param
587
735
  set_content_type_header!
588
736
  ruby = Section.find_by(name: 'ruby')
589
- js = Section.find_by(name: 'javascript')
590
737
  post_object = Post.find(3)
591
- post_object.section_id = js.id
738
+ post_object.section_id = nil
592
739
  post_object.save!
593
740
 
594
- post :create_association, {post_id: 3, association: 'section', sections: ruby.id}
741
+ put :update_association, {post_id: 3, association: 'section', data: {type: 'sections', id: "#{ruby.id}"}}
595
742
 
596
- assert_response :bad_request
597
- assert_match /The relation already exists./, response.body
743
+ assert_response :no_content
598
744
  post_object = Post.find(3)
599
- assert_equal js.id, post_object.section_id
745
+ assert_equal ruby.id, post_object.section_id
600
746
  end
601
747
 
602
748
  def test_update_relationship_has_many_join_table_single
603
749
  set_content_type_header!
604
- put :update_association, {post_id: 3, association: 'tags', tags: []}
750
+ put :update_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: []}}
605
751
  assert_response :no_content
606
752
 
607
753
  post_object = Post.find(3)
608
754
  assert_equal 0, post_object.tags.length
609
755
 
610
- put :update_association, {post_id: 3, association: 'tags', tags: [2]}
756
+ put :update_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [2]}}
611
757
 
612
758
  assert_response :no_content
613
759
  post_object = Post.find(3)
614
760
  assert_equal 1, post_object.tags.length
615
761
 
616
- put :update_association, {post_id: 3, association: 'tags', tags: 5}
762
+ put :update_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [5]}}
617
763
 
618
764
  assert_response :no_content
619
765
  post_object = Post.find(3)
@@ -622,9 +768,19 @@ class PostsControllerTest < ActionController::TestCase
622
768
  assert matches_array? [5], tags
623
769
  end
624
770
 
625
- def test_update_relationship_has_many_join_table
771
+ def test_update_relationship_has_many_join_table_homogenous
772
+ set_content_type_header!
773
+ put :update_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [2, 3]}}
774
+
775
+ assert_response :no_content
776
+ post_object = Post.find(3)
777
+ assert_equal 2, post_object.tags.collect { |tag| tag.id }.length
778
+ assert matches_array? [2, 3], post_object.tags.collect { |tag| tag.id }
779
+ end
780
+
781
+ def test_update_relationship_has_many_join_table_heterogenous
626
782
  set_content_type_header!
627
- put :update_association, {post_id: 3, association: 'tags', tags: [2, 3]}
783
+ put :update_association, {post_id: 3, association: 'tags', data: [{type: 'tags', id: 2}, {type: 'tags', id: 3}]}
628
784
 
629
785
  assert_response :no_content
630
786
  post_object = Post.find(3)
@@ -634,14 +790,14 @@ class PostsControllerTest < ActionController::TestCase
634
790
 
635
791
  def test_create_relationship_has_many_join_table
636
792
  set_content_type_header!
637
- put :update_association, {post_id: 3, association: 'tags', tags: [2, 3]}
793
+ put :update_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [2, 3]}}
638
794
 
639
795
  assert_response :no_content
640
796
  post_object = Post.find(3)
641
797
  assert_equal 2, post_object.tags.collect { |tag| tag.id }.length
642
798
  assert matches_array? [2, 3], post_object.tags.collect { |tag| tag.id }
643
799
 
644
- post :create_association, {post_id: 3, association: 'tags', tags: [5]}
800
+ post :create_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [5]}}
645
801
 
646
802
  assert_response :no_content
647
803
  post_object = Post.find(3)
@@ -649,63 +805,62 @@ class PostsControllerTest < ActionController::TestCase
649
805
  assert matches_array? [2, 3, 5], post_object.tags.collect { |tag| tag.id }
650
806
  end
651
807
 
652
- def test_create_relationship_has_many_missing_tags
808
+ def test_create_relationship_has_many_mismatched_type
809
+ set_content_type_header!
810
+ post :create_association, {post_id: 3, association: 'tags', data: {type: 'comments', ids: [5]}}
811
+
812
+ assert_response :bad_request
813
+ assert_match /Type Mismatch/, response.body
814
+ end
815
+
816
+ def test_create_relationship_has_many_missing_id
817
+ set_content_type_header!
818
+ post :create_association, {post_id: 3, association: 'tags', data: {type: 'tags', idds: [5]}}
819
+
820
+ assert_response :bad_request
821
+ assert_match /The required parameter, ids, is missing/, response.body
822
+ end
823
+
824
+ def test_create_relationship_has_many_missing_data
653
825
  set_content_type_header!
654
826
  post :create_association, {post_id: 3, association: 'tags'}
655
827
 
656
828
  assert_response :bad_request
657
- assert_match /The required parameter, tags, is missing./, response.body
829
+ assert_match /The required parameter, data, is missing./, response.body
830
+ end
831
+
832
+ def test_create_relationship_has_many_join
833
+ set_content_type_header!
834
+ post :create_association, {post_id: 4, association: 'tags', data: {type: 'tags', ids: [1, 2, 3]}}
835
+ assert_response :no_content
658
836
  end
659
837
 
660
838
  def test_create_relationship_has_many_join_table_record_exists
661
839
  set_content_type_header!
662
- put :update_association, {post_id: 3, association: 'tags', tags: [2, 3]}
840
+ put :update_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [2, 3]}}
663
841
 
664
842
  assert_response :no_content
665
843
  post_object = Post.find(3)
666
844
  assert_equal 2, post_object.tags.collect { |tag| tag.id }.length
667
845
  assert matches_array? [2, 3], post_object.tags.collect { |tag| tag.id }
668
846
 
669
- post :create_association, {post_id: 3, association: 'tags', tags: [5, 2]}
847
+ post :create_association, {post_id: 3, association: 'tags', data: {type: 'tags', ids: [5, 2]}}
670
848
 
671
849
  assert_response :bad_request
672
850
  assert_match /The relation to 2 already exists./, response.body
673
851
  end
674
852
 
675
- def test_update_relationship_has_one_mismatch_params
676
- set_content_type_header!
677
- post :create_association, {post_id: 3, association: 'section', authors: 1}
678
-
679
- assert_response :bad_request
680
- assert_match /The required parameter, sections, is missing./, response.body
681
- end
682
-
683
853
  def test_update_relationship_has_many_missing_tags
684
854
  set_content_type_header!
685
855
  put :update_association, {post_id: 3, association: 'tags'}
686
856
 
687
857
  assert_response :bad_request
688
- assert_match /The required parameter, tags, is missing./, response.body
689
- end
690
-
691
- def test_delete_relationship_has_one
692
- set_content_type_header!
693
- ruby = Section.find_by(name: 'ruby')
694
-
695
- post :create_association, {post_id: 9, association: 'section', sections: ruby.id}
696
-
697
- assert_response :no_content
698
-
699
- delete :destroy_association, {post_id: 9, association: 'section'}
700
-
701
- assert_response :no_content
702
- post = Post.find(9)
703
- assert_nil post.section
858
+ assert_match /The required parameter, data, is missing./, response.body
704
859
  end
705
860
 
706
861
  def test_delete_relationship_has_many
707
862
  set_content_type_header!
708
- put :update_association, {post_id: 9, association: 'tags', tags: [2, 3]}
863
+ put :update_association, {post_id: 9, association: 'tags', data: {type: 'tags', ids: [2, 3]}}
709
864
  assert_response :no_content
710
865
  p = Post.find(9)
711
866
  assert_equal [2, 3], p.tag_ids
@@ -719,7 +874,7 @@ class PostsControllerTest < ActionController::TestCase
719
874
 
720
875
  def test_delete_relationship_has_many_does_not_exist
721
876
  set_content_type_header!
722
- put :update_association, {post_id: 9, association: 'tags', tags: [2, 3]}
877
+ put :update_association, {post_id: 9, association: 'tags', data: {type: 'tags', ids: [2, 3]}}
723
878
  assert_response :no_content
724
879
  p = Post.find(9)
725
880
  assert_equal [2, 3], p.tag_ids
@@ -738,12 +893,13 @@ class PostsControllerTest < ActionController::TestCase
738
893
  put :update,
739
894
  {
740
895
  id: 3,
741
- posts: {
896
+ data: {
897
+ type: 'posts',
742
898
  id: 2,
743
899
  title: 'A great new Post',
744
900
  links: {
745
- section: javascript.id,
746
- tags: [3, 4]
901
+ section: {type: 'sections', id: "#{javascript.id}"},
902
+ tags: {type: 'tags', ids: [3, 4]}
747
903
  }
748
904
  }
749
905
  }
@@ -759,12 +915,14 @@ class PostsControllerTest < ActionController::TestCase
759
915
  put :update,
760
916
  {
761
917
  id: 3,
762
- posts: {
918
+ data: {
919
+ type: 'posts',
920
+ id: '3',
763
921
  asdfg: 'aaaa',
764
922
  title: 'A great new Post',
765
923
  links: {
766
- section: javascript.id,
767
- tags: [3, 4]
924
+ section: {type: 'sections', id: "#{javascript.id}"},
925
+ tags: {type: 'tags', ids: [3, 4]}
768
926
  }
769
927
  }
770
928
  }
@@ -780,12 +938,14 @@ class PostsControllerTest < ActionController::TestCase
780
938
  put :update,
781
939
  {
782
940
  id: 3,
783
- posts: {
941
+ data: {
942
+ type: 'posts',
943
+ id: '3',
784
944
  title: 'A great new Post',
785
945
  links: {
786
946
  asdfg: 'aaaa',
787
- section: javascript.id,
788
- tags: [3, 4]
947
+ section: {type: 'sections', id: "#{javascript.id}"},
948
+ tags: {type: 'tags', ids: [3, 4]}
789
949
  }
790
950
  }
791
951
  }
@@ -801,17 +961,56 @@ class PostsControllerTest < ActionController::TestCase
801
961
  put :update,
802
962
  {
803
963
  id: 3,
804
- posts_spelled_wrong: {
964
+ data_spelled_wrong: {
965
+ type: 'posts',
805
966
  title: 'A great new Post',
806
967
  links: {
807
- section: javascript.id,
808
- tags: [3, 4]
968
+ section: {type: 'sections', id: "#{javascript.id}"},
969
+ tags: {type: 'tags', ids: [3, 4]}
809
970
  }
810
971
  }
811
972
  }
812
973
 
813
974
  assert_response :bad_request
814
- assert_match /The required parameter, posts, is missing./, response.body
975
+ assert_match /The required parameter, data, is missing./, response.body
976
+ end
977
+
978
+ def test_update_missing_key
979
+ set_content_type_header!
980
+
981
+ put :update,
982
+ {
983
+ id: 3,
984
+ data: {
985
+ type: 'posts',
986
+ title: 'A great new Post'
987
+ }
988
+ }
989
+
990
+ assert_response :bad_request
991
+ assert_match /The resource object does not contain a key/, response.body
992
+ end
993
+
994
+ def test_update_missing_type
995
+ set_content_type_header!
996
+ javascript = Section.find_by(name: 'javascript')
997
+
998
+ put :update,
999
+ {
1000
+ id: 3,
1001
+ data: {
1002
+ id: '3',
1003
+ type_spelled_wrong: 'posts',
1004
+ title: 'A great new Post',
1005
+ links: {
1006
+ section: {type: 'sections', id: "#{javascript.id}"},
1007
+ tags: {type: 'tags', ids: [3, 4]}
1008
+ }
1009
+ }
1010
+ }
1011
+
1012
+ assert_response :bad_request
1013
+ assert_match /The required parameter, type, is missing./, response.body
815
1014
  end
816
1015
 
817
1016
  def test_update_multiple
@@ -821,38 +1020,42 @@ class PostsControllerTest < ActionController::TestCase
821
1020
  put :update,
822
1021
  {
823
1022
  id: [3, 9],
824
- posts: [
1023
+ data: [
825
1024
  {
1025
+ type: 'posts',
826
1026
  id: 3,
827
1027
  title: 'A great new Post QWERTY',
828
1028
  links: {
829
- section: javascript.id,
830
- tags: [3, 4]
1029
+ section: {type: 'sections', id: "#{javascript.id}"},
1030
+ tags: {type: 'tags', ids: [3, 4]}
831
1031
  }
832
1032
  },
833
1033
  {
1034
+ type: 'posts',
834
1035
  id: 9,
835
1036
  title: 'A great new Post ASDFG',
836
1037
  links: {
837
- section: javascript.id,
838
- tags: [3, 4]
1038
+ section: {type: 'sections', id: "#{javascript.id}"},
1039
+ tags: {type: 'tags', ids: [3, 4]}
839
1040
  }
840
1041
  }
841
- ]}
1042
+ ],
1043
+ include: 'tags'
1044
+ }
842
1045
 
843
1046
  assert_response :success
844
- assert_equal json_response['posts'].size, 2
845
- assert_equal json_response['posts'][0]['links']['author'], '3'
846
- assert_equal json_response['posts'][0]['links']['section'], javascript.id.to_s
847
- assert_equal json_response['posts'][0]['title'], 'A great new Post QWERTY'
848
- assert_equal json_response['posts'][0]['body'], 'AAAA'
849
- assert_equal json_response['posts'][0]['links']['tags'], ['3', '4']
1047
+ assert_equal json_response['data'].size, 2
1048
+ assert_equal json_response['data'][0]['links']['author']['id'], '3'
1049
+ assert_equal json_response['data'][0]['links']['section']['id'], javascript.id.to_s
1050
+ assert_equal json_response['data'][0]['title'], 'A great new Post QWERTY'
1051
+ assert_equal json_response['data'][0]['body'], 'AAAA'
1052
+ assert_equal json_response['data'][0]['links']['tags']['ids'], ['3', '4']
850
1053
 
851
- assert_equal json_response['posts'][1]['links']['author'], '3'
852
- assert_equal json_response['posts'][1]['links']['section'], javascript.id.to_s
853
- assert_equal json_response['posts'][1]['title'], 'A great new Post ASDFG'
854
- assert_equal json_response['posts'][1]['body'], 'AAAA'
855
- assert_equal json_response['posts'][1]['links']['tags'], ['3', '4']
1054
+ assert_equal json_response['data'][1]['links']['author']['id'], '3'
1055
+ assert_equal json_response['data'][1]['links']['section']['id'], javascript.id.to_s
1056
+ assert_equal json_response['data'][1]['title'], 'A great new Post ASDFG'
1057
+ assert_equal json_response['data'][1]['body'], 'AAAA'
1058
+ assert_equal json_response['data'][1]['links']['tags']['ids'], ['3', '4']
856
1059
  end
857
1060
 
858
1061
  def test_update_multiple_missing_keys
@@ -862,19 +1065,21 @@ class PostsControllerTest < ActionController::TestCase
862
1065
  put :update,
863
1066
  {
864
1067
  id: [3, 9],
865
- posts: [
1068
+ data: [
866
1069
  {
1070
+ type: 'posts',
867
1071
  title: 'A great new Post ASDFG',
868
1072
  links: {
869
- section: javascript.id,
870
- tags: [3, 4]
1073
+ section: {type: 'sections', id: "#{javascript.id}"},
1074
+ tags: {type: 'tags', ids: [3, 4]}
871
1075
  }
872
1076
  },
873
1077
  {
1078
+ type: 'posts',
874
1079
  title: 'A great new Post QWERTY',
875
1080
  links: {
876
- section: javascript.id,
877
- tags: [3, 4]
1081
+ section: {type: 'sections', id: "#{javascript.id}"},
1082
+ tags: {type: 'tags', ids: [3, 4]}
878
1083
  }
879
1084
  }
880
1085
  ]}
@@ -890,21 +1095,23 @@ class PostsControllerTest < ActionController::TestCase
890
1095
  put :update,
891
1096
  {
892
1097
  id: [3, 9],
893
- posts: [
1098
+ data: [
894
1099
  {
1100
+ type: 'posts',
895
1101
  id: 3,
896
1102
  title: 'A great new Post ASDFG',
897
1103
  links: {
898
- section: javascript.id,
899
- tags: [3, 4]
1104
+ section: {type: 'sections', id: "#{javascript.id}"},
1105
+ tags: {type: 'tags', ids: [3, 4]}
900
1106
  }
901
1107
  },
902
1108
  {
1109
+ type: 'posts',
903
1110
  id: 8,
904
1111
  title: 'A great new Post QWERTY',
905
1112
  links: {
906
- section: javascript.id,
907
- tags: [3, 4]
1113
+ section: {type: 'sections', id: "#{javascript.id}"},
1114
+ tags: {type: 'tags', ids: [3, 4]}
908
1115
  }
909
1116
  }
910
1117
  ]}
@@ -920,21 +1127,23 @@ class PostsControllerTest < ActionController::TestCase
920
1127
  put :update,
921
1128
  {
922
1129
  id: [3, 9, 2],
923
- posts: [
1130
+ data: [
924
1131
  {
1132
+ type: 'posts',
925
1133
  id: 3,
926
1134
  title: 'A great new Post QWERTY',
927
1135
  links: {
928
- section: javascript.id,
929
- tags: [3, 4]
1136
+ section: {type: 'sections', id: "#{javascript.id}"},
1137
+ tags: {type: 'tags', ids: [3, 4]}
930
1138
  }
931
1139
  },
932
1140
  {
1141
+ type: 'posts',
933
1142
  id: 9,
934
1143
  title: 'A great new Post ASDFG',
935
1144
  links: {
936
- section: javascript.id,
937
- tags: [3, 4]
1145
+ section: {type: 'sections', id: "#{javascript.id}"},
1146
+ tags: {type: 'tags', ids: [3, 4]}
938
1147
  }
939
1148
  }
940
1149
  ]}
@@ -948,11 +1157,13 @@ class PostsControllerTest < ActionController::TestCase
948
1157
  put :update,
949
1158
  {
950
1159
  id: 3,
951
- posts: {
1160
+ data: {
1161
+ type: 'posts',
1162
+ id: '3',
952
1163
  subject: 'A great new Post',
953
1164
  links: {
954
- author: 1,
955
- tags: [3, 4]
1165
+ author: {type: 'people', id: '1'},
1166
+ tags: {type: 'tags', ids: [3, 4]}
956
1167
  }
957
1168
  }
958
1169
  }
@@ -967,11 +1178,12 @@ class PostsControllerTest < ActionController::TestCase
967
1178
  put :update,
968
1179
  {
969
1180
  id: 3,
970
- posts: {
1181
+ data: {
1182
+ type: 'posts',
971
1183
  subject: 'A great new Post',
972
1184
  linked_objects: {
973
- author: 1,
974
- tags: [3, 4]
1185
+ author: {type: 'people', id: '1'},
1186
+ tags: {type: 'tags', ids: [3, 4]}
975
1187
  }
976
1188
  }
977
1189
  }
@@ -1010,37 +1222,51 @@ class PostsControllerTest < ActionController::TestCase
1010
1222
  def test_show_has_one_relationship
1011
1223
  get :show_association, {post_id: '1', association: 'author'}
1012
1224
  assert_response :success
1013
- assert_equal 1, json_response['author']
1225
+ assert_hash_equals json_response,
1226
+ {data: {
1227
+ type: 'people',
1228
+ id: '1',
1229
+ self: 'http://test.host/posts/1/links/author',
1230
+ resource: 'http://test.host/posts/1/author'
1231
+ }
1232
+ }
1014
1233
  end
1015
1234
 
1016
1235
  def test_show_has_many_relationship
1017
1236
  get :show_association, {post_id: '1', association: 'tags'}
1018
1237
  assert_response :success
1019
- assert_equal [1, 2, 3], json_response['tags']
1238
+ assert_hash_equals json_response,
1239
+ {data: {
1240
+ type: 'tags',
1241
+ ids: ['1', '2', '3'],
1242
+ self: 'http://test.host/posts/1/links/tags',
1243
+ resource: 'http://test.host/posts/1/tags'
1244
+ }
1245
+ }
1020
1246
  end
1021
1247
  end
1022
1248
 
1023
1249
  class TagsControllerTest < ActionController::TestCase
1024
1250
  def test_tags_index
1025
- get :index, {ids: '6,7,8,9', include: 'posts,posts.tags,posts.author.posts'}
1251
+ get :index, {filter: {id: '6,7,8,9'}, include: 'posts,posts.tags,posts.author.posts'}
1026
1252
  assert_response :success
1027
- assert_equal 4, json_response['tags'].size
1028
- assert_equal 2, json_response['linked']['posts'].size
1253
+ assert_equal 4, json_response['data'].size
1254
+ assert_equal 2, json_response['linked'].size
1029
1255
  end
1030
1256
 
1031
1257
  def test_tags_show_multiple
1032
1258
  get :show, {id: '6,7,8,9'}
1033
1259
  assert_response :success
1034
- assert json_response['tags'].is_a?(Array)
1035
- assert_equal 4, json_response['tags'].size
1260
+ assert json_response['data'].is_a?(Array)
1261
+ assert_equal 4, json_response['data'].size
1036
1262
  end
1037
1263
 
1038
1264
  def test_tags_show_multiple_with_include
1039
1265
  get :show, {id: '6,7,8,9', include: 'posts,posts.tags,posts.author.posts'}
1040
1266
  assert_response :success
1041
- assert json_response['tags'].is_a?(Array)
1042
- assert_equal 4, json_response['tags'].size
1043
- assert_equal 2, json_response['linked']['posts'].size
1267
+ assert json_response['data'].is_a?(Array)
1268
+ assert_equal 4, json_response['data'].size
1269
+ assert_equal 2, json_response['linked'].size
1044
1270
  end
1045
1271
 
1046
1272
  def test_tags_show_multiple_with_nonexistent_ids
@@ -1058,22 +1284,21 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1058
1284
  def test_expense_entries_index
1059
1285
  get :index
1060
1286
  assert_response :success
1061
- assert json_response['expenseEntries'].is_a?(Array)
1062
- assert_equal 2, json_response['expenseEntries'].size
1287
+ assert json_response['data'].is_a?(Array)
1288
+ assert_equal 2, json_response['data'].size
1063
1289
  end
1064
1290
 
1065
1291
  def test_expense_entries_show
1066
1292
  get :show, {id: 1}
1067
1293
  assert_response :success
1068
- assert json_response['expenseEntries'].is_a?(Hash)
1294
+ assert json_response['data'].is_a?(Hash)
1069
1295
  end
1070
1296
 
1071
1297
  def test_expense_entries_show_include
1072
1298
  get :show, {id: 1, include: 'isoCurrency,employee'}
1073
1299
  assert_response :success
1074
- assert json_response['expenseEntries'].is_a?(Hash)
1075
- assert_equal 1, json_response['linked']['isoCurrencies'].size
1076
- assert_equal 1, json_response['linked']['people'].size
1300
+ assert json_response['data'].is_a?(Hash)
1301
+ assert_equal 2, json_response['linked'].size
1077
1302
  end
1078
1303
 
1079
1304
  def test_expense_entries_show_bad_include_missing_association
@@ -1092,38 +1317,25 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1092
1317
  def test_expense_entries_show_fields
1093
1318
  get :show, {id: 1, include: 'isoCurrency,employee', 'fields' => 'transactionDate'}
1094
1319
  assert_response :success
1095
- assert json_response['expenseEntries'].is_a?(Hash)
1096
- assert json_response['expenseEntries'].has_key?('transactionDate')
1097
- refute json_response['expenseEntries'].has_key?('id')
1098
- refute json_response['expenseEntries'].has_key?('links')
1099
- assert_equal 1, json_response['linked']['isoCurrencies'].size
1100
- assert_equal 1, json_response['linked']['people'].size
1320
+ assert json_response['data'].is_a?(Hash)
1321
+ assert json_response['data'].has_key?('transactionDate')
1101
1322
  end
1102
1323
 
1103
1324
  def test_expense_entries_show_fields_type
1104
1325
  get :show, {id: 1, include: 'isoCurrency,employee', 'fields' => {'expenseEntries' => 'transactionDate'}}
1105
1326
  assert_response :success
1106
- assert json_response['expenseEntries'].is_a?(Hash)
1107
- assert json_response['expenseEntries'].has_key?('transactionDate')
1108
- refute json_response['expenseEntries'].has_key?('id')
1109
- refute json_response['expenseEntries'].has_key?('links')
1110
- assert_equal 1, json_response['linked']['isoCurrencies'].size
1111
- assert_equal 1, json_response['linked']['people'].size
1327
+ assert json_response['data'].is_a?(Hash)
1328
+ assert json_response['data'].has_key?('transactionDate')
1329
+ assert_equal 2, json_response['linked'].size
1112
1330
  end
1113
1331
 
1114
1332
  def test_expense_entries_show_fields_type_many
1115
1333
  get :show, {id: 1, include: 'isoCurrency,employee', 'fields' => {'expenseEntries' => 'transactionDate',
1116
1334
  'isoCurrencies' => 'id,name'}}
1117
1335
  assert_response :success
1118
- assert json_response['expenseEntries'].is_a?(Hash)
1119
- assert json_response['expenseEntries'].has_key?('transactionDate')
1120
- refute json_response['expenseEntries'].has_key?('id')
1121
- refute json_response['expenseEntries'].has_key?('links')
1122
- assert_equal 1, json_response['linked']['isoCurrencies'].size
1123
- assert_equal 1, json_response['linked']['people'].size
1124
- assert json_response['linked']['isoCurrencies'][0].has_key?('id')
1125
- assert json_response['linked']['isoCurrencies'][0].has_key?('name')
1126
- refute json_response['linked']['isoCurrencies'][0].has_key?('countryName')
1336
+ assert json_response['data'].is_a?(Hash)
1337
+ assert json_response['data'].has_key?('transactionDate')
1338
+ assert_equal 2, json_response['linked'].size
1127
1339
  end
1128
1340
 
1129
1341
  def test_create_expense_entries_underscored
@@ -1132,12 +1344,13 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1132
1344
 
1133
1345
  post :create,
1134
1346
  {
1135
- expense_entries: {
1347
+ data: {
1348
+ type: 'expense_entries',
1136
1349
  transaction_date: '2014/04/15',
1137
1350
  cost: 50.58,
1138
1351
  links: {
1139
- employee: 3,
1140
- iso_currency: 'USD'
1352
+ employee: {type: 'people', id: '3'},
1353
+ iso_currency: {type: 'iso_currencies', id: 'USD'}
1141
1354
  }
1142
1355
  },
1143
1356
  include: 'iso_currency',
@@ -1145,12 +1358,12 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1145
1358
  }
1146
1359
 
1147
1360
  assert_response :created
1148
- assert json_response['expense_entries'].is_a?(Hash)
1149
- assert_equal '3', json_response['expense_entries']['links']['employee']
1150
- assert_equal 'USD', json_response['expense_entries']['links']['iso_currency']
1151
- assert_equal 50.58, json_response['expense_entries']['cost']
1361
+ assert json_response['data'].is_a?(Hash)
1362
+ assert_equal '3', json_response['data']['links']['employee']['id']
1363
+ assert_equal 'USD', json_response['data']['links']['iso_currency']['id']
1364
+ assert_equal 50.58, json_response['data']['cost']
1152
1365
 
1153
- delete :destroy, {id: json_response['expense_entries']['id']}
1366
+ delete :destroy, {id: json_response['data']['id']}
1154
1367
  assert_response :no_content
1155
1368
  end
1156
1369
 
@@ -1160,12 +1373,13 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1160
1373
 
1161
1374
  post :create,
1162
1375
  {
1163
- expenseEntries: {
1376
+ data: {
1377
+ type: 'expense_entries',
1164
1378
  transactionDate: '2014/04/15',
1165
1379
  cost: 50.58,
1166
1380
  links: {
1167
- employee: 3,
1168
- isoCurrency: 'USD'
1381
+ employee: {type: 'people', id: '3'},
1382
+ isoCurrency: {type: 'iso_currencies', id: 'USD'}
1169
1383
  }
1170
1384
  },
1171
1385
  include: 'isoCurrency',
@@ -1173,12 +1387,12 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1173
1387
  }
1174
1388
 
1175
1389
  assert_response :created
1176
- assert json_response['expenseEntries'].is_a?(Hash)
1177
- assert_equal '3', json_response['expenseEntries']['links']['employee']
1178
- assert_equal 'USD', json_response['expenseEntries']['links']['isoCurrency']
1179
- assert_equal 50.58, json_response['expenseEntries']['cost']
1390
+ assert json_response['data'].is_a?(Hash)
1391
+ assert_equal '3', json_response['data']['links']['employee']['id']
1392
+ assert_equal 'USD', json_response['data']['links']['isoCurrency']['id']
1393
+ assert_equal 50.58, json_response['data']['cost']
1180
1394
 
1181
- delete :destroy, {id: json_response['expenseEntries']['id']}
1395
+ delete :destroy, {id: json_response['data']['id']}
1182
1396
  assert_response :no_content
1183
1397
  end
1184
1398
 
@@ -1188,12 +1402,13 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1188
1402
 
1189
1403
  post :create,
1190
1404
  {
1191
- 'expense-entries' => {
1405
+ data: {
1406
+ type: 'expense_entries',
1192
1407
  'transaction-date' => '2014/04/15',
1193
1408
  cost: 50.58,
1194
1409
  links: {
1195
- employee: 3,
1196
- 'iso-currency' => 'USD'
1410
+ employee: {type: 'people', id: '3'},
1411
+ 'iso-currency' => {type: 'iso_currencies', id: 'USD'}
1197
1412
  }
1198
1413
  },
1199
1414
  include: 'iso-currency',
@@ -1201,12 +1416,12 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1201
1416
  }
1202
1417
 
1203
1418
  assert_response :created
1204
- assert json_response['expense-entries'].is_a?(Hash)
1205
- assert_equal '3', json_response['expense-entries']['links']['employee']
1206
- assert_equal 'USD', json_response['expense-entries']['links']['iso-currency']
1207
- assert_equal 50.58, json_response['expense-entries']['cost']
1419
+ assert json_response['data'].is_a?(Hash)
1420
+ assert_equal '3', json_response['data']['links']['employee']['id']
1421
+ assert_equal 'USD', json_response['data']['links']['iso-currency']['id']
1422
+ assert_equal 50.58, json_response['data']['cost']
1208
1423
 
1209
- delete :destroy, {id: json_response['expense-entries']['id']}
1424
+ delete :destroy, {id: json_response['data']['id']}
1210
1425
  assert_response :no_content
1211
1426
  end
1212
1427
  end
@@ -1216,116 +1431,88 @@ class IsoCurrenciesControllerTest < ActionController::TestCase
1216
1431
  JSONAPI.configuration.json_key_format = :camelized_key
1217
1432
  end
1218
1433
 
1219
- def test_currencies_index
1220
- JSONAPI.configuration.json_key_format = :camelized_key
1221
- get :index
1222
- assert_response :success
1223
- assert_equal 3, json_response['isoCurrencies'].size
1224
- end
1225
-
1226
- def test_currencies_json_key_underscored
1227
- JSONAPI.configuration.json_key_format = :underscored_key
1228
- get :index
1229
- assert_response :success
1230
- assert_equal 3, json_response['iso_currencies'].size
1231
- end
1232
-
1233
- def test_currencies_json_key_dasherized
1234
- JSONAPI.configuration.json_key_format = :dasherized_key
1235
- get :index
1236
- assert_response :success
1237
- assert_equal 3, json_response['iso-currencies'].size
1238
- end
1239
-
1240
- def test_currencies_custom_json_key
1241
- JSONAPI.configuration.json_key_format = :upper_camelized_key
1242
- get :index
1243
- assert_response :success
1244
- assert_equal 3, json_response['IsoCurrencies'].size
1245
- end
1246
-
1247
1434
  def test_currencies_show
1248
1435
  get :show, {code: 'USD'}
1249
1436
  assert_response :success
1250
- assert json_response['isoCurrencies'].is_a?(Hash)
1437
+ assert json_response['data'].is_a?(Hash)
1251
1438
  end
1252
1439
 
1253
1440
  def test_currencies_json_key_underscored_sort
1254
1441
  JSONAPI.configuration.json_key_format = :underscored_key
1255
- get :index, {sort: 'country_name'}
1442
+ get :index, {sort: '+country_name'}
1256
1443
  assert_response :success
1257
- assert_equal 3, json_response['iso_currencies'].size
1258
- assert_equal 'Canada', json_response['iso_currencies'][0]['country_name']
1259
- assert_equal 'Euro Member Countries', json_response['iso_currencies'][1]['country_name']
1260
- assert_equal 'United States', json_response['iso_currencies'][2]['country_name']
1444
+ assert_equal 3, json_response['data'].size
1445
+ assert_equal 'Canada', json_response['data'][0]['country_name']
1446
+ assert_equal 'Euro Member Countries', json_response['data'][1]['country_name']
1447
+ assert_equal 'United States', json_response['data'][2]['country_name']
1261
1448
 
1262
1449
  # reverse sort
1263
1450
  get :index, {sort: '-country_name'}
1264
1451
  assert_response :success
1265
- assert_equal 3, json_response['iso_currencies'].size
1266
- assert_equal 'United States', json_response['iso_currencies'][0]['country_name']
1267
- assert_equal 'Euro Member Countries', json_response['iso_currencies'][1]['country_name']
1268
- assert_equal 'Canada', json_response['iso_currencies'][2]['country_name']
1452
+ assert_equal 3, json_response['data'].size
1453
+ assert_equal 'United States', json_response['data'][0]['country_name']
1454
+ assert_equal 'Euro Member Countries', json_response['data'][1]['country_name']
1455
+ assert_equal 'Canada', json_response['data'][2]['country_name']
1269
1456
  end
1270
1457
 
1271
1458
  def test_currencies_json_key_dasherized_sort
1272
1459
  JSONAPI.configuration.json_key_format = :dasherized_key
1273
- get :index, {sort: 'country-name'}
1460
+ get :index, {sort: '+country-name'}
1274
1461
  assert_response :success
1275
- assert_equal 3, json_response['iso-currencies'].size
1276
- assert_equal 'Canada', json_response['iso-currencies'][0]['country-name']
1277
- assert_equal 'Euro Member Countries', json_response['iso-currencies'][1]['country-name']
1278
- assert_equal 'United States', json_response['iso-currencies'][2]['country-name']
1462
+ assert_equal 3, json_response['data'].size
1463
+ assert_equal 'Canada', json_response['data'][0]['country-name']
1464
+ assert_equal 'Euro Member Countries', json_response['data'][1]['country-name']
1465
+ assert_equal 'United States', json_response['data'][2]['country-name']
1279
1466
 
1280
1467
  # reverse sort
1281
1468
  get :index, {sort: '-country-name'}
1282
1469
  assert_response :success
1283
- assert_equal 3, json_response['iso-currencies'].size
1284
- assert_equal 'United States', json_response['iso-currencies'][0]['country-name']
1285
- assert_equal 'Euro Member Countries', json_response['iso-currencies'][1]['country-name']
1286
- assert_equal 'Canada', json_response['iso-currencies'][2]['country-name']
1470
+ assert_equal 3, json_response['data'].size
1471
+ assert_equal 'United States', json_response['data'][0]['country-name']
1472
+ assert_equal 'Euro Member Countries', json_response['data'][1]['country-name']
1473
+ assert_equal 'Canada', json_response['data'][2]['country-name']
1287
1474
  end
1288
1475
 
1289
1476
  def test_currencies_json_key_custom_json_key_sort
1290
1477
  JSONAPI.configuration.json_key_format = :upper_camelized_key
1291
- get :index, {sort: 'CountryName'}
1478
+ get :index, {sort: '+CountryName'}
1292
1479
  assert_response :success
1293
- assert_equal 3, json_response['IsoCurrencies'].size
1294
- assert_equal 'Canada', json_response['IsoCurrencies'][0]['CountryName']
1295
- assert_equal 'Euro Member Countries', json_response['IsoCurrencies'][1]['CountryName']
1296
- assert_equal 'United States', json_response['IsoCurrencies'][2]['CountryName']
1480
+ assert_equal 3, json_response['data'].size
1481
+ assert_equal 'Canada', json_response['data'][0]['CountryName']
1482
+ assert_equal 'Euro Member Countries', json_response['data'][1]['CountryName']
1483
+ assert_equal 'United States', json_response['data'][2]['CountryName']
1297
1484
 
1298
1485
  # reverse sort
1299
1486
  get :index, {sort: '-CountryName'}
1300
1487
  assert_response :success
1301
- assert_equal 3, json_response['IsoCurrencies'].size
1302
- assert_equal 'United States', json_response['IsoCurrencies'][0]['CountryName']
1303
- assert_equal 'Euro Member Countries', json_response['IsoCurrencies'][1]['CountryName']
1304
- assert_equal 'Canada', json_response['IsoCurrencies'][2]['CountryName']
1488
+ assert_equal 3, json_response['data'].size
1489
+ assert_equal 'United States', json_response['data'][0]['CountryName']
1490
+ assert_equal 'Euro Member Countries', json_response['data'][1]['CountryName']
1491
+ assert_equal 'Canada', json_response['data'][2]['CountryName']
1305
1492
  end
1306
1493
 
1307
1494
  def test_currencies_json_key_underscored_filter
1308
1495
  JSONAPI.configuration.json_key_format = :underscored_key
1309
- get :index, {country_name: 'Canada'}
1496
+ get :index, {filter: {country_name: 'Canada'}}
1310
1497
  assert_response :success
1311
- assert_equal 1, json_response['iso_currencies'].size
1312
- assert_equal 'Canada', json_response['iso_currencies'][0]['country_name']
1498
+ assert_equal 1, json_response['data'].size
1499
+ assert_equal 'Canada', json_response['data'][0]['country_name']
1313
1500
  end
1314
1501
 
1315
1502
  def test_currencies_json_key_camelized_key_filter
1316
1503
  JSONAPI.configuration.json_key_format = :camelized_key
1317
- get :index, {'countryName' => 'Canada'}
1504
+ get :index, {filter: {'countryName' => 'Canada'}}
1318
1505
  assert_response :success
1319
- assert_equal 1, json_response['isoCurrencies'].size
1320
- assert_equal 'Canada', json_response['isoCurrencies'][0]['countryName']
1506
+ assert_equal 1, json_response['data'].size
1507
+ assert_equal 'Canada', json_response['data'][0]['countryName']
1321
1508
  end
1322
1509
 
1323
1510
  def test_currencies_json_key_custom_json_key_filter
1324
1511
  JSONAPI.configuration.json_key_format = :upper_camelized_key
1325
- get :index, {'CountryName' => 'Canada'}
1512
+ get :index, {filter: {'CountryName' => 'Canada'}}
1326
1513
  assert_response :success
1327
- assert_equal 1, json_response['IsoCurrencies'].size
1328
- assert_equal 'Canada', json_response['IsoCurrencies'][0]['CountryName']
1514
+ assert_equal 1, json_response['data'].size
1515
+ assert_equal 'Canada', json_response['data'][0]['CountryName']
1329
1516
  end
1330
1517
  end
1331
1518
 
@@ -1334,7 +1521,8 @@ class PeopleControllerTest < ActionController::TestCase
1334
1521
  set_content_type_header!
1335
1522
  post :create,
1336
1523
  {
1337
- people: {
1524
+ data: {
1525
+ type: 'people',
1338
1526
  name: 'Steve Jobs',
1339
1527
  email: 'sj@email.zzz',
1340
1528
  dateJoined: DateTime.parse('2014-1-30 4:20:00 UTC +00:00')
@@ -1348,7 +1536,8 @@ class PeopleControllerTest < ActionController::TestCase
1348
1536
  set_content_type_header!
1349
1537
  post :create,
1350
1538
  {
1351
- people: {
1539
+ data: {
1540
+ type: 'people',
1352
1541
  email: 'sj@email.zzz'
1353
1542
  }
1354
1543
  }
@@ -1366,7 +1555,9 @@ class PeopleControllerTest < ActionController::TestCase
1366
1555
  put :update,
1367
1556
  {
1368
1557
  id: 3,
1369
- people: {
1558
+ data: {
1559
+ id: '3',
1560
+ type: 'people',
1370
1561
  name: ''
1371
1562
  }
1372
1563
  }
@@ -1385,39 +1576,36 @@ class PeopleControllerTest < ActionController::TestCase
1385
1576
  end
1386
1577
 
1387
1578
  def test_invalid_filter_value
1388
- get :index, {name: 'L'}
1579
+ get :index, {filter: {name: 'L'}}
1389
1580
  assert_response :bad_request
1390
1581
  end
1391
1582
 
1392
1583
  def test_valid_filter_value
1393
- get :index, {name: 'Joe Author'}
1584
+ get :index, {filter: {name: 'Joe Author'}}
1394
1585
  assert_response :success
1395
- assert_equal json_response['people'].size, 1
1396
- assert_equal json_response['people'][0]['id'], '1'
1397
- assert_equal json_response['people'][0]['name'], 'Joe Author'
1586
+ assert_equal json_response['data'].size, 1
1587
+ assert_equal json_response['data'][0]['id'], '1'
1588
+ assert_equal json_response['data'][0]['name'], 'Joe Author'
1398
1589
  end
1399
1590
  end
1400
1591
 
1401
1592
  class AuthorsControllerTest < ActionController::TestCase
1402
1593
  def test_get_person_as_author
1403
- get :index, {id: '1'}
1594
+ get :index, {filter: {id: '1'}}
1404
1595
  assert_response :success
1405
- assert_equal 1, json_response['authors'].size
1406
- assert_equal '1', json_response['authors'][0]['id']
1407
- assert_equal 'Joe Author', json_response['authors'][0]['name']
1408
- assert_equal nil, json_response['authors'][0]['email']
1409
- assert_equal 1, json_response['authors'][0]['links'].size
1410
- assert_equal 3, json_response['authors'][0]['links']['posts'].size
1596
+ assert_equal 1, json_response['data'].size
1597
+ assert_equal '1', json_response['data'][0]['id']
1598
+ assert_equal 'authors', json_response['data'][0]['type']
1599
+ assert_equal 'Joe Author', json_response['data'][0]['name']
1600
+ assert_equal nil, json_response['data'][0]['email']
1411
1601
  end
1412
1602
 
1413
1603
  def test_get_person_as_author_by_name_filter
1414
- get :index, {name: 'thor'}
1604
+ get :index, {filter: {name: 'thor'}}
1415
1605
  assert_response :success
1416
- assert_equal 3, json_response['authors'].size
1417
- assert_equal '1', json_response['authors'][0]['id']
1418
- assert_equal 'Joe Author', json_response['authors'][0]['name']
1419
- assert_equal 1, json_response['authors'][0]['links'].size
1420
- assert_equal 3, json_response['authors'][0]['links']['posts'].size
1606
+ assert_equal 3, json_response['data'].size
1607
+ assert_equal '1', json_response['data'][0]['id']
1608
+ assert_equal 'Joe Author', json_response['data'][0]['name']
1421
1609
  end
1422
1610
  end
1423
1611
 
@@ -1427,66 +1615,70 @@ class BreedsControllerTest < ActionController::TestCase
1427
1615
  def test_poro_index
1428
1616
  get :index
1429
1617
  assert_response :success
1430
- assert_equal '0', json_response['breeds'][0]['id']
1431
- assert_equal 'Persian', json_response['breeds'][0]['name']
1618
+ assert_equal '0', json_response['data'][0]['id']
1619
+ assert_equal 'Persian', json_response['data'][0]['name']
1432
1620
  end
1433
1621
 
1434
1622
  def test_poro_show
1435
1623
  get :show, {id: '0'}
1436
1624
  assert_response :success
1437
- assert json_response['breeds'].is_a?(Hash)
1438
- assert_equal '0', json_response['breeds']['id']
1439
- assert_equal 'Persian', json_response['breeds']['name']
1625
+ assert json_response['data'].is_a?(Hash)
1626
+ assert_equal '0', json_response['data']['id']
1627
+ assert_equal 'Persian', json_response['data']['name']
1440
1628
  end
1441
1629
 
1442
1630
  def test_poro_show_multiple
1443
1631
  get :show, {id: '0,2'}
1444
1632
  assert_response :success
1445
- assert json_response['breeds'].is_a?(Array)
1446
- assert_equal 2, json_response['breeds'].size
1447
- assert_equal '0', json_response['breeds'][0]['id']
1448
- assert_equal 'Persian', json_response['breeds'][0]['name']
1449
- assert_equal '2', json_response['breeds'][1]['id']
1450
- assert_equal 'Sphinx', json_response['breeds'][1]['name']
1633
+ assert json_response['data'].is_a?(Array)
1634
+ assert_equal 2, json_response['data'].size
1635
+ assert_equal '0', json_response['data'][0]['id']
1636
+ assert_equal 'Persian', json_response['data'][0]['name']
1637
+ assert_equal '2', json_response['data'][1]['id']
1638
+ assert_equal 'Sphinx', json_response['data'][1]['name']
1451
1639
  end
1452
1640
 
1453
1641
  def test_poro_create_simple
1454
1642
  set_content_type_header!
1455
1643
  post :create,
1456
1644
  {
1457
- breeds: {
1645
+ data: {
1646
+ type: 'breeds',
1458
1647
  name: 'tabby'
1459
1648
  }
1460
1649
  }
1461
1650
 
1462
1651
  assert_response :created
1463
- assert json_response['breeds'].is_a?(Hash)
1464
- assert_equal 'Tabby', json_response['breeds']['name']
1652
+ assert json_response['data'].is_a?(Hash)
1653
+ assert_equal 'Tabby', json_response['data']['name']
1465
1654
  end
1466
1655
 
1467
1656
  def test_poro_create_update
1468
1657
  set_content_type_header!
1469
1658
  post :create,
1470
1659
  {
1471
- breeds: {
1660
+ data: {
1661
+ type: 'breeds',
1472
1662
  name: 'CALIC'
1473
1663
  }
1474
1664
  }
1475
1665
 
1476
1666
  assert_response :created
1477
- assert json_response['breeds'].is_a?(Hash)
1478
- assert_equal 'Calic', json_response['breeds']['name']
1667
+ assert json_response['data'].is_a?(Hash)
1668
+ assert_equal 'Calic', json_response['data']['name']
1479
1669
 
1480
1670
  put :update,
1481
1671
  {
1482
- id: json_response['breeds']['id'],
1483
- breeds: {
1672
+ id: json_response['data']['id'],
1673
+ data: {
1674
+ id: json_response['data']['id'],
1675
+ type: 'breeds',
1484
1676
  name: 'calico'
1485
1677
  }
1486
1678
  }
1487
1679
  assert_response :success
1488
- assert json_response['breeds'].is_a?(Hash)
1489
- assert_equal 'Calico', json_response['breeds']['name']
1680
+ assert json_response['data'].is_a?(Hash)
1681
+ assert_equal 'Calico', json_response['data']['name']
1490
1682
  end
1491
1683
 
1492
1684
  def test_poro_delete
@@ -1509,63 +1701,52 @@ class Api::V1::PostsControllerTest < ActionController::TestCase
1509
1701
  def test_show_post_namespaced
1510
1702
  get :show, {id: '1'}
1511
1703
  assert_response :success
1512
- assert_hash_equals(
1513
- {
1514
- posts: {
1515
- id: '1',
1516
- title: 'New post',
1517
- body: 'A body!!!',
1518
- subject: 'New post',
1519
- links: {
1520
- section: nil,
1521
- writer: '1',
1522
- comments: ['1', '2']
1523
- }
1524
- }
1525
- }, json_response
1526
- )
1704
+ assert_equal 'http://test.host/api/v1/posts/1/links/writer', json_response['data']['links']['writer']['self']
1527
1705
  end
1528
1706
 
1529
1707
  def test_show_post_namespaced_include
1530
1708
  get :show, {id: '1', include: 'writer'}
1531
1709
  assert_response :success
1532
- assert_equal '1', json_response['posts']['links']['writer']
1533
- assert_nil json_response['posts']['links']['tags']
1534
- assert_equal '1', json_response['linked']['writers'][0]['id']
1535
- assert_equal 'joe@xyz.fake', json_response['linked']['writers'][0]['email']
1710
+ assert_equal '1', json_response['data']['links']['writer']['id']
1711
+ assert_nil json_response['data']['links']['tags']
1712
+ assert_equal '1', json_response['linked'][0]['id']
1713
+ assert_equal 'writers', json_response['linked'][0]['type']
1714
+ assert_equal 'joe@xyz.fake', json_response['linked'][0]['email']
1536
1715
  end
1537
1716
 
1538
1717
  def test_index_filter_on_association_namespaced
1539
- get :index, {writer: '1'}
1718
+ get :index, {filter: {writer: '1'}}
1540
1719
  assert_response :success
1541
- assert_equal 3, json_response['posts'].size
1720
+ assert_equal 3, json_response['data'].size
1542
1721
  end
1543
1722
 
1544
1723
  def test_sorting_desc_namespaced
1545
1724
  get :index, {sort: '-title'}
1546
1725
 
1547
1726
  assert_response :success
1548
- assert_equal "Update This Later - Multiple", json_response['posts'][0]['title']
1727
+ assert_equal "Update This Later - Multiple", json_response['data'][0]['title']
1549
1728
  end
1550
1729
 
1551
1730
  def test_create_simple_namespaced
1552
1731
  set_content_type_header!
1553
1732
  post :create,
1554
1733
  {
1555
- posts: {
1734
+ data: {
1735
+ type: 'posts',
1556
1736
  title: 'JR - now with Namespacing',
1557
1737
  body: 'JSONAPIResources is the greatest thing since unsliced bread now that it has namespaced resources.',
1558
1738
  links: {
1559
- writer: '3'
1739
+ writer: {type: 'writers', id: '3'}
1560
1740
  }
1561
1741
  }
1562
1742
  }
1563
1743
 
1564
1744
  assert_response :created
1565
- assert json_response['posts'].is_a?(Hash)
1566
- assert_equal '3', json_response['posts']['links']['writer']
1567
- assert_equal 'JR - now with Namespacing', json_response['posts']['title']
1568
- assert_equal 'JSONAPIResources is the greatest thing since unsliced bread now that it has namespaced resources.', json_response['posts']['body']
1745
+ assert json_response['data'].is_a?(Hash)
1746
+ assert_equal '3', json_response['data']['links']['writer']['id']
1747
+ assert_equal 'JR - now with Namespacing', json_response['data']['title']
1748
+ assert_equal 'JSONAPIResources is the greatest thing since unsliced bread now that it has namespaced resources.',
1749
+ json_response['data']['body']
1569
1750
  end
1570
1751
 
1571
1752
  end
@@ -1574,15 +1755,140 @@ class FactsControllerTest < ActionController::TestCase
1574
1755
  def test_type_formatting
1575
1756
  get :show, {id: '1'}
1576
1757
  assert_response :success
1577
- assert json_response['facts'].is_a?(Hash)
1578
- assert_equal 'Jane Author', json_response['facts']['spouseName']
1579
- assert_equal 'First man to run across Antartica.', json_response['facts']['bio']
1580
- assert_equal 23.89/45.6, json_response['facts']['qualityRating']
1581
- assert_equal 47000.56, json_response['facts']['salary']
1582
- assert_equal '2013-08-07T20:25:00.000Z', json_response['facts']['dateTimeJoined']
1583
- assert_equal '1965-06-30', json_response['facts']['birthday']
1584
- assert_equal '2000-01-01T20:00:00Z', json_response['facts']['bedtime']
1585
- assert_equal 'abc', json_response['facts']['photo']
1586
- assert_equal false, json_response['facts']['cool']
1758
+ assert json_response['data'].is_a?(Hash)
1759
+ assert_equal 'Jane Author', json_response['data']['spouseName']
1760
+ assert_equal 'First man to run across Antartica.', json_response['data']['bio']
1761
+ assert_equal 23.89/45.6, json_response['data']['qualityRating']
1762
+ assert_equal 47000.56, json_response['data']['salary']
1763
+ assert_equal '2013-08-07T20:25:00.000Z', json_response['data']['dateTimeJoined']
1764
+ assert_equal '1965-06-30', json_response['data']['birthday']
1765
+ assert_equal '2000-01-01T20:00:00Z', json_response['data']['bedtime']
1766
+ assert_equal 'abc', json_response['data']['photo']
1767
+ assert_equal false, json_response['data']['cool']
1768
+ end
1769
+ end
1770
+
1771
+ class Api::V2::BooksControllerTest < ActionController::TestCase
1772
+ def after_teardown
1773
+ Api::V2::BookResource.paginator :offset
1774
+ end
1775
+
1776
+ def test_books_offset_pagination_no_params
1777
+ Api::V2::BookResource.paginator :offset
1778
+
1779
+ get :index
1780
+ assert_response :success
1781
+ assert_equal 10, json_response['data'].size
1782
+ assert_equal 'Book 0', json_response['data'][0]['title']
1783
+ end
1784
+
1785
+ def test_books_offset_pagination
1786
+ Api::V2::BookResource.paginator :offset
1787
+
1788
+ get :index, {page: {offset: 50, limit: 12}}
1789
+ assert_response :success
1790
+ assert_equal 12, json_response['data'].size
1791
+ assert_equal 'Book 50', json_response['data'][0]['title']
1792
+ end
1793
+
1794
+ def test_books_offset_pagination_bad_page_param
1795
+ Api::V2::BookResource.paginator :offset
1796
+
1797
+ get :index, {page: {offset_bad: 50, limit: 12}}
1798
+ assert_response :bad_request
1799
+ assert_match /offset_bad is not an allowed page parameter./, json_response['errors'][0]['detail']
1800
+ end
1801
+
1802
+ def test_books_offset_pagination_bad_param_value_limit_to_large
1803
+ Api::V2::BookResource.paginator :offset
1804
+
1805
+ get :index, {page: {offset: 50, limit: 1000}}
1806
+ assert_response :bad_request
1807
+ assert_match /Limit exceeds maximum page size of 20./, json_response['errors'][0]['detail']
1808
+ end
1809
+
1810
+ def test_books_offset_pagination_bad_param_value_limit_too_small
1811
+ Api::V2::BookResource.paginator :offset
1812
+
1813
+ get :index, {page: {offset: 50, limit: -1}}
1814
+ assert_response :bad_request
1815
+ assert_match /-1 is not a valid value for limit page parameter./, json_response['errors'][0]['detail']
1816
+ end
1817
+
1818
+ def test_books_offset_pagination_invalid_page_format
1819
+ Api::V2::BookResource.paginator :offset
1820
+
1821
+ get :index, {page: 50}
1822
+ assert_response :bad_request
1823
+ assert_match /Invalid Page Object./, json_response['errors'][0]['detail']
1824
+ end
1825
+
1826
+ def test_books_paged_pagination_no_params
1827
+ Api::V2::BookResource.paginator :paged
1828
+
1829
+ get :index
1830
+ assert_response :success
1831
+ assert_equal 10, json_response['data'].size
1832
+ assert_equal 'Book 0', json_response['data'][0]['title']
1833
+ end
1834
+
1835
+ def test_books_paged_pagination_no_page
1836
+ Api::V2::BookResource.paginator :paged
1837
+
1838
+ get :index, {page: {size: 12}}
1839
+ assert_response :success
1840
+ assert_equal 12, json_response['data'].size
1841
+ assert_equal 'Book 0', json_response['data'][0]['title']
1842
+ end
1843
+
1844
+ def test_books_paged_pagination
1845
+ Api::V2::BookResource.paginator :paged
1846
+
1847
+ get :index, {page: {number: 3, size: 12}}
1848
+ assert_response :success
1849
+ assert_equal 12, json_response['data'].size
1850
+ assert_equal 'Book 24', json_response['data'][0]['title']
1851
+ end
1852
+
1853
+ def test_books_paged_pagination_bad_page_param
1854
+ Api::V2::BookResource.paginator :paged
1855
+
1856
+ get :index, {page: {number_bad: 50, size: 12}}
1857
+ assert_response :bad_request
1858
+ assert_match /number_bad is not an allowed page parameter./, json_response['errors'][0]['detail']
1859
+ end
1860
+
1861
+ def test_books_paged_pagination_bad_param_value_limit_to_large
1862
+ Api::V2::BookResource.paginator :paged
1863
+
1864
+ get :index, {page: {number: 50, size: 1000}}
1865
+ assert_response :bad_request
1866
+ assert_match /size exceeds maximum page size of 20./, json_response['errors'][0]['detail']
1867
+ end
1868
+
1869
+ def test_books_paged_pagination_bad_param_value_limit_too_small
1870
+ Api::V2::BookResource.paginator :paged
1871
+
1872
+ get :index, {page: {number: 50, size: -1}}
1873
+ assert_response :bad_request
1874
+ assert_match /-1 is not a valid value for size page parameter./, json_response['errors'][0]['detail']
1875
+ end
1876
+
1877
+ def test_books_paged_pagination_invalid_page_format_interpret_int_text
1878
+ Api::V2::BookResource.paginator :paged
1879
+
1880
+ get :index, {page: 'qwerty'}
1881
+ assert_response :success
1882
+ assert_equal 10, json_response['data'].size
1883
+ assert_equal 'Book 0', json_response['data'][0]['title']
1884
+ end
1885
+
1886
+ def test_books_paged_pagination_invalid_page_format_interpret_int
1887
+ Api::V2::BookResource.paginator :paged
1888
+
1889
+ get :index, {page: 3}
1890
+ assert_response :success
1891
+ assert_equal 10, json_response['data'].size
1892
+ assert_equal 'Book 20', json_response['data'][0]['title']
1587
1893
  end
1588
1894
  end