diff --git a/.prettierrc b/.prettierrc index 41dcb96..988e56f 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,6 @@ { "trailingComma": "all", "singleQuote": true, - "arrowParens": "avoid" + "arrowParens": "avoid", + "printWidth": 80 } diff --git a/README.md b/README.md new file mode 100644 index 0000000..bac1591 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +

+ Nest Logo +

+ +

+ nestjs-storage is manage file Storage wrapping package flydrive +

+

+ NPM Version + Package License + NPM Downloads +

+ +## Installation + +```bash +$ npm i --save @codebrew/nestjs-storage @slynova/flydrive + +# optional with s3 driver +$ npm i --save @slynova/flydrive-s3 + +# optional with gcs driver +$ npm i --save @slynova/flydrive-gcs +``` + +## Example + +```typescript +// app.module.ts +import { Module } from '@nestjs/common' +import { StorageModule, DriverType } from '@codebrew/nestjs-storage'; + +@Module({ + imports: [StorageModule.forRoot({ + module: AppModule, + imports: [ + StorageModule.forRoot({ + default: 'local', + disks: { + local: { + driver: DriverType.LOCAL, + config: { + root: process.cwd(), + }, + }, + }, + }), + ], + })] +}) +export class AppModule { + constructor(private storage: StorageServic) { + this.storage.getDisk().put('test.txt', 'text content'); + } +} +``` + +## Support + +nestjs-storage is an MIT-licensed open source project. If this library is helpful, please click star to support it. + +## Stay in touch + +- Author - [David Kwon](https://github.com/tienne) + +## License + +nestjs-storage is MIT licensed. diff --git a/lib/interfaces/index.ts b/lib/interfaces/index.ts index 7125c79..7a92b83 100644 --- a/lib/interfaces/index.ts +++ b/lib/interfaces/index.ts @@ -1 +1,3 @@ export * from './Storage-module-options'; +export * from './storage-module-async-options'; +export * from './storage-options-factory'; diff --git a/lib/interfaces/storage-module-async-options.ts b/lib/interfaces/storage-module-async-options.ts new file mode 100644 index 0000000..63b1f44 --- /dev/null +++ b/lib/interfaces/storage-module-async-options.ts @@ -0,0 +1,13 @@ +import { ModuleMetadata, Type } from '@nestjs/common'; +import { StorageModuleOptions } from './storage-module-options'; +import { StorageOptionsFactory } from './storage-options-factory'; + +export interface StorageModuleAsyncOptions + extends Pick { + name?: string; + useFactory?: ( + ...args: any[] + ) => Promise | StorageModuleOptions; + useClass?: Type; + inject?: any[]; +} diff --git a/lib/interfaces/storage-module-options.ts b/lib/interfaces/storage-module-options.ts index 14dd62d..eef10f0 100644 --- a/lib/interfaces/storage-module-options.ts +++ b/lib/interfaces/storage-module-options.ts @@ -1,13 +1,34 @@ import { StorageManagerConfig } from '@slynova/flydrive'; -import { DiskConfigType, DriverType } from '../types'; +import { + DiskConfigType, + DiskGCSConfigType, + DiskLocalConfigType, + DiskS3ConfigType, + DriverType, + DiskType, +} from '../types'; export interface StorageModuleOptions extends StorageManagerConfig { - isGlobal?: boolean; default: string; - disks: Record; + disks: Record; } export interface StorageDiskConfig { driver: DriverType | string; config: DiskConfigType; } + +export interface AwsS3StorageDisk extends StorageDiskConfig { + driver: DriverType.S3 | 's3'; + config: DiskS3ConfigType; +} + +export interface LocalStorageDisk extends StorageDiskConfig { + driver: DriverType.LOCAL | 'local'; + config: DiskLocalConfigType; +} + +export interface GoogleGcsStorageDisk extends StorageDiskConfig { + driver: DriverType.GCS | 'gcs'; + config: DiskGCSConfigType; +} diff --git a/lib/interfaces/storage-options-factory.ts b/lib/interfaces/storage-options-factory.ts new file mode 100644 index 0000000..26c9096 --- /dev/null +++ b/lib/interfaces/storage-options-factory.ts @@ -0,0 +1,5 @@ +import { StorageModuleOptions } from './storage-module-options'; + +export interface StorageOptionsFactory { + createStorageOptions(): Promise | StorageModuleOptions; +} diff --git a/lib/storage-core.module.ts b/lib/storage-core.module.ts new file mode 100644 index 0000000..60f7fc8 --- /dev/null +++ b/lib/storage-core.module.ts @@ -0,0 +1,84 @@ +import { + DynamicModule, + Global, + Inject, + Module, + Provider, + Type, +} from '@nestjs/common'; +import { StorageService } from './storage.service'; + +import { STORAGE_MODULE_OPTIONS } from './storage.constants'; +import { StorageModuleOptions, StorageOptionsFactory } from './interfaces'; +import { ModuleRef } from '@nestjs/core'; +import { StorageModuleAsyncOptions } from './interfaces/storage-module-async-options'; +@Global() +@Module({}) +export class StorageCoreModule { + constructor( + @Inject(STORAGE_MODULE_OPTIONS) + private readonly options: StorageModuleOptions, + private readonly moduleRef: ModuleRef, + ) {} + + static forRoot(options: StorageModuleOptions): DynamicModule { + const storageModuleOptions: Provider = { + provide: STORAGE_MODULE_OPTIONS, + useValue: options, + }; + + return { + module: StorageCoreModule, + providers: [storageModuleOptions, StorageService], + exports: [StorageService], + }; + } + + static forRootAsync(options: StorageModuleAsyncOptions): DynamicModule { + const asyncProviders = this.createAsyncProviders(options); + + return { + module: StorageCoreModule, + imports: options.imports, + providers: [...asyncProviders, StorageService], + exports: [StorageService], + }; + } + + private static createAsyncProviders( + options: StorageModuleAsyncOptions, + ): Provider[] { + if (options.useFactory) { + return [this.createAsyncOptionsProvider(options)]; + } + const useClass = options.useClass as Type; + return [ + this.createAsyncOptionsProvider(options), + { + provide: useClass, + useClass, + }, + ]; + } + + private static createAsyncOptionsProvider( + options: StorageModuleAsyncOptions, + ): Provider { + if (options.useFactory) { + return { + provide: STORAGE_MODULE_OPTIONS, + useFactory: options.useFactory, + inject: options.inject || [], + }; + } + + const inject = [options.useClass as Type]; + + return { + provide: STORAGE_MODULE_OPTIONS, + useFactory: async (optionsFactory: StorageOptionsFactory) => + optionsFactory.createStorageOptions(), + inject, + }; + } +} diff --git a/lib/storage.constants.ts b/lib/storage.constants.ts index 79b6edf..d2af8ea 100644 --- a/lib/storage.constants.ts +++ b/lib/storage.constants.ts @@ -2,3 +2,4 @@ * Injection tokens */ export const STORAGE_TOKEN = 'STORAGE_TOKEN'; +export const STORAGE_MODULE_OPTIONS = 'STORAGE_MODULE_OPTIONS'; diff --git a/lib/storage.module.ts b/lib/storage.module.ts index c894439..80f571c 100644 --- a/lib/storage.module.ts +++ b/lib/storage.module.ts @@ -1,25 +1,22 @@ -import { DynamicModule, FactoryProvider, Module } from '@nestjs/common'; -import { StorageService } from './storage.service'; +import { DynamicModule, Module } from '@nestjs/common'; -import { STORAGE_TOKEN } from './storage.constants'; import { StorageModuleOptions } from './interfaces'; +import { StorageCoreModule } from './storage-core.module'; +import { StorageModuleAsyncOptions } from './interfaces/storage-module-async-options'; -@Module({ - providers: [StorageService], - exports: [StorageService], -}) +@Module({}) export class StorageModule { static forRoot(options: StorageModuleOptions): DynamicModule { return { module: StorageModule, - global: options.isGlobal, - providers: [ - { - provide: STORAGE_TOKEN, - useValue: options, - }, - ], - exports: [StorageService], + imports: [StorageCoreModule.forRoot(options)], + }; + } + + static forRootAsync(options: StorageModuleAsyncOptions): DynamicModule { + return { + module: StorageModule, + imports: [StorageCoreModule.forRootAsync(options)], }; } } diff --git a/lib/storage.service.ts b/lib/storage.service.ts index 48be82f..d4105e9 100644 --- a/lib/storage.service.ts +++ b/lib/storage.service.ts @@ -1,14 +1,16 @@ import { Inject, Injectable } from '@nestjs/common'; import { Storage, StorageManager } from '@slynova/flydrive'; -import { STORAGE_TOKEN } from './storage.constants'; +import { STORAGE_MODULE_OPTIONS } from './storage.constants'; import { StorageModuleOptions } from './interfaces'; @Injectable() export class StorageService { private storageManager: StorageManager; - constructor(@Inject(STORAGE_TOKEN) private options: StorageModuleOptions) { + constructor( + @Inject(STORAGE_MODULE_OPTIONS) private options: StorageModuleOptions, + ) { this.storageManager = new StorageManager(options); } diff --git a/lib/types/disk.type.ts b/lib/types/disk.type.ts new file mode 100644 index 0000000..13ec8ac --- /dev/null +++ b/lib/types/disk.type.ts @@ -0,0 +1,12 @@ +import { + AwsS3StorageDisk, + GoogleGcsStorageDisk, + LocalStorageDisk, + StorageDiskConfig, +} from '../interfaces'; + +export type DiskType = + | AwsS3StorageDisk + | LocalStorageDisk + | GoogleGcsStorageDisk + | StorageDiskConfig; diff --git a/lib/types/index.ts b/lib/types/index.ts index 94bb9a7..3ce638e 100644 --- a/lib/types/index.ts +++ b/lib/types/index.ts @@ -1,2 +1,3 @@ export * from './driver.type'; export * from './disk-config.type'; +export * from './disk.type'; diff --git a/package.json b/package.json index b03da43..6e2f6b2 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,7 @@ "prerelease": "npm run build", "release": "release-it" }, - "dependencies": { - "@slynova/flydrive": "^1.0.2" - }, + "dependencies": {}, "devDependencies": { "@commitlint/cli": "^9.1.1", "@commitlint/config-angular": "^9.1.1", @@ -27,6 +25,7 @@ "@nestjs/core": "^7.3.2", "@nestjs/platform-express": "^7.3.2", "@nestjs/testing": "^7.3.2", + "@slynova/flydrive": "^1.0.2", "@slynova/flydrive-gcs": "^1.0.2", "@slynova/flydrive-s3": "^1.0.2", "@types/jest": "^26.0.5", diff --git a/tests/e2e/local-driver-async.spec.ts b/tests/e2e/local-driver-async.spec.ts new file mode 100644 index 0000000..d0f84ed --- /dev/null +++ b/tests/e2e/local-driver-async.spec.ts @@ -0,0 +1,37 @@ +import { INestApplication } from '@nestjs/common'; +import { Test } from '@nestjs/testing'; +import { AsyncFactoryModule } from '../src/async-factory.module'; +import { StorageService } from '../../lib'; + +describe('async local driver module', () => { + let app: INestApplication, storageService: StorageService; + + const fileName = 'local_async_test.txt', + fileContent = 'hi async module local storage test'; + + beforeEach(async () => { + const moduleRef = await Test.createTestingModule({ + imports: [AsyncFactoryModule], + }).compile(); + + app = moduleRef.createNestApplication(); + await app.init(); + }); + + it(`should load configuration with "async module local driver"`, async () => { + storageService = app.get(StorageService); + }); + + it(`async module put text`, async () => { + await storageService.getDisk().put(`tests/data/${fileName}`, fileContent); + }); + + it(`async module get text file`, async () => { + const res = await storageService.getDisk().get(`tests/data/${fileName}`); + expect(res.raw).toEqual(fileContent); + }); + + afterEach(async () => { + await app.close(); + }); +}); diff --git a/tests/src/app.module.ts b/tests/src/app.module.ts index 677acec..9700174 100644 --- a/tests/src/app.module.ts +++ b/tests/src/app.module.ts @@ -1,12 +1,8 @@ import { DynamicModule, Module } from '@nestjs/common'; - import { DriverType, StorageModule } from '../../lib'; -import { StorageService } from '../../lib/storage.service'; @Module({}) export class AppModule { - constructor(private readonly storageService: StorageService) {} - static withLocalStorage(): DynamicModule { return { module: AppModule, diff --git a/tests/src/async-factory.module.ts b/tests/src/async-factory.module.ts new file mode 100644 index 0000000..0806830 --- /dev/null +++ b/tests/src/async-factory.module.ts @@ -0,0 +1,21 @@ +import { Module } from '@nestjs/common'; +import { DriverType, StorageModule } from '../../lib'; + +@Module({ + imports: [ + StorageModule.forRootAsync({ + useFactory: () => ({ + default: 'local', + disks: { + local: { + driver: DriverType.LOCAL, + config: { + root: process.cwd(), + }, + }, + }, + }), + }), + ], +}) +export class AsyncFactoryModule {}