From 42dd337152c2082d247da0d35c554472da2e27e4 Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 9 Feb 2018 14:50:56 +0100 Subject: [PATCH] =?UTF-8?q?allow=20using=20`returnOld`=20and=20`returnNew`?= =?UTF-8?q?=20attributes=20in=20HTTP=20REST=20APIs=20=E2=80=A6=20(#4479)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG | 9 + .../HttpInterface/api-general-graph-spec.rb | 295 +++++++++++++++++- js/apps/system/_api/gharial/APP/gharial.js | 93 ++++-- js/common/modules/@arangodb/general-graph.js | 11 +- 4 files changed, 367 insertions(+), 41 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 659e13ea18..b6e2f4b866 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,15 @@ devel ----- +* support `returnOld` and `returnNew` attributes for in the following HTTP REST + APIs: + + * /_api/gharial//vertex/ + * /_api/gharial//edge/ + + The exception from this is that the HTTP DELETE verb for these APIs does not + support `returnOld` because that would make the existing API incompatible + * fixed issue #4160: Run arangod with "--database.auto-upgrade" option always crash silently without error log * fix issue #4457: create /var/tmp/arangod with correct user in supervisor mode diff --git a/UnitTests/HttpInterface/api-general-graph-spec.rb b/UnitTests/HttpInterface/api-general-graph-spec.rb index fa79e3180f..17cb884e69 100644 --- a/UnitTests/HttpInterface/api-general-graph-spec.rb +++ b/UnitTests/HttpInterface/api-general-graph-spec.rb @@ -110,9 +110,12 @@ describe ArangoDB do return doc end - def create_vertex (waitForSync, graph_name, collection, body) + def create_vertex (waitForSync, graph_name, collection, body, options = {}) cmd = vertex_endpoint(graph_name, collection) cmd = cmd + "?waitForSync=#{waitForSync}" + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.post(cmd, :body => JSON.dump(body)) return doc end @@ -123,36 +126,48 @@ describe ArangoDB do return doc end - def update_vertex (waitForSync, graph_name, collection, key, body, keepNull = '') + def update_vertex (waitForSync, graph_name, collection, key, body, keepNull = '', options = {}) cmd = vertex_endpoint(graph_name, collection, key) cmd = cmd + "?waitForSync=#{waitForSync}" if keepNull != '' then cmd = cmd + "&keepNull=#{keepNull}" end + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.patch(cmd, :body => JSON.dump(body)) return doc end - def replace_vertex (waitForSync, graph_name, collection, key, body) + def replace_vertex (waitForSync, graph_name, collection, key, body, options = {}) cmd = vertex_endpoint(graph_name, collection, key) cmd = cmd + "?waitForSync=#{waitForSync}" + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.put(cmd, :body => JSON.dump(body)) return doc end - def delete_vertex (waitForSync, graph_name, collection, key) + def delete_vertex (waitForSync, graph_name, collection, key, options = {}) cmd = vertex_endpoint(graph_name, collection, key) cmd = cmd + "?waitForSync=#{waitForSync}" + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.delete(cmd) return doc end - def create_edge (waitForSync, graph_name, collection, from, to, body) + def create_edge (waitForSync, graph_name, collection, from, to, body, options = {}) cmd = edge_endpoint(graph_name, collection) cmd = cmd + "?waitForSync=#{waitForSync}" body["_from"] = from body["_to"] = to + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.post(cmd, :body => JSON.dump(body)) return doc end @@ -163,26 +178,35 @@ describe ArangoDB do return doc end - def update_edge (waitForSync, graph_name, collection, key, body, keepNull = '') + def update_edge (waitForSync, graph_name, collection, key, body, keepNull = '', options = {}) cmd = edge_endpoint(graph_name, collection, key) cmd = cmd + "?waitForSync=#{waitForSync}" if keepNull != '' then cmd = cmd + "&keepNull=#{keepNull}" end + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.patch(cmd, :body => JSON.dump(body)) return doc end - def replace_edge (waitForSync, graph_name, collection, key, body) + def replace_edge (waitForSync, graph_name, collection, key, body, options = {}) cmd = edge_endpoint(graph_name, collection, key) cmd = cmd + "?waitForSync=#{waitForSync}" + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.put(cmd, :body => JSON.dump(body)) return doc end - def delete_edge (waitForSync, graph_name, collection, key) + def delete_edge (waitForSync, graph_name, collection, key, options = {}) cmd = edge_endpoint(graph_name, collection, key) cmd = cmd + "?waitForSync=#{waitForSync}" + options.each do |key,value| + cmd = cmd + "&" + key + "=" + value.to_s + end doc = ArangoDB.delete(cmd) return doc end @@ -417,6 +441,22 @@ describe ArangoDB do doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(sync ? 201 : 202) doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + end + + it "can create a vertex, returnNew" do + name = "Alice" + doc = create_vertex( sync, graph_name, user_collection, {"name" => name}, { "returnNew" => "true" }) + doc.code.should eq(sync ? 201 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 201 : 202) + doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new']['_key'].should_not eq(nil) + doc.parsed_response['new']['name'].should eq(name) end it "can get a vertex" do @@ -457,6 +497,39 @@ describe ArangoDB do doc.parsed_response['code'].should eq(sync ? 200 : 202) doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['vertex']['_key'].should eq(key) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + + doc = get_vertex(graph_name, user_collection, key) + doc.parsed_response['vertex']['name'].should eq(nil) + doc.parsed_response['vertex']['name2'].should eq(name) + doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) + oldTag.should_not eq(doc.headers['etag']) + doc.parsed_response['vertex']['_key'].should eq(key) + end + + it "can replace a vertex, returnOld" do + name = "Alice" + doc = create_vertex( sync, graph_name, user_collection, {"name" => name}) + key = doc.parsed_response['vertex']['_key'] + oldTag = doc.parsed_response['vertex']['_rev'] + oldTag.should eq(doc.headers['etag']) + name = "Bob" + + doc = replace_vertex( sync, graph_name, user_collection, key, {"name2" => name}, { "returnOld" => "true", "returnNew" => true }) + doc.code.should eq(sync ? 200 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['vertex']['_key'].should eq(key) + + doc.parsed_response['old']['_key'].should eq(key) + doc.parsed_response['old']['name'].should eq("Alice") + doc.parsed_response['old']['name2'].should eq(nil) + doc.parsed_response['new']['_key'].should eq(key) + doc.parsed_response['new']['name'].should eq(nil) + doc.parsed_response['new']['name2'].should eq("Bob") doc = get_vertex(graph_name, user_collection, key) doc.parsed_response['vertex']['name'].should eq(nil) @@ -488,6 +561,36 @@ describe ArangoDB do doc.parsed_response['code'].should eq(sync ? 200 : 202) doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['vertex']['_key'].should eq(key) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + + doc = get_vertex(graph_name, user_collection, key) + doc.parsed_response['vertex']['name'].should eq(name) + doc.parsed_response['vertex']['name2'].should eq(name2) + doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['vertex']['_key'].should eq(key) + end + + it "can update a vertex, returnOld" do + name = "Alice" + doc = create_vertex( sync, graph_name, user_collection, {"name" => name}) + key = doc.parsed_response['vertex']['_key'] + name2 = "Bob" + + doc = update_vertex( sync, graph_name, user_collection, key, {"name2" => name2}, "", { "returnOld" => "true", "returnNew" => "true" }) + doc.code.should eq(sync ? 200 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['vertex']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['vertex']['_key'].should eq(key) + + doc.parsed_response['old']['_key'].should eq(key) + doc.parsed_response['old']['name'].should eq(name) + doc.parsed_response['old']['name2'].should eq(nil) + doc.parsed_response['new']['_key'].should eq(key) + doc.parsed_response['new']['name'].should eq(name) + doc.parsed_response['new']['name2'].should eq(name2) doc = get_vertex(graph_name, user_collection, key) doc.parsed_response['vertex']['name'].should eq(name) @@ -545,6 +648,8 @@ describe ArangoDB do doc.code.should eq(sync ? 200 : 202) doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) doc = get_vertex(graph_name, user_collection, key) doc.code.should eq(404) @@ -552,7 +657,28 @@ describe ArangoDB do doc.parsed_response['errorMessage'].should include("document not found") doc.parsed_response['code'].should eq(404) end + + it "can delete a vertex, returnOld" do + name = "Alice" + doc = create_vertex( sync, graph_name, user_collection, {"name" => name}) + key = doc.parsed_response['vertex']['_key'] + doc = delete_vertex( sync, graph_name, user_collection, key, { "returnOld" => "true" }) + doc.code.should eq(sync ? 200 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['removed'].should eq(true) + doc.parsed_response['old']['_key'].should eq(key) + doc.parsed_response['old']['name'].should eq(name) + doc.parsed_response['new'].should eq(nil) + + doc = get_vertex(graph_name, user_collection, key) + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorMessage'].should include("document not found") + doc.parsed_response['code'].should eq(404) + end + it "can not delete a non existing vertex" do key = "unknownKey" @@ -587,10 +713,30 @@ describe ArangoDB do v2.code.should eq(sync ? 201 : 202) v2 = v2.parsed_response['vertex']['_id'] doc = create_edge( sync, graph_name, friend_collection, v1, v2, {}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) doc.parsed_response['error'].should eq(false) - doc.parsed_response['code'].should eq(202) + doc.parsed_response['code'].should eq(sync ? 201 : 202) doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + end + + it "can create an edge, returnNew" do + v1 = create_vertex( sync, graph_name, user_collection, {}) + v1.code.should eq(sync ? 201 : 202) + v1 = v1.parsed_response['vertex']['_id'] + v2 = create_vertex( sync, graph_name, user_collection, {}) + v2.code.should eq(sync ? 201 : 202) + v2 = v2.parsed_response['vertex']['_id'] + doc = create_edge( sync, graph_name, friend_collection, v1, v2, { "value" => "foo" }, { "returnNew" => "true" }) + doc.code.should eq(sync ? 201 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 201 : 202) + doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new']['value'].should eq("foo") end it "can get an edge" do @@ -602,7 +748,7 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] doc = get_edge(graph_name, friend_collection, key) @@ -633,7 +779,7 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] oldTag = doc.parsed_response['edge']['_rev'] oldTag.should eq(doc.headers['etag']) @@ -645,6 +791,51 @@ describe ArangoDB do doc.parsed_response['code'].should eq(sync ? 200 : 202) doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['edge']['_key'].should eq(key) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + + doc = get_edge(graph_name, friend_collection, key) + doc.parsed_response['edge']['type2'].should eq(type) + doc.parsed_response['edge']['type'].should eq(nil) + doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) + oldTag.should_not eq(doc.headers['etag']) + doc.parsed_response['edge']['_key'].should eq(key) + end + + it "can replace an edge, returnOld" do + v1 = create_vertex( sync, graph_name, user_collection, {}) + v1.code.should eq(sync ? 201 : 202) + v1 = v1.parsed_response['vertex']['_id'] + v2 = create_vertex( sync, graph_name, user_collection, {}) + v2.code.should eq(sync ? 201 : 202) + v2 = v2.parsed_response['vertex']['_id'] + type = "married" + doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) + doc.code.should eq(sync ? 201 : 202) + key = doc.parsed_response['edge']['_key'] + oldTag = doc.parsed_response['edge']['_rev'] + oldTag.should eq(doc.headers['etag']) + type = "divorced" + + doc = replace_edge( sync, graph_name, friend_collection, key, {"type2" => type, "_from" => v1, "_to" => v2}, { "returnOld" => "true", "returnNew" => "true" }) + doc.code.should eq(sync ? 200 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['edge']['_key'].should eq(key) + + doc.parsed_response['old']['_key'].should eq(key) + doc.parsed_response['old']['type'].should eq("married") + doc.parsed_response['old']['type2'].should eq(nil) + doc.parsed_response['old']['_from'].should eq(v1) + doc.parsed_response['old']['_to'].should eq(v2) + + doc.parsed_response['new']['_key'].should eq(key) + doc.parsed_response['new']['type'].should eq(nil) + doc.parsed_response['new']['type2'].should eq("divorced") + doc.parsed_response['new']['_from'].should eq(v1) + doc.parsed_response['new']['_to'].should eq(v2) doc = get_edge(graph_name, friend_collection, key) doc.parsed_response['edge']['type2'].should eq(type) @@ -673,7 +864,7 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] type2 = "divorced" @@ -683,6 +874,46 @@ describe ArangoDB do doc.parsed_response['code'].should eq(sync ? 200 : 202) doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) doc.parsed_response['edge']['_key'].should eq(key) + + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + + doc = get_edge(graph_name, friend_collection, key) + doc.parsed_response['edge']['type'].should eq(type) + doc.parsed_response['edge']['type2'].should eq(type2) + doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['edge']['_key'].should eq(key) + end + + it "can update an edge, returnOld" do + v1 = create_vertex( sync, graph_name, user_collection, {}) + v1.code.should eq(sync ? 201 : 202) + v1 = v1.parsed_response['vertex']['_id'] + v2 = create_vertex( sync, graph_name, user_collection, {}) + v2.code.should eq(sync ? 201 : 202) + v2 = v2.parsed_response['vertex']['_id'] + type = "married" + doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) + doc.code.should eq(sync ? 201 : 202) + key = doc.parsed_response['edge']['_key'] + type2 = "divorced" + + doc = update_edge( sync, graph_name, friend_collection, key, {"type2" => type2}, "", { "returnOld" => "true", "returnNew" => "true" }) + doc.code.should eq(sync ? 200 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['edge']['_rev'].should eq(doc.headers['etag']) + doc.parsed_response['edge']['_key'].should eq(key) + doc.parsed_response['old']['_key'].should eq(key) + doc.parsed_response['old']['type'].should eq("married") + doc.parsed_response['old']['type2'].should eq(nil) + doc.parsed_response['old']['_from'].should eq(v1) + doc.parsed_response['old']['_to'].should eq(v2) + doc.parsed_response['new']['_key'].should eq(key) + doc.parsed_response['new']['type'].should eq("married") + doc.parsed_response['new']['type2'].should eq("divorced") + doc.parsed_response['new']['_from'].should eq(v1) + doc.parsed_response['new']['_to'].should eq(v2) doc = get_edge(graph_name, friend_collection, key) doc.parsed_response['edge']['type'].should eq(type) @@ -700,7 +931,7 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] doc = update_edge( sync, graph_name, friend_collection, key, {"type" => nil}, "") @@ -720,7 +951,7 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] doc = update_edge( sync, graph_name, friend_collection, key, {"type" => nil}, true) @@ -740,7 +971,7 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] doc = update_edge( sync, graph_name, friend_collection, key, {"type" => nil}, false) @@ -769,13 +1000,43 @@ describe ArangoDB do v2 = v2.parsed_response['vertex']['_id'] type = "married" doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) - doc.code.should eq(202) + doc.code.should eq(sync ? 201 : 202) key = doc.parsed_response['edge']['_key'] doc = delete_edge( sync, graph_name, friend_collection, key) doc.code.should eq(sync ? 200 : 202) doc.parsed_response['error'].should eq(false) doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['old'].should eq(nil) + doc.parsed_response['new'].should eq(nil) + + doc = get_edge(graph_name, friend_collection, key) + doc.code.should eq(404) + doc.parsed_response['error'].should eq(true) + doc.parsed_response['errorMessage'].should include("document not found") + doc.parsed_response['code'].should eq(404) + end + + it "can delete an edge, returnOld" do + v1 = create_vertex( sync, graph_name, user_collection, {}) + v1.code.should eq(sync ? 201 : 202) + v1 = v1.parsed_response['vertex']['_id'] + v2 = create_vertex( sync, graph_name, user_collection, {}) + v2.code.should eq(sync ? 201 : 202) + v2 = v2.parsed_response['vertex']['_id'] + type = "married" + doc = create_edge( sync, graph_name, friend_collection, v1, v2, {"type" => type}) + doc.code.should eq(sync ? 201 : 202) + key = doc.parsed_response['edge']['_key'] + + doc = delete_edge( sync, graph_name, friend_collection, key, { "returnOld" => "true" }) + doc.code.should eq(sync ? 200 : 202) + doc.parsed_response['error'].should eq(false) + doc.parsed_response['code'].should eq(sync ? 200 : 202) + doc.parsed_response['old']['_from'].should eq(v1) + doc.parsed_response['old']['_to'].should eq(v2) + doc.parsed_response['old']['type'].should eq(type) + doc.parsed_response['new'].should eq(nil) doc = get_edge(graph_name, friend_collection, key) doc.code.should eq(404) diff --git a/js/apps/system/_api/gharial/APP/gharial.js b/js/apps/system/_api/gharial/APP/gharial.js index a0cfa0fe24..52eea88243 100644 --- a/js/apps/system/_api/gharial/APP/gharial.js +++ b/js/apps/system/_api/gharial/APP/gharial.js @@ -129,16 +129,29 @@ function checkCollection (g, collection) { } } -function setResponse (res, name, body, code) { +function setResponse (res, name, body, code, returnOld, returnNew) { res.status(code); if (body._rev) { res.set('etag', body._rev); } - res.json({ - error: false, - [name]: body, - code - }); + let result = { error: false, code }; + if (returnOld) { + result.old = body.old; + delete body.old; + } + if (returnNew) { + result.new = body.new; + delete body.new; + } + if (name === undefined) { + // special hack currently used for HTTP DELETE only + for (let att in body) { + result[att] = body[att]; + } + } else { + result[name] = body; + } + res.json(result); } function matchVertexRevision (req, rev) { @@ -205,6 +218,12 @@ const definitionEdgeCollectionName = joi.string() const waitForSyncFlag = phpCompatFlag .description('define if the request should wait until synced to disk.'); +const returnOldFlag = phpCompatFlag + .description('define if the request should wait return the old version of the updated document.'); + +const returnNewFlag = phpCompatFlag + .description('define if the request should wait return the new version of the updated document.'); + const vertexKey = joi.string() .description('_key attribute of one specific vertex'); @@ -549,13 +568,14 @@ router.delete('/:graph/edge/:definition', function (req, res) { router.post('/:graph/vertex/:collection', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); + const returnNew = Boolean(req.queryParams.returnNew); const name = req.pathParams.graph; const collection = req.pathParams.collection; const g = loadGraph(name); checkCollection(g, collection); let meta; try { - meta = g[collection].save(req.body, {waitForSync}); + meta = g[collection].save(req.body, {waitForSync, returnNew}); } catch (e) { if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_INVALID_EDGE.code) { throw Object.assign( @@ -565,11 +585,12 @@ router.post('/:graph/vertex/:collection', function (req, res) { } throw e; } - setResponse(res, 'vertex', meta, waitForSync ? CREATED : ACCEPTED); + setResponse(res, 'vertex', meta, waitForSync ? CREATED : ACCEPTED, false, returnNew); }) .pathParam('graph', graphName) .pathParam('collection', vertexCollectionName) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnNew', returnNewFlag) .body(joi.any().required(), 'The document to be stored') .error('bad request', 'The edge definition is invalid.') .error('not found', 'Graph or collection not found.') @@ -607,6 +628,8 @@ router.get('/:graph/vertex/:collection/:key', function (req, res) { router.put('/:graph/vertex/:collection/:key', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); + const returnOld = Boolean(req.queryParams.returnOld); + const returnNew = Boolean(req.queryParams.returnNew); const name = req.pathParams.graph; const collection = req.pathParams.collection; const key = req.pathParams.key; @@ -626,13 +649,15 @@ router.put('/:graph/vertex/:collection/:key', function (req, res) { throw e; } matchVertexRevision(req, doc._rev); - const meta = g[collection].replace(id, req.body, {waitForSync}); - setResponse(res, 'vertex', meta, waitForSync ? OK : ACCEPTED); + const meta = g[collection].replace(id, req.body, {waitForSync, returnOld, returnNew}); + setResponse(res, 'vertex', meta, waitForSync ? OK : ACCEPTED, returnOld, returnNew); }) .pathParam('graph', graphName) .pathParam('collection', vertexCollectionName) .pathParam('key', vertexKey) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnOld', returnOldFlag) + .queryParam('returnNew', returnNewFlag) .body(joi.any().required(), 'The document to be stored') .error('bad request', 'The vertex is invalid.') .error('not found', 'The vertex does not exist.') @@ -645,6 +670,8 @@ router.put('/:graph/vertex/:collection/:key', function (req, res) { router.patch('/:graph/vertex/:collection/:key', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); const keepNull = Boolean(req.queryParams.keepNull); + const returnOld = Boolean(req.queryParams.returnOld); + const returnNew = Boolean(req.queryParams.returnNew); const name = req.pathParams.graph; const collection = req.pathParams.collection; const key = req.pathParams.key; @@ -664,13 +691,15 @@ router.patch('/:graph/vertex/:collection/:key', function (req, res) { throw e; } matchVertexRevision(req, doc._rev); - const meta = g[collection].update(id, req.body, {waitForSync, keepNull}); - setResponse(res, 'vertex', meta, waitForSync ? OK : ACCEPTED); + const meta = g[collection].update(id, req.body, {waitForSync, keepNull, returnOld, returnNew}); + setResponse(res, 'vertex', meta, waitForSync ? OK : ACCEPTED, returnOld, returnNew); }) .pathParam('graph', graphName) .pathParam('collection', vertexCollectionName) .pathParam('key', vertexKey) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnOld', returnOldFlag) + .queryParam('returnNew', returnNewFlag) .queryParam('keepNull', keepNullFlag) .body(joi.any().required(), 'The values that should be modified') .error('bad request', 'The vertex is invalid.') @@ -683,6 +712,7 @@ router.patch('/:graph/vertex/:collection/:key', function (req, res) { router.delete('/:graph/vertex/:collection/:key', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); + const returnOld = Boolean(req.queryParams.returnOld); const name = req.pathParams.graph; const collection = req.pathParams.collection; const key = req.pathParams.key; @@ -714,12 +744,17 @@ router.delete('/:graph/vertex/:collection/:key', function (req, res) { } throw e; } - setResponse(res, 'removed', didRemove, waitForSync ? OK : ACCEPTED); + let meta = { removed: didRemove }; + if (returnOld) { + meta.old = doc; + } + setResponse(res, undefined, meta, waitForSync ? OK : ACCEPTED, returnOld, false); }) .pathParam('graph', graphName) .pathParam('collection', vertexCollectionName) .pathParam('key', vertexKey) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnOld', returnOldFlag) .error('not found', 'The vertex does not exist.') .summary('Delete a vertex.') .description(dd` @@ -730,6 +765,8 @@ router.delete('/:graph/vertex/:collection/:key', function (req, res) { // Edge Operations router.post('/:graph/edge/:collection', function (req, res) { + const waitForSync = Boolean(req.queryParams.waitForSync); + const returnNew = Boolean(req.queryParams.returnNew); const name = req.pathParams.graph; const collection = req.pathParams.collection; if (!req.body._from || !req.body._to) { @@ -742,7 +779,7 @@ router.post('/:graph/edge/:collection', function (req, res) { checkCollection(g, collection); let meta; try { - meta = g[collection].save(req.body); + meta = g[collection].save(req.body, {waitForSync, returnNew}); } catch (e) { if (e.errorNum !== errors.ERROR_GRAPH_INVALID_EDGE.code) { throw Object.assign( @@ -752,10 +789,12 @@ router.post('/:graph/edge/:collection', function (req, res) { } throw e; } - setResponse(res, 'edge', meta, ACCEPTED); + setResponse(res, 'edge', meta, waitForSync ? CREATED : ACCEPTED, false, returnNew); }) .pathParam('graph', graphName) .pathParam('collection', edgeCollectionName) + .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnNew', returnNewFlag) .body(joi.object().required(), 'The edge to be stored. Has to contain _from and _to attributes.') .error('bad request', 'The edge is invalid.') .error('not found', 'Graph or collection not found.') @@ -793,6 +832,8 @@ router.get('/:graph/edge/:collection/:key', function (req, res) { router.put('/:graph/edge/:collection/:key', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); + const returnOld = Boolean(req.queryParams.returnOld); + const returnNew = Boolean(req.queryParams.returnNew); const name = req.pathParams.graph; const collection = req.pathParams.collection; const key = req.pathParams.key; @@ -812,13 +853,15 @@ router.put('/:graph/edge/:collection/:key', function (req, res) { throw e; } matchVertexRevision(req, doc._rev); - const meta = g[collection].replace(id, req.body, {waitForSync}); - setResponse(res, 'edge', meta, waitForSync ? OK : ACCEPTED); + const meta = g[collection].replace(id, req.body, {waitForSync, returnOld, returnNew}); + setResponse(res, 'edge', meta, waitForSync ? OK : ACCEPTED, returnOld, returnNew); }) .pathParam('graph', graphName) .pathParam('collection', edgeCollectionName) .pathParam('key', edgeKey) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnOld', returnOldFlag) + .queryParam('returnNew', returnNewFlag) .body(joi.any().required(), 'The document to be stored. _from and _to attributes are ignored') .error('bad request', 'The edge is invalid.') .error('not found', 'The edge does not exist.') @@ -830,6 +873,8 @@ router.put('/:graph/edge/:collection/:key', function (req, res) { router.patch('/:graph/edge/:collection/:key', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); + const returnOld = Boolean(req.queryParams.returnOld); + const returnNew = Boolean(req.queryParams.returnNew); const keepNull = Boolean(req.queryParams.keepNull); const name = req.pathParams.graph; const collection = req.pathParams.collection; @@ -850,13 +895,15 @@ router.patch('/:graph/edge/:collection/:key', function (req, res) { throw e; } matchVertexRevision(req, doc._rev); - const meta = g[collection].update(id, req.body, {waitForSync, keepNull}); - setResponse(res, 'edge', meta, waitForSync ? OK : ACCEPTED); + const meta = g[collection].update(id, req.body, {waitForSync, keepNull, returnOld, returnNew}); + setResponse(res, 'edge', meta, waitForSync ? OK : ACCEPTED, returnOld, returnNew); }) .pathParam('graph', graphName) .pathParam('collection', edgeCollectionName) .pathParam('key', edgeKey) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnOld', returnOldFlag) + .queryParam('returnNew', returnNewFlag) .queryParam('keepNull', keepNullFlag) .body(joi.any().required(), 'The values that should be modified. _from and _to attributes are ignored') .error('bad request', 'The edge is invalid.') @@ -869,6 +916,7 @@ router.patch('/:graph/edge/:collection/:key', function (req, res) { router.delete('/:graph/edge/:collection/:key', function (req, res) { const waitForSync = Boolean(req.queryParams.waitForSync); + const returnOld = Boolean(req.queryParams.returnOld); const name = req.pathParams.graph; const collection = req.pathParams.collection; const key = req.pathParams.key; @@ -900,12 +948,17 @@ router.delete('/:graph/edge/:collection/:key', function (req, res) { } throw e; } - setResponse(res, 'removed', didRemove, waitForSync ? OK : ACCEPTED); + let meta = { removed: didRemove }; + if (returnOld) { + meta.old = doc; + } + setResponse(res, undefined, meta, waitForSync ? OK : ACCEPTED, returnOld, false); }) .pathParam('graph', graphName) .pathParam('collection', edgeCollectionName) .pathParam('key', edgeKey) .queryParam('waitForSync', waitForSyncFlag) + .queryParam('returnOld', returnOldFlag) .error('not found', 'The edge does not exist.') .summary('Delete an edge.') .description('Deletes an edge with the given id, if it is contained within the graph.'); diff --git a/js/common/modules/@arangodb/general-graph.js b/js/common/modules/@arangodb/general-graph.js index fecd5e467b..a374ff2a52 100644 --- a/js/common/modules/@arangodb/general-graph.js +++ b/js/common/modules/@arangodb/general-graph.js @@ -382,10 +382,16 @@ var bindEdgeCollections = function (self, edgeCollections) { // save var oldSave = wrap.insert; wrap.save = wrap.insert = function (from, to, data) { + var options = {}; if (typeof from === 'object' && to === undefined) { data = from; from = data._from; to = data._to; + } else if (typeof from === 'object' && typeof to === 'object' && data === undefined) { + data = from; + options = to; + from = data._from; + to = data._to; } else if (typeof from === 'string' && typeof to === 'string' && typeof data === 'object') { data._from = from; data._to = to; @@ -420,7 +426,7 @@ var bindEdgeCollections = function (self, edgeCollections) { } } ); - return oldSave(data); + return oldSave(data, options); }; // remove @@ -580,9 +586,7 @@ var checkRWPermission = function (c) { let user = users.currentUser(); if (user) { let p = users.permission(user, db._name(), c); - //print(`${user}: ${db._name()}/${c} = ${p}`); if (p !== 'rw') { - //print(`Denied ${user} access to ${db._name()}/${c}`); var err = new ArangoError(); err.errorNum = arangodb.errors.ERROR_FORBIDDEN.code; err.errorMessage = arangodb.errors.ERROR_FORBIDDEN.message; @@ -601,7 +605,6 @@ var checkROPermission = function(c) { let p = users.permission(user, db._name(), c); var err = new ArangoError(); if (p === 'none') { - //print(`Denied ${user} access to ${db._name()}/${c}`); err.errorNum = arangodb.errors.ERROR_FORBIDDEN.code; err.errorMessage = arangodb.errors.ERROR_FORBIDDEN.message; throw err;