eco-helpers 2.6.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +95 -0
  4. data/CHANGELOG.md +135 -2
  5. data/Rakefile +13 -7
  6. data/eco-helpers.gemspec +3 -3
  7. data/lib/eco/api/common/loaders/base.rb +2 -2
  8. data/lib/eco/api/common/loaders/case_base.rb +1 -1
  9. data/lib/eco/api/common/loaders/config/workflow/mailer.rb +5 -5
  10. data/lib/eco/api/common/loaders/error_handler.rb +8 -5
  11. data/lib/eco/api/common/loaders/parser.rb +44 -22
  12. data/lib/eco/api/common/loaders/policy.rb +6 -4
  13. data/lib/eco/api/common/loaders/use_case.rb +13 -7
  14. data/lib/eco/api/common/people/base_parser.rb +0 -2
  15. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +0 -1
  16. data/lib/eco/api/common/people/default_parsers/csv_parser.rb +1 -1
  17. data/lib/eco/api/common/people/default_parsers/date_parser.rb +64 -12
  18. data/lib/eco/api/common/people/default_parsers/freemium_parser.rb +0 -1
  19. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +13 -5
  20. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +0 -1
  21. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +18 -5
  22. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +8 -8
  23. data/lib/eco/api/common/people/default_parsers/select_parser.rb +50 -26
  24. data/lib/eco/api/common/people/default_parsers/send_invites_parser.rb +6 -6
  25. data/lib/eco/api/common/people/default_parsers/xls_parser.rb +9 -12
  26. data/lib/eco/api/common/people/default_parsers.rb +1 -12
  27. data/lib/eco/api/common/people/entries.rb +13 -13
  28. data/lib/eco/api/common/people/entry_factory.rb +76 -45
  29. data/lib/eco/api/common/people/person_attribute_parser.rb +8 -12
  30. data/lib/eco/api/common/people/person_entry.rb +86 -75
  31. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +60 -44
  32. data/lib/eco/api/common/people/person_factory.rb +30 -22
  33. data/lib/eco/api/common/people/person_modifier.rb +11 -13
  34. data/lib/eco/api/common/people/person_parser.rb +101 -39
  35. data/lib/eco/api/common/people/supervisor_helpers.rb +25 -26
  36. data/lib/eco/api/common/session/base_session.rb +9 -9
  37. data/lib/eco/api/common/session/environment.rb +7 -5
  38. data/lib/eco/api/common/session/sftp.rb +59 -32
  39. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +10 -6
  40. data/lib/eco/api/common/version_patches/exception.rb +11 -13
  41. data/lib/eco/api/error.rb +32 -20
  42. data/lib/eco/api/microcases/set_supervisor.rb +0 -3
  43. data/lib/eco/api/organization/node_classifications.rb +82 -0
  44. data/lib/eco/api/organization/policy_groups.rb +4 -6
  45. data/lib/eco/api/organization/tag_tree.rb +169 -93
  46. data/lib/eco/api/organization.rb +1 -0
  47. data/lib/eco/api/session/batch/job.rb +1 -1
  48. data/lib/eco/api/session/config/tagtree.rb +41 -23
  49. data/lib/eco/api/session/config/workflow.rb +113 -88
  50. data/lib/eco/api/session/config.rb +6 -0
  51. data/lib/eco/api/session.rb +51 -29
  52. data/lib/eco/api/usecases/base_io.rb +28 -25
  53. data/lib/eco/api/usecases/default/locations/cli/tagtree_extract_cli.rb +7 -2
  54. data/lib/eco/api/usecases/default/locations/cli/tagtree_upload_cli.rb +21 -0
  55. data/lib/eco/api/usecases/default/locations/csv_to_tree_case.rb +3 -3
  56. data/lib/eco/api/usecases/default/locations/tagtree_extract_case.rb +54 -23
  57. data/lib/eco/api/usecases/default/locations/tagtree_upload_case.rb +87 -0
  58. data/lib/eco/api/usecases/default/locations.rb +1 -0
  59. data/lib/eco/api/usecases/default/people/analyse_people_case.rb +60 -56
  60. data/lib/eco/api/usecases/default/people/change_email_case.rb +8 -9
  61. data/lib/eco/api/usecases/default/people/clean_unknown_tags_case.rb +13 -11
  62. data/lib/eco/api/usecases/default/people/clear_abilities_case.rb +2 -2
  63. data/lib/eco/api/usecases/default/people/org_data_convert_case.rb +25 -27
  64. data/lib/eco/api/usecases/default/people/refresh_case.rb +2 -2
  65. data/lib/eco/api/usecases/default/people/reinvite_trans_case.rb +1 -1
  66. data/lib/eco/api/usecases/default/people/reinvite_trans_cli.rb +0 -1
  67. data/lib/eco/api/usecases/default/people/restore_db_case.rb +39 -34
  68. data/lib/eco/api/usecases/default/people/set_default_tag_case.rb +19 -15
  69. data/lib/eco/api/usecases/default/people/supers_cyclic_identify_case.rb +16 -12
  70. data/lib/eco/api/usecases/default_cases/hris_case.rb +17 -15
  71. data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +30 -16
  72. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +0 -2
  73. data/lib/eco/api/usecases/graphql/base.rb +5 -3
  74. data/lib/eco/api/usecases/graphql/helpers/base/case_env.rb +4 -1
  75. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +14 -0
  76. data/lib/eco/api/usecases/graphql/helpers/base.rb +5 -4
  77. data/lib/eco/api/usecases/graphql/helpers/location/base/classifications_parser.rb +60 -0
  78. data/lib/eco/api/usecases/graphql/helpers/location/base/tree_tracking.rb +72 -0
  79. data/lib/eco/api/usecases/graphql/helpers/location/base.rb +25 -59
  80. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/as_update.rb +59 -0
  81. data/lib/eco/api/usecases/graphql/helpers/location/command/diff/compare.rb +49 -0
  82. data/lib/eco/api/usecases/graphql/helpers/location/command/diff.rb +11 -0
  83. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/commandable.rb +46 -0
  84. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_archive.rb +23 -0
  85. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable/for_unarchive.rb +65 -0
  86. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/diff_sortable.rb +49 -0
  87. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable/relation_safe_sort.rb +119 -0
  88. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages/sortable.rb +59 -0
  89. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs/stages.rb +82 -0
  90. data/lib/eco/api/usecases/graphql/helpers/location/command/diffs.rb +20 -0
  91. data/lib/eco/api/usecases/graphql/helpers/location/command/optimizations.rb +84 -0
  92. data/lib/eco/api/usecases/graphql/helpers/location/command/result.rb +4 -4
  93. data/lib/eco/api/usecases/graphql/helpers/location/command/results.rb +24 -12
  94. data/lib/eco/api/usecases/graphql/helpers/location/command.rb +21 -24
  95. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_map.rb +1 -1
  96. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap/tags_set.rb +10 -11
  97. data/lib/eco/api/usecases/graphql/helpers/location/tags_remap.rb +8 -9
  98. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +41 -12
  99. data/lib/eco/api/usecases/graphql/samples/location/command/results.rb +11 -80
  100. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +89 -0
  101. data/lib/eco/api/usecases/graphql/samples/location/command/service.rb +6 -0
  102. data/lib/eco/api/usecases/graphql/samples/location/command/track_changed_ids.rb +89 -0
  103. data/lib/eco/api/usecases/graphql/samples/location/command.rb +3 -0
  104. data/lib/eco/api/usecases/graphql/samples/location/service/base.rb +9 -0
  105. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/heading.rb +18 -0
  106. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/inputable.rb +53 -0
  107. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/classifications.rb +34 -0
  108. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing/helpers.rb +28 -0
  109. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible/parsing.rb +46 -0
  110. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff/convertible.rb +38 -0
  111. data/lib/eco/api/usecases/graphql/samples/location/service/tree_diff.rb +105 -0
  112. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/discarded.rb +16 -0
  113. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/input.rb +15 -0
  114. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/node_attr_maps.rb +22 -0
  115. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter/parser.rb +45 -0
  116. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/converter.rb +36 -0
  117. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list/output.rb +56 -0
  118. data/lib/eco/api/usecases/graphql/samples/location/service/tree_to_list.rb +41 -0
  119. data/lib/eco/api/usecases/graphql/samples/location/service.rb +8 -0
  120. data/lib/eco/api/usecases/graphql/samples/location.rb +1 -0
  121. data/lib/eco/api/usecases/graphql/utils/sftp.rb +96 -36
  122. data/lib/eco/api/usecases/ooze_cases/export_register_case.rb +8 -6
  123. data/lib/eco/api/usecases/ooze_samples/helpers/creatable.rb +4 -3
  124. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_ooze.rb +39 -25
  125. data/lib/eco/api/usecases/ooze_samples/helpers/exportable_register.rb +13 -15
  126. data/lib/eco/api/usecases/ooze_samples/helpers/filters.rb +50 -21
  127. data/lib/eco/api/usecases/ooze_samples/helpers/ooze_handlers.rb +21 -11
  128. data/lib/eco/api/usecases/ooze_samples/helpers/rescuable.rb +2 -0
  129. data/lib/eco/api/usecases/ooze_samples/helpers/shortcuts.rb +49 -43
  130. data/lib/eco/api/usecases/ooze_samples/ooze_base_case.rb +17 -19
  131. data/lib/eco/api/usecases/ooze_samples/register_export_case.rb +48 -43
  132. data/lib/eco/api/usecases/ooze_samples/register_update_case.rb +34 -34
  133. data/lib/eco/api/usecases/ooze_samples/target_oozes_update_case.rb +8 -10
  134. data/lib/eco/api/usecases.rb +0 -1
  135. data/lib/eco/cli/config/use_cases.rb +31 -29
  136. data/lib/eco/cli_default/input_filters.rb +0 -5
  137. data/lib/eco/cli_default/people_filters.rb +4 -4
  138. data/lib/eco/cli_default/workflow.rb +13 -14
  139. data/lib/eco/csv/table.rb +34 -25
  140. data/lib/eco/data/hashes/array_diff.rb +24 -35
  141. data/lib/eco/data/hashes/diff_result/meta.rb +131 -0
  142. data/lib/eco/data/hashes/diff_result.rb +65 -57
  143. data/lib/eco/data/hashes/sanke_camel_indifferent_access.rb +278 -0
  144. data/lib/eco/data/hashes.rb +1 -1
  145. data/lib/eco/data/locations/convert.rb +1 -1
  146. data/lib/eco/data/locations/node_base/csv_convert.rb +19 -9
  147. data/lib/eco/data/locations/node_base/parsing.rb +4 -2
  148. data/lib/eco/data/locations/node_base/treeify.rb +149 -132
  149. data/lib/eco/data/locations/node_base.rb +15 -4
  150. data/lib/eco/data/locations/node_diff/accessors.rb +13 -5
  151. data/lib/eco/data/locations/node_diff/nodes_diff/clustered_treeify.rb +101 -0
  152. data/lib/eco/data/locations/node_diff/nodes_diff/diffs_tree.rb +99 -0
  153. data/lib/eco/data/locations/node_diff/{selectors.rb → nodes_diff/selectors.rb} +1 -1
  154. data/lib/eco/data/locations/node_diff/nodes_diff.rb +50 -35
  155. data/lib/eco/data/locations/node_diff.rb +45 -17
  156. data/lib/eco/data/locations/node_level/parsing.rb +15 -21
  157. data/lib/eco/data/locations/node_level.rb +66 -22
  158. data/lib/eco/data/locations/node_plain/parsing.rb +1 -1
  159. data/lib/eco/data/locations/node_plain.rb +60 -7
  160. data/lib/eco/data/strings/camel_case.rb +35 -0
  161. data/lib/eco/data/strings/snake_case.rb +18 -0
  162. data/lib/eco/data/strings.rb +8 -0
  163. data/lib/eco/data.rb +1 -0
  164. data/lib/eco/language/methods/call_detector.rb +11 -0
  165. data/lib/eco/language/methods/dsl_able.rb +7 -1
  166. data/lib/eco/language/methods.rb +2 -1
  167. data/lib/eco/language/models/collection.rb +23 -25
  168. data/lib/eco/language/models/parser_serializer.rb +24 -5
  169. data/lib/eco/version.rb +1 -1
  170. data/lib/eco-helpers.rb +0 -1
  171. metadata +54 -9
  172. data/lib/eco/data/hashes/diff_meta.rb +0 -52
