eco-helpers 3.0.12 → 3.0.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -1
  3. data/eco-helpers.gemspec +15 -14
  4. data/lib/eco/api/common/people/default_parsers/date_parser.rb +6 -0
  5. data/lib/eco/api/common/session/mailer/aws_provider.rb +85 -0
  6. data/lib/eco/api/common/session/mailer/provider_base.rb +61 -0
  7. data/lib/eco/api/common/session/mailer/sendgrid_provider.rb +117 -0
  8. data/lib/eco/api/common/session/mailer.rb +42 -71
  9. data/lib/eco/api/common/session/sftp.rb +3 -1
  10. data/lib/eco/api/session/batch/errors.rb +2 -2
  11. data/lib/eco/api/session/batch.rb +66 -28
  12. data/lib/eco/api/session/config/api.rb +96 -37
  13. data/lib/eco/api/session/config/apis/enviro_spaces.rb +106 -0
  14. data/lib/eco/api/session/config/apis/one_off.rb +94 -0
  15. data/lib/eco/api/session/config/apis/service_up.rb +37 -0
  16. data/lib/eco/api/session/config/apis/space_helpers.rb +41 -0
  17. data/lib/eco/api/session/config/apis.rb +81 -132
  18. data/lib/eco/api/session/config.rb +21 -3
  19. data/lib/eco/api/usecases/default_cases/samples/sftp_case.rb +1 -1
  20. data/lib/eco/api/usecases/graphql/helpers/base/error_handling.rb +19 -8
  21. data/lib/eco/api/usecases/graphql/helpers/base/graphql_env.rb +1 -0
  22. data/lib/eco/api/usecases/graphql/samples/location/command/dsl.rb +3 -7
  23. data/lib/eco/api/usecases/graphql/samples/location/command/service/tree_update.rb +6 -2
  24. data/lib/eco/cli/config/options_set.rb +10 -7
  25. data/lib/eco/cli/scripting/args_helpers.rb +18 -9
  26. data/lib/eco/cli_default/options.rb +8 -0
  27. data/lib/eco/cli_default/people.rb +3 -3
  28. data/lib/eco/language/basic_logger.rb +4 -2
  29. data/lib/eco/version.rb +1 -1
  30. metadata +37 -16
@@ -2,8 +2,9 @@ module Eco
2
2
  module API
3
3
  class Session
4
4
  class Batch < Common::Session::BaseSession
5
- DEFAULT_BATCH_BLOCK = 50
6
- VALID_METHODS = %i[get create update upsert delete].freeze
5
+ DEFAULT_BATCH_SIZE = 50
6
+ DEFAULT_JOB_SIZE = 100
7
+ VALID_METHODS = %i[get create update upsert delete].freeze
7
8
 
8
9
  class << self
9
10
  # @return [Boolean] `true` if the method is supported, `false` otherwise.
@@ -12,13 +13,19 @@ module Eco
12
13
  end
13
14
  end
14
15
 
16
+ def batch_size(opts = options)
17
+ return self.class::DEFAULT_JOB_SIZE if job_mode?(opts)
18
+
19
+ self.class::DEFAULT_BATCH_SIZE
20
+ end
21
+
15
22
  # @return [Symbol] the batch mode to run
16
- def batch_mode(opts = self.options)
23
+ def batch_mode(opts = options)
17
24
  opts.dig(:workflow, :batch, :mode) || :batch
18
25
  end
19
26
 
20
27
  # @return [Boolean] are we running in `:job` mode?
21
- def job_mode?(opts = self.options)
28
+ def job_mode?(opts = options)
22
29
  batch_mode(opts) == :job
23
30
  end
24
31
 
@@ -32,10 +39,16 @@ module Eco
32
39
  # @option params [String] :per_page the number of people included per each batch api request.
33
40
  # @option params [String] :q some text to search. Omit this parameter to target all the people.
34
41
  # @return [Array<People>] all the people based on `params`
