mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	cleanup of labels and relations from backend
This commit is contained in:
		| @@ -65,30 +65,70 @@ class Note extends Entity { | |||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async getLabels() { |     async getOwnedAttributes() { | ||||||
|         return await repository.getEntities("SELECT * FROM labels WHERE noteId = ? AND isDeleted = 0", [this.noteId]); |         return await repository.getEntities(`SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ?`, [this.noteId]); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // WARNING: this doesn't take into account the possibility to have multi-valued labels! |     async getAttributes() { | ||||||
|     async getLabelMap() { |         const attributes = await repository.getEntities(` | ||||||
|         const map = {}; |         WITH RECURSIVE tree(noteId, level) AS ( | ||||||
|  |         SELECT ?, 0 | ||||||
|  |             UNION | ||||||
|  |             SELECT branches.parentNoteId, tree.level + 1 FROM branches | ||||||
|  |             JOIN tree ON branches.noteId = tree.noteId | ||||||
|  |             JOIN notes ON notes.noteId = branches.parentNoteId | ||||||
|  |             WHERE notes.isDeleted = 0 AND branches.isDeleted = 0 | ||||||
|  |         ) | ||||||
|  |         SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId  | ||||||
|  |         WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?) | ||||||
|  |         ORDER BY level, noteId, position`, [this.noteId, this.noteId]); | ||||||
|  |         // attributes are ordered so that "closest" attributes are first | ||||||
|  |         // we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter. | ||||||
|  |  | ||||||
|         for (const label of await this.getLabels()) { |         const filteredAttributes = attributes.filter((attr, index) => { | ||||||
|             map[label.name] = label.value; |             if (attr.isDefinition()) { | ||||||
|  |                 const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); | ||||||
|  |  | ||||||
|  |                 // keep only if this element is the first definition for this type & name | ||||||
|  |                 return firstDefinitionIndex === index; | ||||||
|  |             } | ||||||
|  |             else { | ||||||
|  |                 const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name); | ||||||
|  |  | ||||||
|  |                 if (!definitionAttr) { | ||||||
|  |                     return true; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|         return map; |                 const definition = definitionAttr.value; | ||||||
|  |  | ||||||
|  |                 if (definition.multiplicityType === 'multivalue') { | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 else { | ||||||
|  |                     const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); | ||||||
|  |  | ||||||
|  |                     // in case of single-valued attribute we'll keep it only if it's first (closest) | ||||||
|  |                     return firstAttrIndex === index; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         for (const attr of filteredAttributes) { | ||||||
|  |             attr.isOwned = attr.noteId === this.noteId; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return filteredAttributes; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async hasLabel(name) { |     async hasLabel(name) { | ||||||
|         const map = await this.getLabelMap(); |         return !!await this.getLabel(name); | ||||||
|  |  | ||||||
|         return map.hasOwnProperty(name); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // WARNING: this doesn't take into account the possibility to have multi-valued labels! |     // WARNING: this doesn't take into account the possibility to have multi-valued labels! | ||||||
|     async getLabel(name) { |     async getLabel(name) { | ||||||
|         return await repository.getEntity("SELECT * FROM labels WHERE noteId = ? AND name = ?", [this.noteId, name]); |         const attributes = await this.getAttributes(); | ||||||
|  |  | ||||||
|  |         return attributes.find(attr => attr.type === 'label' && attr.name === name); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async getRevisions() { |     async getRevisions() { | ||||||
|   | |||||||
| @@ -6,7 +6,9 @@ const repository = require('../../services/repository'); | |||||||
| const Attribute = require('../../entities/attribute'); | const Attribute = require('../../entities/attribute'); | ||||||
|  |  | ||||||
| async function getEffectiveNoteAttributes(req) { | async function getEffectiveNoteAttributes(req) { | ||||||
|     return await attributeService.getEffectiveAttributes(req.params.noteId); |     const note = await repository.getNote(req.params.noteId); | ||||||
|  |  | ||||||
|  |     return await note.getAttributes(); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function updateNoteAttribute(req) { | async function updateNoteAttribute(req) { | ||||||
| @@ -87,7 +89,9 @@ async function updateNoteAttributes(req) { | |||||||
|         await attributeEntity.save(); |         await attributeEntity.save(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return await attributeService.getEffectiveAttributes(noteId); |     const note = await repository.getNote(noteId); | ||||||
|  |  | ||||||
|  |     return await note.getAttributes(); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getAttributeNames(req) { | async function getAttributeNames(req) { | ||||||
|   | |||||||
| @@ -113,15 +113,18 @@ async function exportToTar(branchId, res) { | |||||||
|             prefix: branch.prefix, |             prefix: branch.prefix, | ||||||
|             type: note.type, |             type: note.type, | ||||||
|             mime: note.mime, |             mime: note.mime, | ||||||
|             labels: (await note.getLabels()).map(label => { |             attributes: (await note.getOwnedAttributes()).map(attribute => { | ||||||
|                 return { |                 return { | ||||||
|                     name: label.name, |                     type: attribute.type, | ||||||
|                     value: label.value |                     name: attribute.name, | ||||||
|  |                     value: attribute.value, | ||||||
|  |                     isInheritable: attribute.isInheritable, | ||||||
|  |                     position: attribute.position | ||||||
|                 }; |                 }; | ||||||
|             }) |             }) | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if (metadata.labels.find(label => label.name === 'excludeFromExport')) { |         if (metadata.attributes.find(attributes => attributes.type === 'label' && attributes.name === 'excludeFromExport')) { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const noteService = require('../../services/notes'); | const noteService = require('../../services/notes'); | ||||||
| const labelService = require('../../services/labels'); | const attributeService = require('../../services/attributes'); | ||||||
| const protectedSessionService = require('../../services/protected_session'); | const protectedSessionService = require('../../services/protected_session'); | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
|  |  | ||||||
| @@ -26,8 +26,8 @@ async function uploadFile(req) { | |||||||
|         mime: file.mimetype |         mime: file.mimetype | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     await labelService.createLabel(note.noteId, "originalFileName", originalName); |     await attributeService.createLabel(note.noteId, "originalFileName", originalName); | ||||||
|     await labelService.createLabel(note.noteId, "fileSize", size); |     await attributeService.createLabel(note.noteId, "fileSize", size); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         noteId: note.noteId |         noteId: note.noteId | ||||||
| @@ -47,8 +47,8 @@ async function downloadFile(req, res) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const labelMap = await note.getLabelMap(); |     const originalFileName = await note.getLabel('originalFileName'); | ||||||
|     const fileName = labelMap.originalFileName || note.title; |     const fileName = originalFileName.value || note.title; | ||||||
|  |  | ||||||
|     res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"'); |     res.setHeader('Content-Disposition', 'file; filename="' + fileName + '"'); | ||||||
|     res.setHeader('Content-Type', note.mime); |     res.setHeader('Content-Type', note.mime); | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
| const labelService = require('../../services/labels'); | const attributeService = require('../../services/attributes'); | ||||||
| const noteService = require('../../services/notes'); | const noteService = require('../../services/notes'); | ||||||
| const Branch = require('../../entities/branch'); | const Branch = require('../../entities/branch'); | ||||||
| const tar = require('tar-stream'); | const tar = require('tar-stream'); | ||||||
| @@ -187,8 +187,8 @@ async function importNotes(files, parentNoteId, noteIdMap) { | |||||||
|  |  | ||||||
|         noteIdMap[file.meta.noteId] = note.noteId; |         noteIdMap[file.meta.noteId] = note.noteId; | ||||||
|  |  | ||||||
|         for (const label of file.meta.labels) { |         for (const attribute of file.meta.attributes) { | ||||||
|             await labelService.createLabel(note.noteId, label.name, label.value); |             await attributeService.createAttribute(attribute); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (file.children.length > 0) { |         if (file.children.length > 0) { | ||||||
|   | |||||||
| @@ -1,70 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| const sql = require('../../services/sql'); |  | ||||||
| const labelService = require('../../services/labels'); |  | ||||||
| const repository = require('../../services/repository'); |  | ||||||
| const Label = require('../../entities/label'); |  | ||||||
|  |  | ||||||
| async function getNoteLabels(req) { |  | ||||||
|     const noteId = req.params.noteId; |  | ||||||
|  |  | ||||||
|     return await repository.getEntities("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function updateNoteLabels(req) { |  | ||||||
|     const noteId = req.params.noteId; |  | ||||||
|     const labels = req.body; |  | ||||||
|  |  | ||||||
|     for (const label of labels) { |  | ||||||
|         let labelEntity; |  | ||||||
|  |  | ||||||
|         if (label.labelId) { |  | ||||||
|             labelEntity = await repository.getLabel(label.labelId); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             // if it was "created" and then immediatelly deleted, we just don't create it at all |  | ||||||
|             if (label.isDeleted) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             labelEntity = new Label(); |  | ||||||
|             labelEntity.noteId = noteId; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         labelEntity.name = label.name; |  | ||||||
|         labelEntity.value = label.value; |  | ||||||
|         labelEntity.position = label.position; |  | ||||||
|         labelEntity.isDeleted = label.isDeleted; |  | ||||||
|  |  | ||||||
|         await labelEntity.save(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return await repository.getEntities("SELECT * FROM labels WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getAllLabelNames() { |  | ||||||
|     const names = await sql.getColumn("SELECT DISTINCT name FROM labels WHERE isDeleted = 0"); |  | ||||||
|  |  | ||||||
|     for (const label of labelService.BUILTIN_LABELS) { |  | ||||||
|         if (!names.includes(label)) { |  | ||||||
|             names.push(label); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     names.sort(); |  | ||||||
|  |  | ||||||
|     return names; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getValuesForLabel(req) { |  | ||||||
|     const labelName = req.params.labelName; |  | ||||||
|  |  | ||||||
|     return await sql.getColumn("SELECT DISTINCT value FROM labels WHERE isDeleted = 0 AND name = ? AND value != '' ORDER BY value", [labelName]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|     getNoteLabels, |  | ||||||
|     updateNoteLabels, |  | ||||||
|     getAllLabelNames, |  | ||||||
|     getValuesForLabel |  | ||||||
| }; |  | ||||||
| @@ -1,64 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| const sql = require('../../services/sql'); |  | ||||||
| const relationService = require('../../services/relations'); |  | ||||||
| const repository = require('../../services/repository'); |  | ||||||
| const Relation = require('../../entities/relation'); |  | ||||||
|  |  | ||||||
| async function getNoteRelations(req) { |  | ||||||
|     const noteId = req.params.noteId; |  | ||||||
|  |  | ||||||
|     return await repository.getEntities("SELECT * FROM relations WHERE isDeleted = 0 AND sourceNoteId = ? ORDER BY position, dateCreated", [noteId]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function updateNoteRelations(req) { |  | ||||||
|     const noteId = req.params.noteId; |  | ||||||
|     const relations = req.body; |  | ||||||
|  |  | ||||||
|     for (const relation of relations) { |  | ||||||
|         let relationEntity; |  | ||||||
|  |  | ||||||
|         if (relation.relationId) { |  | ||||||
|             relationEntity = await repository.getRelation(relation.relationId); |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             // if it was "created" and then immediatelly deleted, we just don't create it at all |  | ||||||
|             if (relation.isDeleted) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             relationEntity = new Relation(); |  | ||||||
|             relationEntity.sourceNoteId = noteId; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         relationEntity.name = relation.name; |  | ||||||
|         relationEntity.targetNoteId = relation.targetNoteId; |  | ||||||
|         relationEntity.isInheritable = relation.isInheritable; |  | ||||||
|         relationEntity.position = relation.position; |  | ||||||
|         relationEntity.isDeleted = relation.isDeleted; |  | ||||||
|  |  | ||||||
|         await relationEntity.save(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return await repository.getEntities("SELECT * FROM relations WHERE isDeleted = 0 AND sourceNoteId = ? ORDER BY position, dateCreated", [noteId]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getAllRelationNames() { |  | ||||||
|     const names = await sql.getColumn("SELECT DISTINCT name FROM relations WHERE isDeleted = 0"); |  | ||||||
|  |  | ||||||
|     for (const relationName of relationService.BUILTIN_RELATIONS) { |  | ||||||
|         if (!names.includes(relationName)) { |  | ||||||
|             names.push(relationName); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     names.sort(); |  | ||||||
|  |  | ||||||
|     return names; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|     getNoteRelations, |  | ||||||
|     updateNoteRelations, |  | ||||||
|     getAllRelationNames |  | ||||||
| }; |  | ||||||
| @@ -1,6 +1,5 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const labelService = require('../../services/labels'); |  | ||||||
| const scriptService = require('../../services/script'); | const scriptService = require('../../services/script'); | ||||||
| const attributeService = require('../../services/attributes'); | const attributeService = require('../../services/attributes'); | ||||||
| const repository = require('../../services/repository'); | const repository = require('../../services/repository'); | ||||||
| @@ -21,7 +20,7 @@ async function run(req) { | |||||||
| } | } | ||||||
|  |  | ||||||
| async function getStartupBundles() { | async function getStartupBundles() { | ||||||
|     const notes = await labelService.getNotesWithLabel("run", "frontendStartup"); |     const notes = await attributeService.getNotesWithLabel("run", "frontendStartup"); | ||||||
|  |  | ||||||
|     const bundles = []; |     const bundles = []; | ||||||
|  |  | ||||||
| @@ -38,9 +37,10 @@ async function getStartupBundles() { | |||||||
|  |  | ||||||
| async function getRelationBundles(req) { | async function getRelationBundles(req) { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|  |     const note = await repository.getNote(noteId); | ||||||
|     const relationName = req.params.relationName; |     const relationName = req.params.relationName; | ||||||
|  |  | ||||||
|     const attributes = await attributeService.getEffectiveAttributes(noteId); |     const attributes = await note.getAttributes(); | ||||||
|     const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName); |     const filtered = attributes.filter(attr => attr.type === 'relation' && attr.name === relationName); | ||||||
|     const targetNoteIds = filtered.map(relation => relation.value); |     const targetNoteIds = filtered.map(relation => relation.value); | ||||||
|     const uniqueNoteIds = Array.from(new Set(targetNoteIds)); |     const uniqueNoteIds = Array.from(new Set(targetNoteIds)); | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
|  |  | ||||||
| const sourceIdService = require('../services/source_id'); | const sourceIdService = require('../services/source_id'); | ||||||
| const sql = require('../services/sql'); | const sql = require('../services/sql'); | ||||||
| const labelService = require('../services/labels'); | const attributeService = require('../services/attributes'); | ||||||
| const config = require('../services/config'); | const config = require('../services/config'); | ||||||
| const optionService = require('../services/options'); | const optionService = require('../services/options'); | ||||||
|  |  | ||||||
| @@ -18,7 +18,7 @@ async function index(req, res) { | |||||||
|  |  | ||||||
| async function getAppCss() { | async function getAppCss() { | ||||||
|     let css = ''; |     let css = ''; | ||||||
|     const notes = labelService.getNotesWithLabel('appCss'); |     const notes = attributeService.getNotesWithLabel('appCss'); | ||||||
|  |  | ||||||
|     for (const note of await notes) { |     for (const note of await notes) { | ||||||
|         css += `/* ${note.noteId} */ |         css += `/* ${note.noteId} */ | ||||||
|   | |||||||
| @@ -43,12 +43,17 @@ async function getNoteWithLabel(name, value) { | |||||||
|     return notes.length > 0 ? notes[0] : null; |     return notes.length > 0 ? notes[0] : null; | ||||||
| } | } | ||||||
|  |  | ||||||
| async function createAttribute(noteId, name, value = "") { | async function createLabel(noteId, name, value = "") { | ||||||
|     return await new Attribute({ |     return await createAttribute({ | ||||||
|         noteId: noteId, |         noteId: noteId, | ||||||
|  |         type: 'label', | ||||||
|         name: name, |         name: name, | ||||||
|         value: value |         value: value | ||||||
|     }).save(); |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | async function createAttribute(attribute) { | ||||||
|  |     return await new Attribute(attribute).save(); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getAttributeNames(type, nameLike) { | async function getAttributeNames(type, nameLike) { | ||||||
| @@ -70,62 +75,11 @@ async function getAttributeNames(type, nameLike) { | |||||||
|     return names; |     return names; | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getEffectiveAttributes(noteId) { |  | ||||||
|     const attributes = await repository.getEntities(` |  | ||||||
|         WITH RECURSIVE tree(noteId, level) AS ( |  | ||||||
|         SELECT ?, 0 |  | ||||||
|             UNION |  | ||||||
|             SELECT branches.parentNoteId, tree.level + 1 FROM branches |  | ||||||
|             JOIN tree ON branches.noteId = tree.noteId |  | ||||||
|             JOIN notes ON notes.noteId = branches.parentNoteId |  | ||||||
|             WHERE notes.isDeleted = 0 AND branches.isDeleted = 0 |  | ||||||
|         ) |  | ||||||
|         SELECT attributes.* FROM attributes JOIN tree ON attributes.noteId = tree.noteId  |  | ||||||
|         WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR attributes.noteId = ?) |  | ||||||
|         ORDER BY level, noteId, position`, [noteId, noteId]); |  | ||||||
|     // attributes are ordered so that "closest" attributes are first |  | ||||||
|     // we order by noteId so that attributes from same note stay together. Actual noteId ordering doesn't matter. |  | ||||||
|  |  | ||||||
|     const filteredAttributes = attributes.filter((attr, index) => { |  | ||||||
|         if (attr.isDefinition()) { |  | ||||||
|             const firstDefinitionIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); |  | ||||||
|  |  | ||||||
|             // keep only if this element is the first definition for this type & name |  | ||||||
|             return firstDefinitionIndex === index; |  | ||||||
|         } |  | ||||||
|         else { |  | ||||||
|             const definitionAttr = attributes.find(el => el.type === attr.type + '-definition' && el.name === attr.name); |  | ||||||
|  |  | ||||||
|             if (!definitionAttr) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             const definition = definitionAttr.value; |  | ||||||
|  |  | ||||||
|             if (definition.multiplicityType === 'multivalue') { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 const firstAttrIndex = attributes.findIndex(el => el.type === attr.type && el.name === attr.name); |  | ||||||
|  |  | ||||||
|                 // in case of single-valued attribute we'll keep it only if it's first (closest) |  | ||||||
|                 return firstAttrIndex === index; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     for (const attr of filteredAttributes) { |  | ||||||
|         attr.isOwned = attr.noteId === noteId; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return filteredAttributes; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     getNotesWithLabel, |     getNotesWithLabel, | ||||||
|     getNoteWithLabel, |     getNoteWithLabel, | ||||||
|  |     createLabel, | ||||||
|     createAttribute, |     createAttribute, | ||||||
|     getAttributeNames, |     getAttributeNames, | ||||||
|     getEffectiveAttributes, |  | ||||||
|     BUILTIN_ATTRIBUTES |     BUILTIN_ATTRIBUTES | ||||||
| }; | }; | ||||||
| @@ -1,8 +1,7 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const sql = require('./sql'); |  | ||||||
| const noteService = require('./notes'); | const noteService = require('./notes'); | ||||||
| const labelService = require('./labels'); | const attributeService = require('./attributes'); | ||||||
| const dateUtils = require('./date_utils'); | const dateUtils = require('./date_utils'); | ||||||
| const repository = require('./repository'); | const repository = require('./repository'); | ||||||
|  |  | ||||||
| @@ -32,7 +31,7 @@ async function getNoteStartingWith(parentNoteId, startsWith) { | |||||||
|  |  | ||||||
| async function getRootCalendarNote() { | async function getRootCalendarNote() { | ||||||
|     // some caching here could be useful (e.g. in CLS) |     // some caching here could be useful (e.g. in CLS) | ||||||
|     let rootNote = await labelService.getNoteWithLabel(CALENDAR_ROOT_LABEL); |     let rootNote = await attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL); | ||||||
|  |  | ||||||
|     if (!rootNote) { |     if (!rootNote) { | ||||||
|         rootNote = (await noteService.createNewNote('root', { |         rootNote = (await noteService.createNewNote('root', { | ||||||
| @@ -41,8 +40,8 @@ async function getRootCalendarNote() { | |||||||
|             isProtected: false |             isProtected: false | ||||||
|         })).note; |         })).note; | ||||||
|  |  | ||||||
|         await labelService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL); |         await attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL); | ||||||
|         await labelService.createLabel(rootNote.noteId, 'sorted'); |         await attributeService.createLabel(rootNote.noteId, 'sorted'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return rootNote; |     return rootNote; | ||||||
| @@ -51,7 +50,7 @@ async function getRootCalendarNote() { | |||||||
| async function getYearNote(dateTimeStr, rootNote) { | async function getYearNote(dateTimeStr, rootNote) { | ||||||
|     const yearStr = dateTimeStr.substr(0, 4); |     const yearStr = dateTimeStr.substr(0, 4); | ||||||
|  |  | ||||||
|     let yearNote = await labelService.getNoteWithLabel(YEAR_LABEL, yearStr); |     let yearNote = await attributeService.getNoteWithLabel(YEAR_LABEL, yearStr); | ||||||
|  |  | ||||||
|     if (!yearNote) { |     if (!yearNote) { | ||||||
|         yearNote = await getNoteStartingWith(rootNote.noteId, yearStr); |         yearNote = await getNoteStartingWith(rootNote.noteId, yearStr); | ||||||
| @@ -60,8 +59,8 @@ async function getYearNote(dateTimeStr, rootNote) { | |||||||
|             yearNote = await createNote(rootNote.noteId, yearStr); |             yearNote = await createNote(rootNote.noteId, yearStr); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         await labelService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr); |         await attributeService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr); | ||||||
|         await labelService.createLabel(yearNote.noteId, 'sorted'); |         await attributeService.createLabel(yearNote.noteId, 'sorted'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return yearNote; |     return yearNote; | ||||||
| @@ -71,7 +70,7 @@ async function getMonthNote(dateTimeStr, rootNote) { | |||||||
|     const monthStr = dateTimeStr.substr(0, 7); |     const monthStr = dateTimeStr.substr(0, 7); | ||||||
|     const monthNumber = dateTimeStr.substr(5, 2); |     const monthNumber = dateTimeStr.substr(5, 2); | ||||||
|  |  | ||||||
|     let monthNote = await labelService.getNoteWithLabel(MONTH_LABEL, monthStr); |     let monthNote = await attributeService.getNoteWithLabel(MONTH_LABEL, monthStr); | ||||||
|  |  | ||||||
|     if (!monthNote) { |     if (!monthNote) { | ||||||
|         const yearNote = await getYearNote(dateTimeStr, rootNote); |         const yearNote = await getYearNote(dateTimeStr, rootNote); | ||||||
| @@ -86,8 +85,8 @@ async function getMonthNote(dateTimeStr, rootNote) { | |||||||
|             monthNote = await createNote(yearNote.noteId, noteTitle); |             monthNote = await createNote(yearNote.noteId, noteTitle); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         await labelService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr); |         await attributeService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr); | ||||||
|         await labelService.createLabel(monthNote.noteId, 'sorted'); |         await attributeService.createLabel(monthNote.noteId, 'sorted'); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return monthNote; |     return monthNote; | ||||||
| @@ -99,7 +98,7 @@ async function getDateNote(dateTimeStr) { | |||||||
|     const dateStr = dateTimeStr.substr(0, 10); |     const dateStr = dateTimeStr.substr(0, 10); | ||||||
|     const dayNumber = dateTimeStr.substr(8, 2); |     const dayNumber = dateTimeStr.substr(8, 2); | ||||||
|  |  | ||||||
|     let dateNote = await labelService.getNoteWithLabel(DATE_LABEL, dateStr); |     let dateNote = await attributeService.getNoteWithLabel(DATE_LABEL, dateStr); | ||||||
|  |  | ||||||
|     if (!dateNote) { |     if (!dateNote) { | ||||||
|         const monthNote = await getMonthNote(dateTimeStr, rootNote); |         const monthNote = await getMonthNote(dateTimeStr, rootNote); | ||||||
| @@ -114,7 +113,7 @@ async function getDateNote(dateTimeStr) { | |||||||
|             dateNote = await createNote(monthNote.noteId, noteTitle); |             dateNote = await createNote(monthNote.noteId, noteTitle); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         await labelService.createLabel(dateNote.noteId, DATE_LABEL, dateStr); |         await attributeService.createLabel(dateNote.noteId, DATE_LABEL, dateStr); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return dateNote; |     return dateNote; | ||||||
|   | |||||||
| @@ -1,13 +1,13 @@ | |||||||
| const eventService = require('./events'); | const eventService = require('./events'); | ||||||
| const scriptService = require('./script'); | const scriptService = require('./script'); | ||||||
| const relationService = require('./relations'); |  | ||||||
| const treeService = require('./tree'); | const treeService = require('./tree'); | ||||||
| const messagingService = require('./messaging'); | const messagingService = require('./messaging'); | ||||||
|  |  | ||||||
| eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { | eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { | ||||||
|     const relations = await relationService.getEffectiveRelations(note.noteId, 'runOnNoteTitleChange'); |     const attributes = await note.getAttributes(); | ||||||
|  |     const runRelations = attributes.filter(relation => relation.type === 'relation' && relation.name === 'runOnNoteTitleChange'); | ||||||
|  |  | ||||||
|     for (const relation of relations) { |     for (const relation of runRelations) { | ||||||
|         const scriptNote = await relation.getTargetNote(); |         const scriptNote = await relation.getTargetNote(); | ||||||
|  |  | ||||||
|         await scriptService.executeNote(scriptNote, scriptNote, note); |         await scriptService.executeNote(scriptNote, scriptNote, note); | ||||||
|   | |||||||
| @@ -1,52 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| const repository = require('./repository'); |  | ||||||
| const Label = require('../entities/label'); |  | ||||||
|  |  | ||||||
| const BUILTIN_LABELS = [ |  | ||||||
|     'disableVersioning', |  | ||||||
|     'calendarRoot', |  | ||||||
|     'archived', |  | ||||||
|     'excludeFromExport', |  | ||||||
|     'run', |  | ||||||
|     'manualTransactionHandling', |  | ||||||
|     'disableInclusion', |  | ||||||
|     'appCss', |  | ||||||
|     'hideChildrenOverview' |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
| async function getNotesWithLabel(name, value) { |  | ||||||
|     let notes; |  | ||||||
|  |  | ||||||
|     if (value !== undefined) { |  | ||||||
|         notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN labels USING(noteId)  |  | ||||||
|           WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ? AND labels.value = ?`, [name, value]); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN labels USING(noteId)  |  | ||||||
|           WHERE notes.isDeleted = 0 AND labels.isDeleted = 0 AND labels.name = ?`, [name]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return notes; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getNoteWithLabel(name, value) { |  | ||||||
|     const notes = await getNotesWithLabel(name, value); |  | ||||||
|  |  | ||||||
|     return notes.length > 0 ? notes[0] : null; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function createLabel(noteId, name, value = "") { |  | ||||||
|     return await new Label({ |  | ||||||
|         noteId: noteId, |  | ||||||
|         name: name, |  | ||||||
|         value: value |  | ||||||
|     }).save(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|     getNotesWithLabel, |  | ||||||
|     getNoteWithLabel, |  | ||||||
|     createLabel, |  | ||||||
|     BUILTIN_LABELS |  | ||||||
| }; |  | ||||||
| @@ -2,7 +2,7 @@ const sql = require('./sql'); | |||||||
| const optionService = require('./options'); | const optionService = require('./options'); | ||||||
| const dateUtils = require('./date_utils'); | const dateUtils = require('./date_utils'); | ||||||
| const syncTableService = require('./sync_table'); | const syncTableService = require('./sync_table'); | ||||||
| const labelService = require('./labels'); | const attributeService = require('./attributes'); | ||||||
| const eventService = require('./events'); | const eventService = require('./events'); | ||||||
| const repository = require('./repository'); | const repository = require('./repository'); | ||||||
| const Note = require('../entities/note'); | const Note = require('../entities/note'); | ||||||
| @@ -93,9 +93,10 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {}) | |||||||
|  |  | ||||||
|     const {note, branch} = await createNewNote(parentNoteId, noteData); |     const {note, branch} = await createNewNote(parentNoteId, noteData); | ||||||
|  |  | ||||||
|  |     // FIXME: need to make this more generic for all kinds of attributes | ||||||
|     if (extraOptions.labels) { |     if (extraOptions.labels) { | ||||||
|         for (const labelName in extraOptions.labels) { |         for (const labelName in extraOptions.labels) { | ||||||
|             await labelService.createLabel(note.noteId, labelName, extraOptions.labels[labelName]); |             await attributeService.createLabel(note.noteId, labelName, extraOptions.labels[labelName]); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -168,8 +169,6 @@ async function saveNoteImages(note) { | |||||||
| } | } | ||||||
|  |  | ||||||
| async function saveNoteRevision(note) { | async function saveNoteRevision(note) { | ||||||
|     const labelsMap = await note.getLabelMap(); |  | ||||||
|  |  | ||||||
|     const now = new Date(); |     const now = new Date(); | ||||||
|     const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval')); |     const noteRevisionSnapshotTimeInterval = parseInt(await optionService.getOption('noteRevisionSnapshotTimeInterval')); | ||||||
|  |  | ||||||
| @@ -181,7 +180,7 @@ async function saveNoteRevision(note) { | |||||||
|     const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime(); |     const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.dateCreated).getTime(); | ||||||
|  |  | ||||||
|     if (note.type !== 'file' |     if (note.type !== 'file' | ||||||
|         && labelsMap.disableVersioning !== 'true' |         && await note.hasLabel('disableVersioning') | ||||||
|         && !existingnoteRevisionId |         && !existingnoteRevisionId | ||||||
|         && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { |         && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,67 +0,0 @@ | |||||||
| "use strict"; |  | ||||||
|  |  | ||||||
| const repository = require('./repository'); |  | ||||||
| const Relation = require('../entities/relation'); |  | ||||||
|  |  | ||||||
| const BUILTIN_RELATIONS = [ |  | ||||||
|     'runOnNoteView', |  | ||||||
|     'runOnNoteTitleChange' |  | ||||||
| ]; |  | ||||||
|  |  | ||||||
| async function getNotesWithRelation(name, targetNoteId) { |  | ||||||
|     let notes; |  | ||||||
|  |  | ||||||
|     if (targetNoteId !== undefined) { |  | ||||||
|         notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN relations ON notes.noteId = relations.sourceNoteId |  | ||||||
|           WHERE notes.isDeleted = 0 AND relations.isDeleted = 0 AND relations.name = ? AND relations.targetNoteId = ?`, [name, targetNoteId]); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         notes = await repository.getEntities(`SELECT notes.* FROM notes JOIN relations ON notes.noteId = relations.sourceNoteId  |  | ||||||
|           WHERE notes.isDeleted = 0 AND relations.isDeleted = 0 AND relations.name = ?`, [name]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return notes; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getNoteWithRelation(name, value) { |  | ||||||
|     const notes = await getNotesWithRelation(name, value); |  | ||||||
|  |  | ||||||
|     return notes.length > 0 ? notes[0] : null; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function createRelation(sourceNoteId, name, targetNoteId) { |  | ||||||
|     return await new Relation({ |  | ||||||
|         sourceNoteId: sourceNoteId, |  | ||||||
|         name: name, |  | ||||||
|         targetNoteId: targetNoteId |  | ||||||
|     }).save(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getEffectiveRelations(noteId, relationName) { |  | ||||||
|     const relations = await repository.getEntities(` |  | ||||||
|         WITH RECURSIVE tree(noteId) AS ( |  | ||||||
|         SELECT ? |  | ||||||
|             UNION |  | ||||||
|             SELECT branches.parentNoteId FROM branches |  | ||||||
|             JOIN tree ON branches.noteId = tree.noteId |  | ||||||
|             JOIN notes ON notes.noteId = branches.parentNoteId |  | ||||||
|             WHERE notes.isDeleted = 0 AND branches.isDeleted = 0 |  | ||||||
|         ) |  | ||||||
|         SELECT relations.* FROM relations JOIN tree ON relations.sourceNoteId = tree.noteId  |  | ||||||
|         WHERE relations.isDeleted = 0 AND (relations.isInheritable = 1 OR relations.sourceNoteId = ?)`, [noteId, noteId]); |  | ||||||
|  |  | ||||||
|     if (relationName) { |  | ||||||
|         return relations.filter(relation => relation.name === relationName); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         return relations; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
|     BUILTIN_RELATIONS, |  | ||||||
|     getNotesWithRelation, |  | ||||||
|     getNoteWithRelation, |  | ||||||
|     createRelation, |  | ||||||
|     getEffectiveRelations |  | ||||||
| }; |  | ||||||
| @@ -41,14 +41,6 @@ async function getAttribute(attributeId) { | |||||||
|     return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]); |     return await getEntity("SELECT * FROM attributes WHERE attributeId = ?", [attributeId]); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function getLabel(labelId) { |  | ||||||
|     return await getEntity("SELECT * FROM labels WHERE labelId = ?", [labelId]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getRelation(relationId) { |  | ||||||
|     return await getEntity("SELECT * FROM relations WHERE relationId = ?", [relationId]); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function getOption(name) { | async function getOption(name) { | ||||||
|     return await getEntity("SELECT * FROM options WHERE name = ?", [name]); |     return await getEntity("SELECT * FROM options WHERE name = ?", [name]); | ||||||
| } | } | ||||||
| @@ -61,6 +53,7 @@ async function updateEntity(entity) { | |||||||
|     const clone = Object.assign({}, entity); |     const clone = Object.assign({}, entity); | ||||||
|  |  | ||||||
|     delete clone.jsonContent; |     delete clone.jsonContent; | ||||||
|  |     delete clone.isOwned; | ||||||
|  |  | ||||||
|     for (const key in clone) { |     for (const key in clone) { | ||||||
|         if (clone[key] !== null && typeof clone[key] === 'object') { |         if (clone[key] !== null && typeof clone[key] === 'object') { | ||||||
| @@ -86,8 +79,6 @@ module.exports = { | |||||||
|     getBranch, |     getBranch, | ||||||
|     getImage, |     getImage, | ||||||
|     getAttribute, |     getAttribute, | ||||||
|     getLabel, |  | ||||||
|     getRelation, |  | ||||||
|     getOption, |     getOption, | ||||||
|     updateEntity, |     updateEntity, | ||||||
|     setEntityConstructor |     setEntityConstructor | ||||||
|   | |||||||
| @@ -235,8 +235,6 @@ const primaryKeys = { | |||||||
|     "recent_notes": "branchId", |     "recent_notes": "branchId", | ||||||
|     "images": "imageId", |     "images": "imageId", | ||||||
|     "note_images": "noteImageId", |     "note_images": "noteImageId", | ||||||
|     "labels": "labelId", |  | ||||||
|     "relations": "relationId", |  | ||||||
|     "api_tokens": "apiTokenId", |     "api_tokens": "apiTokenId", | ||||||
|     "options": "name" |     "options": "name" | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| const sql = require('./sql'); | const sql = require('./sql'); | ||||||
| const sourceIdService = require('./source_id'); | const sourceIdService = require('./source_id'); | ||||||
| const dateUtils = require('./date_utils'); | const dateUtils = require('./date_utils'); | ||||||
| const syncOptions = require('./sync_options'); |  | ||||||
| const log = require('./log'); | const log = require('./log'); | ||||||
| const cls = require('./cls'); | const cls = require('./cls'); | ||||||
| const eventService = require('./events'); | const eventService = require('./events'); | ||||||
| @@ -42,14 +41,6 @@ async function addAttributeSync(attributeId, sourceId) { | |||||||
|     await addEntitySync("attributes", attributeId, sourceId); |     await addEntitySync("attributes", attributeId, sourceId); | ||||||
| } | } | ||||||
|  |  | ||||||
| async function addLabelSync(labelId, sourceId) { |  | ||||||
|     await addEntitySync("labels", labelId, sourceId); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function addRelationSync(relationId, sourceId) { |  | ||||||
|     await addEntitySync("relations", relationId, sourceId); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function addApiTokenSync(apiTokenId, sourceId) { | async function addApiTokenSync(apiTokenId, sourceId) { | ||||||
|     await addEntitySync("api_tokens", apiTokenId, sourceId); |     await addEntitySync("api_tokens", apiTokenId, sourceId); | ||||||
| } | } | ||||||
| @@ -109,8 +100,6 @@ async function fillAllSyncRows() { | |||||||
|     await fillSyncRows("images", "imageId"); |     await fillSyncRows("images", "imageId"); | ||||||
|     await fillSyncRows("note_images", "noteImageId"); |     await fillSyncRows("note_images", "noteImageId"); | ||||||
|     await fillSyncRows("attributes", "attributeId"); |     await fillSyncRows("attributes", "attributeId"); | ||||||
|     await fillSyncRows("labels", "labelId"); |  | ||||||
|     await fillSyncRows("relations", "relationId"); |  | ||||||
|     await fillSyncRows("api_tokens", "apiTokenId"); |     await fillSyncRows("api_tokens", "apiTokenId"); | ||||||
|     await fillSyncRows("options", "name", 'isSynced = 1'); |     await fillSyncRows("options", "name", 'isSynced = 1'); | ||||||
| } | } | ||||||
| @@ -125,8 +114,6 @@ module.exports = { | |||||||
|     addImageSync, |     addImageSync, | ||||||
|     addNoteImageSync, |     addNoteImageSync, | ||||||
|     addAttributeSync, |     addAttributeSync, | ||||||
|     addLabelSync, |  | ||||||
|     addRelationSync, |  | ||||||
|     addApiTokenSync, |     addApiTokenSync, | ||||||
|     addEntitySync, |     addEntitySync, | ||||||
|     cleanupSyncRowsForMissingEntities, |     cleanupSyncRowsForMissingEntities, | ||||||
|   | |||||||
| @@ -33,12 +33,6 @@ async function updateEntity(sync, entity, sourceId) { | |||||||
|     else if (entityName === 'attributes') { |     else if (entityName === 'attributes') { | ||||||
|         await updateAttribute(entity, sourceId); |         await updateAttribute(entity, sourceId); | ||||||
|     } |     } | ||||||
|     else if (entityName === 'labels') { |  | ||||||
|         await updateLabel(entity, sourceId); |  | ||||||
|     } |  | ||||||
|     else if (entityName === 'relations') { |  | ||||||
|         await updateRelation(entity, sourceId); |  | ||||||
|     } |  | ||||||
|     else if (entityName === 'api_tokens') { |     else if (entityName === 'api_tokens') { | ||||||
|         await updateApiToken(entity, sourceId); |         await updateApiToken(entity, sourceId); | ||||||
|     } |     } | ||||||
| @@ -191,34 +185,6 @@ async function updateAttribute(entity, sourceId) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| async function updateLabel(entity, sourceId) { |  | ||||||
|     const origLabel = await sql.getRow("SELECT * FROM labels WHERE labelId = ?", [entity.labelId]); |  | ||||||
|  |  | ||||||
|     if (!origLabel || origLabel.dateModified <= entity.dateModified) { |  | ||||||
|         await sql.transactional(async () => { |  | ||||||
|             await sql.replace("labels", entity); |  | ||||||
|  |  | ||||||
|             await syncTableService.addLabelSync(entity.labelId, sourceId); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         log.info("Update/sync label " + entity.labelId); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function updateRelation(entity, sourceId) { |  | ||||||
|     const origRelation = await sql.getRow("SELECT * FROM relations WHERE relationId = ?", [entity.relationId]); |  | ||||||
|  |  | ||||||
|     if (!origRelation || origRelation.dateModified <= entity.dateModified) { |  | ||||||
|         await sql.transactional(async () => { |  | ||||||
|             await sql.replace("relations", entity); |  | ||||||
|  |  | ||||||
|             await syncTableService.addRelationSync(entity.relationId, sourceId); |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         log.info("Update/sync relation " + entity.relationId); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function updateApiToken(entity, sourceId) { | async function updateApiToken(entity, sourceId) { | ||||||
|     const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]); |     const apiTokenId = await sql.getRow("SELECT * FROM api_tokens WHERE apiTokenId = ?", [entity.apiTokenId]); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user