eco-helpers 2.0.10 → 2.0.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8ca1a088cf60b7608e9f92da680a631c73228ae92fed33484b5669c480350fb
4
- data.tar.gz: 6733d55fbdcf24c2766529c275f86f3132f1c3232b4c8d31f34880f9253dbc82
3
+ metadata.gz: 9ea76fb273e572c873b2f23348c54b56fd4e51cd39d9bf1109ddbc4594ba260f
4
+ data.tar.gz: 56d64b1eb28202f4bd18997ff8854fdd6ae36c758217796f4739012f59d4ef5c
5
5
  SHA512:
6
- metadata.gz: d1cdc2aacd52c590230fe606b079d91e6f3dea01ddbd8857d48170467bbfd089ffa7081c8630aac93ebae4c6e3b8125a446cd7ddc8a6b290c174e906661b2474
7
- data.tar.gz: 356ccf7e50d3f0e12f9ad1dde6f1fe7ad4fd13205334f9319cecd45736f110684eb404113512aca4b1d2c49070fe61eb48b1e69c3ae13f423ae8895288ea0d2d
6
+ metadata.gz: bf49254f9b3f162f278d65f734fba3d3f683906cb1202df4d4cc4f7a90f2d663655e5168cf5bdea13d78b63d8ece32bcd22541655b001f3a9bfad429d89d2de8
7
+ data.tar.gz: 3dcd85969e34079aa050546e427ebe0d28cb7ec358b121f066cd603af23c9c44bb05551ce8db061f1721832981c2b74c1a46aa095c031f9ea0fb8b29b1b65310
data/CHANGELOG.md CHANGED
@@ -1,7 +1,47 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- ## [2.0.10] - 2021-03-xx
4
+ ## [2.0.15] - 2021-04-xx
5
+
6
+ ### Added
7
+ - Use case `Eco::API::UseCases::DefaultCases::SupersHierarchy`, invokable via `-supers-hiearchy`
8
+ ### Changed
9
+ ### Fixed
10
+ - `eco/cli/config/default/workflow.rb` prevent `rescue` looping
11
+
12
+ ## [2.0.14] - 2021-04-15
13
+
14
+ ### Added
15
+ - `Eco::API::UseCases::DefaultCases::ToCsvCase` added option `-internal-names` to avoid overriding data on export
16
+ - `Eco::API::Common::People::PersonEntry#mapped_entry` exposed method for raw `csv` generation
17
+ - `Eco::API::Organization::PresetsFactory` added integrity validation for `person_*` abilities
18
+ - `Eco::API::Session::Batch::Job` more debug info on erron handlers
19
+
20
+ ### Changed
21
+ ### Fixed
22
+ - `Eco::API::Error.get_type` was almost always matching `Eco::API::Error::Unclassified` -> fixed
23
+
24
+ ## [2.0.13] - 2021-03-31
25
+
26
+ ### Added
27
+ - Stats on the `Eco::API::Session::Batch`
28
+ - Allow to inherit and re-use the `Eco::API::UseCases::DefaultCase::HrisCase`
29
+
30
+ ### Fixed
31
+ - Ensure auto-loading for `Eco::API::UseCases`
32
+
33
+ ## [2.0.12] - 2021-03-29
34
+
35
+ ### Changed
36
+ - `Eco::CLI#run` it does include any defined states on the workflow
37
+
38
+
39
+ ## [2.0.11] - 2021-03-29
40
+
41
+ ### Fixed
42
+ - `Eco::API::Session::Batch:Errors#errors` changed `Array#filter` call to `Array#select` call
43
+
44
+ ## [2.0.10] - 2021-03-26
5
45
 
6
46
  ### Added
7
47
  - `Eco::API::Common::People::PersonParser`
@@ -16,34 +56,24 @@ All notable changes to this project will be documented in this file.
16
56
  - Ranamed `#defined_attrs` to `#defined_model_attrs`
17
57
  - Ranamed `#undefined_attrs` to `#undefined_model_attrs`
18
58
 
19
- ### Fixed
20
-
21
59
  ## [2.0.9] - 2021-03-19
22
60
 
23
- ### Added
24
61
  ### Changed
25
62
  - `Eco::API::Session::Batch:Errors#person_ref` moved to the public method
26
63
 
27
- ### Fixed
28
-
29
64
  ## [2.0.8] - 2021-03-09
30
65
 
31
- ### Added
32
- ### Changed
33
66
  ### Fixed
34
67
  - `Ecoportal::API::V1::Person#identity` adjust behavior
35
68
 
36
69
  ## [2.0.7] - 2021-03-09
37
70
 
38
- ### Added
39
- ### Changed
40
71
  ### Fixed
41
72
  - `Eco::API::Session::Batch::JobsGroups` and `Eco::API::Session::Batch::Jobs`: when new `Batch::Job`s are creating during launch, they remained unlaunched
42
73
  - this fix makes `#launch` method to iterate until there are no pending
43
74
 
44
75
  ## [2.0.6] - 2021-03-08
45
76
 
46
- ### Added
47
77
  ### Changed
48
78
  - `Eco::API::Session::Batch:Errors` moved some methods to be private
49
79
  ### Fixed
@@ -51,46 +81,33 @@ All notable changes to this project will be documented in this file.
51
81
 
52
82
  ## [2.0.5] - 2021-02-24
53
83
 
54
- ### Added
55
- ### Changed
56
84
  ### Fixed
57
85
  - `Eco::API::Common::People::PersonParser#symbol_keys` to use `Array#select`
58
86
  - as `Array#filter` was introduced in ruby `2.5.5`
59
87
 
60
88
  ## [2.0.4] - 2021-02-23
61
89
 
62
- ### Added
63
90
  ### Changed
64
91
  - `Eco::API::Common::People::PersonParser#defined_attrs` to include virtual attributes
65
92
 
66
- ### Fixed
67
-
68
93
  ## [2.0.3] - 2021-02-23
69
94
 
70
95
  ### Added
