Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: storing postman collections as directories + newman enhancements #3159

Open
wants to merge 25 commits into
base: develop
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7804629
feat: support collections as local directories
knutties Jul 29, 2023
18649a0
feat: support collections as local directories
knutties Jul 31, 2023
76fd92d
fix: fix top level formatting
knutties Aug 11, 2023
ba07027
feat: add export-import-test command + fixes
knutties Aug 12, 2023
9c0a41e
feat: support event ordering
knutties Aug 12, 2023
a34f272
feat: support new test addition
knutties Aug 13, 2023
bf77f3b
feat: support test removal
knutties Aug 14, 2023
72d511e
fix: fix existing tests
knutties Aug 21, 2023
7b776a7
fix: fix integration/library test executions
knutties Aug 22, 2023
91d14bf
fix: format raw request json for easier editing
knutties Aug 22, 2023
8ad4a68
fix: handle raw body parse errors gracefully
knutties Aug 23, 2023
8af8bf2
build: remove raw body from exported collection
knutties Aug 23, 2023
230ae42
doc: add rationale documentation for dir-* commands
knutties Aug 26, 2023
327ea92
feat: add dir-create/dir-add-folder/dir-remove-folder commands
knutties Aug 27, 2023
347e03a
fix: handle errors gracefully when executing dir-run
knutties Aug 29, 2023
0d4ece2
fix: handle top level event js files
knutties Aug 30, 2023
5332cea
fix: handle body lang type for json formatting
knutties Aug 30, 2023
2e35272
ci: fix json payload in template for add test
knutties Sep 13, 2023
7106e19
refactor: rename test to request across
knutties Oct 6, 2023
74a3901
doc: Update DIR_COMMANDS.md
knutties Oct 6, 2023
d706aa8
ci: Update DIR_COMMANDS.md
knutties Oct 6, 2023
1376f35
refactor: rename test to request across
knutties Oct 7, 2023
297601e
refactor: rename dir-export-import-test to dir-export-import-check
knutties Oct 7, 2023
f71f965
fix: fix lint issues
knutties Oct 7, 2023
7bdb306
test: address test coverage
knutties Oct 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: fix existing tests
knutties committed Sep 17, 2023
commit 72d511e13214850a3b586f76df94595e50c0f266
63 changes: 44 additions & 19 deletions bin/newman.js
Original file line number Diff line number Diff line change
@@ -3,39 +3,61 @@
require('../lib/node-version-check'); // @note that this should not respect CLI --silent

const waterfall = require('async/waterfall'),
{ Command } = require('commander'),
program = new Command(),
version = require('../package.json').version,
{ Command } = require('commander'),
commands = require('../'),
util = require('./util');

program
.name('newman')
.addHelpCommand(false)
.version(version, '-v, --version');

Object.keys(commands).forEach(function (commandSetupFunction) {
commands[commandSetupFunction](program);
});
/**
* Creates a new command instance - useful for testing
*
* @param {Command} Command - Command type from commander library
* @param {Object} commands - list of supported commands - each defining cliSetup and action
*/
function getProgram (Command, commands) {
const program = new Command();

program
.name('newman')
.addHelpCommand(false)
.version(version, '-v, --version');

program.addHelpText('after', `
To get available options for a command:
newman <command> -h`);
Object.keys(commands).forEach((commandSetupFunction) => {
let cliSetup = commands[commandSetupFunction].cliSetup,
action = commands[commandSetupFunction].action;

// Warn on invalid command and then exits.
program.on('command:*', (command) => {
console.error(`error: invalid command \`${command}\`\n`);
program.help();
});
cliSetup(program).action((args, command) => {
action(args, command, program);
});
});

program.addHelpText('after', `
To get available options for a command:
newman <command> -h`);

// Warn on invalid command and then exits.
program.on('command:*', (command) => {
console.error(`error: invalid command \`${command}\`\n`);
program.help();
});

return program;
}

