diff --git a/assets/javascripts/discourse/components/param-field.js.es6 b/assets/javascripts/discourse/components/param-field.js.es6
index 89d1192..17dfb37 100644
--- a/assets/javascripts/discourse/components/param-field.js.es6
+++ b/assets/javascripts/discourse/components/param-field.js.es6
@@ -4,5 +4,5 @@ export default Ember.TextField.extend({
this.get('params')[this.get('pname')] = value;
}
return this.get('params')[this.get('pname')];
- }.property()
+ }.property('params', 'pname')
});
diff --git a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6 b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
index 1a74ec2..47ffd0c 100644
--- a/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
+++ b/assets/javascripts/discourse/controllers/admin-plugins-explorer.js.es6
@@ -137,14 +137,17 @@ export default Ember.ArrayController.extend({
explain: this.get('explain')
}
}).then(function(result) {
+ self.set('results', result);
if (!result.success) {
- return popupAjaxError(result);
+ self.set('showResults', false);
+ return;
}
- console.log(result);
self.set('showResults', true);
+ }).catch(function(result) {
+ self.set('showResults', false);
self.set('results', result);
- }).catch(popupAjaxError).finally(function() {
+ }).finally(function() {
self.set('loading', false);
});
}
diff --git a/assets/javascripts/discourse/models/query.js.es6 b/assets/javascripts/discourse/models/query.js.es6
index a6bb643..2c965c3 100644
--- a/assets/javascripts/discourse/models/query.js.es6
+++ b/assets/javascripts/discourse/models/query.js.es6
@@ -5,10 +5,20 @@ Query = RestModel.extend({
dirty: false,
params: {},
+ _init: function() {
+ this._super();
+ if (!this.get('options')) {
+ this.set('options', {defaults:{}});
+ }
+ this.set('dirty', false);
+ }.on('init'),
+
_initParams: function() {
this.resetParams();
}.on('init').observes('param_names'),
+ options: Em.computed.alias('qopts'),
+
markDirty: function() {
this.set('dirty', true);
}.observes('name', 'description', 'sql', 'options', 'options.defaults'),
@@ -37,7 +47,11 @@ Query = RestModel.extend({
const currentParams = this.get('params');
let defaults = {};
(this.get('param_names') || []).forEach(function(name) {
- defaults[name] = currentParams[name];
+ if (currentParams[name]) {
+ defaults[name] = currentParams[name];
+ } else {
+ delete defaults[name];
+ }
});
this.set('options.defaults', defaults);
},
@@ -80,7 +94,7 @@ Query = RestModel.extend({
});
Query.reopenClass({
- updatePropertyNames: ["name", "description", "sql", "options"]
+ updatePropertyNames: ["name", "description", "sql", "qopts"]
});
export default Query;
diff --git a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs
index c34c7e5..3d1396f 100644
--- a/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs
+++ b/assets/javascripts/discourse/templates/admin/plugins-explorer.hbs
@@ -1,24 +1,38 @@
-
Queries
+Queries
+
{{combo-box valueAttribute="id" value=selectedQueryId nameProperty="listName" content=content castInteger="true" nameChanges="true"}}
{{d-button action="showCreate" icon="plus" class="no-text"}}
{{d-button action="importQuery" label="explorer.import.label" icon="upload"}}
+
{{#if showCreate}}
{{text-field value=newQueryName placeholderKey="explorer.create_placeholder"}}
{{d-button action="create" label="explorer.create" icon="plus" class="btn-primary"}}
{{/if}}
+
+
{{partial "admin/plugins-explorer-show" model=selectedItem}}
+
+{{conditional-loading-spinner condition=loading}}
+
{{#if selectedItem.param_names}}
+
+ {{d-button action="saveDefaults" label="explorer.save_params"}}
+ {{d-button action="resetParams" label="explorer.reset_params"}}
+
{{#each selectedItem.param_names as |pname|}}
- {{param-field params=selectedItem.params pname=pname}} {{pname}}
+
+ {{param-field params=selectedItem.params pname=pname}}
+ {{pname}}
+
{{/each}}
{{/if}}
@@ -27,10 +41,17 @@
{{d-button action="run" label="explorer.run" disabled=runDisabled}}
+
-{{conditional-loading-spinner condition=loading}}
+
{{#if results}}
- {{query-result query=selectedItem content=results}}
+ {{#if showResults}}
+ {{query-result query=selectedItem content=results}}
+ {{else}}
+ {{#each results.errors as |err|}}
+
{{~err}}
+ {{/each}}
+ {{/if}}
{{/if}}
diff --git a/assets/stylesheets/explorer.scss b/assets/stylesheets/explorer.scss
index 96bf019..7b5c041 100644
--- a/assets/stylesheets/explorer.scss
+++ b/assets/stylesheets/explorer.scss
@@ -33,6 +33,18 @@
input {
margin: 9px;
}
+ .param {
+ display: inline-block;
+ overflow-x: visible;
+ }
+ .param-save {
+ float: right;
+ margin: 9px;
+ }
+ .param-name {
+ display: inline-block;
+ width: 70px;
+ }
}
.query-list, .query-edit, .query-results, .query-params {
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index e4a238c..1484ea8 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -38,3 +38,5 @@ en:
run_time: "Query completed in {{value}} ms."
column: "Column {{number}}"
explain_label: "Include query plan?"
+ save_params: "Set Defaults"
+ reset_params: "Reset"
diff --git a/plugin.rb b/plugin.rb
index b3b83d1..876f4b7 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -79,6 +79,15 @@ after_initialize do
query_args = (query.qopts[:defaults] || {}).with_indifferent_access.merge(params)
+ # Rudimentary types
+ query_args.map! do |arg|
+ if arg =~ /\A\d+\z/
+ arg.to_i
+ else
+ arg
+ end
+ end
+
time_start, time_end, explain, err, result = nil
begin
ActiveRecord::Base.connection.transaction do
@@ -89,7 +98,7 @@ after_initialize do
/*
* DataExplorer Query
- * Query: /admin/plugins/explorer/#{query.id}
+ * Query: /admin/plugins/explorer?id=#{query.id}
* Started by: #{opts[:current_user]}
*/
WITH query AS (
@@ -290,11 +299,20 @@ SQL
render json: {success: true, errors: []}
end
+ # Return value:
+ # success - true/false. if false, inspect the errors value.
+ # errors - array of strings.
+ # params - hash. Echo of the query parameters as executed.
+ # duration - float. Time to execute the query, in milliseconds, to 1 decimal place.
+ # columns - array of strings. Titles of the returned columns, in order.
+ # explain - string. (Optional - pass explain=true in the request) Postgres query plan, UNIX newlines.
+ # rows - array of array of strings. Results of the query. In the same order as 'columns'.
def run
query = DataExplorer::Query.find(params[:id].to_i)
query_params = MultiJson.load(params[:params])
opts = {current_user: current_user.username}
opts[:explain] = true if params[:explain] == "true"
+ opts[:limit] = params[:limit].to_i if params[:limit]
result = DataExplorer.run_query(query, query_params, opts)
if result[:error]
@@ -320,7 +338,6 @@ SQL
json = {
success: true,
errors: [],
- params: query_params,
duration: (result[:duration_nanos].to_f / 1_000_000).round(1),
columns: cols,
}