blazer 2.2.2 → 2.2.7
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/CHANGELOG.md +24 -0
- data/LICENSE.txt +1 -1
- data/README.md +49 -17
- data/app/assets/javascripts/blazer/Chart.js +13794 -12099
- data/app/assets/javascripts/blazer/Sortable.js +3695 -1526
- data/app/assets/javascripts/blazer/chartkick.js +296 -46
- data/app/assets/javascripts/blazer/daterangepicker.js +194 -269
- data/app/assets/javascripts/blazer/jquery.js +1150 -642
- data/app/assets/javascripts/blazer/moment-timezone-with-data.js +621 -287
- data/app/assets/javascripts/blazer/moment.js +5085 -2460
- data/app/assets/stylesheets/blazer/daterangepicker.css +394 -253
- data/app/controllers/blazer/base_controller.rb +16 -2
- data/app/controllers/blazer/dashboards_controller.rb +6 -3
- data/app/controllers/blazer/queries_controller.rb +7 -5
- data/app/mailers/blazer/slack_notifier.rb +3 -0
- data/app/models/blazer/dashboard.rb +4 -0
- data/app/views/blazer/_variables.html.erb +3 -1
- data/app/views/blazer/check_mailer/failing_checks.html.erb +1 -0
- data/app/views/blazer/check_mailer/state_change.html.erb +1 -0
- data/app/views/blazer/dashboards/_form.html.erb +1 -1
- data/app/views/blazer/dashboards/show.html.erb +4 -4
- data/app/views/blazer/queries/_form.html.erb +2 -2
- data/app/views/blazer/queries/run.html.erb +9 -8
- data/app/views/blazer/queries/show.html.erb +5 -3
- data/app/views/layouts/blazer/application.html.erb +2 -2
- data/lib/blazer.rb +3 -1
- data/lib/blazer/adapters/influxdb_adapter.rb +45 -0
- data/lib/blazer/result.rb +2 -21
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/install.rb.tt +3 -3
- data/licenses/LICENSE-ace.txt +24 -0
- data/licenses/LICENSE-bootstrap.txt +21 -0
- data/licenses/LICENSE-chart.js.txt +9 -0
- data/licenses/LICENSE-chartkick.js.txt +22 -0
- data/licenses/LICENSE-daterangepicker.txt +21 -0
- data/licenses/LICENSE-fuzzysearch.txt +20 -0
- data/licenses/LICENSE-highlight.js.txt +29 -0
- data/licenses/LICENSE-jquery-ujs.txt +20 -0
- data/licenses/LICENSE-jquery.txt +20 -0
- data/licenses/LICENSE-moment-timezone.txt +20 -0
- data/licenses/LICENSE-moment.txt +22 -0
- data/licenses/LICENSE-selectize.txt +202 -0
- data/licenses/LICENSE-sortable.txt +21 -0
- data/licenses/LICENSE-stickytableheaders.txt +20 -0
- data/licenses/LICENSE-stupidtable.txt +19 -0
- data/licenses/LICENSE-vue.txt +21 -0
- metadata +19 -2
|
@@ -86,8 +86,22 @@ module Blazer
|
|
|
86
86
|
[smart_var, error]
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
# don't pass to url helpers
|
|
90
|
+
#
|
|
91
|
+
# some are dangerous when passed as symbols
|
|
92
|
+
# root_url({host: "evilsite.com"})
|
|
93
|
+
#
|
|
94
|
+
# certain ones (like host) only affect *_url and not *_path
|
|
95
|
+
#
|
|
96
|
+
# when permitted parameters are passed in Rails 6,
|
|
97
|
+
# they appear to be added as GET parameters
|
|
98
|
+
# root_url(params.permit(:host))
|
|
99
|
+
UNPERMITTED_KEYS = [:controller, :action, :id, :host, :query, :dashboard, :query_id, :query_ids, :table_names, :authenticity_token, :utf8, :_method, :commit, :statement, :data_source, :name, :fork_query_id, :blazer, :run_id, :script_name, :original_script_name]
|
|
100
|
+
|
|
101
|
+
# remove unpermitted keys from both params and permitted keys for better sleep
|
|
102
|
+
def variable_params(resource)
|
|
103
|
+
permitted_keys = resource.variables - UNPERMITTED_KEYS.map(&:to_s)
|
|
104
|
+
params.except(*UNPERMITTED_KEYS).slice(*permitted_keys).permit!
|
|
91
105
|
end
|
|
92
106
|
helper_method :variable_params
|
|
93
107
|
|
|
@@ -21,8 +21,11 @@ module Blazer
|
|
|
21
21
|
|
|
22
22
|
def show
|
|
23
23
|
@queries = @dashboard.dashboard_queries.order(:position).preload(:query).map(&:query)
|
|
24
|
+
@statements = []
|
|
24
25
|
@queries.each do |query|
|
|
25
|
-
|
|
26
|
+
statement = query.statement.dup
|
|
27
|
+
process_vars(statement, query.data_source)
|
|
28
|
+
@statements << statement
|
|
26
29
|
end
|
|
27
30
|
@bind_vars ||= []
|
|
28
31
|
|
|
@@ -43,7 +46,7 @@ module Blazer
|
|
|
43
46
|
|
|
44
47
|
def update
|
|
45
48
|
if update_dashboard(@dashboard)
|
|
46
|
-
redirect_to dashboard_path(@dashboard, variable_params)
|
|
49
|
+
redirect_to dashboard_path(@dashboard, variable_params(@dashboard))
|
|
47
50
|
else
|
|
48
51
|
render_errors @dashboard
|
|
49
52
|
end
|
|
@@ -62,7 +65,7 @@ module Blazer
|
|
|
62
65
|
Blazer.transform_statement.call(data_source, statement) if Blazer.transform_statement
|
|
63
66
|
data_source.clear_cache(statement)
|
|
64
67
|
end
|
|
65
|
-
redirect_to dashboard_path(@dashboard, variable_params)
|
|
68
|
+
redirect_to dashboard_path(@dashboard, variable_params(@dashboard))
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
private
|
|
@@ -45,7 +45,7 @@ module Blazer
|
|
|
45
45
|
@query.creator = blazer_user if @query.respond_to?(:creator)
|
|
46
46
|
|
|
47
47
|
if @query.save
|
|
48
|
-
redirect_to query_path(@query, variable_params)
|
|
48
|
+
redirect_to query_path(@query, variable_params(@query))
|
|
49
49
|
else
|
|
50
50
|
render_errors @query
|
|
51
51
|
end
|
|
@@ -156,7 +156,7 @@ module Blazer
|
|
|
156
156
|
process_vars(@statement, @query.data_source)
|
|
157
157
|
Blazer.transform_statement.call(data_source, @statement) if Blazer.transform_statement
|
|
158
158
|
data_source.clear_cache(@statement)
|
|
159
|
-
redirect_to query_path(@query, variable_params)
|
|
159
|
+
redirect_to query_path(@query, variable_params(@query))
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def update
|
|
@@ -168,7 +168,7 @@ module Blazer
|
|
|
168
168
|
@query.errors.add(:base, "Sorry, permission denied")
|
|
169
169
|
end
|
|
170
170
|
if @query.errors.empty? && @query.update(query_params)
|
|
171
|
-
redirect_to query_path(@query, variable_params)
|
|
171
|
+
redirect_to query_path(@query, variable_params(@query))
|
|
172
172
|
else
|
|
173
173
|
render_errors @query
|
|
174
174
|
end
|
|
@@ -176,7 +176,7 @@ module Blazer
|
|
|
176
176
|
|
|
177
177
|
def destroy
|
|
178
178
|
@query.destroy if @query.editable?(blazer_user)
|
|
179
|
-
redirect_to
|
|
179
|
+
redirect_to root_path
|
|
180
180
|
end
|
|
181
181
|
|
|
182
182
|
def tables
|
|
@@ -249,7 +249,9 @@ module Blazer
|
|
|
249
249
|
r[lat_index] && r[lon_index]
|
|
250
250
|
end.map do |r|
|
|
251
251
|
{
|
|
252
|
-
|
|
252
|
+
# Mapbox.js does sanitization with https://github.com/mapbox/sanitize-caja
|
|
253
|
+
# but we should do it here as well
|
|
254
|
+
title: r.each_with_index.map { |v, i| i == lat_index || i == lon_index ? nil : "<strong>#{ERB::Util.html_escape(@columns[i])}:</strong> #{ERB::Util.html_escape(v)}" }.compact.join("<br />").truncate(140),
|
|
253
255
|
latitude: r[lat_index],
|
|
254
256
|
longitude: r[lon_index]
|
|
255
257
|
}
|
|
@@ -60,6 +60,9 @@ module Blazer
|
|
|
60
60
|
ActionController::Base.helpers.pluralize(*args)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# checks shouldn't have variables, but in any case,
|
|
64
|
+
# avoid passing variable params to url helpers
|
|
65
|
+
# (known unsafe parameters are removed, but still not ideal)
|
|
63
66
|
def self.query_url(id)
|
|
64
67
|
Blazer::Engine.routes.url_helpers.query_url(id, ActionMailer::Base.default_url_options)
|
|
65
68
|
end
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
singleDatePicker: true,
|
|
43
43
|
locale: {format: format},
|
|
44
44
|
autoUpdateInput: false,
|
|
45
|
+
autoApply: true,
|
|
45
46
|
startDate: input.val().length > 0 ? moment.tz(input.val(), timeZone) : now
|
|
46
47
|
})
|
|
47
48
|
// hack to start with empty date
|
|
@@ -95,7 +96,8 @@
|
|
|
95
96
|
},
|
|
96
97
|
startDate: dateStr(29),
|
|
97
98
|
endDate: dateStr(),
|
|
98
|
-
opens: "right"
|
|
99
|
+
opens: "right",
|
|
100
|
+
alwaysShowCalendars: true
|
|
99
101
|
},
|
|
100
102
|
function(start, end) {
|
|
101
103
|
setTimeInputs(start, end)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
<head>
|
|
3
3
|
</head>
|
|
4
4
|
<body style="font-family: 'Helvetica Neue', Arial, Helvetica; font-size: 14px; color: #333;">
|
|
5
|
+
<%# check queries shouldn't have variables, but in any case, don't pass them to url helpers %>
|
|
5
6
|
<p><%= link_to "View", query_url(@check.query_id) %></p>
|
|
6
7
|
<% if @error %>
|
|
7
8
|
<p><%= @error %></p>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<%= form_for @dashboard, url: (@dashboard.persisted? ? dashboard_path(@dashboard, variable_params) : dashboards_path(variable_params)), html: {id: "app", class: "small-form"} do |f| %>
|
|
1
|
+
<%= form_for @dashboard, url: (@dashboard.persisted? ? dashboard_path(@dashboard, variable_params(@dashboard)) : dashboards_path(variable_params(@dashboard))), html: {id: "app", class: "small-form"} do |f| %>
|
|
2
2
|
<% if @dashboard.errors.any? %>
|
|
3
3
|
<div class="alert alert-danger"><%= @dashboard.errors.full_messages.first %></div>
|
|
4
4
|
<% end %>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
</h3>
|
|
11
11
|
</div>
|
|
12
12
|
<div class="col-sm-3 text-right">
|
|
13
|
-
<%= link_to "Edit", edit_dashboard_path(@dashboard, variable_params), class: "btn btn-info" %>
|
|
13
|
+
<%= link_to "Edit", edit_dashboard_path(@dashboard, variable_params(@dashboard)), class: "btn btn-info" %>
|
|
14
14
|
</div>
|
|
15
15
|
</div>
|
|
16
16
|
</div>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<% if @data_sources.any? { |ds| ds.cache_mode != "off" } %>
|
|
22
22
|
<p class="text-muted" style="float: right;">
|
|
23
23
|
Some queries may be cached
|
|
24
|
-
<%= link_to "Refresh", refresh_dashboard_path(@dashboard, variable_params), method: :post %>
|
|
24
|
+
<%= link_to "Refresh", refresh_dashboard_path(@dashboard, variable_params(@dashboard)), method: :post %>
|
|
25
25
|
</p>
|
|
26
26
|
<% end %>
|
|
27
27
|
|
|
@@ -33,13 +33,13 @@
|
|
|
33
33
|
|
|
34
34
|
<% @queries.each_with_index do |query, i| %>
|
|
35
35
|
<div class="chart-container">
|
|
36
|
-
<h4><%= link_to query.friendly_name, query_path(query, variable_params), target: "_blank" %></h4>
|
|
36
|
+
<h4><%= link_to query.friendly_name, query_path(query, variable_params(query)), target: "_blank" %></h4>
|
|
37
37
|
<div id="chart-<%= i %>" class="chart">
|
|
38
38
|
<p class="text-muted">Loading...</p>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
41
|
<script>
|
|
42
|
-
<%= blazer_js_var "data", {statement:
|
|
42
|
+
<%= blazer_js_var "data", {statement: @statements[i], query_id: query.id, data_source: query.data_source, only_chart: true} %>
|
|
43
43
|
|
|
44
44
|
runQuery(data, function (data) {
|
|
45
45
|
$("#chart-<%= i %>").html(data)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<% end %>
|
|
4
4
|
|
|
5
5
|
<div id="app" v-cloak>
|
|
6
|
-
<%= form_for @query, url: (@query.persisted? ? query_path(@query, variable_params) : queries_path(variable_params)), html: {autocomplete: "off"} do |f| %>
|
|
6
|
+
<%= form_for @query, url: (@query.persisted? ? query_path(@query, variable_params(@query)) : queries_path(variable_params(@query))), html: {autocomplete: "off"} do |f| %>
|
|
7
7
|
<div class="row">
|
|
8
8
|
<div id="statement-box" class="col-xs-8">
|
|
9
9
|
<div class= "form-group">
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
</div>
|
|
68
68
|
|
|
69
69
|
<script>
|
|
70
|
-
<%= blazer_js_var "params", variable_params %>
|
|
70
|
+
<%= blazer_js_var "params", variable_params(@query) %>
|
|
71
71
|
<%= blazer_js_var "previewStatement", Hash[Blazer.data_sources.map { |k, v| [k, (v.preview_statement rescue "")] }] %>
|
|
72
72
|
|
|
73
73
|
var app = new Vue({
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<% end %>
|
|
21
21
|
|
|
22
22
|
<% if @query && params[:query_id] %>
|
|
23
|
-
<%= link_to "Refresh", refresh_query_path(@query, variable_params), method: :post %>
|
|
23
|
+
<%= link_to "Refresh", refresh_query_path(@query, variable_params(@query)), method: :post %>
|
|
24
24
|
<% end %>
|
|
25
25
|
</p>
|
|
26
26
|
<% end %>
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
<% if @query && @result.forecastable? && !params[:forecast] %>
|
|
38
38
|
·
|
|
39
|
-
<%= link_to "Forecast", query_path(@query, {forecast: "t"}.merge(variable_params)) %>
|
|
39
|
+
<%= link_to "Forecast", query_path(@query, {forecast: "t"}.merge(variable_params(@query))) %>
|
|
40
40
|
<% end %>
|
|
41
41
|
</p>
|
|
42
42
|
<% end %>
|
|
@@ -80,7 +80,8 @@
|
|
|
80
80
|
<%= blazer_js_var "mapboxAccessToken", Blazer.mapbox_access_token %>
|
|
81
81
|
<%= blazer_js_var "markers", @markers %>
|
|
82
82
|
L.mapbox.accessToken = mapboxAccessToken;
|
|
83
|
-
var map = L.mapbox.map('map'
|
|
83
|
+
var map = L.mapbox.map('map')
|
|
84
|
+
.addLayer(L.mapbox.styleLayer('mapbox://styles/mapbox/streets-v11'));
|
|
84
85
|
var featureLayer = L.mapbox.featureLayer().addTo(map);
|
|
85
86
|
var geojson = [];
|
|
86
87
|
for (var i = 0; i < markers.length; i++) {
|
|
@@ -106,13 +107,13 @@
|
|
|
106
107
|
</script>
|
|
107
108
|
<% elsif chart_type == "line" %>
|
|
108
109
|
<% 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]} } %>
|
|
109
|
-
<%= line_chart chart_data, chart_options %>
|
|
110
|
+
<%= line_chart chart_data, **chart_options %>
|
|
110
111
|
<% elsif chart_type == "line2" %>
|
|
111
|
-
<%= 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 %>
|
|
112
|
+
<%= 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 %>
|
|
112
113
|
<% elsif chart_type == "pie" %>
|
|
113
|
-
<%= pie_chart @rows.map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, chart_options %>
|
|
114
|
+
<%= pie_chart @rows.map { |r| [(@boom[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, **chart_options %>
|
|
114
115
|
<% elsif chart_type == "bar" %>
|
|
115
|
-
<%= 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 %>
|
|
116
|
+
<%= 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 %>
|
|
116
117
|
<% elsif chart_type == "bar2" %>
|
|
117
118
|
<% first_20 = @rows.group_by { |r| r[0] }.values.first(20).flatten(1) %>
|
|
118
119
|
<% labels = first_20.map { |r| r[0] }.uniq %>
|
|
@@ -122,7 +123,7 @@
|
|
|
122
123
|
<% first_20 << [l, s, 0] unless first_20.find { |r| r[0] == l && r[1] == s } %>
|
|
123
124
|
<% end %>
|
|
124
125
|
<% end %>
|
|
125
|
-
<%= 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 %>
|
|
126
|
+
<%= 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 %>
|
|
126
127
|
<% elsif chart_type == "scatter" %>
|
|
127
128
|
<%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1], **chart_options %>
|
|
128
129
|
<% elsif @only_chart %>
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
</h3>
|
|
11
11
|
</div>
|
|
12
12
|
<div class="col-sm-3 text-right">
|
|
13
|
-
<%= link_to "Edit", edit_query_path(@query, variable_params), class: "btn btn-default", disabled: !@query.editable?(blazer_user) %>
|
|
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" %>
|
|
13
|
+
<%= link_to "Edit", edit_query_path(@query, variable_params(@query)), class: "btn btn-default", disabled: !@query.editable?(blazer_user) %>
|
|
14
|
+
<%= link_to "Fork", new_query_path(variable_params(@query).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
17
|
<%= button_to "Download", run_queries_path(query_id: @query.id, format: "csv", forecast: params[:forecast]), params: {statement: @statement}, class: "btn btn-primary" %>
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
$("#results").addClass("query-error").html(message)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
<% data = variable_params(@query).merge(statement: @statement, query_id: @query.id, data_source: @query.data_source) %>
|
|
60
|
+
<% data.merge!(forecast: "t") if params[:forecast] %>
|
|
61
|
+
<%= blazer_js_var "data", data %>
|
|
60
62
|
|
|
61
63
|
runQuery(data, showRun, showError)
|
|
62
64
|
</script>
|
|
@@ -11,8 +11,8 @@
|
|
|
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/v3.
|
|
15
|
-
<%= javascript_include_tag "https://api.mapbox.com/mapbox.js/v3.
|
|
14
|
+
<%= stylesheet_link_tag "https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.css", integrity: "sha384-vxzdEt+wZRPNQbhChjmiaFMLWg86IGuq1NGDehJHsD2mphYkxXll/eSs16WWi6Dq", crossorigin: "anonymous" %>
|
|
15
|
+
<%= javascript_include_tag "https://api.mapbox.com/mapbox.js/v3.3.1/mapbox.js", integrity: "sha384-CTBEiDLiZJ8gkAQ3fYGoeiRp81/ecNiBkGz11jXFALOZ6++rbnqmdo6OImkmr1MO", crossorigin: "anonymous" %>
|
|
16
16
|
<% end %>
|
|
17
17
|
<%= csrf_meta_tags %>
|
|
18
18
|
</head>
|
data/lib/blazer.rb
CHANGED
|
@@ -18,6 +18,7 @@ require "blazer/adapters/cassandra_adapter"
|
|
|
18
18
|
require "blazer/adapters/drill_adapter"
|
|
19
19
|
require "blazer/adapters/druid_adapter"
|
|
20
20
|
require "blazer/adapters/elasticsearch_adapter"
|
|
21
|
+
require "blazer/adapters/influxdb_adapter"
|
|
21
22
|
require "blazer/adapters/mongodb_adapter"
|
|
22
23
|
require "blazer/adapters/neo4j_adapter"
|
|
23
24
|
require "blazer/adapters/presto_adapter"
|
|
@@ -119,7 +120,7 @@ module Blazer
|
|
|
119
120
|
def self.extract_vars(statement)
|
|
120
121
|
# strip commented out lines
|
|
121
122
|
# and regex {1} or {1,2}
|
|
122
|
-
statement.gsub(/\-\-.+/, "").gsub(/\/\*.+\*\//m, "").scan(/\{\w*?\}/i).map { |v| v[1...-1] }.reject { |v| /\A\d+(\,\d+)?\z/.match(v) || v.empty? }.uniq
|
|
123
|
+
statement.to_s.gsub(/\-\-.+/, "").gsub(/\/\*.+\*\//m, "").scan(/\{\w*?\}/i).map { |v| v[1...-1] }.reject { |v| /\A\d+(\,\d+)?\z/.match(v) || v.empty? }.uniq
|
|
123
124
|
end
|
|
124
125
|
|
|
125
126
|
def self.run_checks(schedule: nil)
|
|
@@ -220,6 +221,7 @@ Blazer.register_adapter "cassandra", Blazer::Adapters::CassandraAdapter
|
|
|
220
221
|
Blazer.register_adapter "drill", Blazer::Adapters::DrillAdapter
|
|
221
222
|
Blazer.register_adapter "druid", Blazer::Adapters::DruidAdapter
|
|
222
223
|
Blazer.register_adapter "elasticsearch", Blazer::Adapters::ElasticsearchAdapter
|
|
224
|
+
Blazer.register_adapter "influxdb", Blazer::Adapters::InfluxdbAdapter
|
|
223
225
|
Blazer.register_adapter "neo4j", Blazer::Adapters::Neo4jAdapter
|
|
224
226
|
Blazer.register_adapter "presto", Blazer::Adapters::PrestoAdapter
|
|
225
227
|
Blazer.register_adapter "mongodb", Blazer::Adapters::MongodbAdapter
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Blazer
|
|
2
|
+
module Adapters
|
|
3
|
+
class InfluxdbAdapter < BaseAdapter
|
|
4
|
+
def run_statement(statement, comment)
|
|
5
|
+
columns = []
|
|
6
|
+
rows = []
|
|
7
|
+
error = nil
|
|
8
|
+
|
|
9
|
+
begin
|
|
10
|
+
result = client.query(statement, denormalize: false).first
|
|
11
|
+
columns = result["columns"]
|
|
12
|
+
rows = result["values"]
|
|
13
|
+
|
|
14
|
+
# parse time columns
|
|
15
|
+
# current approach isn't ideal, but result doesn't include types
|
|
16
|
+
# another approach would be to check the format
|
|
17
|
+
time_index = columns.index("time")
|
|
18
|
+
if time_index
|
|
19
|
+
rows.each do |row|
|
|
20
|
+
row[time_index] = Time.parse(row[time_index]) if row[time_index]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
rescue => e
|
|
24
|
+
error = e.message
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
[columns, rows, error]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def tables
|
|
31
|
+
client.list_series
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def preview_statement
|
|
35
|
+
"SELECT * FROM {table} LIMIT 10"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
def client
|
|
41
|
+
@client ||= InfluxDB::Client.new(url: settings["url"])
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/blazer/result.rb
CHANGED
|
@@ -94,29 +94,10 @@ module Blazer
|
|
|
94
94
|
case Blazer.forecasting
|
|
95
95
|
when "prophet"
|
|
96
96
|
require "prophet"
|
|
97
|
-
|
|
98
|
-
df =
|
|
99
|
-
Daru::DataFrame.new(
|
|
100
|
-
"ds" => @rows.map { |r| r[0] },
|
|
101
|
-
"y" => @rows.map { |r| r[1] }
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
# TODO determine frequency
|
|
105
|
-
freq = "D"
|
|
106
|
-
|
|
107
|
-
m = Prophet.new
|
|
108
|
-
m.fit(df)
|
|
109
|
-
future = m.make_future_dataframe(periods: count, freq: freq, include_history: false)
|
|
110
|
-
fcst = m.predict(future)
|
|
111
|
-
ds = fcst["ds"]
|
|
112
|
-
if @rows[0][0].is_a?(Date)
|
|
113
|
-
ds = ds.map { |v| v.to_date }
|
|
114
|
-
end
|
|
115
|
-
forecast = ds.zip(fcst["yhat"]).to_h
|
|
97
|
+
forecast = Prophet.forecast(@rows.to_h, count: count)
|
|
116
98
|
else
|
|
117
99
|
require "trend"
|
|
118
|
-
|
|
119
|
-
forecast = Trend.forecast(Hash[@rows], count: count)
|
|
100
|
+
forecast = Trend.forecast(@rows.to_h, count: count)
|
|
120
101
|
end
|
|
121
102
|
|
|
122
103
|
# round integers
|
data/lib/blazer/version.rb
CHANGED
|
@@ -14,12 +14,12 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
|
14
14
|
t.references :query
|
|
15
15
|
t.text :statement
|
|
16
16
|
t.string :data_source
|
|
17
|
-
t.
|
|
17
|
+
t.datetime :created_at
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
create_table :blazer_dashboards do |t|
|
|
21
21
|
t.references :creator
|
|
22
|
-
t.
|
|
22
|
+
t.string :name
|
|
23
23
|
t.timestamps null: false
|
|
24
24
|
end
|
|
25
25
|
|
|
@@ -39,7 +39,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
|
|
|
39
39
|
t.text :slack_channels
|
|
40
40
|
t.string :check_type
|
|
41
41
|
t.text :message
|
|
42
|
-
t.
|
|
42
|
+
t.datetime :last_run_at
|
|
43
43
|
t.timestamps null: false
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Copyright (c) 2010, Ajax.org B.V.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
|
6
|
+
* Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
* Neither the name of Ajax.org B.V. nor the
|
|
12
|
+
names of its contributors may be used to endorse or promote products
|
|
13
|
+
derived from this software without specific prior written permission.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
16
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
17
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
|
19
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
20
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
21
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
22
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
23
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
24
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|