From 154854dc21c757c73b1f3d19c1054f2851d15cf5 Mon Sep 17 00:00:00 2001 From: Philipp Kunz Date: Sun, 24 Nov 2024 19:56:12 +0100 Subject: [PATCH] feat(core): Enhanced directory handling and file restoration from trash --- changelog.md | 6 ++++++ test/test.trash.ts | 19 +++++++++++++++++-- ts/00_commitinfo_data.ts | 2 +- ts/classes.directory.ts | 34 +++++++++++++++++++++++++++++----- ts/classes.file.ts | 30 ++++++++++++++++++++++++++++-- 5 files changed, 81 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 2cc4d38..ce5a0a3 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,11 @@ # Changelog +## 2024-11-24 - 3.3.0 - feat(core) +Enhanced directory handling and file restoration from trash + +- Refined getSubDirectoryByName to handle file paths treated as directories. +- Introduced file restoration function from trash to original or specified paths. + ## 2024-11-24 - 3.2.2 - fix(core) Refactor Bucket class for improved error handling diff --git a/test/test.trash.ts b/test/test.trash.ts index e6caa85..c29a267 100644 --- a/test/test.trash.ts +++ b/test/test.trash.ts @@ -30,7 +30,7 @@ tap.test('should clean all contents', async () => { tap.test('should delete a file into the normally', async () => { const path = 'trashtest/trashme.txt'; - const file = await myBucket.fastPut({ + const file = await myBucket.fastPutStrict({ path, contents: 'I\'m in the trash test content!', }); @@ -44,7 +44,7 @@ tap.test('should delete a file into the normally', async () => { tap.test('should put a file into the trash', async () => { const path = 'trashtest/trashme.txt'; - const file = await myBucket.fastPut({ + const file = await myBucket.fastPutStrict({ path, contents: 'I\'m in the trash test content!', }); @@ -60,4 +60,19 @@ tap.test('should put a file into the trash', async () => { }); }); +tap.test('should restore a file from trash', async () => { + const baseDirectory = await myBucket.getBaseDirectory(); + const file = await baseDirectory.getFileStrict({ + path: 'trashtest/trashme.txt', + getFromTrash: true + }); + const trashFileMeta = await file.getMetaData(); + const data = await trashFileMeta.getCustomMetaData({ + key: 'recycle' + }); + expect(file).toBeInstanceOf(smartbucket.File); + await file.restore(); +}); + + export default tap.start(); diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index aaaeaa3..6ac4176 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartbucket', - version: '3.2.2', + version: '3.3.0', description: 'A TypeScript library offering simple and cloud-agnostic object storage with advanced features like bucket creation, file and directory management, and data streaming.' } diff --git a/ts/classes.directory.ts b/ts/classes.directory.ts index c8cb3e0..798c1fc 100644 --- a/ts/classes.directory.ts +++ b/ts/classes.directory.ts @@ -10,9 +10,9 @@ export class Directory { public parentDirectoryRef: Directory; public name: string; - public tree: string[]; - public files: string[]; - public folders: string[]; + public tree!: string[]; + public files!: string[]; + public folders!: string[]; constructor(bucketRefArg: Bucket, parentDirectory: Directory, name: string) { this.bucketRef = bucketRefArg; @@ -192,9 +192,22 @@ export class Directory { * gets a sub directory by name */ public async getSubDirectoryByName(dirNameArg: string, optionsArg: { + /** + * in s3 a directory does not exist if it is empty + * this option returns a directory even if it is empty + */ getEmptyDirectory?: boolean; + /** + * in s3 a directory does not exist if it is empty + * this option creates a directory even if it is empty using a initializer file + */ createWithInitializerFile?: boolean; + /** + * if the path is a file path, it will be treated as a file and the parent directory will be returned + */ + couldBeFilePath?: boolean; } = {}): Promise { + const dirNameArray = dirNameArg.split('/').filter(str => str.trim() !== ""); optionsArg = { @@ -221,8 +234,19 @@ export class Directory { return returnDirectory || null; }; + if (optionsArg.couldBeFilePath) { + const baseDirectory = await this.bucketRef.getBaseDirectory(); + const existingFile = await baseDirectory.getFile({ + path: dirNameArg, + }); + if (existingFile) { + const adjustedPath = dirNameArg.substring(0, dirNameArg.lastIndexOf('/')); + return this.getSubDirectoryByName(adjustedPath); + } + } + let wantedDirectory: Directory | null = null; - let counter = 0; + let counter = 0; for (const dirNameToSearch of dirNameArray) { counter++; const directoryToSearchIn = wantedDirectory ? wantedDirectory : this; @@ -336,7 +360,7 @@ export class Directory { */ mode?: 'permanent' | 'trash'; }) { - const file = await this.getFile({ + const file = await this.getFileStrict({ path: optionsArg.path, }); await file.delete({ diff --git a/ts/classes.file.ts b/ts/classes.file.ts index 932c1ac..9b0425c 100644 --- a/ts/classes.file.ts +++ b/ts/classes.file.ts @@ -130,6 +130,29 @@ export class File { await this.parentDirectoryRef.listFiles(); } + /** + * restores + */ + public async restore(optionsArg: { + useOriginalPath?: boolean; + toPath?: string; + overwrite?: boolean; + } = {}) { + optionsArg = { + useOriginalPath: (() => { + return optionsArg.toPath ? false : true; + })(), + overwrite: false, + ...optionsArg, + }; + const moveToPath = optionsArg.toPath || (await (await this.getMetaData()).getCustomMetaData({ + key: 'recycle' + })).originalPath; + await this.move({ + path: moveToPath, + }) + } + /** * allows locking the file * @param optionsArg @@ -154,7 +177,7 @@ export class File { }) { const metadata = await this.getMetaData(); await metadata.removeLock({ - force: optionsArg?.force, + force: optionsArg?.force || false, }); } @@ -219,7 +242,10 @@ export class File { // lets update references of this const baseDirectory = await this.parentDirectoryRef.bucketRef.getBaseDirectory(); this.parentDirectoryRef = await baseDirectory.getSubDirectoryByNameStrict( - pathDescriptorArg.directory?.getBasePath()! + await helpers.reducePathDescriptorToPath(pathDescriptorArg), + { + couldBeFilePath: true, + } ); this.name = pathDescriptorArg.path!; }