Skip to content

Commit

Permalink
chore: added support for multiple config files
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashish Kumar authored and Ashish Kumar committed Jun 17, 2024
1 parent 8329148 commit 9022b5b
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 50 deletions.
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ First, import the EnvironmentConfigModule in your application's module and call
import { Module } from '@nestjs/common';
import { EnvironmentConfigModule } from '@nodeflip/nest-env-config';
import { AppConfig } from './app-config';
import { DBConfig } from './db-config';

@Module({
imports: [EnvironmentConfigModule.forRoot(AppConfig)],
imports: [
EnvironmentConfigModule.forRoot({configClass: AppConfig}),
EnvironmentConfigModule.forRoot({configClass: DBConfig, serviceName: 'DB_CONFIG_SERVICE'})
],
})
export class AppModule {}
```
Expand Down Expand Up @@ -50,15 +54,20 @@ Inject the EnvironmentConfigService into your services to access the configurati
import { Injectable } from '@nestjs/common';
import { EnvironmentConfigService } from '@nodeflip/nest-env-config';
import { AppConfig } from './app-config';
import { DBConfig } from './db-config';

@Injectable()
export class AppService {
constructor(private readonly configService: EnvironmentConfigService<AppConfig>) {
console.log(this.configService.config);
constructor(
private readonly appConfigService: EnvironmentConfigService<AppConfig>,
@Inject('DB_CONFIG_SERVICE') private readonly dbConfigService: EnvironmentConfigService<DBConfig>
) {
console.log(this.appConfigService.config);
console.log(this.dbConfigService.config);
}

getPort(): number {
return this.configService.config.port;
return this.dbConfigService.config.port;
}
}
```
Expand Down Expand Up @@ -102,7 +111,7 @@ describe('EnvironmentConfigService', () => {

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [EnvironmentConfigModule.forRoot(DBConfig)],
imports: [EnvironmentConfigModule.forRoot({configClass: DBConfig})],
}).compile();

service = module.get<EnvironmentConfigService<DBConfig>>(EnvironmentConfigService);
Expand Down
55 changes: 42 additions & 13 deletions src/environment-config/environment-config.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,54 @@ import { ConfigModule, ConfigService } from "@nestjs/config";
import { EnvironmentConfigService } from "./environment-config.service";
import { PropClassType } from "./prop.decorator";

export interface EnvironmentConfigModuleOptions<T> {
serviceName?: string;
configClass: T;
}

