chronicle-etl 0.4.2 → 0.4.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/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
|