jsonapi-resources 0.2.0 → 0.3.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|