eco-helpers 1.1.2 → 1.1.3
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.
- checksums.yaml +4 -4
- data/lib/eco/api/common/session/environment.rb +1 -1
- data/lib/eco/api/common/session/mailer.rb +7 -2
- data/lib/eco/api/common/session/s3_uploader.rb +24 -7
- data/lib/eco/api/common/version_patches.rb +1 -0
- data/lib/eco/api/common/version_patches/exception.rb +8 -0
- data/lib/eco/api/session.rb +18 -8
- data/lib/eco/api/session/batch.rb +0 -2
- data/lib/eco/api/session/batch/errors.rb +12 -4
- data/lib/eco/api/session/batch/feedback.rb +40 -24
- data/lib/eco/api/session/batch/job.rb +93 -38
- data/lib/eco/api/session/batch/jobs.rb +45 -9
- data/lib/eco/api/session/batch/jobs_groups.rb +46 -7
- data/lib/eco/api/session/batch/status.rb +6 -0
- data/lib/eco/api/session/config/mailer.rb +8 -0
- data/lib/eco/api/session/config/s3_storage.rb +8 -0
- data/lib/eco/api/session/config/workflow.rb +113 -20
- data/lib/eco/api/session/task.rb +11 -0
- data/lib/eco/cli.rb +1 -5
- data/lib/eco/cli/config/default/workflow.rb +7 -0
- data/lib/eco/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a2efa416d986a9c212bc99ec69bf667778392737f0c635d54a18d19ff8bced9b
|
4
|
+
data.tar.gz: 7747bbfa3253f537d08b65148f8fc7418b0f4190d2754873daeb0b9fc5642b41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60457d180398b01377aaeb6bced7c57fb24010da7acecbca8cbdac46a16d617759a23175e6b7be8c2b8eb4ec3834119e5173d8f2ff14212493a3db35298441ed
|
7
|
+
data.tar.gz: 5fab0a498910811d86f63419193aa02d1aebd52d4a1c63db1d11c173d9905f651e47c1082090ef0a83236f8c1d48d2c206572d25b1881b05dddc6cbc4be69c0e
|
@@ -7,6 +7,7 @@ module Eco
|
|
7
7
|
module Session
|
8
8
|
class Mailer
|
9
9
|
|
10
|
+
# @param enviro [Eco::API::Common::Session::Environment]
|
10
11
|
def initialize (enviro:)
|
11
12
|
raise "Required Environment object (enviro:). Given: #{enviro}" if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
|
12
13
|
@enviro = enviro
|
@@ -16,10 +17,10 @@ module Eco
|
|
16
17
|
# @param to [String] destination email address
|
17
18
|
# @param subject [String] subject of the email
|
18
19
|
# @param body [String] `html` or plain text message
|
19
|
-
def mail(to
|
20
|
+
def mail(to: nil, subject:, body:)
|
20
21
|
ses.send_email(
|
21
22
|
destination: {
|
22
|
-
to_addresses: [to].flatten,
|
23
|
+
to_addresses: [fetch_to(to)].flatten,
|
23
24
|
},
|
24
25
|
source: fetch_from,
|
25
26
|
message: {
|
@@ -64,6 +65,10 @@ module Eco
|
|
64
65
|
@enviro.config || {}
|
65
66
|
end
|
66
67
|
|
68
|
+
def fetch_to(value = nil)
|
69
|
+
value || config.mailer.to
|
70
|
+
end
|
71
|
+
|
67
72
|
def fetch_from(value = nil)
|
68
73
|
value || config.mailer.from
|
69
74
|
end
|
@@ -8,6 +8,7 @@ module Eco
|
|
8
8
|
class S3Uploader
|
9
9
|
attr_reader :prefix
|
10
10
|
|
11
|
+
# @param enviro [Eco::API::Common::Session::Environment]
|
11
12
|
def initialize (enviro:)
|
12
13
|
raise "Required Environment object (enviro:). Given: #{enviro}" if enviro && !enviro.is_a?(Eco::API::Common::Session::Environment)
|
13
14
|
@enviro = enviro
|
@@ -15,6 +16,10 @@ module Eco
|
|
15
16
|
@timestamp = Time.now.iso8601
|
16
17
|
end
|
17
18
|
|
19
|
+
# Uploads `content` to S3 as `filename`
|
20
|
+
# @param filename [String] the name of the object to be created on S3
|
21
|
+
# @param content [String] that to be uploaded
|
22
|
+
# @return [String] S3 path to the uploaded `filename` object
|
18
23
|
def upload(filename, content)
|
19
24
|
if obj = new_s3_object(filename)
|
20
25
|
log_upload(obj) do
|
@@ -24,16 +29,24 @@ module Eco
|
|
24
29
|
return full_path(obj)
|
25
30
|
end
|
26
31
|
|
32
|
+
# Uploads a single file
|
33
|
+
# @param path [String] the target file to be uploaded
|
34
|
+
# @return [String] S3 path to the uploaded `path` file
|
27
35
|
def upload_file(path)
|
28
36
|
File.open(path, "rb") do |f|
|
29
37
|
upload(File.basename(path), f)
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
41
|
+
# @note it will skip subfolders
|
42
|
+
# @param path [String] the target directory to be uploaded
|
43
|
+
# @param recurse [Boolean] deepen in the folder structure? (`false`: default)
|
44
|
+
# @return [Array<String>] S3 paths to all the uploaded files of `path` directory
|
45
|
+
def upload_directory(path, recurse: false)
|
46
|
+
path = File.expand_path(path)
|
47
|
+
prefix = File.expand_path(File.join(path, ".."))
|
48
|
+
wildcard = recurse ? "**/*" : "*"
|
49
|
+
Dir.glob(File.join(path, wildcard)).sort.map do |file|
|
37
50
|
next unless File.file?(file) # Skip directories
|
38
51
|
key = file.sub(prefix,"").gsub(/\\/,"/").sub(/^\/+/,"")
|
39
52
|
|
@@ -43,11 +56,15 @@ module Eco
|
|
43
56
|
end.compact
|
44
57
|
end
|
45
58
|
|
46
|
-
|
59
|
+
# @param path [String] a full path to a S3 object
|
60
|
+
# @return [String] `link` to the S3 object on console
|
61
|
+
def link(path)
|
47
62
|
unless path.is_a?(Enumerable)
|
48
|
-
|
63
|
+
return nil unless path.is_a?(String)
|
64
|
+
return "https://s3.console.aws.amazon.com/s3/object/#{path.sub("s3://","")}?region=#{fetch_region}&tab=overview"
|
65
|
+
return link
|
49
66
|
end
|
50
|
-
path.map {|pth|
|
67
|
+
path.map {|pth| link(pth)}
|
51
68
|
end
|
52
69
|
|
53
70
|
private
|
data/lib/eco/api/session.rb
CHANGED
@@ -201,10 +201,14 @@ module Eco
|
|
201
201
|
job_groups.launch(simulate: simulate)
|
202
202
|
end
|
203
203
|
|
204
|
+
def summary
|
205
|
+
job_groups.summary
|
206
|
+
end
|
207
|
+
|
204
208
|
# Sends an email
|
205
209
|
# @see Eco::API::Common::Session::Mailer#mail
|
206
|
-
def
|
207
|
-
|
210
|
+
def mail(**kargs)
|
211
|
+
mailer.mail(**kargs)
|
208
212
|
end
|
209
213
|
|
210
214
|
# Uploads content into a file, a file or a directory to S3
|
@@ -214,17 +218,23 @@ module Eco
|
|
214
218
|
# @param content [String] content to be uploaded (requires `file`)
|
215
219
|
# @param file [String] name of the file to be uploaded
|
216
220
|
# @param directory [String] name of source directory to be uploaded
|
217
|
-
# @
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
+
# @param recurse [Boolean] used with `directory`: deepen in the folder structure? (`false`: default)
|
222
|
+
# @param link [Boolean] **return** _link(s)_ (`true`) or _path(s)_ (`false`: default)
|
223
|
+
# @return [String, Array<String>] either paths to S3 objects if `link` is `false`, or _link_ otherwise
|
224
|
+
def s3upload(content: nil, file: nil, directory: nil, recurse: false, link: false)
|
225
|
+
if content == :target
|
226
|
+
path = self.do.s3upload_targets
|
227
|
+
elsif content && file
|
228
|
+
path = s3uploader.upload(file, content)
|
221
229
|
elsif file
|
222
|
-
s3uploader.upload_file(file)
|
230
|
+
path = s3uploader.upload_file(file)
|
223
231
|
elsif directory
|
224
|
-
s3uploader.upload_directory(directory)
|
232
|
+
path = s3uploader.upload_directory(directory, recurse: recurse)
|
225
233
|
else
|
226
234
|
logger.error("To use Session.s3upload, you must specify either directory, file or content and file name")
|
227
235
|
end
|
236
|
+
return path unless link
|
237
|
+
s3uploader.link(path)
|
228
238
|
end
|
229
239
|
|
230
240
|
private
|
@@ -38,13 +38,13 @@ module Eco
|
|
38
38
|
status.logger
|
39
39
|
end
|
40
40
|
|
41
|
-
# Was there any **error** as a result of this batch?
|
41
|
+
# Was there any _Sever_ (reply) **error** as a result of this batch?
|
42
42
|
# @return [Boolean] `true` if any of the queried _entries_ got an unsuccessful `Ecoportal::API::Common::BatchResponse`
|
43
43
|
def any?
|
44
44
|
queue.any? {|query| !status[query].success?}
|
45
45
|
end
|
46
46
|
|
47
|
-
# Input entries that got
|
47
|
+
# Input entries that got **error** response from the _Server_.
|
48
48
|
# @raise [Exception] if there are elements of the final `queue` that did not get response
|
49
49
|
# @note discards those that did not get _response_ from the Server (so those that were not queried)
|
50
50
|
# - please, observe that this can only happen if there were repeated entries in the `source_queue`
|
@@ -63,7 +63,7 @@ module Eco
|
|
63
63
|
raise msg
|
64
64
|
end
|
65
65
|
|
66
|
-
response.success
|
66
|
+
response.success?? nil : query
|
67
67
|
end.compact
|
68
68
|
end
|
69
69
|
|
@@ -117,10 +117,18 @@ module Eco
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
+
def message
|
121
|
+
msgs = strs
|
122
|
+
if msgs.length > 0
|
123
|
+
"There were #{msgs.length} errors:\n" + msgs.join("\n")
|
124
|
+
else
|
125
|
+
"There were no errors for the current batch '#{method}'!! ;)"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
120
129
|
def print
|
121
130
|
msgs = strs
|
122
131
|
if msgs.length > 0
|
123
|
-
logger.info()
|
124
132
|
logger.error("There were #{msgs.length} errors:\n" + msgs.join("\n"))
|
125
133
|
else
|
126
134
|
logger.info("There were no errors for the current batch '#{method}'!! ;)")
|
@@ -34,7 +34,12 @@ module Eco
|
|
34
34
|
job.options
|
35
35
|
end
|
36
36
|
|
37
|
-
#
|
37
|
+
# @see Eco::API::Session::Batch::Job#requests
|
38
|
+
def job_requests
|
39
|
+
job.requests
|
40
|
+
end
|
41
|
+
|
42
|
+
# Slightly modifies the behaviour of `Ecoportal::API::Common::BaseModel#as_update`, so schema details fields show the `alt_id`
|
38
43
|
# @note for better feedback
|
39
44
|
# @param entry [Hash, Ecoportal::API::V1::Person, Ecoportal::API::Internal::Person]
|
40
45
|
def as_update(entry)
|
@@ -63,32 +68,43 @@ module Eco
|
|
63
68
|
hash || {}
|
64
69
|
end
|
65
70
|
|
66
|
-
|
67
|
-
|
71
|
+
# @note if `requests` is not provided, it uses the last requests of the parent `Batch::Job` `job`
|
72
|
+
# @param requests [Enumerable<Hash>] raw requests as they would be sent to the _Server_
|
73
|
+
# @return [Eco::API::Session::Batch::RequestStats] the stats object of the current requests
|
74
|
+
def request_stats(requests = nil)
|
75
|
+
requests ||= job.requests
|
76
|
+
return @request_stats if @request_stats && requests == job.requests
|
77
|
+
@request_stats ||= Eco::API::Session::Batch::RequestStats.new(type: type, requests: requests)
|
68
78
|
end
|
69
79
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
# Generates the lines of feedback of the current requests
|
81
|
+
# @note if `requests` is not provided, it uses the last requests of the parent `Batch::Job` `job`
|
82
|
+
# @param requests [Enumerable<Hash>] raw requests as they would be sent to the _Server_
|
83
|
+
# @param max_charts [Integer] the max number of characters for the current feedback message
|
84
|
+
# @param only_stats [Boolean] whether or not should only include a brief summary of stats
|
85
|
+
# @return [String] the feedback message
|
86
|
+
def generate(requests = nil, max_chars: 800, only_stats: false)
|
87
|
+
requests ||= job.requests
|
88
|
+
[].tap do |msg|
|
89
|
+
if !requests || !requests.is_a?(Enumerable) || requests.empty?
|
90
|
+
msg << "#{"*" * 20} Nothing for #{signature} so far :) #{"*" * 20}"
|
91
|
+
else
|
92
|
+
header = "#{"*"*20} #{signature} - Feedback Sample #{"*"*20}"
|
93
|
+
msg << header unless only_stats
|
94
|
+
unless only_stats
|
95
|
+
sample_length = 1
|
96
|
+
sample = requests.slice(0, 20).map do |request|
|
97
|
+
max_chars -= request.pretty_inspect.length
|
98
|
+
sample_length += 1 if max_chars > 0
|
99
|
+
request
|
100
|
+
end
|
101
|
+
msg << "#{sample.slice(0, sample_length).pretty_inspect}"
|
102
|
+
end
|
103
|
+
msg << "#{"+"*5} STATS (job '#{name}') +++ #{type.to_s.upcase} length: #{requests.length} #{"+"*5}"
|
104
|
+
msg << "#{request_stats(requests).message}"
|
105
|
+
msg << "*" * header.length unless only_stats
|
86
106
|
end
|
87
|
-
|
88
|
-
msg << "#{request_stats(requests).message}"
|
89
|
-
msg << "*" * header.length
|
90
|
-
end
|
91
|
-
msg.join("\n")
|
107
|
+
end.join("\n")
|
92
108
|
end
|
93
109
|
|
94
110
|
private
|
@@ -66,16 +66,6 @@ module Eco
|
|
66
66
|
usecase?? usecase.options : {}
|
67
67
|
end
|
68
68
|
|
69
|
-
def match?(type:, sets:)
|
70
|
-
sets = [sets].flatten
|
71
|
-
type == self.type && (sets.order == self.sets.order)
|
72
|
-
end
|
73
|
-
|
74
|
-
# @return [Boolean] has been this `batch job` launched?
|
75
|
-
def pending?
|
76
|
-
@pending
|
77
|
-
end
|
78
|
-
|
79
69
|
# Adds an entry(ies) to the job queue.
|
80
70
|
# @param entry [Person, Enumberable<Person>] the person(s) we want to update, carrying the changes to be done.
|
81
71
|
# @param unique [Boolean] specifies if repeated entries should be avoided in the queue.
|
@@ -97,6 +87,35 @@ module Eco
|
|
97
87
|
end
|
98
88
|
end
|
99
89
|
|
90
|
+
#def match?(type:, sets:)
|
91
|
+
# sets = [sets].flatten
|
92
|
+
# type == self.type && (sets.order == self.sets.order)
|
93
|
+
#end
|
94
|
+
|
95
|
+
# @return [Boolean] has been this `batch job` launched?
|
96
|
+
def pending?
|
97
|
+
@pending
|
98
|
+
end
|
99
|
+
|
100
|
+
# @note it requires launch to be firstly invoked
|
101
|
+
# @raise [Exception] if 'launch' has not firstly invoked
|
102
|
+
# @return [Enumbrable<Hash>] the last requests that the queue will generate
|
103
|
+
def requests
|
104
|
+
raise "Method missuse. Firstly 'launch' should be invoked" unless instance_variable_defined?(:@requests)
|
105
|
+
@requests
|
106
|
+
end
|
107
|
+
|
108
|
+
# @see Eco::API::Session::Batch::Feedback#request_stats
|
109
|
+
def request_stats(requests = nil)
|
110
|
+
feedback.request_stats(requests || self.requests)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @see Eco::API::Session::Batch::Status#errors?
|
114
|
+
# @return [Boolean] `true` if there were Server errors, `false` otherwise
|
115
|
+
def errors?
|
116
|
+
status && status.errors?
|
117
|
+
end
|
118
|
+
|
100
119
|
# Helper/shortcut to obtain a people object out of `input`
|
101
120
|
# @note if `input` is not provided, it will use `queue`
|
102
121
|
# @return [Eco::API::Organization::People]
|
@@ -105,36 +124,71 @@ module Eco
|
|
105
124
|
end
|
106
125
|
|
107
126
|
# Processes the `queue` and, unless `simulate` is `true`, launches against the server:
|
108
|
-
# 1.
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
114
|
-
#
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
127
|
+
# 1. pre_processes the queue obtaining the `requests`:
|
128
|
+
# - if the entries of `queue` got pending _callbacks_ (delayed changes), it processes them
|
129
|
+
# - unless type == `:create`: if there's a defined `api_excluded` _callback_ it calls it (see `Eco::API::Session::Config::People#api_excluded`)
|
130
|
+
# - transforms the result to a `Eco::API::Organization::People` object
|
131
|
+
# - if there are `api policies` defined, it passes the entries through them in order (see `Eco::API::Session::Config#policies`)
|
132
|
+
# - this step is **skipped** if the option `-skip-api-policies` was used in the command line
|
133
|
+
# - at this point all the transformations have taken place...
|
134
|
+
# - only include the entries that, after all above, still hold pending changes (`!as_update.empty?`) to be launched as update
|
135
|
+
# 2. pre launch checks against the `requests`:
|
136
|
+
# - it generates `stats` (`Eco::API::Session::Batch::RequestStats`) out of the requests
|
137
|
+
# - if there is a batch policy declared for the current job `type`, it checks compliance against `stats` (`Eco::API::Session::Batch::Policies`),
|
138
|
+
# - a non-compliant batch will stop the current session by raising an `Exception`
|
139
|
+
# - this setp is **skipped** if the option `-skip-batch-policy` was used in the command line
|
140
|
+
# 3. if we are **not** in `dry-run` (or `simulate`), it:
|
141
|
+
# - backs up the raw queries (`requests`) launched to the Server, if we are **not** in `dry-run` (or `simulate`)
|
142
|
+
# - **launches the batch** request against the _Server_ (see `Eco::API::Session::Batch#launch`)
|
143
|
+
# - links the resulting batch `status` to this `Batch::Job` (see `Eco::API::Session::Batch::Status`)
|
144
|
+
# - prints any `errors` replied by the _Server_
|
145
|
+
# 4. the post launch kicks in, and:
|
146
|
+
# - for success requests, it consolidates the associated entries (see `Ecoportal::API::V1::Person#consolidate!`)
|
147
|
+
# - launches specific error handlers, if there were **errors** from the Server as a result of the `batch.launch`, and there are `Error::Handlers` defined
|
148
|
+
# @return [Eco::API::Session::Batch::Status]
|
119
149
|
def launch(simulate: false)
|
120
|
-
pqueue
|
121
|
-
requests = pqueue.map {|e| as_update(e)}
|
150
|
+
pqueue = processed_queue
|
151
|
+
@requests = pqueue.map {|e| as_update(e)}
|
122
152
|
|
123
153
|
pre_checks(requests, simulate: simulate)
|
124
154
|
|
125
|
-
|
155
|
+
unless simulate
|
126
156
|
if pqueue.length > 0
|
127
157
|
backup_update(requests)
|
128
|
-
|
129
|
-
|
158
|
+
session.batch.launch(pqueue, method: type).tap do |job_status|
|
159
|
+
@status = job_status
|
160
|
+
status.root = self
|
161
|
+
status.errors.print
|
162
|
+
end
|
130
163
|
end
|
131
164
|
end
|
132
165
|
|
133
|
-
|
166
|
+
unless requests.empty?
|
167
|
+
logger.info("--- simulate mode (dry-run) -- job '#{name}' -- this would have launched #{type.to_s.upcase}") if simulate
|
168
|
+
end
|
134
169
|
|
135
|
-
|
170
|
+
post_launch(queue: pqueue, simulate: simulate)
|
136
171
|
@pending = false
|
137
|
-
return
|
172
|
+
return status
|
173
|
+
end
|
174
|
+
|
175
|
+
# Provides a text summary of the current status
|
176
|
+
# @note if `launch` was not invoked, it specifies so
|
177
|
+
# @return [String] the summary
|
178
|
+
def summary
|
179
|
+
[].tap do |msg|
|
180
|
+
if pending?
|
181
|
+
msg << "PENDING - Batch #{type.to_s.upcase} - job '#{name}' - length: #{@queue.length}"
|
182
|
+
else
|
183
|
+
msg << feedback.generate(requests, only_stats: true)
|
184
|
+
if batch_policy && !batch_policy.compliant?(request_stats)
|
185
|
+
msg << "Batch Policy Uncompliance:"
|
186
|
+
msg << batch_policy.uncompliance(request_stats)
|
187
|
+
end
|
188
|
+
|
189
|
+
msg << status.errors.message unless !status
|
190
|
+
end
|
191
|
+
end.join("\n")
|
138
192
|
end
|
139
193
|
|
140
194
|
private
|
@@ -178,28 +232,29 @@ module Eco
|
|
178
232
|
msg = feedback.generate(requests, max_chars: max_chars, only_stats: only_stats)
|
179
233
|
logger.info(msg)
|
180
234
|
|
181
|
-
|
182
|
-
|
235
|
+
# batch_policy
|
236
|
+
stats = request_stats(requests)
|
237
|
+
if simulate && batch_policy && !batch_policy.compliant?(stats)
|
183
238
|
logger.warn("Batch Policy Uncompliance: this and next batches will be aborted!")
|
184
|
-
logger.warn(batch_policy.uncompliance(
|
239
|
+
logger.warn(batch_policy.uncompliance(stats))
|
185
240
|
elsif batch_policy
|
186
241
|
# will throw an Exception if the policy request_stats is not compliant
|
187
|
-
batch_policy.validate!(
|
242
|
+
batch_policy.validate!(stats)
|
188
243
|
end
|
189
244
|
end
|
190
245
|
|
191
246
|
def post_launch(queue: [], simulate: false)
|
192
|
-
if !simulate &&
|
193
|
-
|
194
|
-
if
|
247
|
+
if !simulate && status
|
248
|
+
status.queue.map do |entry|
|
249
|
+
if status.success?(entry)
|
195
250
|
entry.consolidate! if entry.respond_to?(:consolidate!)
|
196
251
|
#else # do not entry.reset! (keep track on changes still)
|
197
252
|
end
|
198
253
|
end
|
199
254
|
# launch_error handlers
|
200
255
|
handlers = session.config.error_handlers
|
201
|
-
if
|
202
|
-
err_types =
|
256
|
+
if status.errors.any? && !handlers.empty?
|
257
|
+
err_types = status.errors.by_type
|
203
258
|
handlers.each do |handler|
|
204
259
|
if entries = err_types[handler.name]
|
205
260
|
handler.launch(people: people(entries), session: session, options: options)
|
@@ -3,6 +3,7 @@ module Eco
|
|
3
3
|
class Session
|
4
4
|
class Batch
|
5
5
|
class Jobs < API::Common::Session::BaseSession
|
6
|
+
include Enumerable
|
6
7
|
attr_reader :name
|
7
8
|
|
8
9
|
def initialize(e, name:)
|
@@ -16,6 +17,23 @@ module Eco
|
|
16
17
|
@callbacks = {}
|
17
18
|
end
|
18
19
|
|
20
|
+
def length
|
21
|
+
count
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty?
|
25
|
+
count == 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def each(params: {}, &block)
|
29
|
+
return to_enum(:each) unless block
|
30
|
+
items.each(&block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def items
|
34
|
+
@jobs.values
|
35
|
+
end
|
36
|
+
|
19
37
|
def [](name)
|
20
38
|
@jobs[name]
|
21
39
|
end
|
@@ -41,30 +59,48 @@ module Eco
|
|
41
59
|
end
|
42
60
|
|
43
61
|
def pending?
|
44
|
-
|
62
|
+
any? {|job| job.pending?}
|
45
63
|
end
|
46
64
|
|
47
65
|
def launch(simulate: false)
|
48
|
-
|
49
|
-
@jobs.each do |name, job|
|
66
|
+
each do |job|
|
50
67
|
if job.pending?
|
51
|
-
|
52
|
-
callback
|
68
|
+
status[job] = job_status = job.launch(simulate: simulate)
|
69
|
+
callback = @callbacks[job]
|
53
70
|
callback.call(job, job_status) if callback
|
54
71
|
end
|
55
72
|
end
|
56
73
|
|
57
|
-
return
|
74
|
+
return status
|
58
75
|
end
|
59
76
|
|
60
77
|
def find_jobs(type:)
|
61
|
-
|
62
|
-
if
|
63
|
-
|
78
|
+
each_with_object([]) do |job, jbs|
|
79
|
+
jbs.push(job) if job.type == type
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def status
|
84
|
+
if block_given?
|
85
|
+
status.each do |job, job_status|
|
86
|
+
yield(job, job_status)
|
64
87
|
end
|
88
|
+
self
|
89
|
+
else
|
90
|
+
@jobs_status ||= {}
|
65
91
|
end
|
66
92
|
end
|
67
93
|
|
94
|
+
def errors?
|
95
|
+
any? {|job| job.errors?}
|
96
|
+
end
|
97
|
+
|
98
|
+
def summary
|
99
|
+
[].tap do |msg|
|
100
|
+
map {|job| msg << job.summary}
|
101
|
+
end.join("\n")
|
102
|
+
end
|
103
|
+
|
68
104
|
end
|
69
105
|
end
|
70
106
|
end
|
@@ -20,6 +20,8 @@ module Eco
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
+
include Enumerable
|
24
|
+
|
23
25
|
def initialize(e)
|
24
26
|
super(e)
|
25
27
|
reset
|
@@ -31,6 +33,23 @@ module Eco
|
|
31
33
|
@callbacks = {}
|
32
34
|
end
|
33
35
|
|
36
|
+
def length
|
37
|
+
count
|
38
|
+
end
|
39
|
+
|
40
|
+
def empty?
|
41
|
+
count == 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def each(params: {}, &block)
|
45
|
+
return to_enum(:each) unless block
|
46
|
+
items.each(&block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def items
|
50
|
+
@groups.values
|
51
|
+
end
|
52
|
+
|
34
53
|
def [](name)
|
35
54
|
@groups[name]
|
36
55
|
end
|
@@ -56,28 +75,48 @@ module Eco
|
|
56
75
|
end
|
57
76
|
|
58
77
|
def pending?
|
59
|
-
|
78
|
+
any? {|group| group.pending?}
|
60
79
|
end
|
61
80
|
|
62
81
|
def launch(simulate: false)
|
63
|
-
|
64
|
-
@order.each.with_index do |group, idx|
|
82
|
+
@order.each_with_index do |group, idx|
|
65
83
|
if group.pending?
|
66
|
-
|
84
|
+
status[group] = group_status = group.launch(simulate: simulate)
|
67
85
|
callback = @callbacks[group]
|
68
86
|
callback.call(group, group_status) if callback
|
69
87
|
self.class.counter(DELAY_BETWEEN_GROUPS) if !simulate && idx < @order.length - 1
|
70
88
|
end
|
71
89
|
end
|
72
|
-
return
|
90
|
+
return status
|
73
91
|
end
|
74
92
|
|
75
93
|
def find_jobs(type:)
|
76
|
-
|
77
|
-
jbs.concat(
|
94
|
+
each_with_object([]) do |group, jbs|
|
95
|
+
jbs.concat(group.find_jobs(type: type))
|
78
96
|
end
|
79
97
|
end
|
80
98
|
|
99
|
+
def status
|
100
|
+
if block_given?
|
101
|
+
status.each do |group, group_status|
|
102
|
+
yield(group, group_status)
|
103
|
+
end
|
104
|
+
self
|
105
|
+
else
|
106
|
+
@groups_status ||= {}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def errors?
|
111
|
+
any? {|group| group.errors?}
|
112
|
+
end
|
113
|
+
|
114
|
+
def summary
|
115
|
+
[].tap do |msg|
|
116
|
+
map {|group| msg << group.summary}
|
117
|
+
end.join("\n")
|
118
|
+
end
|
119
|
+
|
81
120
|
end
|
82
121
|
end
|
83
122
|
end
|
@@ -72,6 +72,12 @@ module Eco
|
|
72
72
|
@errors ||= Eco::API::Session::Batch::Errors.new(status: self)
|
73
73
|
end
|
74
74
|
|
75
|
+
# @see Eco::API::Session::Batch::Errors#any?
|
76
|
+
# @return [Boolean] `true` if there were Server errors, `false` otherwise
|
77
|
+
def errors?
|
78
|
+
errors.any?
|
79
|
+
end
|
80
|
+
|
75
81
|
# Get the assciated `reponse` of an input entry object `key`
|
76
82
|
# @param key [Integer, Hash, Ecoportal::API::V1::Person, Ecoportal::API::Internal::Person] these are the **index options**:
|
77
83
|
# 1. `Integer`: index/position of the entry in the final `queue`
|
@@ -44,6 +44,14 @@ module Eco
|
|
44
44
|
self["region"]
|
45
45
|
end
|
46
46
|
|
47
|
+
def target_files=(value)
|
48
|
+
self["target_files"] = [value].flatten
|
49
|
+
end
|
50
|
+
|
51
|
+
def target_files
|
52
|
+
self["target_files"]
|
53
|
+
end
|
54
|
+
|
47
55
|
def target_directories=(value)
|
48
56
|
self["target_directories"] = [value].flatten
|
49
57
|
end
|
@@ -11,7 +11,8 @@ module Eco
|
|
11
11
|
usecases: nil,
|
12
12
|
launch_jobs: nil,
|
13
13
|
post_launch: {usecases: nil, launch_jobs: nil},
|
14
|
-
end: nil
|
14
|
+
end: nil,
|
15
|
+
close: nil
|
15
16
|
}
|
16
17
|
|
17
18
|
class << self
|
@@ -67,21 +68,38 @@ module Eco
|
|
67
68
|
@after = []
|
68
69
|
end
|
69
70
|
|
71
|
+
# Has this stage run yet?
|
72
|
+
# @note it does **not** include _sub-stages_ that run `before`
|
73
|
+
# @return [Boolean] `true` if it has run, `false` otherwise
|
70
74
|
def pending?
|
71
75
|
@pending
|
72
76
|
end
|
73
77
|
|
78
|
+
# Do not run this stage!
|
74
79
|
def skip!
|
75
80
|
@skip = true
|
76
81
|
@pending = false
|
77
82
|
end
|
78
83
|
|
84
|
+
# Has this stage been marked as to be skipped
|
85
|
+
# @return [Boolean] depends on this order:
|
86
|
+
# - `true` if `skip!` was called
|
87
|
+
# - `false` if the current _stage_ is `root?` (the top stage of the hierarchy)
|
88
|
+
# - `true` if its parent task is to be skipped
|
79
89
|
def skip?
|
80
90
|
return @skip if instance_variable_defined?(:@skip)
|
81
91
|
return false if root?
|
82
92
|
@_parent.skip?
|
83
93
|
end
|
84
94
|
|
95
|
+
# Used in **configuration** time **to configure** the _workflow_ of the target (sub)stage `key`
|
96
|
+
# @note if a `block` is provided it will `yield` the target stage immediately
|
97
|
+
# @param key [Symbol, nil] cases:
|
98
|
+
# - if `key` is not provided, it targets the _current stage_
|
99
|
+
# - if `key` is provided, it targets the specific _sub-stage_
|
100
|
+
# @yield [stage_workflow] further _workflow_ configuration `for` the target stage `key`
|
101
|
+
# @yieldparam stage_workflow [Eco::API::Session::Config::Workflow] the _target stage_ referred by `key`
|
102
|
+
# @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
|
85
103
|
def for(key = nil)
|
86
104
|
raise "A block should be given." unless block_given?
|
87
105
|
if !key
|
@@ -92,6 +110,16 @@ module Eco
|
|
92
110
|
self
|
93
111
|
end
|
94
112
|
|
113
|
+
# Used in **configuration** time **to define** the **behaviour** the target (sub)stage `key`
|
114
|
+
# @note if a `block` is provided it will **not** `yield` the target stage immediately, but when the _workflow_ reaches the stage
|
115
|
+
# @param key [Symbol, nil] cases:
|
116
|
+
# - if `key` is not provided, it targets the _current stage_
|
117
|
+
# - if `key` is provided, it targets the specific _sub-stage_
|
118
|
+
# @yield [stage_workflow, io] the behaviour of the target stage `key` when the _workflow_ reaches it
|
119
|
+
# @yieldparam stage_workflow [Eco::API::Session::Config::Workflow] the _target stage_ referred by `key`
|
120
|
+
# @yieldparam io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
121
|
+
# @yieldreturn io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
122
|
+
# @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
|
95
123
|
def on(key = nil, &block)
|
96
124
|
raise "A block should be given." unless block
|
97
125
|
if !key
|
@@ -102,6 +130,30 @@ module Eco
|
|
102
130
|
self
|
103
131
|
end
|
104
132
|
|
133
|
+
# When there is an `Exception`, you might have defined some `callback` to do something with it (i.e. register, email)
|
134
|
+
# @yield [exception, io] the `callback` to do something with an `Exception` raised within this _workflow_ stage
|
135
|
+
# @yieldparam exception [Exception] the exception object that was raised
|
136
|
+
# @yieldparam io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
137
|
+
# @yieldreturn io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
138
|
+
# @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
|
139
|
+
def rescue(&block)
|
140
|
+
return @rescue unless block
|
141
|
+
@rescue = block
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
# Used in **configuration** time **add previous** `callbacks` **before** the `on` _callback_ of the (sub)stage `key` is actually `run`
|
146
|
+
# @note
|
147
|
+
# - it will **not** `yield` it immediately, but when the _workflow_ reaches the target stage
|
148
|
+
# - in this case, you can define multiple `callbacks`
|
149
|
+
# @param key [Symbol, nil] cases:
|
150
|
+
# - if `key` is not provided, it targets the _current stage_
|
151
|
+
# - if `key` is provided, it targets the specific _sub-stage_
|
152
|
+
# @yield [stage_workflow, io] one of the things to do **before** the `on` _callback_ of the (sub)stage `key` is actually `run`
|
153
|
+
# @yieldparam stage_workflow [Eco::API::Session::Config::Workflow] the _target stage_ referred by `key`
|
154
|
+
# @yieldparam io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
155
|
+
# @yieldreturn io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
156
|
+
# @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
|
105
157
|
def before(key = nil, &block)
|
106
158
|
raise "A block should be given." unless block
|
107
159
|
if !key
|
@@ -112,6 +164,18 @@ module Eco
|
|
112
164
|
self
|
113
165
|
end
|
114
166
|
|
167
|
+
# Used in **configuration** time **add previous** `callbacks` **after** the `on` _callback_ of the (sub)stage `key` is actually `run`
|
168
|
+
# @note
|
169
|
+
# - it will **not** `yield` it immediately, but when the _workflow_ reaches the target stage
|
170
|
+
# - in this case, you can define multiple `callbacks`
|
171
|
+
# @param key [Symbol, nil] cases:
|
172
|
+
# - if `key` is not provided, it targets the _current stage_
|
173
|
+
# - if `key` is provided, it targets the specific _sub-stage_
|
174
|
+
# @yield [stage_workflow, io] one of the things to do **after** the `on` _callback_ of the (sub)stage `key` is actually `run`
|
175
|
+
# @yieldparam stage_workflow [Eco::API::Session::Config::Workflow] the _target stage_ referred by `key`
|
176
|
+
# @yieldparam io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
177
|
+
# @yieldreturn io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
178
|
+
# @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
|
115
179
|
def after(key = nil, &block)
|
116
180
|
raise "A block should be given." unless block
|
117
181
|
if !key
|
@@ -122,29 +186,58 @@ module Eco
|
|
122
186
|
self
|
123
187
|
end
|
124
188
|
|
189
|
+
# Used in run time to **execute the workflow** of the (sub)stage `key`
|
190
|
+
# @note if a `block` is **not** provided:
|
191
|
+
# - it will run the `before` _callbacks_ defined during the configuration time
|
192
|
+
# - it will run the _workflow_ of any defined _**substage**_ of the `key` stage
|
193
|
+
# - it will run the `on` _callback_ defined during the configuration time
|
194
|
+
# - it will mark the stage as **not** `pending?`.
|
195
|
+
# - it will run the `after` _callbacks_ defined during the configuration time
|
196
|
+
# @note if a `block` is provided:
|
197
|
+
# - it will **not** run the workflow of the substages to `key` stage
|
198
|
+
# - it will **not** run the `callback` for `on` defined during the configuration time
|
199
|
+
# - it will rather `yield` the target stage after all the `before` _callbacks_ have been run
|
200
|
+
# - aside of this, the rest will be the same as when the _block_ is provided (see previous note)
|
201
|
+
# @param key [Symbol, nil] cases:
|
202
|
+
# - if `key` is not provided, it targets the _current stage_
|
203
|
+
# - if `key` is provided, it targets the specific _sub-stage_
|
204
|
+
# @yield [stage_workflow, io] if a `block` is provided, see `note`
|
205
|
+
# @yieldparam stage_workflow [Eco::API::Session::Config::Workflow] the _target stage_ referred by `key`
|
206
|
+
# @yieldparam io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
207
|
+
# @yieldreturn io [Eco::API::UseCases::BaseIO] the input/output object carried througout all the _workflow_
|
208
|
+
# @return [Eco::API::Session::Config::Workflow] the current stage object (to ease chainig).
|
125
209
|
def run(key = nil, io:, &block)
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
210
|
+
begin
|
211
|
+
if key
|
212
|
+
io = stage(key).run(io: io, &block)
|
213
|
+
elsif pending?
|
214
|
+
@before.each {|c| io = c.call(self, io)}
|
215
|
+
|
216
|
+
unless skip?
|
217
|
+
io.session.logger.debug("(Workflow: #{path}) running now")
|
218
|
+
if block
|
219
|
+
io = block.call(self, io)
|
220
|
+
else
|
221
|
+
existing_stages.each {|stg| io = stg.run(io: io)}
|
222
|
+
|
223
|
+
unless ready?
|
224
|
+
msg = "(Workflow: #{path}) 'on' callback is not defined, nor block given"
|
225
|
+
io.session.logger.debug(msg)
|
226
|
+
end
|
227
|
+
io = @on.call(self, io) if ready?
|
141
228
|
end
|
142
|
-
|
229
|
+
@pending = false
|
143
230
|
end
|
144
|
-
@pending = false
|
145
|
-
end
|
146
231
|
|
147
|
-
|
232
|
+
@after.each {|c| io = c.call(self, io)}
|
233
|
+
end
|
234
|
+
rescue SystemExit
|
235
|
+
exit
|
236
|
+
rescue Interrupt => i
|
237
|
+
raise i
|
238
|
+
rescue Exception => e
|
239
|
+
self.rescue.call(e, io) if self.rescue
|
240
|
+
raise e
|
148
241
|
end
|
149
242
|
io
|
150
243
|
end
|
data/lib/eco/api/session/task.rb
CHANGED
@@ -115,6 +115,17 @@ module Eco
|
|
115
115
|
Eco::API::Organization::People.new(people)
|
116
116
|
end
|
117
117
|
|
118
|
+
def s3upload_targets
|
119
|
+
[].tap do |paths|
|
120
|
+
session.config.s3storage.target_files.each_with_object(paths) do |file, arr|
|
121
|
+
arr.push(session.s3upload(file: file))
|
122
|
+
end
|
123
|
+
session.config.s3storage.target_directories.each_with_object(paths) do |folder, arr|
|
124
|
+
arr.concat(session.s3upload(directory: folder))
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
118
129
|
private
|
119
130
|
|
120
131
|
# MODIFIERS
|
data/lib/eco/cli.rb
CHANGED
@@ -20,13 +20,9 @@ module Eco
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def workflow
|
24
|
-
@workflow ||= Eco::CLI::Workflow.new(cli: self)
|
25
|
-
end
|
26
|
-
|
27
23
|
def run(session:)
|
28
24
|
io = Eco::API::UseCases::BaseIO.new(session: session, options: options)
|
29
|
-
|
25
|
+
#session.workflow.run(io: io)
|
30
26
|
session.workflow(io: io) do |wf, io|
|
31
27
|
io = wf.run(:options, io: io)
|
32
28
|
io = wf.run(:load, io: io)
|
@@ -2,6 +2,13 @@ ASSETS.cli.config do |config|
|
|
2
2
|
ASSETS.config.workflow do |wf|
|
3
3
|
|
4
4
|
io = nil
|
5
|
+
# default rescue
|
6
|
+
wf.rescue do |exception, io|
|
7
|
+
#io.session.logger.error(exception.patch_full_message)
|
8
|
+
wf.run(:close, io: io)
|
9
|
+
io
|
10
|
+
end
|
11
|
+
|
5
12
|
wf.on(:options) do |wf_options, io|
|
6
13
|
io = io.new(options: config.options_set.process(io: io))
|
7
14
|
end
|
data/lib/eco/version.rb
CHANGED
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: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oscar Segura
|
@@ -288,6 +288,7 @@ files:
|
|
288
288
|
- lib/eco/api/common/version_patches/ecoportal_api/base_model.rb
|
289
289
|
- lib/eco/api/common/version_patches/ecoportal_api/external_person.rb
|
290
290
|
- lib/eco/api/common/version_patches/ecoportal_api/internal_person.rb
|
291
|
+
- lib/eco/api/common/version_patches/exception.rb
|
291
292
|
- lib/eco/api/common/version_patches/hash.rb
|
292
293
|
- lib/eco/api/common/version_patches/hash/deep_merge.rb
|
293
294
|
- lib/eco/api/eco_faker.rb
|