#!/usr/bin/env ts-node-script 'use strict'; const _ = require('underscore'); const lodash = require('lodash'); const merge = require('merge-deep'); //https://github.com/jonschlinkert/merge-deep const jsonMerger = require("json-merger"); //https://github.com/boschni/json-merger const Graph = require("./utils/graph-data-structure"); const fileStream = require('fs'); const path = require('path'); const schema = require('./templates/schema'); // @ts-ignore import MongoDBSchemaGraph from "./MongoDBSchemaGraph"; // @ts-ignore import OracleSchemaGraph from "./OracleSchemaGraph"; // @ts-ignore import MySqlDBSchemaGraph from "./MySqlSchemaGraph"; // @ts-ignore import PostgresSchemaGraph from "./PostgresSchemaGraph"; // @ts-ignore import MSSQLSchemaGraph from "./MSSQLSchemaGraph"; // @ts-ignore import DB2SchemaGraph from "./DB2SchemaGraph"; // @ts-ignore import MongoDBDatabaseDiscovery from "./MongoDBDatabaseDiscovery"; // @ts-ignore import MongoDBDatabaseDiscoveryAdapter from "./MongoDBDatabaseDiscoveryAdapter"; // @ts-ignore import MySQLDatabaseDiscovery from "./MySQLDatabaseDiscovery"; // @ts-ignore import MySQLDatabaseDiscoveryAdapter from "./MySQLDatabaseDiscoveryAdapter"; // @ts-ignore import PostgreSQLDatabaseDiscovery from "./PostgreSQLDatabaseDiscovery"; // @ts-ignore import PostgreSQLDatabaseDiscoveryAdapter from "./PostgreSQLDatabaseDiscoveryAdapter"; // @ts-ignore import OracleDatabaseDiscovery from "./OracleDatabaseDiscovery"; // @ts-ignore import OracleDatabaseDiscoveryAdapter from "./OracleDatabaseDiscoveryAdapter"; // @ts-ignore import MSSQLDatabaseDiscovery from "./MSSQLDatabaseDiscovery"; // @ts-ignore import MSSQLDatabaseDiscoveryAdapter from "./MSSQLDatabaseDiscoveryAdapter"; // @ts-ignore import DB2DatabaseDiscovery from "./DB2DatabaseDiscovery"; // @ts-ignore import DB2DatabaseDiscoveryAdapter from "./DB2DatabaseDiscoveryAdapter"; // @ts-ignore import Converter from './utils/Converter'; // @ts-ignore import FileHelper from "./utils/FileHelper"; const getEdge = (node1, node2) => { let edgeNames = node1.filter(function(val) { return node2.indexOf(val) != -1; }); return edgeNames; } const restructureSchema = (extractedSchema) => { let dbKeys = Object.keys(extractedSchema); //console.log(keys); let nodesJson = {} let nodesArray = []; //console.log(extractedSchema); for(let dbKey of dbKeys ) { let tableKeys = Object.keys(extractedSchema[dbKey]); for(let tableKey of tableKeys) { nodesJson['databaseName'] = dbKey; nodesJson['className'] = tableKey; nodesJson['fields']=extractedSchema[dbKey][tableKey]; //console.log(nodesJson); nodesArray.push(nodesJson); nodesJson = {}; } } return nodesArray; } const addReversedEdges = (graphEdges) => { const edgeKeys = Object.keys(graphEdges); let reversedEdges = []; for(let edgeKey of edgeKeys) { let edgeName = ''; let edges = graphEdges[edgeKey]; for(let edge of edges) { let reversedEdge = lodash.cloneDeep(edge); reversedEdge['source'] = edge['target']; reversedEdge['target'] = edge['source']; edgeName = reversedEdge['source']; reversedEdges.push(reversedEdge); } } //group by source return lodash.groupBy(reversedEdges,'source'); } //This method is used to add fields MongoDb schema //it applies neccessary fields yet removing unnecessary fields const convertMongoSchema = (extractedSchema) => { let dbKeys = Object.keys(extractedSchema); //console.log(dbKeys); for(let dbKey of dbKeys) { let tableKeys = Object.keys(extractedSchema[dbKey]); //console.log(tableKeys); for(let tableKey of tableKeys) { let fieldKeys = Object.keys(extractedSchema[dbKey][tableKey]); //console.log(fieldKeys); for(let fieldKey of fieldKeys ) { //console.log(fieldKey + "\t" + tableKey); let type = extractedSchema[dbKey][tableKey][fieldKey]['type']; let allowNull = extractedSchema[dbKey][tableKey][fieldKey]['allowNull']; let primaryKey = extractedSchema[dbKey][tableKey][fieldKey]['primaryKey']; //console.log(type + "\t" + allowNull + "\t" + primaryKey); //add mongodb fields let mongodbFields = {}; //let trimmedType = type.replace(/\s*\(.*?\)\s*/g, ''); //remove (text inside brackets), eg. VARCHAR(10) -> VARCHAR //console.log(trimedType + "\t" + type); mongodbFields['schemaName'] = tableKey; mongodbFields['fieldName'] = fieldKey; mongodbFields['primaryKey'] = primaryKey; mongodbFields['type'] = type; //mongodbFields['required'] = !allowNull; //swap to mongofields extractedSchema[dbKey][tableKey][fieldKey] = mongodbFields; //console.log(extractedSchema); extractedSchema = schema.insertTemplateToSchema(extractedSchema); } } } //console.log("*****************convertMongoSchema"); //console.log(JSON.stringify(extractedSchema,null,4)); return extractedSchema; } //This method is used ito convert mysql schema to MongoDb schema //it applies neccessary fields yet removing unnecessary fields const convertMySQLSchema = (extractedSchema) => { let dbKeys = Object.keys(extractedSchema); //console.log(dbKeys); for(let dbKey of dbKeys) { let tableKeys = Object.keys(extractedSchema[dbKey]); //console.log(tableKeys); for(let tableKey of tableKeys) { let fieldKeys = Object.keys(extractedSchema[dbKey][tableKey]); for (let fieldKey of fieldKeys) { //console.log(fieldKey + "\t" + tableKey); let type = extractedSchema[dbKey][tableKey][fieldKey]['type']; let allowNull = extractedSchema[dbKey][tableKey][fieldKey]['allowNull']; let primaryKey = extractedSchema[dbKey][tableKey][fieldKey]['primaryKey']; //console.log(type + "\t" + allowNull + "\t" + primaryKey); //add mongodb fields let mongodbFields = {}; let trimmedType = type.replace(/\s*\(.*?\)\s*/g, ''); //remove (text inside brackets), eg. VARCHAR(10) -> VARCHAR //console.log(trimedType + "\t" + type); mongodbFields['schemaName'] = tableKey; mongodbFields['fieldName'] = fieldKey; mongodbFields['primaryKey'] = primaryKey; let translatedDataType = schema.translateDatatype("MySql", trimmedType); //translatedDataType = translatedDataType.replace(/^"(.*)"$/, '$1'); //remove quotes if(translatedDataType !== undefined) { mongodbFields['type'] = translatedDataType; } //mongodbFields['required'] = !allowNull; //swap to mongofields extractedSchema[dbKey][tableKey][fieldKey] = mongodbFields; extractedSchema = schema.insertTemplateToSchema(extractedSchema); } } } return extractedSchema; } //This method is used to convert oracle schema to MongoDb schema //it applies neccessary fields yet removing unnecessary fields const convertOracleSchema = (extractedSchema) => { let dbKeys = Object.keys(extractedSchema); //console.log(dbKeys); for(let dbKey of dbKeys) { let tableKeys = Object.keys(extractedSchema[dbKey]); //console.log(tableKeys); for (let tableKey of tableKeys) { let fieldKeys = Object.keys(extractedSchema[dbKey][tableKey]); for (let fieldKey of fieldKeys) { //console.log(fieldKey + "\t" + tableKey); let type = extractedSchema[dbKey][tableKey][fieldKey]['dbTypeName']; let allowNull = extractedSchema[dbKey][tableKey][fieldKey]['nullable']; let primaryKey = extractedSchema[dbKey][tableKey][fieldKey]['primaryKey']; //console.log(type + "\t" + allowNull + "\t" + primaryKey); //add mongodb fields let mongodbFields = {}; let trimmedType = type.replace(/\s*\(.*?\)\s*/g, ''); //remove (text inside brackets), eg. VARCHAR(10) -> VARCHAR //console.log(trimedType + "\t" + type); mongodbFields['schemaName'] = tableKey; mongodbFields['fieldName'] = fieldKey; mongodbFields['primaryKey'] = (fieldKey === primaryKey); let translatedDataType = schema.translateDatatype("Oracle", trimmedType); if(translatedDataType !== undefined) { mongodbFields['type'] = translatedDataType; } mongodbFields['required'] = !allowNull; //swap to mongofields extractedSchema[dbKey][tableKey][fieldKey] = mongodbFields; extractedSchema = schema.insertTemplateToSchema(extractedSchema); } } } return extractedSchema; } //This method is used ito convert mysql schema to MongoDb schema //it applies neccessary fields yet removing unnecessary fields const convertMSSQLSchema = (extractedSchemas) => { extractedSchemas.forEach(extractedSchema => { let tableNames = Object.keys(extractedSchema.fields); for(let j=0; j VARCHAR let translatedDataType = schema.translateDatatype("MSSQL", trimmedType); if(translatedDataType !== undefined) { extractedSchema.fields[tableNames[j]].type = translatedDataType; } } }) //console.log(JSON.stringify(extractedSchemas,null,3)); return extractedSchemas; } //This method is used ito convert Postgres schema to MongoDb schema //it applies neccessary fields yet removing unnecessary fields const convertPostgresSQLSchema = (extractedSchemas) => { //console.log(JSON.stringify(extractedSchemas,null,3)); extractedSchemas.forEach(extractedSchema => { let tableNames = Object.keys(extractedSchema.fields); for(let j=0; j VARCHAR let translatedDataType = schema.translateDatatype("Postgres", trimmedType); if(translatedDataType !== undefined) { extractedSchema.fields[tableNames[j]].type = translatedDataType; } } }) //console.log(JSON.stringify(extractedSchemas,null,3)); return extractedSchemas; } //This method is used ito convert DB2 schema to MongoDb schema //it applies neccessary fields yet removing unnecessary fields const convertDB2SQLSchema = (extractedSchemas) => { //console.log(JSON.stringify(extractedSchemas,null,3)); extractedSchemas.forEach(extractedSchema => { let tableNames = Object.keys(extractedSchema.fields); for(let j=0; j VARCHAR let translatedDataType = schema.translateDatatype("DB2", trimmedType); if(translatedDataType !== undefined) { extractedSchema.fields[tableNames[j]].type = translatedDataType; } } }) //console.log(JSON.stringify(extractedSchemas,null,3)); return extractedSchemas; } //-------------------------------extract mongodb schema------------------------------------------------------------ export const extractMongoSchema = async(MongoConfiguration, docNames:string) => { let databaseDiscovery = new MongoDBDatabaseDiscoveryAdapter(new MongoDBDatabaseDiscovery(MongoConfiguration)); let extractedMongoDbSchema; let convertedSchema; let restructuredSchema; try { extractedMongoDbSchema = await databaseDiscovery.extractDatabaseSchema(docNames); //console.log(extractedMongoDbSchema); convertedSchema = convertMongoSchema(extractedMongoDbSchema); //console.log(convertedSchema); //restructuredSchema = restructureSchema(extractedMongoDbSchema); //console.log("*****************" + docNames); restructuredSchema = restructureSchema(convertedSchema); //console.log(JSON.stringify(restructuredSchema,null,4)); const mongoDBSchemaGraph = new MongoDBSchemaGraph(); const edgesAndNodesCombined = mongoDBSchemaGraph.getEdgesAndNodes(extractedMongoDbSchema); //console.log(edgesAndNodesCombined); let uniqueEdgeNames = mongoDBSchemaGraph.getUniqueEdgeNames(edgesAndNodesCombined); //console.log(uniqueEdgeNames); let nodesGraph = {nodes: {}}; //nodesGraph.nodes = extractedMongoDbSchema; nodesGraph.nodes = restructuredSchema; let edgesJson = mongoDBSchemaGraph.getEdgesGraph(uniqueEdgeNames, edgesAndNodesCombined); //console.log(JSON.stringify(edgesJson,null,4)); let schemaGraph = jsonMerger.mergeObjects([nodesGraph, edgesJson]); //console.log(JSON.stringify(schemaGraph,null,4)); //get the Ed version of the edge graph const converter = new Converter(); let edEdgesGraph = converter.convertToEdEdgesGraph(uniqueEdgeNames, edgesJson); //console.log(JSON.stringify(edEdgesGraph,null,4)); //to comply with Ed Schema Graph let schemaEdGraph = jsonMerger.mergeObjects([nodesGraph, edEdgesGraph]); return schemaEdGraph; } catch (error) { console.log(error); } } //--------------------------------extract mysql schema --------------------------------------------------------- export const extractMySqlSchema = async(MySQLConfiguration, dbName:string) => { let databaseDiscovery = new MySQLDatabaseDiscoveryAdapter(new MySQLDatabaseDiscovery(MySQLConfiguration)); let restructuredSchema; let convertedSchema; try { let extractedMySqlSchema = await databaseDiscovery.extractDatabaseSchema(dbName); //console.log(JSON.stringify(extractedMySqlSchema,null,4)); convertedSchema = convertMySQLSchema(extractedMySqlSchema); //console.log(convertedSchema); restructuredSchema = restructureSchema(convertedSchema); //console.log(JSON.stringify(restructuredSchema,null,4)); const foreignKeyReferences = databaseDiscovery.getForeignKeyReferences(); //console.log(JSON.stringify(foreignKeyReferences, null, 4)); const mySqlDBSchemaGraph = new MySqlDBSchemaGraph(); let edgesAndNodesCombined = mySqlDBSchemaGraph.getEdgesAndNodes(foreignKeyReferences); //console.log(edgesAndNodesCombined); let uniqueEdgeNames = mySqlDBSchemaGraph.getUniqueEdgeNames(edgesAndNodesCombined); //console.log(uniqueEdgeNames); let nodesGraph = {nodes: {}}; //nodesGraph.nodes = extractedMySqlSchema; nodesGraph.nodes = restructuredSchema; //console.log(nodesGraph); let edgesJson = mySqlDBSchemaGraph.getEdgesGraph(uniqueEdgeNames, edgesAndNodesCombined); //console.log(JSON.stringify(edgesJson,null,4)); let schemaGraph = jsonMerger.mergeObjects([nodesGraph, edgesJson]); //console.log(JSON.stringify(schemaGraph,null,4)); //get the Ed version of the edge graph const converter = new Converter(); let edEdgesGraph = converter.convertToEdEdgesGraph(uniqueEdgeNames, edgesJson); //add the reverse edges and then combined it to the orginal edges let reversedEdges = addReversedEdges(edEdgesGraph['edges']); edEdgesGraph['edges'] = merge(edEdgesGraph['edges'],reversedEdges); //console.log(JSON.stringify(edEdgesGraph,null,4)); //to comply with Ed Schema Graph let schemaEdGraph = jsonMerger.mergeObjects([nodesGraph, edEdgesGraph]); return schemaEdGraph; } catch (error) { console.log(error); } } //------------------ extract postgres schema ----------------------------------------------------------------------- const extractPostgresSchema = async(PostgreSQLConfiguration, dbName) => { //console.log(PostgreSQLConfiguration) let databaseDiscovery = new PostgreSQLDatabaseDiscoveryAdapter(new PostgreSQLDatabaseDiscovery(PostgreSQLConfiguration)); try { let extractedSchema = await databaseDiscovery.extractDatabaseSchema(dbName); //console.log(JSON.stringify(extractedSchema, null, 4)); let foreignKeys = await databaseDiscovery.getForeignKeyReferences(extractedSchema) //console.log(JSON.stringify(foreignKeys, null , 3)); const postgresSchemaGraph = new PostgresSchemaGraph(); //build the edges let edges = await postgresSchemaGraph.buildEdges(foreignKeys) //console.log(JSON.stringify(edges,null,3)) //get the reverse edges let bidirectionalEdges = await postgresSchemaGraph.generateBidirectionalEdges(edges) //console.log(JSON.stringify(edges, null,3)) extractedSchema = schema.buildPostgresSchema(extractedSchema); //console.log(JSON.stringify(extractedSchema, null , 3)); //convert the datatype to the mongo datatype because we use mongo datatype as the standard extractedSchema = convertPostgresSQLSchema(extractedSchema); let nodesGraph = { nodes: [], edges: {} }; nodesGraph.nodes = extractedSchema; nodesGraph.edges = bidirectionalEdges return nodesGraph; } catch (error) { console.log(error); } } //------------------ extract DB2 schema ----------------------------------------------------------------------- const extractDB2Schema = async(DB2Configuration, dbName) => { //console.log(PostgreSQLConfiguration) let databaseDiscovery = new DB2DatabaseDiscoveryAdapter(new DB2DatabaseDiscovery(DB2Configuration)); try { let extractedSchema = await databaseDiscovery.extractDatabaseSchema(dbName); //console.log(JSON.stringify(extractedSchema, null, 4)); let foreignKeys = await databaseDiscovery.getForeignKeyReferences(extractedSchema) //console.log(JSON.stringify(foreignKeys, null , 3)); const db2SchemaGraph = new DB2SchemaGraph(); //build the edges let edges = await db2SchemaGraph.buildEdges(foreignKeys) //console.log(JSON.stringify(edges,null,3)) //get the reverse edges let bidirectionalEdges = await db2SchemaGraph.generateBidirectionalEdges(edges) //console.log(JSON.stringify(edges, null,3)) extractedSchema = schema.buildDB2Schema(extractedSchema); //console.log(JSON.stringify(extractedSchema, null , 3)); //convert the datatype to the mongo datatype because we use mongo datatype as the standard extractedSchema = convertDB2SQLSchema(extractedSchema); let nodesGraph = { nodes: [], edges: {} }; nodesGraph.nodes = extractedSchema; nodesGraph.edges = bidirectionalEdges return nodesGraph; } catch (error) { console.log(error); } } //------------------ extract MSSQL schema ----------------------------------------------------------------------- const extractMSSQLSchema = async(MSSQLConfiguration, dbName) => { //console.log(PostgreSQLConfiguration) let databaseDiscovery = new MSSQLDatabaseDiscoveryAdapter(new MSSQLDatabaseDiscovery(MSSQLConfiguration)); try { const mssqlSchemaGraph = new MSSQLSchemaGraph(); //Create a node by geting the combined extracedMSSQLSchema from each table //inside already build the schemas let extractedSchema = await databaseDiscovery.extractDatabaseSchema(); //console.log(JSON.stringify(extractedSchema, null, 3)); //convert the datatype to the mongo datatype because we use mongo datatype as the standard extractedSchema = convertMSSQLSchema(extractedSchema); //console.log(extractedSchema); let foreignKeys = await databaseDiscovery.getForeignKeyReferences(MSSQLConfiguration) //console.log(JSON.stringify(foreignKeys, null , 3)); //build the edges let edges = await mssqlSchemaGraph.buildEdges(foreignKeys) //console.log(JSON.stringify(edges,null,3)) //get the reverse edges let bidirectionalEdges = await mssqlSchemaGraph.generateBidirectionalEdges(edges) //console.log(JSON.stringify(bidirectionalEdges, null,3)) let nodesGraph = { nodes: [], edges: {} }; nodesGraph.nodes = extractedSchema; nodesGraph.edges = bidirectionalEdges return nodesGraph; } catch (error) { console.log(error); } } //------------------ extract Oracle schema ----------------------------------------------------------------------- export const extractOracleSchema = async(OracleConfiguration, owner) => { let databaseDiscovery = new OracleDatabaseDiscoveryAdapter(new OracleDatabaseDiscovery(OracleConfiguration)); let convertedSchema; let restructuredSchema; try { const extractedOracleSchema = await databaseDiscovery.extractDatabaseSchema(owner); //console.log(extractedOracleSchema); convertedSchema = convertOracleSchema(extractedOracleSchema); //console.log(convertedSchema); //restructuredSchema = restructureSchema(extractedOracleSchema); restructuredSchema = restructureSchema(convertedSchema); const foreignKeyReferences = await databaseDiscovery.getForeignKeyReferences(OracleConfiguration); //console.log(JSON.stringify(foreignKeyReferences, null, 4)); const oracleSchemaGraph = new OracleSchemaGraph(); //refer to oracleSchemaGraph->getForeignKeyReferences let edgesAndNodesCombined = oracleSchemaGraph.getEdgesAndNodes(foreignKeyReferences['rows']); //console.log(edgesAndNodesCombined); let uniqueEdgeNames = oracleSchemaGraph.getUniqueEdgeNames(edgesAndNodesCombined); //console.log(uniqueEdgeNames); let nodesGraph = { nodes: { } }; //nodesGraph.nodes = extractedOracleSchema; nodesGraph.nodes = restructuredSchema; //console.log(nodesGraph); let edgesJson = oracleSchemaGraph.getEdgesGraph(uniqueEdgeNames,edgesAndNodesCombined); //console.log(JSON.stringify(edgesJson,null,4)); let schemaGraph = jsonMerger.mergeObjects([nodesGraph, edgesJson]); //console.log(JSON.stringify(schemaGraph,null,4)); //get the Ed version of the edge graph const converter = new Converter(); let edEdgesGraph = converter.convertToEdEdgesGraph(uniqueEdgeNames, edgesJson); //add the reverse edges and then combined it to the orginal edges let reversedEdges = addReversedEdges(edEdgesGraph['edges']); edEdgesGraph['edges'] = merge(edEdgesGraph['edges'],reversedEdges); //console.log(JSON.stringify(edEdgesGraph,null,4)); //to comply with Ed Schema Graph let schemaEdGraph = jsonMerger.mergeObjects([nodesGraph, edEdgesGraph]); //console.log(JSON.stringify(schemaEdGraph,null,4)); return schemaEdGraph; } catch (error) { console.log(error); } } //Generate a schema based on (multiple) DBMS const generateSchema = async(configurationJson, dirName) => { const fileHelper = new FileHelper(); let schemaFileNames = []; let schemaFileName; let fileNamePrefix = "schema_"; let schemas={}; let connections = JSON.parse(fileHelper.readFile(configurationJson)) const SCHEMA_FCN = { MONGODB : extractMongoSchema, MYSQL : extractMySqlSchema, ORACLE : extractOracleSchema, POSTGRES: extractPostgresSchema, MSSQL : extractMSSQLSchema, DB2 : extractDB2Schema, } let i=0; for(let connection of connections.configuration){ console.log(connection); schemaFileName = `${fileNamePrefix}${i}.json` const DBMS = connection['DBMS'].toUpperCase(); const databaseNames = connection['DATABASE'] ? connection['DATABASE'] : connection['OWNER'] ; let schema = await SCHEMA_FCN[DBMS](connection,databaseNames); //console.log(schema) //write out individual files, //schemaFileNames.push(path.join(dirName, schemaFileName)); //fileHelper.writeFile(path.join(dirName, schemaFileName), schema); //combine every schema into one schema schemas = jsonMerger.mergeObjects([schemas,schema],{defaultArrayMergeOperation: "concat"}); i++; } //combine every schema file from every connection string into one schema file //schemas = jsonMerger.mergeFiles(schemaFileNames, {defaultArrayMergeOperation: "concat"}); //console.log(JSON.stringify(schemas, null,3)) return schemas; } const harvestSchema = (configFile, dirName, schemaFilename) => { const fileHelper = new FileHelper(); let connections = fileHelper.readFile(configFile); if(!fileHelper.isJsonString(connections)) { console.log("Config file has an invalid Json String"); return; } if (!fileStream.existsSync(dirName)) { console.log("Directory does not exist"); return; } //starts creating schema files and combine into one schema (async () => { console.log('Start creating a schema file') let schemas = await generateSchema(configFile,dirName); fileHelper.writeFile(path.join(dirName, schemaFilename ), JSON.stringify(schemas,null,3)); console.log(`${schemaFilename} has been created`); })(); } /******************** Main Entry Point ******************************************/ // console.log(process.argv[0] + "\n" + process.argv[1] + "\n" + process.argv[2] + "\n" + // process.argv[3] + "\n" + process.argv[4] + "\n" + process.argv[5]) if(!process.argv[2] || !process.argv[3] || !process.argv[4] ) { console.log('Please supply three arguments: configFile, outputDirectory and the outputFile'); } else if( process.argv.length == 5 && process.argv[1].includes('MetaModelBuilder.ts')) { //npm run penta-data-mapping schema_config.json schemas schema1_all.json harvestSchema(process.argv[2], process.argv[3], process.argv[4]); } else if( process.argv.length == 5 && process.argv[1].includes('penta-data-mapping')) { //npx penta-data-mapping schema_config.json schemas schema1_all.json harvestSchema(process.argv[2], process.argv[3], process.argv[4]); }