blazer 0.0.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of blazer might be problematic. Click here for more details.

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +261 -45
  4. data/app/assets/javascripts/blazer/Sortable.js +1144 -0
  5. data/app/assets/javascripts/blazer/application.js +2 -1
  6. data/app/assets/javascripts/blazer/chartkick.js +935 -0
  7. data/app/assets/javascripts/blazer/selectize.js +391 -201
  8. data/app/assets/stylesheets/blazer/application.css +17 -2
  9. data/app/assets/stylesheets/blazer/selectize.default.css +3 -2
  10. data/app/controllers/blazer/base_controller.rb +48 -0
  11. data/app/controllers/blazer/checks_controller.rb +51 -0
  12. data/app/controllers/blazer/dashboards_controller.rb +94 -0
  13. data/app/controllers/blazer/queries_controller.rb +29 -101
  14. data/app/helpers/blazer/{queries_helper.rb → base_helper.rb} +1 -1
  15. data/app/mailers/blazer/check_mailer.rb +21 -0
  16. data/app/models/blazer/check.rb +28 -0
  17. data/app/models/blazer/connection.rb +0 -1
  18. data/app/models/blazer/dashboard.rb +12 -0
  19. data/app/models/blazer/dashboard_query.rb +9 -0
  20. data/app/models/blazer/query.rb +5 -0
  21. data/app/views/blazer/check_mailer/failing_checks.html.erb +6 -0
  22. data/app/views/blazer/check_mailer/state_change.html.erb +6 -0
  23. data/app/views/blazer/checks/_form.html.erb +28 -0
  24. data/app/views/blazer/checks/edit.html.erb +1 -0
  25. data/app/views/blazer/checks/index.html.erb +41 -0
  26. data/app/views/blazer/checks/new.html.erb +1 -0
  27. data/app/views/blazer/checks/run.html.erb +9 -0
  28. data/app/views/blazer/dashboards/_form.html.erb +86 -0
  29. data/app/views/blazer/dashboards/edit.html.erb +1 -0
  30. data/app/views/blazer/dashboards/index.html.erb +21 -0
  31. data/app/views/blazer/dashboards/new.html.erb +1 -0
  32. data/app/views/blazer/dashboards/show.html.erb +148 -0
  33. data/app/views/blazer/queries/_form.html.erb +16 -5
  34. data/app/views/blazer/queries/_tables.html +5 -0
  35. data/app/views/blazer/queries/index.html.erb +6 -0
  36. data/app/views/blazer/queries/run.html.erb +59 -44
  37. data/app/views/blazer/queries/show.html.erb +20 -16
  38. data/config/routes.rb +5 -0
  39. data/lib/blazer.rb +46 -2
  40. data/lib/blazer/data_source.rb +70 -0
  41. data/lib/blazer/engine.rb +6 -2
  42. data/lib/blazer/tasks.rb +12 -0
  43. data/lib/blazer/version.rb +1 -1
  44. data/lib/generators/blazer/templates/config.yml +26 -6
  45. data/lib/generators/blazer/templates/install.rb +21 -0
  46. metadata +27 -3
@@ -16,11 +16,11 @@ body {
16
16
  padding-bottom: 20px;
17
17
  }
18
18
 
19
- #results th {
19
+ .results-table th {
20
20
  cursor: pointer;
21
21
  }
22
22
 
23
- #results thead {
23
+ .results-table thead {
24
24
  background-color: #fff;
25
25
  }
26
26
 
