acacode / acacode/swagger-typescript-api

If an error occurs in a web call then it throws 'Response', it would be nice to be able to customize this behaviour

オープン
#95 コメント 2 件 リアクション 6 件 担当者 0 名 GitHub で見る
主要言語
TypeScript
スター
4.1k
フォーク
436
PR マージ指標
30日以内にマージされた PR はありません

説明

Converting the http response into the return data or an error takes place in

return fetch(requestUrl, requestOptions).then(async (response) => {
const data = await this.safeParseResponse(response);
if (!response.ok) throw data;
return data;
});

If this could be placed into a protected function, then its could be sub classed and overridden.

i.e.

```
return fetch(requestUrl, requestOptions).then(async (response) => {
return await this.handleResponse(response);
});
}

protected async handleResponse(response: Response): Promise> {
const data = await this.safeParseResponse(response);
if (!response.ok) throw data;
return data;
}
```

Note safeParseResponse also needs changing to protected, it may also be a good idea to export HttpResponse

Full Template.
```
export type RequestParams = Omit & {
secure?: boolean;
}

{{#hasQueryRoutes}}
export type RequestQueryParamsType = Record;
{{/hasQueryRoutes}}

interface ApiConfig<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}> {
baseUrl?: string;
baseApiParams?: RequestParams;
securityWorker?: (securityData: SecurityDataType) => RequestParams;
}

{{#generateResponses}}
/** Overrided Promise type. Needs for additional typings of `.catch` callback */
type TPromise = Omit, "then" | "catch"> & {
then(onfulfilled?: ((value: ResolveType) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: RejectType) => TResult2 | PromiseLike) | undefined | null): TPromise;
catch(onrejected?: ((reason: RejectType) => TResult | PromiseLike) | undefined | null): TPromise;
}
{{/generateResponses}}

interface HttpResponse extends Response {
data: D | null;
error: E | null;
}

enum BodyType {
Json,
{{#hasFormDataRoutes}}
FormData,
{{/hasFormDataRoutes}}
}

class HttpClient<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}> {
public baseUrl: string = "{{apiConfig.baseUrl}}";
private securityData: SecurityDataType = (null as any);
private securityWorker: null | ApiConfig<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}>["securityWorker"] = null;

private baseApiParams: RequestParams = {
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
}

constructor(apiConfig: ApiConfig<{{#apiConfig.generic}}{{name}},{{/apiConfig.generic}}> = {}) {
Object.assign(this, apiConfig);
}

public setSecurityData = (data: SecurityDataType) => {
this.securityData = data
}

{{#hasQueryRoutes}}
private addQueryParam(query: RequestQueryParamsType, key: string) {
return encodeURIComponent(key) + "=" + encodeURIComponent(Array.isArray(query[key]) ? query[key].join(",") : query[key])
}

protected addQueryParams(rawQuery?: RequestQueryParamsType): string {
const query = rawQuery || {};
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
return keys.length ? `?${keys.map(key =>
typeof query[key] === "object" && !Array.isArray(query[key]) ?
this.addQueryParams(query[key] as object).substring(1) :
this.addQueryParam(query, key)).join("&")
}` : "";
}
{{/hasQueryRoutes}}

private bodyFormatters: Record any> = {
[BodyType.Json]: JSON.stringify,
{{#hasFormDataRoutes}}
[BodyType.FormData]: (input: any) =>
Object.keys(input).reduce((data, key) => {
data.append(key, input[key]);
return data;
}, new FormData()),
{{/hasFormDataRoutes}}
}

private mergeRequestOptions(params: RequestParams, securityParams?: RequestParams): RequestParams {
return {
...this.baseApiParams,
...params,
...(securityParams || {}),
headers: {
...(this.baseApiParams.headers || {}),
...(params.headers || {}),
...((securityParams && securityParams.headers) || {})
}
}
}

protected safeParseResponse = (response: Response): Promise> => {
const r = response as HttpResponse;
r.data = null;
r.error = null;

return response
.json()
.then((data) => {
if (r.ok) {
r.data = data;
} else {
r.error = data;
}
return r;
})
.catch((e) => {
r.error = e;
return r;
});
}

public request = (
path: string,
method: string,
{ secure, ...params }: RequestParams = {},
body?: any,
bodyType?: BodyType,
secureByDefault?: boolean,
): {{#generateResponses}}TPromise>{{/generateResponses}}{{^generateResponses}}Promise>{{/generateResponses}} => {
const requestUrl = `${this.baseUrl}${path}`;
const secureOptions = (secureByDefault || secure) && this.securityWorker ? this.securityWorker(this.securityData) : {};
const requestOptions = {
...this.mergeRequestOptions(params, secureOptions),
method,
body: body ? this.bodyFormatters[bodyType || BodyType.Json](body) : null,
}

return fetch(requestUrl, requestOptions).then(async (response) => {
return await this.handleResponse(response);
});
}

protected async handleResponse(response: Response): Promise> {
const data = await this.safeParseResponse(response);
if (!response.ok) throw data;
return data;
}
}

```

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

評価

この issue はまだ評価されていません。

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。