eco-helpers 0.6.0

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.
Files changed (123) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/README.md +20 -0
  5. data/eco-helpers.gemspec +34 -0
  6. data/lib/eco-helpers.rb +15 -0
  7. data/lib/eco/api.rb +13 -0
  8. data/lib/eco/api/common.rb +10 -0
  9. data/lib/eco/api/common/people.rb +17 -0
  10. data/lib/eco/api/common/people/base_parser.rb +16 -0
  11. data/lib/eco/api/common/people/default_parsers.rb +40 -0
  12. data/lib/eco/api/common/people/default_parsers/boolean_parser.rb +28 -0
  13. data/lib/eco/api/common/people/default_parsers/date_parser.rb +33 -0
  14. data/lib/eco/api/common/people/default_parsers/multi_parser.rb +33 -0
  15. data/lib/eco/api/common/people/default_parsers/numeric_parser.rb +23 -0
  16. data/lib/eco/api/common/people/default_parsers/select_parser.rb +29 -0
  17. data/lib/eco/api/common/people/entries.rb +120 -0
  18. data/lib/eco/api/common/people/person_entry.rb +380 -0
  19. data/lib/eco/api/common/people/person_factory.rb +114 -0
  20. data/lib/eco/api/common/people/person_modifier.rb +62 -0
  21. data/lib/eco/api/common/people/person_parser.rb +140 -0
  22. data/lib/eco/api/common/people/types.rb +47 -0
  23. data/lib/eco/api/common/session.rb +15 -0
  24. data/lib/eco/api/common/session/base_session.rb +46 -0
  25. data/lib/eco/api/common/session/environment.rb +47 -0
  26. data/lib/eco/api/common/session/file_manager.rb +90 -0
  27. data/lib/eco/api/common/session/logger.rb +105 -0
  28. data/lib/eco/api/common/session/mailer.rb +92 -0
  29. data/lib/eco/api/common/session/s3_uploader.rb +110 -0
  30. data/lib/eco/api/common/version_patches.rb +11 -0
  31. data/lib/eco/api/common/version_patches/external_person.rb +11 -0
  32. data/lib/eco/api/eco_faker.rb +59 -0
  33. data/lib/eco/api/organization.rb +13 -0
  34. data/lib/eco/api/organization/account.rb +23 -0
  35. data/lib/eco/api/organization/people.rb +118 -0
  36. data/lib/eco/api/organization/policy_groups.rb +51 -0
  37. data/lib/eco/api/organization/preferences.rb +28 -0
  38. data/lib/eco/api/organization/preferences_reference.json +23 -0
  39. data/lib/eco/api/organization/presets.rb +138 -0
  40. data/lib/eco/api/organization/presets_backup.rb +220 -0
  41. data/lib/eco/api/organization/presets_values.json +10 -0
  42. data/lib/eco/api/organization/tag_tree.rb +134 -0
  43. data/lib/eco/api/organization_old.rb +73 -0
  44. data/lib/eco/api/session.rb +180 -0
  45. data/lib/eco/api/session/batch.rb +132 -0
  46. data/lib/eco/api/session/batch_job.rb +152 -0
  47. data/lib/eco/api/session/batch_jobs.rb +131 -0
  48. data/lib/eco/api/session/batch_status.rb +138 -0
  49. data/lib/eco/api/session/task.rb +92 -0
  50. data/lib/eco/api/session_config.rb +179 -0
  51. data/lib/eco/api/session_config/api.rb +47 -0
  52. data/lib/eco/api/session_config/apis.rb +78 -0
  53. data/lib/eco/api/session_config/files.rb +30 -0
  54. data/lib/eco/api/session_config/logger.rb +54 -0
  55. data/lib/eco/api/session_config/mailer.rb +65 -0
  56. data/lib/eco/api/session_config/people.rb +89 -0
  57. data/lib/eco/api/session_config/s3_bucket.rb +62 -0
  58. data/lib/eco/api/session_config/use_cases.rb +30 -0
  59. data/lib/eco/api/usecases.rb +12 -0
  60. data/lib/eco/api/usecases/base_case.rb +14 -0
  61. data/lib/eco/api/usecases/case_data.rb +13 -0
  62. data/lib/eco/api/usecases/default_cases.rb +53 -0
  63. data/lib/eco/api/usecases/default_cases/change_email_case.rb +47 -0
  64. data/lib/eco/api/usecases/default_cases/create_details_case.rb +29 -0
  65. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +49 -0
  66. data/lib/eco/api/usecases/default_cases/delete_case.rb +20 -0
  67. data/lib/eco/api/usecases/default_cases/email_as_id_case.rb +24 -0
  68. data/lib/eco/api/usecases/default_cases/hris_case.rb +67 -0
  69. data/lib/eco/api/usecases/default_cases/new_email_case.rb +26 -0
  70. data/lib/eco/api/usecases/default_cases/new_id_case.rb +26 -0
  71. data/lib/eco/api/usecases/default_cases/refresh_presets.rb +25 -0
  72. data/lib/eco/api/usecases/default_cases/reinvite_case.rb +22 -0
  73. data/lib/eco/api/usecases/default_cases/remove_account_case.rb +36 -0
  74. data/lib/eco/api/usecases/default_cases/reset_landing_page_case.rb +24 -0
  75. data/lib/eco/api/usecases/default_cases/set_default_tag_case.rb +44 -0
  76. data/lib/eco/api/usecases/default_cases/set_supervisor_case.rb +39 -0
  77. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +36 -0
  78. data/lib/eco/api/usecases/default_cases/update_details_case.rb +30 -0
  79. data/lib/eco/api/usecases/default_cases/upsert_account_case.rb +35 -0
  80. data/lib/eco/api/usecases/use_case.rb +177 -0
  81. data/lib/eco/api/usecases/use_group.rb +104 -0
  82. data/lib/eco/cli.rb +9 -0
  83. data/lib/eco/cli/input.rb +109 -0
  84. data/lib/eco/cli/input_multi.rb +137 -0
  85. data/lib/eco/cli/root.rb +8 -0
  86. data/lib/eco/cli/session.rb +9 -0
  87. data/lib/eco/cli/session/batch.rb +9 -0
  88. data/lib/eco/common.rb +7 -0
  89. data/lib/eco/common/base_cli.rb +116 -0
  90. data/lib/eco/common/language.rb +9 -0
  91. data/lib/eco/data.rb +9 -0
  92. data/lib/eco/data/crypto.rb +7 -0
  93. data/lib/eco/data/crypto/encryption.rb +318 -0
  94. data/lib/eco/data/files.rb +10 -0
  95. data/lib/eco/data/files/directory.rb +93 -0
  96. data/lib/eco/data/files/file_pattern.rb +32 -0
  97. data/lib/eco/data/files/helpers.rb +90 -0
  98. data/lib/eco/data/mapper.rb +54 -0
  99. data/lib/eco/data/random.rb +10 -0
  100. data/lib/eco/data/random/distribution.rb +133 -0
  101. data/lib/eco/data/random/fake.rb +320 -0
  102. data/lib/eco/data/random/values.rb +80 -0
  103. data/lib/eco/language.rb +12 -0
  104. data/lib/eco/language/curry.rb +28 -0
  105. data/lib/eco/language/hash_transform.rb +68 -0
  106. data/lib/eco/language/hash_transform_modifier.rb +114 -0
  107. data/lib/eco/language/match.rb +30 -0
  108. data/lib/eco/language/match_modifier.rb +190 -0
  109. data/lib/eco/language/models.rb +11 -0
  110. data/lib/eco/language/models/attribute_parser.rb +38 -0
  111. data/lib/eco/language/models/collection.rb +181 -0
  112. data/lib/eco/language/models/modifier.rb +68 -0
  113. data/lib/eco/language/models/wrap.rb +114 -0
  114. data/lib/eco/language/values_at.rb +159 -0
  115. data/lib/eco/lexic/dictionary.rb +33 -0
  116. data/lib/eco/lexic/dictionary/dictionary.txt +355484 -0
  117. data/lib/eco/lexic/dictionary/tags.json +38 -0
  118. data/lib/eco/scripting.rb +30 -0
  119. data/lib/eco/scripting/README.md +11 -0
  120. data/lib/eco/scripting/arguments.rb +40 -0
  121. data/lib/eco/tester.rb +97 -0
  122. data/lib/eco/version.rb +3 -0
  123. metadata +325 -0
