jsonapi-resources 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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