jsonapi-resources 0.4.2 → 0.4.3

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/README.md +103 -71
  4. data/Rakefile +2 -2
  5. data/jsonapi-resources.gemspec +2 -2
  6. data/lib/jsonapi-resources.rb +0 -1
  7. data/lib/jsonapi/active_record_operations_processor.rb +10 -2
  8. data/lib/jsonapi/acts_as_resource_controller.rb +26 -24
  9. data/lib/jsonapi/association.rb +50 -15
  10. data/lib/jsonapi/callbacks.rb +1 -2
  11. data/lib/jsonapi/configuration.rb +8 -24
  12. data/lib/jsonapi/error.rb +1 -2
  13. data/lib/jsonapi/error_codes.rb +3 -1
  14. data/lib/jsonapi/exceptions.rb +59 -47
  15. data/lib/jsonapi/include_directives.rb +11 -11
  16. data/lib/jsonapi/mime_types.rb +2 -2
  17. data/lib/jsonapi/operation.rb +28 -11
  18. data/lib/jsonapi/operations_processor.rb +16 -5
  19. data/lib/jsonapi/paginator.rb +19 -19
  20. data/lib/jsonapi/request.rb +175 -196
  21. data/lib/jsonapi/resource.rb +158 -105
  22. data/lib/jsonapi/resource_serializer.rb +37 -26
  23. data/lib/jsonapi/resources/version.rb +2 -2
  24. data/lib/jsonapi/response_document.rb +5 -4
  25. data/lib/jsonapi/routing_ext.rb +24 -19
  26. data/test/controllers/controller_test.rb +261 -31
  27. data/test/fixtures/active_record.rb +206 -8
  28. data/test/fixtures/book_comments.yml +2 -1
  29. data/test/fixtures/books.yml +1 -0
  30. data/test/fixtures/documents.yml +3 -0
  31. data/test/fixtures/people.yml +8 -1
  32. data/test/fixtures/pictures.yml +15 -0
  33. data/test/fixtures/products.yml +3 -0
  34. data/test/fixtures/vehicles.yml +8 -0
  35. data/test/helpers/{hash_helpers.rb → assertions.rb} +6 -1
  36. data/test/integration/requests/request_test.rb +14 -3
  37. data/test/integration/routes/routes_test.rb +47 -0
  38. data/test/test_helper.rb +27 -4
  39. data/test/unit/serializer/include_directives_test.rb +5 -0
  40. data/test/unit/serializer/polymorphic_serializer_test.rb +384 -0
  41. data/test/unit/serializer/serializer_test.rb +19 -1
  42. metadata +14 -4
@@ -22,6 +22,7 @@ module JSONAPI
22
22
  @key_formatter = options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
23
23
  @route_formatter = options.fetch(:route_formatter, JSONAPI.configuration.route_formatter)
24
24
  @base_url = options.fetch(:base_url, '')
25
+ @scope_id = options[:scope_id]
25
26
  end
26
27
 
27
28
  # Converts a single resource, or an array of resources to a hash, conforming to the JSONAPI structure
@@ -45,7 +46,7 @@ module JSONAPI
45
46
  end
46
47
  end
47
48
 
48
- primary_hash = {data: is_resource_collection ? primary_objects : primary_objects[0]}
49
+ primary_hash = { data: is_resource_collection ? primary_objects : primary_objects[0] }
49
50
 
50
51
  primary_hash[:included] = included_objects if included_objects.size > 0
51
52
  primary_hash
@@ -72,6 +73,7 @@ module JSONAPI
72
73
  end
73
74
 
74
75
  private
76
+
75
77
  # Process the primary source object(s). This will then serialize associated object recursively based on the
76
78
  # requested includes. Fields are controlled fields option for each resource type, such
77
79
  # as fields: { people: [:id, :email, :comments], posts: [:id, :title, :author], comments: [:id, :body, :post]}
@@ -100,7 +102,7 @@ module JSONAPI
100
102
  obj_hash = {}
101
103
 
102
104
  id_format = source.class._attribute_options(:id)[:format]
103
- #protect against ids that were declared as an attribute, but did not have a format set.
105
+ # protect against ids that were declared as an attribute, but did not have a format set.
104
106
  id_format = 'id' if id_format == :default
105
107
  obj_hash['id'] = format_value(source.id, id_format)
106
108
 
@@ -115,7 +117,7 @@ module JSONAPI
115
117
  relationships = relationship_data(source, include_directives)
116
118
  obj_hash['relationships'] = relationships unless relationships.nil? || relationships.empty?
117
119
 
118
- return obj_hash
120
+ obj_hash
119
121
  end
120
122
 
121
123
  def requested_fields(model)
@@ -125,9 +127,7 @@ module JSONAPI
125
127
  def attribute_hash(source)
