import { ipcMain } from 'electron' ; import path from 'path' ; import fs from 'fs' ; import os from 'os' ; import helperFunctions from './helperFunctions' ; import generateDummyData from './DummyD/dummyDataMain' ; import { ColumnObj , DBList , DummyRecords } from './BE_types' ; const db = require ( './models' ) ; const { createDBFunc , dropDBFunc , explainQuery , runSQLFunc , runTARFunc , runFullCopyFunc , runHollowCopyFunc , promExecute , } = helperFunctions ; interface Feedback { type : string ; message : string ; } ipcMain . on ( 'return-db-list' , ( event ) => { db . getLists ( ) . then ( ( data ) => { event . sender . send ( 'db-lists' , data ) ; } ) . catch ( ( err ) => { const feedback = { type : 'error' , message : err , } ; event . sender . send ( 'feedback' , feedback ) ; } ) ; } ) ; ipcMain . handle ( 'select-db' , async ( event , dbName ) : Promise < void > => { event . sender . send ( 'async-started' ) ; try { await db . connectToDB ( dbName ) ; const dbsAndTables = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTables ) ; } finally { event . sender . send ( 'async-complete' ) ; } } ) ; ipcMain . handle ( 'drop-db' , async ( event , dbName , currDB ) : Promise < void > => { event . sender . send ( 'async-started' ) ; try { if ( currDB ) await db . connectToDB ( '' ) ; const dropDBScript = dropDBFunc ( dbName ) ; await db . query ( dropDBScript ) ; const dbsAndTables = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTables ) ; } finally { event . sender . send ( 'async-complete' ) ; } } ) ; interface DuplicatePayload { newName : string ; sourceDb : string ; withData : boolean ; } /**
 * Handle duplicate-db events sent from frontend. Cleans up after itself in
 * the event of failure
 */ ipcMain . handle ( 'duplicate-db' , async ( event , { newName , sourceDb , withData } ) => { event . sender . send ( 'async-started' ) ; const tempFilePath = path . resolve ( os . homedir ( ) , 'desktop' , `temp_${ newName }.sql` ) ; try { const dumpCmd = withData ? runFullCopyFunc ( sourceDb , tempFilePath ) : runHollowCopyFunc ( sourceDb , tempFilePath ) ; try { await promExecute ( dumpCmd ) ; } catch ( e ) { throw new Error ( `Failed to dump ${ sourceDb } to temp file at ${ tempFilePath }` ) ; } try { await db . query ( createDBFunc ( newName ) ) ; } catch ( e ) { throw new Error ( `Failed to create Database` ) ; } try { await promExecute ( runSQLFunc ( newName , tempFilePath ) ) ; } catch ( e ) { const dropDBScript = dropDBFunc ( newName ) ; await db . query ( dropDBScript ) ; throw new Error ( 'Failed to populate newly created database' ) ; } const dbsAndTableInfo = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTableInfo ) ; } finally { try { fs . unlinkSync ( tempFilePath ) ; } catch ( e ) { event . sender . send ( 'feedback' , { type : 'error' , message : `Failed to cleanup temp files. ${ tempFilePath } could not be removed.` , } ) ; } event . sender . send ( 'async-complete' ) ; } } ) ; interface ImportPayload { newDbName : string ; filePath : string ; } /**
 * Handle import-db events sent from frontend. Cleans up after itself
 * in the event of failure
 */ ipcMain . handle ( 'import-db' , async ( event , { newDbName , filePath } ) => { event . sender . send ( 'async-started' ) ; try { await db . query ( createDBFunc ( newDbName ) ) ; const ext = path . extname ( filePath ) . toLowerCase ( ) ; if ( ext !== '.sql' && ext !== '.tar' ) throw new Error ( 'Invalid file extension' ) ; const restoreCmd = ext === '.sql' ? runSQLFunc ( newDbName , filePath ) : runTARFunc ( newDbName , filePath ) ; try { await promExecute ( restoreCmd ) ; } catch ( e ) { const dropDBScript = dropDBFunc ( newDbName ) ; await db . query ( dropDBScript ) ; throw new Error ( 'Failed to populate database' ) ; } const dbsAndTableInfo = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTableInfo ) ; } finally { event . sender . send ( 'async-complete' ) ; } } ) ; interface QueryPayload { targetDb : string ; sqlString : string ; selectedDb : string ; } ipcMain . handle ( 'run-query' , async ( event , { targetDb , sqlString , selectedDb } ) => { event . sender . send ( 'async-started' ) ; try { let error ; if ( selectedDb !== targetDb ) await db . connectToDB ( targetDb ) ; let explainResults ; try { const results = await db . query ( explainQuery ( sqlString ) ) ; explainResults = results [ 1 ] . rows ; } catch ( e ) { error = `Failed to get Execution Plan. EXPLAIN might not support this query.` ; } let returnedRows ; try { const results = await db . query ( sqlString ) ; returnedRows = results . rows ; } catch ( e ) { error = e . toString ( ) ; } return { db : targetDb , sqlString , returnedRows , explainResults , error , } ; } finally { if ( selectedDb !== targetDb ) await db . connectToDB ( selectedDb ) ; const dbsAndTables = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTables ) ; event . sender . send ( 'async-complete' ) ; } } ) ; interface ExportPayload { sourceDb : string ; } ipcMain . handle ( 'export-db' , async ( event , { sourceDb } ) => { event . sender . send ( 'async-started' ) ; const FilePath = path . resolve ( os . homedir ( ) , 'desktop' , `${ sourceDb }.sql` ) ; let feedback = { type : '' , message : '' , } ; try { const dumpCmd = runFullCopyFunc ( sourceDb , FilePath ) ; try { await promExecute ( dumpCmd ) ; feedback = { type : 'success' , message : `${ sourceDb } Schema successfully exported to ${ FilePath }` } ; event . sender . send ( 'feedback' , feedback ) ; } catch ( e ) { throw new Error ( `Failed to dump ${ sourceDb } to a file at ${ FilePath }` ) ; } } finally { event . sender . send ( 'async-complete' ) ; } } ) ; interface dummyDataRequestPayload { dbName : string ; tableName : string ; rows : number ; } ipcMain . handle ( 'generate-dummy-data' , async ( event , data ) => { event . sender . send ( 'async-started' ) ; let feedback = { type : '' , message : '' , } ; try { const tableInfo = await db . getTableInfo ( data . tableName ) ; const dummyArray = await generateDummyData ( tableInfo , data . rows ) ; const columnsStringified = '(' . concat ( dummyArray [ 0 ] . join ( ', ' ) ) . concat ( ')' ) ; let insertQuery = `INSERT INTO ${ data . tableName } ${ columnsStringified } VALUES ` ; for ( let i = 1 ; i < dummyArray . length - 1 ; i += 1 ) { const recordStringified = '(' . concat ( dummyArray [ i ] . join ( ', ' ) ) . concat ( '), ' ) ; insertQuery = insertQuery . concat ( recordStringified ) ; } const lastRecordStringified = '(' . concat ( dummyArray [ dummyArray . length - 1 ] . join ( ', ' ) ) . concat ( ');' ) ; insertQuery = insertQuery . concat ( lastRecordStringified ) ; await db . query ( 'Begin;' ) ; await db . query ( insertQuery ) ; await db . query ( 'Commit;' ) ; feedback = { type : 'success' , message : 'Dummy data successfully generated.' , } ; } catch ( err ) { await db . query ( 'Rollback;' ) ; feedback = { type : 'error' , message : err , } ; } finally { const dbsAndTables = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTables ) ; event . sender . send ( 'feedback' , feedback ) ; event . sender . send ( 'async-complete' ) ; } } ) ; interface InitializePayload { newDbName : string ; } ipcMain . handle ( 'initialize-db' , async ( event , { newDbName } ) => { event . sender . send ( 'async-started' ) ; try { await db . query ( createDBFunc ( newDbName ) ) ; await db . connectToDB ( newDbName ) ; const dbsAndTableInfo = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTableInfo ) ; } catch ( e ) { const dropDBScript = dropDBFunc ( newDbName ) ; await db . query ( dropDBScript ) ; throw new Error ( 'Failed to initialize new database' ) ; } finally { event . sender . send ( 'async-complete' ) ; } } ) ; interface UpdatePayload { sqlString : string ; selectedDb : string ; } ipcMain . handle ( 'update-db' , async ( event , { sqlString , selectedDb } ) => { event . sender . send ( 'async-started' ) ; try { let error ; await db . connectToDB ( selectedDb ) ; try { await db . query ( sqlString ) ; } catch ( e ) { if ( e ) throw new Error ( 'Failed to update schema' ) ; } } finally { const dbsAndTables = await db . getLists ( ) ; event . sender . send ( 'db-lists' , dbsAndTables ) ; event . sender . send ( 'async-complete' ) ; } } ) ;