From 24b9260fa0595b48ade8db00c14ad70ebeb08396 Mon Sep 17 00:00:00 2001 From: danielzhao122 Date: Tue, 7 Oct 2025 15:47:08 -0400 Subject: [PATCH] refactor: add missing codec defintions ticket: WP-6196 --- modules/express/openapi-generator.rc.js | 29 +++++++++++++++++++ modules/express/package.json | 1 + .../api/common/calculateMinerFeeInfo.ts | 1 + .../src/typedRoutes/api/common/decrypt.ts | 1 + .../src/typedRoutes/api/common/encrypt.ts | 1 + .../src/typedRoutes/api/common/login.ts | 1 + .../src/typedRoutes/api/common/ping.ts | 1 + .../src/typedRoutes/api/common/pingExpress.ts | 1 + .../typedRoutes/api/common/verifyAddress.ts | 1 + .../src/typedRoutes/api/v1/acceptShare.ts | 2 +- .../typedRoutes/api/v1/consolidateUnspents.ts | 2 +- .../api/v1/constructPendingApprovalTx.ts | 2 +- .../typedRoutes/api/v1/createLocalKeyChain.ts | 2 +- .../typedRoutes/api/v1/deriveLocalKeyChain.ts | 2 +- .../src/typedRoutes/api/v1/fanoutUnspents.ts | 2 +- .../src/typedRoutes/api/v1/pendingApproval.ts | 2 +- .../src/typedRoutes/api/v1/signTransaction.ts | 2 +- .../src/typedRoutes/api/v1/simpleCreate.ts | 2 +- .../src/typedRoutes/api/v2/createAddress.ts | 2 +- .../typedRoutes/api/v2/expressWalletUpdate.ts | 1 + .../api/v2/keychainChangePassword.ts | 1 + .../src/typedRoutes/api/v2/keychainLocal.ts | 2 +- .../typedRoutes/api/v2/lightningInitWallet.ts | 2 +- .../src/typedRoutes/api/v2/lightningState.ts | 2 +- .../src/typedRoutes/api/v2/ofcSignPayload.ts | 7 +++-- .../src/typedRoutes/api/v2/shareWallet.ts | 1 + .../src/typedRoutes/api/v2/unlockWallet.ts | 2 +- .../src/typedRoutes/api/v2/verifyAddress.ts | 2 +- .../schemas/jsonFromStringifiedJson.ts | 4 +++ 29 files changed, 63 insertions(+), 18 deletions(-) create mode 100644 modules/express/openapi-generator.rc.js create mode 100644 modules/express/src/typedRoutes/schemas/jsonFromStringifiedJson.ts diff --git a/modules/express/openapi-generator.rc.js b/modules/express/openapi-generator.rc.js new file mode 100644 index 0000000000..d8be232dac --- /dev/null +++ b/modules/express/openapi-generator.rc.js @@ -0,0 +1,29 @@ +/** + * OpenAPI Generator Configuration + * Custom codec handlers for OpenAPI generation + */ + +module.exports = (E) => { + return { + '.': { + JsonFromStringifiedJson: () => E.right({ type: 'string' }), + Optional: (args) => E.right({ ...args[0], nullable: true }), + }, + 'io-ts-types': { + Json: () => E.right({ type: 'object', properties: {}, required: [] }), + NonEmptyString: () => E.right({ type: 'string', minLength: 1 }), + DateFromISOString: () => E.right({ type: 'string', format: 'date-time' }), + BigIntFromString: () => E.right({ type: 'string' }), + }, + 'io-ts-bigint': { + BigIntFromString: () => E.right({ type: 'string' }), + NegativeBigIntFromString: () => E.right({ type: 'string' }), + PositiveBigIntFromString: () => E.right({ type: 'string' }), + NonZeroBigIntFromString: () => E.right({ type: 'string' }), + NonNegativeBigIntFromString: () => E.right({ type: 'string' }), + }, + 'io-ts-numbers': { + NumberFromString: () => E.right({ type: 'string' }), + }, + }; +}; diff --git a/modules/express/package.json b/modules/express/package.json index 3b978c5d78..1ca8f0a07f 100644 --- a/modules/express/package.json +++ b/modules/express/package.json @@ -4,6 +4,7 @@ "private": true, "description": "Local signing server and proxy for the BitGo platform", "main": "./dist/src/index.js", + "source": "src/typedRoutes/api/index.ts", "types": "./dist/src/index.d.ts", "bin": { "bitgo-express": "./bin/bitgo-express" diff --git a/modules/express/src/typedRoutes/api/common/calculateMinerFeeInfo.ts b/modules/express/src/typedRoutes/api/common/calculateMinerFeeInfo.ts index bfac7395d9..39f73e7c3f 100644 --- a/modules/express/src/typedRoutes/api/common/calculateMinerFeeInfo.ts +++ b/modules/express/src/typedRoutes/api/common/calculateMinerFeeInfo.ts @@ -39,6 +39,7 @@ export const CalculateMinerFeeInfoResponse = t.type({ * 4. The fee rate (in satoshis per kilobyte) * * @operationId express.calculateminerfeeinfo + * @tag express */ export const PostCalculateMinerFeeInfo = httpRoute({ path: '/api/v[12]/calculateminerfeeinfo', diff --git a/modules/express/src/typedRoutes/api/common/decrypt.ts b/modules/express/src/typedRoutes/api/common/decrypt.ts index 62a8a0ab1e..d7ad264169 100644 --- a/modules/express/src/typedRoutes/api/common/decrypt.ts +++ b/modules/express/src/typedRoutes/api/common/decrypt.ts @@ -11,6 +11,7 @@ export const DecryptRequestBody = { * Decrypt * * @operationId express.decrypt + * @tag express */ export const PostDecrypt = httpRoute({ path: '/api/v[12]/decrypt', diff --git a/modules/express/src/typedRoutes/api/common/encrypt.ts b/modules/express/src/typedRoutes/api/common/encrypt.ts index 7cb2c5e964..0a8d46daea 100644 --- a/modules/express/src/typedRoutes/api/common/encrypt.ts +++ b/modules/express/src/typedRoutes/api/common/encrypt.ts @@ -12,6 +12,7 @@ export const EncryptRequestBody = { * Encrypt * * @operationId express.encrypt + * @tag express */ export const PostEncrypt = httpRoute({ path: '/api/v[12]/encrypt', diff --git a/modules/express/src/typedRoutes/api/common/login.ts b/modules/express/src/typedRoutes/api/common/login.ts index 063a764f62..dcffab2523 100644 --- a/modules/express/src/typedRoutes/api/common/login.ts +++ b/modules/express/src/typedRoutes/api/common/login.ts @@ -42,6 +42,7 @@ export const LoginRequest = { * Login * * @operationId express.login + * @tag express */ export const PostLogin = httpRoute({ path: '/api/v[12]/user/login', diff --git a/modules/express/src/typedRoutes/api/common/ping.ts b/modules/express/src/typedRoutes/api/common/ping.ts index 9f86a8d6fc..426adefdae 100644 --- a/modules/express/src/typedRoutes/api/common/ping.ts +++ b/modules/express/src/typedRoutes/api/common/ping.ts @@ -6,6 +6,7 @@ import { BitgoExpressError } from '../../schemas/error'; * Ping * * @operationId express.ping + * @tag express */ export const GetPing = httpRoute({ path: '/api/v[12]/ping', diff --git a/modules/express/src/typedRoutes/api/common/pingExpress.ts b/modules/express/src/typedRoutes/api/common/pingExpress.ts index be694f5210..3b3e80ccf3 100644 --- a/modules/express/src/typedRoutes/api/common/pingExpress.ts +++ b/modules/express/src/typedRoutes/api/common/pingExpress.ts @@ -6,6 +6,7 @@ import { BitgoExpressError } from '../../schemas/error'; * Ping Express * * @operationId express.pingExpress + * @tag express */ export const GetPingExpress = httpRoute({ path: '/api/v[12]/pingexpress', diff --git a/modules/express/src/typedRoutes/api/common/verifyAddress.ts b/modules/express/src/typedRoutes/api/common/verifyAddress.ts index 960ac22397..ecbf780562 100644 --- a/modules/express/src/typedRoutes/api/common/verifyAddress.ts +++ b/modules/express/src/typedRoutes/api/common/verifyAddress.ts @@ -10,6 +10,7 @@ export const VerifyAddressBody = { * Verify Address * * @operationId express.verifyaddress + * @tag express */ export const PostVerifyAddress = httpRoute({ path: '/api/v[12]/verifyaddress', diff --git a/modules/express/src/typedRoutes/api/v1/acceptShare.ts b/modules/express/src/typedRoutes/api/v1/acceptShare.ts index 1fc74f9147..27759027f9 100644 --- a/modules/express/src/typedRoutes/api/v1/acceptShare.ts +++ b/modules/express/src/typedRoutes/api/v1/acceptShare.ts @@ -22,8 +22,8 @@ export const AcceptShareRequestBody = { * When a wallet is shared with a user, they need to accept the share to gain access * to the wallet according to the permissions granted by the sharing user. * - * @tag express * @operationId express.v1.wallet.acceptShare + * @tag express */ export const PostAcceptShare = httpRoute({ path: '/api/v1/walletshare/{shareId}/acceptShare', diff --git a/modules/express/src/typedRoutes/api/v1/consolidateUnspents.ts b/modules/express/src/typedRoutes/api/v1/consolidateUnspents.ts index 96525c58ca..5bfa3be7a0 100644 --- a/modules/express/src/typedRoutes/api/v1/consolidateUnspents.ts +++ b/modules/express/src/typedRoutes/api/v1/consolidateUnspents.ts @@ -71,8 +71,8 @@ export const ConsolidateUnspentsResponse = t.array( * multiple inputs to a single output. This is useful for reducing the number of UTXOs in a wallet, * which can improve performance and reduce transaction fees. * - * @tag express * @operationId express.v1.wallet.consolidateunspents + * @tag express */ export const PutConsolidateUnspents = httpRoute({ path: '/api/v1/wallet/{id}/consolidateunspents', diff --git a/modules/express/src/typedRoutes/api/v1/constructPendingApprovalTx.ts b/modules/express/src/typedRoutes/api/v1/constructPendingApprovalTx.ts index bbdbfb8af3..a0a8352124 100644 --- a/modules/express/src/typedRoutes/api/v1/constructPendingApprovalTx.ts +++ b/modules/express/src/typedRoutes/api/v1/constructPendingApprovalTx.ts @@ -59,8 +59,8 @@ export const ConstructPendingApprovalTxResponse = t.type({ * For transaction request type approvals, either a wallet passphrase or xprv must be provided to sign the transaction. * You can optionally specify fee-related parameters to customize the transaction fee. * - * @tag express * @operationId express.v1.pendingapproval.constructTx + * @tag express */ export const PutConstructPendingApprovalTx = httpRoute({ path: '/api/v1/pendingapprovals/{id}/constructTx', diff --git a/modules/express/src/typedRoutes/api/v1/createLocalKeyChain.ts b/modules/express/src/typedRoutes/api/v1/createLocalKeyChain.ts index ef4a9475dd..8113bf1e99 100644 --- a/modules/express/src/typedRoutes/api/v1/createLocalKeyChain.ts +++ b/modules/express/src/typedRoutes/api/v1/createLocalKeyChain.ts @@ -33,8 +33,8 @@ export const CreateLocalKeyChainResponse = t.type({ * For security reasons, it is highly recommended that you encrypt and destroy * the original xprv immediately to prevent theft. * - * @tag express * @operationId express.v1.keychain.local + * @tag express */ export const PostCreateLocalKeyChain = httpRoute({ path: '/api/v1/keychain/local', diff --git a/modules/express/src/typedRoutes/api/v1/deriveLocalKeyChain.ts b/modules/express/src/typedRoutes/api/v1/deriveLocalKeyChain.ts index eaa4112c73..288c523d50 100644 --- a/modules/express/src/typedRoutes/api/v1/deriveLocalKeyChain.ts +++ b/modules/express/src/typedRoutes/api/v1/deriveLocalKeyChain.ts @@ -44,8 +44,8 @@ export const DeriveLocalKeyChainResponse = t.type({ * both the derived xprv and xpub are returned. If xpub is provided, only the * derived xpub is returned. * - * @tag express * @operationId express.v1.keychain.derive + * @tag express */ export const PostDeriveLocalKeyChain = httpRoute({ path: '/api/v1/keychain/derive', diff --git a/modules/express/src/typedRoutes/api/v1/fanoutUnspents.ts b/modules/express/src/typedRoutes/api/v1/fanoutUnspents.ts index 0c57342280..904c401e3e 100644 --- a/modules/express/src/typedRoutes/api/v1/fanoutUnspents.ts +++ b/modules/express/src/typedRoutes/api/v1/fanoutUnspents.ts @@ -59,8 +59,8 @@ export const FanoutUnspentsResponse = t.type({ * multiple inputs to multiple outputs. This is useful for increasing the number of UTXOs * in a wallet, which can improve transaction parallelization. * - * @tag express * @operationId express.v1.wallet.fanoutunspents + * @tag express */ export const PutFanoutUnspents = httpRoute({ path: '/api/v1/wallet/{id}/fanoutunspents', diff --git a/modules/express/src/typedRoutes/api/v1/pendingApproval.ts b/modules/express/src/typedRoutes/api/v1/pendingApproval.ts index 7490377f86..381a957bb3 100644 --- a/modules/express/src/typedRoutes/api/v1/pendingApproval.ts +++ b/modules/express/src/typedRoutes/api/v1/pendingApproval.ts @@ -28,8 +28,8 @@ export const pendingApprovalRequestBody = { * Handles various approval scenarios including transaction approvals, policy rule changes, * and user change requests. * - * @tag express * @operationId express.v1.pendingapprovals + * @tag express */ export const PutPendingApproval = httpRoute({ diff --git a/modules/express/src/typedRoutes/api/v1/signTransaction.ts b/modules/express/src/typedRoutes/api/v1/signTransaction.ts index 2145fb8f71..d8c147f5db 100644 --- a/modules/express/src/typedRoutes/api/v1/signTransaction.ts +++ b/modules/express/src/typedRoutes/api/v1/signTransaction.ts @@ -31,8 +31,8 @@ export const signTransactionRequestBody = { * signTransaction * Sign a previously created transaction with a keychain * - * @tag express * @operationId express.v1.wallet.signTransaction + * @tag express */ export const PostSignTransaction = httpRoute({ path: '/api/v1/wallet/{id}/signtransaction', diff --git a/modules/express/src/typedRoutes/api/v1/simpleCreate.ts b/modules/express/src/typedRoutes/api/v1/simpleCreate.ts index a1928f68ba..2186b65e59 100644 --- a/modules/express/src/typedRoutes/api/v1/simpleCreate.ts +++ b/modules/express/src/typedRoutes/api/v1/simpleCreate.ts @@ -31,8 +31,8 @@ export const SimpleCreateRequestBody = { * 4. Creates the BitGo key on the service * 5. Creates the wallet on BitGo with the 3 public keys above * - * @tag express * @operationId express.v1.wallet.simplecreate + * @tag express */ export const PostSimpleCreate = httpRoute({ path: '/api/v1/wallets/simplecreate', diff --git a/modules/express/src/typedRoutes/api/v2/createAddress.ts b/modules/express/src/typedRoutes/api/v2/createAddress.ts index 9897c10abd..ec2ed2f6de 100644 --- a/modules/express/src/typedRoutes/api/v2/createAddress.ts +++ b/modules/express/src/typedRoutes/api/v2/createAddress.ts @@ -62,8 +62,8 @@ export const CreateAddressResponse = { /** * Create address for a wallet * - * @tag express * @operationId express.v2.wallet.createAddress + * @tag express */ export const PostCreateAddress = httpRoute({ path: '/api/v2/{coin}/wallet/{id}/address', diff --git a/modules/express/src/typedRoutes/api/v2/expressWalletUpdate.ts b/modules/express/src/typedRoutes/api/v2/expressWalletUpdate.ts index c3119eeac5..dcd56c5743 100644 --- a/modules/express/src/typedRoutes/api/v2/expressWalletUpdate.ts +++ b/modules/express/src/typedRoutes/api/v2/expressWalletUpdate.ts @@ -47,6 +47,7 @@ export const ExpressWalletUpdateResponse = { * For other coins, use the standard wallet update endpoint. * * @operationId express.wallet.update + * @tag express */ export const PutExpressWalletUpdate = httpRoute({ path: '/express/api/v2/{coin}/wallet/{id}', diff --git a/modules/express/src/typedRoutes/api/v2/keychainChangePassword.ts b/modules/express/src/typedRoutes/api/v2/keychainChangePassword.ts index 7e55887a83..6aff9f5555 100644 --- a/modules/express/src/typedRoutes/api/v2/keychainChangePassword.ts +++ b/modules/express/src/typedRoutes/api/v2/keychainChangePassword.ts @@ -39,6 +39,7 @@ export const KeychainChangePasswordResponse = { * Change a keychain's passphrase, re-encrypting the key to a new password. * * @operationId express.v2.keychain.changePassword + * @tag express */ export const PostKeychainChangePassword = httpRoute({ path: '/api/v2/{coin}/keychain/{id}/changepassword', diff --git a/modules/express/src/typedRoutes/api/v2/keychainLocal.ts b/modules/express/src/typedRoutes/api/v2/keychainLocal.ts index 2a04acb235..19860b4a34 100644 --- a/modules/express/src/typedRoutes/api/v2/keychainLocal.ts +++ b/modules/express/src/typedRoutes/api/v2/keychainLocal.ts @@ -33,8 +33,8 @@ export const KeychainLocalResponse = { * * For security reasons, it is highly recommended that you encrypt and destroy the original xprv immediately to prevent theft. * - * @tag express * @operationId express.keychain.local + * @tag express */ export const PostKeychainLocal = httpRoute({ path: '/api/v2/{coin}/keychain/local', diff --git a/modules/express/src/typedRoutes/api/v2/lightningInitWallet.ts b/modules/express/src/typedRoutes/api/v2/lightningInitWallet.ts index da67f9a20c..e5dc149be1 100644 --- a/modules/express/src/typedRoutes/api/v2/lightningInitWallet.ts +++ b/modules/express/src/typedRoutes/api/v2/lightningInitWallet.ts @@ -36,8 +36,8 @@ export const LightningInitWalletResponse = { * Lightning - This is only used for self-custody lightning. Initialize a newly created Lightning Network Daemon (LND) for the first time. * Returns the updated wallet with the encrypted admin macaroon in the `coinSpecific` response field. * - * @tag express * @operationId express.lightning.initWallet + * @tag express */ export const PostLightningInitWallet = httpRoute({ path: '/api/v2/{coin}/wallet/{walletId}/initwallet', diff --git a/modules/express/src/typedRoutes/api/v2/lightningState.ts b/modules/express/src/typedRoutes/api/v2/lightningState.ts index fea0e823cc..bea550427a 100644 --- a/modules/express/src/typedRoutes/api/v2/lightningState.ts +++ b/modules/express/src/typedRoutes/api/v2/lightningState.ts @@ -32,8 +32,8 @@ export const LightningStateResponse = { * * This is only used for self-custody lightning. Get the current state of the lightning node. * - * @tag express * @operationId express.lightning.getState + * @tag express */ export const GetLightningState = httpRoute({ method: 'GET', diff --git a/modules/express/src/typedRoutes/api/v2/ofcSignPayload.ts b/modules/express/src/typedRoutes/api/v2/ofcSignPayload.ts index b848b23f00..4aa0671c6d 100644 --- a/modules/express/src/typedRoutes/api/v2/ofcSignPayload.ts +++ b/modules/express/src/typedRoutes/api/v2/ofcSignPayload.ts @@ -1,7 +1,8 @@ import * as t from 'io-ts'; -import { Json, NonEmptyString, JsonFromString } from 'io-ts-types'; +import { Json, NonEmptyString } from 'io-ts-types'; import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http'; import { BitgoExpressError } from '../../schemas/error'; +import { JsonFromStringifiedJson } from '../../schemas/jsonFromStringifiedJson'; /** * Sign an arbitrary payload using an OFC trading account key. @@ -10,7 +11,7 @@ export const OfcSignPayloadBody = { /** The ID of the OFC wallet to sign the payload with. */ walletId: NonEmptyString, /** The payload to sign. The input can either be a stringified JSON, or a JSON object. */ - payload: t.union([Json, t.string.pipe(JsonFromString)]), + payload: t.union([Json, JsonFromStringifiedJson]), /** The passphrase to decrypt the user key. */ walletPassphrase: optional(t.string), } as const; @@ -35,8 +36,8 @@ export const OfcSignPayloadResponse = { /** Sign payload with an OFC wallet key. * Signs an arbitrary payload with an OFC wallet key. * - * @tag express * @operationId express.ofc.signPayload + * @tag express */ export const PostOfcSignPayload = httpRoute({ path: '/api/v2/ofc/signPayload', diff --git a/modules/express/src/typedRoutes/api/v2/shareWallet.ts b/modules/express/src/typedRoutes/api/v2/shareWallet.ts index e4bd1b2bba..9211330bf7 100644 --- a/modules/express/src/typedRoutes/api/v2/shareWallet.ts +++ b/modules/express/src/typedRoutes/api/v2/shareWallet.ts @@ -76,6 +76,7 @@ export const ShareWalletResponse = { * Share this wallet with another BitGo user. * * @operationId express.v2.wallet.share + * @tag express */ export const PostShareWallet = httpRoute({ path: '/api/v2/{coin}/wallet/{id}/share', diff --git a/modules/express/src/typedRoutes/api/v2/unlockWallet.ts b/modules/express/src/typedRoutes/api/v2/unlockWallet.ts index 2f655c20a3..4dd05fd2c4 100644 --- a/modules/express/src/typedRoutes/api/v2/unlockWallet.ts +++ b/modules/express/src/typedRoutes/api/v2/unlockWallet.ts @@ -42,8 +42,8 @@ export const UnlockLightningWalletResponse = { * * This is only used for self-custody lightning. Unlock the Lightning Network Daemon (LND) node with the given wallet password. * - * @tag express * @operationId express.lightning.unlockWallet + * @tag express */ export const PostUnlockLightningWallet = httpRoute({ method: 'POST', diff --git a/modules/express/src/typedRoutes/api/v2/verifyAddress.ts b/modules/express/src/typedRoutes/api/v2/verifyAddress.ts index f8a8206af2..9400ddeb29 100644 --- a/modules/express/src/typedRoutes/api/v2/verifyAddress.ts +++ b/modules/express/src/typedRoutes/api/v2/verifyAddress.ts @@ -30,8 +30,8 @@ export const VerifyAddressV2Body = { * Returns whether the address is valid for the specified coin. * For UTXO coins, an optional legacy script hash flag can be provided to allow previous script hash versions. * - * @tag express * @operationId express.verifycoinaddress + * @tag express */ export const PostVerifyCoinAddress = httpRoute({ path: '/api/v2/{coin}/verifyaddress', diff --git a/modules/express/src/typedRoutes/schemas/jsonFromStringifiedJson.ts b/modules/express/src/typedRoutes/schemas/jsonFromStringifiedJson.ts new file mode 100644 index 0000000000..c050d476e9 --- /dev/null +++ b/modules/express/src/typedRoutes/schemas/jsonFromStringifiedJson.ts @@ -0,0 +1,4 @@ +import * as t from 'io-ts'; +import { JsonFromString } from 'io-ts-types'; + +export const JsonFromStringifiedJson = t.string.pipe(JsonFromString);