UNPKG

@ply-ct/ply

Version:

REST API Automated Testing

233 lines 10.1 kB
"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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlyRequest = void 0; const cross_fetch_1 = __importDefault(require("cross-fetch")); const response_1 = require("./response"); const log_1 = require("./log"); const result_1 = require("./result"); const form_1 = require("./form"); const util = __importStar(require("./util")); const replace_1 = require("./replace"); const names_1 = require("./names"); class PlyRequest { /** * @param name test name * @param obj object to parse for contents */ constructor(name, obj, logger, retrieval) { this.name = name; this.logger = logger; this.type = 'request'; if (!obj.url) { throw new Error(`Request '${name}' in ${retrieval} is missing 'url'`); } this.url = obj.url.trim(); if (!obj.method) { throw new Error(`Request '${name}' in ${retrieval} is missing 'method'`); } this.method = obj.method.trim(); this.headers = obj.headers || {}; this.body = obj.body; this.start = obj.start || 0; this.end = obj.end; } getSupportedMethod(method) { const upperCase = method.toUpperCase().trim(); if (upperCase === 'GET' || upperCase === 'HEAD' || upperCase === 'POST' || upperCase === 'PUT' || upperCase === 'DELETE' || upperCase === 'CONNECT' || upperCase === 'OPTIONS' || upperCase === 'TRACE' || upperCase === 'PATCH') { return upperCase; } } get isGraphQl() { if (this.body) { return this.body.startsWith('query') || this.body.startsWith('mutation'); } return false; } getRunId(values) { return values[names_1.RUN_ID]; } /** * Call submit() to send the request without producing actual results * or comparing with expected. Useful for cleaning up or restoring * REST resources before/after testing (see Case.before()/after()). */ async submit(values, options, runOptions) { return await this.doSubmit(this.getRunId(values), this.getRequest(values, options, runOptions, true), options, runOptions); } async doSubmit(runId, requestObj, options, runOptions) { const before = new Date().getTime(); const { Authorization: _auth, ...loggedHeaders } = requestObj.headers; const loggedRequest = { ...requestObj, runId, headers: loggedHeaders }; if (runOptions === null || runOptions === void 0 ? void 0 : runOptions.submit) { this.logger.info('Request', loggedRequest); } else { this.logger.debug('Request', loggedRequest); } const ctHeader = util.header(requestObj.headers, 'content-type'); if (ctHeader && ctHeader[1].startsWith('multipart/form-data')) { requestObj = new form_1.MultipartForm(requestObj).getRequest(); } const { url: _url, ...fetchRequest } = requestObj; fetchRequest.headers = { ...(fetchRequest.headers || {}) }; if (!Object.keys(fetchRequest.headers).find((k) => k.toLowerCase() === 'user-agent')) { fetchRequest.headers['User-Agent'] = `Ply-CT/${await util.plyVersion()}`; } const response = await (0, cross_fetch_1.default)(requestObj.url, fetchRequest); const status = { code: response.status, message: response.statusText }; const headers = this.responseHeaders(response.headers); let body; if (util.isBinary(headers, options)) { body = await response.arrayBuffer(); } else { body = await response.text(); } const time = new Date().getTime() - before; const plyResponse = new response_1.PlyResponse(runId, status, headers, body, time); if (runOptions === null || runOptions === void 0 ? void 0 : runOptions.submit) { this.logger.info('Response', plyResponse); } else { this.logger.debug('Response', plyResponse); } return plyResponse; } /** * Request object with substituted values */ getRequest(values, options, runOptions, includeAuthHeader = false) { const replaceOptions = { logger: this.logger, trusted: runOptions === null || runOptions === void 0 ? void 0 : runOptions.trusted }; const url = (0, replace_1.replace)(this.url, values, replaceOptions); if (!url.startsWith('http://') && !url.startsWith('https://')) { throw new Error('Invalid url: ' + url); } const method = (0, replace_1.replace)(this.method, values, replaceOptions).toUpperCase(); if (!this.getSupportedMethod(method)) { throw new Error('Unsupported method: ' + method); } const headers = {}; for (const key of Object.keys(this.headers)) { headers[key] = (0, replace_1.replace)(this.headers[key], values, replaceOptions); } if (!includeAuthHeader) { delete headers.Authorization; } let body = this.body; if (body) { body = (0, replace_1.replace)(body, values, { logger: this.logger, trusted: runOptions === null || runOptions === void 0 ? void 0 : runOptions.trusted }); if (this.isGraphQl) { // graphql this.graphQl = body; body = JSON.stringify({ query: body }, null, options === null || options === void 0 ? void 0 : options.prettyIndent); } } return { name: this.name, type: this.type, url, method, headers, body, submitted: this.submitted, submit: () => { throw new Error('Not implemented'); } }; } responseHeaders(headers) { const obj = {}; headers.forEach((value, name) => { obj[name] = value; }); return obj; } /** * Only to be called in the context of a Suite (hence 'runtime'). * To execute a test programmatically, call one of the Suite.run() overloads. * Or to send a request without testing, call submit(). * @returns result with request invocation and status of 'Pending' */ async run(runtime, values, runOptions, runNum) { var _a; this.submitted = new Date(); const requestObject = this.getRequest(values, runtime.options, runOptions, true); const id = this.logger.level === log_1.LogLevel.debug ? ` (${this.getRunId(values)})` : ''; this.logger.info(`Request '${this.name}'${id} submitted at ${util.timestamp(this.submitted, this.logger.level === log_1.LogLevel.debug)}`); const runOpts = { ...runOptions }; const expectedExists = await runtime.results.expected.exists; if ((runOptions === null || runOptions === void 0 ? void 0 : runOptions.submitIfExpectedMissing) && !expectedExists) { runOpts.submit = true; } const runId = this.getRunId(values); try { const response = await this.doSubmit(runId, requestObject, runtime.options, runOpts); if (response.headers && ((runOptions === null || runOptions === void 0 ? void 0 : runOptions.createExpected) || ((runOptions === null || runOptions === void 0 ? void 0 : runOptions.createExpectedIfMissing) && !expectedExists)) && ((_a = runtime.options.genExcludeResponseHeaders) === null || _a === void 0 ? void 0 : _a.length)) { for (const key of Object.keys(response.headers)) { if (runtime.options.genExcludeResponseHeaders.includes(key)) { delete response.headers[key]; } } } const result = new result_1.PlyResult(this.name, requestObject, response.getResponse(runId, runtime.options, (runOptions === null || runOptions === void 0 ? void 0 : runOptions.submit) ? undefined : runtime.responseMassagers, true)); if (this.graphQl) { result.graphQl = this.graphQl; } return result; } catch (err) { this.logger.error(err.message, err); let errMsg = err.message; if (runNum) errMsg += ` (run ${runNum})`; const requestError = new Error(errMsg); requestError.request = { ...requestObject }; requestError.request.headers = {}; Object.keys(requestObject.headers).forEach((key) => { if (key !== 'Authorization') { requestError.request.headers[key] = requestObject.headers[key]; } }); throw requestError; } } } exports.PlyRequest = PlyRequest; //# sourceMappingURL=request.js.map