From ba3a3914fa62e0dd949a23076a42fa16c5a42a3b Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 13 Jun 2014 09:49:33 +0200 Subject: [PATCH 01/13] Included change of existing edge definition in graph-http-module --- js/apps/system/gharial/gharial.js | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/js/apps/system/gharial/gharial.js b/js/apps/system/gharial/gharial.js index 9eea190f85..d46ff2342f 100644 --- a/js/apps/system/gharial/gharial.js +++ b/js/apps/system/gharial/gharial.js @@ -261,6 +261,45 @@ } ); + /** Replace an edge definition. + * + * Replaces an existing edge definition with the information contained + * within the body. + * This has to contain the edge-collection name, as well as set of from and to + * collections-names respectively. + * This will also change the edge definitions of all other graphs using this + * definition as well. + */ + controller.put("/:graph/edge/:definition", function(req, res) { + var name = req.params("graph"); + var def_name = req.params("definition"); + var body = req.params("edgeDefinition"); + var g = Graph._graph(name); + if (def_name !== body.get("collection")) { + var err = new ArangoError(); + err.errorNum = errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code; + err.errorMessage = errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message; + throw err; + } + g._editEdgeDefinitions(body.forDB()); + setGraphResponse(res, g); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .bodyParam( + "edgeDefinition", "The edge definition to be stored.", Model + ) + .errorResponse( + ArangoError, actions.HTTP_BAD, "The edge definition is invalid.", function(e) { + return { + code: actions.HTTP_BAD, + error: e.errorMessage + }; + } + ); + /** Create a new edge. * * Stores a new edge with the information contained From ecc0768bf30e0fa83b14985314e223d62d5c2812 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 13 Jun 2014 09:56:07 +0200 Subject: [PATCH 02/13] Fixed JSLint warnings --- js/server/modules/org/arangodb/foxx.js | 3 ++- js/server/modules/org/arangodb/foxx/controller.js | 2 +- js/server/modules/org/arangodb/foxx/request_context.js | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/js/server/modules/org/arangodb/foxx.js b/js/server/modules/org/arangodb/foxx.js index 6562c8a9c8..e2850a5f35 100644 --- a/js/server/modules/org/arangodb/foxx.js +++ b/js/server/modules/org/arangodb/foxx.js @@ -37,7 +37,8 @@ var Controller = require("org/arangodb/foxx/controller").Controller, exports.Controller = Controller; exports.Model = Model; exports.Repository = Repository; -exports.requireApp = function(path) { +exports.requireApp = function (path) { + "use strict"; return manager.mountedApp(arangodb.normalizeURL('/' + path)); }; diff --git a/js/server/modules/org/arangodb/foxx/controller.js b/js/server/modules/org/arangodb/foxx/controller.js index 597c8821ce..9cee1b57b0 100644 --- a/js/server/modules/org/arangodb/foxx/controller.js +++ b/js/server/modules/org/arangodb/foxx/controller.js @@ -116,7 +116,7 @@ Controller = function (context, options) { context.foxxes.push(this); if (is.existy(context.manifest.rootElement)) { - this.rootElement =context.manifest.rootElement; + this.rootElement = context.manifest.rootElement; } else { this.rootElement = false; } diff --git a/js/server/modules/org/arangodb/foxx/request_context.js b/js/server/modules/org/arangodb/foxx/request_context.js index 9e0bac5c50..55b5589ab0 100644 --- a/js/server/modules/org/arangodb/foxx/request_context.js +++ b/js/server/modules/org/arangodb/foxx/request_context.js @@ -40,7 +40,8 @@ var RequestContext, createBodyParamBubbleWrap, addCheck; -var elementExtractFactory = function(paramName, rootElement) { +var elementExtractFactory = function (paramName, rootElement) { + "use strict"; var extractElement; if (rootElement) { From 7541149200389966263ceaf85318e0d7c6b61b58 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 13 Jun 2014 15:11:59 +0200 Subject: [PATCH 03/13] Added functions to list all graphs and all vertex/edge collections of a specific graph to http-api --- .../HttpInterface/api-general-graph-spec.rb | 67 ++- .../js/modules/org/arangodb/general-graph.js | 98 ++++- js/apps/system/gharial/gharial.js | 380 ++++++++++++------ .../modules/org/arangodb/general-graph.js | 16 +- 4 files changed, 432 insertions(+), 129 deletions(-) diff --git a/UnitTests/HttpInterface/api-general-graph-spec.rb b/UnitTests/HttpInterface/api-general-graph-spec.rb index c1e39e5462..441e78fb76 100644 --- a/UnitTests/HttpInterface/api-general-graph-spec.rb +++ b/UnitTests/HttpInterface/api-general-graph-spec.rb @@ -47,6 +47,19 @@ def delete_edge_definition (graph_name, definition_name) return doc end +def additional_vertex_collection (graph_name, collection_name) + cmd = URLPREFIX + "/" + graph_name + "/vertex" + body = { :collection => collection_name } + doc = ArangoDB.post(cmd, :body => JSON.dump(body)) + return doc +end + +def delete_vertex_collection (graph_name, collection_name) + cmd = vertex_endpoint(graph_name, collection_name) + doc = ArangoDB.delete(cmd) + return doc +end + def create_vertex (graph_name, collection, body) cmd = vertex_endpoint(graph_name, collection) doc = ArangoDB.post(cmd, :body => JSON.dump(body)) @@ -153,7 +166,7 @@ describe ArangoDB do doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(201) doc.parsed_response['graph']['name'].should eq(graph_name) - # doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['graph']['edgeDefinitions'].should eq(edge_definition) end @@ -166,7 +179,7 @@ describe ArangoDB do doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(201) doc.parsed_response['graph']['name'].should eq(graph_name) - # doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['graph']['edgeDefinitions'].should eq(edge_definition) end @@ -181,7 +194,7 @@ describe ArangoDB do doc.code.should eq(200) doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(200) - doc.parsed_response['graph']['name'].should eq("#{graph_name}") + doc.parsed_response['graph']['name'].should eq(graph_name) doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['graph']['edgeDefinitions'].should eq(edge_definition) end @@ -196,10 +209,54 @@ describe ArangoDB do doc.code.should eq(200) doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(200) - doc.parsed_response['graph']['name'].should eq("#{graph_name}") + doc.parsed_response['graph']['name'].should eq(graph_name) doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['graph']['edgeDefinitions'].should eq(edge_definition) - + end + + it "can delete an edge definition" do + first_def = { "collection" => friend_collection, "from" => [user_collection], "to" => [user_collection] } + edge_definition = [first_def] + create_graph( graph_name, edge_definition ) + doc = delete_edge_definition( graph_name, friend_collection ) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['graph']['name'].should eq(graph_name) + doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['graph']['edgeDefinitions'].should eq([]) + end + + it "can add an additional orphan collection" do + first_def = { "collection" => friend_collection, "from" => [user_collection], "to" => [user_collection] } + edge_definition = [first_def] + create_graph( graph_name, edge_definition ) + doc = additional_vertex_collection( graph_name, product_collection ) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['graph']['name'].should eq(graph_name) + doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['graph']['edgeDefinitions'].should eq(edge_definition) + doc.parsed_response['graph']['orphanCollections'].should eq([product_collection]) + end + + it "can delete an orphan collection" do + first_def = { "collection" => friend_collection, "from" => [user_collection], "to" => [user_collection] } + edge_definition = [first_def] + create_graph( graph_name, edge_definition ) + additional_vertex_collection( graph_name, product_collection ) + doc = delete_vertex_collection( graph_name, product_collection ) + + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['graph']['name'].should eq(graph_name) + doc.parsed_response['graph']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['graph']['edgeDefinitions'].should eq(edge_definition) + doc.parsed_response['graph']['orphanCollections'].should eq([]) end it "can delete a graph again" do diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js index 9ca319f715..39c005ef33 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js @@ -120,6 +120,22 @@ var findOrCreateCollectionsByEdgeDefinitions = function (edgeDefinitions, noCrea ]; }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief find or create a collection by name +//////////////////////////////////////////////////////////////////////////////// + +var findOrCreateOrphanCollections = function (graphName, orphanCollections, noCreate) { + var returnVals = []; + if (!orphanCollections) { + orphanCollections = []; + } + orphanCollections.forEach(function (e) { + findOrCreateCollectionByName(e, ArangoCollection.TYPE_DOCUMENT, noCreate); + returnVals.push(db[e]); + }); + return returnVals; +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief internal function to get graphs collection //////////////////////////////////////////////////////////////////////////////// @@ -1387,6 +1403,15 @@ var _directedRelationDefinition = function ( }; }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a list of all graph names +//////////////////////////////////////////////////////////////////////////////// + +var _list = function() { + var gdb = getGraphCollection(); + return _.pluck(gdb.toArray(), "_key"); +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief create a list of edge definitions //////////////////////////////////////////////////////////////////////////////// @@ -1425,8 +1450,11 @@ var _extendEdgeDefinitions = function (edgeDefinition) { //////////////////////////////////////////////////////////////////////////////// -var _create = function (graphName, edgeDefinitions) { +var _create = function (graphName, edgeDefinitions, orphanCollections) { + if (!orphanCollections) { + orphanCollections = []; + } var gdb = getGraphCollection(), err, graphAlreadyExists = true, @@ -1497,13 +1525,18 @@ var _create = function (graphName, edgeDefinitions) { } collections = findOrCreateCollectionsByEdgeDefinitions(edgeDefinitions, false); + orphanCollections.forEach( + function(oC) { + findOrCreateCollectionByName(oC, ArangoCollection.TYPE_DOCUMENT); + } + ); gdb.save({ 'edgeDefinitions' : edgeDefinitions, '_key' : graphName }); - return new Graph(graphName, edgeDefinitions, collections[0], collections[1]); + return new Graph(graphName, edgeDefinitions, collections[0], collections[1], orphanCollections); }; @@ -1701,7 +1734,10 @@ var createHiddenProperty = function(obj, name, value) { /// @endDocuBlock /// //////////////////////////////////////////////////////////////////////////////// -var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollections) { +var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollections, orphanCollections) { + if (!orphanCollections) { + orphanCollections = []; + } var self = this; // Create Hidden Properties createHiddenProperty(this, "__name", graphName); @@ -1710,7 +1746,7 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti createHiddenProperty(this, "__edgeDefinitions", edgeDefinitions); createHiddenProperty(this, "__idsToRemove", []); createHiddenProperty(this, "__collectionsToLock", []); - createHiddenProperty(this, "__orphanCollections", []); + createHiddenProperty(this, "__orphanCollections", orphanCollections); // fills this.__idsToRemove and this.__collectionsToLock var removeEdge = function (edgeId, options) { @@ -1903,7 +1939,7 @@ var Graph = function(graphName, edgeDefinitions, vertexCollections, edgeCollecti var _graph = function(graphName) { var gdb = getGraphCollection(), - g, collections; + g, collections, orphanCollections; try { g = gdb.document(graphName); @@ -1919,8 +1955,12 @@ var _graph = function(graphName) { } collections = findOrCreateCollectionsByEdgeDefinitions(g.edgeDefinitions, true); + orphanCollections = g.orphanCollections; + if (!orphanCollections) { + orphanCollections = []; + } - return new Graph(graphName, g.edgeDefinitions, collections[0], collections[1]); + return new Graph(graphName, g.edgeDefinitions, collections[0], collections[1], orphanCollections); }; //////////////////////////////////////////////////////////////////////////////// @@ -2033,7 +2073,11 @@ Graph.prototype._edgeCollections = function() { //////////////////////////////////////////////////////////////////////////////// Graph.prototype._vertexCollections = function() { - return _.values(this.__vertexCollections); + var orphans = []; + _.each(this.__orphanCollections, function(o) { + orphans.push(db[o]); + }); + return _.union(_.values(this.__vertexCollections), orphans); }; //////////////////////////////////////////////////////////////////////////////// @@ -2483,6 +2527,12 @@ Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) { this.__edgeCollections[edgeDefinition.collection] = db[edgeDefinition.collection]; edgeDefinition.from.forEach( function(vc) { + //remove from __orphanCollections + var orphanIndex = self.__orphanCollections.indexOf(vc); + if (orphanIndex !== -1) { + self.__orphanCollections.splice(orphanIndex, 1); + } + //push into __vertexCollections if (self.__vertexCollections[vc] === undefined) { self.__vertexCollections[vc] = db[vc]; } @@ -2490,6 +2540,12 @@ Graph.prototype._extendEdgeDefinitions = function(edgeDefinition) { ); edgeDefinition.to.forEach( function(vc) { + //remove from __orphanCollections + var orphanIndex = self.__orphanCollections.indexOf(vc); + if (orphanIndex !== -1) { + self.__orphanCollections.splice(orphanIndex, 1); + } + //push into __vertexCollections if (self.__vertexCollections[vc] === undefined) { self.__vertexCollections[vc] = db[vc]; } @@ -2529,6 +2585,8 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) var self = this; var dropCandidates; var currentEdgeDefinition = {}; + var exOrphanCandidates = []; + var effectedGraphs = []; //check, if in graphs edge definition @@ -2552,6 +2610,7 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) eDs[id].from = edgeDefinition.from; eDs[id].to = edgeDefinition.to; db._graphs.update(graph._key, {edgeDefinitions: eDs}); + effectedGraphs.push(graph._key); if (graph._key === self.__name) { self.__edgeDefinitions[id].from = edgeDefinition.from; self.__edgeDefinitions[id].to = edgeDefinition.to; @@ -2584,11 +2643,11 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) ); } - //push "new" collections into vertexCollections edgeDefinition.from.forEach( function(vc) { if (self.__vertexCollections[vc] === undefined) { + exOrphanCandidates.push(vc); self.__vertexCollections[vc] = db[vc]; } } @@ -2596,6 +2655,7 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) edgeDefinition.to.forEach( function(vc) { if (self.__vertexCollections[vc] === undefined) { + exOrphanCandidates.push(vc); self.__vertexCollections[vc] = db[vc]; } } @@ -2622,6 +2682,26 @@ Graph.prototype._editEdgeDefinitions = function(edgeDefinition, dropCollections) } ); + //orphans treatment + effectedGraphs.forEach( + function(gN) { + var g; + if (gN === self.__name) { + g = self; + } else { + g = _graph(gN); + } + var orphans = g._getOrphanCollections(); + exOrphanCandidates.forEach( + function(eOC) { + if (orphans.indexOf(eOC) !== -1) { + g._removeOrphanCollection(eOC); + } + } + ); + } + ); + }; @@ -2813,6 +2893,7 @@ Graph.prototype._removeOrphanCollection = function(orphanCollectionName, dropCol throw err; } this.__orphanCollections.splice(index, 1); + db._graphs.update(this.__name, {orphanCollections: this.__orphanCollections}); if (dropCollection !== false) { var graphs = getGraphCollection().toArray(); @@ -2851,6 +2932,7 @@ exports._extendEdgeDefinitions = _extendEdgeDefinitions; exports._create = _create; exports._drop = _drop; exports._exists = _exists; +exports._list = _list; // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE diff --git a/js/apps/system/gharial/gharial.js b/js/apps/system/gharial/gharial.js index d46ff2342f..4988071323 100644 --- a/js/apps/system/gharial/gharial.js +++ b/js/apps/system/gharial/gharial.js @@ -36,11 +36,11 @@ actions = require("org/arangodb/actions"), Model = require("org/arangodb/foxx").Model, Graph = require("org/arangodb/general-graph"), + _ = require("underscore"), errors = require("internal").errors, toId = function(c, k) { return c + "/" + k; }, - _ = require("underscore"), setResponse = function (res, name, body, code) { var obj = {}; obj.error = false; @@ -60,10 +60,272 @@ code = code || actions.HTTP_OK; setResponse(res, "graph", { name: g.__name, - edgeDefinitions: g.__edgeDefinitions + edgeDefinitions: g.__edgeDefinitions, + orphanCollections: g._getOrphanCollections() }, code); }; +////////////////////// Graph Creation ///////////////////////////////// + + /** List graphs + * + * Creates a list of all available graphs. + */ + controller.get("/", function(req, res) { + setResponse(res, "graphs", Graph._list()); + }); + + /** Creates a new graph + * + * Creates a new graph object + */ + controller.post("/", function(req, res) { + var infos = req.params("graph"); + var g = Graph._create(infos.get("name"), infos.get("edgeDefinitions")); + setGraphResponse(res, g, actions.HTTP_CREATED); + }).errorResponse( + ArangoError, actions.HTTP_CONFLICT, "Graph creation error.", function(e) { + return { + code: actions.HTTP_CONFLICT, + error: e.errorMessage + }; + } + ).bodyParam("graph", "The required information for a graph", Model); + + /** Drops an existing graph + * + * Drops an existing graph object by name. + * By default all collections not used by other graphs will be dropped as + * well. It can be optionally configured to not drop the collections. + */ + controller.del("/:graph", function(req, res) { + var name = req.params("graph"); + Graph._drop(name); + setResponse(res); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .errorResponse( + ArangoError, actions.HTTP_NOT_FOUND, "The graph does not exist.", function(e) { + return { + code: actions.HTTP_NOT_FOUND, + error: e.errorMessage + }; + } + ); + +/////////////////////// Definitions //////////////////////////////////// + + /** List all vertex collections. + * + * Gets the list of all vertex collections. + */ + controller.get("/:graph/vertex", function(req, res) { + var name = req.params("graph"); + var g = Graph._graph(name); + setResponse(res, "collections", _.map(g._vertexCollections(), function(c) { + return c.name(); + })); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .errorResponse( + ArangoError, actions.HTTP_NOT_FOUND, "The graph could not be found.", function(e) { + return { + code: actions.HTTP_NOT_FOUND, + error: e.errorMessage + }; + } + ); + + /** Create a new vertex collection. + * + * Stores a new vertex collection. + * This has to contain the vertex-collection name. + */ + controller.post("/:graph/vertex", function(req, res) { + var name = req.params("graph"); + var body = req.params("collection"); + var g = Graph._graph(name); + g._addOrphanCollection(body.get("collection")); + setGraphResponse(res, g); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .bodyParam( + "collection", "The vertex collection to be stored.", Model + ) + .errorResponse( + ArangoError, actions.HTTP_BAD, "The vertex collection is invalid.", function(e) { + return { + code: actions.HTTP_BAD, + error: e.errorMessage + }; + } + ); + + /** Delete a vertex collection. + * + * Removes a vertex collection from this graph. + * If this collection is used in one or more edge definitions + * All data stored in the collection is dropped as well as long + * as it is not used in other graphs. + */ + controller.del("/:graph/vertex/:collection", function(req, res) { + var name = req.params("graph"); + var def_name = req.params("collection"); + var g = Graph._graph(name); + g._removeOrphanCollection(def_name); + setGraphResponse(res, g); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .pathParam("collection", { + type: "string", + description: "Name of the vertex collection." + }) + .errorResponse( + ArangoError, actions.HTTP_NOT_FOUND, + "The collection is not found or part of an edge definition." + ); + + /** List all edge collections. + * + * Get the list of all edge collection. + */ + controller.get("/:graph/edge", function(req, res) { + var name = req.params("graph"); + var g = Graph._graph(name); + setResponse(res, "collections", _.map(g._edgeCollections(), function(c) { + return c.name(); + })); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .errorResponse( + ArangoError, actions.HTTP_NOT_FOUND, "The graph could not be found.", function(e) { + return { + code: actions.HTTP_NOT_FOUND, + error: e.errorMessage + }; + } + ); + + /** Create a new edge definition. + * + * Stores a new edge definition with the information contained + * within the body. + * This has to contain the edge-collection name, as well as set of from and to + * collections-names respectively. + */ + controller.post("/:graph/edge", function(req, res) { + var name = req.params("graph"); + var body = req.params("edgeDefinition"); + var g = Graph._graph(name); + g._extendEdgeDefinitions(body.forDB()); + setGraphResponse(res, g); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .bodyParam( + "edgeDefinition", "The edge definition to be stored.", Model + ) + .errorResponse( + ArangoError, actions.HTTP_BAD, "The edge definition is invalid.", function(e) { + return { + code: actions.HTTP_BAD, + error: e.errorMessage + }; + } + ); + + /** Replace an edge definition. + * + * Replaces an existing edge definition with the information contained + * within the body. + * This has to contain the edge-collection name, as well as set of from and to + * collections-names respectively. + * This will also change the edge definitions of all other graphs using this + * definition as well. + */ + controller.put("/:graph/edge/:definition", function(req, res) { + var name = req.params("graph"); + var def_name = req.params("definition"); + var body = req.params("edgeDefinition"); + var g = Graph._graph(name); + if (def_name !== body.get("collection")) { + var err = new ArangoError(); + err.errorNum = errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code; + err.errorMessage = errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message; + throw err; + } + g._editEdgeDefinitions(body.forDB()); + setGraphResponse(res, g); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .pathParam("definition", { + type: "string", + description: "Name of the edge collection in the definition." + }) + .bodyParam( + "edgeDefinition", "The edge definition to be stored.", Model + ) + .errorResponse( + ArangoError, actions.HTTP_BAD, "The edge definition is invalid.", function(e) { + return { + code: actions.HTTP_BAD, + error: e.errorMessage + }; + } + ); + + /** Delete an edge definition. + * + * Removes an existing edge definition from this graph. + * All data stored in the collections is dropped as well as long + * as it is not used in other graphs. + */ + controller.del("/:graph/edge/:definition", function(req, res) { + var name = req.params("graph"); + var def_name = req.params("definition"); + var g = Graph._graph(name); + g._deleteEdgeDefinition(def_name); + setGraphResponse(res, g); + }) + .pathParam("graph", { + type: "string", + description: "Name of the graph." + }) + .pathParam("definition", { + type: "string", + description: "Name of the edge collection in the definition." + }) + .errorResponse( + ArangoError, actions.HTTP_NOT_FOUND, "The edge definition is invalid.", function(e) { + return { + code: actions.HTTP_NOT_FOUND, + error: e.errorMessage + }; + } + ); + +////////////////////// Vertex Operations ///////////////////////////////// + /** Create a new vertex. * * Stores a new vertex with the information contained @@ -229,76 +491,7 @@ } ); - ///////////////////////////////////////////////// Edges ////////// - - /** Create a new edge definition. - * - * Stores a new edge definition with the information contained - * within the body. - * This has to contain the edge-collection name, as well as set of from and to - * collections-names respectively. - */ - controller.post("/:graph/edge", function(req, res) { - var name = req.params("graph"); - var body = req.params("edgeDefinition"); - var g = Graph._graph(name); - g._extendEdgeDefinitions(body.forDB()); - setGraphResponse(res, g); - }) - .pathParam("graph", { - type: "string", - description: "Name of the graph." - }) - .bodyParam( - "edgeDefinition", "The edge definition to be stored.", Model - ) - .errorResponse( - ArangoError, actions.HTTP_BAD, "The edge definition is invalid.", function(e) { - return { - code: actions.HTTP_BAD, - error: e.errorMessage - }; - } - ); - - /** Replace an edge definition. - * - * Replaces an existing edge definition with the information contained - * within the body. - * This has to contain the edge-collection name, as well as set of from and to - * collections-names respectively. - * This will also change the edge definitions of all other graphs using this - * definition as well. - */ - controller.put("/:graph/edge/:definition", function(req, res) { - var name = req.params("graph"); - var def_name = req.params("definition"); - var body = req.params("edgeDefinition"); - var g = Graph._graph(name); - if (def_name !== body.get("collection")) { - var err = new ArangoError(); - err.errorNum = errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code; - err.errorMessage = errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message; - throw err; - } - g._editEdgeDefinitions(body.forDB()); - setGraphResponse(res, g); - }) - .pathParam("graph", { - type: "string", - description: "Name of the graph." - }) - .bodyParam( - "edgeDefinition", "The edge definition to be stored.", Model - ) - .errorResponse( - ArangoError, actions.HTTP_BAD, "The edge definition is invalid.", function(e) { - return { - code: actions.HTTP_BAD, - error: e.errorMessage - }; - } - ); +//////////////////////////// Edge Operations ////////////////////////// /** Create a new edge. * @@ -485,49 +678,6 @@ } ); -///////////////// GRAPH ///////////////////////////////// - - /** Creates a new graph - * - * Creates a new graph object - */ - controller.post("/", function(req, res) { - var infos = req.params("graph"); - var g = Graph._create(infos.get("name"), infos.get("edgeDefinitions")); - setGraphResponse(res, g, actions.HTTP_CREATED); - }).errorResponse( - ArangoError, actions.HTTP_CONFLICT, "Graph creation error.", function(e) { - return { - code: actions.HTTP_CONFLICT, - error: e.errorMessage - }; - } - ).bodyParam("graph", "The required information for a graph", Model); - - /** Drops an existing graph - * - * Drops an existing graph object by name. - * By default all collections not used by other graphs will be dropped as - * well. It can be optionally configured to not drop the collections. - */ - controller.del("/:graph", function(req, res) { - var name = req.params("graph"); - Graph._drop(name); - setResponse(res); - }) - .pathParam("graph", { - type: "string", - description: "Name of the graph." - }) - .errorResponse( - ArangoError, actions.HTTP_NOT_FOUND, "The graph does not exist.", function(e) { - return { - code: actions.HTTP_NOT_FOUND, - error: e.errorMessage - }; - } - ); - diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index e1483319d9..7dfe5e5016 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -1402,6 +1402,15 @@ var _directedRelationDefinition = function ( }; }; +//////////////////////////////////////////////////////////////////////////////// +/// @brief create a list of all graph names +//////////////////////////////////////////////////////////////////////////////// + +var _list = function() { + var gdb = getGraphCollection(); + return _.pluck(gdb.toArray(), "_key"); +}; + //////////////////////////////////////////////////////////////////////////////// /// @brief create a list of edge definitions //////////////////////////////////////////////////////////////////////////////// @@ -2063,7 +2072,11 @@ Graph.prototype._edgeCollections = function() { //////////////////////////////////////////////////////////////////////////////// Graph.prototype._vertexCollections = function() { - return _.values(this.__vertexCollections); + var orphans = []; + _.each(this.__orphanCollections, function(o) { + orphans.push(db[o]); + }); + return _.union(_.values(this.__vertexCollections), orphans); }; //////////////////////////////////////////////////////////////////////////////// @@ -2918,6 +2931,7 @@ exports._extendEdgeDefinitions = _extendEdgeDefinitions; exports._create = _create; exports._drop = _drop; exports._exists = _exists; +exports._list = _list; // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE From 4f60fca3decec029b7f9090551b60888b5651aed Mon Sep 17 00:00:00 2001 From: Jan Steemann Date: Fri, 13 Jun 2014 16:36:01 +0200 Subject: [PATCH 04/13] added DEPRECATED.md --- DEPRECATED.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 DEPRECATED.md diff --git a/DEPRECATED.md b/DEPRECATED.md new file mode 100644 index 0000000000..cbd6a3e7a9 --- /dev/null +++ b/DEPRECATED.md @@ -0,0 +1,10 @@ +Deprecated Features +------------------- + +This file lists all features that have been deprecated in ArangoDB +or are known to become deprecated in a future version of ArangoDB. +Deprecated features will likely be removed in upcoming versions of +ArangoDB and shouldn't be used if possible. + +Scheduled removal dates or releases will be listed here. Possible +migrations will also be detailed here. From fdece972d05025f7670abba3286783aee942577c Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 13 Jun 2014 16:41:48 +0200 Subject: [PATCH 05/13] Added tests for vertex and edge collection lists in http interface. Added docu for _list in general-graph module --- .../HttpInterface/api-general-graph-spec.rb | 35 +++++++++++++++++++ js/apps/system/gharial/gharial.js | 4 +-- .../modules/org/arangodb/general-graph.js | 15 +++++++- .../modules/org/arangodb/graph-blueprint.js | 2 +- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/UnitTests/HttpInterface/api-general-graph-spec.rb b/UnitTests/HttpInterface/api-general-graph-spec.rb index 441e78fb76..594de3a18d 100644 --- a/UnitTests/HttpInterface/api-general-graph-spec.rb +++ b/UnitTests/HttpInterface/api-general-graph-spec.rb @@ -28,6 +28,11 @@ def edge_endpoint(graph_name, collection) return URLPREFIX + "/" + graph_name + "/edge/" + collection end +def list_edge_collections (graph_name) + cmd = URLPREFIX + "/" + graph_name + "/edge" + doc = ArangoDB.get(cmd) + return doc +end def additional_edge_definition (graph_name, edge_definitions) cmd = URLPREFIX + "/" + graph_name + "/edge" @@ -47,6 +52,12 @@ def delete_edge_definition (graph_name, definition_name) return doc end +def list_vertex_collections (graph_name) + cmd = URLPREFIX + "/" + graph_name + "/vertex" + doc = ArangoDB.get(cmd) + return doc +end + def additional_vertex_collection (graph_name, collection_name) cmd = URLPREFIX + "/" + graph_name + "/vertex" body = { :collection => collection_name } @@ -286,6 +297,30 @@ describe ArangoDB do doc.parsed_response['error'].should eq("graph already exists") doc.parsed_response['code'].should eq(409) end + + it "can get a list of vertex collections" do + definition = { "collection" => friend_collection, "from" => [user_collection], "to" => [user_collection] } + create_graph(graph_name, [definition]) + additional_vertex_collection(graph_name, product_collection) + + doc = list_vertex_collections(graph_name) + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['collections'].should eq([product_collection, user_collection]) + end + + it "can get a list of edge collections" do + definition1 = { "collection" => friend_collection, "from" => [user_collection], "to" => [user_collection] } + definition2 = { "collection" => bought_collection, "from" => [user_collection], "to" => [product_collection] } + create_graph(graph_name, [definition1, definition2]) + + doc = list_edge_collections(graph_name) + doc.code.should eq(200) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(200) + doc.parsed_response['collections'].should eq([bought_collection, friend_collection]) + end end context "check vertex operations" do diff --git a/js/apps/system/gharial/gharial.js b/js/apps/system/gharial/gharial.js index 4988071323..647782a0e3 100644 --- a/js/apps/system/gharial/gharial.js +++ b/js/apps/system/gharial/gharial.js @@ -127,7 +127,7 @@ var g = Graph._graph(name); setResponse(res, "collections", _.map(g._vertexCollections(), function(c) { return c.name(); - })); + }).sort()); }) .pathParam("graph", { type: "string", @@ -206,7 +206,7 @@ var g = Graph._graph(name); setResponse(res, "collections", _.map(g._edgeCollections(), function(c) { return c.name(); - })); + }).sort()); }) .pathParam("graph", { type: "string", diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index 7dfe5e5016..e54313e209 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -1403,7 +1403,20 @@ var _directedRelationDefinition = function ( }; //////////////////////////////////////////////////////////////////////////////// -/// @brief create a list of all graph names +/// @startDocuBlock JSF_general_graph_list +/// +/// `general-graph._list()` +/// *List all graphs.* +/// +/// Lists all graph names stored in this database. +/// +/// @EXAMPLES +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphDirectedRelationDefinition} +/// var graph = require("org/arangodb/general-graph"); +/// graph._list(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// var _list = function() { diff --git a/js/server/modules/org/arangodb/graph-blueprint.js b/js/server/modules/org/arangodb/graph-blueprint.js index be053a3f2e..dd735f526f 100644 --- a/js/server/modules/org/arangodb/graph-blueprint.js +++ b/js/server/modules/org/arangodb/graph-blueprint.js @@ -370,7 +370,7 @@ Graph.prototype.initialize = function (name, vertices, edges, waitForSync) { if (from.length !== 1 || to.length !== 1 || from[0] !== to[0]) { throw newGraphError; } - } + } vertices = db._collection(edgeDefinitions[0].from[0]); From b986d7baf68b3c1c27134015d6b6e1b27eba5230 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 13 Jun 2014 17:32:28 +0200 Subject: [PATCH 06/13] Fixes #894 in Safari + Chrome. Firefox is broken due to a bug in our jQuery version (offset is returning a random value...). IE to be tested. --- .../js/graphViewer/ui/eventDispatcherControls.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js b/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js index 8080a63602..35728d6d76 100644 --- a/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js +++ b/js/apps/system/aardvark/frontend/js/graphViewer/ui/eventDispatcherControls.js @@ -121,17 +121,11 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, start, dispatcher }, getCursorPositionInSVG = function (ev) { var pos = getCursorPosition(ev), - off = $('svg').offset(); - // Hack for Firefox - /* - var off = { - left: 166, - top: 171 + off = $('svg#graphViewerSVG').offset(); + return { + x: pos.x - off.left, + y: pos.y - off.top }; - */ - pos.x -= off.left; - pos.y -= off.top; - return pos; }, callbacks = { nodes: {}, @@ -214,7 +208,7 @@ function EventDispatcherControls(list, nodeShaper, edgeShaper, start, dispatcher idprefix = prefix + "_", nodesDown = dispatcher.events.STARTCREATEEDGE(function(startNode, ev) { var pos = getCursorPositionInSVG(ev), - moveCB = edgeShaper.addAnEdgeFollowingTheCursor(pos.x, pos.y); + moveCB = edgeShaper.addAnEdgeFollowingTheCursor(pos.x, pos.y); dispatcher.bind("svg", "mousemove", function(ev) { var pos = getCursorPositionInSVG(ev); moveCB(pos.x, pos.y); From 5c994b6fbf43b93c98993bd206c7a72f208e20ee Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 15 Jun 2014 22:40:29 +0200 Subject: [PATCH 07/13] Started adding links for the graph http module in the gitbook --- .../Books/Users/HttpGharial/Edges.mdpp | 103 ++++++++++++ .../Books/Users/HttpGharial/Management.mdpp | 150 ++++++++++++++++++ Documentation/Books/Users/SUMMARY.md | 4 + 3 files changed, 257 insertions(+) create mode 100644 Documentation/Books/Users/HttpGharial/Edges.mdpp create mode 100644 Documentation/Books/Users/HttpGharial/Management.mdpp diff --git a/Documentation/Books/Users/HttpGharial/Edges.mdpp b/Documentation/Books/Users/HttpGharial/Edges.mdpp new file mode 100644 index 0000000000..5cefaab194 --- /dev/null +++ b/Documentation/Books/Users/HttpGharial/Edges.mdpp @@ -0,0 +1,103 @@ +!CHAPTER Handling Edges + +!SECTION Create an edge + +`POST /system/gharial/graph-name/edge/collection-name`*(create an edge)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the edge collection the edge belongs to. + +!SUBSECTION Description + + + + + +!SECTION Get an edge + +`GET /system/gharial/graph-name/edge/collection-name/edge-key`*(get an edge)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the edge collection the edge belongs to. + +`collection-name (string, required)` +The `_key` attribute of the edge. + +!SUBSECTION Description + + + + + +!SECTION Modify an edge + +`PATCH /system/gharial/graph-name/edge/collection-name/edge-key`*(modify an edge)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the edge collection the edge belongs to. + +`collection-name (string, required)` +The `_key` attribute of the edge. + +!SUBSECTION Description + + + + + +!SECTION Replace an edge + +`PATCH /system/gharial/graph-name/edge/collection-name/edge-key`*(replace an edge)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the edge collection the edge belongs to. + +`collection-name (string, required)` +The `_key` attribute of the edge. + +!SUBSECTION Description + + + + + +!SECTION Remove an edge + +`DELETE /system/gharial/graph-name/edge/collection-name/edge-key`*(remove an edge)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the edge collection the edge belongs to. + +`collection-name (string, required)` +The `_key` attribute of the edge. + +!SUBSECTION Description + + + + diff --git a/Documentation/Books/Users/HttpGharial/Management.mdpp b/Documentation/Books/Users/HttpGharial/Management.mdpp new file mode 100644 index 0000000000..1554fc0089 --- /dev/null +++ b/Documentation/Books/Users/HttpGharial/Management.mdpp @@ -0,0 +1,150 @@ +!CHAPTER Manage your graphs + +!SECTION List all graphs + +`GET /system/gharial/`*(lists all graphs)* + +!SUBSECTION Description + + + + + +!SECTION Create a graph + +`POST /system/gharial/`*(create a graph)* + +!SUBSECTION Description + + + + + +!SECTION Drop a graph + +`DELETE /system/gharial/graph-name`*(drop a graph)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` + +The name of the graph. + +!SUBSECTION Description + + + + + +!SECTION List all vertex collections in the graph + +`GET /system/gharial/graph-name/vertex`*(lists all vertex collections)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` + +!SUBSECTION Description + + + + + +!SECTION Add a new vertex collection to the graph + +`POST /system/gharial/graph-name/vertex`*(add a new vertex collection)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` + +!SUBSECTION Description + + + + + +!SECTION Remove a vertex collection form the graph + +`DELETE /system/gharial/graph-name/vertex/collection-name`*(remove a vertex collection)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the vertex collection. + + +!SUBSECTION Description + + + + + +!SECTION List all edge definitions in the graph + +`GET /system/gharial/graph-name/edge`*(lists all edge definitions)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` + +!SUBSECTION Description + + + + + +!SECTION Add a new edge definition to the graph + +`POST /system/gharial/graph-name/edge`*(add a new edge definition)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` + +!SUBSECTION Description + + + + + +!SECTION Modify an edge definition + +`DELETE /system/gharial/graph-name/edge/definition-name`*(remove a vertex collection)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`definition-name (string, required)` +The name of the edge collection used in the definition. + + +!SUBSECTION Description + + + + + +!SECTION Remove an edge definition form the graph + +`DELETE /system/gharial/graph-name/edge/definition-name`*(remove a vertex collection)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`definition-name (string, required)` +The name of the edge collection used in the definition. + + +!SUBSECTION Description + + + + diff --git a/Documentation/Books/Users/SUMMARY.md b/Documentation/Books/Users/SUMMARY.md index f7faffda8e..fe2b01ad41 100644 --- a/Documentation/Books/Users/SUMMARY.md +++ b/Documentation/Books/Users/SUMMARY.md @@ -167,6 +167,10 @@ * [Graphs](HttpGraphs/README.md) * [Vertex](HttpGraphs/Vertex.md) * [Edges](HttpGraphs/Edge.md) + * [General Graph](HttpGharial/README.md) + * [Management](HttpGharial/Management.md) + * [Vertices](HttpGharial/Vertices.md) + * [Edges](HttpGharial/Edges.md) * [Traversals](HttpTraversal/README.md) * [Replication](HttpReplications/README.md) * [Replication Dump](HttpReplications/ReplicationDump.md) From b8e96d54ba488b82a4c1a75b2d700f27549b0d8b Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 15 Jun 2014 22:42:51 +0200 Subject: [PATCH 08/13] Started adding examples to the http graph module --- js/apps/system/gharial/gharial.js | 59 +++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/js/apps/system/gharial/gharial.js b/js/apps/system/gharial/gharial.js index 647782a0e3..92f37da4bb 100644 --- a/js/apps/system/gharial/gharial.js +++ b/js/apps/system/gharial/gharial.js @@ -67,6 +67,24 @@ ////////////////////// Graph Creation ///////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_list_http_examples +/// @EXAMPLE_ARANGOSH_RUN{HttpGharialList} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// examples.loadGraph("social"); +/// examples.loadGraph("routeplanner"); +/// var url = "/system/gharial"; +/// var response = logCurlRequest('GET', url); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// @END_EXAMPLE_ARANGOSH_RUN +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + + /** List graphs * * Creates a list of all available graphs. @@ -75,6 +93,32 @@ setResponse(res, "graphs", Graph._list()); }); +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_create_http_examples +/// @EXAMPLE_ARANGOSH_RUN{HttpGharialList} +/// var url = "/system/gharial"; +/// body = { +/// name: "myGraph", +/// edgeDefinitions: [{ +/// collection: "edges", +/// from: [ "startVertices" ], +/// to: [ "endVertices" ] +/// }] +/// }; +/// +/// var response = logCurlRequest('POST', url, JSON.stringify(body)); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// +/// var graph = require("org/arangodb/general-graph"); +/// graph._drop("myGraph"); +/// +/// @END_EXAMPLE_ARANGOSH_RUN +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + /** Creates a new graph * * Creates a new graph object @@ -92,6 +136,21 @@ } ).bodyParam("graph", "The required information for a graph", Model); +//////////////////////////////////////////////////////////////////////////////// +/// @startDocuBlock JSF_general_graph_drop_http_examples +/// @EXAMPLE_ARANGOSH_RUN{HttpGharialList} +/// var examples = require("org/arangodb/graph-examples/example-graph.js"); +/// examples.loadGraph("social"); +/// var url = "/system/gharial/social"; +/// var response = logCurlRequest('DELETE', url); +/// +/// assert(response.code === 200); +/// +/// logJsonResponse(response); +/// @END_EXAMPLE_ARANGOSH_RUN +/// @endDocuBlock +//////////////////////////////////////////////////////////////////////////////// + /** Drops an existing graph * * Drops an existing graph object by name. From 2ffd4f2167de841737c18d18d2d25fb181530e8b Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 15 Jun 2014 22:53:51 +0200 Subject: [PATCH 09/13] Added further links for the graph module in the gitbook --- .../Books/Users/HttpGharial/Edges.mdpp | 8 +- .../Books/Users/HttpGharial/Vertices.mdpp | 103 ++++++++++++++++++ 2 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 Documentation/Books/Users/HttpGharial/Vertices.mdpp diff --git a/Documentation/Books/Users/HttpGharial/Edges.mdpp b/Documentation/Books/Users/HttpGharial/Edges.mdpp index 5cefaab194..a99573443c 100644 --- a/Documentation/Books/Users/HttpGharial/Edges.mdpp +++ b/Documentation/Books/Users/HttpGharial/Edges.mdpp @@ -30,7 +30,7 @@ The name of the graph. `collection-name (string, required)` The name of the edge collection the edge belongs to. -`collection-name (string, required)` +`edge-key (string, required)` The `_key` attribute of the edge. !SUBSECTION Description @@ -51,7 +51,7 @@ The name of the graph. `collection-name (string, required)` The name of the edge collection the edge belongs to. -`collection-name (string, required)` +`edge-key (string, required)` The `_key` attribute of the edge. !SUBSECTION Description @@ -72,7 +72,7 @@ The name of the graph. `collection-name (string, required)` The name of the edge collection the edge belongs to. -`collection-name (string, required)` +`edge-key (string, required)` The `_key` attribute of the edge. !SUBSECTION Description @@ -93,7 +93,7 @@ The name of the graph. `collection-name (string, required)` The name of the edge collection the edge belongs to. -`collection-name (string, required)` +`edge-key (string, required)` The `_key` attribute of the edge. !SUBSECTION Description diff --git a/Documentation/Books/Users/HttpGharial/Vertices.mdpp b/Documentation/Books/Users/HttpGharial/Vertices.mdpp new file mode 100644 index 0000000000..19cb23a66e --- /dev/null +++ b/Documentation/Books/Users/HttpGharial/Vertices.mdpp @@ -0,0 +1,103 @@ +!CHAPTER Handling Vertices + +!SECTION Create a vertex + +`POST /system/gharial/graph-name/vertex/collection-name`*(create a vertex)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the vertex collection the edge belongs to. + +!SUBSECTION Description + + + + + +!SECTION Get a vertex + +`GET /system/gharial/graph-name/vertex/collection-name/vertex-key`*(get a vertex)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the vertex collection the vertex belongs to. + +`vertex-key (string, required)` +The `_key` attribute of the vertex. + +!SUBSECTION Description + + + + + +!SECTION Modify an vertex + +`PATCH /system/gharial/graph-name/vertex/collection-name/vertex-key`*(modify a vertex)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the vertex collection the vertex belongs to. + +`vertex-key (string, required)` +The `_key` attribute of the vertex. + +!SUBSECTION Description + + + + + +!SECTION Replace an vertex + +`PATCH /system/gharial/graph-name/vertex/collection-name/vertex-key`*(replace a vertex)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the vertex collection the vertex belongs to. + +`vertex-key (string, required)` +The `_key` attribute of the vertex. + +!SUBSECTION Description + + + + + +!SECTION Remove a vertex + +`DELETE /system/gharial/graph-name/vertex/collection-name/vertex-key`*(remove a vertex)* + +!SUBSECTION URL parameters + +`graph-name (string, required)` +The name of the graph. + +`collection-name (string, required)` +The name of the vertex collection the vertex belongs to. + +`vertex-key (string, required)` +The `_key` attribute of the vertex. + +!SUBSECTION Description + + + + From 348c4c313adb142c07dcabb92d3fc20989ff25aa Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 15 Jun 2014 23:02:37 +0200 Subject: [PATCH 10/13] Added a general readme file for the http documentation of the general graph module --- Documentation/Books/Users/HttpGharial/README.mdpp | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Documentation/Books/Users/HttpGharial/README.mdpp diff --git a/Documentation/Books/Users/HttpGharial/README.mdpp b/Documentation/Books/Users/HttpGharial/README.mdpp new file mode 100644 index 0000000000..3f17714735 --- /dev/null +++ b/Documentation/Books/Users/HttpGharial/README.mdpp @@ -0,0 +1,6 @@ +!CHAPTER General Graphs + +This chapter describes the http interface for the multi-collection graph module. +It allows you to define a graph that is spread across several edge and document collections. +This allows you to structure your models in line with your domain and group them logically in collections and giving you the power to query them in the same graph queries. +There is no need to include the referenced collections within the query, this module will handle it for you. From dc3e3703cc69da0297d9e79a4f557119ef6ec96f Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Sun, 15 Jun 2014 23:12:26 +0200 Subject: [PATCH 11/13] Solved uniqueness conflicts for generated examples --- js/actions/api-simple.js | 2 +- .../modules/org/arangodb/general-graph.js | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/js/actions/api-simple.js b/js/actions/api-simple.js index 6fc9ecd3ae..78db9d57fb 100644 --- a/js/actions/api-simple.js +++ b/js/actions/api-simple.js @@ -1976,7 +1976,7 @@ actions.defineHttp({ /// db._drop(cn); /// @END_EXAMPLE_ARANGOSH_RUN /// Using new Signature for attributes WaitForSync and limit -/// @EXAMPLE_ARANGOSH_RUN{RestSimpleReplaceByExample} +/// @EXAMPLE_ARANGOSH_RUN{RestSimpleReplaceByExampleWaitForSync} /// var cn = "products"; /// db._drop(cn); /// var products = db._create(cn, { waitForSync: true }); diff --git a/js/common/modules/org/arangodb/general-graph.js b/js/common/modules/org/arangodb/general-graph.js index e54313e209..4058ce4ac0 100644 --- a/js/common/modules/org/arangodb/general-graph.js +++ b/js/common/modules/org/arangodb/general-graph.js @@ -1009,7 +1009,7 @@ AQLGenerator.prototype._getLastRestrictableStatementInfo = function() { /// /// Restriction of a query is only valid for collections known to the graph: // -/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLRestricted} +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLRestrictedUnknown} /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("social"); /// var query = g._vertices({name: "Alice"}); @@ -1403,16 +1403,19 @@ var _directedRelationDefinition = function ( }; //////////////////////////////////////////////////////////////////////////////// -/// @startDocuBlock JSF_general_graph_list -/// +/// @startDocuBlock JSF_general_graph_list_call /// `general-graph._list()` /// *List all graphs.* +/// @endDocuBlock /// +/// @startDocuBlock JSF_general_graph_list_info /// Lists all graph names stored in this database. /// /// @EXAMPLES +/// @endDocuBlock +/// @startDocuBlock JSF_general_graph_list_examples /// -/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphDirectedRelationDefinition} +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphList} /// var graph = require("org/arangodb/general-graph"); /// graph._list(); /// @END_EXAMPLE_ARANGOSH_OUTPUT @@ -2849,7 +2852,7 @@ Graph.prototype._addOrphanCollection = function(vertexCollection, createCollecti /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); /// var g = examples._create("myGraph", [ed1]); -/// g._addOrphanCollection("myVC3, true); +/// g._addOrphanCollection("myVC3", true); /// g._getOrphanCollections(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -2874,12 +2877,12 @@ Graph.prototype._getOrphanCollections = function() { /// /// @EXAMPLES /// -/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__getOrphanCollections} +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__removeOrphanCollections} /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); /// var g = examples._create("myGraph", [ed1]); -/// g._addOrphanCollection("myVC3, true); -/// g._addOrphanCollection("myVC4, true); +/// g._addOrphanCollection("myVC3", true); +/// g._addOrphanCollection("myVC4", true); /// g._getOrphanCollections(); /// g._removeOrphanCollection("myVC3"); /// g._getOrphanCollections(); From fdd2b6f79157fcdae1b9c15cfa7cef2c23fe906b Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Mon, 16 Jun 2014 09:09:05 +0200 Subject: [PATCH 12/13] Moved newest version of general graph into web interface --- .../js/modules/org/arangodb/general-graph.js | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js index 39c005ef33..6424440fec 100644 --- a/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js +++ b/js/apps/system/aardvark/frontend/js/modules/org/arangodb/general-graph.js @@ -1010,7 +1010,7 @@ AQLGenerator.prototype._getLastRestrictableStatementInfo = function() { /// /// Restriction of a query is only valid for collections known to the graph: // -/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLRestricted} +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphFluentAQLRestrictedUnknown} /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var g = examples.loadGraph("social"); /// var query = g._vertices({name: "Alice"}); @@ -1404,7 +1404,23 @@ var _directedRelationDefinition = function ( }; //////////////////////////////////////////////////////////////////////////////// -/// @brief create a list of all graph names +/// @startDocuBlock JSF_general_graph_list_call +/// `general-graph._list()` +/// *List all graphs.* +/// @endDocuBlock +/// +/// @startDocuBlock JSF_general_graph_list_info +/// Lists all graph names stored in this database. +/// +/// @EXAMPLES +/// @endDocuBlock +/// @startDocuBlock JSF_general_graph_list_examples +/// +/// @EXAMPLE_ARANGOSH_OUTPUT{generalGraphList} +/// var graph = require("org/arangodb/general-graph"); +/// graph._list(); +/// @END_EXAMPLE_ARANGOSH_OUTPUT +/// @endDocuBlock //////////////////////////////////////////////////////////////////////////////// var _list = function() { @@ -2837,7 +2853,7 @@ Graph.prototype._addOrphanCollection = function(vertexCollection, createCollecti /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); /// var g = examples._create("myGraph", [ed1]); -/// g._addOrphanCollection("myVC3, true); +/// g._addOrphanCollection("myVC3", true); /// g._getOrphanCollections(); /// @END_EXAMPLE_ARANGOSH_OUTPUT /// @@ -2862,12 +2878,12 @@ Graph.prototype._getOrphanCollections = function() { /// /// @EXAMPLES /// -/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__getOrphanCollections} +/// @EXAMPLE_ARANGOSH_OUTPUT{general_graph__removeOrphanCollections} /// var examples = require("org/arangodb/graph-examples/example-graph.js"); /// var ed1 = examples._directedRelationDefinition("myEC1", ["myVC1"], ["myVC2"]); /// var g = examples._create("myGraph", [ed1]); -/// g._addOrphanCollection("myVC3, true); -/// g._addOrphanCollection("myVC4, true); +/// g._addOrphanCollection("myVC3", true); +/// g._addOrphanCollection("myVC4", true); /// g._getOrphanCollections(); /// g._removeOrphanCollection("myVC3"); /// g._getOrphanCollections(); From 50b6533d0dc8f670556287a5ba831352d4d827a0 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Mon, 16 Jun 2014 12:55:12 +0200 Subject: [PATCH 13/13] added configuration to manifest, install and mount Conflicts: js/server/modules/org/arangodb/foxx/manager.js --- arangosh/ArangoShell/ArangoClient.cpp | 1 + .../modules/org/arangodb/foxx/manager.js | 80 ++++++++++++++++--- .../modules/org/arangodb/foxx/manager.js | 79 ++++++++++++++++-- 3 files changed, 141 insertions(+), 19 deletions(-) diff --git a/arangosh/ArangoShell/ArangoClient.cpp b/arangosh/ArangoShell/ArangoClient.cpp index 2816fb1959..d905e057ac 100644 --- a/arangosh/ArangoShell/ArangoClient.cpp +++ b/arangosh/ArangoShell/ArangoClient.cpp @@ -307,6 +307,7 @@ void ArangoClient::parse (ProgramOptions& options, int argc, char* argv[], string const& initFilename) { + // if options are invalid, exit directly if (! options.parse(description, argc, argv)) { LOG_FATAL_AND_EXIT("%s", options.lastError().c_str()); diff --git a/js/client/modules/org/arangodb/foxx/manager.js b/js/client/modules/org/arangodb/foxx/manager.js index 618e388efb..2047f4bd2e 100644 --- a/js/client/modules/org/arangodb/foxx/manager.js +++ b/js/client/modules/org/arangodb/foxx/manager.js @@ -378,7 +378,7 @@ function cmdUsage () { var fm = "foxx-manager"; printf("Example usage:\n"); - printf(" %s install \n", fm); + printf(" %s install option1=value1\n", fm); printf(" %s uninstall \n\n", fm); printf("Further help:\n"); @@ -460,6 +460,43 @@ function fetchDirectoryForInstall (name) { return exports.fetch("directory", name).app; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief extracts command-line options +//////////////////////////////////////////////////////////////////////////////// + +function extractCommandLineOptions (args) { + 'use strict'; + + var options = {}; + var nargs = []; + var i; + + var re1 = /^([-_a-zA-Z0-9]*)=(.*)$/; + var re2 = /^(0|.0|([0-9]*(\.[0-9]*)?))$/; + + for (i = 0; i < args.length; ++i) { + var a = args[i]; + var m = re1.exec(a); + + if (m !== null) { + var k = m[1]; + var v = m[2]; + + if (re2.test(v)) { + options[k] = parseFloat(v); + } + else { + options[k] = v; + } + } + else { + nargs.push(args[i]); + } + } + + return { 'options': options, 'args': nargs }; +} + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -481,17 +518,37 @@ exports.run = function (args) { var printf = arangodb.printf; var res; + function extractOptions () { + var co = extractCommandLineOptions(args); + + if (3 < co.args.length) { + var options = JSON.parse(co.args[3]); + + if (options.hasOwnProperty("configuration")) { + var k; + + for (k in co.options) { + if (co.options.hasOwnProperty(k)) { + options.configuration[k] = co.options[k]; + } + } + } + else { + options.configuration = co.options; + } + + return options; + } + + return { configuration: co.options }; + } + try { if (type === 'fetch') { exports.fetch(args[1], args[2], args[3]); } else if (type === 'mount') { - if (3 < args.length) { - exports.mount(args[1], args[2], JSON.parse(args[3])); - } - else { - exports.mount(args[1], args[2]); - } + exports.mount(args[1], args[2], extractOptions()); } else if (type === 'rescan') { exports.rescan(); @@ -506,16 +563,13 @@ exports.run = function (args) { exports.unmount(args[1]); } else if (type === 'install') { - if (3 < args.length) { - res = exports.install(args[1], args[2], JSON.parse(args[3])); - } - else { - res = exports.install(args[1], args[2]); - } + var options = extractOptions(); + res = exports.install(args[1], args[2], options); printf("Application %s installed successfully at mount point %s\n", res.appId, res.mount); + printf("options used: %s", JSON.stringify(options)); } else if (type === 'replace') { if (3 < args.length) { diff --git a/js/server/modules/org/arangodb/foxx/manager.js b/js/server/modules/org/arangodb/foxx/manager.js index 75a3172d47..93908cb3ac 100644 --- a/js/server/modules/org/arangodb/foxx/manager.js +++ b/js/server/modules/org/arangodb/foxx/manager.js @@ -88,6 +88,7 @@ function getStorage () { //////////////////////////////////////////////////////////////////////////////// /// @brief check a manifest for completeness +/// /// this implements issue #590: Manifest Lint //////////////////////////////////////////////////////////////////////////////// @@ -120,14 +121,15 @@ function checkManifest (filename, mf) { var expected = { "assets": [ false, "object" ], "author": [ false, "string" ], + "configuration": [ false, "object" ], "contributors": [ false, "array" ], "controllers": [ false, "object" ], "defaultDocument": [ false, "string" ], "description": [ true, "string" ], "engines": [ false, "object" ], "files": [ false, "object" ], - "keywords": [ false, "array" ], "isSystem": [ false, "boolean" ], + "keywords": [ false, "array" ], "lib": [ false, "string" ], "license": [ false, "string" ], "name": [ true, "string" ], @@ -363,9 +365,9 @@ function buildFileAsset (app, path, basePath, asset) { var content = buildAssetContent(app, asset.files, basePath); var type; - // ----------------------------------------------------------------------------- + // ............................................................................. // content-type detection - // ----------------------------------------------------------------------------- + // ............................................................................. // contentType explicitly specified for asset if (asset.hasOwnProperty("contentType") && asset.contentType !== '') { @@ -388,9 +390,9 @@ function buildFileAsset (app, path, basePath, asset) { type = arangodb.guessContentType(""); } - // ----------------------------------------------------------------------------- + // ............................................................................. // return content - // ----------------------------------------------------------------------------- + // ............................................................................. return { contentType: type, body: content }; } @@ -528,6 +530,7 @@ function executeAppScript (app, name, mount, prefix) { appContext.mount = mount; appContext.collectionPrefix = prefix; appContext.options = app._options; + appContext.configuration = app._options.configuration; appContext.basePath = fs.join(root, app._path); appContext.isDevelopment = devel; @@ -747,6 +750,7 @@ function routingAalApp (app, mount, options) { appContextTempl.mount = mount; // global mount appContextTempl.options = options; + appContext.configuration = app._options.configuration; appContextTempl.collectionPrefix = prefix; // collection prefix appContextTempl.basePath = fs.join(root, app._path); @@ -908,6 +912,68 @@ function scanDirectory (path) { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief create configuration +//////////////////////////////////////////////////////////////////////////////// + +function checkConfiguration (app, options) { + 'use strict'; + + if (options === undefined || options === null) { + options = {}; + } + + if (! options.hasOwnProperty("configuration")) { + options.configuration = {}; + } + + if (! app._manifest.hasOwnProperty("configuration")) { + return options; + } + + var configuration = options.configuration; + var expected = app._manifest.configuration; + var att; + + for (att in expected) { + if (expected.hasOwnProperty(att)) { + if (configuration.hasOwnProperty(att)) { + var value = configuration[att]; + var expectedType = expected[att].type; + var actualType = Array.isArray(value) ? "array" : typeof(value); + + if (actualType !== expectedType) { + throw new Error( + "configuration for '" + app._manifest.name + "' uses " + + "an invalid data type (" + actualType + ") " + + "for " + expectedType + " attribute '" + att + "'"); + } + } + else if (expected[att].hasOwnProperty("default")) { + configuration[att] = expected[att]["default"]; + } + else { + throw new Error( + "configuration for '" + app._manifest.name + "' is " + + "missing a value for attribute '" + att + "'"); + } + } + } + + // additionally check if there are superfluous attributes in the manifest + for (att in configuration) { + if (configuration.hasOwnProperty(att)) { + if (! expected.hasOwnProperty(att)) { + console.warn("configuration for '%s' contains an unknown attribute '%s'", + app._manifest.name, + att); + } + } + } + + return options; +} + // ----------------------------------------------------------------------------- // --SECTION-- public functions // ----------------------------------------------------------------------------- @@ -950,6 +1016,7 @@ exports.rescan = function () { /// * options: /// collectionPrefix: overwrites the default prefix /// reload: reload the routing info (default: true) +/// configuration: configuration options /// /// Output: /// * appId: the application identifier (must be mounted) @@ -983,7 +1050,7 @@ exports.mount = function (appId, mount, options) { // install the application // ............................................................................. - options = options || { }; + options = checkConfiguration(app, options); var doc;