sql-jarvis 1.8.0 → 1.9.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +1 -0
- data/.github/ISSUE_TEMPLATE.md +7 -0
- data/CHANGELOG.md +17 -1
- data/CONTRIBUTING.md +42 -0
- data/LICENSE.txt +1 -1
- data/README.md +121 -78
- data/app/assets/javascripts/blazer/application.js +1 -0
- data/app/assets/javascripts/blazer/select2.js +5833 -0
- data/app/assets/stylesheets/blazer/application.css +1 -0
- data/app/assets/stylesheets/blazer/select2.min.css +1 -0
- data/app/controllers/blazer/base_controller.rb +11 -2
- data/app/controllers/blazer/queries_controller.rb +29 -15
- data/app/models/blazer/query.rb +15 -0
- data/app/views/blazer/_variables.html.erb +1 -1
- data/app/views/blazer/queries/_form.html.erb +13 -3
- data/app/views/blazer/queries/run.html.erb +2 -0
- data/app/views/blazer/queries/schema.html.erb +2 -0
- data/app/views/blazer/queries/show.html.erb +1 -1
- data/blazer.gemspec +2 -1
- data/lib/blazer.rb +32 -7
- data/lib/blazer/adapters/bigquery_adapter.rb +5 -4
- data/lib/blazer/adapters/cassandra_adapter.rb +59 -0
- data/lib/blazer/adapters/druid_adapter.rb +67 -0
- data/lib/blazer/adapters/snowflake_adapter.rb +73 -0
- data/lib/blazer/data_source.rb +18 -16
- data/lib/blazer/engine.rb +10 -8
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/{config.yml → config.yml.tt} +5 -4
- data/lib/generators/blazer/templates/{install.rb → install.rb.tt} +1 -0
- data/lib/tasks/blazer.rake +1 -1
- data/sql-jarvis-1.9.4.gem +0 -0
- data/sql-jarvis-1.9.5.gem +0 -0
- metadata +30 -6
@@ -0,0 +1 @@
|
|
1
|
+
.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;-webkit-clip-path:inset(50%) !important;clip-path:inset(50%) !important;height:1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important;white-space:nowrap !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__rendered li{list-style:none}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb}
|
@@ -7,7 +7,7 @@ module Blazer
|
|
7
7
|
skip_after_action(*filters, raise: false)
|
8
8
|
skip_around_action(*filters, raise: false)
|
9
9
|
else
|
10
|
-
skip_action_callback
|
10
|
+
skip_action_callback(*filters)
|
11
11
|
end
|
12
12
|
|
13
13
|
protect_from_forgery with: :exception
|
@@ -16,6 +16,10 @@ module Blazer
|
|
16
16
|
http_basic_authenticate_with name: ENV["BLAZER_USERNAME"], password: ENV["BLAZER_PASSWORD"]
|
17
17
|
end
|
18
18
|
|
19
|
+
if Blazer.settings["before_action"]
|
20
|
+
raise Blazer::Error, "The docs for protecting Blazer with a custom before_action had an incorrect example from August 2017 to June 2018. The example method had a boolean return value. However, you must render or redirect if a user is unauthorized rather than return a falsy value. Double check that your before_action works correctly for unauthorized users (if it worked when added, there should be no issue). Then, change before_action to before_action_method in config/blazer.yml."
|
21
|
+
end
|
22
|
+
|
19
23
|
if Blazer.before_action
|
20
24
|
before_action Blazer.before_action.to_sym
|
21
25
|
end
|
@@ -53,7 +57,12 @@ module Blazer
|
|
53
57
|
value = value.to_f
|
54
58
|
end
|
55
59
|
end
|
56
|
-
|
60
|
+
if value.is_a?(Array)
|
61
|
+
var_value = value.map{|v| ActiveRecord::Base.connection.quote(v)}.join(', ')
|
62
|
+
else
|
63
|
+
var_value = ActiveRecord::Base.connection.quote(value)
|
64
|
+
end
|
65
|
+
statement.gsub!("{#{var}}", var_value)
|
57
66
|
end
|
58
67
|
end
|
59
68
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Blazer
|
2
2
|
class QueriesController < BaseController
|
3
3
|
before_action :set_query, only: [:show, :edit, :update, :destroy, :refresh]
|
4
|
+
before_action :set_data_source, only: [:tables, :schema, :cancel]
|
4
5
|
|
5
6
|
def home
|
6
7
|
if params[:filter] == "dashboards"
|
@@ -34,6 +35,7 @@ module Blazer
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def new
|
38
|
+
return render_forbidden unless Blazer::Query.creatable?(blazer_user)
|
37
39
|
@query = Blazer::Query.new(
|
38
40
|
data_source: params[:data_source],
|
39
41
|
name: params[:name]
|
@@ -44,6 +46,7 @@ module Blazer
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def create
|
49
|
+
return render_forbidden unless Blazer::Query.creatable?(blazer_user)
|
47
50
|
@query = Blazer::Query.new(query_params)
|
48
51
|
@query.creator = blazer_user if @query.respond_to?(:creator)
|
49
52
|
|
@@ -83,7 +86,10 @@ module Blazer
|
|
83
86
|
data_source = @query.data_source if @query && @query.data_source
|
84
87
|
@data_source = Blazer.data_sources[data_source]
|
85
88
|
|
86
|
-
|
89
|
+
# ensure viewable
|
90
|
+
if !(@query || Query.new(data_source: @data_source.id)).viewable?(blazer_user)
|
91
|
+
render_forbidden
|
92
|
+
elsif @run_id
|
87
93
|
@timestamp = blazer_params[:timestamp].to_i
|
88
94
|
|
89
95
|
@result = @data_source.run_results(@run_id)
|
@@ -174,20 +180,28 @@ module Blazer
|
|
174
180
|
end
|
175
181
|
|
176
182
|
def tables
|
177
|
-
render json:
|
183
|
+
render json: @data_source.tables
|
178
184
|
end
|
179
185
|
|
180
186
|
def schema
|
181
|
-
@schema =
|
187
|
+
@schema = @data_source.schema
|
182
188
|
end
|
183
189
|
|
184
190
|
def cancel
|
185
|
-
|
191
|
+
@data_source.cancel(blazer_run_id)
|
186
192
|
head :ok
|
187
193
|
end
|
188
194
|
|
189
195
|
private
|
190
196
|
|
197
|
+
def set_data_source
|
198
|
+
@data_source = Blazer.data_sources[params[:data_source]]
|
199
|
+
|
200
|
+
unless Query.new(data_source: @data_source.id).editable?(blazer_user)
|
201
|
+
render_forbidden
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
191
205
|
def continue_run
|
192
206
|
render json: {run_id: @run_id, timestamp: @timestamp}, status: :accepted
|
193
207
|
end
|
@@ -198,7 +212,7 @@ module Blazer
|
|
198
212
|
@first_row = @rows.first || []
|
199
213
|
@column_types = []
|
200
214
|
if @rows.any?
|
201
|
-
@columns.each_with_index do |
|
215
|
+
@columns.each_with_index do |_, i|
|
202
216
|
@column_types << (
|
203
217
|
case @first_row[i]
|
204
218
|
when Integer
|
@@ -248,13 +262,6 @@ module Blazer
|
|
248
262
|
end
|
249
263
|
|
250
264
|
def set_queries(limit = nil)
|
251
|
-
@my_queries =
|
252
|
-
if limit && blazer_user && !params[:filter] && Blazer.audit
|
253
|
-
queries_by_ids(Blazer::Audit.where(user_id: blazer_user.id).where("created_at > ?", 30.days.ago).where("query_id IS NOT NULL").group(:query_id).order("count_all desc").count.keys)
|
254
|
-
else
|
255
|
-
[]
|
256
|
-
end
|
257
|
-
|
258
265
|
@queries = Blazer::Query.named.select(:id, :name, :creator_id, :statement)
|
259
266
|
@queries = @queries.includes(:creator) if Blazer.user_class
|
260
267
|
|
@@ -263,7 +270,6 @@ module Blazer
|
|
263
270
|
elsif blazer_user && params[:filter] == "viewed" && Blazer.audit
|
264
271
|
@queries = queries_by_ids(Blazer::Audit.where(user_id: blazer_user.id).order(created_at: :desc).limit(500).pluck(:query_id).uniq)
|
265
272
|
else
|
266
|
-
@queries = @queries.where("id NOT IN (?)", @my_queries.map(&:id)) if @my_queries.any?
|
267
273
|
@queries = @queries.limit(limit) if limit
|
268
274
|
@queries = @queries.order(:name)
|
269
275
|
end
|
@@ -271,7 +277,7 @@ module Blazer
|
|
271
277
|
|
272
278
|
@more = limit && @queries.size >= limit
|
273
279
|
|
274
|
-
@queries =
|
280
|
+
@queries = @queries.select { |q| !q.name.to_s.start_with?("#") || q.try(:creator).try(:id) == blazer_user.try(:id) }
|
275
281
|
|
276
282
|
@queries =
|
277
283
|
@queries.map do |q|
|
@@ -294,10 +300,18 @@ module Blazer
|
|
294
300
|
|
295
301
|
def set_query
|
296
302
|
@query = Blazer::Query.find(params[:id].to_s.split("-").first)
|
303
|
+
|
304
|
+
unless @query.viewable?(blazer_user)
|
305
|
+
render_forbidden
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
def render_forbidden
|
310
|
+
render plain: "Access denied", status: :forbidden
|
297
311
|
end
|
298
312
|
|
299
313
|
def query_params
|
300
|
-
params.require(:query).permit
|
314
|
+
params.require(:query).permit!
|
301
315
|
end
|
302
316
|
|
303
317
|
def blazer_params
|
data/app/models/blazer/query.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Blazer
|
2
2
|
class Query < Record
|
3
|
+
serialize :assignee_ids, Array
|
4
|
+
|
3
5
|
belongs_to :creator, Blazer::BELONGS_TO_OPTIONAL.merge(class_name: Blazer.user_class.to_s) if Blazer.user_class
|
4
6
|
has_many :checks, dependent: :destroy
|
5
7
|
has_many :dashboard_queries, dependent: :destroy
|
@@ -18,12 +20,25 @@ module Blazer
|
|
18
20
|
name.to_s.sub(/\A[#\*]/, "").gsub(/\[.+\]/, "").strip
|
19
21
|
end
|
20
22
|
|
23
|
+
def viewable?(user)
|
24
|
+
if Blazer.query_viewable
|
25
|
+
Blazer.query_viewable.call(self, user)
|
26
|
+
else
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
21
31
|
def editable?(user)
|
22
32
|
editable = !persisted? || (name.present? && name.first != "*" && name.first != "#") || user == try(:creator)
|
33
|
+
editable &&= viewable?(user)
|
23
34
|
editable &&= Blazer.query_editable.call(self, user) if Blazer.query_editable
|
24
35
|
editable
|
25
36
|
end
|
26
37
|
|
38
|
+
def self.creatable?(user)
|
39
|
+
Blazer.query_creatable.call(user) if Blazer.query_creatable
|
40
|
+
end
|
41
|
+
|
27
42
|
def variables
|
28
43
|
Blazer.extract_vars(statement)
|
29
44
|
end
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<% @bind_vars.each_with_index do |var, i| %>
|
11
11
|
<%= label_tag var, var %>
|
12
12
|
<% if (data = @smart_vars[var]) %>
|
13
|
-
<%= select_tag var, options_for_select([[nil, nil]] + data, selected: params[var]), style: "margin-right: 20px; width: 200px; display: none;" %>
|
13
|
+
<%= select_tag var, options_for_select([[nil, nil]] + data, selected: params[var]), style: "margin-right: 20px; width: 200px; display: none;", multiple: true %>
|
14
14
|
<script>
|
15
15
|
$("#<%= var %>").selectize({
|
16
16
|
create: true
|
@@ -17,7 +17,7 @@
|
|
17
17
|
<%= link_to "Back", :back %>
|
18
18
|
</div>
|
19
19
|
<a :href="dataSourcePath" target="_blank" style="margin-right: 10px;">Schema</a>
|
20
|
-
<%= f.select :data_source, Blazer.data_sources.values.select { |ds| q = @query.dup; q.data_source = ds.id; q.editable?(blazer_user) }.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size
|
20
|
+
<%= f.select :data_source, Blazer.data_sources.values.select { |ds| q = @query.dup; q.data_source = ds.id; q.editable?(blazer_user) }.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size <= 1), style: "width: 140px;" %>
|
21
21
|
<div id="tables" style="display: inline-block; width: 250px; margin-right: 10px;">
|
22
22
|
<select id="table_names" style="width: 240px;" placeholder="Preview table"></select>
|
23
23
|
</div>
|
@@ -34,6 +34,12 @@
|
|
34
34
|
<%= f.label :description %>
|
35
35
|
<%= f.text_area :description, placeholder: "Optional", style: "height: 80px;", class: "form-control" %>
|
36
36
|
</div>
|
37
|
+
<%- if Blazer.assignees.any? -%>
|
38
|
+
<div class="form-group">
|
39
|
+
<%= f.label :assignees %>
|
40
|
+
<%= f.collection_select :assignee_ids, Blazer.assignees, :first, :last, {}, { placeholder: "Assignees", style: "height: 80px;", class: "form-control", multiple: true } %>
|
41
|
+
</div>
|
42
|
+
<%- end -%>
|
37
43
|
<div class="text-right">
|
38
44
|
<%= f.submit "For Enter Press", class: "hide" %>
|
39
45
|
<% if @query.persisted? %>
|
@@ -66,7 +72,7 @@
|
|
66
72
|
|
67
73
|
<script>
|
68
74
|
<%= blazer_js_var "params", variable_params %>
|
69
|
-
<%= blazer_js_var "previewStatement", Hash[Blazer.data_sources.map { |k, v| [k, v.preview_statement] }] %>
|
75
|
+
<%= blazer_js_var "previewStatement", Hash[Blazer.data_sources.map { |k, v| [k, (v.preview_statement rescue "")] }] %>
|
70
76
|
|
71
77
|
var app = new Vue({
|
72
78
|
el: "#app",
|
@@ -173,7 +179,7 @@
|
|
173
179
|
editor.focus()
|
174
180
|
},
|
175
181
|
adjustHeight: function() {
|
176
|
-
//
|
182
|
+
// https://stackoverflow.com/questions/11584061/
|
177
183
|
var editor = this.editor
|
178
184
|
var lines = editor.getSession().getScreenLength()
|
179
185
|
if (lines < 9) {
|
@@ -237,4 +243,8 @@
|
|
237
243
|
this.showEditor()
|
238
244
|
}
|
239
245
|
})
|
246
|
+
|
247
|
+
$(document).ready(function() {
|
248
|
+
$('#query_assignee_ids').select2();
|
249
|
+
});
|
240
250
|
</script>
|
@@ -111,6 +111,8 @@
|
|
111
111
|
<div class="results-container">
|
112
112
|
<% if @columns == ["QUERY PLAN"] %>
|
113
113
|
<pre><code><%= @rows.map { |r| r[0] }.join("\n") %></code></pre>
|
114
|
+
<% elsif @columns == ["PLAN"] && @data_source.adapter == "druid" %>
|
115
|
+
<pre><code><%= @rows[0][0] %></code></pre>
|
114
116
|
<% else %>
|
115
117
|
<table class="table results-table" style="margin-bottom: 0;">
|
116
118
|
<thead>
|
@@ -62,7 +62,7 @@
|
|
62
62
|
</script>
|
63
63
|
<% end %>
|
64
64
|
|
65
|
-
<%
|
65
|
+
<% unless %w(mongodb elasticsearch).include?(Blazer.data_sources[@query.data_source].adapter) %>
|
66
66
|
<script>
|
67
67
|
// do not highlight really long queries
|
68
68
|
// this can lead to performance issues
|
data/blazer.gemspec
CHANGED
@@ -17,7 +17,8 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_dependency "
|
20
|
+
spec.add_dependency "railties", ">= 4"
|
21
|
+
spec.add_dependency "activerecord", ">= 4"
|
21
22
|
spec.add_dependency "chartkick"
|
22
23
|
spec.add_dependency "safely_block", ">= 0.1.1"
|
23
24
|
|
data/lib/blazer.rb
CHANGED
@@ -9,11 +9,14 @@ require "blazer/run_statement"
|
|
9
9
|
require "blazer/adapters/base_adapter"
|
10
10
|
require "blazer/adapters/athena_adapter"
|
11
11
|
require "blazer/adapters/bigquery_adapter"
|
12
|
+
require "blazer/adapters/cassandra_adapter"
|
12
13
|
require "blazer/adapters/drill_adapter"
|
14
|
+
require "blazer/adapters/druid_adapter"
|
13
15
|
require "blazer/adapters/elasticsearch_adapter"
|
14
16
|
require "blazer/adapters/mongodb_adapter"
|
15
17
|
require "blazer/adapters/presto_adapter"
|
16
18
|
require "blazer/adapters/sql_adapter"
|
19
|
+
require "blazer/adapters/snowflake_adapter"
|
17
20
|
require "blazer/engine"
|
18
21
|
|
19
22
|
module Blazer
|
@@ -24,23 +27,27 @@ module Blazer
|
|
24
27
|
attr_accessor :audit
|
25
28
|
attr_reader :time_zone
|
26
29
|
attr_accessor :user_name
|
27
|
-
|
28
|
-
|
30
|
+
attr_writer :user_class
|
31
|
+
attr_writer :user_method
|
29
32
|
attr_accessor :before_action
|
30
33
|
attr_accessor :from_email
|
31
34
|
attr_accessor :cache
|
32
35
|
attr_accessor :transform_statement
|
33
36
|
attr_accessor :check_schedules
|
34
37
|
attr_accessor :mapbox_access_token
|
38
|
+
attr_accessor :assignees
|
35
39
|
attr_accessor :anomaly_checks
|
36
40
|
attr_accessor :async
|
37
41
|
attr_accessor :images
|
42
|
+
attr_accessor :query_viewable
|
38
43
|
attr_accessor :query_editable
|
44
|
+
attr_accessor :query_creatable
|
39
45
|
end
|
40
46
|
self.audit = true
|
41
47
|
self.user_name = :name
|
42
48
|
self.check_schedules = ["5 minutes", "1 hour", "1 day"]
|
43
49
|
self.mapbox_access_token = nil
|
50
|
+
self.assignees = []
|
44
51
|
self.anomaly_checks = false
|
45
52
|
self.async = false
|
46
53
|
self.images = false
|
@@ -61,6 +68,23 @@ module Blazer
|
|
61
68
|
@time_zone = time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone[time_zone.to_s]
|
62
69
|
end
|
63
70
|
|
71
|
+
def self.user_class
|
72
|
+
if !defined?(@user_class)
|
73
|
+
@user_class = settings.key?("user_class") ? settings["user_class"] : (User.name rescue nil)
|
74
|
+
end
|
75
|
+
@user_class
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.user_method
|
79
|
+
if !defined?(@user_method)
|
80
|
+
@user_method = settings["user_method"]
|
81
|
+
if user_class
|
82
|
+
@user_method ||= "current_#{user_class.to_s.downcase.singularize}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@user_method
|
86
|
+
end
|
87
|
+
|
64
88
|
def self.settings
|
65
89
|
@settings ||= begin
|
66
90
|
path = Rails.root.join("config", "blazer.yml").to_s
|
@@ -107,8 +131,6 @@ module Blazer
|
|
107
131
|
end
|
108
132
|
|
109
133
|
def self.run_check(check)
|
110
|
-
rows = nil
|
111
|
-
error = nil
|
112
134
|
tries = 1
|
113
135
|
|
114
136
|
ActiveSupport::Notifications.instrument("run_check.blazer", check_id: check.id, query_id: check.query.id, state_was: check.state) do |instrument|
|
@@ -176,10 +198,13 @@ module Blazer
|
|
176
198
|
end
|
177
199
|
end
|
178
200
|
|
179
|
-
Blazer.register_adapter "drill", Blazer::Adapters::DrillAdapter
|
180
|
-
Blazer.register_adapter "bigquery", Blazer::Adapters::BigQueryAdapter
|
181
201
|
Blazer.register_adapter "athena", Blazer::Adapters::AthenaAdapter
|
202
|
+
Blazer.register_adapter "bigquery", Blazer::Adapters::BigQueryAdapter
|
203
|
+
Blazer.register_adapter "cassandra", Blazer::Adapters::CassandraAdapter
|
204
|
+
Blazer.register_adapter "drill", Blazer::Adapters::DrillAdapter
|
205
|
+
Blazer.register_adapter "druid", Blazer::Adapters::DruidAdapter
|
182
206
|
Blazer.register_adapter "elasticsearch", Blazer::Adapters::ElasticsearchAdapter
|
183
|
-
Blazer.register_adapter "mongodb", Blazer::Adapters::MongodbAdapter
|
184
207
|
Blazer.register_adapter "presto", Blazer::Adapters::PrestoAdapter
|
208
|
+
Blazer.register_adapter "mongodb", Blazer::Adapters::MongodbAdapter
|
185
209
|
Blazer.register_adapter "sql", Blazer::Adapters::SqlAdapter
|
210
|
+
Blazer.register_adapter "snowflake", Blazer::Adapters::SnowflakeAdapter
|
@@ -7,10 +7,11 @@ module Blazer
|
|
7
7
|
error = nil
|
8
8
|
|
9
9
|
begin
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
results = bigquery.query(statement)
|
11
|
+
|
12
|
+
# complete? was removed in google-cloud-bigquery 0.29.0
|
13
|
+
# code is for backward compatibility
|
14
|
+
if !results.respond_to?(:complete?) || results.complete?
|
14
15
|
columns = results.first.keys.map(&:to_s) if results.size > 0
|
15
16
|
rows = results.map(&:values)
|
16
17
|
else
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Blazer
|
2
|
+
module Adapters
|
3
|
+
class CassandraAdapter < BaseAdapter
|
4
|
+
def run_statement(statement, comment)
|
5
|
+
columns = []
|
6
|
+
rows = []
|
7
|
+
error = nil
|
8
|
+
|
9
|
+
begin
|
10
|
+
response = session.execute("#{statement} /*#{comment}*/")
|
11
|
+
rows = response.map { |r| r.values }
|
12
|
+
columns = rows.any? ? response.first.keys : []
|
13
|
+
rescue => e
|
14
|
+
error = e.message
|
15
|
+
end
|
16
|
+
|
17
|
+
[columns, rows, error]
|
18
|
+
end
|
19
|
+
|
20
|
+
def tables
|
21
|
+
session.execute("SELECT table_name FROM system_schema.tables WHERE keyspace_name = '#{keyspace}'").map { |r| r["table_name"] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def schema
|
25
|
+
result = session.execute("SELECT keyspace_name, table_name, column_name, type, position FROM system_schema.columns WHERE keyspace_name = '#{keyspace}'")
|
26
|
+
result.map(&:values).group_by { |r| [r[0], r[1]] }.map { |k, vs| {schema: k[0], table: k[1], columns: vs.sort_by { |v| v[2] }.map { |v| {name: v[2], data_type: v[3]} }} }
|
27
|
+
end
|
28
|
+
|
29
|
+
def preview_statement
|
30
|
+
"SELECT * FROM {table} LIMIT 10"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def cluster
|
36
|
+
@cluster ||= begin
|
37
|
+
require "cassandra"
|
38
|
+
options = {hosts: [uri.host]}
|
39
|
+
options[:port] = uri.port if uri.port
|
40
|
+
options[:username] = uri.user if uri.user
|
41
|
+
options[:password] = uri.password if uri.password
|
42
|
+
::Cassandra.cluster(options)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def session
|
47
|
+
@session ||= cluster.connect(keyspace)
|
48
|
+
end
|
49
|
+
|
50
|
+
def uri
|
51
|
+
@uri ||= URI.parse(data_source.settings["url"])
|
52
|
+
end
|
53
|
+
|
54
|
+
def keyspace
|
55
|
+
@keyspace ||= uri.path[1..-1]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|