sql-jarvis 1.8.0 → 1.9.6
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/.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
|