From 0d9ba36de84996b9a598bf738acafbc30eac054a Mon Sep 17 00:00:00 2001 From: Yury Shkoda Date: Thu, 6 Jul 2023 15:49:24 +0300 Subject: [PATCH] fix(file-upload-form): fixed form data for node env --- src/file/generateFileUploadForm.ts | 4 +- src/file/spec/generateFileUploadForm.spec.ts | 111 +++++++++++++------ src/job-execution/SasjsJobExecutor.ts | 5 +- src/job-execution/WebJobExecutor.ts | 8 +- src/utils/getFormData.ts | 5 + src/utils/index.ts | 1 + src/utils/spec/getFormData.spec.ts | 20 ++++ 7 files changed, 113 insertions(+), 41 deletions(-) create mode 100644 src/utils/getFormData.ts create mode 100644 src/utils/spec/getFormData.spec.ts diff --git a/src/file/generateFileUploadForm.ts b/src/file/generateFileUploadForm.ts index d88f13d..0fc695f 100644 --- a/src/file/generateFileUploadForm.ts +++ b/src/file/generateFileUploadForm.ts @@ -26,12 +26,12 @@ export const generateFileUploadForm = ( ) } - if (typeof FormData === 'undefined' && formData instanceof NodeFormData) { + if (formData instanceof NodeFormData) { formData.append(name, csv, { filename: `${name}.csv`, contentType: 'application/csv' }) - } else { + } else if (formData instanceof FormData) { const file = new Blob([csv], { type: 'application/csv' }) diff --git a/src/file/spec/generateFileUploadForm.spec.ts b/src/file/spec/generateFileUploadForm.spec.ts index fb97fb9..62db1d5 100644 --- a/src/file/spec/generateFileUploadForm.spec.ts +++ b/src/file/spec/generateFileUploadForm.spec.ts @@ -1,4 +1,6 @@ import { generateFileUploadForm } from '../generateFileUploadForm' +import * as NodeFormData from 'form-data' +import { convertToCSV } from '../../utils/convertToCsv' describe('generateFileUploadForm', () => { beforeAll(() => { @@ -11,44 +13,89 @@ describe('generateFileUploadForm', () => { ;(global as any).Blob = BlobMock }) - it('should generate file upload form from data', () => { - const formData = new FormData() - const testTable = 'sometable' - const testTableWithNullVars: { [key: string]: any } = { - [testTable]: [ - { var1: 'string', var2: 232, nullvar: 'A' }, - { var1: 'string', var2: 232, nullvar: 'B' }, - { var1: 'string', var2: 232, nullvar: '_' }, - { var1: 'string', var2: 232, nullvar: 0 }, - { var1: 'string', var2: 232, nullvar: 'z' }, - { var1: 'string', var2: 232, nullvar: null } - ], - [`$${testTable}`]: { formats: { var1: '$char12.', nullvar: 'best.' } } - } - const tableName = Object.keys(testTableWithNullVars).filter((key: string) => - Array.isArray(testTableWithNullVars[key]) - )[0] + describe('browser', () => { + it('should generate file upload form from data', () => { + const formData = new FormData() + const testTable = 'sometable' + const testTableWithNullVars: { [key: string]: any } = { + [testTable]: [ + { var1: 'string', var2: 232, nullvar: 'A' }, + { var1: 'string', var2: 232, nullvar: 'B' }, + { var1: 'string', var2: 232, nullvar: '_' }, + { var1: 'string', var2: 232, nullvar: 0 }, + { var1: 'string', var2: 232, nullvar: 'z' }, + { var1: 'string', var2: 232, nullvar: null } + ], + [`$${testTable}`]: { formats: { var1: '$char12.', nullvar: 'best.' } } + } + const tableName = Object.keys(testTableWithNullVars).filter( + (key: string) => Array.isArray(testTableWithNullVars[key]) + )[0] - jest.spyOn(formData, 'append').mockImplementation(() => {}) + jest.spyOn(formData, 'append').mockImplementation(() => {}) - generateFileUploadForm(formData, testTableWithNullVars) + generateFileUploadForm(formData, testTableWithNullVars) - expect(formData.append).toHaveBeenCalledOnce() - expect(formData.append).toHaveBeenCalledWith( - tableName, - {}, - `${tableName}.csv` - ) + expect(formData.append).toHaveBeenCalledOnce() + expect(formData.append).toHaveBeenCalledWith( + tableName, + {}, + `${tableName}.csv` + ) + }) + + it('should throw an error if too large string was provided', () => { + const formData = new FormData() + const data = { testTable: [{ var1: 'z'.repeat(32765 + 1) }] } + + expect(() => generateFileUploadForm(formData, data)).toThrow( + new Error( + 'The max length of a string value in SASjs is 32765 characters.' + ) + ) + }) }) - it('should throw an error if too large string was provided', () => { - const formData = new FormData() - const data = { testTable: [{ var1: 'z'.repeat(32765 + 1) }] } + describe('node', () => { + it('should generate file upload form from data', () => { + const formData = new NodeFormData() + const testTable = 'sometable' + const testTableWithNullVars: { [key: string]: any } = { + [testTable]: [ + { var1: 'string', var2: 232, nullvar: 'A' }, + { var1: 'string', var2: 232, nullvar: 'B' }, + { var1: 'string', var2: 232, nullvar: '_' }, + { var1: 'string', var2: 232, nullvar: 0 }, + { var1: 'string', var2: 232, nullvar: 'z' }, + { var1: 'string', var2: 232, nullvar: null } + ], + [`$${testTable}`]: { formats: { var1: '$char12.', nullvar: 'best.' } } + } + const tableName = Object.keys(testTableWithNullVars).filter( + (key: string) => Array.isArray(testTableWithNullVars[key]) + )[0] + const csv = convertToCSV(testTableWithNullVars, tableName) - expect(() => generateFileUploadForm(formData, data)).toThrow( - new Error( - 'The max length of a string value in SASjs is 32765 characters.' + jest.spyOn(formData, 'append').mockImplementation(() => {}) + + generateFileUploadForm(formData, testTableWithNullVars) + + expect(formData.append).toHaveBeenCalledOnce() + expect(formData.append).toHaveBeenCalledWith(tableName, csv, { + contentType: 'application/csv', + filename: `${tableName}.csv` + }) + }) + + it('should throw an error if too large string was provided', () => { + const formData = new NodeFormData() + const data = { testTable: [{ var1: 'z'.repeat(32765 + 1) }] } + + expect(() => generateFileUploadForm(formData, data)).toThrow( + new Error( + 'The max length of a string value in SASjs is 32765 characters.' + ) ) - ) + }) }) }) diff --git a/src/job-execution/SasjsJobExecutor.ts b/src/job-execution/SasjsJobExecutor.ts index 6b768f8..0aecbf4 100644 --- a/src/job-execution/SasjsJobExecutor.ts +++ b/src/job-execution/SasjsJobExecutor.ts @@ -10,8 +10,8 @@ import { LoginRequiredError } from '../types/errors' import { generateFileUploadForm } from '../file/generateFileUploadForm' - import { RequestClient } from '../request/RequestClient' +import { getFormData } from '../utils' import { isRelativePath, @@ -53,8 +53,7 @@ export class SasjsJobExecutor extends BaseJobExecutor { * Use the available form data object (FormData in Browser, NodeFormData in * Node) */ - let formData = - typeof FormData === 'undefined' ? new NodeFormData() : new FormData() + let formData = getFormData() if (data) { // file upload approach diff --git a/src/job-execution/WebJobExecutor.ts b/src/job-execution/WebJobExecutor.ts index fe52903..2fd65e5 100644 --- a/src/job-execution/WebJobExecutor.ts +++ b/src/job-execution/WebJobExecutor.ts @@ -16,10 +16,11 @@ import { SASViyaApiClient } from '../SASViyaApiClient' import { isRelativePath, parseSasViyaDebugResponse, - appendExtraResponseAttributes + appendExtraResponseAttributes, + parseWeboutResponse, + getFormData } from '../utils' import { BaseJobExecutor } from './JobExecutor' -import { parseWeboutResponse } from '../utils/parseWeboutResponse' export interface WaitingRequstPromise { promise: Promise | null @@ -112,8 +113,7 @@ export class WebJobExecutor extends BaseJobExecutor { * Use the available form data object (FormData in Browser, NodeFormData in * Node) */ - let formData = - typeof FormData === 'undefined' ? new NodeFormData() : new FormData() + let formData = getFormData() if (data) { const stringifiedData = JSON.stringify(data) diff --git a/src/utils/getFormData.ts b/src/utils/getFormData.ts new file mode 100644 index 0000000..8290af3 --- /dev/null +++ b/src/utils/getFormData.ts @@ -0,0 +1,5 @@ +import { isNode } from './' +import * as NodeFormData from 'form-data' + +export const getFormData = () => + isNode() ? new NodeFormData() : new FormData() diff --git a/src/utils/index.ts b/src/utils/index.ts index 4f02fc8..68def1a 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -20,3 +20,4 @@ export * from './parseWeboutResponse' export * from './serialize' export * from './splitChunks' export * from './validateInput' +export * from './getFormData' diff --git a/src/utils/spec/getFormData.spec.ts b/src/utils/spec/getFormData.spec.ts new file mode 100644 index 0000000..1fd192c --- /dev/null +++ b/src/utils/spec/getFormData.spec.ts @@ -0,0 +1,20 @@ +import { getFormData } from '..' +import * as isNodeModule from '../isNode' +import * as NodeFormData from 'form-data' + +describe('getFormData', () => { + it('should return NodeFormData if environment is Node', () => { + jest.spyOn(isNodeModule, 'isNode').mockImplementation(() => true) + + expect(getFormData() instanceof NodeFormData).toEqual(true) + }) + + it('should return FormData if environment is not Node', () => { + const formDataMock = () => {} + ;(global as any).FormData = formDataMock + + jest.spyOn(isNodeModule, 'isNode').mockImplementation(() => false) + + expect(getFormData() instanceof FormData).toEqual(true) + }) +})