UNPKG

@apica-io/url-xi

Version:

URL Check for integrations and API monitoring

427 lines 22.2 kB
"use strict"; 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