Skip to content

Commit c47d741

Browse files
committed
feat(express): migrated update express wallet to typed routes
TICKET: WP-5416
1 parent 9919b93 commit c47d741

File tree

4 files changed

+141
-3
lines changed

4 files changed

+141
-3
lines changed

modules/express/src/clientRoutes.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1005,9 +1005,11 @@ export async function handleV2EnableTokens(req: express.Request) {
10051005
* Handle Update Wallet
10061006
* @param req
10071007
*/
1008-
async function handleWalletUpdate(req: express.Request): Promise<unknown> {
1008+
export async function handleWalletUpdate(
1009+
req: ExpressApiRouteRequest<'express.wallet.update', 'put'>
1010+
): Promise<unknown> {
10091011
// If it's a lightning coin, use the lightning-specific handler
1010-
if (isLightningCoinName(req.params.coin)) {
1012+
if (isLightningCoinName(req.decoded.coin)) {
10111013
return handleUpdateLightningWalletCoinSpecific(req);
10121014
}
10131015

@@ -1607,7 +1609,7 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
16071609
// generate wallet
16081610
app.post('/api/v2/:coin/wallet/generate', parseBody, prepareBitGo(config), promiseWrapper(handleV2GenerateWallet));
16091611

1610-
app.put('/express/api/v2/:coin/wallet/:id', parseBody, prepareBitGo(config), promiseWrapper(handleWalletUpdate));
1612+
router.put('express.wallet.update', [prepareBitGo(config), typedPromiseWrapper(handleWalletUpdate)]);
16111613

16121614
// change wallet passphrase
16131615
app.post(

modules/express/src/typedRoutes/api/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { PostCreateAddress } from './v2/createAddress';
2626
import { PutFanoutUnspents } from './v1/fanoutUnspents';
2727
import { PostOfcSignPayload } from './v2/ofcSignPayload';
2828
import { PostWalletRecoverToken } from './v2/walletRecoverToken';
29+
import { PutExpressWalletUpdate } from './v2/expressWalletUpdate';
2930

3031
export const ExpressApi = apiSpec({
3132
'express.ping': {
@@ -100,6 +101,9 @@ export const ExpressApi = apiSpec({
100101
'express.v2.wallet.recovertoken': {
101102
post: PostWalletRecoverToken,
102103
},
104+
'express.wallet.update': {
105+
put: PutExpressWalletUpdate,
106+
},
103107
});
104108

105109
export type ExpressApi = typeof ExpressApi;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as t from 'io-ts';
2+
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
3+
import { BitgoExpressError } from '../../schemas/error';
4+
5+
/**
6+
* Parameters for Express Wallet Update
7+
*/
8+
export const ExpressWalletUpdateParams = {
9+
/** Coin ticker / chain identifier */
10+
coin: t.string,
11+
/** Wallet ID */
12+
id: t.string,
13+
} as const;
14+
15+
/**
16+
* Request body for Express Wallet Update
17+
*/
18+
export const ExpressWalletUpdateBody = {
19+
/** The host address of the lightning signer node. */
20+
signerHost: t.string,
21+
/** The TLS certificate for the lighting signer node encoded to base64. */
22+
signerTlsCert: t.string,
23+
/** (Optional) The signer macaroon for the lighting signer node. */
24+
signerMacaroon: optional(t.string),
25+
/** The wallet passphrase (used locally to decrypt and sign). */
26+
passphrase: t.string,
27+
} as const;
28+
29+
/**
30+
* Response for Express Wallet Update
31+
*/
32+
export const ExpressWalletUpdateResponse = {
33+
/** Updated Wallet */
34+
200: t.UnknownRecord,
35+
/** Bad Request */
36+
400: BitgoExpressError,
37+
/** Forbidden */
38+
403: BitgoExpressError,
39+
/** Not Found */
40+
404: BitgoExpressError,
41+
} as const;
42+
43+
/**
44+
* Express - Update Wallet
45+
* The express update wallet route is meant to be used for lightning (lnbtc/tlnbtc).
46+
* For other coins, use the standard wallet update endpoint.
47+
*
48+
* @operationId express.wallet.update
49+
*/
50+
export const PutExpressWalletUpdate = httpRoute({
51+
path: '/express/api/v2/{coin}/wallet/{id}',
52+
method: 'PUT',
53+
request: httpRequest({
54+
params: ExpressWalletUpdateParams,
55+
body: ExpressWalletUpdateBody,
56+
}),
57+
response: ExpressWalletUpdateResponse,
58+
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
2+
import { BitGo } from 'bitgo';
3+
import { common, decodeOrElse } from '@bitgo/sdk-core';
4+
import nock from 'nock';
5+
import { ExpressApiRouteRequest } from '../../../src/typedRoutes/api';
6+
import { ExpressWalletUpdateResponse } from '../../../src/typedRoutes/api/v2/expressWalletUpdate';
7+
import { handleWalletUpdate } from '../../../src/clientRoutes';
8+
import { apiData } from './lightning/lightningSignerFixture';
9+
10+
describe('express.wallet.update (unit)', () => {
11+
let bitgo: TestBitGoAPI;
12+
let bgUrl: string;
13+
14+
before(async function () {
15+
if (!nock.isActive()) {
16+
nock.activate();
17+
}
18+
bitgo = TestBitGo.decorate(BitGo, { env: 'test' });
19+
bitgo.initializeTestVars();
20+
bgUrl = common.Environments[bitgo.getEnv()].uri;
21+
nock.disableNetConnect();
22+
nock.enableNetConnect('127.0.0.1');
23+
});
24+
25+
after(() => {
26+
if (nock.isActive()) {
27+
nock.restore();
28+
}
29+
});
30+
31+
it('decodes successful response', async () => {
32+
const walletId = apiData.wallet.id;
33+
const coin = apiData.wallet.coin;
34+
const wpGet = nock(bgUrl)
35+
.get(`/api/v2/${coin}/wallet/${walletId}`)
36+
.query({ includeBalance: false })
37+
.reply(200, apiData.wallet);
38+
const wpKeychainNocks = [
39+
nock(bgUrl).get(`/api/v2/${coin}/key/${apiData.userAuthKey.id}`).reply(200, apiData.userAuthKey),
40+
nock(bgUrl).get(`/api/v2/${coin}/key/${apiData.nodeAuthKey.id}`).reply(200, apiData.nodeAuthKey),
41+
];
42+
const wpPut = nock(bgUrl)
43+
.put(`/api/v2/${coin}/wallet/${walletId}`)
44+
.reply(200, { id: walletId, label: 'updated', coinSpecific: {} });
45+
46+
const req = {
47+
bitgo,
48+
params: { coin: coin, id: walletId },
49+
body: {
50+
signerHost: 'host.example',
51+
signerTlsCert: 'cert',
52+
signerMacaroon: 'mac and cheeze',
53+
passphrase: apiData.initWalletRequestBody.passphrase,
54+
},
55+
decoded: {
56+
coin: coin,
57+
id: walletId,
58+
signerHost: 'host.example',
59+
signerTlsCert: 'cert',
60+
signerMacaroon: 'mac and cheeze',
61+
passphrase: apiData.initWalletRequestBody.passphrase,
62+
},
63+
} as unknown as ExpressApiRouteRequest<'express.wallet.update', 'put'>;
64+
65+
const res = await handleWalletUpdate(req);
66+
decodeOrElse('ExpressWalletUpdateResponse200', ExpressWalletUpdateResponse[200], res, (errors) => {
67+
throw new Error(`Response did not match expected codec: ${errors}`);
68+
});
69+
70+
wpPut.done();
71+
wpGet.done();
72+
wpKeychainNocks.forEach((s) => s.done());
73+
});
74+
});

0 commit comments

Comments
 (0)