Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: various angular client fixes #167

Merged
merged 8 commits into from
Mar 28, 2024
5 changes: 5 additions & 0 deletions .changeset/clever-pants-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/openapi-ts": patch
---

fix async response interceptors when using angular client
5 changes: 5 additions & 0 deletions .changeset/spotty-apricots-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/openapi-ts": patch
---

fix deprecation warning on `throwError` in Angular client
5 changes: 5 additions & 0 deletions .changeset/stale-deers-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/openapi-ts": patch
---

Do not create or export CancelablePromise when using Angular client
5 changes: 5 additions & 0 deletions .changeset/witty-ladybugs-sneeze.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hey-api/openapi-ts": patch
---

fix providing interceptors in Angular client
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ OpenAPI.interceptors.response.use(async (response) => {

If you need to remove an interceptor, pass the same function to `OpenAPI.interceptors.request.eject()` or `OpenAPI.interceptors.response.eject()`.

> ⚠️ Angular client does not currently support request interceptors and async response interceptors.
> ⚠️ Angular client does not currently support request interceptors.

## Migrating

Expand Down
4 changes: 4 additions & 0 deletions src/templates/client.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AngularHttpRequest } from './core/AngularHttpRequest';
import { BaseHttpRequest } from './core/BaseHttpRequest';
import type { OpenAPIConfig } from './core/OpenAPI';
import { OpenAPI } from './core/OpenAPI';
import { Interceptors } from './core/OpenAPI';
{{else}}
import type { BaseHttpRequest } from './core/BaseHttpRequest';
import type { OpenAPIConfig } from './core/OpenAPI';
Expand Down Expand Up @@ -35,6 +36,9 @@ import { {{{name}}}{{{@root.$config.postfixServices}}} } from './services/{{{nam
PASSWORD: OpenAPI?.PASSWORD,
HEADERS: OpenAPI?.HEADERS,
ENCODE_PATH: OpenAPI?.ENCODE_PATH,
interceptors: {
response: new Interceptors(),
},
} as OpenAPIConfig,
},
{
Expand Down
4 changes: 0 additions & 4 deletions src/templates/core/OpenAPI.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ import type { TResult } from './types';
{{/if}}

type Headers = Record<string, string>;
{{#equals @root.$config.client 'angular'}}
type Middleware<T> = (value: T) => T;
{{else}}
type Middleware<T> = (value: T) => T | Promise<T>;
{{/equals}}
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;

export class Interceptors<T> {
Expand Down
8 changes: 4 additions & 4 deletions src/templates/core/angular/request.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ export const request = <T>(config: OpenAPIConfig, http: HttpClient, options: Api
switchMap(headers => {
return sendRequest<T>(config, options, http, url, body, formData, headers);
}),
map(response => {
switchMap(async response => {
for (const fn of config.interceptors.response._fns) {
response = fn(response)
response = await fn(response)
}
const responseBody = getResponseBody(response);
const responseHeader = getResponseHeader(response, options.responseHeader);
Expand All @@ -87,7 +87,7 @@ export const request = <T>(config: OpenAPIConfig, http: HttpClient, options: Api
}),
catchError((error: HttpErrorResponse) => {
if (!error.status) {
return throwError(error);
return throwError(() => error);
}
return of({
url,
Expand All @@ -102,7 +102,7 @@ export const request = <T>(config: OpenAPIConfig, http: HttpClient, options: Api
return result.body as T;
}),
catchError((error: ApiError) => {
return throwError(error);
return throwError(() => error);
}),
);
};
2 changes: 2 additions & 0 deletions src/templates/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export { ApiError } from './core/ApiError';
{{#if @root.$config.name}}
export { BaseHttpRequest } from './core/BaseHttpRequest';
{{/if}}
{{#notEquals @root.$config.client 'angular'}}
export { CancelablePromise, CancelError } from './core/CancelablePromise';
{{/notEquals}}
export { OpenAPI } from './core/OpenAPI';
export type { OpenAPIConfig } from './core/OpenAPI';
{{/if}}
Expand Down
16 changes: 9 additions & 7 deletions src/utils/write/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ export const writeClientCore = async (
...context,
})
);
await writeFileSync(
path.resolve(outputPath, 'CancelablePromise.ts'),
templates.core.cancelablePromise({
$config: config,
...context,
})
);
if (config.client !== 'angular') {
await writeFileSync(
path.resolve(outputPath, 'CancelablePromise.ts'),
templates.core.cancelablePromise({
$config: config,
...context,
})
);
}
await writeFileSync(
path.resolve(outputPath, 'request.ts'),
templates.core.request({
Expand Down
21 changes: 21 additions & 0 deletions test/__snapshots__/v3_angular/core/ApiError.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ApiRequestOptions } from './ApiRequestOptions';
import type { ApiResult } from './ApiResult';

export class ApiError extends Error {
public readonly url: string;
public readonly status: number;
public readonly statusText: string;
public readonly body: unknown;
public readonly request: ApiRequestOptions;

constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
super(message);

this.name = 'ApiError';
this.url = response.url;
this.status = response.status;
this.statusText = response.statusText;
this.body = response.body;
this.request = request;
}
}
13 changes: 13 additions & 0 deletions test/__snapshots__/v3_angular/core/ApiRequestOptions.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type ApiRequestOptions = {
readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
readonly url: string;
readonly path?: Record<string, unknown>;
readonly cookies?: Record<string, unknown>;
readonly headers?: Record<string, unknown>;
readonly query?: Record<string, unknown>;
readonly formData?: Record<string, unknown>;
readonly body?: any;
readonly mediaType?: string;
readonly responseHeader?: string;
readonly errors?: Record<number, string>;
};
7 changes: 7 additions & 0 deletions test/__snapshots__/v3_angular/core/ApiResult.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type ApiResult<TData = any> = {
readonly body: TData;
readonly ok: boolean;
readonly status: number;
readonly statusText: string;
readonly url: string;
};
54 changes: 54 additions & 0 deletions test/__snapshots__/v3_angular/core/OpenAPI.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { HttpResponse } from '@angular/common/http';
import type { ApiRequestOptions } from './ApiRequestOptions';
import type { TResult } from './types';

type Headers = Record<string, string>;
type Middleware<T> = (value: T) => T | Promise<T>;
type Resolver<T> = (options: ApiRequestOptions) => Promise<T>;

export class Interceptors<T> {
_fns: Middleware<T>[];

constructor() {
this._fns = [];
}

eject(fn: Middleware<T>) {
const index = this._fns.indexOf(fn);
if (index !== -1) {
this._fns = [...this._fns.slice(0, index), ...this._fns.slice(index + 1)];
}
}

use(fn: Middleware<T>) {
this._fns = [...this._fns, fn];
}
}

export type OpenAPIConfig = {
BASE: string;
CREDENTIALS: 'include' | 'omit' | 'same-origin';
ENCODE_PATH?: ((path: string) => string) | undefined;
HEADERS?: Headers | Resolver<Headers> | undefined;
PASSWORD?: string | Resolver<string> | undefined;
RESULT?: TResult;
TOKEN?: string | Resolver<string> | undefined;
USERNAME?: string | Resolver<string> | undefined;
VERSION: string;
WITH_CREDENTIALS: boolean;
interceptors: { response: Interceptors<HttpResponse<any>> };
};

export const OpenAPI: OpenAPIConfig = {
BASE: 'http://localhost:3000/base',
CREDENTIALS: 'include',
ENCODE_PATH: undefined,
HEADERS: undefined,
PASSWORD: undefined,
RESULT: 'body',
TOKEN: undefined,
USERNAME: undefined,
VERSION: '1.0',
WITH_CREDENTIALS: false,
interceptors: { response: new Interceptors() },
};
Loading