Skip to content

Commit c438f17

Browse files
authored
Merge pull request #6022 from BitGo/CSI-419-2
feat: handle rejected promises in bulk wallet share updates
2 parents 1f87634 + cb79082 commit c438f17

File tree

3 files changed

+114
-8
lines changed

3 files changed

+114
-8
lines changed

modules/bitgo/test/v2/unit/wallets.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2273,6 +2273,92 @@ describe('V2 Wallets:', function () {
22732273
encryptStub.firstCall.args[0].should.have.property('password', 'newPassphrase');
22742274
encryptStub.firstCall.args[0].should.have.property('input', originalPrivKey);
22752275
});
2276+
2277+
it('should handle rejected promises and add them to walletShareUpdateErrors', async () => {
2278+
const walletPassphrase = 'bitgo1234';
2279+
2280+
// Mock listSharesV2 to return two shares
2281+
sinon.stub(Wallets.prototype, 'listSharesV2').resolves({
2282+
incoming: [
2283+
{
2284+
id: 'share1',
2285+
coin: 'tsol',
2286+
walletLabel: 'testing',
2287+
fromUser: 'dummyFromUser',
2288+
toUser: 'dummyToUser',
2289+
wallet: 'wallet1',
2290+
permissions: ['spend'],
2291+
state: 'active',
2292+
},
2293+
{
2294+
id: 'share2',
2295+
coin: 'tsol',
2296+
walletLabel: 'testing2',
2297+
fromUser: 'dummyFromUser',
2298+
toUser: 'dummyToUser',
2299+
wallet: 'wallet2',
2300+
permissions: ['spend'],
2301+
state: 'active',
2302+
},
2303+
{
2304+
id: 'share3',
2305+
coin: 'tsol',
2306+
walletLabel: 'testing3',
2307+
fromUser: 'dummyFromUser',
2308+
toUser: 'dummyToUser',
2309+
wallet: 'wallet3',
2310+
permissions: ['spend'],
2311+
state: 'active',
2312+
},
2313+
],
2314+
outgoing: [],
2315+
});
2316+
2317+
// Stub processAcceptShare to throw an error for share2
2318+
// Using 'as any' to bypass TypeScript's private method restriction
2319+
const processAcceptShareStub = sinon.stub(Wallets.prototype as any, 'processAcceptShare');
2320+
processAcceptShareStub
2321+
.withArgs('share1', sinon.match.any, sinon.match.any, sinon.match.any, sinon.match.any)
2322+
.resolves([{ walletShareId: 'share1', status: 'accept' }]);
2323+
processAcceptShareStub
2324+
.withArgs('share2', sinon.match.any, sinon.match.any, sinon.match.any, sinon.match.any)
2325+
.rejects(new Error('Failed to process share2'));
2326+
processAcceptShareStub
2327+
.withArgs('share3', sinon.match.any, sinon.match.any, sinon.match.any, sinon.match.any)
2328+
.resolves([{ walletShareId: 'share3', status: 'accept' }]);
2329+
2330+
// Mock bulkUpdateWalletShareRequest to return a response
2331+
const bulkUpdateStub = sinon.stub(Wallets.prototype, 'bulkUpdateWalletShareRequest').resolves({
2332+
acceptedWalletShares: ['share1', 'share3'],
2333+
rejectedWalletShares: [],
2334+
walletShareUpdateErrors: [], // Empty array that should be populated
2335+
});
2336+
2337+
const result = await wallets.bulkUpdateWalletShare({
2338+
shares: [
2339+
{ walletShareId: 'share1', status: 'accept' },
2340+
{ walletShareId: 'share2', status: 'accept' },
2341+
{ walletShareId: 'share3', status: 'accept' },
2342+
],
2343+
userLoginPassword: walletPassphrase,
2344+
});
2345+
2346+
// Verify bulkUpdateWalletShareRequest was called with only the successful share
2347+
bulkUpdateStub.calledOnce.should.be.true();
2348+
const updateParams = bulkUpdateStub.firstCall.args[0];
2349+
updateParams.should.have.lengthOf(2);
2350+
updateParams[0].walletShareId.should.equal('share1');
2351+
updateParams[0].status.should.equal('accept');
2352+
updateParams[1].walletShareId.should.equal('share3');
2353+
updateParams[1].status.should.equal('accept');
2354+
2355+
// Verify the result contains the error information
2356+
result.should.have.property('walletShareUpdateErrors');
2357+
result.walletShareUpdateErrors.should.be.an.Array();
2358+
result.walletShareUpdateErrors.should.have.lengthOf(1);
2359+
result.walletShareUpdateErrors[0].should.have.property('walletShareId', 'share2');
2360+
result.walletShareUpdateErrors[0].should.have.property('reason', 'Failed to process share2');
2361+
});
22762362
});
22772363
});
22782364

modules/sdk-core/src/bitgo/wallet/wallets.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,14 +1030,24 @@ export class Wallets implements IWallets {
10301030
);
10311031

10321032
// Extract successful updates
1033-
const successfulUpdates = settledUpdates
1034-
.filter(
1035-
(result): result is PromiseFulfilledResult<BulkUpdateWalletShareOptionsRequest[]> =>
1036-
result.status === 'fulfilled'
1037-
)
1038-
.flatMap((result) => result.value);
1039-
1040-
// Send updates to the server
1033+
const successfulUpdates = settledUpdates.flatMap((result) => (result.status === 'fulfilled' ? result.value : []));
1034+
1035+
// Extract failed updates - only from rejected promises
1036+
const failedUpdates = settledUpdates.reduce<Array<{ walletShareId: string; reason: string }>>(
1037+
(acc, result, index) => {
1038+
if (result.status === 'rejected') {
1039+
const rejectedResult = result;
1040+
acc.push({
1041+
walletShareId: resolvedShares[index].walletShareId,
1042+
reason: rejectedResult.reason?.message || String(rejectedResult.reason),
1043+
});
1044+
}
1045+
return acc;
1046+
},
1047+
[]
1048+
);
1049+
1050+
// Send successful updates to the server
10411051
const response = await this.bulkUpdateWalletShareRequest(successfulUpdates);
10421052

10431053
// Process accepted special override cases - reshare with spenders
@@ -1056,6 +1066,11 @@ export class Wallets implements IWallets {
10561066
}
10571067
}
10581068

1069+
// Add information about failed updates to the response
1070+
if (failedUpdates.length > 0) {
1071+
response.walletShareUpdateErrors.push(...failedUpdates);
1072+
}
1073+
10591074
return response;
10601075
}
10611076

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14084,6 +14084,11 @@ long@^5.0.0:
1408414084
resolved "https://registry.npmjs.org/long/-/long-5.3.1.tgz#9d4222d3213f38a5ec809674834e0f0ab21abe96"
1408514085
integrity sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==
1408614086

14087+
long@^5.3.2:
14088+
version "5.3.2"
14089+
resolved "https://registry.npmjs.org/long/-/long-5.3.2.tgz#1d84463095999262d7d7b7f8bfd4a8cc55167f83"
14090+
integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==
14091+
1408714092
long@~3:
1408814093
version "3.2.0"
1408914094
resolved "https://registry.npmjs.org/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"

0 commit comments

Comments
 (0)