jsonapi-resources 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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