mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 22:45:25 +00:00
feat: enhance json environment file support in bruno-cli (#5660)
* feat: enhance json environment file support in bruno-cli feat: add parseEnvironmentJson function to normalize environment JSON structure lint fixes feat: added tests for invalid JSON environment files in CLI and added missing constant defenition. feat: improve JSON environment file handling and update tests Trigger test fix: update CLI command syntax for non-existent JSON environment file test fix: correct CLI command syntax in test for non-existent JSON environment file fix: update CLI command syntax in test for non-existent JSON environment file fix: update test to use temporary path for non-existent JSON environment file trying to fix the tests fix tests refactor: rename ERROR_INVALID_JSON to ERROR_INVALID_FILE and update related error handling in CLI commands and tests fix: update parseEnvironmentJson to preserve secret flag test: improved tests * refactor: move parseEnvironmentJson function to utils/ environment.js file and update imports * test: update tests
This commit is contained in:
@@ -6,6 +6,7 @@ const { getRunnerSummary } = require('@usebruno/common/runner');
|
||||
const { exists, isFile, isDirectory } = require('../utils/filesystem');
|
||||
const { runSingleRequest } = require('../runner/run-single-request');
|
||||
const { getEnvVars } = require('../utils/bru');
|
||||
const { parseEnvironmentJson } = require('../utils/environment');
|
||||
const { isRequestTagsIncluded } = require("@usebruno/common")
|
||||
const makeJUnitOutput = require('../reporters/junit');
|
||||
const makeHtmlOutput = require('../reporters/html');
|
||||
@@ -131,7 +132,7 @@ const builder = async (yargs) => {
|
||||
type: 'string'
|
||||
})
|
||||
.option('env-file', {
|
||||
describe: 'Path to environment file (.bru) - can be absolute or relative path',
|
||||
describe: 'Path to environment file (.bru or .json) - absolute or relative',
|
||||
type: 'string'
|
||||
})
|
||||
.option('env-var', {
|
||||
@@ -306,7 +307,7 @@ const handler = async function (argv) {
|
||||
clientCertConfigJson = JSON.parse(clientCertConfigFileContent);
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Failed to parse Client Certificate Config JSON: ${err.message}`));
|
||||
process.exit(constants.EXIT_STATUS.ERROR_INVALID_JSON);
|
||||
process.exit(constants.EXIT_STATUS.ERROR_INVALID_FILE);
|
||||
}
|
||||
|
||||
if (clientCertConfigJson?.enabled && Array.isArray(clientCertConfigJson?.certs)) {
|
||||
@@ -346,10 +347,29 @@ const handler = async function (argv) {
|
||||
process.exit(constants.EXIT_STATUS.ERROR_ENV_NOT_FOUND);
|
||||
}
|
||||
|
||||
const envBruContent = fs.readFileSync(envFilePath, 'utf8').replace(/\r\n/g, '\n');
|
||||
const envJson = parseEnvironment(envBruContent);
|
||||
envVars = getEnvVars(envJson);
|
||||
envVars.__name__ = envFile ? path.basename(envFilePath, '.bru') : env;
|
||||
const ext = path.extname(envFilePath).toLowerCase();
|
||||
if (ext === '.json') {
|
||||
// Parse Bruno schema JSON environment
|
||||
let envJsonContent;
|
||||
try {
|
||||
envJsonContent = fs.readFileSync(envFilePath, 'utf8');
|
||||
const parsed = JSON.parse(envJsonContent);
|
||||
const normalizedEnv = parseEnvironmentJson(parsed);
|
||||
envVars = getEnvVars(normalizedEnv);
|
||||
const rawName = normalizedEnv?.name;
|
||||
const trimmedName = typeof rawName === 'string' ? rawName.trim() : '';
|
||||
envVars.__name__ = trimmedName || path.basename(envFilePath, '.json');
|
||||
} catch (err) {
|
||||
console.error(chalk.red(`Failed to parse Environment JSON: ${err.message}`));
|
||||
process.exit(constants.EXIT_STATUS.ERROR_INVALID_FILE);
|
||||
}
|
||||
} else {
|
||||
// Default to .bru parsing
|
||||
const envBruContent = fs.readFileSync(envFilePath, 'utf8').replace(/\r\n/g, '\n');
|
||||
const envJson = parseEnvironment(envBruContent);
|
||||
envVars = getEnvVars(envJson);
|
||||
envVars.__name__ = envFile ? path.basename(envFilePath, '.bru') : env;
|
||||
}
|
||||
}
|
||||
|
||||
if (envVar) {
|
||||
|
||||
@@ -23,6 +23,8 @@ const EXIT_STATUS = {
|
||||
ERROR_INCORRECT_ENV_OVERRIDE: 8,
|
||||
// Invalid output format requested
|
||||
ERROR_INCORRECT_OUTPUT_FORMAT: 9,
|
||||
// Invalid file format
|
||||
ERROR_INVALID_FILE: 10,
|
||||
// Everything else
|
||||
ERROR_GENERIC: 255
|
||||
};
|
||||
|
||||
26
packages/bruno-cli/src/utils/environment.js
Normal file
26
packages/bruno-cli/src/utils/environment.js
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Parse a Bruno JSON environment object and normalize variables
|
||||
* Accepts only single environment object: { name?, uid?, variables: [...] }
|
||||
*/
|
||||
const parseEnvironmentJson = (parsed = {}) => {
|
||||
if (!parsed || !Array.isArray(parsed.variables)) {
|
||||
throw new Error('Invalid environment JSON: expected a single environment object with a "variables" array');
|
||||
}
|
||||
|
||||
const normalized = {
|
||||
name: parsed.name,
|
||||
variables: (parsed.variables || []).filter(Boolean).map((variable) => ({
|
||||
name: variable.name,
|
||||
value: variable.value,
|
||||
type: variable.type || 'text',
|
||||
enabled: variable.enabled !== false,
|
||||
secret: variable.secret || false
|
||||
}))
|
||||
};
|
||||
|
||||
return normalized;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
parseEnvironmentJson
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
const { describe, it, expect } = require('@jest/globals');
|
||||
const { getEnvVars } = require('../../src/utils/bru');
|
||||
const { parseEnvironmentJson } = require('../../src/utils/environment');
|
||||
|
||||
describe('parseEnvironmentJson', () => {
|
||||
it('normalizes single environment object', () => {
|
||||
const input = {
|
||||
name: 'My Env',
|
||||
variables: [
|
||||
{ name: 'host', value: 'https://www.httpfaker.org' },
|
||||
{ name: 'token', value: 'abc', enabled: false, secret: true }
|
||||
]
|
||||
};
|
||||
const env = parseEnvironmentJson(input);
|
||||
expect(Array.isArray(env.variables)).toBe(true);
|
||||
expect(env.variables[0]).toEqual({
|
||||
name: 'host',
|
||||
value: 'https://www.httpfaker.org',
|
||||
type: 'text',
|
||||
enabled: true,
|
||||
secret: false
|
||||
});
|
||||
expect(env.variables[1].enabled).toBe(false);
|
||||
expect(env.variables[1].secret).toBe(true);
|
||||
|
||||
const vars = getEnvVars(env);
|
||||
expect(vars).toEqual({ host: 'https://www.httpfaker.org' });
|
||||
});
|
||||
|
||||
it('throws on invalid shape', () => {
|
||||
expect(() => parseEnvironmentJson({ name: 'x' })).toThrow(/Invalid environment JSON/i);
|
||||
});
|
||||
|
||||
it('respects explicit fields and preserves secret flag', () => {
|
||||
const input = {
|
||||
name: 'My Env',
|
||||
variables: [
|
||||
{ name: 'one', value: '1', type: 'text', enabled: true, secret: true },
|
||||
{ name: 'two', value: '2', type: 'file', enabled: false, secret: false }
|
||||
]
|
||||
};
|
||||
const env = parseEnvironmentJson(input);
|
||||
|
||||
expect(env.variables[0]).toEqual({
|
||||
name: 'one',
|
||||
value: '1',
|
||||
type: 'text',
|
||||
enabled: true,
|
||||
secret: true
|
||||
});
|
||||
expect(env.variables[1]).toEqual({
|
||||
name: 'two',
|
||||
value: '2',
|
||||
type: 'file',
|
||||
enabled: false,
|
||||
secret: false
|
||||
});
|
||||
|
||||
const vars = getEnvVars(env);
|
||||
expect(vars).toEqual({ one: '1' });
|
||||
});
|
||||
|
||||
it('defaults secret to false for undefined and null', () => {
|
||||
const input = {
|
||||
name: 'My Env',
|
||||
variables: [
|
||||
{ name: 'three', value: '3', enabled: true },
|
||||
{ name: 'four', value: '4', enabled: true, secret: null }
|
||||
]
|
||||
};
|
||||
const env = parseEnvironmentJson(input);
|
||||
|
||||
expect(env.variables[0]).toEqual({
|
||||
name: 'three',
|
||||
value: '3',
|
||||
type: 'text',
|
||||
enabled: true,
|
||||
secret: false
|
||||
});
|
||||
expect(env.variables[1]).toEqual({
|
||||
name: 'four',
|
||||
value: '4',
|
||||
type: 'text',
|
||||
enabled: true,
|
||||
secret: false
|
||||
});
|
||||
|
||||
const vars = getEnvVars(env);
|
||||
expect(vars).toEqual({ three: '3', four: '4' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user