eco-helpers 2.0.13 → 2.0.14

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: 726f5a5930889b9fd59b4acc4ce7b938e3c6011909c2f5a5bb1a105a36df4d9b
4
- data.tar.gz: 804bc3ae796491543ec3615e9f11308333e7ea6fc9a10f943778f66922aa4a25
3
+ metadata.gz: 697d71754e809691a39f62a86d5fc46e2988578c19c965945e1c06ea73b290b6
4
+ data.tar.gz: 01ef5ca1a9bcf941d44b5f7088f54dd9c2cba950cc562ed0516400811df60f12
5
5
  SHA512:
6
- metadata.gz: f2e1c852b4660ae1bdc3e94bab274f3d4b7168c6affef57315b370af102b71cff19cdb0c2de0b59899f82b7f2f8b0478688cf018f3c52f26c1f1d891ed19cd65
7
- data.tar.gz: 032a93267eb45f7550fbdad7805d0f472a0ea1a98353b089147bd71de785cdeaa4933b233bdd22d4ab69f7a85bfa65ec1f0850fe82e446dcf4d5f503ba1f191b
6
+ metadata.gz: b24db6ddf741e3f0dcc3f080dba19132d7926c85cf211ae0f98c090c7a3cd93f513b6e977ac21ca24d5c2092d524cc1e7ef3d04250bb29ae672400623c823964
7
+ data.tar.gz: 9585e95676aab71322af2f174d06aed7d6226502bed1e2560cfd0915643e682c02fb857c33310f7c5f1b6d083b83267d14194f0f1e315c940048ef1c3c43faff
data/CHANGELOG.md CHANGED
@@ -1,14 +1,24 @@
1
1
  # Change Log
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
+ ## [2.0.14] - 2021-03-xx
5
+
6
+ ### Added
7
+ - `Eco::API::UseCases::DefaultCases::ToCsvCase` added option `-internal-names` to avoid overriding data on export
8
+ - `Eco::API::Common::People::PersonEntry#mapped_entry` exposed method for raw `csv` generation
9
+ - `Eco::API::Organization::PresetsFactory` added integrity validation for `person_*` abilities
10
+ - `Eco::API::Session::Batch::Job` more debug info on erron handlers
11
+
12
+ ### Changed
13
+ ### Fixed
14
+ - `Eco::API::Error.get_type` was almost always matching `Eco::API::Error::Unclassified` -> fixed
15
+
4
16
  ## [2.0.13] - 2021-03-31
5
17
 
6
18
  ### Added
7
19
  - Stats on the `Eco::API::Session::Batch`
8
20
  - Allow to inherit and re-use the `Eco::API::UseCases::DefaultCase::HrisCase`
9
21
 
10
- ### Changed
11
-
12
22
  ### Fixed
13
23
  - Ensure auto-loading for `Eco::API::UseCases`
14
24
 
@@ -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
+ }
@@ -288,10 +288,13 @@ module Eco
288
288
  handlers = session.config.error_handlers
289
289
  if status.errors.any? && !handlers.empty? && !error_handler?
290
290
  err_types = status.errors.by_type
291
+ logger.debug("(#{self.name}) got these error types: #{err_types.keys}")
291
292
  handlers.each do |handler|
292
293
  if entries = err_types[handler.name]
293
294
  handler_job = subjobs_add("#{self.name} => #{handler.name}", usecase: handler)
295
+ logger.debug("Running error handler #{handler.name}")
294
296
  handler.launch(people: people(entries), session: session, options: options, job: handler_job)
297
+ logger.debug("Launching job of error handler: #{handler_job.name}")
295
298
  handler_job.launch(simulate: simulate)
296
299
  end
297
300
  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
@@ -6,7 +6,7 @@ ASSETS.cli.config do |cnf|
6
6
  file = SCR.get_file("-people-to-csv", required: true, should_exist: false)
7
7
  options.deep_merge!(export: {file: {name: file, format: :csv}})
8
8
  options.deep_merge!(export: {options: {nice_header: true}}) if SCR.get_arg("-nice-header")
9
-
9
+ options.deep_merge!(export: {options: {internal_names: true}}) if SCR.get_arg("-internal-names")
10
10
  case_name = SCR.get_arg("-detailed")? "to-csv-detailed" : "to-csv"
11
11
  session.usecases.case(case_name)
12
12
  end
@@ -73,7 +73,7 @@ ASSETS.cli.config do |cnf|
73
73
  end
74
74
 
75
75
  options.deep_merge!(ignore: {missing: {policy_groups: true}}) if SCR.get_arg("-ignore-missing-policy-groups")
76
-
76
+
77
77
  end
78
78
 
79
79
  desc = "Restores the people manager by using a backup.json file"
data/lib/eco/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Eco
2
- VERSION = "2.0.13"
2
+ VERSION = "2.0.14"
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.13
4
+ version: 2.0.14
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