35
- def get_people(people = nil, params: {}, silent: false)
36
- return launch(people, method: :get, params: params, silent: silent).people if people.is_a?(Enumerable)
37
-
38
- get(params: params, silent: silent)
42
+ def get_people(people = nil, params: {}, silent: false, options: self.options)
43
+ return get(params: params, silent: silent, options: options) unless people.is_a?(Enumerable)
44
+
45
+ launch(
46
+ people,
47
+ method: :get,
48
+ params: params,
49
+ silent: silent,
50
+ options: options
51
+ ).people
39
52
  end
40
53
 
41
54
  # launches a batch of `method` type using `people` and the specified `params`
@@ -47,14 +60,26 @@ module Eco
47
60
  # @param params [Hash] api request options.
48
61
  # @option params [String] :per_page the number of people included per each batch api request.
49
62
  # @return [Batch::Status] the `status` of this batch launch.
50
- def launch(people, method:, params: {}, silent: false)
51
- batch_from(people, method: method, params: params, silent: silent)
63
+ def launch(people, method:, params: {}, silent: false, options: self.options)
64
+ batch_from(
65
+ people,
66
+ method: method,
67
+ params: params,
68
+ silent: silent,
69
+ options: options
70
+ )
52
71
  end
53
72
 
54
- def search(data, silent: false, params: {}) # rubocop:disable Metrics/AbcSize
55
- params = {per_page: DEFAULT_BATCH_BLOCK}.merge(params)
73
+ def search(data, silent: false, params: {}, options: self.options) # rubocop:disable Metrics/AbcSize
74
+ params = {per_page: batch_size(options)}.merge(params)
56
75
 
57
- launch(data, method: :get, params: params, silent: silent).tap do |status|
76
+ launch(
77
+ data,
78
+ method: :get,
79
+ params: params,
80
+ silent: silent,
81
+ options: options
82
+ ).tap do |status|
58
83
  status.mode = :search
59
84
 
60
85
  entries = status.queue
@@ -78,7 +103,11 @@ module Eco
78
103
  people_matching = []
79
104
  email = email.to_s.strip.downcase
80
105
  unless email.empty?
81
- people_matching = get(params: params.merge(q: email), silent: silent).select do |person|
106
+ people_matching = get(
107
+ params: params.merge(q: email),
108
+ silent: silent,
109
+ options: options
110
+ ).select do |person|
82
111
  person.email == email
83
112
  end
84
113
  end
@@ -95,15 +124,21 @@ module Eco
95
124
 
96
125
  private
97
126
 
98
- def get(params: {}, silent: false)
127
+ def get(params: {}, silent: false, options: self.options)
99
128
  msg = "cannot batch get without api connnection, please provide a valid api connection!"
100
129
  fatal msg unless (people_api = api&.people)
101
130
 
102
- params = {per_page: DEFAULT_BATCH_BLOCK}.merge(params)
131
+ params = {per_page: batch_size(options)}.merge(params)
103
132
  people_api.get_all(params: params, silent: silent)
104
133
  end
105
134
 
106
- def batch_from(data, method:, params: {}, silent: false)
135
+ def batch_from(
136
+ data,
137
+ method:,
138
+ params: {},
139
+ silent: false,
140
+ options: self.options
141
+ )
107
142
  fatal "Invalid batch method: #{method}." unless self.class.valid_method?(method)
108
143
  return nil if !data || !data.is_a?(Enumerable)
109
144
 
@@ -111,15 +146,16 @@ module Eco
111
146
  fatal msg unless (people_api = api&.people)
112
147
 
113
148
  # param q does not make sense here, even for GET method
114
- params = {per_page: DEFAULT_BATCH_BLOCK}.merge(params)
115
- per_page = params[:per_page] || DEFAULT_BATCH_BLOCK
149
+ params = {per_page: batch_size(options)}.merge(params)
150
+ per_page = params[:per_page] || batch_size(options)
116
151
 
117
152
  launch_batch(
118
153
  data,
119
154
  method: method,
120
155
  per_page: per_page,
121
156
  people_api: people_api,
122
- silent: silent
157
+ silent: silent,
158
+ options: options
123
159
  )
124
160
  end
125
161
 
@@ -133,10 +169,10 @@ module Eco
133
169
  method:,
134
170
  status: nil,
