sqlui 0.1.28 → 0.1.29

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 580bda7206a3282d1dd0dae3754daa8bdf53b3522c5210a0e2542147d3a13aee
4
- data.tar.gz: 6fcfca4ed80216ef293f72e8be9e59896dbd82eb3da76f5761ad3671fa16c32b
3
+ metadata.gz: 9257bcf43bc98bcd5ed8a52d3b2643db1189d0d3b8c2c1444c5d949ff37ed03c
4
+ data.tar.gz: 6a96de39b01459a5b222a2781158523707fbd3e585400dd18a1b4469c55b4c9c
5
5
  SHA512:
6
- metadata.gz: 6ffaff0a56fd85efbf3c3fe69fad3c5f4750b23defc7716004420fb4fa9243983d6a105dbbe81892c3cf859332530920ab88f4312a9ffb7009e6d46103e3dd9d
7
- data.tar.gz: 94cea75b430cfe9289f28fc9eaaa9a5cdd026945d0fb2e66616cfcc0e6bcd3845fcfcc205ad9b047274e81f50c71313f004fe35ba0fc167216c06a9211c04317
6
+ metadata.gz: 32eb854b16a9b345456902379ec7cc8ba5e091562a62de90451f63d28146815a880315b0921303770233d036187d8b26f6164bf04e8dd935232bf3a6ef82e646
7
+ data.tar.gz: 0da14c2be2b04171df6ccef8dc4e4fb1c512440371d2394ae788ecc88994e032bf69633e683bab61e0cc4bc54ae367cb0ce6565cfc4b3f67559b90061ddbdaf9
data/.version CHANGED
@@ -1 +1 @@
1
- 0.1.28
1
+ 0.1.29
data/app/args.rb CHANGED
@@ -9,6 +9,10 @@ class Args
9
9
  value
10
10
  end
11
11
 
12
+ def self.fetch_non_empty_int(hash, key)
13
+ fetch_non_nil(hash, key, Integer)
14
+ end
15
+
12
16
  def self.fetch_non_empty_hash(hash, key)
13
17
  value = fetch_non_nil(hash, key, Hash)
14
18
  raise ArgumentError, "required parameter #{key} empty" if value.empty?
@@ -23,9 +27,13 @@ class Args
23
27
  raise ArgumentError, "required parameter #{key} null" if value.nil?
24
28
 
25
29
  if classes.size.positive? && !classes.find { |clazz| value.is_a?(clazz) }
26
- raise ArgumentError, "required parameter #{key} not a #{classes[0].to_s.downcase}" if classes.size == 1
30
+ if classes.size != 1
31
+ raise ArgumentError, "required parameter #{key} not #{classes.map(&:to_s).map(&:downcase).join(' or ')}"
32
+ end
27
33
 
28
- raise ArgumentError, "required parameter #{key} not #{classes.map(&:to_s).map(&:downcase).join(' or ')}"
34
+ class_name = classes[0].to_s.downcase
35
+ class_name = %w[a e i o u].include?(class_name[0]) ? "an #{class_name}" : "a #{class_name}"
36
+ raise ArgumentError, "required parameter #{key} not #{class_name}"
29
37
  end
30
38
 
31
39
  value
data/app/deep.rb CHANGED
@@ -7,6 +7,10 @@ module Enumerable
7
7
  self
8
8
  end
9
9
 
10
+ def deep_symbolize_keys!
11
+ deep_transform_keys!(&:to_sym)
12
+ end
13
+
10
14
  def deep_dup(result = {})
11
15
  map do |value|
12
16
  value.respond_to?(:deep_dup) ? value.deep_dup : value.clone
@@ -18,11 +22,15 @@ end
18
22
  # Deep extensions for Hash.
19
23
  class Hash
20
24
  def deep_transform_keys!(&block)
21
- transform_keys!(&:to_s)
25
+ transform_keys!(&block)
22
26
  each_value { |value| value.deep_transform_keys!(&block) if value.respond_to?(:deep_transform_keys!) }
23
27
  self
24
28
  end
25
29
 
