@restorecommerce/chassis-srv
Version:
Restore Commerce microservice chassis
555 lines • 22 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ArangoGraph = void 0;
const _ = __importStar(require("lodash"));
const base_1 = require("./base");
const common_1 = require("./common");
const utils_1 = require("./utils");
const graph_1 = require("@restorecommerce/rc-grpc-clients/dist/generated-server/io/restorecommerce/graph");
class ArangoGraph extends base_1.Arango {
constructor(db, graph, edgeDefConfig, logger) {
super(db);
this.graph = graph;
this.edgeDefConfig = edgeDefConfig;
this.logger = logger;
}
/**
* create a Graph instance.
*
* @param {String} graphName graph name
* @param edgeDefinitions — Definitions for the relations of the graph.
* @param options — Options for creating the graph.
* @return {Object} A Graph instance
*/
async createGraphDB(graphName, edgeDefinitions, options) {
if (!this.graph) {
if (_.isNil(graphName)) {
throw new Error('missing graph name');
}
const graph = this.db.graph(graphName);
try {
await graph.create(edgeDefinitions, options);
}
catch (err) {
if (err.message === 'graph already exists') {
return this.graph;
}
throw { code: err.code, message: err.message };
}
return graph;
}
else {
return this.graph;
}
}
/**
* create a new Vertex with given data.
*
* @param {string} collectionName vertex collection name
* @param {Object} data data for vertex
* @return {Object} created vertex
*/
async createVertex(collectionName, data) {
if (_.isNil(collectionName)) {
throw new Error('missing vertex collection name');
}
if (_.isNil(data)) {
throw new Error('missing data for vertex');
}
const collection = this.graph.vertexCollection(collectionName);
let docs = _.cloneDeep(data);
if (!_.isArray(docs)) {
docs = [docs];
}
_.forEach(docs, (document, i) => {
docs[i] = (0, common_1.sanitizeInputFields)(document);
});
const responseDocs = [];
for (const eachDoc of docs) {
let result;
try {
result = await collection.save(eachDoc);
if (!result.error) {
responseDocs.push(eachDoc);
}
}
catch (e) {
responseDocs.push({
error: true,
errorNum: e.code,
errorMessage: e.message
});
}
}
return _.map(responseDocs, common_1.sanitizeOutputFields);
}
/**
* Retreives the vertex with the given documentHandle from the collection.
*
* @param {string} collectionName vertex collection name
* @param {string} documentHandle The handle of the vertex to retrieve.
* This can be either the _id or the _key of a vertex in the collection,
* or a vertex (i.e. an object with an _id or _key property).
* @return {Object} created vertex
*/
async getVertex(collectionName, documentHandle) {
if (_.isNil(collectionName)) {
throw new Error('missing vertex collection name');
}
if (_.isNil(documentHandle)) {
throw new Error('missing document handle');
}
const collection = this.graph.vertexCollection(collectionName);
const doc = await collection.vertex(documentHandle);
return doc;
}
/**
* Deletes the vertex with the given documentHandle from the collection.
*
* @param {string} collectionName vertex collection name
* @param {string[]} documentHandles An array of the documentHandles to be removed.
* This can be either the _id or the _key of a vertex in the collection,
* or a vertex (i.e. an object with an _id or _key property).
* @return {Object} removed vertex
*/
async removeVertex(collectionName, documentHandles) {
if (_.isNil(collectionName)) {
throw new Error('missing vertex collection name');
}
if (_.isNil(documentHandles)) {
throw new Error('missing document handle property');
}
if (!_.isArray(documentHandles)) {
documentHandles = [documentHandles];
}
const collection = this.graph.vertexCollection(collectionName);
const removedVertexList = [];
for (const documentHandle of documentHandles) {
const id = documentHandle.split('/')[1];
const removed = await collection.remove(documentHandle);
if (!removed.error) {
removedVertexList.push({ _id: documentHandle, _key: id, _rev: id });
}
}
return removedVertexList;
}
/**
* gets a new GraphVertexCollection instance with the given name for this graph.
*
* @param {string} collectionName The handle of the vertex to retrieve.
* This can be either the _id or the _key of a vertex in the collection,
* or a vertex (i.e. an object with an _id or _key property).
* @return {Object} created vertex
*/
async getVertexCollection(collectionName) {
if (_.isNil(collectionName)) {
throw new Error('missing vertex collection name');
}
const collection = await this.graph.vertexCollection(collectionName);
return collection;
}
/**
* Fetches all vertex collections from the graph and returns
* an array of collection descriptions.
*
* @return {Array<Object>} vertex list
*/
async listVertexCollections() {
const collections = await this.graph.listVertexCollections();
return collections;
}
/**
* Fetches all vertex collections from the database and returns an array
* of GraphVertexCollection instances for the collections.
*
* @return {Array<Object>} vertex list
*/
async getAllVertexCollections() {
const collections = await this.graph.vertexCollections();
return collections;
}
/**
* Adds the collection with the given collectionName to the graph's
* vertex collections.
*
* @param {string} collectionName Name of the vertex collection to add to the graph.
* @param {boolean} excludeOrphans Whether orphan collections should be excluded.
* @return {Array<Object>} vertex list
*/
async addVertexCollection(collectionName) {
if (_.isNil(collectionName)) {
throw new Error('missing vertex collection name');
}
let collection;
try {
collection = await this.graph.addVertexCollection(collectionName);
}
catch (err) {
if (err.message.indexOf('collection already used in edge def') > -1 || err.message.indexOf('collection used in orphans') > -1) {
return collection;
}
throw new Error(err.message);
}
return collection;
}
/**
* Removes the vertex collection with the given collectionName from the graph.
*
* @param {string} collectionName Name of the vertex collection to remove from the graph.
* @param {boolean} dropCollection If set to true, the collection will
* also be deleted from the database.
* @return {Object } removed vertex
*/
async removeVertexCollection(collectionName, dropCollection) {
if (_.isNil(collectionName)) {
throw new Error('missing vertex collection name');
}
if (_.isNil(dropCollection)) {
dropCollection = false;
}
const collection = await this.graph.removeVertexCollection(collectionName, dropCollection);
return collection;
}
/**
* @return {Graph} A Graph instance
*/
getGraphDB() {
return this.graph;
}
/**
* Creates a new edge between the vertices fromId and toId with the given data.
*
* @param {string} collectionName name of the edge collection
* @param {Object} data The data of the new edge. If fromId and toId are not
* specified, the data needs to contain the properties _from and _to.
* @param {string} fromId The handle of the start vertex of this edge.
* This can be either the _id of a document in the database, the _key of an
* edge in the collection, or a document (i.e. an object with an _id or _key property).
* @param {string} toId The handle of the end vertex of this edge.
* This can be either the _id of a document in the database, the _key of an
* edge in the collection, or a document (i.e. an object with an _id or _key property).
* @return {Object} edge object
*/
async createEdge(collectionName, data, fromId, toId) {
if (_.isNil(collectionName)) {
throw new Error('missing edge collection name');
}
if (_.isNil(data)) {
data = {};
}
const collection = this.graph.edgeCollection(collectionName);
if (fromId) {
Object.assign(data, { _from: fromId });
}
if (toId) {
Object.assign(data, { _to: toId });
}
return collection.save(data);
}
/**
* Retrieves the edge with the given documentHandle from the collection.
*
* @param {String} collectionName collection name
* @param {String} documentHandle edge key
* @return {Object} edge object
*/
async getEdge(collectionName, documentHandle) {
if (_.isNil(collectionName)) {
throw new Error('missing edge collection name');
}
if (_.isNil(documentHandle)) {
throw new Error('missing docuemnt handle');
}
const collection = this.graph.edgeCollection(collectionName);
return collection.edge(documentHandle);
}
/**
* Retrieves a list of all edges of the document with the given documentHandle.
*
* @param {String} collectionName edge collection name
* @param {String} documentHandle The handle of the document to retrieve
* the edges of. This can be either the _id of a document in the database,
* the _key of an edge in the collection, or a document
* (i.e. an object with an _id or _key property).
* @return {Object} edge object
*/
async getAllEdgesForVertice(collectionName, documentHandle) {
if (_.isNil(collectionName)) {
throw new Error('missing edge collection name');
}
if (_.isNil(documentHandle)) {
throw new Error('missing document handle');
}
const collection = this.graph.edgeCollection(collectionName).collection;
return await collection.edges(documentHandle, {});
}
/**
* get all incoming edges.
*
* @param {String} collectionName edge collection name
* @param {String} documentHandle The handle of the document
* @return {[Object]} list of edges
*/
async getInEdges(collectionName, documentHandle) {
if (_.isNil(collectionName)) {
throw new Error('missing edge name');
}
if (_.isNil(documentHandle)) {
throw new Error('missing document handle');
}
const collection = this.graph.edgeCollection(collectionName).collection;
return await collection.inEdges(documentHandle, {});
}
/**
* get all outgoing edges.
*
* @param {String} collectionName edge collection name
* @param {String} documentHandle The handle of the document
* @return {[Object]} list of edges
*/
async getOutEdges(collectionName, documentHandle) {
if (_.isNil(collectionName)) {
throw new Error('missing edge collection name');
}
if (_.isNil(documentHandle)) {
throw new Error('missing document handle');
}
const collection = this.graph.edgeCollection(collectionName).collection;
return collection.outEdges(documentHandle, {});
}
/**
* collection traversal - Performs a traversal starting from the given
* startVertex and following edges contained in this edge collection.
*
* @param {String} collectionName collection name
* @param {String | String[]} startVertex Start vertex or vertices.
* This can be either the _id of a document in the database,
* the _key of an edge in the collection, or a document
* (i.e. an object with an _id or _key property).
* @param {any} opts opts.direction opts.filter, opts.visitor,
* opts.init, opts.expander, opts.sort
* @return {[Object]} edge traversal path
*/
async traversal(vertices, collection, opts, filters) {
if (vertices) {
if (_.isEmpty(vertices.collection_name) && !_.isEmpty(vertices.start_vertex_ids)) {
throw new Error(`missing collection name for vertex id ${vertices.start_vertex_ids}`);
}
else if (!_.isEmpty(vertices.collection_name) && _.isEmpty(vertices.start_vertex_ids)) {
throw new Error(`missing vertex id for collection_name ${vertices.collection_name}`);
}
}
// vertices data
let vertexCollectionName, startVertexIds;
if (vertices) {
vertexCollectionName = vertices.collection_name;
startVertexIds = vertices.start_vertex_ids;
}
// collection data
let collectionName, limit, offset, sort;
if (collection) {
collectionName = collection.collection_name;
limit = collection.limit;
offset = collection.offset;
sort = collection.sorts;
}
if ((_.isUndefined(startVertexIds) || _.isNil(startVertexIds) || _.isEmpty(startVertexIds)) &&
(_.isUndefined(collectionName) || _.isNil(collectionName) || _.isEmpty(collectionName))) {
throw new Error('One of the Vertices or Collection should be defined');
}
// from either vertices or collections
const traversalCollectionName = collectionName && !_.isEmpty(collectionName) ? collectionName : vertexCollectionName;
if (!opts) {
opts = {};
}
// make outbound traversal by default if not provided
if (opts.direction === undefined) {
opts.direction = graph_1.Options_Direction.OUTBOUND;
}
// default options
let defaultOptions = { uniqueVertices: 'global', bfs: true, uniqueEdges: 'path' };
let filter = '';
let rootFilter = '';
let limitFilter = '';
let sortFilter = '';
// include vertices in options if specified
if (opts.include_vertexs) {
defaultOptions.vertexCollections = opts.include_vertexs;
}
// include edges in options if specified
if (opts.include_edges) {
defaultOptions.edgeCollections = opts.include_edges;
}
// exclude vertices
if (opts.exclude_vertexs) {
for (const excludeVertex of opts.exclude_vertexs) {
filter = filter + ` FILTER v._id NOT LIKE "${excludeVertex}%" `;
}
}
// exclude edges
if (opts.exclude_edges) {
for (const excludeEdge of opts.exclude_edges) {
filter = filter + ` FILTER e._id NOT LIKE "${excludeEdge}%" `;
}
}
const rootAndAssociationFilter = (0, utils_1.createGraphsAssociationFilter)(filters, opts.direction, traversalCollectionName, this.edgeDefConfig, filter);
// association fitler
filter = rootAndAssociationFilter.associationFilter;
// root filter
const rootEntityFilter = rootAndAssociationFilter.rootEntityFilter;
if (rootEntityFilter) {
rootFilter = (0, utils_1.buildGraphFilter)([rootEntityFilter], true).q;
}
if (startVertexIds && startVertexIds.length > 0) {
if (rootFilter && !_.isEmpty(rootFilter)) {
rootFilter = ` obj.id IN ${JSON.stringify(startVertexIds)} && ${rootFilter}`;
}
else {
rootFilter = ` obj.id IN ${JSON.stringify(startVertexIds)} `;
}
}
// combined root filter
if (rootFilter && !_.isEmpty(rootFilter)) {
rootFilter = `FILTER ${rootFilter}`;
}
limitFilter = (0, utils_1.buildGraphLimiter)(limit, offset);
if (sort) {
sortFilter = (0, utils_1.buildGraphSorter)(sort);
}
let rootCursor, associationCursor;
try {
defaultOptions = JSON.stringify(defaultOptions);
// traversal data
const traversalQuery = `For obj IN ${traversalCollectionName} ${rootFilter} ${limitFilter} ${sortFilter}
FOR v, e, p IN 1..100 ${graph_1.Options_Direction[opts.direction]} obj GRAPH "${this.graph.name}"
OPTIONS ${defaultOptions}
${filter}
RETURN { v, e, p }`;
associationCursor = await this.db.query(traversalQuery);
const rootEntityQuery = `For obj IN ${traversalCollectionName} ${rootFilter} ${limitFilter} ${sortFilter} return obj`;
rootCursor = await this.db.query(rootEntityQuery);
}
catch (err) {
throw { code: err.code, message: err.message };
}
return { rootCursor, associationCursor };
}
async getAllChildrenNodes(startVertex, edgeName) {
const queryTpl = `FOR v IN 1..1 OUTBOUND @start_vertex @@edge_name RETURN v`;
const result = await this.db.query(queryTpl, {
start_vertex: startVertex,
'@edge_name': edgeName
});
return result;
}
arrUnique(arr) {
return [...new Set(arr)];
}
/**
* Adds the given edge definition to the graph.
*
* @param {string} edgeName edge name
* @param {Object} fromVertice from vertice
* @param {Object} toVertice from vertice
* @return {Object} The added edge definition
*/
async addEdgeDefinition(edgeName, fromVertice, toVertice) {
if (_.isNil(edgeName)) {
throw new Error('missing edge name');
}
if (_.isNil(fromVertice)) {
throw new Error('missing from vertice');
}
if (_.isNil(toVertice)) {
throw new Error('missing to vertice');
}
if (!_.isArray(fromVertice)) {
fromVertice = [fromVertice];
}
if (!_.isArray(toVertice)) {
toVertice = [toVertice];
}
let edgeDef;
try {
edgeDef = await this.graph.addEdgeDefinition({
collection: edgeName,
from: fromVertice,
to: toVertice
});
}
catch (err) {
// if edge def already exists return
if (err.message === `${edgeName} multi use of edge collection in edge def`) {
return edgeDef;
}
throw { code: err.code, message: err.message };
}
}
/**
* Removes the edge definition with the given definitionName form the graph.
*
* @param {string} definitionName Name of the edge definition
* to remove from the graph.
* @param {boolean} dropCollection If set to true, the edge collection
* associated with the definition will also be deleted from the database.
* @return {Object} replaced edge definition
*/
async removeEdgeDefinition(definitionName, dropCollection) {
if (_.isNil(definitionName)) {
throw new Error('missing definition name');
}
return this.graph.removeEdgeDefinition(definitionName, dropCollection);
}
/**
* list graphs.
*
* @return {Promise<any>} list all the graphs
*/
async listGraphs() {
return this.db.listGraphs();
}
/**
* Deletes the edge with the given documentHandle from the collection.
*
* @param {string} collectionName edge collection name
* @param {string} documentHandle The handle of the edge to retrieve.
* This can be either the _id or the _key of an edge in the collection,
* or an edge (i.e. an object with an _id or _key property).
* @return {Object} removed Edge
*/
async removeEdge(collectionName, documentHandle) {
if (_.isNil(collectionName)) {
throw new Error('missing edge collection name');
}
if (_.isNil(documentHandle)) {
throw new Error('missing document handle');
}
const collection = this.graph.edgeCollection(collectionName);
return collection.remove(documentHandle);
}
}
exports.ArangoGraph = ArangoGraph;
//# sourceMappingURL=graph.js.map