Skip to content

Commit 9525e80

Browse files
committed
refactor: apply configuration class
1 parent 44e6174 commit 9525e80

7 files changed

+167
-25
lines changed

lib/http-interface.module.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { GetExchange, HttpInterface } from './decorators';
44
import { StubHttpClient } from './fixtures/stub-http-client';
55
import { HttpInterfaceModule } from './http-interface.module';
66
import { imitation } from './supports';
7+
import { type Configuration } from './supports/configuration';
78
import { FetchHttpClient } from './supports/fetch-http-client';
89
import { NodeFetchInjector } from './supports/node-fetch.injector';
9-
import { type HttpClient } from './types';
1010

1111
describe('HttpInterfaceModule', () => {
1212
@HttpInterface('http://localhost:3000')
@@ -26,10 +26,12 @@ describe('HttpInterfaceModule', () => {
2626
const app = module.createNestApplication();
2727

2828
// when
29-
const injector = app.get<{ httpClient: HttpClient }>(NodeFetchInjector);
29+
const injector = app.get<{ configuration: Configuration }>(
30+
NodeFetchInjector,
31+
);
3032

3133
// then
32-
expect(injector.httpClient).toBeInstanceOf(FetchHttpClient);
34+
expect(injector.configuration.httpClient).toBeInstanceOf(FetchHttpClient);
3335
});
3436

3537
test('should request with given client', async () => {

lib/http-interface.module.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import {
44
DiscoveryService,
55
MetadataScanner,
66
} from '@nestjs/core';
7-
import { FetchHttpClient } from './supports/fetch-http-client';
7+
import { Configuration } from './supports/configuration';
88
import { NodeFetchInjector } from './supports/node-fetch.injector';
99
import { type HttpInterfaceConfig } from './types';
1010

1111
export class HttpInterfaceModule {
12-
static forRoot(config: HttpInterfaceConfig = {}): DynamicModule {
12+
static forRoot(config?: HttpInterfaceConfig): DynamicModule {
1313
return {
1414
global: true,
1515
imports: [DiscoveryModule],
@@ -21,16 +21,12 @@ export class HttpInterfaceModule {
2121
useFactory: (
2222
metadataScanner: MetadataScanner,
2323
discoveryService: DiscoveryService,
24-
) => {
25-
const timeout = config.timeout ?? 5000;
26-
27-
return new NodeFetchInjector(
24+
) =>
25+
new NodeFetchInjector(
2826
metadataScanner,
2927
discoveryService,
30-
config.httpClient ?? new FetchHttpClient(timeout),
31-
config,
32-
);
33-
},
28+
new Configuration(config),
29+
),
3430
},
3531
],
3632
};

lib/supports/configuration.spec.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { type ClassTransformOptions } from 'class-transformer';
2+
import type CircuitBreaker from 'opossum';
3+
import { describe, expect, test } from 'vitest';
4+
import { Configuration } from './configuration';
5+
import { StubHttpClient } from '../fixtures/stub-http-client';
6+
7+
describe('Configuration', () => {
8+
describe('default values', () => {
9+
const configuration = new Configuration();
10+
11+
test('timeout', () => {
12+
// when
13+
const timeout = configuration.timeout;
14+
15+
// then
16+
expect(timeout).toBe(Configuration.DEFAULT_TIMEOUT);
17+
});
18+
19+
test('http client', () => {
20+
// given
21+
const configuration = new Configuration();
22+
23+
// when
24+
const httpClient = configuration.httpClient;
25+
26+
// then
27+
expect(httpClient).toBeInstanceOf(Configuration.DEFAULT_HTTP_CLIENT);
28+
});
29+
30+
test('transform option', () => {
31+
// when
32+
const transformOption = configuration.transformOption;
33+
34+
// then
35+
expect(transformOption).toBeUndefined();
36+
});
37+
38+
test('circuit breaker option', () => {
39+
// when
40+
const circuitBreakerOption = configuration.circuitBreakerOption;
41+
42+
// then
43+
expect(circuitBreakerOption).toBeUndefined();
44+
});
45+
});
46+
47+
describe('custom values', () => {
48+
test('timeout', () => {
49+
// given
50+
const timeout = 1000;
51+
52+
// when
53+
const configuration = new Configuration({ timeout });
54+
55+
// then
56+
expect(configuration.timeout).toBe(timeout);
57+
});
58+
59+
test('http client', () => {
60+
// given
61+
const httpClient = new StubHttpClient();
62+
63+
// when
64+
const configuration = new Configuration({ httpClient });
65+
66+
// then
67+
expect(configuration.httpClient).toBe(httpClient);
68+
});
69+
70+
test('transform option', () => {
71+
// given
72+
const transformOption: ClassTransformOptions = {
73+
enableImplicitConversion: true,
74+
};
75+
76+
// when
77+
const configuration = new Configuration({ transformOption });
78+
79+
// then
80+
expect(configuration.transformOption).toBe(transformOption);
81+
});
82+
83+
test('circuit breaker option', () => {
84+
// given
85+
const circuitBreakerOption: CircuitBreaker.Options = {
86+
errorThresholdPercentage: 50,
87+
resetTimeout: 1000,
88+
};
89+
const timeout = 10000;
90+
91+
// when
92+
const configuration = new Configuration({
93+
circuitBreakerOption,
94+
timeout,
95+
});
96+
97+
// then
98+
expect(configuration.circuitBreakerOption).toMatchInlineSnapshot(`
99+
{
100+
"errorThresholdPercentage": 50,
101+
"resetTimeout": 1000,
102+
"timeout": 10000,
103+
}
104+
`);
105+
});
106+
});
107+
});

lib/supports/configuration.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { type ClassTransformOptions } from 'class-transformer';
2+
import type CircuitBreaker from 'opossum';
3+
import { FetchHttpClient } from './fetch-http-client';
4+
import { type HttpClient, type HttpInterfaceConfig } from '../types';
5+
6+
export class Configuration {
7+
static DEFAULT_TIMEOUT = 5000;
8+
static DEFAULT_HTTP_CLIENT = FetchHttpClient;
9+
10+
constructor(private readonly config: HttpInterfaceConfig = {}) {}
11+
12+
get timeout(): number {
13+
return this.config.timeout ?? Configuration.DEFAULT_TIMEOUT;
14+
}
15+
16+
get httpClient(): HttpClient {
17+
return (
18+
this.config.httpClient ??
19+
new Configuration.DEFAULT_HTTP_CLIENT(this.timeout)
20+
);
21+
}
22+
23+
get transformOption(): ClassTransformOptions | undefined {
24+
return this.config.transformOption;
25+
}
26+
27+
get circuitBreakerOption(): CircuitBreaker.Options | undefined {
28+
if (this.config.circuitBreakerOption == null) {
29+
return undefined;
30+
}
31+
32+
return {
33+
...this.config.circuitBreakerOption,
34+
timeout: this.timeout,
35+
};
36+
}
37+
}

lib/supports/node-fetch.injector.spec.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { MetadataScanner } from '@nestjs/core';
22
import { beforeEach, describe, test, expect } from 'vitest';
3+
import { Configuration } from './configuration';
34
import { imitation } from './imitation';
45
import { NodeFetchInjector } from './node-fetch.injector';
56
import {
@@ -30,7 +31,7 @@ describe('NodeFetchInjector', () => {
3031
const nodeFetchInjector = new NodeFetchInjector(
3132
metadataScanner,
3233
discoveryService,
33-
httpClient,
34+
new Configuration({ httpClient }),
3435
);
3536

3637
beforeEach(() => {
@@ -525,8 +526,10 @@ describe('NodeFetchInjector', () => {
525526
const nodeFetchInjector = new NodeFetchInjector(
526527
metadataScanner,
527528
discoveryService,
528-
httpClient,
529-
{ transformOption: { strategy: 'excludeAll' } },
529+
new Configuration({
530+
httpClient,
531+
transformOption: { strategy: 'excludeAll' },
532+
}),
530533
);
531534
class ResponseTest {
532535
constructor(readonly status: string) {}

lib/supports/node-fetch.injector.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Injectable, type OnModuleInit } from '@nestjs/common';
22
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
33
import { type InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
4+
import { Configuration } from './configuration';
45
import { CircuitBreakerBuilder } from '../builders/circuit-breaker.builder';
56
import { type HttpRequestBuilder } from '../builders/http-request.builder';
67
import { type ResponseBodyBuilder } from '../builders/response-body.builder';
@@ -10,15 +11,13 @@ import {
1011
HTTP_INTERFACE_METADATA,
1112
RESPONSE_BODY_METADATA,
1213
} from '../decorators';
13-
import { HttpClient, HttpInterfaceConfig } from '../types';
1414

1515
@Injectable()
1616
export class NodeFetchInjector implements OnModuleInit {
1717
constructor(
1818
private readonly metadataScanner: MetadataScanner,
1919
private readonly discoveryService: DiscoveryService,
20-
private readonly httpClient: HttpClient,
21-
private readonly httpInterfaceConfig?: HttpInterfaceConfig,
20+
private readonly configuration: Configuration,
2221
) {}
2322

2423
onModuleInit(): void {
@@ -47,25 +46,23 @@ export class NodeFetchInjector implements OnModuleInit {
4746

4847
const circuitBreaker: CircuitBreakerBuilder =
4948
Reflect.getMetadata(CIRCUIT_BREAKER_METADATA, prototype, methodName) ??
50-
new CircuitBreakerBuilder(
51-
this.httpInterfaceConfig?.circuitBreakerOption,
52-
);
49+
new CircuitBreakerBuilder(this.configuration.circuitBreakerOption);
5350
const responseBodyBuilder: ResponseBodyBuilder<unknown> | undefined =
5451
Reflect.getMetadata(RESPONSE_BODY_METADATA, prototype, methodName);
5552

5653
httpRequestBuilder.setBaseUrl(baseUrl);
5754

5855
wrapper.instance[methodName] = circuitBreaker.build(
5956
async (...args: never[]) =>
60-
await this.httpClient
57+
await this.configuration.httpClient
6158
.request(httpRequestBuilder.build(args), httpRequestBuilder.options)
6259
.then(async (response) => {
6360
if (responseBodyBuilder != null) {
6461
const res = await response.json();
6562

6663
return responseBodyBuilder.build(
6764
res,
68-
this.httpInterfaceConfig?.transformOption,
65+
this.configuration.transformOption,
6966
);
7067
}
7168

lib/types/http-interface-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ export interface HttpInterfaceConfig {
66
timeout?: number;
77
httpClient?: HttpClient;
88
transformOption?: ClassTransformOptions;
9-
circuitBreakerOption?: CircuitBreaker.Options;
9+
circuitBreakerOption?: Omit<CircuitBreaker.Options, 'timeout'>;
1010
}

0 commit comments

Comments
 (0)