Skip to content

Commit 712585c

Browse files
committed
fix(express): fix express overriding 2xx status
changed the redirectRequest method so it returns the original status from WP not only the body of the response WP-2961 TICKET: WP-2961
1 parent 2cd6b5d commit 712585c

File tree

2 files changed

+158
-6
lines changed

2 files changed

+158
-6
lines changed

modules/express/src/clientRoutes.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,7 +1114,13 @@ async function handleNetworkV1EnterpriseClientConnections(
11141114
* @param req
11151115
* @param next
11161116
*/
1117-
function redirectRequest(bitgo: BitGo, method: string, url: string, req: express.Request, next: express.NextFunction) {
1117+
export function redirectRequest(
1118+
bitgo: BitGo,
1119+
method: string,
1120+
url: string,
1121+
req: express.Request,
1122+
next: express.NextFunction
1123+
) {
11181124
let request;
11191125

11201126
switch (method) {
@@ -1142,7 +1148,11 @@ function redirectRequest(bitgo: BitGo, method: string, url: string, req: express
11421148
if (req.params.enterpriseId) {
11431149
request.set('enterprise-id', req.params.enterpriseId);
11441150
}
1145-
return request.result();
1151+
1152+
return request.result().then((result) => {
1153+
const status = request.res?.statusCode || 200;
1154+
return { status, body: result };
1155+
});
11461156
}
11471157

11481158
// something has presumably gone wrong
@@ -1217,8 +1227,7 @@ function prepareBitGo(config: Config) {
12171227
next();
12181228
};
12191229
}
1220-
1221-
type RequestHandlerResponse = string | unknown | undefined;
1230+
type RequestHandlerResponse = string | unknown | undefined | { status: number; body: unknown };
12221231
interface RequestHandler extends express.RequestHandler<ParamsDictionary, any, RequestHandlerResponse> {
12231232
(req: express.Request, res: express.Response, next: express.NextFunction):
12241233
| RequestHandlerResponse
@@ -1260,12 +1269,17 @@ function handleRequestHandlerError(res: express.Response, error: unknown) {
12601269
* Promise handler wrapper to handle sending responses and error cases
12611270
* @param promiseRequestHandler
12621271
*/
1263-
function promiseWrapper(promiseRequestHandler: RequestHandler) {
1272+
export function promiseWrapper(promiseRequestHandler: RequestHandler) {
12641273
return async function promWrapper(req: express.Request, res: express.Response, next: express.NextFunction) {
12651274
debug(`handle: ${req.method} ${req.originalUrl}`);
12661275
try {
12671276
const result = await promiseRequestHandler(req, res, next);
1268-
res.status(200).send(result);
1277+
if (typeof result === 'object' && result !== null && 'body' in result && 'status' in result) {
1278+
const { status, body } = result as { status: number; body: unknown };
1279+
res.status(status).send(body);
1280+
} else {
1281+
res.status(200).send(result);
1282+
}
12691283
} catch (e) {
12701284
handleRequestHandlerError(res, e);
12711285
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import 'should';
2+
import * as sinon from 'sinon';
3+
import * as express from 'express';
4+
import { ApiResponseError, BitGo } from 'bitgo';
5+
import { promiseWrapper, redirectRequest } from '../../../src/clientRoutes';
6+
7+
describe('common methods', () => {
8+
const sandbox = sinon.createSandbox();
9+
describe('redirectRequest', () => {
10+
let bitgo: BitGo;
11+
let req: express.Request;
12+
let next: express.NextFunction;
13+
14+
beforeEach(() => {
15+
bitgo = new BitGo({ env: 'test' });
16+
req = {
17+
body: {},
18+
params: {},
19+
bitgo,
20+
} as express.Request;
21+
next = () => undefined;
22+
});
23+
24+
afterEach(() => {
25+
sandbox.verifyAndRestore();
26+
});
27+
28+
it('should handle GET request and return status and body', async () => {
29+
const url = 'https://example.com/api';
30+
const response = { res: { statusCode: 200 }, result: async () => ({ success: true }) };
31+
sandbox
32+
.stub(bitgo, 'get')
33+
.withArgs(url)
34+
.returns(response as any);
35+
36+
const result = await redirectRequest(bitgo, 'GET', url, req, next);
37+
result.status.should.equal(200);
38+
result.body.should.deepEqual({ success: true });
39+
});
40+
41+
it('should handle POST request and return status and body', async () => {
42+
const url = 'https://example.com/api';
43+
req.body = { data: 'test' };
44+
const response = { res: { statusCode: 201 }, result: async () => ({ success: true }) };
45+
sandbox
46+
.stub(bitgo, 'post')
47+
.withArgs(url)
48+
.returns({ send: () => response } as any);
49+
50+
const result = await redirectRequest(bitgo, 'POST', url, req, next);
51+
result.status.should.equal(201);
52+
result.body.should.deepEqual({ success: true });
53+
});
54+
55+
it('should handle error response and return status and body', async () => {
56+
const url = 'https://example.com/api';
57+
const response = {
58+
result: async () => {
59+
throw new ApiResponseError('Bad Request', 400);
60+
},
61+
};
62+
sandbox
63+
.stub(bitgo, 'get')
64+
.withArgs(url)
65+
.returns(response as any);
66+
67+
await redirectRequest(bitgo, 'GET', url, req, next).should.be.rejectedWith(ApiResponseError, {
68+
message: 'Bad Request',
69+
status: 400,
70+
});
71+
});
72+
});
73+
74+
describe('promiseWrapper', () => {
75+
afterEach(() => {
76+
sandbox.restore();
77+
});
78+
it('should handle successful request', async () => {
79+
const handler = sandbox.stub().resolves({ status: 200, body: { success: true } });
80+
const req: any = {};
81+
const res: any = {
82+
status: sandbox.stub().returnsThis(),
83+
send: sandbox.stub().returnsThis(),
84+
};
85+
const next = sandbox.stub();
86+
87+
await promiseWrapper(handler)(req, res, next);
88+
89+
res.status.calledWith(200).should.be.true();
90+
res.send.calledWith({ success: true }).should.be.true();
91+
});
92+
93+
it('should handle successful request with status 202', async () => {
94+
const handler = sandbox.stub().resolves({ status: 202, body: { success: true } });
95+
const req: any = {};
96+
const res: any = {
97+
status: sandbox.stub().returnsThis(),
98+
send: sandbox.stub().returnsThis(),
99+
};
100+
const next = sandbox.stub();
101+
102+
await promiseWrapper(handler)(req, res, next);
103+
104+
res.status.calledWith(202).should.be.true();
105+
res.send.calledWith({ success: true }).should.be.true();
106+
});
107+
108+
it('should handle successful request with default status', async () => {
109+
const handler = sandbox.stub().resolves({ success: true });
110+
const req: any = {};
111+
const res: any = {
112+
status: sandbox.stub().returnsThis(),
113+
send: sandbox.stub().returnsThis(),
114+
};
115+
const next = sandbox.stub();
116+
117+
await promiseWrapper(handler)(req, res, next);
118+
119+
res.status.calledWith(200).should.be.true();
120+
res.send.calledWith({ success: true }).should.be.true();
121+
});
122+
123+
it('should handle error request', async () => {
124+
const handler = sandbox.stub().rejects(new Error('Test error'));
125+
const req: any = {};
126+
const res: any = {
127+
status: sandbox.stub().returnsThis(),
128+
send: sandbox.stub().returnsThis(),
129+
};
130+
const next = sandbox.stub();
131+
132+
await promiseWrapper(handler)(req, res, next);
133+
134+
res.status.calledWith(500).should.be.true();
135+
res.send.calledWithMatch((result: any) => result.message === 'Test error').should.be.true();
136+
});
137+
});
138+
});

0 commit comments

Comments
 (0)