import * as IPCMessages from '../../../../common/ipc/plugins.ipc.messages/index' ; import { ChildProcess } from 'child_process' ; import { EventEmitter } from 'events' ; import { IPCMessagePackage } from './plugin.process.ipc.messagepackage' ; import { guid , Subscription , THandler } from '../../tools/index' ; import { Entry , getElement , IField , getEntryKeyByArgs } from '../../services/service.settings' ; import { IControllerIPCPlugin } from './plugin.process.ipc.interface' ; import { PluginField } from './plugin.process.setting.field' ; import ServiceConfig from '../../services/service.settings' ; import ServiceElectron from '../../services/service.electron' ; import Logger from '../../tools/env.logger' ; export { IPCMessages } ; export default class ControllerIPCPlugin extends EventEmitter implements IControllerIPCPlugin { private _logger : Logger ; private _pluginName : string ; private _token : string ; private _process : ChildProcess ; private _pending : Map < string , ( message : IPCMessages . TMessage ) => any > = new Map ( ) ; private _subscriptions : Map < string , Subscription > = new Map ( ) ; private _handlers : Map < string , Map < string , THandler > > = new Map ( ) ; constructor ( pluginName , process , token ) { super ( ) ; this . _pluginName = pluginName ; this . _process = process ; this . _token = token ; this . _logger = new Logger ( `plugin IPC: ${ this . _pluginName }` ) ; this . _process . on ( 'message' , this . _onMessage . bind ( this ) ) ; this . subscribe ( IPCMessages . SettingsRegisterRequest , this . _ipc_onSettingsRegisterRequest . bind ( this ) ) . then ( ( subscription ) => { this . _subscriptions . set ( 'SettingsRegisterRequest' , subscription ) ; } ) ; this . subscribe ( IPCMessages . SettingsGetRequest , this . _ipc_onSettingsGetRequest . bind ( this ) ) . then ( ( subscription ) => { this . _subscriptions . set ( 'SettingsGetRequest' , subscription ) ; } ) ; } param { IPCMessages . TMessage } message param { string } sequence returns { Promise < void > } public send ( message , sequence ? ) { return new Promise ( ( resolve , reject ) => { const ref = this . _getRefToMessageClass ( message ) ; if ( ref === undefined ) { return reject ( new Error ( `Incorrect type of message` ) ) ; } const messagePackage = new IPCMessagePackage ( { message : message , sequence : sequence , } ) ; this . _send ( messagePackage ) . then ( ( ) => { resolve ( undefined ) ; } ) . catch ( ( sendingError ) => { reject ( sendingError ) ; } ) ; } ) ; } public response ( sequence , message ) { return new Promise ( ( resolve , reject ) => { const ref = this . _getRefToMessageClass ( message ) ; if ( ref === undefined ) { return reject ( new Error ( `Incorrect type of message` ) ) ; } const messagePackage = new IPCMessagePackage ( { message : message , sequence : sequence , } ) ; this . _send ( messagePackage ) . then ( ( ) => { resolve ( undefined ) ; } ) . catch ( ( sendingError ) => { reject ( sendingError ) ; } ) ; } ) ; } param { IPCMessages . TMessage } message returns { Promise < IPCMessages . TMessage | undefined > } public request ( message ) { return new Promise ( ( resolve , reject ) => { const ref = this . _getRefToMessageClass ( message ) ; if ( ref === undefined ) { return reject ( new Error ( `Incorrect type of message` ) ) ; } const messagePackage = new IPCMessagePackage ( { message : message , } ) ; this . _send ( messagePackage , true ) . then ( ( response ) => { resolve ( response ) ; } ) . catch ( ( sendingError ) => { reject ( sendingError ) ; } ) ; } ) ; } public subscribe ( message , handler ) { return new Promise ( ( resolve , reject ) => { if ( ! this . _isValidMessageClassRef ( message ) ) { return reject ( new Error ( `Incorrect reference to message class.` ) ) ; } const signature = ( message as any ) . signature ; const subscriptionId = guid ( ) ; let handlers = this . _handlers . get ( signature ) ; if ( handlers === undefined ) { handlers = new Map ( ) ; } handlers . set ( subscriptionId , handler ) ; this . _handlers . set ( signature , handlers ) ; const subscription = new Subscription ( signature , ( ) => { this . _unsubscribe ( signature , subscriptionId ) ; } , subscriptionId ) ; this . _subscriptions . set ( subscriptionId , subscription ) ; resolve ( subscription ) ; } ) ; } public destroy ( ) { this . _process . removeAllListeners ( 'message' ) ; this . _handlers . clear ( ) ; this . _subscriptions . clear ( ) ; this . _pending . clear ( ) ; } param { IPCMessagePackage } data param { boolean } expectResponse returns { Promise < IPCMessagePackage | undefined > } private _send ( message , expectResponse = false ) { return new Promise ( ( resolve , reject ) => { if ( ! this . _process . send ) { return reject ( new Error ( this . _logger . error ( `IPC isn't available` ) ) ) ; } if ( ! ( message instanceof IPCMessagePackage ) ) { return reject ( new Error ( this . _logger . error ( `Expecting as message instance of IPCMessagePackage` ) ) ) ; } if ( expectResponse ) { this . _pending . set ( message . sequence , resolve ) ; } this . _process . send ( message , ( error ) => { if ( error ) { this . _logger . warn ( `Error while sending message to plugin: ${ error . message }` ) ; return reject ( error ) ; } if ( ! expectResponse ) { return resolve ( undefined ) ; } } ) ; } ) ; } returns private _onMessage ( data ) { try { const message = new IPCMessagePackage ( data ) ; const resolver = this . _pending . get ( message . sequence ) ; this . _pending . delete ( message . sequence ) ; const refMessageClass = this . _getRefToMessageClass ( message . message ) ; if ( refMessageClass === undefined ) { throw new Error ( `Cannot find ref to class of message` ) ; } const instance = new ( refMessageClass as any ) ( message . message ) ; if ( resolver !== undefined ) { return resolver ( instance ) ; } else if ( instance instanceof IPCMessages . PluginInternalMessage || instance instanceof IPCMessages . PluginError ) { this . _redirectToPluginHost ( instance , message . sequence ) ; } else { const handlers = this . _handlers . get ( instance . signature ) ; if ( handlers === undefined ) { return ; } handlers . forEach ( ( handler ) => { handler ( instance , this . response . bind ( this , message . sequence ) ) ; } ) ; } } catch ( e ) { this . _logger . error ( `Incorrect format of IPC message: ${ typeof data }. Error: ${ e . message }` ) ; } } private _redirectToPluginHost ( message , sequence ? ) { ServiceElectron . redirectIPCMessageToPluginRender ( message , sequence ) ; } private _getRefToMessageClass ( message ) { let ref ; Object . keys ( IPCMessages . Map ) . forEach ( ( alias ) => { if ( ref ) { return ; } if ( message instanceof ( IPCMessages . Map as any ) [ alias ] || message . signature === ( IPCMessages . Map as any ) [ alias ] . signature ) { ref = ( IPCMessages . Map as any ) [ alias ] ; } } ) ; return ref ; } private _isValidMessageClassRef ( messageRef ) { let result = false ; if ( typeof ( messageRef as any ) . signature !== 'string' || ( messageRef as any ) . signature . trim ( ) === '' ) { return false ; } Object . keys ( IPCMessages . Map ) . forEach ( ( alias ) => { if ( result ) { return ; } if ( ( messageRef as any ) . signature === ( IPCMessages . Map as any ) [ alias ] . signature ) { result = true ; } } ) ; return result ; } private _unsubscribe ( signature , subscriptionId ) { this . _subscriptions . delete ( subscriptionId ) ; const handlers = this . _handlers . get ( signature ) ; if ( handlers === undefined ) { return ; } handlers . delete ( subscriptionId ) ; if ( handlers . size === 0 ) { this . _handlers . delete ( signature ) ; } else { this . _handlers . set ( signature , handlers ) ; } } private _ipc_onSettingsRegisterRequest ( message , response ) { const request = message as IPCMessages . SettingsRegisterRequest < any > ; const inst = request . entry !== undefined ? new Entry ( request . entry ) : new PluginField < any > ( request . field as IField < any > , getElement ( request . field ?. elSignature , request . field ?. elParams ) , this ) ; ServiceConfig . register ( inst ) . then ( ( value ) => { response ( new IPCMessages . SettingsRegisterResponse ( { value : value , } ) ) . catch ( ( error ) => { this . _logger . warn ( `Fail to send response on SettingsRegisterResponse due error: ${ error . message }` ) ; } ) ; } ) . catch ( ( entryErr ) => { response ( new IPCMessages . SettingsRegisterResponse ( { error : `Field to register entries due error: ${ entryErr . message }` , } ) ) . catch ( ( error ) => { this . _logger . warn ( `Fail to send response on SettingsRegisterResponse due error: ${ error . message }` ) ; } ) ; } ) ; } private _ipc_onSettingsGetRequest ( message , response ) { const request = message as IPCMessages . SettingsGetRequest ; const value = ServiceConfig . get < any > ( getEntryKeyByArgs ( request . path , request . key ) ) ; if ( value instanceof Error ) { response ( new IPCMessages . SettingsGetResponse ( { error : `Field to register entries due error: ${ value . message }` , } ) ) . catch ( ( error ) => { this . _logger . warn ( `Fail to send response on SettingsGetResponse due error: ${ error . message }` ) ; } ) ; } else { response ( new IPCMessages . SettingsGetResponse ( { value : value , } ) ) . catch ( ( error ) => { this . _logger . warn ( `Fail to send response on SettingsGetResponse due error: ${ error . message }` ) ; } ) ; } } }