1
1
require 'pp'
2
+ require 'tty-prompt'
2
3
3
4
module Chronicle
4
5
module ETL
5
6
module CLI
6
7
# CLI commands for working with ETL jobs
7
8
class Jobs < SubcommandBase
8
9
default_task "start"
9
- namespace :jobs
10
+ namespace :jobs
10
11
11
12
class_option :name , aliases : '-j' , desc : 'Job configuration name'
12
13
@@ -26,15 +27,9 @@ class Jobs < SubcommandBase
26
27
class_option :output , aliases : '-o' , desc : 'Output filename' , type : 'string'
27
28
class_option :fields , desc : 'Output only these fields' , type : 'array' , banner : 'field1 field2 ...'
28
29
29
- class_option :log_level , desc : 'Log level (debug, info, warn, error, fatal)' , default : 'info'
30
- class_option :verbose , aliases : '-v' , desc : 'Set log level to verbose' , type : :boolean
31
- class_option :silent , desc : 'Silence all output' , type : :boolean
32
-
33
30
# Thor doesn't like `run` as a command name
34
31
map run : :start
35
32
desc "run" , "Start a job"
36
- option :log_level , desc : 'Log level (debug, info, warn, error, fatal)' , default : 'info'
37
- option :verbose , aliases : '-v' , desc : 'Set log level to verbose' , type : :boolean
38
33
option :dry_run , desc : 'Only run the extraction and transform steps, not the loading' , type : :boolean
39
34
long_desc <<-LONG_DESC
40
35
This will run an ETL job. Each job needs three parts:
@@ -49,25 +44,39 @@ class Jobs < SubcommandBase
49
44
LONG_DESC
50
45
# Run an ETL job
51
46
def start
52
- setup_log_level
53
- job_definition = build_job_definition ( options )
54
- job = Chronicle ::ETL ::Job . new ( job_definition )
55
- runner = Chronicle ::ETL ::Runner . new ( job )
56
- runner . run!
47
+ run_job ( options )
48
+ rescue Chronicle ::ETL ::JobDefinitionError => e
49
+ missing_plugins = e . job_definition . errors
50
+ . select { |error | error . is_a? ( Chronicle ::ETL ::PluginLoadError ) }
51
+ . map ( &:name )
52
+ . uniq
53
+
54
+ install_missing_plugins ( missing_plugins )
55
+ run_job ( options )
57
56
end
58
57
59
58
desc "create" , "Create a job"
60
59
# Create an ETL job
61
60
def create
62
61
job_definition = build_job_definition ( options )
62
+ job_definition . validate!
63
+
63
64
path = File . join ( 'chronicle' , 'etl' , 'jobs' , options [ :name ] )
64
65
Chronicle ::ETL ::Config . write ( path , job_definition . definition )
66
+ rescue Chronicle ::ETL ::JobDefinitionError => e
67
+ Chronicle ::ETL ::Logger . debug ( e . full_message )
68
+ Chronicle ::ETL ::Logger . fatal ( "Job definition error" . red )
65
69
end
66
70
67
71
desc "show" , "Show details about a job"
68
72
# Show an ETL job
69
73
def show
70
- puts Chronicle ::ETL ::Job . new ( build_job_definition ( options ) )
74
+ job_definition = build_job_definition ( options )
75
+ job_definition . validate!
76
+ puts Chronicle ::ETL ::Job . new ( job_definition )
77
+ rescue Chronicle ::ETL ::JobDefinitionError => e
78
+ Chronicle ::ETL ::Logger . debug ( e . full_message )
79
+ Chronicle ::ETL ::Logger . fatal ( "Job definition error" . red )
71
80
end
72
81
73
82
desc "list" , "List all available jobs"
@@ -87,21 +96,39 @@ def list
87
96
88
97
headers = [ 'name' , 'extractor' , 'transformer' , 'loader' ] . map { |h | h . upcase . bold }
89
98
99
+ puts "Available jobs:"
90
100
table = TTY ::Table . new ( headers , job_details )
91
101
puts table . render ( indent : 0 , padding : [ 0 , 2 ] )
92
102
end
93
103
94
104
private
95
105
96
- def setup_log_level
97
- if options [ :silent ]
98
- Chronicle ::ETL ::Logger . log_level = Chronicle ::ETL ::Logger ::SILENT
99
- elsif options [ :verbose ]
100
- Chronicle ::ETL ::Logger . log_level = Chronicle ::ETL ::Logger ::DEBUG
101
- elsif options [ :log_level ]
102
- level = Chronicle ::ETL ::Logger . const_get ( options [ :log_level ] . upcase )
103
- Chronicle ::ETL ::Logger . log_level = level
106
+ def run_job ( options )
107
+ job_definition = build_job_definition ( options )
108
+ job = Chronicle ::ETL ::Job . new ( job_definition )
109
+ runner = Chronicle ::ETL ::Runner . new ( job )
110
+ runner . run!
111
+ end
112
+
113
+ # TODO: probably could merge this with something in cli/plugin
114
+ def install_missing_plugins ( missing_plugins )
115
+ prompt = TTY ::Prompt . new
116
+ message = "Plugin#{ 's' if missing_plugins . count > 1 } specified by job not installed.\n "
117
+ message += "Do you want to install "
118
+ message += missing_plugins . map { |name | "chronicle-#{ name } " . bold } . join ( ", " )
119
+ message += " and start the job?"
120
+ install = prompt . yes? ( message )
121
+ return unless install
122
+
123
+ spinner = TTY ::Spinner . new ( "[:spinner] Installing plugins..." , format : :dots_2 )
124
+ spinner . auto_spin
125
+ missing_plugins . each do |plugin |
126
+ Chronicle ::ETL ::Registry ::PluginRegistry . install ( plugin )
104
127
end
128
+ spinner . success ( "(#{ 'successful' . green } )" )
129
+ rescue Chronicle ::ETL ::PluginNotAvailableError => e
130
+ spinner . error ( "Error" . red )
131
+ Chronicle ::ETL ::Logger . fatal ( "Plugin '#{ e . name } ' could not be installed" . red )
105
132
end
106
133
107
134
# Create job definition by reading config file and then overwriting with flag options
0 commit comments