Skip to content

Commit

Permalink
Release 8.0.0 (#216)
Browse files Browse the repository at this point in the history
* BREAKING_CHANGES: remove default `json` format of the response type

* fix: missing `schema.$ref` in inline enum schemas

* Allow passing custom fetch function (#218)

* Allow passing custom fetch function

* Fixing a mistake

* Using ApiConfig instead of second param

* fix: tests

* chore: refresh schemas

* Fix: query param array serialization (#223)

* make the query params serialization conform to the default open api specs

* uri encode second part

Co-authored-by: stijn.lammens <[email protected]>

* docs: update CHANGELOG

* fix: unused E generic type

* chore: refresh generated test schemas

* bump: up version to 8.0.0

Co-authored-by: Sushruth Shastry <[email protected]>
Co-authored-by: Stijn Lammens <[email protected]>
Co-authored-by: stijn.lammens <[email protected]>
  • Loading branch information
4 people authored Apr 4, 2021
1 parent 8329c4b commit 4b809f3
Show file tree
Hide file tree
Showing 74 changed files with 1,626 additions and 1,395 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# next release

# 8.0.0

BREAKING_CHANGES:
- remove default `json` format of the response type (both for `axios` and `fetch` http clients) (issue #213, thanks @po5i)

Features:
- Allow passing custom fetch function (`fetch` http client only)
- Allow to set global response type format through `HttpClient` constructor
Example:
```ts
const httpClient = new HttpClient({ format: 'json' });
// all request responses will been formatted as json
```
Fixes:
- Missing `schema.$ref` in inline enum schemas
- Array query param values are serialized with the (non-default) comma separated style (issue #222, thanks @Styn, PR #223)
- TypeScript error "TS6133: 'E' is declared but its value is never read." (`axios` http client) (issue #220, thanks @pmbednarczyk )

# 7.0.1

Fixes:
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "swagger-typescript-api",
"version": "7.0.1",
"version": "8.0.0",
"description": "Generate typescript/javascript api from swagger schema",
"scripts": {
"cli:json": "node index.js -r -d -p ./swagger-test-cli.json -n swagger-test-cli.ts",
Expand All @@ -9,8 +9,8 @@
"node:debug": "node --nolazy swagger-test-cli/generate.js",
"contributors": "all-contributors generate",
"cli:help": "node index.js -h",
"test-all": "node ./node_modules/npm-run-all/bin/npm-run-all/index.js generate validate test:* --continue-on-error",
"test-specific": "node ./node_modules/npm-run-all/bin/npm-run-all/index.js test:* --continue-on-error",
"test-all": "node ./node_modules/npm-run-all/bin/npm-run-all/index.js generate validate test:*",
"test-specific": "node ./node_modules/npm-run-all/bin/npm-run-all/index.js generate validate test:*",
"generate": "node tests/generate.js",
"generate:debug": "node --nolazy tests/generate.js",
"validate": "node tests/validate.js",
Expand Down
4 changes: 4 additions & 0 deletions src/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ const attachParsedRef = (originalSchema, parsedSchema) => {

const schemaParsers = {
[SCHEMA_TYPES.ENUM]: (schema, typeName) => {
const refType = getRefType(schema);
const $ref = (refType && refType.$ref) || null;
const enumNamesAsValues = config.enumNamesAsValues;
const keyType = getType(schema);
const enumNames = getEnumNames(schema);
Expand Down Expand Up @@ -277,6 +279,8 @@ const schemaParsers = {

return attachParsedRef(schema, {
...(_.isObject(schema) ? schema : {}),
$ref: $ref,
typeName: ($ref && refType.typeName) || null,
$parsedSchema: true,
schemaType: SCHEMA_TYPES.ENUM,
type: SCHEMA_TYPES.ENUM,
Expand Down
14 changes: 8 additions & 6 deletions src/typeFormatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ const inlineExtraFormatters = {
[SCHEMA_TYPES.ENUM]: (parsedSchema) => {
return {
...parsedSchema,
content: _.uniq(
_.compact([
..._.map(parsedSchema.content, ({ value }) => `${value}`),
parsedSchema.nullable && TS_KEYWORDS.NULL,
]),
).join(" | "),
content: parsedSchema.$ref
? parsedSchema.typeName
: _.uniq(
_.compact([
..._.map(parsedSchema.content, ({ value }) => `${value}`),
parsedSchema.nullable && TS_KEYWORDS.NULL,
]),
).join(" | "),
};
},
};
Expand Down
12 changes: 8 additions & 4 deletions templates/base/http-clients/axios-http-client.eta
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query"
export interface ApiConfig<SecurityDataType = unknown> extends Omit<AxiosRequestConfig, "data" | "cancelToken"> {
securityWorker?: (securityData: SecurityDataType | null) => Promise<AxiosRequestConfig | void> | AxiosRequestConfig | void;
secure?: boolean;
format?: ResponseType;
}

export enum ContentType {
Expand All @@ -39,10 +40,12 @@ export class HttpClient<SecurityDataType = unknown> {
private securityData: SecurityDataType | null = null;
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
private secure?: boolean;
private format?: ResponseType;

constructor({ securityWorker, secure, ...axiosConfig }: ApiConfig<SecurityDataType> = {}) {
constructor({ securityWorker, secure, format, ...axiosConfig }: ApiConfig<SecurityDataType> = {}) {
this.instance = axios.create({ ...axiosConfig, baseURL: axiosConfig.baseURL || "<%~ apiConfig.baseUrl %>" })
this.secure = secure;
this.format = format;
this.securityWorker = securityWorker;
}

Expand All @@ -63,17 +66,18 @@ export class HttpClient<SecurityDataType = unknown> {
};
}

public request = async <T = any, E = any>({
public request = async <T = any, _E = any>({
secure,
path,
type,
query,
format = "json",
format,
body,
...params
}: FullRequestParams): Promise<AxiosResponse<T>> => {
const secureParams = ((typeof secure === 'boolean' ? secure : this.secure) && this.securityWorker && (await this.securityWorker(this.securityData))) || {};
const requestParams = this.mergeRequestParams(params, secureParams);
const responseFormat = (format && this.format) || void 0;

return this.instance.request({
...requestParams,
Expand All @@ -82,7 +86,7 @@ export class HttpClient<SecurityDataType = unknown> {
...(requestParams.headers || {}),
},
params: query,
responseType: format,
responseType: responseFormat,
data: body,
url: path,
});
Expand Down
31 changes: 14 additions & 17 deletions templates/base/http-clients/fetch-http-client.eta
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface ApiConfig<SecurityDataType = unknown> {
baseUrl?: string;
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
customFetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>
}

export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
Expand All @@ -51,6 +52,7 @@ export class HttpClient<SecurityDataType = unknown> {
private securityData: SecurityDataType | null = null;
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
private abortControllers = new Map<CancelToken, AbortController>();
private customFetch = fetch;

private baseApiParams: RequestParams = {
credentials: 'same-origin',
Expand All @@ -67,16 +69,10 @@ export class HttpClient<SecurityDataType = unknown> {
this.securityData = data;
}

private addQueryParam(query: QueryParamsType, key: string) {
private addArrayQueryParam(query: QueryParamsType, key: string) {
const value = query[key];

return (
encodeURIComponent(key) + "=" + encodeURIComponent(
Array.isArray(value) ? value.join(",") :
typeof value === "number" ? value :
`${value}`
)
);
const encodedKey = encodeURIComponent(key);
return `${value.map((val: any) => `${encodedKey}=${encodeURIComponent(typeof val === "number" ? val : `${val}`)}`).join('&')}`;
}

protected toQueryString(rawQuery?: QueryParamsType): string {
Expand All @@ -86,7 +82,7 @@ export class HttpClient<SecurityDataType = unknown> {
.map((key) =>
typeof query[key] === "object" && !Array.isArray(query[key])
? this.toQueryString(query[key] as QueryParamsType)
: this.addQueryParam(query, key),
: this.addArrayQueryParam(query, key),
)
.join("&");
}
Expand Down Expand Up @@ -118,7 +114,7 @@ export class HttpClient<SecurityDataType = unknown> {
},
};
}

private createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
if (this.abortControllers.has(cancelToken)) {
const abortController = this.abortControllers.get(cancelToken);
Expand All @@ -127,7 +123,7 @@ export class HttpClient<SecurityDataType = unknown> {
}
return void 0;
}

const abortController = new AbortController();
this.abortControllers.set(cancelToken, abortController);
return abortController.signal;
Expand All @@ -141,14 +137,14 @@ export class HttpClient<SecurityDataType = unknown> {
this.abortControllers.delete(cancelToken);
}
}

public request = async <T = any, E = any>({
body,
secure,
path,
type,
query,
format = "json",
format,
baseUrl,
cancelToken,
...params
Expand All @@ -157,8 +153,9 @@ export class HttpClient<SecurityDataType = unknown> {
const requestParams = this.mergeRequestParams(params, secureParams);
const queryString = query && this.toQueryString(query);
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
const responseFormat = format && requestParams.format;

return fetch(
return this.customFetch(
`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`,
{
...requestParams,
Expand All @@ -174,7 +171,7 @@ export class HttpClient<SecurityDataType = unknown> {
r.data = (null as unknown) as T;
r.error = (null as unknown) as E;

const data = await response[format]()
const data = !responseFormat ? r : await response[responseFormat]()
.then((data) => {
if (r.ok) {
r.data = data;
Expand All @@ -187,7 +184,7 @@ export class HttpClient<SecurityDataType = unknown> {
r.error = e;
return r;
});

if (cancelToken) {
this.abortControllers.delete(cancelToken);
}
Expand Down
49 changes: 26 additions & 23 deletions tests/generated/v2.0/adafruit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export interface ApiConfig<SecurityDataType = unknown> {
baseUrl?: string;
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
customFetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
}

export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
Expand All @@ -212,6 +213,7 @@ export class HttpClient<SecurityDataType = unknown> {
private securityData: SecurityDataType | null = null;
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
private abortControllers = new Map<CancelToken, AbortController>();
private customFetch = fetch;

private baseApiParams: RequestParams = {
credentials: "same-origin",
Expand All @@ -228,14 +230,12 @@ export class HttpClient<SecurityDataType = unknown> {
this.securityData = data;
};

private addQueryParam(query: QueryParamsType, key: string) {
private addArrayQueryParam(query: QueryParamsType, key: string) {
const value = query[key];

return (
encodeURIComponent(key) +
"=" +
encodeURIComponent(Array.isArray(value) ? value.join(",") : typeof value === "number" ? value : `${value}`)
);
const encodedKey = encodeURIComponent(key);
return `${value
.map((val: any) => `${encodedKey}=${encodeURIComponent(typeof val === "number" ? val : `${val}`)}`)
.join("&")}`;
}

protected toQueryString(rawQuery?: QueryParamsType): string {
Expand All @@ -245,7 +245,7 @@ export class HttpClient<SecurityDataType = unknown> {
.map((key) =>
typeof query[key] === "object" && !Array.isArray(query[key])
? this.toQueryString(query[key] as QueryParamsType)
: this.addQueryParam(query, key),
: this.addArrayQueryParam(query, key),
)
.join("&");
}
Expand Down Expand Up @@ -308,7 +308,7 @@ export class HttpClient<SecurityDataType = unknown> {
path,
type,
query,
format = "json",
format,
baseUrl,
cancelToken,
...params
Expand All @@ -321,8 +321,9 @@ export class HttpClient<SecurityDataType = unknown> {
const requestParams = this.mergeRequestParams(params, secureParams);
const queryString = query && this.toQueryString(query);
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
const responseFormat = format && requestParams.format;

return fetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
...requestParams,
headers: {
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
Expand All @@ -335,19 +336,21 @@ export class HttpClient<SecurityDataType = unknown> {
r.data = (null as unknown) as T;
r.error = (null as unknown) as E;

const data = await response[format]()
.then((data) => {
if (r.ok) {
r.data = data;
} else {
r.error = data;
}
return r;
})
.catch((e) => {
r.error = e;
return r;
});
const data = !responseFormat
? r
: await response[responseFormat]()
.then((data) => {
if (r.ok) {
r.data = data;
} else {
r.error = data;
}
return r;
})
.catch((e) => {
r.error = e;
return r;
});

if (cancelToken) {
this.abortControllers.delete(cancelToken);
Expand Down
Loading

0 comments on commit 4b809f3

Please sign in to comment.