@@ -2,21 +2,35 @@ module Eco
2
2
  module API
3
3
  module Common
4
4
  module People
5
-
6
5
  # Class to define/group a set of parsers/serializers.
7
6
  #
8
- # @attr_reader schema [Ecoportal::API::V1::PersonSchema, nil] schema of person details that this parser will be based upon.
9
- # @attr_reader details_attrs [Array<String>] internal names of schema details attributes.
10
- # @attr_reader all_model_attrs [Array<String>] all the internal name attributes, including _core_, _account_ and _details_.
7
+ # @attr_reader schema [Ecoportal::API::V1::PersonSchema, nil] schema of
8
+ # person details that this parser will be based upon.
9
+ # @attr_reader details_attrs [Array<String>] internal names of
10
+ # chema details attributes.
11
+ # @attr_reader all_model_attrs [Array<String>] all the internal
12
+ # name attributes, including _core_, _account_ and _details_.
11
13
  class PersonParser
12
- extend Eco::API::Common::ClassAutoLoader
14
+ extend Eco::API::Common::ClassAutoLoader
13
15
  autoloads_children_of "Eco::API::Common::Loaders::Parser"
14
16
  autoload_namespace_ignore "Eco::API"
15
17
 
16
- CORE_ATTRS = ["id", "external_id", "email", "name", "supervisor_id", "filter_tags", "contractor_organization_id", "freemium"]
17
- ACCOUNT_ATTRS = ["policy_group_ids", "default_tag", "send_invites", "landing_page_id", "login_provider_ids"]
18
- TYPE = [:select, :text, :date, :number, :phone_number, :boolean, :multiple]
19
- FORMAT = [:csv, :xml, :json, :xls]
18
+ CORE_ATTRS = %w[
19
+ id external_id email name
20
+ supervisor_id filter_tags
21
+ contractor_organization_id freemium
22
+ ].freeze
23
+ ACCOUNT_ATTRS = %w[
24
+ policy_group_ids default_tag
25
+ send_invites landing_page_id
26
+ login_provider_ids
27
+ ].freeze
28
+ TYPE = %i[
29
+ select text date
30
+ number phone_number
31
+ boolean multiple
32
+ ].freeze
33
+ FORMAT = %i[csv xml json xls].freeze
20
34
 
