mirror of
https://github.com/zadam/trilium.git
synced 2025-01-07 15:49:01 +08:00
script to generate large documents, closes #55
This commit is contained in:
parent
42dd8d4754
commit
abfc64af95
15 changed files with 203 additions and 75 deletions
28
package-lock.json
generated
28
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "trilium",
|
||||
"version": "0.9.1-beta",
|
||||
"version": "0.9.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -7121,6 +7121,32 @@
|
|||
"js-tokens": "3.0.2"
|
||||
}
|
||||
},
|
||||
"lorem-ipsum": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lorem-ipsum/-/lorem-ipsum-1.0.4.tgz",
|
||||
"integrity": "sha1-MLcqOx4ZH1UGKvjH36spGuT72RI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"optimist": "0.3.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"optimist": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
|
||||
"integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wordwrap": "0.0.3"
|
||||
}
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"loud-rejection": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
"electron-compile": "^6.4.2",
|
||||
"electron-packager": "^11.1.0",
|
||||
"electron-prebuilt-compile": "2.0.0-beta.5",
|
||||
"lorem-ipsum": "^1.0.4",
|
||||
"tape": "^4.9.0",
|
||||
"xo": "^0.18.0"
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
const Entity = require('./entity');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
const repository = require('../services/repository');
|
||||
const sql = require('../services/sql');
|
||||
|
||||
class Branch extends Entity {
|
||||
static get tableName() { return "branches"; }
|
||||
|
@ -12,9 +13,14 @@ class Branch extends Entity {
|
|||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
async beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (this.notePosition === undefined) {
|
||||
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]);
|
||||
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||
}
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ async function showDialog() {
|
|||
|
||||
await $autoComplete.autocomplete({
|
||||
source: await utils.stopWatch("building autocomplete", autocompleteService.getAutocompleteItems),
|
||||
minLength: 0
|
||||
minLength: 1
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,10 @@ async function getAutocompleteItems(parentNoteId, notePath, titlePath) {
|
|||
}
|
||||
}
|
||||
|
||||
if (parentNoteId === 'root') {
|
||||
console.log(`Generated ${autocompleteItems.length} autocomplete items`);
|
||||
}
|
||||
|
||||
return autocompleteItems;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ const notes = require('../../services/notes');
|
|||
const repository = require('../../services/repository');
|
||||
|
||||
/**
|
||||
* Code in this file deals with moving and cloning note tree rows. Relationship between note and parent note is unique
|
||||
* for not deleted note trees. There may be multiple deleted note-parent note relationships.
|
||||
* Code in this file deals with moving and cloning branches. Relationship between note and parent note is unique
|
||||
* for not deleted branches. There may be multiple deleted note-parent note relationships.
|
||||
*/
|
||||
|
||||
async function moveBranchToParent(req) {
|
||||
|
@ -49,7 +49,7 @@ async function moveBranchBeforeNote(req) {
|
|||
}
|
||||
|
||||
// we don't change dateModified so other changes are prioritized in case of conflict
|
||||
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
||||
// also we would have to sync all those modified branches otherwise hash checks would fail
|
||||
await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0",
|
||||
[beforeNote.parentNoteId, beforeNote.notePosition]);
|
||||
|
||||
|
@ -77,7 +77,7 @@ async function moveBranchAfterNote(req) {
|
|||
}
|
||||
|
||||
// we don't change dateModified so other changes are prioritized in case of conflict
|
||||
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
||||
// also we would have to sync all those modified branches otherwise hash checks would fail
|
||||
await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
|
||||
[afterNote.parentNoteId, afterNote.notePosition]);
|
||||
|
||||
|
|
|
@ -1,64 +1,20 @@
|
|||
"use strict";
|
||||
|
||||
const sql = require('../../services/sql');
|
||||
const syncTable = require('../../services/sync_table');
|
||||
const tree = require('../../services/tree');
|
||||
const Branch = require('../../entities/branch');
|
||||
const cloningService = require('../../services/cloning');
|
||||
|
||||
async function cloneNoteToParent(req) {
|
||||
const noteId = req.params.noteId;
|
||||
const parentNoteId = req.params.parentNoteId;
|
||||
const prefix = req.body.prefix;
|
||||
|
||||
const validationResult = await tree.validateParentChild(parentNoteId, noteId);
|
||||
|
||||
if (!validationResult.success) {
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]);
|
||||
const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||
|
||||
const branch = await new Branch({
|
||||
noteId: noteId,
|
||||
parentNoteId: parentNoteId,
|
||||
prefix: prefix,
|
||||
notePosition: newNotePos,
|
||||
isExpanded: 0
|
||||
}).save();
|
||||
|
||||
await sql.execute("UPDATE branches SET isExpanded = 1 WHERE noteId = ?", [parentNoteId]);
|
||||
|
||||
return { success: true };
|
||||
return await cloningService.cloneNoteToParent(noteId, parentNoteId, prefix);
|
||||
}
|
||||
|
||||
async function cloneNoteAfter(req) {
|
||||
const noteId = req.params.noteId;
|
||||
const afterBranchId = req.params.afterBranchId;
|
||||
|
||||
const afterNote = await tree.getBranch(afterBranchId);
|
||||
|
||||
const validationResult = await tree.validateParentChild(afterNote.parentNoteId, noteId);
|
||||
|
||||
if (!validationResult.result) {
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
// we don't change dateModified so other changes are prioritized in case of conflict
|
||||
// also we would have to sync all those modified note trees otherwise hash checks would fail
|
||||
await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
|
||||
[afterNote.parentNoteId, afterNote.notePosition]);
|
||||
|
||||
await syncTable.addNoteReorderingSync(afterNote.parentNoteId);
|
||||
|
||||
const branch = await new Branch({
|
||||
noteId: noteId,
|
||||
parentNoteId: afterNote.parentNoteId,
|
||||
notePosition: afterNote.notePosition + 1,
|
||||
isExpanded: 0
|
||||
}).save();
|
||||
|
||||
return { success: true };
|
||||
return await cloningService.cloneNoteAfter(noteId, afterBranchId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -110,17 +110,17 @@ async function importNotes(files, parentNoteId) {
|
|||
file.data = file.data.toString("UTF-8");
|
||||
}
|
||||
|
||||
const noteId = await noteService.createNote(parentNoteId, file.meta.title, file.data, {
|
||||
const {note} = await noteService.createNote(parentNoteId, file.meta.title, file.data, {
|
||||
type: file.meta.type,
|
||||
mime: file.meta.mime
|
||||
});
|
||||
|
||||
for (const label of file.meta.labels) {
|
||||
await labelService.createLabel(noteId, label.name, label.value);
|
||||
await labelService.createLabel(note.noteId, label.name, label.value);
|
||||
}
|
||||
|
||||
if (file.children.length > 0) {
|
||||
await importNotes(file.children, noteId);
|
||||
await importNotes(file.children, note.noteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,13 @@ async function saveSearchToNote(req) {
|
|||
searchString: req.params.searchString
|
||||
};
|
||||
|
||||
const noteId = await noteService.createNote('root', 'Search note', noteContent, {
|
||||
const {note} = await noteService.createNote('root', 'Search note', noteContent, {
|
||||
json: true,
|
||||
type: 'search',
|
||||
mime: "application/json"
|
||||
});
|
||||
|
||||
return { noteId };
|
||||
return { noteId: note.noteId };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
56
src/services/cloning.js
Normal file
56
src/services/cloning.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
"use strict";
|
||||
|
||||
const sql = require('./sql');
|
||||
const syncTable = require('./sync_table');
|
||||
const tree = require('./tree');
|
||||
const Branch = require('../entities/branch');
|
||||
|
||||
async function cloneNoteToParent(noteId, parentNoteId, prefix) {
|
||||
const validationResult = await tree.validateParentChild(parentNoteId, noteId);
|
||||
|
||||
if (!validationResult.success) {
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
await new Branch({
|
||||
noteId: noteId,
|
||||
parentNoteId: parentNoteId,
|
||||
prefix: prefix,
|
||||
isExpanded: 0
|
||||
}).save();
|
||||
|
||||
await sql.execute("UPDATE branches SET isExpanded = 1 WHERE noteId = ?", [parentNoteId]);
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async function cloneNoteAfter(noteId, afterBranchId) {
|
||||
const afterNote = await tree.getBranch(afterBranchId);
|
||||
|
||||
const validationResult = await tree.validateParentChild(afterNote.parentNoteId, noteId);
|
||||
|
||||
if (!validationResult.result) {
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
// we don't change dateModified so other changes are prioritized in case of conflict
|
||||
// also we would have to sync all those modified branches otherwise hash checks would fail
|
||||
await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
|
||||
[afterNote.parentNoteId, afterNote.notePosition]);
|
||||
|
||||
await syncTable.addNoteReorderingSync(afterNote.parentNoteId);
|
||||
|
||||
await new Branch({
|
||||
noteId: noteId,
|
||||
parentNoteId: afterNote.parentNoteId,
|
||||
notePosition: afterNote.notePosition + 1,
|
||||
isExpanded: 0
|
||||
}).save();
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
cloneNoteToParent,
|
||||
cloneNoteAfter
|
||||
};
|
|
@ -105,7 +105,7 @@ async function runAllChecks() {
|
|||
LEFT JOIN notes USING(noteId)
|
||||
WHERE
|
||||
notes.noteId IS NULL`,
|
||||
"Missing notes records for following note tree ID > note ID", errorList);
|
||||
"Missing notes records for following branch ID > note ID", errorList);
|
||||
|
||||
await runCheck(`
|
||||
SELECT
|
||||
|
@ -116,7 +116,7 @@ async function runAllChecks() {
|
|||
WHERE
|
||||
notes.isDeleted = 1
|
||||
AND branches.isDeleted = 0`,
|
||||
"Note tree is not deleted even though main note is deleted for following note tree IDs", errorList);
|
||||
"Note tree is not deleted even though main note is deleted for following branch IDs", errorList);
|
||||
|
||||
await runCheck(`
|
||||
SELECT
|
||||
|
@ -128,7 +128,7 @@ async function runAllChecks() {
|
|||
AND child.parentNoteId != 'root'
|
||||
AND (SELECT COUNT(*) FROM branches AS parent WHERE parent.noteId = child.parentNoteId
|
||||
AND parent.isDeleted = 0) = 0`,
|
||||
"All parent note trees are deleted but child note tree is not for these child note tree IDs", errorList);
|
||||
"All parent branches are deleted but child note tree is not for these child note tree IDs", errorList);
|
||||
|
||||
// we do extra JOIN to eliminate orphan notes without note tree (which are reported separately)
|
||||
await runCheck(`
|
||||
|
@ -140,7 +140,7 @@ async function runAllChecks() {
|
|||
WHERE
|
||||
(SELECT COUNT(*) FROM branches WHERE notes.noteId = branches.noteId AND branches.isDeleted = 0) = 0
|
||||
AND notes.isDeleted = 0
|
||||
`, 'No undeleted note trees for note IDs', errorList);
|
||||
`, 'No undeleted branches for note IDs', errorList);
|
||||
|
||||
await runCheck(`
|
||||
SELECT
|
||||
|
|
|
@ -83,7 +83,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
|
|||
noteData.mime = "application/json";
|
||||
}
|
||||
|
||||
const {note} = await createNewNote(parentNoteId, noteData);
|
||||
const {note, branch} = await createNewNote(parentNoteId, noteData);
|
||||
|
||||
if (extraOptions.labels) {
|
||||
for (const labelName in extraOptions.labels) {
|
||||
|
@ -91,7 +91,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
|
|||
}
|
||||
}
|
||||
|
||||
return note.noteId;
|
||||
return {note, branch};
|
||||
}
|
||||
|
||||
async function protectNoteRecursively(note, protect) {
|
||||
|
|
|
@ -11,6 +11,9 @@ async function createConnection() {
|
|||
return await sqlite.open(dataDir.DOCUMENT_PATH, {Promise});
|
||||
}
|
||||
|
||||
let schemaReadyResolve = null;
|
||||
const schemaReady = new Promise((resolve, reject) => schemaReadyResolve = resolve);
|
||||
|
||||
let dbReadyResolve = null;
|
||||
const dbReady = new Promise((resolve, reject) => {
|
||||
cls.init(async () => {
|
||||
|
@ -29,19 +32,20 @@ const dbReady = new Promise((resolve, reject) => {
|
|||
if (tableResults.length !== 1) {
|
||||
await createInitialDatabase();
|
||||
}
|
||||
else {
|
||||
if (!await isUserInitialized()) {
|
||||
log.info("Login/password not initialized. DB not ready.");
|
||||
|
||||
return;
|
||||
}
|
||||
schemaReadyResolve();
|
||||
|
||||
if (!await isDbUpToDate()) {
|
||||
return;
|
||||
}
|
||||
if (!await isUserInitialized()) {
|
||||
log.info("Login/password not initialized. DB not ready.");
|
||||
|
||||
resolve(db);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await isDbUpToDate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(db);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -97,6 +101,7 @@ async function isUserInitialized() {
|
|||
|
||||
module.exports = {
|
||||
dbReady,
|
||||
schemaReady,
|
||||
isUserInitialized,
|
||||
setDbReadyAsResolved,
|
||||
isDbUpToDate
|
||||
|
|
|
@ -37,7 +37,7 @@ async function updateBranch(entity, sourceId) {
|
|||
|
||||
await syncTableService.addBranchSync(entity.branchId, sourceId);
|
||||
|
||||
log.info("Update/sync note tree " + entity.branchId);
|
||||
log.info("Update/sync branch " + entity.branchId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
74
src/tools/generate_document.js
Normal file
74
src/tools/generate_document.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
const fs = require('fs');
|
||||
const dataDir = require('../services/data_dir');
|
||||
|
||||
fs.unlinkSync(dataDir.DOCUMENT_PATH);
|
||||
|
||||
require('../entities/entity_constructor');
|
||||
const optionService = require('../services/options');
|
||||
const sqlInit = require('../services/sql_init');
|
||||
const myScryptService = require('../services/my_scrypt');
|
||||
const passwordEncryptionService = require('../services/password_encryption');
|
||||
const utils = require('../services/utils');
|
||||
const noteService = require('../services/notes');
|
||||
const cls = require('../services/cls');
|
||||
const cloningService = require('../services/cloning');
|
||||
const loremIpsum = require('lorem-ipsum');
|
||||
|
||||
async function setUserNamePassword() {
|
||||
const username = "test";
|
||||
const password = "test";
|
||||
|
||||
await optionService.setOption('username', username);
|
||||
|
||||
await optionService.setOption('passwordVerificationSalt', utils.randomSecureToken(32));
|
||||
await optionService.setOption('passwordDerivedKeySalt', utils.randomSecureToken(32));
|
||||
|
||||
const passwordVerificationKey = utils.toBase64(await myScryptService.getVerificationHash(password));
|
||||
await optionService.setOption('passwordVerificationHash', passwordVerificationKey);
|
||||
|
||||
await passwordEncryptionService.setDataKey(password, utils.randomSecureToken(16));
|
||||
|
||||
sqlInit.setDbReadyAsResolved();
|
||||
}
|
||||
|
||||
const noteCount = parseInt(process.argv[2]);
|
||||
const notes = ['root'];
|
||||
|
||||
function getRandomParentNoteId() {
|
||||
const index = Math.floor(Math.random() * notes.length);
|
||||
|
||||
return notes[index];
|
||||
}
|
||||
|
||||
async function start() {
|
||||
await setUserNamePassword();
|
||||
|
||||
for (let i = 0; i < noteCount; i++) {
|
||||
const title = loremIpsum({ count: 1, units: 'sentences', sentenceLowerBound: 1, sentenceUpperBound: 10 });
|
||||
|
||||
const paragraphCount = Math.floor(Math.random() * Math.random() * 100);
|
||||
const content = loremIpsum({ count: paragraphCount, units: 'paragraphs', sentenceLowerBound: 1, sentenceUpperBound: 15,
|
||||
paragraphLowerBound: 3, paragraphUpperBound: 10, format: 'html' });
|
||||
|
||||
const {note} = await noteService.createNote(getRandomParentNoteId(), title, content);
|
||||
|
||||
console.log(`Created note ${i}: ${title}`);
|
||||
|
||||
notes.push(note.noteId);
|
||||
}
|
||||
|
||||
// we'll create clones for 20% of notes
|
||||
for (let i = 0; i < (noteCount / 5); i++) {
|
||||
const noteIdToClone = getRandomParentNoteId();
|
||||
const parentNoteId = getRandomParentNoteId();
|
||||
const prefix = Math.random() > 0.8 ? "prefix" : null;
|
||||
|
||||
const result = await cloningService.cloneNoteToParent(noteIdToClone, parentNoteId, prefix);
|
||||
|
||||
console.log(`Cloning ${i}:`, result.success ? "succeeded" : "FAILED");
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
sqlInit.schemaReady.then(cls.wrap(start));
|
Loading…
Reference in a new issue