src/lib/models/PhraseModel.js
'use strict'
var _ = require('lodash')
var BaseModel = require('./BaseModel')
var regexpGenerator = require('../regexpGenerator')
var paramsExtractor = require('../paramsExtractor')
var XRegExp = require('xregexp')
var utils = require('../utils.js')
var evaluateCode = require('../compilers/evaluateCode')
var methods = ['get', 'put', 'post', 'delete', 'options']
var ComposrError = require('../ComposrError')
var DEFAULT_PHRASE_PARAMETERS = [
'req',
'res',
// 'next',
'corbelDriver',
'domain',
'require',
'config',
'metrics'
]
var PhraseModel = function (json, domain) {
this.json = _.cloneDeep(json) // Clone to avoid modifications on parent object
this.id = this._generateId(domain)
this.domain = domain
// Stores the id on the raw model
this.json.id = this.id
// The regexp reference dictaminates the routing mechanisms
this.regexpReference = regexpGenerator.regexpReference(this.getUrl())
this.compiled = {
codes: {}
}
}
PhraseModel.prototype = new BaseModel()
PhraseModel.prototype.getUrl = function () {
return this.json.url
}
PhraseModel.prototype.getName = function () {
return this.getUrl().replace(/\//g, '!')
}
PhraseModel.prototype.getRegexp = function () {
return this.getRegexpReference().regexp
}
PhraseModel.prototype.getRegexpReference = function () {
return this.regexpReference
}
PhraseModel.prototype.getMiddlewares = function (verb) {
if (verb && this.json[verb] && this.json[verb].middlewares && Array.isArray(this.json[verb].middlewares)) {
return this.json[verb].middlewares
} else {
return []
}
}
PhraseModel.prototype.getDoc = function (verb) {
if (verb && this.json[verb] && this.json[verb].doc) {
return this.json[verb].doc
} else {
return null
}
}
PhraseModel.prototype.canRun = function (verb) {
return (this.compiled.codes[verb] && this.compiled.codes[verb].error === false) || false
}
PhraseModel.prototype.matchesPath = function (path) {
return XRegExp.test(path, this.getRegexpReference().xregexp)
}
/*
Asume that the path is sanitized without query params.
*/
PhraseModel.prototype.extractParamsFromPath = function extractParamsFromPath (path) {
return paramsExtractor.extract(path, this.getRegexpReference())
}
PhraseModel.prototype.compile = function compile (events) {
var model = this
this.compiled.codes = {}
// Create in memory functions with the evaluation of the codes
methods.forEach(function (method) {
if (model.json[method] && (model.json[method].code || model.json[method].codehash)) {
// @TODO: emit events for the evaluation of the codes
//
if (events) {
events.emit('debug', 'phrase:evaluatecode:', method, model.getId())
}
var code
if (model.json[method].codehash) {
code = utils.decodeFromBase64(model.json[method].codehash)
} else {
code = model.json[method].code
}
var debugInfo = model.json.debug ? model.json.debug[method] : null
var codeCompiled = evaluateCode(code, DEFAULT_PHRASE_PARAMETERS, debugInfo)
if (events) {
if (codeCompiled.error) {
events.emit('warn', model.getId() + ':evaluatecode:wrong_code', codeCompiled.error)
} else {
events.emit('debug', model.getId() + ':evaluatecode:good')
}
}
model.compiled.codes[method] = codeCompiled
}
})
}
// Runs VM script mode
PhraseModel.prototype.__executeScriptMode = function __executeScriptMode (verb, parameters, timeout, file, events) {
var options = {
timeout: timeout || 10000,
displayErrors: true
}
if (file) {
options.filename = file
}
try {
this.compiled.codes[verb].script.runInNewContext(parameters, options)
} catch (e) {
if (e.message && e.message === 'Script execution timed out.') {
// Timeout fired
events.emit('warn', 'phrase:timedout', e, this.getUrl())
parameters.res.send(503, new ComposrError('error:phrase:timedout:' + this.getUrl(), 'The phrase endpoint is timing out', 503))
} else {
throw e
}
}
}
// Runs function mode
PhraseModel.prototype.__executeFunctionMode = function __executeFunctionMode (verb, parameters, timeout, file) {
// @TODO: configure timeout
// @TODO: enable VM if memory bug gets solved
var url = this.getUrl()
var tm = setTimeout(function () {
var hasEnded = parameters.res.finished
if (!hasEnded) {
parameters.res.send(503, new ComposrError('error:phrase:timedout:' + url, 'The phrase endpoint is timing out', 503))
}
clearTimeout(tm)
}, timeout || 10000)
parameters.res.on('end', function () {
clearTimeout(tm)
})
if (file) {
var fn = require(file)
fn(
parameters.req,
parameters.res,
// parameters.next,
parameters.corbelDriver,
parameters.domain,
parameters.require,
parameters.config,
parameters.metrics
)
} else {
this.compiled.codes[verb].fn.call(null,
parameters.req,
parameters.res,
// parameters.next,
parameters.corbelDriver,
parameters.domain,
parameters.require,
parameters.config,
parameters.metrics)
}
}
module.exports = PhraseModel