blazer 2.2.1 → 2.2.6

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eedba409f17c85953be66d2b295f1857fb61de146d5c88288509440f419853e7
4
- data.tar.gz: ad0f8f433ad5be3052eb0237f86121c6a59147b2e240fb2a41d68611e69d3022
3
+ metadata.gz: cfb774d21d8d5756c2fc273c2148542e26550452ea886adf92023c411b45200a
4
+ data.tar.gz: 8200979166341f38e0647fbb566f1fd8ead6895ea254a12ead4901e344d80f38
5
5
  SHA512:
6
- metadata.gz: 8e4236c254472fb2bdc9a8cb107306d7263a11ade5b8f223cefc2363431f6f9ffeb7087d373a95823bf53112c9869871d132764232754898f8740c72ea01ceea
7
- data.tar.gz: bb12b6473ef4702adea12a9847ef020e0f8277d9714816cbfc511262345bffbbc8aa5530a7310dd4a263f634a493286f446bc8c4db48428f6c396109b2dce474
6
+ metadata.gz: d6705d3dd2c27db9aeb36b9afd2e9a335d876f36865c5bbaade10f13cca80436ec3c31787231c16b591fd78481cdbb7f11df82de00417fd71d5005b1db038b11
7
+ data.tar.gz: 50c2230e80ad2a0e487197c8593a4c774a0f58304ed88b704860ce4550029202f677042a685b7457a57c67b6e660a82f4692ba529a5eabb41c17d2d1182c3fce
@@ -1,27 +1,51 @@
1
- ## 2.2.1
1
+ ## 2.2.6 (2020-07-21)
2
+
3
+ - Added experimental support for InfluxDB
4
+ - Added support for forecasting week, month, quarter, and year with Prophet
5
+ - Fixed forecasting link not showing up
6
+
7
+ ## 2.2.5 (2020-06-03)
8
+
9
+ - Updated maps to fix deprecation error
10
+
11
+ ## 2.2.4 (2020-05-30)
12
+
13
+ - Fixed error with new queries
14
+
15
+ ## 2.2.3 (2020-05-30)
16
+
17
+ - Improved query parameter handling
18
+
19
+ ## 2.2.2 (2020-04-13)
20
+
21
+ - Added experimental support for the Socrata Open Data API (SODA)
22
+ - Added experimental Prophet forecasting
23
+ - Fixed query search for non-ASCII characters
24
+
25
+ ## 2.2.1 (2019-10-08)
2
26
 
3
27
  - Added support for Sprockets 4
4
28
  - Improved Snowflake table preview
5
29
  - Fixed bug with refresh link not showing
6
30
 
7
- ## 2.2.0
31
+ ## 2.2.0 (2019-07-12)
8
32
 
9
33
  - Added schema to table preview for Postgres and Redshift
10
34
  - Fixed bug with Slack notifications not sending
11
35
  - Dropped support for Rails 4.2
12
36
 
13
- ## 2.1.0
37
+ ## 2.1.0 (2019-06-04)
14
38
 
