Skip to content

Commit 6822c52

Browse files
committed
Add Either support
1 parent cc55414 commit 6822c52

File tree

4 files changed

+176
-7
lines changed

4 files changed

+176
-7
lines changed

src/templates/core/ApiError.hbs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
import type { ApiRequestOptions } from './ApiRequestOptions';
44
import type { ApiResult } from './ApiResult';
55

6-
export class ApiError extends Error {
6+
export class ApiError<
7+
StatusCode extends number = number,
8+
ApiErrorBody = any,
9+
> extends Error {
710
public readonly url: string;
8-
public readonly status: number;
11+
public readonly status: StatusCode;
912
public readonly statusText: string;
10-
public readonly body: any;
13+
public readonly body: ApiErrorBody;
1114
public readonly request: ApiRequestOptions;
1215

13-
constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
16+
constructor(request: ApiRequestOptions, response: ApiResult<StatusCode, ApiErrorBody>, message: string) {
1417
super(message);
1518

1619
this.name = 'ApiError';

src/templates/core/ApiResult.hbs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
{{>header}}
22

3-
export type ApiResult = {
3+
export type ApiResult<
4+
StatusCode extends number = number,
5+
ApiErrorBody = any,
6+
> = {
47
readonly url: string;
58
readonly ok: boolean;
6-
readonly status: number;
9+
readonly status: StatusCode;
710
readonly statusText: string;
8-
readonly body: any;
11+
readonly body: ApiErrorBody;
912
};
13+
14+
export interface Left<E> {
15+
readonly _tag: 'Left';
16+
readonly left: E;
17+
}
18+
19+
export interface Right<A> {
20+
readonly _tag: 'Right';
21+
readonly right: A;
22+
}
23+
24+
export type Either<E, A> = Left<E> | Right<A>;

src/templates/exportService.hbs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,14 @@ import type { CancelablePromise } from '../core/CancelablePromise';
2525
import { BaseHttpRequest } from '../core/BaseHttpRequest';
2626
{{else}}
2727
import type { BaseHttpRequest } from '../core/BaseHttpRequest';
28+
import { Left, Right, Either } from '../core/ApiResult';
29+
import { ApiError } from '../core/ApiError';
2830
{{/equals}}
2931
{{else}}
3032
import { OpenAPI } from '../core/OpenAPI';
3133
import { request as __request } from '../core/request';
34+
import { Left, Right, Either } from '../core/ApiResult';
35+
import { ApiError } from '../core/ApiError';
3236
{{/if}}
3337

3438
{{#equals @root.httpClient 'angular'}}
@@ -146,5 +150,128 @@ export class {{{name}}}{{{@root.postfix}}} {
146150
});
147151
}
148152

153+
{{#equals @root.httpClient 'angular'}}
154+
{{else}}
155+
/**
156+
{{#if deprecated}}
157+
* @deprecated
158+
{{/if}}
159+
{{#if summary}}
160+
* {{{escapeComment summary}}}
161+
{{/if}}
162+
{{#if description}}
163+
* {{{escapeComment description}}}
164+
{{/if}}
165+
{{#unless @root.useOptions}}
166+
{{#if parameters}}
167+
{{#each parameters}}
168+
* @param {{{name}}} {{#if description}}{{{escapeComment description}}}{{/if}}
169+
{{/each}}
170+
{{/if}}
171+
{{/unless}}
172+
{{#each results}}
173+
* @returns {{{type}}} {{#if description}}{{{escapeComment description}}}{{/if}}
174+
{{/each}}
175+
* @throws ApiError
176+
*/
177+
{{#if @root.exportClient}}
178+
{{#equals @root.httpClient 'angular'}}
179+
public async {{{name}}}Either({{>parameters}}): Observable<{{>result}}> {
180+
try {
181+
const result: ({{>result}}) = await this.httpRequest.request({
182+
{{else}}
183+
public async {{{name}}}Either({{>parameters}}): Promise<Either<ApiError, {{>result}}>> {
184+
try {
185+
const result: ({{>result}}) = await this.httpRequest.request({
186+
{{/equals}}
187+
{{else}}
188+
{{#equals @root.httpClient 'angular'}}
189+
public async {{{name}}}Either({{>parameters}}): Observable<{{>result}}> {
190+
try {
191+
const result: ({{>result}}) = await __request(OpenAPI, this.http, {
192+
{{else}}
193+
public static async {{{name}}}Either({{>parameters}}): Promise<Either<ApiError, {{>result}}>> {
194+
try {
195+
const result: ({{>result}}) = await __request(OpenAPI, {
196+
{{/equals}}
197+
{{/if}}
198+
method: '{{{method}}}',
199+
url: '{{{path}}}',
200+
{{#if parametersPath}}
201+
path: {
202+
{{#each parametersPath}}
203+
'{{{prop}}}': {{{name}}},
204+
{{/each}}
205+
},
206+
{{/if}}
207+
{{#if parametersCookie}}
208+
cookies: {
209+
{{#each parametersCookie}}
210+
'{{{prop}}}': {{{name}}},
211+
{{/each}}
212+
},
213+
{{/if}}
214+
{{#if parametersHeader}}
215+
headers: {
216+
{{#each parametersHeader}}
217+
'{{{prop}}}': {{{name}}},
218+
{{/each}}
219+
},
220+
{{/if}}
221+
{{#if parametersQuery}}
222+
query: {
223+
{{#each parametersQuery}}
224+
'{{{prop}}}': {{{name}}},
225+
{{/each}}
226+
},
227+
{{/if}}
228+
{{#if parametersForm}}
229+
formData: {
230+
{{#each parametersForm}}
231+
'{{{prop}}}': {{{name}}},
232+
{{/each}}
233+
},
234+
{{/if}}
235+
{{#if parametersBody}}
236+
{{#equals parametersBody.in 'formData'}}
237+
formData: {{{parametersBody.name}}},
238+
{{/equals}}
239+
{{#equals parametersBody.in 'body'}}
240+
body: {{{parametersBody.name}}},
241+
{{/equals}}
242+
{{#if parametersBody.mediaType}}
243+
mediaType: '{{{parametersBody.mediaType}}}',
244+
{{/if}}
245+
{{/if}}
246+
{{#if responseHeader}}
247+
responseHeader: '{{{responseHeader}}}',
248+
{{/if}}
249+
{{#if errors}}
250+
errors: {
251+
{{#each errors}}
252+
{{{code}}}: `{{{escapeDescription description}}}`,
253+
{{/each}}
254+
},
255+
{{/if}}
256+
});
257+
258+
const right: Right<{{>result}}> = {
259+
_tag: 'Right',
260+
right: result,
261+
};
262+
263+
return right;
264+
} catch (e) {
265+
const left: Left<ApiError> = {
266+
_tag: 'Left',
267+
left: e as ApiError,
268+
};
269+
270+
return left;
271+
}
272+
}
273+
{{/equals}}
274+
275+
149276
{{/each}}
150277
}

test/e2e/client.node.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ describe('client.node', () => {
2828
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
2929
});
3030

31+
it('can return an Either', async () => {
32+
const { ApiClient } = require('./generated/client/node/index.js');
33+
const tokenRequest = jest.fn().mockResolvedValue('MY_TOKEN');
34+
const client = new ApiClient({
35+
TOKEN: tokenRequest,
36+
USERNAME: undefined,
37+
PASSWORD: undefined,
38+
});
39+
const resultEither = await client.simple.getCallWithoutParametersAndResponseEither();
40+
expect(resultEither._tag).toBe('Right');
41+
const result = resultEither.right;
42+
expect(tokenRequest.mock.calls.length).toBe(1);
43+
expect(result.headers.authorization).toBe('Bearer MY_TOKEN');
44+
});
45+
3146
it('uses credentials', async () => {
3247
const { ApiClient } = require('./generated/client/node/index.js');
3348
const client = new ApiClient({
@@ -116,6 +131,15 @@ describe('client.node', () => {
116131
);
117132
});
118133

134+
it('can return errors as Either', async () => {
135+
const { ApiClient } = require('./generated/client/node/index.js');
136+
const client = new ApiClient();
137+
const result = await client.error.testErrorCodeEither(500);
138+
expect(result._tag).toBe('Left');
139+
expect(result.left.name).toBe('ApiError');
140+
expect(result.left.body.message).toBe('hello world');
141+
});
142+
119143
it('should throw unknown error (409)', async () => {
120144
let error;
121145
try {

0 commit comments

Comments
 (0)