126
128
  requested = requested_fields(source.class._type)
127
129
  fields = source.fetchable_fields & source.class._attributes.keys.to_a
128
- unless requested.nil?
129
- fields = requested & fields
130
- end
130
+ fields = requested & fields unless requested.nil?
131
131
 
132
132
  fields.each_with_object({}) do |name, hash|
133
133
  format = source.class._attribute_options(name)[:format]
@@ -141,9 +141,7 @@ module JSONAPI
141
141
  associations = source.class._associations
142
142
  requested = requested_fields(source.class._type)
143
143
  fields = associations.keys
144
- unless requested.nil?
145
- fields = requested & fields
146
- end
144
+ fields = requested & fields unless requested.nil?
147
145
 
148
146
  field_set = Set.new(fields)
149
147
 
@@ -172,6 +170,7 @@ module JSONAPI
172
170
  resource = source.send(name)
173
171
  if resource
174
172
  id = resource.id
173
+ type = association.type_for_source(source)
175
174
  associations_only = already_serialized?(type, id)
176
175
  if include_linkage && !associations_only
177
176
  add_included_object(type, id, object_hash(resource, ia))
@@ -208,7 +207,12 @@ module JSONAPI
208
207
  end
209
208
 
210
209
  def formatted_module_path_from_klass(klass)
211
- klass.name =~ /::[^:]+\Z/ ? (@route_formatter.format($`).freeze.gsub('::', '/') + '/').downcase : ''
210
+ if klass.name =~ /::[^:]+\Z/
211
+ path = (@route_formatter.format($`).freeze.gsub('::', '/') + '/').downcase
212
+ @scope_id ? "#{path}#{@scope_id}/" : path
213
+ else
214
+ ''
215
+ end
212
216
  end
213
217
 
214
218
  def self_href(source)
@@ -217,7 +221,7 @@ module JSONAPI
217
221
 
218
222
  def already_serialized?(type, id)
219
223
  type = format_key(type)
220
- return @included_objects.key?(type) && @included_objects[type].key?(id)
224
+ @included_objects.key?(type) && @included_objects[type].key?(id)
221
225
  end
222
226
 
223
227
  def format_route(route)
@@ -235,8 +239,9 @@ module JSONAPI
235
239
  def has_one_linkage(source, association)
236
240
  linkage = {}
237
241
  linkage_id = foreign_key_value(source, association)
242
+
238
243
  if linkage_id
239
- linkage[:type] = format_key(association.type)
244
+ linkage[:type] = format_key(association.type_for_source(source))
240
245
  linkage[:id] = linkage_id
241
246
  else
242
247
  linkage = nil
@@ -246,9 +251,10 @@ module JSONAPI
246
251
 
247
252
  def has_many_linkage(source, association)
248
253
  linkage = []
249
- linkage_ids = foreign_key_value(source, association)
250
- linkage_ids.each do |linkage_id|
251
- linkage.append({type: format_key(association.type), id: linkage_id})
254
+ linkage_types_and_values = foreign_key_types_and_values(source, association)
255
+
256
+ linkage_types_and_values.each do |type, value|
257
+ linkage.append({type: format_key(type), id: value})
252
258
  end
253
259
  linkage
254
260
  end
@@ -279,15 +285,24 @@ module JSONAPI
279
285
  end
280
286
  end
281
287
 
282
- # Extracts the foreign key value for an association.
288
+ # Extracts the foreign key value for a has_one association.
283
289
  def foreign_key_value(source, association)
284
290
  foreign_key = association.foreign_key
285
291
  value = source.send(foreign_key)
292
+ IdValueFormatter.format(value)
293
+ end
286
294
 
295
+ def foreign_key_types_and_values(source, association)
287
296
  if association.is_a?(JSONAPI::Association::HasMany)
288
- value.map { |value| IdValueFormatter.format(value) }
289
- elsif association.is_a?(JSONAPI::Association::HasOne)
290
- IdValueFormatter.format(value)
297
+ if association.polymorphic?
298
+ source.model.send(association.name).pluck(:type, :id).map do |type, id|
299
+ [type.pluralize, IdValueFormatter.format(id)]
300
+ end
301
+ else
302
+ source.send(association.foreign_key).map do |value|
303
+ [association.type, IdValueFormatter.format(value)]
304
+ end
305
+ end
291
306
  end
292
307
  end
293
308
 
@@ -301,16 +316,12 @@ module JSONAPI
301
316
  def add_included_object(type, id, object_hash, primary = false)
302
317
  type = format_key(type)
303
318
 
304
- unless @included_objects.key?(type)
305
- @included_objects[type] = {}
306
- end
319
+ @included_objects[type] = {} unless @included_objects.key?(type)
307
320
 
