forest_admin_agent 1.0.0.pre.beta.27 → 1.0.0.pre.beta.28
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 +4 -4
- data/lib/forest_admin_agent/routes/resources/list.rb +1 -0
- data/lib/forest_admin_agent/routes/resources/related/list_related.rb +1 -0
- data/lib/forest_admin_agent/routes/resources/show.rb +1 -0
- data/lib/forest_admin_agent/routes/resources/store.rb +1 -0
- data/lib/forest_admin_agent/routes/resources/update.rb +1 -0
- data/lib/forest_admin_agent/serializer/forest_serializer.rb +23 -11
- data/lib/forest_admin_agent/serializer/forest_serializer_override.rb +122 -2
- data/lib/forest_admin_agent/utils/schema/schema_emitter.rb +1 -1
- data/lib/forest_admin_agent/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2ae114be4b54df0354aca036cdc18ca08ab1c78b72d6975b3002179cea93a18
|
4
|
+
data.tar.gz: fd369183191175beb32c586bd98f2c67ccd21a7438e5b41741c0a5f0667985ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2465a1fb919eccdd5daef0183cf0bc371d312b6fa8462dfcbb6096e587376efad479184269b32617143e53d321c0208b361d99af66eabbeea299c6050dd87e3c
|
7
|
+
data.tar.gz: e8ee04c59cbc81fa1b38478e40507d94434b68e2be5009a7b6403731ac117793b9aa462c567704125a7a96264eee46123c7c915979f91ec20042fea2620dc361
|
@@ -34,6 +34,7 @@ module ForestAdminAgent
|
|
34
34
|
name: args[:params]['collection_name'],
|
35
35
|
content: JSONAPI::Serializer.serialize(
|
36
36
|
records,
|
37
|
+
class_name: @collection.name,
|
37
38
|
is_collection: true,
|
38
39
|
serializer: Serializer::ForestSerializer,
|
39
40
|
include: projection.relations.keys,
|
@@ -34,6 +34,7 @@ module ForestAdminAgent
|
|
34
34
|
name: args[:params]['collection_name'],
|
35
35
|
content: JSONAPI::Serializer.serialize(
|
36
36
|
records[0],
|
37
|
+
class_name: @collection.name,
|
37
38
|
is_collection: false,
|
38
39
|
serializer: Serializer::ForestSerializer,
|
39
40
|
include: projection.relations.keys
|
@@ -20,8 +20,17 @@ module ForestAdminAgent
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def type
|
23
|
-
class_name =
|
24
|
-
@@class_names[class_name] ||= class_name
|
23
|
+
class_name = @options[:class_name]
|
24
|
+
@@class_names[class_name] ||= class_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def id
|
28
|
+
forest_collection = ForestAdminAgent::Facades::Container.datasource.get_collection(@options[:class_name])
|
29
|
+
primary_keys = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(forest_collection)
|
30
|
+
id = []
|
31
|
+
primary_keys.each { |key| id << @object[key] }
|
32
|
+
|
33
|
+
id.join('|')
|
25
34
|
end
|
26
35
|
|
27
36
|
def format_name(attribute_name)
|
@@ -41,7 +50,7 @@ module ForestAdminAgent
|
|
41
50
|
end
|
42
51
|
|
43
52
|
def attributes
|
44
|
-
forest_collection = ForestAdminAgent::Facades::Container.datasource.get_collection(
|
53
|
+
forest_collection = ForestAdminAgent::Facades::Container.datasource.get_collection(@options[:class_name])
|
45
54
|
fields = forest_collection.schema[:fields].select { |_field_name, field| field.type == 'Column' }
|
46
55
|
fields.each { |field_name, _field| add_attribute(field_name) }
|
47
56
|
return {} if attributes_map.nil?
|
@@ -61,11 +70,7 @@ module ForestAdminAgent
|
|
61
70
|
instance_eval(&attr_or_block)
|
62
71
|
else
|
63
72
|
# Default behavior, call a method by the name of the attribute.
|
64
|
-
|
65
|
-
object.try(attr_or_block)
|
66
|
-
rescue
|
67
|
-
nil
|
68
|
-
end
|
73
|
+
object[attr_or_block]
|
69
74
|
end
|
70
75
|
end
|
71
76
|
|
@@ -104,7 +109,8 @@ module ForestAdminAgent
|
|
104
109
|
end
|
105
110
|
|
106
111
|
def relationships
|
107
|
-
|
112
|
+
datasource = ForestAdminAgent::Facades::Container.datasource
|
113
|
+
forest_collection = datasource.get_collection(@options[:class_name])
|
108
114
|
relations_to_many = forest_collection.schema[:fields].select { |_field_name, field| field.type == 'OneToMany' || field.type == 'ManyToMany' }
|
109
115
|
relations_to_one = forest_collection.schema[:fields].select { |_field_name, field| field.type == 'OneToOne' || field.type == 'ManyToOne' }
|
110
116
|
|
@@ -124,10 +130,13 @@ module ForestAdminAgent
|
|
124
130
|
end
|
125
131
|
|
126
132
|
object = has_one_relationship(attribute_name, attr_data)
|
127
|
-
if object.nil?
|
133
|
+
if object.nil? || object.empty?
|
128
134
|
data[formatted_attribute_name]['data'] = nil
|
129
135
|
else
|
130
|
-
|
136
|
+
relation = datasource.get_collection(@options[:class_name]).schema[:fields][attribute_name.to_s]
|
137
|
+
options = @options.clone
|
138
|
+
options[:class_name] = datasource.get_collection(relation.foreign_collection).name
|
139
|
+
related_object_serializer = ForestSerializer.new(object, options)
|
131
140
|
data[formatted_attribute_name]['data'] = {
|
132
141
|
'type' => related_object_serializer.type.to_s,
|
133
142
|
'id' => related_object_serializer.id.to_s,
|
@@ -152,6 +161,9 @@ module ForestAdminAgent
|
|
152
161
|
if @_include_linkages.include?(formatted_attribute_name) || attr_data[:options][:include_data]
|
153
162
|
data[formatted_attribute_name]['data'] = []
|
154
163
|
objects = has_many_relationship(attribute_name, attr_data) || []
|
164
|
+
relation = datasource.get_collection(@options[:class_name]).schema[:fields][attribute_name.to_s]
|
165
|
+
options = @options.clone
|
166
|
+
options[:class_name] = datasource.get_collection(relation.foreign_collection).name
|
155
167
|
objects.each do |obj|
|
156
168
|
related_object_serializer = JSONAPI::Serializer.find_serializer(obj, @options)
|
157
169
|
data[formatted_attribute_name]['data'] << {
|
@@ -48,7 +48,7 @@ module ForestAdminAgent
|
|
48
48
|
end
|
49
49
|
|
50
50
|
# We're finding relationships for compound documents, so skip anything that doesn't exist.
|
51
|
-
next if object.nil?
|
51
|
+
next if object.nil? || object.empty?
|
52
52
|
|
53
53
|
# Full linkage: a request for comments.author MUST automatically include comments
|
54
54
|
# in the response.
|
@@ -58,6 +58,8 @@ module ForestAdminAgent
|
|
58
58
|
# If it is not set, that indicates that this is an inner path and not a leaf and will
|
59
59
|
# be followed by the recursion below.
|
60
60
|
objects.each do |obj|
|
61
|
+
relation = ForestAdminAgent::Facades::Container.datasource.get_collection(options[:class_name]).schema[:fields][attribute_name]
|
62
|
+
relation_class_name = ForestAdminAgent::Facades::Container.datasource.get_collection(relation.foreign_collection).name
|
61
63
|
obj_serializer = JSONAPI::Serializer.find_serializer(obj, options)
|
62
64
|
# Use keys of ['posts', '1'] for the results to enforce uniqueness.
|
63
65
|
# Spec: A compound document MUST NOT include more than one resource object for each
|
@@ -82,7 +84,8 @@ module ForestAdminAgent
|
|
82
84
|
# so merge the include_linkages each time we see it to load all the relevant linkages.
|
83
85
|
current_child_includes += (results[key] && results[key][:include_linkages]) || []
|
84
86
|
current_child_includes.uniq!
|
85
|
-
|
87
|
+
|
88
|
+
results[key] = { object: obj, include_linkages: current_child_includes, class_name: relation_class_name }
|
86
89
|
end
|
87
90
|
end
|
88
91
|
|
@@ -96,6 +99,123 @@ module ForestAdminAgent
|
|
96
99
|
end
|
97
100
|
nil
|
98
101
|
end
|
102
|
+
|
103
|
+
def self.serialize(objects, options = {})
|
104
|
+
# Normalize option strings to symbols.
|
105
|
+
options[:is_collection] = options.delete('is_collection') || options[:is_collection] || false
|
106
|
+
options[:include] = options.delete('include') || options[:include]
|
107
|
+
options[:serializer] = options.delete('serializer') || options[:serializer]
|
108
|
+
options[:namespace] = options.delete('namespace') || options[:namespace]
|
109
|
+
options[:context] = options.delete('context') || options[:context] || {}
|
110
|
+
options[:skip_collection_check] = options.delete('skip_collection_check') || options[:skip_collection_check] || false
|
111
|
+
options[:base_url] = options.delete('base_url') || options[:base_url]
|
112
|
+
options[:jsonapi] = options.delete('jsonapi') || options[:jsonapi]
|
113
|
+
options[:meta] = options.delete('meta') || options[:meta]
|
114
|
+
options[:links] = options.delete('links') || options[:links]
|
115
|
+
options[:fields] = options.delete('fields') || options[:fields] || {}
|
116
|
+
|
117
|
+
# Deprecated: use serialize_errors method instead
|
118
|
+
options[:errors] = options.delete('errors') || options[:errors]
|
119
|
+
|
120
|
+
# Normalize includes.
|
121
|
+
includes = options[:include]
|
122
|
+
includes = (includes.is_a?(String) ? includes.split(',') : includes).uniq if includes
|
123
|
+
|
124
|
+
# Transforms input so that the comma-separated fields are separate symbols in array
|
125
|
+
# and keys are stringified
|
126
|
+
# Example:
|
127
|
+
# {posts: 'title,author,long_comments'} => {'posts' => [:title, :author, :long_comments]}
|
128
|
+
# {posts: ['title', 'author', 'long_comments'} => {'posts' => [:title, :author, :long_comments]}
|
129
|
+
#
|
130
|
+
fields = {}
|
131
|
+
# Normalize fields to accept a comma-separated string or an array of strings.
|
132
|
+
options[:fields].map do |type, whitelisted_fields|
|
133
|
+
whitelisted_fields = [whitelisted_fields] if whitelisted_fields.is_a?(Symbol)
|
134
|
+
whitelisted_fields = whitelisted_fields.split(',') if whitelisted_fields.is_a?(String)
|
135
|
+
fields[type.to_s] = whitelisted_fields.map(&:to_sym)
|
136
|
+
end
|
137
|
+
|
138
|
+
# An internal-only structure that is passed through serializers as they are created.
|
139
|
+
passthrough_options = {
|
140
|
+
context: options[:context],
|
141
|
+
serializer: options[:serializer],
|
142
|
+
namespace: options[:namespace],
|
143
|
+
include: includes,
|
144
|
+
fields: fields,
|
145
|
+
base_url: options[:base_url],
|
146
|
+
class_name: options[:class_name]
|
147
|
+
}
|
148
|
+
|
149
|
+
if !options[:skip_collection_check] && options[:is_collection] && !objects.respond_to?(:each)
|
150
|
+
raise JSONAPI::Serializer::AmbiguousCollectionError.new(
|
151
|
+
'Attempted to serialize a single object as a collection.')
|
152
|
+
end
|
153
|
+
|
154
|
+
# Automatically include linkage data for any relation that is also included.
|
155
|
+
if includes
|
156
|
+
include_linkages = includes.map { |key| key.to_s.split('.').first }
|
157
|
+
passthrough_options[:include_linkages] = include_linkages
|
158
|
+
end
|
159
|
+
|
160
|
+
# Spec: Primary data MUST be either:
|
161
|
+
# - a single resource object or null, for requests that target single resources.
|
162
|
+
# - an array of resource objects or an empty array ([]), for resource collections.
|
163
|
+
# http://jsonapi.org/format/#document-structure-top-level
|
164
|
+
if options[:is_collection] && !objects.any?
|
165
|
+
primary_data = []
|
166
|
+
elsif !options[:is_collection] && objects.nil?
|
167
|
+
primary_data = nil
|
168
|
+
elsif options[:is_collection]
|
169
|
+
# Have object collection.
|
170
|
+
primary_data = serialize_primary_multi(objects, passthrough_options)
|
171
|
+
else
|
172
|
+
# Duck-typing check for a collection being passed without is_collection true.
|
173
|
+
# We always must be told if serializing a collection because the JSON:API spec distinguishes
|
174
|
+
# how to serialize null single resources vs. empty collections.
|
175
|
+
if !options[:skip_collection_check] && objects.is_a?(Array)
|
176
|
+
raise JSONAPI::Serializer::AmbiguousCollectionError.new(
|
177
|
+
'Must provide `is_collection: true` to `serialize` when serializing collections.')
|
178
|
+
end
|
179
|
+
# Have single object.
|
180
|
+
primary_data = serialize_primary(objects, passthrough_options)
|
181
|
+
end
|
182
|
+
result = {
|
183
|
+
'data' => primary_data,
|
184
|
+
}
|
185
|
+
result['jsonapi'] = options[:jsonapi] if options[:jsonapi]
|
186
|
+
result['meta'] = options[:meta] if options[:meta]
|
187
|
+
result['links'] = options[:links] if options[:links]
|
188
|
+
result['errors'] = options[:errors] if options[:errors]
|
189
|
+
|
190
|
+
# If 'include' relationships are given, recursively find and include each object.
|
191
|
+
if includes
|
192
|
+
relationship_data = {}
|
193
|
+
inclusion_tree = parse_relationship_paths(includes)
|
194
|
+
|
195
|
+
# Given all the primary objects (either the single root object or collection of objects),
|
196
|
+
# recursively search and find related associations that were specified as includes.
|
197
|
+
objects = options[:is_collection] ? objects.to_a : [objects]
|
198
|
+
objects.compact.each do |obj|
|
199
|
+
# Use the mutability of relationship_data as the return datastructure to take advantage
|
200
|
+
# of the internal special merging logic.
|
201
|
+
find_recursive_relationships(obj, inclusion_tree, relationship_data, passthrough_options)
|
202
|
+
end
|
203
|
+
|
204
|
+
result['included'] = relationship_data.map do |_, data|
|
205
|
+
included_passthrough_options = {}
|
206
|
+
included_passthrough_options[:base_url] = passthrough_options[:base_url]
|
207
|
+
included_passthrough_options[:context] = passthrough_options[:context]
|
208
|
+
included_passthrough_options[:fields] = passthrough_options[:fields]
|
209
|
+
included_passthrough_options[:serializer] = find_serializer_class(data[:object], options)
|
210
|
+
included_passthrough_options[:namespace] = passthrough_options[:namespace]
|
211
|
+
included_passthrough_options[:include_linkages] = data[:include_linkages]
|
212
|
+
included_passthrough_options[:class_name] = data[:class_name]
|
213
|
+
|
214
|
+
serialize_primary(data[:object], included_passthrough_options)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
result
|
218
|
+
end
|
99
219
|
end
|
100
220
|
end
|
101
221
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: forest_admin_agent
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.beta.
|
4
|
+
version: 1.0.0.pre.beta.28
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthieu
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-01-
|
12
|
+
date: 2024-01-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|