Merge pull request #5107 from sanish-bruno/fix/auth-type-missing-dupe-2

Bug/improve-handling-of -Inherit-for-folders-and-request
This commit is contained in:
lohit
2025-07-15 17:07:34 +05:30
committed by GitHub
8 changed files with 940 additions and 185 deletions

View File

@@ -77,7 +77,7 @@ const Auth = ({ collection, folder }) => {
const parentFolder = folderTreePath[i];
if (parentFolder.type === 'folder') {
const folderAuth = get(parentFolder, 'root.request.auth');
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') {
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') {
effectiveSource = {
type: 'folder',
name: parentFolder.name,

View File

@@ -54,7 +54,7 @@ const Auth = ({ item, collection }) => {
for (let i of [...requestTreePath].reverse()) {
if (i.type === 'folder') {
const folderAuth = get(i, 'root.request.auth');
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'none' && folderAuth.mode !== 'inherit') {
if (folderAuth && folderAuth.mode && folderAuth.mode !== 'inherit') {
effectiveSource = {
type: 'folder',
name: i.name,

View File

@@ -2,7 +2,7 @@ import get from 'lodash/get';
import { validateSchema, transformItemsInCollection, hydrateSeqInCollection, uuid } from '../common';
import each from 'lodash/each';
import postmanTranslation from './postman-translations';
import { invalidVariableCharacterRegex } from '../constants/index';
import { invalidVariableCharacterRegex } from '../constants/index';
const AUTH_TYPES = Object.freeze({
BASIC: 'basic',
@@ -56,7 +56,7 @@ const convertV21Auth = (array) => {
const constructUrlFromParts = (url) => {
if (!url) return '';
const { protocol = 'http', host, path, port, query, hash } = url || {};
const hostStr = Array.isArray(host) ? host.filter(Boolean).join('.') : host || '';
const pathStr = Array.isArray(path) ? path.filter(Boolean).join('/') : path || '';
@@ -64,9 +64,9 @@ const constructUrlFromParts = (url) => {
const queryStr =
query && Array.isArray(query) && query.length > 0
? `?${query
.filter((q) => q && q.key)
.map((q) => `${q.key}=${q.value || ''}`)
.join('&')}`
.filter((q) => q && q.key)
.map((q) => `${q.key}=${q.value || ''}`)
.join('&')}`
: '';
const urlStr = `${protocol}://${hostStr}${portStr}${pathStr ? `/${pathStr}` : ''}${queryStr}`;
return urlStr;
@@ -140,39 +140,44 @@ const importCollectionLevelVariables = (variables, requestObject) => {
requestObject.vars.req = vars;
};
export const processAuth = (auth, requestObject) => {
if (!auth || !auth.type || auth.type === AUTH_TYPES.NOAUTH) {
return;
}
let authValues = auth[auth.type];
if(!authValues) {
console.warn('Unexpected auth.type, auth object doesn\'t have the key', auth.type);
requestObject.auth.mode = auth.type;
authValues = {};
export const processAuth = (auth, requestObject, isCollection = false) => {
// As of 14/05/2025
// When collections are set to "No Auth" in Postman, the auth object is null.
// When folders and requests are set to "Inherit" in Postman, the auth object is null.
// When folders and requests are set to "No Auth" in Postman, the auth object is present.
// Handle collection-specific "No Auth"
if (isCollection && !auth) return; // Return as requestObject is a collection and has a default mode = none
// Handle folder/request specific "Inherit"
if (!auth) return; // Return as requestObject is a folder/request and has a default mode = inherit
// Handle folder/request specific "No Auth"
if (auth.type === AUTH_TYPES.NOAUTH) {
requestObject.auth.mode = AUTH_TYPES.NONE; // Set the mode to none
return; // No further processing needed
}
let authValues = auth[auth.type] ?? [];
if (Array.isArray(authValues)) {
authValues = convertV21Auth(authValues);
}
requestObject.auth.mode = auth.type; // Set the mode based on Postman's auth type
switch (auth.type) {
case AUTH_TYPES.BASIC:
requestObject.auth.mode = AUTH_TYPES.BASIC;
requestObject.auth.basic = {
username: authValues.username || '',
password: authValues.password || ''
};
break;
case AUTH_TYPES.BEARER:
requestObject.auth.mode = AUTH_TYPES.BEARER;
requestObject.auth.bearer = {
token: authValues.token || ''
};
break;
case AUTH_TYPES.AWSV4:
requestObject.auth.mode = AUTH_TYPES.AWSV4;
requestObject.auth.awsv4 = {
accessKeyId: authValues.accessKey || '',
secretAccessKey: authValues.secretKey || '',
@@ -183,7 +188,6 @@ export const processAuth = (auth, requestObject) => {
};
break;
case AUTH_TYPES.APIKEY:
requestObject.auth.mode = AUTH_TYPES.APIKEY;
requestObject.auth.apikey = {
key: authValues.key || '',
value: authValues.value?.toString() || '', // Convert the value to a string as Postman's schema does not rigidly define the type of it,
@@ -191,75 +195,79 @@ export const processAuth = (auth, requestObject) => {
};
break;
case AUTH_TYPES.DIGEST:
requestObject.auth.mode = AUTH_TYPES.DIGEST;
requestObject.auth.digest = {
username: authValues.username || '',
password: authValues.password || ''
};
break;
case AUTH_TYPES.OAUTH2:
const findValueUsingKey = (key) => {
return authValues[key] || '';
};
const findValueUsingKey = (key) => authValues[key] || '';
// Maps Postman's grant_type to the Bruno's grantType string expected in the target object
const oauth2GrantTypeMaps = {
authorization_code_with_pkce: 'authorization_code',
authorization_code: 'authorization_code',
client_credentials: 'client_credentials',
password_credentials: 'password_credentials'
password_credentials: 'password'
};
const grantType = oauth2GrantTypeMaps[findValueUsingKey('grant_type')] || 'authorization_code';
requestObject.auth.mode = AUTH_TYPES.OAUTH2;
if (grantType === 'authorization_code') {
requestObject.auth.oauth2 = {
grantType: 'authorization_code',
authorizationUrl: findValueUsingKey('authUrl'),
callbackUrl: findValueUsingKey('redirect_uri'),
accessTokenUrl: findValueUsingKey('accessTokenUrl'),
refreshTokenUrl: findValueUsingKey('refreshTokenUrl'),
clientId: findValueUsingKey('clientId'),
clientSecret: findValueUsingKey('clientSecret'),
scope: findValueUsingKey('scope'),
state: findValueUsingKey('state'),
pkce: Boolean(findValueUsingKey('grant_type') == 'authorization_code_with_pkce'),
tokenPlacement: findValueUsingKey('addTokenTo') == 'header' ? 'header' : 'url',
credentialsPlacement: findValueUsingKey('client_authentication') == 'body' ? 'body' : 'basic_auth_header'
};
} else if (grantType === 'password_credentials') {
requestObject.auth.oauth2 = {
grantType: 'password',
accessTokenUrl: findValueUsingKey('accessTokenUrl'),
refreshTokenUrl: findValueUsingKey('refreshTokenUrl'),
username: findValueUsingKey('username'),
password: findValueUsingKey('password'),
clientId: findValueUsingKey('clientId'),
clientSecret: findValueUsingKey('clientSecret'),
scope: findValueUsingKey('scope'),
state: findValueUsingKey('state'),
tokenPlacement: findValueUsingKey('addTokenTo') == 'header' ? 'header' : 'url',
credentialsPlacement: findValueUsingKey('client_authentication') == 'body' ? 'body' : 'basic_auth_header'
};
} else if (grantType === 'client_credentials') {
requestObject.auth.oauth2 = {
grantType: 'client_credentials',
accessTokenUrl: findValueUsingKey('accessTokenUrl'),
refreshTokenUrl: findValueUsingKey('refreshTokenUrl'),
clientId: findValueUsingKey('clientId'),
clientSecret: findValueUsingKey('clientSecret'),
scope: findValueUsingKey('scope'),
state: findValueUsingKey('state'),
tokenPlacement: findValueUsingKey('addTokenTo') == 'header' ? 'header' : 'url',
credentialsPlacement: findValueUsingKey('client_authentication') == 'body' ? 'body' : 'basic_auth_header'
};
const postmanGrantType = findValueUsingKey('grant_type');
const targetGrantType = oauth2GrantTypeMaps[postmanGrantType] || 'client_credentials'; // Default
// Common properties for all OAuth2 grant types
const baseOAuth2Config = {
grantType: targetGrantType,
accessTokenUrl: findValueUsingKey('accessTokenUrl'),
refreshTokenUrl: findValueUsingKey('refreshTokenUrl'),
clientId: findValueUsingKey('clientId'),
clientSecret: findValueUsingKey('clientSecret'),
scope: findValueUsingKey('scope'),
state: findValueUsingKey('state'),
tokenPlacement: findValueUsingKey('addTokenTo') === 'header' ? 'header' : 'url',
credentialsPlacement: findValueUsingKey('client_authentication') === 'body' ? 'body' : 'basic_auth_header'
};
switch (postmanGrantType) {
case 'authorization_code':
requestObject.auth.oauth2 = {
...baseOAuth2Config,
authorizationUrl: findValueUsingKey('authUrl'),
callbackUrl: findValueUsingKey('redirect_uri'),
pkce: false // PKCE is not used for standard authorization_code
};
break;
case 'authorization_code_with_pkce':
requestObject.auth.oauth2 = {
...baseOAuth2Config,
authorizationUrl: findValueUsingKey('authUrl'),
callbackUrl: findValueUsingKey('redirect_uri'),
pkce: true, // Explicitly set pkce to true for this grant type
};
break;
case 'password_credentials':
requestObject.auth.oauth2 = {
...baseOAuth2Config,
username: findValueUsingKey('username'),
password: findValueUsingKey('password'),
};
break;
case 'client_credentials':
requestObject.auth.oauth2 = baseOAuth2Config;
break;
default:
console.warn('Unexpected OAuth2 grant type after mapping:', targetGrantType);
requestObject.auth.oauth2 = baseOAuth2Config; // Fallback to default which is Client Credentials
break;
}
break;
default:
requestObject.auth.mode = AUTH_TYPES.NONE;
console.warn('Unexpected auth.type', auth.type);
console.warn('Unexpected auth.type:', auth.type, '- Mode set, but no specific config generated.');
break;
}
};
const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorkers = false } = {}, scriptMap)=> {
const importPostmanV2CollectionItem = (brunoParent, item, { useWorkers = false } = {}, scriptMap) => {
brunoParent.items = brunoParent.items || [];
const folderMap = {};
const requestMap = {};
@@ -289,7 +297,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
},
request: {
auth: {
mode: 'none',
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
@@ -308,19 +316,14 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
brunoParent.items.push(brunoFolderItem);
// Folder level auth
if (i.auth) {
processAuth(i.auth, brunoFolderItem.root.request);
} else if (parentAuth) {
// Inherit parent auth if folder doesn't define its own
processAuth(parentAuth, brunoFolderItem.root.request);
}
processAuth(i.auth, brunoFolderItem.root.request);
if (i.item && i.item.length) {
importPostmanV2CollectionItem(brunoFolderItem, i.item, i.auth ?? parentAuth, { useWorkers }, scriptMap);
importPostmanV2CollectionItem(brunoFolderItem, i.item, { useWorkers }, scriptMap);
}
if (i.event) {
if(useWorkers) {
if (useWorkers) {
scriptMap.set(brunoFolderItem.uid, {
events: i.event,
request: brunoFolderItem.root.request
@@ -353,12 +356,12 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
uid: uuid(),
name: requestName,
type: 'http-request',
seq: index + 1,
seq: index + 1,
request: {
url: url,
method: i?.request?.method?.toUpperCase(),
auth: {
mode: 'none',
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
@@ -389,8 +392,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
brunoParent.items.push(brunoRequestItem);
if (i.event) {
if(useWorkers) {
scriptMap.set(brunoRequestItem.uid, {
if (useWorkers) {
scriptMap.set(brunoRequestItem.uid, {
events: i.event,
request: brunoRequestItem.request
});
@@ -501,9 +504,8 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
});
});
// Handle request-level auth or inherit from parent
const auth = i.request.auth ?? parentAuth;
processAuth(auth, brunoRequestItem.request);
// Request-level auth
processAuth(i.request.auth, brunoRequestItem.request);
each(get(i, 'request.url.query'), (param) => {
brunoRequestItem.request.params.push({
@@ -537,7 +539,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
});
};
const searchLanguageByHeader = (headers) => {
let contentType;
each(headers, (header) => {
@@ -592,19 +594,19 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) =>
}
// Collection level auth
processAuth(collection.auth, brunoCollection.root.request);
processAuth(collection.auth, brunoCollection.root.request, true);
// Create a single scriptMap for all items
const scriptMap = useWorkers ? new Map() : null;
importPostmanV2CollectionItem(brunoCollection, collection.item, collection.auth, { useWorkers }, scriptMap);
importPostmanV2CollectionItem(brunoCollection, collection.item, { useWorkers }, scriptMap);
// Process all scripts in a single call at the top level
if (useWorkers && scriptMap && scriptMap.size > 0) {
try {
const { default: scriptTranslationWorker } = await import('../workers/postman-translator-worker');
const { default: scriptTranslationWorker } = await import('../workers/postman-translator-worker');
const translatedScripts = await scriptTranslationWorker(scriptMap);
// Apply translated scripts to all items in the collection
const applyScriptsToItems = (items) => {
items.forEach(item => {
@@ -614,14 +616,17 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) =>
if (!item.root.request.script) {
item.root.request.script = {};
}
if (!item.root.request.tests) {
item.root.request.tests = '';
}
const script = translatedScripts.get(item.uid).request?.script?.req;
const tests = translatedScripts.get(item.uid).request?.script?.res;
item.root.request.script.req = script && script.length > 0 ? script : '';
item.root.request.script.res = tests && tests.length > 0 ? tests : '';
}
// Recursively apply to nested items
if (item.items && item.items.length > 0) {
applyScriptsToItems(item.items);
@@ -631,26 +636,29 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) =>
if (!item.request.script) {
item.request.script = {};
}
if (!item.request.tests) {
item.request.tests = '';
}
const script = translatedScripts.get(item.uid).request?.script?.req;
const tests = translatedScripts.get(item.uid).request?.script?.res;
item.request.script.req = script && script.length > 0 ? script : '';
item.request.script.res = tests && tests.length > 0 ? tests : '';
}
}
});
};
applyScriptsToItems(brunoCollection.items);
} catch (error) {
console.error('Error in script translation worker:', error);
} finally {
scriptMap.clear();
}
}
return brunoCollection;
};

View File

@@ -2,7 +2,48 @@ import { describe, it, expect } from '@jest/globals';
import postmanToBruno from '../../../src/postman/postman-to-bruno';
describe('Collection Authentication', () => {
it('should handle basic auth at collection level', async() => {
it('should handle no auth at collection level (when auth property is absent)', async () => {
const postmanCollection = {
info: {
name: 'Collection level no auth',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: [],
event: [
{
listen: 'prerequest',
script: {
type: 'text/javascript',
packages: {},
exec: ['']
}
},
{
listen: 'test',
script: {
type: 'text/javascript',
packages: {},
exec: ['']
}
}
]
};
const result = await postmanToBruno(postmanCollection);
// console.log('result', JSON.stringify(result, null, 2));
expect(result.root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle basic auth at collection level', async () => {
const postmanCollection = {
info: {
name: 'Collection level basic auth',
@@ -61,7 +102,7 @@ describe('Collection Authentication', () => {
});
});
it('should handle bearer token auth at collection level', async() => {
it('should handle bearer token auth at collection level', async () => {
const postmanCollection = {
info: {
name: 'Collection level bearer token',
@@ -112,9 +153,9 @@ describe('Collection Authentication', () => {
oauth2: null,
digest: null
});
});
});
it('should handle API key auth at collection level', async() => {
it('should handle API key auth at collection level', async () => {
const postmanCollection = {
info: {
name: 'Collection level api key',
@@ -173,7 +214,7 @@ describe('Collection Authentication', () => {
});
});
it('should handle digest auth at collection level', async() => {
it('should handle digest auth at collection level', async () => {
const postmanCollection = {
info: {
name: 'Collection level digest auth',
@@ -235,8 +276,7 @@ describe('Collection Authentication', () => {
}
});
});
it('should handle missing auth values when auth.type exists', async() => {
it('should handle missing auth values when auth.type exists', async () => {
const postmanCollection = {
info: {
name: 'Collection with missing auth values',
@@ -283,7 +323,7 @@ describe('Collection Authentication', () => {
});
});
it('should handle missing auth values for different auth types', async() => {
it('should handle missing auth values for different auth types', async () => {
const postmanCollection = {
info: {
name: 'Collection with missing auth values for different types',

View File

@@ -2,7 +2,130 @@ import { describe, it, expect } from '@jest/globals';
import postmanToBruno from '../../../src/postman/postman-to-bruno';
describe('Folder Authentication', () => {
it('should handle basic auth at folder level', async() => {
it('should handle "Inherit Auth" at folder level (auth property absent)', async () => {
const postmanCollection = {
info: {
name: 'Folder Inherit Auth',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: [
{
name: 'Inheriting Folder',
items: []
}
],
auth: {
type: 'basic',
basic: [
{
key: 'password',
value: 'testpass',
type: 'string'
},
{
key: 'username',
value: 'testuser',
type: 'string'
}
]
},
event: [
{
listen: 'prerequest',
script: {
type: 'text/javascript',
packages: {},
exec: ['']
}
},
{
listen: 'test',
script: {
type: 'text/javascript',
packages: {},
exec: ['']
}
}
]
};
const result = await postmanToBruno(postmanCollection);
expect(result.items[0].root.request.auth).toEqual({
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle explicit "No Auth" at folder level', async () => {
const postmanCollection = {
info: {
name: 'Folder No Auth',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: [
{
name: 'No Auth Folder',
item: [],
auth: {
type: 'noauth'
}
}
],
auth: {
type: 'basic',
basic: [
{
key: 'password',
value: 'testpass',
type: 'string'
},
{
key: 'username',
value: 'testuser',
type: 'string'
}
]
},
event: [
{
listen: 'prerequest',
script: {
type: 'text/javascript',
packages: {},
exec: ['']
}
},
{
listen: 'test',
script: {
type: 'text/javascript',
packages: {},
exec: ['']
}
}
]
};
const result = await postmanToBruno(postmanCollection);
expect(result.items[0].root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle basic auth at folder level', async () => {
const postmanCollection = {
info: {
name: 'Folder level basic auth',
@@ -65,7 +188,7 @@ describe('Folder Authentication', () => {
});
});
it('should handle bearer token auth at folder level', async() => {
it('should handle bearer token auth at folder level', async () => {
const postmanCollection = {
info: {
name: 'Folder level bearer token',
@@ -120,7 +243,7 @@ describe('Folder Authentication', () => {
});
});
it('should handle API key auth at folder level', async() => {
it('should handle API key auth at folder level', async () => {
const postmanCollection = {
info: {
name: 'Folder level API key',
@@ -180,7 +303,7 @@ describe('Folder Authentication', () => {
});
});
it('should handle digest auth at folder level', async() => {
it('should handle digest auth at folder level', async () => {
const postmanCollection = {
info: {
name: 'Folder level digest auth',
@@ -245,7 +368,7 @@ describe('Folder Authentication', () => {
});
});
it('should handle missing auth values in folder level auth', async() => {
it('should handle missing auth values in folder level auth', async () => {
const postmanCollection = {
info: {
name: 'Folder with missing auth values',

View File

@@ -73,6 +73,276 @@ describe('postman-collection', () => {
const brunoCollection = await postmanToBruno(collectionWithEmptyVars);
expect(brunoCollection.root.request.vars.req).toEqual([]);
});
it('should handle collection with auth object having undefined type', async () => {
const collectionWithUndefinedAuthType = {
'info': {
'_postman_id': '7f91bbd8-cb97-41ac-8d0b-e1fcd8bb4ce9',
'name': 'collection with undefined auth type',
'schema': 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
'auth': {
'basic': [
{ key: 'username', value: 'testuser', type: 'string' },
{ key: 'password', value: 'testpass', type: 'string' }
]
},
'item': [
{
'name': 'request',
'request': {
'method': 'GET',
'header': [],
'url': {
'raw': 'https://api.example.com/test',
'protocol': 'https',
'host': ['api', 'example', 'com'],
'path': ['test']
}
}
}
]
};
const brunoCollection = await postmanToBruno(collectionWithUndefinedAuthType);
// Collection level auth should default to 'none'
expect(brunoCollection.root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
// Request should inherit auth mode
expect(brunoCollection.items[0].request.auth).toEqual({
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle collection with auth object having null type', async () => {
const collectionWithNullAuthType = {
'info': {
'_postman_id': '7f91bbd8-cb97-41ac-8d0b-e1fcd8bb4ce9',
'name': 'collection with null auth type',
'schema': 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
'auth': {
'type': null,
'bearer': {
'token': 'test-token'
}
},
'item': [
{
'name': 'request',
'request': {
'method': 'GET',
'header': [],
'url': {
'raw': 'https://api.example.com/test',
'protocol': 'https',
'host': ['api', 'example', 'com'],
'path': ['test']
}
}
}
]
};
const brunoCollection = await postmanToBruno(collectionWithNullAuthType);
// Collection level auth should default to 'none'
expect(brunoCollection.root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle collection with auth object having unexpected type value', async () => {
const collectionWithUnexpectedAuthType = {
'info': {
'_postman_id': '7f91bbd8-cb97-41ac-8d0b-e1fcd8bb4ce9',
'name': 'collection with unexpected auth type',
'schema': 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
'auth': {
'type': 'unexpected_auth_type',
'basic': [
{ key: 'username', value: 'testuser', type: 'string' },
{ key: 'password', value: 'testpass', type: 'string' }
]
},
'item': [
{
'name': 'request',
'request': {
'method': 'GET',
'header': [],
'url': {
'raw': 'https://api.example.com/test',
'protocol': 'https',
'host': ['api', 'example', 'com'],
'path': ['test']
}
}
}
]
};
const brunoCollection = await postmanToBruno(collectionWithUnexpectedAuthType);
// Collection level auth should default to 'none'
expect(brunoCollection.root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
// Request should inherit auth mode
expect(brunoCollection.items[0].request.auth).toEqual({
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle request with auth object having undefined type', async () => {
const collectionWithRequestUndefinedAuthType = {
'info': {
'_postman_id': '7f91bbd8-cb97-41ac-8d0b-e1fcd8bb4ce9',
'name': 'collection with request undefined auth type',
'schema': 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
'item': [
{
'name': 'request',
'request': {
'method': 'GET',
'header': [],
'url': {
'raw': 'https://api.example.com/test',
'protocol': 'https',
'host': ['api', 'example', 'com'],
'path': ['test']
},
'auth': {
'basic': [
{ key: 'username', value: 'testuser', type: 'string' },
{ key: 'password', value: 'testpass', type: 'string' }
]
}
}
}
]
};
const brunoCollection = await postmanToBruno(collectionWithRequestUndefinedAuthType);
// Collection level auth should default to 'none'
expect(brunoCollection.root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
// Request auth should default to 'none'
expect(brunoCollection.items[0].request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle folder with auth object having unexpected type', async () => {
const collectionWithFolderUnexpectedAuthType = {
'info': {
'_postman_id': '7f91bbd8-cb97-41ac-8d0b-e1fcd8bb4ce9',
'name': 'collection with folder unexpected auth type',
'schema': 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
'item': [
{
'name': 'folder',
'auth': {
'type': 'unexpected_folder_auth_type',
'bearer': {
'token': 'folder-token'
}
},
'item': [
{
'name': 'request',
'request': {
'method': 'GET',
'header': [],
'url': {
'raw': 'https://api.example.com/test',
'protocol': 'https',
'host': ['api', 'example', 'com'],
'path': ['test']
}
}
}
]
}
]
};
const brunoCollection = await postmanToBruno(collectionWithFolderUnexpectedAuthType);
// Folder auth should default to 'none'
expect(brunoCollection.items[0].root.request.auth).toEqual({
mode: 'none',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
// Request should inherit auth mode
expect(brunoCollection.items[0].items[0].request.auth).toEqual({
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
});
// Simple Collection (postman)
@@ -154,7 +424,7 @@ const expectedOutput = {
"url": "https://usebruno.com",
"method": "GET",
"auth": {
"mode": "none",
"mode": "inherit",
"basic": null,
"bearer": null,
"awsv4": null,
@@ -183,7 +453,7 @@ const expectedOutput = {
},
"request": {
"auth": {
"mode": "none",
"mode": "inherit",
"basic": null,
"bearer": null,
"awsv4": null,
@@ -207,7 +477,7 @@ const expectedOutput = {
"url": "https://usebruno.com",
"method": "GET",
"auth": {
"mode": "none",
"mode": "inherit",
"basic": null,
"bearer": null,
"awsv4": null,

View File

@@ -354,16 +354,13 @@ describe('processAuth', () => {
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('oauth2');
expect(requestObject.auth.oauth2).toEqual({
grantType: 'authorization_code',
authorizationUrl: '',
callbackUrl: '',
grantType: 'client_credentials',
accessTokenUrl: '',
refreshTokenUrl: '',
clientId: '',
clientSecret: '',
scope: '',
state: '',
pkce: false,
tokenPlacement: 'url',
credentialsPlacement: 'basic_auth_header'
});
@@ -376,16 +373,13 @@ describe('processAuth', () => {
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('oauth2');
expect(requestObject.auth.oauth2).toEqual({
grantType: 'authorization_code',
authorizationUrl: '',
callbackUrl: '',
grantType: 'client_credentials',
accessTokenUrl: '',
refreshTokenUrl: '',
clientId: '',
clientSecret: '',
scope: '',
state: '',
pkce: false,
tokenPlacement: 'url',
credentialsPlacement: 'basic_auth_header'
});
@@ -425,4 +419,98 @@ describe('processAuth', () => {
credentialsPlacement: 'body'
});
});
it('should handle auth object with undefined type', () => {
const auth = {};
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('none');
expect(requestObject.auth.basic).toBe(null);
expect(requestObject.auth.bearer).toBe(null);
expect(requestObject.auth.awsv4).toBe(null);
expect(requestObject.auth.apikey).toBe(null);
expect(requestObject.auth.oauth2).toBe(null);
expect(requestObject.auth.digest).toBe(null);
});
it('should handle type as null and auth as null', () => {
const auth = {
type: null,
auth: null
};
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('none');
expect(requestObject.auth.basic).toBe(null);
expect(requestObject.auth.bearer).toBe(null);
expect(requestObject.auth.awsv4).toBe(null);
expect(requestObject.auth.apikey).toBe(null);
expect(requestObject.auth.oauth2).toBe(null);
expect(requestObject.auth.digest).toBe(null);
});
it('should handle auth object with undefined type, but basic auth', () => {
const auth = {
basic: [
{ key: 'username', value: 'testuser', type: 'string' },
{ key: 'password', value: 'testpass', type: 'string' }
]
};
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('none');
expect(requestObject.auth.basic).toBe(null);
expect(requestObject.auth.bearer).toBe(null);
expect(requestObject.auth.awsv4).toBe(null);
expect(requestObject.auth.apikey).toBe(null);
expect(requestObject.auth.oauth2).toBe(null);
expect(requestObject.auth.digest).toBe(null);
});
it('should handle auth object with null type', () => {
const auth = {
type: null,
};
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('none');
expect(requestObject.auth.basic).toBe(null);
expect(requestObject.auth.bearer).toBe(null);
expect(requestObject.auth.awsv4).toBe(null);
expect(requestObject.auth.apikey).toBe(null);
expect(requestObject.auth.oauth2).toBe(null);
expect(requestObject.auth.digest).toBe(null);
});
it('should handle auth object with empty string type', () => {
const auth = {
type: null,
basic: {
username: 'testuser',
password: 'testpass'
}
};
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('none');
expect(requestObject.auth.basic).toBe(null);
expect(requestObject.auth.bearer).toBe(null);
expect(requestObject.auth.awsv4).toBe(null);
expect(requestObject.auth.apikey).toBe(null);
expect(requestObject.auth.oauth2).toBe(null);
expect(requestObject.auth.digest).toBe(null);
});
it('should handle auth object with boolean type value', () => {
const auth = {
type: "unknown_auth_type",
unknown_auth_type: {
accessKey: 'test-access-key',
secretKey: 'test-secret-key'
}
};
processAuth(auth, requestObject);
expect(requestObject.auth.mode).toBe('none');
expect(requestObject.auth.basic).toBe(null);
expect(requestObject.auth.bearer).toBe(null);
expect(requestObject.auth.awsv4).toBe(null);
expect(requestObject.auth.apikey).toBe(null);
expect(requestObject.auth.oauth2).toBe(null);
expect(requestObject.auth.digest).toBe(null);
});
});

View File

@@ -2,7 +2,9 @@ import { describe, it, expect } from '@jest/globals';
import postmanToBruno from '../../../src/postman/postman-to-bruno';
describe('Request Authentication', () => {
it('should handle basic auth at request level', async() => {
it('should handle basic auth at request level', async () => {
const postmanCollection = {
info: {
name: 'Request Auth Collection',
@@ -42,7 +44,7 @@ describe('Request Authentication', () => {
});
});
it('should inherit folder auth when request has no auth', async() => {
it('should inherit folder auth when request has no auth', async () => {
const postmanCollection = {
info: {
name: 'Inherit Request Auth Collection',
@@ -57,7 +59,7 @@ describe('Request Authentication', () => {
},
item: [
{
name: 'No Auth Request',
name: 'Inherit Auth Request',
request: {
method: 'GET',
url: 'https://api.example.com/test'
@@ -71,11 +73,9 @@ describe('Request Authentication', () => {
const result = await postmanToBruno(postmanCollection);
expect(result.items[0].items[0].request.auth).toEqual({
mode: 'bearer',
mode: 'inherit',
basic: null,
bearer: {
token: 'foldertoken'
},
bearer: null,
awsv4: null,
apikey: null,
oauth2: null,
@@ -83,31 +83,113 @@ describe('Request Authentication', () => {
});
});
it('should override folder auth with request auth', async() => {
it('should handle "Inherit Auth" for request (auth property absent, inherits from folder)', async () => {
const postmanCollection = {
info: {
name: 'Override Request Auth Collection',
name: 'Request Inherit Auth from Folder',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: [
{
name: 'Auth Folder',
auth: {
type: 'basic',
basic: [
{ key: 'username', value: 'folderuser' },
{ key: 'password', value: 'folderpass' }
]
auth: { // Folder has auth
type: 'bearer',
bearer: [{ key: 'token', value: 'foldertoken' }]
},
item: [
{
name: 'Override Auth Request',
name: 'Inheriting Request',
request: {
method: 'GET',
url: 'https://api.example.com/test'
// auth property is ABSENT for this request, meaning "Inherit auth from parent"
}
}
]
}
]
};
const result = await postmanToBruno(postmanCollection);
expect(result.items[0].items[0].request.auth).toEqual({
mode: 'inherit',
basic: null,
bearer: null, // It should NOT have the folder's token directly here after import
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle "Inherit Auth" for request (auth property absent, inherits from collection if folder also inherits)', async () => {
const postmanCollection = {
info: {
name: 'Request Inherit Auth from Collection',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
auth: { // Collection has auth
type: 'basic',
basic: [
{ key: 'username', value: 'requestuser' },
{ key: 'password', value: 'requestpass' }
]
},
item: [
{
name: 'Inheriting Folder',
// auth property is ABSENT for this folder
item: [
{
name: 'Inheriting Request',
request: {
method: 'GET',
url: 'https://api.example.com/test'
// auth property is ABSENT for this request
}
}
]
}
]
};
const result = await postmanToBruno(postmanCollection);
// Check folder first
expect(result.items[0].root.request.auth).toEqual({
mode: 'inherit',
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
// Then check request
expect(result.items[0].items[0].request.auth).toEqual({
mode: 'inherit',
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
});
it('should handle explicit "No Auth" at request level', async () => {
const postmanCollection = {
info: {
name: 'Request No Auth',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: [
{
name: 'Auth Folder', // Parent folder might have auth
auth: {
type: 'bearer',
bearer: [{ key: 'token', value: 'foldertoken' }]
},
item: [
{
name: 'Explicit No Auth Request',
request: {
method: 'GET',
url: 'https://api.example.com/test',
auth: {
type: 'bearer',
bearer: [{ key: 'token', value: 'requesttoken' }]
auth: { // Request explicitly set to "No Auth"
type: 'noauth'
}
}
}
@@ -119,46 +201,8 @@ describe('Request Authentication', () => {
const result = await postmanToBruno(postmanCollection);
expect(result.items[0].items[0].request.auth).toEqual({
mode: 'bearer',
mode: 'none', // <<<< KEY CHECK
basic: null,
bearer: {
token: 'requesttoken'
},
awsv4: null,
apikey: null,
oauth2: null,
digest: null
});
});
it('should handle missing basic auth values in request level', async() => {
const postmanCollection = {
info: {
name: 'Missing Auth Request Collection',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
item: [
{
name: 'Missing Auth Request',
request: {
method: 'GET',
url: 'https://api.example.com/test',
auth: {
type: 'basic'
}
}
}
]
};
const result = await postmanToBruno(postmanCollection);
expect(result.items[0].request.auth).toEqual({
mode: 'basic',
basic: {
username: '',
password: ''
},
bearer: null,
awsv4: null,
apikey: null,
@@ -166,4 +210,186 @@ describe('Request Authentication', () => {
digest: null
});
});
it('should handle "Inherit Auth" for a request nested under multiple inheriting folders', async () => {
const postmanCollection = {
info: {
name: 'Multi-Level Inherit Auth',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
auth: { // Collection level auth
type: 'basic',
basic: [
{ key: 'username', value: 'collectionUser' },
{ key: 'password', value: 'collectionPass' }
]
},
item: [
{
name: 'Folder Level 1 (Inherit)',
// auth property is ABSENT for this folder, meaning "Inherit"
item: [
{
name: 'Folder Level 2 (Inherit)',
// auth property is ABSENT for this folder, meaning "Inherit"
item: [
{
name: 'Deeply Nested Request (Inherit)',
request: {
method: 'GET',
url: 'https://api.example.com/deep'
// auth property is ABSENT for this request, meaning "Inherit"
}
}
]
}
]
}
]
};
const result = await postmanToBruno(postmanCollection);
// Check Folder Level 1
expect(result.items[0].root.request.auth).toEqual({
mode: 'inherit',
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
// Check Folder Level 2
expect(result.items[0].items[0].root.request.auth).toEqual({
mode: 'inherit',
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
// Check the Request
expect(result.items[0].items[0].items[0].request.auth).toEqual({
mode: 'inherit',
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
});
it('should handle "Inherit Auth" where an intermediate folder has explicit auth', async () => {
const postmanCollection = {
info: {
name: 'Multi-Level Inherit with Override',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
auth: { // Collection level auth
type: 'basic',
basic: [
{ key: 'username', value: 'collectionUser' },
{ key: 'password', value: 'collectionPass' }
]
},
item: [
{
name: 'Folder Level 1 (Explicit Bearer)',
auth: { // This folder has its own auth
type: 'bearer',
bearer: [{ key: 'token', value: 'folder1Token' }]
},
item: [
{
name: 'Folder Level 2 (Inherit from Folder 1)',
// auth property is ABSENT for this folder, meaning "Inherit"
item: [
{
name: 'Deeply Nested Request (Inherit from Folder 1 via Folder 2)',
request: {
method: 'GET',
url: 'https://api.example.com/deep_override'
// auth property is ABSENT for this request, meaning "Inherit"
}
}
]
}
]
}
]
};
const result = await postmanToBruno(postmanCollection);
// Check Folder Level 1
expect(result.items[0].root.request.auth).toEqual({
mode: 'bearer',
basic: null,
bearer: { token: 'folder1Token' }, // Explicitly set
awsv4: null, apikey: null, oauth2: null, digest: null
});
// Check Folder Level 2
expect(result.items[0].items[0].root.request.auth).toEqual({
mode: 'inherit', // Inherits from Folder 1
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
// Check the Request
expect(result.items[0].items[0].items[0].request.auth).toEqual({
mode: 'inherit', // Inherits from Folder 1
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
});
it('should handle "Inherit Auth" where an intermediate folder has explicit "No Auth"', async () => {
const postmanCollection = {
info: {
name: 'Multi-Level Inherit with No Auth Stop',
schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'
},
auth: { // Collection level auth
type: 'basic',
basic: [
{ key: 'username', value: 'collectionUser' },
{ key: 'password', value: 'collectionPass' }
]
},
item: [
{
name: 'Folder Level 1 (Explicit No Auth)',
auth: { // This folder is explicitly "No Auth"
type: 'noauth'
},
item: [
{
name: 'Folder Level 2 (Inherit from Folder 1 - so No Auth)',
// auth property is ABSENT for this folder, meaning "Inherit"
item: [
{
name: 'Deeply Nested Request (Inherit from Folder 1 via Folder 2 - so No Auth)',
request: {
method: 'GET',
url: 'https://api.example.com/deep_no_auth_stop'
// auth property is ABSENT for this request, meaning "Inherit"
}
}
]
}
]
}
]
};
const result = await postmanToBruno(postmanCollection);
// Check Folder Level 1
expect(result.items[0].root.request.auth).toEqual({
mode: 'none', // Explicitly "No Auth"
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
// Check Folder Level 2
expect(result.items[0].items[0].root.request.auth).toEqual({
mode: 'inherit', // Inherits from Folder 1
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
// Check the Request
expect(result.items[0].items[0].items[0].request.auth).toEqual({
mode: 'inherit', // Inherits from Folder 1
basic: null, bearer: null, awsv4: null, apikey: null, oauth2: null, digest: null
});
});
});