308
321
  if already_serialized?(type, id)
309
- if primary
310
- set_primary(type, id)
311
- end
322
+ set_primary(type, id) if primary
312
323
  else
313
- @included_objects[type].store(id, {primary: primary, object_hash: object_hash})
324
+ @included_objects[type].store(id, primary: primary, object_hash: object_hash)
314
325
  end
315
326
  end
316
327
 
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = "0.4.2"
3
+ VERSION = '0.4.3'
4
4
  end
5
- end
5
+ end
@@ -36,7 +36,8 @@ module JSONAPI
36
36
  fields: @options[:fields],
37
37
  base_url: @options.fetch(:base_url, ''),
38
38
  key_formatter: @key_formatter,
39
- route_formatter: @options.fetch(:route_formatter, JSONAPI.configuration.route_formatter)
39
+ route_formatter: @options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
40
+ scope_id: @options[:scope_id]
40
41
  )
41
42
  end
42
43
 
@@ -50,7 +51,7 @@ module JSONAPI
50
51
  @operation_results.results.each do |result|
51
52
  meta.merge!(result.meta)
52
53
 
53
- if JSONAPI.configuration.top_level_meta_include_record_count
54
+ if JSONAPI.configuration.top_level_meta_include_record_count && result.respond_to?(:record_count)
54
55
  meta[JSONAPI.configuration.top_level_meta_record_count_key] = result.record_count
55
56
  end
56
57
  end
@@ -90,7 +91,7 @@ module JSONAPI
90
91
 
91
92
  def results_to_hash
92
93
  if @operation_results.has_errors?
93
- {errors: @operation_results.all_errors}
94
+ { errors: @operation_results.all_errors }
94
95
  else
95
96
  if @operation_results.results.length == 1
96
97
  result = @operation_results.results[0]
@@ -123,4 +124,4 @@ module JSONAPI
123
124
  end
124
125
  end
125
126
  end
126
- end
127
+ end
@@ -1,7 +1,6 @@
1
1
  module ActionDispatch
2
2
  module Routing
3
3
  class Mapper
4
-
5
4
  Resource.class_eval do
6
5
  def unformat_route(route)
7
6
  JSONAPI.configuration.route_formatter.unformat(route.to_s)
@@ -17,7 +16,7 @@ module ActionDispatch
17
16
  JSONAPI.configuration.route_formatter.format(route.to_s)
18
17
  end
19
18
 
20
- def jsonapi_resource(*resources, &block)
19
+ def jsonapi_resource(*resources, &_block)
21
20
  @resource_type = resources.first
22
21
  res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
23
22
 
@@ -57,7 +56,7 @@ module ActionDispatch
57
56
  end
58
57
  end
59
58
 
60
- def jsonapi_resources(*resources, &block)
59
+ def jsonapi_resources(*resources, &_block)
61
60
  @resource_type = resources.first
62
61
  res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
63
62
 
@@ -67,9 +66,10 @@ module ActionDispatch
67
66
 
68
67
  options[:param] = :id
69
68
 
70
- options[:path] = format_route(@resource_type)
69
+ options[:path] = format_route(options[:path] || @resource_type)
71
70
 
72
71
  if options[:except]
72
+ options[:except] = Array(options[:except])
73
73
  options[:except] << :new unless options[:except].include?(:new) || options[:except].include?('new')
74
74
  options[:except] << :edit unless options[:except].include?(:edit) || options[:except].include?('edit')
75
75
  else
@@ -92,7 +92,7 @@ module ActionDispatch
92
92
  if only = options[:only]
93
93
  Array(only).map(&:to_sym)
94
94
  elsif except = options[:except]
95
- default_methods - except
95
+ default_methods - Array(except)
96
96
  else
97
97
  default_methods
98
98
  end
@@ -110,17 +110,17 @@ module ActionDispatch
110
110
 
111
111
  if methods.include?(:show)
112
112
  match "relationships/#{formatted_association_name}", controller: options[:controller],
113
- action: 'show_association', association: link_type.to_s, via: [:get]
113
+ action: 'show_association', association: link_type.to_s, via: [:get]
114
114
  end
115
115
 
116
116
  if methods.include?(:update)
117
117
  match "relationships/#{formatted_association_name}", controller: options[:controller],
118
- action: 'update_association', association: link_type.to_s, via: [:put, :patch]
118
+ action: 'update_association', association: link_type.to_s, via: [:put, :patch]
119
119
  end
120
120
 
121
121
  if methods.include?(:destroy)
122
122
  match "relationships/#{formatted_association_name}", controller: options[:controller],
123
- action: 'destroy_association', association: link_type.to_s, via: [:delete]
123
+ action: 'destroy_association', association: link_type.to_s, via: [:delete]
124
124
  end
125
125
  end
