Expand postman import to handle "inherit" auth type

Allow child items to inherit "No Auth" auths from parent

Simplify processAuth checks by setting mode="inherit" in bruno request object

Allow folders to inherit "No Auth" from parent folder

Reduce inherit fix scope

Revert standard jest test config

Reduce inherit fix scope even more

Reduce inherit fix scope final

Minor format change
This commit is contained in:
Leonard Phillips
2025-05-25 13:06:13 +02:00
committed by sanish-bruno
parent ab4dabf047
commit 0e054259e9
8 changed files with 577 additions and 184 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,47 @@ const importCollectionLevelVariables = (variables, requestObject) => {
requestObject.vars.req = vars;
};
export const processAuth = (auth, requestObject) => {
if (!auth || !auth.type || auth.type === AUTH_TYPES.NOAUTH) {
export const processAuth = (auth, requestObject, collection = 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 (collection && !auth) return; // Return as requestObject is a collection and has a default mode = none
// Handle "Inherit Auth" (typically for non-collections when postmanAuth is null)
if (!auth) {
requestObject.auth.mode = AUTH_TYPES.INHERIT;
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 = {};
// Handle explicit "No Auth"
if (auth.type === AUTH_TYPES.NOAUTH) {
requestObject.auth.mode = 'none';
return;
}
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 +191,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 +198,78 @@ 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 +299,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
},
request: {
auth: {
mode: 'none',
mode: 'inherit',
basic: null,
bearer: null,
awsv4: null,
@@ -308,19 +318,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 +358,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 +394,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 +506,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 +541,7 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
});
};
const searchLanguageByHeader = (headers) => {
let contentType;
each(headers, (header) => {
@@ -592,19 +596,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 +618,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 +638,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

@@ -154,7 +154,7 @@ const expectedOutput = {
"url": "https://usebruno.com",
"method": "GET",
"auth": {
"mode": "none",
"mode": "inherit",
"basic": null,
"bearer": null,
"awsv4": null,
@@ -183,7 +183,7 @@ const expectedOutput = {
},
"request": {
"auth": {
"mode": "none",
"mode": "inherit",
"basic": null,
"bearer": null,
"awsv4": null,
@@ -207,7 +207,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'
});

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