@apica-io/url-xi
Version:
URL Check for integrations and API monitoring
427 lines • 22.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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.TestResultProcessor = void 0;
//import colors from 'colors';
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const util_1 = __importDefault(require("util"));
const uuid_1 = require("uuid");
const helper = __importStar(require("../lib/helpers"));
const testbase_1 = require("../lib/testbase");
class TestResultProcessor extends testbase_1.TestBase {
constructor(debug = false) {
super(debug, 'TestResultProcessor');
this.ResultVersion = '2.0';
this.Type = 'URL-XI';
}
_createResultName(resultName) {
const jobId = process.env.APICA_JOB_ID || '';
return jobId || resultName || 'res_' + (0, uuid_1.v4)();
}
_toCRSFormat(results) {
return toCRSFormat(results);
function toCRSFormat(results) {
const crs = keysToCRS(results);
crs['returncode'] = results.success === true ? 0 : 1;
return crs;
}
function to_snake_case(inputString, extension) {
let s = inputString.substr(0, 1).toLowerCase() + inputString.substr(1);
s = s.replace(/[A-Z]+([A-Z])/gm, (x) => {
return x.substring(0, 1) + x.substr(1).toLowerCase();
});
s = s
.split('')
.map((character) => {
if (character == character.toUpperCase()) {
return '_' + character.toLowerCase();
}
else {
return character;
}
})
.join('');
return extension ? s + extension : s;
}
function isObject(obj) {
return obj === Object(obj) && !Array.isArray(obj) && typeof obj !== 'function';
}
function keysToCRS(obj, extension) {
if (isObject(obj)) {
const n = {};
Object.keys(obj).forEach((k) => {
switch (k) {
case 'returnValue':
n['value'] = obj[k];
break;
case 'startTimestamp':
n['start_timestamp_ms'] = obj[k];
break;
case 'endTimestamp':
n['end_timestamp_ms'] = obj[k];
break;
case 'headers':
case 'data':
n[k] = obj[k];
break;
case 'requestHeaders':
n['request_headers'] = obj[k];
break;
case 'timings':
n['metrics'] = keysToCRS(obj[k], '_ms');
break;
case 'customMetrics':
for (const m in obj[k]) {
const value = obj[k][m];
const metrics = n['metrics'];
if (!metrics[m]) {
metrics[m] = value;
}
}
break;
default:
n[to_snake_case(k, extension)] = keysToCRS(obj[k]);
}
});
return n;
}
else if (Array.isArray(obj)) {
return obj.map((i) => {
return keysToCRS(i);
});
}
return obj;
}
}
saveResults(results, options) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
let producer = ((_a = options === null || options === void 0 ? void 0 : options.package) === null || _a === void 0 ? void 0 : _a.name) ? options.package.name : '';
if (options === null || options === void 0 ? void 0 : options.package.version) {
producer += ' ' + options.package.version;
}
results.resultVersion = this.ResultVersion;
results.type = this.Type;
results.producer = producer;
(_b = results.variables) === null || _b === void 0 ? void 0 : _b.forEach((variable) => {
if (variable.hideValue)
variable.value = '*';
});
TestResultProcessor.roundTimings(results.timings);
results.steps.forEach((step) => {
TestResultProcessor.roundTimings(step.timings);
step.requests.forEach((request) => {
TestResultProcessor.roundTimings(request.timings);
});
});
const resultName = this._createResultName(options.resultName);
const resultFile = path_1.default.resolve(options.resultDir || '', resultName + '.json');
const writeFile = util_1.default.promisify(fs_1.default.writeFile);
let outResult = results;
if (options.outputFormat && options.outputFormat.toLowerCase() === 'crs')
outResult = this._toCRSFormat(results);
const res = JSON.stringify(outResult, null, 2);
yield writeFile(resultFile, res);
return res;
});
}
saveErrors(content, errors, options) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let json = {};
if (content) {
json = helper.toJson(content) || {};
}
let producer = ((_a = options === null || options === void 0 ? void 0 : options.package) === null || _a === void 0 ? void 0 : _a.name) ? options.package.name : '';
if (options === null || options === void 0 ? void 0 : options.package.version) {
producer += ' ' + options.package.version;
}
const resultName = this._createResultName(options.resultName);
let errMessage = errors.length > 0 ? (errors[0].message ? errors[0].message : errors[0].toString()) : '';
if (errors && errors[0] && errors[0].property) {
errMessage = errors[0].stack || errors[0].property;
}
const results = {
name: json.name || resultName,
resultVersion: this.ResultVersion,
type: this.Type,
producer: producer,
baseURL: json.baseURL || '',
success: false,
durationMs: 0,
timings: {},
requestTimeCalculation: 'TotalTime',
flowControl: 'Chained Flow',
contentLength: 0,
startTimestamp: Date.now(),
endTimestamp: Date.now(),
returnValue: 1,
message: errMessage.substring(0, 120),
unit: '',
steps: [],
};
results.errors = errors;
const writeFile = util_1.default.promisify(fs_1.default.writeFile);
const res = JSON.stringify(results);
const resultFile = path_1.default.resolve(options.resultDir || '', resultName + '.json');
yield writeFile(resultFile, res);
return res;
});
}
viewResults(results) {
var _a, _b, _c;
console.info(chalk_1.default.bold.blue(`\n----- Process results [${results.name}] -----\n`));
console.info(chalk_1.default.magenta.bold('----- [Test Summary] -----'));
console.info('\tTest Name: %s', results.name);
console.info('\tFlow Control: %s', results.flowControl);
console.info('\tTotal Response Time: %d', results.durationMs);
console.info('\tTotal Content length: %d', results.contentLength);
console.info('\tTiming Totals');
console.info('\t Socket Wait: %s', chalk_1.default.yellow(results.timings.socketWait.toFixed(1)));
console.info('\t DNS Time: %s', chalk_1.default.green(results.timings.dnsTime.toFixed(1)));
console.info('\t SSL Handshake Time: %s', chalk_1.default.cyan(results.timings.secureHandshake.toFixed(1)));
console.info('\t TCP Time: %s', chalk_1.default.blue(results.timings.tcpConnect.toFixed(1)));
console.info('\t Time To First Byte: %s', chalk_1.default.magenta(results.timings.timeToFirstByte.toFixed(1)));
console.info('\t Download Time: %s', chalk_1.default.red(results.timings.downloadTime.toFixed(1)));
console.info('\tStart Time: %s', new Date(results.startTimestamp).toISOString());
console.info('\tEnd Time: %s', new Date(results.endTimestamp).toISOString());
console.info('\tNumber of steps: %d', ((_a = results === null || results === void 0 ? void 0 : results.steps) === null || _a === void 0 ? void 0 : _a.length) || 0);
console.info('\tReturn value: %d (%s)', results.returnValue, results.unit);
console.info('\tMessage: %s', results.message || '');
const success = `\tResult success: ${results.success}`;
if (results.success)
console.info(chalk_1.default.green.bold(success));
else
console.info(chalk_1.default.red.bold(success));
console.info('');
if ((_b = results === null || results === void 0 ? void 0 : results.variables) === null || _b === void 0 ? void 0 : _b.length) {
console.info(chalk_1.default.cyan.bold('----- [Variables values] -----'));
for (let idx = 0; idx < results.variables.length; idx++) {
const variable = results.variables[idx];
let description = '';
if (variable.description)
description = `description=${variable.description},`;
console.info('\tname=%s [%s], %s value=%s %s', variable.key, variable.usage || 'internal', description, variable.hideValue ? '*' : variable.value, variable.unit || '');
}
console.info('');
}
console.info(chalk_1.default.yellow.bold('----- [Steps result] -----'));
for (let idx = 0; idx < results.steps.length; idx++) {
const stepResult = results.steps[idx];
const stepName = `\t${stepResult.name}`;
console.info('');
if (stepResult.success === true)
console.info(chalk_1.default.green.bold(`${stepName}`));
else
console.info(chalk_1.default.red.bold(`${stepName}`));
console.info('\t\t[Success=%s, Duration=%d, Content-length=%d, Start time=%s, Ignore duration=%s]', stepResult.success, stepResult.durationMs.toFixed(0), stepResult.contentLength, new Date(stepResult.startTimestamp).toISOString(), stepResult.ignoreDuration);
(_c = stepResult.assertions) === null || _c === void 0 ? void 0 : _c.forEach((assertion) => {
const message = `\t\t ${assertion.description} [value=${assertion.value} : ${assertion.expression}]`;
if (assertion.status !== 'info') {
console.info(assertion.status === 'failure' ? chalk_1.default.red(message) : chalk_1.default.yellow(message));
}
else if (this._debug) {
console.info(chalk_1.default.green(message));
}
});
console.info('');
stepResult.requests.forEach((requestResult) => {
var _a;
console.info('\t %s - [%s] %s ', chalk_1.default.white.bold(requestResult.name), chalk_1.default.white.bold((_a = requestResult.method) === null || _a === void 0 ? void 0 : _a.toLocaleUpperCase()), requestResult.url);
if (requestResult.message) {
console.info('\t\t%s', requestResult.message);
}
console.info('\t\t[Success=%s, Duration=%d, Content-length=%d,Start time=%s, Status=(%d : %s)]', requestResult.success, requestResult.durationMs.toFixed(2), requestResult.contentLength, new Date(requestResult.startTimestamp).toISOString(), requestResult.status, requestResult.statusText);
const timings = requestResult.timings || {};
if (timings && timings.totalTime > 0) {
console.info('\t\t Timings [ Wait=%s, DNS=%s, SSL/TLS handshake =%s TCP=%s, FirstByte=%s, Download=%s]', chalk_1.default.yellow(timings.socketWait.toFixed(2)), chalk_1.default.green(timings.dnsTime.toFixed(2)), chalk_1.default.cyan(timings.secureHandshake.toFixed(2)), chalk_1.default.blue(timings.tcpConnect.toFixed(2)), chalk_1.default.magenta(timings.timeToFirstByte.toFixed(2), chalk_1.default.red(timings.downloadTime.toFixed(2))));
const line = ' '.repeat(90);
let timePhases = '';
const showTimePhase = function (color, phase) {
const len = Math.round((phase / timings.totalTime) * line.length);
if (len > 0)
timePhases += color(line.substring(0, len)) + chalk_1.default.reset('');
};
showTimePhase(chalk_1.default.bgYellow, timings.socketWait);
if (timings.dnsTime > 0) {
showTimePhase(chalk_1.default.bgGreen, timings.dnsTime);
}
if (timings.secureHandshake > 0) {
showTimePhase(chalk_1.default.bgCyan, timings.secureHandshake);
}
showTimePhase(chalk_1.default.bgBlue, timings.tcpConnect);
showTimePhase(chalk_1.default.bgMagenta, timings.timeToFirstByte);
showTimePhase(chalk_1.default.bgRed, timings.downloadTime);
console.info('\t\t', timePhases);
}
});
}
console.info('');
}
static setCustomReturnValue(testConfig, results) {
let returnValue = undefined;
if (isNaN(results.returnValue) === false)
results.returnValue = Math.round(Number(results.returnValue.toFixed(0)));
if (results.flowControl === 'Individual Tests') {
results.returnValue = 0;
results.unit = '%';
const stepCount = results.steps.length;
if (stepCount > 0) {
const successSteps = results.steps.filter((stepResult) => {
return stepResult.success;
});
results.returnValue = Math.round((successSteps.length / stepCount) * 100);
}
}
else {
if (results.variables) {
const returnValue = results.variables.find((variable) => {
return variable.usage === 'returnValue' && variable.type === 'number' && !isNaN(variable.value);
});
if (returnValue !== undefined) {
results.returnValue = !isNaN(returnValue.value) ? Math.round(Number(returnValue.value)) : 0;
results.unit = returnValue.unit || 'ms';
}
}
if (returnValue == undefined &&
testConfig.requestTimeCalculation &&
testConfig.requestTimeCalculation !== 'TotalTime') {
switch (testConfig.requestTimeCalculation) {
case 'ContentLength':
returnValue = results.contentLength;
break;
case 'DNS':
returnValue = results.timings.dnsTime;
break;
case 'DownloadTime':
returnValue = results.timings.downloadTime;
break;
case 'Request':
returnValue = results.timings.tcpConnect;
break;
case 'TimeToFirstByte':
returnValue = results.timings.timeToFirstByte;
break;
}
if (returnValue)
results.returnValue = Number(returnValue.toFixed(0));
results.unit = 'ms';
}
}
return results.returnValue;
}
static maskRequestResults(vars, requestResult) {
const maskOutput = '*'.repeat(256);
if (requestResult.requestHeaders.Authorization) {
requestResult.requestHeaders.Authorization = maskOutput.substring(0, 12);
}
if (vars.size > 0) {
Array.from(vars.values()).forEach((variable) => {
if (variable.hideValue && typeof variable.value === 'string') {
const varVal = variable.value.replace(/\s/g, '+');
if (requestResult.url.includes(varVal)) {
requestResult.url = requestResult.url.replace(varVal, maskOutput.substring(0, Math.min(varVal.length, maskOutput.length)));
}
}
});
}
}
static getTimings(response) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
const timingData = {
timingStart: response.timingStart || 0,
timings: response.timings || {},
};
const performanceTimings = {
socketWait: timingData.timings.socket || 0,
dnsTime: (((_a = timingData.timings) === null || _a === void 0 ? void 0 : _a.lookup) || 0) - (((_b = timingData.timings) === null || _b === void 0 ? void 0 : _b.socket) || 0),
secureHandshake: 0,
tcpConnect: (((_c = timingData.timings) === null || _c === void 0 ? void 0 : _c.connect) || 0) - (((_d = timingData.timings) === null || _d === void 0 ? void 0 : _d.lookup) || 0),
timeToFirstByte: (((_e = timingData.timings) === null || _e === void 0 ? void 0 : _e.response) || 0) - (((_f = timingData.timings) === null || _f === void 0 ? void 0 : _f.connect) || 0),
downloadTime: (((_g = timingData.timings) === null || _g === void 0 ? void 0 : _g.end) || 0) - (((_h = timingData.timings) === null || _h === void 0 ? void 0 : _h.response) || 0),
totalTime: ((_j = timingData.timings) === null || _j === void 0 ? void 0 : _j.end) || 0,
};
if (timingData.timings.secureConnect) {
performanceTimings.secureHandshake = timingData.timings.secureConnect - (timingData.timings.connect || 0);
performanceTimings.timeToFirstByte =
(((_k = timingData.timings) === null || _k === void 0 ? void 0 : _k.response) || 0) - (((_l = timingData.timings) === null || _l === void 0 ? void 0 : _l.secureConnect) || 0);
}
return performanceTimings;
}
static incrementTimings(timings, add) {
timings.socketWait += add.socketWait;
timings.dnsTime += add.dnsTime;
timings.secureHandshake += add.secureHandshake;
timings.tcpConnect += add.tcpConnect;
timings.timeToFirstByte += add.timeToFirstByte;
timings.downloadTime += add.downloadTime;
timings.totalTime += add.totalTime;
return timings;
}
static initTimings() {
const timings = {};
timings.socketWait = 0;
timings.dnsTime = 0;
timings.secureHandshake = 0;
timings.tcpConnect = 0;
timings.timeToFirstByte = 0;
timings.downloadTime = 0;
timings.totalTime = 0;
return timings;
}
static roundTimings(timings) {
if (timings) {
timings.socketWait = Math.round(timings.socketWait);
timings.dnsTime = Math.round(timings.dnsTime);
timings.secureHandshake = Math.round(timings.secureHandshake);
timings.tcpConnect = Math.round(timings.tcpConnect);
timings.timeToFirstByte = Math.round(timings.timeToFirstByte);
timings.downloadTime = Math.round(timings.downloadTime);
timings.totalTime = Math.round(timings.totalTime);
}
return timings;
}
static initBaseResultItem(item) {
const now = Date.now();
item.success = false;
item.durationMs = 0;
item.startTimestamp = now;
item.endTimestamp = now;
item.contentLength = 0;
item.timings = this.initTimings();
}
}
exports.TestResultProcessor = TestResultProcessor;
//# sourceMappingURL=testResultProcessor.js.map