UNPKG

composr-core

Version:
301 lines (246 loc) 8.93 kB
'use strict' var phraseValidator = require('../validators/phrase.validator') var PhraseModel = require('../models/PhraseModel') var phraseDao = require('../daos/phraseDao') var BaseManager = require('./base.manager') var queryString = require('query-string') // var ComposrError = require('../ComposrError') // var MetricsFirer = require('../MetricsFirer') var phrasesStore = require('../stores/phrases.store') // var parseToComposrError = require('../parseToComposrError') var WrappedResponse = require('../mock').res var WrappedRequest = require('../mock').req var corbel = require('corbel-js') var utils = require('../utils') var _ = require('lodash') var PhraseManager = function PhraseManager (options) { this.events = options.events this.requirer = options.requirer this.config = options.config || {} } PhraseManager.prototype = new BaseManager({ itemName: 'phrase', store: phrasesStore, model: PhraseModel, dao: phraseDao, validator: phraseValidator }) PhraseManager.prototype.configure = function configure (config) { this.config = { urlBase: config.urlBase } } PhraseManager.prototype.__preAdd = function __preAdd (domain, phraseModel) { var phrasesWithTheSamePath = this._filterByRegexp(domain, phraseModel.getRegexp()) if (phrasesWithTheSamePath.length > 0) { this.events.emit('warn', 'phrase:path:duplicated', phraseModel.getId()) } } PhraseManager.prototype._compile = function _compile (domain, phrase) { try { var PhraseModel = this.model var phraseInstance = new PhraseModel(phrase, domain) phraseInstance.compile(this.events) this.events.emit('debug', 'phrase:compiled', phraseInstance.getId(), Object.keys(phraseInstance.compiled.codes)) return phraseInstance } catch (e) { // Somehow it has tried to compile an invalid phrase. Notify it and return false. // Catching errors and returning false here is important for not having an unstable phrases stack. this.events.emit('errore', 'phrase:not:usable', phrase.url, e) return false } } // Executes a phrase by id PhraseManager.prototype.runById = function runById (id, verb, params, cb) { if (utils.values.isFalsy(verb)) { verb = 'get' } var parameters = getDefaultPhraseParams(params) var phrase = this.getById(id) if (phrase && phrase.canRun(verb)) { var domain = this._extractDomainFromId(id) this._run(phrase, verb, parameters, domain, cb) } else { // @TODO: See if we want to return that error directly or a wrappedResponse with 404 status (or invalid VERB) cb('phrase:cant:be:runned') } } // Executes a phrase matching a path PhraseManager.prototype.runByPath = function runByPath (domain, path, verb, params, version, cb) { if (utils.values.isFalsy(verb)) { verb = 'get' } var phrase = this.getByMatchingPath(domain, path, verb, version) if (phrase) { var parameters = getDefaultPhraseParams(params) var queryParamsString = path.indexOf('?') !== -1 ? path.substring(path.indexOf('?'), path.length) : '' if (!parameters.query && !(parameters.req && parameters.req.query && Object.keys(parameters.req.query).length > 0)) { // If no reqQuery object or req.querty params are sent, extract them parameters.query = queryString.parse(queryParamsString) } if (!parameters.params) { // extract params from path var sanitizedPath = path.replace(queryParamsString, '') parameters.params = phrase.extractParamsFromPath(sanitizedPath) } this._run(phrase, verb, parameters, domain, cb) } else { cb('phrase:cant:be:runned') } } function getDefaultPhraseParams (options) { var defaultParameters = { functionMode: true, query: null, params: null, headers: null, corbelDriver: null, domain: null, timeout: null, body: null } return Object.assign({}, defaultParameters, options) } /* Fills the sandbox with parameters */ /* function buildSandbox (options, urlBase, domain, requirer, reqWrapper, resWrapper, version) { // The object that will be inject on the phrase itself. var sb = { req: reqWrapper, res: resWrapper, // next: nextWrapper.resolve, domain: domain, config: { urlBase: urlBase }, // metrics: new MetricsFirer(domain) metrics: null } // sb.require = options.functionMode ? requirer.forDomain(domain, version, true) : requirer.forDomain(domain, version) // sb.require = requirer(domain, version, options.functionMode) if (!options.corbelDriver && reqWrapper.get('Authorization')) { sb.corbelDriver = corbel.getDriver({ urlBase: urlBase, iamToken: { accessToken: reqWrapper.get('Authorization') }, domain: domain }) } else { sb.corbelDriver = options.corbelDriver } return sb }*/ PhraseManager.prototype._run = function _runPhrase (phrase, verb, params, domain, cb) { this.events.emit('debug', 'running:phrase:' + phrase.getId() + ':' + verb) var urlBase = params.config && params.config.urlBase ? params.config.urlBase : this.config.urlBase var resWrapper = new WrappedResponse(params.res) var reqWrapper = new WrappedRequest(params.req, params) // Fill the sandbox params var sandbox = { req: reqWrapper, res: resWrapper, // next: params.next, require: this.requirer(domain, phrase.getVersion(), params.functionMode), domain: domain, config: { urlBase: urlBase } } if (!params.corbelDriver && reqWrapper.get('Authorization')) { sandbox.corbelDriver = corbel.getDriver({ urlBase: urlBase, iamToken: { accessToken: reqWrapper.get('Authorization') }, domain: domain }) } else { sandbox.corbelDriver = params.corbelDriver } sandbox.res.on('end', function (resp) { cb(null, resp) }) // Execute the phrase if (params.functionMode) { phrase.__executeFunctionMode(verb, sandbox, params.timeout, params.file) } else { phrase.__executeScriptMode(verb, sandbox, params.timeout, params.file, this.events) /* try { } catch (e) { // console.log(e) // @TODO this errors can be: // - corbel errors // - Any thrown error in phrase // How do we handle it? if (params.functionMode) { // Function mode only throws an error when errored this.events.emit('warn', 'phrase:internal:error', e, phrase.getUrl()) var error = parseToComposrError(e, 'error:phrase:exception:' + phrase.getUrl()) sandbox.res.send(error.status, error) } else { // vm throws an error when timedout } }*/ } } // Returns a list of elements matching the same regexp PhraseManager.prototype._filterByRegexp = function _filterByRegexp (domain, regexp) { var candidates = this.store.getAsList(domain) return _.filter(candidates, function (candidate) { return candidate.getRegexp() === regexp }) } // Get all the phrases, or all the phrases for one domain PhraseManager.prototype.getPhrases = function getPhrases (domain) { return this.store.getAsList(domain) } /** CORE Entry point. One of the purposes of composr-core is to provide a fast and reliable getByMatchingPath method. **/ PhraseManager.prototype.getByMatchingPath = function getByMatchingPath (domain, path, verb, version) { var candidate = null if (!verb) { verb = 'get' } domain = utils.values.isFalsy(domain) ? null : domain this.events.emit('debug', 'phrase:getByMatchingPath:' + domain + ':' + path + ':' + verb) if (utils.values.isFalsy(path)) { this.events.emit('error', 'phrase:getByMatchingPath:path:undefined') return candidate } var queryParamsString = path.indexOf('?') !== -1 ? path.substring(path.indexOf('?'), path.length) : '' path = path.replace(queryParamsString, '') if (domain === null) { this.events.emit('warn', 'phrase:getByMatchingPath:noDomain:matchingAgainstAll:expensiveMethod') } var candidates = this.store.getAsList(domain) this.events.emit('debug', 'evaluating:' + candidates.length + ':candidates') candidates = _.compact(candidates.map(function (phrase) { if (phrase.canRun(verb) && phrase.matchesPath(path)) { if (!version || (version && phrase.getVersion() === version)) { return phrase } } })) this.events.emit('debug', 'found:' + candidates.length + ':candidates') if (candidates.length === 0) { this.events.emit('debug', 'notfound:candidates:path:' + path + ':' + verb) return candidate } else { candidate = candidates[0] this.events.emit('debug', 'using:candidate:' + candidate.getId() + ':' + verb) return candidate } } // Counts all the loaded phrases PhraseManager.prototype.count = function count () { return this.store.getAsList().length } // TODO: Remove for using the MD5 check defined in BaseManager PhraseManager.prototype.__shouldSave = function __shouldSave () { return true } module.exports = PhraseManager