sql-jarvis 2.0.1 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -1
- data/LICENSE.txt +1 -1
- data/README.md +153 -52
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
- data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
- data/app/assets/images/blazer/favicon.png +0 -0
- data/app/assets/javascripts/blazer/Chart.js +4195 -3884
- data/app/assets/javascripts/blazer/Sortable.js +1493 -1097
- data/app/assets/javascripts/blazer/ace/ace.js +21294 -4
- data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1991 -3
- data/app/assets/javascripts/blazer/ace/mode-sql.js +110 -1
- data/app/assets/javascripts/blazer/ace/snippets/sql.js +40 -1
- data/app/assets/javascripts/blazer/ace/snippets/text.js +14 -1
- data/app/assets/javascripts/blazer/ace/theme-twilight.js +116 -1
- data/app/assets/javascripts/blazer/application.js +4 -3
- data/app/assets/javascripts/blazer/bootstrap.js +623 -612
- data/app/assets/javascripts/blazer/chartkick.js +1769 -1248
- data/app/assets/javascripts/blazer/daterangepicker.js +263 -115
- data/app/assets/javascripts/blazer/highlight.min.js +3 -0
- data/app/assets/javascripts/blazer/{jquery_ujs.js → jquery-ujs.js} +161 -75
- data/app/assets/javascripts/blazer/jquery.js +9506 -9450
- data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +321 -259
- data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1212 -0
- data/app/assets/javascripts/blazer/queries.js +1 -1
- data/app/assets/javascripts/blazer/routes.js +3 -0
- data/app/assets/javascripts/blazer/selectize.js +3828 -3604
- data/app/assets/javascripts/blazer/stupidtable.js +255 -88
- data/app/assets/javascripts/blazer/vue.js +8015 -4583
- data/app/assets/stylesheets/blazer/application.css +41 -5
- data/app/assets/stylesheets/blazer/bootstrap.css.erb +879 -325
- data/app/assets/stylesheets/blazer/daterangepicker.css +269 -0
- data/app/assets/stylesheets/blazer/selectize.default.css +26 -10
- data/app/controllers/blazer/base_controller.rb +7 -0
- data/app/controllers/blazer/checks_controller.rb +1 -1
- data/app/controllers/blazer/dashboards_controller.rb +0 -4
- data/app/controllers/blazer/queries_controller.rb +20 -12
- data/app/helpers/blazer/base_helper.rb +1 -1
- data/app/mailers/blazer/slack_notifier.rb +76 -0
- data/app/models/blazer/check.rb +9 -0
- data/app/views/blazer/_nav.html.erb +0 -1
- data/app/views/blazer/_variables.html.erb +41 -19
- data/app/views/blazer/checks/_form.html.erb +16 -8
- data/app/views/blazer/checks/edit.html.erb +2 -0
- data/app/views/blazer/checks/index.html.erb +33 -4
- data/app/views/blazer/checks/new.html.erb +2 -0
- data/app/views/blazer/dashboards/_form.html.erb +4 -4
- data/app/views/blazer/dashboards/edit.html.erb +2 -0
- data/app/views/blazer/dashboards/new.html.erb +2 -0
- data/app/views/blazer/dashboards/show.html.erb +7 -3
- data/app/views/blazer/queries/_form.html.erb +11 -6
- data/app/views/blazer/queries/docs.html.erb +131 -0
- data/app/views/blazer/queries/home.html.erb +12 -3
- data/app/views/blazer/queries/run.html.erb +36 -6
- data/app/views/blazer/queries/schema.html.erb +46 -8
- data/app/views/blazer/queries/show.html.erb +4 -4
- data/app/views/layouts/blazer/application.html.erb +3 -3
- data/config/routes.rb +5 -1
- data/lib/blazer.rb +32 -13
- data/lib/blazer/adapters/athena_adapter.rb +1 -1
- data/lib/blazer/adapters/elasticsearch_adapter.rb +14 -17
- data/lib/blazer/adapters/mongodb_adapter.rb +1 -1
- data/lib/blazer/adapters/sql_adapter.rb +7 -1
- data/lib/blazer/engine.rb +4 -0
- data/lib/blazer/result.rb +62 -29
- data/lib/blazer/run_statement_job.rb +6 -9
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/config.yml.tt +13 -2
- data/lib/generators/blazer/templates/install.rb.tt +1 -0
- data/lib/tasks/blazer.rake +1 -0
- metadata +33 -37
- data/.gitattributes +0 -1
- data/.github/ISSUE_TEMPLATE.md +0 -7
- data/.gitignore +0 -14
- data/Gemfile +0 -7
- data/Rakefile +0 -1
- data/app/assets/javascripts/blazer/highlight.pack.js +0 -1
- data/app/assets/javascripts/blazer/moment-timezone.js +0 -1007
- data/app/assets/stylesheets/blazer/daterangepicker-bs3.css +0 -375
- data/blazer.gemspec +0 -30
@@ -1,6 +1,6 @@
|
|
1
1
|
<div id="queries">
|
2
|
-
<div id="header"
|
3
|
-
<div class="pull-right">
|
2
|
+
<div id="header">
|
3
|
+
<div class="pull-right" style="line-height: 34px;">
|
4
4
|
<% if blazer_user %>
|
5
5
|
<div class='btn-group' style='margin-right: 20px;'>
|
6
6
|
<%= link_to "All", root_path, class: !params[:filter] ? 'active btn btn-default btn-xs' : 'btn btn-default btn-xs' %>
|
@@ -21,12 +21,14 @@
|
|
21
21
|
<span class="sr-only">Toggle Dropdown</span>
|
22
22
|
</button>
|
23
23
|
<ul class="dropdown-menu">
|
24
|
+
<li><%= link_to "Checks", checks_path %></li>
|
25
|
+
<li role="separator" class="divider"></li>
|
24
26
|
<li><%= link_to "New Dashboard", new_dashboard_path %></li>
|
25
27
|
<li><%= link_to "New Check", new_check_path %></li>
|
26
28
|
</ul>
|
27
29
|
</div>
|
28
30
|
</div>
|
29
|
-
<input type="text" v-model="searchTerm" placeholder="Start typing a query or person" style="width: 300px; display: inline-block;"
|
31
|
+
<input type="text" v-model="searchTerm" placeholder="Start typing a query, dashboard, or person" style="width: 300px; display: inline-block;" v-focus class="search form-control" />
|
30
32
|
</div>
|
31
33
|
|
32
34
|
<table class="table">
|
@@ -148,6 +150,13 @@
|
|
148
150
|
return Routes.query_path(item.to_param)
|
149
151
|
}
|
150
152
|
}
|
153
|
+
},
|
154
|
+
directives: {
|
155
|
+
focus: {
|
156
|
+
inserted: function (el) {
|
157
|
+
el.focus()
|
158
|
+
}
|
159
|
+
}
|
151
160
|
}
|
152
161
|
})
|
153
162
|
</script>
|
@@ -24,7 +24,7 @@
|
|
24
24
|
<% end %>
|
25
25
|
</p>
|
26
26
|
<% end %>
|
27
|
-
<p class="text-muted">
|
27
|
+
<p class="text-muted" style="margin-bottom: 10px;">
|
28
28
|
<%= pluralize(@rows.size, "row") %>
|
29
29
|
|
30
30
|
<% @checks.select(&:state).each do |check| %>
|
@@ -33,19 +33,46 @@
|
|
33
33
|
· <%= check.message %>
|
34
34
|
<% end %>
|
35
35
|
<% end %>
|
36
|
+
|
37
|
+
<% if @query && @result.forecastable? && !params[:forecast] %>
|
38
|
+
·
|
39
|
+
<%= link_to "Forecast", query_path(@query, {forecast: "t"}.merge(variable_params)) %>
|
40
|
+
<% end %>
|
36
41
|
</p>
|
37
42
|
<% end %>
|
43
|
+
<% if @forecast_error %>
|
44
|
+
<div class="alert alert-danger"><%= @forecast_error %></div>
|
45
|
+
<% end %>
|
38
46
|
<% if @rows.any? %>
|
39
47
|
<% values = @rows.first %>
|
40
48
|
<% chart_id = SecureRandom.hex %>
|
41
49
|
<% column_types = @result.column_types %>
|
42
50
|
<% chart_type = @result.chart_type %>
|
43
|
-
<% chart_options = {id: chart_id
|
51
|
+
<% chart_options = {id: chart_id} %>
|
52
|
+
<% if ["line", "line2"].include?(chart_type) %>
|
53
|
+
<% chart_options.merge!(min: nil) %>
|
54
|
+
<% end %>
|
55
|
+
<% if chart_type == "scatter" %>
|
56
|
+
<% chart_options.merge!(library: {tooltips: {intersect: false}}) %>
|
57
|
+
<% elsif ["bar", "bar2"].include?(chart_type) %>
|
58
|
+
<% chart_options.merge!(library: {tooltips: {intersect: false, axis: 'x'}}) %>
|
59
|
+
<% elsif chart_type != "pie" %>
|
60
|
+
<% if column_types.size == 2 || @forecast %>
|
61
|
+
<% chart_options.merge!(library: {tooltips: {intersect: false, axis: 'x'}}) %>
|
62
|
+
<% else %>
|
63
|
+
<%# chartjs axis: 'x' has poor behavior with multiple series %>
|
64
|
+
<% chart_options.merge!(library: {tooltips: {intersect: false}}) %>
|
65
|
+
<% end %>
|
66
|
+
<% end %>
|
44
67
|
<% series_library = {} %>
|
45
68
|
<% target_index = @columns.index { |k| k.downcase == "target" } %>
|
46
69
|
<% if target_index %>
|
47
70
|
<% series_library[target_index - 1] = {pointStyle: "line", hitRadius: 5, borderColor: "#109618", pointBackgroundColor: "#109618", backgroundColor: "#109618"} %>
|
48
71
|
<% end %>
|
72
|
+
<% if @forecast %>
|
73
|
+
<% color = "#54a3ee" %>
|
74
|
+
<% series_library[1] = {borderDash: [8], borderColor: color, pointBackgroundColor: color, backgroundColor: color, pointHoverBackgroundColor: color} %>
|
75
|
+
<% end %>
|
49
76
|
<% if blazer_maps? && @markers.any? %>
|
50
77
|
<div id="map" style="height: <%= @only_chart ? 300 : 500 %>px;"></div>
|
51
78
|
<script>
|
@@ -76,11 +103,14 @@
|
|
76
103
|
map.fitBounds(featureLayer.getBounds());
|
77
104
|
</script>
|
78
105
|
<% elsif chart_type == "line" %>
|
79
|
-
|
106
|
+
<% chart_data = @columns[1..-1].each_with_index.map{ |k, i| {name: blazer_series_name(k), data: @rows.map{ |r| [r[0], r[i + 1]] }, library: series_library[i]} } %>
|
107
|
+
<%= line_chart chart_data, chart_options %>
|
80
108
|
<% elsif chart_type == "line2" %>
|
81
109
|
<%= line_chart @rows.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.map { |v2| [v2[0], v2[2]] }, library: series_library[i]} }, chart_options %>
|
110
|
+
<% elsif chart_type == "pie" %>
|
111
|
+
<%= pie_chart @rows.map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, chart_options %>
|
82
112
|
<% elsif chart_type == "bar" %>
|
83
|
-
<%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } },
|
113
|
+
<%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } }, chart_options %>
|
84
114
|
<% elsif chart_type == "bar2" %>
|
85
115
|
<% first_20 = @rows.group_by { |r| r[0] }.values.first(20).flatten(1) %>
|
86
116
|
<% labels = first_20.map { |r| r[0] }.uniq %>
|
@@ -90,9 +120,9 @@
|
|
90
120
|
<% first_20 << [l, s, 0] unless first_20.find { |r| r[0] == l && r[1] == s } %>
|
91
121
|
<% end %>
|
92
122
|
<% end %>
|
93
|
-
<%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} },
|
123
|
+
<%= column_chart first_20.group_by { |r| v = r[1]; (@boom[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@boom[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, chart_options %>
|
94
124
|
<% elsif chart_type == "scatter" %>
|
95
|
-
<%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1],
|
125
|
+
<%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1], **chart_options %>
|
96
126
|
<% elsif @only_chart %>
|
97
127
|
<% if @rows.size == 1 && @rows.first.size == 1 %>
|
98
128
|
<% v = @rows.first.first %>
|
@@ -1,13 +1,25 @@
|
|
1
|
-
<% blazer_title "
|
1
|
+
<% blazer_title "Schema: #{@data_source.name}" %>
|
2
|
+
|
3
|
+
<h1>Schema: <%= @data_source.name %></h1>
|
4
|
+
|
5
|
+
<hr />
|
6
|
+
|
7
|
+
<div id="header">
|
8
|
+
<input id="search" type="text" placeholder="Start typing a table or column" style="width: 300px; display: inline-block;" class="search form-control" />
|
9
|
+
</div>
|
2
10
|
|
3
11
|
<% @schema.each do |table| %>
|
4
|
-
<
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
12
|
+
<table class="table schema-table">
|
13
|
+
<thead>
|
14
|
+
<tr>
|
15
|
+
<th colspan="2">
|
16
|
+
<%= table[:table] %>
|
17
|
+
<% if table[:schema] != "public" %>
|
18
|
+
<span class="text-muted" style="font-weight: normal;"><%= table[:schema] %></span>
|
19
|
+
<% end %>
|
20
|
+
</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
11
23
|
<tbody>
|
12
24
|
<% table[:columns].each do |column| %>
|
13
25
|
<tr>
|
@@ -18,3 +30,29 @@
|
|
18
30
|
</tbody>
|
19
31
|
</table>
|
20
32
|
<% end %>
|
33
|
+
|
34
|
+
<script>
|
35
|
+
$("#search").on("keyup", function() {
|
36
|
+
var value = $(this).val().toLowerCase()
|
37
|
+
$(".schema-table").filter(function() {
|
38
|
+
// if found in table name, show entire table
|
39
|
+
// if just found in rows, show row
|
40
|
+
|
41
|
+
var found = $(this).find("thead").text().toLowerCase().indexOf(value) > -1
|
42
|
+
|
43
|
+
if (found) {
|
44
|
+
$(this).find("tbody tr").toggle(true)
|
45
|
+
} else {
|
46
|
+
$(this).find("tbody tr").filter(function() {
|
47
|
+
var found2 = $(this).text().toLowerCase().indexOf(value) > -1
|
48
|
+
$(this).toggle(found2)
|
49
|
+
if (found2) {
|
50
|
+
found = true
|
51
|
+
}
|
52
|
+
})
|
53
|
+
}
|
54
|
+
|
55
|
+
$(this).toggle(found)
|
56
|
+
})
|
57
|
+
}).focus()
|
58
|
+
</script>
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<div class="row" style="padding-top: 13px;">
|
6
6
|
<div class="col-sm-8">
|
7
7
|
<%= render partial: "blazer/nav" %>
|
8
|
-
<h3 style="
|
8
|
+
<h3 style="line-height: 34px; display: inline; margin-left: 5px;">
|
9
9
|
<%= @query.name %>
|
10
10
|
</h3>
|
11
11
|
</div>
|
@@ -14,7 +14,7 @@
|
|
14
14
|
<%= link_to "Fork", new_query_path(variable_params.merge(fork_query_id: @query.id, data_source: @query.data_source, name: @query.name)), class: "btn btn-info" %>
|
15
15
|
|
16
16
|
<% if !@error && @success %>
|
17
|
-
<%= button_to "⤓ .csv", run_queries_path(query_id: @query.id, format: "csv"), params: {statement: @statement}, class: "btn btn-primary" %>
|
17
|
+
<%= button_to "⤓ .csv", run_queries_path(query_id: @query.id, format: "csv", forecast: params[:forecast]), params: {statement: @statement}, class: "btn btn-primary" %>
|
18
18
|
<%= button_to "⤓ .xlsx", run_queries_path(query_id: @query.id, format: "xlsx"), params: {statement: @statement}, class: "btn btn-primary" %>
|
19
19
|
<% end %>
|
20
20
|
</div>
|
@@ -57,13 +57,13 @@
|
|
57
57
|
$("#results").addClass("query-error").html(message)
|
58
58
|
}
|
59
59
|
|
60
|
-
<%= blazer_js_var "data", variable_params.merge(statement: @statement, query_id: @query.id) %>
|
60
|
+
<%= blazer_js_var "data", variable_params.merge(statement: @statement, query_id: @query.id, data_source: @query.data_source) %>
|
61
61
|
|
62
62
|
runQuery(data, showRun, showError)
|
63
63
|
</script>
|
64
64
|
<% end %>
|
65
65
|
|
66
|
-
<% unless %w(mongodb
|
66
|
+
<% unless %w(mongodb).include?(Blazer.data_sources[@query.data_source].adapter) %>
|
67
67
|
<script>
|
68
68
|
// do not highlight really long queries
|
69
69
|
// this can lead to performance issues
|
@@ -4,15 +4,15 @@
|
|
4
4
|
<title><%= blazer_title ? blazer_title : "Blazer" %></title>
|
5
5
|
|
6
6
|
<meta charset="utf-8" />
|
7
|
-
|
7
|
+
<%= favicon_link_tag "blazer/favicon.png" %>
|
8
8
|
<%= stylesheet_link_tag "blazer/application" %>
|
9
9
|
<%= javascript_include_tag "blazer/application" %>
|
10
10
|
<script>
|
11
11
|
<%= blazer_js_var "rootPath", root_path %>
|
12
12
|
</script>
|
13
13
|
<% if blazer_maps? %>
|
14
|
-
<%= stylesheet_link_tag "https://api.mapbox.com/mapbox.js/
|
15
|
-
<%= javascript_include_tag "https://api.mapbox.com/mapbox.js/
|
14
|
+
<%= stylesheet_link_tag "https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.css", integrity: "sha384-o8tecBIfqi9yU5yYK2Ne/9A9hlOGFV9MBvCpmemvJH1XxqOe6h8Bl4mLxMi6PgjG", crossorigin: "anonymous" %>
|
15
|
+
<%= javascript_include_tag "https://api.mapbox.com/mapbox.js/v3.1.1/mapbox.js", integrity: "sha384-j81LqvtvYigFzGSUgAoFijpvoq4yGoCJSOXI9DFaUEpenR029MBE3E/X5Gr+WdO0", crossorigin: "anonymous" %>
|
16
16
|
<% end %>
|
17
17
|
<%= csrf_meta_tags %>
|
18
18
|
</head>
|
data/config/routes.rb
CHANGED
@@ -6,12 +6,16 @@ Blazer::Engine.routes.draw do
|
|
6
6
|
get :tables, on: :collection
|
7
7
|
get :columns, on: :collection
|
8
8
|
get :schema, on: :collection
|
9
|
+
get :docs, on: :collection
|
9
10
|
end
|
11
|
+
|
10
12
|
resources :checks, except: [:show] do
|
11
13
|
get :run, on: :member
|
12
14
|
end
|
13
|
-
|
15
|
+
|
16
|
+
resources :dashboards, except: [:index] do
|
14
17
|
post :refresh, on: :member
|
15
18
|
end
|
19
|
+
|
16
20
|
root to: "queries#home"
|
17
21
|
end
|
data/lib/blazer.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
|
+
# dependencies
|
1
2
|
require "csv"
|
2
3
|
require "yaml"
|
3
4
|
require "chartkick"
|
4
5
|
require "zip/zip"
|
5
6
|
require "axlsx"
|
6
7
|
require "safely/core"
|
8
|
+
|
9
|
+
# modules
|
7
10
|
require "blazer/version"
|
8
11
|
require "blazer/data_source"
|
9
12
|
require "blazer/result"
|
10
13
|
require "blazer/excel_parser"
|
11
14
|
require "blazer/run_statement"
|
15
|
+
|
16
|
+
# adapters
|
12
17
|
require "blazer/adapters/base_adapter"
|
13
18
|
require "blazer/adapters/athena_adapter"
|
14
19
|
require "blazer/adapters/bigquery_adapter"
|
@@ -20,6 +25,8 @@ require "blazer/adapters/mongodb_adapter"
|
|
20
25
|
require "blazer/adapters/presto_adapter"
|
21
26
|
require "blazer/adapters/sql_adapter"
|
22
27
|
require "blazer/adapters/snowflake_adapter"
|
28
|
+
|
29
|
+
# engine
|
23
30
|
require "blazer/engine"
|
24
31
|
|
25
32
|
module Blazer
|
@@ -37,14 +44,18 @@ module Blazer
|
|
37
44
|
attr_accessor :subdomain
|
38
45
|
attr_accessor :cache
|
39
46
|
attr_accessor :transform_statement
|
47
|
+
attr_accessor :transform_variable
|
40
48
|
attr_accessor :check_schedules
|
41
49
|
attr_accessor :mapbox_access_token
|
42
50
|
attr_accessor :assignees
|
43
51
|
attr_accessor :anomaly_checks
|
52
|
+
attr_accessor :forecasting
|
44
53
|
attr_accessor :async
|
45
54
|
attr_accessor :images
|
46
55
|
attr_accessor :query_viewable
|
47
56
|
attr_accessor :query_editable
|
57
|
+
attr_accessor :override_csp
|
58
|
+
attr_accessor :slack_webhook_url
|
48
59
|
attr_accessor :query_creatable
|
49
60
|
end
|
50
61
|
self.audit = true
|
@@ -54,8 +65,10 @@ module Blazer
|
|
54
65
|
self.assignees = []
|
55
66
|
self.subdomain = nil
|
56
67
|
self.anomaly_checks = false
|
68
|
+
self.forecasting = false
|
57
69
|
self.async = false
|
58
70
|
self.images = false
|
71
|
+
self.override_csp = false
|
59
72
|
|
60
73
|
TIMEOUT_MESSAGE = "Query timed out :("
|
61
74
|
TIMEOUT_ERRORS = [
|
@@ -103,20 +116,11 @@ module Blazer
|
|
103
116
|
|
104
117
|
def self.data_sources
|
105
118
|
@data_sources ||= begin
|
106
|
-
ds = Hash
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
]
|
111
|
-
ds.default = ds.values.first
|
119
|
+
ds = Hash.new { |hash, key| raise Blazer::Error, "Unknown data source: #{key}" }
|
120
|
+
settings["data_sources"].each do |id, s|
|
121
|
+
ds[id] = Blazer::DataSource.new(id, s)
|
122
|
+
end
|
112
123
|
ds
|
113
|
-
|
114
|
-
# TODO Blazer 2.0
|
115
|
-
# ds2 = Hash.new { |hash, key| raise Blazer::Error, "Unknown data source: #{key}" }
|
116
|
-
# ds.each do |k, v|
|
117
|
-
# ds2[k] = v
|
118
|
-
# end
|
119
|
-
# ds2
|
120
124
|
end
|
121
125
|
end
|
122
126
|
|
@@ -186,10 +190,15 @@ module Blazer
|
|
186
190
|
|
187
191
|
def self.send_failing_checks
|
188
192
|
emails = {}
|
193
|
+
slack_channels = {}
|
194
|
+
|
189
195
|
Blazer::Check.includes(:query).where(state: ["failing", "error", "timed out", "disabled"]).find_each do |check|
|
190
196
|
check.split_emails.each do |email|
|
191
197
|
(emails[email] ||= []) << check
|
192
198
|
end
|
199
|
+
check.split_slack_channels.each do |channel|
|
200
|
+
(slack_channels[channel] ||= []) << check
|
201
|
+
end
|
193
202
|
end
|
194
203
|
|
195
204
|
emails.each do |email, checks|
|
@@ -197,6 +206,16 @@ module Blazer
|
|
197
206
|
Blazer::CheckMailer.failing_checks(email, checks).deliver_now
|
198
207
|
end
|
199
208
|
end
|
209
|
+
|
210
|
+
slack_channels.each do |channel, checks|
|
211
|
+
Safely.safely do
|
212
|
+
Blazer::SlackNotifier.failing_checks(channel, checks)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.slack?
|
218
|
+
slack_webhook_url.present?
|
200
219
|
end
|
201
220
|
|
202
221
|
def self.adapters
|
@@ -13,7 +13,7 @@ module Blazer
|
|
13
13
|
client.start_query_execution(
|
14
14
|
query_string: statement,
|
15
15
|
# use token so we fetch cached results after query is run
|
16
|
-
client_request_token: Digest::MD5.hexdigest(statement),
|
16
|
+
client_request_token: Digest::MD5.hexdigest([statement,data_source.id].join("/")),
|
17
17
|
query_execution_context: {
|
18
18
|
database: database,
|
19
19
|
},
|
@@ -7,22 +7,17 @@ module Blazer
|
|
7
7
|
error = nil
|
8
8
|
|
9
9
|
begin
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
if
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
hit_keys = (hits.first.try(:keys) || []) - ["_source"]
|
20
|
-
columns = source_keys + hit_keys
|
21
|
-
rows =
|
22
|
-
hits.map do |r|
|
23
|
-
source = r["_source"]
|
24
|
-
source_keys.map { |k| source[k] } + hit_keys.map { |k| r[k] }
|
10
|
+
response = client.xpack.sql.query(body: {query: "#{statement} /*#{comment}*/"})
|
11
|
+
columns = response["columns"].map { |v| v["name"] }
|
12
|
+
# Elasticsearch does not differentiate between dates and times
|
13
|
+
date_indexes = response["columns"].each_index.select { |i| response["columns"][i]["type"] == "date" }
|
14
|
+
if columns.any?
|
15
|
+
rows = response["rows"]
|
16
|
+
date_indexes.each do |i|
|
17
|
+
rows.each do |row|
|
18
|
+
row[i] = Time.parse(row[i])
|
25
19
|
end
|
20
|
+
end
|
26
21
|
end
|
27
22
|
rescue => e
|
28
23
|
error = e.message
|
@@ -32,11 +27,13 @@ module Blazer
|
|
32
27
|
end
|
33
28
|
|
34
29
|
def tables
|
35
|
-
client.indices
|
30
|
+
indices = client.cat.indices(format: "json").map { |v| v["index"] }
|
31
|
+
aliases = client.cat.aliases(format: "json").map { |v| v["alias"] }
|
32
|
+
(indices + aliases).uniq.sort
|
36
33
|
end
|
37
34
|
|
38
35
|
def preview_statement
|
39
|
-
|
36
|
+
"SELECT * FROM \"{table}\" LIMIT 10"
|
40
37
|
end
|
41
38
|
|
42
39
|
protected
|
@@ -7,7 +7,7 @@ module Blazer
|
|
7
7
|
error = nil
|
8
8
|
|
9
9
|
begin
|
10
|
-
documents = db.command({:$eval => "#{statement.strip}.toArray()"}).documents.first["retval"]
|
10
|
+
documents = db.command({:$eval => "#{statement.strip}.toArray()", nolock: true}).documents.first["retval"]
|
11
11
|
columns = documents.flat_map { |r| r.keys }.uniq
|
12
12
|
rows = documents.map { |r| columns.map { |c| r[c] } }
|
13
13
|
rescue => e
|
@@ -155,7 +155,13 @@ module Blazer
|
|
155
155
|
if postgresql? || redshift?
|
156
156
|
execute("SET #{use_transaction? ? "LOCAL " : ""}statement_timeout = #{timeout.to_i * 1000}")
|
157
157
|
elsif mysql?
|
158
|
-
|
158
|
+
# use send as this method is private in Rails 4.2
|
159
|
+
mariadb = connection_model.connection.send(:mariadb?) rescue false
|
160
|
+
if mariadb
|
161
|
+
execute("SET max_statement_time = #{timeout.to_i * 1000}")
|
162
|
+
else
|
163
|
+
execute("SET max_execution_time = #{timeout.to_i * 1000}")
|
164
|
+
end
|
159
165
|
else
|
160
166
|
raise Blazer::TimeoutNotSupported, "Timeout not supported for #{adapter_name} adapter"
|
161
167
|
end
|