eco-helpers 2.0.13 → 2.0.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -2
  3. data/eco-helpers.gemspec +6 -4
  4. data/lib/eco-helpers.rb +2 -0
  5. data/lib/eco/api/common/base_loader.rb +14 -0
  6. data/lib/eco/api/common/people/default_parsers/date_parser.rb +11 -1
  7. data/lib/eco/api/common/people/default_parsers/login_providers_parser.rb +1 -1
  8. data/lib/eco/api/common/people/default_parsers/policy_groups_parser.rb +11 -11
  9. data/lib/eco/api/common/people/person_entry.rb +9 -2
  10. data/lib/eco/api/common/people/supervisor_helpers.rb +27 -0
  11. data/lib/eco/api/common/session/file_manager.rb +2 -2
  12. data/lib/eco/api/common/session/mailer.rb +0 -1
  13. data/lib/eco/api/common/session/s3_uploader.rb +0 -1
  14. data/lib/eco/api/common/session/sftp.rb +0 -1
  15. data/lib/eco/api/common/version_patches/exception.rb +8 -4
  16. data/lib/eco/api/error.rb +5 -3
  17. data/lib/eco/api/microcases.rb +3 -1
  18. data/lib/eco/api/microcases/append_usergroups.rb +0 -1
  19. data/lib/eco/api/microcases/people_cache.rb +2 -2
  20. data/lib/eco/api/microcases/people_load.rb +2 -2
  21. data/lib/eco/api/microcases/people_refresh.rb +2 -2
  22. data/lib/eco/api/microcases/people_search.rb +6 -6
  23. data/lib/eco/api/microcases/preserve_default_tag.rb +23 -0
  24. data/lib/eco/api/microcases/preserve_filter_tags.rb +28 -0
  25. data/lib/eco/api/microcases/preserve_policy_groups.rb +30 -0
  26. data/lib/eco/api/microcases/set_account.rb +0 -1
  27. data/lib/eco/api/organization.rb +1 -0
  28. data/lib/eco/api/organization/people.rb +7 -0
  29. data/lib/eco/api/organization/people_analytics.rb +60 -0
  30. data/lib/eco/api/organization/presets_factory.rb +116 -93
  31. data/lib/eco/api/organization/presets_integrity.json +58 -0
  32. data/lib/eco/api/organization/presets_values.json +5 -4
  33. data/lib/eco/api/policies/default_policies/99_user_access_policy.rb +0 -30
  34. data/lib/eco/api/session.rb +1 -20
  35. data/lib/eco/api/session/batch.rb +23 -7
  36. data/lib/eco/api/session/batch/job.rb +3 -0
  37. data/lib/eco/api/session/config.rb +16 -15
  38. data/lib/eco/api/session/config/api.rb +4 -0
  39. data/lib/eco/api/session/config/apis.rb +80 -0
  40. data/lib/eco/api/session/config/files.rb +7 -0
  41. data/lib/eco/api/session/config/people.rb +3 -19
  42. data/lib/eco/api/usecases/default_cases.rb +4 -1
  43. data/lib/eco/api/usecases/default_cases/abstract_policygroup_abilities_case.rb +161 -0
  44. data/lib/eco/api/usecases/default_cases/analyse_people_case.rb +76 -0
  45. data/lib/eco/api/usecases/default_cases/codes_to_tags_case.rb +2 -3
  46. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +11 -1
  47. data/lib/eco/api/usecases/default_cases/restore_db_case.rb +1 -2
  48. data/lib/eco/api/usecases/default_cases/supers_cyclic_identify_case.rb +72 -0
  49. data/lib/eco/api/usecases/default_cases/supers_hierarchy_case.rb +59 -0
  50. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +104 -26
  51. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +62 -36
  52. data/lib/eco/cli.rb +0 -10
  53. data/lib/eco/cli/config/default/options.rb +19 -17
  54. data/lib/eco/cli/config/default/people_filters.rb +3 -3
  55. data/lib/eco/cli/config/default/usecases.rb +77 -25
  56. data/lib/eco/cli/config/default/workflow.rb +12 -3
  57. data/lib/eco/cli/config/help.rb +1 -0
  58. data/lib/eco/cli/config/options_set.rb +106 -13
  59. data/lib/eco/cli/config/use_cases.rb +33 -33
  60. data/lib/eco/cli/scripting/args_helpers.rb +30 -3
  61. data/lib/eco/data.rb +1 -0
  62. data/lib/eco/data/crypto/encryption.rb +3 -3
  63. data/lib/eco/data/files/directory.rb +28 -20
  64. data/lib/eco/data/files/helpers.rb +6 -4
  65. data/lib/eco/data/fuzzy_match.rb +119 -0
  66. data/lib/eco/data/fuzzy_match/array_helpers.rb +75 -0
  67. data/lib/eco/data/fuzzy_match/chars_position_score.rb +37 -0
  68. data/lib/eco/data/fuzzy_match/ngrams_score.rb +73 -0
  69. data/lib/eco/data/fuzzy_match/pairing.rb +102 -0
  70. data/lib/eco/data/fuzzy_match/result.rb +67 -0
  71. data/lib/eco/data/fuzzy_match/results.rb +53 -0
  72. data/lib/eco/data/fuzzy_match/score.rb +44 -0
  73. data/lib/eco/data/fuzzy_match/stop_words.rb +35 -0
  74. data/lib/eco/data/fuzzy_match/string_helpers.rb +69 -0
  75. data/lib/eco/version.rb +1 -1
  76. metadata +86 -10
  77. data/lib/eco/api/microcases/refresh_abilities.rb +0 -19
  78. data/lib/eco/api/organization/presets_reference.json +0 -59
  79. data/lib/eco/api/usecases/default_cases/refresh_abilities_case.rb +0 -30
