2020-11-07 17:59:37 +02:00
|
|
|
import { PaginationOrderDir } from '@joplin/lib/models/utils/types';
|
|
|
|
import Api, { RequestMethod } from '@joplin/lib/services/rest/Api';
|
|
|
|
import shim from '@joplin/lib/shim';
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('./test-utils.js');
|
2020-11-07 17:59:37 +02:00
|
|
|
const Folder = require('@joplin/lib/models/Folder');
|
|
|
|
const Resource = require('@joplin/lib/models/Resource');
|
|
|
|
const Note = require('@joplin/lib/models/Note');
|
|
|
|
const Tag = require('@joplin/lib/models/Tag');
|
|
|
|
const NoteTag = require('@joplin/lib/models/NoteTag');
|
2020-11-08 18:46:48 +02:00
|
|
|
const ResourceService = require('@joplin/lib/services/ResourceService').default;
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
async function msleep(ms:number) {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
shim.setTimeout(() => {
|
|
|
|
resolve();
|
|
|
|
}, ms);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const createFolderForPagination = async (num:number, time:number) => {
|
|
|
|
await Folder.save({
|
|
|
|
title: `folder${num}`,
|
|
|
|
updated_time: time,
|
|
|
|
created_time: time,
|
|
|
|
}, { autoTimestamp: false });
|
|
|
|
};
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2020-11-09 19:17:51 +02:00
|
|
|
const createNoteForPagination = async (num:number, time:number) => {
|
|
|
|
await Note.save({
|
|
|
|
title: `note${num}`,
|
|
|
|
body: `noteBody${num}`,
|
|
|
|
updated_time: time,
|
|
|
|
created_time: time,
|
|
|
|
}, { autoTimestamp: false });
|
|
|
|
};
|
|
|
|
|
2020-10-10 15:09:54 +02:00
|
|
|
let api:Api = null;
|
2018-09-27 10:14:05 +02:00
|
|
|
|
|
|
|
describe('services_rest_Api', function() {
|
|
|
|
|
|
|
|
beforeEach(async (done) => {
|
|
|
|
api = new Api();
|
|
|
|
await setupDatabaseAndSynchronizer(1);
|
|
|
|
await switchClient(1);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should ping', asyncTest(async () => {
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.GET, 'ping');
|
2020-10-10 15:09:54 +02:00
|
|
|
expect(response).toBe('JoplinClipperServer');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should handle Not Found errors', asyncTest(async () => {
|
2020-11-05 18:58:23 +02:00
|
|
|
const hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'pong'));
|
2020-10-10 15:09:54 +02:00
|
|
|
expect(hasThrown).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should get folders', asyncTest(async () => {
|
2020-10-10 15:09:54 +02:00
|
|
|
await Folder.save({ title: 'mon carnet' });
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.GET, 'folders');
|
|
|
|
expect(response.items.length).toBe(1);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should update folders', asyncTest(async () => {
|
2020-03-14 01:46:14 +02:00
|
|
|
const f1 = await Folder.save({ title: 'mon carnet' });
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.PUT, `folders/${f1.id}`, null, JSON.stringify({
|
2018-09-28 20:24:57 +02:00
|
|
|
title: 'modifié',
|
|
|
|
}));
|
|
|
|
|
2020-03-14 01:46:14 +02:00
|
|
|
const f1b = await Folder.load(f1.id);
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(f1b.title).toBe('modifié');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should delete folders', asyncTest(async () => {
|
2020-03-14 01:46:14 +02:00
|
|
|
const f1 = await Folder.save({ title: 'mon carnet' });
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.DELETE, `folders/${f1.id}`);
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2020-03-14 01:46:14 +02:00
|
|
|
const f1b = await Folder.load(f1.id);
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(!f1b).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create folders', asyncTest(async () => {
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.POST, 'folders', null, JSON.stringify({
|
2018-09-28 20:24:57 +02:00
|
|
|
title: 'from api',
|
|
|
|
}));
|
|
|
|
|
|
|
|
expect(!!response.id).toBe(true);
|
|
|
|
|
2020-03-14 01:46:14 +02:00
|
|
|
const f = await Folder.all();
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(f.length).toBe(1);
|
|
|
|
expect(f[0].title).toBe('from api');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should get one folder', asyncTest(async () => {
|
2020-03-14 01:46:14 +02:00
|
|
|
const f1 = await Folder.save({ title: 'mon carnet' });
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.GET, `folders/${f1.id}`);
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(response.id).toBe(f1.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'folders/doesntexist'));
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(hasThrown).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should get the folder notes', asyncTest(async () => {
|
2020-03-14 01:46:14 +02:00
|
|
|
const f1 = await Folder.save({ title: 'mon carnet' });
|
2020-11-05 18:58:23 +02:00
|
|
|
const response2 = await api.route(RequestMethod.GET, `folders/${f1.id}/notes`);
|
|
|
|
expect(response2.items.length).toBe(0);
|
2018-09-29 13:54:44 +02:00
|
|
|
|
2020-10-10 15:09:54 +02:00
|
|
|
await Note.save({ title: 'un', parent_id: f1.id });
|
|
|
|
await Note.save({ title: 'deux', parent_id: f1.id });
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.GET, `folders/${f1.id}/notes`);
|
|
|
|
expect(response.items.length).toBe(2);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-29 13:54:44 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should fail on invalid paths', asyncTest(async () => {
|
2020-11-05 18:58:23 +02:00
|
|
|
const hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'schtroumpf'));
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(hasThrown).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should get notes', asyncTest(async () => {
|
2018-09-27 20:35:10 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f1 = await Folder.save({ title: 'mon carnet' });
|
|
|
|
const f2 = await Folder.save({ title: 'mon deuxième carnet' });
|
2018-09-27 20:35:10 +02:00
|
|
|
const n1 = await Note.save({ title: 'un', parent_id: f1.id });
|
2020-10-10 15:09:54 +02:00
|
|
|
await Note.save({ title: 'deux', parent_id: f1.id });
|
2018-09-27 20:35:10 +02:00
|
|
|
const n3 = await Note.save({ title: 'trois', parent_id: f2.id });
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.GET, 'notes');
|
|
|
|
expect(response.items.length).toBe(3);
|
2018-09-27 20:35:10 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.GET, `notes/${n1.id}`);
|
2018-09-27 20:35:10 +02:00
|
|
|
expect(response.id).toBe(n1.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.GET, `notes/${n3.id}`, { fields: 'id,title' });
|
2018-09-27 20:35:10 +02:00
|
|
|
expect(Object.getOwnPropertyNames(response).length).toBe(3);
|
|
|
|
expect(response.id).toBe(n3.id);
|
|
|
|
expect(response.title).toBe('trois');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 20:35:10 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create notes', asyncTest(async () => {
|
2018-09-27 10:14:05 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2018-09-27 10:14:05 +02:00
|
|
|
title: 'testing',
|
|
|
|
parent_id: f.id,
|
|
|
|
}));
|
|
|
|
expect(response.title).toBe('testing');
|
|
|
|
expect(!!response.id).toBe(true);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2018-09-27 10:14:05 +02:00
|
|
|
title: 'testing',
|
|
|
|
parent_id: f.id,
|
|
|
|
}));
|
|
|
|
expect(response.title).toBe('testing');
|
|
|
|
expect(!!response.id).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2020-10-10 15:09:54 +02:00
|
|
|
it('should allow setting note properties', asyncTest(async () => {
|
|
|
|
let response:any = null;
|
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2020-10-10 15:09:54 +02:00
|
|
|
title: 'testing',
|
|
|
|
parent_id: f.id,
|
|
|
|
latitude: '48.732071',
|
|
|
|
longitude: '-3.458700',
|
|
|
|
altitude: '21',
|
|
|
|
}));
|
|
|
|
|
|
|
|
const noteId = response.id;
|
2020-10-16 17:26:19 +02:00
|
|
|
|
2020-10-10 15:09:54 +02:00
|
|
|
{
|
|
|
|
const note = await Note.load(noteId);
|
|
|
|
expect(note.latitude).toBe('48.73207100');
|
|
|
|
expect(note.longitude).toBe('-3.45870000');
|
|
|
|
expect(note.altitude).toBe('21.0000');
|
|
|
|
}
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.PUT, `notes/${noteId}`, null, JSON.stringify({
|
2020-10-10 15:09:54 +02:00
|
|
|
latitude: '49',
|
|
|
|
longitude: '-3',
|
|
|
|
altitude: '22',
|
|
|
|
}));
|
|
|
|
|
|
|
|
{
|
|
|
|
const note = await Note.load(noteId);
|
|
|
|
expect(note.latitude).toBe('49.00000000');
|
|
|
|
expect(note.longitude).toBe('-3.00000000');
|
|
|
|
expect(note.altitude).toBe('22.0000');
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should preserve user timestamps when creating notes', asyncTest(async () => {
|
2019-06-22 13:31:04 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
2019-06-22 13:31:04 +02:00
|
|
|
|
|
|
|
const updatedTime = Date.now() - 1000;
|
|
|
|
const createdTime = Date.now() - 10000;
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2019-06-22 13:31:04 +02:00
|
|
|
parent_id: f.id,
|
|
|
|
user_updated_time: updatedTime,
|
|
|
|
user_created_time: createdTime,
|
|
|
|
}));
|
|
|
|
|
|
|
|
expect(response.user_updated_time).toBe(updatedTime);
|
|
|
|
expect(response.user_created_time).toBe(createdTime);
|
2020-10-21 19:12:36 +02:00
|
|
|
|
|
|
|
const timeBefore = Date.now();
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2020-10-21 19:12:36 +02:00
|
|
|
parent_id: f.id,
|
|
|
|
}));
|
|
|
|
|
|
|
|
const newNote = await Note.load(response.id);
|
|
|
|
expect(newNote.user_updated_time).toBeGreaterThanOrEqual(timeBefore);
|
|
|
|
expect(newNote.user_created_time).toBeGreaterThanOrEqual(timeBefore);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should preserve user timestamps when updating notes', asyncTest(async () => {
|
|
|
|
const folder = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
|
|
|
const updatedTime = Date.now() - 1000;
|
|
|
|
const createdTime = Date.now() - 10000;
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2020-10-21 19:12:36 +02:00
|
|
|
parent_id: folder.id,
|
|
|
|
}));
|
|
|
|
|
|
|
|
const noteId = response.id;
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check that if user timestamps are supplied, they are preserved by the API
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.PUT, `notes/${noteId}`, null, JSON.stringify({
|
2020-10-21 19:12:36 +02:00
|
|
|
user_updated_time: updatedTime,
|
|
|
|
user_created_time: createdTime,
|
|
|
|
title: 'mod',
|
|
|
|
}));
|
|
|
|
|
|
|
|
const modNote = await Note.load(noteId);
|
|
|
|
expect(modNote.title).toBe('mod');
|
|
|
|
expect(modNote.user_updated_time).toBe(updatedTime);
|
|
|
|
expect(modNote.user_created_time).toBe(createdTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check if no user timestamps are supplied they are automatically updated.
|
|
|
|
|
|
|
|
const beforeTime = Date.now();
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.PUT, `notes/${noteId}`, null, JSON.stringify({
|
2020-10-21 19:12:36 +02:00
|
|
|
title: 'mod2',
|
|
|
|
}));
|
|
|
|
|
|
|
|
const modNote = await Note.load(noteId);
|
|
|
|
expect(modNote.title).toBe('mod2');
|
|
|
|
expect(modNote.user_updated_time).toBeGreaterThanOrEqual(beforeTime);
|
|
|
|
expect(modNote.user_created_time).toBeGreaterThanOrEqual(createdTime);
|
|
|
|
}
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2019-06-22 13:31:04 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create notes with supplied ID', asyncTest(async () => {
|
2018-11-08 03:14:13 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2018-11-08 03:14:13 +02:00
|
|
|
id: '12345678123456781234567812345678',
|
|
|
|
title: 'testing',
|
|
|
|
parent_id: f.id,
|
|
|
|
}));
|
|
|
|
expect(response.id).toBe('12345678123456781234567812345678');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-11-08 03:14:13 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create todos', asyncTest(async () => {
|
2019-06-28 14:46:55 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'stuff to do' });
|
2019-06-28 14:46:55 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2019-06-28 14:46:55 +02:00
|
|
|
title: 'testing',
|
|
|
|
parent_id: f.id,
|
2019-07-30 09:35:42 +02:00
|
|
|
is_todo: 1,
|
2019-06-28 14:46:55 +02:00
|
|
|
}));
|
|
|
|
expect(response.is_todo).toBe(1);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2019-06-28 14:46:55 +02:00
|
|
|
title: 'testing 2',
|
|
|
|
parent_id: f.id,
|
2019-07-30 09:35:42 +02:00
|
|
|
is_todo: 0,
|
2019-06-28 14:46:55 +02:00
|
|
|
}));
|
|
|
|
expect(response.is_todo).toBe(0);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2019-06-28 14:46:55 +02:00
|
|
|
title: 'testing 3',
|
|
|
|
parent_id: f.id,
|
|
|
|
}));
|
|
|
|
expect(response.is_todo).toBeUndefined();
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2019-06-28 14:46:55 +02:00
|
|
|
title: 'testing 4',
|
|
|
|
parent_id: f.id,
|
2019-07-30 09:35:42 +02:00
|
|
|
is_todo: '1',
|
2019-06-28 14:46:55 +02:00
|
|
|
}));
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2019-06-28 14:46:55 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create folders with supplied ID', asyncTest(async () => {
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.POST, 'folders', null, JSON.stringify({
|
2019-02-07 00:36:39 +02:00
|
|
|
id: '12345678123456781234567812345678',
|
|
|
|
title: 'from api',
|
|
|
|
}));
|
|
|
|
|
|
|
|
expect(response.id).toBe('12345678123456781234567812345678');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create notes with images', asyncTest(async () => {
|
2018-09-27 10:14:05 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2018-09-27 10:14:05 +02:00
|
|
|
title: 'testing image',
|
|
|
|
parent_id: f.id,
|
2019-07-30 09:35:42 +02:00
|
|
|
image_data_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII=',
|
2018-09-27 10:14:05 +02:00
|
|
|
}));
|
|
|
|
|
|
|
|
const resources = await Resource.all();
|
|
|
|
expect(resources.length).toBe(1);
|
|
|
|
|
|
|
|
const resource = resources[0];
|
|
|
|
expect(response.body.indexOf(resource.id) >= 0).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should delete resources', asyncTest(async () => {
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2019-07-13 16:57:53 +02:00
|
|
|
title: 'testing image',
|
|
|
|
parent_id: f.id,
|
2019-07-30 09:35:42 +02:00
|
|
|
image_data_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII=',
|
2019-07-13 16:57:53 +02:00
|
|
|
}));
|
|
|
|
|
|
|
|
const resource = (await Resource.all())[0];
|
|
|
|
|
|
|
|
const filePath = Resource.fullPath(resource);
|
|
|
|
expect(await shim.fsDriver().exists(filePath)).toBe(true);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.DELETE, `resources/${resource.id}`);
|
2019-07-13 16:57:53 +02:00
|
|
|
expect(await shim.fsDriver().exists(filePath)).toBe(false);
|
|
|
|
expect(!(await Resource.load(resource.id))).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2019-07-30 09:35:42 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should create notes from HTML', asyncTest(async () => {
|
2018-09-27 10:14:05 +02:00
|
|
|
let response = null;
|
2019-07-30 09:35:42 +02:00
|
|
|
const f = await Folder.save({ title: 'mon carnet' });
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
response = await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({
|
2018-09-27 10:14:05 +02:00
|
|
|
title: 'testing HTML',
|
|
|
|
parent_id: f.id,
|
|
|
|
body_html: '<b>Bold text</b>',
|
|
|
|
}));
|
|
|
|
|
|
|
|
expect(response.body).toBe('**Bold text**');
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
// it('should filter fields', asyncTest(async () => {
|
|
|
|
// let f = api.fields_({ query: { fields: 'one,two' } } as any, []);
|
|
|
|
// expect(f.length).toBe(2);
|
|
|
|
// expect(f[0]).toBe('one');
|
|
|
|
// expect(f[1]).toBe('two');
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
// f = api.fields_({ query: { fields: 'one ,, two ' } } as any, []);
|
|
|
|
// expect(f.length).toBe(2);
|
|
|
|
// expect(f[0]).toBe('one');
|
|
|
|
// expect(f[1]).toBe('two');
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
// f = api.fields_({ query: { fields: ' ' } } as any, ['def']);
|
|
|
|
// expect(f.length).toBe(1);
|
|
|
|
// expect(f[0]).toBe('def');
|
|
|
|
// }));
|
2018-09-27 10:14:05 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should handle tokens', asyncTest(async () => {
|
2018-09-27 20:35:10 +02:00
|
|
|
api = new Api('mytoken');
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
let hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.GET, 'notes'));
|
2018-09-27 20:35:10 +02:00
|
|
|
expect(hasThrown).toBe(true);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.GET, 'notes', { token: 'mytoken' });
|
|
|
|
expect(response.items.length).toBe(0);
|
2018-09-27 20:35:10 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
hasThrown = await checkThrowAsync(async () => await api.route(RequestMethod.POST, 'notes', null, JSON.stringify({ title: 'testing' })));
|
2018-09-30 11:15:46 +02:00
|
|
|
expect(hasThrown).toBe(true);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-30 11:15:46 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should add tags to notes', asyncTest(async () => {
|
2019-07-30 09:35:42 +02:00
|
|
|
const tag = await Tag.save({ title: 'mon étiquette' });
|
|
|
|
const note = await Note.save({ title: 'ma note' });
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.POST, `tags/${tag.id}/notes`, null, JSON.stringify({
|
2018-09-28 20:24:57 +02:00
|
|
|
id: note.id,
|
|
|
|
}));
|
|
|
|
|
2019-07-30 09:35:42 +02:00
|
|
|
const noteIds = await Tag.noteIds(tag.id);
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(noteIds[0]).toBe(note.id);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should remove tags from notes', asyncTest(async () => {
|
2019-07-30 09:35:42 +02:00
|
|
|
const tag = await Tag.save({ title: 'mon étiquette' });
|
|
|
|
const note = await Note.save({ title: 'ma note' });
|
2018-09-28 20:24:57 +02:00
|
|
|
await Tag.addNote(tag.id, note.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
await api.route(RequestMethod.DELETE, `tags/${tag.id}/notes/${note.id}`);
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2019-07-30 09:35:42 +02:00
|
|
|
const noteIds = await Tag.noteIds(tag.id);
|
2018-09-28 20:24:57 +02:00
|
|
|
expect(noteIds.length).toBe(0);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2019-09-24 00:23:10 +02:00
|
|
|
it('should list all tag notes', asyncTest(async () => {
|
2019-07-30 09:35:42 +02:00
|
|
|
const tag = await Tag.save({ title: 'mon étiquette' });
|
|
|
|
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
|
|
|
|
const note1 = await Note.save({ title: 'ma note un' });
|
|
|
|
const note2 = await Note.save({ title: 'ma note deux' });
|
2018-09-28 20:24:57 +02:00
|
|
|
await Tag.addNote(tag.id, note1.id);
|
|
|
|
await Tag.addNote(tag.id, note2.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.GET, `tags/${tag.id}/notes`);
|
|
|
|
expect(response.items.length).toBe(2);
|
|
|
|
expect('id' in response.items[0]).toBe(true);
|
|
|
|
expect('title' in response.items[0]).toBe(true);
|
2018-09-29 13:54:44 +02:00
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response2 = await api.route(RequestMethod.GET, `notes/${note1.id}/tags`);
|
|
|
|
expect(response2.items.length).toBe(1);
|
2018-09-29 13:54:44 +02:00
|
|
|
await Tag.addNote(tag2.id, note1.id);
|
2020-11-05 18:58:23 +02:00
|
|
|
const response3 = await api.route(RequestMethod.GET, `notes/${note1.id}/tags`);
|
|
|
|
expect(response3.items.length).toBe(2);
|
2019-09-24 00:23:10 +02:00
|
|
|
}));
|
2018-09-28 20:24:57 +02:00
|
|
|
|
2020-03-13 20:44:47 +02:00
|
|
|
it('should update tags when updating notes', asyncTest(async () => {
|
|
|
|
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
|
|
|
|
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
|
|
|
|
const tag3 = await Tag.save({ title: 'mon étiquette 3' });
|
|
|
|
|
|
|
|
const note = await Note.save({
|
|
|
|
title: 'ma note un',
|
|
|
|
});
|
|
|
|
Tag.addNote(tag1.id, note.id);
|
|
|
|
Tag.addNote(tag2.id, note.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
2020-03-13 20:44:47 +02:00
|
|
|
tags: `${tag1.title},${tag3.title}`,
|
|
|
|
}));
|
|
|
|
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
|
|
|
expect(response.tags === `${tag1.title},${tag3.title}`).toBe(true);
|
|
|
|
expect(tagIds.length === 2).toBe(true);
|
|
|
|
expect(tagIds.includes(tag1.id)).toBe(true);
|
|
|
|
expect(tagIds.includes(tag3.id)).toBe(true);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should create and update tags when updating notes', asyncTest(async () => {
|
|
|
|
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
|
|
|
|
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
|
|
|
|
const newTagTitle = 'mon étiquette 3';
|
|
|
|
|
|
|
|
const note = await Note.save({
|
|
|
|
title: 'ma note un',
|
|
|
|
});
|
|
|
|
Tag.addNote(tag1.id, note.id);
|
|
|
|
Tag.addNote(tag2.id, note.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
2020-03-13 20:44:47 +02:00
|
|
|
tags: `${tag1.title},${newTagTitle}`,
|
|
|
|
}));
|
|
|
|
const newTag = await Tag.loadByTitle(newTagTitle);
|
|
|
|
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
|
|
|
expect(response.tags === `${tag1.title},${newTag.title}`).toBe(true);
|
|
|
|
expect(tagIds.length === 2).toBe(true);
|
|
|
|
expect(tagIds.includes(tag1.id)).toBe(true);
|
|
|
|
expect(tagIds.includes(newTag.id)).toBe(true);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should not update tags if tags is not mentioned when updating', asyncTest(async () => {
|
|
|
|
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
|
|
|
|
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
|
|
|
|
|
|
|
|
const note = await Note.save({
|
|
|
|
title: 'ma note un',
|
|
|
|
});
|
|
|
|
Tag.addNote(tag1.id, note.id);
|
|
|
|
Tag.addNote(tag2.id, note.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
2020-03-13 20:44:47 +02:00
|
|
|
title: 'Some other title',
|
|
|
|
}));
|
|
|
|
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
|
|
|
expect(response.tags === undefined).toBe(true);
|
|
|
|
expect(tagIds.length === 2).toBe(true);
|
|
|
|
expect(tagIds.includes(tag1.id)).toBe(true);
|
|
|
|
expect(tagIds.includes(tag2.id)).toBe(true);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should remove tags from note if tags is set to empty string when updating', asyncTest(async () => {
|
|
|
|
const tag1 = await Tag.save({ title: 'mon étiquette 1' });
|
|
|
|
const tag2 = await Tag.save({ title: 'mon étiquette 2' });
|
|
|
|
|
|
|
|
const note = await Note.save({
|
|
|
|
title: 'ma note un',
|
|
|
|
});
|
|
|
|
Tag.addNote(tag1.id, note.id);
|
|
|
|
Tag.addNote(tag2.id, note.id);
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
const response = await api.route(RequestMethod.PUT, `notes/${note.id}`, null, JSON.stringify({
|
2020-03-13 20:44:47 +02:00
|
|
|
tags: '',
|
|
|
|
}));
|
|
|
|
const tagIds = await NoteTag.tagIdsByNoteId(note.id);
|
|
|
|
expect(response.tags === '').toBe(true);
|
|
|
|
expect(tagIds.length === 0).toBe(true);
|
|
|
|
}));
|
2020-11-05 18:58:23 +02:00
|
|
|
|
|
|
|
it('should paginate results', asyncTest(async () => {
|
|
|
|
await createFolderForPagination(1, 1001);
|
|
|
|
await createFolderForPagination(2, 1002);
|
|
|
|
await createFolderForPagination(3, 1003);
|
|
|
|
await createFolderForPagination(4, 1004);
|
|
|
|
|
|
|
|
{
|
|
|
|
const r1 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
fields: ['id', 'title', 'updated_time'],
|
|
|
|
limit: 2,
|
|
|
|
order_dir: PaginationOrderDir.ASC,
|
|
|
|
order_by: 'updated_time',
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r1.items.length).toBe(2);
|
|
|
|
expect(r1.items[0].title).toBe('folder1');
|
|
|
|
expect(r1.items[1].title).toBe('folder2');
|
|
|
|
|
|
|
|
const r2 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
cursor: r1.cursor,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r2.items.length).toBe(2);
|
|
|
|
expect(r2.items[0].title).toBe('folder3');
|
|
|
|
expect(r2.items[1].title).toBe('folder4');
|
|
|
|
|
|
|
|
const r3 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
cursor: r2.cursor,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r3.items.length).toBe(0);
|
|
|
|
expect(r3.cursor).toBe(undefined);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const r1 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
fields: ['id', 'title', 'updated_time'],
|
|
|
|
limit: 3,
|
|
|
|
order_dir: PaginationOrderDir.ASC,
|
|
|
|
order_by: 'updated_time',
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r1.items.length).toBe(3);
|
|
|
|
expect(r1.items[0].title).toBe('folder1');
|
|
|
|
expect(r1.items[1].title).toBe('folder2');
|
|
|
|
expect(r1.items[2].title).toBe('folder3');
|
|
|
|
|
|
|
|
const r2 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
cursor: r1.cursor,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r2.items.length).toBe(1);
|
|
|
|
expect(r2.items[0].title).toBe('folder4');
|
|
|
|
expect(r2.cursor).toBe(undefined);
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should paginate results and handle duplicate cursor field value', asyncTest(async () => {
|
|
|
|
await createFolderForPagination(1, 1001);
|
|
|
|
await createFolderForPagination(2, 1002);
|
|
|
|
await createFolderForPagination(3, 1002);
|
|
|
|
await createFolderForPagination(4, 1003);
|
|
|
|
|
|
|
|
const r1 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
fields: ['id', 'title', 'updated_time'],
|
|
|
|
limit: 2,
|
|
|
|
order_dir: PaginationOrderDir.ASC,
|
|
|
|
order_by: 'updated_time',
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r1.items.length).toBe(2);
|
|
|
|
expect(r1.items[0].title).toBe('folder1');
|
|
|
|
expect(['folder2', 'folder3'].includes(r1.items[1].title)).toBe(true);
|
|
|
|
|
|
|
|
const r2 = await api.route(RequestMethod.GET, 'folders', {
|
|
|
|
cursor: r1.cursor,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r2.items.length).toBe(2);
|
|
|
|
expect(r2.items[0].title).toBe(r1.items[1].title === 'folder2' ? 'folder3' : 'folder2');
|
|
|
|
expect(r2.items[1].title).toBe('folder4');
|
|
|
|
}));
|
|
|
|
|
2020-11-09 19:17:51 +02:00
|
|
|
it('should paginate results and keep the same fields for cursor queries', asyncTest(async () => {
|
|
|
|
await createNoteForPagination(1, 1001);
|
|
|
|
await createNoteForPagination(2, 1002);
|
|
|
|
await createNoteForPagination(3, 1003);
|
|
|
|
|
|
|
|
const r1 = await api.route(RequestMethod.GET, 'notes', {
|
|
|
|
fields: ['id', 'title', 'body'],
|
|
|
|
limit: 2,
|
|
|
|
order_dir: PaginationOrderDir.ASC,
|
|
|
|
order_by: 'updated_time',
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r1.items.length).toBe(2);
|
|
|
|
expect(r1.items[0].title).toBe('note1');
|
|
|
|
expect(r1.items[0].body).toBe('noteBody1');
|
|
|
|
expect(r1.items[1].title).toBe('note2');
|
|
|
|
expect(r1.items[1].body).toBe('noteBody2');
|
|
|
|
|
|
|
|
const r2 = await api.route(RequestMethod.GET, 'notes', {
|
|
|
|
cursor: r1.cursor,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r2.items.length).toBe(1);
|
|
|
|
expect(r2.items[0].title).toBe('note3');
|
|
|
|
expect(r2.items[0].body).toBe('noteBody3');
|
|
|
|
}));
|
|
|
|
|
2020-11-05 18:58:23 +02:00
|
|
|
it('should paginate folder notes', asyncTest(async () => {
|
|
|
|
const folder = await Folder.save({});
|
|
|
|
const note1 = await Note.save({ parent_id: folder.id });
|
|
|
|
await msleep(1);
|
|
|
|
const note2 = await Note.save({ parent_id: folder.id });
|
|
|
|
await msleep(1);
|
|
|
|
const note3 = await Note.save({ parent_id: folder.id });
|
|
|
|
|
|
|
|
const r1 = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`, {
|
|
|
|
limit: 2,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r1.items.length).toBe(2);
|
|
|
|
expect(r1.items[0].id).toBe(note1.id);
|
|
|
|
expect(r1.items[1].id).toBe(note2.id);
|
|
|
|
|
|
|
|
const r2 = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`, {
|
|
|
|
cursor: r1.cursor,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(r2.items.length).toBe(1);
|
|
|
|
expect(r2.items[0].id).toBe(note3.id);
|
|
|
|
}));
|
|
|
|
|
|
|
|
it('should return default fields', asyncTest(async () => {
|
|
|
|
const folder = await Folder.save({ title: 'folder' });
|
|
|
|
const note1 = await Note.save({ title: 'note1', parent_id: folder.id });
|
|
|
|
await Note.save({ title: 'note2', parent_id: folder.id });
|
|
|
|
|
|
|
|
const tag = await Tag.save({ title: 'tag' });
|
|
|
|
await Tag.addNote(tag.id, note1.id);
|
|
|
|
|
|
|
|
{
|
|
|
|
const r = await api.route(RequestMethod.GET, `folders/${folder.id}`);
|
|
|
|
expect('id' in r).toBe(true);
|
|
|
|
expect('title' in r).toBe(true);
|
|
|
|
expect('parent_id' in r).toBe(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const r = await api.route(RequestMethod.GET, `folders/${folder.id}/notes`);
|
|
|
|
expect('id' in r.items[0]).toBe(true);
|
|
|
|
expect('title' in r.items[0]).toBe(true);
|
|
|
|
expect('parent_id' in r.items[0]).toBe(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const r = await api.route(RequestMethod.GET, 'notes');
|
|
|
|
expect('id' in r.items[0]).toBe(true);
|
|
|
|
expect('title' in r.items[0]).toBe(true);
|
|
|
|
expect('parent_id' in r.items[0]).toBe(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const r = await api.route(RequestMethod.GET, `notes/${note1.id}/tags`);
|
|
|
|
expect('id' in r.items[0]).toBe(true);
|
|
|
|
expect('title' in r.items[0]).toBe(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const r = await api.route(RequestMethod.GET, `tags/${tag.id}`);
|
|
|
|
expect('id' in r).toBe(true);
|
|
|
|
expect('title' in r).toBe(true);
|
|
|
|
}
|
|
|
|
}));
|
2020-11-07 18:45:02 +02:00
|
|
|
|
|
|
|
it('should return the notes associated with a resource', asyncTest(async () => {
|
|
|
|
const note = await Note.save({});
|
|
|
|
await shim.attachFileToNote(note, `${__dirname}/../tests/support/photo.jpg`);
|
|
|
|
const resource = (await Resource.all())[0];
|
|
|
|
|
|
|
|
const resourceService = new ResourceService();
|
|
|
|
await resourceService.indexNoteResources();
|
|
|
|
|
|
|
|
const r = await api.route(RequestMethod.GET, `resources/${resource.id}/notes`);
|
|
|
|
|
|
|
|
expect(r.items.length).toBe(1);
|
|
|
|
expect(r.items[0]).toBe(note.id);
|
|
|
|
}));
|
2019-06-28 14:46:55 +02:00
|
|
|
});
|