mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-28 07:04:10 +00:00
Implement legacy Postman global API transformations (#5403)
This commit is contained in:
@@ -350,6 +350,9 @@ function translateCode(code) {
|
||||
// Process all transformations in a single pass
|
||||
processTransformations(ast, transformedNodes);
|
||||
|
||||
// Handle legacy Postman global APIs
|
||||
handleLegacyGlobalAPIs(ast, transformedNodes, code);
|
||||
|
||||
// Handle special Postman syntax patterns
|
||||
handleTestsBracketNotation(ast);
|
||||
|
||||
@@ -787,5 +790,102 @@ function handleTestsBracketNotation(ast) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle legacy Postman global API transformations
|
||||
* This function processes legacy Postman globals like responseBody, responseHeaders, responseTime
|
||||
* while preserving user-defined variables with the same names
|
||||
*
|
||||
* @param {Object} ast - jscodeshift AST
|
||||
* @param {Set} transformedNodes - Set of already transformed nodes
|
||||
* @param {string} code - The original Postman script code
|
||||
*/
|
||||
function handleLegacyGlobalAPIs(ast, transformedNodes, code) {
|
||||
// regex check before the ast traversal
|
||||
const legacyGlobalRegex = /responseBody|responseHeaders|responseTime/;
|
||||
|
||||
if (!legacyGlobalRegex.test(code)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for variable declarations with legacy global names - track which ones have conflicts
|
||||
const conflictingNames = new Set();
|
||||
|
||||
// Check variable declarations
|
||||
ast.find(j.VariableDeclarator).forEach(path => {
|
||||
if (path.value.id.type === 'Identifier') {
|
||||
const varName = path.value.id.name;
|
||||
if (legacyGlobalRegex.test(varName)) {
|
||||
conflictingNames.add(varName);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle JSON.parse(responseBody) → res.getBody()
|
||||
// Only transform if responseBody doesn't have a user variable conflict
|
||||
if (!conflictingNames.has('responseBody')) {
|
||||
ast.find(j.CallExpression).forEach(path => {
|
||||
if (transformedNodes.has(path.node)) return;
|
||||
|
||||
const callExpr = path.value;
|
||||
if (callExpr.callee.type === 'MemberExpression' && callExpr.callee.object.name === 'JSON' && callExpr.callee.property.name === 'parse') {
|
||||
const args = callExpr.arguments;
|
||||
|
||||
// Check if the argument is 'responseBody'
|
||||
if (args.length > 0 && args[0].type === 'Identifier' && args[0].name === 'responseBody') {
|
||||
// Replace JSON.parse(responseBody) with res.getBody()
|
||||
j(path).replaceWith(j.identifier('res.getBody()'));
|
||||
transformedNodes.add(path.node);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handle standalone legacy Postman global variables
|
||||
const legacyGlobals = [
|
||||
{ name: 'responseBody', replacement: 'res.getBody()' },
|
||||
{ name: 'responseHeaders', replacement: 'res.getHeaders()' },
|
||||
{ name: 'responseTime', replacement: 'res.getResponseTime()' }
|
||||
];
|
||||
|
||||
legacyGlobals.forEach(({ name, replacement }) => {
|
||||
// Skip transformation if this name has a user variable conflict
|
||||
if (conflictingNames.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ast.find(j.Identifier, { name }).forEach(path => {
|
||||
if (transformedNodes.has(path.node)) return;
|
||||
|
||||
// Only transform identifiers that are being used as values, not as variable names
|
||||
const parent = path.parent.value;
|
||||
|
||||
// Skip if this is part of a variable declaration (const responseBody = ...)
|
||||
if (parent.type === 'VariableDeclarator' && parent.id === path.node) {
|
||||
return; // Keep unchanged
|
||||
}
|
||||
|
||||
// Skip if this is part of an assignment (responseBody = ...)
|
||||
if (parent.type === 'AssignmentExpression' && parent.left === path.node) {
|
||||
return; // Keep unchanged
|
||||
}
|
||||
|
||||
// Skip if this is part of a function parameter
|
||||
if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression') {
|
||||
return; // Keep unchanged
|
||||
}
|
||||
|
||||
// Skip if this is part of an object property
|
||||
if (parent.type === 'Property' && (parent.key === path.node || parent.value === path.node)) {
|
||||
return; // Keep unchanged
|
||||
}
|
||||
|
||||
// Transform all other references (including function call arguments)
|
||||
// This will transform console.log(responseBody) → console.log(res.getBody())
|
||||
j(path).replaceWith(j.identifier(replacement));
|
||||
transformedNodes.add(path.node);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export { getMemberExpressionString };
|
||||
export default translateCode;
|
||||
@@ -0,0 +1,299 @@
|
||||
import translateCode from '../../../../src/utils/jscode-shift-translator.js';
|
||||
|
||||
describe('Legacy Postman API Translation', () => {
|
||||
describe('handleLegacyGlobalAPIs - No Conflicts', () => {
|
||||
test('should translate responseBody when no user variables exist', () => {
|
||||
const input = `
|
||||
const data = JSON.parse(responseBody);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const data = res.getBody();
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should translate responseHeaders when no user variables exist', () => {
|
||||
const input = `
|
||||
console.log(responseHeaders);
|
||||
const headers = responseHeaders;
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
expect(result).toContain('res.getHeaders()');
|
||||
expect(result).not.toContain('responseHeaders');
|
||||
});
|
||||
|
||||
test('should translate responseTime when no user variables exist', () => {
|
||||
const input = `
|
||||
console.log(responseTime);
|
||||
const time = responseTime;
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
expect(result).toContain('res.getResponseTime()');
|
||||
expect(result).not.toContain('responseTime');
|
||||
});
|
||||
|
||||
test('should translate JSON.parse(responseBody) when no user variables exist', () => {
|
||||
const input = `
|
||||
const data = JSON.parse(responseBody);
|
||||
console.log(data);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
expect(result).toContain('res.getBody()');
|
||||
expect(result).not.toContain('JSON.parse(responseBody)');
|
||||
expect(result).not.toContain('responseBody');
|
||||
});
|
||||
|
||||
test('should translate JSON.parse(responseBody) usage without assignment when no user variables exist', () => {
|
||||
const input = `
|
||||
console.log(JSON.parse(responseBody));
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
console.log(res.getBody());
|
||||
`;
|
||||
|
||||
expect(result).toContain(expected);
|
||||
});
|
||||
|
||||
test('should translate all legacy APIs when no conflicts exist', () => {
|
||||
const input = `
|
||||
const data = JSON.parse(responseBody);
|
||||
const headers = responseHeaders;
|
||||
const time = responseTime;
|
||||
|
||||
console.log(data, headers, time);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
expect(result).toContain('res.getBody()');
|
||||
expect(result).toContain('res.getHeaders()');
|
||||
expect(result).toContain('res.getResponseTime()');
|
||||
expect(result).not.toContain('responseBody');
|
||||
expect(result).not.toContain('responseHeaders');
|
||||
expect(result).not.toContain('responseTime');
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleLegacyGlobalAPIs - With Conflicts', () => {
|
||||
test('should NOT translate responseBody when user variable exists', () => {
|
||||
const input = `
|
||||
const responseBody = pm.response.json();
|
||||
console.log(responseBody);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const responseBody = res.getBody();
|
||||
console.log(responseBody);
|
||||
`;
|
||||
|
||||
// pm.response.json() should be transformed to res.getBody() (Postman API transformation)
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should NOT translate responseHeaders when user variable exists', () => {
|
||||
const input = `
|
||||
const responseHeaders = pm.response.headers;
|
||||
console.log(responseHeaders);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const responseHeaders = res.getHeaders();
|
||||
console.log(responseHeaders);
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should NOT translate responseTime when user variable exists', () => {
|
||||
const input = `
|
||||
const responseTime = pm.response.responseTime;
|
||||
console.log(responseTime);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const responseTime = res.getResponseTime();
|
||||
console.log(responseTime);
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should NOT translate JSON.parse(responseBody) when user variable exists', () => {
|
||||
const input = `
|
||||
const responseBody = pm.response.json();
|
||||
const data = JSON.parse(responseBody);
|
||||
console.log(data);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const responseBody = res.getBody();
|
||||
const data = JSON.parse(responseBody);
|
||||
console.log(data);
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleLegacyGlobalAPIs - Partial Conflicts', () => {
|
||||
test('should translate non-conflicting APIs when some conflicts exist', () => {
|
||||
const input = `
|
||||
const responseBody = pm.response.json();
|
||||
console.log(responseBody);
|
||||
console.log(responseHeaders);
|
||||
console.log(responseTime);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const responseBody = res.getBody();
|
||||
console.log(responseBody);
|
||||
console.log(res.getHeaders());
|
||||
console.log(res.getResponseTime());
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
|
||||
});
|
||||
|
||||
test('should translate JSON.parse(responseBody) only when no conflict exists', () => {
|
||||
const input = `
|
||||
const responseHeaders = pm.response.headers;
|
||||
const data = JSON.parse(responseBody);
|
||||
console.log(responseHeaders);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const responseHeaders = res.getHeaders();
|
||||
const data = res.getBody();
|
||||
console.log(responseHeaders);
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleLegacyGlobalAPIs - Edge Cases', () => {
|
||||
test.skip('should handle function parameters with legacy names', () => {
|
||||
const input = `
|
||||
function test(responseBody) {
|
||||
console.log(responseBody);
|
||||
console.log(responseHeaders);
|
||||
}
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
function test(responseBody) {
|
||||
console.log(responseBody);
|
||||
console.log(res.getHeaders());
|
||||
}
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should handle object properties with legacy names', () => {
|
||||
const input = `
|
||||
const config = {
|
||||
responseBody: 'custom',
|
||||
responseHeaders: 'custom'
|
||||
};
|
||||
console.log(responseTime);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
const expected = `
|
||||
const config = {
|
||||
responseBody: 'custom',
|
||||
responseHeaders: 'custom'
|
||||
};
|
||||
console.log(res.getResponseTime());
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should handle assignments with legacy names', () => {
|
||||
const input = `
|
||||
responseBody = 'new value';
|
||||
responseHeaders = 'new headers';
|
||||
console.log(responseTime);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
const expected = `
|
||||
responseBody = 'new value';
|
||||
responseHeaders = 'new headers';
|
||||
console.log(res.getResponseTime());
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should handle mixed usage patterns', () => {
|
||||
const input = `
|
||||
const responseBody = pm.response.json();
|
||||
const data = JSON.parse(responseBody);
|
||||
console.log(responseHeaders);
|
||||
console.log(responseTime);
|
||||
|
||||
function test(data) {
|
||||
console.log(responseBody);
|
||||
console.log(responseHeaders);
|
||||
}
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
|
||||
const expected = `
|
||||
const responseBody = res.getBody();
|
||||
const data = JSON.parse(responseBody);
|
||||
console.log(res.getHeaders());
|
||||
console.log(res.getResponseTime());
|
||||
|
||||
function test(data) {
|
||||
console.log(responseBody);
|
||||
console.log(res.getHeaders());
|
||||
}
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleLegacyGlobalAPIs - No Legacy APIs', () => {
|
||||
test('should not modify code when no legacy APIs are present', () => {
|
||||
const input = `
|
||||
const data = { name: 'test' };
|
||||
console.log(data.name);
|
||||
`;
|
||||
|
||||
const result = translateCode(input);
|
||||
const expected = `
|
||||
const data = { name: 'test' };
|
||||
console.log(data.name);
|
||||
`;
|
||||
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user