@@ -2,10 +2,19 @@ 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|
7
- io.session.logger.debug(exception.patch_full_message)
8
- wf.run(:close, io: io)
9
+ begin
10
+ next io if rescued
11
+ rescued = true
12
+
13
+ io.session.logger.debug(exception.patch_full_message)
14
+ wf.run(:close, io: io)
15
+ rescue Exception => e
16
+ puts "Some problem in workflow.rescue: #{e}"
17
+ end
9
18
  io
10
19
  end
11
20
 
@@ -24,7 +33,7 @@ ASSETS.cli.config do |config|
24
33
  if io.options.dig(:input, :entries_from)
25
34
  io = io.new(input: config.input.get(io: io))
26
35
  else
27
- opt_case = cases_with_input.values.first[:option]
36
+ opt_case = cases_with_input.values.first.option
28
37
  io = io.new(input: config.input.get(io: io, option: opt_case))
29
38
  end
30
39
  io
@@ -16,6 +16,7 @@ module Eco
16
16
  # Creatas a well aligned line
17
17
  def help_line(key, desc, keys_max_len = key.length, line_len = 100)
18
18
  blanks = keys_max_len + 3 - key.length
19
+ blanks = blanks < 0 ? 0 : blanks
19
20
  top_line = " #{key}#{" "*blanks} "
20
21
  indent = top_line.length
21
22
  first = true
@@ -5,31 +5,53 @@ module Eco
5
5
  include Eco::CLI::Config::Help
6
6
  attr_reader :core_config
7
7
 
8
+ class OptConfig < Struct.new(:name, :namespace, :description, :callback)
9
+ end
10
+
8
11
  def initialize(core_config:)
9
12
  @core_config = core_config
10
- @options_set = {}
11
- @description = {}
13
+ @sets = {}
12
14
  end
13
15
 
14
16
  # @return [String] summary of the options.
15
17
  def help
18
+ indent = 2
19
+ spaces = any_non_general_space_active? ? active_namespaces : namespaces
20
+
16
21
  ["The following are the available options:"].yield_self do |lines|
