UNPKG

@ply-ct/ply

Version:

REST API Automated Testing

284 lines 13.9 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; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.JsDocReader = exports.PlyExampleRequest = void 0; const ply_1 = require("../ply"); const ts_1 = require("../ts"); const yaml = __importStar(require("../yaml")); class PlyExampleRequest { constructor(requestPath, options) { this.options = options; const hash = requestPath.lastIndexOf('#'); if (hash === -1 || hash > requestPath.length - 1) { throw new Error(`Ply example path must include '#<requestName>': ${requestPath}`); } this.suitePath = requestPath.substring(0, hash); this.requestName = requestPath.substring(hash + 1); } async getSuite() { let suite; if (this.suitePath.endsWith('.flow')) { suite = PlyExampleRequest.flowSuites.get(this.suitePath); if (!suite) { suite = await new ply_1.Ply().loadFlow(this.suitePath); PlyExampleRequest.requestSuites.set(this.suitePath, suite); } } else { suite = PlyExampleRequest.requestSuites.get(this.suitePath); if (!suite) { suite = await new ply_1.Ply().loadRequestSuite(this.suitePath); PlyExampleRequest.requestSuites.set(this.suitePath, suite); } } return suite; } async getExpected() { var _a; let expectedObj = PlyExampleRequest.expectedObjs.get(this.suitePath); if (!expectedObj) { const suite = await this.getSuite(); const expected = suite.runtime.results.expected; const contents = (_a = expected.storage) === null || _a === void 0 ? void 0 : _a.read(); if (!contents) throw new Error(`Expected results not found: ${expected.storage}`); expectedObj = yaml.load('' + expected.storage, contents); if (this.suitePath.endsWith('.flow')) { expectedObj = Object.keys(expectedObj).reduce((obj, key) => { const step = expectedObj[key]; if (step.id && step.request && step.response) { obj[step.id] = { id: step.id, request: step.request, response: step.response }; } return obj; }, {}); } PlyExampleRequest.expectedObjs.set(this.suitePath, expectedObj); } return expectedObj; } async getActual() { let actualObj = PlyExampleRequest.actualObjs.get(this.suitePath); if (!actualObj) { const suite = await this.getSuite(); const actual = suite.runtime.results.actual; const contents = actual.read(); if (!contents) throw new Error(`Actual results not found: ${actual}`); actualObj = yaml.load('' + actual, contents); if (this.suitePath.endsWith('.flow')) { actualObj = Object.keys(actualObj).reduce((obj, key) => { const step = actualObj[key]; if (step.id && step.request && step.response) { obj[step.id] = { id: step.id, request: step.request, response: step.response }; } return obj; }, {}); } PlyExampleRequest.actualObjs.set(this.suitePath, actualObj); } return actualObj; } async getExampleRequest() { var _a, _b, _c, _d, _e; if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.samplesFromActual) { const actual = await this.getActual(); return (_c = (_b = actual[this.requestName]) === null || _b === void 0 ? void 0 : _b.request) === null || _c === void 0 ? void 0 : _c.body; } else { const expected = await this.getExpected(); return (_e = (_d = expected[this.requestName]) === null || _d === void 0 ? void 0 : _d.request) === null || _e === void 0 ? void 0 : _e.body; } } async getExampleResponse() { var _a, _b, _c, _d, _e; if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.samplesFromActual) { const actual = await this.getActual(); return (_c = (_b = actual[this.requestName]) === null || _b === void 0 ? void 0 : _b.response) === null || _c === void 0 ? void 0 : _c.body; } else { const expected = await this.getExpected(); return (_e = (_d = expected[this.requestName]) === null || _d === void 0 ? void 0 : _d.response) === null || _e === void 0 ? void 0 : _e.body; } } } exports.PlyExampleRequest = PlyExampleRequest; PlyExampleRequest.requestSuites = new Map(); PlyExampleRequest.flowSuites = new Map(); PlyExampleRequest.expectedObjs = new Map(); PlyExampleRequest.actualObjs = new Map(); class JsDocReader { constructor(ts, sourceFile) { this.ts = ts; this.sourceFile = sourceFile; } async getPlyEndpointMeta(endpointMethod, tag = 'ply', untaggedMethods = false, samplesFromActual) { var _a, _b; const classDecl = this.ts.getClassDeclaration(this.sourceFile.fileName, endpointMethod.class); if (classDecl) { const methodDecl = ts_1.Ts.methodDeclarations(classDecl).find((md) => md.name.getText() === endpointMethod.name); if (methodDecl) { const methodMeta = this.findMethodMeta(methodDecl); if (methodMeta) { const plyMeta = this.readPlyMeta(endpointMethod.class, methodDecl, tag); if (plyMeta || untaggedMethods) { const plyEndpointMeta = { summaries: [methodMeta.summary] }; // @operationId tag const operationId = this.readStringMeta(methodDecl, 'operationId'); if (operationId) plyEndpointMeta.operationId = operationId; const pipe = methodMeta.summary.indexOf('|'); if (pipe > 0 && pipe < methodMeta.summary.length - 1) { plyEndpointMeta.summaries = [ methodMeta.summary.substring(0, pipe).trim(), methodMeta.summary.substring(pipe + 1).trim() ]; } if (methodMeta.description) { plyEndpointMeta.description = methodMeta.description; } // @ply tag let exampleRequest; if (plyMeta === null || plyMeta === void 0 ? void 0 : plyMeta.request) { exampleRequest = await new PlyExampleRequest(plyMeta.request, { samplesFromActual }).getExampleRequest(); } let exampleResponses; if (plyMeta === null || plyMeta === void 0 ? void 0 : plyMeta.responses) { for (const key of Object.keys(plyMeta.responses)) { const responses = plyMeta.responses[key]; for (const response of responses) { const exampleResponse = await new PlyExampleRequest(response, { samplesFromActual }).getExampleResponse(); if (exampleResponse) { if (!exampleResponses) { exampleResponses = {}; } if (!exampleResponses['' + key]) { exampleResponses['' + key] = []; } exampleResponses['' + key].push(exampleResponse); } } } } if (exampleRequest || exampleResponses) { plyEndpointMeta.examples = { ...(exampleRequest && { request: exampleRequest }), ...(exampleResponses && { responses: exampleResponses }) }; } if ((endpointMethod.method === 'post' || endpointMethod.method === 'put' || endpointMethod.method === 'patch') && !((_a = plyEndpointMeta.examples) === null || _a === void 0 ? void 0 : _a.request)) { console.error(`** Warning: No @${tag} sample request for: ${endpointMethod.class}.${endpointMethod.name}()`); } if (Object.keys(((_b = plyEndpointMeta.examples) === null || _b === void 0 ? void 0 : _b.responses) || {}).length === 0) { console.error(`** Warning: No @${tag} sample responses for: ${endpointMethod.class}.${endpointMethod.name}()`); } return plyEndpointMeta; } } } } } /** * Summary is first line or sentence of a JSDoc comment, if any. * Description is the remainder. */ findMethodMeta(methodDeclaration) { const symbol = ts_1.Ts.symbolAtNode(methodDeclaration); const docComment = symbol === null || symbol === void 0 ? void 0 : symbol.getDocumentationComment(this.ts.checker); if ((docComment === null || docComment === void 0 ? void 0 : docComment.length) && docComment[0].kind === 'text' && docComment[0].text) { const lines = docComment[0].text.split(/\r?\n/); const dot = lines[0].indexOf('.'); const meta = { name: methodDeclaration.name.getText(), summary: (dot > 0 ? lines[0].substring(0, dot) : lines[0]).trim() }; let descrip = dot > 0 && dot < lines[0].length + 1 ? lines[0].substring(dot + 1).trim() : ''; for (let i = 1; i < lines.length; i++) { descrip += `\n${lines[i].trim()}`; } if (descrip.length) meta.description = descrip.trim(); return meta; } } readStringMeta(methodDeclaration, tag) { var _a; const symbol = ts_1.Ts.symbolAtNode(methodDeclaration); const jsDocTag = (_a = symbol === null || symbol === void 0 ? void 0 : symbol.getJsDocTags()) === null || _a === void 0 ? void 0 : _a.find((t) => t.name === tag); if (jsDocTag === null || jsDocTag === void 0 ? void 0 : jsDocTag.text) { let tagText = jsDocTag.text; if (Array.isArray(tagText)) { // depends on typescript version tagText = tagText[0].text; } return tagText; } } readPlyMeta(className, methodDeclaration, tag) { var _a; const symbol = ts_1.Ts.symbolAtNode(methodDeclaration); const plyJsDocTag = (_a = symbol === null || symbol === void 0 ? void 0 : symbol.getJsDocTags()) === null || _a === void 0 ? void 0 : _a.find((t) => t.name === tag); if (plyJsDocTag === null || plyJsDocTag === void 0 ? void 0 : plyJsDocTag.text) { let tagText = plyJsDocTag.text; if (Array.isArray(tagText)) { // depends on typescript version tagText = tagText[0].text; } try { const plyMeta = yaml.load(`${methodDeclaration.name.getText()}: @${tag}`, `${tagText}\n`); if (plyMeta.responses) { for (const code of Object.keys(plyMeta.responses)) { if (typeof plyMeta.responses[code] === 'string') { plyMeta.responses[code] = ['' + plyMeta.responses[code]]; } } } return plyMeta; } catch (err) { console.debug(`${err}`, err); throw new Error(`Failed to parse @${tag} tag for ${className}.${symbol === null || symbol === void 0 ? void 0 : symbol.name}():\n${err}`); } } } } exports.JsDocReader = JsDocReader; //# sourceMappingURL=apidocs.js.map