1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

memory driver

This commit is contained in:
Laurent Cozic 2017-06-13 20:12:08 +00:00
parent 8578642307
commit c92b46b651
5 changed files with 441 additions and 321 deletions

View File

@ -2,6 +2,7 @@ require('source-map-support').install();
import { FileApi } from 'src/file-api.js';
import { FileApiDriverLocal } from 'src/file-api-driver-local.js';
import { FileApiDriverMemory } from 'src/file-api-driver-memory.js';
import { Database } from 'src/database.js';
import { DatabaseDriverNode } from 'src/database-driver-node.js';
import { BaseModel } from 'src/base-model.js';
@ -14,356 +15,359 @@ import { sprintf } from 'sprintf-js';
import { _ } from 'src/locale.js';
import { NoteFolderService } from 'src/services/note-folder-service.js';
// name: 'f42b0e23f06948ee9dda3fcf1b1c4205/.folder.md',
// createdTime: 1497216952,
// updatedTime: 1497216952,
// createdTimeOrig: 2017-06-11T21:35:52.362Z,
// updatedTimeOrig: 2017-06-11T21:35:52.362Z,
// isDir: false
// Sun, 11 Jun 2017 21:35:52 GMT
// import moment from 'moment';
// let m = moment('2017-06-11T21:35:52.362Z');
// console.info(Math.round(m.toDate().getTime() / 1000));
// //let m = moment(time, 'YYYY-MM-DDTHH:mm:ss.SSSZ');
// // if (!m.isValid()) {
// // throw new Error('Invalid date: ' + time);
// // }
// // return Math.round(m.toDate().getTime() / 1000);
const vorpal = require('vorpal')();
let db = new Database(new DatabaseDriverNode());
db.setDebugEnabled(false);
let fileDriver = new FileApiDriverLocal();
let fileApi = new FileApi('/home/laurent/Temp/TestImport', fileDriver);
// let fileDriver = new FileApiDriverLocal();
// let fileApi = new FileApi('/home/laurent/Temp/TestImport', fileDriver);
// let synchronizer = new Synchronizer(db, fileApi);
let fileDriver = new FileApiDriverMemory();
let fileApi = new FileApi('/root', fileDriver);
let synchronizer = new Synchronizer(db, fileApi);
// import moment from 'moment-timezone';
// console.info(moment.tz.guess());
db.open({ name: '/home/laurent/Temp/test.sqlite3' }).then(() => {
BaseModel.db_ = db;
fileApi.mkdir('test').then(() => {
return fileApi.mkdir('test2');
}).then(() => {
return Setting.load();
return fileApi.put('test/un', 'abcd1111').then(fileApi.put('test/deux', 'abcd2222'));
}).then(() => {
let commands = [];
let currentFolder = null;
return fileApi.list();
}).then((items) => {
//console.info(items);
}).then(() => {
return fileApi.delete('test/un');
}).then(() => {
return fileApi.get('test/deux').then((content) => { console.info(content); });
}).then(() => {
return fileApi.list('test', true);
}).then((items) => {
console.info(items);
}).catch((error) => {
console.error(error);
}).then(() => {
process.exit();
});
function switchCurrentFolder(folder) {
currentFolder = folder;
updatePrompt();
}
function promptString() {
let path = '~';
if (currentFolder) {
path += '/' + currentFolder.title;
}
return 'joplin:' + path + '$ ';
}
function updatePrompt() {
vorpal.delimiter(promptString());
}
// For now, to go around this issue: https://github.com/dthree/vorpal/issues/114
function quotePromptArg(s) {
if (s.indexOf(' ') >= 0) {
return '"' + s + '"';
}
return s;
}
function autocompleteFolders() {
return Folder.all().then((folders) => {
let output = [];
for (let i = 0; i < folders.length; i++) {
output.push(quotePromptArg(folders[i].title));
}
output.push('..');
output.push('.');
return output;
});
}
function autocompleteItems() {
let promise = null;
if (!currentFolder) {
promise = Folder.all();
} else {
promise = Note.previews(currentFolder.id);
}
return promise.then((items) => {
let output = [];
for (let i = 0; i < items.length; i++) {
output.push(quotePromptArg(items[i].title));
}
return output;
});
}
// db.open({ name: '/home/laurent/Temp/test.sqlite3' }).then(() => {
// BaseModel.db_ = db;
// }).then(() => {
// return Setting.load();
// }).then(() => {
// let commands = [];
// let currentFolder = null;
process.stdin.on('keypress', (_, key) => {
if (key && key.name === 'return') {
updatePrompt();
}
// function switchCurrentFolder(folder) {
// currentFolder = folder;
// updatePrompt();
// }
if (key.name === 'tab') {
vorpal.ui.imprint();
vorpal.log(vorpal.ui.input());
}
});
// function promptString() {
// let path = '~';
// if (currentFolder) {
// path += '/' + currentFolder.title;
// }
// return 'joplin:' + path + '$ ';
// }
commands.push({
usage: 'cd <list-title>',
description: 'Moved to [list-title] - all further operations will happen within this list. Use `cd ..` to go back one level.',
action: function (args, end) {
let folderTitle = args['list-title'];
// function updatePrompt() {
// vorpal.delimiter(promptString());
// }
if (folderTitle == '..') {
switchCurrentFolder(null);
end();
return;
}
// // For now, to go around this issue: https://github.com/dthree/vorpal/issues/114
// function quotePromptArg(s) {
// if (s.indexOf(' ') >= 0) {
// return '"' + s + '"';
// }
// return s;
// }
if (folderTitle == '.') {
end();
return;
}
// function autocompleteFolders() {
// return Folder.all().then((folders) => {
// let output = [];
// for (let i = 0; i < folders.length; i++) {
// output.push(quotePromptArg(folders[i].title));
// }
// output.push('..');
// output.push('.');
// return output;
// });
// }
Folder.loadByField('title', folderTitle).then((folder) => {
switchCurrentFolder(folder);
end();
});
},
autocomplete: autocompleteFolders,
});
// function autocompleteItems() {
// let promise = null;
// if (!currentFolder) {
// promise = Folder.all();
// } else {
// promise = Note.previews(currentFolder.id);
// }
commands.push({
usage: 'mklist <list-title>',
alias: 'mkdir',
description: 'Creates a new list',
action: function (args, end) {
NoteFolderService.save('folder', { title: args['list-title'] }).catch((error) => {
this.log(error);
}).then((folder) => {
switchCurrentFolder(folder);
end();
});
},
});
// return promise.then((items) => {
// let output = [];
// for (let i = 0; i < items.length; i++) {
// output.push(quotePromptArg(items[i].title));
// }
// return output;
// });
// }
commands.push({
usage: 'mknote <note-title>',
alias: 'touch',
description: 'Creates a new note',
action: function (args, end) {
if (!currentFolder) {
this.log('Notes can only be created within a list.');
end();
return;
}
// process.stdin.on('keypress', (_, key) => {
// if (key && key.name === 'return') {
// updatePrompt();
// }
let note = {
title: args['note-title'],
parent_id: currentFolder.id,
};
NoteFolderService.save('note', note).catch((error) => {
this.log(error);
}).then((note) => {
end();
});
},
});
// if (key.name === 'tab') {
// vorpal.ui.imprint();
// vorpal.log(vorpal.ui.input());
// }
// });
commands.push({
usage: 'set <item-title> <prop-name> [prop-value]',
description: 'Sets the given <prop-name> of the given item.',
action: function (args, end) {
let promise = null;
let title = args['item-title'];
let propName = args['prop-name'];
let propValue = args['prop-value'];
if (!propValue) propValue = '';
// commands.push({
// usage: 'cd <list-title>',
// description: 'Moved to [list-title] - all further operations will happen within this list. Use `cd ..` to go back one level.',
// action: function (args, end) {
// let folderTitle = args['list-title'];
if (!currentFolder) {
promise = Folder.loadByField('title', title);
} else {
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
}
// if (folderTitle == '..') {
// switchCurrentFolder(null);
// end();
// return;
// }
promise.then((item) => {
if (!item) {
this.log(_('No item with title "%s" found.', title));
end();
return;
}
// if (folderTitle == '.') {
// end();
// return;
// }
let newItem = Object.assign({}, item);
newItem[propName] = propValue;
let itemType = currentFolder ? 'note' : 'folder';
return NoteFolderService.save(itemType, newItem, item);
}).catch((error) => {
this.log(error);
}).then(() => {
end();
});
},
autocomplete: autocompleteItems,
});
// Folder.loadByField('title', folderTitle).then((folder) => {
// switchCurrentFolder(folder);
// end();
// });
// },
// autocomplete: autocompleteFolders,
// });
commands.push({
usage: 'cat <item-title>',
description: 'Displays the given item data.',
action: function (args, end) {
let title = args['item-title'];
// commands.push({
// usage: 'mklist <list-title>',
// alias: 'mkdir',
// description: 'Creates a new list',
// action: function (args, end) {
// NoteFolderService.save('folder', { title: args['list-title'] }).catch((error) => {
// this.log(error);
// }).then((folder) => {
// switchCurrentFolder(folder);
// end();
// });
// },
// });
let promise = null;
if (!currentFolder) {
promise = Folder.loadByField('title', title);
} else {
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
}
// commands.push({
// usage: 'mknote <note-title>',
// alias: 'touch',
// description: 'Creates a new note',
// action: function (args, end) {
// if (!currentFolder) {
// this.log('Notes can only be created within a list.');
// end();
// return;
// }
promise.then((item) => {
if (!item) {
this.log(_('No item with title "%s" found.', title));
end();
return;
}
// let note = {
// title: args['note-title'],
// parent_id: currentFolder.id,
// };
// NoteFolderService.save('note', note).catch((error) => {
// this.log(error);
// }).then((note) => {
// end();
// });
// },
// });
if (!currentFolder) {
this.log(Folder.toFriendlyString(item));
} else {
this.log(Note.toFriendlyString(item));
}
}).catch((error) => {
this.log(error);
}).then(() => {
end();
});
},
autocomplete: autocompleteItems,
});
// commands.push({
// usage: 'set <item-title> <prop-name> [prop-value]',
// description: 'Sets the given <prop-name> of the given item.',
// action: function (args, end) {
// let promise = null;
// let title = args['item-title'];
// let propName = args['prop-name'];
// let propValue = args['prop-value'];
// if (!propValue) propValue = '';
commands.push({
usage: 'rm <item-title>',
description: 'Deletes the given item. For a list, all the notes within that list will be deleted.',
action: function (args, end) {
let title = args['item-title'];
// if (!currentFolder) {
// promise = Folder.loadByField('title', title);
// } else {
// promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
// }
let promise = null;
let itemType = currentFolder ? 'note' : 'folder';
if (itemType == 'folder') {
promise = Folder.loadByField('title', title);
} else {
promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
}
// promise.then((item) => {
// if (!item) {
// this.log(_('No item with title "%s" found.', title));
// end();
// return;
// }
promise.then((item) => {
if (!item) {
this.log(_('No item with title "%s" found.', title));
end();
return;
}
// let newItem = Object.assign({}, item);
// newItem[propName] = propValue;
// let itemType = currentFolder ? 'note' : 'folder';
// return NoteFolderService.save(itemType, newItem, item);
// }).catch((error) => {
// this.log(error);
// }).then(() => {
// end();
// });
// },
// autocomplete: autocompleteItems,
// });
if (itemType == 'folder') {
return Folder.delete(item.id);
} else {
return Note.delete(item.id);
}
}).catch((error) => {
this.log(error);
}).then(() => {
end();
});
},
autocomplete: autocompleteItems,
});
// commands.push({
// usage: 'cat <item-title>',
// description: 'Displays the given item data.',
// action: function (args, end) {
// let title = args['item-title'];
commands.push({
usage: 'ls [list-title]',
alias: 'll',
description: 'Lists items in [list-title].',
action: function (args, end) {
let folderTitle = args['list-title'];
// let promise = null;
// if (!currentFolder) {
// promise = Folder.loadByField('title', title);
// } else {
// promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
// }
let promise = null;
// promise.then((item) => {
// if (!item) {
// this.log(_('No item with title "%s" found.', title));
// end();
// return;
// }
if (folderTitle == '..') {
promise = Promise.resolve('root');
} else if (folderTitle && folderTitle != '.') {
promise = Folder.loadByField('title', folderTitle);
} else if (currentFolder) {
promise = Promise.resolve(currentFolder);
} else {
promise = Promise.resolve('root');
}
// if (!currentFolder) {
// this.log(Folder.toFriendlyString(item));
// } else {
// this.log(Note.toFriendlyString(item));
// }
// }).catch((error) => {
// this.log(error);
// }).then(() => {
// end();
// });
// },
// autocomplete: autocompleteItems,
// });
promise.then((folder) => {
let p = null
let postfix = '';
if (folder === 'root') {
p = Folder.all();
postfix = '/';
} else if (!folder) {
throw new Error(_('Unknown list: "%s"', folderTitle));
} else {
p = Note.previews(folder.id);
}
// commands.push({
// usage: 'rm <item-title>',
// description: 'Deletes the given item. For a list, all the notes within that list will be deleted.',
// action: function (args, end) {
// let title = args['item-title'];
return p.then((previews) => {
for (let i = 0; i < previews.length; i++) {
this.log(previews[i].title + postfix);
}
});
}).catch((error) => {
this.log(error);
}).then(() => {
end();
});
},
autocomplete: autocompleteFolders,
});
// let promise = null;
// let itemType = currentFolder ? 'note' : 'folder';
// if (itemType == 'folder') {
// promise = Folder.loadByField('title', title);
// } else {
// promise = Folder.loadNoteByField(currentFolder.id, 'title', title);
// }
commands.push({
usage: 'sync',
description: 'Synchronizes with remote storage.',
action: function (args, end) {
synchronizer.start().catch((error) => {
console.error(error);
}).then(() => {
end();
});
},
});
// promise.then((item) => {
// if (!item) {
// this.log(_('No item with title "%s" found.', title));
// end();
// return;
// }
for (let i = 0; i < commands.length; i++) {
let c = commands[i];
let o = vorpal.command(c.usage, c.description);
if (c.alias) {
o.alias(c.alias);
}
if (c.autocomplete) {
o.autocomplete({
data: c.autocomplete,
});
}
o.action(c.action);
}
// if (itemType == 'folder') {
// return Folder.delete(item.id);
// } else {
// return Note.delete(item.id);
// }
// }).catch((error) => {
// this.log(error);
// }).then(() => {
// end();
// });
// },
// autocomplete: autocompleteItems,
// });
vorpal.delimiter(promptString()).show();
});
// commands.push({
// usage: 'ls [list-title]',
// alias: 'll',
// description: 'Lists items in [list-title].',
// action: function (args, end) {
// let folderTitle = args['list-title'];
// let promise = null;
// if (folderTitle == '..') {
// promise = Promise.resolve('root');
// } else if (folderTitle && folderTitle != '.') {
// promise = Folder.loadByField('title', folderTitle);
// } else if (currentFolder) {
// promise = Promise.resolve(currentFolder);
// } else {
// promise = Promise.resolve('root');
// }
// promise.then((folder) => {
// let p = null
// let postfix = '';
// if (folder === 'root') {
// p = Folder.all();
// postfix = '/';
// } else if (!folder) {
// throw new Error(_('Unknown list: "%s"', folderTitle));
// } else {
// p = Note.previews(folder.id);
// }
// return p.then((previews) => {
// for (let i = 0; i < previews.length; i++) {
// this.log(previews[i].title + postfix);
// }
// });
// }).catch((error) => {
// this.log(error);
// }).then(() => {
// end();
// });
// },
// autocomplete: autocompleteFolders,
// });
// commands.push({
// usage: 'sync',
// description: 'Synchronizes with remote storage.',
// action: function (args, end) {
// synchronizer.start().catch((error) => {
// console.error(error);
// }).then(() => {
// end();
// });
// },
// });
// for (let i = 0; i < commands.length; i++) {
// let c = commands[i];
// let o = vorpal.command(c.usage, c.description);
// if (c.alias) {
// o.alias(c.alias);
// }
// if (c.autocomplete) {
// o.autocomplete({
// data: c.autocomplete,
// });
// }
// o.action(c.action);
// }
// vorpal.delimiter(promptString()).show();
// });