17
- max_len = keys_max_len(@options_set.keys)
18
- @options_set.keys.each do |key|
19
- lines << help_line(key, @description[key], max_len)
22
+ max_len = keys_max_len(options_args(spaces)) + indent
23
+ spaces.each do |namespace|
24
+ is_general = (namespace == :general)
25
+ str_indent = is_general ? "" : " " * indent
26
+ lines << help_line(namespace, "", max_len) unless is_general
27
+ options_set(namespace).each do |arg, option|
28
+ lines << help_line(" " * indent + "#{option.name}", option.description, max_len)
29
+ end
20
30
  end
21
31
  lines
22
32
  end.join("\n")
23
33
  end
24
34
 
25
- # @param option [String] the command line option.
35
+ # @return [Array<String>] all the argument of the options in `namespaces`
36
+ def options_args(namespaces)
37
+ namespaces.each_with_object([]) do |space, args|
38
+ args.concat(options_set(space).keys)
39
+ end.uniq
40
+ end
41
+
42
+ # @param option [String, Array<String>] the command line option(s).
43
+ # @param namespace [String] preceding command(s) argument that enables this option.
26
44
  # @param desc [String] description of the option.
27
- def add(option, desc = nil)
45
+ def add(option, desc = nil, namespace: :general)
28
46
  raise "Missing block to define the options builder" unless block_given?
29
- callback = Proc.new
30
- [option].flatten.compact.each do |opt|
31
- @options_set[opt] = callback
32
- @description[opt] = desc
47
+
48
+ opts = [option].flatten.compact
49
+ unless opts.empty?
50
+ callback = Proc.new
51
+ opts.each do |opt|
52
+ puts "Overriding option '#{option}' in '#{namespace}' namespace" if option_exists?(opt, namespace)
53
+ options_set(namespace)[opt] = OptConfig.new(opt, namespace, desc, callback)
54
+ end
33
55
  end
34
56
  self
35
57
  end
@@ -39,12 +61,83 @@ module Eco
39
61
  raise "You need to provide Eco::API::UseCases::BaseIO object. Given: #{io.class}"
40
62
  end
41
63
 
42
- @options_set.each do |arg, callback|
43
- callback.call(io.options, io.session) if SCR.get_arg(arg)
64
+ active_options.each do |option|
65
+ option.callback.call(io.options, io.session)
44
66
  end
67
+
45
68
  io.options
46
69
  end
47
70
 
71
+ def active_options
72
+ @active_options ||= sets.select do |namespace, opts_set|
73
+ active_namespace?(namespace)
74
+ end.each_with_object([]) do |(namespace, opts_set), options|
75
+ opts_set.each do |arg, option|
76
+ options << option if active_option?(arg, namespace)
77
+ end
78
+ end
79
+ end
80
+
81
+ def all_options
82
+ sets.each_with_object([]) do |(namespace, opts_set), options|
83
+ options << opts_set.values
84
+ end
85
+ end
86
+
87
+ def namespaces
88
+ sets.keys.sort_by do |key|
89
+ key == :general
90
+ end
91
+ end
92
+
93
+ def any_non_general_space_active?
94
+ (active_namespaces - [:general]).length > 0
95
+ end
96
+
97
+ def active_namespaces
98
+ @active_namespaces ||= [].tap do |active|
99
+ active << :general
100
+ other = (namespaces - [:general]).select {|nm| SCR.arg?(nm)}
101
+ active.concat(other)
102
+ end
103
+ end
104
+
105
+
106
+ private
107
+
108
+ def active_namespace?(namespace)
109
+ (namespace == :general) || SCR.get_arg(namespace)
110
+ end
111
+
112
+ # Is the option active?
113
+ # 1. If :general namespace, it does just a direct check
114
+ # 2. Otherwise, the `namespace` wording should come first in the `cli` or it is considered inactive
115
+ def active_option?(opt, namespace = :general)
116
+ if namespace == :general
117
+ SCR.get_arg(opt)
118
+ else
119
+ active_namespace?(namespace) && SCR.arg_order?(namespace, opt) && SCR.get_arg(opt)
120
+ end
121
+ end
122
+
123
+ def option_exists?(opt, namespace = :general)
124
+ options_set(namespace).key?(opt)
125
+ end
126
+
127
+ def sets
128
+ @sets ||= {
129
+ general: {}
130
+ }
131
+ end
132
+
133
+ def namespaces
134
+ @sets.keys
135
+ end
136
+
137
+ def options_set(namespace = :general)
138
+ @sets[namespace] ||= {}
139
+ end
140
+
48
141
  end
