eco-helpers 2.0.10 → 2.0.15

Sign up to get free protection for your applications and to get access to all the features.
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