135
171
  job_mode: true, # rubocop:disable Lint/UnusedMethodArgument
136
- per_page: DEFAULT_BATCH_BLOCK,
172
+ options: self.options,
173
+ per_page: batch_size(options),
137
174
  people_api: api&.people,
138
- silent: false,
139
- options: self.options
175
+ silent: false
140
176
  )
141
177
  iteration = 1
142
178
  done = 0
@@ -191,7 +227,8 @@ module Eco
191
227
  job_mode: false,
192
228
  per_page: per_page,
193
229
  people_api: people_api,
194
- silent: silent
230
+ silent: silent,
231
+ options: options
195
232
  )
196
233
  end
197
234
  end
@@ -206,11 +243,12 @@ module Eco
206
243
  end
207
244
 
208
245
  def offer_retry_on(error_type, retries_left = 3, &block)
209
- block.call
210
- rescue error_type
211
- raise unless retries_left.positive?
246
+ yield
247
+ rescue error_type => err
248
+ raise err.class, err.message, cause: nil unless retries_left.positive?
212
249
 
213
- explanation = "Batch TimeOut. You have #{retries_left} retries left."
250
+ explanation = "#{err}\n"
251
+ explanation << "You have #{retries_left} retries left."
214
252
  question = " Do you want to retry (y/N)?"
215
253
 
216
254
  prompt_user(question, default: "Y", explanation: explanation, timeout: 10) do |response|
@@ -4,6 +4,27 @@ module Eco
4
4
  class Config
5
5
  class Api < Hash
6
6
  class << self
7
+ def to_space(value)
8
+ value = value.to_s.strip.gsub(/[- ]/, '_').downcase
9
+ return :default if value.empty?
10
+
11
+ value.to_sym
12
+ end
13
+
14
+ def description(api)
15
+ msg = "Expecting #{self}. Given: #{api.class}"
16
+ raise ArgumentError, msg unless api.is_a?(self)
17
+
18
+ full_name(api.name, space: api.space)
19
+ end
20
+
21
+ def full_name(name, space: :default)
22
+ space = to_space(space)
23
+
24
+ str_space = space == :default ? '' : " (space: :#{space})"
25
+ "'#{name}'#{str_space}"
26
+ end
27
+
7
28
  def to_version(str)
8
29
  case str.to_sym
9
30
  when :external, :v1
@@ -35,22 +56,34 @@ module Eco
35
56
  end
36
57
  end
37
58
 
38
- def initialize(name, root:, key:, host:, version:,
39
- mode: :local, user_key: nil, external_key: nil,
40
- email: nil, pass: nil, org_id: nil)
59
+ def initialize(
60
+ name,
61
+ root:,
62
+ key:,
63
+ host:,
64
+ version:,
65
+ space: :default,
66
+ mode: :local,
67
+ user_key: nil,
68
+ external_key: nil,
69
+ email: nil,
70
+ pass: nil,
71
+ org_id: nil
72
+ )
41
73
  super(nil)
42
- @root = root
43
- @apis = {}
44
- self["name"] = name
45
- self["key"] = key
46
- self["host"] = host
47
- self["version"] = version
48
- self["mode"] = mode
49
- self["user_key"] = user_key
50
- self["external_key"] = external_key
51
- self["email"] = email || ENV['USER_EMAIL']
52
- self["pass"] = pass || ENV['USER_PASS']
53
- self["org_id"] = org_id
74
+ @root = root
75
+ @apis = {}
76
+ self['name'] = name
77
+ self['space'] = to_space(space)
78
+ self['key'] = key
79
+ self['host'] = host
80
+ self['version'] = version
81
+ self['mode'] = mode
82
+ self['user_key'] = user_key
83
+ self['external_key'] = external_key
84
+ self['email'] = email || ENV['USER_EMAIL']
85
+ self['pass'] = pass || ENV['USER_PASS']
86
+ self['org_id'] = org_id
54
87
  end
55
88
 
56
89
  # @return [Eco::API::Session::Config] the `root` config
@@ -67,12 +100,13 @@ module Eco
67
100
  switch_logger = (logger != @logger)
68
101
  @logger = logger if logger
