jsonapionify 0.11.11 → 0.12.0
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 +8 -8
- data/README.md +3 -0
- data/examples/example_api.rb +80 -0
- data/jsonapionify.gemspec +1 -0
- data/lib/jsonapionify/api/action/documentation.rb +2 -2
- data/lib/jsonapionify/api/attribute.rb +3 -2
- data/lib/jsonapionify/api/context.rb +8 -3
- data/lib/jsonapionify/api/context_delegate.rb +6 -0
- data/lib/jsonapionify/api/relationship.rb +5 -10
- data/lib/jsonapionify/api/relationship/many.rb +46 -14
- data/lib/jsonapionify/api/relationship/one.rb +31 -8
- data/lib/jsonapionify/api/resource.rb +3 -6
- data/lib/jsonapionify/api/resource/builders/fields_builder.rb +2 -2
- data/lib/jsonapionify/api/resource/builders/relationship_builder.rb +3 -2
- data/lib/jsonapionify/api/resource/builders/relationships_builder.rb +3 -2
- data/lib/jsonapionify/api/resource/caching.rb +1 -4
- data/lib/jsonapionify/api/resource/callbacks.rb +60 -0
- data/lib/jsonapionify/api/resource/caller.rb +8 -7
- data/lib/jsonapionify/api/resource/defaults/actions.rb +15 -12
- data/lib/jsonapionify/api/resource/defaults/hooks.rb +10 -57
- data/lib/jsonapionify/api/resource/defaults/options.rb +3 -8
- data/lib/jsonapionify/api/resource/defaults/params.rb +0 -2
- data/lib/jsonapionify/api/resource/defaults/request_contexts.rb +24 -29
- data/lib/jsonapionify/api/resource/defaults/response_contexts.rb +11 -18
- data/lib/jsonapionify/api/resource/definitions/actions.rb +38 -22
- data/lib/jsonapionify/api/resource/definitions/attributes.rb +2 -2
- data/lib/jsonapionify/api/resource/definitions/helpers.rb +1 -1
- data/lib/jsonapionify/api/resource/definitions/includes.rb +16 -0
- data/lib/jsonapionify/api/resource/definitions/pagination.rb +7 -11
- data/lib/jsonapionify/api/resource/definitions/params.rb +19 -26
- data/lib/jsonapionify/api/resource/definitions/relationships.rb +3 -3
- data/lib/jsonapionify/api/resource/definitions/request_headers.rb +14 -16
- data/lib/jsonapionify/api/resource/definitions/response_headers.rb +2 -1
- data/lib/jsonapionify/api/resource/definitions/scopes.rb +17 -6
- data/lib/jsonapionify/api/resource/definitions/sorting.rb +8 -7
- data/lib/jsonapionify/api/resource/error_handling.rb +3 -2
- data/lib/jsonapionify/api/resource/exec.rb +3 -1
- data/lib/jsonapionify/api/resource/includer.rb +20 -51
- data/lib/jsonapionify/api/response.rb +3 -1
- data/lib/jsonapionify/callbacks.rb +10 -7
- data/lib/jsonapionify/destructured_proc.rb +27 -0
- data/lib/jsonapionify/structure/collections/base.rb +20 -8
- data/lib/jsonapionify/structure/objects/base.rb +11 -2
- data/lib/jsonapionify/structure/objects/resource_identifier.rb +9 -14
- data/lib/jsonapionify/version.rb +1 -1
- metadata +20 -2
@@ -3,35 +3,52 @@ require 'active_support/core_ext/array/wrap'
|
|
3
3
|
|
4
4
|
module JSONAPIonify::Api
|
5
5
|
module Resource::Definitions::Actions
|
6
|
+
using JSONAPIonify::DestructuredProc
|
6
7
|
ActionNotFound = Class.new StandardError
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
DEFAULT_SAVE_COMMIT = proc do |instance:, request_attributes:, request_relationships:|
|
10
|
+
# Assign the attributes
|
11
|
+
request_attributes.each do |key, value|
|
12
|
+
instance.send "#{key}=", value
|
13
|
+
end
|
14
|
+
|
15
|
+
# Assign the relationships
|
16
|
+
request_relationships.each do |key, value|
|
17
|
+
instance.send "#{key}=", value
|
18
|
+
end
|
19
|
+
|
20
|
+
# Save the instance
|
21
|
+
instance.save if instance.respond_to? :save
|
22
|
+
end
|
23
|
+
|
24
|
+
DEFAULT_DELETE_COMMIT = proc do |instance:|
|
25
|
+
instance.respond_to?(:destroy) ? instance.destroy : instance.respond_to?(:delete) ? instance.delete : nil
|
18
26
|
end
|
19
27
|
|
20
|
-
|
21
|
-
|
22
|
-
|
28
|
+
INSTANCE_RESPONSE = proc do |context, instance:, response_object:, builder: nil|
|
29
|
+
response_object[:data] = build_resource(context: context, instance: instance, &builder)
|
30
|
+
response_object.to_json
|
31
|
+
end
|
32
|
+
|
33
|
+
CREATE_RESPONSE = proc do |context, instance:, response_object:, builder: nil, response_headers:|
|
34
|
+
instance_exec(context, instance: instance, response_object: response_object, builder: builder, &INSTANCE_RESPONSE).tap do
|
35
|
+
response_headers['Location'] = response_object[:data][:links][:self]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
COLLECTION_RESPONSE = proc do |context, response_collection:, links:, response_object:, builder: nil, nested_request: false|
|
40
|
+
response_object[:data] = build_resource_collection(
|
23
41
|
context: context,
|
24
|
-
collection:
|
25
|
-
include_cursors: (
|
42
|
+
collection: response_collection,
|
43
|
+
include_cursors: (links.keys & [:first, :last, :next, :prev]).present?,
|
26
44
|
&builder
|
27
45
|
)
|
28
|
-
|
46
|
+
response_object.to_json unless nested_request
|
29
47
|
end
|
30
48
|
|
31
49
|
def self.extended(klass)
|
32
50
|
klass.class_eval do
|
33
51
|
extend JSONAPIonify::InheritedAttributes
|
34
|
-
include JSONAPIonify::Callbacks
|
35
52
|
define_callbacks(
|
36
53
|
:request,
|
37
54
|
:exception,
|
@@ -57,12 +74,9 @@ module JSONAPIonify::Api
|
|
57
74
|
end
|
58
75
|
|
59
76
|
def create(**options, &block)
|
77
|
+
block ||= DEFAULT_SAVE_COMMIT
|
60
78
|
define_action(:create, 'POST', '', **options, cacheable: false, example_input: :resource, &block).tap do |action|
|
61
|
-
action.response(status: 201)
|
62
|
-
instance_exec(context, &INSTANCE_RESPONSE).tap do
|
63
|
-
response_headers['Location'] = context.response_object[:data][:links][:self]
|
64
|
-
end
|
65
|
-
end
|
79
|
+
action.response(status: 201, &CREATE_RESPONSE)
|
66
80
|
end
|
67
81
|
end
|
68
82
|
|
@@ -73,12 +87,14 @@ module JSONAPIonify::Api
|
|
73
87
|
end
|
74
88
|
|
75
89
|
def update(**options, &block)
|
90
|
+
block ||= DEFAULT_SAVE_COMMIT
|
76
91
|
define_action(:update, 'PATCH', '/:id', **options, cacheable: false, example_input: :resource, &block).tap do |action|
|
77
92
|
action.response(status: 200, &INSTANCE_RESPONSE)
|
78
93
|
end
|
79
94
|
end
|
80
95
|
|
81
96
|
def delete(**options, &block)
|
97
|
+
block ||= DEFAULT_DELETE_COMMIT
|
82
98
|
define_action(:delete, 'DELETE', '/:id', **options, cacheable: false, &block).tap do |action|
|
83
99
|
action.response(status: 204)
|
84
100
|
end
|
@@ -8,8 +8,8 @@ module JSONAPIonify::Api
|
|
8
8
|
inherited_array_attribute :attributes
|
9
9
|
delegate :id_attribute, :attributes, to: :class
|
10
10
|
|
11
|
-
context(:fields, readonly: true) do |
|
12
|
-
|
11
|
+
context(:fields, readonly: true) do |params:|
|
12
|
+
params['fields']&.map do |type, fields|
|
13
13
|
[type, fields.split(',')]
|
14
14
|
end&.to_h
|
15
15
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module JSONAPIonify::Api
|
2
|
+
module Resource::Definitions::Includes
|
3
|
+
|
4
|
+
def self.extended(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
extend JSONAPIonify::InheritedAttributes
|
7
|
+
inherited_hash_attribute :include_definitions
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def includable(relationship, &block)
|
12
|
+
include_definitions[relationship.to_sym] = block
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -99,29 +99,25 @@ module JSONAPIonify::Api
|
|
99
99
|
param :page, :before, actions: %i{list}
|
100
100
|
param :page, :first, actions: %i{list}
|
101
101
|
param :page, :last, actions: %i{list}
|
102
|
-
context :paginated_collection, readonly: true do |context
|
103
|
-
if
|
104
|
-
collection =
|
102
|
+
context :paginated_collection, readonly: true do |context, nested_request: false, sorted_collection:, collection:, request:, params:, links:|
|
103
|
+
if !nested_request
|
104
|
+
collection = sorted_collection
|
105
105
|
_, block = pagination_strategies.to_a.reverse.to_h.find do |mod, _|
|
106
|
-
Object.const_defined?(mod, false) &&
|
106
|
+
Object.const_defined?(mod, false) && collection.class <= Object.const_get(mod, false)
|
107
107
|
end
|
108
108
|
|
109
|
-
links_delegate = PaginationLinksDelegate.new(
|
110
|
-
context.request.url,
|
111
|
-
context.params,
|
112
|
-
context.links
|
113
|
-
)
|
109
|
+
links_delegate = PaginationLinksDelegate.new(request.url, params, links)
|
114
110
|
|
115
111
|
instance_exec(
|
116
112
|
collection,
|
117
|
-
|
113
|
+
request.params['page'] || {},
|
118
114
|
links_delegate,
|
119
115
|
per,
|
120
116
|
context,
|
121
117
|
&block
|
122
118
|
)
|
123
119
|
else
|
124
|
-
|
120
|
+
collection
|
125
121
|
end
|
126
122
|
end
|
127
123
|
end
|
@@ -6,47 +6,40 @@ module JSONAPIonify::Api
|
|
6
6
|
extend JSONAPIonify::InheritedAttributes
|
7
7
|
inherited_hash_attribute :param_definitions
|
8
8
|
|
9
|
-
context(:params, readonly: true, persisted: true) do |
|
10
|
-
|
11
|
-
|
12
|
-
params = self.class.param_definitions.select do |_, v|
|
9
|
+
context(:params, readonly: true, persisted: true) do |request:, action_name:|
|
10
|
+
defined_params = self.class.param_definitions.select do |_, v|
|
13
11
|
v.actions.blank? || v.actions.include?(action_name)
|
14
12
|
end
|
13
|
+
param_defaults = defined_params.values.select(&:has_default?).map(&:default)
|
14
|
+
[*param_defaults, request.params].reduce(:deep_merge)
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
# Validate params before request
|
18
|
+
before do |params:, action_name:, request:, nested_request: false|
|
19
|
+
defined_params = self.class.param_definitions.select do |_, v|
|
20
|
+
v.actions.blank? || v.actions.include?(action_name)
|
21
|
+
end
|
19
22
|
|
20
|
-
required_params =
|
23
|
+
required_params = defined_params.select do |_, v|
|
21
24
|
v.required
|
22
25
|
end
|
23
26
|
|
24
27
|
# Check for validity
|
25
|
-
|
28
|
+
params.each do |k, v|
|
26
29
|
keypath = ParamOptions.hash_to_keypaths(k => v)[0]
|
27
30
|
reserved = ParamOptions.reserved?(k)
|
28
|
-
allowed =
|
31
|
+
allowed = defined_params.keys.include? keypath
|
29
32
|
valid = ParamOptions.valid?(k) || v.is_a?(Hash)
|
30
|
-
unless reserved || (allowed && valid) ||
|
31
|
-
should_error = true
|
33
|
+
unless reserved || (allowed && valid) || nested_request
|
32
34
|
error :parameter_invalid, ParamOptions.keypath_to_string(*keypath)
|
33
35
|
end
|
34
|
-
end unless
|
36
|
+
end unless request.options?
|
35
37
|
|
36
38
|
# Check for requirement
|
37
|
-
missing_params =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
)
|
42
|
-
if context.root_request? && missing_params.present?
|
43
|
-
error :parameters_missing, missing_params
|
44
|
-
end
|
45
|
-
|
46
|
-
halt if should_error
|
47
|
-
|
48
|
-
# Return the params
|
49
|
-
context.request.params
|
39
|
+
missing_params = ParamOptions.missing_parameters(
|
40
|
+
request.params, required_params.values.map(&:keypath)
|
41
|
+
)
|
42
|
+
error :parameters_missing, missing_params if !nested_request && missing_params.present?
|
50
43
|
end
|
51
44
|
|
52
45
|
end
|
@@ -8,7 +8,7 @@ module JSONAPIonify::Api
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
def relates_to_many(name, count_attribute: false, count_hidden:
|
11
|
+
def relates_to_many(name, count_attribute: false, count_hidden: :list, **opts, &block)
|
12
12
|
define_relationship(name, Relationship::Many, **opts, &block).tap do
|
13
13
|
define_relationship_counter(
|
14
14
|
name,
|
@@ -23,8 +23,8 @@ module JSONAPIonify::Api
|
|
23
23
|
define_relationship(name, Relationship::One, **opts, &block)
|
24
24
|
end
|
25
25
|
|
26
|
-
def define_relationship_counter(rel_name, name, hidden
|
27
|
-
attribute name.to_sym, types.Integer, "The number of #{rel_name}.", hidden: hidden
|
26
|
+
def define_relationship_counter(rel_name, name, hidden)
|
27
|
+
attribute name.to_sym, types.Integer, "The number of #{rel_name}.", hidden: hidden do |_, instance, context|
|
28
28
|
rel = context.resource.class.relationship(rel_name)
|
29
29
|
rel_context = rel.new(
|
30
30
|
request: context.request,
|
@@ -6,28 +6,26 @@ module JSONAPIonify::Api
|
|
6
6
|
extend JSONAPIonify::InheritedAttributes
|
7
7
|
inherited_hash_attribute :request_header_definitions
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
# Define context
|
10
|
+
context(:request_headers, persisted: true, readonly: true) do |request:|
|
11
|
+
request.headers
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
+
# Validate headers before request
|
15
|
+
before do |request_headers:, action_name:, nested_request: false|
|
16
|
+
# Gather Definitions
|
17
|
+
defined_headers = self.class.request_header_definitions.select do |_, v|
|
14
18
|
v.actions.blank? || v.actions.include?(action_name)
|
15
19
|
end
|
16
|
-
required_headers = headers.select do |_, v|
|
17
|
-
v.required
|
18
|
-
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if context.root_request? && missing_keys.present?
|
24
|
-
should_error = true
|
25
|
-
error :headers_missing, missing_keys
|
21
|
+
# Gather required Headers
|
22
|
+
required_headers = defined_headers.select do |_, v|
|
23
|
+
v.required
|
26
24
|
end
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
# Gather Missing Keys
|
27
|
+
missing_keys = required_headers.keys.map(&:downcase) - request_headers.keys.map(&:downcase)
|
28
|
+
error :headers_missing, missing_keys if !nested_request && missing_keys.present?
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module JSONAPIonify::Api
|
2
2
|
module Resource::Definitions::ResponseHeaders
|
3
|
+
using JSONAPIonify::DestructuredProc
|
3
4
|
|
4
5
|
def self.extended(klass)
|
5
6
|
klass.class_eval do
|
@@ -8,7 +9,7 @@ module JSONAPIonify::Api
|
|
8
9
|
|
9
10
|
context(:response_headers, persisted: true) do |context|
|
10
11
|
self.class.response_header_definitions.each_with_object({}) do |(name, block), headers|
|
11
|
-
headers[name.to_s] = instance_exec(context, &block)
|
12
|
+
headers[name.to_s] = instance_exec(context, &block.destructure)
|
12
13
|
end
|
13
14
|
end
|
14
15
|
end
|
@@ -16,14 +16,25 @@ module JSONAPIonify::Api
|
|
16
16
|
define_singleton_method(:find_instance) do |id|
|
17
17
|
instance_exec(current_scope, id, OpenStruct.new, &block)
|
18
18
|
end
|
19
|
-
context :instance, persisted: true do |context
|
20
|
-
instance_exec(
|
19
|
+
context :instance, persisted: true do |context, scope:, id:|
|
20
|
+
instance_exec(scope, id, context, &block)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def collection(&block)
|
25
|
-
context :collection do |context
|
26
|
-
Object.new.instance_exec(
|
25
|
+
context :collection do |context, scope:, includes:|
|
26
|
+
collection = Object.new.instance_exec(scope, context, &block)
|
27
|
+
|
28
|
+
# Compute includes manipulations
|
29
|
+
self.class.include_definitions.select do |relationship, _|
|
30
|
+
includes.keys.include? relationship.to_s
|
31
|
+
end.reduce(collection) do |lv, (name, include_block)|
|
32
|
+
lv.instance_exec(lv, includes[name.to_s], &include_block)
|
33
|
+
end
|
34
|
+
|
35
|
+
# TODO: Compute field manipulations
|
36
|
+
|
37
|
+
# TODO: Compute param manipulations
|
27
38
|
end
|
28
39
|
end
|
29
40
|
|
@@ -31,8 +42,8 @@ module JSONAPIonify::Api
|
|
31
42
|
define_singleton_method(:build_instance) do
|
32
43
|
Object.new.instance_exec(current_scope, &block)
|
33
44
|
end
|
34
|
-
context :new_instance, persisted: true, readonly: true do |context
|
35
|
-
Object.new.instance_exec(
|
45
|
+
context :new_instance, persisted: true, readonly: true do |context, scope:|
|
46
|
+
Object.new.instance_exec(scope, context, &block)
|
36
47
|
end
|
37
48
|
end
|
38
49
|
end
|
@@ -3,6 +3,7 @@ require 'set'
|
|
3
3
|
module JSONAPIonify::Api
|
4
4
|
module Resource::Definitions::Sorting
|
5
5
|
using JSONAPIonify::DeepSortCollection
|
6
|
+
using JSONAPIonify::DestructuredProc
|
6
7
|
|
7
8
|
def self.extended(klass)
|
8
9
|
klass.class_eval do
|
@@ -10,20 +11,20 @@ module JSONAPIonify::Api
|
|
10
11
|
delegate :sort_fields_from_sort_string, to: :class
|
11
12
|
|
12
13
|
# Define Contexts
|
13
|
-
context :sorted_collection, readonly: true do |context|
|
14
|
-
if
|
14
|
+
context :sorted_collection, readonly: true do |context, collection:, sort_params:, nested_request: false|
|
15
|
+
if !nested_request
|
15
16
|
_, block = sorting_strategies.to_a.reverse.to_h.find do |mod, _|
|
16
|
-
Object.const_defined?(mod, false) &&
|
17
|
+
Object.const_defined?(mod, false) && collection.class <= Object.const_get(mod, false)
|
17
18
|
end
|
18
19
|
context.reset(:sort_params)
|
19
|
-
instance_exec(
|
20
|
+
instance_exec(collection, sort_params, context, &block.destructure)
|
20
21
|
else
|
21
|
-
|
22
|
+
collection
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
25
|
-
context(:sort_params, readonly: true, persisted: true) do |
|
26
|
-
sort_fields_from_sort_string(
|
26
|
+
context(:sort_params, readonly: true, persisted: true) do |params:|
|
27
|
+
sort_fields_from_sort_string(params['sort'])
|
27
28
|
end
|
28
29
|
|
29
30
|
define_sorting_strategy('Object') do |collection|
|
@@ -41,7 +41,7 @@ module JSONAPIonify::Api
|
|
41
41
|
)
|
42
42
|
}
|
43
43
|
if callbacks
|
44
|
-
run_callbacks :exception, exception,
|
44
|
+
run_callbacks :exception, exception, &evaluate
|
45
45
|
else
|
46
46
|
evaluate.call
|
47
47
|
end
|
@@ -95,7 +95,8 @@ module JSONAPIonify::Api
|
|
95
95
|
errors.evaluate(
|
96
96
|
*args,
|
97
97
|
error_block: lookup_error(name),
|
98
|
-
runtime_block: block
|
98
|
+
runtime_block: block,
|
99
|
+
backtrace: caller
|
99
100
|
)
|
100
101
|
end
|
101
102
|
|
@@ -2,26 +2,16 @@ require 'concurrent'
|
|
2
2
|
|
3
3
|
module JSONAPIonify::Api
|
4
4
|
module Resource::Includer
|
5
|
+
include JSONAPIonify::Structure
|
5
6
|
extend ActiveSupport::Concern
|
6
7
|
|
7
8
|
included do
|
8
|
-
before :list, :create, :read, :update do |context|
|
9
|
-
supports_includes = context.root_request? && context.includes.present?
|
10
|
-
is_active_record = defined?(ActiveRecord) && context.scope.is_a?(Class) && context.scope < ActiveRecord::Base
|
11
|
-
if supports_includes && is_active_record
|
12
|
-
valid_includes = context.includes.select do |k, v|
|
13
|
-
context.scope._reflect_on_association(k)
|
14
|
-
end.to_h
|
15
|
-
context.scope = context.scope.includes valid_includes
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
9
|
after :commit_list do |context|
|
20
10
|
if context.includes.present?
|
21
11
|
included =
|
22
|
-
|
12
|
+
context.response_collection.map do |instance|
|
23
13
|
fetch_included(context, owner: instance)
|
24
|
-
end.reduce(
|
14
|
+
end.reduce(:|)
|
25
15
|
append_included(context, included)
|
26
16
|
end
|
27
17
|
end
|
@@ -35,10 +25,6 @@ module JSONAPIonify::Api
|
|
35
25
|
end
|
36
26
|
end
|
37
27
|
|
38
|
-
context :root_request?, readonly: true do
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
28
|
context :includes do |context|
|
43
29
|
Resource::Includer.includes_to_hashes context.params['include']
|
44
30
|
end
|
@@ -46,53 +32,36 @@ module JSONAPIonify::Api
|
|
46
32
|
|
47
33
|
def append_included(context, included)
|
48
34
|
if included.present?
|
49
|
-
context.response_object[:included]
|
50
|
-
context.response_object[:included].tap(&:uniq!).reject! do |r|
|
51
|
-
Array.wrap(context.response_object[:included]).include?(r) ||
|
52
|
-
Array.wrap(context.response_object[:data]).include?(r)
|
53
|
-
end
|
35
|
+
context.response_object[:included] |= included
|
54
36
|
end
|
55
37
|
end
|
56
38
|
|
57
39
|
def fetch_included(context, **overrides)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
overrides = overrides.merge includes:
|
63
|
-
errors:
|
64
|
-
root_request?:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
res.call_action(:list, context.request, **overrides)
|
71
|
-
end
|
72
|
-
collection.concat expand_body(body)
|
40
|
+
context.includes.reduce(Collections::IncludedResources.new) do |lv, (name, _)|
|
41
|
+
if self.class.include_definitions.keys.include?(name.to_sym)
|
42
|
+
response_object = JSONAPIonify.new_object
|
43
|
+
res = self.class.relationship(name)
|
44
|
+
overrides = overrides.merge includes: context.includes[name],
|
45
|
+
errors: context.errors,
|
46
|
+
root_request?: false,
|
47
|
+
action_name: context.action_name,
|
48
|
+
response_object: response_object
|
49
|
+
action_name = res.rel.is_a?(Relationship::One) ? :read : :list
|
50
|
+
res.call_action(action_name, context.request, context_overrides: overrides)
|
51
|
+
lv | (Array.wrap(response_object[:data]).compact + response_object[:included])
|
73
52
|
else
|
74
53
|
error :relationship_not_includable, res.rel.name
|
54
|
+
lv
|
75
55
|
end
|
76
|
-
end
|
56
|
+
end
|
77
57
|
end
|
78
58
|
|
79
59
|
def self.includes_to_hashes(path)
|
80
|
-
path.to_s.split(',').each_with_object({}) do |
|
81
|
-
rel, *sub_path =
|
60
|
+
path.to_s.split(',').each_with_object({}) do |p, obj|
|
61
|
+
rel, *sub_path = p.split('.')
|
82
62
|
obj[rel] = includes_to_hashes(sub_path.join('.'))
|
83
63
|
end
|
84
64
|
end
|
85
65
|
|
86
|
-
def expand_body(body)
|
87
|
-
case body
|
88
|
-
when Rack::BodyProxy
|
89
|
-
json = JSONAPIonify.parse(body.body.join)
|
90
|
-
Array.wrap(json[:data]) + (json[:included] || [])
|
91
|
-
when Array
|
92
|
-
json = JSONAPIonify.parse(body.join)
|
93
|
-
Array.wrap(json[:data]) + (json[:included] || [])
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
66
|
end
|
98
67
|
end
|