SequenceDiagram/bin/handlers/RenderRequestHandler.js

82 lines
2.0 KiB
JavaScript

const {HttpError} = require('../server/HttpError');
const {RequestHandler} = require('../server/RequestHandler');
const {VirtualSequenceDiagram} = require('../../lib/sequence-diagram');
const NUM_MATCH = '[0-9]+(?:\\.[0-9]+)?';
function beginTimer() {
return process.hrtime();
}
function endTimer(timer) {
const delay = process.hrtime(timer);
return delay[0] * 1e9 + delay[1];
}
function getNumeric(v, name) {
if(!v) {
return null;
}
const n = Number.parseFloat(v);
if(Number.isNaN(n)) {
throw new HttpError(400, 'Invalid value for ' + name);
}
return n;
}
function readEncoded(str, encoding) {
switch(encoding) {
case 'b64':
return Buffer
.from(decodeURIComponent(str), 'base64')
.toString('utf8');
case 'uri':
return str.split('/').map(decodeURIComponent).join('\n');
default:
throw new HttpError(400, 'Unknown encoding');
}
}
class RenderRequestHandler extends RequestHandler {
constructor(baseUrlPattern) {
super('GET', new RegExp(
`^${baseUrlPattern}/` +
`(?:(?:w(${NUM_MATCH}))?(?:h(${NUM_MATCH}))?/|z(${NUM_MATCH})/)?` +
'(?:(uri|b64)/)?' +
'(.*?)' +
'(?:\\.(svg))?$',
'i'
));
this.info = `Rendering sequence diagrams at ${baseUrlPattern}/`;
}
handle(req, res, {match, pickEncoding, log, writeEncoded}) {
this.applyCommonHeaders(req, res);
const encoding = pickEncoding();
const size = {
height: getNumeric(match[2], 'height'),
width: getNumeric(match[1], 'width'),
zoom: getNumeric(match[3], 'zoom'),
};
const code = readEncoded(match[5], (match[4] || 'uri').toLowerCase());
const format = (match[6] || 'svg').toLowerCase();
const timer = beginTimer();
const svg = VirtualSequenceDiagram.render(code, {size});
const delay = endTimer(timer);
log('RENDER (' + (delay / 1e6).toFixed(3) + 'ms)');
switch(format) {
case 'svg':
res.setHeader('Content-Type', 'image/svg+xml; charset=utf-8');
writeEncoded(encoding, svg);
break;
default:
throw new HttpError(400, 'Unsupported image format');
}
}
}
module.exports = {RenderRequestHandler};