49
142
  end
50
143
  end
@@ -5,18 +5,35 @@ module Eco
5
5
  include Eco::CLI::Config::Help
6
6
  attr_reader :core_config
7
7
 
8
+ class CaseConfig < Struct.new(:cases_config, :option, :type, :description, :casename, :callback)
9
+
10
+ def add_option(arg, desc = nil, &block)
11
+ core_config.options_set.add(arg, desc, namespace: option, &block)
12
+ self
13
+ end
14
+
15
+ private
16
+
17
+ def core_config
18
+ cases_config.core_config
19
+ end
20
+ end
21
+
22
+ class ActiveCase < Struct.new(:index, :option, :callback)
23
+
24
+ end
25
+
8
26
  def initialize(core_config:)
9
27
  @core_config = core_config
10
28
  @linked_cases = {}
11
- @description = {}
12
29
  end
13
30
 
14
31
  # @return [String] summary of the use cases.
15
32
  def help
16
33
  ["The following are the available use cases:"].yield_self do |lines|
17
34
  max_len = keys_max_len(@linked_cases.keys)
18
- @linked_cases.keys.sort.each do |key|
19
- lines << help_line(key, @description[key], max_len)
35
+ @linked_cases.keys.sort.each do |option_case|
36
+ lines << help_line(option_case, @linked_cases[option_case].description, max_len)
20
37
  end
21
38
  lines
22
39
  end.join("\n")
@@ -33,18 +50,8 @@ module Eco
33
50
  raise "You must specify a valid 'case_name' when no block is provided" unless case_name
34
51
  raise "'case_name' expected to be a String. Given: #{case_name.class}" unless case_name.is_a?(String)
35
52
  end
36
-
37
- @linked_cases[option_case] = {
38
- type => {
39
- option: option_case,
40
- type: type,
41
- casename: case_name,
42
- callback: callback
43
- }
44
- }
45
- @description[option_case] = desc
46
-
47
- self
53
+ puts "Overriding case config '#{option_case}'" if @linked_cases.key?(option_case)
54
+ @linked_cases[option_case] = CaseConfig.new(self, option_case, type, desc, case_name, callback)
48
55
  end
49
56
 
50
57
  # Scopes/identifies which usecases are being invoked from the command line
@@ -55,20 +62,13 @@ module Eco
55
62
  def active(io:)
56
63
  validate_io!(io)
57
64
  return @active_cases unless !@active_cases
58
- active_cases = {}
59
- @linked_cases.each do |option_case, types|
65
+ @active_cases = @linked_cases.each_with_object({}) do |(option_case, data), active_cases|
60
66
  next nil unless SCR.get_arg(option_case)
61
- types.each do |type, data|
62
- if usecase = get_usecase(io: io, data: data)
63
- active_cases[usecase] = {
64
- index: SCR.get_arg_index(option_case),
65
- option: option_case,
66
- callback: data[:callback]
67
- }
68
- end
67
+ if usecase = get_usecase(io: io, data: data)
68
+ index = SCR.get_arg_index(option_case)
69
+ active_cases[usecase] = ActiveCase.new(index, option_case, data.callback)
69
70
  end
70
- end
71
- @active_cases = active_cases.sort_by {|c, d| d[:index]}.to_h
71
+ end.sort_by {|c, d| d.index}.to_h
72
72
  end
