import { Observable , Subscription , Subject } from 'rxjs' ; import { IPair } from '../../../../thirdparty/code/engine' ; import { IEnvironment , INewInformation } from '../environment/component' ; import { Session } from '../../../../controller/session/session' ; import { copy } from '../../../../../../../../client.libs/chipmunk.client.toolkit/src/tools/tools.object' ; import { IPreset } from '../../../../../../../../common/ipc/electron.ipc.messages' ; import ElectronIpcService , { IPC } from '../../../../services/service.electron.ipc' ; import TabsSessionsService from '../../../../services/service.sessions.tabs' ; import EventsSessionService from '../../../../services/standalone/service.events.session' ; import * as Toolkit from 'chipmunk.client.toolkit' ; interface IPresetInfo { pwd ? : string ; shell ? : string ; env ? : IEnvironment [ ] ; } export class ShellService { public readonly saveAs : string = 'Save as..' ; private _presets : IPC . IPreset [ ] = [ { custom : false , title : this . saveAs , information : { pwd : '' , shell : '' , env : [ ] , } , } , { title : 'Default' , custom : false , information : { pwd : '' , shell : '' , env : [ ] , } , } , ] ; private _sessionID : string | undefined ; private _shells : string [ ] = [ ] ; private _defaultInformation : INewInformation | undefined ; private _selectedPresetTitle : string = this . _presets [ 1 ] . title ; private _logger : Toolkit . Logger = new Toolkit . Logger ( 'SidebarAppShellService' ) ; private _subscriptions : { [ key ] : Toolkit . Subscription | Subscription ; } = { } ; private _subjects : { onRestored : Subject < void > ; onShellChange : Subject < string > ; } = { onRestored : new Subject < void > ( ) , onShellChange : new Subject < string > ( ) , } ; constructor ( ) { const session = TabsSessionsService . getActive ( ) ; if ( session !== undefined ) { this . _sessionID = session . getGuid ( ) ; } else { this . _logger . error ( 'Session not available' ) ; } this . _subscriptions . onSessionChange = EventsSessionService . getObservable ( ) . onSessionChange . subscribe ( this . _onSessionChange . bind ( this ) ) ; this . _init ( ) ; } public destroy ( ) { Object . keys ( this . _subscriptions ) . forEach ( ( key ) => { this . _subscriptions [ key ] . unsubscribe ( ) ; } ) ; } public get shells ( ) : string [ ] { return this . _shells ; } public get defaultInformation ( ) : INewInformation { if ( this . _defaultInformation === undefined ) { throw new Error ( this . _logger . error ( `default information isn't inited` ) ) ; } return this . _defaultInformation ; } public get presets ( ) : IPC . IPreset [ ] { return this . _presets . slice ( ) ; } public set selectedPresetTitle ( title ) { this . _selectedPresetTitle = title ; } public get selectedPresetTitle ( ) : string { return this . _selectedPresetTitle ; } public getObservable ( ) { return { onRestored : this . _subjects . onRestored . asObservable ( ) , onShellChange : this . _subjects . onShellChange . asObservable ( ) , } ; } public resetSelectedPreset ( ) { if ( this . _sessionID === undefined ) { return ; } this . _getPreset ( this . _selectedPresetTitle ) . information = copy ( this . _defaultInformation ) ; this . setPreset ( this . _sessionID ) . catch ( ( error ) => { this . _logger . error ( error . message ) ; } ) ; this . _subjects . onShellChange . next ( this . defaultInformation . shell ) ; } public terminate ( request , command ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request ( new IPC . ShellProcessKillRequest ( request ) , IPC . ShellProcessKillResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { return reject ( new Error ( `Fail to terminate process "${ command }" due error: ${ response . error }` ) ) ; } resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Fail to terminate process "${ command }" due error: ${ error . message }` ) ) ; } ) ; } ) ; } public getRunning ( sessionID ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellProcessListResponse > ( new IPC . ShellProcessListRequest ( { session : sessionID } ) , IPC . ShellProcessListResponse ) . then ( resolve ) . catch ( ( error ) => { reject ( new Error ( `Fail to get running processes due error: ${ error . message }` ) ) ; } ) ; } ) ; } public getHistory ( sessionID ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellProcessHistoryGetResponse > ( new IPC . ShellProcessHistoryGetRequest ( { session : sessionID } ) , IPC . ShellProcessHistoryGetResponse ) . then ( resolve ) . catch ( ( error ) => { reject ( new Error ( `Fail to get history of processes due error: ${ error . message }` ) ) ; } ) ; } ) ; } public setBundle ( sessionID , bundle ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellProcessBundleSetResponse > ( new IPC . ShellProcessBundleSetRequest ( { session : sessionID , bundle : bundle } ) , IPC . ShellProcessBundleSetResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { reject ( new Error ( `Fail to save bundle of processes due error: ${ response . error }` ) ) ; } else { resolve ( response ) ; } } ) . catch ( ( error ) => { reject ( new Error ( `Fail to save bundle of processes due error: ${ error . message }` ) ) ; } ) ; } ) ; } public removeBundles ( sessionID , bundles ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellProcessBundleRemoveResponse > ( new IPC . ShellProcessBundleRemoveRequest ( { session : sessionID , bundles : bundles } ) , IPC . ShellProcessBundleRemoveResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { reject ( new Error ( `Fail to save bundles of processes due error: ${ response . error }` ) ) ; } else { resolve ( response ) ; } } ) . catch ( ( error ) => { reject ( new Error ( `Fail to remove bundles of processes due error: ${ error . message }` ) ) ; } ) ; } ) ; } public getEnv ( request ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellEnvResponse > ( new IPC . ShellEnvRequest ( request ) , IPC . ShellEnvResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { reject ( new Error ( `Failed to reqeust environment information due to Error: ${ response . error }` ) ) ; } else { this . _shells = response . shells ; this . _defaultInformation = { env : this . _convertEnv ( response . env ) , shell : response . shell , pwd : response . pwd , } ; if ( this . _presets [ 1 ] . information . pwd !== undefined && this . _presets [ 1 ] . information . pwd . trim ( ) === '' ) { this . _presets [ 1 ] . information = copy ( this . _defaultInformation ) ; this . setPreset ( request . session ) ; } resolve ( ) ; } } ) . catch ( ( error ) => { reject ( new Error ( `Failed to reqeust environment information due to Error: ${ error . message }` ) ) ; } ) ; } ) ; } public setEnv ( request ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request ( new IPC . ShellSetEnvRequest ( { pwd : request . pwd , shell : request . shell , session : request . session , env : request . env , } ) , IPC . ShellSetEnvResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { reject ( new Error ( `Failed to set environment due to Error: ${ response . error }` ) ) ; } const selectedPreset = this . _getPreset ( this . _selectedPresetTitle ) ; if ( request . shell !== undefined ) { selectedPreset . information . shell = request . shell ; } if ( request . pwd !== undefined ) { selectedPreset . information . pwd = request . pwd ; } if ( request . env !== undefined ) { selectedPreset . information . env = copy ( request . env ) ; } resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to set environment due to Error: ${ error . message }` ) ) ; } ) ; } ) ; } public getDetails ( request ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellProcessDetailsResponse > ( new IPC . ShellProcessDetailsRequest ( request ) , IPC . ShellProcessDetailsResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { reject ( new Error ( `Failed to reqeust process details of due to Error: ${ response . error }` ) ) ; } else if ( response . info === undefined ) { reject ( new Error ( this . _logger . error ( `ShellProcessDetailsResponse returns invalid data` ) ) ) ; } else { resolve ( response . info ) ; } } ) . catch ( ( error ) => { reject ( new Error ( `Failed to send a process details reqeust due to Error: ${ error . message }` ) ) ; } ) ; } ) ; } public clearRecent ( ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellRecentCommandsClearResponse > ( new IPC . ShellRecentCommandsClearRequest ( ) , IPC . ShellRecentCommandsClearResponse ) . then ( ( response ) => { if ( response . error ) { return reject ( new Error ( `Fail to reset recent commands due error: ${ response . error }` ) ) ; } resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Fail send request to reset recent commands due error: ${ error . message }` ) ) ; } ) ; } ) ; } public runCommand ( request ) { return new Promise ( ( resolve , reject ) => { const preset = this . _getPreset ( this . _selectedPresetTitle ) ; if ( preset . information . pwd === undefined || preset . information . shell === undefined ) { return reject ( new Error ( this . _logger . error ( `Fields pwd or shell aren't defined` ) ) ) ; } ElectronIpcService . request ( new IPC . ShellProcessRunRequest ( { session : request . session , command : request . command , pwd : preset . information . pwd , shell : preset . information . shell , } ) , IPC . ShellProcessRunResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { return reject ( new Error ( `Failed to run command ${ request . command } due Error: ${ response . error }` ) ) ; } resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to run command ${ request . command } due Error: ${ error . message }` ) ) ; } ) ; } ) ; } public loadRecentCommands ( ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellRecentCommandsResponse > ( new IPC . ShellRecentCommandsRequest ( ) , IPC . ShellRecentCommandsResponse ) . then ( ( response ) => { resolve ( response . commands . map ( ( recent ) => { return { id : '' , caption : ' ' , description : recent , tcaption : ' ' , tdescription : recent , } ; } ) ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Fail to get list of recent commands due error: ${ error . message }` ) ) ; } ) ; } ) ; } public removeRecentCommand ( request ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . send ( new IPC . ShellRecentCommandsRemove ( request ) ) . then ( ( ) => { resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to remove recent command due error: ${ error . message }` ) ) ; } ) ; } ) ; } public setPwd ( request ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellPwdResponse > ( new IPC . ShellPwdRequest ( request ) , IPC . ShellPwdResponse ) . then ( ( response ) => { if ( response . error !== undefined ) { return reject ( new Error ( `Failed to set pwd due to the error: ${ response . error }` ) ) ; } if ( this . _sessionID === undefined ) { return reject ( new Error ( this . _logger . error ( `Cannot proccedd with "setPwd" because session guid is undefined` ) ) ) ; } if ( response . value . trim ( ) !== '' ) { this . _getPreset ( this . _selectedPresetTitle ) . information . pwd = response . value ; this . setPreset ( this . _sessionID , { pwd : response . value } ) . catch ( ( error ) => { reject ( `Failed to set pwd of preset due to the error ${ error . message }` ) ; } ) ; } resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to set pwd due to the error: ${ error . message }` ) ) ; } ) ; } ) ; } public setPreset ( session , info ? ) { return new Promise ( ( resolve , reject ) => { const preset = this . _getPreset ( this . _selectedPresetTitle ) ; ElectronIpcService . request ( new IPC . ShellPresetSetRequest ( { session : session , preset : info === undefined ? preset : { title : preset . title , custom : preset . custom , information : info } , } ) , IPC . ShellPresetSetResponse ) . then ( ( response ) => { resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to set preset due to the error: ${ error . message }` ) ) ; } ) ; } ) ; } public removePreset ( request ) { this . _presets = this . _presets . filter ( ( preset ) => { return preset . title !== this . _selectedPresetTitle ; } ) ; this . _selectedPresetTitle = this . _presets [ this . _presets . length - 1 ] . title ; return new Promise ( ( resolve , reject ) => { ElectronIpcService . request ( new IPC . ShellPresetRemoveRequest ( { session : request . session , title : request . title , } ) , IPC . ShellPresetRemoveResponse ) . then ( ( response ) => { resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to remove preset due to the error: ${ error . message }` ) ) ; } ) ; } ) ; } public restoreSession ( request , outside = false ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellLoadResponse > ( new IPC . ShellLoadRequest ( request ) , IPC . ShellLoadResponse ) . then ( ( response ) => { if ( response . presetTitle . trim ( ) === '' ) { return resolve ( ) ; } this . _presets . forEach ( ( preset ) => { if ( preset . title === response . presetTitle ) { this . _selectedPresetTitle = preset . title ; } } ) ; if ( ! outside ) { this . _subjects . onRestored . next ( ) ; } resolve ( ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Fail to restore preset settings in session ${ request . session } due to the error: ${ error . message }` ) ) ; } ) ; } ) ; } public addPreset ( title , prev ) { return this . _presets . push ( { title : title , information : copy ( this . _getPreset ( prev ) . information ) , custom : true , } ) ; } public getPreset ( title ) { return copy ( this . _getPreset ( title ) ) ; } public _getPreset ( title ) { const preset = this . _presets . find ( ( _ ) => _ . title === title ) ; return preset === undefined ? this . _presets [ 1 ] : preset ; } private _getPresets ( session ) { return new Promise ( ( resolve , reject ) => { ElectronIpcService . request < IPC . ShellPresetGetResponse > ( new IPC . ShellPresetGetRequest ( { session : session } ) , IPC . ShellPresetGetResponse ) . then ( ( response ) => { resolve ( response ) ; } ) . catch ( ( error ) => { reject ( new Error ( `Failed to get presets due to the error: ${ error . message }` ) ) ; } ) ; } ) ; } private _init ( ) { if ( this . _sessionID === undefined ) { throw new Error ( this . _logger . error ( `Cannot init service because session guid is undefined` ) ) ; } this . _getPresets ( this . _sessionID ) . then ( ( response ) => { if ( response . session !== this . _sessionID ) { return ; } if ( response . presets . length > 0 ) { for ( const preset of response . presets ) { if ( preset . title === 'Default' ) { this . _presets [ 1 ] . information = copy ( preset . information ) ; break ; } } this . _presets . push ( ... response . presets . filter ( ( preset ) => { return preset . title !== 'Default' ; } ) ) ; } this . getEnv ( { session : this . _sessionID } ) . then ( ( ) => { const shell = this . _getPreset ( this . _selectedPresetTitle ) . information . shell ; shell !== undefined && this . _subjects . onShellChange . next ( shell ) ; } ) . catch ( ( error ) => { this . _logger . error ( `Failed to get environment information due to the error: ${ error . message }` ) ; } ) ; } ) . catch ( ( error ) => { this . _logger . error ( `Failed to initialize service due to the error: ${ error . message }` ) ; } ) ; } private _convertEnv ( environment ) { const env = [ ] ; Object . keys ( environment ) . forEach ( ( variable ) => { env . push ( { value : environment [ variable ] , variable : variable , custom : false , editing : { value : false , variable : false , } , selected : false , } ) ; } ) ; return env ; } private _saveSession ( request ) { ElectronIpcService . request ( new IPC . ShellSaveRequest ( request ) , IPC . ShellSaveResponse ) . catch ( ( error ) => { this . _logger . error ( `Fail to save preset settings in session ${ request . session } due to error: ${ error . message }` ) ; } ) ; } private _onSessionChange ( session ) { if ( session !== undefined ) { this . _sessionID !== undefined && this . _saveSession ( { session : this . _sessionID , presetTitle : this . _selectedPresetTitle , } ) ; this . _sessionID = session . getGuid ( ) ; this . restoreSession ( { session : this . _sessionID } ) . catch ( ( error ) => { this . _logger . error ( error . message ) ; } ) ; } } }