/**
* Starts the script execution.
* callback is required when this is required as a module in tests.
*
* @param {String[]} argv - Argument vector.
* @param {Command} program - Commander command instance to run
* @param {?Function} callback - The callback function invoked on the completion of execution.
*/
function run (argv, callback) {
function run (argv, program, callback) {
if (!program) {
program = getProgram(Command, commands);
}

waterfall([
(next) => {
// cache original argv, required to parse nested options later.
@@ -74,4 +96,7 @@ function run (argv, callback) {
!module.parent && run(process.argv);

// Export to allow debugging and testing.
module.exports = run;
module.exports = {
run,
getProgram
};
103 changes: 54 additions & 49 deletions lib/commands/dir-add-test/index.js
Original file line number Diff line number Diff line change
@@ -7,65 +7,70 @@ const commandUtil = require('../../../bin/util'),
/*
@param {Command} - An commander Command instance to which this command is added
*/
module.exports = function (program) {
program
function cliSetup (program) {
return program
.command('dir-add-test <test-path>')
.description('Add a test to directory based Postman collection in the given path')
.usage('<test-path> [options]')
.addOption(new Option('-t, --type <method>', 'HTTP Method template').choices(['GET', 'POST']).default('GET'))
.option('-f, --force-overwrite', 'overwrite if test already exists', false)
.action((testPath, command) => {
const options = commandUtil.commanderToObject(command),
methodTemplateMap = {
GET: './lib/commands/dir-add-test/templates/GET template',
POST: './lib/commands/dir-add-test/templates/POST body template'
},
parentDir = path.dirname(testPath),
trimmedTestPath = testPath.replace(/\/+$/, ''),
testPathBaseName = path.basename(testPath),
metaFilePath = path.join(parentDir, '.meta.json');
.option('-f, --force-overwrite', 'overwrite if test already exists', false);
}

function action (testPath, command) {
const options = commandUtil.commanderToObject(command),
methodTemplateMap = {
GET: './lib/commands/dir-add-test/templates/GET template',
POST: './lib/commands/dir-add-test/templates/POST body template'
},
parentDir = path.dirname(testPath),
trimmedTestPath = testPath.replace(/\/+$/, ''),
testPathBaseName = path.basename(testPath),
metaFilePath = path.join(parentDir, '.meta.json');

// check if test with same name exists when forceOverwrite is false
if (!options.forceOverwrite) {
dirUtils.assertDirectoryAbsence(testPath);
}
// check if test with same name exists when forceOverwrite is false
if (!options.forceOverwrite) {
dirUtils.assertDirectoryAbsence(testPath);
}

// check if testPath's parent is already a collection folder
dirUtils.assertCollectionDir(parentDir);
// check if testPath's parent is already a collection folder
dirUtils.assertCollectionDir(parentDir);

// copy request, response, event files
try {
fs.cpSync(methodTemplateMap[options.type],
trimmedTestPath,
{ recursive: true });
}
catch (e) {
console.error(`Could not copy from template at ${methodTemplateMap[options.type]}`);
fs.rmSync(`${testPath}`, { recursive: true, force: true });
process.exit(-1);
}
// copy request, response, event files
try {
fs.cpSync(methodTemplateMap[options.type],
trimmedTestPath,
{ recursive: true });
}
catch (e) {
console.error(`Could not copy from template at ${methodTemplateMap[options.type]}`);
fs.rmSync(`${testPath}`, { recursive: true, force: true });
process.exit(-1);
}

// add new test to parent's .meta.json
try {
fs.accessSync(metaFilePath, fs.constants.R_OK);
let meta = JSON.parse(fs.readFileSync(metaFilePath)),
childrenOrder = meta.childrenOrder;
// add new test to parent's .meta.json
try {
fs.accessSync(metaFilePath, fs.constants.R_OK);
let meta = JSON.parse(fs.readFileSync(metaFilePath)),
childrenOrder = meta.childrenOrder;

if (!childrenOrder.includes(testPathBaseName)) {
childrenOrder.push(testPathBaseName);
}
if (!childrenOrder.includes(testPathBaseName)) {
childrenOrder.push(testPathBaseName);
}

meta = {
childrenOrder
};
meta = {
childrenOrder
};

dirUtils.createFile(metaFilePath, JSON.stringify(meta, null, 2));
}
catch (e) {
console.error(`Could not update ${metaFilePath} with new request ${testPath}: ${e}`);
fs.rmSync(`${testPath}`, { recursive: true, force: true });
process.exit(-1);
}
});
dirUtils.createFile(metaFilePath, JSON.stringify(meta, null, 2));
}
catch (e) {
console.error(`Could not update ${metaFilePath} with new request ${testPath}: ${e}`);
fs.rmSync(`${testPath}`, { recursive: true, force: true });
process.exit(-1);
}
}

module.exports = {
cliSetup,
action
};
94 changes: 50 additions & 44 deletions lib/commands/dir-export-import-test/index.js
Original file line number Diff line number Diff line change
@@ -7,51 +7,57 @@ const commandUtil = require('../../../bin/util');
/*
@param {Command} - An commander Command instance to which this command is added
*/
module.exports = function (program) {
program
function cliSetup (program) {
return program
.command('dir-export-import-test <postman-collection-file>')
.description('Check if an export followed by import results in same collection')
.usage('<postman-collection-file>')
.option('-s, --substitute-slashes', 'if slashes are found in name field - substitute them')
.action((collectionFile, command) => {
const collectionFilePath = collectionFile.startsWith('/') ?
collectionFile : path.join(process.cwd(), collectionFile),
options = commandUtil.commanderToObject(command);

let inputCollection = require(collectionFilePath),
inputCollectionCloned,
outputCollection = {},
tempDir = dirUtils.createTempDir(),
collectionDir;

dirUtils.assertFileExistence(collectionFilePath);

// handle different format postman collection file
if (inputCollection.collection) {
inputCollection = inputCollection.collection;
}
inputCollectionCloned = JSON.parse(JSON.stringify(inputCollection));

collectionDir = inputCollection.info.name;

try {
// create export directory under a temporary directory
process.chdir(tempDir);
dirUtils.traverse(inputCollection, [], options);
}
catch (e) {
console.error(e);
fs.rmSync(tempDir, { recursive: true, force: true });
process.exit(1);
}

dirUtils.assertDirectoryExistence(collectionDir);
outputCollection = dirUtils.dirTreeToCollectionJson(collectionDir);
// clean-up temp directory
fs.rmSync(`./${collectionDir}`, { recursive: true, force: true });
// console.log(`${JSON.stringify(inputCollection, null, 2)}`);
// console.log(`${JSON.stringify(outputCollection, null, 2)}`);

assert.deepStrictEqual(inputCollectionCloned, outputCollection);
});
.option('-s, --substitute-slashes', 'if slashes are found in name field - substitute them');
}

function action (collectionFile, command) {
const collectionFilePath = collectionFile.startsWith('/') ?
collectionFile : path.join(process.cwd(), collectionFile),
options = commandUtil.commanderToObject(command);

let inputCollection = require(collectionFilePath),
inputCollectionCloned,
outputCollection = {},
tempDir = dirUtils.createTempDir(),
collectionDir;

dirUtils.assertFileExistence(collectionFilePath);

// handle different format postman collection file
if (inputCollection.collection) {
inputCollection = inputCollection.collection;
}
inputCollectionCloned = JSON.parse(JSON.stringify(inputCollection));

collectionDir = inputCollection.info.name;

try {
// create export directory under a temporary directory
process.chdir(tempDir);
dirUtils.traverse(inputCollection, [], options);
}
catch (e) {
console.error(e);
fs.rmSync(tempDir, { recursive: true, force: true });
process.exit(1);
}

dirUtils.assertDirectoryExistence(collectionDir);
outputCollection = dirUtils.dirTreeToCollectionJson(collectionDir);
// clean-up temp directory
fs.rmSync(`./${collectionDir}`, { recursive: true, force: true });
// console.log(`${JSON.stringify(inputCollection, null, 2)}`);
// console.log(`${JSON.stringify(outputCollection, null, 2)}`);

assert.deepStrictEqual(inputCollectionCloned, outputCollection);
}

module.exports = {
cliSetup,
action
};
69 changes: 37 additions & 32 deletions lib/commands/dir-export/index.js
Original file line number Diff line number Diff line change
@@ -6,40 +6,45 @@ const fs = require('fs');
/*
@param {Command} - An commander Command instance to which this command is added
*/
module.exports = function (program) {
program
function cliSetup (program) {
return program
.command('dir-export <postman-collection-file>')
.description('Convert a Postman collection file into its directory representation')
.usage('<postman-collection-file> [options]')
.option('-s, --substitute-slashes', 'if slashes are found in name field - substitute them')
.option('-f, --force-overwrite', 'overwrite if directory already exists')
.action((collectionFile, command) => {
let collectionFilePath = path.isAbsolute(collectionFile) ?
collectionFile : path.join(process.cwd(), collectionFile),
options = commandUtil.commanderToObject(command);

dirUtils.assertFileExistence(collectionFilePath);

let collection = require(collectionFilePath);

// handle different format postman collection file
if (collection.collection) {
collection = collection.collection;
}

if (options.forceOverwrite) {
fs.rmSync(`./${collection.info.name}`, { recursive: true, force: true });
}

try {
dirUtils.traverse(collection, [], options);
}
catch (e) {
console.error(e);
fs.rmSync(`./${collection.info.name}`, { recursive: true, force: true });
process.exit(1);
}

process.exit(0);
});
.option('-f, --force-overwrite', 'overwrite if directory already exists');
}

function action (collectionFile, command) {
let collectionFilePath = path.isAbsolute(collectionFile) ?
collectionFile : path.join(process.cwd(), collectionFile),
options = commandUtil.commanderToObject(command);

dirUtils.assertFileExistence(collectionFilePath);

let collection = require(collectionFilePath);

// handle different format postman collection file
if (collection.collection) {
collection = collection.collection;
}

if (options.forceOverwrite) {
fs.rmSync(`./${collection.info.name}`, { recursive: true, force: true });
}

try {
dirUtils.traverse(collection, [], options);
}
catch (e) {
console.error(e);
process.exit(1);
}

process.exit(0);
}

module.exports = {
cliSetup,
action
};
Loading