smartswagger/ts/smartswagger.classes.smartswagger.ts
2021-11-19 20:16:01 +01:00

244 lines
7.4 KiB
TypeScript

import path from 'path';
import * as plugins from './smartswagger.plugins';
interface RedocProps {
'x-tagGroups': any;
}
type IExtendedApiDoc = plugins.openapiTypes.OpenAPIV3.Document & RedocProps;
export class Smartswagger {
// STATIC
/**
*
* @param urlArg a url arg that contains an original swagger.json in the response
* @returns an instance of
*/
public static async createFromUrl(urlArg: string) {
const jsonResponse = await plugins.nodeFetch(urlArg, {
headers: {
accept: 'application/json',
},
});
const apiDoc = await jsonResponse.json();
const newSMartswaggerInstance = new Smartswagger(apiDoc);
return newSMartswaggerInstance;
}
// INSTANCE
/**
* the basic info of the api doc
*/
public baseInfo: plugins.openapiTypes.OpenAPIV3.InfoObject;
public apiDoc: IExtendedApiDoc;
constructor(apiDocArg: IExtendedApiDoc) {
this.apiDoc = apiDocArg;
}
/**
* dereferences the document at hand
*/
public async deref() {
this.apiDoc = (await plugins.swaggerParser.dereference(
this.apiDoc
)) as IExtendedApiDoc;
}
public async addServer(serverArg: plugins.openapiTypes.OpenAPIV3.ServerObject) {
await this.deref();
this.apiDoc.servers = this.apiDoc.servers || [];
this.apiDoc.servers.push(serverArg);
}
/**
* merge a document from url
* @param documentToMergeArg
* @param basePathArg
*/
public async mergeDocument(
documentToMergeArg: IExtendedApiDoc,
basePathArg: string
) {
console.log(`merging document with name ${documentToMergeArg.info?.title}`);
await this.deref();
// lets get a dereferenced version of the document we want to merge
const documentToMerge = (await plugins.swaggerParser.dereference(
documentToMergeArg
)) as IExtendedApiDoc;
for (const path of Object.keys(documentToMerge.paths)) {
const pathToMerge = plugins.path.join(basePathArg, path);
this.apiDoc.paths = this.apiDoc.paths || {};
this.apiDoc.paths[pathToMerge] = documentToMerge.paths[path];
}
// merge tag groups
this.apiDoc['x-tagGroups'] = this.apiDoc['x-tagGroups'] || [];
if (documentToMerge['x-tagGroups']) {
for (const xTagGroup of documentToMerge['x-tagGroups']) {
this.apiDoc['x-tagGroups'].push(xTagGroup);
}
}
console.log('merged!');
}
/**
* merges a document from url
*/
public async mergeDocumentFromUrl(documentUrlArg: string, basePathArg: string = '') {
console.log(`getting document at ${documentUrlArg} for merging...`)
const documentResponse = await plugins.nodeFetch(documentUrlArg, {
headers: {
'accept-encoding': 'application/json',
},
});
const documentString = await documentResponse.text();
const apiDoc: IExtendedApiDoc = JSON.parse(documentString);
console.log(`document successfully fetched!`);
await this.mergeDocument(apiDoc, basePathArg);
}
/**
* merge multiple documents in parallel
* @param urlArrayArg
*/
public async mergeManyDocumentsFromUrl(urlArrayArg: {url: string, basePath: string}[]) {
const promiseArray: Promise<void>[] = [];
for (const urlArg of urlArrayArg) {
promiseArray.push(this.mergeDocumentFromUrl(urlArg.url, urlArg.basePath));
};
await Promise.all(promiseArray);
}
/**
* merges a component to routes based on regex
*/
public mergeComponentToRoutes(
routeDescriptor: {
includeRegexpArray: RegExp[];
excludeRegexpArray: RegExp[];
},
componentArg: plugins.openapiTypes.OpenAPIV3.ComponentsObject
) {
for (const pathCandidateRoute of Object.keys(this.apiDoc.paths)) {
let included: boolean = false;
let excluded: boolean = false;
for (const regExp of routeDescriptor.includeRegexpArray) {
if (regExp.test(pathCandidateRoute)) {
included = true;
break;
}
}
if (included) {
for (const regExp of routeDescriptor.excludeRegexpArray) {
if (regExp.test(pathCandidateRoute)) {
excluded = true;
break;
}
}
}
if (included && !excluded) {
// lets do the actual component inclusion
const pathCandidate = this.apiDoc.paths[pathCandidateRoute];
const instrumentMethod = (methodArg: typeof pathCandidate.get) => {
if (!methodArg) {
return;
}
if (componentArg.securitySchemes) {
}
};
}
}
}
private cacheResult: string = '';
private cacheCreationUnixTimestamp: number;
/**
* returns an express middleware using 'swagger-ui-express'
*/
public getSlashApiUiMiddleware() {
return (req: plugins.smartexpress.Request, res: plugins.smartexpress.Response) => {
res.setHeader('content-type', 'text/html');
res.write(`
<html>
<head>
<title>${this.apiDoc.info?.title || 'no name set'} - SwaggerUI</title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui.css">
<style>
body {
margin: 0px;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
</body>
<script src="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui-standalone-preset.js"></script>
<script src="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui-bundle.js"></script>
<script>
window.onload = function() {
// Build a system
const ui = SwaggerUIBundle({
url: "/apischema",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
})
window.ui = ui
}
</script>
</html>
`);
res.end();
};
}
public getSlashRedocMiddleware() {
return (req: plugins.smartexpress.Request, res: plugins.smartexpress.Response) => {
res.setHeader('content-type', 'text/html');
res.write(`
<html>
<head>
<title>${this.apiDoc.info?.title || 'no name set'} - Redoc </title>
<!-- needed for adaptive design -->
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
<!--
Redoc doesn't change outer page styles
-->
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<redoc spec-url='/apischema'></redoc>
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
</body>
</html>
`);
res.end();
};
}
public getSlashApiSchemaMiddleware() {
return async (req: plugins.smartexpress.Request, res: plugins.smartexpress.Response) => {
res.header('content-type', 'application/json');
res.write(JSON.stringify(this.apiDoc));
res.end();
};
}
}