Skip to content

Commit b0221c8

Browse files
committed
refactor: separated headers creation logic into a new builder
1 parent 59af838 commit b0221c8

11 files changed

+151
-41
lines changed

lib/builders/http-request.builder.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { type PathVariableBuilder } from "./path-variable.builder";
22
import { type RequestBodyBuilder } from "./request-body.builder";
33
import { type RequestFormBuilder } from "./request-form.builder";
4+
import { type RequestHeaderBuilder } from "./request-header.builder";
45
import { type RequestParamBuilder } from "./request-param.builder";
56
import { UrlBuilder } from "./url.builder";
67
import {
78
PATH_VARIABLE_METADATA,
89
REQUEST_BODY_METADATA,
910
REQUEST_FORM_METADATA,
11+
REQUEST_HEADER_METADATA,
1012
REQUEST_PARAM_METADATA,
1113
} from "../decorators";
1214
import { type HttpMethod } from "../types/http-method";
@@ -17,6 +19,7 @@ export class HttpRequestBuilder {
1719
private readonly requestParamBuilder?: RequestParamBuilder;
1820
private readonly requestBodyBuilder?: RequestBodyBuilder;
1921
private readonly requestFormBuilder?: RequestFormBuilder;
22+
private readonly requestHeaderBuilder?: RequestHeaderBuilder;
2023

2124
constructor(
2225
readonly target: object,
@@ -28,6 +31,7 @@ export class HttpRequestBuilder {
2831
this.requestParamBuilder = this.getMetadata(REQUEST_PARAM_METADATA);
2932
this.requestBodyBuilder = this.getMetadata(REQUEST_BODY_METADATA);
3033
this.requestFormBuilder = this.getMetadata(REQUEST_FORM_METADATA);
34+
this.requestHeaderBuilder = this.getMetadata(REQUEST_HEADER_METADATA);
3135
}
3236

3337
setBaseUrl(baseUrl: string): void {
@@ -45,13 +49,11 @@ export class HttpRequestBuilder {
4549
this.pathVariableBuilder,
4650
this.requestParamBuilder
4751
);
52+
const headers = this.requestHeaderBuilder?.build(args);
4853

4954
return new Request(urlBuilder.build(), {
5055
method: this.method,
51-
headers:
52-
payload !== undefined
53-
? { "Content-Type": "application/json" }
54-
: undefined,
56+
headers,
5557
body: payload,
5658
});
5759
}

lib/builders/path-variable.builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class PathVariableBuilder {
1212
build(url: string, args: any[]): string {
1313
return this.metadata.reduce<string>(
1414
(acc, [index, key]) =>
15-
acc.replace(new RegExp(`{${key}}`, "g"), args[index]),
15+
acc.replace(new RegExp(`{${key}}`, "g"), String(args[index])),
1616
url
1717
);
1818
}

lib/builders/request-form.builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ export class RequestFormBuilder {
1616

1717
this.metadata.forEach(([index, key]) => {
1818
if (key != null) {
19-
form.append(key, args[index]);
19+
form.append(key, String(args[index]));
2020
return;
2121
}
2222

2323
TupleArrayBuilder.of<string, any>(args[index]).forEach(([key, value]) => {
24-
form.append(key, value);
24+
form.append(key, String(value));
2525
});
2626
});
2727

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 { RequestHeaderBuilder } from "./request-header.builder";
3+
4+
describe("RequestHeaderBuilder", () => {
5+
test("should build header with explicit key", () => {
6+
// given
7+
const builder = new RequestHeaderBuilder(0, "keyword");
8+
const args = ["search"];
9+
10+
// when
11+
const actual = builder.build(args);
12+
13+
// then
14+
expect(actual).toEqual({ keyword: "search" });
15+
});
16+
17+
test("should build header without key", () => {
18+
// given
19+
const builder = new RequestHeaderBuilder(1);
20+
const args = ["invalid", { foo: "bar" }];
21+
22+
// when
23+
const actual = builder.build(args);
24+
25+
// then
26+
expect(actual).toEqual({ foo: "bar" });
27+
});
28+
});
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 RequestHeaderBuilder {
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[]): HeadersInit {
15+
return this.metadata.reduce<Record<string, string>>((acc, [index, key]) => {
16+
if (key != null) {
17+
acc[key] = String(args[index]);
18+
return acc;
19+
}
20+
21+
TupleArrayBuilder.of<string, unknown>(args[index]).forEach(
22+
([key, value]) => {
23+
acc[key] = String(value);
24+
}
25+
);
26+
27+
return acc;
28+
}, {});
29+
}
30+
}

lib/builders/request-param.builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ export class RequestParamBuilder {
1616
const result = this.metadata.reduce<Record<string, any>>(
1717
(acc, [index, key]) => {
1818
if (key != null) {
19-
acc[key] = args[index];
19+
acc[key] = String(args[index]);
2020
return acc;
2121
}
2222

2323
TupleArrayBuilder.of<string, unknown>(args[index]).forEach(
2424
([key, value]) => {
25-
acc[key] = value;
25+
acc[key] = String(value);
2626
}
2727
);
2828

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { describe, expect, test } from "vitest";
2+
import { TupleArrayBuilder } from "./tuple-array.builder";
3+
4+
describe("TupleArrayBuilder", () => {
5+
test("should return empty array when value is null", () => {
6+
// given
7+
const value = null;
8+
9+
// when
10+
const actual = TupleArrayBuilder.of(value);
11+
12+
// then
13+
expect(actual).toEqual([]);
14+
});
15+
16+
test("should return empty array when value is undefined", () => {
17+
// given
18+
const value = undefined;
19+
20+
// when
21+
const actual = TupleArrayBuilder.of(value);
22+
23+
// then
24+
expect(actual).toEqual([]);
25+
});
26+
27+
test("should return empty array when value is not an object", () => {
28+
// given
29+
const value = "string";
30+
31+
// when
32+
const actual = TupleArrayBuilder.of(value);
33+
34+
// then
35+
expect(actual).toEqual([]);
36+
});
37+
38+
test("should return array of tuples when value is an object", () => {
39+
// given
40+
const value = { foo: "bar" };
41+
42+
// when
43+
const actual = TupleArrayBuilder.of(value);
44+
45+
// then
46+
expect(actual).toEqual([["foo", "bar"]]);
47+
});
48+
49+
test("should return array of tuples when value is an object with multiple keys", () => {
50+
// given
51+
const value = { foo: "bar", baz: "qux" };
52+
53+
// when
54+
const actual = TupleArrayBuilder.of(value);
55+
56+
// then
57+
expect(actual).toEqual([
58+
["foo", "bar"],
59+
["baz", "qux"],
60+
]);
61+
});
62+
});

lib/builders/tuple-array.builder.ts

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

7-
if (value instanceof Map) {
8-
return [...value.entries()];
9-
}
10-
11-
if (typeof value === "object" && value !== null) {
7+
if (typeof value === "object") {
128
return Object.entries(value) as Array<[A, B]>;
139
}
1410

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import { describe, test, expect } from "vitest";
22
import { REQUEST_HEADER_METADATA } from "./constants";
3-
import {
4-
RequestHeader,
5-
type RequestHeaderMetadata,
6-
} from "./request-header.decorator";
3+
import { RequestHeader } from "./request-header.decorator";
4+
import { type RequestHeaderBuilder } from "../builders/request-header.builder";
75

86
describe("RequestHeader", () => {
97
test("should set request header metadata with empty key", () => {
@@ -15,15 +13,14 @@ describe("RequestHeader", () => {
1513
}
1614

1715
// when
18-
const result: RequestHeaderMetadata = Reflect.getMetadata(
16+
const result: RequestHeaderBuilder = Reflect.getMetadata(
1917
REQUEST_HEADER_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).toEqual([[0, undefined]]);
2724
});
2825

2926
test("should set request header metadata with key", () => {
@@ -35,14 +32,13 @@ describe("RequestHeader", () => {
3532
}
3633

3734
// when
38-
const result: RequestHeaderMetadata = Reflect.getMetadata(
35+
const result: RequestHeaderBuilder = Reflect.getMetadata(
3936
REQUEST_HEADER_METADATA,
4037
TestService.prototype,
4138
"request"
4239
);
4340

4441
// then
45-
expect(result).toHaveLength(1);
46-
expect(result.get(0)).toBe("foo");
42+
expect(result.metadata).toEqual([[0, "foo"]]);
4743
});
4844
});

lib/decorators/request-header.decorator.ts

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

64
export function RequestHeader(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_HEADER_METADATA, target, propertyKey) ??
14-
new MetadataMap();
10+
const builder: RequestHeaderBuilder | undefined = Reflect.getMetadata(
11+
REQUEST_HEADER_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_HEADER_METADATA,
20-
metadata,
23+
new RequestHeaderBuilder(parameterIndex, key),
2124
target,
2225
propertyKey
2326
);

0 commit comments

Comments
 (0)