argosjs
Version:
Ethereum smart-contract events visualiser
574 lines (573 loc) • 29.8 kB
JavaScript
"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;