jsonapi-resources 0.2.0 → 0.3.0.pre1
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +5 -2
- data/Gemfile +3 -1
- data/README.md +52 -13
- data/jsonapi-resources.gemspec +1 -1
- data/lib/jsonapi-resources.rb +1 -0
- data/lib/jsonapi/association.rb +1 -9
- data/lib/jsonapi/error_codes.rb +1 -0
- data/lib/jsonapi/exceptions.rb +9 -5
- data/lib/jsonapi/formatter.rb +9 -18
- data/lib/jsonapi/paginator.rb +4 -15
- data/lib/jsonapi/request.rb +26 -42
- data/lib/jsonapi/resource.rb +35 -45
- data/lib/jsonapi/resource_controller.rb +6 -32
- data/lib/jsonapi/resource_serializer.rb +62 -33
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/routing_ext.rb +4 -4
- data/test/config/database.yml +2 -1
- data/test/controllers/controller_test.rb +200 -160
- data/test/fixtures/active_record.rb +44 -201
- data/test/fixtures/book_comments.yml +11 -0
- data/test/fixtures/books.yml +6 -0
- data/test/fixtures/comments.yml +17 -0
- data/test/fixtures/comments_tags.yml +20 -0
- data/test/fixtures/expense_entries.yml +13 -0
- data/test/fixtures/facts.yml +11 -0
- data/test/fixtures/iso_currencies.yml +17 -0
- data/test/fixtures/people.yml +24 -0
- data/test/fixtures/posts.yml +96 -0
- data/test/fixtures/posts_tags.yml +59 -0
- data/test/fixtures/preferences.yml +18 -0
- data/test/fixtures/sections.yml +8 -0
- data/test/fixtures/tags.yml +39 -0
- data/test/helpers/hash_helpers.rb +0 -7
- data/test/integration/requests/request_test.rb +86 -28
- data/test/integration/routes/routes_test.rb +14 -25
- data/test/test_helper.rb +41 -17
- data/test/unit/jsonapi_request/jsonapi_request_test.rb +152 -0
- data/test/unit/operation/operations_processor_test.rb +13 -2
- data/test/unit/resource/resource_test.rb +68 -13
- data/test/unit/serializer/serializer_test.rb +328 -220
- metadata +33 -6
- data/lib/jsonapi/resource_for.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7979f6e3a6f09ec2999fb78b2e0a06aa8f6f8be4
|
4
|
+
data.tar.gz: 6819f84c905130eaf614059393c59e55de8c457c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7389937f7df1ce1ece2ee0a481e626c7c8385cdddda73c12ec7625192eea77d27c4daa9d3bc73a5a10a0eb6aeab8fa4944459b52d0e8fefa8d36469f84dcf230
|
7
|
+
data.tar.gz: 0e3c2330066fc109530a627b9d5126e9cb60365c7dd5727230df9f9f9de27b3c9089637de1d447677774d76784d9816107d6d06df3a411d67fc779d481c2c329
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
@@ -10,10 +10,12 @@ platforms :jruby do
|
|
10
10
|
gem 'activerecord-jdbcsqlite3-adapter'
|
11
11
|
end
|
12
12
|
|
13
|
-
version = ENV['RAILS_VERSION'] || '
|
13
|
+
version = ENV['RAILS_VERSION'] || 'default'
|
14
14
|
rails = case version
|
15
15
|
when 'master'
|
16
16
|
{:github => 'rails/rails'}
|
17
|
+
when 'default'
|
18
|
+
'>= 4.2'
|
17
19
|
else
|
18
20
|
"~> #{version}"
|
19
21
|
end
|
data/README.md
CHANGED
@@ -166,6 +166,7 @@ The system will lookup a value formatter named `DateWithTimezoneValueFormatter`
|
|
166
166
|
#### Primary Key
|
167
167
|
|
168
168
|
Resources are always represented using a key of `id`. If the underlying model does not use `id` as the primary key you can use the `primary_key` method to tell the resource which field on the model to use as the primary key. Note: this doesn't have to be the actual primary key of the model. For example you may wish to use integers internally and a different scheme publicly.
|
169
|
+
By default only integer values are allowed for primary key. To change this behavior you can override `verify_key` class method:
|
169
170
|
|
170
171
|
```ruby
|
171
172
|
class CurrencyResource < JSONAPI::Resource
|
@@ -173,8 +174,11 @@ class CurrencyResource < JSONAPI::Resource
|
|
173
174
|
attributes :code, :name
|
174
175
|
|
175
176
|
has_many :expense_entries
|
176
|
-
end
|
177
177
|
|
178
|
+
def self.verify_key(key, context = nil)
|
179
|
+
key && String(key)
|
180
|
+
end
|
181
|
+
end
|
178
182
|
```
|
179
183
|
|
180
184
|
#### Model Name
|
@@ -260,11 +264,11 @@ end
|
|
260
264
|
|
261
265
|
##### Finders
|
262
266
|
|
263
|
-
Basic finding by filters is supported by resources. This is implemented in the `find
|
267
|
+
Basic finding by filters is supported by resources. This is implemented in the `find` and `find_by_key` finder methods. Currently this is implemented for `ActiveRecord` based resources. The finder methods rely on the `records` method to get an `Arel` relation. It is therefore possible to override `records` to affect the three find related methods.
|
264
268
|
|
265
269
|
###### Customizing base records for finder methods
|
266
270
|
|
267
|
-
If you need to change the base records on which `find
|
271
|
+
If you need to change the base records on which `find` and `find_by_key` operate, you can override the `records` method on the resource class.
|
268
272
|
|
269
273
|
For example to allow a user to only retrieve his own posts you can do the following:
|
270
274
|
|
@@ -279,6 +283,41 @@ class PostResource < JSONAPI::Resource
|
|
279
283
|
end
|
280
284
|
```
|
281
285
|
|
286
|
+
When you create a relationship, a method is created to fetch record(s) for that relationship. This method calls `records_for(association_name)` by default.
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
class PostResource < JSONAPI::Resource
|
290
|
+
has_one :author
|
291
|
+
has_many :comments
|
292
|
+
|
293
|
+
# def record_for_author(options = {})
|
294
|
+
# records_for("author", options)
|
295
|
+
# end
|
296
|
+
|
297
|
+
# def records_for_comments(options = {})
|
298
|
+
# records_for("comments", options)
|
299
|
+
# end
|
300
|
+
end
|
301
|
+
|
302
|
+
```
|
303
|
+
|
304
|
+
For example, you may want raise an error if the user is not authorized to view the associated records.
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
class BaseResource < JSONAPI::Resource
|
308
|
+
def records_for(association_name, options={})
|
309
|
+
context = options[:context]
|
310
|
+
records = model.public_send(association_name)
|
311
|
+
|
312
|
+
unless context.current_user.can_view?(records)
|
313
|
+
raise NotAuthorizedError
|
314
|
+
end
|
315
|
+
|
316
|
+
records
|
317
|
+
end
|
318
|
+
end
|
319
|
+
```
|
320
|
+
|
282
321
|
###### Applying Filters
|
283
322
|
|
284
323
|
The `apply_filter` method is called to apply each filter to the `Arel` relation. You may override this method to gain control over how the filters are applied to the `Arel` relation.
|
@@ -286,7 +325,7 @@ The `apply_filter` method is called to apply each filter to the `Arel` relation.
|
|
286
325
|
This example shows how you can implement different approaches for different filters.
|
287
326
|
|
288
327
|
```ruby
|
289
|
-
def apply_filter(records, filter, value)
|
328
|
+
def self.apply_filter(records, filter, value)
|
290
329
|
case filter
|
291
330
|
when :visibility
|
292
331
|
records.where('users.publicly_visible = ?', value == :public)
|
@@ -307,7 +346,7 @@ end
|
|
307
346
|
|
308
347
|
###### Override finder methods
|
309
348
|
|
310
|
-
Finally if you have more complex requirements for finding you can override the `find
|
349
|
+
Finally if you have more complex requirements for finding you can override the `find` and `find_by_key` methods on the resource class.
|
311
350
|
|
312
351
|
Here's an example that defers the `find` operation to a `current_user` set on the `context` option:
|
313
352
|
|
@@ -367,15 +406,15 @@ end
|
|
367
406
|
|
368
407
|
##### Paginator Configuration
|
369
408
|
|
370
|
-
The default paginator, which will be used for all resources, is set using `JSONAPI.configure`. For example
|
409
|
+
The default paginator, which will be used for all resources, is set using `JSONAPI.configure`. For example, in your `config/initializers/jsonapi_resources.rb`:
|
371
410
|
|
372
411
|
```ruby
|
373
412
|
JSONAPI.configure do |config|
|
374
413
|
# built in paginators are :none, :offset, :cursor, :paged
|
375
|
-
|
414
|
+
config.default_paginator = :offset
|
376
415
|
|
377
|
-
|
378
|
-
|
416
|
+
config.default_page_size = 10
|
417
|
+
config.maximum_page_size = 20
|
379
418
|
end
|
380
419
|
```
|
381
420
|
|
@@ -630,7 +669,7 @@ The `serialize_to_hash` method also takes some optional parameters:
|
|
630
669
|
|
631
670
|
An array of resources. Nested resources can be specified with dot notation.
|
632
671
|
|
633
|
-
*Purpose*: determines which objects will be side loaded with the source objects in
|
672
|
+
*Purpose*: determines which objects will be side loaded with the source objects in an `included` section
|
634
673
|
|
635
674
|
*Example*: ```include: ['comments','author','comments.tags','author.posts']```
|
636
675
|
|
@@ -828,9 +867,9 @@ This way all DateTime values will be formatted to display in the specified timez
|
|
828
867
|
|
829
868
|
#### Key Format
|
830
869
|
|
831
|
-
|
870
|
+
By default JR uses dasherized keys as per the [JSON API naming recommendations](http://jsonapi.org/recommendations/#naming). This can be changed by specifying a different key formatter.
|
832
871
|
|
833
|
-
For example to use camel cased keys with an initial lowercase character (JSON's default) create an initializer and add the following:
|
872
|
+
For example, to use camel cased keys with an initial lowercase character (JSON's default) create an initializer and add the following:
|
834
873
|
|
835
874
|
```
|
836
875
|
JSONAPI.configure do |config|
|
@@ -839,7 +878,7 @@ JSONAPI.configure do |config|
|
|
839
878
|
end
|
840
879
|
```
|
841
880
|
|
842
|
-
This will cause the serializer to use the `CamelizedKeyFormatter`.
|
881
|
+
This will cause the serializer to use the `CamelizedKeyFormatter`. You can also create your own `KeyFormatter`, for example:
|
843
882
|
|
844
883
|
```ruby
|
845
884
|
class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter
|
data/jsonapi-resources.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
|
-
spec.required_ruby_version = '>=
|
20
|
+
spec.required_ruby_version = '>= 2.0'
|
21
21
|
|
22
22
|
spec.add_development_dependency 'bundler', '~> 1.5'
|
23
23
|
spec.add_development_dependency 'rake'
|
data/lib/jsonapi-resources.rb
CHANGED
data/lib/jsonapi/association.rb
CHANGED
@@ -6,15 +6,7 @@ module JSONAPI
|
|
6
6
|
@name = name.to_s
|
7
7
|
@options = options
|
8
8
|
@acts_as_set = options.fetch(:acts_as_set, false) == true
|
9
|
-
@
|
10
|
-
|
11
|
-
if @key.nil?
|
12
|
-
@foreign_key = options[:foreign_key ] ? options[:foreign_key ].to_sym : nil
|
13
|
-
else
|
14
|
-
# :nocov:
|
15
|
-
warn '[DEPRECATION] `key` is deprecated in associations. Please use `foreign_key` instead.'
|
16
|
-
# :nocov:
|
17
|
-
end
|
9
|
+
@foreign_key = options[:foreign_key ] ? options[:foreign_key ].to_sym : nil
|
18
10
|
end
|
19
11
|
|
20
12
|
def primary_key
|
data/lib/jsonapi/error_codes.rb
CHANGED
data/lib/jsonapi/exceptions.rb
CHANGED
@@ -102,17 +102,21 @@ module JSONAPI
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
class
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
class InvalidFieldFormat < Error
|
106
|
+
def errors
|
107
|
+
[JSONAPI::Error.new(code: JSONAPI::INVALID_FIELD_FORMAT,
|
108
|
+
status: :bad_request,
|
109
|
+
title: 'Invalid field format',
|
110
|
+
detail: 'Fields must specify a type.')]
|
109
111
|
end
|
112
|
+
end
|
110
113
|
|
114
|
+
class InvalidLinksObject < Error
|
111
115
|
def errors
|
112
116
|
[JSONAPI::Error.new(code: JSONAPI::INVALID_LINKS_OBJECT,
|
113
117
|
status: :bad_request,
|
114
118
|
title: 'Invalid Links Object',
|
115
|
-
detail:
|
119
|
+
detail: 'Data is not a valid Links Object.')]
|
116
120
|
end
|
117
121
|
end
|
118
122
|
|
data/lib/jsonapi/formatter.rb
CHANGED
@@ -9,19 +9,10 @@ module JSONAPI
|
|
9
9
|
arg
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
formatter_class_name = "#{format.to_s.camelize}Formatter"
|
16
|
-
Object.const_get formatter_class_name if formatter_class_name
|
17
|
-
end
|
18
|
-
else
|
19
|
-
def formatter_for(format)
|
20
|
-
formatter_class_name = "#{format.to_s.camelize}Formatter"
|
21
|
-
formatter_class_name.safe_constantize if formatter_class_name
|
22
|
-
end
|
12
|
+
def formatter_for(format)
|
13
|
+
formatter_class_name = "#{format.to_s.camelize}Formatter"
|
14
|
+
formatter_class_name.safe_constantize if formatter_class_name
|
23
15
|
end
|
24
|
-
# :nocov:
|
25
16
|
end
|
26
17
|
end
|
27
18
|
|
@@ -32,7 +23,7 @@ module JSONAPI
|
|
32
23
|
end
|
33
24
|
|
34
25
|
def unformat(formatted_key)
|
35
|
-
super
|
26
|
+
super
|
36
27
|
end
|
37
28
|
end
|
38
29
|
end
|
@@ -44,7 +35,7 @@ module JSONAPI
|
|
44
35
|
end
|
45
36
|
|
46
37
|
def unformat(formatted_route)
|
47
|
-
super
|
38
|
+
super
|
48
39
|
end
|
49
40
|
end
|
50
41
|
end
|
@@ -77,7 +68,7 @@ class CamelizedKeyFormatter < JSONAPI::KeyFormatter
|
|
77
68
|
end
|
78
69
|
|
79
70
|
def unformat(formatted_key)
|
80
|
-
formatted_key.to_s.underscore
|
71
|
+
formatted_key.to_s.underscore
|
81
72
|
end
|
82
73
|
end
|
83
74
|
end
|
@@ -89,7 +80,7 @@ class DasherizedKeyFormatter < JSONAPI::KeyFormatter
|
|
89
80
|
end
|
90
81
|
|
91
82
|
def unformat(formatted_key)
|
92
|
-
formatted_key.to_s.underscore
|
83
|
+
formatted_key.to_s.underscore
|
93
84
|
end
|
94
85
|
end
|
95
86
|
end
|
@@ -121,7 +112,7 @@ class CamelizedRouteFormatter < JSONAPI::RouteFormatter
|
|
121
112
|
end
|
122
113
|
|
123
114
|
def unformat(formatted_route)
|
124
|
-
formatted_route.to_s.underscore
|
115
|
+
formatted_route.to_s.underscore
|
125
116
|
end
|
126
117
|
end
|
127
118
|
end
|
@@ -133,7 +124,7 @@ class DasherizedRouteFormatter < JSONAPI::RouteFormatter
|
|
133
124
|
end
|
134
125
|
|
135
126
|
def unformat(formatted_route)
|
136
|
-
formatted_route.to_s.underscore
|
127
|
+
formatted_route.to_s.underscore
|
137
128
|
end
|
138
129
|
end
|
139
130
|
end
|
data/lib/jsonapi/paginator.rb
CHANGED
@@ -4,25 +4,14 @@ module JSONAPI
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def apply(relation)
|
7
|
-
#
|
8
|
-
relation
|
9
|
-
# :nocov:
|
7
|
+
# relation
|
10
8
|
end
|
11
9
|
|
12
10
|
class << self
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
paginator_class_name = "#{paginator.to_s.camelize}Paginator"
|
17
|
-
Object.const_get(paginator_class_name) if paginator_class_name
|
18
|
-
end
|
19
|
-
else
|
20
|
-
def paginator_for(paginator)
|
21
|
-
paginator_class_name = "#{paginator.to_s.camelize}Paginator"
|
22
|
-
paginator_class_name.safe_constantize if paginator_class_name
|
23
|
-
end
|
11
|
+
def paginator_for(paginator)
|
12
|
+
paginator_class_name = "#{paginator.to_s.camelize}Paginator"
|
13
|
+
paginator_class_name.safe_constantize if paginator_class_name
|
24
14
|
end
|
25
|
-
# :nocov:
|
26
15
|
end
|
27
16
|
end
|
28
17
|
end
|
data/lib/jsonapi/request.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
require 'jsonapi/resource_for'
|
2
1
|
require 'jsonapi/operation'
|
3
2
|
require 'jsonapi/paginator'
|
4
3
|
|
5
4
|
module JSONAPI
|
6
5
|
class Request
|
7
|
-
include ResourceFor
|
8
|
-
|
9
6
|
attr_accessor :fields, :include, :filters, :sort_criteria, :errors, :operations,
|
10
7
|
:resource_klass, :context, :paginator, :source_klass, :source_id
|
11
8
|
|
@@ -25,7 +22,7 @@ module JSONAPI
|
|
25
22
|
end
|
26
23
|
|
27
24
|
def setup(params)
|
28
|
-
@resource_klass ||=
|
25
|
+
@resource_klass ||= Resource.resource_for(params[:controller]) if params[:controller]
|
29
26
|
|
30
27
|
unless params.nil?
|
31
28
|
case params[:action]
|
@@ -36,8 +33,8 @@ module JSONAPI
|
|
36
33
|
parse_sort_criteria(params[:sort])
|
37
34
|
parse_pagination(params[:page])
|
38
35
|
when 'get_related_resource', 'get_related_resources'
|
39
|
-
@source_klass =
|
40
|
-
@source_id = params.require(@source_klass._as_parent_key)
|
36
|
+
@source_klass = Resource.resource_for(params.require(:source))
|
37
|
+
@source_id = @source_klass.verify_key(params.require(@source_klass._as_parent_key), @context)
|
41
38
|
parse_fields(params[:fields])
|
42
39
|
parse_include(params[:include])
|
43
40
|
parse_filters(params[:filter])
|
@@ -55,7 +52,7 @@ module JSONAPI
|
|
55
52
|
params.require(:association),
|
56
53
|
params.require(@resource_klass._as_parent_key))
|
57
54
|
when 'update_association'
|
58
|
-
parse_update_association_operation(params.
|
55
|
+
parse_update_association_operation(params.fetch(:data),
|
59
56
|
params.require(:association),
|
60
57
|
params.require(@resource_klass._as_parent_key))
|
61
58
|
when 'update'
|
@@ -85,15 +82,13 @@ module JSONAPI
|
|
85
82
|
extracted_fields = {}
|
86
83
|
|
87
84
|
# Extract the fields for each type from the fields parameters
|
88
|
-
if fields.is_a?(
|
89
|
-
resource_fields = fields.split(',') unless fields.empty?
|
90
|
-
type = @resource_klass._type
|
91
|
-
extracted_fields[type] = resource_fields
|
92
|
-
elsif fields.is_a?(ActionController::Parameters)
|
85
|
+
if fields.is_a?(ActionController::Parameters)
|
93
86
|
fields.each do |field, value|
|
94
87
|
resource_fields = value.split(',') unless value.nil? || value.empty?
|
95
88
|
extracted_fields[field] = resource_fields
|
96
89
|
end
|
90
|
+
else
|
91
|
+
raise JSONAPI::Exceptions::InvalidFieldFormat.new
|
97
92
|
end
|
98
93
|
|
99
94
|
# Validate the fields
|
@@ -101,10 +96,16 @@ module JSONAPI
|
|
101
96
|
underscored_type = unformat_key(type)
|
102
97
|
extracted_fields[type] = []
|
103
98
|
begin
|
104
|
-
|
99
|
+
if type != format_key(type)
|
100
|
+
raise JSONAPI::Exceptions::InvalidResource.new(type)
|
101
|
+
end
|
102
|
+
type_resource = Resource.resource_for(@resource_klass.module_path + underscored_type.to_s)
|
105
103
|
rescue NameError
|
106
104
|
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
105
|
+
rescue JSONAPI::Exceptions::InvalidResource => e
|
106
|
+
@errors.concat(e.errors)
|
107
107
|
end
|
108
|
+
|
108
109
|
if type_resource.nil? || !(@resource_klass._type == underscored_type ||
|
109
110
|
@resource_klass._has_association?(underscored_type))
|
110
111
|
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
@@ -131,7 +132,7 @@ module JSONAPI
|
|
131
132
|
association_name = unformat_key(include_parts.first)
|
132
133
|
|
133
134
|
association = resource_klass._association(association_name)
|
134
|
-
if association
|
135
|
+
if association && format_key(association_name) == include_parts.first
|
135
136
|
unless include_parts.last.empty?
|
136
137
|
check_include(Resource.resource_for(@resource_klass.module_path + association.class_name.to_s), include_parts.last.partition('.'))
|
137
138
|
end
|
@@ -158,7 +159,7 @@ module JSONAPI
|
|
158
159
|
return unless filters
|
159
160
|
@filters = {}
|
160
161
|
filters.each do |key, value|
|
161
|
-
filter = unformat_key(key)
|
162
|
+
filter = unformat_key(key)
|
162
163
|
if @resource_klass._allowed_filter?(filter)
|
163
164
|
@filters[filter] = value
|
164
165
|
else
|
@@ -214,7 +215,7 @@ module JSONAPI
|
|
214
215
|
|
215
216
|
def verify_and_remove_type(params)
|
216
217
|
#remove type and verify it matches the resource
|
217
|
-
if params[:type] == @resource_klass._type
|
218
|
+
if unformat_key(params[:type]) == @resource_klass._type
|
218
219
|
params.delete(:type)
|
219
220
|
else
|
220
221
|
if params[:type].nil?
|
@@ -235,7 +236,7 @@ module JSONAPI
|
|
235
236
|
end
|
236
237
|
|
237
238
|
if !raw.is_a?(Hash) || raw.length != 2 || !(raw.has_key?('type') && raw.has_key?('id'))
|
238
|
-
raise JSONAPI::Exceptions::InvalidLinksObject.new
|
239
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new
|
239
240
|
end
|
240
241
|
|
241
242
|
{
|
@@ -246,23 +247,18 @@ module JSONAPI
|
|
246
247
|
|
247
248
|
def parse_has_many_links_object(raw)
|
248
249
|
if raw.nil?
|
249
|
-
raise JSONAPI::Exceptions::InvalidLinksObject.new
|
250
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new
|
250
251
|
end
|
251
252
|
|
252
253
|
links_object = {}
|
253
|
-
if raw.is_a?(
|
254
|
-
if raw.length != 2 || !(raw.has_key?('type') && raw.has_key?('ids')) || !(raw['ids'].is_a?(Array))
|
255
|
-
raise JSONAPI::Exceptions::InvalidLinksObject.new(raw)
|
256
|
-
end
|
257
|
-
links_object[raw['type']] = raw['ids']
|
258
|
-
elsif raw.is_a?(Array)
|
254
|
+
if raw.is_a?(Array)
|
259
255
|
raw.each do |link|
|
260
256
|
link_object = parse_has_one_links_object(link)
|
261
257
|
links_object[link_object[:type]] ||= []
|
262
258
|
links_object[link_object[:type]].push(link_object[:id])
|
263
259
|
end
|
264
260
|
else
|
265
|
-
raise JSONAPI::Exceptions::InvalidLinksObject.new
|
261
|
+
raise JSONAPI::Exceptions::InvalidLinksObject.new
|
266
262
|
end
|
267
263
|
links_object
|
268
264
|
end
|
@@ -291,7 +287,7 @@ module JSONAPI
|
|
291
287
|
end
|
292
288
|
|
293
289
|
unless links_object[:id].nil?
|
294
|
-
association_resource =
|
290
|
+
association_resource = Resource.resource_for(@resource_klass.module_path + links_object[:type])
|
295
291
|
checked_has_one_associations[param] = association_resource.verify_key(links_object[:id], @context)
|
296
292
|
else
|
297
293
|
checked_has_one_associations[param] = nil
|
@@ -311,14 +307,10 @@ module JSONAPI
|
|
311
307
|
end
|
312
308
|
|
313
309
|
links_object.each_pair do |type, keys|
|
314
|
-
association_resource =
|
310
|
+
association_resource = Resource.resource_for(@resource_klass.module_path + type)
|
315
311
|
checked_has_many_associations[param] = association_resource.verify_keys(keys, @context)
|
316
312
|
end
|
317
313
|
end
|
318
|
-
else
|
319
|
-
# :nocov:
|
320
|
-
raise JSONAPI::Exceptions::InvalidLinksObject.new(key)
|
321
|
-
# :nocov:
|
322
314
|
end
|
323
315
|
end
|
324
316
|
else
|
@@ -358,23 +350,14 @@ module JSONAPI
|
|
358
350
|
association = resource_klass._association(association_type)
|
359
351
|
|
360
352
|
if association.is_a?(JSONAPI::Association::HasMany)
|
361
|
-
|
362
|
-
type = data.require(:type)
|
363
|
-
|
364
|
-
object_params = {links: {association.name => {'type' => type, 'ids' => ids}}}
|
353
|
+
object_params = {links: {association.name => data}}
|
365
354
|
verified_param_set = parse_params(object_params, @resource_klass.updateable_fields(@context))
|
366
355
|
|
367
356
|
@operations.push JSONAPI::CreateHasManyAssociationOperation.new(resource_klass,
|
368
357
|
parent_key,
|
369
358
|
association_type,
|
370
359
|
verified_param_set[:has_many].values[0])
|
371
|
-
else
|
372
|
-
# :nocov:
|
373
|
-
@errors.concat(JSONAPI::Exceptions::InvalidLinksObject.new(:data).errors)
|
374
|
-
# :nocov:
|
375
360
|
end
|
376
|
-
rescue ActionController::ParameterMissing => e
|
377
|
-
@errors.concat(JSONAPI::Exceptions::ParameterMissing.new(e.param).errors)
|
378
361
|
end
|
379
362
|
|
380
363
|
def parse_update_association_operation(data, association_type, parent_key)
|
@@ -484,7 +467,8 @@ module JSONAPI
|
|
484
467
|
end
|
485
468
|
|
486
469
|
def unformat_key(key)
|
487
|
-
@key_formatter.unformat(key)
|
470
|
+
unformatted_key = @key_formatter.unformat(key)
|
471
|
+
unformatted_key.nil? ? nil : unformatted_key.to_sym
|
488
472
|
end
|
489
473
|
end
|
490
474
|
end
|