mirror of https://github.com/laurent22/joplin.git
Fxied tests
parent
439d29387f
commit
a088061de9
|
@ -189,6 +189,7 @@ export default abstract class BaseModel<T> {
|
||||||
protected async isNew(object: T, options: SaveOptions): Promise<boolean> {
|
protected async isNew(object: T, options: SaveOptions): Promise<boolean> {
|
||||||
if (options.isNew === false) return false;
|
if (options.isNew === false) return false;
|
||||||
if (options.isNew === true) return true;
|
if (options.isNew === true) return true;
|
||||||
|
if ('id' in object && !(object as WithUuid).id) throw new Error('ID cannot be undefined or null');
|
||||||
return !(object as WithUuid).id;
|
return !(object as WithUuid).id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -266,8 +266,8 @@ export default class FileModel extends BaseModel<File> {
|
||||||
|
|
||||||
if ('name' in file && !file.is_root) {
|
if ('name' in file && !file.is_root) {
|
||||||
const existingFile = await this.fileByName(parentId, file.name);
|
const existingFile = await this.fileByName(parentId, file.name);
|
||||||
if (existingFile && options.isNew) throw new ErrorConflict(`Already a file with name "${file.name}"`);
|
if (existingFile && options.isNew) throw new ErrorConflict(`Already a file with name "${file.name}" (1)`);
|
||||||
if (existingFile && file.id === existingFile.id) throw new ErrorConflict(`Already a file with name "${file.name}"`);
|
if (existingFile && file.id !== existingFile.id) throw new ErrorConflict(`Already a file with name "${file.name}" (2)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('name' in file) {
|
if ('name' in file) {
|
||||||
|
@ -471,8 +471,30 @@ export default class FileModel extends BaseModel<File> {
|
||||||
const isNew = await this.isNew(object, options);
|
const isNew = await this.isNew(object, options);
|
||||||
|
|
||||||
const file: File = { ... object };
|
const file: File = { ... object };
|
||||||
|
let sourceFile: File = null;
|
||||||
|
|
||||||
if ('content' in file) file.size = file.content ? file.content.byteLength : 0;
|
if ('content' in file) {
|
||||||
|
let sourceFileId: string = null;
|
||||||
|
|
||||||
|
if (file.id) {
|
||||||
|
if (!('source_file_id' in file)) throw new Error('source_file_id is required when setting the content');
|
||||||
|
sourceFileId = file.source_file_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileSize = file.content ? file.content.byteLength : 0;
|
||||||
|
|
||||||
|
if (sourceFileId) {
|
||||||
|
sourceFile = {
|
||||||
|
id: sourceFileId,
|
||||||
|
content: file.content,
|
||||||
|
size: fileSize,
|
||||||
|
source_file_id: '',
|
||||||
|
};
|
||||||
|
delete file.content;
|
||||||
|
} else {
|
||||||
|
file.size = fileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isNew) {
|
if (isNew) {
|
||||||
if (!file.parent_id && !file.is_root) file.parent_id = await this.userRootFileId();
|
if (!file.parent_id && !file.is_root) file.parent_id = await this.userRootFileId();
|
||||||
|
@ -486,7 +508,10 @@ export default class FileModel extends BaseModel<File> {
|
||||||
file.owner_id = this.userId;
|
file.owner_id = this.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.save(file, options);
|
return this.withTransaction(async () => {
|
||||||
|
if (sourceFile) await this.save(sourceFile);
|
||||||
|
return super.save(file, options);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async childrenCount(id: string): Promise<number> {
|
public async childrenCount(id: string): Promise<number> {
|
||||||
|
|
|
@ -60,8 +60,23 @@ router.put('api/files/:id/content', async (path: SubPath, ctx: AppContext) => {
|
||||||
// https://github.com/laurent22/joplin/issues/4402
|
// https://github.com/laurent22/joplin/issues/4402
|
||||||
const buffer = result?.files?.file ? await fs.readFile(result.files.file.path) : Buffer.alloc(0);
|
const buffer = result?.files?.file ? await fs.readFile(result.files.file.path) : Buffer.alloc(0);
|
||||||
|
|
||||||
const file: File = await fileModel.pathToFile(fileId, { mustExist: false, returnFullEntity: false });
|
const parsedFile: File = await fileModel.pathToFile(fileId, { mustExist: false });
|
||||||
file.content = buffer;
|
|
||||||
|
const isNewFile = !parsedFile.id;
|
||||||
|
|
||||||
|
const file: File = {
|
||||||
|
name: parsedFile.name,
|
||||||
|
content: buffer,
|
||||||
|
source_file_id: 'source_file_id' in parsedFile ? parsedFile.source_file_id : '',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isNewFile) {
|
||||||
|
file.id = parsedFile.id;
|
||||||
|
} else {
|
||||||
|
file.name = parsedFile.name;
|
||||||
|
if ('parent_id' in parsedFile) file.parent_id = parsedFile.parent_id;
|
||||||
|
}
|
||||||
|
|
||||||
return fileModel.toApiOutput(await fileModel.save(file, { validationRules: { mustBeFile: true } }));
|
return fileModel.toApiOutput(await fileModel.save(file, { validationRules: { mustBeFile: true } }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,8 +85,12 @@ router.del('api/files/:id/content', async (path: SubPath, ctx: AppContext) => {
|
||||||
const fileId = path.id;
|
const fileId = path.id;
|
||||||
const file: File = await fileModel.pathToFile(fileId, { mustExist: false, returnFullEntity: false });
|
const file: File = await fileModel.pathToFile(fileId, { mustExist: false, returnFullEntity: false });
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
file.content = Buffer.alloc(0);
|
|
||||||
await fileModel.save(file, { validationRules: { mustBeFile: true } });
|
await fileModel.save({
|
||||||
|
id: file.id,
|
||||||
|
content: Buffer.alloc(0),
|
||||||
|
source_file_id: file.source_file_id,
|
||||||
|
}, { validationRules: { mustBeFile: true } });
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('api/files/:id/delta', async (path: SubPath, ctx: AppContext) => {
|
router.get('api/files/:id/delta', async (path: SubPath, ctx: AppContext) => {
|
||||||
|
|
|
@ -44,7 +44,7 @@ describe('api_shares', function() {
|
||||||
test('should share a file with another user', async function() {
|
test('should share a file with another user', async function() {
|
||||||
const { user: user1, session: session1 } = await createUserAndSession(1);
|
const { user: user1, session: session1 } = await createUserAndSession(1);
|
||||||
const { user: user2, session: session2 } = await createUserAndSession(2);
|
const { user: user2, session: session2 } = await createUserAndSession(2);
|
||||||
await createFile(user1.id, 'root:/test.txt:', 'testing share');
|
await createFile(user1.id, 'root:/test.txt:', 'created by sharer');
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
// Create the file share object
|
// Create the file share object
|
||||||
|
@ -85,7 +85,16 @@ describe('api_shares', function() {
|
||||||
expect(results.items[0].name).toBe('test.txt');
|
expect(results.items[0].name).toBe('test.txt');
|
||||||
|
|
||||||
const fileContent = await getApi<Buffer>(session2.id, 'files/root:/test.txt:/content');
|
const fileContent = await getApi<Buffer>(session2.id, 'files/root:/test.txt:/content');
|
||||||
expect(fileContent.toString()).toBe('testing share');
|
expect(fileContent.toString()).toBe('created by sharer');
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// If file is changed by sharee, sharer should see the change too
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
await updateFile(user2.id, 'root:/test.txt:', 'modified by sharee');
|
||||||
|
const fileContent = await getApi<Buffer>(session1.id, 'files/root:/test.txt:/content');
|
||||||
|
expect(fileContent.toString()).toBe('modified by sharee');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should get updated time of shared file', async function() {
|
test('should get updated time of shared file', async function() {
|
||||||
|
@ -131,6 +140,10 @@ describe('api_shares', function() {
|
||||||
|
|
||||||
await msleep(1);
|
await msleep(1);
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// If file is changed on sharer side, sharee should see the changes
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
await updateFile(user1.id, sharerFile.id, 'from sharer');
|
await updateFile(user1.id, sharerFile.id, 'from sharer');
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -147,8 +160,15 @@ describe('api_shares', function() {
|
||||||
expect(page2.items[0].item.updated_time).toBe(page1.items[0].item.updated_time);
|
expect(page2.items[0].item.updated_time).toBe(page1.items[0].item.updated_time);
|
||||||
cursor2 = page2.cursor;
|
cursor2 = page2.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// If file is changed on sharee side, sharer should see the changes
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: test delta:
|
// TODO: test delta:
|
||||||
// - File is changed on sharer side
|
// - File is changed on sharer side
|
||||||
// - File is changed on sharee side
|
// - File is changed on sharee side
|
||||||
|
|
|
@ -239,9 +239,12 @@ export async function createFile(userId: string, path: string, content: string):
|
||||||
|
|
||||||
export async function updateFile(userId: string, path: string, content: string): Promise<File> {
|
export async function updateFile(userId: string, path: string, content: string): Promise<File> {
|
||||||
const fileModel = models().file({ userId });
|
const fileModel = models().file({ userId });
|
||||||
const file: File = await fileModel.pathToFile(path, { returnFullEntity: false });
|
const file: File = await fileModel.pathToFile(path, { returnFullEntity: true });
|
||||||
file.content = Buffer.from(content);
|
await fileModel.save({
|
||||||
await fileModel.save(file);
|
id: file.id,
|
||||||
|
content: Buffer.from(content),
|
||||||
|
source_file_id: file.source_file_id,
|
||||||
|
});
|
||||||
return fileModel.load(file.id);
|
return fileModel.load(file.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue