Skip to content

Commit fa6ddb5

Browse files
committed
refactor: replace request param metadata from map to builder
1 parent 5dbffa3 commit fa6ddb5

11 files changed

+131
-74
lines changed

lib/builders/payload-builder.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { describe, test, expect } from "vitest";
2+
import { PayloadBuilder } from "./payload-builder";
3+
4+
describe("PayloadBuilder", () => {
5+
test("should return undefined when no metadata is provided", () => {
6+
// given
7+
const args = ["arg"];
8+
const urlBuilder = new PayloadBuilder(args);
9+
10+
// when
11+
const actual = urlBuilder.build();
12+
13+
// then
14+
expect(actual).toBeUndefined();
15+
});
16+
});

lib/utils/payload-builder.ts renamed to lib/builders/payload-builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { TupleArrayBuilder } from "./tuple-array-builder";
1+
import { TupleArrayBuilder } from "./tuple-array.builder";
22
import {
33
type RequestBodyMetadata,
44
type RequestFormMetadata,

lib/builders/request-param.builder.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { TupleArrayBuilder } from "./tuple-array.builder";
2+
3+
export class RequestParamBuilder {
4+
metadata: Array<[index: number, key: string | undefined]> = [];
5+
6+
constructor(index: number, key?: string) {
7+
this.metadata.push([index, key]);
8+
}
9+
10+
get length(): number {
11+
return this.metadata.length;
12+
}
13+
14+
add(index: number, key?: string): void {
15+
this.metadata.push([index, key]);
16+
}
17+
18+
build(args: any[]): string {
19+
if (this.metadata.length === 0) {
20+
return "";
21+
}
22+
23+
const searchParams = new URLSearchParams();
24+
this.metadata.forEach(([index, key]) => {
25+
if (key != null) {
26+
searchParams.set(key, args[index]);
27+
return;
28+
}
29+
30+
TupleArrayBuilder.of<string, unknown>(args[index]).forEach(
31+
([key, value]) => {
32+
searchParams.set(key, `${value?.toString() ?? ""}`);
33+
}
34+
);
35+
});
36+
37+
return "?" + searchParams.toString();
38+
}
39+
}

lib/utils/tuple-array-builder.ts renamed to lib/builders/tuple-array.builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export class TupleArrayBuilder {
22
static of<A, B>(value: unknown): Array<[A, B]> {
3-
if (typeof value === "undefined") {
3+
if (value === undefined) {
44
return [];
55
}
66

lib/utils/url-builder.spec.ts renamed to lib/builders/url.builder.spec.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
import { describe, test, expect } from "vitest";
2-
import { URLBuilder } from "./url-builder";
3-
import {
4-
type PathVariableMetadata,
5-
type RequestParamMetadata,
6-
} from "../decorators";
2+
import { RequestParamBuilder } from "./request-param.builder";
3+
import { UrlBuilder } from "./url.builder";
4+
import { type PathVariableMetadata } from "../decorators";
75
import { MetadataMap } from "../types/metadata-map";
86

9-
describe("URLBuilder", () => {
7+
describe("UrlBuilder", () => {
108
test("should build with base url", () => {
119
// given
1210
const host = "https://example.com";
1311
const path = "//api/1";
1412
const args = [1];
15-
const urlBuilder = new URLBuilder(host, path, args);
13+
const urlBuilder = new UrlBuilder(host, path, args);
1614

1715
// when
1816
const actual = urlBuilder.build();
@@ -27,7 +25,7 @@ describe("URLBuilder", () => {
2725
const path = "api/users/{id}";
2826
const args = [1, 2];
2927
const pathParam: PathVariableMetadata = new MetadataMap([[1, "id"]]);
30-
const urlBuilder = new URLBuilder(host, path, args, { pathParam });
28+
const urlBuilder = new UrlBuilder(host, path, args, { pathParam });
3129

3230
// when
3331
const actual = urlBuilder.build();
@@ -41,8 +39,8 @@ describe("URLBuilder", () => {
4139
const host = "https://example.com";
4240
const path = "";
4341
const args = ["search"];
44-
const queryParam: RequestParamMetadata = new MetadataMap([[0, "keyword"]]);
45-
const urlBuilder = new URLBuilder(host, path, args, { queryParam });
42+
const queryParam = new RequestParamBuilder(0, "keyword");
43+
const urlBuilder = new UrlBuilder(host, path, args, { queryParam });
4644

4745
// when
4846
const actual = urlBuilder.build();
@@ -56,8 +54,8 @@ describe("URLBuilder", () => {
5654
const host = "https://example.com";
5755
const path = "api/user";
5856
const args = [{ keyword: "search" }];
59-
const queryParam: RequestParamMetadata = new MetadataMap([[0, undefined]]);
60-
const urlBuilder = new URLBuilder(host, path, args, { queryParam });
57+
const queryParam = new RequestParamBuilder(0, undefined);
58+
const urlBuilder = new UrlBuilder(host, path, args, { queryParam });
6159

6260
// when
6361
const actual = urlBuilder.build();

lib/utils/url-builder.ts renamed to lib/builders/url.builder.ts

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
1-
import { TupleArrayBuilder } from "./tuple-array-builder";
2-
import {
3-
type PathVariableMetadata,
4-
type RequestParamMetadata,
5-
} from "../decorators";
1+
import { type RequestParamBuilder } from "./request-param.builder";
2+
import { type PathVariableMetadata } from "../decorators";
63

7-
export class URLBuilder {
4+
export class UrlBuilder {
85
#pathParams: Array<[number, string]> = [];
9-
#queryParams: Array<[number, string | undefined]> = [];
6+
#requestParamBuilder: RequestParamBuilder | undefined;
107

118
constructor(
129
private readonly host: string,
1310
private readonly path: string,
1411
private readonly args: any[],
1512
metadata: {
1613
pathParam?: PathVariableMetadata;
17-
queryParam?: RequestParamMetadata;
14+
queryParam?: RequestParamBuilder;
1815
} = {}
1916
) {
2017
if (this.host.length === 0) {
2118
this.host = this.path;
2219
this.path = "";
2320
}
2421
this.#pathParams = metadata.pathParam?.toArray() ?? [];
25-
this.#queryParams = metadata.queryParam?.toArray() ?? [];
22+
this.#requestParamBuilder = metadata.queryParam;
2623
}
2724

2825
build(): string {
29-
return this.replacePathVariable() + this.appendQueryParams();
26+
return (
27+
this.replacePathVariable() +
28+
(this.#requestParamBuilder?.build(this.args) ?? "")
29+
);
3030
}
3131

3232
private replacePathVariable(): string {
@@ -37,28 +37,6 @@ export class URLBuilder {
3737
);
3838
}
3939

40-
private appendQueryParams(): string {
41-
if (this.#queryParams.length === 0) {
42-
return "";
43-
}
44-
45-
const searchParams = new URLSearchParams();
46-
this.#queryParams.forEach(([paramIndex, queryParamKey]) => {
47-
if (typeof queryParamKey !== "undefined") {
48-
searchParams.set(queryParamKey, this.args[paramIndex]);
49-
return;
50-
}
51-
52-
TupleArrayBuilder.of<string, unknown>(this.args[paramIndex]).forEach(
53-
([key, value]) => {
54-
searchParams.set(key, `${value?.toString() ?? ""}`);
55-
}
56-
);
57-
});
58-
59-
return "?" + searchParams.toString();
60-
}
61-
6240
get url(): string {
6341
if (this.path === "") {
6442
return this.host;

lib/decorators/http-interface.decorator.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe("HttpInterface", () => {
99
class TestService {}
1010

1111
// when
12-
const result = Reflect.getMetadata(
12+
const result: string = Reflect.getMetadata(
1313
HTTP_INTERFACE_METADATA,
1414
TestService.prototype
1515
);
@@ -24,7 +24,7 @@ describe("HttpInterface", () => {
2424
class TestService {}
2525

2626
// when
27-
const result = Reflect.getMetadata(
27+
const result: string = Reflect.getMetadata(
2828
HTTP_INTERFACE_METADATA,
2929
TestService.prototype
3030
);
Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { describe, test, expect } from "vitest";
22
import { REQUEST_PARAM_METADATA } from "./constants";
3-
import {
4-
RequestParam,
5-
type RequestParamMetadata,
6-
} from "./request-param.decorator";
3+
import { RequestParam } from "./request-param.decorator";
4+
import { type RequestParamBuilder } from "../builders/request-param.builder";
75

86
describe("RequestParam", () => {
97
test("should set request param metadata with empty key", () => {
@@ -15,34 +13,59 @@ describe("RequestParam", () => {
1513
}
1614

1715
// when
18-
const result: RequestParamMetadata = Reflect.getMetadata(
16+
const result: RequestParamBuilder = Reflect.getMetadata(
1917
REQUEST_PARAM_METADATA,
2018
TestService.prototype,
2119
"request"
2220
);
2321

2422
// then
25-
expect(result).toHaveLength(1);
26-
expect(result.get(0)).toBeUndefined();
23+
expect(result.metadata).toHaveLength(1);
24+
expect(result.metadata[0]).toEqual([0, undefined]);
2725
});
2826

2927
test("should set request param metadata with key", () => {
3028
// given
3129
class TestService {
32-
request(@RequestParam("foo") bar: string): string {
30+
request(_foo: string, @RequestParam("bar") bar: string): string {
3331
return bar;
3432
}
3533
}
3634

3735
// when
38-
const result: RequestParamMetadata = Reflect.getMetadata(
36+
const result: RequestParamBuilder = Reflect.getMetadata(
3937
REQUEST_PARAM_METADATA,
4038
TestService.prototype,
4139
"request"
4240
);
4341

4442
// then
45-
expect(result).toHaveLength(1);
46-
expect(result.get(0)).toBe("foo");
43+
expect(result.metadata).toHaveLength(1);
44+
expect(result.metadata[0]).toEqual([1, "bar"]);
45+
});
46+
47+
test("should set request param metadata with multiple decorator", () => {
48+
// given
49+
class TestService {
50+
request(
51+
@RequestParam("foo") foo: string,
52+
@RequestParam() bar: { bar: string }
53+
): string {
54+
return foo;
55+
}
56+
}
57+
58+
// when
59+
const result: RequestParamBuilder = Reflect.getMetadata(
60+
REQUEST_PARAM_METADATA,
61+
TestService.prototype,
62+
"request"
63+
);
64+
65+
// then
66+
expect(result.metadata).toEqual([
67+
[1, undefined],
68+
[0, "foo"],
69+
]);
4770
});
4871
});

lib/decorators/request-param.decorator.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
import { REQUEST_PARAM_METADATA } from "./constants";
2-
import { MetadataMap } from "../types/metadata-map";
3-
4-
export type RequestParamMetadata = MetadataMap<number, string | undefined>;
2+
import { RequestParamBuilder } from "../builders/request-param.builder";
53

64
export function RequestParam(key?: string): ParameterDecorator {
75
return (target, propertyKey, parameterIndex) => {
8-
if (typeof propertyKey === "undefined") {
6+
if (propertyKey == null) {
97
return;
108
}
119

12-
const metadata =
13-
Reflect.getMetadata(REQUEST_PARAM_METADATA, target, propertyKey) ??
14-
new MetadataMap();
10+
const metadata: RequestParamBuilder | undefined = Reflect.getMetadata(
11+
REQUEST_PARAM_METADATA,
12+
target,
13+
propertyKey
14+
);
1515

16-
metadata.set(parameterIndex, key);
16+
if (metadata != null) {
17+
metadata.add(parameterIndex, key);
18+
return;
19+
}
1720

1821
Reflect.defineMetadata(
1922
REQUEST_PARAM_METADATA,
20-
metadata,
23+
new RequestParamBuilder(parameterIndex, key),
2124
target,
2225
propertyKey
2326
);

lib/node-fetch.injector.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
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 { type RequestParamBuilder } from "./builders/request-param.builder";
5+
import { TupleArrayBuilder } from "./builders/tuple-array.builder";
6+
import { UrlBuilder } from "./builders/url.builder";
47
import {
58
HTTP_EXCHANGE_METADATA,
69
HTTP_INTERFACE_METADATA,
@@ -10,11 +13,8 @@ import {
1013
REQUEST_BODY_METADATA,
1114
REQUEST_PARAM_METADATA,
1215
type RequestBodyMetadata,
13-
type RequestParamMetadata,
1416
} from "./decorators";
1517
import { HttpClient } from "./types/http-client.interface";
16-
import { TupleArrayBuilder } from "./utils/tuple-array-builder";
17-
import { URLBuilder } from "./utils/url-builder";
1818

1919
@Injectable()
2020
export class NodeFetchInjector implements OnModuleInit {
@@ -53,7 +53,7 @@ export class NodeFetchInjector implements OnModuleInit {
5353
const pathMetadata = getMetadata<PathVariableMetadata>(
5454
PATH_VARIABLE_METADATA
5555
);
56-
const requestParamMetadata = getMetadata<RequestParamMetadata>(
56+
const requestParamMetadata = getMetadata<RequestParamBuilder>(
5757
REQUEST_PARAM_METADATA
5858
);
5959
const requestBodyMetadata = getMetadata<RequestBodyMetadata>(
@@ -84,7 +84,7 @@ export class NodeFetchInjector implements OnModuleInit {
8484
{}
8585
);
8686

87-
const urlBuilder = new URLBuilder(
87+
const urlBuilder = new UrlBuilder(
8888
baseUrl,
8989
httpExchangeMetadata.url,
9090
args,

0 commit comments

Comments
 (0)