@@ -0,0 +1,152 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class BatchJob < API::Common::Session::BaseSession
5
+ TYPES = [:create, :update, :delete, :get]
6
+ SETS = [:core, :details, :account]
7
+
8
+ attr_reader :name, :status
9
+
10
+ def initialize(name, type:, sets:, root:)
11
+ raise "A name is required to refer a job. Given: #{name}" if !name
12
+ raise "Type should be one of #{TYPES}. Given: #{type}" if !BatchJob.valid_type?(type)
13
+ raise "Sets should be some of #{SETS}. Given: #{sets}" if !BatchJob.valid_sets?(sets)
14
+ super(root.enviro)
15
+
16
+ @name = name
17
+ @type = type
18
+ @sets = [sets].flatten
19
+ @root = root
20
+ reset
21
+ end
22
+
23
+ def reset
24
+ @queue = []
25
+ @callbacks = {}
26
+ @status = nil
27
+ end
28
+
29
+ def signature
30
+ "job \"#{@name}\" ['#{@type.to_s.upcase}': #{sets_title}]"
31
+ end
32
+
33
+ def match?(type:, sets:)
34
+ sets = [sets].flatten
35
+ type == @type && (sets.order == @sets.order)
36
+ end
37
+
38
+ def pending?
39
+ @queue.length > 0
40
+ end
41
+
42
+ def add(data)
43
+ unless !data
44
+ @queue.push(data)
45
+ @callbacks[data] = Proc.new if block_given?
46
+ end
47
+ end
48
+
49
+ def people
50
+ Eco::API::Organization::People.new(@queue)
51
+ end
52
+
53
+ def processed_queue
54
+ @queue.map do |entry|
55
+ callback = @callbacks[entry]
56
+ e = entry
57
+ e = callback.call(entry) if callback
58
+ e = nil if as_update(e).empty?
59
+ e
60
+ end.compact
61
+ end
62
+
63
+ def launch(simulate: false)
64
+ queue = processed_queue
65
+ launch_feedback(queue, simulate ? 2500 : 800)
66
+
67
+ if !simulate && queue.length > 0
68
+ backup_update(queue)
69
+ @status = batch.launch(queue, method: @type.to_s)
70
+ @status.root = self
71
+ end
72
+
73
+ logger.info("Simulate: this would have launched: '#{@type.to_s}'") if simulate
74
+ return @status
75
+ end
76
+
77
+ def core?
78
+ sets.include?(:core)
79
+ end
80
+
81
+ def details?
82
+ sets.include?(:dettails)
83
+ end
84
+
85
+ def account?
86
+ sets.include?(:account)
87
+ end
88
+
89
+ def self.valid_type?(value)
90
+ TYPES.include?(value)
91
+ end
92
+
93
+ def self.valid_sets?(value)
94
+ sets = [value].flatten
95
+ sets.all? { |s| SETS.include?(s) }
96
+ end
97
+
98
+ private
99
+
100
+ def as_update(update)
101
+ hash = update if update.is_a?(Hash)
102
+ hash = update.as_update if update.is_a?(Ecoportal::API::V1::Person)
103
+ fields = hash&.dig('details', 'fields')
104
+ fields&.map! { |fld| fld&.slice("id", "alt_id", "value") }
105
+ hash || {}
106
+ end
107
+
108
+ def sets_title
109
+ "#{@sets.map {|s| s.to_s}.join(", ")}"
110
+ end
111
+
112
+ def launch_feedback(data, max_chars = 800)
113
+ if !data || !data.is_a?(Array) || data.empty?
114
+ logger.warn("#{"*" * 20} Nothing for #{signature} so far :) #{"*" * 20}")
115
+ return
116
+ end
117
+ header = ("*" * 20) + " #{signature} - Feedback Sample " + ("*" * 20)
118
+ logger.info(header)
119
+
120
+ sample_length = 1
121
+ sample = data.slice(0, 20).map do |entry|
122
+ update = as_update(entry)
123
+ max_chars -= update.pretty_inspect.length
124
+ sample_length += 1 if max_chars > 0
125
+ update
126
+ end
127
+
128
+ logger.info("#{sample.slice(0, sample_length).pretty_inspect}")
129
+ logger.info("#{@type.to_s.upcase} length: #{data.length}")
130
+ logger.info("*" * header.length)
131
+ end
132
+
133
+ def backup_update(data)
134
+ data_body = data.map { |u| as_update(u) }
135
+ dir = config.people.requests_folder
136
+ file = File.join(dir, "#{@type.to_s}_data.json")
137
+ file_manager.save_json(data_body, file, :timestamp)
138
+ end
139
+
140
+ def session
141
+ @root.session
142
+ end
143
+
144
+ def batch
145
+ session.batch
146
+ end
147
+
148
+ end
149
+
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,131 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class JobGroups < API::Common::Session::BaseSession
5
+ DELAY_BETWEEN_GROUPS = 2
6
+
7
+ def initialize(session:)
8
+ raise("JobGroups requires a session object") unless session.is_a?(API::Session)
9
+ super(session.enviro)
10
+ @session = session
11
+ reset
12
+ end
13
+
14
+ def reset
15
+ @order = []
16
+ @groups = {}
17
+ @callbacks = {}
18
+ end
19
+
20
+ def [](name)
21
+ @groups[name]
22
+ end
23
+
24
+ def exists?(name)
25
+ @groups.key?(name)
26
+ end
27
+
28
+ def new(name, order: :last)
29
+ raise("Can't create job group named #{'name'} because it already exists.") if exists?(name)
30
+
31
+ group = BatchJobs.new(name, session: @session)
32
+ @groups[name] = group
33
+
34
+ if order == :last
35
+ @order.push(group)
36
+ else
37
+ @order.unshift(group)
38
+ end
39
+
40
+ @callbacks[group] = Proc.new if block_given?
41
+
42
+ group
43
+ end
44
+
45
+ def pending?
46
+ @groups.any? do |group|
47
+ group.pending?
48
+ end
49
+ end
50
+
51
+ def launch(simulate: false)
52
+ groups_status = {}
53
+ @order.each.with_index do |group, idx|
54
+ groups_status[group] = group_status = group.launch(simulate: simulate)
55
+ callback = @callbacks[group]
56
+ callback.call(group, group_status) if callback
57
+ JobGroups.counter(DELAY_BETWEEN_GROUPS) if !simulate && idx < @order.length - 1
58
+ end
59
+ return groups_status
60
+ end
61
+
62
+ def self.counter(seconds)
63
+ puts "\n"
64
+ while seconds + 1 > 0 do
65
+ print " Waiting #{seconds}\r"
66
+ $stdout.flush
67
+ seconds -= 1
68
+ sleep 1
69
+ end
70
+ print "#{" " * 40}"
71
+ $stdout.flush
72
+ puts ""
73
+ end
74
+
75
+ end
76
+
77
+ class BatchJobs < API::Common::Session::BaseSession
78
+ attr_reader :name, :session
79
+
80
+ def initialize(name, session:)
81
+ raise("BatchJobs requires a session object") unless session.is_a?(API::Session)
82
+ super(session.enviro)
83
+ @session = session
84
+ @name = name
85
+ reset
86
+ end
87
+
88
+ def reset
89
+ @jobs = {}
90
+ @callbacks = {}
91
+ end
92
+
93
+ def [](name)
94
+ @jobs[name]
95
+ end
96
+
97
+ def exists?(name)
98
+ @jobs.key?(name)
99
+ end
100
+
101
+ def new(name, type:, sets:)
102
+ raise("Can't create job named '#{name}' because it already exists.") if exists?(name)
103
+
104
+ job = BatchJob.new(name, type: type, sets: sets, root: self)
105
+ @jobs[name] = job
106
+ @callbacks[job] = Proc.new if block_given?
107
+
108
+ job
109
+ end
110
+
111
+ def pending?
112
+ @jobs.keys.any? do |key|
113
+ @jobs[key].pending?
114
+ end
115
+ end
116
+
117
+ def launch(simulate: false)
118
+ group_status = {}
119
+ @jobs.each do |name, job|
120
+ group_status[job] = job_status = job.launch(simulate: simulate)
121
+ callback = @callbacks[job]
122
+ callback.call(job, job_status) if callback
123
+ end
124
+
125
+ return group_status
126
+ end
127
+
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,138 @@
1
+ require 'json'
2
+ require 'pp'
3
+ require 'securerandom' # for uuid (perhaps metter to have structure i.e. Session:id-Batch:id)
4
+
5
+ module Eco
6
+ module API
7
+ class Session
8
+ class BatchStatus < Common::Session::BaseSession
9
+ attr_reader :queue, :method
10
+ attr_reader :root
11
+
12
+ def initialize(e, queue:, method:)
13
+ super(e)
14
+
15
+ unless queue && queue.is_a?(Array)
16
+ msg = "In batch operations you must batch an array. Received: #{queue}"
17
+ logger.fatal(msg)
18
+ raise msg
19
+ end
20
+ @method = method
21
+ @queue = queue
22
+ @hash = @queue.each_with_index.map do |entry, i|
23
+ [entry, i]
24
+ end.to_h
25
+ @responses = Array(0..@queue.length-1)
26
+ end
27
+
28
+ def root=(object)
29
+ @root = object
30
+ end
31
+
32
+ def [](key)
33
+ @responses[to_index(key)]
34
+ end
35
+
36
+ def []=(key, response)
37
+ @responses[to_index(key)] = response
38
+ end
39
+
40
+ def received?(key)
41
+ !!self[key]
42
+ end
43
+
44
+ def success?(key)
45
+ self[key]&.success?
46
+ end
47
+
48
+ def people
49
+ raise "This batch wasn't a 'get'. Can't obtain people without 'get' method" unless method == "get"
50
+ out = Array(0..queue.length-1)
51
+ @responses.each_with_index do |respose, i|
52
+ out[i] = response.result
53
+ end
54
+ out
55
+ end
56
+
57
+ def error_queries
58
+ queue.each_with_index.map do |query,i|
59
+ self[i].success? ? nil : query
60
+ end.compact
61
+ end
62
+
63
+ def errors?
64
+ queue.any? {|query| !self[query].success?}
65
+ end
66
+
67
+ def errors_count
68
+ error_queries.length
69
+ end
70
+
71
+ def str_error(key)
72
+ msg = ""
73
+ unless success?(key)
74
+ i = to_index(key)
75
+ entry = queue[i]
76
+ response = self[i]
77
+ msg = "Error #{response.status}: #{response.body}\n"
78
+ msg += "-- Failed to batch #{method} (entry #{i+1}). Person: #{person_ref(entry)}"
79
+ end
80
+ msg
81
+ end
82
+
83
+ def str_errors(sort: :by_status)
84
+ queries = error_queries
85
+ if sort == :by_status
86
+ queries = error_queries.sort_by do |query|
87
+ self[query].status
88
+ end
89
+ end
90
+ strs = []
91
+ if queries.length > 0
92
+ strs = queries.map {|query| str_error(query)}
93
+ end
94
+ strs
95
+ end
96
+
97
+ def print_error(key)
98
+ unless success?(key)
99
+ logger.error(str_error(key))
100
+ end
101
+ end
102
+
103
+ def print_errors(sort: :by_status)
104
+ strs = str_errors(sort: sort)
105
+ if strs.length > 0
106
+ logger.error(strs.join("\n"))
107
+ #strs.each {|str| logger.error(str)}
108
+ else
109
+ logger.info("There were no errors for the current batch '#{method}'!! ;)")
110
+ end
111
+ end
112
+
113
+
114
+ private
115
+
116
+ def person_ref(entry)
117
+ is_person = entry.is_a?(Ecoportal::API::Internal::Person) || entry.is_a?(Ecoportal::API::V1::Person)
118
+ ref = nil
119
+ ref = "'#{entry.name}' ('#{entry.external_id}': '#{entry.email}')" if is_person
120
+ ref
121
+ end
122
+
123
+ def to_index(key)
124
+ key.is_a?(Integer) ? valid_index(index: key) : valid_index(entry: key)
125
+ end
126
+
127
+ def valid_index(index: nil, entry: nil)
128
+ index ||= @hash[entry]
129
+ unless index && index < @queue.length
130
+ raise "You must provide either the index or the original entry object of the batch"
131
+ end
132
+ index
133
+ end
134
+
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,92 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class Task < API::Common::Session::BaseSession
5
+ NEWEST_FILE_MODE = [:newest, :last_file, :previous]
6
+ LOAD_FILE_MODE = [:file]
7
+ SAVE_FILE_MODE = [:save]
8
+ API_MODE = [:api, :api_get]
9
+
10
+ attr_reader :batch
11
+
12
+ def initialize(root, env)
13
+ super(env)
14
+ @root = root
15
+ end
16
+
17
+ def batch
18
+ @root&.batch
19
+ end
20
+
21
+ def file_people(filename = enviro.config.people.cache)
22
+ logger.info("Going to get all the people via API")
23
+ people = batch.get_people
24
+ file = file_manager.save_json(people, filename, :timestamp)
25
+ logger.info("#{people.length} people loaded and saved locally to #{file}.")
26
+ API::Organization::People.new(people)
27
+ end
28
+
29
+ def load_people(filename = enviro.config.people.cache, modifier: [:newest, :api])
30
+ modifier = [modifier].flatten
31
+ people = []
32
+ case
33
+ when !!filename && (load_file?(modifier) || newest_file?(modifier))
34
+ case
35
+ when newest_file?(modifier)
36
+ # search input file based on pattern (in case the name has a timestamp)
37
+ file = file_manager.dir.newest_file(file: filename)
38
+ logger.info("previous file found: #{file}") if !!file
39
+ else
40
+ file = file_manager.dir.file(filename)
41
+ end
42
+
43
+ if !file
44
+ logger.error("could not find the file #{file_manager.dir.file(filename)}")
45
+ exit if !use_api?(modifier)
46
+ people = self.load_people(modifier: modifier - NEWEST_FILE_MODE - LOAD_FILE_MODE)
47
+ else
48
+ people = file_manager.load_json(file)
49
+ if !!people && people.is_a?(Array)
50
+ logger.info("#{people&.length} people loaded from file #{file}")
51
+ end
52
+ end
53
+ when use_api?(modifier)
54
+ # no previous file: use API to get all people
55
+ logger.info("Going to get all the people via API")
56
+ people = batch.get_people
57
+
58
+ if save_file?(modifier) && people && people.length > 0
59
+ file = file_manager.save_json(people, filename, :timestamp)
60
+ logger.info("#{people.length } people saved to file #{file}.")
61
+ end
62
+ end
63
+ Eco::API::Organization::People.new(people)
64
+ end
65
+
66
+ private
67
+
68
+ # MODIFIERS
69
+ def use_api?(modifier)
70
+ modifiers = [modifier].flatten
71
+ modifiers.any? { |m| API_MODE.include?(m) }
72
+ end
73
+
74
+ def load_file?(modifier)
75
+ modifiers = [modifier].flatten
76
+ modifiers.any? { |m| LOAD_FILE_MODE.include?(m) }
77
+ end
78
+
79
+ def newest_file?(modifier)
80
+ modifiers = [modifier].flatten
81
+ modifiers.any? { |m| NEWEST_FILE_MODE.include?(m) }
82
+ end
83
+
84
+ def save_file?(modifier)
85
+ modifiers = [modifier].flatten
86
+ modifiers.any? { |m| SAVE_FILE_MODE.include?(m) }
87
+ end
88
+
89
+ end
90
+ end
91
+ end
92
+ end