/*global require, applicationContext, parseInt*/ //////////////////////////////////////////////////////////////////////////////// /// @brief A Foxx.Controller to show all Foxx Applications /// /// @file /// /// DISCLAIMER /// /// Copyright 2010-2013 triagens 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 triAGENS GmbH, Cologne, Germany /// /// @author Michael Hackstein /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// "use strict"; // Initialise a new FoxxController called controller under the urlPrefix: "foxxes". var FoxxController = require("org/arangodb/foxx").Controller, controller = new FoxxController(applicationContext), ArangoError = require("org/arangodb").ArangoError, underscore = require("underscore"), joi = require("joi"), notifications = require("org/arangodb/configuration").notifications, db = require("internal").db, foxxInstallKey = joi.string().required().description( "The _key attribute, where the information of this Foxx-Install is stored." ), appname = joi.string().required(); var foxxes = new (require("lib/foxxes").Foxxes)(); var FoxxManager = require("org/arangodb/foxx/manager"); var docus = new (require("lib/swagger").Swagger)(); var sortVersions = function(a, b) { var left = a.split(".").map(function(c) { return parseInt(c, 10); }); var right = b.split(".").map(function(c) { return parseInt(c, 10); }); if (left[0] === right[0]) { if (left[1] === right[1]) { if (left[2] < right[2]) { return 1; } if (left[2] < right[2]) { return -1; } return 0; } if (left[1] < right[1]) { return 1; } return -1; } if (left[0] < right[0]) { return 1; } return -1; }; var installFromGithub = function(res, url, name, version) { var appID = FoxxManager.fetchFromGithub(url, name, version); var app = db._aal.firstExample({"app": appID}); res.json({ error: false, configuration: app.manifest.configuration || {}, app: appID, name: app.name, version: app.version }); }; controller.get("/whoAmI", function(req, res) { res.json({ name: req.user }); }); controller.get("/unauthorized", function(req, res) { throw new ArangoError(); }).errorResponse(ArangoError, 401, "unauthorized"); /** Is version check allowed * * Check if version check is allowed */ controller.get("shouldCheckVersion", function(req, res) { var versions = notifications.versions(); if (!versions || versions.enableVersionNotification === false) { res.json(false); } else { res.json(true); } }); /** Disable version check * * Disable the version check in web interface */ controller.post("disableVersionCheck", function(req, res) { notifications.setVersions({ enableVersionNotification: false }); res.json("ok"); }); /** Fetch a foxx from temp folder * * Makes a foxx uploaded to the temp folder * available for mounting. */ controller.post("/foxxes/inspect", function (req, res) { var content = JSON.parse(req.requestBody), path = content.filename; res.json(foxxes.inspect(path)); }).errorResponse(ArangoError, 500, "No valid app"); // ............................................................................. // install // ............................................................................. controller.put("/foxxes/install", function (req, res) { var content = JSON.parse(req.requestBody), name = content.name, mount = content.mount, version = content.version, options = content.options; res.json(foxxes.install(name, mount, version, options)); }).summary("Installs a new foxx") .notes("This function is used to install a new foxx."); /** Install a Foxx from fishbowl * * Downloads a Foxx from the fishbowl and returns the required configuration for this foxx. */ controller.post("/foxxes/fishbowlinstall", function (req, res) { var content = JSON.parse(req.requestBody), name = content.name, version = content.version, url, appInfo = FoxxManager.getFishbowlStorage().firstExample({ name: name }); if (version !== undefined) { if (appInfo.versions.hasOwnProperty(version)) { url = appInfo.versions[version].location; } else { throw "Banana"; } } else { var keys = Object.keys(appInfo.versions); if (keys.length > 1) { keys = keys.sort(sortVersions); } url = appInfo.versions[keys[0]].location; } installFromGithub(res, url, name, "v" + version); }); /** Install a Foxx from Github * * Install a Foxx with URL and ..... */ controller.post("/foxxes/gitinstall", function (req, res) { var content = JSON.parse(req.requestBody), name = content.name, url = content.url, version = content.version; installFromGithub(res, url, name, version); }); /** Remove an uninstalled Foxx * * Remove the Foxx with the given key. */ controller.del("/foxxes/purge/:key", function (req, res) { res.json(FoxxManager.purge(req.params("key"))); }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("Remove a Foxx.") .notes("This function is used to remove a foxx."); /** Remove all uninstalled Foxx versions * * Remove the Foxx with the given array of versions app ids. */ controller.del("/foxxes/purgeall/:key", function (req, res) { var name = req.params("key"); var allFoxxes = foxxes.viewAll(); var toDelete = []; underscore.each(allFoxxes, function(myFoxx) { if (myFoxx.name === name) { toDelete.push(myFoxx.app); } }); underscore.each(toDelete, function(myFoxx) { FoxxManager.purge(myFoxx); }); res.json({res: true}); }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("Remove a all existing Foxx versions .") .notes("This function is used to remove all versions of a foxx."); /** Get info about github information of an available Foxx * * Returns github information of Foxx */ controller.get("/foxxes/gitinfo/:key", function (req, res) { res.json(FoxxManager.gitinfo(req.params("key"))); }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("List git information of a Foxx") .notes("This function is used to display all available github information of a foxx"); /** Get info about mount points of an installed Foxx * * Returns mount points of Foxx */ controller.get("/foxxes/mountinfo/:key", function (req, res) { res.json(FoxxManager.mountinfo(req.params("key"))); }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("List mount points of a Foxx") .notes("This function is used to display all available mount points of a foxx"); /** Get info about all mount points of all installed Foxx * * Returns mount points of all Foxxes */ controller.get("/foxxes/mountinfo/", function (req, res) { res.json(FoxxManager.mountinfo()); }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("List mount points of all Foxx") .notes("This function is used to display all available mount points of all foxxes"); /** Uninstall a Foxx * * Uninstall the Foxx with the given key. */ controller.del("/foxxes/:key", function (req, res) { res.json(foxxes.uninstall(req.params("key"))); }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("Uninstall a Foxx.") .notes("This function is used to uninstall a foxx."); /** Update a Foxx * * Update the Foxx with the given information. */ controller.put("/foxxes/:key", function (req, res) { var content = JSON.parse(req.requestBody), active = content.active; // TODO: Other changes applied to foxx! e.g. Mount if (active) { res.json(foxxes.activate()); } else { res.json(foxxes.deactivate()); } }).pathParam("key", { type: foxxInstallKey, allowMultiple: false }).summary("Update a foxx.") .notes("Used to either activate/deactivate a foxx, or change the mount point."); /** Get the thubmail of a Foxx * * Request the Thumbnail stored for a Foxx */ controller.get("/foxxes/thumbnail/:app", function (req, res) { res.transformations = [ "base64decode" ]; res.body = foxxes.thumbnail(req.params("app")); // evil mimetype detection attempt... var start = require("internal").base64Decode(res.body.substr(0, 8)); if (start.indexOf("PNG") !== -1) { res.contentType = "image/png"; } }).pathParam("app", { type: appname.description( "The appname which is used to identify the foxx in the list of available foxxes." ), allowMultiple: false }).summary("Get the thumbnail of a foxx.") .notes("Used to request the thumbnail of the given Foxx in order to display it on the screen."); /** List all Foxxes * * Get a List of all Foxxes available and running * */ controller.get('/foxxes', function (req, res) { res.json(foxxes.viewAll()); }).summary("List of all foxxes.") .notes("This function simply returns the list of all running foxxes"); /** List all Foxxes in Fishbowl * * Get the information for all Apps availbale in the Fishbowl and ready for download * */ controller.get('/foxxes/fishbowl', function (req, res) { // FoxxManager.update(); res.json(FoxxManager.availableJson()); }).summary("List of all foxx apps submitted to the fishbowl store.") .notes("This function contacts the fishbowl and reports which apps are available for install"); /** List available Documentation * * Get the list of all running Foxxes with links to their documentation */ controller.get('/docus', function (req, res) { res.json(docus.list(req.protocol + "://" + req.headers.host + "/_db/" + req.database + req.path + "/")); }).summary("List documentation of all foxxes.") .notes("This function simply returns the list of all running" + " foxxes and supplies the paths for the swagger documentation"); /** Get Documentation for one Foxx * * Get the complete documentation available for one Foxx * */ controller.get("/docu/:key",function (req, res) { var subPath = req.path.substr(0, req.path.lastIndexOf("[") - 1), key = req.params("key"), path = req.protocol + "://" + req.headers.host + "/_db/" + encodeURIComponent(req.database) + subPath + "/" + encodeURIComponent(key) + "/"; res.json(docus.listOne(path, key)); }).summary("List documentation of one foxxes.") .notes("This function simply returns one specific" + " foxx and supplies the paths for the swagger documentation"); /** Subroutes for API Documentation * * Get the Elements of the API Documentation subroutes * */ controller.get('/docu/:key/*', function(req, res) { var mountPoint = ""; underscore.each(req.suffix, function(part) { mountPoint += "/" + part; }); res.json(docus.show(mountPoint)); }).summary("List the API for one foxx") .notes("This function lists the API of the foxx" + " running under the given mount point"); /** Subroutes for API Documentation * * Get the Elements of the API Documentation subroutes * */ controller.get('/swagger/:mount', function(req, res) { var subPath = req.path.substr(0, req.path.lastIndexOf("[") - 1), mount = decodeURIComponent(req.params("mount")), path = req.protocol + "://" + req.headers.host + "/_db/" + encodeURIComponent(req.database) + subPath + "/" + encodeURIComponent(mount) + "/", candidate = db._aal.firstExample({ mount: mount }); if (candidate === null) { throw "no entry found for mount"; } res.json(docus.show(mount)); // res.json(docus.listOneForMount(path, mount)); }).summary("Returns the generated Swagger JSON description for one foxx") .notes("This function returns the Swagger JSON API description of the foxx" + " running under the given mount point"); /** Move Foxx to other Mount * * Move a running Foxx from one mount point to another * */ controller.put('/foxx/move/:key', function(req, res) { var body = req.body(); var mountPoint = body.mount; var app = body.app; var key = req.params("key"); var prefix = body.prefix; var result = foxxes.move(key, app, mountPoint, prefix); if (result.error) { res.status(result.status); res.body = result.message; return; } res.json(result); }) .summary("Move one foxx to another moint point") .notes ("This function moves one installed foxx" + " to a given mount point."); /** Download stored queries * * Download and export all queries from the given username. * */ controller.post("/query/upload/:user", function(req, res) { var user = req.params("user"); var response, queries, userColl, storedQueries, queriesToSave; queries = req.body(); userColl = db._users.byExample({"user": user}).toArray()[0]; queriesToSave = userColl.userData.queries || [ ]; underscore.each(queries, function(newq) { var found = false, i; for (i = 0; i < queriesToSave.length; ++i) { if (queriesToSave[i].name === newq.name) { queriesToSave[i] = newq; found = true; break; } } if (! found) { queriesToSave.push(newq); } }); var toUpdate = { userData: { queries: queriesToSave } } var result = db._users.update(userColl, toUpdate, true); res.json(result); }).summary("Upload user queries") .notes("This function uploads all given user queries"); /** Download stored queries * * Download and export all queries from the given username. * */ controller.get("/query/download/:user", function(req, res) { var user = req.params("user"); var result = db._users.byExample({"user": user}).toArray()[0]; res.set("Content-Type", "application/json"); res.set("Content-Disposition", "attachment; filename=queries.json"); res.json(result.userData.queries); }).summary("Download all user queries") .notes("This function downloads all user queries from the given user"); /** Download a query result * * Download and export all queries from the given username. * */ controller.get("/query/result/download/:query", function(req, res) { var query = req.params("query"), parsedQuery; var internal = require("internal"); query = internal.base64Decode(query); try { parsedQuery = JSON.parse(query); } catch (ignore) { } var result = db._query(parsedQuery.query, parsedQuery.bindVars).toArray(); res.set("Content-Type", "application/json"); res.set("Content-Disposition", "attachment; filename=results.json"); res.json(result); }).summary("Download the result of a query") .notes("This function downloads the result of a user query."); // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- /// Local Variables: /// mode: outline-minor /// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint" /// End: