From d6123437f3154215827237cdcd7f7ca6a9b7851d Mon Sep 17 00:00:00 2001 From: Devesh-Skyflow Date: Thu, 23 Apr 2026 16:22:57 +0530 Subject: [PATCH 1/2] SK-2771: fix deidentifyFile() throws `object is not iterable` when waitTime is small --- src/vault/controller/detect/index.ts | 9 ++--- test/vault/controller/detect.test.js | 50 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/vault/controller/detect/index.ts b/src/vault/controller/detect/index.ts index 83672c11..b39030e7 100644 --- a/src/vault/controller/detect/index.ts +++ b/src/vault/controller/detect/index.ts @@ -352,7 +352,7 @@ class DetectController { if (response.status?.toUpperCase() === 'IN_PROGRESS' ) { if (currentWaitTime >= maxWaitTime) { - resolve({ runId }); // Resolve with runId if max wait time is exceeded + resolve({ data: { runId, status: 'IN_PROGRESS' }, runId }); } else { const nextWaitTime = currentWaitTime * 2; let waitTime = 0; @@ -368,7 +368,7 @@ class DetectController { }, waitTime * 1000); } } else if (response.status?.toUpperCase() === 'SUCCESS') { - resolve([response, runId]); // Resolve with the processed file response and runId + resolve({ data: response, runId }); } else if (response.status?.toUpperCase() === 'FAILED') { reject(new SkyflowError(SKYFLOW_ERROR_CODE.INTERNAL_SERVER_ERROR, [response.message])); @@ -605,7 +605,7 @@ class DetectController { this.waitTime = options?.getWaitTime() ?? this.waitTime; var reqType : DeidenitfyFileRequestTypes = this.getReqType(fileExtension); - var promiseReq: Promise<[DeidentifyFileDetectRunResponse, string]>; + var promiseReq: Promise<{ data: DeidentifyFileDetectRunResponse & { runId?: string; status?: string }, runId: string }>; switch (reqType){ case DeidenitfyFileRequestTypes.AUDIO: promiseReq = this.buildAudioRequest(fileObj, options, fileExtension) @@ -708,12 +708,13 @@ class DetectController { break; } - promiseReq.then(([data, runId]) => { + promiseReq.then(({ data, runId }) => { if(runId && data.status === "IN_PROGRESS") { resolve(new DeidentifyFileResponse({ runId: runId, status: data.status, })); + return; } if (options?.getOutputDirectory() && data.status === "SUCCESS") { this.processDeidentifyFileResponse(data, options.getOutputDirectory() as string, fileBaseName); diff --git a/test/vault/controller/detect.test.js b/test/vault/controller/detect.test.js index bb313469..9b4d9fd1 100644 --- a/test/vault/controller/detect.test.js +++ b/test/vault/controller/detect.test.js @@ -1059,4 +1059,54 @@ describe('deidentifyFile', () => { expect.any(Buffer) ); }); + + test('should return runId and IN_PROGRESS status when waitTime is exceeded before processing completes', async () => { + const file = new File(['dummy content'], 'test.pdf', { type: 'application/pdf' }); + const request = new DeidentifyFileRequest({ file }); + const options = new DeidentifyFileOptions(); + options.setWaitTime(1); // very small waitTime — will expire immediately + + mockVaultClient.filesAPI.deidentifyPdf.mockImplementation(() => ({ + withRawResponse: jest.fn().mockResolvedValue({ + data: { run_id: 'run123' }, + rawResponse: { headers: { get: jest.fn().mockReturnValue('req-id') } }, + }), + })); + + // Always IN_PROGRESS — never finishes within waitTime + mockVaultClient.filesAPI.getRun.mockResolvedValue({ status: 'IN_PROGRESS' }); + + const promise = detectController.deidentifyFile(request, options); + await jest.runAllTimersAsync(); + const result = await promise; + + expect(result.runId).toBe('run123'); + expect(result.status).toBe('IN_PROGRESS'); + }); + + test('should not call parseDeidentifyFileResponse when waitTime is exceeded (IN_PROGRESS early return)', async () => { + const file = new File(['dummy content'], 'test.pdf', { type: 'application/pdf' }); + const request = new DeidentifyFileRequest({ file }); + const options = new DeidentifyFileOptions(); + options.setWaitTime(1); + + mockVaultClient.filesAPI.deidentifyPdf.mockImplementation(() => ({ + withRawResponse: jest.fn().mockResolvedValue({ + data: { run_id: 'run456' }, + rawResponse: { headers: { get: jest.fn().mockReturnValue('req-id') } }, + }), + })); + + mockVaultClient.filesAPI.getRun.mockResolvedValue({ status: 'IN_PROGRESS' }); + + const promise = detectController.deidentifyFile(request, options); + await jest.runAllTimersAsync(); + const result = await promise; + + // Only runId and status should be set — no file data + expect(result.runId).toBe('run456'); + expect(result.status).toBe('IN_PROGRESS'); + expect(result.fileBase64).toBeUndefined(); + expect(result.extension).toBeUndefined(); + }); }); \ No newline at end of file From e845ba31f48ac8256facd9d4863d0cb0a9944a85 Mon Sep 17 00:00:00 2001 From: Devesh-Skyflow Date: Thu, 23 Apr 2026 17:09:35 +0530 Subject: [PATCH 2/2] fix: type --- src/vault/controller/detect/index.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vault/controller/detect/index.ts b/src/vault/controller/detect/index.ts index b39030e7..9c69c6bb 100644 --- a/src/vault/controller/detect/index.ts +++ b/src/vault/controller/detect/index.ts @@ -352,7 +352,7 @@ class DetectController { if (response.status?.toUpperCase() === 'IN_PROGRESS' ) { if (currentWaitTime >= maxWaitTime) { - resolve({ data: { runId, status: 'IN_PROGRESS' }, runId }); + resolve({ data: { status: 'IN_PROGRESS' }, runId }); } else { const nextWaitTime = currentWaitTime * 2; let waitTime = 0; @@ -605,7 +605,10 @@ class DetectController { this.waitTime = options?.getWaitTime() ?? this.waitTime; var reqType : DeidenitfyFileRequestTypes = this.getReqType(fileExtension); - var promiseReq: Promise<{ data: DeidentifyFileDetectRunResponse & { runId?: string; status?: string }, runId: string }>; + type PollResult = + | { data: DeidentifyFileDetectRunResponse; runId: string } + | { data: { status: string }; runId: string }; + var promiseReq: Promise; switch (reqType){ case DeidenitfyFileRequestTypes.AUDIO: promiseReq = this.buildAudioRequest(fileObj, options, fileExtension) @@ -716,10 +719,11 @@ class DetectController { })); return; } - if (options?.getOutputDirectory() && data.status === "SUCCESS") { - this.processDeidentifyFileResponse(data, options.getOutputDirectory() as string, fileBaseName); + const fullResponse = data as DeidentifyFileDetectRunResponse; + if (options?.getOutputDirectory() && fullResponse.status === "SUCCESS") { + this.processDeidentifyFileResponse(fullResponse, options.getOutputDirectory() as string, fileBaseName); } - const deidentifiedFileResponse = this.parseDeidentifyFileResponse(data, runId, data.status); + const deidentifiedFileResponse = this.parseDeidentifyFileResponse(fullResponse, runId, fullResponse.status); resolve(deidentifiedFileResponse); }).catch(error => { reject(error)