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.
@@ -3,6 +3,7 @@
3
3
  *= require ./selectize.default
4
4
  *= require ./github
5
5
  *= require ./daterangepicker-bs3
6
+ *= require ./select2.min
6
7
  *= require_self
7
8
  */
8
9
 
@@ -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 *filters
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
- statement.gsub!("{#{var}}", ActiveRecord::Base.connection.quote(value))
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
- if @run_id
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: Blazer.data_sources[params[:data_source]].tables
183
+ render json: @data_source.tables
178
184
  end
179
185
 
180
186
  def schema
181
- @schema = Blazer.data_sources[params[:data_source]].schema
187
+ @schema = @data_source.schema
182
188
  end
183
189
 
184
190
  def cancel
185
- Blazer.data_sources[params[:data_source]].cancel(blazer_run_id)
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 |column, i|
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 = (@my_queries + @queries).select { |q| !q.name.to_s.start_with?("#") || q.try(:creator).try(:id) == blazer_user.try(:id) }
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(:name, :description, :statement, :data_source)
314
+ params.require(:query).permit!
301
315
  end
302
316
 
303
317
  def blazer_params
@@ -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 == 1), style: "width: 140px;" %>
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
- // http://stackoverflow.com/questions/11584061/
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>
@@ -1,3 +1,5 @@
1
+ <% blazer_title "Database Schema" %>
2
+
1
3
  <% @schema.each do |table| %>
2
4
  <h4>
3
5
  <%= table[:table] %>
@@ -62,7 +62,7 @@
62
62
  </script>
63
63
  <% end %>
64
64
 
65
- <% if %w[sql presto drill bigquery athena].include?(Blazer.data_sources[@query.data_source].adapter) %>
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 "rails", ">= 4"
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
- attr_accessor :user_class
28
- attr_accessor :user_method
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
- options = {}
11
- options[:timeout] = data_source.timeout.to_i * 1000 if data_source.timeout
12
- results = bigquery.query(statement, options) # ms
13
- if results.complete?
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