126
126
 
@@ -136,22 +136,22 @@ module ActionDispatch
136
136
 
137
137
  if methods.include?(:show)
138
138
  match "relationships/#{formatted_association_name}", controller: options[:controller],
139
- action: 'show_association', association: link_type.to_s, via: [:get]
139
+ action: 'show_association', association: link_type.to_s, via: [:get]
140
140
  end
141
141
 
142
142
  if methods.include?(:create)
143
143
  match "relationships/#{formatted_association_name}", controller: options[:controller],
144
- action: 'create_association', association: link_type.to_s, via: [:post]
144
+ action: 'create_association', association: link_type.to_s, via: [:post]
145
145
  end
146
146
 
147
147
  if methods.include?(:update)
148
148
  match "relationships/#{formatted_association_name}", controller: options[:controller],
149
- action: 'update_association', association: link_type.to_s, via: [:put, :patch]
149
+ action: 'update_association', association: link_type.to_s, via: [:put, :patch]
150
150
  end
151
151
 
152
152
  if methods.include?(:destroy)
153
153
  match "relationships/#{formatted_association_name}/:keys", controller: options[:controller],
154
- action: 'destroy_association', association: link_type.to_s, via: [:delete]
154
+ action: 'destroy_association', association: link_type.to_s, via: [:delete]
155
155
  end
156
156
  end
157
157
 
@@ -163,13 +163,17 @@ module ActionDispatch
163
163
  association = source._associations[association_name]
164
164
 
165
165
  formatted_association_name = format_route(association.name)
166
- related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(association.class_name.underscore.pluralize))
167
- options[:controller] ||= related_resource._type.to_s
168
166
 
167
+ if association.polymorphic?
168
+ options[:controller] ||= association.class_name.underscore.pluralize
169
+ else
170
+ related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(association.class_name.underscore.pluralize))
171
+ options[:controller] ||= related_resource._type.to_s
172
+ end
169
173
 
170
174
  match "#{formatted_association_name}", controller: options[:controller],
171
- association: association.name, source: resource_type_with_module_prefix(source._type),
172
- action: 'get_related_resource', via: [:get]
175
+ association: association.name, source: resource_type_with_module_prefix(source._type),
176
+ action: 'get_related_resource', via: [:get]
173
177
  end
174
178
 
175
179
  def jsonapi_related_resources(*association)
@@ -184,14 +188,15 @@ module ActionDispatch
184
188
  options[:controller] ||= related_resource._type.to_s
185
189
 
186
190
  match "#{formatted_association_name}", controller: options[:controller],
187
- association: association.name, source: resource_type_with_module_prefix(source._type),
188
- action: 'get_related_resources', via: [:get]
191
+ association: association.name, source: resource_type_with_module_prefix(source._type),
192
+ action: 'get_related_resources', via: [:get]
189
193
  end
190
194
 
191
195
  private
196
+
192
197
  def resource_type_with_module_prefix(resource = nil)
193
198
  resource_name = resource || @scope[:jsonapi_resource]
194
- [@scope[:module], resource_name].compact.collect(&:to_s).join("/")
199
+ [@scope[:module], resource_name].compact.collect(&:to_s).join('/')
195
200
  end
196
201
  end
197
202
  end
@@ -54,19 +54,19 @@ class PostsControllerTest < ActionController::TestCase
54
54
  end
55
55
 
56
56
  def test_index_include_one_level_query_count
57
- query_count = count_queries do
57
+ count_queries do
58
58
  get :index, {include: 'author'}
59
59
  end
60
60
  assert_response :success
61
- assert_equal 2, query_count
61
+ assert_query_count(2)
62
62
  end
63
63
 
64
64
  def test_index_include_two_levels_query_count
65
- query_count = count_queries do
65
+ count_queries do
66
66
  get :index, {include: 'author,author.comments'}
67
67
  end
68
68
  assert_response :success
69
- assert_equal 3, query_count
69
+ assert_query_count(3)
70
70
  end
71
71
 
72
72
  def test_index_filter_by_ids_and_fields
@@ -76,10 +76,10 @@ class PostsControllerTest < ActionController::TestCase
76
76
 
77
77
  # type, id, links, attributes, relationships
78
78
  assert_equal 5, json_response['data'][0].size
79
- assert json_response['data'][0].has_key?('type')
80
- assert json_response['data'][0].has_key?('id')
81
- assert json_response['data'][0]['attributes'].has_key?('title')
82
- assert json_response['data'][0].has_key?('links')
79
+ assert json_response['data'][0].key?('type')
80
+ assert json_response['data'][0].key?('id')
81
+ assert json_response['data'][0]['attributes'].key?('title')
82
+ assert json_response['data'][0].key?('links')
83
83
  end
84
84
 
