Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add methods to read and update file metadata #90

15 changes: 13 additions & 2 deletions .github/workflows/node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:

services:
rabbitmq:
image: rabbitmq:3.12-management-alpine
image: rabbitmq:3.13-management-alpine
options: >-
--health-cmd "rabbitmq-diagnostics status"
--health-interval 10s
Expand All @@ -58,6 +58,16 @@ jobs:
ports:
- 5672:5672

redis:
image: redis:7.4-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379

steps:
- name: Check out repository code
uses: actions/checkout@v4
Expand Down Expand Up @@ -114,7 +124,8 @@ jobs:
S3_REGION: ${{ env.S3_REGION }}
GC_BUCKET: ${{ env.GC_BUCKET }}
GC_PROJECT_ID: ${{ env.GC_PROJECT_ID }}
run: npx nx affected -t lint,test,build --parallel=3
PREFIX: ${{ matrix.node-version }}
run: npx nx affected -t lint,test,build --parallel=2

- name: Upload coverage
uses: actions/upload-artifact@v4
Expand Down
38 changes: 30 additions & 8 deletions packages/file-storage/src/lib/file-storage-fs.class.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import { createReadStream, createWriteStream, Dirent, ObjectEncodingOptions, stat, unlink } from 'node:fs';
import { access, mkdir, readdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
import {
BigIntStats,
createReadStream,
createWriteStream,
Dirent,
ObjectEncodingOptions,
stat,
Stats,
unlink,
} from 'node:fs';
import { access, mkdir, readdir, readFile, rename, rm, stat as statPromise, writeFile } from 'node:fs/promises';
import { normalize, resolve as resolvePath, sep } from 'node:path';
import { finished, Readable } from 'node:stream';

import type {
FileStorage,
FileStorageBaseArgs,
FileStorageConfig,
FileStorageConfigFactory,
} from './file-storage.class';
import type { FileStorage } from './file-storage.class';
import type { FileStorageBaseArgs, FileStorageConfig, FileStorageConfigFactory } from './file-storage.types';
import type {
FileStorageLocalDeleteDir,
FileStorageLocalDownloadFile,
FileStorageLocalDownloadStream,
FileStorageLocalFileExists,
FileStorageLocalGetFileMeta,
FileStorageLocalReadDir,
FileStorageLocalSetup,
FileStorageLocalUploadFile,
Expand Down Expand Up @@ -131,6 +137,22 @@ export class FileStorageLocal implements FileStorage {
);
}

async getFileMeta(
args: FileStorageLocalGetFileMeta & {
options: { bigint: false | undefined };
},
): Promise<Stats>;
async getFileMeta(
args: FileStorageLocalGetFileMeta & {
options: { bigint: true };
},
): Promise<BigIntStats>;
async getFileMeta(args: FileStorageLocalGetFileMeta): Promise<Stats | BigIntStats> {
const { filePath, options, request } = args;
const fileName = await this.transformFilePath(filePath, MethodTypes.READ, request);
return statPromise(fileName, options);
}

async deleteDir(args: FileStorageLocalDeleteDir): Promise<void> {
const { options = { recursive: true, force: true }, dirPath, request } = args;
const dirName = await this.transformFilePath(dirPath, MethodTypes.DELETE, request);
Expand Down
50 changes: 37 additions & 13 deletions packages/file-storage/src/lib/file-storage-fs.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import type { BigIntOptions, Dirent, ObjectEncodingOptions, StatOptions, WriteFileOptions } from 'node:fs';
import type {
BigIntOptions,
BigIntStats,
Dirent,
ObjectEncodingOptions,
StatOptions,
Stats,
WriteFileOptions,
} from 'node:fs';

import type { FileStorageBaseArgs, FileStorageDirBaseArgs, FileStorageReadDirBaseArgs } from './file-storage.class';
import type {
FileStorageBaseArgs,
FileStorageDeleteDir,
FileStorageDownloadFile,
FileStorageDownloadStream,
FileStorageFileExists,
FileStorageGetFileMeta,
FileStorageMoveFile,
FileStorageReadDir,
FileStorageUploadFile,
FileStorageUploadStream,
} from './file-storage.types';

export type StreamOptions = {
flags?: string;
Expand All @@ -20,44 +39,49 @@ export type FileStorageLocalSetup = {
[key: string]: unknown;
};

export interface FileStorageLocalFileExists extends FileStorageBaseArgs {
export interface FileStorageLocalFileExists extends FileStorageFileExists {
options?: StatOptions | BigIntOptions;
}

export interface FileStorageLocalMoveFile extends FileStorageBaseArgs {
export interface FileStorageLocalMoveFile extends FileStorageMoveFile {
newFilePath: string;
}

export interface FileStorageLocalUploadFile extends FileStorageBaseArgs {
content: string | Uint8Array | Buffer;
export interface FileStorageLocalUploadFile extends FileStorageUploadFile {
options?: WriteFileOptions;
}

export interface FileStorageLocalUploadStream extends FileStorageBaseArgs {
export interface FileStorageLocalUploadStream extends FileStorageUploadStream {
options?: BufferEncoding | StreamOptions;
}

export interface FileStorageLocalDownloadFile extends FileStorageBaseArgs {
options:
export interface FileStorageLocalDownloadFile extends FileStorageDownloadFile {
options?:
| { encoding?: null; flag?: string }
| { encoding: BufferEncoding; flag?: string }
| BufferEncoding
| (ObjectEncodingOptions & { flag?: string })
| undefined
| null;
// options?: Record<string, any> | BufferEncoding | null;
}

export interface FileStorageLocalDownloadStream extends FileStorageBaseArgs {
export interface FileStorageLocalDownloadStream extends FileStorageDownloadStream {
options?: BufferEncoding | StreamOptions;
}

export interface FileStorageLocalDeleteDir extends FileStorageDirBaseArgs {
export type FileStorageLocalDeleteFile = FileStorageBaseArgs;

export interface FileStorageLocalGetFileMeta extends FileStorageGetFileMeta {
options?: { bigint?: boolean | undefined };
}
export type FileStorageLocalGetFileMetaOutput = Stats | BigIntStats;

export interface FileStorageLocalDeleteDir extends FileStorageDeleteDir {
options?: { recursive?: boolean; force?: boolean };
}

export type ReadDirOutput = string[] | Buffer[] | Dirent[];

export interface FileStorageLocalReadDir<R = string[], T = ReadDirOutput> extends FileStorageReadDirBaseArgs<R, T> {
export interface FileStorageLocalReadDir<R = string[], T = ReadDirOutput> extends FileStorageReadDir<R, T> {
options?: { encoding: BufferEncoding; withFileTypes?: boolean; recursive?: boolean } | BufferEncoding;
}
15 changes: 13 additions & 2 deletions packages/file-storage/src/lib/file-storage-google.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import type { File, GetFilesResponse } from '@google-cloud/storage';
import { Injectable } from '@nestjs/common';
import { finished, type Readable } from 'node:stream';

import type { FileStorage, FileStorageConfig, FileStorageConfigFactory } from './file-storage.class';
import type { FileStorage } from './file-storage.class';
import type { FileStorageConfig, FileStorageConfigFactory } from './file-storage.types';
import type {
FileStorageGoogleConfig,
FileStorageGoogleDeleteDir,
FileStorageGoogleDeleteFile,
FileStorageGoogleDownloadFile,
FileStorageGoogleDownloadStream,
FileStorageGoogleFileExists,
FileStorageGoogleGetFileMeta,
FileStorageGoogleGetFileMetaOutput,
FileStorageGoogleMoveFile,
FileStorageGoogleReadDir,
FileStorageGoogleSetup,
Expand Down Expand Up @@ -125,6 +128,14 @@ export class FileStorageGoogle implements FileStorage {
}
}

async getFileMeta(args: FileStorageGoogleGetFileMeta): Promise<FileStorageGoogleGetFileMetaOutput> {
const { storage, bucket } = this.config;
const { options = {}, request } = args;
const filePath = await this.transformFilePath(args.filePath, MethodTypes.READ, request, options);
const [metadata] = await storage.bucket(bucket).file(filePath).getMetadata(options);
return metadata;
}

async deleteDir(args: FileStorageGoogleDeleteDir): Promise<void> {
const { storage, bucket } = this.config;
const { options = {}, request } = args;
Expand All @@ -135,7 +146,7 @@ export class FileStorageGoogle implements FileStorage {
// TODO: make default serializer compliant with the other readDir implementations
async readDir<R = string[]>(args: FileStorageGoogleReadDir<R>): Promise<R> {
const defaultSerializer = (res: GetFilesResponse) => {
return res[0].map((file: File) => file.name) as R;
return res[0].map((file: File) => (prefix ? file.name.replace(`${prefix}/`, '') : file.name)) as R;
};
const { storage, bucket } = this.config;
const { dirPath, request, serializer = defaultSerializer, options = {} } = args;
Expand Down
46 changes: 33 additions & 13 deletions packages/file-storage/src/lib/file-storage-google.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,32 @@ import type {
CreateWriteStreamOptions,
DeleteFilesOptions,
DownloadOptions,
FileMetadata,
FileOptions,
GetFilesOptions,
GetFilesResponse,
MoveOptions,
SaveOptions,
Storage,
} from '@google-cloud/storage';
import type { DeleteOptions, ExistsOptions } from '@google-cloud/storage/build/cjs/src/nodejs-common/service-object';
import type {
DeleteOptions,
ExistsOptions,
GetMetadataOptions,
} from '@google-cloud/storage/build/cjs/src/nodejs-common/service-object';

import type { FileStorageBaseArgs, FileStorageDirBaseArgs, FileStorageReadDirBaseArgs } from './file-storage.class';
import type {
FileStorageDeleteDir,
FileStorageDeleteFile,
FileStorageDownloadFile,
FileStorageDownloadStream,
FileStorageFileExists,
FileStorageGetFileMeta,
FileStorageMoveFile,
FileStorageReadDir,
FileStorageUploadFile,
FileStorageUploadStream,
} from './file-storage.types';

// TODO: add authentication options
export interface FileStorageGoogleSetup {
Expand All @@ -29,40 +45,44 @@ export interface FileStorageGoogleConfig {
[key: string]: any;
}

export interface FileStorageGoogleFileExists extends FileStorageBaseArgs {
export interface FileStorageGoogleFileExists extends FileStorageFileExists {
options?: ExistsOptions & FileOptions;
}

export interface FileStorageGoogleMoveFile extends FileStorageBaseArgs {
newFilePath: string;
export interface FileStorageGoogleMoveFile extends FileStorageMoveFile {
options?: FileOptions & MoveOptions;
}

export interface FileStorageGoogleUploadFile extends FileStorageBaseArgs {
content: string | Uint8Array | Buffer;
export interface FileStorageGoogleUploadFile extends FileStorageUploadFile {
options?: FileOptions & SaveOptions;
}

export interface FileStorageGoogleUploadStream extends FileStorageBaseArgs {
export interface FileStorageGoogleUploadStream extends FileStorageUploadStream {
options?: CreateWriteStreamOptions & FileOptions;
}

export interface FileStorageGoogleDownloadFile extends FileStorageBaseArgs {
export interface FileStorageGoogleDownloadFile extends FileStorageDownloadFile {
options?: DownloadOptions & FileOptions;
}

export interface FileStorageGoogleDownloadStream extends FileStorageBaseArgs {
export interface FileStorageGoogleDownloadStream extends FileStorageDownloadStream {
options?: CreateReadStreamOptions & FileOptions;
}

export interface FileStorageGoogleDeleteFile extends FileStorageBaseArgs {
export interface FileStorageGoogleDeleteFile extends FileStorageDeleteFile {
options?: DeleteOptions & FileOptions;
}

export interface FileStorageGoogleDeleteDir extends FileStorageDirBaseArgs {
export interface FileStorageGoogleGetFileMeta extends FileStorageGetFileMeta {
options?: GetMetadataOptions & FileOptions;
}

export type FileStorageGoogleGetFileMetaOutput = FileMetadata;

export interface FileStorageGoogleDeleteDir extends FileStorageDeleteDir {
options?: Omit<DeleteFilesOptions, 'prefix'> & FileOptions;
}

export interface FileStorageGoogleReadDir<R = string[]> extends FileStorageReadDirBaseArgs<R, GetFilesResponse> {
export interface FileStorageGoogleReadDir<R = string[]> extends FileStorageReadDir<R, GetFilesResponse> {
options?: Omit<GetFilesOptions, 'prefix'> & FileOptions;
}
12 changes: 11 additions & 1 deletion packages/file-storage/src/lib/file-storage-s3.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import type { DeleteObjectsCommandInput, ListObjectsCommandInput, ListObjectsCom
import type { Options as UploadOptions } from '@aws-sdk/lib-storage';
import { PassThrough, Readable } from 'node:stream';

import type { FileStorage, FileStorageConfig, FileStorageConfigFactory } from './file-storage.class';
import type { FileStorage } from './file-storage.class';
import type { FileStorageConfig, FileStorageConfigFactory } from './file-storage.types';
import type {
FileStorageS3Config,
FileStorageS3DeleteDir,
FileStorageS3DeleteFile,
FileStorageS3DownloadFile,
FileStorageS3DownloadStream,
FileStorageS3FileExists,
FileStorageS3GetFileMeta,
FileStorageS3GetFileMetaOutput,
FileStorageS3MoveFile,
FileStorageS3ReadDir,
FileStorageS3Setup,
Expand Down Expand Up @@ -166,6 +169,13 @@ export class FileStorageS3 implements FileStorage {
return true;
}

async getFileMeta(args: FileStorageS3GetFileMeta): Promise<FileStorageS3GetFileMetaOutput> {
const { filePath, options = {}, request } = args;
const Key = await this.transformFilePath(filePath, MethodTypes.READ, request, options);
const { s3, bucket: Bucket } = this.config;
return s3.headObject({ ...options, Bucket, Key });
}

async deleteDir(args: FileStorageS3DeleteDir): Promise<void> {
const { dirPath, options = {}, request } = args;
const { s3, bucket: Bucket } = this.config;
Expand Down
Loading