30
+ def deep_symbolize_keys!
31
+ deep_transform_keys!(&:to_sym)
32
+ end
33
+
26
34
  def deep_dup(result = {})
27
35
  each do |key, value|
28
36
  result[key] = value.respond_to?(:deep_dup) ? value.deep_dup : value.clone
@@ -55,4 +63,15 @@ class Hash
55
63
  self.[](path[0]).deep_delete(*path[1..])
56
64
  end
57
65
  end
66
+
67
+ def deep_merge!(hash)
68
+ hash.each do |key, value|
69
+ if self[key].is_a?(Hash) && value.is_a?(Hash)
70
+ self[key].deep_merge!(value)
71
+ else
72
+ self[key] = value
73
+ end
74
+ end
75
+ self
76
+ end
58
77
  end
data/app/server.rb CHANGED
@@ -5,7 +5,6 @@ require 'json'
5
5
  require 'sinatra/base'
6
6
  require 'uri'
7
7
  require_relative 'database_metadata'
8
- require_relative 'environment'
9
8
  require_relative 'mysql_types'
10
9
  require_relative 'sql_parser'
11
10
  require_relative 'sqlui'
@@ -15,8 +14,8 @@ class Server < Sinatra::Base
15
14
  def self.init_and_run(config, resources_dir)
16
15
  set :logging, true
17
16
  set :bind, '0.0.0.0'
18
- set :port, Environment.server_port
19
- set :env, Environment.server_env
17
+ set :port, config.port
18
+ set :environment, config.environment
20
19
  set :raise_errors, false
21
20
  set :show_exceptions, false
22
21
 
@@ -41,18 +40,18 @@ class Server < Sinatra::Base
41
40
  get "#{database.url_path}/sqlui.css" do
42
41
  @css ||= File.read(File.join(resources_dir, 'sqlui.css'))
43
42
  status 200
44
- headers 'Content-Type': 'text/css'
43
+ headers 'Content-Type' => 'text/css'
45
44
  body @css
46
45
  end
47
46
 
48
47
  get "#{database.url_path}/sqlui.js" do
49
48
  @js ||= File.read(File.join(resources_dir, 'sqlui.js'))
50
49
  status 200
51
- headers 'Content-Type': 'text/javascript'
50
+ headers 'Content-Type' => 'text/javascript'
52
51
  body @js
53
52
  end
54
53
 
55
- get "#{database.url_path}/metadata" do
54
+ post "#{database.url_path}/metadata" do
56
55
  metadata = database.with_client do |client|
57
56
  {
58
57
  server: "#{config.name} - #{database.display_name}",
@@ -77,7 +76,7 @@ class Server < Sinatra::Base
77
76
  }
78
77
  end
79
78
  status 200
80
- headers 'Content-Type': 'application/json'
79
+ headers 'Content-Type' => 'application/json'
81
80
  body metadata.to_json
82
81
  end
83
82
 
@@ -114,21 +113,21 @@ class Server < Sinatra::Base
114
113
  result[:query] = full_sql
115
114
 
116
115
  status 200
117
- headers 'Content-Type': 'application/json'
116
+ headers 'Content-Type' => 'application/json'
118
117
  body result.to_json
119
118
  end
120
119
 
121
120
  get(%r{#{Regexp.escape(database.url_path)}/(query|graph|structure|saved)}) do
122
121
  @html ||= File.read(File.join(resources_dir, 'sqlui.html'))
123
122
  status 200
124
- headers 'Content-Type': 'text/html'
123
+ headers 'Content-Type' => 'text/html'
125
124
  body @html
126
125
  end
127
126
  end
128
127
 
129
128
  error do |e|
130
129
  status 500
131
- headers 'Content-Type': 'application/json'
130
+ headers 'Content-Type' => 'application/json'
132
131
  message = e.message.lines.first&.strip || 'unexpected error'
133
132
  message = "#{message[0..80]}…" if message.length > 80
134
133
  result = {
@@ -145,7 +144,7 @@ class Server < Sinatra::Base
145
144
 
146
145
  def client_error(message, stacktrace: nil)
147
146
  status(400)
148
- headers('Content-Type': 'application/json')
147
+ headers 'Content-Type' => 'application/json'
149
148
  body({ error: message, stacktrace: stacktrace }.compact.to_json)
150
149
  end
151
150
 
data/app/sqlui_config.rb CHANGED
@@ -1,17 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
- require_relative 'database_config'
5
4
  require_relative 'args'
5
+ require_relative 'database_config'
6
+ require_relative 'deep'
6
7
 
7
8
  # App config including database configs.
8
9
  class SqluiConfig
9
- attr_reader :name, :list_url_path, :database_configs
10
+ attr_reader :name, :port, :environment, :list_url_path, :database_configs
10
11
 
11
- def initialize(filename)
12
- config = YAML.safe_load(ERB.new(File.read(filename)).result)
13
- deep_symbolize!(config)
12
+ def initialize(filename, overrides = {})
13
+ config = YAML.safe_load(ERB.new(File.read(filename)).result).deep_merge!(overrides)
14
+ config.deep_symbolize_keys!
14
15
  @name = Args.fetch_non_empty_string(config, :name).strip
16
+ @port = Args.fetch_non_empty_int(config, :port)
17
+ @environment = Args.fetch_non_empty_string(config, :environment).strip
15
18
  @list_url_path = Args.fetch_non_empty_string(config, :list_url_path).strip
16
19
  raise ArgumentError, 'list_url_path should start with a /' unless @list_url_path.start_with?('/')
17
20
  if @list_url_path.length > 1 && @list_url_path.end_with?('/')
@@ -30,15 +33,4 @@ class SqluiConfig
30
33
 
31
34
  config
32
35
  end
33
-
34
- private
35
-
36
- def deep_symbolize!(object)
37
- return object unless object.is_a? Hash
38
-
39
- object.transform_keys!(&:to_sym)
40
- object.each_value { |child| deep_symbolize!(child) }
41
-
42
- object
43
- end
44
36
  end
@@ -322,3 +322,9 @@ select {
322
322
  0% { transform: rotate(0deg); }
323
323
  100% { transform: rotate(360deg); }
324
324
  }
325
+
326
+ .result-time {
327
+ font-family: monospace;
328
+ color: #333;
329
+ font-size: 14px;
330
+ }
@@ -40,6 +40,7 @@
40
40
 
41
41
  <div id="fetch-sql-box" class="fetch-sql-box tab-content-element graph-element query-element" style="display: none;">
42
42
  <div id="result-loader" class="loader"></div>
43
+ <p id="result-time" class="result-time"></p>
43
44
  </div>
44
45
 
45
46
  <div id="saved-box" class="saved-box tab-content-element saved-element" style="display: none;">
@@ -23963,10 +23963,10 @@
23963
23963
  function selectTab (event, tab) {
23964
23964
  const url = new URL(window.location);
23965
23965
  setTabInUrl(url, tab);
23966
- route(event.target, event, url);
23966
+ route(event.target, event, url, true);
23967
23967
  }
23968
23968
 
23969
- function route (target = null, event = null, url = null) {
23969
+ function route (target = null, event = null, url = null, internal = false) {
23970
23970
  if (url) {
23971
23971
  if (event) {
23972
23972
  event.preventDefault();
@@ -24004,10 +24004,10 @@
24004
24004
 
24005
24005
  switch (window.tab) {
24006
24006
  case 'query':
24007
- selectResultTab();
24007
+ selectResultTab(internal);
24008
24008
  break
24009
24009
  case 'graph':
24010
- selectGraphTab();
24010
+ selectGraphTab(internal);
24011
24011
  break
24012
24012
  case 'saved':
24013
24013
  selectSavedTab();
@@ -24158,21 +24158,21 @@
24158
24158
  });
24159
24159
  }
24160
24160
 
24161
- function selectGraphTab () {
24161
+ function selectGraphTab (internal) {
24162
24162
  document.getElementById('query-box').style.display = 'flex';
24163
24163
  document.getElementById('submit-box').style.display = 'flex';
24164
24164
  document.getElementById('graph-box').style.display = 'flex';
24165
24165
  document.getElementById('graph-status').style.display = 'flex';
24166
24166
  document.getElementById('fetch-sql-box').style.display = 'none';
24167
24167
  document.getElementById('cancel-button').style.display = 'none';
24168
- maybeFetchResult();
24168
+ maybeFetchResult(internal);
24169
24169
 
24170
24170
  const selection = getSelection();
24171
24171
  focus();
24172
24172
  setSelection(selection);
24173
24173
  }
24174
24174
 
24175
- function selectResultTab () {
24175
+ function selectResultTab (internal) {
24176
24176
  document.getElementById('query-box').style.display = 'flex';
24177
24177
  document.getElementById('submit-box').style.display = 'flex';
24178
24178
  document.getElementById('result-box').style.display = 'flex';
@@ -24182,7 +24182,7 @@
24182
24182
  const selection = getSelection();
24183
24183
  focus();
24184
24184
  setSelection(selection);
24185
- maybeFetchResult();
24185
+ maybeFetchResult(internal);
24186
24186
  }
24187
24187
 
24188
24188
  function selectSavedTab () {
@@ -24212,7 +24212,7 @@
24212
24212
  viewLinkElement.href = viewUrl.pathname + viewUrl.search;
24213
24213
  viewLinkElement.addEventListener('click', function (event) {
24214
24214
  clearResult();
24215
- route(event.target, event, viewUrl);
24215
+ route(event.target, event, viewUrl, true);
24216
24216
  });
24217
24217
 
24218
24218
  const runUrl = new URL(window.location.origin + window.location.pathname);
@@ -24226,7 +24226,7 @@
24226
24226
  runLinkElement.href = runUrl.pathname + runUrl.search;
24227
24227
  runLinkElement.addEventListener('click', function (event) {
24228
24228
  clearResult();
24229
- route(event.target, event, runUrl);
24229
+ route(event.target, event, runUrl, true);
24230
24230
  });
24231
24231
 
24232
24232
  const nameElement = document.createElement('h2');
@@ -24296,20 +24296,21 @@
24296
24296
  url.searchParams.delete('run');
24297
24297
  }
24298
24298
 
24299
- route(target, event, url);
24299
+ route(target, event, url, true);
24300
24300
  }
24301
24301
 
24302
24302
  function clearResult () {
24303
- const existingFetch = window.sqlFetch;
24304
- if (existingFetch?.state === 'pending') {
24305
- existingFetch.state = 'aborted';
24306
- existingFetch.fetchController.abort();
24303
+ if (window.sqlFetch?.state === 'pending' || window.sqlFetch?.spinner === 'always') {
24304
+ window.sqlFetch.state = 'aborted';
24305
+ window.sqlFetch.spinner = 'never';
24306
+ window.sqlFetch.fetchController.abort();
24307
+ displaySqlFetch(window.sqlFetch);
24308
+ return
24307
24309
  }
24308
24310
  window.sqlFetch = null;
24309
-
24311
+ clearSpinner();
24310
24312
  clearGraphBox();
24311
24313
  clearGraphStatus();
24312
-
24313
24314
  clearResultBox();
24314
24315
  clearResultStatus();
24315
24316
  }
@@ -24336,7 +24337,30 @@
24336
24337
  }
24337
24338
  }
24338
24339
 
24339
- function fetchSql (sqlFetch, selection, callback) {
24340
+ function updateResultTime (sqlFetch) {
24341
+ if (window.sqlFetch === sqlFetch) {
24342
+ if (sqlFetch.state === 'pending' || sqlFetch.spinner === 'always') {
24343
+ displaySqlFetch(sqlFetch);
24344
+ setTimeout(() => { updateResultTime(sqlFetch); }, 500);
24345
+ }
24346
+ }
24347
+ }
24348
+
24349
+ function fetchSql (sqlFetch) {
24350
+ window.sqlFetch = sqlFetch;
24351
+ updateResultTime(sqlFetch);
24352
+ setTimeout(function () {
24353
+ if (window.sqlFetch === sqlFetch && sqlFetch.state === 'pending') {
24354
+ window.sqlFetch.spinner = 'always';
24355
+ displaySqlFetch(sqlFetch);
24356
+ setTimeout(function () {
24357
+ if (window.sqlFetch === sqlFetch) {
24358
+ window.sqlFetch.spinner = 'if_pending';
24359
+ displaySqlFetch(sqlFetch);
24360
+ }
24361
+ }, 400); // If we display a spinner, ensure it is displayed for at least 400 ms
24362
+ }
24363
+ }, 300); // Don't display the spinner unless the response takes more than 300 ms
24340
24364
  fetch('query', {
24341
24365
  headers: {
24342
24366
  Accept: 'application/json',
@@ -24345,7 +24369,7 @@
24345
24369
  method: 'POST',
24346
24370
  body: JSON.stringify({
24347
24371
  sql: sqlFetch.sql,
24348
- selection
24372
+ selection: sqlFetch.selection
24349
24373
  }),
24350
24374
  signal: sqlFetch.fetchController.signal
24351
24375
  })
@@ -24368,14 +24392,14 @@
24368
24392
  sqlFetch.error_message = 'failed to execute query';
24369
24393
  }
24370
24394
  }
24371
- callback(sqlFetch);
24395
+ displaySqlFetch(sqlFetch);
24372
24396
  });
24373
24397
  } else {
24374
24398
  response.text().then((result) => {
24375
24399
  sqlFetch.state = 'error';
24376
24400
  sqlFetch.error_message = 'failed to execute query';
24377
24401
  sqlFetch.error_details = result;
24378
- callback(sqlFetch);
24402
+ displaySqlFetch(sqlFetch);
24379
24403
  });
24380
24404
  }
24381
24405
  })
@@ -24385,42 +24409,32 @@
24385
24409
  sqlFetch.error_message = 'failed to execute query';
24386
24410
  sqlFetch.error_details = error;
24387
24411
  }
