UNPKG

@apica-io/url-xi

Version:

URL Check for integrations and API monitoring

719 lines 33.4 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.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