21
35
  attr_reader :schema
22
36
  attr_reader :details_attrs, :all_model_attrs
@@ -33,16 +47,18 @@ module Eco
33
47
  # value.to_s
34
48
  # end
35
49
  # end
36
- # @param schema [Ecoportal::API::V1::PersonSchema, nil] schema of person details that this parser will be based upon.
50
+ # @param schema [Ecoportal::API::V1::PersonSchema, nil] schema of
51
+ # person details that this parser will be based upon.
37
52
  def initialize(schema: nil)
38
- raise "Constructor needs a PersonSchema. Given: #{schema}" if schema && !schema.is_a?(Ecoportal::API::V1::PersonSchema)
53
+ msg = "Constructor needs a PersonSchema. Given: #{schema.class}"
54
+ raise msg if schema && !schema.is_a?(Ecoportal::API::V1::PersonSchema)
39
55
  @details_attrs = []
40
56
  @parsers = {}
41
57
  @patch_version = 0
42
58
 
43
59
  if schema
44
60
  @schema = Ecoportal::API::Internal::PersonSchema.new(JSON.parse(schema.doc.to_json))
45
- @details_attrs = @schema&.fields.map { |fld| fld.alt_id }
61
+ @details_attrs = @schema&.fields&.map(&:alt_id)
46
62
  end
