diff --git a/src/client/metadataTransfer.ts b/src/client/metadataTransfer.ts index 78fb58cfcb..891e5a6cbd 100644 --- a/src/client/metadataTransfer.ts +++ b/src/client/metadataTransfer.ts @@ -14,7 +14,6 @@ import { MetadataConverter, SfdxFileFormat } from '../convert'; import { MetadataTransferError } from '../errors'; import { ComponentSet } from '../collections'; import { AsyncResult, MetadataRequestStatus, MetadataTransferResult, RequestStatus } from './types'; - export interface MetadataTransferOptions { usernameOrConnection: string | Connection; components?: ComponentSet; @@ -191,10 +190,25 @@ export abstract class MetadataTransfer + (e as Error).message.includes(retryableNetworkError) + ) + ) { + this.logger.debug('Network error on the request', e); + await Lifecycle.getInstance().emitWarning('Network error occurred. Continuing to poll.'); + return { completed: false }; + } + throw e; } } this.logger.debug(`MDAPI status update: ${mdapiStatus.status}`); diff --git a/test/client/metadataTransfer.test.ts b/test/client/metadataTransfer.test.ts index a3430d4e83..90f4de36c5 100644 --- a/test/client/metadataTransfer.test.ts +++ b/test/client/metadataTransfer.test.ts @@ -215,7 +215,6 @@ describe('MetadataTransfer', () => { const originalError = new Error('whoops'); const expectedError = new MetadataTransferError('md_request_fail', originalError.message); checkStatus.throws(originalError); - let error: Error; operation.onError((e) => (error = e)); await operation.pollStatus(); @@ -224,6 +223,18 @@ describe('MetadataTransfer', () => { expect(error.message).to.deep.equal(expectedError.message); }); + it('should tolerate network errors', async () => { + const { checkStatus } = operation.lifecycle; + const networkError1 = new Error('something something ETIMEDOUT something'); + const networkError2 = new Error('something something ENOTFOUND something'); + checkStatus.onFirstCall().throws(networkError1); + checkStatus.onSecondCall().throws(networkError2); + checkStatus.onThirdCall().resolves({ done: true }); + + await operation.pollStatus(); + expect(checkStatus.callCount).to.equal(3); + }); + it('should throw wrapped error if there are no error listeners', async () => { const { checkStatus } = operation.lifecycle; const originalError = new Error('whoops');