@@ -68,3 +68,18 @@ input.search:focus {
68
68
  .ace_gutter-cell.error {
69
69
  background-color: red;
70
70
  }
71
+
72
+ .chart {
73
+ height: 300px;
74
+ text-align: center;
75
+ display: flex;
76
+ justify-content: center;
77
+ flex-direction: column;
78
+ }
79
+
80
+ .chart > .results-container {
81
+ height: 300px;
82
+ border: solid 1px #ddd;
83
+ overflow: scroll;
84
+ text-align: left;
85
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
- * selectize.default.css (v0.10.1) - Default Theme
3
- * Copyright (c) 2013 Brian Reavis & contributors
2
+ * selectize.default.css (v0.12.1) - Default Theme
3
+ * Copyright (c) 2013–2015 Brian Reavis & contributors
4
4
  *
5
5
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6
6
  * file except in compliance with the License. You may obtain a copy of the License at:
@@ -189,6 +189,7 @@
189
189
  border: 1px solid #aaaaaa;
190
190
  }
191
191
  .selectize-input > input {
192
+ display: inline-block !important;
192
193
  padding: 0 !important;
193
194
  min-height: 0 !important;
194
195
  max-height: none !important;
@@ -0,0 +1,48 @@
1
+ module Blazer
2
+ class BaseController < ApplicationController
3
+ # skip all filters
4
+ skip_filter *_process_action_callbacks.map(&:filter)
5
+
6
+ protect_from_forgery with: :exception
7
+
8
+ if ENV["BLAZER_PASSWORD"]
9
+ http_basic_authenticate_with name: ENV["BLAZER_USERNAME"], password: ENV["BLAZER_PASSWORD"]
10
+ end
11
+
12
+ layout "blazer/application"
13
+
14
+ before_action :ensure_database_url
15
+
16
+ private
17
+
18
+ def ensure_database_url
19
+ render text: "BLAZER_DATABASE_URL required" if !ENV["BLAZER_DATABASE_URL"] && !Rails.env.development?
20
+ end
21
+
22
+ def process_vars(statement)
23
+ (@bind_vars ||= []).concat(extract_vars(statement)).uniq!
24
+ @success = @bind_vars.all? { |v| params[v] }
25
+
26
+ if @success
27
+ @bind_vars.each do |var|
28
+ value = params[var].presence
29
+ value = value.to_i if value.to_i.to_s == value
30
+ if var.end_with?("_at")
31
+ value = Blazer.time_zone.parse(value) rescue nil
32
+ end
33
+ statement.gsub!("{#{var}}", ActiveRecord::Base.connection.quote(value))
34
+ end
35
+ end
36
+ end
37
+
38
+ def extract_vars(statement)
39
+ statement.scan(/\{.*?\}/).map { |v| v[1...-1] }.uniq
40
+ end
41
+ helper_method :extract_vars
42
+
43
+ def variable_params
44
+ params.except(:controller, :action, :id, :host, :query, :table_names, :authenticity_token, :utf8, :_method, :commit, :statement, :data_source)
45
+ end
46
+ helper_method :variable_params
47
+ end
48
+ end
@@ -0,0 +1,51 @@
1
+ module Blazer
2
+ class ChecksController < BaseController
3
+ before_action :set_check, only: [:edit, :update, :destroy, :run]
4
+
5
+ def index
6
+ @checks = Blazer::Check.joins(:query).includes(:query).order("state, blazer_queries.name, blazer_checks.id").to_a
7
+ @checks.select! { |c| "#{c.query.name} #{c.emails}".downcase.include?(params[:q]) } if params[:q]
8
+ end
9
+
10
+ def new
11
+ @check = Blazer::Check.new
12
+ end
13
+
14
+ def create
15
+ @check = Blazer::Check.new(check_params)
16
+
17
+ if @check.save
18
+ redirect_to run_check_path(@check)
19
+ else
20
+ render :new
21
+ end
22
+ end
23
+
24
+ def update
25
+ if @check.update(check_params)
26
+ redirect_to run_check_path(@check)
27
+ else
28
+ render :edit
29
+ end
30
+ end
31
+
32
+ def destroy
33
+ @check.destroy
34
+ redirect_to checks_path
35
+ end
36
+
37
+ def run
38
+ @query = @check.query
39
+ end
40
+
41
+ private
42
+
43
+ def check_params
44
+ params.require(:check).permit(:query_id, :emails)
45
+ end
46
+
47
+ def set_check
48
+ @check = Blazer::Check.find(params[:id])
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,94 @@
1
+ module Blazer
2
+ class DashboardsController < BaseController
3
+ before_action :set_dashboard, only: [:show, :edit, :update, :destroy]
4
+
5
+ def index
6
+ @dashboards = Blazer::Dashboard.order(:name)
7
+ end
8
+
9
+ def new
10
+ @dashboard = Blazer::Dashboard.new
11
+ end
12
+
13
+ def create
14
+ @dashboard = Blazer::Dashboard.new
15
+
16
+ if update_dashboard(@dashboard)
17
+ redirect_to dashboard_path(@dashboard)
18
+ else
19
+ render :new
20
+ end
21
+ end
22
+
23
+ def show
24
+ @queries = @dashboard.dashboard_queries.order(:position).preload(:query).map(&:query)
25
+ @queries.each do |query|
26
+ process_vars(query.statement)
27
+ end
28
+ @bind_vars ||= []
29
+
30
+ @smart_vars = {}
31
+ @sql_errors = []
32
+ data_sources = @queries.map { |q| Blazer.data_sources[q.data_source] }.uniq
33
+ @bind_vars.each do |var|
34
+ data_sources.each do |data_source|
35
+ query = data_source.smart_variables[var]
36
+ if query
37
+ rows, error = data_source.run_statement(query)
38
+ (@smart_vars[var] ||= []).concat rows.map { |v| v.values.reverse }
39
+ @sql_errors << error if error
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ def edit
46
+ end
47
+
48
+ def update
49
+ if update_dashboard(@dashboard)
50
+ redirect_to dashboard_path(@dashboard)
51
+ else
52
+ render :edit
53
+ end
54
+ end
55
+
56
+ def destroy
57
+ @dashboard.destroy
58
+ redirect_to dashboards_path
59
+ end
60
+
61
+ protected
62
+
63
+ def dashboard_params
64
+ params.require(:dashboard).permit(:name)
65
+ end
66
+
67
+ def set_dashboard
68
+ @dashboard = Blazer::Dashboard.find(params[:id])
69
+ end
70
+
71
+ def update_dashboard(dashboard)
72
+ dashboard.assign_attributes(dashboard_params)
73
+ Blazer::Dashboard.transaction do
74
+ if params[:query_ids].is_a?(Array)
75
+ query_ids = params[:query_ids].map(&:to_i)
76
+ @queries = Blazer::Query.find(query_ids).sort_by { |q| query_ids.index(q.id) }
77
+ end
78
+ if dashboard.save
79
+ if @queries
80
+ @queries.each_with_index do |query, i|
81
+ dashboard_query = dashboard.dashboard_queries.where(query_id: query.id).first_or_initialize
82
+ dashboard_query.position = i
83
+ dashboard_query.save!
84
+ end
85
+ if dashboard.persisted?
86
+ dashboard.dashboard_queries.where.not(query_id: query_ids).destroy_all
87
+ end
88
+ end
89
+ true
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,27 +1,16 @@
1
1
  module Blazer
2
- class QueriesController < ApplicationController
3
- # skip all filters
4
- skip_filter *_process_action_callbacks.map(&:filter)
5
-
6
- protect_from_forgery with: :exception
7
-
8
- if ENV["BLAZER_PASSWORD"]
9
- http_basic_authenticate_with name: ENV["BLAZER_USERNAME"], password: ENV["BLAZER_PASSWORD"]
10
- end
11
-
12
- layout "blazer/application"
13
-
14
- before_action :ensure_database_url
2
+ class QueriesController < BaseController
15
3
  before_action :set_query, only: [:show, :edit, :update, :destroy]
16
4
 
17
5
  def index
18
6
  @queries = Blazer::Query.order(:name)
19
7
  @queries = @queries.includes(:creator) if Blazer.user_class
20
8
  @trending_queries = Blazer::Audit.group(:query_id).where("created_at > ?", 2.days.ago).having("COUNT(DISTINCT user_id) >= 3").uniq.count(:user_id)
9
+ @checks = Blazer::Check.group(:query_id).count
21
10
  end
22
11
 
23
12
  def new
24
- @query = Blazer::Query.new(statement: params[:statement])
13
+ @query = Blazer::Query.new(statement: params[:statement], data_source: params[:data_source])
25
14
  end
26
15
 
27
16
  def create
@@ -41,10 +30,11 @@ module Blazer
41
30
 
42
31
  @smart_vars = {}
43
32
  @sql_errors = []
33
+ data_source = Blazer.data_sources[@query.data_source]
44
34
  @bind_vars.each do |var|
45
- query = smart_variables[var]
35
+ query = data_source.smart_variables[var]
46
36
  if query
47
- rows, error = run_statement(query)
37
+ rows, error = data_source.run_statement(query)
48
38
  @smart_vars[var] = rows.map { |v| v.values.reverse }
49
39
  @sql_errors << error if error
50
40
  end
@@ -57,19 +47,31 @@ module Blazer
57
47
  def run
58
48
  @statement = params[:statement]
59
49
  process_vars(@statement)
50
+ @only_chart = params[:only_chart]
60
51
 
61
52
  if @success
62
53
  @query = Query.find_by(id: params[:query_id]) if params[:query_id]
63
54
 
55
+ data_source = params[:data_source]
56
+ data_source = @query.data_source if @query && @query.data_source
57
+
64
58
  # audit
65
59
  if Blazer.audit
66
60
  audit = Blazer::Audit.new(statement: @statement)
67
61
  audit.query = @query
62
+ audit.data_source = data_source
68
63
  audit.user = current_user if respond_to?(:current_user) && Blazer.user_class
69
64
  audit.save!
70
65
  end
71
66
 
72
- @rows, @error = run_statement(@statement)
67
+ @data_source = Blazer.data_sources[data_source]
68
+ @rows, @error = @data_source.run_statement(@statement)
69
+
70
+ if @query && !@error.to_s.include?("canceling statement due to statement timeout")
71
+ @query.checks.each do |check|
72
+ check.update_state(@rows, @error)
73
+ end
74
+ end
73
75
 
74
76
  @columns = {}
75
77
  if @rows.any?
@@ -88,19 +90,19 @@ module Blazer
88
90
 
89
91
  @filename = @query.name.parameterize if @query
90
92
 
91
- @min_width_types = (@rows.first || {}).select { |k, v| v.is_a?(Time) || v.is_a?(String) || smart_columns[k] }.keys
93
+ @min_width_types = (@rows.first || {}).select { |k, v| v.is_a?(Time) || v.is_a?(String) || @data_source.smart_columns[k] }.keys
92
94
 
93
95
  @boom = {}
94
96
  @columns.keys.each do |key|
95
- query = smart_columns[key]
97
+ query = @data_source.smart_columns[key]
96
98
  if query
97
99
  values = @rows.map { |r| r[key] }.compact.uniq
98
- rows, error = run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
100
+ rows, error = @data_source.run_statement(ActiveRecord::Base.send(:sanitize_sql_array, [query.sub("{value}", "(?)"), values]))
99
101
  @boom[key] = Hash[rows.map(&:values)]
100
102
  end
101
103
  end
102
104
 
103
- @linked_columns = linked_columns
105
+ @linked_columns = @data_source.linked_columns
104
106
  end
105
107
 
106
108
  respond_to do |format|
@@ -126,18 +128,19 @@ module Blazer
126
128
  redirect_to root_url
127
129
  end
128
130
 
129
- private
130
-
131
- def ensure_database_url
132
- render text: "BLAZER_DATABASE_URL required" if !ENV["BLAZER_DATABASE_URL"] && !Rails.env.development?
131
+ def tables
132
+ @tables = Blazer.data_sources[params[:data_source]].tables.keys
133
+ render partial: "tables", layout: false
133
134
  end
134
135
 
136
+ private
137
+
135
138
  def set_query
136
139
  @query = Blazer::Query.find(params[:id].to_s.split("-").first)
137
140
  end
138
141
 
139
142
  def query_params
140
- params.require(:query).permit(:name, :description, :statement)
143
+ params.require(:query).permit(:name, :description, :statement, :data_source)
141
144
  end
142
145
 
143
146
  def csv_data(rows)
@@ -150,80 +153,5 @@ module Blazer
150
153
  end
151
154
  end
152
155
  end
153
-
154
- def run_statement(statement)
155
- rows = []
156
- error = nil
157
- begin
158
- Blazer::Connection.transaction do
159
- Blazer::Connection.connection.execute("SET statement_timeout = #{Blazer.timeout * 1000}") if Blazer.timeout && postgresql?
160
- result = Blazer::Connection.connection.select_all(statement)
161
- result.each do |untyped_row|
162
- row = {}
163
- untyped_row.each do |k, v|
164
- row[k] = result.column_types.empty? ? v : result.column_types[k].send(:type_cast, v)
165
- end
166
- rows << row
167
- end
168
- raise ActiveRecord::Rollback
169
- end
170
- rescue ActiveRecord::StatementInvalid => e
171
- error = e.message.sub(/.+ERROR: /, "")
172
- end
173
- [rows, error]
174
- end
175
-
176
- def extract_vars(statement)
177
- statement.scan(/\{.*?\}/).map { |v| v[1...-1] }.uniq
178
- end
179
-
180
- def process_vars(statement)
181
- @bind_vars = extract_vars(statement)
182
- @success = @bind_vars.all? { |v| params[v] }
183
-
184
- if @success
185
- @bind_vars.each do |var|
186
- value = params[var].presence
187
- value = value.to_i if value.to_i.to_s == value
188
- if var.end_with?("_at")
189
- value = Blazer.time_zone.parse(value) rescue nil
190
- end
191
- statement.gsub!("{#{var}}", ActiveRecord::Base.connection.quote(value))
192
- end
193
- end
194
- end
195
-
196
- def variable_params
197
- params.except(:controller, :action, :id, :host, :query, :table_names, :authenticity_token, :utf8, :_method, :commit, :statement)
198
- end
199
- helper_method :variable_params
200
-
201
- def settings
202
- YAML.load(File.read(Rails.root.join("config", "blazer.yml")))
203
- end
204
-
205
- def linked_columns
206
- settings["linked_columns"] || {}
207
- end
208
-
209
- def smart_columns
210
- settings["smart_columns"] || {}
211
- end
212
-
213
- def smart_variables
214
- settings["smart_variables"] || {}
215
- end
216
-
217
- def tables
218
- default_schema = postgresql? ? "public" : Blazer::Connection.connection_config[:database]
219
- schema = Blazer::Connection.connection_config[:schema] || default_schema
220
- rows, error = run_statement(Blazer::Connection.send(:sanitize_sql_array, ["SELECT table_name, column_name, ordinal_position, data_type FROM information_schema.columns WHERE table_schema = ?", schema]))
221
- Hash[rows.group_by { |r| r["table_name"] }.map { |t, f| [t, f.sort_by { |f| f["ordinal_position"] }.map { |f| f.slice("column_name", "data_type") }] }.sort_by { |t, _f| t }]
222
- end
223
- helper_method :tables
224
-
225
- def postgresql?
226
- Blazer::Connection.connection.adapter_name == "PostgreSQL"
227
- end
228
156
  end
229
157
  end