diff --git a/__tests__/files/file.spec.ts b/__tests__/files/file.spec.ts index 2c02fb530..62ae2acd0 100644 --- a/__tests__/files/file.spec.ts +++ b/__tests__/files/file.spec.ts @@ -22,8 +22,36 @@ describe('File creation', () => { // path checks expect(file.basename).toBe('picture.jpg') expect(file.extension).toBe('.jpg') - expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.dirname).toBe('/') expect(file.root).toBe('/files/emma/Photos') + expect(file.path).toBe('/picture.jpg') + expect(file.isDavRessource).toBe(true) + expect(file.permissions).toBe(Permission.READ) + }) + + test('Valid dav file with root', () => { + const file = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + root: '/files/emma' + }) + + expect(file).toBeInstanceOf(File) + expect(file.type).toBe(FileType.File) + + // various data + expect(file.mime).toBe('image/jpeg') + expect(file.owner).toBe('emma') + expect(file.size).toBeUndefined() + expect(file.attributes).toStrictEqual({}) + + // path checks + expect(file.basename).toBe('picture.jpg') + expect(file.extension).toBe('.jpg') + expect(file.dirname).toBe('/Photos') + expect(file.root).toBe('/files/emma') + expect(file.path).toBe('/Photos/picture.jpg') expect(file.isDavRessource).toBe(true) expect(file.permissions).toBe(Permission.READ) }) @@ -63,18 +91,18 @@ describe('File data change', () => { }) expect(file.basename).toBe('picture.jpg') - expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.dirname).toBe('/') expect(file.root).toBe('/files/emma/Photos') file.rename('picture-old.jpg') expect(file.basename).toBe('picture-old.jpg') - expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.dirname).toBe('/') expect(file.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture-old.jpg') expect(file.root).toBe('/files/emma/Photos') }) - test('Changing source', () => { + test('Moving a file', () => { const file = new File({ source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', mime: 'image/jpeg', @@ -82,14 +110,34 @@ describe('File data change', () => { }) expect(file.basename).toBe('picture.jpg') - expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.dirname).toBe('/') expect(file.root).toBe('/files/emma/Photos') file.move('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/picture-old.jpg') expect(file.basename).toBe('picture-old.jpg') - expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures') + expect(file.dirname).toBe('/') expect(file.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/picture-old.jpg') expect(file.root).toBe('/files/emma/Pictures') }) + + test('Moving a file to a different folder with root', () => { + const file = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + root: '/files/emma' + }) + + expect(file.basename).toBe('picture.jpg') + expect(file.dirname).toBe('/Photos') + expect(file.root).toBe('/files/emma') + + file.move('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/Old/picture-old.jpg') + + expect(file.basename).toBe('picture-old.jpg') + expect(file.dirname).toBe('/Pictures/Old') + expect(file.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/Old/picture-old.jpg') + expect(file.root).toBe('/files/emma') + }) }) diff --git a/__tests__/files/folder.spec.ts b/__tests__/files/folder.spec.ts index 364eb22af..893cf7d3f 100644 --- a/__tests__/files/folder.spec.ts +++ b/__tests__/files/folder.spec.ts @@ -21,7 +21,32 @@ describe('File creation', () => { // path checks expect(folder.basename).toBe('Photos') expect(folder.extension).toBeNull() - expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.dirname).toBe('/') + expect(folder.root).toBe('/files/emma') + expect(folder.isDavRessource).toBe(true) + expect(folder.permissions).toBe(Permission.READ) + }) + + test('Valid dav folder with root', () => { + const folder = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/Berlin', + owner: 'emma', + root: '/files/emma' + }) + + expect(folder).toBeInstanceOf(Folder) + expect(folder.type).toBe(FileType.Folder) + + // various data + expect(folder.mime).toBe('httpd/unix-directory') + expect(folder.owner).toBe('emma') + expect(folder.size).toBeUndefined() + expect(folder.attributes).toStrictEqual({}) + + // path checks + expect(folder.basename).toBe('Berlin') + expect(folder.extension).toBeNull() + expect(folder.dirname).toBe('/Photos') expect(folder.root).toBe('/files/emma') expect(folder.isDavRessource).toBe(true) expect(folder.permissions).toBe(Permission.READ) @@ -60,32 +85,51 @@ describe('Folder data change', () => { }) expect(folder.basename).toBe('Photos') - expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.dirname).toBe('/') expect(folder.root).toBe('/files/emma') folder.rename('Pictures') expect(folder.basename).toBe('Pictures') - expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.dirname).toBe('/') expect(folder.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures') expect(folder.root).toBe('/files/emma') }) - test('Changing source', () => { + test('Moving a folder', () => { const folder = new Folder({ source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/', owner: 'emma' }) expect(folder.basename).toBe('Photos') - expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.dirname).toBe('/') expect(folder.root).toBe('/files/emma') folder.move('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/') expect(folder.basename).toBe('Pictures') - expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.dirname).toBe('/') expect(folder.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures') expect(folder.root).toBe('/files/emma') }) + + test('Moving a folder to a different location with root', () => { + const folder = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/', + owner: 'emma', + root: '/files/emma' + }) + + expect(folder.basename).toBe('Photos') + expect(folder.dirname).toBe('/') + expect(folder.root).toBe('/files/emma') + + folder.move('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/1/2/3') + + expect(folder.basename).toBe('3') + expect(folder.dirname).toBe('/Pictures/1/2') + expect(folder.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/1/2/3') + expect(folder.root).toBe('/files/emma') + }) }) diff --git a/__tests__/files/node.spec.ts b/__tests__/files/node.spec.ts index a66a54f79..41d2480b8 100644 --- a/__tests__/files/node.spec.ts +++ b/__tests__/files/node.spec.ts @@ -122,6 +122,21 @@ describe('Sanity checks', () => { owner: true as unknown as string, })).toThrowError('Invalid owner') }) + + test('Invalid root', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + root: true as unknown as string, + })).toThrowError('Invalid root format') + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + root: 'https://cloud.domain.com/remote.php/dav/', + })).toThrowError('Root must start with a leading slash') + }) }) describe('Dav service detection', () => { @@ -171,3 +186,47 @@ describe('Dav service detection', () => { expect(file2.isDavRessource).toBe(false) }) }) + + +describe('Root and paths detection', () => { + test('Unknown root', () => { + const file1 = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }) + expect(file1.root).toBe('/files/emma/Photos') + expect(file1.dirname).toBe('/') + }) + + test('Provided root dav service', () => { + const file1 = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + root: '/files/emma', + }) + expect(file1.root).toBe('/files/emma') + expect(file1.dirname).toBe('/Photos') + }) + + test('Root with ending slash is removed', () => { + const file1 = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + root: '/files/emma/', + }) + expect(file1.root).toBe('/files/emma') + }) + + test('Root and source are the same', () => { + const file1 = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma', + mime: 'image/jpeg', + owner: 'emma', + root: '/files/emma', + }) + expect(file1.dirname).toBe('/') + }) +}) \ No newline at end of file diff --git a/lib/files/node.ts b/lib/files/node.ts index 59e764c71..35c65f98b 100644 --- a/lib/files/node.ts +++ b/lib/files/node.ts @@ -66,8 +66,12 @@ export abstract class Node { /** * Get the directory path leading to this object + * Will use the relative path to root if available */ get dirname(): string { + if (this.root) { + return dirname(this.source.split(this.root).pop() || '/') + } return dirname(this.source) } @@ -131,12 +135,27 @@ export abstract class Node { * Get the dav root of this object */ get root(): string|null { + // If provided (recommended), use the root and strip away the ending slash + if (this._data.root) { + return this._data.root.replace(/^(.+)\/$/, '$1') + } + + // Use the source to get the root from the dav service if (this.isDavRessource) { - return this.dirname.split(this._knownDavService).pop() || null + const root = dirname(this.source) + return root.split(this._knownDavService).pop() || null } + return null } + /** + * Get the absolute path of this object relative to the root + */ + get path(): string|null { + return (this.dirname + '/' + this.basename).replace(/\/\//g, '/') + } + /** * Move the node to a new destination * @@ -155,6 +174,6 @@ export abstract class Node { if (basename.includes('/')) { throw new Error('Invalid basename') } - this.move(this.dirname + '/' + basename) + this.move(dirname(this.source) + '/' + basename) } } diff --git a/lib/files/nodeData.ts b/lib/files/nodeData.ts index 3ddb01828..830da3ba8 100644 --- a/lib/files/nodeData.ts +++ b/lib/files/nodeData.ts @@ -28,7 +28,8 @@ export default interface NodeData { /** Unique ID */ id?: number - /** URL to the ressource + /** + * URL to the ressource. * e.g. https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg * or https://domain.com/Photos/picture.jpg */ @@ -53,6 +54,13 @@ export default interface NodeData { owner: string|null attributes?: Attribute + + /** + * The absolute root of the home relative to the service. + * It is highly recommended to provide that information. + * e.g. /files/emma + */ + root?: string } /** @@ -105,4 +113,12 @@ export const validateData = (data: NodeData) => { if ('attributes' in data && typeof data.attributes !== 'object') { throw new Error('Invalid attributes format') } + + if ('root' in data && typeof data.root !== 'string') { + throw new Error('Invalid root format') + } + + if (data.root && !data.root.startsWith('/')) { + throw new Error('Root must start with a leading slash') + } }