UNPKG

argosjs

Version:

Ethereum smart-contract events visualiser

574 lines (573 loc) 29.8 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); var Neode = require("neode"); var errors = __importStar(require("../utils/error")); var Database_1 = require("./Database"); var Neo4J = /** @class */ (function (_super) { __extends(Neo4J, _super); /** * Create a connection to Neo4J database * @param {string} connection neo4j bolt * @param {string} username neo4j username * @param {string} password neo4j password * @param {boolean} enterpriseMode neo4j enterprise mode * @param {object} settings neo4 driver settings */ function Neo4J(connection, username, password, enterpriseMode, settings) { var _this = _super.call(this) || this; _this._dbInstance = new Neode(connection, username, password, enterpriseMode); _this._dbSession = _this._dbInstance.session(); return _this; } /** * Create a connection to Neo4J database * @param {string} connection neo4j bolt * @param {string} username neo4j username * @param {string} password neo4j password * @param {boolean} enterpriseMode neo4j enterprise mode * @param {object} settings neo4 driver settings */ Neo4J.createInstance = function (connection, username, password, enterpriseMode, settings) { if (enterpriseMode === void 0) { enterpriseMode = false; } if (settings === void 0) { settings = {}; } return new Neo4J(connection, username, password, enterpriseMode, settings); }; /** * Connect to the database */ Neo4J.prototype.dbConnect = function () { return Promise.all([ this._dbSession = this._dbInstance.session() ]); }; /** * Reconnect to the database */ Neo4J.prototype.dbReconnect = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.dbTerminate()]; case 1: _a.sent(); return [4 /*yield*/, this.dbConnect()]; case 2: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Close connection to the database */ Neo4J.prototype.dbTerminate = function () { return Promise.all([ this._dbSession.close() ]); }; /** * Load a model * @param {DatabaseModels} model loaded model using require() */ Neo4J.prototype.dbCreateModel = function (model) { // console.log("Model: ", model, this._dbInstance); this._dbInstance = this._dbInstance.with(model); this._models = model; }; /** * Get all nodes' type */ Neo4J.prototype.getNodeTypes = function () { var result = Object.keys(this._models); // console.log(result); return result; }; /** * Get all relationships' type */ Neo4J.prototype.getRelTypes = function () { var _this = this; var result = []; this.getNodeTypes().forEach(function (model) { // Get all model's relationships var localRelTypes = _this.findRelationshipModels(model).map(function (relKey) { var rel = _this._models[model][relKey]; return rel.relationship; }); localRelTypes.forEach(function (localRelType) { if (!result.includes(localRelType)) { result.push(localRelType); } }); }); // console.log(result); return result; }; /** * Delete all entry in the database */ Neo4J.prototype.dbClearAll = function () { return __awaiter(this, void 0, void 0, function () { var cypher; return __generator(this, function (_a) { switch (_a.label) { case 0: cypher = { query: "MATCH (n) DETACH DELETE n" }; return [4 /*yield*/, this.executeQuery(cypher)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * Tell the database to execute a query * @param {QueryData} queryData where parameters * @returns {Promise<any>} the result of queries */ Neo4J.prototype.executeQuery = function (queryData) { return __awaiter(this, void 0, void 0, function () { var result; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.dbReconnect()]; case 1: _a.sent(); return [4 /*yield*/, this._dbInstance.cypher(queryData.query, queryData.params) .catch(function (err) { return errors.throwError({ type: "ERROR_DB_QUERY" /* ERROR_DB_QUERY */, reason: "Could not execute query. \n" + err, level: "error", dump: { query: queryData } }); })]; case 2: result = _a.sent(); return [2 /*return*/, (result) ? result.records : undefined]; } }); }); }; /** * Tell the database to execute a query * @param {QueryData[]} queries a string query * @returns {Promise<any>} the result of queries */ Neo4J.prototype.executeQueries = function (queries) { return __awaiter(this, void 0, void 0, function () { var result; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.dbReconnect()]; case 1: _a.sent(); return [4 /*yield*/, this._dbInstance.batch(queries) .catch(function (err) { return errors.throwError({ type: "ERROR_DB_QUERY" /* ERROR_DB_QUERY */, reason: "Could not batch-execute queries" + err, level: "error", dump: { queries: queries } }); })]; case 2: result = _a.sent(); return [2 /*return*/, result]; } }); }); }; /** * * @param fileName */ Neo4J.prototype.exportCSV = function (fileName) { return __awaiter(this, void 0, void 0, function () { var _loop_1, this_1, _i, _a, modelName; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: _loop_1 = function (modelName) { var relationshipProps, model, _i, relationshipProps_1, relationshipProp, rel, relType, relTargetType, relProps, attributes, primaryAttribut, relPropsProj, attributeProj; return __generator(this, function (_a) { switch (_a.label) { case 0: relationshipProps = this_1.findRelationshipModels(modelName); model = this_1._models[modelName]; _i = 0, relationshipProps_1 = relationshipProps; _a.label = 1; case 1: if (!(_i < relationshipProps_1.length)) return [3 /*break*/, 5]; relationshipProp = relationshipProps_1[_i]; rel = model[relationshipProp]; if (rel === undefined) { return [3 /*break*/, 4]; } relType = rel.relationship; relTargetType = rel.target; relProps = Object.keys(rel.properties).sort(); attributes = this_1.findAttributesModels(modelName); primaryAttribut = attributes.filter(function (attribute) { return Object.keys(_this._models[modelName][attribute]).includes("primary"); }); relPropsProj = relProps.map(function (projection) { return "r." + projection + " AS " + projection; }); return [4 /*yield*/, this_1.executeQuery({ query: "CALL apoc.export.csv.query({query}, {file}, {config}) YIELD file, source, format, nodes, relationships, properties, time, rows, data", params: { query: "MATCH (src:" + relTargetType + ")-[r:" + relType + "]->(tgt:" + relTargetType + ") RETURN " + relPropsProj.join(", ") + ", tgt." + primaryAttribut + " AS target, src." + primaryAttribut + " AS source", file: fileName + "_rel_" + relationshipProp + ".csv", config: null } })]; case 2: _a.sent(); if (!(attributes.length > 1)) return [3 /*break*/, 4]; attributeProj = attributes.map(function (attr) { return "n." + attr + " AS " + attr; }); return [4 /*yield*/, this_1.executeQuery({ query: "CALL apoc.export.csv.query({query}, {file}, {config}) YIELD file, source, format, nodes, relationships, properties, time, rows, data", params: { query: "MATCH (n: " + relTargetType + ") RETURN " + attributeProj.join(", "), file: fileName + "_nds_" + relationshipProp + ".csv", config: null } })]; case 3: _a.sent(); _a.label = 4; case 4: _i++; return [3 /*break*/, 1]; case 5: return [2 /*return*/]; } }); }; this_1 = this; _i = 0, _a = Object.keys(this._models); _b.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 4]; modelName = _a[_i]; return [5 /*yield**/, _loop_1(modelName)]; case 2: _b.sent(); _b.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/]; } }); }); }; /** * Import from CSV */ Neo4J.prototype.importCSV = function (fileName) { return __awaiter(this, void 0, void 0, function () { var _loop_2, this_2, _i, _a, modelName; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: _loop_2 = function (modelName) { var relationshipProps, model, _i, relationshipProps_2, relationshipProp, rel, relType, relTargetType, relProps, attributes, primaryAttribut, relPropsProj, attributeProj; return __generator(this, function (_a) { switch (_a.label) { case 0: relationshipProps = this_2.findRelationshipModels(modelName); model = this_2._models[modelName]; _i = 0, relationshipProps_2 = relationshipProps; _a.label = 1; case 1: if (!(_i < relationshipProps_2.length)) return [3 /*break*/, 5]; relationshipProp = relationshipProps_2[_i]; rel = model[relationshipProp]; // console.log(rel, this._models); if (rel === undefined) { return [3 /*break*/, 4]; } relType = rel.relationship; relTargetType = rel.target; relProps = Object.keys(rel.properties).sort(); attributes = this_2.findAttributesModels(modelName); primaryAttribut = attributes.filter(function (attribute) { return Object.keys(_this._models[modelName][attribute]).includes("primary"); }); relPropsProj = relProps.map(function (projection) { return "r." + projection + " = row." + projection; }); return [4 /*yield*/, this_2.executeQuery({ // e.g 0x7545s465e45.._rel_transfer.csv query: "LOAD CSV WITH HEADERS FROM 'file:///" + fileName + "_rel_" + relationshipProp + ".csv' AS row\n" + "MERGE (src:" + relTargetType + " {" + primaryAttribut + ": row.source})\n" + "MERGE (tgt:" + relTargetType + " {" + primaryAttribut + ": row.target})\n" + "MERGE (src)-[r:" + relType + "]->(tgt) ON CREATE SET " + relPropsProj.join(", ") })]; case 2: _a.sent(); if (!(attributes.length > 1)) return [3 /*break*/, 4]; attributeProj = attributes.map(function (attr) { return attr + ": row." + attr; }); return [4 /*yield*/, this_2.executeQuery({ query: "LOAD CSV WITH HEADERS FROM 'file:///" + fileName + "_nds_" + relationshipProp + ".csv' AS row\n" + "MERGE (n:" + relTargetType + " {" + attributeProj.join(", ") + "})\n" })]; case 3: _a.sent(); _a.label = 4; case 4: _i++; return [3 /*break*/, 1]; case 5: return [2 /*return*/]; } }); }; this_2 = this; _i = 0, _a = Object.keys(this._models); _b.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 4]; modelName = _a[_i]; return [5 /*yield**/, _loop_2(modelName)]; case 2: _b.sent(); _b.label = 3; case 3: _i++; return [3 /*break*/, 1]; case 4: return [2 /*return*/]; } }); }); }; /** * Prepare queries and batch-persist them to DB * @param eidss the extracted data * @param PS the persistence strategy */ Neo4J.prototype.persistDataToDB = function (eidss, PS) { return __awaiter(this, void 0, void 0, function () { var queries, nodeStrats, relStrats; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: queries = []; nodeStrats = PS.NodeStrategy; relStrats = PS.RelationshipStrategy; eidss.forEach(function (eids) { Object.keys(relStrats).forEach(function (relItr) { var relStrat = relStrats[parseInt(relItr)]; var sourceKey = Object.keys(nodeStrats).filter(function (itr) { return nodeStrats[parseInt(itr)].nodeAlias === relStrat.source; })[0]; var targetKey = Object.keys(nodeStrats).filter(function (itr) { return nodeStrats[parseInt(itr)].nodeAlias === relStrat.target; })[0]; var sourceNode = nodeStrats[parseInt(sourceKey)]; var targetNode = nodeStrats[parseInt(targetKey)]; // For example, [ attr: toInteger({preparedParamName}), ... ] var sourceMergeStrat = Object.keys(sourceNode.mergeStrategy).map(function (dbAttr) { var propType = _this.findNodeAttrType(sourceNode, dbAttr); var sanitiser = _this.findSanitiser(propType); return dbAttr + ": " + sanitiser + "({" + sourceNode.mergeStrategy[dbAttr] + dbAttr + "})"; }); var targetMergeStrat = Object.keys(targetNode.mergeStrategy).map(function (dbAttr) { var propType = _this.findNodeAttrType(targetNode, dbAttr); var sanitiser = _this.findSanitiser(propType); return dbAttr + ": " + sanitiser + "({" + targetNode.mergeStrategy[dbAttr] + dbAttr + "})"; }); // For example, [ rel.attr = toInteger({prepareParamName}), ... ] var relCreateStrat = Object.keys(relStrat.createStrategy).map(function (dbAttr) { var propType = _this.findRelationshipAttrType(relStrat, dbAttr); var sanitiser = _this.findSanitiser(propType); return relStrat.relAlias + "." + dbAttr + " = " + sanitiser + " ({" + relStrat.createStrategy[dbAttr] + dbAttr + "})"; }); // Format for prepared query params: { param1: val1, param2: val2, ... } var cypherParams = {}; Object.keys(sourceNode.mergeStrategy).map(function (dbAttr) { cypherParams[sourceNode.mergeStrategy[dbAttr] + dbAttr] = _this.prepareCypherParams(eids, sourceNode.mergeStrategy[dbAttr]); }); Object.keys(targetNode.mergeStrategy).map(function (dbAttr) { cypherParams[targetNode.mergeStrategy[dbAttr] + dbAttr] = _this.prepareCypherParams(eids, targetNode.mergeStrategy[dbAttr]); }); Object.keys(relStrat.createStrategy).map(function (dbAttr) { cypherParams[relStrat.createStrategy[dbAttr] + dbAttr] = _this.prepareCypherParams(eids, relStrat.createStrategy[dbAttr]); }); // Handle direction var directionFrom = "-"; var directionTo = "-"; switch (relStrat.direction) { case "in": directionFrom = "<-"; break; case "out": default: directionTo = "->"; break; case "both": directionFrom = "<-"; directionTo = "->"; break; } // Join with ", " (EZ) var cypher = { query: "MERGE (" + sourceNode.nodeAlias + ":" + sourceNode.nodeType + " {" + sourceMergeStrat.join(", ") + "})\n" + "MERGE (" + targetNode.nodeAlias + ":" + targetNode.nodeType + " {" + targetMergeStrat.join(", ") + "})\n " + "MERGE (" + sourceNode.nodeAlias + ")" + directionFrom + "[" + relStrat.relAlias + ":" + relStrat.relType + "]" + directionTo + "(" + targetNode.nodeAlias + ") " + "ON CREATE SET " + relCreateStrat.join(", "), params: cypherParams }; // console.log(cypher); queries.push(cypher); }); }); return [4 /*yield*/, this.executeQueries(queries)]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; Neo4J.prototype.prepareCypherParams = function (eids, eidsKey) { var result = eids[eidsKey]; if (result) { return result; } errors.throwError({ type: "ERROR_DB_PERSIST" /* ERROR_DB_PERSIST */, reason: "propName '" + eidsKey + "' was not found in the EventInfoDataStruct. Check for DataExtractionStrategies", level: "warn", dump: { eids: eids, eidsKey: eidsKey } }); }; /** * Find the type of Node property * @param nodeStrat * @param propKey */ Neo4J.prototype.findNodeAttrType = function (nodeStrat, propKey) { try { var prop = this._models[nodeStrat.nodeType][propKey]; return prop.type; } catch (error) { errors.throwError({ type: "ERROR_DB_PERSIST" /* ERROR_DB_PERSIST */, level: "error", reason: "Could not find type for node property `" + propKey + "`. Check the coherence between DB model and PersistenceStrategies " }); } return undefined; }; /** * Find the type of Relationship property * @param relStrat * @param propKey */ Neo4J.prototype.findRelationshipAttrType = function (relStrat, propKey) { var _this = this; try { // Search in the models the model that contain the relationship var modelRel = Object.keys(this._models).filter(function (model) { return _this.findRelationshipModels(model).filter(function (relKey) { return _this._models[model][relKey].relationship === relStrat.relType; }); })[0]; var propType = this._models[modelRel][relStrat.relAlias].properties[propKey]; return propType; } catch (error) { errors.throwError({ type: "ERROR_DB_PERSIST" /* ERROR_DB_PERSIST */, level: "error", reason: "Could not find type for relationship property `" + propKey + "`. Check the coherence between DB model and PersistenceStrategies " }); } return undefined; }; /** * find the proper neo4j sanitiser for the incoming data as string * @param type the type in question. <a src="https://github.com/adam-cowley/neode#property-types">Reference</a> */ Neo4J.prototype.findSanitiser = function (type) { var sanitiser = ""; switch (type) { case "integer": sanitiser = "toInteger"; break; case "float": case "number": sanitiser = "toFloat"; break; default: break; } return sanitiser; }; Neo4J.prototype.findRelationshipModels = function (modelKey) { var model = this._models[modelKey]; var result = Object.keys(model).filter(function (m) { return (model[m].type === "relationship"); }).sort(); return result; }; Neo4J.prototype.findAttributesModels = function (modelKey) { var model = this._models[modelKey]; var result = Object.keys(model).filter(function (m) { return model[m].type !== "relationship"; }).sort(); return result; }; return Neo4J; }(Database_1.Database)); exports.Neo4J = Neo4J; exports.default = Neo4J;