chronicle-etl 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f041e90fc6019ecbc2424e8e75e7f21fa5ba715c2cdbde73d61c298e9ca82e07
4
- data.tar.gz: 2feda7669f8fefc7ad80fe7918530a86ad2ced431f75e70ad42653c871b67d90
3
+ metadata.gz: f2b6fdca3723ec52287c070a0dd08d0cfaf825f5e8f46da0d5a34172c0008573
4
+ data.tar.gz: e15181ba7edc1698404af8ff8c05d5367786ea809360393e825ca5ee5eef6c75
5
5
  SHA512:
6
- metadata.gz: 4a40c72dcb6514037c6e53214dc0af3bfba20c272b959c3e83496658b4b8dc3f841d7399aa215a5fcc5c5cd494278223666b8b881f645ed2c61b667351ccde94
7
- data.tar.gz: 5b5450b76a8c03d7cb8405888b2e059390c1585a66524dd7403b458c828a1936422e03a9654ec6d270b634bf6bfe17d6dafda54e05aca8a8732207366d03ffd2
6
+ metadata.gz: c5508dcfc5e1367122ebbc191dc60a76cc7b1f088ae9ebd52bae41e07420f54ea7977a35f2b25839933b06d4673e2a120feecbd958efee07e0d313eaa7a5d167
7
+ data.tar.gz: adcb90549af364189c5ae3b0811c039277aba7dc6fbf2fbd6de8b89c572948d0dc00ba8da59db03252f4770737423e62c3a1175ef018742ef8bd7aee14837f63
@@ -0,0 +1,31 @@
1
+ module Chronicle
2
+ module ETL
3
+ module CLI
4
+ # Base class for CLI commands
5
+ class CLIBase < ::Thor
6
+ no_commands do
7
+ # Shorthand for cli_exit(status: :failure)
8
+ def cli_fail(message: nil, exception: nil)
9
+ cli_exit(status: :failure, message: message, exception: exception)
10
+ end
11
+
12
+ # Exit from CLI
13
+ #
14
+ # @params status Can be eitiher :success or :failure
15
+ # @params message to print
16
+ # @params exception stacktrace if log_level is set to debug
17
+ def cli_exit(status: :success, message: nil, exception: nil)
18
+ exit_code = status == :success ? 0 : 1
19
+ log_level = status == :success ? :info : :fatal
20
+
21
+ message = message.red if status != :success
22
+
23
+ Chronicle::ETL::Logger.debug(exception.full_message) if exception
24
+ Chronicle::ETL::Logger.send(log_level, message) if message
25
+ exit(exit_code)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -39,15 +39,13 @@ module Chronicle
39
39
  desc "show PHASE IDENTIFIER", "Show information about a connector"
40
40
  def show(phase, identifier)
41
41
  unless ['extractor', 'transformer', 'loader'].include?(phase)
42
- Chronicle::ETL::Logger.fatal("Phase argument must be one of: [extractor, transformer, loader]")
43
- exit 1
42
+ cli_fail(message: "Phase argument must be one of: [extractor, transformer, loader]")
44
43
  end
45
44
 
46
45
  begin
47
46
  connector = Chronicle::ETL::Registry.find_by_phase_and_identifier(phase.to_sym, identifier)
48
- rescue Chronicle::ETL::ConnectorNotAvailableError, Chronicle::ETL::PluginError
49
- Chronicle::ETL::Logger.fatal("Could not find #{phase} #{identifier}")
50
- exit 1
47
+ rescue Chronicle::ETL::ConnectorNotAvailableError, Chronicle::ETL::PluginError => e
48
+ cli_fail(message: "Could not find #{phase} #{identifier}", exception: e)
51
49
  end
52
50
 
53
51
  puts connector.klass.to_s.bold
@@ -45,15 +45,19 @@ module Chronicle
45
45
  LONG_DESC
46
46
  # Run an ETL job
47
47
  def start
