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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96eaee3dd7897d77b22857cf8f812ce6d0639af324565c54f649c1e8068b7fb4
4
- data.tar.gz: c9af686fe8f01d45eb3ea6a0dc89de276b789dfd6e3f64b515e17107b233b2d3
3
+ metadata.gz: a7764e6e3e78228f59b9a5f5baf2aa8602c627882905a23270007b4fe1a09c9f
4
+ data.tar.gz: aca94b571b1c70c000e0d88f09182f2339a306187c5f9651968e9692981d79d9
5
5
  SHA512:
6
- metadata.gz: 66bdc8b7c98b2aa0e60e205ac64b47a302e2cd8dbe1efd1720ad36c04a4f4c1a70b2cbb4d4978c67a17bd0f86893d34b47ff809f95a3e06952c002b872872553
7
- data.tar.gz: f205d190dc159c0218bc1cafa52f139a6faaa133a01190ae1b189d2b8b6ef2470f49b634169effcf2021023803ec58f1793b8ce4c019db9d1dd025df7bbf5493
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.20] - 2024-12-xx
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
@@ -0,0 +1,9 @@
1
+ class Eco::API::Common::Loaders::CliConfig < Eco::API::Common::Loaders::Config
2
+ class << self
3
+ def config
4
+ ASSETS.cli.config
5
+ end
6
+ end
7
+
8
+ delegate_missing_to :config
9
+ end
@@ -33,5 +33,6 @@ class Eco::API::Common::Loaders::Config
33
33
  delegate_missing_to :config
34
34
  end
35
35
 
36
+ require_relative 'config/cli'
36
37
  require_relative 'config/workflow'
37
38
  require_relative 'config/session'
@@ -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(:warn) {
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 = %w[UNKNOWN FATAL ERROR WARN INFO DEBUG].freeze
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 nil unless value
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
- LEVELS
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 LEVELS.any? {|lev| str == lev}
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
- # rubocop:disable Metrics/BlockLength
2
- ASSETS.cli.config do |cnf|
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
- cnf.input(default_option: "-entries-from") do |session, str_opt, options|
23
- input = []
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 = formats.reduce(nil) do |matched, (frm, selectors)|
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 && formats[format][:extname]) || [File.extname(file.first)]
42
- file = file.select {|f| ext.any? {|e| File.extname(f) == e}}.tap do |files|
43
- if files.empty?
44
- session.log(:error) {
45
- "Could not find any file with extension: #{ext} in folder '#{folder}'"
46
- }
47
- exit(1)
48
- end
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 ||= formats.reduce(nil) do |matched, (frm, selectors)|
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: file}})
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. '#{sch_name}' does not exist"
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
- MAX_GET_PARTIAL = 12_000
1
+ class Eco::CliDefault::People < Eco::API::Common::Loaders::CliConfig
2
+ MAX_GET_PARTIAL = 12_000
2
3
 
3
- ASSETS.cli.config do |cnf|
4
- cnf.people do |input, session, options|
5
- get = options.dig(:people, :get)
6
- get = {} if get.nil?
4
+ class << self
5
+ attr_reader :options, :session
6
+ attr_writer :get_full, :get_partial
7
7
 
8
- from_remote = get && get[:from] == :remote
9
- from_local = get && get[:from] == :local
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
- get_full = from_remote && get[:type] == :full
12
- get_partial = from_remote && get[:type] == :partial
13
- get_by_file = from_local && get[:type] == :file
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
- if input.count > MAX_GET_PARTIAL
21
- get_full = true
22
- get_partial = false
23
-
24
- msg = "(Optimization) "
25
- msg << "Switching from partial to full people download. "
26
- msg << "Input (#{input.count}) surpases MAX_GET_PARTIAL "
27
- msg << "(#{MAX_GET_PARTIAL}) entries."
28
- session.log(:info) { msg }
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
- if get == false
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(get[:file], modifier: :file)
115
+ session.micro.people_load(source_file, modifier: :file)
50
116
  else
51
- options.deep_merge!(people: {
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
- options.deep_merge!(people: {
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. '#{sch_name}' does not exist"
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
- def log(level, &block)
25
- return unless logger.respond_to?(:level)
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
- logger&.send(level, &block)
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
@@ -1,3 +1,3 @@
1
1
  module Eco
2
- VERSION = '3.0.20'.freeze
2
+ VERSION = '3.0.21'.freeze
3
3
  end
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.20
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-07 00:00:00.000000000 Z
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