85
85
  def test_index_filter_by_ids_and_fields_specify_type
@@ -89,10 +89,10 @@ class PostsControllerTest < ActionController::TestCase
89
89
 
90
90
  # type, id, links, attributes, relationships
91
91
  assert_equal 5, json_response['data'][0].size
92
- assert json_response['data'][0].has_key?('type')
93
- assert json_response['data'][0].has_key?('id')
94
- assert json_response['data'][0]['attributes'].has_key?('title')
95
- assert json_response['data'][0].has_key?('links')
92
+ assert json_response['data'][0].key?('type')
93
+ assert json_response['data'][0].key?('id')
94
+ assert json_response['data'][0]['attributes'].key?('title')
95
+ assert json_response['data'][0].key?('links')
96
96
  end
97
97
 
98
98
  def test_index_filter_by_ids_and_fields_specify_unrelated_type
@@ -108,13 +108,16 @@ class PostsControllerTest < ActionController::TestCase
108
108
 
109
109
  # type, id, links, relationships
110
110
  assert_equal 4, json_response['data'][0].size
111
- assert json_response['data'][0].has_key?('type')
112
- assert json_response['data'][0].has_key?('id')
113
- assert json_response['data'][0]['relationships'].has_key?('author')
111
+ assert json_response['data'][0].key?('type')
112
+ assert json_response['data'][0].key?('id')
113
+ assert json_response['data'][0]['relationships'].key?('author')
114
114
  end
115
115
 
116
116
  def test_filter_association_single
117
- get :index, {filter: {tags: '5,1'}}
117
+ count_queries do
118
+ get :index, {filter: {tags: '5,1'}}
119
+ end
120
+ assert_query_count(1)
118
121
  assert_response :success
119
122
  assert_equal 3, json_response['data'].size
120
123
  assert_match /New post/, response.body
@@ -123,7 +126,10 @@ class PostsControllerTest < ActionController::TestCase
123
126
  end
124
127
 
125
128
  def test_filter_associations_multiple
126
- get :index, {filter: {tags: '5,1', comments: '3'}}
129
+ count_queries do
130
+ get :index, {filter: {tags: '5,1', comments: '3'}}
131
+ end
132
+ assert_query_count(1)
127
133
  assert_response :success
128
134
  assert_equal 1, json_response['data'].size
129
135
  assert_match /JR Solves your serialization woes!/, response.body
@@ -227,6 +233,15 @@ class PostsControllerTest < ActionController::TestCase
227
233
  assert_nil json_response['included']
228
234
  end
229
235
 
236
+ def test_show_does_not_include_records_count_in_meta
237
+ JSONAPI.configuration.top_level_meta_include_record_count = true
238
+ get :show, { id: Post.first.id }
239
+ assert_response :success
240
+ assert_equal json_response['meta'], nil
241
+ ensure
242
+ JSONAPI.configuration.top_level_meta_include_record_count = false
243
+ end
244
+
230
245
  def test_show_single_with_includes
231
246
  get :show, {id: '1', include: 'comments'}
232
247
  assert_response :success
@@ -317,7 +332,7 @@ class PostsControllerTest < ActionController::TestCase
317
332
  }
318
333
 
319
334
  assert_response :unprocessable_entity
320
- # Todo: check if this validation is working
335
+ # TODO: check if this validation is working
321
336
  assert_match /author - can't be blank/, response.body
322
337
  end
323
338
 
@@ -621,6 +636,28 @@ class PostsControllerTest < ActionController::TestCase
621
636
  json_response['data']['relationships']['tags']['data'])
622
637
  end
623
638
 
639
+ def test_update_with_internal_server_error
640
+ set_content_type_header!
641
+ post_object = Post.find(3)
642
+ title = post_object.title
643
+
644
+ put :update,
645
+ {
646
+ id: 3,
647
+ data: {
648
+ id: '3',
649
+ type: 'posts',
650
+ attributes: {
651
+ title: 'BOOM'
652
+ }
653
+ }
654
+ }
655
+
656
+ assert_response 500
657
+ post_object = Post.find(3)
658
+ assert_equal title, post_object.title
659
+ end
660
+
624
661
  def test_update_remove_links
625
662
  set_content_type_header!
626
663
  put :update,
@@ -1518,7 +1555,7 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1518
1555
  get :show, {id: 1, include: 'isoCurrency,employee', 'fields' => {'expenseEntries' => 'transactionDate'}}
1519
1556
  assert_response :success
1520
1557
  assert json_response['data'].is_a?(Hash)
1521
- assert json_response['data']['attributes'].has_key?('transactionDate')
1558
+ assert json_response['data']['attributes'].key?('transactionDate')
1522
1559
  assert_equal 2, json_response['included'].size
1523
1560
  end
1524
1561
 
