run Prettier

This commit is contained in:
Rishabh Nambiar 2018-10-10 17:26:23 +05:30
parent b352e747c7
commit 9d7db064af
18 changed files with 543 additions and 460 deletions

View File

@ -1,5 +1,3 @@
import buildPluginAdapter from 'admin/adapters/build-plugin';
import buildPluginAdapter from "admin/adapters/build-plugin";
export default buildPluginAdapter('explorer').extend({
});
export default buildPluginAdapter("explorer").extend({});

View File

@ -1,61 +1,60 @@
import { observes } from 'ember-addons/ember-computed-decorators';
import { observes } from "ember-addons/ember-computed-decorators";
export default Ember.Component.extend({
@observes('hideSchema')
@observes("hideSchema")
_onHideSchema() {
this.appEvents.trigger('ace:resize');
this.appEvents.trigger("ace:resize");
},
@observes('everEditing')
@observes("everEditing")
_onInsertEditor() {
Ember.run.schedule('afterRender', this, () => this._bindControls());
Ember.run.schedule("afterRender", this, () => this._bindControls());
},
_bindControls() {
if (this._state !== "inDOM") {
return;
}
const $editPane = this.$().find('.query-editor');
const $editPane = this.$().find(".query-editor");
if (!$editPane.length) {
return;
}
const oldGrippie = this.get('grippie');
const oldGrippie = this.get("grippie");
if (oldGrippie) {
oldGrippie.off('mousedown mousemove mouseup');
$editPane.off('mousemove mouseup');
oldGrippie.off("mousedown mousemove mouseup");
$editPane.off("mousemove mouseup");
}
const $grippie = $editPane.find('.grippie');
const $targets = $editPane.find('.ace-wrapper,.grippie-target');
const $body = $('body');
const $grippie = $editPane.find(".grippie");
const $targets = $editPane.find(".ace-wrapper,.grippie-target");
const $body = $("body");
const self = this;
this.set('grippie', $grippie);
this.set("grippie", $grippie);
const mousemove = function(e) {
const diff = self.get('startY') - e.screenY;
const newHeight = self.get('startSize') - diff;
const diff = self.get("startY") - e.screenY;
const newHeight = self.get("startSize") - diff;
//Em.Logger.debug("new height", newHeight);
$targets.height(newHeight);
self.appEvents.trigger('ace:resize');
self.appEvents.trigger("ace:resize");
};
let mouseup;
mouseup = function(e) {
mousemove(e);
$body.off('mousemove', mousemove);
$body.off('mouseup', mouseup);
self.set('startY', null);
self.set('startSize', null);
$body.off("mousemove", mousemove);
$body.off("mouseup", mouseup);
self.set("startY", null);
self.set("startSize", null);
};
$grippie.on('mousedown', function(e) {
self.set('startY', e.screenY);
self.set('startSize', $targets.height());
$grippie.on("mousedown", function(e) {
self.set("startY", e.screenY);
self.set("startSize", $targets.height());
$body.on('mousemove', mousemove);
$body.on('mouseup', mouseup);
$body.on("mousemove", mousemove);
$body.on("mouseup", mouseup);
e.preventDefault();
});
},
@ -67,10 +66,9 @@ export default Ember.Component.extend({
willDestroyElement() {
this._super();
if (this.get('everEditing')) {
this.get('grippie').off('mousedown');
this.set('grippie', null);
if (this.get("everEditing")) {
this.get("grippie").off("mousedown");
this.set("grippie", null);
}
}
});

View File

@ -1,13 +1,15 @@
export default Ember.Component.extend({
tagName: 'ol',
tagName: "ol",
enuminfo: function() {
const hash = this.get('col.enum');
const hash = this.get("col.enum");
let result = [];
for (let key in hash) {
if (!hash.hasOwnProperty(key)) { continue; }
result.push({value: key, name: hash[key]});
if (!hash.hasOwnProperty(key)) {
continue;
}
result.push({ value: key, name: hash[key] });
}
return result;
}.property('col.enum')
}.property("col.enum")
});

View File

@ -1,18 +1,22 @@
export default Ember.Component.extend({
classNameBindings: [':schema-table', 'open'],
tagName: 'li',
classNameBindings: [":schema-table", "open"],
tagName: "li",
open: Em.computed.alias('table.open'),
open: Em.computed.alias("table.open"),
_bindClicks: function() {
const self = this;
this.$().find('.schema-table-name').click(function(e) {
self.set('open', !self.get('open'));
e.preventDefault();
});
}.on('didInsertElement'),
this.$()
.find(".schema-table-name")
.click(function(e) {
self.set("open", !self.get("open"));
e.preventDefault();
});
}.on("didInsertElement"),
_cleanup: function() {
this.$().find('.schema-table-name').off('click');
}.on('willDestroyElement')
this.$()
.find(".schema-table-name")
.off("click");
}.on("willDestroyElement")
});

View File

@ -1,18 +1,17 @@
import debounce from 'discourse/lib/debounce';
import debounce from "discourse/lib/debounce";
export default Ember.Component.extend({
actions: {
expandSchema() {
this.set('hideSchema', false);
this.set("hideSchema", false);
},
collapseSchema() {
this.set('hideSchema', true);
this.set("hideSchema", true);
}
},
transformedSchema: function() {
const schema = this.get('schema');
const schema = this.get("schema");
for (let key in schema) {
if (!schema.hasOwnProperty(key)) {
@ -47,21 +46,20 @@ export default Ember.Component.extend({
}
col.havetypeinfo = !!(col.notes || col.enum || col.column_desc);
});
}
return schema;
}.property('schema'),
}.property("schema"),
rfilter: function() {
if (!Em.isBlank(this.get('filter'))) {
return new RegExp(this.get('filter'));
if (!Em.isBlank(this.get("filter"))) {
return new RegExp(this.get("filter"));
}
}.property('filter'),
}.property("filter"),
filterTables: function(schema) {
let tables = [];
const filter = this.get('rfilter'),
const filter = this.get("rfilter"),
haveFilter = !!filter;
for (let key in schema) {
@ -114,17 +112,20 @@ export default Ember.Component.extend({
},
triggerFilter: debounce(function() {
this.set('filteredTables', this.filterTables(this.get('transformedSchema')));
this.set('loading', false);
}, 500).observes('filter'),
this.set(
"filteredTables",
this.filterTables(this.get("transformedSchema"))
);
this.set("loading", false);
}, 500).observes("filter"),
setLoading: function() {
this.set('loading', true);
}.observes('filter'),
this.set("loading", true);
}.observes("filter"),
init() {
this._super();
this.set('loading', true);
this.set("loading", true);
this.triggerFilter();
}
});

View File

@ -1,19 +1,21 @@
import debounce from 'discourse/lib/debounce';
import highlightSyntax from 'discourse/lib/highlight-syntax';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import debounce from "discourse/lib/debounce";
import highlightSyntax from "discourse/lib/highlight-syntax";
import { bufferedRender } from "discourse-common/lib/buffered-render";
export default Ember.Component.extend(bufferedRender({
buildBuffer(buffer) {
buffer.push("<pre><code class='" + this.get('codeClass') + "'>");
buffer.push(Handlebars.Utils.escapeExpression(this.get('value')));
buffer.push("</code></pre>");
},
export default Ember.Component.extend(
bufferedRender({
buildBuffer(buffer) {
buffer.push("<pre><code class='" + this.get("codeClass") + "'>");
buffer.push(Handlebars.Utils.escapeExpression(this.get("value")));
buffer.push("</code></pre>");
},
_refreshHighlight: debounce(function() {
this.rerenderBuffer();
}, 50).observes('value'),
_refreshHighlight: debounce(function() {
this.rerenderBuffer();
}, 50).observes("value"),
_applyHighlight: function() {
highlightSyntax(this.$());
}.on('didInsertElement')
}));
_applyHighlight: function() {
highlightSyntax(this.$());
}.on("didInsertElement")
})
);

View File

@ -4,68 +4,70 @@ export default Ember.Component.extend({
expectedRootObjectName: null,
hover: 0,
classNames: ['json-uploader'],
classNames: ["json-uploader"],
_initialize: function() {
const $this = this.$();
const self = this;
const $fileInput = $this.find('#js-file-input');
this.set('fileInput', $fileInput[0]);
const $fileInput = $this.find("#js-file-input");
this.set("fileInput", $fileInput[0]);
$fileInput.on('change', function() {
$fileInput.on("change", function() {
self.fileSelected(this.files);
});
$this.on('dragover', function(e) {
$this.on("dragover", function(e) {
if (e.preventDefault) e.preventDefault();
return false;
});
$this.on('dragenter', function(e) {
$this.on("dragenter", function(e) {
if (e.preventDefault) e.preventDefault();
self.set('hover', self.get('hover') + 1);
self.set("hover", self.get("hover") + 1);
return false;
});
$this.on('dragleave', function(e) {
$this.on("dragleave", function(e) {
if (e.preventDefault) e.preventDefault();
self.set('hover', self.get('hover') - 1);
self.set("hover", self.get("hover") - 1);
return false;
});
$this.on('drop', function(e) {
$this.on("drop", function(e) {
if (e.preventDefault) e.preventDefault();
self.set('hover', 0);
self.set("hover", 0);
self.fileSelected(e.dataTransfer.files);
return false;
});
}.on('didInsertElement'),
}.on("didInsertElement"),
accept: function() {
return ".json,application/json,application/x-javascript,text/json" + (this.get('extension') ? "," + this.get('extension') : "");
}.property('extension'),
return (
".json,application/json,application/x-javascript,text/json" +
(this.get("extension") ? "," + this.get("extension") : "")
);
}.property("extension"),
setReady: function() {
let parsed;
try {
parsed = JSON.parse(this.get('value'));
parsed = JSON.parse(this.get("value"));
} catch (e) {
this.set('ready', false);
this.set("ready", false);
return;
}
const rootObject = parsed[this.get('expectedRootObjectName')];
const rootObject = parsed[this.get("expectedRootObjectName")];
if (rootObject !== null && rootObject !== undefined) {
this.set('ready', true);
this.set("ready", true);
} else {
this.set('ready', false);
this.set("ready", false);
}
}.observes('destination', 'expectedRootObjectName'),
}.observes("destination", "expectedRootObjectName"),
actions: {
selectFile: function() {
const $fileInput = $(this.get('fileInput'));
const $fileInput = $(this.get("fileInput"));
$fileInput.click();
}
},
@ -88,15 +90,14 @@ export default Ember.Component.extend({
});
const firstFile = fileList[0];
this.set('loading', true);
this.set("loading", true);
let reader = new FileReader();
reader.onload = function(evt) {
self.set('value', evt.target.result);
self.set('loading', false);
self.set("value", evt.target.result);
self.set("loading", false);
};
reader.readAsText(firstFile);
}
});

View File

@ -1,9 +1,9 @@
export default Ember.TextField.extend({
value: function(key, value) {
if (arguments.length > 1) {
this.get('params')[this.get('pname')] = value;
this.get("params")[this.get("pname")] = value;
}
return this.get('params')[this.get('pname')];
}.property('params', 'pname')
return this.get("params")[this.get("pname")];
}.property("params", "pname")
});

View File

@ -2,30 +2,30 @@
const Category = Discourse.Category;
const layoutMap = {
int: 'int',
bigint: 'int',
boolean: 'boolean',
string: 'generic',
time: 'generic',
date: 'generic',
datetime: 'generic',
double: 'string',
user_id: 'user_id',
post_id: 'string',
topic_id: 'generic',
category_id: 'generic',
group_id: 'generic',
badge_id: 'generic',
int_list: 'generic',
string_list: 'generic',
user_list: 'user_list'
int: "int",
bigint: "int",
boolean: "boolean",
string: "generic",
time: "generic",
date: "generic",
datetime: "generic",
double: "string",
user_id: "user_id",
post_id: "string",
topic_id: "generic",
category_id: "generic",
group_id: "generic",
badge_id: "generic",
int_list: "generic",
string_list: "generic",
user_list: "user_list"
};
function allowsInputTypeTime() {
try {
const inp = document.createElement('input');
inp.attributes.type = 'time';
inp.attributes.type = 'date';
const inp = document.createElement("input");
inp.attributes.type = "time";
inp.attributes.type = "date";
return true;
} catch (e) {
return false;
@ -33,77 +33,88 @@ function allowsInputTypeTime() {
}
export default Ember.Component.extend({
classNameBindings: ["valid:valid:invalid", ":param"],
classNameBindings: ['valid:valid:invalid', ':param'],
boolTypes: [
{ name: I18n.t("explorer.types.bool.true"), id: "Y" },
{ name: I18n.t("explorer.types.bool.false"), id: "N" },
{ name: I18n.t("explorer.types.bool.null_"), id: "#null" }
],
boolTypes: [ {name: I18n.t('explorer.types.bool.true'), id: 'Y'}, {name: I18n.t('explorer.types.bool.false'), id: 'N'}, {name: I18n.t('explorer.types.bool.null_'), id: '#null'} ],
value: Ember.computed('params', 'info.identifier', {
value: Ember.computed("params", "info.identifier", {
get() {
return this.get('params')[this.get('info.identifier')];
return this.get("params")[this.get("info.identifier")];
},
set(key, value) {
this.get('params')[this.get('info.identifier')] = value.toString();
this.get("params")[this.get("info.identifier")] = value.toString();
return value;
}
}),
valueBool: Ember.computed('params', 'info.identifier', {
valueBool: Ember.computed("params", "info.identifier", {
get() {
return this.get('params')[this.get('info.identifier')] !== 'false';
return this.get("params")[this.get("info.identifier")] !== "false";
},
set(key, value) {
value = !!value;
this.get('params')[this.get('info.identifier')] = value.toString();
this.get("params")[this.get("info.identifier")] = value.toString();
return value;
}
}),
valid: function() {
const type = this.get('info.type'),
value = this.get('value');
const type = this.get("info.type"),
value = this.get("value");
if (Em.isEmpty(this.get('value'))) {
return this.get('info.nullable');
if (Em.isEmpty(this.get("value"))) {
return this.get("info.nullable");
}
const intVal = parseInt(value, 10);
const intValid = !isNaN(intVal) && intVal < 2147483648 && intVal > -2147483649;
const intValid =
!isNaN(intVal) && intVal < 2147483648 && intVal > -2147483649;
const isPositiveInt = /^\d+$/.test(value);
switch (type) {
case 'int':
case "int":
return /^-?\d+$/.test(value) && intValid;
case 'bigint':
case "bigint":
return /^-?\d+$/.test(value) && !isNaN(intVal);
case 'boolean':
case "boolean":
return /^Y|N|#null|true|false/.test(value);
case 'double':
return !isNaN(parseFloat(value)) || /^(-?)Inf(inity)?$/i.test(value) || /^(-?)NaN$/i.test(value);
case 'int_list':
return value.split(',').every(function(i) {
case "double":
return (
!isNaN(parseFloat(value)) ||
/^(-?)Inf(inity)?$/i.test(value) ||
/^(-?)NaN$/i.test(value)
);
case "int_list":
return value.split(",").every(function(i) {
return /^(-?\d+|null)$/.test(i.trim());
});
case 'post_id':
case "post_id":
return isPositiveInt || /\d+\/\d+(\?u=.*)?$/.test(value);
case 'category_id':
case "category_id":
if (!isPositiveInt && value !== value.dasherize()) {
this.set('value', value.dasherize());
this.set("value", value.dasherize());
}
if (isPositiveInt) {
return !!this.site.categories.find(function(c) {
return c.get('id') === intVal;
return c.get("id") === intVal;
});
} else if (/\//.test(value)) {
const match = /(.*)\/(.*)/.exec(value);
if (!match) return false;
const result = Category.findBySlug(match[2].dasherize(), match[1].dasherize());
const result = Category.findBySlug(
match[2].dasherize(),
match[1].dasherize()
);
return !!result;
} else {
return !!Category.findBySlug(value.dasherize());
}
case 'group_id':
const groups = this.site.get('groups');
case "group_id":
const groups = this.site.get("groups");
if (isPositiveInt) {
return !!groups.find(function(g) {
return g.id === intVal;
@ -115,20 +126,20 @@ export default Ember.Component.extend({
}
}
return true;
}.property('value', 'info.type', 'info.nullable'),
}.property("value", "info.type", "info.nullable"),
layoutType: function() {
const type = this.get('info.type');
const type = this.get("info.type");
if ((type === "time" || type === "date") && !allowsInputTypeTime()) {
return "string";
}
if (layoutMap[type]) {
return layoutMap[type];
}
return 'generic';
}.property('info.type'),
return "generic";
}.property("info.type"),
layoutName: function() {
return "admin/components/q-params/" + this.get('layoutType');
}.property('layoutType')
return "admin/components/q-params/" + this.get("layoutType");
}.property("layoutType")
});

View File

@ -1,12 +1,12 @@
import { ajax } from 'discourse/lib/ajax';
import Badge from 'discourse/models/badge';
import { getOwner } from 'discourse-common/lib/get-owner';
import { default as computed } from 'ember-addons/ember-computed-decorators';
import { ajax } from "discourse/lib/ajax";
import Badge from "discourse/models/badge";
import { getOwner } from "discourse-common/lib/get-owner";
import { default as computed } from "ember-addons/ember-computed-decorators";
function randomIdShort() {
return 'xxxxxxxx'.replace(/[xy]/g, function() {
return "xxxxxxxx".replace(/[xy]/g, function() {
/*eslint-disable*/
return (Math.random() * 16 | 0).toString(16);
return ((Math.random() * 16) | 0).toString(16);
/*eslint-enable*/
});
}
@ -24,153 +24,167 @@ function transformedRelTable(table, modelClass) {
}
const QueryResultComponent = Ember.Component.extend({
layoutName: 'explorer-query-result',
layoutName: "explorer-query-result",
rows: Em.computed.alias('content.rows'),
columns: Em.computed.alias('content.columns'),
params: Em.computed.alias('content.params'),
explainText: Em.computed.alias('content.explain'),
hasExplain: Em.computed.notEmpty('content.explain'),
rows: Em.computed.alias("content.rows"),
columns: Em.computed.alias("content.columns"),
params: Em.computed.alias("content.params"),
explainText: Em.computed.alias("content.explain"),
hasExplain: Em.computed.notEmpty("content.explain"),
@computed('content.result_count')
@computed("content.result_count")
resultCount: function(count) {
if (count === 1000) {
return I18n.t('explorer.max_result_count', { count });
return I18n.t("explorer.max_result_count", { count });
} else {
return I18n.t('explorer.result_count', { count });
return I18n.t("explorer.result_count", { count });
}
},
colCount: function() {
return this.get('content.columns').length;
}.property('content.columns.length'),
return this.get("content.columns").length;
}.property("content.columns.length"),
duration: function() {
return I18n.t('explorer.run_time', {value: I18n.toNumber(this.get('content.duration'), {precision: 1})});
}.property('content.duration'),
return I18n.t("explorer.run_time", {
value: I18n.toNumber(this.get("content.duration"), { precision: 1 })
});
}.property("content.duration"),
parameterAry: function() {
let arr = [];
const params = this.get('params');
const params = this.get("params");
for (var key in params) {
if (params.hasOwnProperty(key)) {
arr.push({key: key, value: params[key]});
arr.push({ key: key, value: params[key] });
}
}
return arr;
}.property('params.@each'),
}.property("params.@each"),
columnDispNames: function() {
if (!this.get('columns')) {
if (!this.get("columns")) {
return [];
}
return this.get('columns').map(function(colName) {
return this.get("columns").map(function(colName) {
if (colName.endsWith("_id")) {
return colName.slice(0, -3);
}
const dIdx = colName.indexOf('$');
const dIdx = colName.indexOf("$");
if (dIdx >= 0) {
return colName.substring(dIdx + 1);
}
return colName;
});
}.property('content', 'columns.@each'),
}.property("content", "columns.@each"),
fallbackTemplate: function() {
return getOwner(this).lookup('template:explorer/text.raw');
return getOwner(this).lookup("template:explorer/text.raw");
}.property(),
columnTemplates: function() {
const self = this;
if (!this.get('columns')) {
if (!this.get("columns")) {
return [];
}
return this.get('columns').map(function(colName, idx) {
return this.get("columns").map(function(colName, idx) {
let viewName = "text";
if (self.get('content.colrender')[idx]) {
viewName = self.get('content.colrender')[idx];
if (self.get("content.colrender")[idx]) {
viewName = self.get("content.colrender")[idx];
}
// After `findRawTemplates` is in stable this should be updated to use that
let template = getOwner(self).lookup('template:explorer/' + viewName + '.raw');
let template = getOwner(self).lookup(
"template:explorer/" + viewName + ".raw"
);
if (!template) {
template = Discourse.RAW_TEMPLATES[`javascripts/explorer/${viewName}`];
}
return {name: viewName, template };
return { name: viewName, template };
});
}.property('content', 'columns.@each'),
}.property("content", "columns.@each"),
transformedUserTable: function() {
return transformedRelTable(this.get('content.relations.user'));
}.property('content.relations.user'),
return transformedRelTable(this.get("content.relations.user"));
}.property("content.relations.user"),
transformedBadgeTable: function() {
return transformedRelTable(this.get('content.relations.badge'), Badge);
}.property('content.relations.badge'),
return transformedRelTable(this.get("content.relations.badge"), Badge);
}.property("content.relations.badge"),
transformedPostTable: function() {
return transformedRelTable(this.get('content.relations.post'));
}.property('content.relations.post'),
return transformedRelTable(this.get("content.relations.post"));
}.property("content.relations.post"),
transformedTopicTable: function() {
return transformedRelTable(this.get('content.relations.topic'));
}.property('content.relations.topic'),
return transformedRelTable(this.get("content.relations.topic"));
}.property("content.relations.topic"),
transformedGroupTable: function() {
return transformedRelTable(this.get('site.groups'));
}.property('site.groups'),
return transformedRelTable(this.get("site.groups"));
}.property("site.groups"),
lookupUser(id) {
return this.get('transformedUserTable')[id];
return this.get("transformedUserTable")[id];
},
lookupBadge(id) {
return this.get('transformedBadgeTable')[id];
return this.get("transformedBadgeTable")[id];
},
lookupPost(id) {
return this.get('transformedPostTable')[id];
return this.get("transformedPostTable")[id];
},
lookupTopic(id) {
return this.get('transformedTopicTable')[id];
return this.get("transformedTopicTable")[id];
},
lookupGroup(id) {
return this.get('transformedGroupTable')[id];
return this.get("transformedGroupTable")[id];
},
lookupCategory(id) {
return this.site.get('categoriesById')[id];
return this.site.get("categoriesById")[id];
},
downloadResult(format) {
// Create a frame to submit the form in (?)
// to avoid leaving an about:blank behind
let windowName = randomIdShort();
const newWindowContents = "<style>body{font-size:36px;display:flex;justify-content:center;align-items:center;}</style><body>Click anywhere to close this window once the download finishes.<script>window.onclick=function(){window.close()};</script>";
const newWindowContents =
"<style>body{font-size:36px;display:flex;justify-content:center;align-items:center;}</style><body>Click anywhere to close this window once the download finishes.<script>window.onclick=function(){window.close()};</script>";
window.open('data:text/html;base64,' + btoa(newWindowContents), windowName);
window.open("data:text/html;base64," + btoa(newWindowContents), windowName);
let form = document.createElement("form");
form.setAttribute('id', 'query-download-result');
form.setAttribute('method', 'post');
form.setAttribute('action', Discourse.getURL('/admin/plugins/explorer/queries/' + this.get('query.id') + '/run.' + format + '?download=1'));
form.setAttribute('target', windowName);
form.setAttribute('style', 'display:none;');
form.setAttribute("id", "query-download-result");
form.setAttribute("method", "post");
form.setAttribute(
"action",
Discourse.getURL(
"/admin/plugins/explorer/queries/" +
this.get("query.id") +
"/run." +
format +
"?download=1"
)
);
form.setAttribute("target", windowName);
form.setAttribute("style", "display:none;");
function addInput(name, value) {
let field;
field = document.createElement('input');
field.setAttribute('name', name);
field.setAttribute('value', value);
field = document.createElement("input");
field.setAttribute("name", name);
field.setAttribute("value", value);
form.appendChild(field);
}
addInput('params', JSON.stringify(this.get('params')));
addInput('explain', this.get('hasExplain'));
addInput('limit', '1000000');
addInput("params", JSON.stringify(this.get("params")));
addInput("explain", this.get("hasExplain"));
addInput("limit", "1000000");
ajax('/session/csrf.json').then(function(csrf) {
addInput('authenticity_token', csrf.csrf);
ajax("/session/csrf.json").then(function(csrf) {
addInput("authenticity_token", csrf.csrf);
document.body.appendChild(form);
form.submit();
Em.run.next('afterRender', function() {
Em.run.next("afterRender", function() {
document.body.removeChild(form);
});
});
@ -178,13 +192,12 @@ const QueryResultComponent = Ember.Component.extend({
actions: {
downloadResultJson() {
this.downloadResult('json');
this.downloadResult("json");
},
downloadResultCsv() {
this.downloadResult('csv');
this.downloadResult("csv");
}
}
});
export default QueryResultComponent;

View File

@ -1,12 +1,14 @@
import { categoryLinkHTML } from 'discourse/helpers/category-link';
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { categoryLinkHTML } from "discourse/helpers/category-link";
import { autoUpdatingRelativeAge } from "discourse/lib/formatter";
import { bufferedRender } from "discourse-common/lib/buffered-render";
function icon_or_image_replacement(str, ctx) {
str = Ember.get(ctx.contexts[0], str);
if (Ember.isEmpty(str)) { return ""; }
if (Ember.isEmpty(str)) {
return "";
}
if (str.indexOf('fa-') === 0) {
if (str.indexOf("fa-") === 0) {
return new Handlebars.SafeString("<i class='fa " + str + "'></i>");
} else {
return new Handlebars.SafeString("<img src='" + str + "'>");
@ -16,64 +18,78 @@ function icon_or_image_replacement(str, ctx) {
function category_badge_replacement(str, ctx) {
const category = Ember.get(ctx.contexts[0], str);
return categoryLinkHTML(category, {
allowUncategorized: true,
allowUncategorized: true
});
}
function bound_date_replacement(str, ctx) {
const value = Ember.get(ctx.contexts[0], str);
return new Handlebars.SafeString(autoUpdatingRelativeAge(new Date(value), {title: true }));
return new Handlebars.SafeString(
autoUpdatingRelativeAge(new Date(value), { title: true })
);
}
const esc = Handlebars.Utils.escapeExpression;
const QueryRowContentComponent = Ember.Component.extend(bufferedRender({
tagName: "tr",
const QueryRowContentComponent = Ember.Component.extend(
bufferedRender({
tagName: "tr",
buildBuffer(buffer) {
const self = this;
const row = this.get('row');
const parentView = self.get('parentView');
const fallback = this.get('fallbackTemplate');
const helpers = {
"icon-or-image": icon_or_image_replacement,
"category-link": category_badge_replacement,
"reltime": bound_date_replacement,
};
buildBuffer(buffer) {
const self = this;
const row = this.get("row");
const parentView = self.get("parentView");
const fallback = this.get("fallbackTemplate");
const helpers = {
"icon-or-image": icon_or_image_replacement,
"category-link": category_badge_replacement,
reltime: bound_date_replacement
};
const parts = this.get('columnTemplates').map(function(t, idx) {
const value = row[idx],
id = parseInt(value);
const parts = this.get("columnTemplates").map(function(t, idx) {
const value = row[idx],
id = parseInt(value);
const ctx = {value, id, baseuri: Discourse.BaseUri === '/' ? '' : Discourse.BaseUri };
const params = {};
const ctx = {
value,
id,
baseuri: Discourse.BaseUri === "/" ? "" : Discourse.BaseUri
};
const params = {};
if (row[idx] === null) {
return "NULL";
} else if (t.name === "text") {
return esc(row[idx]);
}
if (row[idx] === null) {
return "NULL";
} else if (t.name === "text") {
return esc(row[idx]);
}
const lookupFunc = parentView[`lookup${t.name.capitalize()}`];
if (lookupFunc) {
ctx[t.name] = parentView[`lookup${t.name.capitalize()}`](id);
}
const lookupFunc = parentView[`lookup${t.name.capitalize()}`];
if (lookupFunc) {
ctx[t.name] = parentView[`lookup${t.name.capitalize()}`](id);
}
if (t.name === "category" || t.name === "badge" || t.name === "reltime") {
// only replace helpers if needed
params.helpers = helpers;
}
if (
t.name === "category" ||
t.name === "badge" ||
t.name === "reltime"
) {
// only replace helpers if needed
params.helpers = helpers;
}
try {
return new Handlebars.SafeString((t.template || fallback)(ctx, params));
} catch (e) {
console.error(e);
return "error";
}
});
try {
return new Handlebars.SafeString(
(t.template || fallback)(ctx, params)
);
} catch (e) {
console.error(e);
return "error";
}
});
buffer.push("<td>" + parts.join("</td><td>") + "</td>");
}
}));
buffer.push("<td>" + parts.join("</td><td>") + "</td>");
}
})
);
export default QueryRowContentComponent;

View File

@ -1,9 +1,9 @@
import showModal from 'discourse/lib/show-modal';
import Query from 'discourse/plugins/discourse-data-explorer/discourse/models/query';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import { ajax } from 'discourse/lib/ajax';
import showModal from "discourse/lib/show-modal";
import Query from "discourse/plugins/discourse-data-explorer/discourse/models/query";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { ajax } from "discourse/lib/ajax";
const NoQuery = Query.create({name: "No queries", fake: true});
const NoQuery = Query.create({ name: "No queries", fake: true });
export default Ember.Controller.extend({
queryParams: { selectedQueryId: "id" },
@ -14,90 +14,97 @@ export default Ember.Controller.extend({
loading: false,
explain: false,
saveDisabled: Ember.computed.not('selectedItem.dirty'),
runDisabled: Ember.computed.alias('selectedItem.dirty'),
results: Em.computed.alias('selectedItem.results'),
saveDisabled: Ember.computed.not("selectedItem.dirty"),
runDisabled: Ember.computed.alias("selectedItem.dirty"),
results: Em.computed.alias("selectedItem.results"),
asc: null,
order: null,
editing: false,
everEditing: false,
showRecentQueries: true,
sortBy: ['last_run_at:desc'],
sortedQueries: Em.computed.sort('model', 'sortBy'),
sortBy: ["last_run_at:desc"],
sortedQueries: Em.computed.sort("model", "sortBy"),
createDisabled: function() {
return (this.get('newQueryName') || "").trim().length === 0;
}.property('newQueryName'),
return (this.get("newQueryName") || "").trim().length === 0;
}.property("newQueryName"),
selectedItem: function() {
const id = parseInt(this.get('selectedQueryId'));
const item = this.get('content').find(q => q.get('id') === id);
!isNaN(id) ? this.set('showRecentQueries', false) : this.set('showRecentQueries', true);
if (id < 0) this.set('editDisabled', true);
const id = parseInt(this.get("selectedQueryId"));
const item = this.get("content").find(q => q.get("id") === id);
!isNaN(id)
? this.set("showRecentQueries", false)
: this.set("showRecentQueries", true);
if (id < 0) this.set("editDisabled", true);
return item || NoQuery;
}.property('selectedQueryId'),
}.property("selectedQueryId"),
othersDirty: function() {
const selected = this.get('selectedItem');
return !!this.get('content').find(q => q !== selected && q.get('dirty'));
}.property('selectedItem', 'selectedItem.dirty'),
const selected = this.get("selectedItem");
return !!this.get("content").find(q => q !== selected && q.get("dirty"));
}.property("selectedItem", "selectedItem.dirty"),
setEverEditing: function() {
if (this.get('editing') && !this.get('everEditing')) {
this.set('everEditing', true);
if (this.get("editing") && !this.get("everEditing")) {
this.set("everEditing", true);
}
}.observes('editing'),
}.observes("editing"),
addCreatedRecord(record) {
this.get('model').pushObject(record);
this.set('selectedQueryId', Ember.get(record, 'id'));
this.get('selectedItem').set('dirty', false);
this.set('showResults', false);
this.set('results', null);
this.set('editing', true);
this.get("model").pushObject(record);
this.set("selectedQueryId", Ember.get(record, "id"));
this.get("selectedItem").set("dirty", false);
this.set("showResults", false);
this.set("results", null);
this.set("editing", true);
},
save() {
const self = this;
this.set('loading', true);
if (this.get('selectedItem.description') === '') this.set('selectedItem.description', '');
return this.get('selectedItem').save().then(function() {
const query = self.get('selectedItem');
query.markNotDirty();
self.set('editing', false);
}).catch(function(x) {
popupAjaxError(x);
throw x;
}).finally(function() {
self.set('loading', false);
});
this.set("loading", true);
if (this.get("selectedItem.description") === "")
this.set("selectedItem.description", "");
return this.get("selectedItem")
.save()
.then(function() {
const query = self.get("selectedItem");
query.markNotDirty();
self.set("editing", false);
})
.catch(function(x) {
popupAjaxError(x);
throw x;
})
.finally(function() {
self.set("loading", false);
});
},
actions: {
dummy() {},
importQuery() {
showModal('import-query');
this.set('showCreate', false);
showModal("import-query");
this.set("showCreate", false);
},
showCreate() {
this.set('showCreate', true);
this.set("showCreate", true);
},
editName() {
this.set('editing', true);
this.set("editing", true);
},
download() {
window.open(this.get('selectedItem.downloadUrl'), "_blank");
window.open(this.get("selectedItem.downloadUrl"), "_blank");
},
scrollTop() {
window.scrollTo(0,0);
this.set('editing', false);
this.set('everEditing', false);
window.scrollTo(0, 0);
this.set("editing", false);
this.set("everEditing", false);
},
goHome() {
@ -107,18 +114,18 @@ export default Ember.Controller.extend({
showResults: false,
editDisabled: false,
selectedQueryId: null,
sortBy: ['last_run_at:desc']
sortBy: ["last_run_at:desc"]
});
this.send('refreshModel');
this.transitionToRoute('adminPlugins.explorer');
this.send("refreshModel");
this.transitionToRoute("adminPlugins.explorer");
},
resetParams() {
this.get('selectedItem').resetParams();
this.get("selectedItem").resetParams();
},
saveDefaults() {
this.get('selectedItem').saveDefaults();
this.get("selectedItem").saveDefaults();
},
save() {
@ -126,102 +133,122 @@ export default Ember.Controller.extend({
},
saverun() {
this.save().then(() => this.send('run'));
this.save().then(() => this.send("run"));
},
sortByProperty(property) {
if (this.sortBy[0] === `${property}:desc`) {
this.set('sortBy', [`${property}:asc`]);
this.set("sortBy", [`${property}:asc`]);
} else {
this.set('sortBy', [`${property}:desc`]);
this.set("sortBy", [`${property}:desc`]);
}
},
create() {
const name = this.get("newQueryName").trim();
this.set('loading', true);
this.set('showCreate', false);
this.set('showRecentQueries', false);
this.set("loading", true);
this.set("showCreate", false);
this.set("showRecentQueries", false);
this.store
.createRecord('query', { name })
.createRecord("query", { name })
.save()
.then(result => this.addCreatedRecord(result.target) )
.then(result => this.addCreatedRecord(result.target))
.catch(popupAjaxError)
.finally(() => this.set('loading', false));
.finally(() => this.set("loading", false));
},
discard() {
const self = this;
this.set('loading', true);
this.store.find('query', this.get('selectedItem.id')).then(function(result) {
const query = self.get('selectedItem');
query.setProperties(result.getProperties(Query.updatePropertyNames));
query.markNotDirty();
self.set('editing', false);
}).catch(popupAjaxError).finally(function() {
self.set('loading', false);
});
this.set("loading", true);
this.store
.find("query", this.get("selectedItem.id"))
.then(function(result) {
const query = self.get("selectedItem");
query.setProperties(result.getProperties(Query.updatePropertyNames));
query.markNotDirty();
self.set("editing", false);
})
.catch(popupAjaxError)
.finally(function() {
self.set("loading", false);
});
},
destroy() {
const self = this;
const query = this.get('selectedItem');
this.set('loading', true);
this.set('showResults', false);
this.store.destroyRecord('query', query).then(function() {
query.set('destroyed', true);
}).catch(popupAjaxError).finally(function() {
self.set('loading', false);
});
const query = this.get("selectedItem");
this.set("loading", true);
this.set("showResults", false);
this.store
.destroyRecord("query", query)
.then(function() {
query.set("destroyed", true);
})
.catch(popupAjaxError)
.finally(function() {
self.set("loading", false);
});
},
recover() {
const self = this;
const query = this.get('selectedItem');
this.set('loading', true);
this.set('showResults', true);
query.save().then(function() {
query.set('destroyed', false);
}).catch(popupAjaxError).finally(function() {
self.set('loading', false);
});
const query = this.get("selectedItem");
this.set("loading", true);
this.set("showResults", true);
query
.save()
.then(function() {
query.set("destroyed", false);
})
.catch(popupAjaxError)
.finally(function() {
self.set("loading", false);
});
},
run() {
const self = this;
if (this.get('selectedItem.dirty')) {
if (this.get("selectedItem.dirty")) {
return;
}
if (this.get('runDisabled')) {
if (this.get("runDisabled")) {
return;
}
this.set('loading', true);
this.set('showResults', false);
ajax("/admin/plugins/explorer/queries/" + this.get('selectedItem.id') + "/run", {
type: "POST",
data: {
params: JSON.stringify(this.get('selectedItem.params')),
explain: this.get('explain')
}
}).then(function(result) {
self.set('results', result);
if (!result.success) {
self.set('showResults', false);
return;
this.set("loading", true);
this.set("showResults", false);
ajax(
"/admin/plugins/explorer/queries/" +
this.get("selectedItem.id") +
"/run",
{
type: "POST",
data: {
params: JSON.stringify(this.get("selectedItem.params")),
explain: this.get("explain")
}
}
)
.then(function(result) {
self.set("results", result);
if (!result.success) {
self.set("showResults", false);
return;
}
self.set('showResults', true);
}).catch(function(err) {
self.set('showResults', false);
if (err.jqXHR && err.jqXHR.status === 422 && err.jqXHR.responseJSON) {
self.set('results', err.jqXHR.responseJSON);
} else {
popupAjaxError(err);
}
}).finally(function() {
self.set('loading', false);
});
self.set("showResults", true);
})
.catch(function(err) {
self.set("showResults", false);
if (err.jqXHR && err.jqXHR.status === 422 && err.jqXHR.responseJSON) {
self.set("results", err.jqXHR.responseJSON);
} else {
popupAjaxError(err);
}
})
.finally(function() {
self.set("loading", false);
});
}
}
});

View File

@ -1,39 +1,42 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import ModalFunctionality from "discourse/mixins/modal-functionality";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Ember.Controller.extend(ModalFunctionality, {
notReady: Em.computed.not('ready'),
notReady: Em.computed.not("ready"),
adminPluginsExplorer: Ember.inject.controller(),
ready: function() {
let parsed;
try {
parsed = JSON.parse(this.get('queryFile'));
parsed = JSON.parse(this.get("queryFile"));
} catch (e) {
return false;
}
return !!parsed["query"];
}.property('queryFile'),
}.property("queryFile"),
actions: {
doImport: function() {
const self = this;
const object = JSON.parse(this.get('queryFile')).query;
const object = JSON.parse(this.get("queryFile")).query;
// Slight fixup before creating object
object.id = 0; // 0 means no Id yet
this.set('loading', true);
this.store.createRecord('query', object).save().then(function(query) {
self.send('closeModal');
self.set('loading', false);
this.set("loading", true);
this.store
.createRecord("query", object)
.save()
.then(function(query) {
self.send("closeModal");
self.set("loading", false);
const parentController = self.get('adminPluginsExplorer');
parentController.addCreatedRecord(query.target);
}).catch(popupAjaxError);
const parentController = self.get("adminPluginsExplorer");
parentController.addCreatedRecord(query.target);
})
.catch(popupAjaxError);
}
}
});

View File

@ -1,7 +1,7 @@
export default {
resource: 'admin.adminPlugins',
path: '/plugins',
resource: "admin.adminPlugins",
path: "/plugins",
map() {
this.route('explorer');
this.route("explorer");
}
};

View File

@ -1,11 +1,11 @@
export default {
name: 'initialize-data-explorer',
name: "initialize-data-explorer",
initialize(container) {
container.lookup('store:main').addPluralization('query', 'queries');
container.lookup("store:main").addPluralization("query", "queries");
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) { // eslint-disable-line no-extend-native
String.prototype.endsWith = function(searchString, position) {
// eslint-disable-line no-extend-native
var subjectString = this.toString();
if (position === undefined || position > subjectString.length) {
position = subjectString.length;

View File

@ -14,12 +14,10 @@ export default function binarySearch(list, target, keyProp) {
if (Em.get(list[guess], keyProperty) === target) {
return guess;
}
else {
} else {
if (Em.get(list[guess], keyProperty) < target) {
min = guess + 1;
}
else {
} else {
max = guess - 1;
}
}

View File

@ -1,4 +1,4 @@
import RestModel from 'discourse/models/rest';
import RestModel from "discourse/models/rest";
const Query = RestModel.extend({
dirty: false,
@ -7,62 +7,66 @@ const Query = RestModel.extend({
_init: function() {
this._super();
this.set('dirty', false);
}.on('init'),
this.set("dirty", false);
}.on("init"),
_initParams: function() {
this.resetParams();
}.on('init').observes('param_info'),
}
.on("init")
.observes("param_info"),
markDirty: function() {
this.set('dirty', true);
}.observes('name', 'description', 'sql'),
this.set("dirty", true);
}.observes("name", "description", "sql"),
markNotDirty() {
this.set('dirty', false);
this.set("dirty", false);
},
hasParams: function() {
return this.get('param_info.length') > 0;
}.property('param_info'),
return this.get("param_info.length") > 0;
}.property("param_info"),
resetParams() {
const newParams = {};
const oldParams = this.get('params');
const paramInfo = this.get('param_info') || [];
const oldParams = this.get("params");
const paramInfo = this.get("param_info") || [];
paramInfo.forEach(function(pinfo) {
const name = pinfo.identifier;
if (oldParams[pinfo.identifier]) {
newParams[name] = oldParams[name];
} else if (pinfo['default'] !== null) {
newParams[name] = pinfo['default'];
} else if (pinfo['type'] === 'boolean') {
newParams[name] = 'false';
} else if (pinfo["default"] !== null) {
newParams[name] = pinfo["default"];
} else if (pinfo["type"] === "boolean") {
newParams[name] = "false";
} else {
newParams[name] = '';
newParams[name] = "";
}
});
this.set('params', newParams);
this.set("params", newParams);
},
downloadUrl: function() {
// TODO - can we change this to use the store/adapter?
return Discourse.getURL("/admin/plugins/explorer/queries/" + this.get('id') + ".json?export=1");
}.property('id'),
return Discourse.getURL(
"/admin/plugins/explorer/queries/" + this.get("id") + ".json?export=1"
);
}.property("id"),
listName: function() {
let name = this.get('name');
if (this.get('dirty')) {
let name = this.get("name");
if (this.get("dirty")) {
name += " (*)";
}
if (this.get('destroyed')) {
if (this.get("destroyed")) {
name += " (deleted)";
}
return name;
}.property('name', 'dirty', 'destroyed'),
}.property("name", "dirty", "destroyed"),
createProperties() {
if (this.get('sql')) {
if (this.get("sql")) {
// Importing
return this.updateProperties();
}
@ -71,8 +75,8 @@ const Query = RestModel.extend({
updateProperties() {
let props = this.getProperties(Query.updatePropertyNames);
if (this.get('destroyed')) {
props.id = this.get('id');
if (this.get("destroyed")) {
props.id = this.get("id");
}
return props;
}

View File

@ -1,19 +1,23 @@
import { ajax } from 'discourse/lib/ajax';
import { ajax } from "discourse/lib/ajax";
export default Discourse.Route.extend({
controllerName: 'admin-plugins-explorer',
controllerName: "admin-plugins-explorer",
model() {
const p1 = this.store.findAll('query');
const p2 = ajax('/admin/plugins/explorer/schema.json', {cache: true});
return p1.then(model => {
model.forEach(query => query.markNotDirty());
const p1 = this.store.findAll("query");
const p2 = ajax("/admin/plugins/explorer/schema.json", { cache: true });
return p1
.then(model => {
model.forEach(query => query.markNotDirty());
return p2.then(schema => {return {model, schema};});
}).catch(() => {
p2.catch(() => {});
return { model: null, schema: null, disallow: true };
});
return p2.then(schema => {
return { model, schema };
});
})
.catch(() => {
p2.catch(() => {});
return { model: null, schema: null, disallow: true };
});
},
setupController: function(controller, model) {
@ -21,7 +25,8 @@ export default Discourse.Route.extend({
},
actions: {
refreshModel: function() {
this.refresh();
refreshModel: function() {
this.refresh();
}
}
}});
});