eco-helpers 2.5.3 → 2.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -2
- data/eco-helpers.gemspec +1 -1
- data/lib/eco/api/common/people/person_entry.rb +0 -1
- data/lib/eco/api/organization/tag_tree.rb +38 -9
- data/lib/eco/api/session/config/tagtree.rb +38 -14
- data/lib/eco/api/session/config.rb +8 -2
- data/lib/eco/api/usecases/default_cases/reinvite_trans_case.rb +2 -2
- data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +15 -0
- data/lib/eco/api/usecases/graphql/helpers/base.rb +3 -11
- data/lib/eco/api/usecases/graphql/helpers/location/base.rb +8 -2
- data/lib/eco/api/usecases/graphql/helpers/location/command.rb +14 -6
- data/lib/eco/api/usecases/graphql/utils/sftp.rb +74 -0
- data/lib/eco/api/usecases/graphql/utils.rb +6 -0
- data/lib/eco/api/usecases/graphql.rb +1 -0
- data/lib/eco/data/files/helpers.rb +3 -3
- data/lib/eco/data/hashes/array_diff.rb +11 -57
- data/lib/eco/data/hashes/diff_meta.rb +52 -0
- data/lib/eco/data/hashes/diff_result.rb +37 -25
- data/lib/eco/data/hashes.rb +1 -0
- data/lib/eco/data/locations/node_diff/accessors.rb +46 -0
- data/lib/eco/data/locations/node_diff/nodes_diff.rb +90 -0
- data/lib/eco/data/locations/node_diff/selectors.rb +20 -0
- data/lib/eco/data/locations/node_diff.rb +55 -0
- data/lib/eco/data/locations/node_level.rb +1 -4
- data/lib/eco/data/locations/node_plain.rb +2 -5
- data/lib/eco/data/locations.rb +1 -0
- data/lib/eco/version.rb +1 -1
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d241b7bc57bd81e9b3a6b1d71ecab5f7530b7b6df176301adc21584722f2b247
|
4
|
+
data.tar.gz: a8beafa1e6cd1e3425291121c55b78fe144f155021ab149148fcc6ce35569984
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2bad589998061077bac0f6adb500fdbb9402b77578da68bf43a84f83101a2bf862213acb00707a363f5583c47ec94a876c367360c562043865e573232b272c4c
|
7
|
+
data.tar.gz: 7c33f358f5cbb7ab0b3118d7b0b20e86a9f686f2e1f49216e23bea5ab504ca4bb0bbb2eeab8bb37daf0d11b997af0160212ff7ab2d1fe382fa05a6b354529f48
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,37 @@
|
|
1
1
|
# Change Log
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
-
## [2.5.
|
4
|
+
## [2.5.6] - 2023-08-xx
|
5
|
+
|
6
|
+
### Added
|
7
|
+
### Changed
|
8
|
+
### Fixed
|
9
|
+
|
10
|
+
## [2.5.5] - 2023-08-03
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- `Eco::API::Organization::TagTree` - **added/improved methods**
|
14
|
+
- `#as_json` new parameter `max_depth:` to be able to cut the tree
|
15
|
+
- `#active_tree` new method to exclude archived nodes.
|
16
|
+
- `#truncate` new method to obtain a tree cut to `max_depth:`
|
17
|
+
- `Eco::API::Session::Config::Tagtree`: **exposed** `include_archived:` (a.k.a. `inludeArchivedNodes`).
|
18
|
+
- This change states that `session.tagtree` does not retrieve archived nodes by default, while `session.live_tree` does retrieve archived nodes.
|
19
|
+
- This change required an update on the `ecoportal-api-graphql` **gem**
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- `Eco::API::Organization::TagTree#path` - dups the result (rather than exposing the internal path array)
|
23
|
+
|
24
|
+
## [2.5.4] - 2023-07-27
|
25
|
+
|
26
|
+
### Added
|
27
|
+
- SFTP case helpers: `Eco::API::UseCases::GraphQL::Utils::Sftp`
|
28
|
+
- `Eco::Data::Locations::NodeDiff` and `Eco::Data::Locations::NodeDiff::NodeDiffs`
|
29
|
+
- Aim to identify changes in the locations structure
|
30
|
+
|
31
|
+
### Changed
|
32
|
+
- Some internal tidy up in `Eco::API::UseCases::GraphQL`
|
33
|
+
|
34
|
+
## [2.5.3] - 2023-07-19
|
5
35
|
|
6
36
|
### Added
|
7
37
|
- _GraphQL base case_ for **locations structure update**.
|
@@ -52,7 +82,6 @@ All notable changes to this project will be documented in this file.
|
|
52
82
|
- `Eco::API::Session::Config::Workflow#exit_handle`
|
53
83
|
- Allows to define a callback on `SystemExit` (`exit` call).
|
54
84
|
|
55
|
-
### Changed
|
56
85
|
### Fixed
|
57
86
|
- `Eco::API::Session::Config::Workflow` on `SystemExit` preserve original exit `status` value (i.e. `0`, `1`)
|
58
87
|
- It was changing an `exit 1` to be an `exit 0`
|
data/eco-helpers.gemspec
CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
|
33
33
|
spec.add_dependency 'ecoportal-api', '>= 0.9.4', '< 0.10'
|
34
34
|
spec.add_dependency 'ecoportal-api-v2', '>= 1.1.3', '< 1.2'
|
35
|
-
spec.add_dependency 'ecoportal-api-graphql', '>= 0.3.
|
35
|
+
spec.add_dependency 'ecoportal-api-graphql', '>= 0.3.9', '< 0.4'
|
36
36
|
spec.add_dependency 'aws-sdk-s3', '>= 1.83.0', '< 2'
|
37
37
|
spec.add_dependency 'aws-sdk-ses', '>= 1.36.0', '< 2'
|
38
38
|
spec.add_dependency 'dotenv', '>= 2.7.6', '< 3'
|
@@ -76,9 +76,23 @@ module Eco
|
|
76
76
|
@archived
|
77
77
|
end
|
78
78
|
|
79
|
+
def active?
|
80
|
+
!archived?
|
81
|
+
end
|
82
|
+
|
79
83
|
# @return [Eco::API::Organization::TagTree]
|
80
84
|
def dup
|
81
|
-
self.class.new(as_json)
|
85
|
+
self.class.new(as_json, name: name, id: id)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Eco::API::Organization::TagTree] with **non** `archived` nodes only
|
89
|
+
def active_tree
|
90
|
+
self.class.new(as_json(include_archived: false), name: name, id: id)
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Eco::API::Organization::TagTree] with nodes up to `max_depth`
|
94
|
+
def truncate(max_depth: total_depth)
|
95
|
+
self.class.new(as_json(max_depth: max_depth), name: name, id: id)
|
82
96
|
end
|
83
97
|
|
84
98
|
# Iterate through all the nodes of this tree
|
@@ -161,11 +175,26 @@ module Eco
|
|
161
175
|
# Returns a tree of Hashes form nested via `nodes` (or just a list of hash nodes)
|
162
176
|
# @yield [node_json, node] block for custom output json model
|
163
177
|
# @yiledreturn [Hash] the custom json model.
|
164
|
-
# @include_children [Boolean] whether it should return a tree hash or just a list of hash nodes.
|
178
|
+
# @param include_children [Boolean] whether it should return a tree hash or just a list of hash nodes.
|
179
|
+
# @param include_archived [Boolean] whether it should include archived nodes.
|
180
|
+
# @param max_depth [Boolean] up to what level `depth` nodes should be included.
|
165
181
|
# @return [Array[Hash]] where `Hash` is a `node` (i.e. `{"tag" => TAG, "nodes": Array[Hash]}`)
|
166
|
-
def as_json(include_children: true, &block)
|
167
|
-
|
168
|
-
|
182
|
+
def as_json(include_children: true, include_archived: true, max_depth: total_depth, &block)
|
183
|
+
max_depth ||= total_depth
|
184
|
+
return nil if max_depth < depth
|
185
|
+
return [] if top? && !include_children
|
186
|
+
return nil if archived? && !include_archived
|
187
|
+
|
188
|
+
if include_children
|
189
|
+
child_nodes = nodes
|
190
|
+
child_nodes = child_nodes.select(&:active?) unless include_archived
|
191
|
+
kargs = {
|
192
|
+
include_children: include_children,
|
193
|
+
include_archived: include_archived,
|
194
|
+
max_depth: max_depth
|
195
|
+
}
|
196
|
+
children_json = child_nodes.map {|nd| nd.as_json(**kargs, &block)}.compact
|
197
|
+
end
|
169
198
|
|
170
199
|
if top?
|
171
200
|
children_json
|
@@ -180,8 +209,8 @@ module Eco
|
|
180
209
|
|
181
210
|
# Returns a plain list form of hash nodes.
|
182
211
|
# @return [Array[Hash]] where `Hash` is a plain `node`
|
183
|
-
def as_nodes_json
|
184
|
-
all_nodes.map {|nd| nd.as_json(include_children: false)}
|
212
|
+
def as_nodes_json(&block)
|
213
|
+
all_nodes.map {|nd| nd.as_json(include_children: false, &block)}
|
185
214
|
end
|
186
215
|
|
187
216
|
# @return [Boolean] `true` if there are tags in the node, `false` otherwise.
|
@@ -287,8 +316,8 @@ module Eco
|
|
287
316
|
# @param key [String] tag to find the path to.
|
288
317
|
# @return [Array<String>]
|
289
318
|
def path(key = nil)
|
290
|
-
return @path if !key
|
291
|
-
@hash_paths[key.upcase]
|
319
|
+
return @path.dup if !key
|
320
|
+
@hash_paths[key.upcase].dup
|
292
321
|
end
|
293
322
|
|
294
323
|
# Helper to assign tags to a person account.
|
@@ -3,18 +3,30 @@ module Eco
|
|
3
3
|
class Session
|
4
4
|
class Config
|
5
5
|
class TagTree < BaseConfig
|
6
|
+
class MissingTagtree < StandardError
|
7
|
+
end
|
8
|
+
|
6
9
|
attr_key :file
|
7
10
|
|
8
|
-
|
11
|
+
# @param include_archived [Boolean] whether or not it should include archived nodes.
|
12
|
+
# @return [Eco::API::Organization::TagTree]
|
13
|
+
def scope_tree(enviro: nil, include_archived: true, raise_on_missing: true)
|
9
14
|
return @tagtree if instance_variable_defined?(:@tagtree) && @tagtree.enviro == enviro
|
10
15
|
if tree_file = self.file
|
11
16
|
if (tree = file_manager.load_json(tree_file)) && !tree.empty?
|
12
17
|
@tagtree = Eco::API::Organization::TagTree.new(tree, enviro: enviro)
|
13
18
|
end
|
14
19
|
end
|
15
|
-
|
20
|
+
|
21
|
+
kargs = {
|
22
|
+
enviro: enviro,
|
23
|
+
includeArchivedNodes: include_archived
|
24
|
+
}
|
25
|
+
|
26
|
+
@tagtree ||= live_tree(**kargs).tap do |tree|
|
16
27
|
unless tree && !tree.empty?
|
17
|
-
|
28
|
+
msg = "Could not find a local or live locations structure."
|
29
|
+
raise MissingTagtree, msg
|
18
30
|
end
|
19
31
|
end
|
20
32
|
end
|
@@ -23,13 +35,18 @@ module Eco
|
|
23
35
|
# If `id` is provided, it only retrieves this locations structure.
|
24
36
|
def live_tree(id: nil, enviro: nil, include_archived: false, **kargs, &block)
|
25
37
|
return @live_tree if instance_variable_defined?(:@live_tree) && @live_tree.enviro == enviro
|
38
|
+
|
39
|
+
kargs = {
|
40
|
+
enviro: enviro,
|
41
|
+
includeArchivedNodes: include_archived
|
42
|
+
}.merge(kargs)
|
43
|
+
|
26
44
|
if id
|
27
|
-
args = {id: id
|
45
|
+
args = { id: id }.merge(kargs)
|
28
46
|
@live_tree = live_tree_get(**args, &block)
|
29
47
|
else
|
30
|
-
|
31
|
-
|
32
|
-
trees = live_trees(enviro: enviro, &block)
|
48
|
+
kargs
|
49
|
+
trees = live_trees(**kargs, &block)
|
33
50
|
@live_tree = trees.reject do |tree|
|
34
51
|
tree.empty?
|
35
52
|
end.max do |a,b|
|
@@ -45,26 +62,33 @@ module Eco
|
|
45
62
|
|
46
63
|
# Gets a single locations structure
|
47
64
|
# @note it does not memoize
|
65
|
+
# @param include_archived [Boolean] whether or not to include archived **nodes**
|
66
|
+
# @return [Eco::API::Organization::TagTree, NilClass]
|
48
67
|
def live_tree_get(id: nil, enviro: nil, include_archived: false, **kargs, &block)
|
49
68
|
return nil unless apis.active_api.version_available?(:graphql)
|
50
69
|
return nil unless graphql = apis.api(version: :graphql)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
70
|
+
|
71
|
+
kargs = {
|
72
|
+
id: id,
|
73
|
+
includeArchivedNodes: include_archived
|
74
|
+
}.merge(kargs)
|
75
|
+
|
76
|
+
return nil unless tree = graphql.currentOrganization.locationStructure(**kargs, &block)
|
55
77
|
args = { enviro: enviro, id: tree.id, name: tree.name }
|
56
78
|
Eco::API::Organization::TagTree.new(tree.treeify, **args)
|
57
79
|
end
|
58
80
|
|
59
81
|
# Retrieves all the location structures of the organisation
|
82
|
+
# @param include_archived [Boolean] whether or not to include archived **nodes**
|
83
|
+
# @return [Array<Eco::API::Organization::TagTree>]
|
60
84
|
def live_trees(enviro: nil, include_archived: false, **kargs, &block)
|
61
85
|
[].tap do |eco_trees|
|
62
86
|
next unless apis.active_api.version_available?(:graphql)
|
63
87
|
next unless graphql = apis.api(version: :graphql)
|
88
|
+
|
64
89
|
kargs = {
|
65
|
-
|
66
|
-
|
67
|
-
}.merge(kargs).slice(:includeArchived, :includeUnpublished)
|
90
|
+
includeArchivedNodes: include_archived
|
91
|
+
}.merge(kargs)
|
68
92
|
|
69
93
|
next unless trees = graphql.currentOrganization.locationStructures(**kargs, &block)
|
70
94
|
trees.each do |tree|
|
@@ -235,9 +235,15 @@ module Eco
|
|
235
235
|
end
|
236
236
|
|
237
237
|
# It uses the `tagtree.json` file and in its absence, if `graphql` enabled, the largest `life_tagtree`
|
238
|
+
# @note it does NOT include archived nodes by default.
|
239
|
+
# - This is for legacy (most usecases don't)
|
240
|
+
# @param include_archived [Boolean] whether or not it should include archived nodes.
|
238
241
|
# @return [Eco::API::Organization::TagTree]
|
239
|
-
def tagtree(enviro: nil)
|
240
|
-
|
242
|
+
def tagtree(enviro: nil, include_archived: false, raise_on_missing: true)
|
243
|
+
kargs = {
|
244
|
+
enviro: enviro, include_archived: include_archived, raise_on_missing: raise_on_missing
|
245
|
+
}
|
246
|
+
@tagtree ||= tagtree_config.scope_tree(**kargs)
|
241
247
|
end
|
242
248
|
|
243
249
|
# It obtains the first of the live tagtree in the org
|
@@ -12,9 +12,9 @@ class Eco::API::UseCases::DefaultCases::ReinviteTransCase < Eco::API::Common::Lo
|
|
12
12
|
invite = session.new_job("main", "invite", :update, usecase, :account)
|
13
13
|
users.each do |person|
|
14
14
|
if force_invite?
|
15
|
-
person.account.send_invites = true
|
16
|
-
else
|
17
15
|
person.account.force_send_invites = true
|
16
|
+
else
|
17
|
+
person.account.send_invites = true
|
18
18
|
end
|
19
19
|
invite.add(person)
|
20
20
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Eco::API::UseCases::GraphQL::Helpers::Base
|
2
|
+
# Basic stuff you would need in any use case
|
3
|
+
module CaseEnv
|
4
|
+
include Eco::Language::AuxiliarLogger
|
5
|
+
attr_reader :session, :options
|
6
|
+
|
7
|
+
def config
|
8
|
+
session.config
|
9
|
+
end
|
10
|
+
|
11
|
+
def simulate?
|
12
|
+
options.dig(:simulate)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,20 +1,12 @@
|
|
1
1
|
module Eco::API::UseCases::GraphQL::Helpers
|
2
2
|
module Base
|
3
|
-
|
4
|
-
|
3
|
+
require_relative 'base/case_env'
|
4
|
+
include Eco::API::UseCases::GraphQL::Helpers::Base::CaseEnv
|
5
5
|
|
6
6
|
def graphql
|
7
7
|
@graphql ||= session.api(version: :graphql)
|
8
8
|
end
|
9
9
|
|
10
|
-
def config
|
11
|
-
session.config
|
12
|
-
end
|
13
|
-
|
14
|
-
def simulate?
|
15
|
-
options.dig(:simulate)
|
16
|
-
end
|
17
|
-
|
18
10
|
# Keep a copy of the requests/responses for future reference
|
19
11
|
def backup(data, type:)
|
20
12
|
dry_run = simulate? ? "_dry_run" : ""
|
@@ -24,7 +16,7 @@ module Eco::API::UseCases::GraphQL::Helpers
|
|
24
16
|
end
|
25
17
|
|
26
18
|
def exit_error(msg)
|
27
|
-
|
19
|
+
log(:error) { msg }
|
28
20
|
exit(1)
|
29
21
|
end
|
30
22
|
end
|
@@ -68,7 +68,7 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
|
|
68
68
|
msg << "nor options(:source, :structure_id). "
|
69
69
|
msg << "Infering active locations structure."
|
70
70
|
log(:warn) { msg }
|
71
|
-
if self.current_tree =
|
71
|
+
if self.current_tree = session_live_tree
|
72
72
|
@target_structure_id = current_tree.id
|
73
73
|
end
|
74
74
|
end
|
@@ -81,7 +81,13 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
|
|
81
81
|
tree_init = current_tree
|
82
82
|
target_id = target_structure_id
|
83
83
|
return current_tree if current_tree != tree_init
|
84
|
-
self.current_tree =
|
84
|
+
self.current_tree = session_live_tree(id: target_id)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Unique access point to retrieve the live tree
|
88
|
+
# @note ensures archived nodes are retrieved.
|
89
|
+
def session_live_tree(id: nil)
|
90
|
+
session.live_tree(id: id, include_archived: true)
|
85
91
|
end
|
86
92
|
end
|
87
93
|
end
|
@@ -3,17 +3,25 @@ module Eco::API::UseCases::GraphQL::Helpers::Location
|
|
3
3
|
include Eco::Language::AuxiliarLogger
|
4
4
|
include Eco::API::UseCases::GraphQL::Helpers::Location::Base
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
# Whether to stop or continue on command fail
|
9
|
-
FORCE_CONTINUE = false
|
6
|
+
DEFAULT_COMMANDS_PER_PAGE = 45
|
7
|
+
DEFAULT_FORCE_CONTINUE = false
|
10
8
|
|
9
|
+
# Prevents each request from timing out
|
11
10
|
def commands_per_page
|
12
|
-
self.class
|
11
|
+
if self.class.const_defined?(:COMMANDS_PER_PAGE)
|
12
|
+
self.class::COMMANDS_PER_PAGE
|
13
|
+
else
|
14
|
+
DEFAULT_COMMANDS_PER_PAGE
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
18
|
+
# Whether to stop or continue on command fail
|
15
19
|
def force_continue?
|
16
|
-
self.class
|
20
|
+
if self.class.const_defined?(:FORCE_CONTINUE)
|
21
|
+
self.class::FORCE_CONTINUE
|
22
|
+
else
|
23
|
+
DEFAULT_FORCE_CONTINUE
|
24
|
+
end
|
17
25
|
end
|
18
26
|
|
19
27
|
# With given the commands, it generates the input of the endpoint mutation.
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Eco::API::UseCases::GraphQL::Utils
|
2
|
+
module Sftp
|
3
|
+
include Eco::API::UseCases::GraphQL::Helpers::Base::CaseEnv
|
4
|
+
|
5
|
+
|
6
|
+
def remote_subfolder
|
7
|
+
nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def remote_folder(subfolder = remote_subfolder)
|
11
|
+
"#{sftp_config.remote_folder}/#{subfolder || ''}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def sftp_group_id
|
15
|
+
if self.class.const_defined?(:SFTP_GROUP)
|
16
|
+
self.class.const_get(:TARGET_STRUCTURE_ID)
|
17
|
+
elsif group_id = options.dig(:sftp, :group)
|
18
|
+
group_id
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def upload(local_file, remote_folder: self.remote_folder, gid: sftp_group_id)
|
23
|
+
return false unless local_file && File.exists?(local_file)
|
24
|
+
dest_file = "#{remote_folder}/#{File.basename(local_file)}"
|
25
|
+
res = sftp_session.upload!(local_file, dest_file)
|
26
|
+
attrs = sftp_session.stat!(dest_file)
|
27
|
+
if gid && gid != attrs.gid
|
28
|
+
stat_res = sftp_session.setstat!(dest_file, {permissions: 0660, uid: attrs.uid, gid: gid})
|
29
|
+
end
|
30
|
+
logger.info("Uploaded '#{local_file}' (#{res})")
|
31
|
+
end
|
32
|
+
|
33
|
+
def ensure_remote_empty
|
34
|
+
files = with_remote_files
|
35
|
+
unless files.empty?
|
36
|
+
msg = "There are still files in the remote folder that will be deleted: '#{remote_folder}':\n"
|
37
|
+
msg += " • " + files.map do |file|
|
38
|
+
file.longname
|
39
|
+
end.join("\n • ") + "\n"
|
40
|
+
session.prompt_user("Do you want to proceed to delete? (Y/n):", explanation: msg, default: "Y", timeout: 3) do |response|
|
41
|
+
if response.upcase.start_with?("Y")
|
42
|
+
files.each do |file|
|
43
|
+
remote_full_path = to_remote_path(file.name)
|
44
|
+
res = sftp_session.remove(remote_full_path)
|
45
|
+
logger.info("Deleted remote file: '#{remote_full_path}' (#{res})")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def with_remote_files
|
53
|
+
sftp.files(remote_folder).each do |remote_file|
|
54
|
+
yield(remote_file) if block_given?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_remote_path(file, subfolder: nil)
|
59
|
+
remote_folder(subfolder) + "/" + file
|
60
|
+
end
|
61
|
+
|
62
|
+
def sftp_config
|
63
|
+
session.config.sftp
|
64
|
+
end
|
65
|
+
|
66
|
+
def sftp_session
|
67
|
+
sftp.sftp_session
|
68
|
+
end
|
69
|
+
|
70
|
+
def sftp
|
71
|
+
session.sftp
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -75,11 +75,11 @@ module Eco
|
|
75
75
|
Dir.exist?(path) || Dir.exist?(File.expand_path(path))
|
76
76
|
end
|
77
77
|
|
78
|
-
def timestamp(timestamp_pattern = DEFAULT_TIMESTAMP_PATTERN)
|
79
|
-
|
78
|
+
def timestamp(timestamp_pattern = DEFAULT_TIMESTAMP_PATTERN, date = Time.now)
|
79
|
+
date.strftime(timestamp_pattern)
|
80
80
|
end
|
81
81
|
|
82
|
-
def timestamp_file(filename, timestamp_pattern =
|
82
|
+
def timestamp_file(filename, timestamp_pattern =DEFAULT_TIMESTAMP_PATTERN)
|
83
83
|
file_pattern = Eco::Data::Files::FilePattern.new(filename)
|
84
84
|
file_pattern.resolve(start: timestamp(timestamp_pattern) + '_')
|
85
85
|
end
|
@@ -3,47 +3,16 @@ module Eco
|
|
3
3
|
module Hashes
|
4
4
|
class ArrayDiff
|
5
5
|
extend Eco::Language::Models::ClassHelpers
|
6
|
-
|
7
|
-
|
8
|
-
def key(value = nil)
|
9
|
-
return @key unless value
|
10
|
-
@key = value.to_s
|
11
|
-
end
|
12
|
-
|
13
|
-
def key?
|
14
|
-
!!@key
|
15
|
-
end
|
16
|
-
|
17
|
-
def compare(*attrs)
|
18
|
-
compared_attrs.push(*attrs.map(&:to_s)).uniq!
|
19
|
-
end
|
20
|
-
|
21
|
-
def case_sensitive(value = nil)
|
22
|
-
@case_sensitive = false unless instance_variable_defined?(:@case_sensitive)
|
23
|
-
return @case_sensitive unless value
|
24
|
-
@case_sensitive = !!value
|
25
|
-
end
|
26
|
-
|
27
|
-
def case_sensitive?
|
28
|
-
!!@case_sensitive
|
29
|
-
end
|
30
|
-
|
31
|
-
def compared_attrs
|
32
|
-
@compared_attrs ||= []
|
33
|
-
@compared_attrs
|
34
|
-
end
|
35
|
-
end
|
6
|
+
# We can change the `DiffResult` class (items)
|
7
|
+
class_resolver :diff_result_class, "Eco::Data::Hash::DiffResult"
|
36
8
|
|
37
9
|
include Eco::Language::AuxiliarLogger
|
38
10
|
|
39
11
|
attr_reader :source1, :source2
|
40
12
|
attr_reader :src_h1, :src_h2
|
41
13
|
|
42
|
-
|
43
|
-
|
44
|
-
def initialize(source1, source2, logger: nil, **kargs)
|
14
|
+
def initialize(source1, source2, logger: nil)
|
45
15
|
@logger = logger if logger
|
46
|
-
@options = kargs
|
47
16
|
@source1 = source1
|
48
17
|
@source2 = source2
|
49
18
|
@src_h1 = by_key(source1)
|
@@ -67,14 +36,11 @@ module Eco
|
|
67
36
|
!diffs.empty?
|
68
37
|
end
|
69
38
|
|
39
|
+
# All the items that contain the diff of a node.
|
40
|
+
# @return [Array<Eco::Data::Hash::DiffResult>]
|
70
41
|
def source_results
|
71
42
|
@source_results ||= paired_sources.each_with_object([]) do |(src1, src2), res|
|
72
|
-
|
73
|
-
key: key,
|
74
|
-
compare: compared_attrs,
|
75
|
-
case_sensitive: case_sensitive?
|
76
|
-
}
|
77
|
-
res << diff_result_class.new(src1, src2, **args)
|
43
|
+
res << diff_result_class.new(src1, src2)
|
78
44
|
end
|
79
45
|
end
|
80
46
|
|
@@ -91,35 +57,23 @@ module Eco
|
|
91
57
|
all_keys.map {|key| [src_h1[key], src_h2[key]]}
|
92
58
|
end
|
93
59
|
|
60
|
+
# @return [String] the `key` attribute of `diff_result_class`
|
94
61
|
def key
|
95
|
-
|
96
|
-
|
97
|
-
end.tap do |k|
|
98
|
-
raise "missing main key attr to pair records. Given: #{k}" unless k.is_a?(String)
|
62
|
+
diff_result_class.key.tap do |k|
|
63
|
+
raise "#{diff_result_class}: missing main key attr to pair records. Given: #{k}" unless k.is_a?(String)
|
99
64
|
end
|
100
65
|
end
|
101
66
|
|
102
67
|
def case_sensitive?
|
103
|
-
|
68
|
+
diff_result_class.case_sensitive?
|
104
69
|
end
|
105
70
|
|
106
71
|
def compared_attrs
|
107
|
-
|
108
|
-
self.class.compared_attrs
|
109
|
-
end.yield_self do |attrs|
|
110
|
-
raise "compared_attrs should be an array" unless attrs.is_a?(Array)
|
111
|
-
attrs.map(&:to_s)
|
112
|
-
end
|
72
|
+
diff_result_class.compared_attrs.map(&:to_s)
|
113
73
|
end
|
114
74
|
|
115
75
|
private
|
116
76
|
|
117
|
-
def options_or(opt)
|
118
|
-
opt = opt.to_sym
|
119
|
-
return @options[opt] if @options.key?(opt)
|
120
|
-
yield
|
121
|
-
end
|
122
|
-
|
123
77
|
def symbolize_keys(hash)
|
124
78
|
hash.each_with_object({}) do |(k, v), h|
|
125
79
|
h[k.to_sym] = v
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Eco
|
2
|
+
module Data
|
3
|
+
module Hashes
|
4
|
+
module DiffMeta
|
5
|
+
class << self
|
6
|
+
def included(base)
|
7
|
+
super(base)
|
8
|
+
base.extend ClassMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# @param value [String, NilClass]
|
14
|
+
# @return [String] the attribute that is key of the node diff elements.
|
15
|
+
def key(value = nil)
|
16
|
+
return @key unless value
|
17
|
+
@key = value.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Boolean] is there a `key` attribute defined?
|
21
|
+
def key?
|
22
|
+
!!@key
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param attrs [Array<Symbol>, Array<String>]
|
26
|
+
# @return [Array<String>] the comparable attributes
|
27
|
+
def compare(*attrs)
|
28
|
+
compared_attrs.push(*attrs.map(&:to_s)).uniq!
|
29
|
+
compared_attrs
|
30
|
+
end
|
31
|
+
|
32
|
+
# Whether or not the diff calc of a node should be done case sensitive or insensitive.
|
33
|
+
def case_sensitive(value = nil)
|
34
|
+
@case_sensitive = false unless instance_variable_defined?(:@case_sensitive)
|
35
|
+
return @case_sensitive unless value
|
36
|
+
@case_sensitive = !!value
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Boolean] are comparisons of values done case sensitive?
|
40
|
+
def case_sensitive?
|
41
|
+
!!@case_sensitive
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Array<String>] the comparable attributes
|
45
|
+
def compared_attrs
|
46
|
+
@compared_attrs ||= []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -2,19 +2,32 @@ module Eco
|
|
2
2
|
module Data
|
3
3
|
module Hashes
|
4
4
|
class DiffResult
|
5
|
-
|
5
|
+
extend Eco::Language::Models::ClassHelpers
|
6
|
+
include Eco::Data::Hashes::DiffMeta
|
7
|
+
|
8
|
+
inheritable_class_vars :key, :compared_attrs, :case_sensitive
|
9
|
+
|
6
10
|
attr_reader :src1, :src2
|
7
11
|
|
8
|
-
|
9
|
-
# - `:all` compares the matching attrs between both hashes only
|
10
|
-
def initialize(src1, src2, key:, compare: :all, case_sensitive: false)
|
11
|
-
@key = key
|
12
|
-
@compare = compare
|
13
|
-
@case_sensitive = case_sensitive
|
12
|
+
def initialize(src1, src2)
|
14
13
|
@src1 = src1
|
15
14
|
@src2 = src2
|
16
15
|
end
|
17
16
|
|
17
|
+
def key
|
18
|
+
self.class.key
|
19
|
+
end
|
20
|
+
|
21
|
+
def case_sensitive?
|
22
|
+
self.class.case_sensitive?
|
23
|
+
end
|
24
|
+
|
25
|
+
def dup(src1: nil, src2: nil)
|
26
|
+
src1 ||= self.src1
|
27
|
+
src2 ||= self.src2
|
28
|
+
self.class.new(src1.dup, src2.dup)
|
29
|
+
end
|
30
|
+
|
18
31
|
def new?
|
19
32
|
!src1 && !!src2
|
20
33
|
end
|
@@ -27,35 +40,39 @@ module Eco
|
|
27
40
|
!new? && !del? && diff?
|
28
41
|
end
|
29
42
|
|
43
|
+
# @note `diff_attrs` may not include the `key` attribute
|
44
|
+
# This is always included via `new?` (new key value) and `del?` (missing key value)
|
45
|
+
# @return [Boolean] was there any change?
|
30
46
|
def diff?
|
31
47
|
new? || del? || !diff_attrs.empty?
|
32
48
|
end
|
33
49
|
|
34
|
-
# Is the key attr value changing?
|
50
|
+
# Is the `key` attr value changing?
|
35
51
|
def key?
|
36
52
|
!(new? || del?) && diff_attr?(key)
|
37
53
|
end
|
38
54
|
|
55
|
+
# Is `attr` part of the attributes that change?
|
39
56
|
def diff_attr?(attr)
|
40
57
|
return true if new?
|
41
58
|
return true if del?
|
42
59
|
diff_attrs.include?(attr.to_s)
|
43
60
|
end
|
44
61
|
|
62
|
+
# @return [Value] the current value of `attr` (in `src2`)
|
45
63
|
def attr(attr)
|
46
64
|
return nil unless src2
|
47
65
|
src2[attr.to_s]
|
48
66
|
end
|
49
67
|
|
68
|
+
# @return [Value] the previous value of `attr` (in `src1`)
|
50
69
|
def attr_prev(attr)
|
51
70
|
return nil unless src1
|
52
71
|
src1[attr.to_s]
|
53
72
|
end
|
54
73
|
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
74
|
+
# @note the `key` attribute will always be added (even if there's no change)
|
75
|
+
# @return [Hash] hash with the differences as per `src2`
|
59
76
|
def diff_hash
|
60
77
|
target_attrs = [key] | compared_attrs
|
61
78
|
return src2.slice(*target_attrs) if new?
|
@@ -63,12 +80,14 @@ module Eco
|
|
63
80
|
src2.slice(key, *diff_attrs)
|
64
81
|
end
|
65
82
|
|
83
|
+
# @return [Array<Symbol>] hash with the differences as per `src2`
|
66
84
|
def diff_attrs
|
67
85
|
@diff_attrs ||= comparable_attrs.each_with_object([]) do |attr, out|
|
68
86
|
out << attr unless eq?(src1[attr], src2[attr])
|
69
87
|
end
|
70
88
|
end
|
71
89
|
|
90
|
+
# @return [Boolean] whether `val1` is equal to `val2`
|
72
91
|
def eq?(val1, val2)
|
73
92
|
return true if val1 == val2
|
74
93
|
return false if case_sensitive?
|
@@ -76,25 +95,18 @@ module Eco
|
|
76
95
|
val1.upcase == val2.upcase
|
77
96
|
end
|
78
97
|
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
|
98
|
+
# @note when is `new?` or to be deleted (`del?`), it returns empty array.
|
99
|
+
# @return [Array<String>] the set of attributes that are comparable in this instance.
|
83
100
|
def comparable_attrs
|
84
101
|
return [] if new? || del?
|
85
102
|
compared_attrs
|
86
103
|
end
|
87
104
|
|
105
|
+
# @return [Array<String>] the set of attributes that are comparable in this class.
|
88
106
|
def compared_attrs
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
src1.keys & src2.keys
|
93
|
-
elsif @compare.is_a?(Array)
|
94
|
-
@compare.map(&:to_s)
|
95
|
-
else
|
96
|
-
raise "Expecting 'compare' to be sym (:all) or Array<String>. Given: #{@compare.class}"
|
97
|
-
end
|
107
|
+
comp_attrs = self.class.compared_attrs.map(&:to_s).uniq
|
108
|
+
return comp_attrs unless comp_attrs.empty?
|
109
|
+
(src1&.keys || []) & (src2&.keys || [])
|
98
110
|
end
|
99
111
|
end
|
100
112
|
end
|
data/lib/eco/data/hashes.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
class Eco::Data::Locations::NodeDiff
|
2
|
+
module Accessors
|
3
|
+
class << self
|
4
|
+
def included(base)
|
5
|
+
super(base)
|
6
|
+
base.extend Eco::Language::Models::ClassHelpers
|
7
|
+
base.extend ClassMethods
|
8
|
+
base.inheritable_class_vars :exposed_attrs
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Creates the defined accessor attributes against `NodeDiff`
|
14
|
+
# It also creates a method with question mark that evaluates true if **any** the attr changed.
|
15
|
+
# @note the defined attributes are expected to be the keys within
|
16
|
+
# the source Hashes that are being compared.
|
17
|
+
# @note accessing `src1` (prev) attributes, will have it's method as `prev_[attrName]`
|
18
|
+
def attr_expose(*attrs)
|
19
|
+
attrs.each do |attr|
|
20
|
+
meth = attr.to_sym
|
21
|
+
methp = "prev_#{meth}".to_sym
|
22
|
+
methq = "diff_#{meth}?".to_sym
|
23
|
+
|
24
|
+
define_method meth do
|
25
|
+
attr(meth)
|
26
|
+
end
|
27
|
+
|
28
|
+
define_method methp do
|
29
|
+
attr_prev(meth)
|
30
|
+
end
|
31
|
+
|
32
|
+
exposed_attrs |= [meth]
|
33
|
+
|
34
|
+
define_method methq do
|
35
|
+
diff_attr?(meth)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Keeps track on what attributes have been exposed.
|
41
|
+
def exposed_attrs
|
42
|
+
@exposed_attrs ||= []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class Eco::Data::Locations::NodeDiff
|
2
|
+
# Adjusts ArrayDiff for Nodes diffs in a location structure
|
3
|
+
class NodesDiff < Eco::Data::Hashes::ArrayDiff
|
4
|
+
extend Eco::Data::Locations::NodeDiff::Selectors
|
5
|
+
|
6
|
+
SELECTORS = %i[id name id_name insert unarchive update move archive]
|
7
|
+
selector *SELECTORS
|
8
|
+
|
9
|
+
class_resolver :diff_result_class, Eco::Data::Locations::NodeDiff
|
10
|
+
attr_reader :original_tree
|
11
|
+
|
12
|
+
def initialize(*args, original_tree:, **kargs, &block)
|
13
|
+
super(*args, **kargs, &block)
|
14
|
+
@original_tree = original_tree
|
15
|
+
end
|
16
|
+
|
17
|
+
def diffs
|
18
|
+
@diffs ||= super.select do |res|
|
19
|
+
res.unarchive? || res.id_name? || res.insert? || res.move? || res.archive?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def diffs_details
|
24
|
+
section = '#' * 10
|
25
|
+
msg = ''
|
26
|
+
if insert?
|
27
|
+
msg << " #{section} I N S E R T S #{section}\n"
|
28
|
+
msg << insert.map {|d| d.diff_hash.pretty_inspect}.join('')
|
29
|
+
end
|
30
|
+
|
31
|
+
if update?
|
32
|
+
msg << "\n #{section} U P D A T E S #{section}\n"
|
33
|
+
update.each do |d|
|
34
|
+
flags = ''
|
35
|
+
#flags << 'i' if d.id?
|
36
|
+
flags << 'n' if d.diff_name?
|
37
|
+
flags << 'm' if d.move?
|
38
|
+
flags << 'u' if d.unarchive?
|
39
|
+
msg << "<< #{flags} >> "
|
40
|
+
msg << d.diff_hash.pretty_inspect
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if archive?
|
45
|
+
msg << "\n #{section} A R C H I V E S #{section}\n"
|
46
|
+
msg << archive.map {|d| d.diff_hash.pretty_inspect}.join('')
|
47
|
+
end
|
48
|
+
|
49
|
+
msg
|
50
|
+
end
|
51
|
+
|
52
|
+
def diffs_summary
|
53
|
+
return "There were no differences identified" if diffs.empty?
|
54
|
+
msg = "Identified #{diffs.count} differences:\n"
|
55
|
+
msg << when_present(insert, '') do |count|
|
56
|
+
" • #{count} nodes to insert\n"
|
57
|
+
end
|
58
|
+
msg << when_present(update, '') do |count|
|
59
|
+
" • #{count} nodes to update\n"
|
60
|
+
end
|
61
|
+
# msg << when_present(id, '') do |count|
|
62
|
+
# " • #{count} nodes to change id\n"
|
63
|
+
# end
|
64
|
+
msg << when_present(name, '') do |count|
|
65
|
+
" • #{count} nodes to change name\n"
|
66
|
+
end
|
67
|
+
msg << when_present(move, '') do |count|
|
68
|
+
" • #{count} nodes to move\n"
|
69
|
+
end
|
70
|
+
msg << when_present(unarchive, '') do |count|
|
71
|
+
" • #{count} nodes to unarchive\n"
|
72
|
+
end
|
73
|
+
msg << when_present(archive, '') do |count|
|
74
|
+
" • #{count} nodes to archive\n"
|
75
|
+
end
|
76
|
+
msg
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def when_present(list, default = nil)
|
82
|
+
count = list.count
|
83
|
+
if count > 0
|
84
|
+
yield(count)
|
85
|
+
else
|
86
|
+
default
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Eco::Data::Locations::NodeDiff
|
2
|
+
module Selectors
|
3
|
+
# Creates selector methods on `diffs` (nodes that changed)
|
4
|
+
# It also creates a method with question mark that evaluates true if **any** diff matches.
|
5
|
+
# @note the selector method name with a question mark should exist in the `diff_result_class`
|
6
|
+
def selector(*attrs)
|
7
|
+
attrs.each do |attr|
|
8
|
+
meth = attr.to_sym
|
9
|
+
methq = "#{meth}?".to_sym
|
10
|
+
define_method meth do
|
11
|
+
diffs.select(&methq)
|
12
|
+
end
|
13
|
+
|
14
|
+
define_method methq do
|
15
|
+
diffs.any(&methq)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Eco::Data::Locations
|
2
|
+
# Differences between node before and after
|
3
|
+
# @note this works with `Hash` object where basic keys are:
|
4
|
+
# - `nodeId`
|
5
|
+
# - `name`
|
6
|
+
# - `parentId`
|
7
|
+
# - `archived`
|
8
|
+
# @note other properties can be part of the `Hash` although
|
9
|
+
# they may not influence the results.
|
10
|
+
class NodeDiff < Eco::Data::Hashes::DiffResult
|
11
|
+
require_relative 'node_diff/accessors'
|
12
|
+
require_relative 'node_diff/selectors'
|
13
|
+
require_relative 'node_diff/nodes_diff'
|
14
|
+
|
15
|
+
include Eco::Data::Locations::NodeDiff::Accessors
|
16
|
+
|
17
|
+
key :nodeId
|
18
|
+
compare :parentId, :name, :archived
|
19
|
+
case_sensitive false
|
20
|
+
|
21
|
+
attr_expose :nodeId, :name, :parentId, :archived
|
22
|
+
|
23
|
+
alias_method :insert?, :new?
|
24
|
+
|
25
|
+
alias_method :diff_name_src?, :diff_name?
|
26
|
+
# Has the property `name` changed?
|
27
|
+
def diff_name?
|
28
|
+
diff_name_src? && update?
|
29
|
+
end
|
30
|
+
alias_method :name?, :diff_name?
|
31
|
+
alias_method :id? , :diff_name? # currently a change of name is a change of id (tag)
|
32
|
+
#alias_method :id? , :key?
|
33
|
+
#alias_method :nodeId? , :id?
|
34
|
+
|
35
|
+
# Has any of `id` or `name` properties changed?
|
36
|
+
def id_name?
|
37
|
+
id? || diff_name?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Has the parent id changed?
|
41
|
+
def move?
|
42
|
+
update? && diff_parentId?
|
43
|
+
end
|
44
|
+
|
45
|
+
# Has the `archived` property changed and it was `true`?
|
46
|
+
def unarchive?
|
47
|
+
!archived && update? && diff_archived?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Has the `archived` property changed and it was `false`?
|
51
|
+
def archive?
|
52
|
+
!prev_archived && (del? || archived)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -17,6 +17,8 @@ module Eco::Data::Locations
|
|
17
17
|
def id
|
18
18
|
clean_id(super)
|
19
19
|
end
|
20
|
+
# backwards compatibility
|
21
|
+
alias_method :tag, :id
|
20
22
|
|
21
23
|
def name
|
22
24
|
super || self.id
|
@@ -25,10 +27,5 @@ module Eco::Data::Locations
|
|
25
27
|
def parentId
|
26
28
|
self.parent_id
|
27
29
|
end
|
28
|
-
|
29
|
-
# backwards compatibility
|
30
|
-
def tag
|
31
|
-
id
|
32
|
-
end
|
33
30
|
end
|
34
31
|
end
|
data/lib/eco/data/locations.rb
CHANGED
data/lib/eco/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eco-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.5.
|
4
|
+
version: 2.5.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oscar Segura
|
@@ -156,7 +156,7 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - ">="
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.3.
|
159
|
+
version: 0.3.9
|
160
160
|
- - "<"
|
161
161
|
- !ruby/object:Gem::Version
|
162
162
|
version: '0.4'
|
@@ -166,7 +166,7 @@ dependencies:
|
|
166
166
|
requirements:
|
167
167
|
- - ">="
|
168
168
|
- !ruby/object:Gem::Version
|
169
|
-
version: 0.3.
|
169
|
+
version: 0.3.9
|
170
170
|
- - "<"
|
171
171
|
- !ruby/object:Gem::Version
|
172
172
|
version: '0.4'
|
@@ -592,6 +592,7 @@ files:
|
|
592
592
|
- lib/eco/api/usecases/graphql/base.rb
|
593
593
|
- lib/eco/api/usecases/graphql/helpers.rb
|
594
594
|
- lib/eco/api/usecases/graphql/helpers/base.rb
|
595
|
+
- lib/eco/api/usecases/graphql/helpers/base/case_env.rb
|
595
596
|
- lib/eco/api/usecases/graphql/helpers/location.rb
|
596
597
|
- lib/eco/api/usecases/graphql/helpers/location/base.rb
|
597
598
|
- lib/eco/api/usecases/graphql/helpers/location/command.rb
|
@@ -603,6 +604,8 @@ files:
|
|
603
604
|
- lib/eco/api/usecases/graphql/samples/location/command/dsl.rb
|
604
605
|
- lib/eco/api/usecases/graphql/samples/location/command/results.rb
|
605
606
|
- lib/eco/api/usecases/graphql/samples/location/dsl.rb
|
607
|
+
- lib/eco/api/usecases/graphql/utils.rb
|
608
|
+
- lib/eco/api/usecases/graphql/utils/sftp.rb
|
606
609
|
- lib/eco/api/usecases/ooze_cases.rb
|
607
610
|
- lib/eco/api/usecases/ooze_cases/export_register_case.rb
|
608
611
|
- lib/eco/api/usecases/ooze_samples.rb
|
@@ -670,6 +673,7 @@ files:
|
|
670
673
|
- lib/eco/data/fuzzy_match/string_helpers.rb
|
671
674
|
- lib/eco/data/hashes.rb
|
672
675
|
- lib/eco/data/hashes/array_diff.rb
|
676
|
+
- lib/eco/data/hashes/diff_meta.rb
|
673
677
|
- lib/eco/data/hashes/diff_result.rb
|
674
678
|
- lib/eco/data/locations.rb
|
675
679
|
- lib/eco/data/locations/convert.rb
|
@@ -681,6 +685,10 @@ files:
|
|
681
685
|
- lib/eco/data/locations/node_base/serial.rb
|
682
686
|
- lib/eco/data/locations/node_base/tag_validations.rb
|
683
687
|
- lib/eco/data/locations/node_base/treeify.rb
|
688
|
+
- lib/eco/data/locations/node_diff.rb
|
689
|
+
- lib/eco/data/locations/node_diff/accessors.rb
|
690
|
+
- lib/eco/data/locations/node_diff/nodes_diff.rb
|
691
|
+
- lib/eco/data/locations/node_diff/selectors.rb
|
684
692
|
- lib/eco/data/locations/node_level.rb
|
685
693
|
- lib/eco/data/locations/node_level/builder.rb
|
686
694
|
- lib/eco/data/locations/node_level/cleaner.rb
|