71
96
  - `Eco::API::UseCases::OozeSamples::OozeUpdateCase`: added integration to update page instances (non-templates)
72
97
 
73
- ### Changed
74
98
  ### Fixed
75
99
  - `Eco::API::Policies::DefaultPolicies::UserAccess` complete default policy code.
76
100
 
77
101
  ## [2.0.2] - 2021-02-22
78
102
 
79
- ### Added
80
103
  ### Changed
81
104
  - upgraded dependencies with `ecoportal-api`
82
105
 
83
- ### Fixed
84
-
85
-
86
106
  ## [2.0.1] - 2021-02-22
87
107
 
88
- ### Added
89
108
  ### Changed
90
109
  - upgraded dependencies with `ecoportal-api-oozes` to `ecoportal-api-v2`
91
110
 
92
- ### Fixed
93
-
94
111
  ## [1.5.15] - 2021-02-17
95
112
 
96
113
  ### Added
@@ -104,7 +121,6 @@ All notable changes to this project will be documented in this file.
104
121
  - `Eco::API::MicroCases#people_search`
105
122
  - `Eco::API::MicroCases#refresh`
106
123
 
107
- ### Changed
108
124
  ### Fixed
109
125
  - `Eco::API::Policies::DefaultPolicies::UserAccess` typos in default api policy
110
126
 
@@ -126,11 +142,8 @@ All notable changes to this project will be documented in this file.
126
142
  - `Eco::API::Common::People::PersonEntry` add error log when wrong email error is detected
127
143
  - previously it would have crashed
128
144
 
129
- ### Fixed
130
-
131
145
  ## [1.5.13] - 2021-02-01
132
146
 
133
- ### Added
134
147
  ### Changed
135
148
  - upgraded dependency with `ecoportal-api-oozes`
136
149
 
@@ -146,23 +159,16 @@ All notable changes to this project will be documented in this file.
146
159
  ### Changed
147
160
  - upgraded `ecoportal-api` dependency
148
161
 
149
- ### Fixed
150
-
151
-
152
162
  ## [1.5.11] - 2021-01-25
153
163
 
154
164
  ### Added
155
165
  - `Eco::API::Organization::TagTree#subtag?` to check if the tag is in any subtree.
156
166
 
157
- ### Changed
158
-
159
167
  ### Fixed
160
168
  - `Eco::API::MicroCases#set_supervisor` shouldn't set it if the entry does not have it.
161
169
 
162
170
  ## [1.5.10] - 2021-01-19
163
171
 
164
- ### Added
165
- ### Changed
166
172
  ### Fixed
167
173
  - `Eco::API::Session::Batch::Errors#print` show the row number of the input data.
168
174
 
@@ -171,14 +177,8 @@ All notable changes to this project will be documented in this file.
171
177
  ### Added
172
178
  - `Eco::API::Organization::TagTree#subtags` to get all the tags but those of the highest level.
173
179
 
174
- ### Changed
175
- ### Fixed
176
-
177
-
178
180
  ## [1.5.8] - 2021-01-05
179
181
 
180
- ### Added
181
- ### Changed
182
182
  ### Fixed
183
183
  - `Eco::API::Session::Batch::Jobs#job` shouldn't be calling the post-launch callback function on creation.
184
184
  - `Eco::API::Session#new_job` should include a `&block` parameter.
@@ -186,15 +186,11 @@ All notable changes to this project will be documented in this file.
186
186
 
187
187
  ## [1.5.7] - 2020-12-17
188
188
 
189
- ### Added
190
- ### Changed
191
189
  ### Fixed
192
190
  - `Eco::API::Sesssion#parse_attribute` was not using phase argument
193
191
 
194
192
  ## [1.5.6] - 2020-12-04
195
193
 
196
- ### Added
197
- ### Changed
198
194
  ### Fixed
199
195
  - `Eco::API::UseCases::DefaultCases::RestoreDBCase` fixed typo and slightly improved
200
196
  - fixed some back-end errors when chaining usecases
@@ -202,8 +198,6 @@ All notable changes to this project will be documented in this file.
202
198
 
203
199
  ## [1.5.5] - 2020-12-03
204
200
 
205
- ### Added
206
- ### Changed
207
201
  ### Fixed
208
202
  - rubies previous to `2.5` do not have `yield_self`
209
203
 
@@ -211,14 +205,11 @@ All notable changes to this project will be documented in this file.
211
205
 
212
206
  ### Added
213
207
  - update `ecoportal-api` dependency
214
- ### Changed
215
208
  ### Fixed
216
209
  - `Eco::API::MicroCases#people_refresh` typo
217
210
 
218
211
  ## [1.5.3] - 2020-11-30
219
212
 
220
- ### Added
221
- ### Changed
222
213
  ### Fixed
223
214
  - `Eco::API::Session::Batch::RequestStats#blanked_value?` better blank detection
224
215
  - `Eco::API::MicroCases#with_each_starter` rectified typo
@@ -359,14 +350,11 @@ All notable changes to this project will be documented in this file.
359
350
 
360
351
  ## [1.4.2] - 2020-07-23
361
352
 
362
- ### Added
363
- ### Changed
364
353
  ### Fixed
365
354
  - preserve backtrace on logging
366
355
 
367
356
  ## [1.4.0] - 2020-07-14
368
357
 
369
- ### Added
370
358
  ### Changed
371
359
  - change abilities to align with ecoPortal release `1.5.0`
372
360
  - remove some patches on `ecoportal-api`
@@ -376,22 +364,17 @@ All notable changes to this project will be documented in this file.
376
364
 
377
365
  ## [1.3.19] - 2020-07-23
378
366
 
379
- ### Added
380
- ### Changed
381
367
  ### Fixed
382
368
  - preserve backtrace on logging
383
369
 
384
370
  ## [1.3.18] - 2020-07-08
385
371
 
386
- ### Added
387
- ### Changed
388
372
  ### Fixed
389
373
  - the `update` case was missing the code to use the `default_usergroup`
