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.
This commit is contained in:
sanish chirayath
2026-04-01 20:45:51 +05:30
committed by lohit-bruno
parent 3097f3aa76
commit 04fdd6f8a9
2 changed files with 189 additions and 22 deletions

View File

@@ -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
});

View File

@@ -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)