Skip to content

Commit 792e96c

Browse files
authored
Fix quote mismatched enum values (#1382)
Signed-off-by: Sora Morimoto <[email protected]>
1 parent 44dbd82 commit 792e96c

File tree

5 files changed

+663
-3
lines changed

5 files changed

+663
-3
lines changed

.changeset/fix-enum-quotation.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"swagger-typescript-api": patch
3+
---
4+
5+
Fix enum string value quoting when schema type mismatches.

src/schema-parser/base-schema-parsers/enum.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,35 @@ export class EnumSchemaParser extends MonoSchemaParser {
7171
if (value === null) {
7272
return this.config.Ts.NullValue(value);
7373
}
74+
7475
if (
7576
keyType.includes(this.schemaUtils.getSchemaType({ type: "number" }))
7677
) {
77-
return this.config.Ts.NumberValue(value);
78+
const maybeNumber = typeof value === "number" ? value : Number(value);
79+
if (!Number.isNaN(maybeNumber)) {
80+
return this.config.Ts.NumberValue(maybeNumber);
81+
}
7882
}
83+
7984
if (
8085
keyType.includes(this.schemaUtils.getSchemaType({ type: "boolean" }))
8186
) {
82-
return this.config.Ts.BooleanValue(value);
87+
if (typeof value === "boolean") {
88+
return this.config.Ts.BooleanValue(value);
89+
}
90+
if (value === "true" || value === "false") {
91+
return this.config.Ts.BooleanValue(value === "true");
92+
}
8393
}
8494

85-
return this.config.Ts.StringValue(value);
95+
switch (typeof value) {
96+
case "number":
97+
return this.config.Ts.NumberValue(value);
98+
case "boolean":
99+
return this.config.Ts.BooleanValue(value);
100+
default:
101+
return this.config.Ts.StringValue(value);
102+
}
86103
};
87104

88105
if (Array.isArray(enumNames) && lodash.size(enumNames)) {

tests/__snapshots__/extended.test.ts.snap

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10292,6 +10292,322 @@ export class Api<
1029210292
"
1029310293
`;
1029410294

10295+
exports[`extended > 'enum-type-mismatch' 1`] = `
10296+
"/* eslint-disable */
10297+
/* tslint:disable */
10298+
// @ts-nocheck
10299+
/*
10300+
* ---------------------------------------------------------------
10301+
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
10302+
* ## ##
10303+
* ## AUTHOR: acacode ##
10304+
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
10305+
* ---------------------------------------------------------------
10306+
*/
10307+
10308+
export type GetStatusData = ModelsConsultStatus;
10309+
10310+
/** @format int32 */
10311+
export enum ModelsConsultStatus {
10312+
Scheduled = "Scheduled",
10313+
CancelledByClient = "CancelledByClient",
10314+
}
10315+
10316+
export namespace Status {
10317+
/**
10318+
* No description
10319+
* @name GetStatus
10320+
* @request GET:/status
10321+
*/
10322+
export namespace GetStatus {
10323+
export type RequestParams = {};
10324+
export type RequestQuery = {};
10325+
export type RequestBody = never;
10326+
export type RequestHeaders = {};
10327+
export type ResponseBody = GetStatusData;
10328+
}
10329+
}
10330+
10331+
export type QueryParamsType = Record<string | number, any>;
10332+
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
10333+
10334+
export interface FullRequestParams extends Omit<RequestInit, "body"> {
10335+
/** set parameter to \`true\` for call \`securityWorker\` for this request */
10336+
secure?: boolean;
10337+
/** request path */
10338+
path: string;
10339+
/** content type of request body */
10340+
type?: ContentType;
10341+
/** query params */
10342+
query?: QueryParamsType;
10343+
/** format of response (i.e. response.json() -> format: "json") */
10344+
format?: ResponseFormat;
10345+
/** request body */
10346+
body?: unknown;
10347+
/** base url */
10348+
baseUrl?: string;
10349+
/** request cancellation token */
10350+
cancelToken?: CancelToken;
10351+
}
10352+
10353+
export type RequestParams = Omit<
10354+
FullRequestParams,
10355+
"body" | "method" | "query" | "path"
10356+
>;
10357+
10358+
export interface ApiConfig<SecurityDataType = unknown> {
10359+
baseUrl?: string;
10360+
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
10361+
securityWorker?: (
10362+
securityData: SecurityDataType | null,
10363+
) => Promise<RequestParams | void> | RequestParams | void;
10364+
customFetch?: typeof fetch;
10365+
}
10366+
10367+
export interface HttpResponse<D extends unknown, E extends unknown = unknown>
10368+
extends Response {
10369+
data: D;
10370+
error: E;
10371+
}
10372+
10373+
type CancelToken = Symbol | string | number;
10374+
10375+
export enum ContentType {
10376+
Json = "application/json",
10377+
JsonApi = "application/vnd.api+json",
10378+
FormData = "multipart/form-data",
10379+
UrlEncoded = "application/x-www-form-urlencoded",
10380+
Text = "text/plain",
10381+
}
10382+
10383+
export class HttpClient<SecurityDataType = unknown> {
10384+
public baseUrl: string = "";
10385+
private securityData: SecurityDataType | null = null;
10386+
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
10387+
private abortControllers = new Map<CancelToken, AbortController>();
10388+
private customFetch = (...fetchParams: Parameters<typeof fetch>) =>
10389+
fetch(...fetchParams);
10390+
10391+
private baseApiParams: RequestParams = {
10392+
credentials: "same-origin",
10393+
headers: {},
10394+
redirect: "follow",
10395+
referrerPolicy: "no-referrer",
10396+
};
10397+
10398+
constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
10399+
Object.assign(this, apiConfig);
10400+
}
10401+
10402+
public setSecurityData = (data: SecurityDataType | null) => {
10403+
this.securityData = data;
10404+
};
10405+
10406+
protected encodeQueryParam(key: string, value: any) {
10407+
const encodedKey = encodeURIComponent(key);
10408+
return \`\${encodedKey}=\${encodeURIComponent(typeof value === "number" ? value : \`\${value}\`)}\`;
10409+
}
10410+
10411+
protected addQueryParam(query: QueryParamsType, key: string) {
10412+
return this.encodeQueryParam(key, query[key]);
10413+
}
10414+
10415+
protected addArrayQueryParam(query: QueryParamsType, key: string) {
10416+
const value = query[key];
10417+
return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
10418+
}
10419+
10420+
protected toQueryString(rawQuery?: QueryParamsType): string {
10421+
const query = rawQuery || {};
10422+
const keys = Object.keys(query).filter(
10423+
(key) => "undefined" !== typeof query[key],
10424+
);
10425+
return keys
10426+
.map((key) =>
10427+
Array.isArray(query[key])
10428+
? this.addArrayQueryParam(query, key)
10429+
: this.addQueryParam(query, key),
10430+
)
10431+
.join("&");
10432+
}
10433+
10434+
protected addQueryParams(rawQuery?: QueryParamsType): string {
10435+
const queryString = this.toQueryString(rawQuery);
10436+
return queryString ? \`?\${queryString}\` : "";
10437+
}
10438+
10439+
private contentFormatters: Record<ContentType, (input: any) => any> = {
10440+
[ContentType.Json]: (input: any) =>
10441+
input !== null && (typeof input === "object" || typeof input === "string")
10442+
? JSON.stringify(input)
10443+
: input,
10444+
[ContentType.JsonApi]: (input: any) =>
10445+
input !== null && (typeof input === "object" || typeof input === "string")
10446+
? JSON.stringify(input)
10447+
: input,
10448+
[ContentType.Text]: (input: any) =>
10449+
input !== null && typeof input !== "string"
10450+
? JSON.stringify(input)
10451+
: input,
10452+
[ContentType.FormData]: (input: any) => {
10453+
if (input instanceof FormData) {
10454+
return input;
10455+
}
10456+
10457+
return Object.keys(input || {}).reduce((formData, key) => {
10458+
const property = input[key];
10459+
formData.append(
10460+
key,
10461+
property instanceof Blob
10462+
? property
10463+
: typeof property === "object" && property !== null
10464+
? JSON.stringify(property)
10465+
: \`\${property}\`,
10466+
);
10467+
return formData;
10468+
}, new FormData());
10469+
},
10470+
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
10471+
};
10472+
10473+
protected mergeRequestParams(
10474+
params1: RequestParams,
10475+
params2?: RequestParams,
10476+
): RequestParams {
10477+
return {
10478+
...this.baseApiParams,
10479+
...params1,
10480+
...(params2 || {}),
10481+
headers: {
10482+
...(this.baseApiParams.headers || {}),
10483+
...(params1.headers || {}),
10484+
...((params2 && params2.headers) || {}),
10485+
},
10486+
};
10487+
}
10488+
10489+
protected createAbortSignal = (
10490+
cancelToken: CancelToken,
10491+
): AbortSignal | undefined => {
10492+
if (this.abortControllers.has(cancelToken)) {
10493+
const abortController = this.abortControllers.get(cancelToken);
10494+
if (abortController) {
10495+
return abortController.signal;
10496+
}
10497+
return void 0;
10498+
}
10499+
10500+
const abortController = new AbortController();
10501+
this.abortControllers.set(cancelToken, abortController);
10502+
return abortController.signal;
10503+
};
10504+
10505+
public abortRequest = (cancelToken: CancelToken) => {
10506+
const abortController = this.abortControllers.get(cancelToken);
10507+
10508+
if (abortController) {
10509+
abortController.abort();
10510+
this.abortControllers.delete(cancelToken);
10511+
}
10512+
};
10513+
10514+
public request = async <T = any, E = any>({
10515+
body,
10516+
secure,
10517+
path,
10518+
type,
10519+
query,
10520+
format,
10521+
baseUrl,
10522+
cancelToken,
10523+
...params
10524+
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
10525+
const secureParams =
10526+
((typeof secure === "boolean" ? secure : this.baseApiParams.secure) &&
10527+
this.securityWorker &&
10528+
(await this.securityWorker(this.securityData))) ||
10529+
{};
10530+
const requestParams = this.mergeRequestParams(params, secureParams);
10531+
const queryString = query && this.toQueryString(query);
10532+
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
10533+
const responseFormat = format || requestParams.format;
10534+
10535+
return this.customFetch(
10536+
\`\${baseUrl || this.baseUrl || ""}\${path}\${queryString ? \`?\${queryString}\` : ""}\`,
10537+
{
10538+
...requestParams,
10539+
headers: {
10540+
...(requestParams.headers || {}),
10541+
...(type && type !== ContentType.FormData
10542+
? { "Content-Type": type }
10543+
: {}),
10544+
},
10545+
signal:
10546+
(cancelToken
10547+
? this.createAbortSignal(cancelToken)
10548+
: requestParams.signal) || null,
10549+
body:
10550+
typeof body === "undefined" || body === null
10551+
? null
10552+
: payloadFormatter(body),
10553+
},
10554+
).then(async (response) => {
10555+
const r = response.clone() as HttpResponse<T, E>;
10556+
r.data = null as unknown as T;
10557+
r.error = null as unknown as E;
10558+
10559+
const data = !responseFormat
10560+
? r
10561+
: await response[responseFormat]()
10562+
.then((data) => {
10563+
if (r.ok) {
10564+
r.data = data;
10565+
} else {
10566+
r.error = data;
10567+
}
10568+
return r;
10569+
})
10570+
.catch((e) => {
10571+
r.error = e;
10572+
return r;
10573+
});
10574+
10575+
if (cancelToken) {
10576+
this.abortControllers.delete(cancelToken);
10577+
}
10578+
10579+
if (!response.ok) throw data;
10580+
return data;
10581+
});
10582+
};
10583+
}
10584+
10585+
/**
10586+
* @title Enum Type Mismatch
10587+
* @version 1.0.0
10588+
*/
10589+
export class Api<
10590+
SecurityDataType extends unknown,
10591+
> extends HttpClient<SecurityDataType> {
10592+
status = {
10593+
/**
10594+
* No description
10595+
*
10596+
* @name GetStatus
10597+
* @request GET:/status
10598+
*/
10599+
getStatus: (params: RequestParams = {}) =>
10600+
this.request<GetStatusData, any>({
10601+
path: \`/status\`,
10602+
method: "GET",
10603+
format: "json",
10604+
...params,
10605+
}),
10606+
};
10607+
}
10608+
"
10609+
`;
10610+
1029510611
exports[`extended > 'enums' 1`] = `
1029610612
"/* eslint-disable */
1029710613
/* tslint:disable */

0 commit comments

Comments
 (0)