Source: model-api-app.js

'use strict';

var express = require('express');
var mongoose = require('mongoose');
var ModelAPIRouter = require('./model-api-router');
var ModelAPIError = require('./model-api-error');
var controllers = require('./controllers');
var middlewares = require('./middlewares');

var path = require('path');

var readMiddlwares = require('./helpers').readMiddlwares;

/**
 * @class ModelAPIExpress - Express application that provides an RESTfull API for different models
 *
 * @param  {Object} [options]         API configuration parameters
 * @param  {String} options.title    the title of API <SWAGGER>
 * @param  {String} options.version  the version of API <SWAGGER>
 * @param  {String} options.path     the path of api root. The application doesn't mount it self on this path. <SWAGGER>
 * @param  {String} options.host     the host to reach API. <SWAGGER>
 * @param  {Boolean} [options.options]  allows or denies the OPTION end-point on the application level
 * @param  {Function} [options.transformResponse] the function to transform standard response. For details see Transform response
 * @return {ModelAPIExpress}         Created API application
 */
function ModelAPIExpress(options) {
  options = options || {};
  var app = express();
  app.__proto__ = ModelAPIExpress.prototype;
  app.$__ = {};
  app.$__.options = options;
  app._urls = [];
  app._urlsDefined = false;

  app.use(middlewares.registerApi(true, this));

  var transformResponse = options.transformResponse instanceof Function ?
    options.transformResponse :
    null
  ;

  if (transformResponse) {
    app.use(middlewares.transformResponse(transformResponse));
  }

  app.on('mount', app.__finalize.bind(app));

  var __originalListen = app.listen;
  app.listen = function() {
    app.__finalize();
    return __originalListen.apply(app, arguments);
  }

  return app;
};

ModelAPIExpress.prototype = Object.create(Function.prototype);
ModelAPIExpress.prototype.constructor = ModelAPIExpress;

/**
 * SWAGGER_SUPPORT indicates that the module version supports the swagger
 */
ModelAPIExpress.prototype.SWAGGER_SUPPORT = true;

/**
 * ModelAPIExpress.prototype.expose - exposes the mongoose model as RESTFull json service
 *
 * @param  {String} [path]               the path to mount exposed model
 * @param  {Function} [middleware]    one or more middlewares that should be called before each end-point controller
 * @param  {Mongoose.Model} model        Mongoosee model to be exposed
 * @param  {Object} [routerOptions]        ModelAPIRouter configuration object (see [Router configuration](configuration.html#router))
 * @return {ModelAPIExpress}             itself
 */
ModelAPIExpress.prototype.expose = function(path, middleware, model, routerOptions) {
  var options, si = 0, ml;
  path = arguments.length && arguments[0];
  if (typeof path === "string") {
    si = 1;
  } else {
    path = null;
  }
  var middlewares = readMiddlwares(arguments, si);
  ml = middlewares && middlewares.length || 0;

  model = arguments[ml + si];
  options = arguments[ml + si + 1] || {};
  if (path) { options.path = path }
  if (ml) { options.middlewares = middlewares }

  var router = new ModelAPIRouter(model, options);
  router.attachTo(this);
  return this;
};

/**
 * ModelAPIExpress.prototype.__attachOptions - attaches the options controller
 *
 * @private
 */
ModelAPIExpress.prototype.__attachOptions = function() {
  var app = this;
  var options = app.$__.options;
  if (options && !(options.options === false) && !options.optionsAttached) {
    app.options('/', controllers.options.bind(app));
    app._urls.push([
      'options', '/', 'List all end-points of current application'
    ]);
    options.optionsAttached = true;
  }
}

/**
 * ModelAPIExpress.prototype.urls - returns avaliable list of end-points
 *
 * @return {Array}  list of end-points
 */
ModelAPIExpress.prototype.urls = function() {
  if (this._urlsDefined) return this._urls;

  if (!this.__routers || !this.__routers.length) return [];

  this._urlsDefined = true;
  if (this.mountpath != '/') {
    for (var i=0; i<this._urls.length; i++) {
      this._urls[i][1] = path.join(this.mountpath, this._urls[i][1]);
    }
  }
  var router;
  for (var i=0; i<this.__routers.length; i++) {
    router = this.__routers[i];
    this._urls = this._urls.concat(router.urls(this.mountpath));
  }
  return this._urls;
}

/**
 * @private
 */
ModelAPIExpress.prototype.__addModelAPIrouter = function(router) {

  this.__routers = this.__routers || [];
  this.__routers.push(router);
}

/**
 * @private
 */
ModelAPIExpress.prototype.__finalize = function() {
  this.__attachOptions();
  this.use(controllers.notSupported);
  this.use(controllers.error);
  this.urls();
}

module.exports = exports = ModelAPIExpress;