@ply-ct/ply
Version:
REST API Automated Testing
284 lines • 13.9 kB
JavaScript
;
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