Skip to content

Commit e633d12

Browse files
feat(mbe): add typing for typed request handlers
Ticket: WP-4622
1 parent 6de52b8 commit e633d12

File tree

3 files changed

+45
-25
lines changed

3 files changed

+45
-25
lines changed

src/masterBitgoExpress/generateWallet.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
GenerateWalletOptions,
32
promiseProps,
43
RequestTracer,
54
SupplementGenerateWalletOptions,
@@ -11,12 +10,14 @@ import {
1110
} from '@bitgo/sdk-core';
1211
import { createEnclavedExpressClient } from './enclavedExpressClient';
1312
import _ from 'lodash';
14-
import { BitGoRequest } from '../types/request';
13+
import { MasterApiSpecRouteRequest } from './routers/masterApiSpec';
1514

1615
/**
1716
* This route is used to generate a multisig wallet when enclaved express is enabled
1817
*/
19-
export async function handleGenerateWalletOnPrem(req: BitGoRequest) {
18+
export async function handleGenerateWalletOnPrem(
19+
req: MasterApiSpecRouteRequest<'v1.wallet.generate', 'post'>,
20+
) {
2021
const bitgo = req.bitgo;
2122
const baseCoin = bitgo.coin(req.params.coin);
2223

@@ -27,19 +28,14 @@ export async function handleGenerateWalletOnPrem(req: BitGoRequest) {
2728
);
2829
}
2930

30-
const params = req.body as GenerateWalletOptions;
3131
const reqId = new RequestTracer();
3232

3333
// Assign the default multiSig type value based on the coin
34-
if (!params.multisigType) {
35-
params.multisigType = baseCoin.getDefaultMultisigType();
34+
if (!req.decoded.multisigType) {
35+
req.decoded.multisigType = baseCoin.getDefaultMultisigType();
3636
}
3737

38-
if (typeof params.label !== 'string') {
39-
throw new Error('missing required string parameter label');
40-
}
41-
42-
const { label, enterprise } = params;
38+
const { label, enterprise } = req.decoded;
4339

4440
// Create wallet parameters with type assertion to allow 'onprem' subtype
4541
const walletParams = {
@@ -97,9 +93,9 @@ export async function handleGenerateWalletOnPrem(req: BitGoRequest) {
9793
userKeychain: userKeychainPromise(),
9894
backupKeychain: backupKeychainPromise(),
9995
bitgoKeychain: baseCoin.keychains().createBitGo({
100-
enterprise: params.enterprise,
96+
enterprise: req.decoded.enterprise,
10197
reqId,
102-
isDistributedCustody: params.isDistributedCustody,
98+
isDistributedCustody: req.decoded.isDistributedCustody,
10399
}),
104100
});
105101

src/masterBitgoExpress/routers/masterApiSpec.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
11
import * as t from 'io-ts';
2-
import { apiSpec, httpRoute, httpRequest, HttpResponse } from '@api-ts/io-ts-http';
3-
import { createRouter, type WrappedRouter } from '@api-ts/typed-express-router';
2+
import {
3+
apiSpec,
4+
httpRoute,
5+
httpRequest,
6+
HttpResponse,
7+
Method as HttpMethod,
8+
} from '@api-ts/io-ts-http';
9+
import {
10+
createRouter,
11+
TypedRequestHandler,
12+
type WrappedRouter,
13+
} from '@api-ts/typed-express-router';
414
import { Response } from '@api-ts/response';
5-
import express, { Request } from 'express';
15+
import express from 'express';
616
import { BitGo } from 'bitgo';
7-
import { BitGoRequest, isBitGoRequest } from '../../types/request';
17+
import { BitGoRequest } from '../../types/request';
818
import { MasterExpressConfig } from '../../config';
919
import { handleGenerateWalletOnPrem } from '../generateWallet';
1020
import { withResponseHandler } from '../../shared/responseHandler';
@@ -86,6 +96,20 @@ export const MasterApiSpec = apiSpec({
8696
},
8797
});
8898

99+
export type MasterApiSpec = typeof MasterApiSpec;
100+
101+
export type MasterApiSpecRouteHandler<
102+
ApiName extends keyof MasterApiSpec,
103+
Method extends keyof MasterApiSpec[ApiName] & HttpMethod,
104+
> = TypedRequestHandler<MasterApiSpec, ApiName, Method>;
105+
106+
export type MasterApiSpecRouteRequest<
107+
ApiName extends keyof MasterApiSpec,
108+
Method extends keyof MasterApiSpec[ApiName] & HttpMethod,
109+
> = BitGoRequest & Parameters<MasterApiSpecRouteHandler<ApiName, Method>>[0];
110+
111+
export type GenericMasterApiSpecRouteRequest = MasterApiSpecRouteRequest<any, any>;
112+
89113
// Create router with handlers
90114
export function createMasterApiRouter(
91115
cfg: MasterExpressConfig,
@@ -98,10 +122,7 @@ export function createMasterApiRouter(
98122

99123
// Generate wallet endpoint handler
100124
router.post('v1.wallet.generate', [
101-
withResponseHandler(async (req: BitGoRequest | Request) => {
102-
if (!isBitGoRequest(req)) {
103-
throw new Error('Invalid request type');
104-
}
125+
withResponseHandler(async (req: GenericMasterApiSpecRouteRequest) => {
105126
const result = await handleGenerateWalletOnPrem(req);
106127
return Response.ok(result);
107128
}),

src/shared/responseHandler.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import { BitGoRequest } from '../types/request';
21
import { Request, Response as ExpressResponse, NextFunction } from 'express';
2+
import {
3+
GenericMasterApiSpecRouteRequest,
4+
MasterApiSpecRouteRequest,
5+
} from '../masterBitgoExpress/routers/masterApiSpec';
36

47
// Extend Express Response to include sendEncoded
58
interface EncodedResponse extends ExpressResponse {
@@ -13,7 +16,7 @@ interface ApiResponse {
1316
}
1417

1518
type ServiceFunction = (
16-
req: Request | BitGoRequest,
19+
req: MasterApiSpecRouteRequest<any, any>,
1720
res: EncodedResponse,
1821
next: NextFunction,
1922
) => Promise<ApiResponse> | ApiResponse;
@@ -24,9 +27,9 @@ type ServiceFunction = (
2427
* @returns Express middleware function that handles the response encoding
2528
*/
2629
export function withResponseHandler(fn: ServiceFunction) {
27-
return async (req: Request | BitGoRequest, res: EncodedResponse, next: NextFunction) => {
30+
return async (req: Request, res: EncodedResponse, next: NextFunction) => {
2831
try {
29-
const result = await Promise.resolve(fn(req, res, next));
32+
const result = await fn(req as unknown as GenericMasterApiSpecRouteRequest, res, next);
3033
return res.sendEncoded(result.type, result.payload);
3134
} catch (error) {
3235
// Log the error

0 commit comments

Comments
 (0)