blazer 3.4.0 → 3.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +23 -27
- data/app/assets/javascripts/blazer/Sortable.js +826 -1162
- data/app/assets/javascripts/blazer/ace/ace.js +15775 -13360
- data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1411 -668
- data/app/assets/javascripts/blazer/ace/mode-sql.js +12 -6
- data/app/assets/javascripts/blazer/ace/theme-twilight.js +3 -3
- data/app/assets/javascripts/blazer/application.js +94 -52
- data/app/assets/javascripts/blazer/chart.umd.js +6 -6
- data/app/assets/javascripts/blazer/daterangepicker.js +193 -199
- data/app/assets/javascripts/blazer/highlight.min.js +235 -373
- data/app/assets/javascripts/blazer/moment-timezone-with-data.js +116 -82
- data/app/assets/javascripts/blazer/moment.js +368 -365
- data/app/assets/javascripts/blazer/queries.js +68 -58
- data/app/assets/javascripts/blazer/routes.js +11 -11
- data/app/assets/javascripts/blazer/tablesort.js +42 -0
- data/app/assets/javascripts/blazer/tom-select.base.js +4175 -0
- data/app/assets/stylesheets/blazer/application.css +114 -5
- data/app/assets/stylesheets/blazer/tom-select.css +502 -0
- data/app/controllers/blazer/base_controller.rb +1 -1
- data/app/controllers/blazer/checks_controller.rb +5 -2
- data/app/controllers/blazer/queries_controller.rb +3 -1
- data/app/helpers/blazer/base_helper.rb +1 -1
- data/app/models/blazer/check.rb +35 -57
- data/app/views/blazer/_nav.html.erb +1 -1
- data/app/views/blazer/_variables.html.erb +57 -42
- data/app/views/blazer/checks/_form.html.erb +20 -16
- data/app/views/blazer/checks/index.html.erb +17 -20
- data/app/views/blazer/dashboards/_form.html.erb +63 -48
- data/app/views/blazer/dashboards/show.html.erb +23 -16
- data/app/views/blazer/queries/_caching.html.erb +1 -1
- data/app/views/blazer/queries/_chart.html.erb +8 -0
- data/app/views/blazer/queries/_form.html.erb +153 -180
- data/app/views/blazer/queries/_map.html.erb +8 -0
- data/app/views/blazer/queries/docs.html.erb +6 -6
- data/app/views/blazer/queries/edit.html.erb +1 -0
- data/app/views/blazer/queries/home.html.erb +101 -102
- data/app/views/blazer/queries/new.html.erb +1 -0
- data/app/views/blazer/queries/run.html.erb +23 -34
- data/app/views/blazer/queries/schema.html.erb +20 -20
- data/app/views/blazer/queries/show.html.erb +19 -12
- data/app/views/blazer/uploads/index.html.erb +10 -10
- data/app/views/layouts/blazer/application.html.erb +3 -3
- data/lib/blazer/adapters/athena_adapter.rb +12 -6
- data/lib/blazer/adapters/clickhouse_adapter.rb +136 -0
- data/lib/blazer/adapters/druid_adapter.rb +0 -2
- data/lib/blazer/adapters/snowflake2_adapter.rb +198 -0
- data/lib/blazer/adapters/soda_adapter.rb +0 -2
- data/lib/blazer/adapters/sql_adapter.rb +4 -0
- data/lib/blazer/adapters.rb +2 -0
- data/lib/blazer/check_types.rb +20 -0
- data/lib/blazer/email_notifier.rb +49 -0
- data/lib/blazer/result.rb +15 -0
- data/lib/blazer/slack_notifier.rb +39 -7
- data/lib/blazer/version.rb +1 -1
- data/lib/blazer.rb +32 -26
- data/lib/generators/blazer/templates/config.yml.tt +0 -2
- data/licenses/LICENSE-chart.js.txt +1 -1
- metadata +12 -31
- data/app/assets/javascripts/blazer/bootstrap.js +0 -2580
- data/app/assets/javascripts/blazer/jquery.js +0 -10872
- data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +0 -325
- data/app/assets/javascripts/blazer/rails-ujs.js +0 -746
- data/app/assets/javascripts/blazer/selectize.js +0 -3891
- data/app/assets/javascripts/blazer/stupidtable-custom-settings.js +0 -13
- data/app/assets/javascripts/blazer/stupidtable.js +0 -281
- data/app/assets/javascripts/blazer/vue.global.prod.js +0 -1
- data/app/assets/stylesheets/blazer/selectize.css +0 -403
- data/licenses/LICENSE-jquery.txt +0 -20
- data/licenses/LICENSE-rails-ujs.txt +0 -20
- data/licenses/LICENSE-stickytableheaders.txt +0 -20
- data/licenses/LICENSE-stupidtable.txt +0 -19
- data/licenses/LICENSE-vue.txt +0 -21
- /data/licenses/{LICENSE-selectize.txt → LICENSE-tom-select.txt} +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
let pendingQueries = []
|
|
2
|
+
const runningQueries = []
|
|
3
|
+
const maxQueries = 3
|
|
4
4
|
|
|
5
5
|
function runQuery(data, success, error) {
|
|
6
6
|
if (!data.data_source) {
|
|
7
7
|
throw new Error("Data source is required to cancel queries")
|
|
8
8
|
}
|
|
9
9
|
data.run_id = uuid()
|
|
10
|
-
|
|
10
|
+
const query = {
|
|
11
11
|
data: data,
|
|
12
12
|
success: success,
|
|
13
13
|
error: error,
|
|
@@ -22,7 +22,7 @@ function runQuery(data, success, error) {
|
|
|
22
22
|
|
|
23
23
|
function runNext() {
|
|
24
24
|
if (runningQueries.length < maxQueries) {
|
|
25
|
-
|
|
25
|
+
const query = pendingQueries.shift()
|
|
26
26
|
if (query) {
|
|
27
27
|
runningQueries.push(query)
|
|
28
28
|
runQueryHelper(query)
|
|
@@ -32,99 +32,109 @@ function runNext() {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function runQueryHelper(query) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
const formdata = createFormData(csrfProtect(query.data))
|
|
36
|
+
const controller = new AbortController()
|
|
37
|
+
fetch(Routes.run_queries_path(), {method: "POST", body: formdata, signal: controller.signal})
|
|
38
|
+
.then(function (response) {
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
throw new Error(response.statusText)
|
|
41
|
+
}
|
|
42
|
+
return response.text()
|
|
43
|
+
})
|
|
44
|
+
.then(function (text) {
|
|
45
|
+
if (text[0] == "{") {
|
|
46
|
+
query.data.blazer = JSON.parse(text)
|
|
47
|
+
setTimeout(function () {
|
|
48
|
+
if (!query.canceled) {
|
|
49
|
+
runQueryHelper(query)
|
|
50
|
+
}
|
|
51
|
+
}, 1000)
|
|
52
|
+
} else {
|
|
45
53
|
if (!query.canceled) {
|
|
46
|
-
|
|
54
|
+
query.success(text)
|
|
47
55
|
}
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
if (!query.canceled) {
|
|
51
|
-
query.success(d)
|
|
56
|
+
queryComplete(query)
|
|
52
57
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
cancelServerQuery(query)
|
|
60
|
-
} else {
|
|
61
|
-
var message = (typeof errorThrown === "string") ? errorThrown : errorThrown.message
|
|
62
|
-
if (!message) {
|
|
63
|
-
message = "An error occurred"
|
|
58
|
+
})
|
|
59
|
+
.catch(function (error) {
|
|
60
|
+
if (error.name == "AbortError") {
|
|
61
|
+
cancelServerQuery(query)
|
|
62
|
+
} else {
|
|
63
|
+
query.error(error.message)
|
|
64
64
|
}
|
|
65
|
-
query
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
})
|
|
69
|
-
query.xhr = xhr
|
|
70
|
-
return xhr
|
|
65
|
+
queryComplete(query)
|
|
66
|
+
})
|
|
67
|
+
query.controller = controller
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
function queryComplete(query) {
|
|
74
|
-
|
|
71
|
+
const index = runningQueries.indexOf(query)
|
|
75
72
|
runningQueries.splice(index, 1)
|
|
76
73
|
runNext()
|
|
77
74
|
}
|
|
78
75
|
|
|
79
76
|
function uuid() {
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
if (window.crypto && window.crypto.randomUUID) {
|
|
78
|
+
return window.crypto.randomUUID()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
|
|
82
|
+
const r = (Math.random() * 16) | 0,
|
|
83
|
+
v = c == "x" ? r : (r & 0x3) | 0x8
|
|
82
84
|
return v.toString(16)
|
|
83
85
|
})
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
function cancelAllQueries() {
|
|
87
89
|
pendingQueries = []
|
|
88
|
-
for (
|
|
89
|
-
cancelQuery(
|
|
90
|
+
for (const query of runningQueries) {
|
|
91
|
+
cancelQuery(query)
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
// needed for Chrome
|
|
94
96
|
// queries are canceled before unload with Firefox and Safari
|
|
95
|
-
|
|
97
|
+
window.addEventListener("beforeunload", cancelAllQueries)
|
|
96
98
|
|
|
97
99
|
function cancelQuery(query) {
|
|
98
100
|
query.canceled = true
|
|
99
|
-
if (query.
|
|
100
|
-
query.
|
|
101
|
+
if (query.controller) {
|
|
102
|
+
query.controller.abort()
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
function createFormData(params) {
|
|
107
|
+
const formdata = new FormData()
|
|
108
|
+
for (const [key, value] of Object.entries(params)) {
|
|
109
|
+
if (typeof value === "object") {
|
|
110
|
+
// handle single level of nesting
|
|
111
|
+
for (const [k, v] of Object.entries(value)) {
|
|
112
|
+
formdata.append(`${key}[${k}]`, v)
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
formdata.append(key, value)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return formdata
|
|
119
|
+
}
|
|
120
|
+
|
|
104
121
|
function cancelServerQuery(query) {
|
|
105
122
|
// tell server
|
|
106
|
-
|
|
107
|
-
|
|
123
|
+
const path = Routes.cancel_queries_path()
|
|
124
|
+
const data = {run_id: query.run_id, data_source: query.data_source}
|
|
125
|
+
const formdata = createFormData(csrfProtect(data))
|
|
108
126
|
if (navigator.sendBeacon) {
|
|
109
127
|
// use FormData over Blob and URLSearchParams for maximum compatibility
|
|
110
128
|
// Blob works with Chrome 81+ and URLSearchParams works with Chrome 88+
|
|
111
|
-
var formdata = new FormData()
|
|
112
|
-
var params = csrfProtect(data)
|
|
113
|
-
for (var key in params) {
|
|
114
|
-
if (Object.prototype.hasOwnProperty.call(params, key)) {
|
|
115
|
-
formdata.append(key, params[key])
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
129
|
navigator.sendBeacon(path, formdata)
|
|
119
130
|
} else {
|
|
120
|
-
|
|
121
|
-
$.post(path, data)
|
|
131
|
+
fetch(path, {method: "POST", body: formdata})
|
|
122
132
|
}
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
function csrfProtect(payload) {
|
|
126
|
-
|
|
127
|
-
|
|
136
|
+
const param = document.querySelector("meta[name=csrf-param]")?.getAttribute("content")
|
|
137
|
+
const token = document.querySelector("meta[name=csrf-token]")?.getAttribute("content")
|
|
128
138
|
if (param && token) payload[param] = token
|
|
129
139
|
return payload
|
|
130
140
|
}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
var Routes = {
|
|
2
|
-
run_queries_path: function() {
|
|
2
|
+
run_queries_path: function () {
|
|
3
3
|
return rootPath + "queries/run"
|
|
4
4
|
},
|
|
5
|
-
cancel_queries_path: function() {
|
|
5
|
+
cancel_queries_path: function () {
|
|
6
6
|
return rootPath + "queries/cancel"
|
|
7
7
|
},
|
|
8
|
-
schema_queries_path: function(params) {
|
|
9
|
-
return rootPath + "queries/schema?" +
|
|
8
|
+
schema_queries_path: function (params) {
|
|
9
|
+
return rootPath + "queries/schema?" + pathParams(params)
|
|
10
10
|
},
|
|
11
|
-
docs_queries_path: function(params) {
|
|
12
|
-
return rootPath + "queries/docs?" +
|
|
11
|
+
docs_queries_path: function (params) {
|
|
12
|
+
return rootPath + "queries/docs?" + pathParams(params)
|
|
13
13
|
},
|
|
14
|
-
tables_queries_path: function(params) {
|
|
15
|
-
return rootPath + "queries/tables?" +
|
|
14
|
+
tables_queries_path: function (params) {
|
|
15
|
+
return rootPath + "queries/tables?" + pathParams(params)
|
|
16
16
|
},
|
|
17
|
-
queries_path: function() {
|
|
17
|
+
queries_path: function () {
|
|
18
18
|
return rootPath + "queries"
|
|
19
19
|
},
|
|
20
|
-
query_path: function(id) {
|
|
20
|
+
query_path: function (id) {
|
|
21
21
|
return rootPath + "queries/" + id
|
|
22
22
|
},
|
|
23
|
-
dashboard_path: function(id) {
|
|
23
|
+
dashboard_path: function (id) {
|
|
24
24
|
return rootPath + "dashboards/" + id
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function removeDelimiters(string) {
|
|
2
|
+
return string.replace(/,/g, "")
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function compareInt(a, b) {
|
|
6
|
+
return parseInt(removeDelimiters(a), 10) - parseInt(removeDelimiters(b), 10)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function compareFloat(a, b) {
|
|
10
|
+
return parseFloat(removeDelimiters(a)) - parseFloat(removeDelimiters(b))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function compareString(a, b) {
|
|
14
|
+
return a.localeCompare(b, undefined, {sensitivity: "base"})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function tablesort(element) {
|
|
18
|
+
let lastIndex = -1
|
|
19
|
+
let reverse = false
|
|
20
|
+
|
|
21
|
+
element.addEventListener("click", function (e) {
|
|
22
|
+
const target = e.target.closest("th")
|
|
23
|
+
if (target) {
|
|
24
|
+
const columnIndex = Array.from(element.querySelectorAll("thead th")).indexOf(target)
|
|
25
|
+
const sortType = target.getAttribute("data-sort")
|
|
26
|
+
const compareFn = sortType == "int" ? compareInt : (sortType == "float" ? compareFloat : compareString)
|
|
27
|
+
reverse = columnIndex != lastIndex ? false : !reverse
|
|
28
|
+
|
|
29
|
+
const tbody = element.querySelector("tbody")
|
|
30
|
+
const rows = Array.from(tbody.querySelectorAll("tr"))
|
|
31
|
+
rows.sort(function (a, b) {
|
|
32
|
+
const av = a.children[columnIndex].textContent
|
|
33
|
+
const bv = b.children[columnIndex].textContent
|
|
34
|
+
const r = compareFn(av, bv)
|
|
35
|
+
return reverse ? -r : r
|
|
36
|
+
})
|
|
37
|
+
tbody.replaceChildren(...rows)
|
|
38
|
+
|
|
39
|
+
lastIndex = columnIndex
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
}
|