69
102
 
70
- if (current = get(version)) && !switch_logger
71
- return current
72
- end
103
+ current = get(version)
104
+ return current if current && !switch_logger
73
105
 
74
106
  unless api_params?(version)
75
- raise "The api configuration for '#{name}' is missing data for the api version '#{self.version(version)}'"
107
+ msg = "The api configuration for #{description} "
108
+ msg << "is missing data for the api version '#{self.version(version)}'"
109
+ raise ArgumentError, msg
76
110
  end
77
111
 
78
112
  new_api(version).tap do |pi|
@@ -89,7 +123,16 @@ module Eco
89
123
  end
90
124
 
91
125
  def name
92
- self["name"]
126
+ self['name']
127
+ end
128
+
129
+ def space
130
+ self['space'] ||= :default
131
+ to_space(self['space'])
132
+ end
133
+
134
+ def description
135
+ self.class.description(self)
93
136
  end
94
137
 
95
138
  def one_off?
@@ -97,45 +140,45 @@ module Eco
97
140
  end
98
141
 
99
142
  def key
100
- self["key"]
143
+ self['key']
101
144
  end
102
145
 
103
146
  def user_key
104
- self["user_key"] || @root.default_user_key
147
+ self['user_key'] || @root.default_user_key
105
148
  end
106
149
 
107
150
  def external_key
108
- self["external_key"] || (%i[v1 v2].include?(version) && key)
151
+ self['external_key'] || (%i[v1 v2].include?(version) && key)
109
152
  end
110
153
 
111
154
  def internal_key
112
- (version == :v0) && self["key"]
155
+ (version == :v0) && self['key']
113
156
  end
114
157
 
115
158
  def org_id
116
- self["org_id"]
159
+ self['org_id']
117
160
  end
118
161
 
119
162
  def email
120
- self["email"] || @root.default_email
163
+ self['email'] || @root.default_email
121
164
  end
122
165
 
123
166
  def pass
124
- self["pass"] || @root.default_pass
167
+ self['pass'] || @root.default_pass
125
168
  end
126
169
 
127
170
  def host
128
- self["host"]
171
+ self['host']
129
172
  end
130
173
 
131
174
  # @param mode [Symbol] to define if running on `:remote` or `:local`
132
175
  def mode=(mode)
133
- self["mode"] = mode == :remote ? :remote : :local
176
+ self['mode'] = mode == :remote ? :remote : :local
134
177
  end
135
178
 
136
179
  # @return [Symbol] if running on `:remote` or `:local`
137
180
  def mode
138
- self["mode"]
181
+ self['mode']
139
182
  end
140
183
 
141
184
  def local?
@@ -152,7 +195,7 @@ module Eco
152
195
  end
153
196
 
154
197
  def version(value = nil)
155
- self.class.to_version(value || self["version"])
198
+ self.class.to_version(value || self['version'])
156
199
  end
157
200
 
158
201
  # if no low level connection messages: use `IO::NULL`
@@ -163,9 +206,14 @@ module Eco
163
206
 
164
207
  private
165
208
 
209
+ def to_space(...)
210
+ self.class.to_space(...)
211
+ end
212
+
166
213
  # Generates a **new** `API` object of version `version`.
167
214
  def new_api(version) # rubocop:disable Metrics/AbcSize
168
215
  klass = self.class.api_class(version)
216
+
169
217
  case self.version(version)
170
218
  when :v0
171
219
  klass.new(internal_key, host: host, logger: logger)
@@ -174,14 +222,25 @@ module Eco
174
222
  when :v2
175
223
  klass.new(user_key: user_key, org_key: external_key, host: host, logger: logger)
176
224
  when :graphql
177
- kargs = {host: host, org_id: org_id, email: email, pass: pass}
225
+ kargs = {
226
+ host: host,
227
+ org_id: org_id,
228
+ email: email,
229
+ pass: pass
230
+ }
231
+
178
232
  kargs.merge!({no_schema: true}) if ENV['GRAPHQL_FETCH_SCHEMA'] == "false"
179
- klass.new(host: host, org_id: org_id, email: email, pass: pass)
233
+ klass.new(**kargs)
180
234
  end.tap do |api|
