Archived
2

modified api.ts to directly incorporate swagger-ui-express code

This commit is contained in:
VLE2FE 2020-07-28 10:30:10 +02:00
parent a8e8b28c11
commit 1988a67b35
9 changed files with 489 additions and 377 deletions

View File

@ -30,8 +30,8 @@ const docs = [
const errors = [];
const nmDocs = 'C:\\Users\\vle2fe\\Documents\\Data\\All_200717\\nmDocs'; // NormMaster Documents
const dptFiles = 'C:\\Users\\vle2fe\\Documents\\Data\\All_200717\\DPT'; // Spectrum files
// const host = 'http://localhost:3000';
const host = 'https://definma-api.apps.de1.bosch-iot-cloud.com';
const host = 'http://localhost:3000';
// const host = 'https://definma-api.apps.de1.bosch-iot-cloud.com';
const requiredProperties = ['samplenumber','materialnumber','materialname','supplier','reinforcementmaterial','material','granulate/part','color','charge/batch','comments'];
dict = { // dictionary
'Granulat': 'granulate',

14
package-lock.json generated
View File

@ -3773,17 +3773,9 @@
}
},
"swagger-ui-dist": {
"version": "3.24.3",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.24.3.tgz",
"integrity": "sha512-kB8qobP42Xazaym7sD9g5mZuRL4416VIIYZMqPEIskkzKqbPLQGEiHA3ga31bdzyzFLgr6Z797+6X1Am6zYpbg=="
},
"swagger-ui-express": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.2.tgz",
"integrity": "sha512-bVT16qj6WdNlEKFkSLOoTeGuqEm2lfOFRq6mVHAx+viA/ikORE+n4CS3WpVcYmQzM4HE6+DUFgAWcMRBJNpjcw==",
"requires": {
"swagger-ui-dist": "^3.18.1"
}
"version": "3.30.2",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.30.2.tgz",
"integrity": "sha512-hAu/ig5N8i0trXXbrC7rwbXV4DhpEAsZhYXDs1305OjmDgjGC0thINbb0197idy3Pp+B6w7u426SUM43GAP7qw=="
},
"term-size": {
"version": "2.2.0",

View File

@ -37,7 +37,7 @@
"lodash": "^4.17.15",
"mongo-sanitize": "^1.1.0",
"mongoose": "^5.8.7",
"swagger-ui-express": "4.1.2"
"swagger-ui-dist": "^3.30.2"
},
"devDependencies": {
"@types/bcrypt": "^3.0.0",

View File

@ -1,48 +1,131 @@
import swagger from 'swagger-ui-express';
import express from 'express';
import swaggerUi from 'swagger-ui-dist';
import jsonRefParser, {JSONSchema} from '@apidevtools/json-schema-ref-parser';
import oasParser from '@apidevtools/swagger-parser';
// modifies the normal swagger-ui-express package
// modified from https://github.com/scottie1984/swagger-ui-express
// usage: app.use('/api-doc', api.serve(), api.setup());
// the paths property can be split using allOf
// further route documentation can be included in the x-doc property
export default class api {
static serve () {
return swagger.serve;
}
static setup () {
let apiDoc: JSONSchema = {};
jsonRefParser.bundle('api/api.yaml', (err, doc) => { // parse yaml
if (err) throw err;
apiDoc = doc;
apiDoc.servers.splice(process.env.NODE_ENV === 'production', 1);
apiDoc.paths = apiDoc.paths.allOf.reduce((s, e) => Object.assign(s, e)); // bundle routes
apiDoc = this.resolveXDoc(apiDoc);
oasParser.validate(apiDoc, (err, api) => { // validate oas schema
if (err) {
console.error(err);
}
else {
console.info(process.env.NODE_ENV === 'test' ? '' : 'API ok, version ' + api.info.version);
swagger.setup(apiDoc);
}
});
});
return swagger.setup(apiDoc, {customCssUrl: '/static/styles/swagger.css'})
}
private static resolveXDoc (doc) { // resolve x-doc properties recursively
Object.keys(doc).forEach(key => {
if (doc[key] !== null && doc[key].hasOwnProperty('x-doc')) { // add x-doc to description, is styled via css
doc[key].description += '<details class="docs"><summary>docs</summary>' + doc[key]['x-doc'] + '</details>';
export default function api () {
// generate apiDoc
let apiDoc: JSONSchema = {};
jsonRefParser.bundle('api/api.yaml', (err, doc) => { // parse yaml
if (err) throw err;
apiDoc = doc;
apiDoc.servers.splice(process.env.NODE_ENV === 'production', 1);
apiDoc.paths = apiDoc.paths.allOf.reduce((s, e) => Object.assign(s, e)); // bundle routes
apiDoc = resolveXDoc(apiDoc);
oasParser.validate(apiDoc, (err, api) => { // validate oas schema
if (err) {
console.error(err);
}
else if (typeof doc[key] === 'object' && doc[key] !== null) { // go deeper into recursion
doc[key] = this.resolveXDoc(doc[key]);
else {
console.info(process.env.NODE_ENV === 'test' ? '' : 'API ok, version ' + api.info.version);
}
});
return doc;
}
}
});
return [
(req, res, next) => { // serve init js and apiDoc file
switch (req.url) {
case '/swagger-ui-init.js':
res.set('Content-Type', 'application/javascript');
res.send(jsTplString);
break;
case '/apidoc.json':
res.set('Content-Type', 'application/json');
res.send(apiDoc);
break;
default:
next();
}
}, // serve swagger files
express.static(swaggerUi.getAbsoluteFSPath(), {index: false}),
(req, res) => { // serve html file as default
res.send(htmlTplString);
}
];
}
function resolveXDoc (doc) { // resolve x-doc properties recursively
Object.keys(doc).forEach(key => {
if (doc[key] !== null && doc[key].hasOwnProperty('x-doc')) { // add x-doc to description, is styled via css
doc[key].description += '<details class="docs"><summary>docs</summary>' + doc[key]['x-doc'] + '</details>';
}
else if (typeof doc[key] === 'object' && doc[key] !== null) { // go deeper into recursion
doc[key] = resolveXDoc(doc[key]);
}
});
return doc;
}
// templates
const htmlTplString = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>API documentation</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" >
<link rel="stylesheet" type="text/css" href="/static/styles/swagger-ui.css" >
<link rel="icon" type="image/png" href="/static/img/favicon.ico">
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position:absolute;width:0;height:0">
<defs>
<symbol viewBox="0 0 20 20" id="unlocked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"></path>
</symbol>
<symbol viewBox="0 0 20 20" id="locked">
<path d="M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="close">
<path d="M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow">
<path d="M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"/>
</symbol>
<symbol viewBox="0 0 20 20" id="large-arrow-down">
<path d="M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="jump-to">
<path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z"/>
</symbol>
<symbol viewBox="0 0 24 24" id="expand">
<path d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
</symbol>
</defs>
</svg>
<div id="swagger-ui"></div>
<script src="./swagger-ui-bundle.js"> </script>
<script src="./swagger-ui-standalone-preset.js"> </script>
<script src="./swagger-ui-init.js"> </script>
</body>
</html>
`;
const jsTplString = `
window.onload = function() {
// Build a system
window.ui = SwaggerUIBundle({
url: '/api-doc/apidoc.json',
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: 'StandaloneLayout'
});
}
`;

View File

@ -24,8 +24,38 @@ app.disable('x-powered-by');
// get port from environment, defaults to 3000
const port = process.env.PORT || 3000;
//middleware
app.use(helmet());
// security headers
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: [`'none'`],
baseUri: [`'self'`],
formAction: [`'none'`],
frameAncestors: [`'none'`]
}
}
}));
// special CSP header for api-doc
app.use('/api-doc', helmet.contentSecurityPolicy({
directives: {
defaultSrc: [`'none'`],
scriptSrc: [`'self'`],
connectSrc: [`'self'`],
styleSrc: [`'self'`, `'unsafe-inline'`],
imgSrc: [`'self'`, 'data:'],
baseUri: [`'self'`],
formAction: [`'none'`],
frameAncestors: [`'none'`]
}
}));
// special CSP header for the bosch-logo.svg
app.use('/static/img/bosch-logo.svg', helmet.contentSecurityPolicy({
directives: {
styleSrc: [`'unsafe-inline'`]
}
}));
// middleware
app.use(contentFilter()); // filter URL query attacks
app.use(express.json({ limit: '5mb'}));
app.use(express.urlencoded({ extended: false, limit: '5mb' }));
@ -71,7 +101,7 @@ app.use('/', require('./routes/measurement'));
app.use('/static', express.static('static'));
// Swagger UI
app.use('/api-doc', api.serve(), api.setup());
app.use('/api-doc', api());
app.use((req, res) => { // 404 error handling
res.status(404).json({status: 'Not found'});

View File

@ -44,7 +44,14 @@ router.put('/template/:collection(measurement|condition|material)/' + IdValidate
const {error, value: template} = TemplateValidate.input(req.body, 'change');
if (error) return res400(error, res);
const templateData = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
// find given template
const templateRef = await model(req).findById(req.params.id).lean().exec().catch(err => {next(err);}) as any;
if (templateRef instanceof Error) return;
if (!templateRef) {
return res.status(404).json({status: 'Not found'});
}
// find latest version
const templateData = await model(req).findOne({first_id: templateRef.first_id}).sort({version: -1}).lean().exec().catch(err => {next(err);}) as any;
if (templateData instanceof Error) return;
if (!templateData) {
return res.status(404).json({status: 'Not found'});

BIN
static/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,323 @@
/*Bosch styling for swagger*/
/*GET: dark blue*/
/*POST: dark green*/
/*PUT: turquoise*/
/*DELETE: fuchsia*/
:root {
--red: #ea0016;
--dark-blue: #005691;
--dark-blue-w75: #bfd5e3;
--dark-green: #006249;
--dark-green-w75: #bfd8d1;
--turquoise: #00a8b0;
--turquoise-w75: #bfe9eb;
--fuchsia: #b90276;
--fuchsia-w75: #edc0dd;
--light-grey: #bfc0c2;
--light-grey-w75: #efeff0;
--light-green: #78be20;
}
body {
background: #fff;
}
body:before {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 16px;
content: '';
background-repeat: no-repeat;
background-size: cover;
background-image: url(/static/img/header.svg);
}
body:after {
position: absolute;
right: 25px;
top: 36px;
width: 135px;
height: 48px;
content: '';
background-repeat: no-repeat;
background-size: cover;
background-image: url(/static/img/bosch-logo.svg);
}
.swagger-ui {
font-family: "Bosch Sans", sans-serif;
}
/*custom docs*/
.docs {
position: relative;
font-size: 14px;
}
.docs > summary {
position: absolute;
right: 0;
top: -25px;
cursor: pointer;
}
.docs-open:hover {
text-decoration: underline;
}
/*Remove topbar*/
.swagger-ui .topbar {
display: none
}
/*Remove models view*/
.swagger-ui .models {
display: none;
}
/*Remove application/json select*/
.swagger-ui .opblock .opblock-section-header > label, .swagger-ui .response-controls {
display: none;
}
/*Remove border radius*/
.swagger-ui .opblock, .swagger-ui .opblock .opblock-summary-method, .swagger-ui select {
border-radius: 0;
box-shadow: none;
}
/*remove links in response*/
.swagger-ui .response-col_links {
display: none;
}
/*remove version*/
.swagger-ui .info .title span {
display: none;
}
/*separator before methods*/
.swagger-ui .scheme-container {
box-shadow: none;
border-bottom: 1px solid var(--light-grey);
}
/*tag separator*/
.swagger-ui .opblock-tag {
border-bottom: 1px solid var(--light-grey);
}
/*parameters/responses bar*/
.swagger-ui .opblock .opblock-section-header {
box-shadow: none;
background: #fff;
}
/*select*/
.swagger-ui select {
background-color: var(--light-grey-w75);
border: none;
height: 36px;
}
/*button*/
.swagger-ui .btn {
border-radius: 0;
box-shadow: none;
}
.swagger-ui .btn:hover {
box-shadow: none;
}
/*authorize button */
.swagger-ui .btn.authorize {
color: var(--light-green);
border-color: var(--light-green);
}
.swagger-ui .btn.authorize svg {
fill: var(--light-green);
}
/*auth inputs*/
.swagger-ui .auth-container input[type="password"], .swagger-ui .auth-container input[type="text"] {
border-radius: 0;
box-shadow: none;
border-color: var(--light-grey);
}
.swagger-ui .dialog-ux .modal-ux {
border-radius: 0;
}
/*cancel button*/
.swagger-ui .btn.cancel {
color: var(--red);
border-color: var(--red);
}
/*download button*/
.swagger-ui .download-contents {
border-radius: 0;
height: 28px;
width: 80px;
}
/*model*/
.swagger-ui .model-box {
border-radius: 0;
}
/*execute button*/
.swagger-ui .btn.execute {
background-color: var(--dark-blue);
border-color: var(--dark-blue);
height: 30px;
line-height: 0.7;
}
.swagger-ui .btn-group .btn:last-child {
border-radius: 0;
height: 30px;
border-color: var(--dark-blue);
}
.swagger-ui .btn-group .btn:first-child {
border-radius: 0;
}
.swagger-ui .btn-group {
padding: 0 20px;
}
/*parameter input*/
.swagger-ui .parameters-col_description input[type="text"] {
border-radius: 0;
}
/*required label*/
.swagger-ui .parameter__name.required > span {
color: var(--red) !important;
}
.swagger-ui .parameter__name.required::after {
color: var(--red);
}
/*Remove colored parameters bar*/
.swagger-ui .opblock.opblock-get .tab-header .tab-item.active h4 span::after, .swagger-ui .opblock.opblock-post .tab-header .tab-item.active h4 span::after, .swagger-ui .opblock.opblock-put .tab-header .tab-item.active h4 span::after, .swagger-ui .opblock.opblock-delete .tab-header .tab-item.active h4 span::after {
background: none;
}
/*code*/
.swagger-ui .opblock-body pre.microlight {
border-radius: 0;
}
.swagger-ui .highlight-code > .microlight {
min-height: 0;
}
/*request body*/
.swagger-ui textarea {
border-radius: 0;
}
/*parameters smaller padding*/
.swagger-ui .execute-wrapper {
padding-top: 0;
padding-bottom: 0;
}
.swagger-ui .btn.execute {
margin-bottom: 20px;
}
.swagger-ui .opblock-description-wrapper {
margin-top: 20px;
}
.swagger-ui .opblock-description-wrapper {
margin-top: 5px;
}
.opblock-section .opblock-section-request-body > div > div {
padding-top: 18px;
}
/*response element positions*/
.swagger-ui .model-example {
position: relative;
margin-top: 0;
}
.swagger-ui .tab {
position: absolute;
top: -35px;
right: 0;
}
.swagger-ui table tbody tr td {
padding: 0;
}
.swagger-ui .renderedMarkdown p {
margin: 8px auto;
}
/*Method colors*/
.swagger-ui .opblock.opblock-get .opblock-summary-method {
background: var(--dark-blue);
}
.swagger-ui .opblock.opblock-get .opblock-summary {
border-color: var(--dark-blue);
}
.swagger-ui .opblock.opblock-get {
background: var(--dark-blue-w75);
border-color: var(--dark-blue);
}
.swagger-ui .opblock.opblock-post .opblock-summary-method {
background: var(--dark-green);
}
.swagger-ui .opblock.opblock-post .opblock-summary {
border-color: var(--dark-green);
}
.swagger-ui .opblock.opblock-post {
background: var(--dark-green-w75);
border-color: var(--dark-green);
}
.swagger-ui .opblock.opblock-put .opblock-summary-method {
background: var(--turquoise);
}
.swagger-ui .opblock.opblock-put .opblock-summary {
border-color: var(--turquoise);
}
.swagger-ui .opblock.opblock-put {
background: var(--turquoise-w75);
border-color: var(--turquoise);
}
.swagger-ui .opblock.opblock-delete .opblock-summary-method {
background: var(--fuchsia);
}
.swagger-ui .opblock.opblock-delete .opblock-summary {
border-color: var(--fuchsia);
}
.swagger-ui .opblock.opblock-delete {
background: var(--fuchsia-w75);
border-color: var(--fuchsia);
}

File diff suppressed because one or more lines are too long