73
73
 
74
74
  def process(io:)
@@ -79,7 +79,7 @@ module Eco
79
79
  processed = true
80
80
  io = case_io(io: io, usecase: usecase)
81
81
  # some usecases have a callback to collect the parameters
82
- data[:callback]&.call(*io.params)
82
+ data.callback&.call(*io.params)
83
83
  io = usecase.launch(io: io)
84
84
  end
85
85
  processed
@@ -100,17 +100,17 @@ module Eco
100
100
  end
101
101
 
102
102
  def get_usecase(io:, data:)
103
- usecase = if case_name = data[:casename]
104
- io.session.usecases.case(case_name, type: data[:type])
103
+ usecase = if case_name = data.casename
104
+ io.session.usecases.case(case_name, type: data.type)
105
105
  end
106
- usecase ||= if callback = data[:callback]
106
+ usecase ||= if callback = data.callback
107
107
  # identify/retrieve usecase via callback
108
- params = io.params(keyed: true).merge(type: data[:type])
108
+ params = io.params(keyed: true).merge(type: data.type)
109
109
  io = io.new(**params, validate: false)
110
110
  callback.call(*io.params).tap do |usecase|
111
111
  unless usecase.is_a?(Eco::API::UseCases::UseCase)
112
112
  msg = "When adding a usecase, without specifying 'case_name:', "
113
- msg += "the block that integrates usecase for cli option '#{data[:option]}'"
113
+ msg += "the block that integrates usecase for cli option '#{data.option}'"
114
114
  msg += " must return an Eco::API::UseCases::UseCase object. It returns #{usecase.class}"
115
115
  raise msg
116
116
  end
@@ -3,6 +3,7 @@ module Eco
3
3
  class Scripting
4
4
  module ArgsHelpers
5
5
 
6
+ # @return [Array<String] the command line arguments.
6
7
  def argv
7
8
  @argv || ARGV
8
9
  end
@@ -11,10 +12,18 @@ module Eco
11
12
  Argument.is_modifier?(value)
12
13
  end
13
14
 
15
+ # @return [Arguments] supported known arguments.
14
16
  def arguments
15
17
  @arguments ||= Arguments.new(argv)
16
18
  end
17
19
 
20
+ # Registers an argument as a known one.
21
+ def known_argument(key, with_param: false)
22
+ arguments.add(key, with_param: with_param)
23
+ end
24
+
25
+
26
+ # Validation to stop the `script` if among `argv` there's any **unknown** argument.
18
27
  def stop_on_unknown!(exclude: [], only_options: false)
19
28
  # validate only those that are options
20
29
  unknown = arguments.unknown(exclude: exclude)
@@ -23,18 +32,35 @@ module Eco
23
32
  end
24
33
 
25
34
  unless unknown.empty?
26
- raise "There are unknown options in your command line arguments: #{unknown}"
35
+ msg = "There are unknown options in your command line arguments:\n"
36
+ msg += "#{unknown}\n"
37
+ msg += "Please, remember that use case specific options should come after the use case in the command line.\n"
38
+ msg += "Use 'ruby main.rb -org [-usecase] --help -options' for more information"
39
+ raise msg
27
40
  end
28
41
  end
29
42
 
43
+ # @return [Boolean] if `key` is in the command line.
44
+ def arg?(key)
45
+ argv.include?(key)
46
+ end
47
+
48
+ # @return [Integer, nil] the position of `key` in the command line.
30
49
  def get_arg_index(key)
31
- return nil if !argv.include?(key)
50
+ return nil if !arg?(key)
32
51
  argv.index(key)
33
52
  end
34
53
 
54
+ # @return [Boolean] if `key1` precedes `key2` in the command line.
55
+ def arg_order?(key1, key2)
56
+ return false unless (k1 = get_arg_index(key1)) && k2 = get_arg_index(key2)
57
+ k1 < k2
58
+ end
59
+
60
+ # @return [String, Boolean] the argument value if `with_param` or a `Boolean` if not.
35
61
  def get_arg(key, with_param: false, valid: true)
