@apica-io/asm-pm-runner
Version:
Run a postman collection in Apica ASM.
532 lines • 22.8 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NewmanRunner = void 0;
const _ = __importStar(require("lodash"));
const postman_collection_1 = require("postman-collection");
const newman = __importStar(require("newman"));
const crs = __importStar(require("../model/crsformat"));
const chalk_1 = __importDefault(require("chalk"));
const Apica_ReturnValue = "_Apica_ReturnValue";
const Apica_ReturnUnit = "_Apica_ReturnUnit";
const Apica_Message = "_Apica_Message";
const helpers = __importStar(require("../lib/helpers"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const cryptify_1 = __importDefault(require("cryptify"));
const log4js_1 = require("log4js");
const VAR_Blacklist_Patterns = [
"secret",
"token",
"client_id",
"password",
"username",
"api",
"key",
];
class NewmanRunner {
constructor(config) {
this.config = {};
this.collection = {};
this.lastItemGroup = "";
this.successFulRequests = 0;
this.failedRequests = 0;
this.tempResult = [];
this.tempFiles = [];
this.result = {};
this.changedVars = new Map();
this.error = {};
this.config = config;
this.logger = (0, log4js_1.getLogger)(NewmanRunner.name);
}
init() {
let content = fs.readFileSync(this.config.collection).toString();
this.collection = new postman_collection_1.Collection(JSON.parse(content));
}
run() {
return __awaiter(this, void 0, void 0, function* () {
let options = {
collection: this.collection,
bail: true,
timeout: 180000,
timeoutRequest: 45000,
};
try {
options["verbose"] = true;
if (this.config.envVars) {
let envVars = [];
this.config.envVars.forEach((element) => {
let pos = element.indexOf("=");
let envVar = {};
if (pos > 0) {
envVar.value = element.substring(pos + 1);
envVar.key = element.substring(0, pos);
}
else {
envVar.key = element;
envVar.value = undefined;
}
envVars.push(envVar);
});
options.envVar = envVars;
this.logger.debug("env variables=%s", this.config.envVars);
}
if (this.config.environment) {
let env_path = this.config.environment;
options.environment = yield this.getDataFile(env_path);
}
if (this.config.sslConfig.cert && this.config.sslConfig.key) {
let ssl_key_path, ssl_cert_path;
if (this.config.dataDir) {
ssl_cert_path = path.resolve(this.config.dataDir, this.config.sslConfig.cert);
ssl_key_path = path.resolve(this.config.dataDir, this.config.sslConfig.key);
}
else {
ssl_cert_path = this.config.sslConfig.cert;
ssl_key_path = this.config.sslConfig.key;
}
options.sslClientCert = yield this.getDataFile(ssl_cert_path);
options.sslClientKey = yield this.getDataFile(ssl_key_path);
options.sslClientPassphrase = this.config.sslConfig.passPhrase;
}
yield new Promise((resolve, reject) => {
newman
.run(options, (err, data) => {
if (err)
return reject(err);
resolve(data);
})
.on("beforeItem", (err, args) => this.beforeItem(err, args))
.on("request", (err, args) => this.request(err, args))
.on("test", (err, args) => this.test(err, args))
.on("exception", (err, args) => this.exception(err, args))
.on("assertion", (err, args) => this.assertion(err, args))
.on("beforeDone", (err, args) => this.beforeDone(err, args));
});
}
catch (err) {
this.logger.error(err.message || err);
throw err;
}
finally {
this.cleanUpTempFiles();
}
this.log("Result: ", this.result.success, this.result.message);
});
}
getDataFile(pathName) {
return __awaiter(this, void 0, void 0, function* () {
if (fs.existsSync(pathName)) {
if (this.config.decryptKey) {
let tmpFile = os.tmpdir() +
"/" +
process.pid +
"_" +
path.basename(pathName);
fs.copyFileSync(pathName, tmpFile);
let crypto = new cryptify_1.default(tmpFile, this.config.decryptKey, undefined, undefined, true);
let files = yield crypto.decrypt();
if (files && files.length > 0) {
this.logger.debug("Decrypting %s", tmpFile);
this.tempFiles.push(tmpFile);
}
return tmpFile;
}
return pathName;
}
else {
let err = new Error(`File ${pathName} not found`);
err.name = " ERR_FILE_NOT_FOUND";
throw err;
}
});
}
cleanUpTempFiles() {
this.logger.debug("Clean up temporary files", this.tempFiles);
this.tempFiles.forEach((file) => {
if (fs.existsSync(file)) {
fs.unlinkSync(file);
}
});
}
saveResult() {
if (this.config.resultDir) {
let jobId = process.env.APICA_JOB_ID || "";
let fileName = jobId || this.collection.name + "_res";
let resultFile = path.resolve(this.config.resultDir, fileName + ".json");
let res = JSON.stringify(this.result, null, 2);
fs.writeFileSync(resultFile, res);
this.logger.debug("Results written to %s", resultFile);
}
}
log(message, ...optionalParams) {
if (this.config.verbose) {
console.log((0, chalk_1.default) `{green ${message}}`, ...optionalParams);
}
}
toResultHeaders(list) {
let headers = {};
list.each((header) => {
headers[header.key] = header.value;
});
return headers;
}
getStepName(item, collection) {
let names = [];
item.forEachParent((item) => {
names.push(item.name);
});
if (names.length > 0)
return _.reverse(names).join("/");
return collection.name;
}
createResult(collection, runResult) {
let packInfo = helpers.getPackageInfo();
let result = this.result;
result.name = collection.name;
result.type = "Postman.Collection";
result.result_version = packInfo.version || "1.0";
result.producer = packInfo.name;
if (packInfo.name) {
result.producer = packInfo.name + " " + packInfo.version;
}
result.value = 0;
result.unit = "ms";
result.returncode = 0;
result.success = true;
if (this.error.message) {
result.message = `Exception ${this.error.message}, ${this.error.name} `;
}
else {
result.message = `Successful requests ${this.successFulRequests}, Failed requests ${this.failedRequests} `;
}
crs.initBaseResult(result, collection.name);
if (collection.description)
result.description = collection.description.toString();
let lastItemGroup = undefined;
result.variables = [];
let step = undefined;
result.steps = [];
if (!result.errors && this.error.message) {
result.errors = [];
result.errors.push(this.error);
}
this.tempResult.forEach((tempResult) => {
let requestResult = tempResult.result;
let itemGroup = tempResult.item.parent() || undefined;
if (!lastItemGroup || !_.isEqual(itemGroup, lastItemGroup)) {
step = {};
crs.initBaseResult(step, this.getStepName(tempResult.item, collection));
step.start_timestamp_ms = requestResult.start_timestamp_ms;
step.requests = [];
result.steps.push(step);
lastItemGroup = itemGroup;
}
step.requests.push(requestResult);
step.success = true;
step.end_timestamp_ms = requestResult.end_timestamp_ms;
step.content_length += requestResult.content_length;
step.duration_ms += requestResult.duration_ms;
result.duration_ms += requestResult.duration_ms;
result.content_length += step.content_length;
if (requestResult.metrics) {
crs.incrementTimingPhases(step.metrics, requestResult.metrics);
crs.incrementTimingPhases(result.metrics, requestResult.metrics);
crs.roundTimingPhases(requestResult.metrics);
}
if (requestResult.error) {
if (!result.errors)
result.errors = [];
result.errors.push(requestResult.error);
step.success = result.success = requestResult.success = false;
}
if (tempResult.assertions && tempResult.assertions.length > 0) {
if (!step.assertions)
step.assertions = [];
if (step.assertions.length === 0) {
_.assign(step.assertions, tempResult.assertions);
}
else {
step.assertions = _.concat(step.assertions, tempResult.assertions);
}
if (tempResult.assertions.find((assertion) => assertion.status === "failure")) {
step.success = result.success = false;
}
}
});
let firstRequest = _.first(this.tempResult) || {};
if (firstRequest.result) {
result.start_timestamp_ms = firstRequest.result.start_timestamp_ms;
let lastRequest = _.last(this.tempResult) || {};
if (lastRequest.result)
result.end_timestamp_ms = lastRequest.result.end_timestamp_ms;
}
else {
result.success = false;
}
result.returncode = result.success ? 0 : 1;
result.value = result.duration_ms;
let custom_return_value = undefined;
let custom_return_unit = undefined;
let custom_message = '';
collection.variables.each((variable) => {
var _a;
let key = variable.key || "";
let newValue = this.changedVars.get(key);
let value = (newValue === undefined) ? variable.value : newValue;
switch (key) {
case Apica_ReturnValue:
if (result.success && _.isNumber(value)) {
custom_return_value = Number(value);
}
break;
case Apica_ReturnUnit:
if (result.success && value && _.isString(value)) {
custom_return_unit = value;
}
break;
case Apica_Message:
if (value && _.isString(value)) {
custom_message = value;
}
break;
default:
if (!key.startsWith("_") && (value !== undefined && value !== '') &&
!VAR_Blacklist_Patterns.find((name) => key.toLocaleLowerCase().includes(name))) {
let s = variable.value;
let link = s.startsWith("http://") || s.startsWith("https://");
let _var = {
key: key || "",
type: link
? "url"
: isNaN(value)
? "string"
: "number",
usage: newValue ? "inResponse" : "input",
value: value,
};
(_a = result.variables) === null || _a === void 0 ? void 0 : _a.push(_var);
}
}
});
if (custom_return_value != undefined) {
result.value = Number(custom_return_value);
if (custom_return_unit)
result.unit = custom_return_unit;
}
result.steps.forEach((step) => {
crs.roundTimingPhases(step.metrics);
step.requests.forEach((request) => {
let authHeader = request.request_headers && request.request_headers["Authorization"];
if (authHeader) {
const elem = authHeader.split(" ");
if (elem.length > 0) {
request.request_headers["Authorization"] = elem[0] + " {secret}";
}
}
});
});
crs.roundTimingPhases(result.metrics);
if (result.success) {
result.message = custom_message ? custom_message : `Requests:${runResult.stats.requests.total}, tests:${runResult.stats.tests.total}, assertions:${runResult.stats.assertions.total}`;
}
else if (result.errors && result.errors[0]) {
let err = result.errors[0];
result.message = custom_message ? `${custom_message}` : `${err.name} ${err.message}`;
}
}
timingPhases(timings) {
// bail out if timing information is not provided
if (!(timings && timings.offset)) {
return undefined;
}
let offset = timings.offset;
// REFER: https://github.com/postmanlabs/postman-request/blob/v2.88.1-postman.5/request.js#L996
let phases = {
secure_handshake_ms: 0,
prepare: offset.request,
socket_wait_ms: offset.socket - offset.request,
dns_time_ms: offset.lookup - offset.socket,
tcp_connect_ms: offset.connect - offset.lookup,
time_to_first_byte_ms: offset.response - offset.connect,
download_time_ms: offset.end - offset.response,
process_time_ms: offset.done - offset.end,
total_time_ms: offset.done,
};
if (offset.secureConnect) {
phases.secure_handshake_ms = offset.secureConnect - offset.connect;
phases.time_to_first_byte_ms =
offset.response - offset.secureConnect;
}
return phases;
}
beforeDone(err, args) {
//this.log("beforeDone", args)
let summary = args.summary;
let collection = summary.collection;
this.log("Collection", collection.name);
//this.log("Variables", summary.collection.variables.members)
let run = summary.run;
this.log("run.stats", run.stats);
this.createResult(collection, run);
}
beforeItem(err, args) {
let item = args.item;
let itemGroup = item.parent() || undefined;
if (itemGroup && itemGroup.name != this.lastItemGroup) {
this.log("itemGroup", itemGroup.name);
this.lastItemGroup = itemGroup.name || "";
}
}
request(err, args) {
let item = args.item;
let request = args.request;
let url = request.url.toString();
let response = args.response;
this.log("item", item.name);
if (err)
this.log("request error", err);
else {
this.log("request", request.method, url);
this.log("response", response.status, response.code, response.reason(), response.responseTime, response.size());
}
let timings = _.last(_.get(args, "history.execution.data"));
timings = timings && timings.timings;
let tmpResult = {};
tmpResult.request = request;
tmpResult.item = item;
let result = {};
crs.initBaseResult(result, item.name);
result.url = url;
result.method = request.method;
result.success = true;
if (!err) {
this.successFulRequests++;
result.start_timestamp_ms -= response.responseTime;
result.duration_ms = response.responseTime;
let size = response.size();
result.content_length = size.body ? size.body : 0;
result.status = response.code;
result.status_text = response.reason();
if (timings) {
result.metrics = this.timingPhases(timings);
this.log("timingPhases", result.metrics);
}
result.request_headers = this.toResultHeaders(request.headers);
result.headers = this.toResultHeaders(response.headers);
}
else {
this.failedRequests++;
result.success = false;
result.error = new Error();
result.error.message = err.message;
result.error.name = err.name;
result.error.stack = "";
result.duration_ms = result.content_length = 0;
result.status = -1;
result.status_text = err.message;
}
tmpResult.result = result;
this.tempResult.push(tmpResult);
}
exception(err, args) {
if (args.error) {
this.log("error", args.error);
this.error = args.error;
}
}
test(err, args) {
let item = args.item;
let _error = undefined;
args.executions.forEach((execution) => {
var _a, _b, _c, _d, _e, _f;
if (execution.error) {
_error = execution.error;
let compacted = (_c = (_b = (_a = execution.result) === null || _a === void 0 ? void 0 : _a.collectionVariables) === null || _b === void 0 ? void 0 : _b.mutations) === null || _c === void 0 ? void 0 : _c.compacted;
if (compacted && !_.isEmpty(compacted)) {
let changedVar = compacted[Apica_Message];
if (changedVar && !_.isEmpty(changedVar)) {
this.changedVars.set(changedVar[0], changedVar[1]);
this.log(Apica_Message + " value changed", changedVar);
}
}
}
else {
let compacted = (_f = (_e = (_d = execution.result) === null || _d === void 0 ? void 0 : _d.collectionVariables) === null || _e === void 0 ? void 0 : _e.mutations) === null || _f === void 0 ? void 0 : _f.compacted;
if (compacted && !_.isEmpty(compacted)) {
Object.keys(compacted).forEach((key) => {
let changedVar = compacted[key];
if (changedVar.length > 1) {
this.changedVars.set(changedVar[0], changedVar[1]);
this.log("changed variable", changedVar);
}
});
}
}
});
if (_error) {
let error = new Error();
error.name = _.clone(_error.name);
error.message = _.clone(_error.message) + " in [" + item.name + "]";
error.stack = "";
let last = _.last(this.tempResult) || {};
if (last.result)
last.result.error = error;
this.log("error", error);
}
}
assertion(err, args) {
let item = args.item;
this.log("assertion", args.assertion, args.error ? false : true);
let last = _.last(this.tempResult) || {};
if (last.result) {
if (!last.assertions)
last.assertions = [];
let assertion = {
source: item.name,
description: args.assertion,
status: args.error ? "failure" : "info",
expression: args.assertion,
value: args.error ? false : true,
};
last.assertions.push(assertion);
}
}
}
exports.NewmanRunner = NewmanRunner;
//# sourceMappingURL=newmanRunner.js.map