Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
aa9a2e9220 | |||
154854dc21 | |||
8e9041fbbf | |||
16a82ac50a |
13
changelog.md
13
changelog.md
@ -1,5 +1,18 @@
|
||||
# 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
|
||||
|
||||
- Ensured safe access using non-null assertions when finding a bucket.
|
||||
- Enhanced fastPut method by adding fastPutStrict for safer operations.
|
||||
- Added explicit error handling and type checking in fastExists method.
|
||||
|
||||
## 2024-11-24 - 3.2.1 - fix(metadata)
|
||||
Fix metadata handling for deleted files
|
||||
|
||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "3.2.1",
|
||||
"version": "3.3.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "3.2.1",
|
||||
"version": "3.3.0",
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"@push.rocks/smartpath": "^5.0.18",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartbucket",
|
||||
"version": "3.2.1",
|
||||
"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.",
|
||||
"main": "dist_ts/index.js",
|
||||
"typings": "dist_ts/index.d.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();
|
||||
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartbucket',
|
||||
version: '3.2.1',
|
||||
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.'
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export class Bucket {
|
||||
public static async getBucketByName(smartbucketRef: SmartBucket, bucketNameArg: string) {
|
||||
const command = new plugins.s3.ListBucketsCommand({});
|
||||
const buckets = await smartbucketRef.s3Client.send(command);
|
||||
const foundBucket = buckets.Buckets.find((bucket) => bucket.Name === bucketNameArg);
|
||||
const foundBucket = buckets.Buckets!.find((bucket) => bucket.Name === bucketNameArg);
|
||||
|
||||
if (foundBucket) {
|
||||
console.log(`bucket with name ${bucketNameArg} exists.`);
|
||||
@ -88,14 +88,15 @@ export class Bucket {
|
||||
contents: string | Buffer;
|
||||
overwrite?: boolean;
|
||||
}
|
||||
): Promise<File> {
|
||||
): Promise<File | null> {
|
||||
try {
|
||||
const reducedPath = await helpers.reducePathDescriptorToPath(optionsArg);
|
||||
const exists = await this.fastExists({ path: reducedPath });
|
||||
|
||||
if (exists && !optionsArg.overwrite) {
|
||||
console.error(`Object already exists at path '${reducedPath}' in bucket '${this.name}'.`);
|
||||
return;
|
||||
const errorText = `Object already exists at path '${reducedPath}' in bucket '${this.name}'.`;
|
||||
console.error(errorText);
|
||||
return null;
|
||||
} else if (exists && optionsArg.overwrite) {
|
||||
console.log(
|
||||
`Overwriting existing object at path '${reducedPath}' in bucket '${this.name}'.`
|
||||
@ -128,6 +129,14 @@ export class Bucket {
|
||||
}
|
||||
}
|
||||
|
||||
public async fastPutStrict(...args: Parameters<Bucket['fastPut']>) {
|
||||
const file = await this.fastPut(...args);
|
||||
if (!file) {
|
||||
throw new Error(`File not stored at path '${args[0].path}'`);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* get file
|
||||
*/
|
||||
@ -152,7 +161,7 @@ export class Bucket {
|
||||
},
|
||||
});
|
||||
await done.promise;
|
||||
return completeFile;
|
||||
return completeFile!;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,7 +229,7 @@ export class Bucket {
|
||||
return chunk;
|
||||
},
|
||||
finalFunction: async (cb) => {
|
||||
return null;
|
||||
return null!;
|
||||
},
|
||||
});
|
||||
|
||||
@ -392,8 +401,8 @@ export class Bucket {
|
||||
await this.smartbucketRef.s3Client.send(command);
|
||||
console.log(`Object '${optionsArg.path}' exists in bucket '${this.name}'.`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
if (error.name === 'NotFound') {
|
||||
} catch (error: any) {
|
||||
if (error?.name === 'NotFound') {
|
||||
console.log(`Object '${optionsArg.path}' does not exist in bucket '${this.name}'.`);
|
||||
return false;
|
||||
} else {
|
||||
|
@ -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<Directory | null> {
|
||||
|
||||
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({
|
||||
|
@ -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!;
|
||||
}
|
||||
|
Reference in New Issue
Block a user