36
62
  # track what a known option looks like
37
- arguments.add(key, with_param: with_param)
63
+ known_argument(key, with_param: with_param)
38
64
  return nil unless index = get_arg_index(key)
39
65
  value = true
40
66
  if with_param
@@ -45,6 +71,7 @@ module Eco
45
71
  return value
46
72
  end
47
73
 
74
+ # @return [String] the filename.
48
75
  def get_file(key, required: false, should_exist: true)
49
76
  filename = get_arg(key, with_param: true)
50
77
  if !filename && required
data/lib/eco/data.rb CHANGED
@@ -6,3 +6,4 @@ end
6
6
  require_relative 'data/crypto'
7
7
  require_relative 'data/files'
8
8
  require_relative 'data/mapper'
9
+ require_relative 'data/fuzzy_match'
@@ -1,7 +1,7 @@
1
1
  require 'openssl'
2
- require 'json'
2
+ # 'json'
3
3
  require 'base64'
4
- require 'pp'
4
+ #require 'pp'
5
5
 
6
6
  require_relative '../../cli/scripting'
7
7
 
@@ -147,7 +147,7 @@ module Eco
147
147
  return str_c
148
148
  #EncryptedData.new({content: str_c, key: key, iv: iv})
149
149
 
150
-
150
+
151
151
  end
152
152
  def aes256_decrypt(data, key: , iv: , block_octets: BLOCK_OCTETS)
153
153
  block_bits = block_bits * 8
@@ -5,6 +5,29 @@ module Eco
5
5
  module Files
6
6
  class Directory
7
7
 
8
+ class << self
9
+ def create(path, includes_file: false)
10
+ return true if Files.file_exists?(path)
11
+
12
+ parts = Files.split(File.expand_path(path))
13
+ filename = parts.pop if includes_file
14
+
15
+ return true if Files.dir_exists?(File.join(*parts))
16
+
17
+ subpath = nil
18
+ begin
19
+ parts.each do |curr|
20
+ subpath = subpath ? File.join(subpath, curr) : curr
21
+ Dir.mkdir(subpath) unless Files.dir_exists?(subpath)
22
+ end
23
+ rescue Exception => e
24
+ pp e
25
+ return false
26
+ end
27
+ true
28
+ end
29
+ end
30
+
8
31
  attr_reader :dir_path
9
32
 
10
33
  def initialize(dir_path = Dir.pwd)
@@ -14,12 +37,14 @@ module Eco
14
37
  end
15
38
 
16
39
  def exists?
17
- Files.dir_exists(@dir_path)
40
+ Files.dir_exists?(@dir_path)
18
41
  end
19
42
 
20
43
  def create
21
- succeed = Directory.create(File.expand_path(@dir_path)) unless self.exists?
22
- self.full_path if succeed
44
+ return self.full_path if self.exists?
45
+ if succeed = Directory.create(File.expand_path(@dir_path))
46
+ return self.full_path
47
+ end
23
48
  end
24
49
 
25
50
  def full_path
@@ -57,23 +82,6 @@ module Eco
57
82
  File.join(*args)
58
83
  end
59
84
 
60
- def self.create(path, includes_file: false)
61
- return true if Files.file_exists?(path)
62
- parts = Files.split(File.expand_path(path))
63
- filename = parts.pop if includes_file
64
- return true if Files.dir_exists?(File.join(*parts))
65
- subpath = nil
66
- begin
67
- parts.each do |curr|
68
- subpath = subpath ? File.join(subpath, curr) : curr
69
- Dir.mkdir(subpath) unless Files.dir_exists?(subpath)
70
- end
71
- rescue Exception => e
72
- pp e
73
- end
74
- false
75
- end
76
-
77
85
  private
78
86
 
79
87
  def file_pattern(value)