Merge pull request #19 in ~VLE2FE/dfop-api from develop to master
* commit '91c11046a0e87f98e33d5e5e394b38500fa8f5e5': minor fixes api and headers modified api.ts to directly incorporate swagger-ui-express code added first_id for template response /authorized returns level, added device to spectrum measurement
This commit is contained in:
		@@ -37,6 +37,9 @@
 | 
			
		||||
                method:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  example: 'basic'
 | 
			
		||||
                level:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  example: read
 | 
			
		||||
      401:
 | 
			
		||||
        $ref: 'api.yaml#/components/responses/401'
 | 
			
		||||
      500:
 | 
			
		||||
 
 | 
			
		||||
@@ -158,6 +158,10 @@ Template:
 | 
			
		||||
      type: number
 | 
			
		||||
      readOnly: true
 | 
			
		||||
      example: 1
 | 
			
		||||
    first_id:
 | 
			
		||||
      readOnly: true
 | 
			
		||||
      type: string
 | 
			
		||||
      example: 5ea0450ed851c30a90e70894
 | 
			
		||||
    parameters:
 | 
			
		||||
      type: array
 | 
			
		||||
      items:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
call npm run tsc-full
 | 
			
		||||
copy package.json dist\package.json
 | 
			
		||||
copy package-lock.json dist\package-lock.json
 | 
			
		||||
copy .nvmrc dist\.nvmrc
 | 
			
		||||
Xcopy /E /I api dist\api
 | 
			
		||||
Xcopy /E /I static dist\static
 | 
			
		||||