24388
- callback(sqlFetch);
24412
+ displaySqlFetch(sqlFetch);
24389
24413
  });
24390
24414
  }
24391
24415
 
24392
- function maybeFetchResult () {
24416
+ function maybeFetchResult (internal) {
24393
24417
  const url = new URL(window.location);
24394
24418
  const params = url.searchParams;
24395
24419
  const sql = params.get('sql');
24396
24420
  const file = params.get('file');
24397
24421
  const selection = params.get('selection');
24398
- const run = ['1', 'true'].includes(params.get('run')?.toLowerCase());
24422
+ const hasSqluiReferrer = document.referrer && new URL(document.referrer).origin === url.origin;
24423
+
24424
+ // Only allow auto-run if coming from another SQLUI page. The idea here is to let the app link to URLs with run=true
24425
+ // but not other apps. This allows meta/shift-clicking to run a query.
24426
+ let run = false;
24427
+ if (params.has('run')) {
24428
+ run = (internal || hasSqluiReferrer) && ['1', 'true'].includes(params.get('run')?.toLowerCase());
24429
+ url.searchParams.delete('run');
24430
+ window.history.replaceState({}, '', url);
24431
+ }
24399
24432
 
24400
24433
  if (params.has('file') && params.has('sql')) {
24401
24434
  // TODO: show an error.
24402
24435
  throw new Error('You can only specify a file or sql, not both.')
24403
24436
  }
24404
24437
 
24405
- const sqlFetch = {
24406
- fetchController: new AbortController(),
24407
- state: 'pending',
24408
- sql,
24409
- file,
24410
- selection
24411
- };
24412
-
24413
- if (params.has('file')) {
24414
- const fileDetails = window.metadata.saved[params.get('file')];
24415
- if (!fileDetails) {
24416
- throw new Error(`no such file: ${params.get('file')}`)
24417
- }
24418
- sqlFetch.file = file;
24419
- sqlFetch.sql = fileDetails.contents;
24420
- } else if (params.has('sql')) {
24421
- sqlFetch.sql = sql;
24422
- }
24423
-
24424
24438
  const existingRequest = window.sqlFetch;
24425
24439
  if (existingRequest) {
24426
24440
  const selectionMatches = selection === existingRequest.selection;
@@ -24439,14 +24453,11 @@
24439
24453
 
24440
24454
  clearResult();
24441
24455
 
24456
+ const sqlFetch = buildSqlFetch(sql, file, selection);
24442
24457
  if (params.has('sql') || params.has('file')) {
24443
24458
  setValue(sqlFetch.sql);
24444
24459
  if (run) {
24445
- url.searchParams.delete('run');
24446
- window.history.replaceState({}, '', url);
24447
- window.sqlFetch = sqlFetch;
24448
- displaySqlFetch(sqlFetch);
24449
- fetchSql(sqlFetch, selection, displaySqlFetch);
24460
+ fetchSql(sqlFetch);
24450
24461
  }
24451
24462
  }
24452
24463
  if (params.has('selection')) {
@@ -24455,12 +24466,42 @@
24455
24466
  }
24456
24467
  }
24457
24468
 
24469
+ function buildSqlFetch (sql, file, selection) {
24470
+ const sqlFetch = {
24471
+ fetchController: new AbortController(),
24472
+ state: 'pending',
24473
+ startedAt: window.performance.now(),
24474
+ spinner: 'never',
24475
+ finished: null,
24476
+ sql,
24477
+ file,
24478
+ selection
24479
+ };
24480
+
24481
+ if (file) {
24482
+ const fileDetails = window.metadata.saved[file];
24483
+ if (!fileDetails) {
24484
+ throw new Error(`no such file: ${file}`)
24485
+ }
24486
+ sqlFetch.file = file;
24487
+ sqlFetch.sql = fileDetails.contents;
24488
+ } else if (sql) {
24489
+ sqlFetch.sql = sql;
24490
+ }
24491
+
24492
+ return sqlFetch
24493
+ }
24494
+
24458
24495
  function displaySqlFetchInResultTab (fetch) {
24459
- if (fetch.state === 'pending') {
24496
+ if (fetch.state === 'pending' || fetch.spinner === 'always') {
24460
24497
  clearResultBox();
24461
- document.getElementById('cancel-button').style.display = 'flex';
24462
- document.getElementById('result-box').style.display = 'none';
24463
- document.getElementById('fetch-sql-box').style.display = 'flex';
24498
+ if (fetch.spinner === 'never') {
24499
+ document.getElementById('result-box').style.display = 'flex';
24500
+ clearSpinner();
24501
+ } else {
24502
+ document.getElementById('result-box').style.display = 'none';
24503
+ displaySpinner(fetch);
24504
+ }
24464
24505
  return
24465
24506
  }
24466
24507
 
@@ -24470,6 +24511,7 @@
24470
24511
 
24471
24512
  if (fetch.state === 'aborted') {
24472
24513
  clearResultBox();
24514
+ document.getElementById('result-status').innerText = 'query cancelled';
24473
24515
  return
24474
24516
  }
24475
24517
 
@@ -24544,12 +24586,44 @@
24544
24586
  }
24545
24587
  }
24546
24588
 
24589
+ function clearSpinner () {
24590
+ document.getElementById('cancel-button').style.display = 'none';
24591
+ document.getElementById('fetch-sql-box').style.display = 'none';
24592
+ }
24593
+
24594
+ function displaySpinner (fetch) {
24595
+ document.getElementById('cancel-button').style.display = 'flex';
24596
+ document.getElementById('fetch-sql-box').style.display = 'flex';
24597
+
24598
+ const elapsed = window.performance.now() - fetch.startedAt;
24599
+ const seconds = Math.floor((elapsed / 1000) % 60);
24600
+ const minutes = Math.floor((elapsed / 1000 / 60) % 60);
24601
+ let display = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
24602
+ if (elapsed >= 1000 * 60 * 60) {
24603
+ const hours = Math.floor((elapsed / 1000 / 60 / 60) % 24);
24604
+ display = `${hours.toString().padStart(2, '0')}:${display}`;
24605
+ if (elapsed >= 1000 * 60 * 60 * 24) {
24606
+ const days = Math.floor(elapsed / 1000 / 60 / 60 / 24);
24607
+ if (days === 1) {
24608
+ display = `${days} day ${display}`;
24609
+ } else {
24610
+ display = `${days.toLocaleString()} days ${display}`;
24611
+ }
24612
+ }
24613
+ }
24614
+ document.getElementById('result-time').innerText = display;
24615
+ }
24616
+
24547
24617
  function displaySqlFetchInGraphTab (fetch) {
24548
- if (fetch.state === 'pending') {
24618
+ if (fetch.state === 'pending' || fetch.spinner === 'always') {
24549
24619
  clearGraphBox();
24550
- document.getElementById('cancel-button').style.display = 'flex';
24551
- document.getElementById('graph-box').style.display = 'none';
24552
- document.getElementById('fetch-sql-box').style.display = 'flex';
24620
+ if (fetch.spinner === 'never') {
24621
+ document.getElementById('graph-box').style.display = 'flex';
24622
+ clearSpinner();
24623
+ } else {
24624
+ document.getElementById('graph-box').style.display = 'none';
24625
+ displaySpinner(fetch);
24626
+ }
24553
24627
  return
24554
24628
  }
24555
24629
 
@@ -24559,6 +24633,7 @@
24559
24633
 
24560
24634
  if (fetch.state === 'aborted') {
24561
24635
  clearGraphBox();
24636
+ document.getElementById('graph-status').innerText = 'query cancelled';
24562
24637
  return
24563
24638
  }
24564
24639
 
@@ -24647,7 +24722,7 @@
24647
24722
  headers: {
24648
24723
  Accept: 'application/json'
24649
24724
  },
24650
- method: 'GET'
24725
+ method: 'POST'
24651
24726
  })
24652
24727
  ])
