Skip to content

Commit

Permalink
feat(core): Add possibility to use a custom validate function
Browse files Browse the repository at this point in the history
  • Loading branch information
LeoAnesi committed Nov 13, 2020
1 parent eb20870 commit b375b3f
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 1 deletion.
6 changes: 5 additions & 1 deletion lib/config.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export class ConfigModule {
...process.env,
};
}
if (options.validationSchema) {
if (options.validate) {
const validatedConfig = options.validate(config);
validatedEnvConfig = validatedConfig;
this.assignVariablesToProcess(validatedConfig);
} else if (options.validationSchema) {
const validationOptions = this.getSchemaValidationOptions(options);
const {
error,
Expand Down
10 changes: 10 additions & 0 deletions lib/interfaces/config-module-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ export interface ConfigModuleOptions {
*/
encoding?: string;

/**
* Function to validate env vars, it takes an object containing environment
* variables as input and outputs validated environment variables.
* If exception is thrown in the function it would prevent the application
* from bootstrapping.
* Also, environment variables can be edited through this function, changes
* will be reflected in process.env.
*/
validate?: (config: Record<string, any>) => Record<string, any>;

/**
* Environment variables validation schema (Joi).
*/
Expand Down
88 changes: 88 additions & 0 deletions tests/e2e/validate-function.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { INestApplication } from '@nestjs/common';
import { Test } from '@nestjs/testing';
import { join } from 'path';
import { ConfigService } from '../../lib';
import { AppModule } from '../src/app.module';

describe('Schema validation', () => {
let app: INestApplication;
const errorMessage = 'Validation error';
const validate = (config: Record<string, any>) => {
if (!('PORT' in config && 'DATABASE_NAME' in config)) {
throw new Error(errorMessage);
}

return {};
};

it(`should prevent application from bootstrapping if error is thrown due to loaded env variables`, async () => {
let hasThrown = false;
try {
const module = await Test.createTestingModule({
imports: [AppModule.withValidateFunction(validate)],
}).compile();

app = module.createNestApplication();
await app.init();
} catch (err) {
hasThrown = true;
expect(err.message).toEqual(errorMessage);
}
expect(hasThrown).toBe(true);
});

it(`should prevent application from bootstrapping if error is thrown even when ignoreEnvFile is true`, async () => {
let hasThrown = false;
try {
const module = await Test.createTestingModule({
imports: [AppModule.withValidateFunction(validate, undefined, true)],
}).compile();

app = module.createNestApplication();
await app.init();
} catch (err) {
hasThrown = true;
expect(err.message).toEqual(errorMessage);
}
expect(hasThrown).toBe(true);
});

it(`should load env variables if everything is ok`, async () => {
let hasThrown = false;
const module = await Test.createTestingModule({
imports: [
AppModule.withValidateFunction(validate, join(__dirname, '.env.valid')),
],
}).compile();

app = module.createNestApplication();
await app.init();

const configService = app.get(ConfigService);
expect(typeof configService.get('PORT')).not.toBe(undefined);
expect(typeof configService.get('DATABASE_NAME')).not.toBe(undefined);
});

it(`should parse loaded env variables`, async () => {
const validateAndTransform = (config: Record<string, any>) => {
return {
PORT: Number(config.PORT),
DATABASE_NAME: config.DATABASE_NAME,
};
};
const module = await Test.createTestingModule({
imports: [
AppModule.withValidateFunction(
validateAndTransform,
join(__dirname, '.env.valid'),
),
],
}).compile();

app = module.createNestApplication();
await app.init();

const configService = app.get(ConfigService);
expect(typeof configService.get('PORT')).toEqual('number');
});
});
17 changes: 17 additions & 0 deletions tests/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ export class AppModule {
};
}

static withValidateFunction(
validate: (config: Record<string, any>) => Record<string, any>,
envFilePath?: string,
ignoreEnvFile?: boolean,
): DynamicModule {
return {
module: AppModule,
imports: [
ConfigModule.forRoot({
envFilePath,
ignoreEnvFile,
validate,
}),
],
};
}

static withForFeature(): DynamicModule {
return {
module: AppModule,
Expand Down

0 comments on commit b375b3f

Please sign in to comment.