refactoring of note creation APIs WIP

This commit is contained in:
zadam 2019-11-16 11:09:52 +01:00
parent de02e9e889
commit 13c0411533
13 changed files with 115 additions and 107 deletions

View File

@ -475,7 +475,7 @@ class Note extends Entity {
/** /**
* @return {Promise<Attribute>} * @return {Promise<Attribute>}
*/ */
async createAttribute(type, name, value = "") { async addAttribute(type, name, value = "") {
const attr = new Attribute({ const attr = new Attribute({
noteId: this.noteId, noteId: this.noteId,
type: type, type: type,
@ -490,12 +490,12 @@ class Note extends Entity {
return attr; return attr;
} }
async createLabel(name, value = "") { async addLabel(name, value = "") {
return await this.createAttribute(LABEL, name, value); return await this.addAttribute(LABEL, name, value);
} }
async createRelation(name, targetNoteId) { async addRelation(name, targetNoteId) {
return await this.createAttribute(RELATION, name, targetNoteId); return await this.addAttribute(RELATION, name, targetNoteId);
} }
/** /**

View File

@ -31,7 +31,11 @@ async function addClipping(req) {
let clippingNote = await findClippingNote(todayNote, pageUrl); let clippingNote = await findClippingNote(todayNote, pageUrl);
if (!clippingNote) { if (!clippingNote) {
clippingNote = (await noteService.createNote(todayNote.noteId, title, '')).note; clippingNote = (await noteService.createNewNote({
parentNoteId: todayNote.noteId,
title: title,
content: ''
})).note;
await clippingNote.setLabel('clipType', 'clippings'); await clippingNote.setLabel('clipType', 'clippings');
await clippingNote.setLabel('pageUrl', pageUrl); await clippingNote.setLabel('pageUrl', pageUrl);
@ -51,7 +55,11 @@ async function createNote(req) {
const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate()); const todayNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
const {note} = await noteService.createNote(todayNote.noteId, title, content); const {note} = await noteService.createNewNote({
parentNoteId: todayNote.noteId,
title,
content
});
await note.setLabel('clipType', clipType); await note.setLabel('clipType', clipType);

View File

@ -53,10 +53,10 @@ async function getChildren(req) {
} }
async function createNote(req) { async function createNote(req) {
const parentNoteId = req.params.parentNoteId; const params = Object.assign({}, req.body); // clone
const newNote = req.body; params.parentNoteId = req.params.parentNoteId;
const { note, branch } = await noteService.createNewNote(parentNoteId, newNote, req); const { note, branch } = await noteService.createNewNote(params);
note.cssClass = (await note.getLabels("cssClass")).map(label => label.value).join(" "); note.cssClass = (await note.getLabels("cssClass")).map(label => label.value).join(" ");

View File

@ -26,10 +26,10 @@ async function uploadImage(req) {
async function saveNote(req) { async function saveNote(req) {
const parentNote = await dateNoteService.getDateNote(req.headers['x-local-date']); const parentNote = await dateNoteService.getDateNote(req.headers['x-local-date']);
const {note, branch} = await noteService.createNewNote(parentNote.noteId, { const {note, branch} = await noteService.createNewNote({
parentNoteId: parentNote.noteId,
title: req.body.title, title: req.body.title,
content: req.body.content, content: req.body.content,
target: 'into',
isProtected: false, isProtected: false,
type: 'text', type: 'text',
mime: 'text/html' mime: 'text/html'

View File

@ -178,7 +178,7 @@ function BackendScriptApi(currentNote, apiParams) {
*/ */
/** /**
* @typedef {object} CreateNoteExtraOptions * @typedef {object} CreateNoteParams
* @property {boolean} [json=false] - should the note be JSON * @property {boolean} [json=false] - should the note be JSON
* @property {boolean} [isProtected=false] - should the note be protected * @property {boolean} [isProtected=false] - should the note be protected
* @property {string} [type='text'] - note type * @property {string} [type='text'] - note type
@ -189,13 +189,10 @@ function BackendScriptApi(currentNote, apiParams) {
/** /**
* @method * @method
* *
* @param {string} parentNoteId - create new note under this parent * @param {CreateNoteParams} [extraOptions={}]
* @param {string} title
* @param {string} [content=""]
* @param {CreateNoteExtraOptions} [extraOptions={}]
* @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
*/ */
this.createNote = noteService.createNote; this.createNote = noteService.createNewNote;
/** /**
* Creates new note according to given params and force all connected clients to refresh their tree. * Creates new note according to given params and force all connected clients to refresh their tree.
@ -205,11 +202,11 @@ function BackendScriptApi(currentNote, apiParams) {
* @param {string} parentNoteId - create new note under this parent * @param {string} parentNoteId - create new note under this parent
* @param {string} title * @param {string} title
* @param {string} [content=""] * @param {string} [content=""]
* @param {CreateNoteExtraOptions} [extraOptions={}] * @param {CreateNoteParams} [extraOptions={}]
* @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch
*/ */
this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) { this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) {
const ret = await noteService.createNote(parentNoteId, title, content, extraOptions); const ret = await noteService.createNewNote(parentNoteId, title, content, extraOptions);
ws.refreshTree(); ws.refreshTree();

View File

@ -14,10 +14,10 @@ const DAYS = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Satur
const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December']; const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
async function createNote(parentNoteId, noteTitle, noteText) { async function createNote(parentNoteId, noteTitle, noteText) {
return (await noteService.createNewNote(parentNoteId, { return (await noteService.createNewNote({
parentNoteId: parentNoteId,
title: noteTitle, title: noteTitle,
content: noteText, content: noteText,
target: 'into',
isProtected: false isProtected: false
})).note; })).note;
} }
@ -35,7 +35,8 @@ async function getRootCalendarNote() {
let rootNote = await attributeService.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({
parentNoteId: 'root',
title: 'Calendar', title: 'Calendar',
target: 'into', target: 'into',
isProtected: false isProtected: false

View File

@ -57,14 +57,17 @@ async function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSw
const parentNote = await repository.getNote(parentNoteId); const parentNote = await repository.getNote(parentNoteId);
const {note} = await noteService.createNote(parentNoteId, fileName, buffer, { const {note} = await noteService.createNewNote({
target: 'into', parentNoteId,
title: fileName,
content: buffer,
type: 'image', type: 'image',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
mime: 'image/' + imageFormat.ext.toLowerCase(), mime: 'image/' + imageFormat.ext.toLowerCase(),
attributes: [{ type: 'label', name: 'originalFileName', value: originalName }] isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable()
}); });
await note.addLabel('originalFileName', originalName);
return { return {
fileName, fileName,
note, note,

View File

@ -30,7 +30,10 @@ async function importEnex(taskContext, file, parentNote) {
: file.originalname; : file.originalname;
// root note is new note into all ENEX/notebook's notes will be imported // root note is new note into all ENEX/notebook's notes will be imported
const rootNote = (await noteService.createNote(parentNote.noteId, rootNoteTitle, "", { const rootNote = (await noteService.createNewNote({
parentNoteId: parentNote.noteId,
title: rootNoteTitle,
content: "",
type: 'text', type: 'text',
mime: 'text/html', mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
@ -207,14 +210,20 @@ async function importEnex(taskContext, file, parentNote) {
// following is workaround for this issue: https://github.com/Leonidas-from-XIV/node-xml2js/issues/484 // following is workaround for this issue: https://github.com/Leonidas-from-XIV/node-xml2js/issues/484
content = extractContent(xmlObject['en-note']); content = extractContent(xmlObject['en-note']);
const noteEntity = (await noteService.createNote(rootNote.noteId, title, content, { const noteEntity = (await noteService.createNewNote({
attributes, parentNoteId: rootNote.noteId,
title,
content,
utcDateCreated, utcDateCreated,
type: 'text', type: 'text',
mime: 'text/html', mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note; })).note;
for (const attr of resource.attributes) {
await note.addAttribute(attr.type, attr.name, attr.value);
}
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();
let noteContent = await noteEntity.getContent(); let noteContent = await noteEntity.getContent();
@ -231,13 +240,19 @@ async function importEnex(taskContext, file, parentNote) {
} }
const createFileNote = async () => { const createFileNote = async () => {
const resourceNote = (await noteService.createNote(noteEntity.noteId, resource.title, resource.content, { const resourceNote = (await noteService.createNewNote({
attributes: resource.attributes, parentNoteId: noteEntity.noteId,
title: resource.title,
content: resource.content,
type: 'file', type: 'file',
mime: resource.mime, mime: resource.mime,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
})).note; })).note;
for (const attr of resource.attributes) {
await note.addAttribute(attr.type, attr.name, attr.value);
}
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();
const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`; const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`;

View File

@ -44,8 +44,11 @@ async function importOpml(taskContext, fileBuffer, parentNote) {
throw new Error("Unrecognized OPML version " + opmlVersion); throw new Error("Unrecognized OPML version " + opmlVersion);
} }
const {note} = await noteService.createNote(parentNoteId, title, content, { const {note} = await noteService.createNewNote({
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), parentNoteId,
title,
content,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable()
}); });
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();

View File

@ -41,16 +41,18 @@ async function importImage(file, parentNote, taskContext) {
async function importFile(taskContext, file, parentNote) { async function importFile(taskContext, file, parentNote) {
const originalName = file.originalname; const originalName = file.originalname;
const size = file.size;
const {note} = await noteService.createNote(parentNote.noteId, originalName, file.buffer, { const {note} = await noteService.createNewNote({
target: 'into', parentNoteId: parentNote.noteId,
title: originalName,
content: file.buffer,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
type: 'file', type: 'file',
mime: mimeService.getMime(originalName) || file.mimetype, mime: mimeService.getMime(originalName) || file.mimetype
attributes: [{ type: "label", name: "originalFileName", value: originalName }]
}); });
await note.addLabel("originalFileName", originalName);
taskContext.increaseProgressCount(); taskContext.increaseProgressCount();
return note; return note;
@ -62,7 +64,10 @@ async function importCodeNote(taskContext, file, parentNote) {
const detectedMime = mimeService.getMime(file.originalname) || file.mimetype; const detectedMime = mimeService.getMime(file.originalname) || file.mimetype;
const mime = mimeService.normalizeMimeType(detectedMime); const mime = mimeService.normalizeMimeType(detectedMime);
const {note} = await noteService.createNote(parentNote.noteId, title, content, { const {note} = await noteService.createNewNote({
parentNoteId: parentNote.noteId,
title,
content,
type: 'code', type: 'code',
mime: mime, mime: mime,
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable() isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable()
@ -78,7 +83,10 @@ async function importPlainText(taskContext, file, parentNote) {
const plainTextContent = file.buffer.toString("UTF-8"); const plainTextContent = file.buffer.toString("UTF-8");
const htmlContent = convertTextToHtml(plainTextContent); const htmlContent = convertTextToHtml(plainTextContent);
const {note} = await noteService.createNote(parentNote.noteId, title, htmlContent, { const {note} = await noteService.createNewNote({
parentNoteId: parentNote.noteId,
title,
content: htmlContent,
type: 'text', type: 'text',
mime: 'text/html', mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
@ -118,7 +126,10 @@ async function importMarkdown(taskContext, file, parentNote) {
const title = getFileNameWithoutExtension(file.originalname); const title = getFileNameWithoutExtension(file.originalname);
const {note} = await noteService.createNote(parentNote.noteId, title, htmlContent, { const {note} = await noteService.createNewNote({
parentNoteId: parentNote.noteId,
title,
content: htmlContent,
type: 'text', type: 'text',
mime: 'text/html', mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
@ -133,7 +144,10 @@ async function importHtml(taskContext, file, parentNote) {
const title = getFileNameWithoutExtension(file.originalname); const title = getFileNameWithoutExtension(file.originalname);
const content = file.buffer.toString("UTF-8"); const content = file.buffer.toString("UTF-8");
const {note} = await noteService.createNote(parentNote.noteId, title, content, { const {note} = await noteService.createNewNote({
parentNoteId: parentNote.noteId,
title,
content,
type: 'text', type: 'text',
mime: 'text/html', mime: 'text/html',
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),

View File

@ -177,8 +177,11 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
return; return;
} }
({note} = await noteService.createNote(parentNoteId, noteTitle, '', { ({note} = await noteService.createNewNote({
noteId, parentNoteId: parentNoteId,
title: noteTitle,
content: '',
noteId: noteId,
type: noteMeta ? noteMeta.type : 'text', type: noteMeta ? noteMeta.type : 'text',
mime: noteMeta ? noteMeta.mime : 'text/html', mime: noteMeta ? noteMeta.mime : 'text/html',
prefix: noteMeta ? noteMeta.prefix : '', prefix: noteMeta ? noteMeta.prefix : '',
@ -324,7 +327,10 @@ async function importTar(taskContext, fileBuffer, importRootNote) {
await note.setContent(content); await note.setContent(content);
} }
else { else {
({note} = await noteService.createNote(parentNoteId, noteTitle, content, { ({note} = await noteService.createNewNote({
parentNoteId: parentNoteId,
title: noteTitle,
content: content,
noteId, noteId,
type, type,
mime, mime,

View File

@ -34,30 +34,24 @@ async function triggerNoteTitleChanged(note) {
await eventService.emit(eventService.NOTE_TITLE_CHANGED, note); await eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
} }
function deriveTypeAndMime(noteData, parentNote) { function deriveMime(type, mime) {
if (!noteData.type) { if (!type) {
if (parentNote.type === 'text' || parentNote.type === 'code') { throw new Error(`Note type is a required param`);
noteData.type = parentNote.type;
noteData.mime = parentNote.mime;
} else {
// inheriting note type makes sense only for text and code
noteData.type = 'text';
noteData.mime = 'text/html';
}
} }
if (!noteData.mime) { if (mime) {
if (noteData.type === 'text') { return mime;
noteData.mime = 'text/html';
} else if (noteData.type === 'code') {
noteData.mime = 'text/plain';
} else if (noteData.type === 'relation-map' || noteData.type === 'search') {
noteData.mime = 'application/json';
}
} }
noteData.type = noteData.type || parentNote.type; if (type === 'text') {
noteData.mime = noteData.mime || parentNote.mime; mime = 'text/html';
} else if (type === 'code') {
mime = 'text/plain';
} else if (['relation-map', 'search'].includes(type)) {
mime = 'application/json';
}
return mime;
} }
async function copyChildAttributes(parentNote, childNote) { async function copyChildAttributes(parentNote, childNote) {
@ -92,7 +86,7 @@ async function copyChildAttributes(parentNote, childNote) {
* - {integer} notePosition - default is last existing notePosition in a parent + 10 * - {integer} notePosition - default is last existing notePosition in a parent + 10
* *
* @param params * @param params
* @return {Promise<{note: Entity, branch: Entity}>} * @return {Promise<{note: Note, branch: Branch}>}
*/ */
async function createNewNote(params) { async function createNewNote(params) {
const parentNote = await repository.getNote(params.parentNoteId); const parentNote = await repository.getNote(params.parentNoteId);
@ -105,14 +99,12 @@ async function createNewNote(params) {
throw new Error(`Note title must not be empty`); throw new Error(`Note title must not be empty`);
} }
deriveTypeAndMime(params, parentNote);
const note = await new Note({ const note = await new Note({
noteId: params.noteId, // optionally can force specific noteId noteId: params.noteId, // optionally can force specific noteId
title: params.title, title: params.title,
isProtected: !!params.isProtected, isProtected: !!params.isProtected,
type: params.type, type: params.type,
mime: params.mime mime: deriveMime(params.type, params.mime)
}).save(); }).save();
await note.setContent(params.content); await note.setContent(params.content);
@ -161,41 +153,6 @@ async function createTextNote(parentNoteId, title, content = "", params = {}) {
return await createNewNote(params); return await createNewNote(params);
} }
/**
* @deprecated
*/
async function createNote(parentNoteId, title, content = "", extraOptions = {}) {
if (!parentNoteId) throw new Error("Empty parentNoteId");
if (!title) throw new Error("Empty title");
const noteData = {
title: title,
content: content,
target: 'into',
noteId: extraOptions.noteId,
isProtected: !!extraOptions.isProtected,
type: extraOptions.type,
mime: extraOptions.mime,
dateCreated: extraOptions.dateCreated,
isExpanded: extraOptions.isExpanded,
notePosition: extraOptions.notePosition
};
const {note, branch} = await createNewNote(parentNoteId, title, noteData);
for (const attr of extraOptions.attributes || []) {
await attributeService.createAttribute({
noteId: note.noteId,
type: attr.type,
name: attr.name,
value: attr.value,
isInheritable: !!attr.isInheritable
});
}
return {note, branch};
}
async function protectNoteRecursively(note, protect, taskContext) { async function protectNoteRecursively(note, protect, taskContext) {
await protectNote(note, protect); await protectNote(note, protect);
@ -555,7 +512,6 @@ sqlInit.dbReady.then(() => {
module.exports = { module.exports = {
createNewNote, createNewNote,
createNote,
updateNote, updateNote,
deleteBranch, deleteBranch,
protectNoteRecursively, protectNoteRecursively,

View File

@ -50,7 +50,12 @@ async function start() {
const content = loremIpsum({ count: paragraphCount, units: 'paragraphs', sentenceLowerBound: 1, sentenceUpperBound: 15, const content = loremIpsum({ count: paragraphCount, units: 'paragraphs', sentenceLowerBound: 1, sentenceUpperBound: 15,
paragraphLowerBound: 3, paragraphUpperBound: 10, format: 'html' }); paragraphLowerBound: 3, paragraphUpperBound: 10, format: 'html' });
const {note} = await noteService.createNote(getRandomParentNoteId(), title, content); const {note} = await noteService.createNewNote({
parentNoteId: getRandomParentNoteId(),
title,
content,
type: 'text'
});
console.log(`Created note ${i}: ${title}`); console.log(`Created note ${i}: ${title}`);