47
63
 
48
64
  @all_model_attrs = CORE_ATTRS + ACCOUNT_ATTRS + @details_attrs
@@ -57,6 +73,25 @@ module Eco
57
73
  self.class.new(schema: schema || self.schema).merge(self)
58
74
  end
59
75
 
76
+ # @!group selection options of all select attributes
77
+
78
+ # Select Options
79
+ def select_tables
80
+ return nil unless @schema
81
+
82
+ @select_tables ||= @schema.fields.select do |fld|
83
+ fld.type == "select"
84
+ end.to_h do |fld|
85
+ msg = "The schema selection field '#{fld.name}' is missing selection options."
86
+ raise msg unless fld.options&.any?
87
+
88
+ options_hash = fld.options.to_h { |v| [v.downcase.strip, v] }
89
+ [fld.alt_id, options_hash]
90
+ end
91
+ end
92
+
93
+ # @!endgroup
94
+
60
95
  # @!group Scopping attributes (identifying, presence & active)
61
96
 
62
97
  # @return [Array<Eco::API::Common::Loaders::Parser::RequiredAttrs>]
@@ -71,29 +106,35 @@ module Eco
71
106
  end
72
107
 
73
108
  # Scopes `source_attrs` using the _**core** attributes_.
74
- # @note use this helper to know which among your attributes are **core** ones.
109
+ # @note use this helper to know which among your attributes are
110
+ # **core** ones.
75
111
  # @param source_attrs [Array<String>]
76
- # @return [Array<String>] the scoped **core** attributes, if `source_attrs` is not `nil`. All the _core attributes_, otherwise.
112
+ # @return [Array<String>] the scoped **core** attributes,
113
+ # if `source_attrs` is not `nil`. All the _core attributes_, otherwise.
77
114
  def target_attrs_core(source_attrs = nil)
78
- return CORE_ATTRS if !source_attrs
115
+ return CORE_ATTRS unless source_attrs
79
116
  scoped_attrs(source_attrs, CORE_ATTRS)
80
117
  end
81
118
 
82
119
  # Scopes `source_attrs` using the schema _**details** attributes_.
83
- # @note use this helper to know which among your attributes are schema **details** ones.
120
+ # @note use this helper to know which among your attributes
121
+ # are schema **details** ones.
84
122
  # @param source_attrs [Array<String>]
85
- # @return [Array<String>] the scoped **details** attributes, if `source_attrs` is not `nil`. All the _details attributes_, otherwise.
123
+ # @return [Array<String>] the scoped **details** attributes,
124
+ # if `source_attrs` is not `nil`. All the _details attributes_, otherwise.
86
125
  def target_attrs_details(source_attrs = nil)
87
- return @details_attrs if !source_attrs
126
+ return @details_attrs unless source_attrs
88
127
  scoped_attrs(source_attrs, @details_attrs)
89
128
  end
90
129
 
91
130
  # Scopes `source_attrs` using the schema _**account** attributes_.
92
131
  # @note use this helper to know which among your attributes are **account** ones.
93
132
  # @param source_attrs [Array<String>]
94
- # @return [Array<String>] the scoped **account** attributes, if `source_attrs` is not `nil`. All the _account attributes_, otherwise.
133
+ # @return [Array<String>] the scoped **account** attributes,
134
+ # if `source_attrs` is not `nil`.
135
+ # All the _account attributes_, otherwise.
95
136
  def target_attrs_account(source_attrs = nil)
96
- return ACCOUNT_ATTRS if !source_attrs
137
+ return ACCOUNT_ATTRS unless source_attrs
97
138
  scoped_attrs(source_attrs, ACCOUNT_ATTRS)
98
139
  end
99
140
 
@@ -128,12 +169,17 @@ module Eco
128
169
  @parsers.keys.select {|k| k.is_a?(Symbol)}
129
170
  end
130
171
 
131
- # Returns a list of all the internal attributes of the model that have a parser defined & that should be active.
132
- # @param source_data [Hash, Array<String>] the data that we scope for parsing
133
- # @param phase [Symbol] the phase when the attr parser is expected to be called.
172
+ # Returns a list of all the internal attributes of the model that have
173
+ # a parser defined & that should be active.
174
+ # @param source_data [Hash, Array<String>] the data that we scope for
175
+ # parsing
176
+ # @param phase [Symbol] the phase when the attr parser is expected
177
+ # to be called.
134
178
  # Can be [:internal, :final, :person]
135
- # @param process [Symbol] either `:parse` or `:serialize`, depending if we want to parse or serialize the `attr`.
136
- # @return [Array<String>] list of all attribute defined parsers that should be active for the given `source_data`.
179
+ # @param process [Symbol] either `:parse` or `:serialize`, depending
180
+ # if we want to parse or serialize the `attr`.
181
+ # @return [Array<String>] list of all attribute defined parsers that
182
+ # should be active for the given `source_data`.
137
183
  def active_attrs(source_data, phase = :any, process: :parse)
138
184
  defined_model_attrs.select do |attr|
139
185
  if process == :serialize
@@ -165,8 +211,10 @@ module Eco
165
211
  # @param parser [Eco::API::Common::People::PersonParser] a `PersonParser` containing defined parsers.
