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 +4 -4
- data/lib/chronicle/etl/cli/cli_base.rb +31 -0
- data/lib/chronicle/etl/cli/connectors.rb +3 -5
- data/lib/chronicle/etl/cli/jobs.rb +19 -29
- data/lib/chronicle/etl/cli/main.rb +4 -1
- data/lib/chronicle/etl/cli/plugins.rb +12 -12
- data/lib/chronicle/etl/cli/subcommand_base.rb +1 -1
- data/lib/chronicle/etl/cli.rb +1 -0
- data/lib/chronicle/etl/exceptions.rb +1 -0
- data/lib/chronicle/etl/job_definition.rb +16 -5
- data/lib/chronicle/etl/registry/plugin_registry.rb +8 -3
- data/lib/chronicle/etl/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2b6fdca3723ec52287c070a0dd08d0cfaf825f5e8f46da0d5a34172c0008573
|
4
|
+
data.tar.gz: e15181ba7edc1698404af8ff8c05d5367786ea809360393e825ca5ee5eef6c75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
127
|
-
|
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
|
-
|
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 < ::
|
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(
|
17
|
-
|
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
|
-
|
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
|
-
|
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 < ::
|
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)
|
data/lib/chronicle/etl/cli.rb
CHANGED
@@ -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
|
40
|
+
@errors[:plugins] ||= []
|
41
|
+
@errors[:plugins] << e
|
36
42
|
end
|
43
|
+
end
|
37
44
|
|
38
|
-
|
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
|
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
|
40
|
-
|
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}
|
59
|
+
raise Chronicle::ETL::PluginNotAvailableError.new(name), "Plugin #{name} could not be installed."
|
55
60
|
end
|
56
61
|
|
57
62
|
# Uninstall a plugin
|
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.
|
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-
|
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
|