Skip to content

Commit ebaa188

Browse files
committed
test: add jsonapi-media-type test
1 parent d7ae545 commit ebaa188

File tree

3 files changed

+590
-0
lines changed

3 files changed

+590
-0
lines changed
Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`basic > jsonapi-media-type 1`] = `
4+
"/* eslint-disable */
5+
/* tslint:disable */
6+
// @ts-nocheck
7+
/*
8+
* ---------------------------------------------------------------
9+
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
10+
* ## ##
11+
* ## AUTHOR: acacode ##
12+
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
13+
* ---------------------------------------------------------------
14+
*/
15+
16+
export interface Pet {
17+
/** @format uuid */
18+
id?: string;
19+
type: "pet";
20+
attributes: {
21+
name: string;
22+
status?: "available" | "pending" | "sold";
23+
photoUrls: string[];
24+
};
25+
}
26+
27+
export interface Error {
28+
status?: string;
29+
detail?: string;
30+
}
31+
32+
export type QueryParamsType = Record<string | number, any>;
33+
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
34+
35+
export interface FullRequestParams extends Omit<RequestInit, "body"> {
36+
/** set parameter to \`true\` for call \`securityWorker\` for this request */
37+
secure?: boolean;
38+
/** request path */
39+
path: string;
40+
/** content type of request body */
41+
type?: ContentType;
42+
/** query params */
43+
query?: QueryParamsType;
44+
/** format of response (i.e. response.json() -> format: "json") */
45+
format?: ResponseFormat;
46+
/** request body */
47+
body?: unknown;
48+
/** base url */
49+
baseUrl?: string;
50+
/** request cancellation token */
51+
cancelToken?: CancelToken;
52+
}
53+
54+
export type RequestParams = Omit<
55+
FullRequestParams,
56+
"body" | "method" | "query" | "path"
57+
>;
58+
59+
export interface ApiConfig<SecurityDataType = unknown> {
60+
baseUrl?: string;
61+
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
62+
securityWorker?: (
63+
securityData: SecurityDataType | null,
64+
) => Promise<RequestParams | void> | RequestParams | void;
65+
customFetch?: typeof fetch;
66+
}
67+
68+
export interface HttpResponse<D extends unknown, E extends unknown = unknown>
69+
extends Response {
70+
data: D;
71+
error: E;
72+
}
73+
74+
type CancelToken = Symbol | string | number;
75+
76+
export enum ContentType {
77+
Json = "application/json",
78+
JsonApi = "application/vnd.api+json",
79+
FormData = "multipart/form-data",
80+
UrlEncoded = "application/x-www-form-urlencoded",
81+
Text = "text/plain",
82+
}
83+
84+
export class HttpClient<SecurityDataType = unknown> {
85+
public baseUrl: string = "https://api.petstore.localhost";
86+
private securityData: SecurityDataType | null = null;
87+
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
88+
private abortControllers = new Map<CancelToken, AbortController>();
89+
private customFetch = (...fetchParams: Parameters<typeof fetch>) =>
90+
fetch(...fetchParams);
91+
92+
private baseApiParams: RequestParams = {
93+
credentials: "same-origin",
94+
headers: {},
95+
redirect: "follow",
96+
referrerPolicy: "no-referrer",
97+
};
98+
99+
constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
100+
Object.assign(this, apiConfig);
101+
}
102+
103+
public setSecurityData = (data: SecurityDataType | null) => {
104+
this.securityData = data;
105+
};
106+
107+
protected encodeQueryParam(key: string, value: any) {
108+
const encodedKey = encodeURIComponent(key);
109+
return \`\${encodedKey}=\${encodeURIComponent(typeof value === "number" ? value : \`\${value}\`)}\`;
110+
}
111+
112+
protected addQueryParam(query: QueryParamsType, key: string) {
113+
return this.encodeQueryParam(key, query[key]);
114+
}
115+
116+
protected addArrayQueryParam(query: QueryParamsType, key: string) {
117+
const value = query[key];
118+
return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
119+
}
120+
121+
protected toQueryString(rawQuery?: QueryParamsType): string {
122+
const query = rawQuery || {};
123+
const keys = Object.keys(query).filter(
124+
(key) => "undefined" !== typeof query[key],
125+
);
126+
return keys
127+
.map((key) =>
128+
Array.isArray(query[key])
129+
? this.addArrayQueryParam(query, key)
130+
: this.addQueryParam(query, key),
131+
)
132+
.join("&");
133+
}
134+
135+
protected addQueryParams(rawQuery?: QueryParamsType): string {
136+
const queryString = this.toQueryString(rawQuery);
137+
return queryString ? \`?\${queryString}\` : "";
138+
}
139+
140+
private contentFormatters: Record<ContentType, (input: any) => any> = {
141+
[ContentType.Json]: (input: any) =>
142+
input !== null && (typeof input === "object" || typeof input === "string")
143+
? JSON.stringify(input)
144+
: input,
145+
[ContentType.JsonApi]: (input: any) =>
146+
input !== null && (typeof input === "object" || typeof input === "string")
147+
? JSON.stringify(input)
148+
: input,
149+
[ContentType.Text]: (input: any) =>
150+
input !== null && typeof input !== "string"
151+
? JSON.stringify(input)
152+
: input,
153+
[ContentType.FormData]: (input: any) =>
154+
Object.keys(input || {}).reduce((formData, key) => {
155+
const property = input[key];
156+
formData.append(
157+
key,
158+
property instanceof Blob
159+
? property
160+
: typeof property === "object" && property !== null
161+
? JSON.stringify(property)
162+
: \`\${property}\`,
163+
);
164+
return formData;
165+
}, new FormData()),
166+
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
167+
};
168+
169+
protected mergeRequestParams(
170+
params1: RequestParams,
171+
params2?: RequestParams,
172+
): RequestParams {
173+
return {
174+
...this.baseApiParams,
175+
...params1,
176+
...(params2 || {}),
177+
headers: {
178+
...(this.baseApiParams.headers || {}),
179+
...(params1.headers || {}),
180+
...((params2 && params2.headers) || {}),
181+
},
182+
};
183+
}
184+
185+
protected createAbortSignal = (
186+
cancelToken: CancelToken,
187+
): AbortSignal | undefined => {
188+
if (this.abortControllers.has(cancelToken)) {
189+
const abortController = this.abortControllers.get(cancelToken);
190+
if (abortController) {
191+
return abortController.signal;
192+
}
193+
return void 0;
194+
}
195+
196+
const abortController = new AbortController();
197+
this.abortControllers.set(cancelToken, abortController);
198+
return abortController.signal;
199+
};
200+
201+
public abortRequest = (cancelToken: CancelToken) => {
202+
const abortController = this.abortControllers.get(cancelToken);
203+
204+
if (abortController) {
205+
abortController.abort();
206+
this.abortControllers.delete(cancelToken);
207+
}
208+
};
209+
210+
public request = async <T = any, E = any>({
211+
body,
212+
secure,
213+
path,
214+
type,
215+
query,
216+
format,
217+
baseUrl,
218+
cancelToken,
219+
...params
220+
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
221+
const secureParams =
222+
((typeof secure === "boolean" ? secure : this.baseApiParams.secure) &&
223+
this.securityWorker &&
224+
(await this.securityWorker(this.securityData))) ||
225+
{};
226+
const requestParams = this.mergeRequestParams(params, secureParams);
227+
const queryString = query && this.toQueryString(query);
228+
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
229+
const responseFormat = format || requestParams.format;
230+
231+
return this.customFetch(
232+
\`\${baseUrl || this.baseUrl || ""}\${path}\${queryString ? \`?\${queryString}\` : ""}\`,
233+
{
234+
...requestParams,
235+
headers: {
236+
...(requestParams.headers || {}),
237+
...(type && type !== ContentType.FormData
238+
? { "Content-Type": type }
239+
: {}),
240+
},
241+
signal:
242+
(cancelToken
243+
? this.createAbortSignal(cancelToken)
244+
: requestParams.signal) || null,
245+
body:
246+
typeof body === "undefined" || body === null
247+
? null
248+
: payloadFormatter(body),
249+
},
250+
).then(async (response) => {
251+
const r = response.clone() as HttpResponse<T, E>;
252+
r.data = null as unknown as T;
253+
r.error = null as unknown as E;
254+
255+
const data = !responseFormat
256+
? r
257+
: await response[responseFormat]()
258+
.then((data) => {
259+
if (r.ok) {
260+
r.data = data;
261+
} else {
262+
r.error = data;
263+
}
264+
return r;
265+
})
266+
.catch((e) => {
267+
r.error = e;
268+
return r;
269+
});
270+
271+
if (cancelToken) {
272+
this.abortControllers.delete(cancelToken);
273+
}
274+
275+
if (!response.ok) throw data;
276+
return data;
277+
});
278+
};
279+
}
280+
281+
/**
282+
* @title JSON:API Swagger Petstore
283+
* @version 1.1
284+
* @baseUrl https://api.petstore.localhost
285+
*/
286+
export class Api<
287+
SecurityDataType extends unknown,
288+
> extends HttpClient<SecurityDataType> {
289+
pets = {
290+
/**
291+
* @description Add a new pet to the store.
292+
*
293+
* @tags pet
294+
* @name AddPet
295+
* @summary Add a new pet to the store.
296+
* @request POST:/pets
297+
*/
298+
addPet: (
299+
data: {
300+
data: Pet;
301+
},
302+
params: RequestParams = {},
303+
) =>
304+
this.request<
305+
{
306+
data: Pet;
307+
},
308+
{
309+
errors: Error[];
310+
}
311+
>({
312+
path: \`/pets\`,
313+
method: "POST",
314+
body: data,
315+
type: ContentType.JsonApi,
316+
...params,
317+
}),
318+
319+
/**
320+
* @description Update an existing pet by Id.
321+
*
322+
* @tags pet
323+
* @name UpdatePet
324+
* @summary Update an existing pet.
325+
* @request PATCH:/pets/{petId}
326+
*/
327+
updatePet: (
328+
petId: string,
329+
data: {
330+
data: Pet;
331+
},
332+
params: RequestParams = {},
333+
) =>
334+
this.request<
335+
{
336+
data: Pet;
337+
},
338+
{
339+
errors: Error[];
340+
}
341+
>({
342+
path: \`/pets/\${petId}\`,
343+
method: "PATCH",
344+
body: data,
345+
type: ContentType.JsonApi,
346+
...params,
347+
}),
348+
};
349+
}
350+
"
351+
`;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as fs from "node:fs/promises";
2+
import * as os from "node:os";
3+
import * as path from "node:path";
4+
5+
import { afterAll, beforeAll, describe, expect, test } from "vitest";
6+
7+
import { generateApi } from "../../../src/index.js";
8+
9+
describe("basic", async () => {
10+
let tmpdir = "";
11+
12+
beforeAll(async () => {
13+
tmpdir = await fs.mkdtemp(path.join(os.tmpdir(), "swagger-typescript-api"));
14+
});
15+
16+
afterAll(async () => {
17+
await fs.rm(tmpdir, { recursive: true });
18+
});
19+
20+
test("jsonapi-media-type", async () => {
21+
await generateApi({
22+
fileName: "schema",
23+
input: path.resolve(import.meta.dirname, "schema.json"),
24+
output: tmpdir,
25+
silent: true,
26+
enumNamesAsValues: true,
27+
});
28+
29+
const content = await fs.readFile(path.join(tmpdir, "schema.ts"), {
30+
encoding: "utf8",
31+
});
32+
33+
expect(content).toMatchSnapshot();
34+
});
35+
});

0 commit comments

Comments
 (0)