166
212
  # @return [Eco::API::Common::People::PersonParser] the current object (to ease chainig).
167
213
  def merge(parser)
168
- return self if !parser
169
- raise "Expected a PersonParser object. Given #{parser}" if !parser.is_a?(PersonParser)
214
+ return self unless parser
215
+ msg = "Expected a PersonParser object. Given #{parser.class}"
216
+ raise msg unless parser.is_a?(PersonParser)
217
+
170
218
  to_h.merge!(parser.to_h)
171
219
  patched!
172
220
  self
@@ -183,7 +231,9 @@ module Eco
183
231
  # @return [Eco::API::Common::People::PersonParser] the current object (to ease chainig).
184
232
  def define_attribute(attr, dependencies: {}, &definition)
185
233
  unless valid?(attr)
186
- msg = "The attribute '#{attr_to_str(attr)}' is not part of core, account or target schema, or does not match any type: #{@details_attrs}"
234
+ msg = "The attribute '#{attr_to_str(attr)}' is not part of "
235
+ msg << "core, account or target schema, or does "
236
+ msg << "not match any type: #{@details_attrs}"
187
237
  raise msg
188
238
  end
189
239
  Eco::API::Common::People::PersonAttributeParser.new(attr, dependencies: dependencies).tap do |parser|
@@ -197,19 +247,27 @@ module Eco
197
247
 
198
248
  # @!group Launching parser/serializer
199
249
 
200
- # Call to parser `source` value of attribute or type `attr` into an internal valid value.
201
- # @note dependencies introduced on `parse` call will be merged with those defined during the
202
- # initialization of the parser `attr`.
203
- # @raise [Exception] if there is **no** parser for attribute or type `attr`.
250
+ # Call to parser `source` value of attribute or type `attr` into
251
+ # an internal valid value.
252
+ # @note dependencies introduced on `parse` call will be merged with
253
+ # those defined during the initialization of the parser `attr`.
254
+ # @raise [Exception] if there is **no** parser for attribute or
255
+ # type `attr`.
204
256
  # @param attr [String] target attribute or type to **parse**.
205
257
  # @param source [Any] source value to be parsed.
206
- # @param phase [Symbol] the phase when the attr parser is expected to be called.
258
+ # @param phase [Symbol] the phase when the attr parser is expected
259
+ # to be called.
207
260
  # Must be [:internal, :final]
208
261
  # @param deps [Hash] key-value pairs of call dependencies.
209
262
  # @return [Any] a valid internal value.
210
263
  def parse(attr, source, phase = :internal, deps: {})
211
- raise "There is no parser for attribute '#{attr}'" if !self.defined?(attr)
212
- @parsers[attr].parse(source, phase, dependencies: deps)
264
+ msg = "There is no parser for attribute '#{attr}'"
265
+ raise msg unless self.defined?(attr)
266
+
267
+ @parsers[attr].parse(source, phase, dependencies: deps) do |_dkey, dval|
268
+ next dval unless dval.is_a?(Proc)
269
+ dval.call(self)
270
+ end
213
271
  end
214
272
 
215
273
  # Call to serialise `object` value of attribute or type `attr` into an external valid value.
@@ -223,8 +281,13 @@ module Eco
223
281
  # @param deps [Hash] key-value pairs of call dependencies.
224
282
  # @return a valid external value.
225
283
  def serialize(attr, object, phase = :person, deps: {})
226
- raise "There is no parser for attribute '#{attr}'" if !self.defined?(attr)
227
- @parsers[attr].serialize(object, phase, dependencies: deps)
284
+ msg = "There is no parser for attribute '#{attr}'"
285
+ raise msg unless self.defined?(attr)
286
+
287
+ @parsers[attr].serialize(object, phase, dependencies: deps) do |_dkey, dval|
288
+ next dval unless dval.is_a?(Proc)
289
+ dval.call(self)
290
+ end
228
291
  end
229
292
  # @!endgroup
230
293
 
@@ -247,7 +310,7 @@ module Eco
247
310
  end
248
311
 
249
312
  def attr_to_str(attr)
250
- attr.is_a?(Symbol)? ":#{attr.to_s}" : "#{attr.to_s}"
313
+ attr.is_a?(Symbol)? ":#{attr}" : attr.to_s
251
314
  end
252
315
 
253
316
  def valid?(attr)
@@ -265,7 +328,6 @@ module Eco
265
328
  def valid_format?(attr)
266
329
  attr.is_a?(Symbol) && FORMAT.include?(attr)
267
330
  end
268
-
269
331
  end
270
332
  end
271
333
  end
@@ -18,12 +18,13 @@ module Eco
18
18
  # @return [Array<PersonEntry>] `values` sorted by supervisors/subordinates
19
19
  def sort_by_supervisors(values, supervisors_first: true)
20
20
  raise "Expected non hash Enumerable. Given: #{values.class}" if values.is_a?(Hash)
21
- return [] unless values && values.is_a?(Enumerable)
22
- roam = Proc.new do |tree|
21
+ return [] unless values.is_a?(Enumerable)
22
+ roam = proc do |tree|
23
23
  [].tap do |out|
24
24
  sub_outs = tree.empty?? [] : tree.map {|sup, subtree| roam.call(subtree)}
25
+
25
26
  tree.each do |sup, subtree|
