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,6 +1,6 @@
1
1
  <div id="queries">
2
2
  <div id="header">
3
- <div class="pull-right" style="line-height: 34px;">
3
+ <div class="pull-right header-div">
4
4
  <% if blazer_user %>
5
5
  <%= link_to "All", root_path, class: !params[:filter] ? "active" : nil, style: "margin-right: 40px;" %>
6
6
 
@@ -28,7 +28,7 @@
28
28
  </ul>
29
29
  </div>
30
30
  </div>
31
- <input type="text" v-model="searchTerm" placeholder="Start typing a query, dashboard, or person" style="width: 300px; display: inline-block;" v-focus class="search form-control" />
31
+ <input id="search" type="text" placeholder="Start typing a query, dashboard, or person" class="search form-control" autofocus />
32
32
  </div>
33
33
 
34
34
  <table class="table">
@@ -36,134 +36,133 @@
36
36
  <tr>
37
37
  <th>Name</th>
38
38
  <% if Blazer.user_class %>
39
- <th style="width: 20%; text-align: right;">Mastermind</th>
39
+ <th class="user-header">Mastermind</th>
40
40
  <% end%>
41
41
  </tr>
42
42
  </thead>
43
- <tbody class="list" v-cloak>
44
- <tr v-for="query in visibleItems">
45
- <td>
46
- <a :href="itemPath(query)" :class="{ dashboard: query.dashboard }">{{ query.name }}</a>
47
- <span class="vars">{{ query.vars }}</span>
48
- </td>
49
- <% if Blazer.user_class %>
50
- <td class="creator">{{ query.creator }}</td>
51
- <% end %>
52
- </tr>
53
- </tbody>
43
+ <tbody id="items" class="list"></tbody>
54
44
  </table>
55
45
 
56
- <p v-if="more" class="text-muted">Loading...</p>
46
+ <p id="loading" class="text-muted hide">Loading...</p>
57
47
  </div>
58
48
 
59
49
  <%= javascript_tag nonce: true do %>
60
50
  <%= blazer_js_var "dashboards", @dashboards %>
61
51
  <%= blazer_js_var "queries", @queries %>
62
52
  <%= blazer_js_var "more", @more %>
53
+ <%= blazer_js_var "showUser", !!Blazer.user_class %>
54
+
55
+ const search = document.getElementById("search")
56
+ const itemsList = document.getElementById("items")
57
+ const loading = document.getElementById("loading")
63
58
 