390
374
 
391
375
 
392
376
  ## [1.3.17] - 2020-07-06
393
377
 
394
- ### Added
395
378
  ### Changed
396
379
  - the `hris` case should not only include as `leavers` those that have account, but anyone that leaves
397
380
  * as we could have active people with no account
@@ -414,7 +397,6 @@ All notable changes to this project will be documented in this file.
414
397
  ### Added
415
398
  - default usecase to export to `csv` (`-detailed`) now includes `"Supervisor Name"` column
416
399
  ### Changed
417
- ### Fixed
418
400
 
419
401
  ## [1.3.14] - 2020-06-10
420
402
 
@@ -422,33 +404,24 @@ All notable changes to this project will be documented in this file.
422
404
  - `Eco::API::Common::People::SupervisorHelpers` now has its methods as class methods
423
405
  ### Changed
424
406
  - upgraded `ecoportal-api` gem dependency to minimum version `0.5.6`
425
- ### Fixed
426
407
 
427
408
 
428
409
  ## [1.3.13] - 2020-05-29
429
410
 
430
- ### Added
431
- ### Changed
432
411
  ### Fixed
433
412
  - `Eco::API::Organization::TagTree#tag?` to accept `nil` by returning `false`
434
413
  - `Eco::API::Common::People::DefaultParsers::DateParser` will parse to `Date` class
