forest_admin_agent 1.0.0.pre.beta.27 → 1.0.0.pre.beta.28
Sign up to get free protection for your applications and to get access to all the features.
- 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
|