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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +23 -27
  4. data/app/assets/javascripts/blazer/Sortable.js +826 -1162
  5. data/app/assets/javascripts/blazer/ace/ace.js +15775 -13360
  6. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1411 -668
  7. data/app/assets/javascripts/blazer/ace/mode-sql.js +12 -6
  8. data/app/assets/javascripts/blazer/ace/theme-twilight.js +3 -3
  9. data/app/assets/javascripts/blazer/application.js +94 -52
  10. data/app/assets/javascripts/blazer/chart.umd.js +6 -6
  11. data/app/assets/javascripts/blazer/daterangepicker.js +193 -199
  12. data/app/assets/javascripts/blazer/highlight.min.js +235 -373
  13. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +116 -82
  14. data/app/assets/javascripts/blazer/moment.js +368 -365
  15. data/app/assets/javascripts/blazer/queries.js +68 -58
  16. data/app/assets/javascripts/blazer/routes.js +11 -11
  17. data/app/assets/javascripts/blazer/tablesort.js +42 -0
  18. data/app/assets/javascripts/blazer/tom-select.base.js +4175 -0
  19. data/app/assets/stylesheets/blazer/application.css +114 -5
  20. data/app/assets/stylesheets/blazer/tom-select.css +502 -0
  21. data/app/controllers/blazer/base_controller.rb +1 -1
  22. data/app/controllers/blazer/checks_controller.rb +5 -2
  23. data/app/controllers/blazer/queries_controller.rb +3 -1
  24. data/app/helpers/blazer/base_helper.rb +1 -1
  25. data/app/models/blazer/check.rb +35 -57
  26. data/app/views/blazer/_nav.html.erb +1 -1
  27. data/app/views/blazer/_variables.html.erb +57 -42
  28. data/app/views/blazer/checks/_form.html.erb +20 -16
  29. data/app/views/blazer/checks/index.html.erb +17 -20
  30. data/app/views/blazer/dashboards/_form.html.erb +63 -48
  31. data/app/views/blazer/dashboards/show.html.erb +23 -16
  32. data/app/views/blazer/queries/_caching.html.erb +1 -1
  33. data/app/views/blazer/queries/_chart.html.erb +8 -0
  34. data/app/views/blazer/queries/_form.html.erb +153 -180
  35. data/app/views/blazer/queries/_map.html.erb +8 -0
  36. data/app/views/blazer/queries/docs.html.erb +6 -6
  37. data/app/views/blazer/queries/edit.html.erb +1 -0
  38. data/app/views/blazer/queries/home.html.erb +101 -102
  39. data/app/views/blazer/queries/new.html.erb +1 -0
  40. data/app/views/blazer/queries/run.html.erb +23 -34
  41. data/app/views/blazer/queries/schema.html.erb +20 -20
  42. data/app/views/blazer/queries/show.html.erb +19 -12
  43. data/app/views/blazer/uploads/index.html.erb +10 -10
  44. data/app/views/layouts/blazer/application.html.erb +3 -3
  45. data/lib/blazer/adapters/athena_adapter.rb +12 -6
  46. data/lib/blazer/adapters/clickhouse_adapter.rb +136 -0
  47. data/lib/blazer/adapters/druid_adapter.rb +0 -2
  48. data/lib/blazer/adapters/snowflake2_adapter.rb +198 -0
  49. data/lib/blazer/adapters/soda_adapter.rb +0 -2
  50. data/lib/blazer/adapters/sql_adapter.rb +4 -0
  51. data/lib/blazer/adapters.rb +2 -0
  52. data/lib/blazer/check_types.rb +20 -0
  53. data/lib/blazer/email_notifier.rb +49 -0
  54. data/lib/blazer/result.rb +15 -0
  55. data/lib/blazer/slack_notifier.rb +39 -7
  56. data/lib/blazer/version.rb +1 -1
  57. data/lib/blazer.rb +32 -26
  58. data/lib/generators/blazer/templates/config.yml.tt +0 -2
  59. data/licenses/LICENSE-chart.js.txt +1 -1
  60. metadata +12 -31
  61. data/app/assets/javascripts/blazer/bootstrap.js +0 -2580
  62. data/app/assets/javascripts/blazer/jquery.js +0 -10872
  63. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +0 -325
  64. data/app/assets/javascripts/blazer/rails-ujs.js +0 -746
  65. data/app/assets/javascripts/blazer/selectize.js +0 -3891
  66. data/app/assets/javascripts/blazer/stupidtable-custom-settings.js +0 -13
  67. data/app/assets/javascripts/blazer/stupidtable.js +0 -281
  68. data/app/assets/javascripts/blazer/vue.global.prod.js +0 -1
  69. data/app/assets/stylesheets/blazer/selectize.css +0 -403
  70. data/licenses/LICENSE-jquery.txt +0 -20
  71. data/licenses/LICENSE-rails-ujs.txt +0 -20
  72. data/licenses/LICENSE-stickytableheaders.txt +0 -20
  73. data/licenses/LICENSE-stupidtable.txt +0 -19
  74. data/licenses/LICENSE-vue.txt +0 -21
  75. /data/licenses/{LICENSE-selectize.txt → LICENSE-tom-select.txt} +0 -0
@@ -1,13 +1,13 @@
1
- var pendingQueries = []
2
- var runningQueries = []
3
- var maxQueries = 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
- var query = {
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
- var query = pendingQueries.shift()
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
- var xhr = $.ajax({
36
- url: Routes.run_queries_path(),
37
- method: "POST",
38
- data: query.data,
39
- dataType: "html"
40
- }).done( function (d) {
41
- if (d[0] == "{") {
42
- var response = $.parseJSON(d)
43
- query.data.blazer = response
44
- setTimeout( function () {
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
- runQueryHelper(query)
54
+ query.success(text)
47
55
  }
48
- }, 1000)
49
- } else {
50
- if (!query.canceled) {
51
- query.success(d)
56
+ queryComplete(query)
52
57
  }
53
- queryComplete(query)
54
- }
55
- }).fail( function(jqXHR, textStatus, errorThrown) {
56
- // check jqXHR.status instead of query.canceled
57
- // so it works for page navigation with Firefox and Safari
58
- if (jqXHR.status === 0) {
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.error(message)
66
- }
67
- queryComplete(query)
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
- var index = runningQueries.indexOf(query)
71
+ const index = runningQueries.indexOf(query)
75
72
  runningQueries.splice(index, 1)
76
73
  runNext()
77
74
  }
78
75
 
79
76
  function uuid() {
80
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
81
- var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8)
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 (var i = 0; i < runningQueries.length; i++) {
89
- cancelQuery(runningQueries[i])
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
- $(window).on("unload", cancelAllQueries)
97
+ window.addEventListener("beforeunload", cancelAllQueries)
96
98
 
97
99
  function cancelQuery(query) {
98
100
  query.canceled = true
99
- if (query.xhr) {
100
- query.xhr.abort()
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
- var path = Routes.cancel_queries_path()
107
- var data = {run_id: query.run_id, data_source: query.data_source}
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
- // TODO make sync
121
- $.post(path, data)
131
+ fetch(path, {method: "POST", body: formdata})
122
132
  }
123
133
  }
124
134
 
125
135
  function csrfProtect(payload) {
126
- var param = $("meta[name=csrf-param]").attr("content")
127
- var token = $("meta[name=csrf-token]").attr("content")
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?" + $.param(params)
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?" + $.param(params)
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?" + $.param(params)
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
+ }