@Module({})
export class EnvironmentConfigModule {
static forRoot<T>(configClass: T): DynamicModule {
const environmentConfigServiceProvider: Provider = {
provide: EnvironmentConfigService,
useFactory: (configService: ConfigService) => {
return new EnvironmentConfigService<T>(
configService,
configClass as PropClassType<T>
);
},
inject: [ConfigService],
};
static forRoot<T>(
options:
| EnvironmentConfigModuleOptions<T>
| EnvironmentConfigModuleOptions<T>[]
): DynamicModule {
const providers: Provider[] = [];
if (Array.isArray(options)) {
options.forEach((option) => {
providers.push({
provide: option.serviceName
? option.serviceName
: EnvironmentConfigService,
useFactory: (configService: ConfigService) => {
return new EnvironmentConfigService<T>(
configService,
option.configClass as PropClassType<T>
);
},
inject: [ConfigService],
});
});
} else {
providers.push({
provide: options.serviceName
? options.serviceName
: EnvironmentConfigService,
useFactory: (configService: ConfigService) => {
return new EnvironmentConfigService<T>(
configService,
options.configClass as PropClassType<T>
);
},
inject: [ConfigService],
});
}

return {
module: EnvironmentConfigModule,
imports: [ConfigModule.forRoot({ isGlobal: true })],
providers: [environmentConfigServiceProvider, ConfigService],
exports: [EnvironmentConfigService],
providers: [...providers, ConfigService],
exports: [...providers, ConfigService],
};
}
}
37 changes: 12 additions & 25 deletions src/environment-config/environment-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,26 @@ import { ConfigService } from "@nestjs/config";

import { PropClassType } from "./prop.decorator";

/**
* A service that provides configuration settings by extracting them from
* the environment variables and mapping them to a specified configuration class.
*
* @template T - The type of the configuration class.
*/
@Injectable()
export class EnvironmentConfigService<T> {
/**
* The configuration object containing the mapped environment variables.
*/
public readonly config: T;

/**
* Creates an instance of EnvironmentConfigService.
*
* @param {ConfigService} configService - The ConfigService instance to access environment variables.
* @param {PropClassType<T>} configClass - The configuration class type that defines the properties and their default values.
*/
constructor(
private readonly configService: ConfigService,
configClass: PropClassType<T>
configClass: PropClassType<T> | PropClassType<T>[]
) {
this.config = this.getConfigObject(configClass);
if (Array.isArray(configClass)) {
this.config = configClass.reduce((acc, cls) => {
acc = {
...acc,
...this.getConfigObject(cls),
};
return acc;
}, {} as T);
} else {
this.config = this.getConfigObject(configClass);
}
}

/**
* Maps the environment variables to the configuration class properties.
*
* @private
* @param {T} configClass - The configuration class instance.
* @returns {T} The configuration object with properties populated from environment variables or their default values.
*/
private getConfigObject(configClass: T): T {
const configObject: any = {};
const cls = configClass as PropClassType<T>;
Expand Down
36 changes: 29 additions & 7 deletions src/test/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,40 @@ import { Test, TestingModule } from "@nestjs/testing";
import { EnvironmentConfigModule } from "../environment-config/environment-config.module";
import { EnvironmentConfigService } from "../environment-config/environment-config.service";
import { DBConfig } from "./db-config";
import { RabitMqConfig } from "./mq.config";

describe("EnvironmentConfigService", () => {
let service: EnvironmentConfigService<DBConfig>;
let dbservice: EnvironmentConfigService<DBConfig>;
let mqservice: EnvironmentConfigService<RabitMqConfig>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [EnvironmentConfigModule.forRoot(DBConfig)],
imports: [
EnvironmentConfigModule.forRoot({
configClass: DBConfig,
serviceName: "DBConfigService",
}),
EnvironmentConfigModule.forRoot({ configClass: RabitMqConfig }),
],
}).compile();

service = module.get<EnvironmentConfigService<DBConfig>>(
dbservice =
module.get<EnvironmentConfigService<DBConfig>>("DBConfigService");
mqservice = module.get<EnvironmentConfigService<RabitMqConfig>>(
EnvironmentConfigService
);
});

it("should be defined", () => {
expect(service).toBeDefined();
it("dbservice should be defined", () => {
expect(dbservice).toBeDefined();
});

it("should retrieve configuration properties", () => {
const config = service.config;
it("mqservice should be defined", () => {
expect(mqservice).toBeDefined();
});

it("should retrieve configuration properties from dbservice", () => {
const config = dbservice.config;
expect(config).toBeDefined();
expect(config.name).toBe("test_db");
expect(config.username).toBe("test_user");
Expand All @@ -31,4 +45,12 @@ describe("EnvironmentConfigService", () => {
expect(config.port).toBe(5432);
expect(config.logging).toBe(false);
});

it("should retrieve configuration properties from mqservice", () => {
const config = mqservice.config;
expect(config).toBeDefined();
expect(config.uri).toBe("amqp://test:test@localhost:5672");
expect(config.exchangeName).toBe("test_exchange");
expect(config.queueExchange).toBe("test_queue_exchange");
});
});
18 changes: 18 additions & 0 deletions src/test/mq.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Prop } from "../environment-config/prop.decorator";

export class RabitMqConfig {
@Prop("uri", "amqp://test:test@localhost:5672")
uri: string;

@Prop("exchange", "test_exchange")
exchangeName: string;

@Prop("queueExchangeName", "test_queue_exchange")
queueExchange?: string;

@Prop("queueName", "test_queue")
queueName?: string;

@Prop("queueRoutingKey", "test_routing_key")
queueRoutingKey?: string;
}

0 comments on commit 9022b5b

Please sign in to comment.