@@ -1527,7 +1564,7 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
1527
1564
  'isoCurrencies' => 'id,name'}}
1528
1565
  assert_response :success
1529
1566
  assert json_response['data'].is_a?(Hash)
1530
- assert json_response['data']['attributes'].has_key?('transactionDate')
1567
+ assert json_response['data']['attributes'].key?('transactionDate')
1531
1568
  assert_equal 2, json_response['included'].size
1532
1569
  end
1533
1570
 
@@ -1863,7 +1900,7 @@ class PeopleControllerTest < ActionController::TestCase
1863
1900
  def test_get_related_resource
1864
1901
  JSONAPI.configuration.json_key_format = :dasherized_key
1865
1902
  JSONAPI.configuration.route_format = :underscored_key
1866
- get :get_related_resource, {post_id: '2', association: 'author', :source=>'posts'}
1903
+ get :get_related_resource, {post_id: '2', association: 'author', source:'posts'}
1867
1904
  assert_response :success
1868
1905
  assert_hash_equals(
1869
1906
  {
@@ -1907,7 +1944,13 @@ class PeopleControllerTest < ActionController::TestCase
1907
1944
  "related" => "http://test.host/people/1/hair_cut"
1908
1945
  },
1909
1946
  "data" => nil
1910
- }
1947
+ },
1948
+ vehicles: {
1949
+ links: {
1950
+ self: "http://test.host/people/1/relationships/vehicles",
1951
+ related: "http://test.host/people/1/vehicles"
1952
+ }
1953
+ }
1911
1954
  }
1912
1955
  }
1913
1956
  },
@@ -1916,7 +1959,7 @@ class PeopleControllerTest < ActionController::TestCase
1916
1959
  end
1917
1960
 
1918
1961
  def test_get_related_resource_nil
1919
- get :get_related_resource, {post_id: '17', association: 'author', :source=>'posts'}
1962
+ get :get_related_resource, {post_id: '17', association: 'author', source:'posts'}
1920
1963
  assert_response :success
1921
1964
  assert_hash_equals json_response,
