Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/nestjs/config; branch 'fe…
Browse files Browse the repository at this point in the history
…at/validation-add-support-for-class-validator' of https://github.com/LeoAnesi/config into LeoAnesi-feat/validation-add-support-for-class-validator
  • Loading branch information
kamilmysliwiec committed Nov 17, 2020
2 parents 3d521ce + b375b3f commit 86c9042
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 2 deletions.
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
12 changes: 11 additions & 1 deletion lib/interfaces/config-module-options.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,24 @@ 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).
*/
validationSchema?: any;

/**
* Schema validation options.
* See: https://hapi.dev/family/joi/?v=16.1.8#anyvalidatevalue-options
* See: https://joi.dev/api/?v=17.3.0#anyvalidatevalue-options
*/
validationOptions?: Record<string, any>;

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 86c9042

Please sign in to comment.