64
- var prepareSearch = function (list) {
65
- var i, q, searchStr
66
- for (i = 0; i < list.length; i++) {
67
- q = list[i]
68
- searchStr = q.name + q.creator
69
- if (q.creator === "You") {
59
+ function prepareSearch(items) {
60
+ for (const item of items) {
61
+ let searchStr = item.name + item.creator
62
+ if (item.creator === "You") {
70
63
  searchStr += "mine me"
71
64
  }
72
- q.searchStr = prepareQuery(searchStr)
65
+ item.searchStr = prepareQuery(searchStr)
73
66
  }
74
67
  }
75
68
 
76
- var prepareQuery = function (str) {
69
+ function prepareQuery(str) {
77
70
  return str.toLowerCase()
78
71
  }
79
72
 
80
- var app = Vue.createApp({
81
- data: function() {
82
- return {
83
- searchTerm: "",
84
- more: more,
85
- updateCounter: 0
86
- }
87
- },
88
- created: function() {
89
- this.listItems = dashboards.concat(queries)
90
-
91
- prepareSearch(this.listItems)
92
-
93
- this.queryIds = {}
94
- for (i = 0; i < queries.length; i++) {
95
- this.queryIds[queries[i].id] = true
96
- }
73
+ function itemPath(item) {
74
+ if (item.dashboard) {
75
+ return Routes.dashboard_path(item.to_param)
76
+ } else {
77
+ return Routes.query_path(item.to_param)
78
+ }
79
+ }
97
80
 
98
- if (this.more) {
99
- var _this = this
81
+ function renderItem(item) {
82
+ const tr = document.createElement("tr")
100
83
 
101
- $.getJSON(Routes.queries_path(), function (data) {
102
- var i, j, newValues, val, size = 500;
84
+ const td = document.createElement("td")
103
85
 
104
- var newValues = []
105
- for (j = 0; j < data.length; j++) {
106
- val = data[j]
107
- if (val && !_this.queryIds[val.id]) {
108
- newValues.push(val)
109
- }
110
- }
86
+ const a = document.createElement("a")
87
+ a.setAttribute("href", itemPath(item))
88
+ if (item.dashboard) {
89
+ a.classList.add("dashboard")
90
+ }
91
+ a.textContent = item.name
92
+ td.append(a)
93
+
94
+ if (item.vars) {
95
+ const span = document.createElement("span")
96
+ span.classList.add("vars")
97
+ span.textContent = item.vars
98
+ td.append(" ", span)
99
+ }
111
100
 
112
- prepareSearch(newValues)
101
+ tr.append(td)
102
+ if (showUser) {
103
+ const td2 = document.createElement("td")
104
+ td2.classList.add("creator")
105
+ td2.textContent = item.creator
106
+ tr.append(td2)
107
+ }
108
+ return tr
109
+ }
113
110
 
114
- _this.listItems = _this.listItems.concat(newValues)
115
- _this.more = false
116
- // hack to get to update
117
- _this.updateCounter++
118
- })
119
- }
120
- },
121
- computed: {
122
- visibleItems: function () {
123
- // hack to get to update
124
- this.updateCounter
125
-
126
- var pageSize = 200
127
- var q, i
128
-
129
- if (this.searchTerm.length > 0) {
130
- var term = prepareQuery(this.searchTerm)
131
- var items = []
132
- var fuzzyItems = []
133
- for (i = 0; i < this.listItems.length; i++) {
134
- q = this.listItems[i]
135
- if (q.searchStr.indexOf(term) !== -1) {
136
- items.push(q)
137
- if (items.length == pageSize) {
138
- break
139
- }
140
- } else if (fuzzysearch(term, q.searchStr)) {
141
- fuzzyItems.push(q)
142
- }
111
+ function visibleItems() {
112
+ const searchTerm = search.value
113
+ const pageSize = 200
114
+
115
+ if (searchTerm.length > 0) {
116
+ const term = prepareQuery(searchTerm)
117
+ const exactItems = []
118
+ const fuzzyItems = []
119
+ for (const item of items) {
120
+ if (item.searchStr.includes(term)) {
121
+ exactItems.push(item)
122
+ if (exactItems.length == pageSize) {
123
+ break
143
124
  }
144
- return items.concat(fuzzyItems).slice(0, pageSize)
145
- } else {
146
- return this.listItems.slice(0, pageSize)
125
+ } else if (fuzzyItems.length < pageSize && fuzzysearch(term, item.searchStr)) {
126
+ fuzzyItems.push(item)
147
127
  }
148
128
  }
149
- },
150
- methods: {
151
- itemPath: function (item) {
152
- if (item.dashboard) {
153
- return Routes.dashboard_path(item.to_param)
154
- } else {
155
- return Routes.query_path(item.to_param)
156
- }
157
- }
158
- },
159
- directives: {
160
- focus: {
161
- inserted: function (el) {
162
- el.focus()
129
+ return exactItems.concat(fuzzyItems).slice(0, pageSize)
130
+ } else {
131
+ return items.slice(0, pageSize)
132
+ }
133
+ }
134
+
135
+ let items = dashboards.concat(queries)
136
+ prepareSearch(items)
137
+
138
+ const queryIds = new Set()
139
+ for (const query of queries) {
140
+ queryIds.add(query.id)
141
+ }
142
+
143
+ if (more) {
144
+ show(loading)
145
+
146
+ getJSON(Routes.queries_path(), function (queries) {
147
+ hide(loading)
148
+
149
+ const newItems = []
150
+ for (const query of queries) {
151
+ if (!queryIds.has(query.id)) {
152
+ newItems.push(query)
163
153
  }
164
154
  }
165
- }
166
- })
167
- app.config.compilerOptions.whitespace = "preserve"
168
- app.mount("#queries")
155
+
156
+ prepareSearch(newItems)
157
+ items = items.concat(newItems)
158
+ renderItems()
159
+ })
160
+ }
161
+
162
+ function renderItems() {
163
+ itemsList.replaceChildren(...visibleItems().map(renderItem))
164
+ }
165
+
166
+ search.addEventListener("input", renderItems)
167
+ renderItems()
169
168
  <% end %>
@@ -1,2 +1,3 @@
1
1
  <% blazer_title "New Query" %>
2
+
2
3
  <%= render partial: "form" %>
@@ -23,7 +23,7 @@
23
23
  <%= pluralize(@rows.size, "row") %>
24
24
 
25
25
  <% @checks.select(&:state).each do |check| %>
26
- &middot; <small class="check-state <%= check.state.parameterize.gsub("-", "_") %>"><%= link_to check.state.upcase, edit_check_path(check) %></small>
26
+ &middot; <%= tag.small link_to(check.state.upcase, edit_check_path(check)), class: ["check-state", check.state.parameterize.gsub("-", "_")] %>
27
27
  <% if check.try(:message) %>
28
28
  &middot; <%= check.message %>
29
29
  <% end %>
@@ -43,10 +43,9 @@
43
43
  <% end %>
44
44
  <% if @rows.any? %>
45
45
  <% values = @rows.first %>
46
- <% chart_id = SecureRandom.hex %>
47
46
  <% column_types = @result.column_types %>
48
47
  <% chart_type = @result.chart_type %>
49
- <% chart_options = {id: chart_id, thousands: t("number.format.delimiter"), decimal: t("number.format.separator")} %>
48
+ <% chart_options = {thousands: t("number.format.delimiter"), decimal: t("number.format.separator")} %>
50
49
  <% if ["line", "line2"].include?(chart_type) %>
51
50
  <% chart_options.merge!(min: nil) %>
52
51
  <% end %>
@@ -72,33 +71,23 @@
72
71
  <% color = "#54a3ee" %>
73
72
  <% series_library[1] = {borderDash: [8], borderColor: color, pointBackgroundColor: color, backgroundColor: color, pointHoverBackgroundColor: color} %>
74
73
  <% end %>
74
+ <% map_options = {accessToken: Blazer.mapbox_access_token, tooltips: {hover: false, html: true}} %>
75
75
  <% if @markers.any? %>
76
- <% map_id = SecureRandom.hex %>
77
- <%= content_tag :div, nil, id: map_id, style: "height: #{@only_chart ? 300 : 500}px;" %>
78
- <%= javascript_tag nonce: true do %>
79
- <%= blazer_js_var "mapboxAccessToken", Blazer.mapbox_access_token %>
80
- <%= blazer_js_var "markers", @markers %>
81
- <%= blazer_js_var "mapId", map_id %>
82
- new Mapkick.Map(mapId, markers, {accessToken: mapboxAccessToken, tooltips: {hover: false, html: true}});
83
- <% end %>
76
+ <%= render partial: "map", locals: {type: "Map", data: @markers, options: map_options} %>
84
77
  <% elsif @geojson.any? %>
85
- <% map_id = SecureRandom.hex %>
86
- <%= content_tag :div, nil, id: map_id, style: "height: #{@only_chart ? 300 : 500}px;" %>
87
- <%= javascript_tag nonce: true do %>
88
- <%= blazer_js_var "mapboxAccessToken", Blazer.mapbox_access_token %>
89
- <%= blazer_js_var "geojson", @geojson %>
90
- <%= blazer_js_var "mapId", map_id %>
91
- new Mapkick.AreaMap(mapId, geojson, {accessToken: mapboxAccessToken, tooltips: {hover: false, html: true}});
92
- <% end %>
78
+ <%= render partial: "map", locals: {type: "AreaMap", data: @geojson, options: map_options} %>
93
79
  <% elsif chart_type == "line" %>
94
- <% chart_data = @columns[1..-1].each_with_index.map{ |k, i| {name: blazer_series_name(k), data: @rows.map{ |r| [r[0], r[i + 1]] }, library: series_library[i]} } %>
95
- <%= line_chart chart_data, **chart_options %>
80
+ <% chart_data = @columns[1..-1].each_with_index.map { |k, i| {name: blazer_series_name(k), data: @rows.map { |r| [r[0], r[i + 1]] }, library: series_library[i]} } %>
81
+ <%= render partial: "chart", locals: {type: "LineChart", data: chart_data, options: chart_options} %>
96
82
  <% elsif chart_type == "line2" %>
97
- <%= line_chart @rows.group_by { |r| v = r[1]; (@smart_values[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.map { |v2| [v2[0], v2[2]] }, library: series_library[i]} }, **chart_options %>
83
+ <% chart_data = @rows.group_by { |r| v = r[1]; (@smart_values[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.map { |v2| [v2[0], v2[2]] }, library: series_library[i]} } %>
84
+ <%= render partial: "chart", locals: {type: "LineChart", data: chart_data, options: chart_options} %>
98
85
  <% elsif chart_type == "pie" %>
99
- <%= pie_chart @rows.map { |r| [(@smart_values[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] }, **chart_options %>
86
+ <% chart_data = @rows.map { |r| [(@smart_values[@columns[0]] || {})[r[0].to_s] || r[0], r[1]] } %>
87
+ <%= render partial: "chart", locals: {type: "PieChart", data: chart_data, options: chart_options} %>
100
88
  <% elsif chart_type == "bar" %>
101
- <%= column_chart (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@smart_values[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } }, **chart_options %>
89
+ <% chart_data = (values.size - 1).times.map { |i| name = @columns[i + 1]; {name: blazer_series_name(name), data: @rows.first(20).map { |r| [(@smart_values[@columns[0]] || {})[r[0].to_s] || r[0], r[i + 1]] } } } %>
90
+ <%= render partial: "chart", locals: {type: "ColumnChart", data: chart_data, options: chart_options} %>
102
91
  <% elsif chart_type == "bar2" %>
103
92
  <% first_20 = @rows.group_by { |r| r[0] }.values.first(20).flatten(1) %>
104
93
  <% labels = first_20.map { |r| r[0] }.uniq %>
@@ -108,9 +97,11 @@
108
97
  <% first_20 << [l, s, 0] unless first_20.find { |r| r[0] == l && r[1] == s } %>
109
98
  <% end %>
110
99
  <% end %>
111
- <%= column_chart first_20.group_by { |r| v = r[1]; (@smart_values[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@smart_values[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} }, **chart_options %>
100
+ <% chart_data = first_20.group_by { |r| v = r[1]; (@smart_values[@columns[1]] || {})[v.to_s] || v }.each_with_index.map { |(name, v), i| {name: blazer_series_name(name), data: v.sort_by { |r2| labels.index(r2[0]) }.map { |v2| v3 = v2[0]; [(@smart_values[@columns[0]] || {})[v3.to_s] || v3, v2[2]] }} } %>
101
+ <%= render partial: "chart", locals: {type: "ColumnChart", data: chart_data, options: chart_options} %>
112
102
  <% elsif chart_type == "scatter" %>
113
- <%= scatter_chart @rows, xtitle: @columns[0], ytitle: @columns[1], **chart_options %>
103
+ <% chart_options.merge!(xtitle: @columns[0], ytitle: @columns[1]) %>
104
+ <%= render partial: "chart", locals: {type: "ScatterChart", data: @rows, options: chart_options} %>
114
105
  <% elsif @only_chart %>
115
106
  <% if @rows.size == 1 && @rows.first.size == 1 %>
116
107
  <% v = @rows.first.first %>
@@ -127,21 +118,19 @@
127
118
  <% unless @only_chart && !@no_chart %>
128
119
  <% header_width = 100 / @columns.size.to_f %>
129
120
  <div class="results-container">
130
- <% if @columns == ["QUERY PLAN"] %>
131
- <pre><code><%= @rows.map { |r| r[0] }.join("\n") %></code></pre>
132
- <% elsif @columns == ["PLAN"] && @data_source.adapter == "druid" %>
133
- <pre><code><%= @rows[0][0] %></code></pre>
121
+ <% if @explain %>
122
+ <pre><code><%= @explain %></code></pre>
134
123
  <% else %>
135
124
  <table class="table results-table">
136
125
  <thead>
137
126
  <tr>
138
127
  <% @columns.each_with_index do |key, i| %>
139
128
  <% type = @column_types[i] %>
140
- <th style="width: <%= header_width %>%;" data-sort="<%= type %>">
141
- <div style="min-width: <%= @min_width_types.include?(i) ? 180 : 60 %>px;">
129
+ <%= tag.th style: "width: #{header_width}%;", data: {sort: type} do %>
130
+ <%= tag.div style: "min-width: #{@min_width_types.include?(i) ? 180 : 60}px;" do %>
142
131
  <%= key %>
143
- </div>
144
- </th>
132
+ <% end %>
133
+ <% end %>
145
134
  <% end %>
146
135
  </tr>
147
136
  </thead>
@@ -5,7 +5,7 @@
5
5
  <hr />
6
6
 
7
7
  <div id="header">
8
- <input id="search" type="text" placeholder="Start typing a table or column" style="width: 300px; display: inline-block;" class="search form-control" />
8
+ <input id="search" type="text" placeholder="Start typing a table or column" class="search form-control" autofocus />
9
9
  </div>
10
10
 
11
11
  <% @schema.each do |table| %>
@@ -20,7 +20,7 @@
20
20
  <tbody>
21
21
  <% table[:columns].each do |column| %>
22
22
  <tr>
23
- <td style="width: 60%;"><%= column[:name] %></td>
23
+ <td class="column-name"><%= column[:name] %></td>
24
24
  <td class="text-muted"><%= column[:data_type] %></td>
25
25
  </tr>
26
26
  <% end %>
@@ -29,27 +29,27 @@
29
29
  <% end %>
30
30
 
31
31
  <%= javascript_tag nonce: true do %>
32
- $("#search").on("keyup", function() {
33
- var value = $(this).val().toLowerCase()
34
- $(".schema-table").filter(function() {
32
+ document.getElementById("search").addEventListener("input", function () {
33
+ const value = this.value
34
+ for (const schemaTable of document.querySelectorAll(".schema-table")) {
35
35
  // if found in table name, show entire table
36
36
  // if just found in rows, show row
37
-
38
- var found = $(this).find("thead").text().toLowerCase().indexOf(value) > -1
39
-
40
- if (found) {
41
- $(this).find("tbody tr").toggle(true)
42
- } else {
43
- $(this).find("tbody tr").filter(function() {
44
- var found2 = $(this).text().toLowerCase().indexOf(value) > -1
45
- $(this).toggle(found2)
46
- if (found2) {
47
- found = true
37
+ const tableFound = schemaTable.querySelector("thead").textContent.toLowerCase().includes(value)
38
+
39
+ let anyColumnFound = false
40
+ for (const column of schemaTable.querySelectorAll("tbody tr")) {
41
+ if (tableFound) {
42
+ show(column)
43
+ } else {
44
+ const columnFound = column.textContent.toLowerCase().includes(value)
45
+ toggle(column, columnFound)
46
+ if (columnFound) {
47
+ anyColumnFound = true
48
48
  }
49
- })
49
+ }
50
50
  }
51
51
 
52
- $(this).toggle(found)
53
- })
54
- }).focus()
52
+ toggle(schemaTable, tableFound || anyColumnFound)
53
+ }
54
+ })
55
55
  <% end %>
@@ -2,10 +2,10 @@
2
2
 
3
3
  <div class="topbar">
4
4
  <div class="container">
5
- <div class="row" style="padding-top: 13px;">
5
+ <div class="row name-row">
6
6
  <div class="col-sm-9">
7
7
  <%= render partial: "blazer/nav" %>
8
- <h3 style="line-height: 34px; display: inline; margin-left: 5px;">
8
+ <h3 class="name-header">
9
9
  <%= @query.name %>
10
10
  </h3>
11
11
  </div>
@@ -21,7 +21,7 @@
21
21
  </div>
22
22
  </div>
23
23
 
24
- <div style="margin-bottom: 60px;"></div>
24
+ <div class="topbar-margin"></div>
25
25
 
26
26
  <% if @sql_errors.any? %>
27
27
  <div class="alert alert-danger">
@@ -34,7 +34,7 @@
34
34
  <% end %>
35
35
 
36
36
  <% if @query.description.present? %>
37
- <p style="white-space: pre-line;"><%= @query.description %></p>
37
+ <p class="query-description"><%= @query.description %></p>
38
38
  <% end %>
39
39
 
40
40
  <%= render partial: "blazer/variables", locals: {action: query_path(@query)} %>
@@ -47,26 +47,33 @@
47
47
  </div>
48
48
 
49
49
  <%= javascript_tag nonce: true do %>
50
+ const results = document.getElementById("results")
51
+
50
52
  function showRun(data) {
51
- $("#results").html(data)
52
- $("#results table").stupidtable(stupidtableCustomSettings).stickyTableHeaders({fixedOffset: 60})
53
+ renderResults(results, data)
54
+ const table = results.querySelector("table")
55
+ if (table) {
56
+ tablesort(table)
57
+ table.classList.add("stickytable")
58
+ }
53
59
  }
54
60
 
55
61
  function showError(message) {
56
- $("#results").addClass("query-error").html(message)
62
+ results.classList.add("query-error")
63
+ results.textContent = message
57
64
  }
58
65
 
59
- <%= blazer_js_var "data", @run_data %>
66
+ <%= blazer_js_var "runData", @run_data %>
60
67
 
61
- runQuery(data, showRun, showError)
68
+ runQuery(runData, showRun, showError)
62
69
  <% end %>
63
70
  <% end %>
64
71
 
65
72
  <%= javascript_tag nonce: true do %>
66
73
  // do not highlight really long queries
67
74
  // this can lead to performance issues
68
- var code = $("#code code")
69
- if (code.text().length < 10000) {
70
- hljs.highlightElement(code.get(0))
75
+ const code = document.querySelector("#code code")
76
+ if (code.textContent.length < 10000) {
77
+ hljs.highlightElement(code)
71
78
  }
72
79
  <% end %>
@@ -1,7 +1,7 @@
1
1
  <% blazer_title "Uploads" %>
2
2
 
3
3
  <div id="header">
4
- <div class="pull-right" style="line-height: 34px;">
4
+ <div class="pull-right header-div">
5
5
  <div class="btn-group">
6
6
  <%= link_to "New Upload", new_upload_path, class: "btn btn-info" %>
7
7
  <button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
@@ -19,16 +19,16 @@
19
19
  </div>
20
20
  </div>
21
21
 
22
- <input id="search" type="text" placeholder="Start typing a table or person" style="width: 300px; display: inline-block;" class="search form-control" />
22
+ <input id="search" type="text" placeholder="Start typing a table or person" class="search form-control" autofocus />
23
23
  </div>
24
24
 
25
25
  <table id="uploads" class="table">
26
26
  <thead>
27
27
  <tr>
28
28
  <th>Table</th>
29
- <th style="width: 60%;"></th>
29
+ <th class="description-header"></th>
30
30
  <% if Blazer.user_class %>
31
- <th style="width: 20%; text-align: right;">Mastermind</th>
31
+ <th class="user-header">Mastermind</th>
32
32
  <% end%>
33
33
  </tr>
34
34
  </thead>
@@ -46,10 +46,10 @@
46
46
  </table>
47
47
 
48
48
  <%= javascript_tag nonce: true do %>
49
- $("#search").on("keyup", function() {
50
- var value = $(this).val().toLowerCase()
51
- $("#uploads tbody tr").filter( function() {
52
- $(this).toggle($(this).text().toLowerCase().indexOf(value) > -1)
53
- })
54
- }).focus()
49
+ document.getElementById("search").addEventListener("input", function () {
50
+ const value = this.value.toLowerCase()
51
+ for (const upload of document.querySelectorAll("#uploads tbody tr")) {
52
+ toggle(upload, upload.textContent.toLowerCase().includes(value))
53
+ }
54
+ })
55
55
  <% end %>
@@ -6,10 +6,10 @@
6
6
  <meta charset="utf-8" />
7
7
  <%= favicon_link_tag "blazer/favicon.png" %>
8
8
  <% if defined?(Propshaft::Railtie) && Rails.application.assets.is_a?(Propshaft::Assembly) %>
9
- <%= stylesheet_link_tag "blazer/bootstrap-propshaft", "blazer/bootstrap", "blazer/selectize", "blazer/github", "blazer/daterangepicker", "blazer/application" %>
10
- <%= javascript_include_tag "blazer/jquery", "blazer/rails-ujs", "blazer/stupidtable", "blazer/stupidtable-custom-settings", "blazer/jquery.stickytableheaders", "blazer/selectize", "blazer/highlight.min", "blazer/moment", "blazer/moment-timezone-with-data", "blazer/daterangepicker", "blazer/chart.umd", "blazer/chartjs-adapter-date-fns.bundle", "blazer/chartkick", "blazer/mapkick.bundle", "blazer/ace/ace", "blazer/ace/ext-language_tools", "blazer/ace/theme-twilight", "blazer/ace/mode-sql", "blazer/ace/snippets/text", "blazer/ace/snippets/sql", "blazer/Sortable", "blazer/bootstrap", "blazer/vue.global.prod", "blazer/routes", "blazer/queries", "blazer/fuzzysearch", "blazer/application", nonce: true %>
9
+ <%= stylesheet_link_tag "blazer/bootstrap-propshaft", "blazer/bootstrap", "blazer/tom-select", "blazer/github", "blazer/daterangepicker", "blazer/application", nonce: true %>
10
+ <%= javascript_include_tag "blazer/tablesort", "blazer/tom-select.base", "blazer/highlight.min", "blazer/moment", "blazer/moment-timezone-with-data", "blazer/daterangepicker", "blazer/chart.umd", "blazer/chartjs-adapter-date-fns.bundle", "blazer/chartkick", "blazer/mapkick.bundle", "blazer/ace/ace", "blazer/ace/ext-language_tools", "blazer/ace/theme-twilight", "blazer/ace/mode-sql", "blazer/ace/snippets/text", "blazer/ace/snippets/sql", "blazer/Sortable", "blazer/routes", "blazer/queries", "blazer/fuzzysearch", "blazer/application", nonce: true %>
11
11
  <% else %>
12
- <%= stylesheet_link_tag "blazer/application" %>
12
+ <%= stylesheet_link_tag "blazer/application", nonce: true %>
13
13
  <%= javascript_include_tag "blazer/application", nonce: true %>
14
14
  <% end %>
15
15
  <%= javascript_tag nonce: true do %>
@@ -32,9 +32,7 @@ module Blazer
32
32
  query_string: statement,
33
33
  # use token so we fetch cached results after query is run
34
34
  client_request_token: request_token,
35
- query_execution_context: {
36
- database: database
37
- }
35
+ query_execution_context: {database: database, catalog: catalog}.compact
38
36
  }
39
37
 
40
38
  if settings["output_location"]
@@ -78,7 +76,7 @@ module Blazer
78
76
  untyped_rows.concat page.result_set.rows.map { |r| r.data.map(&:var_char_value) }
79
77
  end
80
78
 
81
- utc = ActiveSupport::TimeZone['Etc/UTC']
79
+ utc = ActiveSupport::TimeZone["Etc/UTC"]
82
80
 
83
81
  rows = untyped_rows[1..-1] || []
84
82
  rows = untyped_rows[0..-1] unless column_info.present?
@@ -119,11 +117,11 @@ module Blazer
119
117
  end
120
118
 
121
119
  def tables
122
- glue.get_tables(database_name: database).table_list.map(&:name).sort
120
+ glue_tables.table_list.map(&:name).sort
123
121
  end
124
122
 
125
123
  def schema
126
- glue.get_tables(database_name: database).table_list.map { |t| {table: t.name, columns: t.storage_descriptor.columns.map { |c| {name: c.name, data_type: c.type} }} }
124
+ glue_tables.table_list.map { |t| {table: t.name, columns: t.storage_descriptor.columns.map { |c| {name: c.name, data_type: c.type} }} }
127
125
  end
128
126
 
129
127
  def preview_statement
@@ -146,6 +144,14 @@ module Blazer
146
144
  @database ||= settings["database"] || "default"
147
145
  end
148
146
 
147
+ def catalog
148
+ @catalog ||= settings["catalog"]
149
+ end
150
+
151
+ def glue_tables
152
+ glue.get_tables({database_name: database, catalog_id: catalog}.compact)
153
+ end
154
+
149
155
  # note: this setting is experimental
150
156
  # it does *not* need to be set to use engine version 2
151
157
  # prepared statements must be manually deleted if enabled