26
- sout = subtree.empty?? [] :roam.call(subtree)
27
+ sout = subtree.empty?? [] : roam.call(subtree)
27
28
  supervisors_first ? sout.unshift(sup) : sout.push(sup)
28
29
  out.concat(sout)
29
30
  end
@@ -34,13 +35,14 @@ module Eco
34
35
  end
35
36
 
36
37
  # Identifies all the cyclic supervisor chains
37
- # @note as `supervisors_tree` will have any entry involved in a cycle at the top, it just checks all the top entries against their offspring
38
+ # @note as `supervisors_tree` will have any entry involved in a cycle at the top,
39
+ # it just checks all the top entries against their offspring
38
40
  # @return [Array<Array>] the sets of entries that are cyclic
39
41
  def identify_cyclic_chains(values)
40
42
  raise "Expected non hash Enumerable. Given: #{values.class}" if values.is_a?(Hash)
41
- return [] unless values && values.is_a?(Enumerable)
43
+ return [] unless values.is_a?(Enumerable)
42
44
 
43
- identify = Proc.new do |top_sup, offspring, chain = [top_sup]|
45
+ identify = proc do |top_sup, offspring, chain = [top_sup]|
44
46
  next [] if offspring.empty?
45
47
  offspring.each_with_object([]) do |(sup, subordinates), set|
46
48
  break set unless set.empty?
@@ -80,15 +82,15 @@ module Eco
80
82
  # @param values [Enumerable<Object>] of objects with methods:
81
83
  # `id`, `external_id` and `supervisor_id`
82
84
  # @return [Hash] the tree structure
83
- def supervisors_tree(values)
85
+ def supervisors_tree(values) # rubocop:disable Metrics/AbcSize
84
86
  raise "Expected non hash Enumerable. Given: #{values.class}" if values.is_a?(Hash)
85
- return {} unless values && values.is_a?(Enumerable)
87
+ return {} unless values.is_a?(Enumerable)
86
88
  idx = get_super_indexes(values)
87
89
 
88
90
  processed = []
89
- subtree = Proc.new do |entry, level, toptree|
91
+ subtree = proc do |entry, level, toptree|
90
92
  if processed.include?(entry)
91
- next {} unless toptree.key?(entry) && level > 0
93
+ next {} unless toptree.key?(entry) && level.positive?
92
94
  # needs to be moved as a child
93
95
  subnodes = toptree.delete(entry)
94
96
  processed.delete(entry)
@@ -109,7 +111,7 @@ module Eco
109
111
 
110
112
  {}.tap do |tree|
111
113
  idx[:by_sup].keys.each do |sup_id|
112
- if sup = idx[:supers][sup_id]
114
+ if (sup = idx[:supers][sup_id])
113
115
  tree.merge!(subtree.call(sup, 0, tree))
114
116
  else
115
117
  idx[:by_sup][sup_id].each do |sub|
@@ -122,7 +124,7 @@ module Eco
122
124
 
123
125
  private
124
126
 
125
- def get_super_indexes(values)
127
+ def get_super_indexes(values) # rubocop:disable Metrics/AbcSize
126
128
  raise "Expected non hash Enumerable. Given: #{values.class}" if values.is_a?(Hash)
127
129
  {}.tap do |indexes|
128
130
  indexes[:by_id] = values.map do |e|
@@ -133,45 +135,42 @@ module Eco
133
135
  end.compact.to_h
134
136
 
135
137
  indexes[:by_sup] = {}.tap do |by_s|
136
- values.group_by do |e|
137
- e.supervisor_id
138
- end.tap do |by_sup|
138
+ values.group_by(&:supervisor_id).tap do |by_sup|
139
139
  by_s[nil] = by_sup.delete(nil) if by_sup.key?(nil)
140
140
  by_s.merge!(by_sup)
141
141
  end
142
142
  end
143
143
 
144
144
  indexes[:supers] = {}.tap do |sups|
145
- indexes[:by_ext].select do |ext, e|
145
+ indexes[:by_ext].select do |ext, _e|
146
146
  ext && indexes[:by_sup].key?(ext)
147
147
  end.tap {|found| sups.merge!(found)}
148
- indexes[:by_id].select do |id, e|
148
+ indexes[:by_id].select do |id, _e|
149
149
  id && indexes[:by_sup].key?(id)
150
150
  end.tap {|found| sups.merge!(found)}
151
151
  end
152
152
 
153
- indexes[:is_super] = Proc.new do |entry|
154
- !!(indexes[:supers][entry.id] || indexes[:supers][entry.external_id])
153
+ indexes[:is_super] = proc do |entry|
154
+ is = indexes[:supers][entry.id] || indexes[:supers][entry.external_id]
155
+ !is.nil?
155
156
  end
156
157
 
157
- indexes[:subordinates] = Proc.new do |entry|
158
+ indexes[:subordinates] = proc do |entry|
158
159
  subs = nil
159
- if sup = indexes[:supers][entry.id] || indexes[:supers][entry.external_id]
160
- subs ||= indexes[:by_sup][sup.id] unless !sup.id
161
- subs ||= indexes[:by_sup][sup.external_id] unless !sup.external_id
160
+ sup = indexes[:supers][entry.id] || indexes[:supers][entry.external_id]
161
+ if sup
162
+ subs ||= indexes[:by_sup][sup.id] if sup.id
163
+ subs ||= indexes[:by_sup][sup.external_id] if sup.external_id
162
164
  end
