jsonapi-resources 0.9.0 → 0.9.12
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 +5 -5
- data/lib/jsonapi/acts_as_resource_controller.rb +3 -2
- data/lib/jsonapi/configuration.rb +15 -1
- data/lib/jsonapi/exceptions.rb +22 -15
- data/lib/jsonapi/include_directives.rb +11 -1
- data/lib/jsonapi/link_builder.rb +81 -80
- data/lib/jsonapi/operation_result.rb +1 -1
- data/lib/jsonapi/processor.rb +8 -7
- data/lib/jsonapi/relationship.rb +47 -2
- data/lib/jsonapi/relationship_builder.rb +4 -4
- data/lib/jsonapi/request_parser.rb +130 -54
- data/lib/jsonapi/resource.rb +134 -11
- data/lib/jsonapi/resource_controller_metal.rb +2 -2
- data/lib/jsonapi/resource_serializer.rb +64 -44
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +14 -9
- data/lib/jsonapi/routing_ext.rb +40 -16
- metadata +35 -8
@@ -90,20 +90,19 @@ module JSONAPI
|
|
90
90
|
primary_hash
|
91
91
|
end
|
92
92
|
|
93
|
-
def
|
93
|
+
def serialize_to_relationship_hash(source, requested_relationship)
|
94
94
|
if requested_relationship.is_a?(JSONAPI::Relationship::ToOne)
|
95
95
|
data = to_one_linkage(source, requested_relationship)
|
96
96
|
else
|
97
97
|
data = to_many_linkage(source, requested_relationship)
|
98
98
|
end
|
99
99
|
|
100
|
-
{
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
}
|
100
|
+
rel_hash = { 'data': data }
|
101
|
+
|
102
|
+
links = default_relationship_links(source, requested_relationship)
|
103
|
+
rel_hash['links'] = links unless links.blank?
|
104
|
+
|
105
|
+
rel_hash
|
107
106
|
end
|
108
107
|
|
109
108
|
def query_link(query_params)
|
@@ -133,7 +132,6 @@ module JSONAPI
|
|
133
132
|
supplying_attribute_fields: supplying_attribute_fields(resource_klass).sort,
|
134
133
|
supplying_relationship_fields: supplying_relationship_fields(resource_klass).sort,
|
135
134
|
link_builder_base_url: link_builder.base_url,
|
136
|
-
route_formatter_class: link_builder.route_formatter.uncached.class.name,
|
137
135
|
key_formatter_class: key_formatter.uncached.class.name,
|
138
136
|
always_include_to_one_linkage_data: always_include_to_one_linkage_data,
|
139
137
|
always_include_to_many_linkage_data: always_include_to_many_linkage_data
|
@@ -152,7 +150,7 @@ module JSONAPI
|
|
152
150
|
obj_hash['attributes'] = source.attributes_json if source.attributes_json
|
153
151
|
|
154
152
|
relationships = cached_relationships_hash(source, include_directives)
|
155
|
-
obj_hash['relationships'] = relationships unless relationships.
|
153
|
+
obj_hash['relationships'] = relationships unless relationships.blank?
|
156
154
|
|
157
155
|
obj_hash['meta'] = source.meta_json if source.meta_json
|
158
156
|
else
|
@@ -173,7 +171,7 @@ module JSONAPI
|
|
173
171
|
obj_hash['attributes'] = attributes unless attributes.empty?
|
174
172
|
|
175
173
|
relationships = relationships_hash(source, fetchable_fields, include_directives)
|
176
|
-
obj_hash['relationships'] = relationships unless relationships.
|
174
|
+
obj_hash['relationships'] = relationships unless relationships.blank?
|
177
175
|
|
178
176
|
meta = meta_hash(source)
|
179
177
|
obj_hash['meta'] = meta unless meta.empty?
|
@@ -251,7 +249,9 @@ module JSONAPI
|
|
251
249
|
|
252
250
|
def links_hash(source)
|
253
251
|
links = custom_links_hash(source)
|
254
|
-
links
|
252
|
+
if !links.key?('self') && !source.class.exclude_link?(:self)
|
253
|
+
links['self'] = link_builder.self_link(source)
|
254
|
+
end
|
255
255
|
links.compact
|
256
256
|
end
|
257
257
|
|
@@ -288,7 +288,8 @@ module JSONAPI
|
|
288
288
|
include_linked_children = ia && !ia[:include_related].empty?
|
289
289
|
|
290
290
|
if field_set.include?(name)
|
291
|
-
|
291
|
+
ro = relationship_object(source, relationship, include_linkage)
|
292
|
+
hash[format_key(name)] = ro unless ro.blank?
|
292
293
|
end
|
293
294
|
|
294
295
|
# If the object has been serialized once it will be in the related objects list,
|
@@ -298,7 +299,8 @@ module JSONAPI
|
|
298
299
|
resources = if source.preloaded_fragments.has_key?(format_key(name))
|
299
300
|
source.preloaded_fragments[format_key(name)].values
|
300
301
|
else
|
301
|
-
[
|
302
|
+
options = { filters: ia && ia[:include_filters] || {} }
|
303
|
+
[source.public_send(name, options)].flatten(1).compact
|
302
304
|
end
|
303
305
|
resources.each do |resource|
|
304
306
|
next if self_referential_and_already_in_source(resource)
|
@@ -380,6 +382,13 @@ module JSONAPI
|
|
380
382
|
link_builder.relationships_related_link(source, relationship)
|
381
383
|
end
|
382
384
|
|
385
|
+
def default_relationship_links(source, relationship)
|
386
|
+
links = {}
|
387
|
+
links['self'] = self_link(source, relationship) unless relationship.exclude_link?(:self)
|
388
|
+
links['related'] = related_link(source, relationship) unless relationship.exclude_link?(:related)
|
389
|
+
links.compact
|
390
|
+
end
|
391
|
+
|
383
392
|
def to_one_linkage(source, relationship)
|
384
393
|
linkage_id = foreign_key_value(source, relationship)
|
385
394
|
linkage_type = format_key(relationship.type_for_source(source))
|
@@ -393,24 +402,29 @@ module JSONAPI
|
|
393
402
|
|
394
403
|
def to_many_linkage(source, relationship)
|
395
404
|
linkage = []
|
405
|
+
include_config = include_directives.include_config(relationship.name.to_sym) if include_directives
|
406
|
+
include_filters = include_config[:include_filters] if include_config
|
407
|
+
options = { filters: include_filters || {} }
|
408
|
+
|
396
409
|
linkage_types_and_values = if source.preloaded_fragments.has_key?(format_key(relationship.name))
|
397
410
|
source.preloaded_fragments[format_key(relationship.name)].map do |_, resource|
|
398
411
|
[relationship.type, resource.id]
|
399
412
|
end
|
400
413
|
elsif relationship.polymorphic?
|
401
|
-
assoc = source.
|
414
|
+
assoc = source.public_send("records_for_#{relationship.name}", options)
|
402
415
|
# Avoid hitting the database again for values already pre-loaded
|
403
416
|
if assoc.respond_to?(:loaded?) and assoc.loaded?
|
404
417
|
assoc.map do |obj|
|
405
418
|
[obj.type.underscore.pluralize, obj.id]
|
406
419
|
end
|
407
420
|
else
|
421
|
+
assoc = assoc.unscope(:includes) if assoc.is_a?(ActiveRecord::Relation)
|
408
422
|
assoc.pluck(:type, :id).map do |type, id|
|
409
423
|
[type.underscore.pluralize, id]
|
410
424
|
end
|
411
425
|
end
|
412
426
|
else
|
413
|
-
source.public_send(relationship.name).map do |value|
|
427
|
+
source.public_send(relationship.name, options).map do |value|
|
414
428
|
[relationship.type, value.id]
|
415
429
|
end
|
416
430
|
end
|
@@ -423,46 +437,51 @@ module JSONAPI
|
|
423
437
|
linkage
|
424
438
|
end
|
425
439
|
|
426
|
-
def
|
440
|
+
def relationship_object_to_one(source, relationship, include_linkage)
|
427
441
|
include_linkage = include_linkage | @always_include_to_one_linkage_data | relationship.always_include_linkage_data
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
442
|
+
relationship_object_hash = {}
|
443
|
+
|
444
|
+
links = default_relationship_links(source, relationship)
|
445
|
+
|
446
|
+
relationship_object_hash['links'] = links unless links.blank?
|
447
|
+
relationship_object_hash[:data] = to_one_linkage(source, relationship) if include_linkage
|
448
|
+
relationship_object_hash
|
434
449
|
end
|
435
450
|
|
436
|
-
def
|
451
|
+
def relationship_object_to_many(source, relationship, include_linkage)
|
437
452
|
include_linkage = include_linkage | relationship.always_include_linkage_data
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
453
|
+
relationship_object_hash = {}
|
454
|
+
|
455
|
+
links = default_relationship_links(source, relationship)
|
456
|
+
relationship_object_hash['links'] = links unless links.blank?
|
457
|
+
relationship_object_hash[:data] = to_many_linkage(source, relationship) if include_linkage
|
458
|
+
relationship_object_hash
|
444
459
|
end
|
445
460
|
|
446
|
-
def
|
461
|
+
def relationship_object(source, relationship, include_linkage = false)
|
447
462
|
if relationship.is_a?(JSONAPI::Relationship::ToOne)
|
448
|
-
|
463
|
+
relationship_object_to_one(source, relationship, include_linkage)
|
449
464
|
elsif relationship.is_a?(JSONAPI::Relationship::ToMany)
|
450
|
-
|
465
|
+
relationship_object_to_many(source, relationship, include_linkage)
|
451
466
|
end
|
452
467
|
end
|
453
468
|
|
454
469
|
# Extracts the foreign key value for a to_one relationship.
|
455
470
|
def foreign_key_value(source, relationship)
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
source.
|
463
|
-
|
464
|
-
|
465
|
-
|
471
|
+
# If you have changed the key_name, don't even try to look at `"#{relationship.name}_id"`
|
472
|
+
# just load the association and call the custom key_name
|
473
|
+
foreign_key_type_changed = relationship.options[:foreign_key_type_changed] || false
|
474
|
+
related_resource_id =
|
475
|
+
if source.preloaded_fragments.has_key?(format_key(relationship.name))
|
476
|
+
source.preloaded_fragments[format_key(relationship.name)].values.first.try(:id)
|
477
|
+
elsif !foreign_key_type_changed && source.respond_to?("#{relationship.name}_id")
|
478
|
+
# If you have direct access to the underlying id, you don't have to load the relationship
|
479
|
+
# which can save quite a lot of time when loading a lot of data.
|
480
|
+
# This does not apply to e.g. has_one :through relationships.
|
481
|
+
source.public_send("#{relationship.name}_id")
|
482
|
+
else
|
483
|
+
source.public_send(relationship.name).try(:id)
|
484
|
+
end
|
466
485
|
return nil unless related_resource_id
|
467
486
|
@id_formatter.format(related_resource_id)
|
468
487
|
end
|
@@ -523,8 +542,9 @@ module JSONAPI
|
|
523
542
|
def generate_link_builder(primary_resource_klass, options)
|
524
543
|
LinkBuilder.new(
|
525
544
|
base_url: options.fetch(:base_url, ''),
|
526
|
-
route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
|
527
545
|
primary_resource_klass: primary_resource_klass,
|
546
|
+
route_formatter: options.fetch(:route_formatter, JSONAPI.configuration.route_formatter),
|
547
|
+
url_helpers: options.fetch(:url_helpers, options[:controller]),
|
528
548
|
)
|
529
549
|
end
|
530
550
|
end
|
@@ -64,14 +64,19 @@ module JSONAPI
|
|
64
64
|
|
65
65
|
# Build pagination links
|
66
66
|
if result.is_a?(JSONAPI::ResourcesOperationResult) || result.is_a?(JSONAPI::RelatedResourcesOperationResult)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
67
|
+
result.pagination_params.each_pair do |link_name, params|
|
68
|
+
if result.is_a?(JSONAPI::RelatedResourcesOperationResult)
|
69
|
+
relationship = result.source_resource.class._relationships[result._type.to_sym]
|
70
|
+
unless relationship.exclude_link?(link_name)
|
71
|
+
link = @serializer.link_builder.relationships_related_link(result.source_resource, relationship, query_params(params))
|
72
|
+
end
|
73
|
+
else
|
74
|
+
unless @serializer.link_builder.primary_resource_klass.exclude_link?(link_name)
|
75
|
+
link = @serializer.link_builder.query_link(query_params(params))
|
73
76
|
end
|
74
77
|
end
|
78
|
+
links[link_name] = link unless link.blank?
|
79
|
+
end
|
75
80
|
end
|
76
81
|
end
|
77
82
|
|
@@ -109,9 +114,9 @@ module JSONAPI
|
|
109
114
|
@serializer.serialize_to_hash(result.resource)
|
110
115
|
when JSONAPI::ResourcesOperationResult
|
111
116
|
@serializer.serialize_to_hash(result.resources)
|
112
|
-
when JSONAPI::
|
113
|
-
@serializer.
|
114
|
-
|
117
|
+
when JSONAPI::RelationshipOperationResult
|
118
|
+
@serializer.serialize_to_relationship_hash(result.parent_resource,
|
119
|
+
result.relationship)
|
115
120
|
when JSONAPI::OperationResult
|
116
121
|
{}
|
117
122
|
end
|
data/lib/jsonapi/routing_ext.rb
CHANGED
@@ -20,6 +20,12 @@ module ActionDispatch
|
|
20
20
|
@resource_type = resources.first
|
21
21
|
res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
|
22
22
|
|
23
|
+
res._routed = true
|
24
|
+
|
25
|
+
unless res.singleton?
|
26
|
+
warn "Singleton routes created for non singleton resource #{res}. Links may not be generated correctly."
|
27
|
+
end
|
28
|
+
|
23
29
|
options = resources.extract_options!.dup
|
24
30
|
options[:controller] ||= @resource_type
|
25
31
|
options.merge!(res.routing_resource_options)
|
@@ -80,6 +86,12 @@ module ActionDispatch
|
|
80
86
|
@resource_type = resources.first
|
81
87
|
res = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(@resource_type))
|
82
88
|
|
89
|
+
res._routed = true
|
90
|
+
|
91
|
+
if res.singleton?
|
92
|
+
warn "Singleton resource #{res} should use `jsonapi_resource` instead."
|
93
|
+
end
|
94
|
+
|
83
95
|
options = resources.extract_options!.dup
|
84
96
|
options[:controller] ||= @resource_type
|
85
97
|
options.merge!(res.routing_resource_options)
|
@@ -153,19 +165,23 @@ module ActionDispatch
|
|
153
165
|
methods = links_methods(options)
|
154
166
|
|
155
167
|
if methods.include?(:show)
|
156
|
-
match "relationships/#{formatted_relationship_name}",
|
157
|
-
|
168
|
+
match "relationships/#{formatted_relationship_name}",
|
169
|
+
controller: options[:controller],
|
170
|
+
action: 'show_relationship', relationship: link_type.to_s, via: [:get],
|
171
|
+
as: "relationships/#{link_type}"
|
158
172
|
end
|
159
173
|
|
160
174
|
if res.mutable?
|
161
175
|
if methods.include?(:update)
|
162
|
-
match "relationships/#{formatted_relationship_name}",
|
163
|
-
|
176
|
+
match "relationships/#{formatted_relationship_name}",
|
177
|
+
controller: options[:controller],
|
178
|
+
action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
|
164
179
|
end
|
165
180
|
|
166
181
|
if methods.include?(:destroy)
|
167
|
-
match "relationships/#{formatted_relationship_name}",
|
168
|
-
|
182
|
+
match "relationships/#{formatted_relationship_name}",
|
183
|
+
controller: options[:controller],
|
184
|
+
action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
|
169
185
|
end
|
170
186
|
end
|
171
187
|
end
|
@@ -182,23 +198,24 @@ module ActionDispatch
|
|
182
198
|
|
183
199
|
if methods.include?(:show)
|
184
200
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
185
|
-
|
201
|
+
action: 'show_relationship', relationship: link_type.to_s, via: [:get],
|
202
|
+
as: "relationships/#{link_type}"
|
186
203
|
end
|
187
204
|
|
188
205
|
if res.mutable?
|
189
206
|
if methods.include?(:create)
|
190
207
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
191
|
-
|
208
|
+
action: 'create_relationship', relationship: link_type.to_s, via: [:post]
|
192
209
|
end
|
193
210
|
|
194
211
|
if methods.include?(:update)
|
195
212
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
196
|
-
|
213
|
+
action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
|
197
214
|
end
|
198
215
|
|
199
216
|
if methods.include?(:destroy)
|
200
217
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
201
|
-
|
218
|
+
action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
|
202
219
|
end
|
203
220
|
end
|
204
221
|
end
|
@@ -210,6 +227,8 @@ module ActionDispatch
|
|
210
227
|
relationship_name = relationship.first
|
211
228
|
relationship = source._relationships[relationship_name]
|
212
229
|
|
230
|
+
relationship._routed = true
|
231
|
+
|
213
232
|
formatted_relationship_name = format_route(relationship.name)
|
214
233
|
|
215
234
|
if relationship.polymorphic?
|
@@ -219,9 +238,10 @@ module ActionDispatch
|
|
219
238
|
options[:controller] ||= related_resource._type.to_s
|
220
239
|
end
|
221
240
|
|
222
|
-
match
|
223
|
-
|
224
|
-
|
241
|
+
match formatted_relationship_name, controller: options[:controller],
|
242
|
+
relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
|
243
|
+
action: 'get_related_resource', via: [:get],
|
244
|
+
as: "related/#{relationship_name}"
|
225
245
|
end
|
226
246
|
|
227
247
|
def jsonapi_related_resources(*relationship)
|
@@ -231,13 +251,17 @@ module ActionDispatch
|
|
231
251
|
relationship_name = relationship.first
|
232
252
|
relationship = source._relationships[relationship_name]
|
233
253
|
|
254
|
+
relationship._routed = true
|
255
|
+
|
234
256
|
formatted_relationship_name = format_route(relationship.name)
|
235
257
|
related_resource = JSONAPI::Resource.resource_for(resource_type_with_module_prefix(relationship.class_name.underscore))
|
236
258
|
options[:controller] ||= related_resource._type.to_s
|
237
259
|
|
238
|
-
match
|
239
|
-
|
240
|
-
|
260
|
+
match formatted_relationship_name,
|
261
|
+
controller: options[:controller],
|
262
|
+
relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
|
263
|
+
action: 'get_related_resources', via: [:get],
|
264
|
+
as: "related/#{relationship_name}"
|
241
265
|
end
|
242
266
|
|
243
267
|
protected
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Gebhardt
|
@@ -9,22 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-02-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - "
|
18
|
+
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '0'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - "
|
25
|
+
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -109,6 +109,34 @@ dependencies:
|
|
109
109
|
- - ">="
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: database_cleaner
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: memory_profiler
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
112
140
|
- !ruby/object:Gem::Dependency
|
113
141
|
name: activerecord
|
114
142
|
requirement: !ruby/object:Gem::Requirement
|
@@ -216,8 +244,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
216
244
|
- !ruby/object:Gem::Version
|
217
245
|
version: '0'
|
218
246
|
requirements: []
|
219
|
-
|
220
|
-
rubygems_version: 2.5.1
|
247
|
+
rubygems_version: 3.0.3
|
221
248
|
signing_key:
|
222
249
|
specification_version: 4
|
223
250
|
summary: Easily support JSON API in Rails.
|