Skip to content

Commit 9048a33

Browse files
Merge pull request #162 from bitgopatmcl/add-original-decoded-request-to-superagent-error
feat: add decoded response to decodeExpecting errors
2 parents d3724a4 + e9537c1 commit 9048a33

File tree

2 files changed

+57
-18
lines changed

2 files changed

+57
-18
lines changed

packages/superagent-wrapper/src/request.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as h from '@api-ts/io-ts-http';
22
import * as E from 'fp-ts/Either';
33
import * as t from 'io-ts';
4+
import * as PathReporter from 'io-ts/lib/PathReporter';
45
import { URL } from 'url';
56
import { pipe } from 'fp-ts/function';
67

@@ -13,15 +14,24 @@ type SuccessfulResponses<Route extends h.HttpRoute> = {
1314
};
1415
}[keyof Route['response']];
1516

16-
type DecodedResponse<Route extends h.HttpRoute> =
17+
export type DecodedResponse<Route extends h.HttpRoute> =
1718
| SuccessfulResponses<Route>
1819
| {
1920
status: 'decodeError';
20-
error: unknown;
21+
error: string;
2122
body: unknown;
2223
original: Response;
2324
};
2425

26+
export class DecodeError extends Error {
27+
readonly decodedResponse: DecodedResponse<h.HttpRoute>;
28+
29+
constructor(message: string, decodedResponse: DecodedResponse<h.HttpRoute>) {
30+
super(message);
31+
this.decodedResponse = decodedResponse;
32+
}
33+
}
34+
2535
const decodedResponse = <Route extends h.HttpRoute>(res: DecodedResponse<Route>) => res;
2636

2737
type ExpectedDecodedResponse<
@@ -134,7 +144,7 @@ const patchRequest = <
134144
// DISCUSS: what's this non-standard HTTP status code?
135145
decodedResponse<Route>({
136146
status: 'decodeError',
137-
error,
147+
error: PathReporter.failure(error).join('\n'),
138148
body: res.body,
139149
original: res,
140150
}),
@@ -146,9 +156,16 @@ const patchRequest = <
146156
status: StatusCode,
147157
) =>
148158
patchedReq.decode().then((res) => {
149-
if (res.status !== status) {
150-
const error = res.error ?? `Unexpected status code ${String(res.status)}`;
151-
throw new Error(JSON.stringify(error));
159+
if (res.original.status !== status) {
160+
const error = `Unexpected response ${String(
161+
res.original.status,
162+
)}: ${JSON.stringify(res.original.body)}`;
163+
throw new DecodeError(error, res as DecodedResponse<h.HttpRoute>);
164+
} else if (res.status === 'decodeError') {
165+
const error = `Could not decode response ${String(
166+
res.original.status,
167+
)}: ${JSON.stringify(res.original.body)}`;
168+
throw new DecodeError(error, res as DecodedResponse<h.HttpRoute>);
152169
} else {
153170
return res as ExpectedDecodedResponse<Route, StatusCode>;
154171
}

packages/superagent-wrapper/test/request.test.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import superagent from 'superagent';
99
import supertest from 'supertest';
1010
import { URL } from 'url';
1111

12-
import { superagentRequestFactory, supertestRequestFactory } from '../src/request';
12+
import {
13+
DecodeError,
14+
superagentRequestFactory,
15+
supertestRequestFactory,
16+
} from '../src/request';
1317
import { buildApiClient } from '../src/routes';
1418

1519
const PostTestRoute = h.httpRoute({
@@ -33,6 +37,9 @@ const PostTestRoute = h.httpRoute({
3337
bar: t.number,
3438
baz: t.boolean,
3539
}),
40+
401: t.type({
41+
message: t.string,
42+
}),
3643
},
3744
});
3845

@@ -75,11 +82,16 @@ testApp.post('/test/:id', (req, res) => {
7582
res.send({
7683
invalid: 'response',
7784
});
78-
} else if (req.headers['x-send-unexpected-status-code']) {
85+
} else if (req.headers['x-send-unknown-status-code']) {
7986
res.status(400);
8087
res.send({
8188
error: 'bad request',
8289
});
90+
} else if (req.headers['x-send-unexpected-status-code']) {
91+
res.status(401);
92+
res.send({
93+
message: 'unauthorized',
94+
});
8395
} else {
8496
const response = PostTestRoute.response[200].encode({
8597
...params,
@@ -129,10 +141,10 @@ describe('request', () => {
129141
});
130142
});
131143

132-
it('gracefully handles unexpected status codes', async () => {
144+
it('gracefully handles unknown status codes', async () => {
133145
const response = await apiClient['api.v1.test']
134146
.post({ id: 1337, foo: 'test', bar: 42 })
135-
.set('x-send-unexpected-status-code', 'true')
147+
.set('x-send-unknown-status-code', 'true')
136148
.decode();
137149

138150
assert.equal(response.status, 'decodeError');
@@ -169,21 +181,32 @@ describe('request', () => {
169181
.post({ id: 1337, foo: 'test', bar: 42 })
170182
.set('x-send-unexpected-status-code', 'true')
171183
.decodeExpecting(200)
172-
.then(() => false)
173-
.catch(() => true);
184+
.then(() => '')
185+
.catch((err) => (err instanceof DecodeError ? err.message : ''));
186+
187+
assert.deepEqual(result, 'Unexpected response 401: {"message":"unauthorized"}');
188+
});
189+
190+
it('throws for unknown responses', async () => {
191+
const result = await apiClient['api.v1.test']
192+
.post({ id: 1337, foo: 'test', bar: 42 })
193+
.set('x-send-unknown-status-code', 'true')
194+
.decodeExpecting(200)
195+
.then(() => '')
196+
.catch((err) => (err instanceof DecodeError ? err.message : ''));
174197

175-
assert.isTrue(result);
198+
assert.deepEqual(result, 'Unexpected response 400: {"error":"bad request"}');
176199
});
177200

178201
it('throws for decode errors', async () => {
179202
const result = await apiClient['api.v1.test']
180203
.post({ id: 1337, foo: 'test', bar: 42 })
181204
.set('x-send-invalid-response-body', 'true')
182205
.decodeExpecting(200)
183-
.then(() => false)
184-
.catch(() => true);
206+
.then(() => '')
207+
.catch((err) => (err instanceof DecodeError ? err.message : ''));
185208

186-
assert.isTrue(result);
209+
assert.deepEqual(result, 'Could not decode response 200: {"invalid":"response"}');
187210
});
188211
});
189212

@@ -210,8 +233,7 @@ describe('request', () => {
210233
.set('x-send-unexpected-status-code', 'true')
211234
.decode();
212235

213-
assert.equal(req.status, 'decodeError');
214-
assert.equal(req.original.status, 400);
236+
assert.equal(req.status, 401);
215237
});
216238
});
217239
});

0 commit comments

Comments
 (0)