From ac25770c0e2aaf9f3142e93b8c898a91005fe58d Mon Sep 17 00:00:00 2001 From: azivner Date: Thu, 9 Aug 2018 20:08:00 +0200 Subject: [PATCH] added runOnAttributeChange event --- src/entities/attribute.js | 12 +++++++++ src/entities/entity_constructor.js | 25 +++++++++++++++--- src/public/javascripts/services/bundle.js | 8 +++--- src/public/javascripts/services/script_api.js | 7 ++--- .../javascripts/services/script_context.js | 4 +-- src/routes/api/script.js | 2 +- src/scripts/Edited files on day.tar | Bin 4092 -> 4096 bytes src/services/handlers.js | 19 ++++++++++--- src/services/repository.js | 11 ++++++-- src/services/script.js | 14 +++++----- src/services/script_context.js | 8 +++--- 11 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/entities/attribute.js b/src/entities/attribute.js index 38d92cd06..f8a8dbd42 100644 --- a/src/entities/attribute.js +++ b/src/entities/attribute.js @@ -28,6 +28,18 @@ class Attribute extends Entity { return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); } + async getTargetNote() { + if (this.type !== 'relation') { + throw new Error(`Attribute ${this.attributeId} is not relation`); + } + + if (!this.value) { + return null; + } + + return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.value]); + } + isDefinition() { return this.type === 'label-definition' || this.type === 'relation-definition'; } diff --git a/src/entities/entity_constructor.js b/src/entities/entity_constructor.js index fb73cd4c9..8adf645a0 100644 --- a/src/entities/entity_constructor.js +++ b/src/entities/entity_constructor.js @@ -9,6 +9,22 @@ const ApiToken = require('../entities/api_token'); const Option = require('../entities/option'); const repository = require('../services/repository'); +const TABLE_NAME_TO_ENTITY = { + "attributes": Attribute.constructor, + "images": Image.constructor, + "note_images": NoteImage.constructor, + "branches": Branch.constructor, + "notes": Note.constructor, + "note_revisions": NoteRevision.constructor, + "recent_notes": RecentNote.constructor, + "options": Option.constructor, + "api_tokens": ApiToken.constructor, +}; + +function getEntityFromTableName(tableName) { + return TABLE_NAME_TO_ENTITY[tableName]; +} + function createEntityFromRow(row) { let entity; @@ -46,8 +62,9 @@ function createEntityFromRow(row) { return entity; } -repository.setEntityConstructor(createEntityFromRow); - module.exports = { - createEntityFromRow -}; \ No newline at end of file + createEntityFromRow, + getEntityFromTableName +}; + +repository.setEntityConstructor(module.exports); diff --git a/src/public/javascripts/services/bundle.js b/src/public/javascripts/services/bundle.js index 87866aa89..c12e29c21 100644 --- a/src/public/javascripts/services/bundle.js +++ b/src/public/javascripts/services/bundle.js @@ -1,14 +1,14 @@ import ScriptContext from "./script_context.js"; import server from "./server.js"; -async function getAndExecuteBundle(noteId, workNote = null) { +async function getAndExecuteBundle(noteId, workEntity = null) { const bundle = await server.get('script/bundle/' + noteId); - await executeBundle(bundle, workNote); + await executeBundle(bundle, workEntity); } -async function executeBundle(bundle, workNote) { - const apiContext = ScriptContext(bundle.note, bundle.allNotes, workNote); +async function executeBundle(bundle, workEntity) { + const apiContext = ScriptContext(bundle.note, bundle.allNotes, workEntity); return await (function () { return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`); diff --git a/src/public/javascripts/services/script_api.js b/src/public/javascripts/services/script_api.js index 88d6ac45d..a727d3e8b 100644 --- a/src/public/javascripts/services/script_api.js +++ b/src/public/javascripts/services/script_api.js @@ -4,7 +4,7 @@ import utils from './utils.js'; import infoService from './info.js'; import linkService from './link.js'; -function ScriptApi(startNote, currentNote, workNote = null) { +function ScriptApi(startNote, currentNote, workEntity = null) { const $pluginButtons = $("#plugin-buttons"); async function activateNote(notePath) { @@ -44,7 +44,8 @@ function ScriptApi(startNote, currentNote, workNote = null) { params: prepareParams(params), startNoteId: startNote.noteId, currentNoteId: currentNote.noteId, - workNoteId: workNote ? workNote.noteId : null + workEntityName: workEntity ? workEntity.constructor.tableName() : null + workEntityId: workEntity ? workEntity.noteId : null }); return ret.executionResult; @@ -53,7 +54,7 @@ function ScriptApi(startNote, currentNote, workNote = null) { return { startNote: startNote, currentNote: currentNote, - workNote: workNote, + workEntity: workEntity, addButtonToToolbar, activateNote, getInstanceName: () => window.glob.instanceName, diff --git a/src/public/javascripts/services/script_context.js b/src/public/javascripts/services/script_context.js index 78a3cffd5..ce9d44e69 100644 --- a/src/public/javascripts/services/script_context.js +++ b/src/public/javascripts/services/script_context.js @@ -1,13 +1,13 @@ import ScriptApi from './script_api.js'; import utils from './utils.js'; -function ScriptContext(startNote, allNotes, workNote = null) { +function ScriptContext(startNote, allNotes, workEntity = null) { const modules = {}; return { modules: modules, notes: utils.toObject(allNotes, note => [note.noteId, note]), - apis: utils.toObject(allNotes, note => [note.noteId, ScriptApi(startNote, note, workNote)]), + apis: utils.toObject(allNotes, note => [note.noteId, ScriptApi(startNote, note, workEntity)]), require: moduleNoteIds => { return moduleName => { const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); diff --git a/src/routes/api/script.js b/src/routes/api/script.js index 7748e25b5..6c3d50465 100644 --- a/src/routes/api/script.js +++ b/src/routes/api/script.js @@ -6,7 +6,7 @@ const repository = require('../../services/repository'); async function exec(req) { const result = await scriptService.executeScript(req.body.script, req.body.params, req.body.startNoteId, - req.body.currentNoteId, req.body.workNoteId); + req.body.currentNoteId, req.body.workEntityName, req.body.workEntityId); return { executionResult: result }; } diff --git a/src/scripts/Edited files on day.tar b/src/scripts/Edited files on day.tar index 66e34b8739235079cc3f065895d32b2dda2b1f60..895581469eb668a31e2f38cea1cebae5b7735d93 100644 GIT binary patch delta 30 gcmew(-=MG|hK { +async function runAttachedRelations(note, relationName, workEntity) { const attributes = await note.getAttributes(); - const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === 'runOnNoteTitleChange'); + const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === relationName); for (const relation of runRelations) { const scriptNote = await relation.getTargetNote(); - await scriptService.executeNote(scriptNote, scriptNote, note); + await scriptService.executeNote(scriptNote, scriptNote, workEntity); } +} + +eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { + await runAttachedRelations(note, 'runOnNoteTitleChange', note); if (!note.isRoot()) { const parents = await note.getParentNotes(); @@ -24,4 +29,12 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { } } } +}); + +eventService.subscribe(eventService.ENTITY_CHANGED, async ({ entityId, entityName }) => { + if (entityName === 'attributes') { + const attribute = await repository.getEntityFromName(entityName, entityId); + + await runAttachedRelations(await attribute.getNote(), 'runOnAttributeChange', attribute); + } }); \ No newline at end of file diff --git a/src/services/repository.js b/src/services/repository.js index 6d3c88819..b6418940c 100644 --- a/src/services/repository.js +++ b/src/services/repository.js @@ -9,10 +9,16 @@ async function setEntityConstructor(constructor) { entityConstructor = constructor; } +async function getEntityFromName(entityName, entityId) { + const entityConstructor = entityConstructor.getEntityFromTableName(entityName); + + return await getEntity(`SELECT * FROM ${entityConstructor.tableName()} WHERE ${entityConstructor.primaryKeyName()} = ?`, [entityId]); +} + async function getEntities(query, params = []) { const rows = await sql.getRows(query, params); - return rows.map(entityConstructor); + return rows.map(entityConstructor.createEntityFromRow); } async function getEntity(query, params = []) { @@ -22,7 +28,7 @@ async function getEntity(query, params = []) { return null; } - return entityConstructor(row); + return entityConstructor.createEntityFromRow(row); } async function getNote(noteId) { @@ -73,6 +79,7 @@ async function updateEntity(entity) { } module.exports = { + getEntityFromName, getEntities, getEntity, getNote, diff --git a/src/services/script.js b/src/services/script.js index b75063849..bc521468f 100644 --- a/src/services/script.js +++ b/src/services/script.js @@ -4,17 +4,17 @@ const repository = require('./repository'); const cls = require('./cls'); const sourceIdService = require('./source_id'); -async function executeNote(note, targetNote) { +async function executeNote(note, workEntity) { if (!note.isJavaScript()) { return; } const bundle = await getScriptBundle(note); - await executeBundle(bundle, note, targetNote); + await executeBundle(bundle, note, workEntity); } -async function executeBundle(bundle, startNote, workNote = null) { +async function executeBundle(bundle, startNote, workEntity = null) { if (!startNote) { // this is the default case, the only exception is when we want to preserve frontend startNote startNote = bundle.note; @@ -23,7 +23,7 @@ async function executeBundle(bundle, startNote, workNote = null) { // last \r\n is necessary if script contains line comment on its last line const script = "async function() {\r\n" + bundle.script + "\r\n}"; - const ctx = new ScriptContext(startNote, bundle.allNotes, workNote); + const ctx = new ScriptContext(startNote, bundle.allNotes, workEntity); if (await bundle.note.hasLabel('manualTransactionHandling')) { return await execute(ctx, script, ''); @@ -37,10 +37,10 @@ async function executeBundle(bundle, startNote, workNote = null) { * This method preserves frontend startNode - that's why we start execution from currentNote and override * bundle's startNote. */ -async function executeScript(script, params, startNoteId, currentNoteId, targetNoteId) { +async function executeScript(script, params, startNoteId, currentNoteId, workEntityName, workEntityId) { const startNote = await repository.getNote(startNoteId); const currentNote = await repository.getNote(currentNoteId); - const targetNote = await repository.getNote(targetNoteId); + const workEntity = await repository.getEntityFromName(workEntityName, workEntityId); currentNote.content = `return await (${script}\r\n)(${getParams(params)})`; currentNote.type = 'code'; @@ -48,7 +48,7 @@ async function executeScript(script, params, startNoteId, currentNoteId, targetN const bundle = await getScriptBundle(currentNote); - return await executeBundle(bundle, startNote, targetNote); + return await executeBundle(bundle, startNote, workEntity); } async function execute(ctx, script, paramsStr) { diff --git a/src/services/script_context.js b/src/services/script_context.js index 3b1f8ee2c..bfbaf1390 100644 --- a/src/services/script_context.js +++ b/src/services/script_context.js @@ -10,10 +10,10 @@ const config = require('./config'); const repository = require('./repository'); const axios = require('axios'); -function ScriptContext(startNote, allNotes, workNote = null) { +function ScriptContext(startNote, allNotes, workEntity = null) { this.modules = {}; this.notes = utils.toObject(allNotes, note => [note.noteId, note]); - this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note, workNote)]); + this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note, workEntity)]); this.require = moduleNoteIds => { return moduleName => { const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId)); @@ -28,10 +28,10 @@ function ScriptContext(startNote, allNotes, workNote = null) { }; } -function ScriptApi(startNote, currentNote, workNote) { +function ScriptApi(startNote, currentNote, workEntity) { this.startNote = startNote; this.currentNote = currentNote; - this.workNote = workNote; + this.workEntity = workEntity; this.axios = axios;