24653
24728
  .then((results) => {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqlui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.28
4
+ version: 0.1.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Dower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-01 00:00:00.000000000 Z
11
+ date: 2022-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mysql2
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.0'
27
- - !ruby/object:Gem::Dependency
28
- name: puma
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '6.0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '6.0'
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: sinatra
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +39,19 @@ dependencies:
53
39
  - !ruby/object:Gem::Version
54
40
  version: '3.0'
55
41
  - !ruby/object:Gem::Dependency
56
- name: puma
42
+ name: webrick
57
43
  requirement: !ruby/object:Gem::Requirement
58
44
  requirements:
59
45
  - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '6.0'
62
- type: :development
47
+ version: '1.0'
48
+ type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '6.0'
54
+ version: '1.0'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rspec-core
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -136,20 +122,6 @@ dependencies:
136
122
  - - "~>"
137
123
  - !ruby/object:Gem::Version
138
124
  version: '4.0'
139
- - !ruby/object:Gem::Dependency
140
- name: watir
141
- requirement: !ruby/object:Gem::Requirement
142
- requirements:
143
- - - "~>"
144
- - !ruby/object:Gem::Version
145
- version: '7.0'
146
- type: :development
147
- prerelease: false
148
- version_requirements: !ruby/object:Gem::Requirement
149
- requirements:
150
- - - "~>"
151
- - !ruby/object:Gem::Version
152
- version: '7.0'
153
125
  description: A SQL UI.
154
126
  email: nicholasdower@gmail.com
155
127
  executables:
@@ -162,7 +134,6 @@ files:
162
134
  - app/database_config.rb
163
135
  - app/database_metadata.rb
164
136
  - app/deep.rb
165
- - app/environment.rb
166
137
  - app/mysql_types.rb
167
138
  - app/server.rb
168
139
  - app/sql_parser.rb
data/app/environment.rb DELETED
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Parses and provides access to environment variables.
4
- class Environment
5
- APP_ENV = ENV.fetch('APP_ENV', 'development').to_sym
6
- APP_PORT = ENV.fetch('APP_PORT', 8080)
7
-
8
- def self.server_env
9
- APP_ENV
10
- end
11
-
12
- def self.development?
13
- APP_ENV == :development
14
- end
15
-
16
- def self.production?
17
- APP_ENV == :production
18
- end
19
-
20
- def self.server_port
21
- APP_PORT
22
- end
23
- end