Skip to content

Commit bf2adbb

Browse files
committed
feat: reimplement securityWorker
1 parent 69a26ec commit bf2adbb

File tree

1 file changed

+154
-76
lines changed

1 file changed

+154
-76
lines changed

templates/base/http-clients/ky-http-client.ejs

+154-76
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,14 @@
22
const { apiConfig, generateResponses, config } = it;
33
%>
44

5-
import type { KyInstance, Options as KyOptions, SearchParamsOption } from "ky";
5+
import type {
6+
BeforeRequestHook,
7+
Hooks,
8+
KyInstance,
9+
Options as KyOptions,
10+
NormalizedOptions,
11+
SearchParamsOption,
12+
} from "ky";
613
import ky from "ky";
714

815
type KyResponse<Data> = Response & {
@@ -19,7 +26,10 @@ export type ResponsePromise<Data> = {
1926

2027
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
2128

22-
export interface FullRequestParams extends Omit<KyOptions, "json" | "body" | "searchParams"> {
29+
export interface FullRequestParams
30+
extends Omit<KyOptions, "json" | "body" | "searchParams"> {
31+
/** set parameter to `true` for call `securityWorker` for this request */
32+
secure?: boolean;
2333
/** request path */
2434
path: string;
2535
/** content type of request body */
@@ -32,9 +42,17 @@ export interface FullRequestParams extends Omit<KyOptions, "json" | "body" | "se
3242
body?: unknown;
3343
}
3444

35-
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
36-
37-
export interface ApiConfig<SecurityDataType = unknown> extends Omit<KyOptions, "data" | "cancelToken"> {
45+
export type RequestParams = Omit<
46+
FullRequestParams,
47+
"body" | "method" | "query" | "path"
48+
>;
49+
50+
export interface ApiConfig<SecurityDataType = unknown>
51+
extends Omit<KyOptions, "data" | "cancelToken"> {
52+
securityWorker?: (
53+
securityData: SecurityDataType | null,
54+
) => Promise<NormalizedOptions | void> | NormalizedOptions | void;
55+
secure?: boolean;
3856
format?: ResponseType;
3957
}
4058

@@ -46,92 +64,152 @@ export enum ContentType {
4664
}
4765

4866
export class HttpClient<SecurityDataType = unknown> {
49-
public ky: KyInstance;
50-
private format?: ResponseType;
67+
public ky: KyInstance;
68+
private securityData: SecurityDataType | null = null;
69+
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
70+
private secure?: boolean;
71+
private format?: ResponseType;
72+
73+
constructor({
74+
securityWorker,
75+
secure,
76+
format,
77+
...options
78+
}: ApiConfig<SecurityDataType> = {}) {
79+
this.ky = ky.create({ ...options, prefixUrl: options.prefixUrl || "" });
80+
this.secure = secure;
81+
this.format = format;
82+
this.securityWorker = securityWorker;
83+
}
84+
85+
public setSecurityData = (data: SecurityDataType | null) => {
86+
this.securityData = data;
87+
};
88+
89+
protected mergeRequestParams(
90+
params1: KyOptions,
91+
params2?: KyOptions,
92+
): KyOptions {
93+
return {
94+
...params1,
95+
...params2,
96+
headers: {
97+
...params1.headers,
98+
...(params2 && params2.headers),
99+
},
100+
};
101+
}
51102

52-
constructor({ format, ...options }: ApiConfig<SecurityDataType> = {}) {
53-
this.ky = ky.create({ ...options, prefixUrl: options.prefixUrl || "<%~ apiConfig.baseUrl %>" })
54-
this.format = format;
103+
protected stringifyFormItem(formItem: unknown) {
104+
if (typeof formItem === "object" && formItem !== null) {
105+
return JSON.stringify(formItem);
106+
} else {
107+
return `${formItem}`;
55108
}
56-
57-
protected stringifyFormItem(formItem: unknown) {
58-
if (typeof formItem === "object" && formItem !== null) {
59-
return JSON.stringify(formItem);
60-
} else {
61-
return `${formItem}`;
109+
}
110+
111+
protected createFormData(input: Record<string, unknown>): FormData {
112+
return Object.keys(input || {}).reduce((formData, key) => {
113+
const property = input[key];
114+
const propertyContent: any[] =
115+
property instanceof Array ? property : [property];
116+
117+
for (const formItem of propertyContent) {
118+
const isFileType = formItem instanceof Blob || formItem instanceof File;
119+
formData.append(
120+
key,
121+
isFileType ? formItem : this.stringifyFormItem(formItem),
122+
);
62123
}
63-
}
64-
65-
protected createFormData(input: Record<string, unknown>): FormData {
66-
return Object.keys(input || {}).reduce((formData, key) => {
67-
const property = input[key];
68-
const propertyContent: any[] = (property instanceof Array) ? property : [property]
69-
70-
for (const formItem of propertyContent) {
71-
const isFileType = formItem instanceof Blob || formItem instanceof File;
72-
formData.append(
73-
key,
74-
isFileType ? formItem : this.stringifyFormItem(formItem)
75-
);
76-
}
77-
78-
return formData;
79-
}, new FormData());
80-
}
81124

82-
public request = <T = any, _E = any>({
83-
path,
84-
type,
85-
query,
86-
format,
87-
body,
88-
...options
125+
return formData;
126+
}, new FormData());
127+
}
128+
129+
public request = <T = any, _E = any>({
130+
secure = this.secure,
131+
path,
132+
type,
133+
query,
134+
format,
135+
body,
136+
...options
89137
<% if (config.unwrapResponseData) { %>
90-
}: FullRequestParams): Promise<T> => {
138+
}: FullRequestParams): Promise<T> => {
91139
<% } else { %>
92-
}: FullRequestParams): ResponsePromise<T> => {
140+
}: FullRequestParams): ResponsePromise<T> => {
93141
<% } %>
94-
if (body) {
95-
if (type === ContentType.FormData) {
96-
body = typeof body === "object" ? this.createFormData(body as Record<string, unknown>) : body;
97-
} else if (type === ContentType.Text) {
98-
body = typeof body !== "string" ? JSON.stringify(body) : body;
99-
}
100-
}
142+
if (body) {
143+
if (type === ContentType.FormData) {
144+
body =
145+
typeof body === "object"
146+
? this.createFormData(body as Record<string, unknown>)
147+
: body;
148+
} else if (type === ContentType.Text) {
149+
body = typeof body !== "string" ? JSON.stringify(body) : body;
150+
}
151+
}
101152

102-
let headers: Headers | Record<string, string | undefined> | undefined;
103-
if (options.headers instanceof Headers) {
104-
headers = new Headers(options.headers);
105-
if (type && type !== ContentType.FormData) {
106-
headers.set('Content-Type', type);
107-
}
108-
} else {
109-
headers = { ...options.headers } as Record<string, string | undefined>;
110-
if (type && type !== ContentType.FormData) {
111-
headers['Content-Type'] = type;
153+
let headers: Headers | Record<string, string | undefined> | undefined;
154+
if (options.headers instanceof Headers) {
155+
headers = new Headers(options.headers);
156+
if (type && type !== ContentType.FormData) {
157+
headers.set("Content-Type", type);
158+
}
159+
} else {
160+
headers = { ...options.headers } as Record<string, string | undefined>;
161+
if (type && type !== ContentType.FormData) {
162+
headers["Content-Type"] = type;
163+
}
164+
}
165+
166+
let hooks: Hooks | undefined;
167+
if (secure && this.securityWorker) {
168+
const securityWorker: BeforeRequestHook = async (_request, options) => {
169+
const secureOptions = await this.securityWorker!(this.securityData);
170+
if (secureOptions && typeof secureOptions === "object") {
171+
const headers = new Headers(options.headers);
172+
if (secureOptions && secureOptions.headers) {
173+
const secureHeaders = new Headers(secureOptions.headers);
174+
secureHeaders.forEach((value, key) => {
175+
headers.set(key, value);
176+
});
112177
}
178+
Object.assign(options, secureOptions);
179+
options.headers = headers;
113180
}
181+
};
182+
183+
hooks = {
184+
...options.hooks,
185+
beforeRequest:
186+
options.hooks && options.hooks.beforeRequest
187+
? [securityWorker, ...options.hooks.beforeRequest]
188+
: [securityWorker],
189+
};
190+
}
114191

115-
const request = this.ky(path.replace(/^\//, ''), {
116-
...options,
117-
headers,
118-
searchParams: query,
119-
body: body as any,
120-
});
192+
const request = this.ky(path.replace(/^\//, ""), {
193+
...options,
194+
headers,
195+
searchParams: query,
196+
body: body as any,
197+
hooks,
198+
});
121199

122200
<% if (config.unwrapResponseData) { %>
123-
const responseFormat = (format || this.format) || undefined;
124-
return responseFormat === "json"
125-
? request.json()
126-
: responseFormat === "arrayBuffer"
127-
? request.arrayBuffer()
128-
: responseFormat === "blob"
201+
const responseFormat = format || this.format || undefined;
202+
return (responseFormat === "json"
203+
? request.json()
204+
: responseFormat === "arrayBuffer"
205+
? request.arrayBuffer()
206+
: responseFormat === "blob"
129207
? request.blob()
130208
: responseFormat === "formData"
131-
? request.formData()
132-
: request.text();
209+
? request.formData()
210+
: request.text()) as Promise<T>;
133211
<% } else { %>
134-
return request;
212+
return request;
135213
<% } %>
136-
};
214+
};
137215
}

0 commit comments

Comments
 (0)