forest_admin_agent 1.46.1 → 1.46.2
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/action/actions.rb +2 -12
- data/lib/forest_admin_agent/routes/query_handler.rb +32 -7
- data/lib/forest_admin_agent/routes/resources/count.rb +1 -22
- data/lib/forest_admin_agent/routes/resources/csv.rb +1 -21
- data/lib/forest_admin_agent/routes/resources/delete.rb +36 -34
- data/lib/forest_admin_agent/routes/resources/list.rb +2 -22
- data/lib/forest_admin_agent/routes/resources/related/count_related.rb +2 -1
- data/lib/forest_admin_agent/routes/resources/related/csv_related.rb +2 -12
- data/lib/forest_admin_agent/routes/resources/related/dissociate_related.rb +4 -8
- data/lib/forest_admin_agent/routes/resources/related/list_related.rb +4 -14
- data/lib/forest_admin_agent/utils/query_string_parser.rb +1 -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: 66c2c677df8cd9ee2cdb91925ea87a84e7d7b949d5ebf2f57104de3591e1df24
|
|
4
|
+
data.tar.gz: 8a844fc3354a9e522f04038e6f966252370650980035e1ad59763a0a91d74331
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f1c7ffe69debc653eeec2013f3038e61a16119c78d18930851e8a2d45b09c32ebb4378843295c38fb05d2b3d6d779e1a9ab65f76decd08a587c41ee91ef1a46e
|
|
7
|
+
data.tar.gz: 42b9d25705b3695cdeb9858026362f5732df3f54433afec6e17fd351615411cd8fa98bec3129e101ab1df5595e3c87f6d5c78297a3130387f35b7deab356033d
|
|
@@ -11,6 +11,7 @@ module ForestAdminAgent
|
|
|
11
11
|
include ForestAdminDatasourceToolkit::Components::Query
|
|
12
12
|
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
|
13
13
|
include ForestAdminDatasourceCustomizer::Decorators::Action
|
|
14
|
+
include ForestAdminAgent::Routes::QueryHandler
|
|
14
15
|
|
|
15
16
|
def initialize(collection, action)
|
|
16
17
|
@action_name = action
|
|
@@ -223,18 +224,7 @@ module ForestAdminAgent
|
|
|
223
224
|
def get_record_selection(args, context)
|
|
224
225
|
attributes = args.dig(:params, :data, :attributes)
|
|
225
226
|
|
|
226
|
-
|
|
227
|
-
scope = context.permissions.get_scope(context.collection)
|
|
228
|
-
filter = Filter.new(
|
|
229
|
-
condition_tree: ConditionTreeFactory.intersect(
|
|
230
|
-
[
|
|
231
|
-
scope,
|
|
232
|
-
ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
|
|
233
|
-
context.collection, args
|
|
234
|
-
)
|
|
235
|
-
]
|
|
236
|
-
)
|
|
237
|
-
)
|
|
227
|
+
filter = build_context_filter(context.collection, args, context.permissions, context.caller)
|
|
238
228
|
|
|
239
229
|
# Restrict the filter to the selected records for single or bulk actions
|
|
240
230
|
if context.collection.schema[:actions][@action_name].scope != Types::ActionScope::GLOBAL
|
|
@@ -23,23 +23,25 @@ module ForestAdminAgent
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def parse_query_segment(collection, args, permissions, caller)
|
|
26
|
-
|
|
26
|
+
segment_query = QueryStringParser.subset_or_query(args, :segmentQuery)
|
|
27
|
+
return unless segment_query
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
connection_name = QueryStringParser.subset_or_query(args, :connectionName)
|
|
30
|
+
unless connection_name
|
|
29
31
|
raise ForestAdminAgent::Http::Exceptions::UnprocessableError, 'Missing native query connection attribute'
|
|
30
32
|
end
|
|
31
33
|
|
|
32
|
-
QueryValidator.valid?(
|
|
34
|
+
QueryValidator.valid?(segment_query)
|
|
33
35
|
|
|
34
|
-
permissions.can_execute_query_segment?(collection,
|
|
36
|
+
permissions.can_execute_query_segment?(collection, segment_query, connection_name)
|
|
35
37
|
|
|
36
38
|
ids = execute_query(
|
|
37
39
|
collection.datasource,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
segment_query,
|
|
41
|
+
connection_name,
|
|
40
42
|
permissions,
|
|
41
43
|
caller,
|
|
42
|
-
args
|
|
44
|
+
QueryStringParser.subset_or_query(args, :contextVariables)
|
|
43
45
|
).map(&:values)
|
|
44
46
|
|
|
45
47
|
condition_tree_segment = ConditionTree::ConditionTreeFactory.match_ids(collection, ids)
|
|
@@ -47,6 +49,29 @@ module ForestAdminAgent
|
|
|
47
49
|
|
|
48
50
|
condition_tree_segment
|
|
49
51
|
end
|
|
52
|
+
|
|
53
|
+
def build_context_filter(collection, args, permissions, caller, sort: nil)
|
|
54
|
+
condition_tree = QueryStringParser.parse_condition_tree(collection, args)
|
|
55
|
+
search = QueryStringParser.parse_search(collection, args)
|
|
56
|
+
search_extended = QueryStringParser.parse_search_extended(args)
|
|
57
|
+
applied = { condition_tree: condition_tree, search: search, search_extended: search_extended }
|
|
58
|
+
applied[:sort] = sort if sort
|
|
59
|
+
permissions.assert_can_read_query_fields(collection, **applied)
|
|
60
|
+
|
|
61
|
+
Filter.new(
|
|
62
|
+
condition_tree: ConditionTree::ConditionTreeFactory.intersect(
|
|
63
|
+
[
|
|
64
|
+
permissions.get_scope(collection),
|
|
65
|
+
condition_tree,
|
|
66
|
+
parse_query_segment(collection, args, permissions, caller)
|
|
67
|
+
]
|
|
68
|
+
),
|
|
69
|
+
search: search,
|
|
70
|
+
search_extended: search_extended,
|
|
71
|
+
sort: sort,
|
|
72
|
+
segment: QueryStringParser.parse_segment(collection, args)
|
|
73
|
+
)
|
|
74
|
+
end
|
|
50
75
|
end
|
|
51
76
|
end
|
|
52
77
|
end
|
|
@@ -19,28 +19,7 @@ module ForestAdminAgent
|
|
|
19
19
|
context.permissions.can?(:browse, context.collection)
|
|
20
20
|
|
|
21
21
|
if context.collection.is_countable?
|
|
22
|
-
|
|
23
|
-
context.collection, args
|
|
24
|
-
)
|
|
25
|
-
search = QueryStringParser.parse_search(context.collection, args)
|
|
26
|
-
search_extended = QueryStringParser.parse_search_extended(args)
|
|
27
|
-
context.permissions.assert_can_read_query_fields(
|
|
28
|
-
context.collection,
|
|
29
|
-
condition_tree: condition_tree, search: search, search_extended: search_extended
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
33
|
-
condition_tree: ConditionTreeFactory.intersect(
|
|
34
|
-
[
|
|
35
|
-
context.permissions.get_scope(context.collection),
|
|
36
|
-
parse_query_segment(context.collection, args, context.permissions, context.caller),
|
|
37
|
-
condition_tree
|
|
38
|
-
]
|
|
39
|
-
),
|
|
40
|
-
search: search,
|
|
41
|
-
search_extended: search_extended,
|
|
42
|
-
segment: QueryStringParser.parse_segment(context.collection, args)
|
|
43
|
-
)
|
|
22
|
+
filter = build_context_filter(context.collection, args, context.permissions, context.caller)
|
|
44
23
|
aggregation = ForestAdminDatasourceToolkit::Components::Query::Aggregation.new(operation: 'Count')
|
|
45
24
|
result = context.collection.aggregate(context.caller, filter, aggregation)
|
|
46
25
|
|
|
@@ -23,28 +23,8 @@ module ForestAdminAgent
|
|
|
23
23
|
context.permissions.can?(:browse, context.collection)
|
|
24
24
|
context.permissions.can?(:export, context.collection)
|
|
25
25
|
|
|
26
|
-
condition_tree = QueryStringParser.parse_condition_tree(context.collection, args)
|
|
27
|
-
search = QueryStringParser.parse_search(context.collection, args)
|
|
28
|
-
search_extended = QueryStringParser.parse_search_extended(args)
|
|
29
26
|
sort = QueryStringParser.parse_sort(context.collection, args)
|
|
30
|
-
context.permissions.
|
|
31
|
-
context.collection,
|
|
32
|
-
condition_tree: condition_tree, sort: sort, search: search, search_extended: search_extended
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
36
|
-
condition_tree: ConditionTreeFactory.intersect(
|
|
37
|
-
[
|
|
38
|
-
context.permissions.get_scope(context.collection),
|
|
39
|
-
parse_query_segment(context.collection, args, context.permissions, context.caller),
|
|
40
|
-
condition_tree
|
|
41
|
-
]
|
|
42
|
-
),
|
|
43
|
-
search: search,
|
|
44
|
-
search_extended: search_extended,
|
|
45
|
-
sort: sort,
|
|
46
|
-
segment: QueryStringParser.parse_segment(context.collection, args)
|
|
47
|
-
)
|
|
27
|
+
filter = build_context_filter(context.collection, args, context.permissions, context.caller, sort: sort)
|
|
48
28
|
requested = QueryStringParser.parse_requested_projection(context.collection, args)
|
|
49
29
|
projection = context.permissions.redact_projection(
|
|
50
30
|
context.collection,
|
|
@@ -8,6 +8,9 @@ module ForestAdminAgent
|
|
|
8
8
|
include ForestAdminAgent::Builder
|
|
9
9
|
include ForestAdminDatasourceToolkit::Components::Query
|
|
10
10
|
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
|
11
|
+
include ForestAdminAgent::Routes::QueryHandler
|
|
12
|
+
|
|
13
|
+
NULLIFIED_POLYMORPHIC_TYPES = %w[PolymorphicOneToOne PolymorphicOneToMany].freeze
|
|
11
14
|
|
|
12
15
|
def setup_routes
|
|
13
16
|
add_route('forest_delete_bulk', 'delete', '/:collection_name', ->(args) { handle_request_bulk(args) })
|
|
@@ -39,45 +42,44 @@ module ForestAdminAgent
|
|
|
39
42
|
selection_ids[:ids])
|
|
40
43
|
condition_tree_ids = condition_tree_ids.inverse if selection_ids[:are_excluded]
|
|
41
44
|
|
|
42
|
-
context.collection.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
filter = build_context_filter(context.collection, args, context.permissions, context.caller)
|
|
46
|
+
filter = filter.override(
|
|
47
|
+
condition_tree: ConditionTree::ConditionTreeFactory.intersect([filter.condition_tree, condition_tree_ids])
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
relations = nullified_polymorphic_relations(context.collection)
|
|
51
|
+
# ponytail: loads every deleted record's keys at once, page it if select-all deletes outgrow memory
|
|
52
|
+
records = if relations.any?
|
|
53
|
+
context.collection.list(context.caller, filter,
|
|
54
|
+
Projection.new(relations.map(&:origin_key_target).uniq))
|
|
55
|
+
end
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
else
|
|
50
|
-
pk_names = ForestAdminDatasourceToolkit::Utils::Schema.primary_keys(context.collection)
|
|
51
|
-
index = pk_names.index(field_schema.origin_key_target)
|
|
52
|
-
pk_hash[index] if index
|
|
53
|
-
end
|
|
54
|
-
end
|
|
57
|
+
context.collection.delete(context.caller, filter)
|
|
58
|
+
relations.each { |relation| nullify_polymorphic_references(context, relation, records) }
|
|
59
|
+
end
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Nodes::ConditionTreeLeaf.new(field_schema.origin_type_field, Operators::EQUAL,
|
|
61
|
-
context.collection.name.gsub('__', '::'))
|
|
62
|
-
]
|
|
63
|
-
)
|
|
64
|
-
filter = Filter.new(condition_tree: condition_tree)
|
|
65
|
-
context.datasource.get_collection(field_schema.foreign_collection)
|
|
66
|
-
.update(context.caller, filter, { field_schema.origin_key => nil,
|
|
67
|
-
field_schema.origin_type_field => nil })
|
|
61
|
+
def nullified_polymorphic_relations(collection)
|
|
62
|
+
collection.schema[:fields].values.select do |field_schema|
|
|
63
|
+
NULLIFIED_POLYMORPHIC_TYPES.include?(field_schema.type) &&
|
|
64
|
+
!(field_schema.respond_to?(:cascade_on_delete) && field_schema.cascade_on_delete)
|
|
68
65
|
end
|
|
66
|
+
end
|
|
69
67
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Utils::QueryStringParser.parse_condition_tree(context.collection, args),
|
|
74
|
-
condition_tree_ids,
|
|
75
|
-
context.permissions.get_scope(context.collection)
|
|
76
|
-
]
|
|
77
|
-
)
|
|
78
|
-
)
|
|
68
|
+
def nullify_polymorphic_references(context, relation, records)
|
|
69
|
+
origin_values = records.map { |record| record[relation.origin_key_target] }
|
|
70
|
+
return if origin_values.empty?
|
|
79
71
|
|
|
80
|
-
|
|
72
|
+
condition_tree = Nodes::ConditionTreeBranch.new(
|
|
73
|
+
'And',
|
|
74
|
+
[
|
|
75
|
+
Nodes::ConditionTreeLeaf.new(relation.origin_key, Operators::IN, origin_values),
|
|
76
|
+
Nodes::ConditionTreeLeaf.new(relation.origin_type_field, Operators::EQUAL,
|
|
77
|
+
context.collection.name.gsub('__', '::'))
|
|
78
|
+
]
|
|
79
|
+
)
|
|
80
|
+
context.datasource.get_collection(relation.foreign_collection)
|
|
81
|
+
.update(context.caller, Filter.new(condition_tree: condition_tree),
|
|
82
|
+
{ relation.origin_key => nil, relation.origin_type_field => nil })
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
end
|
|
@@ -18,29 +18,9 @@ module ForestAdminAgent
|
|
|
18
18
|
context = build(args)
|
|
19
19
|
context.permissions.can?(:browse, context.collection)
|
|
20
20
|
|
|
21
|
-
condition_tree = QueryStringParser.parse_condition_tree(context.collection, args)
|
|
22
|
-
search = QueryStringParser.parse_search(context.collection, args)
|
|
23
|
-
search_extended = QueryStringParser.parse_search_extended(args)
|
|
24
21
|
sort = QueryStringParser.parse_sort(context.collection, args)
|
|
25
|
-
context.permissions.
|
|
26
|
-
|
|
27
|
-
condition_tree: condition_tree, sort: sort, search: search, search_extended: search_extended
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
31
|
-
condition_tree: ConditionTreeFactory.intersect(
|
|
32
|
-
[
|
|
33
|
-
context.permissions.get_scope(context.collection),
|
|
34
|
-
condition_tree,
|
|
35
|
-
parse_query_segment(context.collection, args, context.permissions, context.caller)
|
|
36
|
-
]
|
|
37
|
-
),
|
|
38
|
-
page: QueryStringParser.parse_pagination(args),
|
|
39
|
-
search: search,
|
|
40
|
-
search_extended: search_extended,
|
|
41
|
-
sort: sort,
|
|
42
|
-
segment: QueryStringParser.parse_segment(context.collection, args)
|
|
43
|
-
)
|
|
22
|
+
filter = build_context_filter(context.collection, args, context.permissions, context.caller, sort: sort)
|
|
23
|
+
.override(page: QueryStringParser.parse_pagination(args))
|
|
44
24
|
|
|
45
25
|
projection = redacted_projection_with_pks(context, context.collection, args)
|
|
46
26
|
records = context.collection.list(context.caller, filter, projection)
|
|
@@ -8,6 +8,7 @@ module ForestAdminAgent
|
|
|
8
8
|
include ForestAdminAgent::Builder
|
|
9
9
|
include ForestAdminDatasourceToolkit::Utils
|
|
10
10
|
include ForestAdminDatasourceToolkit::Components::Query
|
|
11
|
+
include ForestAdminAgent::Routes::QueryHandler
|
|
11
12
|
|
|
12
13
|
def setup_routes
|
|
13
14
|
add_route(
|
|
@@ -25,7 +26,7 @@ module ForestAdminAgent
|
|
|
25
26
|
context.permissions.can?(:browse, context.child_collection)
|
|
26
27
|
|
|
27
28
|
if context.child_collection.is_countable?
|
|
28
|
-
filter =
|
|
29
|
+
filter = build_context_filter(context.child_collection, args, context.permissions, context.caller)
|
|
29
30
|
primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
30
31
|
result = Collection.aggregate_relation(
|
|
31
32
|
context.collection,
|
|
@@ -6,6 +6,7 @@ module ForestAdminAgent
|
|
|
6
6
|
include ForestAdminAgent::Utils
|
|
7
7
|
include ForestAdminDatasourceToolkit::Utils
|
|
8
8
|
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
|
9
|
+
include ForestAdminAgent::Routes::QueryHandler
|
|
9
10
|
|
|
10
11
|
def setup_routes
|
|
11
12
|
add_route(
|
|
@@ -23,18 +24,7 @@ module ForestAdminAgent
|
|
|
23
24
|
context = build(args)
|
|
24
25
|
context.permissions.can?(:browse, context.child_collection)
|
|
25
26
|
context.permissions.can?(:export, context.child_collection)
|
|
26
|
-
|
|
27
|
-
context.child_collection, args
|
|
28
|
-
)
|
|
29
|
-
context.permissions.assert_can_read_query_fields(
|
|
30
|
-
context.child_collection, condition_tree: condition_tree
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
34
|
-
condition_tree: ConditionTreeFactory.intersect(
|
|
35
|
-
[context.permissions.get_scope(context.child_collection), condition_tree]
|
|
36
|
-
)
|
|
37
|
-
)
|
|
27
|
+
filter = build_context_filter(context.child_collection, args, context.permissions, context.caller)
|
|
38
28
|
requested = ForestAdminAgent::Utils::QueryStringParser.parse_requested_projection(
|
|
39
29
|
context.child_collection, args
|
|
40
30
|
)
|
|
@@ -8,6 +8,7 @@ module ForestAdminAgent
|
|
|
8
8
|
include ForestAdminAgent::Builder
|
|
9
9
|
include ForestAdminDatasourceToolkit::Utils
|
|
10
10
|
include ForestAdminDatasourceToolkit::Components::Query
|
|
11
|
+
include ForestAdminAgent::Routes::QueryHandler
|
|
11
12
|
|
|
12
13
|
def setup_routes
|
|
13
14
|
add_route(
|
|
@@ -126,14 +127,9 @@ module ForestAdminAgent
|
|
|
126
127
|
raise ForestAdminDatasourceToolkit::Exceptions::ForestException, 'Expected no empty id list'
|
|
127
128
|
end
|
|
128
129
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
context.permissions.get_scope(context.child_collection),
|
|
133
|
-
Utils::QueryStringParser.parse_condition_tree(context.child_collection, args),
|
|
134
|
-
selected_ids
|
|
135
|
-
]
|
|
136
|
-
)
|
|
130
|
+
filter = build_context_filter(context.child_collection, args, context.permissions, context.caller)
|
|
131
|
+
filter.override(
|
|
132
|
+
condition_tree: ConditionTree::ConditionTreeFactory.intersect([filter.condition_tree, selected_ids])
|
|
137
133
|
)
|
|
138
134
|
end
|
|
139
135
|
end
|
|
@@ -8,6 +8,7 @@ module ForestAdminAgent
|
|
|
8
8
|
include ForestAdminAgent::Builder
|
|
9
9
|
include ForestAdminDatasourceToolkit::Utils
|
|
10
10
|
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
|
|
11
|
+
include ForestAdminAgent::Routes::QueryHandler
|
|
11
12
|
|
|
12
13
|
def setup_routes
|
|
13
14
|
add_route(
|
|
@@ -23,21 +24,10 @@ module ForestAdminAgent
|
|
|
23
24
|
def handle_request(args = {})
|
|
24
25
|
context = build(args)
|
|
25
26
|
context.permissions.can?(:browse, context.child_collection)
|
|
26
|
-
condition_tree = ForestAdminAgent::Utils::QueryStringParser.parse_condition_tree(
|
|
27
|
-
context.child_collection, args
|
|
28
|
-
)
|
|
29
27
|
sort = ForestAdminAgent::Utils::QueryStringParser.parse_sort(context.child_collection, args)
|
|
30
|
-
context.permissions.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
filter = ForestAdminDatasourceToolkit::Components::Query::Filter.new(
|
|
35
|
-
condition_tree: ConditionTreeFactory.intersect(
|
|
36
|
-
[context.permissions.get_scope(context.child_collection), condition_tree]
|
|
37
|
-
),
|
|
38
|
-
page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args),
|
|
39
|
-
sort: sort
|
|
40
|
-
)
|
|
28
|
+
filter = build_context_filter(context.child_collection, args, context.permissions, context.caller,
|
|
29
|
+
sort: sort)
|
|
30
|
+
.override(page: ForestAdminAgent::Utils::QueryStringParser.parse_pagination(args))
|
|
41
31
|
projection = redacted_projection_with_pks(context, context.child_collection, args)
|
|
42
32
|
primary_key_values = Utils::Id.unpack_id(context.collection, args[:params]['id'], with_key: true)
|
|
43
33
|
records = Collection.list_relation(
|
|
@@ -318,8 +318,7 @@ module ForestAdminAgent
|
|
|
318
318
|
end
|
|
319
319
|
|
|
320
320
|
def self.parse_segment(collection, args)
|
|
321
|
-
segment = args.dig(:params, :
|
|
322
|
-
:segment) || args.dig(:params, :segment)
|
|
321
|
+
segment = subset_or_query(args, :segment) || args.dig(:params, :segment)
|
|
323
322
|
|
|
324
323
|
return unless segment
|
|
325
324
|
|
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.46.
|
|
4
|
+
version: 1.46.2
|
|
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: 2026-10-
|
|
12
|
+
date: 2026-10-20 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activerecord
|