blazer 2.2.0 → 2.2.5
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 +4 -4
- data/CHANGELOG.md +70 -47
- data/README.md +76 -16
- data/app/assets/stylesheets/blazer/application.css +1 -1
- data/app/assets/stylesheets/blazer/{selectize.default.css → selectize.css} +0 -0
- data/app/controllers/blazer/base_controller.rb +16 -2
- data/app/controllers/blazer/dashboards_controller.rb +2 -2
- data/app/controllers/blazer/queries_controller.rb +7 -5
- data/app/helpers/blazer/base_helper.rb +1 -1
- data/app/mailers/blazer/slack_notifier.rb +3 -0
- data/app/models/blazer/dashboard.rb +4 -0
- 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 +3 -3
- data/app/views/blazer/queries/_form.html.erb +3 -3
- data/app/views/blazer/queries/home.html.erb +1 -1
- data/app/views/blazer/queries/run.html.erb +8 -6
- data/app/views/blazer/queries/show.html.erb +3 -3
- data/app/views/layouts/blazer/application.html.erb +2 -2
- data/lib/blazer.rb +3 -1
- data/lib/blazer/adapters/athena_adapter.rb +2 -1
- data/lib/blazer/adapters/soda_adapter.rb +96 -0
- data/lib/blazer/adapters/sql_adapter.rb +8 -1
- data/lib/blazer/engine.rb +15 -4
- data/lib/blazer/result.rb +38 -4
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/config.yml.tt +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bc246636d4907ea33d0194ed415aaa5669013ee3e71728ef024f89ac9860feb
|
4
|
+
data.tar.gz: 3c4d7cf6b528716d80578aea44ef733e8e30c040785d585b4abf183251b87e3e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66e86f97070e03626320557b119666898db9ffbf812edd6b4c502384d6ad1b1e4b428d9d832be83ba0eaa004188403db2f66cc7b6a76c70dd351c1861fd8d6b5
|
7
|
+
data.tar.gz: df1eeee72f609e1c4b5a19da492803bc86737ff26d3fce9ef86490cb54b3444baecd2ceaa4dc2fa5c1773d68462e28927142f67878ee6284de97a4cb7743cf63
|
data/CHANGELOG.md
CHANGED
@@ -1,21 +1,45 @@
|
|
1
|
-
## 2.2.
|
1
|
+
## 2.2.5 (2020-06-03)
|
2
|
+
|
3
|
+
- Updated maps to fix deprecation error
|
4
|
+
|
5
|
+
## 2.2.4 (2020-05-30)
|
6
|
+
|
7
|
+
- Fixed error with new queries
|
8
|
+
|
9
|
+
## 2.2.3 (2020-05-30)
|
10
|
+
|
11
|
+
- Improved query parameter handling
|
12
|
+
|
13
|
+
## 2.2.2 (2020-04-13)
|
14
|
+
|
15
|
+
- Added experimental support for the Socrata Open Data API (SODA)
|
16
|
+
- Added experimental Prophet forecasting
|
17
|
+
- Fixed query search for non-ASCII characters
|
18
|
+
|
19
|
+
## 2.2.1 (2019-10-08)
|
20
|
+
|
21
|
+
- Added support for Sprockets 4
|
22
|
+
- Improved Snowflake table preview
|
23
|
+
- Fixed bug with refresh link not showing
|
24
|
+
|
25
|
+
## 2.2.0 (2019-07-12)
|
2
26
|
|
3
27
|
- Added schema to table preview for Postgres and Redshift
|
4
28
|
- Fixed bug with Slack notifications not sending
|
5
29
|
- Dropped support for Rails 4.2
|
6
30
|
|
7
|
-
## 2.1.0
|
31
|
+
## 2.1.0 (2019-06-04)
|
8
32
|
|
9
33
|
- Require latest Chartkick to prevent possible XSS - see [#245](https://github.com/ankane/blazer/issues/245)
|
10
34
|
|
11
|
-
## 2.0.2
|
35
|
+
## 2.0.2 (2019-05-26)
|
12
36
|
|
13
37
|
- Added support for variable transformation for blind indexing
|
14
38
|
- Added experimental support for Neo4j
|
15
39
|
- Added experimental support for Salesforce
|
16
40
|
- Fixed JavaScript sorting for numbers with commas
|
17
41
|
|
18
|
-
## 2.0.1
|
42
|
+
## 2.0.1 (2019-01-07)
|
19
43
|
|
20
44
|
- Added favicon
|
21
45
|
- Added search for checks and schema
|
@@ -26,7 +50,7 @@
|
|
26
50
|
- Improved docs for new installs
|
27
51
|
- Fixed error with canceling queries
|
28
52
|
|
29
|
-
## 2.0.0
|
53
|
+
## 2.0.0 (2019-01-03)
|
30
54
|
|
31
55
|
- Added support for Slack
|
32
56
|
- Added `async` option
|
@@ -39,68 +63,67 @@ Breaking changes
|
|
39
63
|
|
40
64
|
- Dropped support for Rails < 4.2
|
41
65
|
|
42
|
-
## 1.9.0
|
66
|
+
## 1.9.0 (2018-06-17)
|
43
67
|
|
44
68
|
- Prompt developers to check custom `before_action`
|
45
69
|
- Better ordering on home page
|
46
70
|
- Added support for Snowflake
|
47
71
|
|
48
|
-
## 1.8.2
|
72
|
+
## 1.8.2 (2018-02-22)
|
49
73
|
|
50
74
|
- Added support for Cassandra
|
51
75
|
- Fixes for Druid
|
52
|
-
|
53
|
-
## 1.8.1
|
54
|
-
|
55
76
|
- Added support for Amazon Athena
|
56
77
|
- Added support for Druid
|
57
78
|
- Fixed query cancellation
|
58
79
|
|
59
|
-
|
80
|
+
There was no 1.8.1 release.
|
81
|
+
|
82
|
+
## 1.8.0 (2017-05-01)
|
60
83
|
|
61
84
|
- Added support for Rails 5.1
|
62
85
|
|
63
|
-
## 1.7.10
|
86
|
+
## 1.7.10 (2017-04-03)
|
64
87
|
|
65
88
|
- Added support for Google BigQuery
|
66
89
|
- Require `drill-sergeant` gem for Apache Drill
|
67
90
|
- Better handling of checks with variables
|
68
91
|
|
69
|
-
## 1.7.9
|
92
|
+
## 1.7.9 (2017-03-20)
|
70
93
|
|
71
94
|
- Added beta support for Apache Drill
|
72
95
|
- Added email validation for checks
|
73
96
|
- Updated Chart.js to 2.5.0
|
74
97
|
|
75
|
-
## 1.7.8
|
98
|
+
## 1.7.8 (2017-02-06)
|
76
99
|
|
77
100
|
- Added support for custom adapters
|
78
101
|
- Fixed bug with scatter charts on dashboards
|
79
102
|
- Fixed table preview for SQL Server
|
80
103
|
- Fixed issue when `default_url_options` set
|
81
104
|
|
82
|
-
## 1.7.7
|
105
|
+
## 1.7.7 (2016-12-17)
|
83
106
|
|
84
107
|
- Fixed preview error for MySQL
|
85
108
|
- Fixed error with timeouts for MySQL
|
86
109
|
|
87
|
-
## 1.7.6
|
110
|
+
## 1.7.6 (2016-12-13)
|
88
111
|
|
89
112
|
- Added scatter chart
|
90
113
|
- Fixed issue with false values showing up blank
|
91
114
|
- Fixed preview for table names with certain characters
|
92
115
|
|
93
|
-
## 1.7.5
|
116
|
+
## 1.7.5 (2016-11-22)
|
94
117
|
|
95
118
|
- Fixed issue with check emails sometimes failing for default Rails 5 ActiveJob adapter
|
96
119
|
- Fixed sorting for new dashboards
|
97
120
|
|
98
|
-
## 1.7.4
|
121
|
+
## 1.7.4 (2016-11-06)
|
99
122
|
|
100
123
|
- Removed extra dependencies added in 1.7.1
|
101
124
|
- Fixed `send_failing_checks` for default Rails 5 ActiveJob adapter
|
102
125
|
|
103
|
-
## 1.7.3
|
126
|
+
## 1.7.3 (2016-11-01)
|
104
127
|
|
105
128
|
- Fixed JavaScript errors
|
106
129
|
- Fixed query cancel error
|
@@ -108,20 +131,20 @@ Breaking changes
|
|
108
131
|
- Include sample data in email when bad data checks fail
|
109
132
|
- Fixed deprecation warnings
|
110
133
|
|
111
|
-
## 1.7.2
|
134
|
+
## 1.7.2 (2016-10-30)
|
112
135
|
|
113
136
|
- Cancel all queries on page nav
|
114
137
|
- Prevent Ace from taking over find command
|
115
138
|
- Added ability to use hashes for smart columns
|
116
139
|
- Added ability to inherit smart variables and columns from other data sources
|
117
140
|
|
118
|
-
## 1.7.1
|
141
|
+
## 1.7.1 (2016-10-29)
|
119
142
|
|
120
143
|
- Do not fork when enter key pressed
|
121
144
|
- Use custom version of Chart.js to fix label overlap
|
122
145
|
- Improved performance of home page
|
123
146
|
|
124
|
-
## 1.7.0
|
147
|
+
## 1.7.0 (2016-09-07)
|
125
148
|
|
126
149
|
- Added ability to cancel queries on backend for Postgres and Redshift
|
127
150
|
- Only run 3 queries at a time on dashboards
|
@@ -129,65 +152,65 @@ Breaking changes
|
|
129
152
|
- Attempt to reconnect when connection issues
|
130
153
|
- Fixed issues with caching
|
131
154
|
|
132
|
-
## 1.6.2
|
155
|
+
## 1.6.2 (2016-08-11)
|
133
156
|
|
134
157
|
- Added basic query permissions
|
135
158
|
- Added ability to use arrays and hashes for smart variables
|
136
159
|
- Added cancel button for queries
|
137
160
|
- Added `lat` and `lng` as map keys
|
138
161
|
|
139
|
-
## 1.6.1
|
162
|
+
## 1.6.1 (2016-07-30)
|
140
163
|
|
141
164
|
- Added support for Presto [beta]
|
142
165
|
- Added support for Elasticsearch timeouts
|
143
166
|
- Fixed error in Rails 5
|
144
167
|
|
145
|
-
## 1.6.0
|
168
|
+
## 1.6.0 (2016-07-28)
|
146
169
|
|
147
170
|
- Added support for MongoDB [beta]
|
148
171
|
- Added support for Elasticsearch [beta]
|
149
172
|
- Fixed deprecation warning in Rails 5
|
150
173
|
|
151
|
-
## 1.5.1
|
174
|
+
## 1.5.1 (2016-07-24)
|
152
175
|
|
153
176
|
- Added anomaly detection for data less than 2 weeks
|
154
177
|
- Added autolinking urls
|
155
178
|
- Added support for images
|
156
179
|
|
157
|
-
## 1.5.0
|
180
|
+
## 1.5.0 (2016-06-29)
|
158
181
|
|
159
182
|
- Added new bar chart format
|
160
183
|
- Added anomaly detection checks
|
161
184
|
- Added `async` option for polling
|
162
185
|
|
163
|
-
## 1.4.0
|
186
|
+
## 1.4.0 (2016-06-09)
|
164
187
|
|
165
188
|
- Added `slow` cache mode
|
166
189
|
- Fixed `BLAZER_DATABASE_URL required` error
|
167
190
|
- Fixed issue with duplicate column names
|
168
191
|
|
169
|
-
## 1.3.5
|
192
|
+
## 1.3.5 (2016-05-11)
|
170
193
|
|
171
194
|
- Fixed error with checks
|
172
195
|
|
173
|
-
## 1.3.4
|
196
|
+
## 1.3.4 (2016-05-11)
|
174
197
|
|
175
198
|
- Fixed issue with missing queries
|
176
199
|
|
177
|
-
## 1.3.3
|
200
|
+
## 1.3.3 (2016-05-08)
|
178
201
|
|
179
202
|
- Fixed error with Rails 4.1 and below
|
180
203
|
|
181
|
-
## 1.3.2
|
204
|
+
## 1.3.2 (2016-05-07)
|
182
205
|
|
183
206
|
- Added support for Rails 5
|
184
207
|
- Attempt to reconnect for checks
|
185
208
|
|
186
|
-
## 1.3.1
|
209
|
+
## 1.3.1 (2016-05-06)
|
187
210
|
|
188
211
|
- Fixed migration error
|
189
212
|
|
190
|
-
## 1.3.0
|
213
|
+
## 1.3.0 (2016-05-06)
|
191
214
|
|
192
215
|
- Added schedule for checks
|
193
216
|
- Switched to Chart.js for charts
|
@@ -196,11 +219,11 @@ Breaking changes
|
|
196
219
|
- Raise error when timeout not supported
|
197
220
|
- Added creator to dashboards and checks
|
198
221
|
|
199
|
-
## 1.2.1
|
222
|
+
## 1.2.1 (2016-04-26)
|
200
223
|
|
201
224
|
- Fixed checks
|
202
225
|
|
203
|
-
## 1.2.0
|
226
|
+
## 1.2.0 (2016-03-22)
|
204
227
|
|
205
228
|
- Added non-editable queries
|
206
229
|
- Added variable defaults
|
@@ -209,7 +232,7 @@ Breaking changes
|
|
209
232
|
- Hide variables from commented out lines
|
210
233
|
- Fixed regex as variable names
|
211
234
|
|
212
|
-
## 1.1.1
|
235
|
+
## 1.1.1 (2016-03-06)
|
213
236
|
|
214
237
|
- Added `before_action` option
|
215
238
|
- Added invert option for checks
|
@@ -218,7 +241,7 @@ Breaking changes
|
|
218
241
|
- Fixed request URI too large
|
219
242
|
- Prevent accidental backspace nav on query page
|
220
243
|
|
221
|
-
## 1.1.0
|
244
|
+
## 1.1.0 (2015-12-27)
|
222
245
|
|
223
246
|
- Replaced pie charts with column charts
|
224
247
|
- Fixed error with datepicker
|
@@ -226,55 +249,55 @@ Breaking changes
|
|
226
249
|
- Added a notice when editing a query that is part of a dashboard
|
227
250
|
- Added refresh for dashboards
|
228
251
|
|
229
|
-
## 1.0.4
|
252
|
+
## 1.0.4 (2015-11-04)
|
230
253
|
|
231
254
|
- Added recently viewed queries and dashboards to home page
|
232
255
|
- Fixed refresh when transform statement is used
|
233
256
|
- Fixed error when no user model
|
234
257
|
|
235
|
-
## 1.0.3
|
258
|
+
## 1.0.3 (2015-10-18)
|
236
259
|
|
237
260
|
- Added maps
|
238
261
|
- Added support for Rails 4.0
|
239
262
|
|
240
|
-
## 1.0.2
|
263
|
+
## 1.0.2 (2015-10-11)
|
241
264
|
|
242
265
|
- Fixed error when installing
|
243
266
|
- Added `schemas` option
|
244
267
|
|
245
|
-
## 1.0.1
|
268
|
+
## 1.0.1 (2015-10-08)
|
246
269
|
|
247
270
|
- Added comments to queries
|
248
271
|
- Added `cache` option
|
249
272
|
- Added `user_method` option
|
250
273
|
- Added `use_transaction` option
|
251
274
|
|
252
|
-
## 1.0.0
|
275
|
+
## 1.0.0 (2015-10-04)
|
253
276
|
|
254
277
|
- Added support for multiple data sources
|
255
278
|
- Added dashboards
|
256
279
|
- Added checks
|
257
280
|
- Added support for Redshift
|
258
281
|
|
259
|
-
## 0.0.8
|
282
|
+
## 0.0.8 (2015-09-05)
|
260
283
|
|
261
284
|
- Easier to edit queries with variables
|
262
285
|
- Dynamically expand editor height as needed
|
263
286
|
- No need for spaces in search
|
264
287
|
|
265
|
-
## 0.0.7
|
288
|
+
## 0.0.7 (2015-07-23)
|
266
289
|
|
267
290
|
- Fixed error when no `User` class
|
268
291
|
- Fixed forking a query with variables
|
269
292
|
- Set time zone after Rails initializes
|
270
293
|
|
271
|
-
## 0.0.6
|
294
|
+
## 0.0.6 (2015-06-18)
|
272
295
|
|
273
296
|
- Added fork button
|
274
297
|
- Fixed trending
|
275
298
|
- Fixed time zones for date select
|
276
299
|
|
277
|
-
## 0.0.5
|
300
|
+
## 0.0.5 (2015-01-31)
|
278
301
|
|
279
302
|
- Added support for Rails 4.2
|
280
303
|
- 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/
|
7
|
+
[![Screenshot](https://blazer.dokkuapp.com/assets/blazer-90fb6ce8ad25614c6f9c3b4619cbfbd66fa3d6567dac34d83df540f6688665f1.png)](https://blazer.dokkuapp.com)
|
8
8
|
|
9
|
-
Blazer
|
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,32 @@ 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
|
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
|
+
**Note:** Prophet only supports daily forecasts right now.
|
438
|
+
|
439
|
+
Add [prophet](https://github.com/ankane/prophet) to your Gemfile:
|
440
|
+
|
441
|
+
```ruby
|
442
|
+
gem 'prophet-rb'
|
443
|
+
```
|
444
|
+
|
445
|
+
And add to `config/blazer.yml`:
|
446
|
+
|
447
|
+
```yml
|
448
|
+
forecasting: prophet
|
449
|
+
```
|
450
|
+
|
451
|
+
### Trend
|
452
|
+
|
453
|
+
[Trend](https://trendapi.org/) uses an external service.
|
454
|
+
|
433
455
|
Add [trend](https://github.com/ankane/trend) to your Gemfile:
|
434
456
|
|
435
457
|
```ruby
|
@@ -442,8 +464,6 @@ And add to `config/blazer.yml`:
|
|
442
464
|
forecasting: trend
|
443
465
|
```
|
444
466
|
|
445
|
-
A forecast link will appear for queries that return 2 columns with types timestamp and numeric.
|
446
|
-
|
447
467
|
## Data Sources
|
448
468
|
|
449
469
|
Blazer supports multiple data sources :tada:
|
@@ -475,11 +495,12 @@ data_sources:
|
|
475
495
|
- [IBM DB2 and Informix](#ibm-db2-and-informix)
|
476
496
|
- [MongoDB](#mongodb-1)
|
477
497
|
- [MySQL](#mysql-1)
|
478
|
-
- [Neo4j](#neo4j
|
498
|
+
- [Neo4j](#neo4j)
|
479
499
|
- [Oracle](#oracle)
|
480
500
|
- [PostgreSQL](#postgresql-1)
|
481
501
|
- [Presto](#presto)
|
482
|
-
- [Salesforce](#salesforce
|
502
|
+
- [Salesforce](#salesforce)
|
503
|
+
- [Socrata Open Data API (SODA)](#socrata-open-data-api-soda)
|
483
504
|
- [Snowflake](#snowflake)
|
484
505
|
- [SQLite](#sqlite)
|
485
506
|
- [SQL Server](#sql-server)
|
@@ -539,9 +560,7 @@ data_sources:
|
|
539
560
|
|
540
561
|
### Druid
|
541
562
|
|
542
|
-
|
543
|
-
|
544
|
-
Set:
|
563
|
+
Enable [SQL support](http://druid.io/docs/latest/querying/sql.html#configuration) on the broker and set:
|
545
564
|
|
546
565
|
```yml
|
547
566
|
data_sources:
|
@@ -597,7 +616,9 @@ data_sources:
|
|
597
616
|
url: mysql2://user:password@hostname:3306/database
|
598
617
|
```
|
599
618
|
|
600
|
-
### Neo4j
|
619
|
+
### Neo4j
|
620
|
+
|
621
|
+
*Experimental*
|
601
622
|
|
602
623
|
Add [neo4j-core](https://github.com/neo4jrb/neo4j-core) to your Gemfile and set:
|
603
624
|
|
@@ -632,7 +653,9 @@ data_sources:
|
|
632
653
|
url: presto://user@hostname:8080/catalog
|
633
654
|
```
|
634
655
|
|
635
|
-
### Salesforce
|
656
|
+
### Salesforce
|
657
|
+
|
658
|
+
*Experimental*
|
636
659
|
|
637
660
|
Add [restforce](https://github.com/restforce/restforce) to your Gemfile and set:
|
638
661
|
|
@@ -655,15 +678,52 @@ SALESFORCE_API_VERSION="41.0"
|
|
655
678
|
|
656
679
|
Supports [SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm)
|
657
680
|
|
681
|
+
### Socrata Open Data API (SODA)
|
682
|
+
|
683
|
+
*Experimental*
|
684
|
+
|
685
|
+
Set:
|
686
|
+
|
687
|
+
```yml
|
688
|
+
data_sources:
|
689
|
+
my_source:
|
690
|
+
adapter: soda
|
691
|
+
url: https://soda.demo.socrata.com/resource/4tka-6guv.json
|
692
|
+
app_token: ...
|
693
|
+
```
|
694
|
+
|
695
|
+
Supports [SoQL](https://dev.socrata.com/docs/functions/)
|
696
|
+
|
658
697
|
### Snowflake
|
659
698
|
|
660
|
-
First, install
|
699
|
+
First, install ODBC. For Homebrew, use:
|
700
|
+
|
701
|
+
```sh
|
702
|
+
brew install unixodbc
|
703
|
+
```
|
704
|
+
|
705
|
+
For Ubuntu, use:
|
706
|
+
|
707
|
+
```sh
|
708
|
+
sudo apt-get install unixodbc-dev
|
709
|
+
```
|
710
|
+
|
711
|
+
For Heroku, use the [Apt buildpack](https://github.com/heroku/heroku-buildpack-apt) and create an `Aptfile` with:
|
712
|
+
|
713
|
+
```text
|
714
|
+
unixodbc-dev
|
715
|
+
https://sfc-repo.snowflakecomputing.com/odbc/linux/2.19.16/snowflake-odbc-2.19.16.x86_64.deb
|
716
|
+
```
|
717
|
+
|
718
|
+
> This installs the driver at `/app/.apt/usr/lib/snowflake/odbc/lib/libSnowflake.so`
|
719
|
+
|
720
|
+
Then, download the [Snowflake ODBC driver](https://docs.snowflake.net/manuals/user-guide/odbc-download.html). Add [odbc_adapter](https://github.com/localytics/odbc_adapter) to your Gemfile and set:
|
661
721
|
|
662
722
|
```yml
|
663
723
|
data_sources:
|
664
724
|
my_source:
|
665
725
|
adapter: snowflake
|
666
|
-
|
726
|
+
conn_str: Driver=/path/to/libSnowflake.so;uid=user;pwd=password;server=host.snowflakecomputing.com
|
667
727
|
```
|
668
728
|
|
669
729
|
### SQLite
|
@@ -719,8 +779,8 @@ Blazer supports a basic permissions model.
|
|
719
779
|
|
720
780
|
Have team members who want to learn SQL? Here are a few great, free resources.
|
721
781
|
|
722
|
-
- [
|
723
|
-
- [
|
782
|
+
- [The Data School](https://dataschool.com/learn-sql/)
|
783
|
+
- [SQLBolt](https://sqlbolt.com/)
|
724
784
|
|
725
785
|
## Useful Tools
|
726
786
|
|
File without changes
|
@@ -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
|
+
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
|
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
|
}
|
@@ -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
|
@@ -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">
|
@@ -51,7 +51,7 @@
|
|
51
51
|
<% words << pluralize(dashboards_count, "dashboard") if dashboards_count > 0 %>
|
52
52
|
<% words << pluralize(checks_count, "check") if checks_count > 0 %>
|
53
53
|
<% if words.any? %>
|
54
|
-
<div class="alert alert-info"
|
54
|
+
<div class="alert alert-info">
|
55
55
|
Part of <%= words.to_sentence %>. Be careful when editing.
|
56
56
|
</div>
|
57
57
|
<% end %>
|
@@ -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({
|
@@ -12,15 +12,15 @@
|
|
12
12
|
<p class="text-muted" style="float: right;">
|
13
13
|
<% if @cached_at %>
|
14
14
|
Cached <%= time_ago_in_words(@cached_at, include_seconds: true) %> ago
|
15
|
-
<% elsif
|
15
|
+
<% elsif params[:query_id] %>
|
16
16
|
Cached just now
|
17
17
|
<% if @data_source.cache_mode == "slow" %>
|
18
18
|
(over <%= "%g" % @data_source.cache_slow_threshold %>s)
|
19
19
|
<% end %>
|
20
20
|
<% end %>
|
21
21
|
|
22
|
-
<% if @query &&
|
23
|
-
<%= link_to "Refresh", refresh_query_path(@query, variable_params), method: :post %>
|
22
|
+
<% if @query && params[:query_id] %>
|
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 %>
|
@@ -67,7 +67,8 @@
|
|
67
67
|
<% series_library = {} %>
|
68
68
|
<% target_index = @columns.index { |k| k.downcase == "target" } %>
|
69
69
|
<% if target_index %>
|
70
|
-
<%
|
70
|
+
<% color = "#109618" %>
|
71
|
+
<% series_library[target_index - 1] = {pointStyle: "line", hitRadius: 5, borderColor: color, pointBackgroundColor: color, backgroundColor: color, pointHoverBackgroundColor: color} %>
|
71
72
|
<% end %>
|
72
73
|
<% if @forecast %>
|
73
74
|
<% color = "#54a3ee" %>
|
@@ -79,7 +80,8 @@
|
|
79
80
|
<%= blazer_js_var "mapboxAccessToken", Blazer.mapbox_access_token %>
|
80
81
|
<%= blazer_js_var "markers", @markers %>
|
81
82
|
L.mapbox.accessToken = mapboxAccessToken;
|
82
|
-
var map = L.mapbox.map('map'
|
83
|
+
var map = L.mapbox.map('map')
|
84
|
+
.addLayer(L.mapbox.styleLayer('mapbox://styles/mapbox/streets-v11'));
|
83
85
|
var featureLayer = L.mapbox.featureLayer().addTo(map);
|
84
86
|
var geojson = [];
|
85
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,7 @@
|
|
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
|
+
<%= blazer_js_var "data", variable_params(@query).merge(statement: @statement, query_id: @query.id, data_source: @query.data_source) %>
|
60
60
|
|
61
61
|
runQuery(data, showRun, showError)
|
62
62
|
</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
@@ -22,6 +22,7 @@ require "blazer/adapters/mongodb_adapter"
|
|
22
22
|
require "blazer/adapters/neo4j_adapter"
|
23
23
|
require "blazer/adapters/presto_adapter"
|
24
24
|
require "blazer/adapters/salesforce_adapter"
|
25
|
+
require "blazer/adapters/soda_adapter"
|
25
26
|
require "blazer/adapters/sql_adapter"
|
26
27
|
require "blazer/adapters/snowflake_adapter"
|
27
28
|
|
@@ -118,7 +119,7 @@ module Blazer
|
|
118
119
|
def self.extract_vars(statement)
|
119
120
|
# strip commented out lines
|
120
121
|
# 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
|
122
|
+
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
123
|
end
|
123
124
|
|
124
125
|
def self.run_checks(schedule: nil)
|
@@ -223,5 +224,6 @@ Blazer.register_adapter "neo4j", Blazer::Adapters::Neo4jAdapter
|
|
223
224
|
Blazer.register_adapter "presto", Blazer::Adapters::PrestoAdapter
|
224
225
|
Blazer.register_adapter "mongodb", Blazer::Adapters::MongodbAdapter
|
225
226
|
Blazer.register_adapter "salesforce", Blazer::Adapters::SalesforceAdapter
|
227
|
+
Blazer.register_adapter "soda", Blazer::Adapters::SodaAdapter
|
226
228
|
Blazer.register_adapter "sql", Blazer::Adapters::SqlAdapter
|
227
229
|
Blazer.register_adapter "snowflake", Blazer::Adapters::SnowflakeAdapter
|
@@ -32,7 +32,7 @@ module Blazer
|
|
32
32
|
query_execution_id: query_execution_id
|
33
33
|
)
|
34
34
|
rescue Aws::Athena::Errors::InvalidRequestException => e
|
35
|
-
|
35
|
+
unless e.message.start_with?("Query has not yet finished.")
|
36
36
|
raise e
|
37
37
|
end
|
38
38
|
if Time.now < stop_at
|
@@ -56,6 +56,7 @@ module Blazer
|
|
56
56
|
utc = ActiveSupport::TimeZone['Etc/UTC']
|
57
57
|
|
58
58
|
rows = untyped_rows[1..-1] || []
|
59
|
+
rows = untyped_rows[0..-1] unless column_info.present?
|
59
60
|
column_types.each_with_index do |ct, i|
|
60
61
|
# TODO more column_types
|
61
62
|
case ct
|
@@ -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
|
@@ -42,7 +42,7 @@ module Blazer
|
|
42
42
|
def tables
|
43
43
|
sql = add_schemas("SELECT table_schema, table_name FROM information_schema.tables")
|
44
44
|
result = data_source.run_statement(sql, refresh_cache: true)
|
45
|
-
if postgresql? || redshift?
|
45
|
+
if postgresql? || redshift? || snowflake?
|
46
46
|
result.rows.sort_by { |r| [r[0] == default_schema ? "" : r[0], r[1]] }.map do |row|
|
47
47
|
table =
|
48
48
|
if row[0] == default_schema
|
@@ -51,6 +51,8 @@ module Blazer
|
|
51
51
|
"#{row[0]}.#{row[1]}"
|
52
52
|
end
|
53
53
|
|
54
|
+
table = table.downcase if snowflake?
|
55
|
+
|
54
56
|
{
|
55
57
|
table: table,
|
56
58
|
value: connection_model.connection.quote_table_name(table)
|
@@ -148,6 +150,10 @@ module Blazer
|
|
148
150
|
["SQLServer", "tinytds", "mssql"].include?(adapter_name)
|
149
151
|
end
|
150
152
|
|
153
|
+
def snowflake?
|
154
|
+
data_source.adapter == "snowflake"
|
155
|
+
end
|
156
|
+
|
151
157
|
def adapter_name
|
152
158
|
# prevent bad data source from taking down queries/new
|
153
159
|
connection_model.connection.adapter_name rescue nil
|
@@ -172,6 +178,7 @@ module Blazer
|
|
172
178
|
else
|
173
179
|
where = "table_schema NOT IN (?)"
|
174
180
|
schemas = ["information_schema"]
|
181
|
+
schemas.map!(&:upcase) if snowflake?
|
175
182
|
schemas << "pg_catalog" if postgresql? || redshift?
|
176
183
|
end
|
177
184
|
connection_model.send(:sanitize_sql_array, ["#{query} WHERE #{where}", schemas])
|
data/lib/blazer/engine.rb
CHANGED
@@ -3,10 +3,21 @@ module Blazer
|
|
3
3
|
isolate_namespace Blazer
|
4
4
|
|
5
5
|
initializer "blazer" do |app|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
if defined?(Sprockets) && Sprockets::VERSION >= "4"
|
7
|
+
app.config.assets.precompile << "blazer/application.js"
|
8
|
+
app.config.assets.precompile << "blazer/application.css"
|
9
|
+
app.config.assets.precompile << "blazer/glyphicons-halflings-regular.eot"
|
10
|
+
app.config.assets.precompile << "blazer/glyphicons-halflings-regular.svg"
|
11
|
+
app.config.assets.precompile << "blazer/glyphicons-halflings-regular.ttf"
|
12
|
+
app.config.assets.precompile << "blazer/glyphicons-halflings-regular.woff"
|
13
|
+
app.config.assets.precompile << "blazer/glyphicons-halflings-regular.woff2"
|
14
|
+
app.config.assets.precompile << "blazer/favicon.png"
|
15
|
+
else
|
16
|
+
# use a proc instead of a string
|
17
|
+
app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/application\.(js|css)\z/ }
|
18
|
+
app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/.+\.(eot|svg|ttf|woff|woff2)\z/ }
|
19
|
+
app.config.assets.precompile << proc { |path| path == "blazer/favicon.png" }
|
20
|
+
end
|
10
21
|
|
11
22
|
Blazer.time_zone ||= Blazer.settings["time_zone"] || Time.zone
|
12
23
|
Blazer.audit = Blazer.settings.key?("audit") ? Blazer.settings["audit"] : true
|
data/lib/blazer/result.rb
CHANGED
@@ -85,11 +85,45 @@ 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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
92
|
+
count = (@rows.size * 0.25).round.clamp(30, 365)
|
93
|
+
|
94
|
+
case Blazer.forecasting
|
95
|
+
when "prophet"
|
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
|
116
|
+
else
|
117
|
+
require "trend"
|
118
|
+
|
119
|
+
forecast = Trend.forecast(Hash[@rows], count: count)
|
120
|
+
end
|
121
|
+
|
122
|
+
# round integers
|
123
|
+
if @rows[0][1].is_a?(Integer)
|
124
|
+
forecast = forecast.map { |k, v| [k, v.round] }.to_h
|
125
|
+
end
|
126
|
+
|
93
127
|
@rows.each do |row|
|
94
128
|
row[2] = nil
|
95
129
|
end
|
data/lib/blazer/version.rb
CHANGED
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.
|
4
|
+
version: 2.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -140,7 +140,7 @@ files:
|
|
140
140
|
- app/assets/stylesheets/blazer/bootstrap.css.erb
|
141
141
|
- app/assets/stylesheets/blazer/daterangepicker.css
|
142
142
|
- app/assets/stylesheets/blazer/github.css
|
143
|
-
- app/assets/stylesheets/blazer/selectize.
|
143
|
+
- app/assets/stylesheets/blazer/selectize.css
|
144
144
|
- app/controllers/blazer/base_controller.rb
|
145
145
|
- app/controllers/blazer/checks_controller.rb
|
146
146
|
- app/controllers/blazer/dashboards_controller.rb
|
@@ -190,6 +190,7 @@ files:
|
|
190
190
|
- lib/blazer/adapters/presto_adapter.rb
|
191
191
|
- lib/blazer/adapters/salesforce_adapter.rb
|
192
192
|
- lib/blazer/adapters/snowflake_adapter.rb
|
193
|
+
- lib/blazer/adapters/soda_adapter.rb
|
193
194
|
- lib/blazer/adapters/sql_adapter.rb
|
194
195
|
- lib/blazer/data_source.rb
|
195
196
|
- lib/blazer/detect_anomalies.R
|
@@ -221,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
221
222
|
- !ruby/object:Gem::Version
|
222
223
|
version: '0'
|
223
224
|
requirements: []
|
224
|
-
rubygems_version: 3.
|
225
|
+
rubygems_version: 3.1.2
|
225
226
|
signing_key:
|
226
227
|
specification_version: 4
|
227
228
|
summary: Explore your data with SQL. Easily create charts and dashboards, and share
|