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

Abierto
#95 2 comentarios 6 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
TypeScript
Estrellas
4.1k
Forks
436
Métricas de merge de PR
Sin PR fusionados en 30 d

Descripción

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;
}
}

```

Guía de contribución

No hay ninguna guía de contribución indexada para este repositorio

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.