eco-helpers 3.0.20 → 3.0.21
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/CHANGELOG.md +21 -3
- data/lib/eco/api/common/loaders/config/cli.rb +9 -0
- data/lib/eco/api/common/loaders/config.rb +1 -0
- data/lib/eco/api/common/people/default_parsers/xls_parser.rb +1 -1
- data/lib/eco/api/common/session/logger/cache.rb +10 -4
- data/lib/eco/api/common/session/logger/channels.rb +41 -0
- data/lib/eco/api/common/session/logger.rb +9 -0
- data/lib/eco/api/session/batch/job.rb +11 -0
- data/lib/eco/cli_default/input.rb +49 -29
- data/lib/eco/cli_default/options.rb +4 -1
- data/lib/eco/cli_default/people.rb +102 -47
- data/lib/eco/cli_default/people_filters.rb +4 -1
- data/lib/eco/language/auxiliar_logger.rb +16 -3
- data/lib/eco/language/basic_logger.rb +1 -0
- data/lib/eco/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7764e6e3e78228f59b9a5f5baf2aa8602c627882905a23270007b4fe1a09c9f
|
4
|
+
data.tar.gz: aca94b571b1c70c000e0d88f09182f2339a306187c5f9651968e9692981d79d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcef313643bc67ba9d90755e3ce8a3a3a39c1973404b2dcd35292b76c67d158ad3e9c666414e2f6254776eeb800f4d07fc2ab10953a15e9e2b293d2b8f15303e
|
7
|
+
data.tar.gz: 3d12155562df35e9ecadea021a758f922f35ab68d689592e4a319e41232b4865a87e22c0e333b1083e42340418f57388f24139748246d08652142ef52f1a041d
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,27 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
-
## [3.0.
|
5
|
+
## [3.0.21] - 2024-12-16
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
- `Eco::API::Common::Session::Logger`
|
10
|
+
- Implement **channels** logging.
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
- Moved `input` and `people` **cli_default configurations** to a class definition
|
15
|
+
- `Eco::Language::AuxiliarLogger#log`
|
16
|
+
- Allow for multiple levels
|
17
|
+
- Return `NilClass`
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
|
21
|
+
- `Eco::API::Session::Batch::Job#summary`
|
22
|
+
- Ensure a summary of pending jobs can be handled, even if `#launch` was NOT explicitly called.
|
23
|
+
- As we don't consolidate, it may give more actual updates than pending, yet this is better than providing the `count` of entries in the job's queue.
|
24
|
+
|
25
|
+
## [3.0.20] - 2024-12-07
|
6
26
|
|
7
27
|
### Added
|
8
28
|
|
@@ -16,8 +36,6 @@ All notable changes to this project will be documented in this file.
|
|
16
36
|
|
17
37
|
- On failure, `Eco::API::Session::Job#summary` to give precise estimates on what is pending to be run.
|
18
38
|
|
19
|
-
### Fixed
|
20
|
-
|
21
39
|
## [3.0.19] - 2024-11-21
|
22
40
|
|
23
41
|
### Added
|
@@ -27,7 +27,7 @@ class Eco::API::Common::People::DefaultParsers::XLSParser < Eco::API::Common::Lo
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def expected_headers
|
30
|
-
log(:
|
30
|
+
log(:info) {
|
31
31
|
"Headers detection is using your fields_map.json file (native behaviour)"
|
32
32
|
}
|
33
33
|
session.fields_mapper.list(:external).uniq
|
@@ -4,7 +4,8 @@ module Eco
|
|
4
4
|
module Session
|
5
5
|
class Logger
|
6
6
|
class Cache
|
7
|
-
LEVELS
|
7
|
+
LEVELS = %w[UNKNOWN FATAL ERROR WARN INFO DEBUG].freeze
|
8
|
+
CHANNELS = Logger::CHANNELS.map(&:to_s).map(&:upcase).freeze
|
8
9
|
|
9
10
|
def initialize
|
10
11
|
reset
|
@@ -56,7 +57,8 @@ module Eco
|
|
56
57
|
end
|
57
58
|
|
58
59
|
def to_datetime(value)
|
59
|
-
return
|
60
|
+
return unless value
|
61
|
+
|
60
62
|
Time.parse(value)
|
61
63
|
end
|
62
64
|
|
@@ -64,7 +66,7 @@ module Eco
|
|
64
66
|
levels = [value].flatten.map {|v| to_level(v)}.compact
|
65
67
|
return levels unless levels.empty?
|
66
68
|
|
67
|
-
|
69
|
+
valid_levels
|
68
70
|
end
|
69
71
|
|
70
72
|
def to_level(value)
|
@@ -75,7 +77,7 @@ module Eco
|
|
75
77
|
|
76
78
|
def valid_level!(str)
|
77
79
|
return true unless str
|
78
|
-
return true if
|
80
|
+
return true if valid_levels.any? {|lev| str == lev}
|
79
81
|
|
80
82
|
msg = "Unknown level #{str}. Should be one of #{LEVELS}"
|
81
83
|
raise ArgumentError, msg
|
@@ -87,6 +89,10 @@ module Eco
|
|
87
89
|
|
88
90
|
value
|
89
91
|
end
|
92
|
+
|
93
|
+
def valid_levels
|
94
|
+
@valid_levels ||= self.class::LEVELS | self.class::CHANNELS
|
95
|
+
end
|
90
96
|
end
|
91
97
|
end
|
92
98
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Eco
|
2
|
+
module API
|
3
|
+
module Common
|
4
|
+
module Session
|
5
|
+
class Logger
|
6
|
+
module Channels
|
7
|
+
CHANNELS = %i[general].freeze
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def included(base)
|
11
|
+
super
|
12
|
+
base.extend ClassMethods
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def channels!(&def_block)
|
18
|
+
str = "Block with channel implementation expected. None given."
|
19
|
+
raise ArgumentError, str unless block_given?
|
20
|
+
|
21
|
+
channels.each do |channel|
|
22
|
+
meth = channel.to_s.downcase.to_sym
|
23
|
+
|
24
|
+
define_method(meth) do |msg = nil, &block|
|
25
|
+
def_block.call(meth, msg, &block)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def channels
|
33
|
+
self::CHANNELS
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -5,6 +5,15 @@ module Eco
|
|
5
5
|
class Logger < Eco::Language::BasicLogger
|
6
6
|
TIMESTAMP_PATTERN = '%Y-%m-%dT%H:%M:%S'.freeze
|
7
7
|
|
8
|
+
require_relative 'logger/channels'
|
9
|
+
include Channels
|
10
|
+
|
11
|
+
channels! do |channel, message = nil, &block|
|
12
|
+
format_proc(console: true) do |severity, datetime, msg, formatted_msg|
|
13
|
+
cache.add(severity, datetime, msg, formatted_msg)
|
14
|
+
end.call(channel, Time.now, 'prog_name', message || block.call)
|
15
|
+
end
|
16
|
+
|
8
17
|
attr_reader :cache
|
9
18
|
|
10
19
|
def initialize(file_level: ::Logger::DEBUG, log_file: nil, enviro: nil, **kargs)
|
@@ -146,6 +146,16 @@ module Eco
|
|
146
146
|
@pending
|
147
147
|
end
|
148
148
|
|
149
|
+
# @note some times we need a summary of what actually
|
150
|
+
# is going to be run without invoking `launch`
|
151
|
+
# (i.e. when there's a uncompliance with the batch policy)
|
152
|
+
def ensure_requests!
|
153
|
+
return if instance_variable_defined?(:@requests)
|
154
|
+
|
155
|
+
pqueue = processed_queue
|
156
|
+
@requests = as_update(pqueue)
|
157
|
+
end
|
158
|
+
|
149
159
|
# @note it requires launch to be firstly invoked
|
150
160
|
# @raise [Exception] if 'launch' has not firstly invoked
|
151
161
|
# @return [Enumbrable<Hash>] the last requests that the queue will generate
|
@@ -250,6 +260,7 @@ module Eco
|
|
250
260
|
def summary
|
251
261
|
[].tap do |msg|
|
252
262
|
msg << "PENDING Job -------->\n" if pending?
|
263
|
+
ensure_requests!
|
253
264
|
msg << feedback.generate(requests, only_stats: true)
|
254
265
|
|
255
266
|
if batch_policy && !batch_policy.compliant?(request_stats)
|
@@ -1,6 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
formats = {
|
1
|
+
class Eco::CliDefault::Input < Eco::API::Common::Loaders::CliConfig
|
2
|
+
FORMATS = {
|
4
3
|
csv: {
|
5
4
|
option: ["-csv"],
|
6
5
|
extname: [".csv", ".txt"]
|
@@ -17,51 +16,74 @@ ASSETS.cli.config do |cnf|
|
|
17
16
|
option: ["-json"],
|
18
17
|
extname: [".json"]
|
19
18
|
}
|
20
|
-
}
|
19
|
+
}.freeze
|
21
20
|
|
22
|
-
|
23
|
-
|
21
|
+
class << self
|
22
|
+
attr_reader :options, :session
|
24
23
|
|
24
|
+
def encoding
|
25
|
+
options.dig(:input, :file, :encoding)
|
26
|
+
end
|
27
|
+
|
28
|
+
def format_by_cli
|
29
|
+
FORMATS.reduce(nil) do |matched, (frm, selectors)|
|
30
|
+
next matched if matched
|
31
|
+
|
32
|
+
used = selectors[:option].reduce(false) do |us, option|
|
33
|
+
SCR.get_arg(option) || us
|
34
|
+
end
|
35
|
+
|
36
|
+
next frm if used
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def format_by_ext(ext)
|
41
|
+
FORMATS.reduce(nil) do |matched, (frm, selectors)|
|
42
|
+
next matched if matched
|
43
|
+
next frm if selectors[:extname].any? {|e| ext == e}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
input(default_option: "-entries-from") do |session, str_opt, options|
|
49
|
+
@options = options
|
50
|
+
@session = session
|
51
|
+
input = []
|
25
52
|
next input unless SCR.get_arg(str_opt)
|
26
53
|
|
27
54
|
file = SCR.get_file(str_opt, required: true)
|
28
55
|
|
29
56
|
# Command line check
|
30
|
-
format =
|
31
|
-
used = selectors[:option].reduce(false) {|us, option| SCR.get_arg(option) || us}
|
32
|
-
next matched if matched
|
33
|
-
next frm if used
|
34
|
-
end
|
57
|
+
format = format_by_cli
|
35
58
|
|
36
59
|
# File/Folder check
|
37
60
|
file = File.expand_path(file)
|
61
|
+
|
38
62
|
if File.directory?(file)
|
39
63
|
folder = file
|
40
64
|
file = Dir.glob("#{file}/*").reject {|f| File.directory?(f)}
|
41
|
-
ext = (format
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
65
|
+
ext = FORMATS.dig(format, :extname)
|
66
|
+
ext ||= [File.extname(file.first)]
|
67
|
+
file = file.select do |f|
|
68
|
+
ext.any? {|e| File.extname(f) == e}
|
69
|
+
end.tap do |files|
|
70
|
+
next unless files.empty?
|
71
|
+
|
72
|
+
session.log(:error) {
|
73
|
+
"Could not find any file with extension: #{ext} in folder '#{folder}'"
|
74
|
+
}
|
75
|
+
exit(1)
|
49
76
|
end
|
50
77
|
else
|
51
|
-
ext = File.extname(file)
|
78
|
+
ext = [File.extname(file)]
|
52
79
|
end
|
53
80
|
|
54
|
-
format ||=
|
55
|
-
next matched if matched
|
56
|
-
next frm if selectors[:extname].any? {|e| ext == e}
|
57
|
-
end
|
81
|
+
format ||= format_by_ext(ext.first)
|
58
82
|
format ||= :csv
|
59
83
|
|
60
|
-
options.deep_merge!(input: {file: {name:
|
84
|
+
options.deep_merge!(input: {file: {name: file}})
|
61
85
|
options.deep_merge!(input: {file: {format: format}})
|
62
86
|
|
63
|
-
encoding = options.dig(:input, :file, :encoding)
|
64
|
-
|
65
87
|
case format
|
66
88
|
when :xml
|
67
89
|
[file].flatten.each {|f| session.config.files.validate(:xml, f)}
|
@@ -83,5 +105,3 @@ ASSETS.cli.config do |cnf|
|
|
83
105
|
input
|
84
106
|
end
|
85
107
|
end
|
86
|
-
|
87
|
-
# rubocop:enable Metrics/BlockLength
|
@@ -69,7 +69,10 @@ ASSETS.cli.config do |cnf| # rubocop:disable Metrics/BlockLength
|
|
69
69
|
sch_id = session.schemas.to_id(sch_name)
|
70
70
|
|
71
71
|
unless sch_id
|
72
|
-
msg = "You need to specify a correct schema id or name.
|
72
|
+
msg = "You need to specify a correct schema id or name. "
|
73
|
+
msg << "'#{sch_name}' does not exist. Correct options are: "
|
74
|
+
msg << session.schemas.map(&:name).join(", ")
|
75
|
+
|
73
76
|
session.log(:error) { msg }
|
74
77
|
exit(1)
|
75
78
|
end
|
@@ -1,69 +1,124 @@
|
|
1
|
-
|
1
|
+
class Eco::CliDefault::People < Eco::API::Common::Loaders::CliConfig
|
2
|
+
MAX_GET_PARTIAL = 12_000
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
get = {} if get.nil?
|
4
|
+
class << self
|
5
|
+
attr_reader :options, :session
|
6
|
+
attr_writer :get_full, :get_partial
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
def get_options # rubocop:disable Naming/AccessorMethodName
|
9
|
+
get = options.dig(:people, :get)
|
10
|
+
get = {} if get.nil?
|
11
|
+
get
|
12
|
+
end
|
13
|
+
|
14
|
+
def no_get?
|
15
|
+
get_options == false
|
16
|
+
end
|
10
17
|
|
11
|
-
|
12
|
-
|
13
|
-
|
18
|
+
def from_remote?
|
19
|
+
return false unless get_options
|
20
|
+
|
21
|
+
get_options[:from] == :remote
|
22
|
+
end
|
23
|
+
|
24
|
+
def from_local?
|
25
|
+
return false unless get_options
|
26
|
+
|
27
|
+
get_options[:from] == :local
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_full?
|
31
|
+
return @get_full if instance_variable_defined?(:@get_full)
|
32
|
+
return false unless from_remote?
|
33
|
+
|
34
|
+
get_options[:type] == :full
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_partial?
|
38
|
+
return @get_partial if instance_variable_defined?(:@get_partial)
|
39
|
+
return false unless from_remote?
|
40
|
+
|
41
|
+
@get_partial = (get_options[:type] == :partial)
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_by_file?
|
45
|
+
return false unless from_local?
|
46
|
+
|
47
|
+
get_options[:type] == :file
|
48
|
+
end
|
49
|
+
|
50
|
+
def source_file
|
51
|
+
return unless get_by_file?
|
52
|
+
|
53
|
+
get_options[:file]
|
54
|
+
end
|
55
|
+
|
56
|
+
def switch_to_full_remote!
|
57
|
+
self.get_full = true
|
58
|
+
self.get_partial = false
|
59
|
+
|
60
|
+
options.deep_merge!(people: {
|
61
|
+
get: {
|
62
|
+
from: :remote,
|
63
|
+
type: :full
|
64
|
+
}
|
65
|
+
})
|
66
|
+
end
|
67
|
+
|
68
|
+
def switch_to_full_local!
|
69
|
+
self.get_full = true
|
70
|
+
self.get_partial = false
|
71
|
+
|
72
|
+
options.deep_merge!(people: {
|
73
|
+
get: {
|
74
|
+
from: :local,
|
75
|
+
type: :full
|
76
|
+
}
|
77
|
+
})
|
78
|
+
end
|
79
|
+
|
80
|
+
def optimize_get_partial!(input)
|
81
|
+
return unless get_partial?
|
14
82
|
|
15
|
-
# -get-partial: validate input present and under max
|
16
|
-
if get_partial
|
17
83
|
msg = "To use -get-partial (partial updates), you need to use -entries-from"
|
18
84
|
raise msg unless input.is_a?(Enumerable)
|
19
85
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
options.deep_merge!(people: {
|
31
|
-
get: {
|
32
|
-
from: :remote,
|
33
|
-
type: :full
|
34
|
-
}
|
35
|
-
})
|
36
|
-
end
|
86
|
+
return unless input.count > MAX_GET_PARTIAL
|
87
|
+
|
88
|
+
msg = "(Optimization) "
|
89
|
+
msg << "Switching from partial to full people download. "
|
90
|
+
msg << "Input (#{input.count}) surpases MAX_GET_PARTIAL "
|
91
|
+
msg << "(#{MAX_GET_PARTIAL}) entries."
|
92
|
+
session.log(:info) { msg }
|
93
|
+
|
94
|
+
switch_to_full_remote!
|
37
95
|
end
|
96
|
+
end
|
97
|
+
|
98
|
+
people do |input, session, options|
|
99
|
+
@options = options
|
100
|
+
@session = session
|
38
101
|
|
39
|
-
|
102
|
+
# -get-partial: validate input present and under max
|
103
|
+
optimize_get_partial!(input) if get_partial?
|
104
|
+
|
105
|
+
if no_get?
|
40
106
|
Eco::API::Organization::People.new([])
|
41
|
-
elsif get_full
|
107
|
+
elsif get_full?
|
42
108
|
# -get-people
|
43
109
|
session.micro.people_cache
|
44
|
-
elsif get_partial
|
110
|
+
elsif get_partial?
|
45
111
|
# -get-partial
|
46
112
|
session.micro.people_search(input, options: options)
|
47
|
-
elsif get_by_file
|
113
|
+
elsif get_by_file?
|
48
114
|
# -people-from-backup
|
49
|
-
session.micro.people_load(
|
115
|
+
session.micro.people_load(source_file, modifier: :file)
|
50
116
|
else
|
51
|
-
|
52
|
-
get: {
|
53
|
-
from: :local,
|
54
|
-
type: :full
|
55
|
-
}
|
56
|
-
})
|
57
|
-
|
117
|
+
switch_to_full_local!
|
58
118
|
people = session.micro.people_load(modifier: %i[newest save])
|
59
119
|
|
60
120
|
if people.empty?
|
61
|
-
|
62
|
-
get: {
|
63
|
-
from: :remote,
|
64
|
-
type: :full
|
65
|
-
}
|
66
|
-
})
|
121
|
+
switch_to_full_remote!
|
67
122
|
people = session.micro.people_cache
|
68
123
|
end
|
69
124
|
|
@@ -92,7 +92,10 @@ ASSETS.cli.config do |cnf| # rubocop:disable Metrics/BlockLength
|
|
92
92
|
sch_id = session.schemas.to_id(sch_name)
|
93
93
|
|
94
94
|
unless sch_id
|
95
|
-
msg = "You need to specify a correct schema id or name.
|
95
|
+
msg = "You need to specify a correct schema id or name. "
|
96
|
+
msg << "'#{sch_name}' does not exist. Correct options are: "
|
97
|
+
msg << session.schemas.map(&:name).join(", ")
|
98
|
+
|
96
99
|
session.log(:error) { msg }
|
97
100
|
exit(1)
|
98
101
|
end
|
@@ -21,10 +21,23 @@ module Eco
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Shortcut to logger.
|
24
|
-
|
25
|
-
|
24
|
+
# @todo allow for more channels (atm it's just :general )
|
25
|
+
# @note when `:general` is included, it ensures at least
|
26
|
+
# `:info` level is also logged.
|
27
|
+
# @return [NilClass]
|
28
|
+
def log(*levels, &block)
|
29
|
+
return unless logger
|
26
30
|
|
27
|
-
|
31
|
+
levels = levels.compact.uniq.map(&:to_sym)
|
32
|
+
levels.unshift(:info) if levels.include?(:general) && levels.length == 1
|
33
|
+
|
34
|
+
levels.each do |level|
|
35
|
+
next unless logger.respond_to?(:level)
|
36
|
+
|
37
|
+
logger.send(level, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
28
41
|
end
|
29
42
|
end
|
30
43
|
end
|
@@ -67,6 +67,7 @@ module Eco
|
|
67
67
|
def format_proc(console: true, &block)
|
68
68
|
proc do |severity, datetime, _progname, msg|
|
69
69
|
str_stamp = console ? console_timestamp(datetime) : timestamp(datetime)
|
70
|
+
|
70
71
|
"#{severity.to_s[0]}: #{str_stamp}#{msg}\n".tap do |formatted_msg|
|
71
72
|
block&.call(severity, datetime, msg, formatted_msg)
|
72
73
|
end
|
data/lib/eco/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eco-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.21
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oscar Segura
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
11
|
+
date: 2024-12-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -538,6 +538,7 @@ files:
|
|
538
538
|
- lib/eco/api/common/loaders/base.rb
|
539
539
|
- lib/eco/api/common/loaders/case_base.rb
|
540
540
|
- lib/eco/api/common/loaders/config.rb
|
541
|
+
- lib/eco/api/common/loaders/config/cli.rb
|
541
542
|
- lib/eco/api/common/loaders/config/session.rb
|
542
543
|
- lib/eco/api/common/loaders/config/workflow.rb
|
543
544
|
- lib/eco/api/common/loaders/config/workflow/mailer.rb
|
@@ -580,6 +581,7 @@ files:
|
|
580
581
|
- lib/eco/api/common/session/helpers/prompt_user.rb
|
581
582
|
- lib/eco/api/common/session/logger.rb
|
582
583
|
- lib/eco/api/common/session/logger/cache.rb
|
584
|
+
- lib/eco/api/common/session/logger/channels.rb
|
583
585
|
- lib/eco/api/common/session/logger/log.rb
|
584
586
|
- lib/eco/api/common/session/mailer.rb
|
585
587
|
- lib/eco/api/common/session/mailer/aws_provider.rb
|