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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +49 -17
  5. data/app/assets/javascripts/blazer/Chart.js +13794 -12099
  6. data/app/assets/javascripts/blazer/Sortable.js +3695 -1526
  7. data/app/assets/javascripts/blazer/chartkick.js +296 -46
  8. data/app/assets/javascripts/blazer/daterangepicker.js +194 -269
  9. data/app/assets/javascripts/blazer/jquery.js +1150 -642
  10. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +621 -287
  11. data/app/assets/javascripts/blazer/moment.js +5085 -2460
  12. data/app/assets/stylesheets/blazer/daterangepicker.css +394 -253
  13. data/app/controllers/blazer/base_controller.rb +16 -2
  14. data/app/controllers/blazer/dashboards_controller.rb +6 -3
  15. data/app/controllers/blazer/queries_controller.rb +7 -5
  16. data/app/mailers/blazer/slack_notifier.rb +3 -0
  17. data/app/models/blazer/dashboard.rb +4 -0
  18. data/app/views/blazer/_variables.html.erb +3 -1
  19. data/app/views/blazer/check_mailer/failing_checks.html.erb +1 -0
  20. data/app/views/blazer/check_mailer/state_change.html.erb +1 -0
  21. data/app/views/blazer/dashboards/_form.html.erb +1 -1
  22. data/app/views/blazer/dashboards/show.html.erb +4 -4
  23. data/app/views/blazer/queries/_form.html.erb +2 -2
  24. data/app/views/blazer/queries/run.html.erb +9 -8
  25. data/app/views/blazer/queries/show.html.erb +5 -3
  26. data/app/views/layouts/blazer/application.html.erb +2 -2
  27. data/lib/blazer.rb +3 -1
  28. data/lib/blazer/adapters/influxdb_adapter.rb +45 -0
  29. data/lib/blazer/result.rb +2 -21
  30. data/lib/blazer/version.rb +1 -1
  31. data/lib/generators/blazer/templates/install.rb.tt +3 -3
  32. data/licenses/LICENSE-ace.txt +24 -0
  33. data/licenses/LICENSE-bootstrap.txt +21 -0
  34. data/licenses/LICENSE-chart.js.txt +9 -0
  35. data/licenses/LICENSE-chartkick.js.txt +22 -0
  36. data/licenses/LICENSE-daterangepicker.txt +21 -0
  37. data/licenses/LICENSE-fuzzysearch.txt +20 -0
  38. data/licenses/LICENSE-highlight.js.txt +29 -0
  39. data/licenses/LICENSE-jquery-ujs.txt +20 -0
  40. data/licenses/LICENSE-jquery.txt +20 -0
  41. data/licenses/LICENSE-moment-timezone.txt +20 -0
  42. data/licenses/LICENSE-moment.txt +22 -0
  43. data/licenses/LICENSE-selectize.txt +202 -0
  44. data/licenses/LICENSE-sortable.txt +21 -0
  45. data/licenses/LICENSE-stickytableheaders.txt +20 -0
  46. data/licenses/LICENSE-stupidtable.txt +19 -0
  47. data/licenses/LICENSE-vue.txt +21 -0
  48. metadata +19 -2
@@ -86,8 +86,22 @@ module Blazer
86
86
  [smart_var, error]
87
87
  end
88
88
 
89
- def variable_params
90
- params.except(: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).permit!
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
- process_vars(query.statement, query.data_source)
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 root_url
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
- title: r.each_with_index.map{ |v, i| i == lat_index || i == lon_index ? nil : "<strong>#{@columns[i]}:</strong> #{v}" }.compact.join("<br />").truncate(140),
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
@@ -6,6 +6,10 @@ module Blazer
6
6
 
7
7
  validates :name, presence: true
8
8
 
9
+ def variables
10
+ queries.flat_map { |q| q.variables }.uniq
11
+ end
12
+
9
13
  def to_param
10
14
  [id, name.gsub("'", "").parameterize].join("-")
11
15
  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)
@@ -1,5 +1,6 @@
1
1
  <ul>
2
2
  <% @checks.each do |check| %>
3
+ <%# check queries shouldn't have variables, but in any case, don't pass them to url helpers %>
3
4
  <li><%= link_to check.query.name, query_url(check.query_id) %> <%= check.state %></li>
4
5
  <% end %>
5
6
  </ul>
@@ -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: query.statement, query_id: query.id, data_source: query.data_source, only_chart: true} %>
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
  &middot;
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', 'mapbox.streets');
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
- <%= blazer_js_var "data", variable_params.merge(statement: @statement, query_id: @query.id, data_source: @query.data_source) %>
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.2.1/mapbox.css", integrity: "sha384-vxzdEt+wZRPNQbhChjmiaFMLWg86IGuq1NGDehJHsD2mphYkxXll/eSs16WWi6Dq", crossorigin: "anonymous" %>
15
- <%= javascript_include_tag "https://api.mapbox.com/mapbox.js/v3.2.1/mapbox.js", integrity: "sha384-dRMjy8ZlKk6czEwpaK3yySKItGPpGOplDM1+osgTwGhmxeTPpjB4kmRbDr2Y7lfQ", crossorigin: "anonymous" %>
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>
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "2.2.2"
2
+ VERSION = "2.2.7"
3
3
  end
@@ -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.timestamp :created_at
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.text :name
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.timestamp :last_run_at
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.