22const { 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";
613import ky from "ky";
714
815type KyResponse<Data > = Response & {
@@ -19,7 +26,10 @@ export type ResponsePromise<Data> = {
1926
2027export 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,157 @@ export enum ContentType {
4664}
4765
4866export 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+ let { headers } = options;
172+ if (secureOptions.headers) {
173+ const mergedHeaders = new Headers(headers);
174+ const secureHeaders = new Headers(secureOptions.headers);
175+ secureHeaders.forEach((value, key) => {
176+ mergedHeaders.set(key, value);
177+ });
178+ headers = mergedHeaders;
112179 }
180+ return new Request(request.url, {
181+ ...options,
182+ ...secureOptions,
183+ headers,
184+ });
113185 }
186+ };
187+
188+ hooks = {
189+ ...options.hooks,
190+ beforeRequest:
191+ options.hooks && options.hooks.beforeRequest
192+ ? [securityWorker, ...options.hooks.beforeRequest]
193+ : [securityWorker],
194+ };
195+ }
114196
115- const request = this.ky(path.replace(/^\//, ''), {
116- ...options,
117- headers,
118- searchParams: query,
119- body: body as any,
120- });
197+ const request = this.ky(path.replace(/^\//, ""), {
198+ ...options,
199+ headers,
200+ searchParams: query,
201+ body: body as any,
202+ hooks,
203+ });
121204
122205<% 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"
206+ const responseFormat = format || this .format || undefined ;
207+ return ( responseFormat === " json"
208+ ? request .json ()
209+ : responseFormat === " arrayBuffer"
210+ ? request .arrayBuffer ()
211+ : responseFormat === " blob"
129212 ? request .blob ()
130213 : responseFormat === " formData"
131- ? request .formData ()
132- : request .text ();
214+ ? request .formData ()
215+ : request .text ()) as Promise < T > ;
133216< % } else { % >
134- return request;
217+ return request;
135218< % } %>
136- };
219+ };
137220}
0 commit comments