jsonapi-resources 0.4.4 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +73 -39
- data/lib/jsonapi-resources.rb +2 -1
- data/lib/jsonapi/acts_as_resource_controller.rb +5 -5
- data/lib/jsonapi/configuration.rb +13 -1
- data/lib/jsonapi/error.rb +2 -2
- data/lib/jsonapi/exceptions.rb +32 -20
- data/lib/jsonapi/link_builder.rb +141 -0
- data/lib/jsonapi/operation.rb +34 -34
- data/lib/jsonapi/operation_result.rb +3 -3
- data/lib/jsonapi/operations_processor.rb +7 -7
- data/lib/jsonapi/{association.rb → relationship.rb} +12 -4
- data/lib/jsonapi/request.rb +85 -85
- data/lib/jsonapi/resource.rb +153 -121
- data/lib/jsonapi/resource_serializer.rb +89 -92
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +1 -1
- data/lib/jsonapi/routing_ext.rb +42 -42
- data/test/controllers/controller_test.rb +90 -100
- data/test/fixtures/active_record.rb +38 -5
- data/test/fixtures/author_details.yml +9 -0
- data/test/fixtures/vehicles.yml +10 -2
- data/test/integration/requests/request_test.rb +17 -17
- data/test/integration/routes/routes_test.rb +20 -20
- data/test/test_helper.rb +18 -1
- data/test/unit/jsonapi_request/jsonapi_request_test.rb +1 -1
- data/test/unit/operation/operations_processor_test.rb +21 -21
- data/test/unit/resource/resource_test.rb +13 -13
- data/test/unit/serializer/link_builder_test.rb +183 -0
- data/test/unit/serializer/polymorphic_serializer_test.rb +73 -74
- data/test/unit/serializer/serializer_test.rb +342 -91
- metadata +8 -3
@@ -0,0 +1,141 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
class LinkBuilder
|
3
|
+
attr_reader :base_url,
|
4
|
+
:primary_resource_klass,
|
5
|
+
:route_formatter
|
6
|
+
|
7
|
+
def initialize(config = {})
|
8
|
+
@base_url = config[:base_url]
|
9
|
+
@primary_resource_klass = config[:primary_resource_klass]
|
10
|
+
@route_formatter = config[:route_formatter]
|
11
|
+
@is_engine = !!engine_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def engine?
|
15
|
+
@is_engine
|
16
|
+
end
|
17
|
+
|
18
|
+
def engine_name
|
19
|
+
@engine_name ||= build_engine_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def primary_resources_url
|
23
|
+
if engine?
|
24
|
+
engine_primary_resources_url
|
25
|
+
else
|
26
|
+
regular_primary_resources_url
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def query_link(query_params)
|
31
|
+
"#{ primary_resources_url }?#{ query_params.to_query }"
|
32
|
+
end
|
33
|
+
|
34
|
+
def relationships_related_link(source, relationship)
|
35
|
+
"#{ self_link(source) }/#{ route_for_relationship(relationship) }"
|
36
|
+
end
|
37
|
+
|
38
|
+
def relationships_self_link(source, relationship)
|
39
|
+
"#{ self_link(source) }/relationships/#{ route_for_relationship(relationship) }"
|
40
|
+
end
|
41
|
+
|
42
|
+
def self_link(source)
|
43
|
+
if engine?
|
44
|
+
engine_resource_url(source)
|
45
|
+
else
|
46
|
+
regular_resource_url(source)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def build_engine_name
|
53
|
+
scopes = module_scopes_from_class(primary_resource_klass)
|
54
|
+
|
55
|
+
unless scopes.empty?
|
56
|
+
"#{ scopes.first.to_s.camelize }::Engine".safe_constantize
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def engine_path_from_resource_class(klass)
|
61
|
+
path_name = engine_resources_path_name_from_class(klass)
|
62
|
+
engine_name.routes.url_helpers.public_send(path_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def engine_primary_resources_path
|
66
|
+
engine_path_from_resource_class(primary_resource_klass)
|
67
|
+
end
|
68
|
+
|
69
|
+
def engine_primary_resources_url
|
70
|
+
"#{ base_url }#{ engine_primary_resources_path }"
|
71
|
+
end
|
72
|
+
|
73
|
+
def engine_resource_path(source)
|
74
|
+
resource_path_name = engine_resource_path_name_from_source(source)
|
75
|
+
engine_name.routes.url_helpers.public_send(resource_path_name, source.id)
|
76
|
+
end
|
77
|
+
|
78
|
+
def engine_resource_path_name_from_source(source)
|
79
|
+
scopes = module_scopes_from_class(source.class)[1..-1]
|
80
|
+
base_path_name = scopes.map { |scope| scope.downcase }.join("_")
|
81
|
+
end_path_name = source.class._type.to_s.singularize
|
82
|
+
"#{ base_path_name }_#{ end_path_name }_path"
|
83
|
+
end
|
84
|
+
|
85
|
+
def engine_resource_url(source)
|
86
|
+
"#{ base_url }#{ engine_resource_path(source) }"
|
87
|
+
end
|
88
|
+
|
89
|
+
def engine_resources_path_name_from_class(klass)
|
90
|
+
scopes = module_scopes_from_class(klass)[1..-1]
|
91
|
+
base_path_name = scopes.map { |scope| scope.downcase }.join("_")
|
92
|
+
end_path_name = klass._type.to_s
|
93
|
+
"#{ base_path_name }_#{ end_path_name }_path"
|
94
|
+
end
|
95
|
+
|
96
|
+
def format_route(route)
|
97
|
+
route_formatter.format(route.to_s)
|
98
|
+
end
|
99
|
+
|
100
|
+
def formatted_module_path_from_class(klass)
|
101
|
+
scopes = module_scopes_from_class(klass)
|
102
|
+
|
103
|
+
unless scopes.empty?
|
104
|
+
"/#{ scopes.map(&:downcase).join('/') }/"
|
105
|
+
else
|
106
|
+
"/"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def module_scopes_from_class(klass)
|
111
|
+
klass.name.to_s.split("::")[0...-1]
|
112
|
+
end
|
113
|
+
|
114
|
+
def regular_primary_resources_path
|
115
|
+
[
|
116
|
+
formatted_module_path_from_class(primary_resource_klass),
|
117
|
+
route_formatter.format(primary_resource_klass._type.to_s),
|
118
|
+
].join
|
119
|
+
end
|
120
|
+
|
121
|
+
def regular_primary_resources_url
|
122
|
+
"#{ base_url }#{ regular_primary_resources_path }"
|
123
|
+
end
|
124
|
+
|
125
|
+
def regular_resource_path(source)
|
126
|
+
[
|
127
|
+
formatted_module_path_from_class(source.class),
|
128
|
+
route_formatter.format(source.class._type.to_s),
|
129
|
+
"/#{ source.id }",
|
130
|
+
].join
|
131
|
+
end
|
132
|
+
|
133
|
+
def regular_resource_url(source)
|
134
|
+
"#{ base_url }#{ regular_resource_path(source) }"
|
135
|
+
end
|
136
|
+
|
137
|
+
def route_for_relationship(relationship)
|
138
|
+
format_route(relationship.name)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/jsonapi/operation.rb
CHANGED
@@ -90,12 +90,12 @@ module JSONAPI
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
-
class
|
94
|
-
attr_reader :parent_key, :
|
93
|
+
class ShowRelationshipOperation < Operation
|
94
|
+
attr_reader :parent_key, :relationship_type
|
95
95
|
|
96
96
|
def initialize(resource_klass, options = {})
|
97
97
|
@parent_key = options.fetch(:parent_key)
|
98
|
-
@
|
98
|
+
@relationship_type = options.fetch(:relationship_type)
|
99
99
|
@transactional = false
|
100
100
|
super(resource_klass, options)
|
101
101
|
end
|
@@ -105,7 +105,7 @@ module JSONAPI
|
|
105
105
|
|
106
106
|
return JSONAPI::LinksObjectOperationResult.new(:ok,
|
107
107
|
parent_resource,
|
108
|
-
resource_klass.
|
108
|
+
resource_klass._relationship(@relationship_type))
|
109
109
|
|
110
110
|
rescue JSONAPI::Exceptions::Error => e
|
111
111
|
return JSONAPI::ErrorsOperationResult.new(e.errors[0].code, e.errors)
|
@@ -113,12 +113,12 @@ module JSONAPI
|
|
113
113
|
end
|
114
114
|
|
115
115
|
class ShowRelatedResourceOperation < Operation
|
116
|
-
attr_reader :source_klass, :source_id, :
|
116
|
+
attr_reader :source_klass, :source_id, :relationship_type
|
117
117
|
|
118
118
|
def initialize(resource_klass, options = {})
|
119
119
|
@source_klass = options.fetch(:source_klass)
|
120
120
|
@source_id = options.fetch(:source_id)
|
121
|
-
@
|
121
|
+
@relationship_type = options.fetch(:relationship_type)
|
122
122
|
@transactional = false
|
123
123
|
super(resource_klass, options)
|
124
124
|
end
|
@@ -126,7 +126,7 @@ module JSONAPI
|
|
126
126
|
def apply
|
127
127
|
source_resource = @source_klass.find_by_key(@source_id, context: @context)
|
128
128
|
|
129
|
-
related_resource = source_resource.
|
129
|
+
related_resource = source_resource.public_send(@relationship_type)
|
130
130
|
|
131
131
|
return JSONAPI::ResourceOperationResult.new(:ok, related_resource)
|
132
132
|
|
@@ -136,12 +136,12 @@ module JSONAPI
|
|
136
136
|
end
|
137
137
|
|
138
138
|
class ShowRelatedResourcesOperation < Operation
|
139
|
-
attr_reader :source_klass, :source_id, :
|
139
|
+
attr_reader :source_klass, :source_id, :relationship_type, :filters, :sort_criteria, :paginator
|
140
140
|
|
141
141
|
def initialize(resource_klass, options = {})
|
142
142
|
@source_klass = options.fetch(:source_klass)
|
143
143
|
@source_id = options.fetch(:source_id)
|
144
|
-
@
|
144
|
+
@relationship_type = options.fetch(:relationship_type)
|
145
145
|
@filters = options[:filters]
|
146
146
|
@sort_criteria = options[:sort_criteria]
|
147
147
|
@paginator = options[:paginator]
|
@@ -152,7 +152,7 @@ module JSONAPI
|
|
152
152
|
def apply
|
153
153
|
source_resource = @source_klass.find_by_key(@source_id, context: @context)
|
154
154
|
|
155
|
-
related_resource = source_resource.
|
155
|
+
related_resource = source_resource.public_send(@relationship_type,
|
156
156
|
filters: @filters,
|
157
157
|
sort_criteria: @sort_criteria,
|
158
158
|
paginator: @paginator)
|
@@ -218,109 +218,109 @@ module JSONAPI
|
|
218
218
|
end
|
219
219
|
end
|
220
220
|
|
221
|
-
class
|
222
|
-
attr_reader :resource_id, :
|
221
|
+
class ReplaceToOneRelationshipOperation < Operation
|
222
|
+
attr_reader :resource_id, :relationship_type, :key_value
|
223
223
|
|
224
224
|
def initialize(resource_klass, options = {})
|
225
225
|
@resource_id = options.fetch(:resource_id)
|
226
226
|
@key_value = options.fetch(:key_value)
|
227
|
-
@
|
227
|
+
@relationship_type = options.fetch(:relationship_type).to_sym
|
228
228
|
super(resource_klass, options)
|
229
229
|
end
|
230
230
|
|
231
231
|
def apply
|
232
232
|
resource = @resource_klass.find_by_key(@resource_id, context: @context)
|
233
|
-
result = resource.
|
233
|
+
result = resource.replace_to_one_link(@relationship_type, @key_value)
|
234
234
|
|
235
235
|
return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
|
236
236
|
end
|
237
237
|
end
|
238
238
|
|
239
|
-
class
|
240
|
-
attr_reader :resource_id, :
|
239
|
+
class ReplacePolymorphicToOneRelationshipOperation < Operation
|
240
|
+
attr_reader :resource_id, :relationship_type, :key_value, :key_type
|
241
241
|
|
242
242
|
def initialize(resource_klass, options = {})
|
243
243
|
@resource_id = options.fetch(:resource_id)
|
244
244
|
@key_value = options.fetch(:key_value)
|
245
245
|
@key_type = options.fetch(:key_type)
|
246
|
-
@
|
246
|
+
@relationship_type = options.fetch(:relationship_type).to_sym
|
247
247
|
super(resource_klass, options)
|
248
248
|
end
|
249
249
|
|
250
250
|
def apply
|
251
251
|
resource = @resource_klass.find_by_key(@resource_id, context: @context)
|
252
|
-
result = resource.
|
252
|
+
result = resource.replace_polymorphic_to_one_link(@relationship_type, @key_value, @key_type)
|
253
253
|
|
254
254
|
return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
|
255
255
|
end
|
256
256
|
end
|
257
257
|
|
258
|
-
class
|
259
|
-
attr_reader :resource_id, :
|
258
|
+
class CreateToManyRelationshipOperation < Operation
|
259
|
+
attr_reader :resource_id, :relationship_type, :data
|
260
260
|
|
261
261
|
def initialize(resource_klass, options)
|
262
262
|
@resource_id = options.fetch(:resource_id)
|
263
263
|
@data = options.fetch(:data)
|
264
|
-
@
|
264
|
+
@relationship_type = options.fetch(:relationship_type).to_sym
|
265
265
|
super(resource_klass, options)
|
266
266
|
end
|
267
267
|
|
268
268
|
def apply
|
269
269
|
resource = @resource_klass.find_by_key(@resource_id, context: @context)
|
270
|
-
result = resource.
|
270
|
+
result = resource.create_to_many_links(@relationship_type, @data)
|
271
271
|
|
272
272
|
return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
|
273
273
|
end
|
274
274
|
end
|
275
275
|
|
276
|
-
class
|
277
|
-
attr_reader :resource_id, :
|
276
|
+
class ReplaceToManyRelationshipOperation < Operation
|
277
|
+
attr_reader :resource_id, :relationship_type, :data
|
278
278
|
|
279
279
|
def initialize(resource_klass, options)
|
280
280
|
@resource_id = options.fetch(:resource_id)
|
281
281
|
@data = options.fetch(:data)
|
282
|
-
@
|
282
|
+
@relationship_type = options.fetch(:relationship_type).to_sym
|
283
283
|
super(resource_klass, options)
|
284
284
|
end
|
285
285
|
|
286
286
|
def apply
|
287
287
|
resource = @resource_klass.find_by_key(@resource_id, context: @context)
|
288
|
-
result = resource.
|
288
|
+
result = resource.replace_to_many_links(@relationship_type, @data)
|
289
289
|
|
290
290
|
return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
|
291
291
|
end
|
292
292
|
end
|
293
293
|
|
294
|
-
class
|
295
|
-
attr_reader :resource_id, :
|
294
|
+
class RemoveToManyRelationshipOperation < Operation
|
295
|
+
attr_reader :resource_id, :relationship_type, :associated_key
|
296
296
|
|
297
297
|
def initialize(resource_klass, options)
|
298
298
|
@resource_id = options.fetch(:resource_id)
|
299
299
|
@associated_key = options.fetch(:associated_key)
|
300
|
-
@
|
300
|
+
@relationship_type = options.fetch(:relationship_type).to_sym
|
301
301
|
super(resource_klass, options)
|
302
302
|
end
|
303
303
|
|
304
304
|
def apply
|
305
305
|
resource = @resource_klass.find_by_key(@resource_id, context: @context)
|
306
|
-
result = resource.
|
306
|
+
result = resource.remove_to_many_link(@relationship_type, @associated_key)
|
307
307
|
|
308
308
|
return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
|
309
309
|
end
|
310
310
|
end
|
311
311
|
|
312
|
-
class
|
313
|
-
attr_reader :resource_id, :
|
312
|
+
class RemoveToOneRelationshipOperation < Operation
|
313
|
+
attr_reader :resource_id, :relationship_type
|
314
314
|
|
315
315
|
def initialize(resource_klass, options)
|
316
316
|
@resource_id = options.fetch(:resource_id)
|
317
|
-
@
|
317
|
+
@relationship_type = options.fetch(:relationship_type).to_sym
|
318
318
|
super(resource_klass, options)
|
319
319
|
end
|
320
320
|
|
321
321
|
def apply
|
322
322
|
resource = @resource_klass.find_by_key(@resource_id, context: @context)
|
323
|
-
result = resource.
|
323
|
+
result = resource.remove_to_one_link(@relationship_type)
|
324
324
|
|
325
325
|
return JSONAPI::OperationResult.new(result == :completed ? :no_content : :accepted)
|
326
326
|
end
|
@@ -43,11 +43,11 @@ module JSONAPI
|
|
43
43
|
end
|
44
44
|
|
45
45
|
class LinksObjectOperationResult < OperationResult
|
46
|
-
attr_accessor :parent_resource, :
|
46
|
+
attr_accessor :parent_resource, :relationship
|
47
47
|
|
48
|
-
def initialize(code, parent_resource,
|
48
|
+
def initialize(code, parent_resource, relationship, options = {})
|
49
49
|
@parent_resource = parent_resource
|
50
|
-
@
|
50
|
+
@relationship = relationship
|
51
51
|
super(code, options)
|
52
52
|
end
|
53
53
|
end
|
@@ -5,18 +5,18 @@ module JSONAPI
|
|
5
5
|
:operations,
|
6
6
|
:find_operation,
|
7
7
|
:show_operation,
|
8
|
-
:
|
8
|
+
:show_relationship_operation,
|
9
9
|
:show_related_resource_operation,
|
10
10
|
:show_related_resources_operation,
|
11
11
|
:create_resource_operation,
|
12
12
|
:remove_resource_operation,
|
13
13
|
:replace_fields_operation,
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
14
|
+
:replace_to_one_relationship_operation,
|
15
|
+
:replace_polymorphic_to_one_relationship_operation,
|
16
|
+
:create_to_many_relationship_operation,
|
17
|
+
:replace_to_many_relationship_operation,
|
18
|
+
:remove_to_many_relationship_operation,
|
19
|
+
:remove_to_one_relationship_operation
|
20
20
|
|
21
21
|
class << self
|
22
22
|
def operations_processor_for(operations_processor)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module JSONAPI
|
2
|
-
class
|
2
|
+
class Relationship
|
3
3
|
attr_reader :acts_as_set, :foreign_key, :type, :options, :name,
|
4
|
-
:class_name, :polymorphic
|
4
|
+
:class_name, :polymorphic, :always_include_linkage_data
|
5
5
|
|
6
6
|
def initialize(name, options = {})
|
7
7
|
@name = name.to_s
|
@@ -11,6 +11,7 @@ module JSONAPI
|
|
11
11
|
@module_path = options[:module_path] || ''
|
12
12
|
@relation_name = options.fetch(:relation_name, @name)
|
13
13
|
@polymorphic = options.fetch(:polymorphic, false) == true
|
14
|
+
@always_include_linkage_data = options.fetch(:always_include_linkage_data, false) == true
|
14
15
|
end
|
15
16
|
|
16
17
|
alias_method :polymorphic?, :polymorphic
|
@@ -45,12 +46,19 @@ module JSONAPI
|
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
|
-
class
|
49
|
+
class ToOne < Relationship
|
50
|
+
attr_reader :foreign_key_on
|
51
|
+
|
49
52
|
def initialize(name, options = {})
|
50
53
|
super
|
51
54
|
@class_name = options.fetch(:class_name, name.to_s.camelize)
|
52
55
|
@type = class_name.underscore.pluralize.to_sym
|
53
56
|
@foreign_key ||= "#{name}_id".to_sym
|
57
|
+
@foreign_key_on = options.fetch(:foreign_key_on, :self)
|
58
|
+
end
|
59
|
+
|
60
|
+
def belongs_to?
|
61
|
+
foreign_key_on == :self
|
54
62
|
end
|
55
63
|
|
56
64
|
def polymorphic_type
|
@@ -58,7 +66,7 @@ module JSONAPI
|
|
58
66
|
end
|
59
67
|
end
|
60
68
|
|
61
|
-
class
|
69
|
+
class ToMany < Relationship
|
62
70
|
def initialize(name, options = {})
|
63
71
|
super
|
64
72
|
@class_name = options.fetch(:class_name, name.to_s.camelize.singularize)
|
data/lib/jsonapi/request.rb
CHANGED
@@ -58,7 +58,7 @@ module JSONAPI
|
|
58
58
|
parse_filters(params[:filter])
|
59
59
|
parse_sort_criteria(params[:sort])
|
60
60
|
parse_pagination(params[:page])
|
61
|
-
add_show_related_resource_operation(params[:
|
61
|
+
add_show_related_resource_operation(params[:relationship])
|
62
62
|
end
|
63
63
|
|
64
64
|
def setup_get_related_resources_action(params)
|
@@ -69,7 +69,7 @@ module JSONAPI
|
|
69
69
|
parse_filters(params[:filter])
|
70
70
|
parse_sort_criteria(params[:sort])
|
71
71
|
parse_pagination(params[:page])
|
72
|
-
add_show_related_resources_operation(params[:
|
72
|
+
add_show_related_resources_operation(params[:relationship])
|
73
73
|
end
|
74
74
|
|
75
75
|
def setup_show_action(params)
|
@@ -79,8 +79,8 @@ module JSONAPI
|
|
79
79
|
add_show_operation
|
80
80
|
end
|
81
81
|
|
82
|
-
def
|
83
|
-
|
82
|
+
def setup_show_relationship_action(params)
|
83
|
+
add_show_relationship_operation(params[:relationship], params.require(@resource_klass._as_parent_key))
|
84
84
|
end
|
85
85
|
|
86
86
|
def setup_create_action(params)
|
@@ -89,15 +89,15 @@ module JSONAPI
|
|
89
89
|
parse_add_operation(params.require(:data))
|
90
90
|
end
|
91
91
|
|
92
|
-
def
|
93
|
-
|
94
|
-
params.require(:
|
92
|
+
def setup_create_relationship_action(params)
|
93
|
+
parse_add_relationship_operation(params.require(:data),
|
94
|
+
params.require(:relationship),
|
95
95
|
params.require(@resource_klass._as_parent_key))
|
96
96
|
end
|
97
97
|
|
98
|
-
def
|
99
|
-
|
100
|
-
params.require(:
|
98
|
+
def setup_update_relationship_action(params)
|
99
|
+
parse_update_relationship_operation(params.fetch(:data),
|
100
|
+
params.require(:relationship),
|
101
101
|
params.require(@resource_klass._as_parent_key))
|
102
102
|
end
|
103
103
|
|
@@ -111,8 +111,8 @@ module JSONAPI
|
|
111
111
|
parse_remove_operation(params)
|
112
112
|
end
|
113
113
|
|
114
|
-
def
|
115
|
-
|
114
|
+
def setup_destroy_relationship_action(params)
|
115
|
+
parse_remove_relationship_operation(params)
|
116
116
|
end
|
117
117
|
|
118
118
|
def initialize_source(params)
|
@@ -158,7 +158,7 @@ module JSONAPI
|
|
158
158
|
end
|
159
159
|
|
160
160
|
if type_resource.nil? || !(@resource_klass._type == underscored_type ||
|
161
|
-
@resource_klass.
|
161
|
+
@resource_klass._has_relationship?(underscored_type))
|
162
162
|
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
163
163
|
else
|
164
164
|
unless values.nil?
|
@@ -180,12 +180,12 @@ module JSONAPI
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def check_include(resource_klass, include_parts)
|
183
|
-
|
183
|
+
relationship_name = unformat_key(include_parts.first)
|
184
184
|
|
185
|
-
|
186
|
-
if
|
185
|
+
relationship = resource_klass._relationship(relationship_name)
|
186
|
+
if relationship && format_key(relationship_name) == include_parts.first
|
187
187
|
unless include_parts.last.empty?
|
188
|
-
check_include(Resource.resource_for(@resource_klass.module_path +
|
188
|
+
check_include(Resource.resource_for(@resource_klass.module_path + relationship.class_name.to_s.underscore), include_parts.last.partition('.'))
|
189
189
|
end
|
190
190
|
else
|
191
191
|
@errors.concat(JSONAPI::Exceptions::InvalidInclude.new(format_key(resource_klass._type),
|
@@ -280,30 +280,30 @@ module JSONAPI
|
|
280
280
|
)
|
281
281
|
end
|
282
282
|
|
283
|
-
def
|
284
|
-
@operations.push JSONAPI::
|
283
|
+
def add_show_relationship_operation(relationship_type, parent_key)
|
284
|
+
@operations.push JSONAPI::ShowRelationshipOperation.new(
|
285
285
|
@resource_klass,
|
286
286
|
context: @context,
|
287
|
-
|
287
|
+
relationship_type: relationship_type,
|
288
288
|
parent_key: @resource_klass.verify_key(parent_key)
|
289
289
|
)
|
290
290
|
end
|
291
291
|
|
292
|
-
def add_show_related_resource_operation(
|
292
|
+
def add_show_related_resource_operation(relationship_type)
|
293
293
|
@operations.push JSONAPI::ShowRelatedResourceOperation.new(
|
294
294
|
@resource_klass,
|
295
295
|
context: @context,
|
296
|
-
|
296
|
+
relationship_type: relationship_type,
|
297
297
|
source_klass: @source_klass,
|
298
298
|
source_id: @source_id
|
299
299
|
)
|
300
300
|
end
|
301
301
|
|
302
|
-
def add_show_related_resources_operation(
|
302
|
+
def add_show_related_resources_operation(relationship_type)
|
303
303
|
@operations.push JSONAPI::ShowRelatedResourcesOperation.new(
|
304
304
|
@resource_klass,
|
305
305
|
context: @context,
|
306
|
-
|
306
|
+
relationship_type: relationship_type,
|
307
307
|
source_klass: @source_klass,
|
308
308
|
source_id: @source_id,
|
309
309
|
filters: @source_klass.verify_filters(@filters, @context),
|
@@ -346,7 +346,7 @@ module JSONAPI
|
|
346
346
|
end
|
347
347
|
end
|
348
348
|
|
349
|
-
def
|
349
|
+
def parse_to_one_links_object(raw)
|
350
350
|
if raw.nil?
|
351
351
|
return {
|
352
352
|
type: nil,
|
@@ -364,13 +364,13 @@ module JSONAPI
|
|
364
364
|
}
|
365
365
|
end
|
366
366
|
|
367
|
-
def
|
367
|
+
def parse_to_many_links_object(raw)
|
368
368
|
fail JSONAPI::Exceptions::InvalidLinksObject.new if raw.nil?
|
369
369
|
|
370
370
|
links_object = {}
|
371
371
|
if raw.is_a?(Array)
|
372
372
|
raw.each do |link|
|
373
|
-
link_object =
|
373
|
+
link_object = parse_to_one_links_object(link)
|
374
374
|
links_object[link_object[:type]] ||= []
|
375
375
|
links_object[link_object[:type]].push(link_object[:id])
|
376
376
|
end
|
@@ -384,39 +384,39 @@ module JSONAPI
|
|
384
384
|
verify_permitted_params(params, allowed_fields)
|
385
385
|
|
386
386
|
checked_attributes = {}
|
387
|
-
|
388
|
-
|
387
|
+
checked_to_one_relationships = {}
|
388
|
+
checked_to_many_relationships = {}
|
389
389
|
|
390
390
|
params.each do |key, value|
|
391
391
|
case key.to_s
|
392
392
|
when 'relationships'
|
393
393
|
value.each do |link_key, link_value|
|
394
394
|
param = unformat_key(link_key)
|
395
|
-
|
396
|
-
if
|
395
|
+
relationship = @resource_klass._relationship(param)
|
396
|
+
if relationship.is_a?(JSONAPI::Relationship::ToOne)
|
397
397
|
if link_value.nil?
|
398
398
|
linkage = nil
|
399
399
|
else
|
400
400
|
linkage = link_value[:data]
|
401
401
|
end
|
402
402
|
|
403
|
-
links_object =
|
404
|
-
if !
|
403
|
+
links_object = parse_to_one_links_object(linkage)
|
404
|
+
if !relationship.polymorphic? && links_object[:type] && (links_object[:type].to_s != relationship.type.to_s)
|
405
405
|
fail JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
|
406
406
|
end
|
407
407
|
|
408
408
|
unless links_object[:id].nil?
|
409
|
-
|
410
|
-
|
411
|
-
if
|
412
|
-
|
409
|
+
relationship_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(links_object[:type]).to_s)
|
410
|
+
relationship_id = relationship_resource.verify_key(links_object[:id], @context)
|
411
|
+
if relationship.polymorphic?
|
412
|
+
checked_to_one_relationships[param] = { id: relationship_id, type: unformat_key(links_object[:type].to_s) }
|
413
413
|
else
|
414
|
-
|
414
|
+
checked_to_one_relationships[param] = relationship_id
|
415
415
|
end
|
416
416
|
else
|
417
|
-
|
417
|
+
checked_to_one_relationships[param] = nil
|
418
418
|
end
|
419
|
-
elsif
|
419
|
+
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
|
420
420
|
if link_value.is_a?(Array) && link_value.length == 0
|
421
421
|
linkage = []
|
422
422
|
elsif link_value.is_a?(Hash)
|
@@ -425,22 +425,22 @@ module JSONAPI
|
|
425
425
|
fail JSONAPI::Exceptions::InvalidLinksObject.new
|
426
426
|
end
|
427
427
|
|
428
|
-
links_object =
|
428
|
+
links_object = parse_to_many_links_object(linkage)
|
429
429
|
|
430
|
-
# Since we do not yet support polymorphic
|
431
|
-
#
|
432
|
-
# ToDo: Support Polymorphic
|
430
|
+
# Since we do not yet support polymorphic relationships we will raise an error if the type does not match the
|
431
|
+
# relationship's type.
|
432
|
+
# ToDo: Support Polymorphic relationships
|
433
433
|
|
434
434
|
if links_object.length == 0
|
435
|
-
|
435
|
+
checked_to_many_relationships[param] = []
|
436
436
|
else
|
437
|
-
if links_object.length > 1 || !links_object.has_key?(unformat_key(
|
437
|
+
if links_object.length > 1 || !links_object.has_key?(unformat_key(relationship.type).to_s)
|
438
438
|
fail JSONAPI::Exceptions::TypeMismatch.new(links_object[:type])
|
439
439
|
end
|
440
440
|
|
441
441
|
links_object.each_pair do |type, keys|
|
442
|
-
|
443
|
-
|
442
|
+
relationship_resource = Resource.resource_for(@resource_klass.module_path + unformat_key(type).to_s)
|
443
|
+
checked_to_many_relationships[param] = relationship_resource.verify_keys(keys, @context)
|
444
444
|
end
|
445
445
|
end
|
446
446
|
end
|
@@ -457,8 +457,8 @@ module JSONAPI
|
|
457
457
|
|
458
458
|
return {
|
459
459
|
'attributes' => checked_attributes,
|
460
|
-
'
|
461
|
-
'
|
460
|
+
'to_one' => checked_to_one_relationships,
|
461
|
+
'to_many' => checked_to_many_relationships
|
462
462
|
}.deep_transform_keys { |key| unformat_key(key) }
|
463
463
|
end
|
464
464
|
|
@@ -501,64 +501,64 @@ module JSONAPI
|
|
501
501
|
end
|
502
502
|
# :nocov:
|
503
503
|
|
504
|
-
def
|
505
|
-
|
504
|
+
def parse_add_relationship_operation(data, relationship_type, parent_key)
|
505
|
+
relationship = resource_klass._relationship(relationship_type)
|
506
506
|
|
507
|
-
if
|
508
|
-
object_params = { relationships: { format_key(
|
507
|
+
if relationship.is_a?(JSONAPI::Relationship::ToMany)
|
508
|
+
object_params = { relationships: { format_key(relationship.name) => { data: data } } }
|
509
509
|
verified_param_set = parse_params(object_params, updatable_fields)
|
510
510
|
|
511
|
-
@operations.push JSONAPI::
|
511
|
+
@operations.push JSONAPI::CreateToManyRelationshipOperation.new(
|
512
512
|
resource_klass,
|
513
513
|
context: @context,
|
514
514
|
resource_id: parent_key,
|
515
|
-
|
516
|
-
data: verified_param_set[:
|
515
|
+
relationship_type: relationship_type,
|
516
|
+
data: verified_param_set[:to_many].values[0]
|
517
517
|
)
|
518
518
|
end
|
519
519
|
end
|
520
520
|
|
521
|
-
def
|
522
|
-
|
523
|
-
if
|
524
|
-
if
|
525
|
-
object_params = { relationships: { format_key(
|
521
|
+
def parse_update_relationship_operation(data, relationship_type, parent_key)
|
522
|
+
relationship = resource_klass._relationship(relationship_type)
|
523
|
+
if relationship.is_a?(JSONAPI::Relationship::ToOne)
|
524
|
+
if relationship.polymorphic?
|
525
|
+
object_params = { relationships: { format_key(relationship.name) => { data: data } } }
|
526
526
|
verified_param_set = parse_params(object_params, updatable_fields)
|
527
527
|
|
528
|
-
@operations.push JSONAPI::
|
528
|
+
@operations.push JSONAPI::ReplacePolymorphicToOneRelationshipOperation.new(
|
529
529
|
resource_klass,
|
530
530
|
context: @context,
|
531
531
|
resource_id: parent_key,
|
532
|
-
|
533
|
-
key_value: verified_param_set[:
|
534
|
-
key_type: verified_param_set[:
|
532
|
+
relationship_type: relationship_type,
|
533
|
+
key_value: verified_param_set[:to_one].values[0][:id],
|
534
|
+
key_type: verified_param_set[:to_one].values[0][:type]
|
535
535
|
)
|
536
536
|
else
|
537
|
-
object_params = { relationships: { format_key(
|
537
|
+
object_params = { relationships: { format_key(relationship.name) => { data: data } } }
|
538
538
|
verified_param_set = parse_params(object_params, updatable_fields)
|
539
539
|
|
540
|
-
@operations.push JSONAPI::
|
540
|
+
@operations.push JSONAPI::ReplaceToOneRelationshipOperation.new(
|
541
541
|
resource_klass,
|
542
542
|
context: @context,
|
543
543
|
resource_id: parent_key,
|
544
|
-
|
545
|
-
key_value: verified_param_set[:
|
544
|
+
relationship_type: relationship_type,
|
545
|
+
key_value: verified_param_set[:to_one].values[0]
|
546
546
|
)
|
547
547
|
end
|
548
|
-
elsif
|
549
|
-
unless
|
550
|
-
fail JSONAPI::Exceptions::
|
548
|
+
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
|
549
|
+
unless relationship.acts_as_set
|
550
|
+
fail JSONAPI::Exceptions::ToManySetReplacementForbidden.new
|
551
551
|
end
|
552
552
|
|
553
|
-
object_params = { relationships: { format_key(
|
553
|
+
object_params = { relationships: { format_key(relationship.name) => { data: data } } }
|
554
554
|
verified_param_set = parse_params(object_params, updatable_fields)
|
555
555
|
|
556
|
-
@operations.push JSONAPI::
|
556
|
+
@operations.push JSONAPI::ReplaceToManyRelationshipOperation.new(
|
557
557
|
resource_klass,
|
558
558
|
context: @context,
|
559
559
|
resource_id: parent_key,
|
560
|
-
|
561
|
-
data: verified_param_set[:
|
560
|
+
relationship_type: relationship_type,
|
561
|
+
data: verified_param_set[:to_many].values[0]
|
562
562
|
)
|
563
563
|
end
|
564
564
|
end
|
@@ -619,29 +619,29 @@ module JSONAPI
|
|
619
619
|
@errors.concat(e.errors)
|
620
620
|
end
|
621
621
|
|
622
|
-
def
|
623
|
-
|
622
|
+
def parse_remove_relationship_operation(params)
|
623
|
+
relationship_type = params[:relationship]
|
624
624
|
|
625
625
|
parent_key = params[resource_klass._as_parent_key]
|
626
626
|
|
627
|
-
|
628
|
-
if
|
627
|
+
relationship = resource_klass._relationship(relationship_type)
|
628
|
+
if relationship.is_a?(JSONAPI::Relationship::ToMany)
|
629
629
|
keys = parse_key_array(params[:keys])
|
630
630
|
keys.each do |key|
|
631
|
-
@operations.push JSONAPI::
|
631
|
+
@operations.push JSONAPI::RemoveToManyRelationshipOperation.new(
|
632
632
|
resource_klass,
|
633
633
|
context: @context,
|
634
634
|
resource_id: parent_key,
|
635
|
-
|
635
|
+
relationship_type: relationship_type,
|
636
636
|
associated_key: key
|
637
637
|
)
|
638
638
|
end
|
639
639
|
else
|
640
|
-
@operations.push JSONAPI::
|
640
|
+
@operations.push JSONAPI::RemoveToOneRelationshipOperation.new(
|
641
641
|
resource_klass,
|
642
642
|
context: @context,
|
643
643
|
resource_id: parent_key,
|
644
|
-
|
644
|
+
relationship_type: relationship_type
|
645
645
|
)
|
646
646
|
end
|
647
647
|
end
|