163
165
  subs
164
166
  end
165
167
  end
166
-
167
168
  end
168
-
169
169
  end
170
170
 
171
171
  class << self
172
172
  include SupervisorHelpers::ClassMethods
173
173
  end
174
-
175
174
  end
176
175
  end
177
176
  end
@@ -3,19 +3,17 @@ module Eco
3
3
  module Common
4
4
  module Session
5
5
  class BaseSession
6
- attr_accessor :session
7
- attr_accessor :environment, :config
6
+ attr_writer :session, :config
7
+ attr_reader :environment
8
8
  alias_method :enviro, :environment
9
- alias_method :enviro=, :environment=
10
-
11
- attr_reader :api, :file_manager, :logger
12
- alias_method :fm, :file_manager
13
9
 
14
10
  include Session::Helpers
15
11
 
16
- def initialize(e)
17
- raise "Expected object Eco::API::Common::Session::Environment. Given: #{e.class}" unless e.is_a?(Environment)
18
- self.environment = e
12
+ def initialize(env)
13
+ msg = "Expected object Eco::API::Common::Session::Environment. Given: #{env.class}"
14
+ raise msg unless env.is_a?(Environment)
15
+
16
+ self.environment = env
19
17
  end
20
18
 
21
19
  def session
@@ -26,6 +24,7 @@ module Eco
26
24
  @environment = nil
27
25
  @environment = value if value.is_a?(Environment)
28
26
  end
27
+ alias_method :enviro=, :environment=
29
28
 
30
29
  def config
31
30
  enviro.config
@@ -42,6 +41,7 @@ module Eco
42
41
  def file_manager
43
42
  enviro.file_manager
44
43
  end
44
+ alias_method :fm, :file_manager
45
45
 
46
46
  def mailer
47
47
  enviro.mailer
@@ -5,7 +5,6 @@ module Eco
5
5
  class Environment
6
6
  attr_reader :config, :session
7
7
  attr_reader :file_manager, :logger
8
- attr_reader :mailer, :sftp, :s3uploader
9
8
 
10
9
  alias_method :fm, :file_manager
11
10
 
@@ -13,12 +12,15 @@ module Eco
13
12
  #@param session [Eco::API::Session, nil] the current session
14
13
  def initialize(init = {}, session:)
15
14
  init = init.conf if init.is_a?(Environment)
15
+
16
16
  msg = "Expected object Eco::API::Session::Config or Environment. Given: #{init}"
17
17
  raise msg unless init.is_a?(Eco::API::Session::Config)
18
- raise "Expected an Eco::API::Session object. Given: #{session}" if session && !session.is_a?(Eco::API::Session)
19
18
 
20
- @config = init
21
- @session = session
19
+ invalid_session = session && !session.is_a?(Eco::API::Session)
20
+ raise "Expected an Eco::API::Session object. Given: #{session}" if invalid_session
21
+
22
+ @config = init
23
+ @session = session
22
24
  @file_manager = Eco::API::Common::Session::FileManager.new(enviro: self)
23
25
  @logger = Eco::API::Common::Session::Logger.new(enviro: self)
24
26
  end
@@ -43,7 +45,7 @@ module Eco
43
45
 
44
46
  def s3uploader
45
47
  return nil unless s3uploader?
46
- @s3uploader ||= Eco::API::Common::Session::S3Uploader.new(enviro: self)
48
+ @s3uploader ||= Eco::API::Common::Session::S3Uploader.new(enviro: self)
47
49
  end
48
50
 
49
51
  def s3uploader?
@@ -3,8 +3,11 @@ module Eco
3
3
  module Common
4
4
  module Session
5
5
  class SFTP
6
- def initialize (enviro:)
7
- raise "Required Environment object (enviro:). Given: #{enviro}" if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
6
+ include Eco::Language::AuxiliarLogger
7
+
8
+ def initialize(enviro:)
9
+ invalid_env = enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
10
+ raise "Required Environment object (enviro:). Given: #{enviro}" if invalid_env
8
11
  @enviro = enviro
9
12
  end
10
13
 
@@ -15,23 +18,22 @@ module Eco
15
18
  # @see Net::SFTP::Session
16
19
  def sftp_session
17
20
  require "net/sftp"
18
- begin
19
- @sftp_session ||= Net::SFTP.start(
20
- host,
21
- fetch_user,
22
- **session_options
23
- )
24
- rescue Exception => e
25
- msg = "Could not open SFTP session. Possible misconfiguration: #{e}"
26
- logger.error(msg)
27
- raise
28
- end
29
- @sftp_session
21
+
22
+ @sftp_session ||= Net::SFTP.start(
23
+ host,
24
+ fetch_user,
25
+ **session_options
26
+ )
27
+ rescue StandardError => err
28
+ log(:error) {
29
+ "Could not open SFTP session. Possible misconfiguration: #{err}"
30
+ }
31
+ raise
30
32
  end
31
33
 
32
34
  # @see Net::SFTP::Operations::Dir#entries
33
35
  def entries(path)
34
- sftp_session.dir.entries(path).sort_by {|rf| rf.name}
36
+ sftp_session.dir.entries(path).sort_by(&:name)
35
37
  end
36
38
 
37
39
  # **Files** of the remote directory.
@@ -40,8 +42,9 @@ module Eco
40
42
  # @param pattern [Regexp] if given, filters by using this pattern
