Skip to content

Commit f4ee459

Browse files
feat: integrate user change password batching flow
2 parents c741540 + 327a7b2 commit f4ee459

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

modules/sdk-api/src/bitgoAPI.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1932,6 +1932,36 @@ export class BitGoAPI implements BitGoBase {
19321932
password: this.calculateHMAC(user.username, newPassword),
19331933
};
19341934

1935+
// Calculate payload size in KB
1936+
const payloadSizeBytes = JSON.stringify(updatePasswordParams).length;
1937+
const payloadSizeKB = Math.ceil(payloadSizeBytes / 1024);
1938+
1939+
// Check if batching flow is enabled
1940+
try {
1941+
const batchingFlowCheck = await this.get(this.url('/user/checkBatchingPasswordFlow', 2))
1942+
.query({ payloadSize: payloadSizeKB.toString() })
1943+
.result();
1944+
1945+
if (batchingFlowCheck.isBatchingFlowEnabled) {
1946+
await this.processKeychainPasswordUpdatesInBatches(
1947+
updatePasswordParams.keychains,
1948+
updatePasswordParams.v2_keychains,
1949+
batchingFlowCheck.noOfBatches,
1950+
3
1951+
);
1952+
// Call changepassword API without keychains for batching flow
1953+
return this.post(this.url('/user/changepassword'))
1954+
.send({
1955+
version: updatePasswordParams.version,
1956+
oldPassword: updatePasswordParams.oldPassword,
1957+
password: updatePasswordParams.password,
1958+
})
1959+
.result();
1960+
}
1961+
} catch (error) {
1962+
// batching flow check failed
1963+
}
1964+
19351965
return this.post(this.url('/user/changepassword')).send(updatePasswordParams).result();
19361966
}
19371967

@@ -2173,4 +2203,63 @@ export class BitGoAPI implements BitGoBase {
21732203
const result = await req;
21742204
return result.body;
21752205
}
2206+
2207+
/**
2208+
* Process keychain password updates in batches with retry logic
2209+
* @param keychains - The v1 keychains to update
2210+
* @param v2Keychains - The v2 keychains to update
2211+
* @param noOfBatches - Number of batches to split the keychains into
2212+
* @param maxRetries - Maximum number of retries per batch
2213+
* @private
2214+
*/
2215+
private async processKeychainPasswordUpdatesInBatches(
2216+
keychains: Record<string, string>,
2217+
v2Keychains: Record<string, string>,
2218+
noOfBatches: number,
2219+
maxRetries: number
2220+
): Promise<void> {
2221+
// Split keychains into batches
2222+
const v1KeychainEntries = Object.entries(keychains);
2223+
const v2KeychainEntries = Object.entries(v2Keychains);
2224+
2225+
const v1BatchSize = Math.ceil(v1KeychainEntries.length / noOfBatches);
2226+
const v2BatchSize = Math.ceil(v2KeychainEntries.length / noOfBatches);
2227+
2228+
// Call batching API for each batch with retry logic
2229+
for (let i = 0; i < noOfBatches; i++) {
2230+
const v1Batch = Object.fromEntries(v1KeychainEntries.slice(i * v1BatchSize, (i + 1) * v1BatchSize));
2231+
const v2Batch = Object.fromEntries(v2KeychainEntries.slice(i * v2BatchSize, (i + 1) * v2BatchSize));
2232+
2233+
let retryCount = 0;
2234+
let success = false;
2235+
2236+
while (retryCount < maxRetries && !success) {
2237+
try {
2238+
const response = await this.put(this.url('/user/keychains', 2))
2239+
.send({
2240+
keychains: v1Batch,
2241+
v2_keychains: v2Batch,
2242+
})
2243+
.result();
2244+
2245+
// Check if there are any failed keychains in the response
2246+
const hasFailed =
2247+
(response.failed?.v1 && Object.keys(response.failed.v1).length > 0) ||
2248+
(response.failed?.v2 && Object.keys(response.failed.v2).length > 0);
2249+
2250+
if (hasFailed) {
2251+
throw new Error(`Batch ${i + 1} had failed keychains: ${JSON.stringify(response.failed)}`);
2252+
}
2253+
2254+
success = true;
2255+
} catch (error) {
2256+
retryCount++;
2257+
2258+
if (retryCount >= maxRetries) {
2259+
throw new Error(`Batch ${i + 1} failed after ${maxRetries} retries: ${error.message}`);
2260+
}
2261+
}
2262+
}
2263+
}
2264+
}
21762265
}

0 commit comments

Comments
 (0)