mirror of https://gitee.com/bigwinds/arangodb
1049 lines
32 KiB
JavaScript
1049 lines
32 KiB
JavaScript
'use strict';
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2010-2013 triAGENS GmbH, Cologne, Germany
|
|
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Michael Hackstein
|
|
/// @author Alan Plum
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const _ = require('lodash');
|
|
const joi = require('joi');
|
|
const dd = require('dedent');
|
|
const statuses = require('statuses');
|
|
const httperr = require('http-errors');
|
|
const errors = require('@arangodb').errors;
|
|
const cluster = require('@arangodb/cluster');
|
|
const Graph = require('@arangodb/general-graph');
|
|
const createRouter = require('@arangodb/foxx/router');
|
|
const actions = require('@arangodb/actions');
|
|
|
|
const NOT_MODIFIED = statuses('not modified');
|
|
const ACCEPTED = statuses('accepted');
|
|
const CREATED = statuses('created');
|
|
const OK = statuses('ok');
|
|
|
|
const router = createRouter();
|
|
module.context.use(router);
|
|
|
|
router.use((req, res, next) => {
|
|
try {
|
|
next();
|
|
} catch (e) {
|
|
if (e.isArangoError) {
|
|
const status = actions.arangoErrorToHttpCode(e.errorNum);
|
|
res.throw(status, e.errorMessage, {errorNum: e.errorNum, cause: e});
|
|
}
|
|
if (e.statusCode === NOT_MODIFIED) {
|
|
res.status(NOT_MODIFIED);
|
|
return;
|
|
}
|
|
throw e;
|
|
}
|
|
});
|
|
|
|
function collectionRepresentation(collection, showProperties, showCount, showFigures) {
|
|
const result = {
|
|
id: collection._id,
|
|
name: collection.name(),
|
|
isSystem: (result.name.charAt(0) === '_'),
|
|
status: collection.status(),
|
|
type: collection.type()
|
|
};
|
|
|
|
if (showProperties) {
|
|
const properties = collection.properties();
|
|
result.doCompact = properties.doCompact;
|
|
result.isVolatile = properties.isVolatile;
|
|
result.journalSize = properties.journalSize;
|
|
result.keyOptions = properties.keyOptions;
|
|
result.waitForSync = properties.waitForSync;
|
|
if (cluster.isCoordinator()) {
|
|
result.shardKeys = properties.shardKeys;
|
|
result.numberOfShards = properties.numberOfShards;
|
|
}
|
|
}
|
|
|
|
if (showCount) {
|
|
result.count = collection.count();
|
|
}
|
|
|
|
if (showFigures) {
|
|
const figures = collection.figures();
|
|
|
|
if (figures) {
|
|
result.figures = figures;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function checkCollection(g, collection) {
|
|
if (!g[collection]) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.message),
|
|
{errorNum: errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code}
|
|
);
|
|
}
|
|
}
|
|
|
|
function setResponse(res, name, body, code) {
|
|
res.status(code);
|
|
if (body._rev) {
|
|
res.set('etag', body._rev);
|
|
}
|
|
res.json({
|
|
error: false,
|
|
[name]: body,
|
|
code
|
|
});
|
|
}
|
|
|
|
function matchVertexRevision(req, rev) {
|
|
if (req.headers['if-none-match']) {
|
|
if (rev === req.headers['if-none-match'].replace(/(^["']|["']$)/g, '')) {
|
|
throw httperr(NOT_MODIFIED);
|
|
}
|
|
}
|
|
|
|
if (req.headers['if-match']) {
|
|
if (rev !== req.headers['if-match'].replace(/(^["']|["']$)/g, '')) {
|
|
throw Object.assign(
|
|
new httperr.PreconditionFailed('wrong revision'),
|
|
{errorNum: errors.ERROR_GRAPH_INVALID_VERTEX.code}
|
|
);
|
|
}
|
|
}
|
|
|
|
if (req.queryParams.rev) {
|
|
if (rev !== req.queryParams.rev) {
|
|
throw Object.assign(
|
|
new httperr.PreconditionFailed('wrong revision'),
|
|
{errorNum: errors.ERROR_GRAPH_INVALID_VERTEX.code}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function graphForClient(g) {
|
|
return {
|
|
name: g.__name,
|
|
edgeDefinitions: g.__edgeDefinitions,
|
|
orphanCollections: g._orphanCollections(),
|
|
_id : g.__id,
|
|
_rev : g.__rev
|
|
};
|
|
}
|
|
|
|
// PHP clients like to convert booleans to numbers
|
|
const phpCompatFlag = joi.alternatives().try(
|
|
joi.boolean(),
|
|
joi.number().integer()
|
|
);
|
|
|
|
const graphName = joi.string()
|
|
.description("Name of the graph.");
|
|
|
|
const vertexCollectionName = joi.string()
|
|
.description('Name of the vertex collection.');
|
|
|
|
const edgeCollectionName = joi.string()
|
|
.description('Name of the edge collection.');
|
|
|
|
const dropCollectionFlag = phpCompatFlag
|
|
.description('Flag to drop collection as well.');
|
|
|
|
const definitionEdgeCollectionName = joi.string()
|
|
.description('Name of the edge collection in the definition.');
|
|
|
|
const waitForSyncFlag = phpCompatFlag
|
|
.description('define if the request should wait until synced to disk.');
|
|
|
|
const vertexKey = joi.string()
|
|
.description('_key attribute of one specific vertex');
|
|
|
|
const edgeKey = joi.string()
|
|
.description('_key attribute of one specific edge.');
|
|
|
|
const keepNullFlag = phpCompatFlag
|
|
.description('define if null values should not be deleted.');
|
|
|
|
|
|
// Graph Creation
|
|
|
|
router.get('/', function(req, res) {
|
|
setResponse(res, 'graphs', Graph._listObjects(), OK);
|
|
})
|
|
.summary('List graphs')
|
|
.description('Creates a list of all available graphs.');
|
|
|
|
|
|
router.post('/', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
let g;
|
|
try {
|
|
g = Graph._create(
|
|
req.body.name,
|
|
req.body.edgeDefinitions,
|
|
req.body.orphanCollections,
|
|
{waitForSync}
|
|
);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_DUPLICATE.code) {
|
|
throw Object.assign(
|
|
new httperr.Conflict(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
if (e.isArangoError && [
|
|
errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code,
|
|
errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code,
|
|
errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code,
|
|
errors.ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS.code
|
|
].indexOf(e.errorNum) !== -1) {
|
|
throw Object.assign(
|
|
new httperr.BadRequest(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), waitForSync ? CREATED : ACCEPTED);
|
|
})
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.body(joi.object({
|
|
name: joi.string().required(),
|
|
edgeDefinitions: joi.any().optional(),
|
|
orphanCollections: joi.any().optional()
|
|
}).required(), 'The required information for a graph')
|
|
.error('bad request', 'Graph creation error.')
|
|
.error('conflict', 'Graph creation error.')
|
|
.summary('Creates a new graph')
|
|
.description('Creates a new graph object');
|
|
|
|
|
|
router.get('/:graph', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), OK);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.error('not found', 'Graph could not be found.')
|
|
.summary('Get information of a graph')
|
|
.description(dd`
|
|
Selects information for a given graph.
|
|
Will return the edge definitions as well as the vertex collections.
|
|
Or throws a 404 if the graph does not exist.
|
|
`);
|
|
|
|
|
|
router.delete('/:graph', function(req, res) {
|
|
const dropCollections = Boolean(req.queryParams.dropCollections);
|
|
const name = req.pathParams.graph;
|
|
try {
|
|
Graph._drop(name, dropCollections);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'removed', true, ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.queryParam('dropCollections', dropCollectionFlag)
|
|
.error('not found', 'The graph does not exist.')
|
|
.summary('Drops an existing graph')
|
|
.description(dd`
|
|
Drops an existing graph object by name.
|
|
Optionally all collections not used by other graphs
|
|
can be dropped as well.
|
|
`);
|
|
|
|
|
|
// Definitions
|
|
|
|
router.get('/:graph/vertex', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
const mapFunc = (
|
|
req.pathParams.collectionObjects
|
|
? (c) => collectionRepresentation(c, false, false, false)
|
|
: (c) => c.name()
|
|
);
|
|
setResponse(res, 'collections', _.map(g._vertexCollections(), mapFunc).sort(), OK);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('List all vertex collections.')
|
|
.description('Gets the list of all vertex collections.');
|
|
|
|
|
|
router.post('/:graph/vertex', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
try {
|
|
g._addVertexCollection(req.body.collection);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
if (e.isArangoError && [
|
|
errors.ERROR_GRAPH_WRONG_COLLECTION_TYPE_VERTEX.code,
|
|
errors.ERROR_GRAPH_COLLECTION_USED_IN_EDGE_DEF.code,
|
|
errors.ERROR_GRAPH_COLLECTION_USED_IN_ORPHANS.code
|
|
].indexOf(e.errorNum) !== -1) {
|
|
throw Object.assign(
|
|
new httperr.BadRequest(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.body(joi.object({
|
|
collection: joi.any().required()
|
|
}).required(), 'The vertex collection to be stored.')
|
|
.error('bad request', 'The vertex collection is invalid.')
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('Create a new vertex collection.')
|
|
.description('Stores a new vertex collection. This has to contain the vertex-collection name.');
|
|
|
|
|
|
router.delete('/:graph/vertex/:collection', function(req, res) {
|
|
const dropCollection = Boolean(req.queryParams.dropCollection);
|
|
const name = req.pathParams.graph;
|
|
const defName = req.pathParams.collection;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
try {
|
|
g._removeVertexCollection(defName, dropCollection);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_VERTEX_COL_DOES_NOT_EXIST.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_IN_ORPHAN_COLLECTION.code) {
|
|
throw Object.assign(
|
|
new httperr.BadRequest(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', vertexCollectionName)
|
|
.queryParam('dropCollection', dropCollectionFlag)
|
|
.error('bad request', 'The collection is not found or part of an edge definition.')
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('Delete a vertex collection.')
|
|
.description('Removes a vertex collection from this graph. If this collection is used in one or more edge definitions');
|
|
|
|
|
|
router.get('/:graph/edge', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'collections', _.map(g._edgeCollections(), (c) => c.name()).sort(), OK);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('List all edge collections.')
|
|
.description('Get the list of all edge collection.');
|
|
|
|
|
|
router.post('/:graph/edge', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
try {
|
|
g._extendEdgeDefinitions(req.body);
|
|
} catch (e) {
|
|
if (e.isArangoError && [
|
|
errors.ERROR_GRAPH_COLLECTION_MULTI_USE.code,
|
|
errors.ERROR_GRAPH_COLLECTION_USE_IN_MULTI_GRAPHS.code,
|
|
errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.code
|
|
].indexOf(e.errorNum) !== -1) {
|
|
throw Object.assign(
|
|
new httperr.BadRequest(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.body(joi.any().required(), 'The edge definition to be stored.')
|
|
.error('bad request', 'The edge definition is invalid.')
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('Create a new edge definition.')
|
|
.description(dd`
|
|
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.
|
|
`);
|
|
|
|
|
|
router.put('/:graph/edge/:definition', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
const defName = req.pathParams.definition;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
if (defName !== req.body.collection) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.message),
|
|
{errorNum: errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code}
|
|
);
|
|
}
|
|
try {
|
|
g._editEdgeDefinitions(req.body);
|
|
} catch (e) {
|
|
if (e.isArangoError && [
|
|
errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code,
|
|
errors.ERROR_GRAPH_CREATE_MALFORMED_EDGE_DEFINITION.code
|
|
].indexOf(e.errorNum) !== -1) {
|
|
throw Object.assign(
|
|
new httperr.BadRequest(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('definition', definitionEdgeCollectionName)
|
|
.body(joi.object().required(), 'The edge definition to be stored.')
|
|
.error('bad request', 'The edge definition is invalid.')
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('Replace an edge definition.')
|
|
.description(dd`
|
|
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.
|
|
`);
|
|
|
|
|
|
router.delete('/:graph/edge/:definition', function(req, res) {
|
|
const dropCollection = Boolean(req.queryParams.dropCollection);
|
|
const name = req.pathParams.graph;
|
|
const defName = req.pathParams.definition;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
g._deleteEdgeDefinition(defName, dropCollection);
|
|
} catch (e) {
|
|
if (e.isArangoError && [
|
|
errors.ERROR_GRAPH_NOT_FOUND.code,
|
|
errors.ERROR_GRAPH_EDGE_COLLECTION_NOT_USED.code,
|
|
].indexOf(e.errorNum) !== -1) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'graph', graphForClient(g), ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('definition', definitionEdgeCollectionName)
|
|
.queryParam('dropCollection', dropCollectionFlag)
|
|
.error('not found', 'The graph could not be found.')
|
|
.summary('Delete an edge definition.')
|
|
.description(dd`
|
|
Removes an existing edge definition from this graph.
|
|
All data stored in the edge collection are dropped as well
|
|
as long as it is not used in other graphs.
|
|
`);
|
|
|
|
|
|
// Vertex Operations
|
|
|
|
router.post('/:graph/vertex/:collection', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let meta;
|
|
try {
|
|
meta = g[collection].save(req.body, {waitForSync});
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_INVALID_EDGE.code) {
|
|
throw Object.assign(
|
|
new httperr.BadRequest(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'vertex', meta, waitForSync ? CREATED : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', vertexCollectionName)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.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.')
|
|
.summary('Create a new vertex.')
|
|
.description('Stores a new vertex with the information contained within the body into the given collection.');
|
|
|
|
|
|
router.get('/:graph/vertex/:collection/:key', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
setResponse(res, 'vertex', doc, OK);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', vertexCollectionName)
|
|
.pathParam('key', vertexKey)
|
|
.error('not found', 'The vertex does not exist.')
|
|
.summary('Get a vertex.')
|
|
.description('Gets a vertex with the given key if it is contained within your graph.');
|
|
|
|
|
|
router.put('/:graph/vertex/:collection/:key', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
const meta = g[collection].replace(id, req.body, {waitForSync});
|
|
setResponse(res, 'vertex', meta, waitForSync ? OK : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', vertexCollectionName)
|
|
.pathParam('key', vertexKey)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.body(joi.any().required(), 'The document to be stored')
|
|
.error('bad request', 'The vertex is invalid.')
|
|
.error('not found', 'The vertex does not exist.')
|
|
.summary('Replace a vertex.')
|
|
.description(dd`
|
|
Replaces a vertex with the given id by the content in the body.
|
|
This will only run successfully if the vertex is contained within the graph.
|
|
`);
|
|
|
|
|
|
router.patch('/:graph/vertex/:collection/:key', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const keepNull = Boolean(req.queryParams.keepNull);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
const meta = g[collection].update(id, req.body, {waitForSync, keepNull});
|
|
setResponse(res, 'vertex', meta, waitForSync ? OK : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', vertexCollectionName)
|
|
.pathParam('key', vertexKey)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.queryParam('keepNull', keepNullFlag)
|
|
.body(joi.any().required(), 'The values that should be modified')
|
|
.error('bad request', 'The vertex is invalid.')
|
|
.error('not found', 'The vertex does not exist.')
|
|
.summary('Update a vertex.')
|
|
.description(dd`
|
|
Updates a vertex with the given id by adding the content in the body.
|
|
This will only run successfully if the vertex is contained within the graph.
|
|
`);
|
|
|
|
|
|
router.delete('/:graph/vertex/:collection/:key', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
let didRemove;
|
|
try {
|
|
didRemove = g[collection].remove(id, {waitForSync});
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'removed', didRemove, waitForSync ? OK : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', vertexCollectionName)
|
|
.pathParam('key', vertexKey)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.error('not found', 'The vertex does not exist.')
|
|
.summary('Delete a vertex.')
|
|
.description(dd`
|
|
Deletes a vertex with the given id, if it is contained within the graph.
|
|
Furthermore all edges connected to this vertex will be deleted.
|
|
`);
|
|
|
|
|
|
// Edge Operations
|
|
|
|
router.post('/:graph/edge/:collection', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
if (!req.body._from || !req.body._to) {
|
|
throw Object.assign(
|
|
new httperr.Gone(errors.ERROR_GRAPH_INVALID_EDGE.message),
|
|
{errorNum: errors.ERROR_GRAPH_INVALID_EDGE.code}
|
|
);
|
|
}
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let meta;
|
|
try {
|
|
meta = g[collection].save(req.body);
|
|
} catch(e) {
|
|
if (e.errorNum !== errors.ERROR_GRAPH_INVALID_EDGE.code) {
|
|
throw Object.assign(
|
|
new httperr.Gone(e.errorMessage),
|
|
{errorNum: errors.ERROR_GRAPH_INVALID_EDGE.code, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'edge', meta, ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', edgeCollectionName)
|
|
.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.')
|
|
.summary('Create a new edge.')
|
|
.description('Stores a new edge with the information contained within the body into the given collection.');
|
|
|
|
|
|
router.get('/:graph/edge/:collection/:key', function(req, res) {
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
setResponse(res, 'edge', doc, OK);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', edgeCollectionName)
|
|
.pathParam('key', edgeKey)
|
|
.error('not found', 'The edge does not exist.')
|
|
.summary('Load an edge.')
|
|
.description('Loads an edge with the given id if it is contained within your graph.');
|
|
|
|
|
|
router.put('/:graph/edge/:collection/:key', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
const meta = g[collection].replace(id, req.body, {waitForSync});
|
|
setResponse(res, 'edge', meta, waitForSync ? OK : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', edgeCollectionName)
|
|
.pathParam('key', edgeKey)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.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.')
|
|
.summary('Replace an edge.')
|
|
.description(dd`
|
|
Replaces an edge with the given id by the content in the body.
|
|
This will only run successfully if the edge is contained within the graph.
|
|
`);
|
|
|
|
|
|
router.patch('/:graph/edge/:collection/:key', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const keepNull = Boolean(req.queryParams.keepNull);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
const meta = g[collection].update(id, req.body, {waitForSync, keepNull});
|
|
setResponse(res, 'edge', meta, waitForSync ? OK : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', edgeCollectionName)
|
|
.pathParam('key', edgeKey)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.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.')
|
|
.error('not found', 'The edge does not exist.')
|
|
.summary('Update an edge.')
|
|
.description(dd`
|
|
Updates an edge with the given id by adding the content in the body.
|
|
This will only run successfully if the edge is contained within the graph.
|
|
`);
|
|
|
|
|
|
router.delete('/:graph/edge/:collection/:key', function(req, res) {
|
|
const waitForSync = Boolean(req.queryParams.waitForSync);
|
|
const name = req.pathParams.graph;
|
|
const collection = req.pathParams.collection;
|
|
const key = req.pathParams.key;
|
|
const id = `${collection}/${key}`;
|
|
let g;
|
|
try {
|
|
g = Graph._graph(name);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_GRAPH_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
checkCollection(g, collection);
|
|
let doc;
|
|
try {
|
|
doc = g[collection].document(id);
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
matchVertexRevision(req, doc._rev);
|
|
let didRemove;
|
|
try {
|
|
didRemove = g[collection].remove(id, {waitForSync});
|
|
} catch (e) {
|
|
if (e.isArangoError && e.errorNum === errors.ERROR_ARANGO_DOCUMENT_NOT_FOUND.code) {
|
|
throw Object.assign(
|
|
new httperr.NotFound(e.errorMessage),
|
|
{errorNum: e.errorNum, cause: e}
|
|
);
|
|
}
|
|
throw e;
|
|
}
|
|
setResponse(res, 'removed', didRemove, waitForSync ? OK : ACCEPTED);
|
|
})
|
|
.pathParam('graph', graphName)
|
|
.pathParam('collection', edgeCollectionName)
|
|
.pathParam('key', edgeKey)
|
|
.queryParam('waitForSync', waitForSyncFlag)
|
|
.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.');
|