15
39
  - Require latest Chartkick to prevent possible XSS - see [#245](https://github.com/ankane/blazer/issues/245)
16
40
 
17
- ## 2.0.2
41
+ ## 2.0.2 (2019-05-26)
18
42
 
19
43
  - Added support for variable transformation for blind indexing
20
44
  - Added experimental support for Neo4j
21
45
  - Added experimental support for Salesforce
22
46
  - Fixed JavaScript sorting for numbers with commas
23
47
 
24
- ## 2.0.1
48
+ ## 2.0.1 (2019-01-07)
25
49
 
26
50
  - Added favicon
27
51
  - Added search for checks and schema
@@ -32,7 +56,7 @@
32
56
  - Improved docs for new installs
33
57
  - Fixed error with canceling queries
34
58
 
35
- ## 2.0.0
59
+ ## 2.0.0 (2019-01-03)
36
60
 
37
61
  - Added support for Slack
38
62
  - Added `async` option
@@ -45,68 +69,67 @@ Breaking changes
45
69
 
46
70
  - Dropped support for Rails < 4.2
47
71
 
48
- ## 1.9.0
72
+ ## 1.9.0 (2018-06-17)
49
73
 
50
74
  - Prompt developers to check custom `before_action`
51
75
  - Better ordering on home page
52
76
  - Added support for Snowflake
53
77
 
54
- ## 1.8.2
78
+ ## 1.8.2 (2018-02-22)
55
79
 
56
80
  - Added support for Cassandra
57
81
  - Fixes for Druid
58
-
59
- ## 1.8.1
60
-
61
82
  - Added support for Amazon Athena
62
83
  - Added support for Druid
63
84
  - Fixed query cancellation
64
85
 
65
- ## 1.8.0
86
+ There was no 1.8.1 release.
87
+
88
+ ## 1.8.0 (2017-05-01)
66
89
 
67
90
  - Added support for Rails 5.1
68
91
 
69
- ## 1.7.10
92
+ ## 1.7.10 (2017-04-03)
70
93
 
71
94
  - Added support for Google BigQuery
72
95
  - Require `drill-sergeant` gem for Apache Drill
73
96
  - Better handling of checks with variables
74
97
 
75
- ## 1.7.9
98
+ ## 1.7.9 (2017-03-20)
76
99
 
77
100
  - Added beta support for Apache Drill
78
101
  - Added email validation for checks
79
102
  - Updated Chart.js to 2.5.0
80
103
 
81
- ## 1.7.8
104
+ ## 1.7.8 (2017-02-06)
82
105
 
83
106
  - Added support for custom adapters
84
107
  - Fixed bug with scatter charts on dashboards
85
108
  - Fixed table preview for SQL Server
86
109
  - Fixed issue when `default_url_options` set
87
110
 
88
- ## 1.7.7
111
+ ## 1.7.7 (2016-12-17)
89
112
 
90
113
  - Fixed preview error for MySQL
91
114
  - Fixed error with timeouts for MySQL
92
115
 
93
- ## 1.7.6
116
+ ## 1.7.6 (2016-12-13)
94
117
 
95
118
  - Added scatter chart
96
119
  - Fixed issue with false values showing up blank
97
120
  - Fixed preview for table names with certain characters
98
121
 
99
- ## 1.7.5
122
+ ## 1.7.5 (2016-11-22)
100
123
 
101
124
  - Fixed issue with check emails sometimes failing for default Rails 5 ActiveJob adapter
102
125
  - Fixed sorting for new dashboards
103
126
 
104
- ## 1.7.4
127
+ ## 1.7.4 (2016-11-06)
105
128
 
106
129
  - Removed extra dependencies added in 1.7.1
107
130
  - Fixed `send_failing_checks` for default Rails 5 ActiveJob adapter
108
131
 
109
- ## 1.7.3
132
+ ## 1.7.3 (2016-11-01)
110
133
 
111
134
  - Fixed JavaScript errors
112
135
  - Fixed query cancel error
@@ -114,20 +137,20 @@ Breaking changes
114
137
  - Include sample data in email when bad data checks fail
115
138
  - Fixed deprecation warnings
116
139
 
117
- ## 1.7.2
140
+ ## 1.7.2 (2016-10-30)
118
141
 
119
142
  - Cancel all queries on page nav
120
143
  - Prevent Ace from taking over find command
121
144
  - Added ability to use hashes for smart columns
122
145
  - Added ability to inherit smart variables and columns from other data sources
123
146
 
124
- ## 1.7.1
147
+ ## 1.7.1 (2016-10-29)
125
148
 
126
149
  - Do not fork when enter key pressed
127
150
  - Use custom version of Chart.js to fix label overlap
128
151
  - Improved performance of home page
129
152
 
130
- ## 1.7.0
153
+ ## 1.7.0 (2016-09-07)
131
154
 
132
155
  - Added ability to cancel queries on backend for Postgres and Redshift
133
156
  - Only run 3 queries at a time on dashboards
@@ -135,65 +158,65 @@ Breaking changes
135
158
  - Attempt to reconnect when connection issues
136
159
  - Fixed issues with caching
137
160
 
138
- ## 1.6.2
161
+ ## 1.6.2 (2016-08-11)
139
162
 
140
163
  - Added basic query permissions
141
164
  - Added ability to use arrays and hashes for smart variables
142
165
  - Added cancel button for queries
143
166
  - Added `lat` and `lng` as map keys
144
167
 
145
- ## 1.6.1
168
+ ## 1.6.1 (2016-07-30)
146
169
 
147
170
  - Added support for Presto [beta]
148
171
  - Added support for Elasticsearch timeouts
149
172
  - Fixed error in Rails 5
150
173
 
151
- ## 1.6.0
174
+ ## 1.6.0 (2016-07-28)
152
175
 
153
176
  - Added support for MongoDB [beta]
154
177
  - Added support for Elasticsearch [beta]
155
178
  - Fixed deprecation warning in Rails 5
156
179
 
157
- ## 1.5.1
180
+ ## 1.5.1 (2016-07-24)
158
181
 
159
182
  - Added anomaly detection for data less than 2 weeks
160
183
  - Added autolinking urls
161
184
  - Added support for images
162
185
 
163
- ## 1.5.0
186
+ ## 1.5.0 (2016-06-29)
164
187
 
165
188
  - Added new bar chart format
166
189
  - Added anomaly detection checks
167
190
  - Added `async` option for polling
168
191
 
169
- ## 1.4.0
192
+ ## 1.4.0 (2016-06-09)
170
193
 
171
194
  - Added `slow` cache mode
172
195
  - Fixed `BLAZER_DATABASE_URL required` error
173
196
  - Fixed issue with duplicate column names
174
197
 
175
- ## 1.3.5
198
+ ## 1.3.5 (2016-05-11)
176
199
 
177
200
  - Fixed error with checks
178
201
 
179
- ## 1.3.4
202
+ ## 1.3.4 (2016-05-11)
180
203
 
181
204
  - Fixed issue with missing queries
182
205
 
183
- ## 1.3.3
206
+ ## 1.3.3 (2016-05-08)
184
207
 
185
208
  - Fixed error with Rails 4.1 and below
186
209
 
187
- ## 1.3.2
210
+ ## 1.3.2 (2016-05-07)
188
211
 
189
212
  - Added support for Rails 5
190
213
  - Attempt to reconnect for checks
191
214
 
192
- ## 1.3.1
215
+ ## 1.3.1 (2016-05-06)
193
216
 
194
217
  - Fixed migration error
195
218
 
196
- ## 1.3.0
219
+ ## 1.3.0 (2016-05-06)
197
220
 
198
221
  - Added schedule for checks
199
222
  - Switched to Chart.js for charts
@@ -202,11 +225,11 @@ Breaking changes
202
225
  - Raise error when timeout not supported
203
226
  - Added creator to dashboards and checks
204
227
 
205
- ## 1.2.1
228
+ ## 1.2.1 (2016-04-26)
206
229
 
207
230
  - Fixed checks
208
231
 
209
- ## 1.2.0
232
+ ## 1.2.0 (2016-03-22)
210
233
 
211
234
  - Added non-editable queries
212
235
  - Added variable defaults
@@ -215,7 +238,7 @@ Breaking changes
215
238
  - Hide variables from commented out lines
216
239
  - Fixed regex as variable names
217
240
 
218
- ## 1.1.1
241
+ ## 1.1.1 (2016-03-06)
219
242
 
220
243
  - Added `before_action` option
221
244
  - Added invert option for checks
@@ -224,7 +247,7 @@ Breaking changes
224
247
  - Fixed request URI too large
225
248
  - Prevent accidental backspace nav on query page
226
249
 
227
- ## 1.1.0
250
+ ## 1.1.0 (2015-12-27)
228
251
 
229
252
  - Replaced pie charts with column charts
230
253
  - Fixed error with datepicker
@@ -232,55 +255,55 @@ Breaking changes
232
255
  - Added a notice when editing a query that is part of a dashboard
233
256
  - Added refresh for dashboards
234
257
 
235
- ## 1.0.4
258
+ ## 1.0.4 (2015-11-04)
236
259
 
237
260
  - Added recently viewed queries and dashboards to home page
238
261
  - Fixed refresh when transform statement is used
239
262
  - Fixed error when no user model
240
263
 
241
- ## 1.0.3
264
+ ## 1.0.3 (2015-10-18)
242
265
 
243
266
  - Added maps
244
267
  - Added support for Rails 4.0
245
268
 
246
- ## 1.0.2
269
+ ## 1.0.2 (2015-10-11)
247
270
 
248
271
  - Fixed error when installing
249
272
  - Added `schemas` option
250
273
 
251
- ## 1.0.1
274
+ ## 1.0.1 (2015-10-08)
252
275
 
253
276
  - Added comments to queries
254
277
  - Added `cache` option
255
278
  - Added `user_method` option
256
279
  - Added `use_transaction` option
257
280
 
258
- ## 1.0.0
281
+ ## 1.0.0 (2015-10-04)
259
282
 
260
283
  - Added support for multiple data sources
261
284
  - Added dashboards
262
285
  - Added checks
263
286
  - Added support for Redshift
264
287
 
265
- ## 0.0.8
288
+ ## 0.0.8 (2015-09-05)
266
289
 
267
290
  - Easier to edit queries with variables
268
291
  - Dynamically expand editor height as needed
269
292
  - No need for spaces in search
270
293
 
271
- ## 0.0.7
294
+ ## 0.0.7 (2015-07-23)
272
295
 
273
296
  - Fixed error when no `User` class
274
297
  - Fixed forking a query with variables
275
298
  - Set time zone after Rails initializes
276
299
 
277
- ## 0.0.6
300
+ ## 0.0.6 (2015-06-18)
278
301
 
279
302
  - Added fork button
280
303
  - Fixed trending
281
304
  - Fixed time zones for date select
282
305
 
283
- ## 0.0.5
306
+ ## 0.0.5 (2015-01-31)
284
307
 
285
308
  - Added support for Rails 4.2
286
309
  - Fixed error with `mysql2` adapter
data/README.md CHANGED
@@ -4,9 +4,9 @@ Explore your data with SQL. Easily create charts and dashboards, and share them
4
4
 
5
5
  [Try it out](https://blazer.dokkuapp.com)
6
6
 
7
- [![Screenshot](https://blazer.dokkuapp.com/assets/screenshot-6ca3115a518b488026e48be83ba0d4c9.png)](https://blazer.dokkuapp.com)
7
+ [![Screenshot](https://blazer.dokkuapp.com/assets/blazer-90fb6ce8ad25614c6f9c3b4619cbfbd66fa3d6567dac34d83df540f6688665f1.png)](https://blazer.dokkuapp.com)
8
8
 
9
- Blazer 2.0 was recently released! See [instructions for upgrading](#20).
9
+ Blazer is also available as a [Docker image](https://github.com/ankane/blazer-docker).
10
10
 
11
11
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
12
12
 
@@ -426,10 +426,30 @@ If you’re on Heroku, follow [these additional instructions](#anomaly-detection
426
426
 
427
427
  ## Forecasting
428
428
 
429
- Blazer has experimental support for forecasting through [Trend](https://trendapi.org/).
429
+ Blazer has experimental support for two different forecasting methods.
430
+
431
+ A forecast link will appear for queries that return 2 columns with types timestamp and numeric.
430
432
 
431
433
  [Example](https://blazer.dokkuapp.com/queries/18-forecast?forecast=t)
432
434
 
435
+ ### Prophet
436
+
437
+ Add [prophet](https://github.com/ankane/prophet) to your Gemfile:
438
+
439
+ ```ruby
440
+ gem 'prophet-rb', '>= 0.2.1'
441
+ ```
442
+
443
+ And add to `config/blazer.yml`:
444
+
445
+ ```yml
446
+ forecasting: prophet
447
+ ```
448
+
449
+ ### Trend
450
+
451
+ [Trend](https://trendapi.org/) uses an external service.
452
+
433
453
  Add [trend](https://github.com/ankane/trend) to your Gemfile:
434
454
 
435
455
  ```ruby
@@ -442,8 +462,6 @@ And add to `config/blazer.yml`:
442
462
  forecasting: trend
443
463
  ```
444
464
 
445
- A forecast link will appear for queries that return 2 columns with types timestamp and numeric.
446
-
447
465
  ## Data Sources
448
466
 
449
467
  Blazer supports multiple data sources :tada:
@@ -473,13 +491,15 @@ data_sources:
473
491
  - [Elasticsearch](#elasticsearch)
474
492
  - [Google BigQuery](#google-bigquery)
475
493
  - [IBM DB2 and Informix](#ibm-db2-and-informix)
494
+ - [InfluxDB](#influxdb)
476
495
  - [MongoDB](#mongodb-1)
477
496
  - [MySQL](#mysql-1)
478
- - [Neo4j](#neo4j-experimental)
497
+ - [Neo4j](#neo4j)
479
498
  - [Oracle](#oracle)
480
499
  - [PostgreSQL](#postgresql-1)
481
500
  - [Presto](#presto)
482
- - [Salesforce](#salesforce-experimental)
501
+ - [Salesforce](#salesforce)
502
+ - [Socrata Open Data API (SODA)](#socrata-open-data-api-soda)
483
503
  - [Snowflake](#snowflake)
484
504
  - [SQLite](#sqlite)
485
505
  - [SQL Server](#sql-server)
@@ -508,7 +528,7 @@ data_sources:
508
528
 
509
529
  ### Amazon Redshift
510
530
 
511
- Add [activerecord4-redshift-adapter](https://github.com/aamine/activerecord4-redshift-adapter) or [activerecord5-redshift-adapter](https://github.com/ConsultingMD/activerecord5-redshift-adapter) to your Gemfile and set:
531
+ Add [activerecord6-redshift-adapter](https://github.com/kwent/activerecord6-redshift-adapter) or [activerecord5-redshift-adapter](https://github.com/ConsultingMD/activerecord5-redshift-adapter) to your Gemfile and set:
512
532
 
513
533
  ```yml
514
534
  data_sources:
@@ -573,7 +593,28 @@ data_sources:
573
593
 
574
594
  ### IBM DB2 and Informix
575
595
 
576
- Use [ibm_db](https://github.com/ibmdb/ruby-ibmdb).
596
+ Add [ibm_db](https://github.com/ibmdb/ruby-ibmdb) to your Gemfile and set:
597
+
598
+ ```yml
599
+ data_sources:
600
+ my_source:
601
+ url: ibm-db://user:password@hostname:50000/database
602
+ ```
603
+
604
+ ### InfluxDB
605
+
606
+ *Experimental*
607
+
608
+ Add [influxdb](https://github.com/influxdata/influxdb-ruby) to your Gemfile and set:
609
+
610
+ ```yml
611
+ data_sources:
612
+ my_source:
613
+ adapter: influxdb
614
+ url: http://user:password@hostname:8086/database
615
+ ```
616
+
617
+ Supports [InfluxQL](https://docs.influxdata.com/influxdb/v1.8/query_language/explore-data/)
577
618
 
578
619
  ### MongoDB
579
620
 
@@ -595,7 +636,9 @@ data_sources:
595
636
  url: mysql2://user:password@hostname:3306/database
596
637
  ```
597
638
 
598
- ### Neo4j [experimental]
639
+ ### Neo4j
640
+
641
+ *Experimental*
599
642
 
600
643
  Add [neo4j-core](https://github.com/neo4jrb/neo4j-core) to your Gemfile and set:
601
644
 
@@ -608,11 +651,17 @@ data_sources:
608
651
 
609
652
  ### Oracle
610
653
 
611
- Use [activerecord-oracle_enhanced-adapter](https://github.com/rsim/oracle-enhanced).
654
+ Add [activerecord-oracle_enhanced-adapter](https://github.com/rsim/oracle-enhanced) and [ruby-oci8](https://github.com/kubo/ruby-oci8) to your Gemfile and set:
655
+
656
+ ```yml
657
+ data_sources:
658
+ my_source:
659
+ url: oracle-enhanced://user:password@hostname:1521/database
660
+ ```
612
661
 
613
662
  ### PostgreSQL
614
663
 
615
- Add [pg](https://bitbucket.org/ged/ruby-pg/wiki/Home) to your Gemfile (if it’s not there) and set:
664
+ Add [pg](https://github.com/ged/ruby-pg) to your Gemfile (if it’s not there) and set:
616
665
 
617
666
  ```yml
618
667
  data_sources:
@@ -630,7 +679,9 @@ data_sources:
630
679
  url: presto://user@hostname:8080/catalog
631
680
  ```
632
681
 
633
- ### Salesforce [experimental]
682
+ ### Salesforce
683
+
684
+ *Experimental*
634
685
 
635
686
  Add [restforce](https://github.com/restforce/restforce) to your Gemfile and set:
636
687
 
@@ -653,6 +704,22 @@ SALESFORCE_API_VERSION="41.0"
653
704
 
654
705
  Supports [SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm)
655
706
 
707
+ ### Socrata Open Data API (SODA)
708
+
709
+ *Experimental*
710
+
711
+ Set:
712
+
713
+ ```yml
714
+ data_sources:
715
+ my_source:
716
+ adapter: soda
717
+ url: https://soda.demo.socrata.com/resource/4tka-6guv.json
718
+ app_token: ...
719
+ ```
720
+
721
+ Supports [SoQL](https://dev.socrata.com/docs/functions/)
722
+
656
723
  ### Snowflake
657
724
 
658
725
  First, install ODBC. For Homebrew, use:
@@ -671,7 +738,7 @@ For Heroku, use the [Apt buildpack](https://github.com/heroku/heroku-buildpack-a
671
738
 
672
739
  ```text
673
740
  unixodbc-dev
674
- https://sfc-repo.snowflakecomputing.com/odbc/linux/2.19.16/snowflake-odbc-2.19.16.x86_64.deb
741
+ https://sfc-repo.snowflakecomputing.com/odbc/linux/2.21.5/snowflake-odbc-2.21.5.x86_64.deb
675
742
  ```
676
743
 
677
744
  > This installs the driver at `/app/.apt/usr/lib/snowflake/odbc/lib/libSnowflake.so`
@@ -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
+ BLACKLISTED_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 blacklisted keys from both params and permitted keys for better sleep
102
+ def variable_params(resource)
103
+ permitted_keys = resource.variables - BLACKLISTED_KEYS.map(&:to_s)
104
+ params.except(*BLACKLISTED_KEYS).permit(*permitted_keys)
91
105
  end
92
106
  helper_method :variable_params
93
107
 
@@ -43,7 +43,7 @@ module Blazer
43
43
 
44
44
  def update
45
45
  if update_dashboard(@dashboard)
46
- redirect_to dashboard_path(@dashboard, variable_params)
46
+ redirect_to dashboard_path(@dashboard, variable_params(@dashboard))
47
47
  else
48
48
  render_errors @dashboard
49
49
  end
@@ -62,7 +62,7 @@ module Blazer
62
62
  Blazer.transform_statement.call(data_source, statement) if Blazer.transform_statement
63
63
  data_source.clear_cache(statement)
64
64
  end
65
- redirect_to dashboard_path(@dashboard, variable_params)
65
+ redirect_to dashboard_path(@dashboard, variable_params(@dashboard))
66
66
  end
67
67
 
68
68
  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
  }
@@ -14,7 +14,7 @@ module Blazer
14
14
  def blazer_format_value(key, value)
15
15
  if value.is_a?(Numeric) && !key.to_s.end_with?("id") && !key.to_s.start_with?("id")
16
16
  number_with_delimiter(value)
17
- elsif value =~ BLAZER_URL_REGEX
17
+ elsif value.is_a?(String) && value =~ BLAZER_URL_REGEX
18
18
  # see if image or link
19
19
  if Blazer.images && (key.include?("image") || BLAZER_IMAGE_EXT.include?(value.split(".").last.split("?").first.try(:downcase)))
20
20
  link_to value, target: "_blank" do
@@ -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 blacklist isn't 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
@@ -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,7 +33,7 @@
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>
@@ -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({
@@ -71,7 +71,7 @@
71
71
  }
72
72
 
73
73
  var prepareQuery = function (str) {
74
- return str.toLowerCase().replace(/\W+/g, "")
74
+ return str.toLowerCase()
75
75
  }
76
76
 
77
77
  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', 'ankane.ioo8nki0');
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++) {
@@ -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.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" %>
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,10 +18,12 @@ 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"
24
25
  require "blazer/adapters/salesforce_adapter"
26
+ require "blazer/adapters/soda_adapter"
25
27
  require "blazer/adapters/sql_adapter"
26
28
  require "blazer/adapters/snowflake_adapter"
27
29
 
@@ -118,7 +120,7 @@ module Blazer
118
120
  def self.extract_vars(statement)
119
121
  # strip commented out lines
120
122
  # and regex {1} or {1,2}
121
- 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
122
124
  end
123
125
 
124
126
  def self.run_checks(schedule: nil)
@@ -219,9 +221,11 @@ Blazer.register_adapter "cassandra", Blazer::Adapters::CassandraAdapter
219
221
  Blazer.register_adapter "drill", Blazer::Adapters::DrillAdapter
220
222
  Blazer.register_adapter "druid", Blazer::Adapters::DruidAdapter
221
223
  Blazer.register_adapter "elasticsearch", Blazer::Adapters::ElasticsearchAdapter
224
+ Blazer.register_adapter "influxdb", Blazer::Adapters::InfluxdbAdapter
222
225
  Blazer.register_adapter "neo4j", Blazer::Adapters::Neo4jAdapter
223
226
  Blazer.register_adapter "presto", Blazer::Adapters::PrestoAdapter
224
227
  Blazer.register_adapter "mongodb", Blazer::Adapters::MongodbAdapter
225
228
  Blazer.register_adapter "salesforce", Blazer::Adapters::SalesforceAdapter
229
+ Blazer.register_adapter "soda", Blazer::Adapters::SodaAdapter
226
230
  Blazer.register_adapter "sql", Blazer::Adapters::SqlAdapter
227
231
  Blazer.register_adapter "snowflake", Blazer::Adapters::SnowflakeAdapter
@@ -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
@@ -0,0 +1,96 @@
1
+ module Blazer
2
+ module Adapters
3
+ class SodaAdapter < BaseAdapter
4
+ def run_statement(statement, comment)
5
+ columns = []
6
+ rows = []
7
+ error = nil
8
+
9
+ # remove comments manually
10
+ statement = statement.gsub(/--.+/, "")
11
+ # only supports single line /* */ comments
12
+ # regex not perfect, but should be good enough
13
+ statement = statement.gsub(/\/\*.+\*\//, "")
14
+
15
+ # remove trailing semicolon
16
+ statement = statement.sub(/;\s*\z/, "")
17
+
18
+ # remove whitespace
19
+ statement = statement.squish
20
+
21
+ uri = URI(settings["url"])
22
+ uri.query = URI.encode_www_form("$query" => statement)
23
+
24
+ req = Net::HTTP::Get.new(uri)
25
+ req["X-App-Token"] = settings["app_token"] if settings["app_token"]
26
+
27
+ options = {
28
+ use_ssl: uri.scheme == "https",
29
+ open_timeout: 3,
30
+ read_timeout: 30
31
+ }
32
+
33
+ begin
34
+ # use Net::HTTP instead of soda-ruby for types and better error messages
35
+ res = Net::HTTP.start(uri.hostname, uri.port, options) do |http|
36
+ http.request(req)
37
+ end
38
+
39
+ if res.is_a?(Net::HTTPSuccess)
40
+ body = JSON.parse(res.body)
41
+
42
+ columns = JSON.parse(res["x-soda2-fields"])
43
+ column_types = columns.zip(JSON.parse(res["x-soda2-types"])).to_h
44
+
45
+ columns.reject! { |f| f.start_with?(":@") }
46
+ # rows can be missing some keys in JSON, so need to map by column
47
+ rows = body.map { |r| columns.map { |c| r[c] } }
48
+
49
+ columns.each_with_index do |column, i|
50
+ # nothing to do for boolean
51
+ case column_types[column]
52
+ when "number"
53
+ # check if likely an integer column
54
+ if rows.all? { |r| r[i].to_i == r[i].to_f }
55
+ rows.each do |row|
56
+ row[i] = row[i].to_i
57
+ end
58
+ else
59
+ rows.each do |row|
60
+ row[i] = row[i].to_f
61
+ end
62
+ end
63
+ when "floating_timestamp"
64
+ # check if likely a date column
65
+ if rows.all? { |r| r[i].end_with?("T00:00:00.000") }
66
+ rows.each do |row|
67
+ row[i] = Date.parse(row[i])
68
+ end
69
+ else
70
+ utc = ActiveSupport::TimeZone["Etc/UTC"]
71
+ rows.each do |row|
72
+ row[i] = utc.parse(row[i])
73
+ end
74
+ end
75
+ end
76
+ end
77
+ else
78
+ error = JSON.parse(res.body)["message"] rescue "Bad response: #{res.code}"
79
+ end
80
+ rescue => e
81
+ error = e.message
82
+ end
83
+
84
+ [columns, rows, error]
85
+ end
86
+
87
+ def preview_statement
88
+ "SELECT * LIMIT 10"
89
+ end
90
+
91
+ def tables
92
+ ["all"]
93
+ end
94
+ end
95
+ end
96
+ end
@@ -85,11 +85,26 @@ module Blazer
85
85
  @forecastable ||= Blazer.forecasting && column_types == ["time", "numeric"] && @rows.size >= 10
86
86
  end
87
87
 
88
+ # TODO cache it?
89
+ # don't want to put result data (even hashed version)
90
+ # into cache without developer opt-in
88
91
  def forecast
89
- # TODO cache it?
90
- # don't want to put result data (even hashed version)
91
- # into cache without developer opt-in
92
- forecast = Trend.forecast(Hash[@rows], count: 30)
92
+ count = (@rows.size * 0.25).round.clamp(30, 365)
93
+
94
+ case Blazer.forecasting
95
+ when "prophet"
96
+ require "prophet"
97
+ forecast = Prophet.forecast(@rows.to_h, count: count)
98
+ else
99
+ require "trend"
100
+ forecast = Trend.forecast(@rows.to_h, count: count)
101
+ end
102
+
103
+ # round integers
104
+ if @rows[0][1].is_a?(Integer)
105
+ forecast = forecast.map { |k, v| [k, v.round] }.to_h
106
+ end
107
+
93
108
  @rows.each do |row|
94
109
  row[2] = nil
95
110
  end
@@ -1,3 +1,3 @@
1
1
  module Blazer
2
- VERSION = "2.2.1"
2
+ VERSION = "2.2.6"
3
3
  end
@@ -67,7 +67,7 @@ check_schedules:
67
67
 
68
68
  # enable forecasting
69
69
  # note: with trend, time series are sent to https://trendapi.org
70
- # forecasting: trend
70
+ # forecasting: trend / prophet
71
71
 
72
72
  # enable map
73
73
  # mapbox_access_token: <%%= ENV["MAPBOX_ACCESS_TOKEN"] %>
@@ -19,7 +19,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration<%= migration_version
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
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blazer
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-09 00:00:00.000000000 Z
11
+ date: 2020-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -185,11 +185,13 @@ files:
185
185
  - lib/blazer/adapters/drill_adapter.rb
186
186
  - lib/blazer/adapters/druid_adapter.rb
187
187
  - lib/blazer/adapters/elasticsearch_adapter.rb
188
+ - lib/blazer/adapters/influxdb_adapter.rb
188
189
  - lib/blazer/adapters/mongodb_adapter.rb
189
190
  - lib/blazer/adapters/neo4j_adapter.rb
190
191
  - lib/blazer/adapters/presto_adapter.rb
191
192
  - lib/blazer/adapters/salesforce_adapter.rb
192
193
  - lib/blazer/adapters/snowflake_adapter.rb
194
+ - lib/blazer/adapters/soda_adapter.rb
193
195
  - lib/blazer/adapters/sql_adapter.rb
194
196
  - lib/blazer/data_source.rb
195
197
  - lib/blazer/detect_anomalies.R
@@ -221,7 +223,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
221
223
  - !ruby/object:Gem::Version
222
224
  version: '0'
223
225
  requirements: []
224
- rubygems_version: 3.0.3
226
+ rubygems_version: 3.1.2
225
227
  signing_key:
226
228
  specification_version: 4
227
229
  summary: Explore your data with SQL. Easily create charts and dashboards, and share