From a555b6319cc5f74bf0a79ef7934822b75484d4fe Mon Sep 17 00:00:00 2001
From: azivner
Date: Sat, 24 Feb 2018 14:42:52 -0500
Subject: [PATCH] support for backend jobs and other script API changes
---
src/entities/note.js | 42 +++++++++++++++++++++++++++
src/plugins/reddit.js | 2 +-
src/public/javascripts/note_editor.js | 2 ++
src/public/javascripts/server.js | 12 ++++++++
src/routes/api/script.js | 6 ++++
src/services/date_notes.js | 6 ++--
src/services/script.js | 36 ++++++++++++++++++++++-
src/services/script_context.js | 11 ++++---
8 files changed, 106 insertions(+), 11 deletions(-)
diff --git a/src/entities/note.js b/src/entities/note.js
index f8787011d..3014b15b1 100644
--- a/src/entities/note.js
+++ b/src/entities/note.js
@@ -43,6 +43,48 @@ class Note extends Entity {
return this.repository.getEntities("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]);
}
+ async getChild(name) {
+ return this.repository.getEntity(`
+ SELECT notes.*
+ FROM note_tree
+ JOIN notes USING(noteId)
+ WHERE notes.isDeleted = 0
+ AND note_tree.isDeleted = 0
+ AND note_tree.parentNoteId = ?
+ AND notes.title = ?`, [this.noteId, name]);
+ }
+
+ async getChildren() {
+ return this.repository.getEntities(`
+ SELECT notes.*
+ FROM note_tree
+ JOIN notes USING(noteId)
+ WHERE notes.isDeleted = 0
+ AND note_tree.isDeleted = 0
+ AND note_tree.parentNoteId = ?`, [this.noteId]);
+ }
+
+ async getParents() {
+ return this.repository.getEntities(`
+ SELECT parent_notes.*
+ FROM
+ note_tree AS child_tree
+ JOIN notes AS parent_notes ON parent_notes.noteId = child_tree.parentNoteId
+ WHERE child_tree.noteId = ?
+ AND child_tree.isDeleted = 0
+ AND parent_notes.isDeleted = 0`, [this.noteId]);
+ }
+
+ async getNoteTree() {
+ return this.repository.getEntities(`
+ SELECT note_tree.*
+ FROM note_tree
+ JOIN notes USING(noteId)
+ WHERE notes.isDeleted = 0
+ AND note_tree.isDeleted = 0
+ AND note_tree.noteId = ?`, [this.noteId]);
+ }
+
beforeSaving() {
this.content = JSON.stringify(this.jsonContent, null, '\t');
diff --git a/src/plugins/reddit.js b/src/plugins/reddit.js
index cf3d93e0c..18237eb88 100644
--- a/src/plugins/reddit.js
+++ b/src/plugins/reddit.js
@@ -109,7 +109,7 @@ karma: ${comment.score}, created at ${dateTimeStr}
`
let redditAccounts = [];
async function runImport() {
- const rootNoteId = await date_notes.getRootNoteId();
+ const rootNoteId = await date_notes.getRootCalendarNoteId();
// technically mutex shouldn't be necessary but we want to avoid doing potentially expensive import
// concurrently with sync
diff --git a/src/public/javascripts/note_editor.js b/src/public/javascripts/note_editor.js
index 216ba3e48..616acd7ae 100644
--- a/src/public/javascripts/note_editor.js
+++ b/src/public/javascripts/note_editor.js
@@ -174,6 +174,8 @@ const noteEditor = (function() {
codeEditor.setOption("mode", info.mime);
CodeMirror.autoLoadMode(codeEditor, info.mode);
}
+
+ codeEditor.refresh();
}
}
diff --git a/src/public/javascripts/server.js b/src/public/javascripts/server.js
index bbdab12f4..b6f2e335b 100644
--- a/src/public/javascripts/server.js
+++ b/src/public/javascripts/server.js
@@ -32,6 +32,10 @@ const server = (function() {
}
function prepareParams(params) {
+ if (!params) {
+ return params;
+ }
+
return params.map(p => {
if (typeof p === "function") {
return "!@#Function: " + p.toString();
@@ -52,6 +56,13 @@ const server = (function() {
return ret.executionResult;
}
+ async function setJob(opts) {
+ opts.job = opts.job.toString();
+ opts.params = prepareParams(opts.params);
+
+ await post('script/job', opts);
+ }
+
let i = 1;
const reqResolves = {};
@@ -116,6 +127,7 @@ const server = (function() {
put,
remove,
exec,
+ setJob,
ajax,
// don't remove, used from CKEditor image upload!
getHeaders
diff --git a/src/routes/api/script.js b/src/routes/api/script.js
index ad09dfe12..b1674d8e6 100644
--- a/src/routes/api/script.js
+++ b/src/routes/api/script.js
@@ -17,6 +17,12 @@ router.post('/exec', auth.checkApiAuth, wrap(async (req, res, next) => {
});
}));
+router.post('/job', auth.checkApiAuth, wrap(async (req, res, next) => {
+ await script.setJob(req.body);
+
+ res.send({});
+}));
+
router.get('/startup', auth.checkApiAuth, wrap(async (req, res, next) => {
const noteIds = await attributes.getNoteIdsWithAttribute("run_on_startup");
const repository = new Repository(req);
diff --git a/src/services/date_notes.js b/src/services/date_notes.js
index d93c868fd..a5e483ced 100644
--- a/src/services/date_notes.js
+++ b/src/services/date_notes.js
@@ -29,7 +29,7 @@ async function getNoteStartingWith(parentNoteId, startsWith) {
AND note_tree.isDeleted = 0`, [parentNoteId]);
}
-async function getRootNoteId() {
+async function getRootCalendarNoteId() {
let rootNoteId = await sql.getValue(`SELECT notes.noteId FROM notes JOIN attributes USING(noteId)
WHERE attributes.name = '${CALENDAR_ROOT_ATTRIBUTE}' AND notes.isDeleted = 0`);
@@ -91,7 +91,7 @@ async function getMonthNoteId(dateTimeStr, rootNoteId) {
async function getDateNoteId(dateTimeStr, rootNoteId = null) {
if (!rootNoteId) {
- rootNoteId = await getRootNoteId();
+ rootNoteId = await getRootCalendarNoteId();
}
const dateStr = dateTimeStr.substr(0, 10);
@@ -119,7 +119,7 @@ async function getDateNoteId(dateTimeStr, rootNoteId = null) {
}
module.exports = {
- getRootNoteId,
+ getRootCalendarNoteId,
getYearNoteId,
getMonthNoteId,
getDateNoteId
diff --git a/src/services/script.js b/src/services/script.js
index 9b860d49d..00db4c1d0 100644
--- a/src/services/script.js
+++ b/src/services/script.js
@@ -18,7 +18,40 @@ async function executeScript(dataKey, script, params) {
return ret;
}
+const timeouts = {};
+const intervals = {};
+
+function clearExistingJob(name) {
+ if (timeouts[name]) {
+ clearTimeout(timeouts[name]);
+
+ delete timeouts[name];
+ }
+
+ if (intervals[name]) {
+ clearInterval(intervals[name]);
+
+ delete intervals[name];
+ }
+}
+
+async function setJob(opts) {
+ clearExistingJob(opts.name);
+
+ if (opts.runEveryMs && opts.runEveryMs > 0) {
+ intervals[opts.name] = setInterval(() => executeScript(null, opts.job, opts.params), opts.runEveryMs);
+ }
+
+ if (opts.initialRunAfterMs && opts.initialRunAfterMs > 0) {
+ timeouts[opts.name] = setTimeout(() => executeScript(null, opts.job, opts.params), opts.initialRunAfterMs);
+ }
+}
+
function getParams(params) {
+ if (!params) {
+ return params;
+ }
+
return params.map(p => {
if (typeof p === "string" && p.startsWith("!@#Function: ")) {
return p.substr(13);
@@ -30,5 +63,6 @@ function getParams(params) {
}
module.exports = {
- executeScript
+ executeScript,
+ setJob
};
\ No newline at end of file
diff --git a/src/services/script_context.js b/src/services/script_context.js
index a4da9b55c..ce508e612 100644
--- a/src/services/script_context.js
+++ b/src/services/script_context.js
@@ -23,10 +23,10 @@ function ScriptContext(noteId, dataKey) {
return notes.length > 0 ? notes[0] : null;
};
- this.createNote = async function (parentNoteId, name, jsonContent, extraOptions = {}) {
+ this.createNote = async function (parentNoteId, title, content = "", extraOptions = {}) {
const note = {
- title: name,
- content: extraOptions.json ? JSON.stringify(jsonContent, null, '\t') : jsonContent,
+ title: title,
+ content: extraOptions.json ? JSON.stringify(content, null, '\t') : content,
target: 'into',
isProtected: extraOptions.isProtected !== undefined ? extraOptions.isProtected : false,
type: extraOptions.type,
@@ -58,10 +58,9 @@ function ScriptContext(noteId, dataKey) {
this.updateEntity = this.repository.updateEntity;
- this.log = function(message) {
- log.info(`Script: ${message}`);
- };
+ this.log = message => log.info(`Script: ${message}`);
+ this.getRootCalendarNoteId = date_notes.getRootCalendarNoteId;
this.getDateNoteId = date_notes.getDateNoteId;
}