48
- run_job(options)
49
- rescue Chronicle::ETL::JobDefinitionError => e
50
- missing_plugins = e.job_definition.errors
51
- .select { |error| error.is_a?(Chronicle::ETL::PluginLoadError) }
52
- .map(&:name)
53
- .uniq
48
+ job_definition = build_job_definition(options)
49
+
50
+ if job_definition.plugins_missing?
51
+ missing_plugins = job_definition.errors[:plugins]
52
+ .select { |error| error.is_a?(Chronicle::ETL::PluginLoadError) }
53
+ .map(&:name)
54
+ .uniq
55
+ install_missing_plugins(missing_plugins)
56
+ end
54
57
 
55
- install_missing_plugins(missing_plugins)
56
- run_job(options)
58
+ run_job(job_definition)
59
+ rescue Chronicle::ETL::JobDefinitionError => e
60
+ cli_fail(message: "Error running job.\n#{job_definition.errors}", exception: e)
57
61
  end
58
62
 
59
63
  desc "create", "Create a job"
@@ -65,8 +69,7 @@ LONG_DESC
65
69
  path = File.join('chronicle', 'etl', 'jobs', options[:name])
66
70
  Chronicle::ETL::Config.write(path, job_definition.definition)
67
71
  rescue Chronicle::ETL::JobDefinitionError => e
68
- Chronicle::ETL::Logger.debug(e.full_message)
69
- Chronicle::ETL::Logger.fatal("Job definition error".red)
72
+ cli_fail(message: "Job definition error", exception: e)
70
73
  end
71
74
 
72
75
  desc "show", "Show details about a job"
@@ -76,9 +79,7 @@ LONG_DESC
76
79
  job_definition.validate!
77
80
  puts Chronicle::ETL::Job.new(job_definition)
78
81
  rescue Chronicle::ETL::JobDefinitionError => e
79
- Chronicle::ETL::Logger.debug(e.full_message)
80
- Chronicle::ETL::Logger.fatal("Job definition error".red)
81
- exit 1
82
+ cli_fail(message: "Job definition error", exception: e)
82
83
  end
83
84
 
84
85
  desc "list", "List all available jobs"
@@ -102,15 +103,12 @@ LONG_DESC
102
103
  table = TTY::Table.new(headers, job_details)
103
104
  puts table.render(indent: 0, padding: [0, 2])
104
105
  rescue Chronicle::ETL::ConfigError => e
105
- Chronicle::ETL::Logger.debug(e.full_message)
106
- Chronicle::ETL::Logger.fatal("Error reading config. #{e.message}".red)
107
- exit 1
106
+ cli_fail(message: "Config error. #{e.message}", exception: e)
108
107
  end
109
108
 
110
109
  private
111
110
 
112
- def run_job(options)
113
- job_definition = build_job_definition(options)
111
+ def run_job(job_definition)
114
112
  job = Chronicle::ETL::Job.new(job_definition)
115
113
  runner = Chronicle::ETL::Runner.new(job)
116
114
  runner.run!
@@ -123,18 +121,10 @@ LONG_DESC
123
121
  message += "Do you want to install "
124
122
  message += missing_plugins.map { |name| "chronicle-#{name}".bold}.join(", ")
125
123
  message += " and start the job?"
126
- install = prompt.yes?(message)
127
- return unless install
124
+ will_install = prompt.yes?(message)
125
+ cli_fail(message: "Must install #{missing_plugins.join(", ")} plugin to run job") unless will_install
128
126
 
129
- spinner = TTY::Spinner.new("[:spinner] Installing plugins...", format: :dots_2)
130
- spinner.auto_spin
131
- missing_plugins.each do |plugin|
132
- Chronicle::ETL::Registry::PluginRegistry.install(plugin)
133
- end
134
- spinner.success("(#{'successful'.green})")
135
- rescue Chronicle::ETL::PluginNotAvailableError => e
136
- spinner.error("Error".red)
137
- Chronicle::ETL::Logger.fatal("Plugin '#{e.name}' could not be installed".red)
127
+ Chronicle::ETL::CLI::Plugins.new.install(*missing_plugins)
138
128
  end
139
129
 
140
130
  # Create job definition by reading config file and then overwriting with flag options
