From ac0dfae9a893aa8f8777c3ab2715f441701a76ba Mon Sep 17 00:00:00 2001 From: mulahasanovic Date: Thu, 14 May 2026 10:43:16 +0200 Subject: [PATCH] fix: skip formats table in generateTableUploadForm to avoid empty sasjsdata --- src/file/generateTableUploadForm.ts | 7 ++ src/file/spec/generateTableUploadForm.spec.ts | 71 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 src/file/spec/generateTableUploadForm.spec.ts diff --git a/src/file/generateTableUploadForm.ts b/src/file/generateTableUploadForm.ts index d7a13ef..ec33272 100644 --- a/src/file/generateTableUploadForm.ts +++ b/src/file/generateTableUploadForm.ts @@ -11,6 +11,13 @@ export const generateTableUploadForm = ( let tableCounter = 0 for (const tableName in data) { + if ( + isFormatsTable(tableName) && + Object.keys(data).includes(tableName.replace(/^\$/, '')) + ) { + continue + } + tableCounter++ // Formats table should not be sent as part of 'sasjs_tables' diff --git a/src/file/spec/generateTableUploadForm.spec.ts b/src/file/spec/generateTableUploadForm.spec.ts new file mode 100644 index 0000000..ebb994a --- /dev/null +++ b/src/file/spec/generateTableUploadForm.spec.ts @@ -0,0 +1,71 @@ +import { generateTableUploadForm } from '../generateTableUploadForm' +import { convertToCSV } from '../../utils/convertToCsv' +import NodeFormData from 'form-data' + +describe('generateTableUploadForm', () => { + it('should skip formats table and emit single sasjs1data for paired data + $data', () => { + const tableName = 'jsdata' + const data: { [key: string]: any } = { + [tableName]: [ + { var1: 'string', var2: 232 }, + { var1: 'string', var2: 233 } + ], + [`$${tableName}`]: { formats: { var1: '$char12.', var2: 'best.' } } + } + const expectedCsv = convertToCSV(data, tableName) + + const formData = new NodeFormData() + const { requestParams } = generateTableUploadForm(formData, data) + + expect(requestParams.sasjs_tables).toBe(tableName) + expect(requestParams.sasjs1data).toBe(expectedCsv) + expect(requestParams.sasjs2data).toBeUndefined() + }) + + it('should number sequentially across multiple tables w/ paired formats', () => { + const data: { [key: string]: any } = { + tableA: [{ a: 1 }], + $tableA: { formats: { a: 'best.' } }, + tableB: [{ b: 'x' }], + $tableB: { formats: { b: '$char1.' } } + } + const expectedCsvA = convertToCSV(data, 'tableA') + const expectedCsvB = convertToCSV(data, 'tableB') + + const formData = new NodeFormData() + const { requestParams } = generateTableUploadForm(formData, data) + + expect(requestParams.sasjs_tables).toBe('tableA tableB') + expect(requestParams.sasjs1data).toBe(expectedCsvA) + expect(requestParams.sasjs2data).toBe(expectedCsvB) + expect(requestParams.sasjs3data).toBeUndefined() + }) + + it('should throw if string value exceeds 32765 chars', () => { + const formData = new NodeFormData() + const data = { testTable: [{ var1: 'z'.repeat(32766) }] } + + expect(() => generateTableUploadForm(formData, data)).toThrow( + new Error( + 'The max length of a string value in SASjs is 32765 characters.' + ) + ) + }) + + it('should append chunks to formData when csv exceeds 16k', () => { + const tableName = 'big' + const data = { [tableName]: [{ var1: 'z'.repeat(16001) }] } + + const formData = new NodeFormData() + const appendSpy = jest.spyOn(formData, 'append') + + const { requestParams } = generateTableUploadForm(formData, data) + + expect(requestParams.sasjs_tables).toBe(tableName) + expect(requestParams.sasjs1data).toBeUndefined() + expect(appendSpy).toHaveBeenCalled() + expect(appendSpy.mock.calls.every(([key]) => key === 'sasjs1data')).toBe( + true + ) + }) +})