Skip to content

Commit 59af838

Browse files
committed
refactor: separated form creation logic into a new builder
1 parent 5be6910 commit 59af838

File tree

7 files changed

+140
-43
lines changed

7 files changed

+140
-43
lines changed

lib/builders/http-request.builder.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import { type PathVariableBuilder } from "./path-variable.builder";
22
import { type RequestBodyBuilder } from "./request-body.builder";
3+
import { type RequestFormBuilder } from "./request-form.builder";
34
import { type RequestParamBuilder } from "./request-param.builder";
45
import { UrlBuilder } from "./url.builder";
56
import {
67
PATH_VARIABLE_METADATA,
78
REQUEST_BODY_METADATA,
9+
REQUEST_FORM_METADATA,
810
REQUEST_PARAM_METADATA,
911
} from "../decorators";
1012
import { type HttpMethod } from "../types/http-method";
1113

1214
export class HttpRequestBuilder {
1315
private baseUrl = "";
14-
private readonly pathVariableBuilder: PathVariableBuilder | undefined;
15-
private readonly requestParamBuilder: RequestParamBuilder | undefined;
16-
private readonly requestBodyBuilder: RequestBodyBuilder | undefined;
16+
private readonly pathVariableBuilder?: PathVariableBuilder;
17+
private readonly requestParamBuilder?: RequestParamBuilder;
18+
private readonly requestBodyBuilder?: RequestBodyBuilder;
19+
private readonly requestFormBuilder?: RequestFormBuilder;
1720

1821
constructor(
1922
readonly target: object,
@@ -24,18 +27,24 @@ export class HttpRequestBuilder {
2427
this.pathVariableBuilder = this.getMetadata(PATH_VARIABLE_METADATA);
2528
this.requestParamBuilder = this.getMetadata(REQUEST_PARAM_METADATA);
2629
this.requestBodyBuilder = this.getMetadata(REQUEST_BODY_METADATA);
30+
this.requestFormBuilder = this.getMetadata(REQUEST_FORM_METADATA);
2731
}
2832

2933
setBaseUrl(baseUrl: string): void {
3034
this.baseUrl = baseUrl;
3135
}
3236

3337
build(args: any[]): Request {
34-
const payload = this.requestBodyBuilder?.build(args);
35-
const urlBuilder = new UrlBuilder(this.baseUrl, this.url, args, {
36-
pathParam: this.pathVariableBuilder,
37-
queryParam: this.requestParamBuilder,
38-
});
38+
const payload =
39+
this.requestBodyBuilder?.build(args) ??
40+
this.requestFormBuilder?.build(args);
41+
const urlBuilder = new UrlBuilder(
42+
this.baseUrl,
43+
this.url,
44+
args,
45+
this.pathVariableBuilder,
46+
this.requestParamBuilder
47+
);
3948

4049
return new Request(urlBuilder.build(), {
4150
method: this.method,
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { describe, test, expect } from "vitest";
2+
import { RequestFormBuilder } from "./request-form.builder";
3+
4+
describe("RequestFormBuilder", () => {
5+
test("should build form data with explicit key", () => {
6+
// given
7+
const builder = new RequestFormBuilder(0, "keyword");
8+
const args = ["search"];
9+
10+
// when
11+
const actual = builder.build(args);
12+
13+
// then
14+
expect([...actual.entries()]).toEqual([["keyword", "search"]]);
15+
});
16+
17+
test("should build form data without key", () => {
18+
// given
19+
const builder = new RequestFormBuilder(1);
20+
const args = ["invalid", { foo: "bar" }];
21+
22+
// when
23+
const actual = builder.build(args);
24+
25+
// then
26+
expect([...actual.entries()]).toEqual([["foo", "bar"]]);
27+
});
28+
});

lib/builders/request-form.builder.ts

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

lib/builders/url.builder.spec.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ describe("UrlBuilder", () => {
2323
const host = "https://example.com";
2424
const path = "api/users/{id}";
2525
const args = [1, 2];
26-
const pathParam = new PathVariableBuilder(1, "id");
27-
const urlBuilder = new UrlBuilder(host, path, args, { pathParam });
26+
const pathBuilder = new PathVariableBuilder(1, "id");
27+
const urlBuilder = new UrlBuilder(host, path, args, pathBuilder);
2828

2929
// when
3030
const actual = urlBuilder.build();
@@ -38,8 +38,14 @@ describe("UrlBuilder", () => {
3838
const host = "https://example.com";
3939
const path = "";
4040
const args = ["search"];
41-
const queryParam = new RequestParamBuilder(0, "keyword");
42-
const urlBuilder = new UrlBuilder(host, path, args, { queryParam });
41+
const paramBuilder = new RequestParamBuilder(0, "keyword");
42+
const urlBuilder = new UrlBuilder(
43+
host,
44+
path,
45+
args,
46+
undefined,
47+
paramBuilder
48+
);
4349

4450
// when
4551
const actual = urlBuilder.build();
@@ -53,8 +59,14 @@ describe("UrlBuilder", () => {
5359
const host = "https://example.com";
5460
const path = "api/user";
5561
const args = [{ keyword: "search" }];
56-
const queryParam = new RequestParamBuilder(0, undefined);
57-
const urlBuilder = new UrlBuilder(host, path, args, { queryParam });
62+
const paramBuilder = new RequestParamBuilder(0, undefined);
63+
const urlBuilder = new UrlBuilder(
64+
host,
65+
path,
66+
args,
67+
undefined,
68+
paramBuilder
69+
);
5870

5971
// when
6072
const actual = urlBuilder.build();

lib/builders/url.builder.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,17 @@ import { type PathVariableBuilder } from "./path-variable.builder";
22
import { type RequestParamBuilder } from "./request-param.builder";
33

44
export class UrlBuilder {
5-
private readonly pathVariableBuilder: PathVariableBuilder | undefined;
6-
private readonly requestParamBuilder: RequestParamBuilder | undefined;
7-
85
constructor(
96
private readonly host: string,
107
private readonly path: string,
118
private readonly args: any[],
12-
metadata: {
13-
pathParam?: PathVariableBuilder;
14-
queryParam?: RequestParamBuilder;
15-
} = {}
9+
private readonly pathVariableBuilder?: PathVariableBuilder,
10+
private readonly requestParamBuilder?: RequestParamBuilder
1611
) {
17-
if (this.host.length === 0) {
12+
if (this.host === "") {
1813
this.host = this.path;
1914
this.path = "";
2015
}
21-
this.pathVariableBuilder = metadata.pathParam;
22-
this.requestParamBuilder = metadata.queryParam;
2316
}
2417

2518
build(): string {
Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { describe, test, expect } from "vitest";
22
import { REQUEST_FORM_METADATA } from "./constants";
3-
import {
4-
RequestForm,
5-
type RequestFormMetadata,
6-
} from "./request-form.decorator";
3+
import { RequestForm } from "./request-form.decorator";
4+
import { type RequestFormBuilder } from "../builders/request-form.builder";
5+
import { type RequestParamBuilder } from "../builders/request-param.builder";
76

87
describe("RequestForm", () => {
98
test("should set request form metadata with empty key", () => {
@@ -15,15 +14,14 @@ describe("RequestForm", () => {
1514
}
1615

1716
// when
18-
const result: RequestFormMetadata = Reflect.getMetadata(
17+
const result: RequestFormBuilder = Reflect.getMetadata(
1918
REQUEST_FORM_METADATA,
2019
TestService.prototype,
2120
"request"
2221
);
2322

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

2927
test("should set request form metadata with key", () => {
@@ -35,14 +33,38 @@ describe("RequestForm", () => {
3533
}
3634

3735
// when
38-
const result: RequestFormMetadata = Reflect.getMetadata(
36+
const result: RequestFormBuilder = Reflect.getMetadata(
3937
REQUEST_FORM_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).toEqual([[0, "foo"]]);
44+
});
45+
46+
test("should set request form metadata with multiple decorator", () => {
47+
// given
48+
class TestService {
49+
request(
50+
@RequestForm("foo") foo: string,
51+
@RequestForm() bar: { bar: string }
52+
): string {
53+
return foo;
54+
}
55+
}
56+
57+
// when
58+
const result: RequestParamBuilder = Reflect.getMetadata(
59+
REQUEST_FORM_METADATA,
60+
TestService.prototype,
61+
"request"
62+
);
63+
64+
// then
65+
expect(result.metadata).toEqual([
66+
[1, undefined],
67+
[0, "foo"],
68+
]);
4769
});
4870
});

lib/decorators/request-form.decorator.ts

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

64
export function RequestForm(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_FORM_METADATA, target, propertyKey) ??
14-
new MetadataMap();
10+
const builder: RequestFormBuilder | undefined = Reflect.getMetadata(
11+
REQUEST_FORM_METADATA,
12+
target,
13+
propertyKey
14+
);
1515

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

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

0 commit comments

Comments
 (0)