@@ -4,7 +4,7 @@ module Chronicle
4
4
  module ETL
5
5
  module CLI
6
6
  # Main entrypoint for CLI app
7
- class Main < ::Thor
7
+ class Main < Chronicle::ETL::CLI::CLIBase
8
8
  class_before :set_log_level
9
9
  class_before :set_color_output
10
10
 
@@ -92,6 +92,9 @@ module Chronicle
92
92
  end
93
93
 
94
94
  no_commands do
95
+ def testb
96
+ puts "hi"
97
+ end
95
98
  def set_color_output
96
99
  String.disable_colorization true if options[:'no-color'] || ENV['NO_COLOR']
97
100
  end
@@ -3,7 +3,6 @@
3
3
  require "tty-prompt"
4
4
  require "tty-spinner"
5
5
 
6
-
7
6
  module Chronicle
8
7
  module ETL
9
8
  module CLI
@@ -13,16 +12,19 @@ module Chronicle
13
12
  namespace :plugins
14
13
 
15
14
  desc "install", "Install a plugin"
16
- def install(name)
17
- spinner = TTY::Spinner.new("[:spinner] Installing plugin #{name}...", format: :dots_2)
15
+ def install(*plugins)
16
+ cli_fail(message: "Please specify a plugin to install") unless plugins.any?
17
+
18
+ spinner = TTY::Spinner.new("[:spinner] Installing #{plugins.join(", ")}...", format: :dots_2)
18
19
  spinner.auto_spin
19
- Chronicle::ETL::Registry::PluginRegistry.install(name)
20
+ plugins.each do |plugin|
21
+ spinner.update(title: "Installing #{plugin}")
22
+ Chronicle::ETL::Registry::PluginRegistry.install(plugin)
23
+ rescue Chronicle::ETL::PluginError => e
24
+ spinner.error("Error".red)
25
+ cli_fail(message: "Plugin '#{plugin}' could not be installed", exception: e)
26
+ end
20
27
  spinner.success("(#{'successful'.green})")
21
- rescue Chronicle::ETL::PluginError => e
22
- spinner.error("Error".red)
23
- Chronicle::ETL::Logger.debug(e.full_message)
24
- Chronicle::ETL::Logger.fatal("Plugin '#{name}' could not be installed".red)
25
- exit 1
26
28
  end
27
29
 
28
30
  desc "uninstall", "Unintall a plugin"
@@ -33,9 +35,7 @@ module Chronicle
33
35
  spinner.success("(#{'successful'.green})")
34
36
  rescue Chronicle::ETL::PluginError => e
35
37
  spinner.error("Error".red)
36
- Chronicle::ETL::Logger.debug(e.full_message)
37
- Chronicle::ETL::Logger.fatal("Plugin '#{name}' could not be uninstalled (was it installed?)".red)
38
- exit 1
38
+ cli_fail(message: "Plugin '#{name}' could not be uninstalled (was it installed?)", exception: e)
39
39
  end
40
40
 
41
41
  desc "list", "Lists available plugins"
@@ -2,7 +2,7 @@ module Chronicle
2
2
  module ETL
3
3
  module CLI
4
4
  # Base class for CLI subcommands. Overrides Thor methods so we can use command:subcommand syntax
5
- class SubcommandBase < ::Thor
5
+ class SubcommandBase < Chronicle::ETL::CLI::CLIBase
6
6
  # Print usage instructions for a subcommand
7
7
  def self.help(shell, subcommand = false)
8
8
  list = printable_commands(true, subcommand)
@@ -2,6 +2,7 @@ require 'thor'
2
2
  require 'thor/hollaback'
3
3
  require 'chronicle/etl'
4
4
 
5
+ require 'chronicle/etl/cli/cli_base'
5
6
  require 'chronicle/etl/cli/subcommand_base'
6
7
  require 'chronicle/etl/cli/connectors'
7
8
  require 'chronicle/etl/cli/jobs'
@@ -23,6 +23,7 @@ module Chronicle
23
23
  end
24
24
  end
25
25
 
