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,6 +1,6 @@
|
|
|
1
1
|
<div id="queries">
|
|
2
2
|
<div id="header">
|
|
3
|
-
<div class="pull-right
|
|
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
|
|
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
|
|
39
|
+
<th class="user-header">Mastermind</th>
|
|
40
40
|
<% end%>
|
|
41
41
|
</tr>
|
|
42
42
|
</thead>
|
|
43
|
-
<tbody class="list"
|
|
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
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
65
|
+
item.searchStr = prepareQuery(searchStr)
|
|
73
66
|
}
|
|
74
67
|
}
|
|
75
68
|
|
|
76
|
-
|
|
69
|
+
function prepareQuery(str) {
|
|
77
70
|
return str.toLowerCase()
|
|
78
71
|
}
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
return
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
99
|
-
|
|
81
|
+
function renderItem(item) {
|
|
82
|
+
const tr = document.createElement("tr")
|
|
100
83
|
|
|
101
|
-
|
|
102
|
-
var i, j, newValues, val, size = 500;
|
|
84
|
+
const td = document.createElement("td")
|
|
103
85
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
145
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
168
|
-
|
|
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 %>
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
<%= pluralize(@rows.size, "row") %>
|
|
24
24
|
|
|
25
25
|
<% @checks.select(&:state).each do |check| %>
|
|
26
|
-
·
|
|
26
|
+
· <%= 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
|
· <%= 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 = {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
<%=
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 @
|
|
131
|
-
<pre><code><%= @
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
144
|
-
|
|
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"
|
|
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
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
|
|
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
|
-
|
|
53
|
-
}
|
|
54
|
-
})
|
|
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
|
|
5
|
+
<div class="row name-row">
|
|
6
6
|
<div class="col-sm-9">
|
|
7
7
|
<%= render partial: "blazer/nav" %>
|
|
8
|
-
<h3
|
|
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
|
|
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
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
62
|
+
results.classList.add("query-error")
|
|
63
|
+
results.textContent = message
|
|
57
64
|
}
|
|
58
65
|
|
|
59
|
-
<%= blazer_js_var "
|
|
66
|
+
<%= blazer_js_var "runData", @run_data %>
|
|
60
67
|
|
|
61
|
-
runQuery(
|
|
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
|
-
|
|
69
|
-
if (code.
|
|
70
|
-
hljs.highlightElement(code
|
|
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
|
|
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"
|
|
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
|
|
29
|
+
<th class="description-header"></th>
|
|
30
30
|
<% if Blazer.user_class %>
|
|
31
|
-
<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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
})
|
|
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/
|
|
10
|
-
<%= javascript_include_tag "blazer/
|
|
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[
|
|
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
|
-
|
|
120
|
+
glue_tables.table_list.map(&:name).sort
|
|
123
121
|
end
|
|
124
122
|
|
|
125
123
|
def schema
|
|
126
|
-
|
|
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
|