181
- unless !api || log_connection?
182
- @logger.info("Created api#{self.version(version)} connection on '#{name}' enviro, pointing to '#{host}' in '#{mode}' mode")
183
- api.logger.level = ::Logger::UNKNOWN if api.respond_to?(:logger)
184
- end
235
+ next unless api
236
+ next if log_connection? # prevent over-logging
237
+
238
+ msg = "Created api#{self.version(version)} connection "
239
+ msg << "on enviro #{description}, "
240
+ msg << "pointing to '#{host}' in '#{mode}' mode"
241
+
242
+ @logger.info(msg)
243
+ api.logger.level = ::Logger::UNKNOWN if api.respond_to?(:logger)
185
244
  end
186
245
  end
187
246
 
@@ -0,0 +1,106 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class Config
5
+ class Apis
6
+ module EnviroSpaces
7
+ class << self
8
+ def included(base)
9
+ target_class = Eco::API::Session::Config::Apis
10
+ msg = "To be included in #{target_class}. "
11
+ msg << "Included in #{base}"
12
+ raise msg unless base <= target_class
13
+
14
+ super
15
+ end
16
+ end
17
+
18
+ include Session::Config::Apis::SpaceHelpers
19
+
20
+ def active_space
21
+ active_api&.space || space_option
22
+ end
23
+
24
+ def enviros(space)
25
+ apis(space).keys
26
+ end
27
+
28
+ # APIs of a particualr `space`
29
+ def apis(space = space_option)
30
+ api_space(space)
31
+ end
32
+
33
+ # Any APIs (of any space or in a particular `space`)
34
+ def apis?(space = nil)
35
+ return apis(space).any? unless space.nil?
36
+
37
+ spaces.each_key.any? do |space|
38
+ apis?(space)
39
+ end
40
+ end
41
+
42
+ def api?(name, space: space_option)
43
+ apis(space).key?(name)
44
+ end
45
+
46
+ # Deprecated
47
+ def defined?(name, space: space_option)
48
+ apis(space).key?(name)
49
+ end
50
+
51
+ def any_defined?(*names, space: space_option)
52
+ [names].flatten.any? do |name|
53
+ api?(name, space: space)
54
+ end
55
+ end
56
+
57
+ # @return [Array<Symbol>] the spaces where the enviro `name`
58
+ # has an api defined
59
+ def enviro_spaces(name)
60
+ spaces.keys.select do |space|
61
+ api?(name, space: space)
62
+ end
63
+ end
64
+
65
+ def enviro_spaces_str(name)
66
+ return '' unless enviro_spaces?(name)
67
+
68
+ ":#{enviro_spaces(name).join(', :')}"
69
+ end
70
+
71
+ def enviro_spaces?(name)
72
+ enviro_spaces(name).any?
73
+ end
74
+
75
+ def missing_api_message(name, space: space_option)
76
+ space ||= space_option
77
+
78
+ msg = "Missing credentials for "
79
+ msg << "#{full_name(name, space: space)} Api env."
80
+
81
+ if enviro_spaces?(name)
82
+ msg << "\n Available space(s) for '#{name}' enviro: "
83
+ msg << enviro_spaces_str(name)
84
+ msg << "\n"
85
+ end
86
+
87
+ msg
88
+ end
89
+
90
+ private
91
+
92
+ def spaces
93
+ self['spaces'] ||= {}
94
+ end
95
+
96
+ def api_space(space = space_option)
97
+ space ||= space_option
98
+
99
+ spaces[space.to_sym] ||= {}
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,94 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class Config
5
+ class Apis
6
+ module OneOff
7
+ private
8
+
9
+ def one_off?
10
+ @is_one_off ||=
11
+ SCR.get_arg('-api-key') ||
12
+ SCR.get_arg('-one-off')
13
+ end
14
+
15
+ def one_off_key
16
+ return @one_off_key if instance_variable_defined?(:@one_off_key)
17
+
18
+ return unless one_off?
19
+
20
+ Dotenv.load('./.env_one_off')
21
+ SCR.get_arg('-api-key', with_param: true).then do |key|
22
+ one_off_key_env(key)
23
+ end
24
+ end
25
+
26
+ def one_off_key_env(key)
27
+ return unless one_off?
28
+
29
+ if key
30
+ env_file_set_var('./.env_one_off', one_off_key_env_var, key)
31
+ key
32
+ else
33
+ Dotenv.load('./.env_one_off')
34
+
35
+ ENV[one_off_key_env_var].tap do |k|
36
+ msg = "At least the first time, "
37
+ msg << "you should provide the -api-key"
38
+ raise msg unless k
39
+ end
40
+ end
41
+ end
42
+
43
+ def one_off_key_env_var
44
+ @one_off_key_env_var ||= "#{one_off_org}_KEY"
45
+ end
46
+
47
+ def one_off_org
48
+ return @one_off_org if instance_variable_defined?(:@one_off_org)
49
+
50
+ msg = "You should specify -org NAME when using -api-key or -one-off"
51
+ raise msg unless org = SCR.get_arg('-org', with_param: true)
52
+
53
+ str_org = "#{org.downcase.split(/[^a-z]+/).join('_')}_#{one_off_enviro.gsub('.', '_')}"
54
+ @one_off_org ||= str_org.to_sym
55
+ end
56
+
57
+ def one_off_enviro
58
+ return @one_off_enviro if instance_variable_defined?(:@one_off_enviro)
59
+
60
+ enviro = 'live'
61
+ enviro = SCR.get_arg('-enviro', with_param: true) if SCR.get_arg('-enviro')
62
+
63
+ @one_off_enviro ||= enviro.downcase
64
+ end
65
+
66
+ def env_file_set_var(file, var, value)
67
+ pattern = /"#{var}=(?<value>[^ \r\n]+)"/
68
+
69
+ File.open(file, "w+") do |fd|
70
+ found = false
71
+
72
+ fd.each_line do |line|
73
+ next unless (match = line.match(pattern))
74
+
75
+ found = true
76
+ # IO::SEEK_CUR => Seeks to _amount_ plus current position
77
+ fd.seek(-(line.length + 1), IO::SEEK_CUR)
78
+ fd.write line.gsub(match[:value], value)
79
+ end
80
+
81
+ fd << "#{var}=#{value}" unless found
82
+ end
83
+
84
+ true
85
+ rescue StandardError => err
86
+ puts "#{err}"
87
+ false
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,37 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class Config
5
+ class Apis
6
+ module ServiceUp
7
+ class << self
8
+ def included(base)
9
+ target_class = Eco::API::Session::Config::Apis
10
+ msg = "To be included in #{target_class}. "
11
+ msg << "Included in #{base}"
12
+ raise msg unless base <= target_class
13
+
14
+ super
15
+ end
16
+ end
17
+
18
+ def service_up?
19
+ @api_test ||=
20
+ Session::Config::Api.
21
+ api_class(active_api.version).
22
+ new(
23
+ 'foobar',
24
+ host: active_api.host,
25
+ logger: ::Logger.new(IO::NULL)
26
+ )
27
+
28
+ status = @api_test.client.get('/policy_groups').status
29
+ # 401 Unauthorized "Permission denied. API key may be invalid."
30
+ status == 401
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ module Eco
2
+ module API
3
+ class Session
4
+ class Config
5
+ class Apis
6
+ module SpaceHelpers
7
+ def space_option?
8
+ SCR.get_arg('-space')
9
+ end
10
+
11
+ def space_option
12
+ :default.then do |default|
13
+ value = SCR.get_arg('-space', with_param: true)
14
+
15
+ value = nil if value.to_s.strip.empty?
16
+ value = to_space(value) unless value.nil?
17
+ next default unless value
18
+
19
+ value
20
+ end
21
+ end
22
+
23
+ def set_options_space!(space)
24
+ ASSETS.cli.options.deep_merge!(api: {space: space})
25
+ end
26
+
27
+ private
28
+
29
+ def to_space(...)
30
+ Session::Config::Api.to_space(...)
31
+ end
32
+
33
+ def full_name(...)
34
+ Session::Config::Api.full_name(...)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end