41
43
  # @return [Array<Net::SFTP::Protocol::V01::Name>]
42
44
  def files(path, pattern: nil)
43
- entries = entries(path).select {|remote_file| remote_file.file?}
45
+ entries = entries(path).select(&:file?)
44
46
  return entries unless pattern
47
+
45
48
  entries.select {|remote_file| remote_file.name =~ pattern}
46
49
  end
47
50
 
@@ -51,20 +54,20 @@ module Eco
51
54
  # @param pattern [Regexp] if given, filters by using this pattern
52
55
  # @return [Array<Net::SFTP::Protocol::V01::Name>]
53
56
  def folders(path, pattern: nil)
54
- entries = entries(path).select {|remote_file| remote_file.directory?}
57
+ entries = entries(path).select(&:directory?)
55
58
  return entries unless pattern
59
+
56
60
  entries.select {|remote_file| remote_file.name =~ pattern}
57
61
  end
58
62
 
59
63
  # @see Net::SFTP::Session#rename
60
- def move(fullname_source, fullname_dest, flags=0x0001, override: true, &callback)
61
- begin
62
- sftp_session.rename!(fullname_source, fullname_dest, flags, &callback)
63
- rescue Net::SFTP::StatusException => e
64
- raise unless override
65
- sftp_session.remove(fullname_dest)
66
- sftp_session.rename!(fullname_source, fullname_dest, flags, &callback)
67
- end
64
+ def move(fullname_source, fullname_dest, flags = 0x0001, override: true, &callback)
65
+ sftp_session.rename!(fullname_source, fullname_dest, flags, &callback)
66
+ rescue Net::SFTP::StatusException => _err
67
+ raise unless override
68
+
69
+ sftp_session.remove(fullname_dest)
70
+ sftp_session.rename!(fullname_source, fullname_dest, flags, &callback)
68
71
  end
69
72
 
70
73
  # Downloads the files specified to a local folder
@@ -78,10 +81,29 @@ module Eco
78
81
  dest_fullname = File.join(local_folder || ".", basename)
79
82
  puts " • #{dest_fullname}"
80
83
  sftp_session.download(fullname, dest_fullname)
81
- end.each do |dw|
82
- # run SSH event loop while dw.active?
83
- dw.wait
84
+ end.each(&:wait)
85
+ # run SSH event loop while dw.active?
86
+ end
87
+
88
+ # Upload a file to the specific `remote_folder`
89
+ def upload(local_file, remote_folder:, gid: nil)
90
+ return false unless local_file && File.exist?(local_file)
91
+
92
+ dest_file = "#{remote_folder}/#{File.basename(local_file)}"
93
+ res = sftp_session.upload!(local_file, dest_file)
94
+
95
+ unless gid.nil?
96
+ attrs = sftp_session.stat!(dest_file)
97
+ unless gid == attrs.gid
98
+ flags = {permissions: 0o660, uid: attrs.uid, gid: gid}
99
+ _stat_res = sftp_session.setstat!(dest_file, flags)
100
+ end
84
101
  end
102
+
103
+ log(:info) {
104
+ "Uploaded '#{local_file}' (#{res})"
105
+ }
106
+ true
85
107
  end
86
108
 
87
109
  private
@@ -94,21 +116,26 @@ module Eco
94
116
  opts.merge!({password: fetch_password})
95
117
  else
96
118
  opts.merge!({
97
- keys: fetch_key_files,
98
- keys_only: true
119
+ keys: fetch_key_files,
120
+ keys_only: true
99
121
  })
100
122
  end
101
123
  end
102
124
  end
103
125
 
104
126
  def windows_basename(remote_fullname)
105
- parts = remote_fullname.split(/[\\\/]/).map {|node| node.gsub(/[<>:\\\/\|?*]/, '_')}
127
+ dir_sep = /[\\\/]/
128
+ patr_re = /[<>:\\\/|?*]/
129
+ parts = remote_fullname.split(dir_sep).map do |node|
130
+ node.gsub(patr_re, '_')
131
+ end
106
132
  local_fullname = File.join(*parts)
107
133
  File.basename(local_fullname)
108
134
  end
109
135
 
110
136
  def logger
111
- @enviro&.logger || ::Logger.new(IO::NULL)
137
+ @enviro&.logger ||
138
+ (defined?(super)? super : ::Logger.new(IO::NULL))
112
139
  end
113
140
 
114
141
  def config
@@ -6,12 +6,16 @@ module Eco
6
6
  attr_accessor :entry
7
7
 
8
8
  def identify(section = :person)
9
- if entry && section == :entry
10
- entry.to_s(:identify)
11
- else
12
- str_id = id ? "id: '#{id}'; " : ""
13
- "'#{name}' (#{str_id}ext_id: '#{external_id}'; email: '#{email}')"
14
- end
9
+ return entry.to_s(:identify) if entry && section == :entry
10
+ str_id = id ? "id: '#{id}'; " : ""
11
+ "'#{name}' (#{str_id}ext_id: '#{external_id}'; email: '#{email}')"
12
+ end
13
+
14
+ attr_writer :subordinates
15
+
16
+ # 2024-03-04 patch for API property deletetion
17
+ def subordinates
18
+ @subordinates ||= 0
15
19
  end
16
20
  end
17
21
  Ecoportal::API::V1::Person.include PersonPatch