From 04fdd6f8a9136d8cfc3d8a6a051cc56210c4bffe Mon Sep 17 00:00:00 2001 From: sanish chirayath Date: Wed, 1 Apr 2026 20:45:51 +0530 Subject: [PATCH] feat: add helper to ensure string conversion for non-string values in Postman to Bruno conversion (#7644) * feat: add helper to ensure string conversion for non-string values in Postman to Bruno conversion - Introduced `ensureString` function to convert numeric and non-string values to strings, defaulting null/undefined to an empty string. - Updated request handling in `importPostmanV2CollectionItem` to utilize `ensureString` for headers, parameters, and body fields. - Added tests to verify correct conversion of numeric values to strings in headers, query parameters, and body fields. * test: add test for numeric value conversion in Postman to Bruno transformation - Implemented a new test case to verify that numeric values in example request and response fields are correctly converted to strings during the Postman to Bruno conversion process. - The test checks various components including request headers, query parameters, path parameters, and body fields to ensure proper string conversion. * test: add multipart form value test for numeric conversion in Postman to Bruno transformation - Added a new test case to verify that numeric values in multipart form data are correctly converted to strings during the Postman to Bruno conversion process. - The test checks the conversion of numeric values in the request body to ensure proper handling in the transformation. --- .../src/postman/postman-to-bruno.js | 56 ++++--- .../postman-to-bruno/postman-to-bruno.spec.js | 155 ++++++++++++++++++ 2 files changed, 189 insertions(+), 22 deletions(-) diff --git a/packages/bruno-converters/src/postman/postman-to-bruno.js b/packages/bruno-converters/src/postman/postman-to-bruno.js index de5f05707..ee544c7a8 100644 --- a/packages/bruno-converters/src/postman/postman-to-bruno.js +++ b/packages/bruno-converters/src/postman/postman-to-bruno.js @@ -73,6 +73,18 @@ const isItemAFolder = (item) => { return !item.request; }; +/** + * Postman allows non-string values (e.g. numbers) in fields like header values, + * query param values, etc. Bruno expects these to be strings. + * This helper converts non-null values to strings, defaulting null/undefined to ''. + */ +const ensureString = (value) => { + if (value == null) return ''; + if (typeof value === 'string') return value; + if (typeof value === 'object') return JSON.stringify(value); + return String(value); +}; + const convertV21Auth = (array) => { return array.reduce((accumulator, currentValue) => { accumulator[currentValue.key] = currentValue.value; @@ -470,12 +482,12 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } const isFile = param.type === 'file' || (param.type === 'default' && param.src); const value = isFile ? (Array.isArray(param.src) ? param.src : param.src ? [param.src] : []) - : (Array.isArray(param.value) ? param.value.join('') : param.value ?? ''); + : (Array.isArray(param.value) ? param.value.join('') : ensureString(param.value)); brunoRequestItem.request.body.multipartForm.push({ uid: uuid(), type: isFile ? 'file' : 'text', - name: param.key ?? '', + name: ensureString(param.key), value, description: transformDescription(param.description), enabled: !param.disabled, @@ -490,8 +502,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } if (param.key == null && param.value == null) return; brunoRequestItem.request.body.formUrlEncoded.push({ uid: uuid(), - name: param.key ?? '', - value: param.value ?? '', + name: ensureString(param.key), + value: ensureString(param.value), description: transformDescription(param.description), enabled: !param.disabled }); @@ -526,8 +538,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } if (header.key == null && header.value == null) return; brunoRequestItem.request.headers.push({ uid: uuid(), - name: header.key ?? '', - value: header.value ?? '', + name: ensureString(header.key), + value: ensureString(header.value), description: transformDescription(header.description), enabled: !header.disabled }); @@ -542,8 +554,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } } brunoRequestItem.request.params.push({ uid: uuid(), - name: param.key ?? '', - value: param.value ?? '', + name: ensureString(param.key), + value: ensureString(param.value), description: transformDescription(param.description), type: 'query', enabled: !param.disabled @@ -558,8 +570,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } brunoRequestItem.request.params.push({ uid: uuid(), - name: param.key, - value: param.value ?? '', + name: ensureString(param.key), + value: ensureString(param.value), description: transformDescription(param.description), type: 'path', enabled: true @@ -616,8 +628,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } if (header.key == null && header.value == null) return; example.request.headers.push({ uid: uuid(), - name: header.key ?? '', - value: header.value ?? '', + name: ensureString(header.key), + value: ensureString(header.value), description: transformDescription(header.description), enabled: !header.disabled }); @@ -632,8 +644,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } } example.request.params.push({ uid: uuid(), - name: param.key ?? '', - value: param.value ?? '', + name: ensureString(param.key), + value: ensureString(param.value), description: transformDescription(param.description), type: 'query', enabled: !param.disabled @@ -646,8 +658,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } if (!param.key) return; example.request.params.push({ uid: uuid(), - name: param.key, - value: param.value ?? '', + name: ensureString(param.key), + value: ensureString(param.value), description: transformDescription(param.description), type: 'path', enabled: true @@ -666,12 +678,12 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } const isFile = param.type === 'file' || (param.type === 'default' && param.src); const value = isFile ? (Array.isArray(param.src) ? param.src : param.src ? [param.src] : []) - : (Array.isArray(param.value) ? param.value.join('') : param.value ?? ''); + : (Array.isArray(param.value) ? param.value.join('') : ensureString(param.value)); example.request.body.multipartForm.push({ uid: uuid(), type: isFile ? 'file' : 'text', - name: param.key ?? '', + name: ensureString(param.key), value, description: transformDescription(param.description), enabled: !param.disabled, @@ -686,8 +698,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } if (param.key == null && param.value == null) return; example.request.body.formUrlEncoded.push({ uid: uuid(), - name: param.key ?? '', - value: param.value ?? '', + name: ensureString(param.key), + value: ensureString(param.value), description: transformDescription(param.description), enabled: !param.disabled }); @@ -717,8 +729,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } if (header.key == null && header.value == null) return; example.response.headers.push({ uid: uuid(), - name: header.key ?? '', - value: header.value ?? '', + name: ensureString(header.key), + value: ensureString(header.value), description: transformDescription(header.description), enabled: true }); diff --git a/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js b/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js index d55a8115b..17459f84b 100644 --- a/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js +++ b/packages/bruno-converters/tests/postman/postman-to-bruno/postman-to-bruno.spec.js @@ -769,6 +769,161 @@ describe('postman-collection', () => { expect(params[2].value).toBe(''); expect(params[2].type).toBe('query'); }); + + it('should convert numeric values to strings in headers, params, and body fields', async () => { + const collectionWithNumericValues = { + info: { + _postman_id: 'test-numeric-values', + name: 'collection with numeric values', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + item: [ + { + name: 'request with numeric values', + request: { + method: 'POST', + header: [ + { key: 'X-Account-Id', value: 0 }, + { key: 'X-Retry-Count', value: 3 } + ], + url: { + raw: 'https://example.com/api/:accountId', + protocol: 'https', + host: ['example', 'com'], + path: ['api', ':accountId'], + query: [ + { key: 'limit', value: 100 }, + { key: 'offset', value: 0 } + ], + variable: [ + { key: 'accountId', value: 0 } + ] + }, + body: { + mode: 'urlencoded', + urlencoded: [ + { key: 'timeout', value: 5000 } + ] + } + } + }, + { + name: 'request with numeric multipart form values', + request: { + method: 'POST', + header: [], + url: { raw: 'https://example.com/upload' }, + body: { + mode: 'formdata', + formdata: [ + { key: 'retries', value: 3, type: 'text' }, + { key: 'priority', value: 0, type: 'text' } + ] + } + } + } + ] + }; + + const brunoCollection = await postmanToBruno(collectionWithNumericValues); + const item = brunoCollection.items[0]; + + // Headers should have string values + expect(item.request.headers[0].value).toBe('0'); + expect(item.request.headers[1].value).toBe('3'); + + // Query params should have string values + const queryParams = item.request.params.filter((p) => p.type === 'query'); + expect(queryParams[0].value).toBe('100'); + expect(queryParams[1].value).toBe('0'); + + // Path params should have string values + const pathParams = item.request.params.filter((p) => p.type === 'path'); + expect(pathParams[0].value).toBe('0'); + + // Form URL-encoded should have string values + expect(item.request.body.formUrlEncoded[0].value).toBe('5000'); + + // Multipart form should have string values + const multipartItem = brunoCollection.items[1]; + expect(multipartItem.request.body.multipartForm[0].value).toBe('3'); + expect(multipartItem.request.body.multipartForm[1].value).toBe('0'); + }); + + it('should convert numeric values to strings in example request and response fields', async () => { + const collectionWithNumericExamples = { + info: { + _postman_id: 'test-numeric-examples', + name: 'collection with numeric example values', + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json' + }, + item: [ + { + name: 'request with numeric example', + request: { + method: 'GET', + header: [], + url: { raw: 'https://example.com/api' } + }, + response: [ + { + name: 'Example with numerics', + originalRequest: { + method: 'GET', + header: [ + { key: 'X-Account-Id', value: 42 } + ], + url: { + raw: 'https://example.com/api/:id?page=1', + protocol: 'https', + host: ['example', 'com'], + path: ['api', ':id'], + query: [ + { key: 'page', value: 1 } + ], + variable: [ + { key: 'id', value: 99 } + ] + }, + body: { + mode: 'urlencoded', + urlencoded: [ + { key: 'retries', value: 3 } + ] + } + }, + status: 'OK', + code: 200, + header: [ + { key: 'X-RateLimit-Remaining', value: 0 } + ], + body: '{"ok": true}' + } + ] + } + ] + }; + + const brunoCollection = await postmanToBruno(collectionWithNumericExamples); + const example = brunoCollection.items[0].examples[0]; + + // Example request headers + expect(example.request.headers[0].value).toBe('42'); + + // Example request query params + const queryParams = example.request.params.filter((p) => p.type === 'query'); + expect(queryParams[0].value).toBe('1'); + + // Example request path params + const pathParams = example.request.params.filter((p) => p.type === 'path'); + expect(pathParams[0].value).toBe('99'); + + // Example request form URL-encoded + expect(example.request.body.formUrlEncoded[0].value).toBe('3'); + + // Example response headers + expect(example.response.headers[0].value).toBe('0'); + }); }); // Simple Collection (postman)