View File

@ -36,7 +36,7 @@ class FileApiDriverLocal {
};
}
setFileTimestamp(path, timestamp) {
setTimestamp(path, timestamp) {
return new Promise((resolve, reject) => {
fs.utimes(path, timestamp, timestamp, (error) => {
if (error) {

View File

@ -0,0 +1,109 @@
class FileApiDriverMemory {
constructor(baseDir) {
this.items_ = [];
}
currentTimestamp() {
return Math.round((new Date()).getTime() / 1000);
}
itemIndexByPath(path) {
for (let i = 0; i < this.items_.length; i++) {
if (this.items_[i].path == path) return i;
}
return -1;
}
itemByPath(path) {
let index = this.itemIndexByPath(path);
return index < 0 ? null : this.items_[index];
}
newItem(path, isDir = false) {
return {
path: path,
isDir: isDir,
updatedTime: this.currentTimestamp(),
createdTime: this.currentTimestamp(),
content: '',
};
}
stat(path) {
let item = this.itemIndexByPath(path);
if (!item) return Promise.reject(new Error('File not found: ' + path));
return Promise.resolve(item);
}
setTimestamp(path, timestamp) {
let item = this.itemIndexByPath(path);
if (!item) return Promise.reject(new Error('File not found: ' + path));
item.updatedTime = timestamp;
return Promise.resolve();
}
list(path) {
let output = [];
for (let i = 0; i < this.items_.length; i++) {
let item = this.items_[i];
if (item.path == path) continue;
if (item.path.indexOf(path + '/') === 0) {
let s = item.path.substr(path.length + 1);
if (s.split('/').length === 1) {
let it = Object.assign({}, item);
it.path = it.path.substr(path.length + 1);
output.push(it);
}
}
}
return Promise.resolve(output);
}
get(path) {
let item = this.itemByPath(path);
if (!item) return Promise.reject(new Error('File not found: ' + path));
if (item.isDir) return Promise.reject(new Error(path + ' is a directory, not a file'));
return Promise.resolve(item.content);
}
mkdir(path) {
let index = this.itemIndexByPath(path);
if (index >= 0) return Promise.resolve();
this.items_.push(this.newItem(path, true));
return Promise.resolve();
}
put(path, content) {
let index = this.itemIndexByPath(path);
if (index < 0) {
let item = this.newItem(path, false);
item.content = content;
this.items_.push(item);
} else {
this.items_[index].content = content;
}
return Promise.resolve();
}
delete(path) {
let index = this.itemIndexByPath(path);
if (index >= 0) {
this.items_.splice(index, 1);
}
return Promise.resolve();
}
move(oldPath, newPath) {
let sourceItem = this.itemByPath(oldPath);
if (!sourceItem) return Promise.reject(new Error('Path not found: ' + oldPath));
this.delete(newPath); // Overwrite if newPath already exists
sourceItem.path = newPath;
return Promise.resolve();
}
}
export { FileApiDriverMemory };

View File

@ -7,8 +7,15 @@ class FileApi {
this.driver_ = driver;
}
list(path, recursive = false) {
return this.driver_.list(this.baseDir_ + '/' + path, recursive).then((items) => {
fullPath_(path) {
let output = this.baseDir_;
if (path != '') output += '/' + path;
return output;
}
list(path = '', recursive = false) {
let fullPath = this.fullPath_(path);
return this.driver_.list(fullPath, recursive).then((items) => {
if (recursive) {
let chain = [];
for (let i = 0; i < items.length; i++) {
@ -16,10 +23,10 @@ class FileApi {
if (!item.isDir) continue;
chain.push(() => {
return this.list(path + '/' + item.name, true).then((children) => {
return this.list(item.path, true).then((children) => {
for (let j = 0; j < children.length; j++) {
let md = children[j];
md.name = item.name + '/' + md.name;
md.path = item.path + '/' + md.path;
items.push(md);
}
});
@ -35,28 +42,28 @@ class FileApi {
});
}
setFileTimestamp(path, timestamp) {
return this.driver_.setFileTimestamp(this.baseDir_ + '/' + path, timestamp);
setTimestamp(path, timestamp) {
return this.driver_.setTimestamp(this.fullPath_(path), timestamp);
}
mkdir(path) {
return this.driver_.mkdir(this.baseDir_ + '/' + path);
return this.driver_.mkdir(this.fullPath_(path));
}
get(path) {
return this.driver_.get(this.baseDir_ + '/' + path);
return this.driver_.get(this.fullPath_(path));
}
put(path, content) {
return this.driver_.put(this.baseDir_ + '/' + path, content);
return this.driver_.put(this.fullPath_(path), content);
}
delete(path) {
return this.driver_.delete(this.baseDir_ + '/' + path);
return this.driver_.delete(this.fullPath_(path));
}
move(oldPath, newPath) {
return this.driver_.move(this.baseDir_ + '/' + oldPath, this.baseDir_ + '/' + newPath);
return this.driver_.move(this.fullPath_(oldPath), this.fullPath_(newPath));
}
}

View File

@ -122,11 +122,11 @@ class Synchronizer {
return this.api().mkdir(path).then(() => {
return this.api().put(Folder.systemMetadataPath(parent, item), Folder.toFriendlyString(item));
}).then(() => {
return this.api().setFileTimestamp(Folder.systemMetadataPath(parent, item), item.updated_time);
return this.api().setTimestamp(Folder.systemMetadataPath(parent, item), item.updated_time);
});
} else {
return this.api().put(path, Note.toFriendlyString(item)).then(() => {
return this.api().setFileTimestamp(path, item.updated_time);
return this.api().setTimestamp(path, item.updated_time);
});
}
});