jsonapi-resources 0.9.0 → 0.10.6
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/LICENSE.txt +1 -1
- data/README.md +34 -11
- data/lib/bug_report_templates/rails_5_latest.rb +125 -0
- data/lib/bug_report_templates/rails_5_master.rb +140 -0
- data/lib/jsonapi/active_relation/adapters/join_left_active_record_adapter.rb +27 -0
- data/lib/jsonapi/active_relation/join_manager.rb +303 -0
- data/lib/jsonapi/active_relation_resource.rb +884 -0
- data/lib/jsonapi/acts_as_resource_controller.rb +122 -105
- data/lib/jsonapi/basic_resource.rb +1162 -0
- data/lib/jsonapi/cached_response_fragment.rb +127 -0
- data/lib/jsonapi/compiled_json.rb +11 -1
- data/lib/jsonapi/configuration.rb +71 -8
- data/lib/jsonapi/error.rb +27 -0
- data/lib/jsonapi/error_codes.rb +2 -0
- data/lib/jsonapi/exceptions.rb +80 -50
- data/lib/jsonapi/formatter.rb +3 -3
- data/lib/jsonapi/include_directives.rb +18 -65
- data/lib/jsonapi/link_builder.rb +74 -80
- data/lib/jsonapi/operation.rb +16 -5
- data/lib/jsonapi/operation_result.rb +74 -16
- data/lib/jsonapi/path.rb +43 -0
- data/lib/jsonapi/path_segment.rb +76 -0
- data/lib/jsonapi/processor.rb +239 -111
- data/lib/jsonapi/relationship.rb +153 -15
- data/lib/jsonapi/request_parser.rb +430 -367
- data/lib/jsonapi/resource.rb +3 -1253
- data/lib/jsonapi/resource_controller_metal.rb +5 -2
- data/lib/jsonapi/resource_fragment.rb +47 -0
- data/lib/jsonapi/resource_id_tree.rb +112 -0
- data/lib/jsonapi/resource_identity.rb +42 -0
- data/lib/jsonapi/resource_serializer.rb +143 -285
- data/lib/jsonapi/resource_set.rb +176 -0
- data/lib/jsonapi/resources/railtie.rb +9 -0
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/response_document.rb +105 -83
- data/lib/jsonapi/routing_ext.rb +48 -26
- data/lib/jsonapi-resources.rb +20 -4
- data/lib/tasks/check_upgrade.rake +52 -0
- metadata +50 -20
- data/lib/jsonapi/cached_resource_fragment.rb +0 -127
- data/lib/jsonapi/operation_dispatcher.rb +0 -88
- data/lib/jsonapi/operation_results.rb +0 -35
- data/lib/jsonapi/relationship_builder.rb +0 -167
@@ -0,0 +1,176 @@
|
|
1
|
+
module JSONAPI
|
2
|
+
# Contains a hash of resource types which contain a hash of resources, relationships and primary status keyed by
|
3
|
+
# resource id.
|
4
|
+
class ResourceSet
|
5
|
+
|
6
|
+
attr_reader :resource_klasses, :populated
|
7
|
+
|
8
|
+
def initialize(resource_id_tree = nil)
|
9
|
+
@populated = false
|
10
|
+
@resource_klasses = resource_id_tree.nil? ? {} : flatten_resource_id_tree(resource_id_tree)
|
11
|
+
end
|
12
|
+
|
13
|
+
def populate!(serializer, context, find_options)
|
14
|
+
# For each resource klass we want to generate the caching key
|
15
|
+
|
16
|
+
# Hash for collecting types and ids
|
17
|
+
# @type [Hash<Class<Resource>, Id[]]]
|
18
|
+
missed_resource_ids = {}
|
19
|
+
|
20
|
+
# Array for collecting CachedResponseFragment::Lookups
|
21
|
+
# @type [Lookup[]]
|
22
|
+
lookups = []
|
23
|
+
|
24
|
+
|
25
|
+
# Step One collect all of the lookups for the cache, or keys that don't require cache access
|
26
|
+
@resource_klasses.each_key do |resource_klass|
|
27
|
+
|
28
|
+
serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
|
29
|
+
context_json = resource_klass.attribute_caching_context(context).to_json
|
30
|
+
context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
|
31
|
+
context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
|
32
|
+
|
33
|
+
if resource_klass.caching?
|
34
|
+
cache_ids = @resource_klasses[resource_klass].map do |(k, v)|
|
35
|
+
# Store the hashcode of the cache_field to avoid storing objects and to ensure precision isn't lost
|
36
|
+
# on timestamp types (i.e. string conversions dropping milliseconds)
|
37
|
+
[k, resource_klass.hash_cache_field(v[:cache_id])]
|
38
|
+
end
|
39
|
+
|
40
|
+
lookups.push(
|
41
|
+
CachedResponseFragment::Lookup.new(
|
42
|
+
resource_klass,
|
43
|
+
serializer_config_key,
|
44
|
+
context,
|
45
|
+
context_key,
|
46
|
+
cache_ids
|
47
|
+
)
|
48
|
+
)
|
49
|
+
else
|
50
|
+
missed_resource_ids[resource_klass] = @resource_klasses[resource_klass].keys
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if lookups.any?
|
55
|
+
raise "You've declared some Resources as caching without providing a caching store" if JSONAPI.configuration.resource_cache.nil?
|
56
|
+
|
57
|
+
# Step Two execute the cache lookup
|
58
|
+
found_resources = CachedResponseFragment.lookup(lookups, context)
|
59
|
+
else
|
60
|
+
found_resources = {}
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# Step Three collect the results and collect hit/miss stats
|
65
|
+
stats = {}
|
66
|
+
found_resources.each do |resource_klass, resources|
|
67
|
+
resources.each do |id, cached_resource|
|
68
|
+
stats[resource_klass] ||= {}
|
69
|
+
|
70
|
+
if cached_resource.nil?
|
71
|
+
stats[resource_klass][:misses] ||= 0
|
72
|
+
stats[resource_klass][:misses] += 1
|
73
|
+
|
74
|
+
# Collect misses
|
75
|
+
missed_resource_ids[resource_klass] ||= []
|
76
|
+
missed_resource_ids[resource_klass].push(id)
|
77
|
+
else
|
78
|
+
stats[resource_klass][:hits] ||= 0
|
79
|
+
stats[resource_klass][:hits] += 1
|
80
|
+
|
81
|
+
register_resource(resource_klass, cached_resource)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
report_stats(stats)
|
87
|
+
|
88
|
+
writes = []
|
89
|
+
|
90
|
+
# Step Four find any of the missing resources and join them into the result
|
91
|
+
missed_resource_ids.each_pair do |resource_klass, ids|
|
92
|
+
find_opts = {context: context, fields: find_options[:fields]}
|
93
|
+
found_resources = resource_klass.find_to_populate_by_keys(ids, find_opts)
|
94
|
+
|
95
|
+
found_resources.each do |resource|
|
96
|
+
relationship_data = @resource_klasses[resource_klass][resource.id][:relationships]
|
97
|
+
|
98
|
+
if resource_klass.caching?
|
99
|
+
|
100
|
+
serializer_config_key = serializer.config_key(resource_klass).gsub("/", "_")
|
101
|
+
context_json = resource_klass.attribute_caching_context(context).to_json
|
102
|
+
context_b64 = JSONAPI.configuration.resource_cache_digest_function.call(context_json)
|
103
|
+
context_key = "ATTR-CTX-#{context_b64.gsub("/", "_")}"
|
104
|
+
|
105
|
+
writes.push(CachedResponseFragment::Write.new(
|
106
|
+
resource_klass,
|
107
|
+
resource,
|
108
|
+
serializer,
|
109
|
+
serializer_config_key,
|
110
|
+
context,
|
111
|
+
context_key,
|
112
|
+
relationship_data
|
113
|
+
))
|
114
|
+
end
|
115
|
+
|
116
|
+
register_resource(resource_klass, resource)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Step Five conditionally write to the cache
|
121
|
+
CachedResponseFragment.write(writes) unless JSONAPI.configuration.resource_cache.nil?
|
122
|
+
|
123
|
+
mark_populated!
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
def mark_populated!
|
128
|
+
@populated = true
|
129
|
+
end
|
130
|
+
|
131
|
+
def register_resource(resource_klass, resource, primary = false)
|
132
|
+
@resource_klasses[resource_klass] ||= {}
|
133
|
+
@resource_klasses[resource_klass][resource.id] ||= {primary: resource.try(:primary) || primary, relationships: {}}
|
134
|
+
@resource_klasses[resource_klass][resource.id][:resource] = resource
|
135
|
+
end
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
def report_stats(stats)
|
140
|
+
return unless JSONAPI.configuration.resource_cache_usage_report_function || JSONAPI.configuration.resource_cache.nil?
|
141
|
+
|
142
|
+
stats.each_pair do |resource_klass, stat|
|
143
|
+
JSONAPI.configuration.resource_cache_usage_report_function.call(
|
144
|
+
resource_klass.name,
|
145
|
+
stat[:hits] || 0,
|
146
|
+
stat[:misses] || 0
|
147
|
+
)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def flatten_resource_id_tree(resource_id_tree, flattened_tree = {})
|
152
|
+
resource_id_tree.fragments.each_pair do |resource_rid, fragment|
|
153
|
+
|
154
|
+
resource_klass = resource_rid.resource_klass
|
155
|
+
id = resource_rid.id
|
156
|
+
|
157
|
+
flattened_tree[resource_klass] ||= {}
|
158
|
+
|
159
|
+
flattened_tree[resource_klass][id] ||= {primary: fragment.primary, relationships: {}}
|
160
|
+
flattened_tree[resource_klass][id][:cache_id] ||= fragment.cache
|
161
|
+
|
162
|
+
fragment.related.try(:each_pair) do |relationship_name, related_rids|
|
163
|
+
flattened_tree[resource_klass][id][:relationships][relationship_name] ||= Set.new
|
164
|
+
flattened_tree[resource_klass][id][:relationships][relationship_name].merge(related_rids)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
related_resource_id_trees = resource_id_tree.related_resource_id_trees
|
169
|
+
related_resource_id_trees.try(:each_value) do |related_resource_id_tree|
|
170
|
+
flatten_resource_id_tree(related_resource_id_tree, flattened_tree)
|
171
|
+
end
|
172
|
+
|
173
|
+
flattened_tree
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -1,81 +1,138 @@
|
|
1
1
|
module JSONAPI
|
2
2
|
class ResponseDocument
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
attr_reader :serialized_results
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@serialized_results = []
|
7
|
+
@result_codes = []
|
8
|
+
@error_results = []
|
9
|
+
@global_errors = []
|
10
|
+
|
6
11
|
@options = options
|
7
12
|
|
13
|
+
@top_level_meta = @options.fetch(:base_meta, {})
|
14
|
+
@top_level_links = @options.fetch(:base_links, {})
|
15
|
+
|
8
16
|
@key_formatter = @options.fetch(:key_formatter, JSONAPI.configuration.key_formatter)
|
9
17
|
end
|
10
18
|
|
11
|
-
def
|
12
|
-
|
19
|
+
def has_errors?
|
20
|
+
@error_results.length > 0 || @global_errors.length > 0
|
21
|
+
end
|
13
22
|
|
14
|
-
|
15
|
-
|
23
|
+
def add_result(result, operation)
|
24
|
+
if result.is_a?(JSONAPI::ErrorsOperationResult)
|
25
|
+
# Clear any serialized results
|
26
|
+
@serialized_results = []
|
16
27
|
|
17
|
-
|
18
|
-
|
28
|
+
# In JSONAPI v1 we only have one operation so all errors can be kept together
|
29
|
+
result.errors.each do |error|
|
30
|
+
add_global_error(error)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
@serialized_results.push result.to_hash(operation.options[:serializer])
|
34
|
+
@result_codes.push result.code.to_i
|
35
|
+
update_links(operation.options[:serializer], result)
|
36
|
+
update_meta(result)
|
37
|
+
end
|
38
|
+
end
|
19
39
|
|
20
|
-
|
40
|
+
def add_global_error(error)
|
41
|
+
@global_errors.push error
|
21
42
|
end
|
22
43
|
|
23
|
-
def
|
24
|
-
if
|
25
|
-
@
|
44
|
+
def contents
|
45
|
+
if has_errors?
|
46
|
+
return { 'errors' => @global_errors }
|
26
47
|
else
|
27
|
-
@
|
48
|
+
hash = @serialized_results[0]
|
49
|
+
meta = top_level_meta
|
50
|
+
hash.merge!('meta' => meta) unless meta.empty?
|
51
|
+
|
52
|
+
links = top_level_links
|
53
|
+
hash.merge!('links' => links) unless links.empty?
|
54
|
+
|
55
|
+
return hash
|
28
56
|
end
|
29
57
|
end
|
30
58
|
|
31
|
-
|
59
|
+
def status
|
60
|
+
status_codes = if has_errors?
|
61
|
+
@global_errors.collect do |error|
|
62
|
+
error.status.to_i
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@result_codes
|
66
|
+
end
|
67
|
+
|
68
|
+
# Count the unique status codes
|
69
|
+
counts = status_codes.each_with_object(Hash.new(0)) { |code, counts| counts[code] += 1 }
|
70
|
+
|
71
|
+
# if there is only one status code we can return that
|
72
|
+
return counts.keys[0].to_i if counts.length == 1
|
73
|
+
|
74
|
+
# :nocov: not currently used
|
75
|
+
|
76
|
+
# if there are many we should return the highest general code, 200, 400, 500 etc.
|
77
|
+
max_status = 0
|
78
|
+
status_codes.each do |status|
|
79
|
+
code = status.to_i
|
80
|
+
max_status = code if max_status < code
|
81
|
+
end
|
82
|
+
return (max_status / 100).floor * 100
|
83
|
+
# :nocov:
|
84
|
+
end
|
32
85
|
|
33
|
-
|
34
|
-
# and the result of each operation. The keys are then formatted.
|
35
|
-
def top_level_meta
|
36
|
-
meta = @options.fetch(:base_meta, {})
|
86
|
+
private
|
37
87
|
|
38
|
-
|
88
|
+
def update_meta(result)
|
89
|
+
@top_level_meta.merge!(result.meta)
|
39
90
|
|
40
|
-
|
41
|
-
|
91
|
+
if JSONAPI.configuration.top_level_meta_include_record_count && result.respond_to?(:record_count)
|
92
|
+
@top_level_meta[JSONAPI.configuration.top_level_meta_record_count_key] = result.record_count
|
93
|
+
end
|
42
94
|
|
43
|
-
|
44
|
-
|
45
|
-
|
95
|
+
if JSONAPI.configuration.top_level_meta_include_page_count && result.respond_to?(:page_count)
|
96
|
+
@top_level_meta[JSONAPI.configuration.top_level_meta_page_count_key] = result.page_count
|
97
|
+
end
|
46
98
|
|
47
|
-
|
48
|
-
|
99
|
+
if result.warnings.any?
|
100
|
+
@top_level_meta[:warnings] = result.warnings.collect do |warning|
|
101
|
+
warning.to_hash
|
49
102
|
end
|
50
103
|
end
|
104
|
+
end
|
51
105
|
|
52
|
-
|
106
|
+
def top_level_meta
|
107
|
+
@top_level_meta.as_json.deep_transform_keys { |key| @key_formatter.format(key) }
|
53
108
|
end
|
54
109
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
links[link_name] = @serializer.query_link(query_params(params))
|
73
|
-
end
|
110
|
+
def update_links(serializer, result)
|
111
|
+
@top_level_links.merge!(result.links)
|
112
|
+
|
113
|
+
# Build pagination links
|
114
|
+
if result.is_a?(JSONAPI::ResourceSetOperationResult) ||
|
115
|
+
result.is_a?(JSONAPI::ResourcesSetOperationResult) ||
|
116
|
+
result.is_a?(JSONAPI::RelatedResourcesSetOperationResult)
|
117
|
+
|
118
|
+
result.pagination_params.each_pair do |link_name, params|
|
119
|
+
if result.is_a?(JSONAPI::RelatedResourcesSetOperationResult)
|
120
|
+
relationship = result.source_resource.class._relationships[result._type.to_sym]
|
121
|
+
unless relationship.exclude_link?(link_name)
|
122
|
+
link = serializer.link_builder.relationships_related_link(result.source_resource, relationship, query_params(params))
|
123
|
+
end
|
124
|
+
else
|
125
|
+
unless serializer.link_builder.primary_resource_klass.exclude_link?(link_name)
|
126
|
+
link = serializer.link_builder.query_link(query_params(params))
|
74
127
|
end
|
128
|
+
end
|
129
|
+
@top_level_links[link_name] = link unless link.blank?
|
75
130
|
end
|
76
131
|
end
|
132
|
+
end
|
77
133
|
|
78
|
-
|
134
|
+
def top_level_links
|
135
|
+
@top_level_links.deep_transform_keys { |key| @key_formatter.format(key) }
|
79
136
|
end
|
80
137
|
|
81
138
|
def query_params(params)
|
@@ -96,40 +153,5 @@ module JSONAPI
|
|
96
153
|
|
97
154
|
query_params
|
98
155
|
end
|
99
|
-
|
100
|
-
def results_to_hash
|
101
|
-
if @operation_results.has_errors?
|
102
|
-
{ errors: @operation_results.all_errors }
|
103
|
-
else
|
104
|
-
if @operation_results.results.length == 1
|
105
|
-
result = @operation_results.results[0]
|
106
|
-
|
107
|
-
case result
|
108
|
-
when JSONAPI::ResourceOperationResult
|
109
|
-
@serializer.serialize_to_hash(result.resource)
|
110
|
-
when JSONAPI::ResourcesOperationResult
|
111
|
-
@serializer.serialize_to_hash(result.resources)
|
112
|
-
when JSONAPI::LinksObjectOperationResult
|
113
|
-
@serializer.serialize_to_links_hash(result.parent_resource,
|
114
|
-
result.relationship)
|
115
|
-
when JSONAPI::OperationResult
|
116
|
-
{}
|
117
|
-
end
|
118
|
-
|
119
|
-
elsif @operation_results.results.length > 1
|
120
|
-
resources = []
|
121
|
-
@operation_results.results.each do |result|
|
122
|
-
case result
|
123
|
-
when JSONAPI::ResourceOperationResult
|
124
|
-
resources.push(result.resource)
|
125
|
-
when JSONAPI::ResourcesOperationResult
|
126
|
-
resources.concat(result.resources)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
@serializer.serialize_to_hash(resources)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
156
|
end
|
135
157
|
end
|
data/lib/jsonapi/routing_ext.rb
CHANGED
@@ -18,7 +18,13 @@ module ActionDispatch
|
|
18
18
|
|
19
19
|
def jsonapi_resource(*resources, &_block)
|
20
20
|
@resource_type = resources.first
|
21
|
-
res = JSONAPI::Resource.
|
21
|
+
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
|
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
|
22
28
|
|
23
29
|
options = resources.extract_options!.dup
|
24
30
|
options[:controller] ||= @resource_type
|
@@ -33,8 +39,8 @@ module ActionDispatch
|
|
33
39
|
end
|
34
40
|
|
35
41
|
if res._immutable
|
36
|
-
options[:except] << :create
|
37
|
-
options[:except] << :update
|
42
|
+
options[:except] << :create unless options[:except].include?(:create) || options[:except].include?('create')
|
43
|
+
options[:except] << :update unless options[:except].include?(:update) || options[:except].include?('update')
|
38
44
|
options[:except] << :destroy unless options[:except].include?(:destroy) || options[:except].include?('destroy')
|
39
45
|
end
|
40
46
|
|
@@ -64,7 +70,7 @@ module ActionDispatch
|
|
64
70
|
end
|
65
71
|
|
66
72
|
def jsonapi_relationships(options = {})
|
67
|
-
res = JSONAPI::Resource.
|
73
|
+
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
|
68
74
|
res._relationships.each do |relationship_name, relationship|
|
69
75
|
if relationship.is_a?(JSONAPI::Relationship::ToMany)
|
70
76
|
jsonapi_links(relationship_name, options)
|
@@ -78,7 +84,13 @@ module ActionDispatch
|
|
78
84
|
|
79
85
|
def jsonapi_resources(*resources, &_block)
|
80
86
|
@resource_type = resources.first
|
81
|
-
res = JSONAPI::Resource.
|
87
|
+
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(@resource_type))
|
88
|
+
|
89
|
+
res._routed = true
|
90
|
+
|
91
|
+
if res.singleton?
|
92
|
+
warn "Singleton resource #{res} should use `jsonapi_resource` instead."
|
93
|
+
end
|
82
94
|
|
83
95
|
options = resources.extract_options!.dup
|
84
96
|
options[:controller] ||= @resource_type
|
@@ -102,8 +114,8 @@ module ActionDispatch
|
|
102
114
|
end
|
103
115
|
|
104
116
|
if res._immutable
|
105
|
-
options[:except] << :create
|
106
|
-
options[:except] << :update
|
117
|
+
options[:except] << :create unless options[:except].include?(:create) || options[:except].include?('create')
|
118
|
+
options[:except] << :update unless options[:except].include?(:update) || options[:except].include?('update')
|
107
119
|
options[:except] << :destroy unless options[:except].include?(:destroy) || options[:except].include?('destroy')
|
108
120
|
end
|
109
121
|
|
@@ -147,25 +159,26 @@ module ActionDispatch
|
|
147
159
|
formatted_relationship_name = format_route(link_type)
|
148
160
|
options = links.extract_options!.dup
|
149
161
|
|
150
|
-
res = JSONAPI::Resource.
|
162
|
+
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
|
151
163
|
options[:controller] ||= res._type.to_s
|
152
164
|
|
153
165
|
methods = links_methods(options)
|
154
166
|
|
155
167
|
if methods.include?(:show)
|
156
168
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
157
|
-
|
169
|
+
action: 'show_relationship', relationship: link_type.to_s, via: [:get],
|
170
|
+
as: "relationships/#{link_type}"
|
158
171
|
end
|
159
172
|
|
160
173
|
if res.mutable?
|
161
174
|
if methods.include?(:update)
|
162
175
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
163
|
-
|
176
|
+
action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
|
164
177
|
end
|
165
178
|
|
166
179
|
if methods.include?(:destroy)
|
167
180
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
168
|
-
|
181
|
+
action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
|
169
182
|
end
|
170
183
|
end
|
171
184
|
end
|
@@ -175,69 +188,77 @@ module ActionDispatch
|
|
175
188
|
formatted_relationship_name = format_route(link_type)
|
176
189
|
options = links.extract_options!.dup
|
177
190
|
|
178
|
-
res = JSONAPI::Resource.
|
191
|
+
res = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
|
179
192
|
options[:controller] ||= res._type.to_s
|
180
193
|
|
181
194
|
methods = links_methods(options)
|
182
195
|
|
183
196
|
if methods.include?(:show)
|
184
197
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
185
|
-
|
198
|
+
action: 'show_relationship', relationship: link_type.to_s, via: [:get],
|
199
|
+
as: "relationships/#{link_type}"
|
186
200
|
end
|
187
201
|
|
188
202
|
if res.mutable?
|
189
203
|
if methods.include?(:create)
|
190
204
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
191
|
-
|
205
|
+
action: 'create_relationship', relationship: link_type.to_s, via: [:post]
|
192
206
|
end
|
193
207
|
|
194
208
|
if methods.include?(:update)
|
195
209
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
196
|
-
|
210
|
+
action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
|
197
211
|
end
|
198
212
|
|
199
213
|
if methods.include?(:destroy)
|
200
214
|
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
201
|
-
|
215
|
+
action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
|
202
216
|
end
|
203
217
|
end
|
204
218
|
end
|
205
219
|
|
206
220
|
def jsonapi_related_resource(*relationship)
|
207
|
-
source = JSONAPI::Resource.
|
221
|
+
source = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
|
208
222
|
options = relationship.extract_options!.dup
|
209
223
|
|
210
224
|
relationship_name = relationship.first
|
211
225
|
relationship = source._relationships[relationship_name]
|
212
226
|
|
227
|
+
relationship._routed = true
|
228
|
+
|
213
229
|
formatted_relationship_name = format_route(relationship.name)
|
214
230
|
|
215
231
|
if relationship.polymorphic?
|
216
232
|
options[:controller] ||= relationship.class_name.underscore.pluralize
|
217
233
|
else
|
218
|
-
related_resource = JSONAPI::Resource.
|
234
|
+
related_resource = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(relationship.class_name.underscore.pluralize))
|
219
235
|
options[:controller] ||= related_resource._type.to_s
|
220
236
|
end
|
221
237
|
|
222
|
-
match
|
223
|
-
|
224
|
-
|
238
|
+
match formatted_relationship_name, controller: options[:controller],
|
239
|
+
relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
|
240
|
+
action: 'show_related_resource', via: [:get],
|
241
|
+
as: "related/#{relationship_name}"
|
225
242
|
end
|
226
243
|
|
227
244
|
def jsonapi_related_resources(*relationship)
|
228
|
-
source = JSONAPI::Resource.
|
245
|
+
source = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix)
|
229
246
|
options = relationship.extract_options!.dup
|
230
247
|
|
231
248
|
relationship_name = relationship.first
|
232
249
|
relationship = source._relationships[relationship_name]
|
233
250
|
|
251
|
+
relationship._routed = true
|
252
|
+
|
234
253
|
formatted_relationship_name = format_route(relationship.name)
|
235
|
-
related_resource = JSONAPI::Resource.
|
254
|
+
related_resource = JSONAPI::Resource.resource_klass_for(resource_type_with_module_prefix(relationship.class_name.underscore))
|
236
255
|
options[:controller] ||= related_resource._type.to_s
|
237
256
|
|
238
|
-
match
|
239
|
-
|
240
|
-
|
257
|
+
match formatted_relationship_name,
|
258
|
+
controller: options[:controller],
|
259
|
+
relationship: relationship.name, source: resource_type_with_module_prefix(source._type),
|
260
|
+
action: 'index_related_resources', via: [:get],
|
261
|
+
as: "related/#{relationship_name}"
|
241
262
|
end
|
242
263
|
|
243
264
|
protected
|
@@ -250,6 +271,7 @@ module ActionDispatch
|
|
250
271
|
@scope = @scope.parent
|
251
272
|
end
|
252
273
|
# :nocov:
|
274
|
+
|
253
275
|
private
|
254
276
|
|
255
277
|
def resource_type_with_module_prefix(resource = nil)
|
data/lib/jsonapi-resources.rb
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
+
require 'jsonapi/resources/railtie'
|
1
2
|
require 'jsonapi/naive_cache'
|
2
3
|
require 'jsonapi/compiled_json'
|
4
|
+
require 'jsonapi/basic_resource'
|
5
|
+
require 'jsonapi/active_relation_resource'
|
3
6
|
require 'jsonapi/resource'
|
4
|
-
require 'jsonapi/
|
7
|
+
require 'jsonapi/cached_response_fragment'
|
5
8
|
require 'jsonapi/response_document'
|
6
9
|
require 'jsonapi/acts_as_resource_controller'
|
7
|
-
|
10
|
+
if Rails::VERSION::MAJOR >= 6
|
11
|
+
ActiveSupport.on_load(:action_controller_base) do
|
12
|
+
require 'jsonapi/resource_controller'
|
13
|
+
end
|
14
|
+
else
|
15
|
+
require 'jsonapi/resource_controller'
|
16
|
+
end
|
8
17
|
require 'jsonapi/resource_controller_metal'
|
9
18
|
require 'jsonapi/resources/version'
|
10
19
|
require 'jsonapi/configuration'
|
@@ -17,11 +26,18 @@ require 'jsonapi/exceptions'
|
|
17
26
|
require 'jsonapi/error'
|
18
27
|
require 'jsonapi/error_codes'
|
19
28
|
require 'jsonapi/request_parser'
|
20
|
-
require 'jsonapi/operation_dispatcher'
|
21
29
|
require 'jsonapi/processor'
|
22
30
|
require 'jsonapi/relationship'
|
23
31
|
require 'jsonapi/include_directives'
|
32
|
+
require 'jsonapi/operation'
|
24
33
|
require 'jsonapi/operation_result'
|
25
|
-
require 'jsonapi/operation_results'
|
26
34
|
require 'jsonapi/callbacks'
|
27
35
|
require 'jsonapi/link_builder'
|
36
|
+
require 'jsonapi/active_relation/adapters/join_left_active_record_adapter'
|
37
|
+
require 'jsonapi/active_relation/join_manager'
|
38
|
+
require 'jsonapi/resource_identity'
|
39
|
+
require 'jsonapi/resource_fragment'
|
40
|
+
require 'jsonapi/resource_id_tree'
|
41
|
+
require 'jsonapi/resource_set'
|
42
|
+
require 'jsonapi/path'
|
43
|
+
require 'jsonapi/path_segment'
|