'use strict'; //////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2013-2014 triAGENS GmbH, Cologne, Germany /// Copyright 2015-2016 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Lucas Dohmen /// @author Alan Plum //////////////////////////////////////////////////////////////////////////////// const RequestContext = require('@arangodb/foxx/legacy/request_context'); const BaseMiddleware = require('@arangodb/foxx/legacy/base_middleware').BaseMiddleware; const _ = require('lodash'); const is = require('@arangodb/is'); const internal = require('@arangodb/foxx/legacy/internals'); const swagger = require('@arangodb/foxx/legacy/swagger'); var authControllerProps = { //////////////////////////////////////////////////////////////////////////////// /// JSF_foxx_controller_getUsers /// @brief Get the users of this controller //////////////////////////////////////////////////////////////////////////////// getUsers() { const foxxAuthentication = require('@arangodb/foxx/legacy/authentication'); return new foxxAuthentication.Users(this.applicationContext); }, //////////////////////////////////////////////////////////////////////////////// /// JSF_foxx_controller_getAuth /// @brief Get the auth object of this controller //////////////////////////////////////////////////////////////////////////////// getAuth() { return this.auth; }, //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_login //////////////////////////////////////////////////////////////////////////////// login(route, opts) { var authentication = require('@arangodb/foxx/legacy/authentication'); return this.post(route, authentication.createStandardLoginHandler(this.getAuth(), this.getUsers(), opts)); }, //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_logout //////////////////////////////////////////////////////////////////////////////// logout(route, opts) { var authentication = require('@arangodb/foxx/legacy/authentication'); return this.post(route, authentication.createStandardLogoutHandler(this.getAuth(), opts)); }, //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_register //////////////////////////////////////////////////////////////////////////////// register(route, opts) { var authentication = require('@arangodb/foxx/legacy/authentication'); return this.post( route, authentication.createStandardRegistrationHandler(this.getAuth(), this.getUsers(), opts) ).errorResponse(authentication.UserAlreadyExistsError, 400, 'User already exists'); }, //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_changePassword //////////////////////////////////////////////////////////////////////////////// changePassword(route, opts) { var authentication = require('@arangodb/foxx/legacy/authentication'); return this.post(route, authentication.createStandardChangePasswordHandler(this.getUsers(), opts)); } }; var sessionControllerProps = { //////////////////////////////////////////////////////////////////////////////// /// JSF_foxx_controller_getSessions /// @brief Get the sessions object of this controller //////////////////////////////////////////////////////////////////////////////// getSessions() { return this.sessions; }, //////////////////////////////////////////////////////////////////////////////// /// @brief defines a route to logout/destroy the session //////////////////////////////////////////////////////////////////////////////// destroySession(route, opts) { var method = opts.method; if (typeof method === 'string') { method = method.toLowerCase(); } if (!method || typeof this[method] !== 'function') { method = 'post'; } var sessions = require('@arangodb/foxx/legacy/sessions'); return this[method](route, sessions.createDestroySessionHandler(this.getSessions(), opts)); } }; class Controller { //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_initializer //////////////////////////////////////////////////////////////////////////////// constructor(context, options) { this.currentPriority = 0; var urlPrefix, baseMiddleware; if (is.notExisty(context)) { throw new Error('parameter is missing'); } this.routingInfo = { routes: [] }; // Models for the Documentation this.models = {}; options = options || {}; urlPrefix = options.urlPrefix || ''; if (urlPrefix === '') { urlPrefix = context.prefix; } else if (context.prefix !== '') { urlPrefix = context.prefix + '/' + urlPrefix; } this.injected = Object.create(null); this.injectors = Object.create(null); this.routingInfo.urlPrefix = urlPrefix; this.collectionPrefix = context.collectionPrefix; this.extensions = {}; baseMiddleware = new BaseMiddleware(); this.routingInfo.middleware = [ { url: { match: '/*' }, action: { callback: baseMiddleware.functionRepresentation, options: { name: context.name, version: context.version, mount: context.mount, isDevelopment: context.isDevelopment, isProduction: context.isProduction, prefix: context.prefix, options: context.options } } } ]; this.allRoutes = new RequestContext.Buffer(); context.foxxes.push(this); if (is.existy(context.manifest.rootElement)) { this.rootElement = context.manifest.rootElement; } else { this.rootElement = false; } this.applicationContext = context; } addInjector(name, factory) { if (factory === undefined) { _.extend(this.injectors, name); } else { this.injectors[name] = factory; } } //////////////////////////////////////////////////////////////////////////////// /// /// The *handleRequest* method is the raw way to create a new route. You /// probably wont call it directly, but it is used in the other request methods: /// /// When defining a route you can also define a so called 'parameterized' route /// like */goose/:barn*. In this case you can later get the value the user /// provided for *barn* via the *params* function (see the Request object). //////////////////////////////////////////////////////////////////////////////// handleRequest(method, route, callback) { var constraints = {queryParams: {}, urlParams: {}}; var newRoute = internal.constructRoute(method, route, callback, this, constraints); var requestContext = new RequestContext( this.allRoutes, this.models, newRoute, route, this.rootElement, constraints, this.extensions ); this.routingInfo.routes.push(newRoute); if (method === 'post' || method === 'put' || method === 'patch') { const Model = require('@arangodb/foxx/legacy').Model; let UndocumentedBody = Model.extend({}); requestContext.bodyParam('undocumented body', { description: 'Undocumented body param', type: UndocumentedBody, allowInvalid: true }); } return requestContext; } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_head //////////////////////////////////////////////////////////////////////////////// head(route, callback) { return this.handleRequest('head', route, callback); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_get //////////////////////////////////////////////////////////////////////////////// get(route, callback) { return this.handleRequest('get', route, callback); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_post //////////////////////////////////////////////////////////////////////////////// post(route, callback) { return this.handleRequest('post', route, callback); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_put //////////////////////////////////////////////////////////////////////////////// put(route, callback) { return this.handleRequest('put', route, callback); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_patch //////////////////////////////////////////////////////////////////////////////// patch(route, callback) { return this.handleRequest('patch', route, callback); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_delete //////////////////////////////////////////////////////////////////////////////// delete(route, callback) { return this.handleRequest('delete', route, callback); } del(route, callback) { return this.delete(route, callback); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_before //////////////////////////////////////////////////////////////////////////////// before(path, func) { if (is.notExisty(func)) { func = path; path = '/*'; } this.routingInfo.middleware.push({ priority: this.currentPriority = this.currentPriority + 1, url: {match: path}, action: { callback(req, res, opts, next) { var result = func(req, res, opts); if (result !== false) { next(); } } } }); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_after //////////////////////////////////////////////////////////////////////////////// after(path, func) { if (is.notExisty(func)) { func = path; path = '/*'; } this.routingInfo.middleware.push({ priority: this.currentPriority = this.currentPriority + 1, url: {match: path}, action: { callback(req, res, opts, next) { next(); func(req, res, opts); } } }); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_around //////////////////////////////////////////////////////////////////////////////// around(path, func) { if (is.notExisty(func)) { func = path; path = '/*'; } this.routingInfo.middleware.push({ priority: this.currentPriority = this.currentPriority + 1, url: {match: path}, action: { callback(req, res, opts, next) { func(req, res, opts, next); } } }); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_activateAuthentication //////////////////////////////////////////////////////////////////////////////// activateAuthentication(opts) { var authentication = require('@arangodb/foxx/legacy/authentication'); _.extend(this, authControllerProps); this.auth = authentication.createAuthObject(this.applicationContext, opts); this.before('/*', authentication.createAuthenticationMiddleware(this.auth, this.applicationContext)); this.after('/*', authentication.createSessionUpdateMiddleware()); } //////////////////////////////////////////////////////////////////////////////// /// @brief activate sessions with the giveon options for this controller //////////////////////////////////////////////////////////////////////////////// activateSessions(opts) { var sessions = require('@arangodb/foxx/legacy/sessions'); _.extend(this, sessionControllerProps); this.sessions = new sessions.Sessions(opts); sessions.decorateController(this.sessions, this); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_apiDocumentation //////////////////////////////////////////////////////////////////////////////// apiDocumentation(route, opts) { if (route.charAt(route.length - 1) !== '/') { route += '/'; } var mountPath = this.applicationContext.mount; return this.get(route + '*', swagger.createSwaggerRouteHandler(mountPath, opts)); } //////////////////////////////////////////////////////////////////////////////// /// @brief was docuBlock JSF_foxx_controller_extend //////////////////////////////////////////////////////////////////////////////// extend(extensions) { var attr; var extensionWrapper = function(scope, functionName) { return function() { this.applyChain.push({ functionName: functionName, argumentList: arguments }); return this; }.bind(scope); }; for (attr in extensions) { if (extensions.hasOwnProperty(attr)) { this.extensions[attr] = extensions[attr]; this.allRoutes[attr] = extensionWrapper(this.allRoutes, attr); } } } } exports.Controller = Controller;