435
414
  * it was parsing to `Time` class, while the native gem `ecoportal-api` parses as `Date` ([reference](https://gitlab.com/ecoPortal/ecoportal-api/-/blob/master/lib/ecoportal/api/v1/schema_field.rb))
436
415
  ## [1.3.12] - 2020-05-19
437
416
 
438
- ### Added
439
417
  ### Changed
440
418
  - stop using `email` as `external_id` on `People#person` & `People#find`
441
419
  * this should result in more accurate searches when using `:strict` options
442
- ### Fixed
443
-
444
420
 
445
421
  ## [1.3.11] - 2020-05-12
446
422
 
447
- ### Added
448
423
  ### Changed
449
424
  - remove popping up comments on `Eco::API::Organization::PolicyGroups#`
450
- ### Fixed
451
-
452
425
 
453
426
  ## [1.3.10] - 2020-05-12
454
427
 
@@ -457,14 +430,11 @@ All notable changes to this project will be documented in this file.
457
430
  - `config.people.default_usergroup`, when defined, will have effect on usecases: `update` (this case was missing the change)
458
431
  * on account creation, if the input file did not specify `policy_group_ids`
459
432
 
460
- ### Changed
461
433
  ### Fixed
462
434
  - `upsert`, `hris` and `create` usecases: fixed condition for use of default_usergroup
463
435
 
464
436
  ## [1.3.9] - 2020-05-12
465
437
 
466
- ### Added
467
- ### Changed
468
438
  ### Fixed
469
439
 
470
440
  - `usecase` callback was not receiving `usecase` paramater
@@ -8,7 +8,7 @@ module Eco
8
8
  # @return [Symbol] the `type` of usecase (i.e. `:sync`, `:transform`, `:import`, `:other`)
9
9
  def type(value = nil)
10
10
  unless value
11
- return @type || raise("You should specify a type of case [:sync, :transform, :import, :other] for #{self.class}")
11
+ return @type || raise("You should specify a type of case [:sync, :transform, :import, :other] for #{self}")
12
12
  end
13
13
  @type = value
14
14
  end
@@ -57,6 +57,11 @@ module Eco
57
57
  @internal_entry
58
58
  end
59
59
 
60
+ # @return [Hash] entry `Hash` with **internal** attribute names, but **external** types and values.
61
+ def mapped_entry
62
+ @mapped_entry
63
+ end
64
+
60
65
  # @note values ready to be set to a person.
61
66
  # @return [Hash] entry `Hash` with **internal** attribute names, values and types.
62
67
  def final_entry
data/lib/eco/api/error.rb CHANGED
@@ -80,6 +80,7 @@ module Eco
80
80
  next 1 if k1 < k2
81
81
  0
82
82
  end.tap do |siblings|
83
+ siblings.delete(Unclassified)
83
84
  if direct
84
85
  siblings.reject! do |si|
85
86
  siblings.any? {|s| si < s}
@@ -96,17 +97,18 @@ module Eco
96
97
  err_msg =~ @match
97
98
  end
98
99
 
99
- def get_type(err_msg)
100
+ def get_type(err_msg, first: true)
100
101
  type = nil
101
102
  descendants(direct: true).reverse.each do |klass|
102
103
  if klass.err_match?(err_msg)
103
104
  type = klass
104
105
  if klass.descendants?(direct: true)
105
- type = klass.get_type(err_msg) || type
106
+ type = klass.get_type(err_msg, first: false) || type
106
107
  end
107
108
  end
108
109
  end
109
- type
110
+ return type unless first
111
+ type || Unclassified
110
112
  end
111
113
 
112
114
  def known_err_class?(klass)
@@ -4,38 +4,27 @@ module Eco
4
4
 
5
5
  class PresetsFactory
6
6
  ABILITIES = File.join(__dir__, 'presets_values.json')
7
+ INTEGRITY = File.join(__dir__, 'presets_integrity.json')
7
8
  DEFAULT_CUSTOM = 'presets_custom.json'
8
9
  DEFAULT_MAP = 'presets_map.json'
9
10
 
10
11
  def initialize(presets_custom: DEFAULT_CUSTOM, presets_map: DEFAULT_MAP, enviro: nil, policy_groups: nil)
11
- @abilities = JSON.load(File.open(ABILITIES))
12
- @habilities = @abilities.map do |key, values|
13
- h_values = values.map { |v| [v, true] }.to_h
14
- [key, h_values]
15
- end.to_h
16
-
17
12
  fatal("Expecting Environment object. Given: #{enviro}") if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
18
- @enviro = enviro
19
-
20
- policy_groups = policy_groups || @enviro&.api&.policy_groups.to_a
21
- if policy_groups.is_a?(Eco::API::Organization::PolicyGroups)
22
- @policy_groups = policy_groups
23
- else
24
- @policy_groups = Eco::API::Organization::PolicyGroups.new(policy_groups)
25
- end
13
+ @enviro = enviro
26
14
 
27
- init_custom(presets_custom)
28
- init_map(presets_map)
15
+ @policy_groups = policy_groups
16
+ @presets_custom_file = presets_custom || DEFAULT_CUSTOM
17
+ @presets_map_file = presets_map || DEFAULT_MAP
29
18
  end
30
19
 
31
20
  def new(*policy_group_ids_or_names)
32
21
 
33
22
  names = policy_group_ids_or_names.map do |id_name|
34
- @policy_groups.to_name(id_name)&.downcase
23
+ policy_groups.to_name(id_name)&.downcase
35
24
  end.compact
36
25
 
37
- if @presets_map
38
- preset_names = names.map { |name| @presets_map.fetch(name, nil) }
26
+ if presets_map
27
+ preset_names = names.map { |name| presets_map.fetch(name, nil) }
39
28
  else # option to do not use preset mapping (so just the policy group name)
40
29
  preset_names = names
41
30
  end
@@ -44,69 +33,11 @@ module Eco
44
33
 
45
34
  # @return [Array<String>] all the abilities
46
35
  def keys
47
- @abilities.keys
36
+ abilities_model.keys
48
37
  end
49
38
 
50
39
  private
51
40
 
52
- def init_custom(file = DEFAULT_CUSTOM)
53
- @presets_custom = nil
54
-
55
- return if !file
56
- file = File.expand_path(file)
57
-
58
- if File.exists?(file)
59
- @presets_custom = JSON.load(File.open(file))
60
-
61
- errors = @presets_custom.map do |key, preset|
62
- (err = preset_errors(preset)) ? "{ '#{key}' preset -> #{err}}": nil
63
- end.compact
64
-
65
- fatal("File '#{file}' contains invalid presets:\n #{errors.join("\n ")}") if errors.length > 0
66
- end
67
-
68
- end
69
-
70
- def init_map(file = DEFAULT_MAP)
71
- @presets_map = nil
72
-
73
- return if !file
74
- file = File.expand_path(file)
75
-
76
- if File.exists?(file)
77
- fatal("Maps file specified without custom presets file. Aborting!") if !@presets_custom
78
- @presets_map = JSON.load(File.open(file))
79
-
80
- errors = []
81
- if @policy_groups.length > 0
82
- errors = @policy_groups.map do |pg|
83
- exists = @presets_map[pg.name.downcase] || @presets_custom[pg.name.downcase]
84
- exists ? nil : "'#{pg.name}'"
85
- end.compact
86
-
87
- warn("No maps or no preset for policy group(s): #{errors.join(", ")}") if errors.length > 0
88
- end
89
-
90
- errors = @presets_map.map do |source, dest|
91
- @presets_custom[dest] ? nil : "'#{dest}'"
92
- end.compact
93
-
94
- warn("Unexisting mapped preset(s): #{errors.uniq.join(", ")}") if errors.length > 0
95
- end
96
-
97
- end
98
-
99
- def fatal(msg)
100
- raise msg if !@enviro
101
- @enviro.logger.fatal(msg)
102
- raise msg
103
- end
104
-
105
- def warn(msg)
106
- raise msg if !@enviro
107
- @enviro.logger.warn(msg)
108
- end
109
-
110
41
  def compile(*preset_names)
111
42
  fatal("You need to specify an existing file for the custom presets.") if !@presets_custom
112
43
  @presets_custom.values_at(*preset_names).compact.reduce({}) do |p1, p2|
@@ -117,7 +48,7 @@ module Eco
117
48
  def merge(preset1, preset2)
118
49
  keys = preset1.keys | preset2.keys
119
50
 
120
- @abilities.each_with_object({}) do |(key, values), result|
51
+ abilities_model.each_with_object({}) do |(key, values), result|
121
52
  next unless keys.include?(key)
122
53
  idx = [
123
54
  values.index(preset1[key]),
@@ -129,17 +60,149 @@ module Eco
129
60
 
130
61
  # unsused: only play with the given abilities
131
62
  def empty_model
132
- JSON.parse(@abilities.to_json).transform_values {|v| nil }
63
+ JSON.parse(abilities_model.to_json).transform_values {|v| nil }
133
64
  end
134
65
 
135
66
  def preset_errors(preset)
136
67
  return "No preset given" if !preset
137
68
  errors = preset.map do |k, v|
138
- @habilities.dig(k, v) ? nil : "#{k}:#{v}"
69
+ value_exists?(k, v) ? nil : "#{k}:#{v}"
139
70
  end.compact
140
- return " unknown: {#{errors.join(", ")}}" if errors.length > 0
71
+ return " Unknown: {#{errors.join(", ")}}" if errors.length > 0
141
72
  nil
142
73
  end
74
+
75
+ def preset_integrity(preset)
76
+ preset.each_with_object([]) do |(ability, value), errors|
77
+ next unless checks = integrity_model[ability]
78
+
79
+ suberrors = []
80
+
81
+ checks.each do |check|
82
+ next unless check["value"] == value
83
+ check["conditions"].each do |cond, targets|
84
+ case cond
85
+ when "at_least"
86
+ targets.each do |other, minimum|
87
+ unless (ability_value_idx(other, minimum) <= ability_value_idx(other, preset[other]))
88
+ suberrors << "'#{other}' should be at least '#{minimum}'"
89
+ end
90
+ end
91
+ when "one_of"
92
+ unless targets.any? {|other, expected| preset[other] == expected}
93
+ suberrors << targets.each_with_object([]) do |(other, expected), out|
94
+ out << "'#{other}': '#{expected}'"
95
+ end.join(", ").yield_self do |msg|
96
+ "there should be at least one of: {#{msg}}"
97
+ end
98
+ end
99
+ else
100
+ warn("Unsuported integrity condition statement '#{cond}' in '#{ability}' with level '#{value}'")
101
+ end
102
+ end
103
+ end
104
+
105
+ if suberrors.length > 0
106
+ errors << "Incorrect value '#{value}' for '#{ability}' - reasons: {#{suberrors.join(", ")}}"
107
+ end
108
+ end.yield_self do |errors|
109
+ " Integrity errors: { #{errors.join(", ")} }" if errors.length > 0
110
+ end
111
+ end
112
+
113
+ def integrity_model
114
+ @integrity_model ||= JSON.load(File.open(INTEGRITY))
115
+ end
116
+
117
+ def value_exists?(ability, value)
118
+ abilities_model_inverted.dig(ability, value)
119
+ end
120
+
121
+ def abilities_model_inverted
122
+ @abilities_model_inverted ||= abilities_model.each_with_object({}) do |(key, values), out|
123
+ out[key] = values.each_with_object({}) {|v, h| h[v] = true }
124
+ end
125
+ end
126
+
127
+ def ability_value_idx(ability, value)
128
+ abilities_model[ability].index(value)
129
+ end
130
+
131
+ def abilities_model
132
+ @abilities_model ||= JSON.load(File.open(ABILITIES))
133
+ end
134
+
135
+ def policy_groups
136
+ return @policy_groups if @policy_groups.is_a?(Eco::API::Organization::PolicyGroups)
137
+ @policy_groups ||= @enviro&.api&.policy_groups.to_a
138
+
139
+ unless @policy_groups.is_a?(Eco::API::Organization::PolicyGroups)
140
+ @policy_groups = Eco::API::Organization::PolicyGroups.new(@policy_groups)
141
+ end
142
+ @policy_groups
143
+ end
144
+
145
+ def presets_custom
146
+ return @presets_custom if instance_variable_defined?(:@presets_custom)
147
+ @presets_custom = nil
148
+ if @presets_custom_file
149
+ if (file = File.expand_path(@presets_custom_file)) && File.exists?(file)
150
+ @presets_custom = JSON.load(File.open(file)).tap do |custom_presets|
151
+ errors = custom_presets.each_with_object([]) do |(key, preset), errors|
152
+ if err = preset_errors(preset)
153
+ errors << "{ '#{key}' preset -> #{err}}"
154
+ end
155
+ if err = preset_integrity(preset)
156
+ errors << "{ '#{key}' preset -> #{err}}"
157
+ end
158
+ end
159
+
160
+ fatal("File '#{file}' contains invalid presets:\n #{errors.join("\n ")}") if errors.length > 0
161
+ end
162
+ end
163
+ end
164
+ end
165
+
166
+ def presets_map
167
+ return @presets_map if instance_variable_defined?(:@presets_map)
168
+ @presets_map = nil
169
+ if @presets_map_file
170
+ if (file = File.expand_path(@presets_map_file)) && File.exists?(file)
171
+ fatal("Maps file specified without 'presets_custom.json' file. Aborting!") if !presets_custom
172
+ @presets_map = JSON.load(File.open(file)).tap do |map_presets|
173
+
174
+ errors = []
175
+ if policy_groups.length > 0
176
+ errors = policy_groups.map do |pg|
177
+ exists = map_presets[pg.name.downcase] || presets_custom[pg.name.downcase]
178
+ exists ? nil : "'#{pg.name}'"
179
+ end.compact
180
+
181
+ warn("No maps or no preset for policy group(s): #{errors.join(", ")}") if errors.length > 0
182
+ end
183
+
184
+ errors = map_presets.map do |source, dest|
185
+ presets_custom[dest] ? nil : "'#{dest}'"
186
+ end.compact
187
+
188
+ warn("Unexisting mapped preset(s): #{errors.uniq.join(", ")}") if errors.length > 0
189
+
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ def fatal(msg)
196
+ raise msg if !@enviro
197
+ @enviro.logger.fatal(msg)
198
+ raise msg
199
+ end
200
+
201
+ def warn(msg)
202
+ raise msg if !@enviro
203
+ @enviro.logger.warn(msg)
204
+ end
205
+
143
206
  end
144
207
 
145
208
  end
@@ -0,0 +1,52 @@
1
+ {
2
+ "person_core_create": [
3
+ { "value": "create", "conditions": {
4
+ "at_least": {"person_core": "view_people_manager"}
5
+ }
6
+ }
7
+ ],
8
+ "person_core_edit": [
9
+ { "value": "edit", "conditions": {
10
+ "at_least": {"person_core": "view_people_manager"}
11
+ }
12
+ }
13
+ ],
14
+ "person_details": [
15
+ { "value": "view", "conditions": {
16
+ "at_least": {"person_core": "attach"}
17
+ }
18
+ },
19
+ { "value": "edit_public", "conditions": {
20
+ "one_of": {
21
+ "person_core_edit": "edit",
22
+ "person_core_create": "create"
23
+ }
24
+ }
25
+ },
26
+ { "value": "view_private", "conditions": {
27
+ "at_least": {"person_core": "attach" }
28
+ }
29
+ },
30
+ { "value": "edit_private", "conditions": {
31
+ "one_of": {
32
+ "person_core_edit": "edit",
33
+ "person_core_create": "create"
34
+ }
35
+ }
36
+ }
37
+ ],
38
+ "person_account": [
39
+ { "value": "view", "conditions": {
40
+ "at_least": {"person_core": "attach" }
41
+ }
42
+ },
43
+ { "value": "create", "conditions": {
44
+ "at_least": {"person_core_create": "create"}
45
+ }
46
+ },
47
+ { "value": "edit", "conditions": {
48
+ "at_least": {"person_core_edit": "edit"}
49
+ }
50
+ }
51
+ ]
52
+ }
@@ -105,10 +105,15 @@ module Eco
105
105
  iterations = (data.length.to_f / per_page).ceil
106
106
 
107
107
  Eco::API::Session::Batch::Status.new(enviro, queue: data, method: method).tap do |status|
108
+ start_time = Time.now
109
+ start_slice = Time.now; slice = []
108
110
  data.each_slice(per_page) do |slice|
109
- msg = "starting batch '#{method}' iteration #{iteration}/#{iterations}, with #{slice.length} entries of #{data.length} -- #{done} done"
111
+ msg = "starting batch '#{method}' iteration #{iteration}/#{iterations},"
112
+ msg += " with #{slice.length} entries of #{data.length} -- #{done} done"
113
+ msg += " (last: #{str_stats(start_slice, slice.length)}; total: #{str_stats(start_time, done)})"
110
114
  logger.info(msg) unless silent
111
115
 
116
+ start_slice = Time.now
112
117
  people_api.batch do |batch|
113
118
  slice.each do |person|
114
119
  batch.public_send(method, person) do |response|
@@ -118,12 +123,23 @@ module Eco
118
123
  end
119
124
  end # next batch
120
125
 
121
- iteration += 1
122
- done += slice.length
126
+ iteration += 1
127
+ done += slice.length
123
128
  end # next slice
124
129
  end
125
130
  end
126
131
 
132
+ def str_stats(start, count)
133
+ now = Time.now
134
+ secs = (now - start).round(3)
135
+ if secs > 0.0
136
+ per_sec = (count.to_f / secs).round(2)
137
+ "#{secs}s -> #{per_sec} people/s"
138
+ else
139
+ " -- "
140
+ end
141
+ end
142
+
127
143
  end
128
144
  end
129
145
  end
@@ -124,7 +124,7 @@ module Eco
124
124
  # - please, observe that this can only happen if there were repeated entries in the `source_queue`
125
125
  # @return [Array<Hash>, Array<Ecoportal::API::V1::Person>, Array<Ecoportal::API::Internal::Person>]
126
126
  def entries
127
- queue.filter.with_index do |query, i|
127
+ queue.select.with_index do |query, i|
128
128
  unless response = status[i]
129
129
  msg = "Error: query with no response. You might have duplicated entries in your queue.\n"
130
130
  msg += "Queue length: #{queue.length}; Queue elements class: #{queue.first.class}\n"
@@ -45,25 +45,22 @@ module Eco
45
45
  # @!group Pure feedback methods
46
46
 
47
47
  # Slightly modifies the behaviour of `Ecoportal::API::Common::BaseModel#as_update`, so schema details fields show the `alt_id`
48
+ # It also fixes possible patch updates that are incomplete or unnecessary.
48
49
  # @note for better feedback
49
50
  # @param entry [Hash, Ecoportal::API::V1::Person, Ecoportal::API::Internal::Person]
50
- def as_update(entry)
51
+ # @param add_feedback [Boolean] if `true` it tweak the hash update with additional data.
52
+ def as_update(entry, add_feedback: true)
51
53
  case
52
54
  when entry.is_a?(Hash)
53
55
  hash = entry
54
56
  else #entry.is_a?(Ecoportal::API::V1::Person)
55
57
  if only_ids?
56
- hash = {
57
- "id" => entry.id,
58
- "external_id" => entry.external_id,
59
- "email" => entry.email
60
- }
61
58
  hash = entry.as_json.slice("id", "external_id", "email")
62
59
  else
63
60
  hash = entry.as_update
64
- #if entry.details
65
- if hash["details"]
66
- if hfields = hash.dig("details", "fields")
61
+
62
+ if add_feedback && details = hash["details"]
63
+ if hfields = details["fields"]
67
64
  hfields.each do |fld|
68
65
  fld.merge!("alt_id" => entry.details.get_field(fld["id"]).alt_id)
69
66
  end
@@ -169,7 +169,8 @@ module Eco
169
169
 
170
170
  unless simulate
171
171
  if pqueue.length > 0
172
- backup_update(requests)
172
+ req_backup = pqueue.map {|e| as_update(e, add_feedback: false)}
173
+ backup_update(req_backup)
173
174
  session.batch.launch(pqueue, method: type).tap do |job_status|
174
175
  @status = job_status
175
176
  status.root = self
@@ -217,7 +218,7 @@ module Eco
217
218
  [].tap do |msg|
218
219
  subjobs.map {|subjob| msg << subjob.summary}
219
220
  end.join("\n")
220
- end
221
+ end
221
222
 
222
223
  def as_update(*args)
223
224
  feedback.as_update(*args)
@@ -287,10 +288,13 @@ module Eco
287
288
  handlers = session.config.error_handlers
288
289
  if status.errors.any? && !handlers.empty? && !error_handler?
289
290
  err_types = status.errors.by_type
291
+ logger.debug("(#{self.name}) got these error types: #{err_types.keys}")
290
292
  handlers.each do |handler|
291
293
  if entries = err_types[handler.name]
292
294
  handler_job = subjobs_add("#{self.name} => #{handler.name}", usecase: handler)
295
+ logger.debug("Running error handler #{handler.name}")
293
296
  handler.launch(people: people(entries), session: session, options: options, job: handler_job)
297
+ logger.debug("Launching job of error handler: #{handler_job.name}")
294
298
  handler_job.launch(simulate: simulate)
295
299
  end
296
300
  end
@@ -30,6 +30,7 @@ module Eco
30
30
  @usecases = {}
31
31
  @cache_init = false
32
32
  @cases_by_name = {}
33
+ init_caches
33
34
  end
34
35
 
35
36
  def length
@@ -79,6 +80,7 @@ module Eco
79
80
 
80
81
  # @return [Eco::API::UseCases] a copy of instance object of `self`.
81
82
  def dup
83
+ init_caches
82
84
  self.class.new.merge(self)
83
85
  end
84
86
 
@@ -32,6 +32,7 @@ require_relative 'default_cases/reset_landing_page_case'
32
32
  require_relative 'default_cases/restore_db_case'
33
33
  require_relative 'default_cases/set_default_tag_case'
34
34
  require_relative 'default_cases/set_supervisor_case'
35
+ require_relative 'default_cases/supers_hierarchy_case'
35
36
  require_relative 'default_cases/switch_supervisor_case'
36
37
  require_relative 'default_cases/to_csv_case'
37
38
  require_relative 'default_cases/to_csv_detailed_case'
@@ -2,18 +2,17 @@ class Eco::API::UseCases::DefaultCases::HrisCase < Eco::API::Common::Loaders::Us
2
2
  name "hris"
3
3
  type :sync
4
4
 
5
+ attr_reader :creation, :update, :supers, :leavers
6
+
5
7
  def main(entries, people, session, options, usecase)
6
8
  micro = session.micro
7
- creation = session.new_job("main", "create", :create, usecase)
8
- update = session.new_job("main", "update", :update, usecase)
9
- supers = session.new_job("post", "supers", :update, usecase, :core)
10
- leavers = session.new_job("post", "leavers", :update, usecase, :account)
9
+ @creation = session.new_job("main", "create", :create, usecase)
10
+ @update = session.new_job("main", "update", :update, usecase)
11
+ @supers = session.new_job("post", "supers", :update, usecase, :core)
12
+ @leavers = session.new_job("post", "leavers", :update, usecase, :account)
11
13
 
12
14
  micro.with_each_leaver(entries, people, options) do |person|
13
- leavers.add(person) do |person|
14
- person.supervisor_id = nil
15
- person.account = nil if person.account
16
- end
15
+ leavers.add(person, &method(:leavers_callback))
17
16
  end
18
17
 
19
18
  micro.with_each(entries, people, options) do |entry, person|
@@ -24,4 +23,11 @@ class Eco::API::UseCases::DefaultCases::HrisCase < Eco::API::Common::Loaders::Us
24
23
  end
25
24
  end
26
25
 
26
+ private
27
+
28
+ def leavers_callback(person)
29
+ person.supervisor_id = nil
30
+ person.account = nil if person.account
31
+ end
32
+
27
33
  end
@@ -0,0 +1,59 @@
1
+ class Eco::API::UseCases::DefaultCases::SupersHierarchy < Eco::API::Common::Loaders::UseCase
2
+ name "supers-hierarchy"
3
+ type :export
4
+
5
+ attr_reader :session, :people, :options
6
+
7
+ def main(people, session, options, usecase)
8
+ @session = session; @options = options; @people = people
9
+
10
+ save!(hierarchy)
11
+ exit(0)
12
+ end
13
+
14
+ private
15
+
16
+ def hierarchy
17
+ Eco::API::Common::People::SupervisorHelpers.supervisors_tree(people)
18
+ end
19
+
20
+ def file
21
+ @file ||= options.dig(:output, :file) || "supers_hierarchy.txt"
22
+ end
23
+
24
+ def save!(data)
25
+ ext = File.extname(file).downcase.delete(".")
26
+
27
+ File.open(file, "w") do |fd|
28
+ if ext == "txt"
29
+ create_file(data, file: file, format: :txt)
30
+ elsif ext == "html"
31
+ puts "html is still not supported"
32
+ exit(1)
33
+ create_file(data, file: file, format: :html)
34
+ elsif ext == "json"
35
+ puts "json is still not supported"
36
+ exit(1)
37
+ create_file(data, file: file, format: :json)
38
+ end
39
+ end
40
+ end
41
+
42
+ def create_file(tree, file:, format: :txt)
43
+ File.open(file, "w") do |fd|
44
+ fd << tree_to_str(tree, format: format)
45
+ end
46
+ puts "Generated file #{file}"
47
+ end
48
+
49
+ def tree_to_str(tree, lev: 0, format: :txt)
50
+ raise "Required Hash tree structure. Given: #{tree.class}" unless tree.is_a?(Hash)
51
+ "".tap do |str|
52
+ tree.each do |entry, subtree|
53
+ str << "#{" " * lev}#{(lev > 0)? "+-#{lev}- " : ""}#{entry.name} (#{entry.external_id}|#{entry.email}|#{entry.id})\n"
54
+ str << tree_to_str(subtree, lev: lev + 1, format: format) unless !subtree || subtree.empty?
55
+ end
56
+ end
57
+ end
58
+
59
+ end
@@ -2,7 +2,11 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
2
2
  name "to-csv"
3
3
  type :export
4
4
 
5
+ attr_reader :session, :options, :people
6
+
5
7
  def main(people, session, options, usecase)
8
+ @session = session; @options = options; @people = people
9
+
6
10
  unless people && !people.empty?
7
11
  session.logger.warn("No source people to create the file... aborting!")
8
12
  return false
@@ -14,32 +18,48 @@ class Eco::API::UseCases::DefaultCases::ToCsvCase < Eco::API::Common::Loaders::U
14
18
  end
15
19
 
16
20
  session.logger.info("going to create file: #{file}")
17
- CSV.open(file, "w") do |csv|
18
- deps = {"supervisor_id" => {people: people}}
19
- entry = session.new_entry(people.first, dependencies: deps)
20
- header = entry.external_entry.keys
21
-
22
- if options.dig(:nice_header) || options.dig(:export, :options, :nice_header)
23
- name_maps = session.schema.fields_by_alt_id.transform_values do |fld|
24
- fld.name
25
- end.merge({
26
- "policy_group_ids" => "User Group(s)",
27
- "email" => "Email",
28
- "name" => "Name",
29
- "supervisor_id" => "Manager ID",
30
- "filter_tags" => "Locations",
31
- "default_tag" => "Default Location",
32
- "id" => "ecoPortal ID"
33
- })
34
- header = header.map {|name| name_maps[name] ? name_maps[name] : name}
35
- end
36
21
 
37
- csv << header
22
+ CSV.open(file, "w") do |csv|
23
+ csv << spot_header
38
24
  people.each do |person|
39
- csv << session.new_entry(person, dependencies: deps).external_entry.values
25
+ csv << target_entry_type(person).values
40
26
  end
41
27
  end
42
28
  exit(0)
43
29
  end
44
30
 
31
+ private
32
+
33
+ def target_entry_type(person)
34
+ session.new_entry(person, dependencies: deps).yield_self do |person_entry|
35
+ options.dig(:export, :options, :internal_names) ? person_entry.mapped_entry : person_entry.external_entry
36
+ end
37
+ end
38
+
39
+ def deps
40
+ @deps ||= {"supervisor_id" => {people: people}}
41
+ end
42
+
43
+ def spot_header
44
+ entry = target_entry_type(people.first)
45
+ header = entry.keys
46
+
47
+ if options.dig(:nice_header) || options.dig(:export, :options, :nice_header)
48
+ name_maps = session.schema.fields_by_alt_id.transform_values do |fld|
49
+ fld.name
50
+ end.merge({
51
+ "policy_group_ids" => "User Group(s)",
52
+ "email" => "Email",
53
+ "name" => "Name",
54
+ "supervisor_id" => "Manager ID",
55
+ "filter_tags" => "Locations",
56
+ "default_tag" => "Default Location",
57
+ "id" => "ecoPortal ID"
58
+ })
59
+ header = header.map {|name| name_maps[name] ? name_maps[name] : name}
60
+ end
61
+ header
62
+ end
63
+
64
+
45
65
  end
data/lib/eco/cli.rb CHANGED
@@ -22,16 +22,17 @@ module Eco
22
22
 
23
23
  def run(session:)
24
24
  io = Eco::API::UseCases::BaseIO.new(session: session, options: options)
25
- #session.workflow.run(io: io)
26
- session.workflow(io: io) do |wf, io|
27
- io = wf.run(:options, io: io)
28
- io = wf.run(:load, io: io)
29
- io = wf.run(:usecases, io: io)
30
- io = wf.run(:launch_jobs, io: io)
31
- io = wf.run(:post_launch, io: io)
32
- io = wf.run(:end, io: io)
33
- io = wf.run(:close, io: io)
34
- end
25
+ session.workflow(io: io).run(io: io)
26
+ #session.workflow(io: io) do |wf, io|
27
+ # io = wf.run(:options, io: io)
28
+ # io = wf.run(:load, io: io)
29
+ # io = wf.run(:usecases, io: io)
30
+ # io = wf.run(:launch_jobs, io: io)
31
+ # io = wf.run(:post_launch, io: io)
32
+ # io = wf.run(:report, io: io)
33
+ # io = wf.run(:end, io: io)
34
+ # io = wf.run(:close, io: io)
35
+ #end
35
36
  end
36
37
 
37
38
  end
@@ -1,12 +1,18 @@
1
1
  ASSETS.cli.config do |cnf|
2
2
  cnf.usecases do |cases|
3
3
 
4
+ desc = "Draws the Supervisors hiearchy in a file (use option -to file.ext)"
5
+ cases.add("-supers-hierarchy", :export, desc, case_name: "supers-hierarchy") do |people, session, options|
6
+ file = (SCR.get_arg("-to") && SCR.get_file("-to", required: true, should_exist: false)) || "supers_hierarchy.txt"
7
+ options.deep_merge!(output: {file: file})
8
+ end
9
+
4
10
  desc = "It exports to a CSV the (filtered) people"
5
11
  cases.add("-people-to-csv", :export, desc) do |people, session, options|
6
12
  file = SCR.get_file("-people-to-csv", required: true, should_exist: false)
7
13
  options.deep_merge!(export: {file: {name: file, format: :csv}})
8
14
  options.deep_merge!(export: {options: {nice_header: true}}) if SCR.get_arg("-nice-header")
9
-
15
+ options.deep_merge!(export: {options: {internal_names: true}}) if SCR.get_arg("-internal-names")
10
16
  case_name = SCR.get_arg("-detailed")? "to-csv-detailed" : "to-csv"
11
17
  session.usecases.case(case_name)
12
18
  end
@@ -73,7 +79,7 @@ ASSETS.cli.config do |cnf|
73
79
  end
74
80
 
75
81
  options.deep_merge!(ignore: {missing: {policy_groups: true}}) if SCR.get_arg("-ignore-missing-policy-groups")
76
-
82
+
77
83
  end
78
84
 
79
85
  desc = "Restores the people manager by using a backup.json file"
@@ -2,8 +2,13 @@ ASSETS.cli.config do |config|
2
2
  ASSETS.config.workflow do |wf|
3
3
 
4
4
  io = nil
5
+ rescued = false
6
+
5
7
  # default rescue
6
8
  wf.rescue do |exception, io|
9
+ next io if rescued
10
+ rescued = true
11
+
7
12
  io.session.logger.debug(exception.patch_full_message)
8
13
  wf.run(:close, io: io)
9
14
  io
data/lib/eco/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Eco
2
- VERSION = "2.0.10"
2
+ VERSION = "2.0.15"
3
3
  end
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.0.10
4
+ version: 2.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura
@@ -340,6 +340,7 @@ files:
340
340
  - lib/eco/api/organization/preferences.rb
341
341
  - lib/eco/api/organization/preferences_reference.json
342
342
  - lib/eco/api/organization/presets_factory.rb
343
+ - lib/eco/api/organization/presets_integrity.json
343
344
  - lib/eco/api/organization/presets_reference.json
344
345
  - lib/eco/api/organization/presets_values.json
345
346
  - lib/eco/api/organization/tag_tree.rb
@@ -398,6 +399,7 @@ files:
398
399
  - lib/eco/api/usecases/default_cases/restore_db_case.rb
399
400
  - lib/eco/api/usecases/default_cases/set_default_tag_case.rb
400
401
  - lib/eco/api/usecases/default_cases/set_supervisor_case.rb
402
+ - lib/eco/api/usecases/default_cases/supers_hierarchy_case.rb
401
403
  - lib/eco/api/usecases/default_cases/switch_supervisor_case.rb
402
404
  - lib/eco/api/usecases/default_cases/to_csv_case.rb
403
405
  - lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb