finery 3.0.0

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 (153) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +426 -0
  3. data/CONTRIBUTING.md +49 -0
  4. data/LICENSE.txt +25 -0
  5. data/README.md +1144 -0
  6. data/app/assets/fonts/blazer/glyphicons-halflings-regular.eot +0 -0
  7. data/app/assets/fonts/blazer/glyphicons-halflings-regular.svg +288 -0
  8. data/app/assets/fonts/blazer/glyphicons-halflings-regular.ttf +0 -0
  9. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff +0 -0
  10. data/app/assets/fonts/blazer/glyphicons-halflings-regular.woff2 +0 -0
  11. data/app/assets/images/blazer/favicon.png +0 -0
  12. data/app/assets/javascripts/blazer/Sortable.js +3709 -0
  13. data/app/assets/javascripts/blazer/ace/ace.js +19630 -0
  14. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1981 -0
  15. data/app/assets/javascripts/blazer/ace/mode-sql.js +215 -0
  16. data/app/assets/javascripts/blazer/ace/snippets/sql.js +16 -0
  17. data/app/assets/javascripts/blazer/ace/snippets/text.js +9 -0
  18. data/app/assets/javascripts/blazer/ace/theme-twilight.js +18 -0
  19. data/app/assets/javascripts/blazer/ace.js +6 -0
  20. data/app/assets/javascripts/blazer/application.js +87 -0
  21. data/app/assets/javascripts/blazer/bootstrap.js +2580 -0
  22. data/app/assets/javascripts/blazer/chart.umd.js +13 -0
  23. data/app/assets/javascripts/blazer/chartjs-adapter-date-fns.bundle.js +6322 -0
  24. data/app/assets/javascripts/blazer/chartjs-plugin-annotation.min.js +7 -0
  25. data/app/assets/javascripts/blazer/chartkick.js +2570 -0
  26. data/app/assets/javascripts/blazer/daterangepicker.js +1578 -0
  27. data/app/assets/javascripts/blazer/fuzzysearch.js +24 -0
  28. data/app/assets/javascripts/blazer/highlight.min.js +466 -0
  29. data/app/assets/javascripts/blazer/jquery.js +10872 -0
  30. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +325 -0
  31. data/app/assets/javascripts/blazer/mapkick.bundle.js +1029 -0
  32. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +1548 -0
  33. data/app/assets/javascripts/blazer/moment.js +5685 -0
  34. data/app/assets/javascripts/blazer/queries.js +130 -0
  35. data/app/assets/javascripts/blazer/rails-ujs.js +746 -0
  36. data/app/assets/javascripts/blazer/routes.js +26 -0
  37. data/app/assets/javascripts/blazer/selectize.js +3891 -0
  38. data/app/assets/javascripts/blazer/stupidtable-custom-settings.js +13 -0
  39. data/app/assets/javascripts/blazer/stupidtable.js +281 -0
  40. data/app/assets/javascripts/blazer/vue.global.prod.js +1 -0
  41. data/app/assets/stylesheets/blazer/application.css +243 -0
  42. data/app/assets/stylesheets/blazer/bootstrap-propshaft.css +10 -0
  43. data/app/assets/stylesheets/blazer/bootstrap-sprockets.css.erb +10 -0
  44. data/app/assets/stylesheets/blazer/bootstrap.css +6828 -0
  45. data/app/assets/stylesheets/blazer/daterangepicker.css +410 -0
  46. data/app/assets/stylesheets/blazer/github.css +125 -0
  47. data/app/assets/stylesheets/blazer/selectize.css +403 -0
  48. data/app/controllers/blazer/base_controller.rb +133 -0
  49. data/app/controllers/blazer/checks_controller.rb +56 -0
  50. data/app/controllers/blazer/dashboards_controller.rb +99 -0
  51. data/app/controllers/blazer/queries_controller.rb +468 -0
  52. data/app/controllers/blazer/uploads_controller.rb +147 -0
  53. data/app/helpers/blazer/base_helper.rb +83 -0
  54. data/app/models/blazer/audit.rb +6 -0
  55. data/app/models/blazer/check.rb +104 -0
  56. data/app/models/blazer/connection.rb +5 -0
  57. data/app/models/blazer/dashboard.rb +17 -0
  58. data/app/models/blazer/dashboard_query.rb +9 -0
  59. data/app/models/blazer/query.rb +42 -0
  60. data/app/models/blazer/record.rb +5 -0
  61. data/app/models/blazer/upload.rb +11 -0
  62. data/app/models/blazer/uploads_connection.rb +7 -0
  63. data/app/views/blazer/_nav.html.erb +18 -0
  64. data/app/views/blazer/_variables.html.erb +127 -0
  65. data/app/views/blazer/check_mailer/failing_checks.html.erb +7 -0
  66. data/app/views/blazer/check_mailer/state_change.html.erb +48 -0
  67. data/app/views/blazer/checks/_form.html.erb +79 -0
  68. data/app/views/blazer/checks/edit.html.erb +3 -0
  69. data/app/views/blazer/checks/index.html.erb +72 -0
  70. data/app/views/blazer/checks/new.html.erb +3 -0
  71. data/app/views/blazer/dashboards/_form.html.erb +82 -0
  72. data/app/views/blazer/dashboards/edit.html.erb +3 -0
  73. data/app/views/blazer/dashboards/new.html.erb +3 -0
  74. data/app/views/blazer/dashboards/show.html.erb +53 -0
  75. data/app/views/blazer/queries/_caching.html.erb +16 -0
  76. data/app/views/blazer/queries/_cohorts.html.erb +48 -0
  77. data/app/views/blazer/queries/_form.html.erb +255 -0
  78. data/app/views/blazer/queries/docs.html.erb +147 -0
  79. data/app/views/blazer/queries/edit.html.erb +2 -0
  80. data/app/views/blazer/queries/home.html.erb +169 -0
  81. data/app/views/blazer/queries/new.html.erb +2 -0
  82. data/app/views/blazer/queries/run.html.erb +188 -0
  83. data/app/views/blazer/queries/schema.html.erb +55 -0
  84. data/app/views/blazer/queries/show.html.erb +72 -0
  85. data/app/views/blazer/uploads/_form.html.erb +27 -0
  86. data/app/views/blazer/uploads/edit.html.erb +3 -0
  87. data/app/views/blazer/uploads/index.html.erb +55 -0
  88. data/app/views/blazer/uploads/new.html.erb +3 -0
  89. data/app/views/layouts/blazer/application.html.erb +25 -0
  90. data/config/routes.rb +25 -0
  91. data/lib/blazer/adapters/athena_adapter.rb +182 -0
  92. data/lib/blazer/adapters/base_adapter.rb +76 -0
  93. data/lib/blazer/adapters/bigquery_adapter.rb +79 -0
  94. data/lib/blazer/adapters/cassandra_adapter.rb +70 -0
  95. data/lib/blazer/adapters/clickhouse_adapter.rb +84 -0
  96. data/lib/blazer/adapters/drill_adapter.rb +38 -0
  97. data/lib/blazer/adapters/druid_adapter.rb +102 -0
  98. data/lib/blazer/adapters/elasticsearch_adapter.rb +61 -0
  99. data/lib/blazer/adapters/hive_adapter.rb +55 -0
  100. data/lib/blazer/adapters/ignite_adapter.rb +64 -0
  101. data/lib/blazer/adapters/influxdb_adapter.rb +57 -0
  102. data/lib/blazer/adapters/neo4j_adapter.rb +62 -0
  103. data/lib/blazer/adapters/opensearch_adapter.rb +52 -0
  104. data/lib/blazer/adapters/presto_adapter.rb +54 -0
  105. data/lib/blazer/adapters/salesforce_adapter.rb +50 -0
  106. data/lib/blazer/adapters/snowflake_adapter.rb +82 -0
  107. data/lib/blazer/adapters/soda_adapter.rb +105 -0
  108. data/lib/blazer/adapters/spark_adapter.rb +14 -0
  109. data/lib/blazer/adapters/sql_adapter.rb +324 -0
  110. data/lib/blazer/adapters.rb +18 -0
  111. data/lib/blazer/annotations.rb +47 -0
  112. data/lib/blazer/anomaly_detectors.rb +22 -0
  113. data/lib/blazer/check_mailer.rb +27 -0
  114. data/lib/blazer/data_source.rb +270 -0
  115. data/lib/blazer/engine.rb +42 -0
  116. data/lib/blazer/forecasters.rb +7 -0
  117. data/lib/blazer/result.rb +178 -0
  118. data/lib/blazer/result_cache.rb +71 -0
  119. data/lib/blazer/run_statement.rb +44 -0
  120. data/lib/blazer/run_statement_job.rb +20 -0
  121. data/lib/blazer/slack_notifier.rb +94 -0
  122. data/lib/blazer/statement.rb +77 -0
  123. data/lib/blazer/version.rb +3 -0
  124. data/lib/blazer.rb +286 -0
  125. data/lib/finery.rb +3 -0
  126. data/lib/generators/blazer/install_generator.rb +22 -0
  127. data/lib/generators/blazer/templates/config.yml.tt +83 -0
  128. data/lib/generators/blazer/templates/install.rb.tt +47 -0
  129. data/lib/generators/blazer/templates/uploads.rb.tt +10 -0
  130. data/lib/generators/blazer/uploads_generator.rb +18 -0
  131. data/lib/tasks/blazer.rake +20 -0
  132. data/lib/tasks/finery.rake +20 -0
  133. data/licenses/LICENSE-ace.txt +24 -0
  134. data/licenses/LICENSE-bootstrap.txt +21 -0
  135. data/licenses/LICENSE-chart.js.txt +9 -0
  136. data/licenses/LICENSE-chartjs-adapter-date-fns.txt +9 -0
  137. data/licenses/LICENSE-chartkick.js.txt +22 -0
  138. data/licenses/LICENSE-date-fns.txt +21 -0
  139. data/licenses/LICENSE-daterangepicker.txt +21 -0
  140. data/licenses/LICENSE-fuzzysearch.txt +20 -0
  141. data/licenses/LICENSE-highlight.js.txt +29 -0
  142. data/licenses/LICENSE-jquery.txt +20 -0
  143. data/licenses/LICENSE-kurkle-color.txt +9 -0
  144. data/licenses/LICENSE-mapkick-bundle.txt +1029 -0
  145. data/licenses/LICENSE-moment-timezone.txt +20 -0
  146. data/licenses/LICENSE-moment.txt +22 -0
  147. data/licenses/LICENSE-rails-ujs.txt +20 -0
  148. data/licenses/LICENSE-selectize.txt +202 -0
  149. data/licenses/LICENSE-sortable.txt +21 -0
  150. data/licenses/LICENSE-stickytableheaders.txt +20 -0
  151. data/licenses/LICENSE-stupidtable.txt +19 -0
  152. data/licenses/LICENSE-vue.txt +21 -0
  153. metadata +250 -0
@@ -0,0 +1,403 @@
1
+ /**
2
+ * selectize.default.css (v0.12.6) - Default Theme
3
+ * Copyright (c) 2013–2015 Brian Reavis & contributors
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6
+ * file except in compliance with the License. You may obtain a copy of the License at:
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ *
9
+ * Unless required by applicable law or agreed to in writing, software distributed under
10
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
+ * ANY KIND, either express or implied. See the License for the specific language
12
+ * governing permissions and limitations under the License.
13
+ *
14
+ * @author Brian Reavis <brian@thirdroute.com>
15
+ */
16
+ .selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
17
+ visibility: visible !important;
18
+ background: #f2f2f2 !important;
19
+ background: rgba(0, 0, 0, 0.06) !important;
20
+ border: 0 none !important;
21
+ -webkit-box-shadow: inset 0 0 12px 4px #fff;
22
+ box-shadow: inset 0 0 12px 4px #fff;
23
+ }
24
+ .selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
25
+ content: '!';
26
+ visibility: hidden;
27
+ }
28
+ .selectize-control.plugin-drag_drop .ui-sortable-helper {
29
+ -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
30
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
31
+ }
32
+ .selectize-dropdown-header {
33
+ position: relative;
34
+ padding: 5px 8px;
35
+ border-bottom: 1px solid #d0d0d0;
36
+ background: #f8f8f8;
37
+ -webkit-border-radius: 3px 3px 0 0;
38
+ -moz-border-radius: 3px 3px 0 0;
39
+ border-radius: 3px 3px 0 0;
40
+ }
41
+ .selectize-dropdown-header-close {
42
+ position: absolute;
43
+ right: 8px;
44
+ top: 50%;
45
+ color: #303030;
46
+ opacity: 0.4;
47
+ margin-top: -12px;
48
+ line-height: 20px;
49
+ font-size: 20px !important;
50
+ }
51
+ .selectize-dropdown-header-close:hover {
52
+ color: #000000;
53
+ }
54
+ .selectize-dropdown.plugin-optgroup_columns .optgroup {
55
+ border-right: 1px solid #f2f2f2;
56
+ border-top: 0 none;
57
+ float: left;
58
+ -webkit-box-sizing: border-box;
59
+ -moz-box-sizing: border-box;
60
+ box-sizing: border-box;
61
+ }
62
+ .selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
63
+ border-right: 0 none;
64
+ }
65
+ .selectize-dropdown.plugin-optgroup_columns .optgroup:before {
66
+ display: none;
67
+ }
68
+ .selectize-dropdown.plugin-optgroup_columns .optgroup-header {
69
+ border-top: 0 none;
70
+ }
71
+ .selectize-control.plugin-remove_button [data-value] {
72
+ position: relative;
73
+ padding-right: 24px !important;
74
+ }
75
+ .selectize-control.plugin-remove_button [data-value] .remove {
76
+ z-index: 1;
77
+ /* fixes ie bug (see #392) */
78
+ position: absolute;
79
+ top: 0;
80
+ right: 0;
81
+ bottom: 0;
82
+ width: 17px;
83
+ text-align: center;
84
+ font-weight: bold;
85
+ font-size: 12px;
86
+ color: inherit;
87
+ text-decoration: none;
88
+ vertical-align: middle;
89
+ display: inline-block;
90
+ padding: 2px 0 0 0;
91
+ border-left: 1px solid #0073bb;
92
+ -webkit-border-radius: 0 2px 2px 0;
93
+ -moz-border-radius: 0 2px 2px 0;
94
+ border-radius: 0 2px 2px 0;
95
+ -webkit-box-sizing: border-box;
96
+ -moz-box-sizing: border-box;
97
+ box-sizing: border-box;
98
+ }
99
+ .selectize-control.plugin-remove_button [data-value] .remove:hover {
100
+ background: rgba(0, 0, 0, 0.05);
101
+ }
102
+ .selectize-control.plugin-remove_button [data-value].active .remove {
103
+ border-left-color: #00578d;
104
+ }
105
+ .selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
106
+ background: none;
107
+ }
108
+ .selectize-control.plugin-remove_button .disabled [data-value] .remove {
109
+ border-left-color: #aaaaaa;
110
+ }
111
+ .selectize-control.plugin-remove_button .remove-single {
112
+ position: absolute;
113
+ right: 0;
114
+ top: 0;
115
+ font-size: 23px;
116
+ }
117
+ .selectize-control {
118
+ position: relative;
119
+ }
120
+ .selectize-dropdown,
121
+ .selectize-input,
122
+ .selectize-input input {
123
+ color: #303030;
124
+ font-family: inherit;
125
+ font-size: 13px;
126
+ line-height: 18px;
127
+ -webkit-font-smoothing: inherit;
128
+ }
129
+ .selectize-input,
130
+ .selectize-control.single .selectize-input.input-active {
131
+ background: #fff;
132
+ cursor: text;
133
+ display: inline-block;
134
+ }
135
+ .selectize-input {
136
+ border: 1px solid #d0d0d0;
137
+ padding: 8px 8px;
138
+ display: inline-block;
139
+ width: 100%;
140
+ overflow: hidden;
141
+ position: relative;
142
+ z-index: 1;
143
+ -webkit-box-sizing: border-box;
144
+ -moz-box-sizing: border-box;
145
+ box-sizing: border-box;
146
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
147
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
148
+ -webkit-border-radius: 3px;
149
+ -moz-border-radius: 3px;
150
+ border-radius: 3px;
151
+ }
152
+ .selectize-control.multi .selectize-input.has-items {
153
+ padding: 5px 8px 2px;
154
+ }
155
+ .selectize-input.full {
156
+ background-color: #fff;
157
+ }
158
+ .selectize-input.disabled,
159
+ .selectize-input.disabled * {
160
+ cursor: default !important;
161
+ }
162
+ .selectize-input.focus {
163
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
164
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
165
+ }
166
+ .selectize-input.dropdown-active {
167
+ -webkit-border-radius: 3px 3px 0 0;
168
+ -moz-border-radius: 3px 3px 0 0;
169
+ border-radius: 3px 3px 0 0;
170
+ }
171
+ .selectize-input > * {
172
+ vertical-align: baseline;
173
+ display: -moz-inline-stack;
174
+ display: inline-block;
175
+ transform: scale(1);
176
+ *display: inline;
177
+ }
178
+ .selectize-control.multi .selectize-input > div {
179
+ cursor: pointer;
180
+ margin: 0 3px 3px 0;
181
+ padding: 2px 6px;
182
+ background: #1da7ee;
183
+ color: #fff;
184
+ border: 1px solid #0073bb;
185
+ }
186
+ .selectize-control.multi .selectize-input > div.active {
187
+ background: #92c836;
188
+ color: #fff;
189
+ border: 1px solid #00578d;
190
+ }
191
+ .selectize-control.multi .selectize-input.disabled > div,
192
+ .selectize-control.multi .selectize-input.disabled > div.active {
193
+ color: #ffffff;
194
+ background: #d2d2d2;
195
+ border: 1px solid #aaaaaa;
196
+ }
197
+ .selectize-input > input {
198
+ display: inline-block !important;
199
+ padding: 0 !important;
200
+ min-height: 0 !important;
201
+ max-height: none !important;
202
+ max-width: 100% !important;
203
+ margin: 0 1px !important;
204
+ text-indent: 0 !important;
205
+ border: 0 none !important;
206
+ background: none !important;
207
+ line-height: inherit !important;
208
+ -webkit-user-select: auto !important;
209
+ -webkit-box-shadow: none !important;
210
+ box-shadow: none !important;
211
+ }
212
+ .selectize-input > input::-ms-clear {
213
+ display: none;
214
+ }
215
+ .selectize-input > input:focus {
216
+ outline: none !important;
217
+ }
218
+ .selectize-input::after {
219
+ content: ' ';
220
+ display: block;
221
+ clear: left;
222
+ }
223
+ .selectize-input.dropdown-active::before {
224
+ content: ' ';
225
+ display: block;
226
+ position: absolute;
227
+ background: #f0f0f0;
228
+ height: 1px;
229
+ bottom: 0;
230
+ left: 0;
231
+ right: 0;
232
+ }
233
+ .selectize-dropdown {
234
+ position: absolute;
235
+ z-index: 10;
236
+ border: 1px solid #d0d0d0;
237
+ background: #fff;
238
+ margin: -1px 0 0 0;
239
+ border-top: 0 none;
240
+ -webkit-box-sizing: border-box;
241
+ -moz-box-sizing: border-box;
242
+ box-sizing: border-box;
243
+ -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
244
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
245
+ -webkit-border-radius: 0 0 3px 3px;
246
+ -moz-border-radius: 0 0 3px 3px;
247
+ border-radius: 0 0 3px 3px;
248
+ }
249
+ .selectize-dropdown [data-selectable] {
250
+ cursor: pointer;
251
+ overflow: hidden;
252
+ }
253
+ .selectize-dropdown [data-selectable] .highlight {
254
+ background: rgba(125, 168, 208, 0.2);
255
+ -webkit-border-radius: 1px;
256
+ -moz-border-radius: 1px;
257
+ border-radius: 1px;
258
+ }
259
+ .selectize-dropdown .option,
260
+ .selectize-dropdown .optgroup-header {
261
+ padding: 5px 8px;
262
+ }
263
+ .selectize-dropdown .option,
264
+ .selectize-dropdown [data-disabled],
265
+ .selectize-dropdown [data-disabled] [data-selectable].option {
266
+ cursor: inherit;
267
+ opacity: 0.5;
268
+ }
269
+ .selectize-dropdown [data-selectable].option {
270
+ opacity: 1;
271
+ }
272
+ .selectize-dropdown .optgroup:first-child .optgroup-header {
273
+ border-top: 0 none;
274
+ }
275
+ .selectize-dropdown .optgroup-header {
276
+ color: #303030;
277
+ background: #fff;
278
+ cursor: default;
279
+ }
280
+ .selectize-dropdown .active {
281
+ background-color: #f5fafd;
282
+ color: #495c68;
283
+ }
284
+ .selectize-dropdown .active.create {
285
+ color: #495c68;
286
+ }
287
+ .selectize-dropdown .create {
288
+ color: rgba(48, 48, 48, 0.5);
289
+ }
290
+ .selectize-dropdown-content {
291
+ overflow-y: auto;
292
+ overflow-x: hidden;
293
+ max-height: 200px;
294
+ -webkit-overflow-scrolling: touch;
295
+ }
296
+ .selectize-control.single .selectize-input,
297
+ .selectize-control.single .selectize-input input {
298
+ cursor: pointer;
299
+ }
300
+ .selectize-control.single .selectize-input.input-active,
301
+ .selectize-control.single .selectize-input.input-active input {
302
+ cursor: text;
303
+ }
304
+ .selectize-control.single .selectize-input:after {
305
+ content: ' ';
306
+ display: block;
307
+ position: absolute;
308
+ top: 50%;
309
+ right: 15px;
310
+ margin-top: -3px;
311
+ width: 0;
312
+ height: 0;
313
+ border-style: solid;
314
+ border-width: 5px 5px 0 5px;
315
+ border-color: #808080 transparent transparent transparent;
316
+ }
317
+ .selectize-control.single .selectize-input.dropdown-active:after {
318
+ margin-top: -4px;
319
+ border-width: 0 5px 5px 5px;
320
+ border-color: transparent transparent #808080 transparent;
321
+ }
322
+ .selectize-control.rtl.single .selectize-input:after {
323
+ left: 15px;
324
+ right: auto;
325
+ }
326
+ .selectize-control.rtl .selectize-input > input {
327
+ margin: 0 4px 0 -2px !important;
328
+ }
329
+ .selectize-control .selectize-input.disabled {
330
+ opacity: 0.5;
331
+ background-color: #fafafa;
332
+ }
333
+ .selectize-control.multi .selectize-input.has-items {
334
+ padding-left: 5px;
335
+ padding-right: 5px;
336
+ }
337
+ .selectize-control.multi .selectize-input.disabled [data-value] {
338
+ color: #999;
339
+ text-shadow: none;
340
+ background: none;
341
+ -webkit-box-shadow: none;
342
+ box-shadow: none;
343
+ }
344
+ .selectize-control.multi .selectize-input.disabled [data-value],
345
+ .selectize-control.multi .selectize-input.disabled [data-value] .remove {
346
+ border-color: #e6e6e6;
347
+ }
348
+ .selectize-control.multi .selectize-input.disabled [data-value] .remove {
349
+ background: none;
350
+ }
351
+ .selectize-control.multi .selectize-input [data-value] {
352
+ text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
353
+ -webkit-border-radius: 3px;
354
+ -moz-border-radius: 3px;
355
+ border-radius: 3px;
356
+ background-color: #1b9dec;
357
+ background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
358
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
359
+ background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
360
+ background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
361
+ background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
362
+ background-repeat: repeat-x;
363
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
364
+ -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
365
+ box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
366
+ }
367
+ .selectize-control.multi .selectize-input [data-value].active {
368
+ background-color: #0085d4;
369
+ background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
370
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
371
+ background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
372
+ background-image: -o-linear-gradient(top, #008fd8, #0075cf);
373
+ background-image: linear-gradient(to bottom, #008fd8, #0075cf);
374
+ background-repeat: repeat-x;
375
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
376
+ }
377
+ .selectize-control.single .selectize-input {
378
+ -webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
379
+ box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
380
+ background-color: #f9f9f9;
381
+ background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
382
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
383
+ background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
384
+ background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
385
+ background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
386
+ background-repeat: repeat-x;
387
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
388
+ }
389
+ .selectize-control.single .selectize-input,
390
+ .selectize-dropdown.single {
391
+ border-color: #b8b8b8;
392
+ }
393
+ .selectize-dropdown .optgroup-header {
394
+ padding-top: 7px;
395
+ font-weight: bold;
396
+ font-size: 0.85em;
397
+ }
398
+ .selectize-dropdown .optgroup {
399
+ border-top: 1px solid #f0f0f0;
400
+ }
401
+ .selectize-dropdown .optgroup:first-child {
402
+ border-top: 0 none;
403
+ }
@@ -0,0 +1,133 @@
1
+ module Blazer
2
+ class BaseController < ApplicationController
3
+ # skip filters
4
+ filters = _process_action_callbacks.map(&:filter) - [:activate_authlogic]
5
+ skip_before_action(*filters, raise: false)
6
+ skip_after_action(*filters, raise: false)
7
+ skip_around_action(*filters, raise: false)
8
+
9
+ clear_helpers
10
+
11
+ protect_from_forgery with: :exception
12
+
13
+ if ENV["BLAZER_PASSWORD"]
14
+ http_basic_authenticate_with name: ENV["BLAZER_USERNAME"], password: ENV["BLAZER_PASSWORD"]
15
+ end
16
+
17
+ if Blazer.settings["before_action"]
18
+ raise Blazer::Error, "The docs for protecting Blazer with a custom before_action had an incorrect example from August 2017 to June 2018. The example method had a boolean return value. However, you must render or redirect if a user is unauthorized rather than return a falsy value. Double check that your before_action works correctly for unauthorized users (if it worked when added, there should be no issue). Then, change before_action to before_action_method in config/blazer.yml."
19
+ end
20
+
21
+ if Blazer.before_action
22
+ before_action Blazer.before_action.to_sym
23
+ end
24
+
25
+ # if Blazer.override_csp
26
+ # after_action do
27
+ # response.headers['Content-Security-Policy'] = "default-src 'self' https: 'unsafe-inline' 'unsafe-eval' data: blob:"
28
+ # end
29
+ # end
30
+
31
+ layout "blazer/application"
32
+
33
+ private
34
+
35
+ def process_vars(statement, var_params = nil)
36
+ var_params ||= request.query_parameters
37
+ (@bind_vars ||= []).concat(statement.variables).uniq!
38
+ # update in-place so populated in view and consistent across queries on dashboard
39
+ @bind_vars.each do |var|
40
+ if !var_params[var]
41
+ default = statement.data_source.variable_defaults[var]
42
+ # only add if default exists
43
+ var_params[var] = default if default
44
+ end
45
+ end
46
+ runnable = @bind_vars.all? { |v| var_params[v] }
47
+ statement.add_values(var_params) if runnable
48
+ runnable
49
+ end
50
+
51
+ def refresh_query(query)
52
+ statement = query.statement_object
53
+ runnable = process_vars(statement)
54
+ cohort_analysis_statement(statement) if statement.cohort_analysis?
55
+ statement.clear_cache if runnable
56
+ end
57
+
58
+ def add_cohort_analysis_vars
59
+ @bind_vars << "cohort_period" unless @bind_vars.include?("cohort_period")
60
+ @smart_vars["cohort_period"] = ["day", "week", "month"] if @smart_vars
61
+ # TODO create var_params method
62
+ request.query_parameters["cohort_period"] ||= "week"
63
+ end
64
+
65
+ def parse_smart_variables(var, data_source)
66
+ smart_var_data_source =
67
+ ([data_source] + Array(data_source.settings["inherit_smart_settings"]).map { |ds| Blazer.data_sources[ds] }).find { |ds| ds.smart_variables[var] }
68
+
69
+ if smart_var_data_source
70
+ query = smart_var_data_source.smart_variables[var]
71
+
72
+ if query.is_a? Hash
73
+ smart_var = query.map { |k,v| [v, k] }
74
+ elsif query.is_a? Array
75
+ smart_var = query.map { |v| [v, v] }
76
+ elsif query
77
+ result = smart_var_data_source.run_statement(query)
78
+ smart_var = result.rows.map { |v| v.reverse }
79
+ error = result.error if result.error
80
+ end
81
+ end
82
+
83
+ [smart_var, error]
84
+ end
85
+
86
+ def cohort_analysis_statement(statement)
87
+ @cohort_period = params["cohort_period"] || "week"
88
+ @cohort_period = "week" unless ["day", "week", "month"].include?(@cohort_period)
89
+
90
+ # for now
91
+ @conversion_period = @cohort_period
92
+ @cohort_days =
93
+ case @cohort_period
94
+ when "day"
95
+ 1
96
+ when "week"
97
+ 7
98
+ when "month"
99
+ 30
100
+ end
101
+
102
+ statement.apply_cohort_analysis(period: @cohort_period, days: @cohort_days)
103
+ end
104
+
105
+ def variable_params(resource, var_params = nil)
106
+ permitted_keys = resource.variables
107
+ var_params ||= request.query_parameters
108
+ var_params.slice(*permitted_keys)
109
+ end
110
+ helper_method :variable_params
111
+
112
+ def nested_variable_params(resource)
113
+ variable_params(resource, request.query_parameters["variables"] || {})
114
+ end
115
+ helper_method :nested_variable_params
116
+
117
+ def blazer_user
118
+ send(Blazer.user_method) if Blazer.user_method && respond_to?(Blazer.user_method, true)
119
+ end
120
+ helper_method :blazer_user
121
+
122
+ def render_errors(resource)
123
+ @errors = resource.errors
124
+ action = resource.persisted? ? :edit : :new
125
+ render action, status: :unprocessable_entity
126
+ end
127
+
128
+ # do not inherit from ApplicationController - #120
129
+ def default_url_options
130
+ {}
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,56 @@
1
+ module Blazer
2
+ class ChecksController < BaseController
3
+ before_action :set_check, only: [:edit, :update, :destroy, :run]
4
+
5
+ def index
6
+ state_order = [nil, "disabled", "error", "timed out", "failing", "passing"]
7
+ @checks = Blazer::Check.joins(:query).includes(:query).order("blazer_queries.name, blazer_checks.id").to_a.sort_by { |q| state_order.index(q.state) || 99 }
8
+ @checks.select! { |c| "#{c.query.name} #{c.emails}".downcase.include?(params[:q]) } if params[:q]
9
+ end
10
+
11
+ def new
12
+ @check = Blazer::Check.new(query_id: params[:query_id])
13
+ end
14
+
15
+ def create
16
+ @check = Blazer::Check.new(check_params)
17
+ # use creator_id instead of creator
18
+ # since we setup association without checking if column exists
19
+ @check.creator = blazer_user if @check.respond_to?(:creator_id=) && blazer_user
20
+
21
+ if @check.save
22
+ redirect_to query_path(@check.query)
23
+ else
24
+ render_errors @check
25
+ end
26
+ end
27
+
28
+ def update
29
+ if @check.update(check_params)
30
+ redirect_to query_path(@check.query)
31
+ else
32
+ render_errors @check
33
+ end
34
+ end
35
+
36
+ def destroy
37
+ @check.destroy
38
+ redirect_to checks_path
39
+ end
40
+
41
+ def run
42
+ @query = @check.query
43
+ redirect_to query_path(@query)
44
+ end
45
+
46
+ private
47
+
48
+ def check_params
49
+ params.require(:check).permit(:query_id, :emails, :slack_channels, :invert, :check_type, :schedule)
50
+ end
51
+
52
+ def set_check
53
+ @check = Blazer::Check.find(params[:id])
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,99 @@
1
+ module Blazer
2
+ class DashboardsController < BaseController
3
+ before_action :set_dashboard, only: [:show, :edit, :update, :destroy, :refresh]
4
+
5
+ def new
6
+ @dashboard = Blazer::Dashboard.new
7
+ end
8
+
9
+ def create
10
+ @dashboard = Blazer::Dashboard.new
11
+ # use creator_id instead of creator
12
+ # since we setup association without checking if column exists
13
+ @dashboard.creator = blazer_user if @dashboard.respond_to?(:creator_id=) && blazer_user
14
+
15
+ if update_dashboard(@dashboard)
16
+ redirect_to dashboard_path(@dashboard)
17
+ else
18
+ render_errors @dashboard
19
+ end
20
+ end
21
+
22
+ def show
23
+ @queries = @dashboard.dashboard_queries.order(:position).preload(:query).map(&:query)
24
+ @queries.each do |query|
25
+ @success = process_vars(query.statement_object)
26
+ end
27
+ @bind_vars ||= []
28
+
29
+ @smart_vars = {}
30
+ @sql_errors = []
31
+ @data_sources = @queries.map { |q| Blazer.data_sources[q.data_source] }.uniq
32
+ @bind_vars.each do |var|
33
+ @data_sources.each do |data_source|
34
+ smart_var, error = parse_smart_variables(var, data_source)
35
+ ((@smart_vars[var] ||= []).concat(smart_var)).uniq! if smart_var
36
+ @sql_errors << error if error
37
+ end
38
+ end
39
+
40
+ add_cohort_analysis_vars if @queries.any?(&:cohort_analysis?)
41
+ end
42
+
43
+ def edit
44
+ end
45
+
46
+ def update
47
+ if update_dashboard(@dashboard)
48
+ redirect_to dashboard_path(@dashboard, params: variable_params(@dashboard))
49
+ else
50
+ render_errors @dashboard
51
+ end
52
+ end
53
+
54
+ def destroy
55
+ @dashboard.destroy
56
+ redirect_to root_path
57
+ end
58
+
59
+ def refresh
60
+ @dashboard.queries.each do |query|
61
+ refresh_query(query)
62
+ end
63
+ redirect_to dashboard_path(@dashboard, params: variable_params(@dashboard))
64
+ end
65
+
66
+ private
67
+
68
+ def dashboard_params
69
+ params.require(:dashboard).permit(:name)
70
+ end
71
+
72
+ def set_dashboard
73
+ @dashboard = Blazer::Dashboard.find(params[:id])
74
+ end
75
+
76
+ def update_dashboard(dashboard)
77
+ dashboard.assign_attributes(dashboard_params)
78
+ Blazer::Dashboard.transaction do
79
+ if params[:query_ids].is_a?(Array)
80
+ query_ids = params[:query_ids].map(&:to_i)
81
+ @queries = Blazer::Query.find(query_ids).sort_by { |q| query_ids.index(q.id) }
82
+ end
83
+ if dashboard.save
84
+ if @queries
85
+ @queries.each_with_index do |query, i|
86
+ dashboard_query = dashboard.dashboard_queries.where(query_id: query.id).first_or_initialize
87
+ dashboard_query.position = i
88
+ dashboard_query.save!
89
+ end
90
+ if dashboard.persisted?
91
+ dashboard.dashboard_queries.where.not(query_id: query_ids).destroy_all
92
+ end
93
+ end
94
+ true
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end