1922
1965
  {
@@ -2133,6 +2176,7 @@ end
2133
2176
  class Api::V2::BooksControllerTest < ActionController::TestCase
2134
2177
  def setup
2135
2178
  JSONAPI.configuration.json_key_format = :dasherized_key
2179
+ $test_user = Person.find(1)
2136
2180
  end
2137
2181
 
2138
2182
  def after_teardown
@@ -2155,7 +2199,7 @@ class Api::V2::BooksControllerTest < ActionController::TestCase
2155
2199
  JSONAPI.configuration.top_level_meta_include_record_count = false
2156
2200
 
2157
2201
  assert_response :success
2158
- assert_equal 1000, json_response['meta']['record-count']
2202
+ assert_equal 901, json_response['meta']['record-count']
2159
2203
  assert_equal 10, json_response['data'].size
2160
2204
  assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2161
2205
  end
@@ -2170,7 +2214,7 @@ class Api::V2::BooksControllerTest < ActionController::TestCase
2170
2214
  JSONAPI.configuration.top_level_meta_record_count_key = :record_count
2171
2215
 
2172
2216
  assert_response :success
2173
- assert_equal 1000, json_response['meta']['total-records']
2217
+ assert_equal 901, json_response['meta']['total-records']
2174
2218
  assert_equal 10, json_response['data'].size
2175
2219
  assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2176
2220
  end
@@ -2178,25 +2222,25 @@ class Api::V2::BooksControllerTest < ActionController::TestCase
2178
2222
  def test_books_offset_pagination_no_params_includes_query_count_one_level
2179
2223
  Api::V2::BookResource.paginator :offset
2180
2224
 
2181
- query_count = count_queries do
2225
+ count_queries do
2182
2226
  get :index, {include: 'book-comments'}
2183
2227
  end
2184
2228
  assert_response :success
2185
2229
  assert_equal 10, json_response['data'].size
2186
2230
  assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2187
- assert_equal 3, query_count
2231
+ assert_query_count(3)
2188
2232
  end
2189
2233
 
2190
2234
  def test_books_offset_pagination_no_params_includes_query_count_two_levels
2191
2235
  Api::V2::BookResource.paginator :offset
2192
2236
 
2193
- query_count = count_queries do
2237
+ count_queries do
2194
2238
  get :index, {include: 'book-comments,book-comments.author'}
2195
2239
  end
2196
2240
  assert_response :success
2197
2241
  assert_equal 10, json_response['data'].size
2198
2242
  assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2199
- assert_equal 4, query_count
2243
+ assert_query_count(4)
2200
2244
  end
2201
2245
 
2202
2246
  def test_books_offset_pagination
@@ -2315,6 +2359,192 @@ class Api::V2::BooksControllerTest < ActionController::TestCase
2315
2359
  assert_equal 10, json_response['data'].size
2316
2360
  assert_equal 'Book 20', json_response['data'][0]['attributes']['title']
2317
2361
  end
2362
+
2363
+ def test_books_included_paged
2364
+ Api::V2::BookResource.paginator :offset
2365
+
2366
+ count_queries do
2367
+ get :index, {filter: {id: '0'}, include: 'book-comments'}
2368
+ end
2369
+ assert_response :success
2370
+ assert_equal 1, json_response['data'].size
2371
+ assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2372
+ assert_query_count(3)
2373
+ end
2374
+
2375
+ def test_books_banned_non_book_admin
2376
+ $test_user = Person.find(1)
2377
+ Api::V2::BookResource.paginator :offset
2378
+ count_queries do
2379
+ JSONAPI.configuration.top_level_meta_include_record_count = true
2380
+ get :index, {page: {offset: 50, limit: 12}}
2381
+ JSONAPI.configuration.top_level_meta_include_record_count = false
2382
+ end
2383
+ assert_response :success
2384
+ assert_equal 12, json_response['data'].size
2385
+ assert_equal 'Book 50', json_response['data'][0]['attributes']['title']
2386
+ assert_equal 901, json_response['meta']['record-count']
2387
+ assert_query_count(2)
2388
+ end
2389
+
2390
+ def test_books_banned_non_book_admin_includes_switched
2391
+ $test_user = Person.find(1)
2392
+ Api::V2::BookResource.paginator :offset
2393
+ count_queries do
2394
+ JSONAPI.configuration.top_level_meta_include_record_count = true
2395
+ get :index, {page: {offset: 0, limit: 12}, include: 'book-comments'}
2396
+ JSONAPI.configuration.top_level_meta_include_record_count = false
2397
+ end
2398
+
2399
+ assert_response :success
2400
+ assert_equal 12, json_response['data'].size
2401
+ assert_equal 130, json_response['included'].size
2402
+ assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2403
+ assert_equal 26, json_response['data'][0]['relationships']['book-comments']['data'].size
2404
+ assert_equal 'book-comments', json_response['included'][0]['type']
2405
+ assert_equal 901, json_response['meta']['record-count']
2406
+ assert_query_count(3)
2407
+ end
2408
+
2409
+ def test_books_banned_non_book_admin_includes_nested_includes
2410
+ $test_user = Person.find(1)
2411
+ Api::V2::BookResource.paginator :offset
2412
+ count_queries do
2413
+ JSONAPI.configuration.top_level_meta_include_record_count = true
2414
+ get :index, {page: {offset: 0, limit: 12}, include: 'book-comments.author'}
2415
+ JSONAPI.configuration.top_level_meta_include_record_count = false
2416
+ end
2417
+ assert_response :success
2418
+ assert_equal 12, json_response['data'].size
2419
+ assert_equal 131, json_response['included'].size
2420
+ assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2421
+ assert_equal 901, json_response['meta']['record-count']
2422
+ assert_query_count(4)
2423
+ end
2424
+
2425
+ def test_books_banned_admin
2426
+ $test_user = Person.find(5)
2427
+ Api::V2::BookResource.paginator :offset
2428
+ query_count = count_queries do
2429
+ JSONAPI.configuration.top_level_meta_include_record_count = true
2430
+ get :index, {page: {offset: 50, limit: 12}, filter: {banned: 'true'}}
2431
+ JSONAPI.configuration.top_level_meta_include_record_count = false
2432
+ end
2433
+ assert_response :success
2434
+ assert_equal 12, json_response['data'].size
2435
+ assert_equal 'Book 651', json_response['data'][0]['attributes']['title']
2436
+ assert_equal 99, json_response['meta']['record-count']
2437
+ assert_query_count(2)
2438
+ end
2439
+
2440
+ def test_books_not_banned_admin
2441
+ $test_user = Person.find(5)
2442
+ Api::V2::BookResource.paginator :offset
2443
+ count_queries do
2444
+ JSONAPI.configuration.top_level_meta_include_record_count = true
2445
+ get :index, {page: {offset: 50, limit: 12}, filter: {banned: 'false'}}
2446
+ JSONAPI.configuration.top_level_meta_include_record_count = false
2447
+ end
2448
+ assert_response :success
2449
+ assert_equal 12, json_response['data'].size
2450
+ assert_equal 'Book 50', json_response['data'][0]['attributes']['title']
2451
+ assert_equal 901, json_response['meta']['record-count']
2452
+ assert_query_count(2)
2453
+ end
2454
+
2455
+ def test_books_banned_non_book_admin_overlapped
2456
+ $test_user = Person.find(1)
2457
+ Api::V2::BookResource.paginator :offset
2458
+ count_queries do
2459
+ JSONAPI.configuration.top_level_meta_include_record_count = true
2460
+ get :index, {page: {offset: 590, limit: 20}}
2461
+ JSONAPI.configuration.top_level_meta_include_record_count = false
2462
+ end
2463
+ assert_response :success
2464
+ assert_equal 20, json_response['data'].size
2465
+ assert_equal 'Book 590', json_response['data'][0]['attributes']['title']
2466
+ assert_equal 901, json_response['meta']['record-count']
2467
+ assert_query_count(2)
2468
+ end
2469
+
2470
+ def test_books_included_exclude_unapproved
2471
+ $test_user = Person.find(1)
2472
+ Api::V2::BookResource.paginator :none
2473
+
2474
+ count_queries do
2475
+ get :index, {filter: {id: '0,1,2,3,4'}, include: 'book-comments'}
2476
+ end
2477
+ assert_response :success
2478
+ assert_equal 5, json_response['data'].size
2479
+ assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2480
+ assert_equal 130, json_response['included'].size
2481
+ assert_equal 26, json_response['data'][0]['relationships']['book-comments']['data'].size
2482
+ assert_query_count(2)
2483
+ end
2484
+
2485
+ def test_books_included_all_comments_for_admin
2486
+ $test_user = Person.find(5)
2487
+ Api::V2::BookResource.paginator :none
2488
+
2489
+ get :index, {filter: {id: '0,1,2,3,4'}, include: 'book-comments'}
2490
+ assert_response :success
2491
+ assert_equal 5, json_response['data'].size
2492
+ assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
2493
+ assert_equal 255, json_response['included'].size
2494
+ assert_equal 51, json_response['data'][0]['relationships']['book-comments']['data'].size
2495
+ end
2496
+
2497
+ def test_books_filter_by_book_comment_id_limited_user
2498
+ $test_user = Person.find(1)
2499
+ get :index, {filter: {book_comments: '0,52' }}
2500
+ assert_response :success
2501
+ assert_equal 1, json_response['data'].size
2502
+ end
2503
+
2504
+ def test_books_filter_by_book_comment_id_admin_user
2505
+ $test_user = Person.find(5)
2506
+ get :index, {filter: {book_comments: '0,52' }}
2507
+ assert_response :success
2508
+ assert_equal 2, json_response['data'].size
2509
+ end
2510
+ end
2511
+
2512
+ class Api::V2::BookCommentsControllerTest < ActionController::TestCase
2513
+ def setup
2514
+ JSONAPI.configuration.json_key_format = :dasherized_key
2515
+ Api::V2::BookCommentResource.paginator :none
2516
+ $test_user = Person.find(1)
2517
+ end
2518
+
2519
+ def test_book_comments_all_for_admin
2520
+ $test_user = Person.find(5)
2521
+ count_queries do
2522
+ get :index
2523
+ end
2524
+ assert_response :success
2525
+ assert_equal 255, json_response['data'].size
2526
+ assert_query_count(1)
2527
+ end
2528
+
2529
+ def test_book_comments_unapproved_context_based
2530
+ $test_user = Person.find(5)
2531
+ count_queries do
2532
+ get :index, {filter: {approved: 'false'}}
2533
+ end
2534
+ assert_response :success
2535
+ assert_equal 125, json_response['data'].size
2536
+ assert_query_count(1)
2537
+ end
2538
+
2539
+ def test_book_comments_exclude_unapproved_context_based
2540
+ $test_user = Person.find(1)
2541
+ count_queries do
2542
+ get :index
2543
+ end
2544
+ assert_response :success
2545
+ assert_equal 130, json_response['data'].size
2546
+ assert_query_count(1)
2547
+ end
2318
2548
  end
2319
2549
 
2320
2550
  class Api::V4::BooksControllerTest < ActionController::TestCase
@@ -2329,7 +2559,7 @@ class Api::V4::BooksControllerTest < ActionController::TestCase
2329
2559
  assert_response :success
2330
2560
  assert_equal 12, json_response['data'].size
2331
2561
  assert_equal 'Book 50', json_response['data'][0]['attributes']['title']
2332
- assert_equal 1000, json_response['meta']['totalRecords']
2562
+ assert_equal 901, json_response['meta']['totalRecords']
2333
2563
  JSONAPI.configuration.operations_processor = :active_record
2334
2564
  end
2335
2565
 
@@ -2397,4 +2627,4 @@ class Api::V1::PlanetsControllerTest < ActionController::TestCase
2397
2627
  assert_response :unprocessable_entity
2398
2628
  assert_match /Save failed or was cancelled/, json_response['errors'][0]['detail']
2399
2629
  end
2400
- end
2630
+ end