@apica-io/url-xi
Version:
URL Check for integrations and API monitoring
719 lines • 33.4 kB
JavaScript
"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.TestConfig = void 0;
const fs_1 = __importDefault(require("fs"));
const util_1 = __importDefault(require("util"));
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const testbase_1 = require("../lib/testbase");
const ats_rest_1 = require("../lib/ats-rest");
const ITestConfig_1 = require("../model/ITestConfig");
const schemaValidator = __importStar(require("../util/schemaValidator"));
const helpers = __importStar(require("../lib/helpers"));
const faker = __importStar(require("faker"));
const vm = __importStar(require("vm2"));
const csv = __importStar(require("csvtojson/v2"));
const parseJson = require('parse-json');
const streamZip = __importStar(require("node-stream-zip"));
const api_1 = require("../lib/api");
const cryptify_1 = __importDefault(require("cryptify"));
const FAKER_PREFIX = '$faker.';
const NOT_ALLOWED_MODULES = ['child_process', 'fs'];
class TestConfig extends testbase_1.TestBase {
constructor(headers, debug) {
super(debug, 'TestConfig');
this._varMap = new Map();
this._varTemp = new Map();
this._jsonData = new Map();
this._certificates = new Map();
this._project = '';
this.lastZipFile = '';
this._ats = undefined;
this.configData = {};
this._headers = headers;
this._errors = [];
}
getATS() {
return this._ats;
}
_saveError(error) {
let s = JSON.stringify(error);
if (s === '{}') {
this._errors.push({
name: error.name || '',
message: error.message || error,
});
}
else {
this._errors.push(error);
}
}
readFile(pathName) {
return __awaiter(this, void 0, void 0, function* () {
this._errors = [];
try {
let readFile = util_1.default.promisify(fs_1.default.readFile);
let content = yield yield readFile(pathName);
return content;
}
catch (error) {
this._logger.error(error);
this._saveError(error);
return undefined;
}
});
}
getURLContent(api, lib) {
return __awaiter(this, void 0, void 0, function* () {
let config = {};
let response = yield api.get(lib.src, config);
return response.data;
});
}
loadZipLibrary(zipFile, source, subDir) {
return __awaiter(this, void 0, void 0, function* () {
let zipParts = path_1.default.parse(zipFile);
try {
let cachedZip = true;
if (!this._zip || zipFile !== this.lastZipFile) {
if (this._zip)
this._zip.close();
cachedZip = false;
yield helpers.fileExist(zipFile);
this._zip = new streamZip.async({ file: zipFile });
this.lastZipFile = zipFile;
}
let zipEntry = zipParts.name + path_1.default.sep + subDir + path_1.default.sep + source;
this._logger.debug('Read project asset from zip=%s, file=%s, cached=%s', zipFile, zipEntry, cachedZip);
return yield this._zip.entryData(zipEntry);
}
catch (error) {
let err = new Error();
err.message = `zip file=${zipFile}, Entry=${zipParts.name}${path_1.default.sep}${subDir}${path_1.default.sep}${source}. Error=${error.message}`;
err.name = 'ZIP_' + error.name;
err.stack = '';
throw err;
}
});
}
loadLibraries(options) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
let readFile = util_1.default.promisify(fs_1.default.readFile);
let api = new api_1.Api();
let projectDir = options.project || '.';
if (options.tableServer)
this._ats = new ats_rest_1.ATSRest(api, options.tableServer);
if (this.configData.includes) {
for (let idx = 0; idx < this.configData.includes.length; idx++) {
let lib = (_a = this.configData) === null || _a === void 0 ? void 0 : _a.includes[idx];
let data = undefined;
let parts = undefined;
let src = lib.src;
this._logger.debug(`Loading library: ${lib.name} type=${lib.type}, scope=${lib.scope}, src=${lib.src}`);
try {
parts = path_1.default.parse(lib.src);
switch (lib.type) {
case 'data':
if (!parts.ext && (parts.ext === '.json' || parts.ext === '.csv'))
throw 'File extension json or csv is required';
break;
case 'vars':
if (!parts.ext && parts.ext !== '.json')
throw 'File extension .json is required';
break;
case 'ats':
if (lib.scope !== 'url') {
throw `Library scope must be url for type ats. Lib=${lib.name}`;
}
}
switch (lib.scope) {
case 'url':
if (lib.type === 'ats') {
let atsOptions = lib.options || '';
let table = atsOptions.table || '';
let columns = atsOptions.columns || '';
let emptyOK = atsOptions.emptyOK || false;
if (!table || !columns || columns.length == 0) {
throw `Invalid table/columns specification for ATS. table=${table},columns=${columns}`;
}
let ats = this._ats;
if (src === 'default') {
if (!ats)
ats = this._ats = new ats_rest_1.ATSRest(api, 'http://localhost:8992');
}
else {
ats = new ats_rest_1.ATSRest(api, src);
}
src = ats.getUrl();
this._logger.debug('Loading data from ATS at %s', src);
data = yield ats.getTableContent(table, columns);
if (!data || data.length === 0) {
let err = new Error();
err.message = `ATS table ${table} should not be empty`;
err.name = 'AssertionError';
throw err;
}
}
else {
data = yield this.getURLContent(api, lib);
}
break;
case 'project':
let subDir = 'lib';
switch (lib.type) {
case 'data':
case 'certificate':
subDir = 'data';
break;
case 'vars':
subDir = 'vars';
break;
}
if (options.project && options.project.endsWith('.zip')) {
data = yield this.loadZipLibrary(options.project, lib.src, subDir);
}
else {
let fileName = path_1.default.resolve(projectDir, lib.src);
if (!fs_1.default.existsSync(fileName)) {
fileName = path_1.default.resolve(projectDir, subDir, lib.src);
}
if (options.decryptKey) {
let tmpFile = os_1.default.tmpdir() + '/' + process.pid + '_' + lib.src;
fs_1.default.copyFileSync(fileName, tmpFile);
let crypto = new cryptify_1.default(tmpFile, options.decryptKey || '', undefined, undefined, true);
let files = yield crypto.decrypt();
if (files && files.length > 0) {
this._logger.info('Decrypting %s', tmpFile);
}
data = yield readFile(tmpFile);
fs_1.default.unlinkSync(tmpFile);
}
else {
data = yield readFile(fileName);
}
}
/*
if (lib.type != 'certificate')
data = data.toString();
*/
break;
case 'fileSystem':
data = yield readFile(lib.src);
if (lib.type != 'certificate')
data = data.toString();
break;
}
switch (lib.type) {
case 'ats':
this._jsonData.set(lib.name, data);
break;
case 'certificate':
this._certificates.set(lib.name, data);
break;
case 'data':
let json = {};
if (parts.ext === '.json')
json = JSON.parse(data);
else if (parts.ext === '.csv') {
json = yield csv.default(lib.options ? lib.options : {}).fromString(data.toString());
}
if (Array.isArray(json))
this._jsonData.set(lib.name, json);
else
throw 'Invalid json format . Must be an array of objects';
break;
case 'vars':
let vars = JSON.parse(data);
let schemaOK = schemaValidator.validateVars(vars);
if (schemaOK) {
vars.forEach((variable) => {
this._varTemp.set(variable.key, variable);
});
}
else {
let message = `Schema validation errors. Loading vars [${lib.name}] from src=${lib.src} with scope=${lib.scope}`;
let e = {
message: message,
name: 'ValidationError',
};
this._errors.push(e);
schemaValidator.getErrors().forEach((error) => {
this._errors.push(error);
});
}
break;
}
}
catch (error) {
let m = error.message || error;
let message = `[${m}]. When loading resource library [${lib.name}] from src=${src} with scope=${lib.scope}`;
let e = {
message: message,
name: error.name || 'SyntaxError',
};
this._errors.push(e);
this._logger.error(e.message);
}
finally {
}
}
}
});
}
compileJavaScript(script) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
let jsEval = '';
let scriptFile = '';
this._logger.trace('Compile script %s, scope=%s', script.name || '', script.scope);
try {
if ((_a = script === null || script === void 0 ? void 0 : script.options) === null || _a === void 0 ? void 0 : _a.builtin) {
const notAllowed = script.options.builtin.some((r) => NOT_ALLOWED_MODULES.includes(r));
if (notAllowed) {
let err = new Error(`Script ${script.name} contains a not allowed builtin module [${script.options.builtin}]`);
err.name = 'AccessError';
throw err;
}
}
if (Array.isArray(script.script))
jsEval = script.script.join('\n');
else if (!((_b = script.script) === null || _b === void 0 ? void 0 : _b.endsWith('.js'))) {
jsEval = script.script;
}
else {
if (this._project.endsWith('.zip')) {
let data = yield this.loadZipLibrary(this._project, script.script, 'lib');
scriptFile = path_1.default.resolve(this._project, 'lib', script.script);
jsEval = data.toString();
}
else {
scriptFile = path_1.default.resolve(this._project, 'lib', script.script);
jsEval = fs_1.default.readFileSync(scriptFile).toString();
}
}
if (script.async) {
const preFix = ['(async () => {', ' try {'].join('\n');
const postFix = [
'} catch (e) {',
'logger.error(e.message);',
'module.exports.asyncError=e;',
'}',
'})();',
].join('\n');
jsEval = `${preFix}\n${jsEval}\n${postFix}`;
}
if (scriptFile) {
script.compiledScript = new vm.VMScript(jsEval, scriptFile).compile();
}
else {
script.compiledScript = new vm.VMScript(jsEval).compile();
}
}
catch (error) {
this._logger.error(error);
let stack = error.stack.toString();
let message = error.message;
let pos = stack.indexOf('at');
if (pos > -1 && script.name) {
message += ` in the script [${script.name}] `;
stack = stack.substring(0, pos);
}
else
stack = '';
let e = { message: message, name: error.name, stack: stack };
this._errors.push(e);
}
});
}
compileScripts() {
return __awaiter(this, void 0, void 0, function* () {
if (this.configData.scripts) {
for (let script of this.configData.scripts)
yield this.compileJavaScript(script);
}
for (let step of this.configData.steps) {
if (step.scripts) {
for (let script of step.scripts)
yield this.compileJavaScript(script);
}
for (let request of step.requests) {
if (request.scripts) {
for (let script of request.scripts)
yield this.compileJavaScript(script);
}
}
}
});
}
create(content, options) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
this._errors = [];
if (options.project)
this._project = options.project;
//this.configData = JSON.parse(content)
try {
this.configData = parseJson(content, options.testFile);
}
catch (error) {
let m = error.message;
let pos = m.lastIndexOf('.json');
m = pos >= 0 ? m.substring(0, pos + 5) : m;
//let m: string = s.replace(/\[\d+m\[\d+m/gm, ' ')
let e = { message: m, name: error.name };
this._logger.error(error.message);
this._errors.push(e);
return false;
}
this._logger.debug('Parsing of %s OK', ((_a = this === null || this === void 0 ? void 0 : this.configData) === null || _a === void 0 ? void 0 : _a.name) || 'Unknown ');
if (!this.configData.hideSteps && options.hideSteps) {
this.configData.hideSteps = true;
}
if (!this.configData.flowControl) {
this.configData.flowControl = 'Chained Flow';
}
if (options.baseURL) {
this.configData.baseURL = options.baseURL;
}
let schemaOK = schemaValidator.validate(this.configData);
if (!schemaOK) {
//this._errors = schemaValidator.getErrors()
schemaValidator.getErrors().forEach((error) => {
if (error.schema)
delete error.schema;
this._errors.push(error);
});
return schemaOK;
}
if (this.configData.variables) {
for (let idx = 0; idx < this.configData.variables.length; idx++) {
let v = this.configData.variables[idx];
this._varTemp.set(v.key, v);
}
}
yield this.compileScripts();
if (this._errors.length > 0) {
this._logger.error('Compilation errors found=%d', this._errors.length);
return false;
}
yield this.loadLibraries(options);
if (this._errors.length > 0) {
this._logger.error('Load Libraries not successful. Error found=%d', this._errors.length);
return false;
}
try {
let packInfo = helpers.getPackageInfo();
let defaultUserAgent = `url-xi-${packInfo.version}`;
let defConfig = {
timeout: ITestConfig_1.ResultConfig.timeout,
headers: { 'User-Agent': defaultUserAgent },
};
if (!((_b = this.configData) === null || _b === void 0 ? void 0 : _b.config))
this.configData.config = JSON.parse('{}');
if ((_c = this.configData.config) === null || _c === void 0 ? void 0 : _c.headers)
this.configData.config.headers = Object.assign({}, this.configData.config.headers, defConfig.headers);
else if (this.configData.config)
this.configData.config.headers = defConfig.headers;
this.configData.config = Object.assign({}, defConfig, this.configData.config);
if (this._headers) {
this.configData.config.headers = Object.assign({}, this.configData.config.headers, this._headers);
}
this._logger.debug('config:', this.configData.config);
let inpMap = new Map();
//let inputs: string[] = options.inputs
this._logger.debug('Inputs=%s', options.inputs);
if (options.inputs) {
options.inputs.forEach((option) => {
let arr = option.split('=').map((item) => item.trim());
for (let idx = 0; idx < arr.length - 1; idx += 2) {
inpMap.set(arr[idx], helpers.unDotify(arr[idx + 1]));
}
});
}
this._varTemp.forEach((variable, key) => {
var _a;
let isNumber = variable.value !== undefined && !isNaN(variable.value);
if (!isNumber) {
if (helpers.isDotedString(variable.value))
variable.value = eval(helpers.unDotify(variable.value));
else if (typeof variable.value === 'string')
variable.value = this.replaceWithVarValue(variable.value);
}
let validationError = false;
switch (variable.usage) {
case 'input':
let envName = ((this.configData.envPrefix || 'URL_XI') + '_' + variable.key)
.toUpperCase()
.replace(/\s/g, '_');
let value = inpMap.get(variable.key) || process.env[envName] || variable.value;
if (variable.type === 'number' && value !== undefined && !isNaN(value))
value = Number(value);
variable.value = value;
if (variable.validation !== undefined) {
try {
let ok = eval(variable.validation) ? true : false;
if (!ok) {
let message = `Validation of input parameter ${variable.key} failed. Value=${value}`;
throw message;
}
}
catch (error) {
let m = error.message || error;
let message = `${m}. Variable=${variable.key}`;
let e = {
message: message,
name: error.name || 'AssertionError',
};
this._errors.push(e);
this._logger.error(e.message);
validationError = true;
}
}
}
if (!validationError) {
if (variable.type === 'header' && ((_a = this.configData.config) === null || _a === void 0 ? void 0 : _a.headers)) {
this.configData.config.headers[variable.key] = variable.value;
}
else {
this._varMap.set(variable.key, variable);
}
}
});
if (this._errors.length > 0) {
this._logger.error('Validation of variables not successful. Errors found=%d', this._errors.length);
return false;
}
}
catch (error) {
this._logger.error(error.message);
this._saveError(error);
return false;
}
return true;
});
}
errors() {
return this._errors || [];
}
setRequestTimeCalculation(calc) {
switch (calc.toLowerCase()) {
case 'dns':
this.configData.requestTimeCalculation = 'DNS';
break;
case 'download':
case 'downloadtime':
this.configData.requestTimeCalculation = 'DownloadTime';
break;
case 'request':
this.configData.requestTimeCalculation = 'Request';
break;
case 'timetofirstbyte':
case 'firstbyte':
this.configData.requestTimeCalculation = 'TimeToFirstByte';
break;
case 'contentlength':
this.configData.requestTimeCalculation = 'ContentLength';
break;
case 'total':
case 'totaltime':
default:
this.configData.requestTimeCalculation = 'TotalTime';
break;
}
}
replaceFromJSON(json) {
let myThis = this;
//thislet obj = helpers.clone(json);
let obj = Object.assign({}, json);
traverse(obj);
return obj;
function traverse(o) {
for (var i in o) {
let value = o[i];
if (value !== null) {
switch (typeof value) {
case 'object':
traverse(value);
break;
case 'string':
let m = /^{{([A-Za-z_][\w]*\w)}}$/.exec(value);
if (m && m.length > 1) {
let v = m[1];
let variable = myThis._varMap.get(v) || undefined;
if (variable) {
o[i] = variable.value;
}
}
else {
o[i] = myThis.replaceWithVarValue(value);
}
}
}
}
}
}
getVars() {
return this._varMap;
}
setResultVariables(vars) {
this._varMap.forEach((variable, key) => {
switch (variable.usage) {
case 'inResponse':
case 'returnValue':
case 'urlLink':
case 'input':
if (!variable.hideValue)
vars.push(variable);
}
});
}
setCustomMetrics() {
let customMetrics = {};
this._varMap.forEach((variable, key) => {
switch (variable.usage) {
case 'metric':
if (variable.type === 'number' && variable.value && !isNaN(variable.value)) {
customMetrics[variable.key] = Math.round(Number(variable.value));
}
}
});
let empty = Object.keys(customMetrics).length === 0 && customMetrics.constructor === Object;
return empty ? null : customMetrics;
}
setVariableValue(key, value) {
let variable = this._varMap.get(key) || undefined;
if (variable) {
variable.value = value;
if (variable.type === 'boolean')
variable.value = value ? true : false;
}
else {
variable = {
key: key,
usage: '',
type: !isNaN(value) ? 'number' : 'string',
value: value,
};
this._varMap.set(key, variable);
}
}
getVariableValue(key) {
let v = this._varMap.get(key);
return v ? v.value : null;
}
replaceWithVarValue(str, keepMustache) {
let vars = this._varMap;
let ret = '';
let myThis = this;
let val = str.replace(/{{(\$?[A-Za-z_][\w\.]*\w)}}/gm, function (x, y) {
switch (y) {
case '$timestamp':
ret = Date.now();
break;
default:
if (y.startsWith(FAKER_PREFIX)) {
try {
let fakeAsset = y.substr(FAKER_PREFIX.length);
ret = faker.fake(`{{${fakeAsset}}}`);
}
catch (e) {
ret = '';
}
}
else if (y.startsWith('$data.')) {
ret = '';
let lib = y.split('.');
if (lib.length > 1) {
let json = myThis._jsonData.get(lib[1]);
if (json) {
if (lib.length === 2) {
ret = json;
}
else {
let index = myThis.getVariableValue('$lap') || 0;
json = json[index];
for (let idx = 2; idx < lib.length; idx++) {
json = json[lib[idx]];
if (!json)
break;
}
ret = json;
}
}
}
}
else {
let v = vars.get(y);
ret = v ? v.value : undefined;
if (v) {
ret = myThis.getVariableValue(v.key);
}
else if (keepMustache) {
ret = x;
}
}
}
return ret;
});
if (ret && Array.isArray(ret))
return ret;
ret = val === 'undefined' && ret === undefined ? '' : val === 'undefined' ? str : val;
return ret;
}
setupHttpsAgent(config) {
if (config.ca)
config.ca = this.replaceContextOption(config.ca.toString());
if (config.cert)
config.cert = this.replaceContextOption(config.cert);
if (config.key)
config.key = this.replaceContextOption(config.key);
if (config.pfx)
config.pfx = this.replaceContextOption(config.pfx);
return config;
}
replaceContextOption(contextOption) {
let content = undefined;
if (typeof contextOption === 'string') {
let m = /^{{\$cert\.([\w,\.]+)}}$/.exec(contextOption);
if (m && m.length > 1) {
let key = m[1];
content = this._certificates.get(key) || undefined;
}
}
return content ? content : contextOption;
}
}
exports.TestConfig = TestConfig;
//# sourceMappingURL=testConfig.js.map