@@ -158,6 +158,9 @@ async function importCsv(doc) {
 | 
			
		||||
              newE[field] = '';
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          // if(newE['materialname'] === '') {  // TODO: is this replacement okay?
 | 
			
		||||
          //   newE['materialname'] = newE['material'];
 | 
			
		||||
          // }
 | 
			
		||||
          if (newE['supplier'] === '') {  // empty supplier fields
 | 
			
		||||
            newE['supplier'] = 'unknown';
 | 
			
		||||
          }
 | 
			
		||||
@@ -194,7 +197,8 @@ async function allDpts() {
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const measurement_template = res.data.find(e => e.name === 'spectrum')._id;
 | 
			
		||||
  const measurement_templates = res.data.filter(e => e.name === 'spectrum');
 | 
			
		||||
  const measurement_template = measurement_templates[measurement_templates.length - 1]._id;
 | 
			
		||||
  res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: host + '/samples?status=all',
 | 
			
		||||
@@ -207,7 +211,7 @@ async function allDpts() {
 | 
			
		||||
  res.data.forEach(sample => {
 | 
			
		||||
    sampleIds[sample.number] = sample._id;
 | 
			
		||||
  });
 | 
			
		||||
  const dptRegex = /(.*?)_(.*?)_(\d+|\d+_\d+).DPT/;
 | 
			
		||||
  const dptRegex = /(.*?)_(.*?)_(\d+|[a-zA-Z0-9]+_\d+).DPT/;
 | 
			
		||||
  const dpts = fs.readdirSync(dptFiles);
 | 
			
		||||
  for (let i in dpts) {
 | 
			
		||||
    const regexRes = dptRegex.exec(dpts[i])
 | 
			
		||||
@@ -268,8 +272,8 @@ async function allKfVz() {
 | 
			
		||||
      password: 'Abc123!#'
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  const kf_template = res.data.find(e => e.name === 'kf')._id;
 | 
			
		||||
  const vz_template = res.data.find(e => e.name === 'vz')._id;
 | 
			
		||||
  const kf_template = res.data.filter(e => e.name === 'kf').sort((a, b) => b.version - a.version)[0]._id;
 | 
			
		||||
  const vz_template = res.data.filter(e => e.name === 'vz').sort((a, b) => b.version - a.version)[0]._id;
 | 
			
		||||
  res = await axios({
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: host + '/samples?status=all',
 | 
			
		||||
@@ -314,7 +318,7 @@ async function allKfVz() {
 | 
			
		||||
        errors.push(`KF/VZ upload for ${JSON.stringify(sample)} failed: ${JSON.stringify(err.response.data)}`);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    if (sample['VZ (ml/g)']) {
 | 
			
		||||
    if (sample['vz(ml/g)']) {
 | 
			
		||||
      await axios({
 | 
			
		||||
        method: 'post',
 | 
			
		||||
        url: host + '/measurement/new',
 | 
			
		||||
@@ -379,7 +383,7 @@ async function allSamples() {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    samples.push({
 | 
			
		||||
      number: sample['samplenumber'],
 | 
			
		||||
      number: sample['samplenumber'].replace(/[A-Z][a-z]0\d_/, ''),  // remove leading An_01 and Eh_01
 | 
			
		||||
      type: sampleType(sample['granulate/part']),
 | 
			
		||||
      batch: sample['charge/batch'],
 | 
			
		||||
      material_id: material._id,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -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",
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
    "tsc": "tsc",
 | 
			
		||||
    "tsc-full": "del /q dist\\* & (for /d %x in (dist\\*) do @rd /s /q \"%x\") & tsc",
 | 
			
		||||
    "build": "build.bat",
 | 
			
		||||
    "build-push": "build.bat && cf push",
 | 
			
		||||
    "build-push": "build.bat && timeout 3 && cf push",
 | 
			
		||||
    "test": "mocha dist/**/**.spec.js",
 | 
			
		||||
    "start": "node index.js",
 | 
			
		||||
    "dev": "nodemon -e ts,yaml --exec \"tsc && node dist/index.js || exit 1\"",
 | 
			
		||||
@@ -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",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										155
									
								
								src/api.ts
									
									
									
									
									
								
							
							
						
						
									
										155
									
								
								src/api.ts
									
									
									
									
									
								
							@@ -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",
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
`;
 | 
			
		||||
							
								
								
									
										43
									
								
								src/index.ts
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/index.ts
									
									
									
									
									
								
							@@ -8,7 +8,7 @@ import cors from 'cors';
 | 
			
		||||
import api from './api';
 | 
			
		||||
import db from './db';
 | 
			
		||||
 | 
			
		||||
// TODO: working demo branch
 | 
			
		||||
// TODO: check header, also in UI
 | 
			
		||||
 | 
			
		||||
// tell if server is running in debug or production environment
 | 
			
		||||
console.info(process.env.NODE_ENV === 'production' ? '===== PRODUCTION =====' : process.env.NODE_ENV === 'test' ? '' :'===== DEVELOPMENT =====');
 | 
			
		||||
@@ -24,8 +24,43 @@ app.disable('x-powered-by');
 | 
			
		||||
// get port from environment, defaults to 3000
 | 
			
		||||
const port = process.env.PORT || 3000;
 | 
			
		||||
 | 
			
		||||
//middleware
 | 
			
		||||
app.use(helmet());
 | 
			
		||||
// security headers
 | 
			
		||||
const defaultHeaderConfig = {
 | 
			
		||||
  contentSecurityPolicy: {
 | 
			
		||||
    directives: {
 | 
			
		||||
      defaultSrc: [`'none'`],
 | 
			
		||||
      baseUri: [`'self'`],
 | 
			
		||||
      formAction: [`'none'`],
 | 
			
		||||
      frameAncestors: [`'none'`]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  frameguard: {
 | 
			
		||||
    action: 'deny'
 | 
			
		||||
  },
 | 
			
		||||
  permittedCrossDomainPolicies: true,
 | 
			
		||||
  refererPolicy: true
 | 
			
		||||
};
 | 
			
		||||
app.use(helmet(defaultHeaderConfig));
 | 
			
		||||
// special CSP header for api-doc
 | 
			
		||||
app.use('/api-doc', helmet.contentSecurityPolicy({
 | 
			
		||||
  ...defaultHeaderConfig,
 | 
			
		||||
  directives: {
 | 
			
		||||
    defaultSrc: [`'none'`],
 | 
			
		||||
    scriptSrc: [`'self'`],
 | 
			
		||||
    connectSrc: [`'self'`],
 | 
			
		||||
    styleSrc: [`'self'`, `'unsafe-inline'`],
 | 
			
		||||
    imgSrc: [`'self'`, 'data:']
 | 
			
		||||
  }
 | 
			
		||||
}));
 | 
			
		||||
// special CSP header for the bosch-logo.svg
 | 
			
		||||
app.use('/static/img/bosch-logo.svg', helmet.contentSecurityPolicy({
 | 
			
		||||
  ...defaultHeaderConfig,
 | 
			
		||||
  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 +106,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'});
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ describe('/measurement', () => {
 | 
			
		||||
        url: '/measurement/800000000000000000000001',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}, measurement_template: '300000000000000000000001'}
 | 
			
		||||
        res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I'}, measurement_template: '300000000000000000000001'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('returns the measurement for an API key', done => {
 | 
			
		||||
@@ -27,7 +27,7 @@ describe('/measurement', () => {
 | 
			
		||||
        url: '/measurement/800000000000000000000001',
 | 
			
		||||
        auth: {key: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}, measurement_template: '300000000000000000000001'}
 | 
			
		||||
        res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I'}, measurement_template: '300000000000000000000001'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('returns deleted measurements for a maintain/admin user', done => {
 | 
			
		||||
@@ -80,7 +80,7 @@ describe('/measurement', () => {
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {},
 | 
			
		||||
        res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}, measurement_template: '300000000000000000000001'}
 | 
			
		||||
        res: {_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I'}, measurement_template: '300000000000000000000001'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('keeps unchanged values', done => {
 | 
			
		||||
@@ -89,10 +89,10 @@ describe('/measurement', () => {
 | 
			
		||||
        url: '/measurement/800000000000000000000001',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}}
 | 
			
		||||
        req: {values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I'}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]}, measurement_template: '300000000000000000000001'});
 | 
			
		||||
        should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]], device: 'Alpha I'}, measurement_template: '300000000000000000000001'});
 | 
			
		||||
        MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(data).have.property('status',globals.status.validated);
 | 
			
		||||
@@ -126,7 +126,7 @@ describe('/measurement', () => {
 | 
			
		||||
        req: {values: {dpt: [[1,2],[3,4],[5,6]]}}
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[1,2],[3,4],[5,6]]}, measurement_template: '300000000000000000000001'});
 | 
			
		||||
        should(res.body).be.eql({_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[1,2],[3,4],[5,6]], device: 'Alpha I'}, measurement_template: '300000000000000000000001'});
 | 
			
		||||
        MeasurementModel.findById('800000000000000000000001').lean().exec((err, data: any) => {
 | 
			
		||||
          should(data).have.only.keys('_id', 'sample_id', 'values', 'measurement_template', 'status', '__v');
 | 
			
		||||
          should(data.sample_id.toString()).be.eql('400000000000000000000001');
 | 
			
		||||
@@ -144,7 +144,7 @@ describe('/measurement', () => {
 | 
			
		||||
        url: '/measurement/800000000000000000000001',
 | 
			
		||||
        auth: {basic: 'janedoe'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        req: {values: {dpt: [[1,2],[3,4],[5,6]]}},
 | 
			
		||||
        req: {values: {dpt: [[1,2],[3,4],[5,6]], device: 'Alpha I'}},
 | 
			
		||||
        log: {
 | 
			
		||||
          collection: 'measurements',
 | 
			
		||||
          dataAdd: {
 | 
			
		||||
 
 | 
			
		||||
@@ -179,7 +179,7 @@ describe('/', () => {
 | 
			
		||||
        url: '/authorized',
 | 
			
		||||
        auth: {key: 'admin'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        res: {status: 'Authorization successful', method: 'key'}
 | 
			
		||||
        res: {status: 'Authorization successful', method: 'key', level: 'admin'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
    it('works with basic auth', done => {
 | 
			
		||||
@@ -188,7 +188,7 @@ describe('/', () => {
 | 
			
		||||
        url: '/authorized',
 | 
			
		||||
        auth: {basic: 'admin'},
 | 
			
		||||
        httpStatus: 200,
 | 
			
		||||
        res: {status: 'Authorization successful', method: 'basic'}
 | 
			
		||||
        res: {status: 'Authorization successful', method: 'basic', level: 'admin'}
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
@@ -242,7 +242,7 @@ describe('The /api/{url} redirect', () => {
 | 
			
		||||
      url: '/api/authorized',
 | 
			
		||||
      auth: {basic: 'admin'},
 | 
			
		||||
      httpStatus: 200,
 | 
			
		||||
      res: {status: 'Authorization successful', method: 'basic'}
 | 
			
		||||
      res: {status: 'Authorization successful', method: 'basic', level: 'admin'}
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
  it('is disabled in production', done => {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ router.get('/', (req, res) => {
 | 
			
		||||
 | 
			
		||||
router.get('/authorized', (req, res) => {
 | 
			
		||||
  if (!req.auth(res, globals.levels)) return;
 | 
			
		||||
  res.json({status: 'Authorization successful', method: req.authDetails.method});
 | 
			
		||||
  res.json({status: 'Authorization successful', method: req.authDetails.method, level: req.authDetails.level});
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// TODO: evaluate exact changelog functionality (restoring, delting after time, etc.)
 | 
			
		||||
 
 | 
			
		||||
@@ -266,7 +266,7 @@ describe('/sample', () => {
 | 
			
		||||
        httpStatus: 200
 | 
			
		||||
      }).end((err, res) => {
 | 
			
		||||
        if (err) return done(err);
 | 
			
		||||
        should(res.body).have.lengthOf(2);
 | 
			
		||||
        should(res.body.filter(e => e.spectrum.dpt)).have.lengthOf(2);
 | 
			
		||||
        should(res.body[0].spectrum).have.property('dpt', [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
 | 
			
		||||
        should(res.body[1].spectrum).have.property('dpt', [[3996.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
 | 
			
		||||
        done();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,12 +22,11 @@ import csv from '../helpers/csv';
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
 | 
			
		||||
// TODO: check added filter
 | 
			
		||||
// TODO: use query pointer
 | 
			
		||||
// TODO: convert filter value to number according to table model
 | 
			
		||||
// TODO: validation for filter parameters
 | 
			
		||||
// TODO: location/device sort/filter
 | 
			
		||||
 | 
			
		||||
// TODO: think about material numbers
 | 
			
		||||
// TODO: think about filter keys with measurement template versions
 | 
			
		||||
 | 
			
		||||
router.get('/samples', async (req, res, next) => {
 | 
			
		||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'all')) return;
 | 
			
		||||
@@ -104,9 +103,9 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
  if (filters.sort[0].indexOf('measurements.') >= 0) {  // sorting with measurements as starting collection
 | 
			
		||||
    collection = MeasurementModel;
 | 
			
		||||
    const [,measurementName, measurementParam] = filters.sort[0].split('.');
 | 
			
		||||
    const measurementTemplate = await MeasurementTemplateModel.findOne({name: measurementName}).lean().exec().catch(err => {next(err);});
 | 
			
		||||
    if (measurementTemplate instanceof Error) return;
 | 
			
		||||
    if (!measurementTemplate) {
 | 
			
		||||
    const measurementTemplates = await MeasurementTemplateModel.find({name: measurementName}).lean().exec().catch(err => {next(err);});
 | 
			
		||||
    if (measurementTemplates instanceof Error) return;
 | 
			
		||||
    if (!measurementTemplates) {
 | 
			
		||||
      return res.status(400).json({status: 'Invalid body format', details: filters.sort[0] + ' not found'});
 | 
			
		||||
    }
 | 
			
		||||
    let sortStartValue = null;
 | 
			
		||||
@@ -118,7 +117,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
      }
 | 
			
		||||
      sortStartValue = fromSample.values[measurementParam];
 | 
			
		||||
    }
 | 
			
		||||
    queryPtr[0].$match.$and.push({measurement_template: mongoose.Types.ObjectId(measurementTemplate._id)});  // find measurements to sort
 | 
			
		||||
    queryPtr[0].$match.$and.push({measurement_template: {$in: measurementTemplates.map(e => e._id)}});  // find measurements to sort
 | 
			
		||||
    if (filters.filters.find(e => e.field === filters.sort[0])) {  // sorted measurement should also be filtered
 | 
			
		||||
      queryPtr[0].$match.$and.push(...filterQueries(filters.filters.filter(e => e.field === filters.sort[0]).map(e => {e.field = 'values.' + e.field.split('.')[2]; return e; })));
 | 
			
		||||
    }
 | 
			
		||||
@@ -194,7 +193,16 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
        if (!fromSample) {
 | 
			
		||||
          return res.status(400).json({status: 'Invalid body format', details: 'from-id not found'});
 | 
			
		||||
        }
 | 
			
		||||
        sortStartValue = fromSample[filters.sort[0]];
 | 
			
		||||
        console.log(fromSample);
 | 
			
		||||
        console.log(filters.sort[0]);
 | 
			
		||||
        console.log(fromSample[filters.sort[0]]);
 | 
			
		||||
        const filterKey = filters.sort[0].split('.');
 | 
			
		||||
        if (filterKey.length === 2) {
 | 
			
		||||
          sortStartValue = fromSample[0][filterKey[0]][filterKey[1]];
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          sortStartValue = fromSample[0][filterKey[0]];
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      queryPtr.push(...sortQuery(filters, [filters.sort[0], '_id'], sortStartValue));
 | 
			
		||||
    }
 | 
			
		||||
@@ -290,19 +298,22 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
          as: 'measurements'
 | 
			
		||||
        }});
 | 
			
		||||
    }
 | 
			
		||||
    measurementTemplates.filter(e => e.name !== 'spectrum').forEach(template => {  // TODO: hard coded dpt for special treatment, change later
 | 
			
		||||
    measurementTemplates.forEach(template => {  // TODO: hard coded dpt for special treatment, change later
 | 
			
		||||
      queryPtr.push({$addFields: {[template.name]: {$let: {  // add measurements as property [template.name], if one result, array is reduced to direct values
 | 
			
		||||
              vars: {arr: {$filter: {input: '$measurements', cond: {$eq: ['$$this.measurement_template', mongoose.Types.ObjectId(template._id)]}}}},
 | 
			
		||||
              in:{$cond: [{$lte: [{$size: '$$arr'}, 1]}, {$arrayElemAt: ['$$arr', 0]}, '$$arr']}
 | 
			
		||||
            }}}}, {$addFields: {[template.name]: {$cond: ['$' + template.name + '.values', '$' + template.name + '.values', template.parameters.reduce((s, e) => {s[e.name] = null; return s;}, {})]}}});
 | 
			
		||||
      if (measurementFieldsFields.find(e => e === 'spectrum')) {
 | 
			
		||||
        queryPtr.push({$unwind: '$spectrum'});
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    if (measurementFieldsFields.find(e => e === 'spectrum')) {  // TODO: remove hardcoded as well
 | 
			
		||||
      queryPtr.push(
 | 
			
		||||
        {$addFields: {spectrum: {$filter: {input: '$measurements', cond: {$eq: ['$$this.measurement_template', measurementTemplates.filter(e => e.name === 'spectrum')[0]._id]}}}}},
 | 
			
		||||
        {$addFields: {spectrum: '$spectrum.values'}},
 | 
			
		||||
        {$unwind: '$spectrum'}
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    // if (measurementFieldsFields.find(e => e === 'spectrum')) {  // TODO: remove hardcoded as well
 | 
			
		||||
    //   queryPtr.push(
 | 
			
		||||
    //     {$addFields: {spectrum: {$filter: {input: '$measurements', cond: {$eq: ['$$this.measurement_template', measurementTemplates.filter(e => e.name === 'spectrum')[0]._id]}}}}},
 | 
			
		||||
    //     {$addFields: {spectrum: '$spectrum.values'}},
 | 
			
		||||
    //     {$unwind: '$spectrum'}
 | 
			
		||||
    //   );
 | 
			
		||||
    // }
 | 
			
		||||
    // queryPtr.push({$unset: 'measurements'});
 | 
			
		||||
    queryPtr.push({$project: {measurements: 0}});
 | 
			
		||||
  }
 | 
			
		||||
@@ -316,9 +327,8 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
    projection._id = false;
 | 
			
		||||
  }
 | 
			
		||||
  queryPtr.push({$project: projection});
 | 
			
		||||
 | 
			
		||||
  if (!fieldsToAdd.find(e => /spectrum\./.test(e))) {  // use streaming when including spectrum files
 | 
			
		||||
    collection.aggregate(query).exec((err, data) => {
 | 
			
		||||
    collection.aggregate(query).allowDiskUse(true).exec((err, data) => {
 | 
			
		||||
      if (err) return next(err);
 | 
			
		||||
      if (data[0] && data[0].count) {
 | 
			
		||||
        res.header('x-total-items', data[0].count.length > 0 ? data[0].count[0].count : 0);
 | 
			
		||||
@@ -354,7 +364,7 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
    res.writeHead(200, {'Content-Type': 'application/json; charset=utf-8'});
 | 
			
		||||
    res.write('[');
 | 
			
		||||
    let count = 0;
 | 
			
		||||
    const stream = collection.aggregate(query).cursor().exec();
 | 
			
		||||
    const stream = collection.aggregate(query).allowDiskUse(true).cursor().exec();
 | 
			
		||||
    stream.on('data', data => {
 | 
			
		||||
      if (filters.fields.indexOf('added') >= 0) {  // add added date
 | 
			
		||||
        data.added = data._id.getTimestamp();
 | 
			
		||||
@@ -364,6 +374,9 @@ router.get('/samples', async (req, res, next) => {
 | 
			
		||||
      }
 | 
			
		||||
      res.write((count === 0 ? '' : ',\n') + JSON.stringify(data)); count ++;
 | 
			
		||||
    });
 | 
			
		||||
    stream.on('error', err => {
 | 
			
		||||
      console.error(err);
 | 
			
		||||
    });
 | 
			
		||||
    stream.on('close', () => {
 | 
			
		||||
      res.write(']');
 | 
			
		||||
      res.end();
 | 
			
		||||
 
 | 
			
		||||
@@ -25,10 +25,11 @@ describe('/template', () => {
 | 
			
		||||
          const json = require('../test/db.json');
 | 
			
		||||
          should(res.body).have.lengthOf(json.collections.condition_templates.length);
 | 
			
		||||
          should(res.body).matchEach(condition => {
 | 
			
		||||
            should(condition).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
			
		||||
            should(condition).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
 | 
			
		||||
            should(condition).have.property('_id').be.type('string');
 | 
			
		||||
            should(condition).have.property('name').be.type('string');
 | 
			
		||||
            should(condition).have.property('version').be.type('number');
 | 
			
		||||
            should(condition).have.property('first_id').be.type('string');
 | 
			
		||||
            should(condition.parameters).matchEach(number => {
 | 
			
		||||
              should(number).have.only.keys('name', 'range');
 | 
			
		||||
              should(number).have.property('name').be.type('string');
 | 
			
		||||
@@ -62,7 +63,7 @@ describe('/template', () => {
 | 
			
		||||
          url: '/template/condition/200000000000000000000001',
 | 
			
		||||
          auth: {basic: 'janedoe'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('rejects an API key', done => {
 | 
			
		||||
@@ -98,7 +99,7 @@ describe('/template', () => {
 | 
			
		||||
          auth: {basic: 'admin'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          req: {},
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('keeps unchanged properties', done => {
 | 
			
		||||
@@ -108,7 +109,7 @@ describe('/template', () => {
 | 
			
		||||
          auth: {basic: 'admin'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          req: {name: 'heat treatment', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]},
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('keeps only one unchanged property', done => {
 | 
			
		||||
@@ -118,7 +119,7 @@ describe('/template', () => {
 | 
			
		||||
          auth: {basic: 'admin'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          req: {name: 'heat treatment'},
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
          res: {_id: '200000000000000000000001', name: 'heat treatment', version: 1, first_id: '200000000000000000000001', parameters: [{name: 'material', range: {values: ['copper', 'hot air']}}, {name: 'weeks', range: {min: 1, max: 10}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('changes the given properties', done => {
 | 
			
		||||
@@ -136,6 +137,7 @@ describe('/template', () => {
 | 
			
		||||
              should(data.first_id.toString()).be.eql('200000000000000000000001');
 | 
			
		||||
              should(data).have.property('name', 'heat aging');
 | 
			
		||||
              should(data).have.property('version', 2);
 | 
			
		||||
              should(data.first_id.toString()).be.eql('200000000000000000000001');
 | 
			
		||||
              should(data).have.property('parameters').have.lengthOf(1);
 | 
			
		||||
              should(data.parameters[0]).have.property('name', 'time');
 | 
			
		||||
              should(data.parameters[0]).have.property('range');
 | 
			
		||||
@@ -191,7 +193,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'time', range: {values: [1, 2, 5]}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {values: [1, 2, 5]}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {values: [1, 2, 5]}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -204,7 +206,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'time', range: {min: 1, max: 11}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {min: 1, max: 11}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {min: 1, max: 11}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -217,7 +219,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'time', range: {type: 'array'}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {type: 'array'}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {type: 'array'}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -230,7 +232,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'time', range: {}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, parameters: [{name: 'time', range: {}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'heat treatment', version: 2, first_id: '200000000000000000000001', parameters: [{name: 'time', range: {}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -310,9 +312,10 @@ describe('/template', () => {
 | 
			
		||||
          req: {name: 'heat treatment3', parameters: [{name: 'material', range: {values: ['copper']}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(res.body).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
			
		||||
          should(res.body).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
 | 
			
		||||
          should(res.body).have.property('name', 'heat treatment3');
 | 
			
		||||
          should(res.body).have.property('version', 1);
 | 
			
		||||
          should(res.body._id).be.eql(res.body.first_id);
 | 
			
		||||
          should(res.body).have.property('parameters').have.lengthOf(1);
 | 
			
		||||
          should(res.body.parameters[0]).have.property('name', 'material');
 | 
			
		||||
          should(res.body.parameters[0]).have.property('range');
 | 
			
		||||
@@ -336,6 +339,7 @@ describe('/template', () => {
 | 
			
		||||
            should(data.first_id.toString()).be.eql(data._id.toString());
 | 
			
		||||
            should(data).have.property('name', 'heat aging');
 | 
			
		||||
            should(data).have.property('version', 1);
 | 
			
		||||
            should(res.body._id).be.eql(res.body.first_id);
 | 
			
		||||
            should(data).have.property('parameters').have.lengthOf(1);
 | 
			
		||||
            should(data.parameters[0]).have.property('name', 'time');
 | 
			
		||||
            should(data.parameters[0]).have.property('range');
 | 
			
		||||
@@ -480,8 +484,9 @@ describe('/template', () => {
 | 
			
		||||
          const json = require('../test/db.json');
 | 
			
		||||
          should(res.body).have.lengthOf(json.collections.measurement_templates.length);
 | 
			
		||||
          should(res.body).matchEach(measurement => {
 | 
			
		||||
            should(measurement).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
			
		||||
            should(measurement).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
 | 
			
		||||
            should(measurement).have.property('_id').be.type('string');
 | 
			
		||||
            should(measurement).have.property('first_id').be.type('string');
 | 
			
		||||
            should(measurement).have.property('name').be.type('string');
 | 
			
		||||
            should(measurement).have.property('version').be.type('number');
 | 
			
		||||
            should(measurement.parameters).matchEach(number => {
 | 
			
		||||
@@ -517,7 +522,7 @@ describe('/template', () => {
 | 
			
		||||
          url: '/template/measurement/300000000000000000000001',
 | 
			
		||||
          auth: {basic: 'janedoe'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: { type: 'array'}}]}
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: { type: 'array'}}, {name: 'device', range: {}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('rejects an API key', done => {
 | 
			
		||||
@@ -553,7 +558,7 @@ describe('/template', () => {
 | 
			
		||||
          auth: {basic: 'admin'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          req: {},
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: { type: 'array'}}]}
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: { type: 'array'}}, {name: 'device', range: {}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('keeps unchanged properties', done => {
 | 
			
		||||
@@ -562,8 +567,8 @@ describe('/template', () => {
 | 
			
		||||
          url: '/template/measurement/300000000000000000000001',
 | 
			
		||||
          auth: {basic: 'admin'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          req: {name: 'spectrum', parameters: [{name: 'dpt', range: { type: 'array'}}]},
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: {type: 'array'}}]}
 | 
			
		||||
          req: {name: 'spectrum', parameters: [{name: 'dpt', range: { type: 'array'}}, {name: 'device', range: {}}]},
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: {type: 'array'}}, {name: 'device', range: {}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('keeps only one unchanged property', done => {
 | 
			
		||||
@@ -573,7 +578,7 @@ describe('/template', () => {
 | 
			
		||||
          auth: {basic: 'admin'},
 | 
			
		||||
          httpStatus: 200,
 | 
			
		||||
          req: {name: 'spectrum'},
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, parameters: [{name: 'dpt', range: {type: 'array'}}]}
 | 
			
		||||
          res: {_id: '300000000000000000000001', name: 'spectrum', version: 1, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: {type: 'array'}}, {name: 'device', range: {}}]}
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
      it('changes the given properties', done => {
 | 
			
		||||
@@ -585,7 +590,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {name: 'IR spectrum', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'IR spectrum', version: 2, parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'IR spectrum', version: 2, first_id: '300000000000000000000001', parameters: [{name: 'data point table', range: {min: 0, max: 1000}}]});
 | 
			
		||||
          TemplateMeasurementModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
			
		||||
            if (err) return done(err);
 | 
			
		||||
            should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
 | 
			
		||||
@@ -626,17 +631,19 @@ describe('/template', () => {
 | 
			
		||||
          req: {name: 'IR spectrum'},
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'IR spectrum', version: 2, parameters: [{name: 'dpt', range: {type: 'array'}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'IR spectrum', version: 2, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: {type: 'array'}}, {name: 'device', range: {}}]});
 | 
			
		||||
          TemplateMeasurementModel.findById(res.body._id).lean().exec((err, data:any) => {
 | 
			
		||||
            if (err) return done(err);
 | 
			
		||||
            should(data).have.only.keys('_id', 'first_id', 'name', 'version', 'parameters', '__v');
 | 
			
		||||
            should(data.first_id.toString()).be.eql('300000000000000000000001');
 | 
			
		||||
            should(data).have.property('name', 'IR spectrum');
 | 
			
		||||
            should(data).have.property('version', 2);
 | 
			
		||||
            should(data).have.property('parameters').have.lengthOf(1);
 | 
			
		||||
            should(data).have.property('parameters').have.lengthOf(2);
 | 
			
		||||
            should(data.parameters[0]).have.property('name', 'dpt');
 | 
			
		||||
            should(data.parameters[0]).have.property('range');
 | 
			
		||||
            should(data.parameters[0].range).have.property('type', 'array');
 | 
			
		||||
            should(data.parameters[1]).have.property('name', 'device');
 | 
			
		||||
            should(data.parameters[1]).have.property('range');
 | 
			
		||||
            done();
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
@@ -650,7 +657,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'spectrum', version: 2, parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'spectrum', version: 2, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: {values: [1, 2, 5]}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -663,7 +670,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'spectrum', version: 2, parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'spectrum', version: 2, first_id: '300000000000000000000001', parameters: [{name: 'dpt', range: {min: 0, max: 1000}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -676,7 +683,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'dpt2', range: {type: 'array'}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'spectrum', version: 2, parameters: [{name: 'dpt2', range: {type: 'array'}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'spectrum', version: 2, first_id: '300000000000000000000001', parameters: [{name: 'dpt2', range: {type: 'array'}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -689,7 +696,7 @@ describe('/template', () => {
 | 
			
		||||
          req: {parameters: [{name: 'weight %', range: {}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'kf', version: 2, parameters: [{name: 'weight %', range: {}}]});
 | 
			
		||||
          should(_.omit(res.body, '_id')).be.eql({name: 'kf', version: 2, first_id: '300000000000000000000002', parameters: [{name: 'weight %', range: {}}]});
 | 
			
		||||
          done();
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
@@ -759,9 +766,10 @@ describe('/template', () => {
 | 
			
		||||
          req: {name: 'vz', parameters: [{name: 'vz', range: {min: 1}}]}
 | 
			
		||||
        }).end((err, res) => {
 | 
			
		||||
          if (err) return done(err);
 | 
			
		||||
          should(res.body).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
			
		||||
          should(res.body).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
 | 
			
		||||
          should(res.body).have.property('name', 'vz');
 | 
			
		||||
          should(res.body).have.property('version', 1);
 | 
			
		||||
          should(res.body._id).be.eql(res.body.first_id);
 | 
			
		||||
          should(res.body).have.property('parameters').have.lengthOf(1);
 | 
			
		||||
          should(res.body.parameters[0]).have.property('name', 'vz');
 | 
			
		||||
          should(res.body.parameters[0]).have.property('range');
 | 
			
		||||
@@ -909,7 +917,7 @@ describe('/template', () => {
 | 
			
		||||
          const json = require('../test/db.json');
 | 
			
		||||
          should(res.body).have.lengthOf(json.collections.material_templates.length);
 | 
			
		||||
          should(res.body).matchEach(measurement => {
 | 
			
		||||
            should(measurement).have.only.keys('_id', 'name', 'version', 'parameters');
 | 
			
		||||
            should(measurement).have.only.keys('_id', 'name', 'version', 'first_id', 'parameters');
 | 
			
		||||
            should(measurement).have.property('_id').be.type('string');
 | 
			
		||||
            should(measurement).have.property('name').be.type('string');
 | 
			
		||||
            should(measurement).have.property('version').be.type('number');
 | 
			
		||||
 
 | 
			
		||||
@@ -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'});
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ router.put('/user:username([/](?!key|new).?*|/?)', async (req, res, next) => {
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// TODO: only possible if no data is linked to user, otherwise change status, etc.
 | 
			
		||||
router.delete('/user:username([/](?!key|new).?*|/?)', (req, res, next) => {  // this path matches /user, /user/ and /user/xxx, but not /user/key or user/new. See https://forbeslindesay.github.io/express-route-tester/ for the generated regex
 | 
			
		||||
  if (!req.auth(res, ['read', 'write', 'maintain', 'dev', 'admin'], 'basic')) return;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -62,9 +62,8 @@ export default class SampleValidate {
 | 
			
		||||
    'material.name',
 | 
			
		||||
    'material.supplier',
 | 
			
		||||
    'material.group',
 | 
			
		||||
    'material.number',
 | 
			
		||||
    'material.properties.*',
 | 
			
		||||
    'measurements.(?!spectrum)*'
 | 
			
		||||
    'measurements.(?!spectrum\.dpt)*'
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  private static fieldKeys = [
 | 
			
		||||
 
 | 
			
		||||
@@ -65,6 +65,7 @@ export default class TemplateValidate {
 | 
			
		||||
      _id: IdValidate.get(),
 | 
			
		||||
      name: this.template.name,
 | 
			
		||||
      version: this.template.version,
 | 
			
		||||
      first_id: IdValidate.get(),
 | 
			
		||||
      parameters: this.template.parameters
 | 
			
		||||
    }).validate(data, {stripUnknown: true});
 | 
			
		||||
    return error !== undefined? null : value;
 | 
			
		||||
 
 | 
			
		||||
@@ -403,7 +403,8 @@
 | 
			
		||||
            [3997.12558,98.00555],
 | 
			
		||||
            [3995.08519,98.03253],
 | 
			
		||||
            [3993.04480,98.02657]
 | 
			
		||||
          ]
 | 
			
		||||
          ],
 | 
			
		||||
          "device": "Alpha I"
 | 
			
		||||
        },
 | 
			
		||||
        "status": 10,
 | 
			
		||||
        "measurement_template": {"$oid":"300000000000000000000001"},
 | 
			
		||||
@@ -470,7 +471,8 @@
 | 
			
		||||
            [3996.12558,98.00555],
 | 
			
		||||
            [3995.08519,98.03253],
 | 
			
		||||
            [3993.04480,98.02657]
 | 
			
		||||
          ]
 | 
			
		||||
          ],
 | 
			
		||||
          "device": "Alpha II"
 | 
			
		||||
        },
 | 
			
		||||
        "status": 10,
 | 
			
		||||
        "measurement_template": {"$oid":"300000000000000000000001"},
 | 
			
		||||
@@ -551,6 +553,10 @@
 | 
			
		||||
            "range": {
 | 
			
		||||
              "type": "array"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "name": "device",
 | 
			
		||||
            "range": {}
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "__v": 0
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								static/img/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								static/img/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 KiB  | 
							
								
								
									
										82
									
								
								static/img/header.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								static/img/header.svg
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="300" width="720" version="1.1" y="0" x="0" viewBox="0 0 720 300">
 | 
			
		||||
  <style type="text/css">
 | 
			
		||||
    .st0 {
 | 
			
		||||
      fill: url("#SVGID_1_");
 | 
			
		||||
    }
 | 
			
		||||
    .st1 {
 | 
			
		||||
      fill: url("#SVGID_2_");
 | 
			
		||||
    }
 | 
			
		||||
    .st2 {
 | 
			
		||||
      fill: url("#SVGID_3_");
 | 
			
		||||
    }
 | 
			
		||||
    .st3 {
 | 
			
		||||
      fill: url("#SVGID_4_");
 | 
			
		||||
    }
 | 
			
		||||
    .st4 {
 | 
			
		||||
      fill: url("#SVGID_5_");
 | 
			
		||||
    }
 | 
			
		||||
    .st5 {
 | 
			
		||||
      fill: #AF2024;
 | 
			
		||||
    }
 | 
			
		||||
    .st6 {
 | 
			
		||||
      fill: url("#SVGID_6_");
 | 
			
		||||
    }
 | 
			
		||||
    .st7 {
 | 
			
		||||
      fill: #941B1E;
 | 
			
		||||
    }
 | 
			
		||||
    .st8 {
 | 
			
		||||
      fill: #B12739;
 | 
			
		||||
    }
 | 
			
		||||
    .st9 {
 | 
			
		||||
      fill: #952432;
 | 
			
		||||
    }
 | 
			
		||||
    .st10 {
 | 
			
		||||
      fill: #D42027;
 | 
			
		||||
    }
 | 
			
		||||
    .st11 {
 | 
			
		||||
      fill: url("#SVGID_7_");
 | 
			
		||||
    }
 | 
			
		||||
    .st12 {
 | 
			
		||||
      fill: url("#SVGID_8_");
 | 
			
		||||
    }
 | 
			
		||||
    .st13 {
 | 
			
		||||
      fill: #1C9A48;
 | 
			
		||||
    }
 | 
			
		||||
    .st14 {
 | 
			
		||||
      fill: url("#SVGID_9_");
 | 
			
		||||
    }
 | 
			
		||||
    .st15 {
 | 
			
		||||
      fill: url("#SVGID_10_");
 | 
			
		||||
    }
 | 
			
		||||
    .st16 {
 | 
			
		||||
      fill: #2A3886;
 | 
			
		||||
    }
 | 
			
		||||
    .st17 {
 | 
			
		||||
      fill: url("#SVGID_11_");
 | 
			
		||||
    }
 | 
			
		||||
    .st18 {
 | 
			
		||||
      fill: url("#SVGID_12_");
 | 
			
		||||
    }
 | 
			
		||||
    .st19 {
 | 
			
		||||
      fill: url("#SVGID_13_");
 | 
			
		||||
    }
 | 
			
		||||
    .st20 {
 | 
			
		||||
      fill: url("#SVGID_14_");
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
  <g transform="translate(-1.55,-3.3)">
 | 
			
		||||
    <linearGradient id="SVGID_1_" y2="-32.663" gradientUnits="userSpaceOnUse" y1="-32.663" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="842.08" x1="118.98"><stop stop-color="#952331" offset="0"/><stop stop-color="#921C1D" offset=".036094"/><stop stop-color="#B02739" offset=".084649"/><stop stop-color="#AD1F24" offset=".1237"/><stop stop-color="#C72026" offset=".1509"/><stop stop-color="#D42027" offset=".1697"/><stop stop-color="#CC2431" offset=".1758"/><stop stop-color="#B72B4C" offset=".1888"/><stop stop-color="#953371" offset=".2074"/><stop stop-color="#88357F" offset=".2142"/><stop stop-color="#853681" offset=".2436"/><stop stop-color="#6F368B" offset=".2638"/><stop stop-color="#39428F" offset=".2911"/><stop stop-color="#233D7D" offset=".3242"/><stop stop-color="#322C6F" offset=".4181"/><stop stop-color="#2A3885" offset=".494"/><stop stop-color="#1D62A1" offset=".5581"/><stop stop-color="#276CA5" offset=".5702"/><stop stop-color="#438EB3" offset=".6103"/><stop stop-color="#55A5BC" offset=".6399"/><stop stop-color="#5CAFBF" offset=".6556"/><stop stop-color="#56ABBD" offset=".6777"/><stop stop-color="#439FB8" offset=".7058"/><stop stop-color="#188EAF" offset=".7372"/><stop stop-color="#038BAE" offset=".7426"/><stop stop-color="#069292" offset=".7898"/><stop stop-color="#05A14B" offset=".8875"/><stop stop-color="#03927E" offset="1"/></linearGradient><rect width="723.1" y="0" x="0" height="306.4" class="st0" fill="url(#SVGID_1_)"/>
 | 
			
		||||
    <linearGradient id="SVGID_2_" y2="-109.26" gradientUnits="userSpaceOnUse" y1="-109.26" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="235.98" x1="325.08"><stop stop-color="#893680" offset="0"/><stop stop-color="#893680" offset=".3354"/><stop stop-color="#8D316D" offset=".5025"/><stop stop-color="#90294D" offset=".8398"/><stop stop-color="#902541" offset="1"/></linearGradient><polygon points="175.1 153.2 117 306.4 206.1 306.4" fill="url(#SVGID_2_)" class="st1"/>
 | 
			
		||||
    <linearGradient id="SVGID_3_" y2="-82.284" gradientUnits="userSpaceOnUse" y1="120.24" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="446.55" x1="478.93"><stop stop-color="#322C6F" offset="0"/><stop stop-color="#322C6F" offset=".2427"/><stop stop-color="#302F72" offset=".4599"/><stop stop-color="#2A3A7E" offset=".7155"/><stop stop-color="#154A93" offset=".9896"/><stop stop-color="#134B94" offset="1"/></linearGradient><polygon points="288.4 153.2 310.7 306.4 358.1 306.4 358.1 0 312.9 0" fill="url(#SVGID_3_)" class="st2"/>
 | 
			
		||||
    <linearGradient id="SVGID_4_" y2="-32.663" gradientUnits="userSpaceOnUse" y1="-32.663" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="372.88" x1="294.08"><stop stop-color="#6F378D" offset="0"/><stop stop-color="#3A4291" offset="1"/></linearGradient><polygon points="175.1 153.2 206.1 306.4 253.9 153.2 209.4 0 209.4 0" fill="url(#SVGID_4_)" class="st3"/>
 | 
			
		||||
    <linearGradient id="SVGID_5_" y2="-32.663" gradientUnits="userSpaceOnUse" y1="-32.663" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="325.08" x1="431.88"><stop stop-color="#233D7D" offset="0"/><stop stop-color="#293D7D" offset=".2495"/><stop stop-color="#3A3C80" offset=".5446"/><stop stop-color="#513B84" offset=".8616"/><stop stop-color="#5D3A86" offset="1"/></linearGradient><polygon points="253.9 153.2 206.1 306.4 310.7 306.4 288.4 153.2 312.9 0 209.4 0" fill="url(#SVGID_5_)" class="st4"/><polygon points="116.1 0 55.7 0 55.7 94.8 89.9 153.2 55.7 211.6 55.7 306.4 117 306.4 95.2 153.2" fill="#af2024" class="st5"/>
 | 
			
		||||
    <linearGradient id="SVGID_6_" y2="43.937" gradientUnits="userSpaceOnUse" y1="43.937" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="232.67" x1="329.11"><stop stop-color="#893680" offset="0"/><stop stop-color="#893680" offset=".3354"/><stop stop-color="#8D316D" offset=".5025"/><stop stop-color="#90294D" offset=".8398"/><stop stop-color="#902541" offset="1"/></linearGradient><polygon points="175.1 153.2 209.4 0 116.1 0" fill="url(#SVGID_6_)" class="st6"/><polygon points="55.7 94.8 55.7 0 0 0" fill="#941b1e" class="st7"/><polygon points="55.7 211.6 89.9 153.2 55.7 94.8" fill="#b12739" class="st8"/><polygon points="55.7 211.6 0 306.4 55.7 306.4" fill="#941b1e" class="st7"/><polygon points="55.7 94.8 0 0 0 306.4 55.7 211.6" fill="#952432" class="st9"/><polygon points="116.1 0 95.2 153.2 117 306.4 175.1 153.2" fill="#d42027" class="st10"/>
 | 
			
		||||
    <linearGradient id="SVGID_7_" y2="-186.06" gradientUnits="userSpaceOnUse" y1="120.44" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="748.96" x1="748.96"><stop stop-color="#94BE55" offset="0"/><stop stop-color="#93BD58" offset=".044340"/><stop stop-color="#8BBC6A" offset=".3891"/><stop stop-color="#86BC75" offset=".7149"/><stop stop-color="#84BC79" offset="1"/></linearGradient><path d="m641.6 259.6c1.7-25.4 10-54.6 18.8-85.6 1.4-5 2.8-10 4.2-15.1-1.4-5.5-2.8-10.9-4.2-16.2-8.8-33.3-17-64.7-18.8-92-1.4-21.2 1.4-37 8.9-50.6h-45.9c-7.5 18.3-10.3 29.1-8.9 50.3 1.7 27.3 10 58.7 18.8 92 13 49.3 28 106.2 23.2 164.2h12.9c-7.6-12.8-10.4-27.3-9-47z" class="st11" fill="url(#SVGID_7_)"/>
 | 
			
		||||
    <linearGradient id="SVGID_8_" y2="-184.45" gradientUnits="userSpaceOnUse" y1="117.29" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="733.49" x1="653.76"><stop stop-color="#08A24B" offset="0"/><stop stop-color="#0AA14E" offset=".1678"/><stop stop-color="#0B9E57" offset=".4047"/><stop stop-color="#099A67" offset=".6827"/><stop stop-color="#04947D" offset=".9898"/><stop stop-color="#04937E" offset="1"/></linearGradient><path d="m614.5 142.3c-8.8-33.3-17-64.7-18.8-92-1.4-21.2 1.4-32 8.9-50.3h-35.4c5.7 53.9-3.8 106.7-13.6 166.8-5.7 35-11.7 71.3-13.2 100.6-1.1 21.1 0.4 32.8 1.8 39h93.5c4.8-57.9-10.3-114.8-23.2-164.1z" class="st12" fill="url(#SVGID_8_)"/><path class="st13" fill="#1c9a48" d="m664.6 158.9c-1.4 5.1-2.8 10.1-4.2 15.1-8.8 31-17 60.2-18.8 85.6-1.4 19.7 1.4 34.2 9 46.9h33c4.2-51.8-7.2-102.3-19-147.6z"/>
 | 
			
		||||
    <linearGradient id="SVGID_9_" y2="-185.96" gradientUnits="userSpaceOnUse" y1="120.54" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="812.83" x1="812.83"><stop stop-color="#69A060" offset="0"/><stop stop-color="#639D5C" offset=".039895"/><stop stop-color="#4C944F" offset=".2192"/><stop stop-color="#378E47" offset=".4184"/><stop stop-color="#298B44" offset=".6515"/><stop stop-color="#238A43" offset="1"/></linearGradient><path d="m680.5 0c10.7 55.3-2.5 110.4-15.9 158.9 11.7 45.3 23.2 95.8 18.9 147.6h39.6v-306.5h-42.6z" class="st14" fill="url(#SVGID_9_)"/>
 | 
			
		||||
    <linearGradient id="SVGID_10_" y2="-185.86" gradientUnits="userSpaceOnUse" y1="120.54" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="652.45" x1="652.45"><stop stop-color="#05B5DC" offset="0"/><stop stop-color="#04B0D7" offset=".2197"/><stop stop-color="#05A4C9" offset=".5371"/><stop stop-color="#0591B4" offset=".9122"/><stop stop-color="#058CAE" offset="1"/></linearGradient><path d="m542.3 267.4c1.5-29.4 7.5-65.6 13.2-100.6 9.8-60.1 19.3-112.8 13.6-166.8h-70.8c-1.4 11.4-2.9 19.2-1.8 41.8 1.5 31.6 7.5 70.5 13.2 108.2 8.4 55.4 16.6 108.8 15.1 156.4h19.2c-1.3-6.2-2.8-17.9-1.7-39z" class="st15" fill="url(#SVGID_10_)"/><polygon points="375.7 153.2 358.1 0 358.1 306.4" fill="#2a3886" class="st16"/>
 | 
			
		||||
    <linearGradient id="SVGID_11_" y2="77.136" gradientUnits="userSpaceOnUse" y1="-4.3281" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="796.71" x1="751.05"><stop stop-color="#62B16E" offset="0"/><stop stop-color="#87B957" offset="1"/></linearGradient><path d="m641.6 50.6c1.7 27.3 10 58.7 18.8 92 1.4 5.3 2.8 10.7 4.2 16.2 13.5-48.4 26.6-103.5 15.9-158.8h-30c-7.5 13.6-10.3 29.4-8.9 50.6z" class="st17" fill="url(#SVGID_11_)"/>
 | 
			
		||||
    <linearGradient id="SVGID_12_" y2="-189.28" gradientUnits="userSpaceOnUse" y1="113.71" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="631.59" x1="550.4"><stop stop-color="#069AD4" offset="0"/><stop stop-color="#30A0CE" offset=".3525"/><stop stop-color="#5BB0C0" offset="1"/></linearGradient><path d="m509.8 150c-5.7-37.7-11.7-76.6-13.2-108.2-1.1-22.7 0.4-30.4 1.8-41.8h-41.5c1.5 40.1-1.5 85.3-7 160.8-3.1 43.5-8 110.5-7 145.7h82.1c1.4-47.7-6.8-101.1-15.2-156.5z" class="st18" fill="url(#SVGID_12_)"/>
 | 
			
		||||
    <linearGradient id="SVGID_13_" y2="-185.86" gradientUnits="userSpaceOnUse" y1="120.54" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="505.33" x1="505.33"><stop stop-color="#1E458E" offset="0"/><stop stop-color="#1F4F96" offset=".2411"/><stop stop-color="#2B6AAB" offset=".7292"/><stop stop-color="#337BB9" offset="1"/></linearGradient><polygon points="358.1 306.4 414.6 306.4 414.6 0 358.1 0 375.7 153.2" fill="url(#SVGID_13_)" class="st19"/>
 | 
			
		||||
    <linearGradient id="SVGID_14_" y2="120.54" gradientUnits="userSpaceOnUse" y1="-185.86" gradientTransform="matrix(1 0 0 -1 -118.98 120.54)" x2="554.92" x1="554.92"><stop stop-color="#3F9AC9" offset="0"/><stop stop-color="#2062A2" offset="1"/></linearGradient><path d="m449.9 160.8c5.5-75.5 8.5-120.6 7-160.8h-42.2l-0.1 306.4h28.3c-1-35.1 3.8-102.1 7-145.6z" class="st20" fill="url(#SVGID_14_)"/></g>
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 After Width: | Height: | Size: 9.9 KiB  | 
							
								
								
									
										345
									
								
								static/styles/swagger-ui.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										345
									
								
								static/styles/swagger-ui.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,345 @@
 | 
			
		||||
/*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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*clipboard button*/
 | 
			
		||||
.swagger-ui .copy-to-clipboard {
 | 
			
		||||
  border-radius: 0;
 | 
			
		||||
  top: 19px;
 | 
			
		||||
  height: 28px;
 | 
			
		||||
}
 | 
			
		||||
.swagger-ui .copy-to-clipboard > button {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  bottom: 3px;
 | 
			
		||||
}
 | 
			
		||||
.swagger-ui .curl-command .copy-to-clipboard {
 | 
			
		||||
  border-radius: 0;
 | 
			
		||||
  top: 24px;
 | 
			
		||||
}
 | 
			
		||||
.swagger-ui .curl-command .copy-to-clipboard > button {
 | 
			
		||||
  position: relative;
 | 
			
		||||
  bottom: 7px;
 | 
			
		||||
  right: 1px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*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;
 | 
			
		||||
  background: #41444e !important;
 | 
			
		||||
  padding: 0.5em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.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
											
										
									
								
							
		Reference in New Issue
	
	Block a user