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

(feature) add module asynchronous config using factory/existing/class #65

Merged
merged 3 commits into from
Nov 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ import { AzureStorageModule } from '@nestjs/azure-storage';
export class AppModule {}
```

If you want to use asynchronous configuration options using factory or class,

```typescript
const storageConfigFactory = async () => {
sasKey: process.env['AZURE_STORAGE_SAS_KEY'],
accountName: process.env['AZURE_STORAGE_ACCOUNT'],
containerName: 'nest-demo-container',
};

@Module({
controllers: [AppController],
providers: [AppService],
imports: [
AzureStorageModule.withConfigAsync({
useFactory: storageConfigFactory,
}),
],
})
export class AppModule {}
```

> You may provide a default `containerName` name for the whole module, this will apply to all controllers withing this module. You can also provide (override) the `containerName` in the controller, for each route.

## Story examples
Expand Down
62 changes: 62 additions & 0 deletions lib/azure-nest-storage.module.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { AZURE_STORAGE_MODULE_OPTIONS } from './azure-storage.constant';
import { AzureStorageModule } from './azure-nest-storage.module';
import {
AzureStorageOptions,
AzureStorageOptionsFactory,
} from './azure-storage.service';
import { ValueProvider, FactoryProvider } from '@nestjs/common/interfaces';

describe('AzureStorageModule', () => {
let options: AzureStorageOptions;

beforeEach(() => {
options = {
accountName: 'test_account',
containerName: 'test_container',
sasKey: 'FAKE_SAS_KEY',
};
});

it('should have storage options provider when options passed', async () => {
const module = AzureStorageModule.withConfig(options);
expect(module.providers.length).toBe(1);
expect((module.providers[0] as ValueProvider<any>).provide).toBe(
AZURE_STORAGE_MODULE_OPTIONS,
);
expect(await (module.providers[0] as ValueProvider<any>).useValue).toBe(
options,
);
});

it('should have storage options provider when async options passed using factory', async () => {
const factory = async () => options;
const module = AzureStorageModule.withConfigAsync({
useFactory: factory,
});
expect(module.providers.length).toBe(1);
expect((module.providers[0] as FactoryProvider<any>).provide).toBe(
AZURE_STORAGE_MODULE_OPTIONS,
);
expect(
await (module.providers[0] as FactoryProvider<any>).useFactory(),
).toBe(options);
});

it('should have storage options provider when async options passed using existing factory', async () => {
class TestAzStorageOptionsFactory implements AzureStorageOptionsFactory {
createAzureStorageOptions():
| AzureStorageOptions
| Promise<AzureStorageOptions> {
return options;
}
}

const module = AzureStorageModule.withConfigAsync({
useExisting: TestAzStorageOptionsFactory,
});
expect(module.providers.length).toBe(1);
expect((module.providers[0] as FactoryProvider<any>).provide).toBe(
AZURE_STORAGE_MODULE_OPTIONS,
);
});
});
45 changes: 44 additions & 1 deletion lib/azure-nest-storage.module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { DynamicModule, Module } from '@nestjs/common';
import { DynamicModule, Module, Provider } from '@nestjs/common';
import { AzureMulterStorageService } from './azure-multer.service';
import { AZURE_STORAGE_MODULE_OPTIONS } from './azure-storage.constant';
import {
AzureStorageOptions,
AzureStorageService,
AzureStorageAsyncOptions,
AzureStorageOptionsFactory,
} from './azure-storage.service';

const PUBLIC_PROVIDERS = [AzureMulterStorageService, AzureStorageService];
Expand All @@ -19,4 +21,45 @@ export class AzureStorageModule {
providers: [{ provide: AZURE_STORAGE_MODULE_OPTIONS, useValue: options }],
};
}

static withConfigAsync(options: AzureStorageAsyncOptions): DynamicModule {
return {
module: AzureStorageModule,
providers: [...this.createAsyncConfigProviders(options)],
};
}

private static createAsyncConfigProviders(
options: AzureStorageAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncConfigProvider(options)];
}

return [
this.createAsyncConfigProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}

private static createAsyncConfigProvider(
options: AzureStorageAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: AZURE_STORAGE_MODULE_OPTIONS,
useFactory: async (...args: any[]) => await options.useFactory(...args),
inject: options.inject || [],
};
}
return {
provide: AZURE_STORAGE_MODULE_OPTIONS,
useFactory: async (optionsFactory: AzureStorageOptionsFactory) =>
await optionsFactory.createAzureStorageOptions(),
inject: [options.useExisting || options.useClass],
};
}
}
17 changes: 16 additions & 1 deletion lib/azure-storage.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as Azure from '@azure/storage-blob';
import { ServiceClientOptions } from '@azure/ms-rest-js';

import { Inject, Injectable, Logger } from '@nestjs/common';
import { Inject, Injectable, Logger, Type } from '@nestjs/common';
import { AZURE_STORAGE_MODULE_OPTIONS } from './azure-storage.constant';

export const APP_NAME = 'AzureStorageService';
Expand All @@ -13,6 +13,21 @@ export interface AzureStorageOptions {
clientOptions?: ServiceClientOptions;
}

export interface AzureStorageAsyncOptions {
useExisting?: Type<AzureStorageOptionsFactory>;
useClass?: Type<AzureStorageOptionsFactory>;
useFactory?: (
...args: any[]
) => Promise<AzureStorageOptions> | AzureStorageOptions;
inject?: any[];
}

export interface AzureStorageOptionsFactory {
createAzureStorageOptions():
| AzureStorageOptions
| Promise<AzureStorageOptions>;
}

export interface UploadedFileMetadata {
fieldname: string;
originalname: string;
Expand Down