-
Notifications
You must be signed in to change notification settings - Fork 0
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
BC-8092 - implement config validation #21
Changes from 5 commits
f7081a6
5fb4178
5f355b8
173fbd7
fa3f52a
d003566
7df8094
f400d33
aa65fc6
ea88874
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
export interface XApiKeyConfig { | ||
ADMIN_API__ALLOWED_API_KEYS: string[]; | ||
import { Transform } from 'class-transformer'; | ||
import { IsArray } from 'class-validator'; | ||
|
||
export class XApiKeyConfig { | ||
@Transform(({ value }) => value.split(',').map((part: string) => (part.split(':').pop() ?? '').trim())) | ||
@IsArray() | ||
public ADMIN_API__ALLOWED_API_KEYS = ['randomString']; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sollte der default Wert wirklich bei der Anforderung festgelegt werden? Ich finde das der bessere Ort die Validierung, bzw. das lesen/zuweisen ist. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isDefined() ? |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,4 @@ | ||
import { Injectable, UnauthorizedException } from '@nestjs/common'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { PassportStrategy } from '@nestjs/passport'; | ||
import { Strategy } from 'passport-headerapikey/lib/Strategy.js'; | ||
import { XApiKeyConfig } from '../config/index.js'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. index ist hier unnötig. |
||
|
@@ -9,9 +8,9 @@ import { StrategyType } from '../interface/index.js'; | |
export class XApiKeyStrategy extends PassportStrategy(Strategy, StrategyType.API_KEY) { | ||
private readonly allowedApiKeys: string[]; | ||
|
||
public constructor(private readonly configService: ConfigService<XApiKeyConfig, true>) { | ||
public constructor(private readonly config: XApiKeyConfig) { | ||
super({ header: 'X-API-KEY' }, false); | ||
this.allowedApiKeys = this.configService.get<string[]>('ADMIN_API__ALLOWED_API_KEYS'); | ||
this.allowedApiKeys = this.config.ADMIN_API__ALLOWED_API_KEYS; | ||
} | ||
|
||
public validate(apiKey: string, done: (error: Error | null, data: boolean | null) => void): void { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { IsUrl } from 'class-validator'; | ||
|
||
export class AuthorizationConfig { | ||
@IsUrl({ require_tld: false }) | ||
public API_HOST = 'http://localhost:3030'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Die verschiedenen Werte für prod, test, local sollten aus einer anderen Quelle kommen. Den default als local zu nutzen finde ich nicht gut. Man könnte sicher argumentieren das test eh im test file definiert werden muss. Nur gibt es einzelne envs die immer wieder gesetzt werden müssten und einmalig definiert einfach praktischer sind. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { DynamicModule, Module } from '@nestjs/common'; | ||
import { ConfigModule } from '@nestjs/config'; | ||
import { Configuration } from './configuration.service.js'; | ||
|
||
@Module({}) | ||
export class ConfigurationModule { | ||
public static register<T extends object>(Constructor: new () => T): DynamicModule { | ||
return { | ||
imports: [ConfigModule.forRoot({ cache: true })], | ||
providers: [ | ||
Configuration, | ||
{ | ||
provide: Constructor, | ||
useFactory: (config: Configuration): T => config.getAllValidConfigsByType(Constructor), | ||
inject: [Configuration], | ||
}, | ||
], | ||
CeEv marked this conversation as resolved.
Show resolved
Hide resolved
|
||
exports: [Configuration, Constructor], | ||
module: ConfigurationModule, | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { createMock } from '@golevelup/ts-jest'; | ||
import { ConfigService } from '@nestjs/config'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { IsString } from 'class-validator'; | ||
import { Configuration } from './configuration.service.js'; | ||
|
||
class TestConfig { | ||
@IsString() | ||
public TEST_VALUE!: string; | ||
} | ||
|
||
describe(Configuration.name, () => { | ||
let module: TestingModule; | ||
let service: Configuration; | ||
let configService: ConfigService; | ||
|
||
beforeAll(async () => { | ||
module = await Test.createTestingModule({ | ||
providers: [ | ||
Configuration, | ||
{ | ||
provide: ConfigService, | ||
useValue: createMock<ConfigService>(), | ||
}, | ||
], | ||
}).compile(); | ||
|
||
service = module.get<Configuration>(Configuration); | ||
configService = module.get<ConfigService>(ConfigService); | ||
}); | ||
|
||
afterAll(async () => { | ||
await module.close(); | ||
}); | ||
|
||
afterEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
describe('getAllValidConfigsByType', () => { | ||
describe('when value is valid', () => { | ||
it('should return valid configs', () => { | ||
jest.spyOn(configService, 'get').mockReturnValueOnce('test'); | ||
|
||
const result = service.getAllValidConfigsByType(TestConfig); | ||
|
||
expect(result).toEqual({ TEST_VALUE: 'test' }); | ||
}); | ||
}); | ||
|
||
describe('when value is not valid', () => { | ||
it('should throw error', () => { | ||
jest.spyOn(configService, 'get').mockReturnValueOnce(123); | ||
|
||
expect(() => service.getAllValidConfigsByType(TestConfig)).toThrow(/isString/); | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
configuration und config sind sich zu ähnlich um ausdrücken zu können was für was ist.