26
+ class PluginConflictError < PluginError; end
26
27
  class PluginNotAvailableError < PluginError; end
27
28
  class PluginLoadError < PluginError; end
28
29
 
@@ -26,20 +26,32 @@ module Chronicle
26
26
  @definition = SKELETON_DEFINITION
27
27
  end
28
28
 
29
+ def valid?
30
+ validate
31
+ @errors.empty?
32
+ end
33
+
29
34
  def validate
30
- @errors = []
35
+ @errors = {}
31
36
 
32
37
  Chronicle::ETL::Registry::PHASES.each do |phase|
33
38
  __send__("#{phase}_klass".to_sym)
34
39
  rescue Chronicle::ETL::PluginError => e
35
- @errors << e
40
+ @errors[:plugins] ||= []
41
+ @errors[:plugins] << e
36
42
  end
43
+ end
37
44
 
38
- @errors.empty?
45
+ def plugins_missing?
46
+ validate
47
+
48
+ @errors[:plugins] || []
49
+ .filter { |e| e.instance_of?(Chronicle::ETL::PluginLoadError) }
50
+ .any?
39
51
  end
40
52
 
41
53
  def validate!
42
- raise(Chronicle::ETL::JobDefinitionError.new(self), "Job definition is invalid") unless validate
54
+ raise(Chronicle::ETL::JobDefinitionError.new(self), "Job definition is invalid") unless valid?
43
55
 
44
56
  true
45
57
  end
@@ -48,7 +60,6 @@ module Chronicle
48
60
  def add_config(config = {})
49
61
  @definition = @definition.deep_merge(config)
50
62
  load_credentials
51
- validate
52
63
  end
53
64
 
54
65
  # Is this job continuing from a previous run?
@@ -36,8 +36,11 @@ module Chronicle
36
36
  # By default, activates the latest available version of a gem
37
37
  # so don't have to run Kernel#gem separately
38
38
  require "chronicle/#{name}"
39
- rescue LoadError
40
- raise Chronicle::ETL::PluginLoadError.new(name), "Plugin #{name} couldn't be loaded" if exists?(name)
39
+ rescue Gem::ConflictError => e
40
+ # TODO: figure out if there's more we can do here
41
+ raise Chronicle::ETL::PluginConflictError.new(name), "Plugin '#{name}' couldn't be loaded. #{e.message}"
42
+ rescue LoadError => e
43
+ raise Chronicle::ETL::PluginLoadError.new(name), "Plugin '#{name}' couldn't be loaded" if exists?(name)
41
44
 
42
45
  raise Chronicle::ETL::PluginNotAvailableError.new(name), "Plugin #{name} doesn't exist"
43
46
  end
@@ -49,9 +52,11 @@ module Chronicle
49
52
 
50
53
  Gem::DefaultUserInteraction.ui = Gem::SilentUI.new
51
54
  Gem.install(gem_name)
55
+
56
+ activate(name)
52
57
  rescue Gem::UnsatisfiableDependencyError
53
58
  # TODO: we need to catch a lot more than this here
54
- raise Chronicle::ETL::PluginNotAvailableError.new(name), "Plugin #{name} doesn't exist"
59
+ raise Chronicle::ETL::PluginNotAvailableError.new(name), "Plugin #{name} could not be installed."
55
60
  end
56
61
 
57
62
  # Uninstall a plugin
@@ -1,5 +1,5 @@
1
1
  module Chronicle
2
2
  module ETL
3
- VERSION = "0.4.2"
3
+ VERSION = "0.4.3"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chronicle-etl
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Louis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-12 00:00:00.000000000 Z
11
+ date: 2022-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -359,6 +359,7 @@ files:
359
359
  - exe/chronicle-etl
360
360
  - lib/chronicle/etl.rb
361
361
  - lib/chronicle/etl/cli.rb
362
+ - lib/chronicle/etl/cli/cli_base.rb
362
363
  - lib/chronicle/etl/cli/connectors.rb
363
364
  - lib/chronicle/etl/cli/jobs.rb
364
365
  - lib/chronicle/etl/cli/main.rb