import * as SocketIOClient from 'socket.io-client'; import { DyFM_Error, DyFM_Log, DyFM_Async, DyFM_Array, DyFM_errorFlag } from '@futdevpro/fsm-dynamo'; import { DyFM_SocketEvent, DyFM_SocketEvent_Key } from '@futdevpro/fsm-dynamo/socket'; import { DyNTS_SingletonService } from '../../../_services/base/singleton.service'; import { DyNTS_global_settings } from '../../../_collections/global-settings.const'; import { DyNTS_SocketClientService_Params } from '../_models/socket-client-service-params.control-model'; export abstract class DyNTS_SocketClient_ServiceBase extends DyNTS_SingletonService /* DyFM_SocketClient_ServiceBase */ { private _params: DyNTS_SocketClientService_Params; protected get params(): DyNTS_SocketClientService_Params { return this._params; } get name(): string { return this.params.name; } protected socket: SocketIOClient.Socket; protected incomingEvents: DyFM_SocketEvent[]; protected _connected: boolean = false; get connected(): boolean { return this._connected; } protected debugLog: boolean = DyNTS_global_settings.log_settings.setup; protected logFn: boolean = DyNTS_global_settings.log_settings.functions; private readonly defaultEvents: DyFM_SocketEvent[] = [ new DyFM_SocketEvent({ eventKey: DyFM_SocketEvent_Key.outgoingNewConnection, tasks: [ async () => { this._connected = true; DyFM_Log.success(`< > socket-client (${this.params.name}) connected!`); }, ], }), new DyFM_SocketEvent({ eventKey: DyFM_SocketEvent_Key.disconnect, tasks: [ async () => { this._connected = false; DyFM_Log.warn(` socket-client (${this.params.name}) disconnected!`); if (this._params.reconnect) { await DyFM_Async.delay(this._params.reconnectDelay); this.connectSocket(); } }, ], }), new DyFM_SocketEvent({ eventKey: DyFM_SocketEvent_Key.subscriptionSuccessful, tasks: [ async () => { DyFM_Log.success(`<=--> socket-client (${this.params.name}) subscription successful!`); }, ], }), new DyFM_SocketEvent({ eventKey: DyFM_SocketEvent_Key.error, tasks: [ async (content: any) => { if (content instanceof DyFM_Error) { content.logSimple(`=--> socket-client (${this.params.name}) ERROR`); } else { DyFM_Log.error(`=--> socket-client (${this.params.name}) ERROR:`, content); } }, ], }), ]; protected constructor() { super(); this.asyncConstructor().catch((error: any) => { if ( DyNTS_global_settings.log_settings.highDetailedLogs || !(error instanceof DyFM_Error) ) { DyFM_Log.H_error( `socket-client (${this.params?.name}) Service setup failed! ` + `(DyNTS_SocketClientService constructor catch)`, `\n ERROR:`, error ); } else { error.logSimple( `socket-client (${this.params?.name}) Service setup failed! ` + `(DyNTS_SocketClientService constructor catch)` ); } }); } private async asyncConstructor(): Promise { try { this._params = this.getParams(); this.incomingEvents = this.getIncomingEvents() ?? []; const socketAddress = this.params.port ? `${this.params.address}:${this.params.port}` : this.params.address; this.socket = SocketIOClient.io( socketAddress, { reconnection: this.params.reconnect, reconnectionDelay: this.params.reconnectDelay, ...this.params.socketOptions, } ); await this.setupDefaultEvents(); await this.setupSocketEvents(); DyFM_Log.success(`\nsocket-client (${this.params?.name}) Service setup finished`); await this.connectSocket(); } catch (error) { DyFM_Log.error(`socket-client (${this.params?.name}) Service setup failed!`, error); throw new DyFM_Error({ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-SCS-001`, error: error, }); } } private async setupDefaultEvents(): Promise { try { if (this.logFn) console.log('\nFn:. setupDefaultEvents'); this.defaultEvents.forEach((defaultEvent: DyFM_SocketEvent) => { defaultEvent.serviceName = this.params.name; const eventDeclared = this.incomingEvents.find( (event: DyFM_SocketEvent) => event.eventKey === defaultEvent.eventKey ); if (!eventDeclared) { this.incomingEvents.push(defaultEvent); } else { eventDeclared.tasks.unshift(...defaultEvent.tasks); } }); } catch (error) { DyFM_Log.error( `socket-client (${this.params.name}) Service Setup Default Events failed, ` + `ERROR:`, error ); throw error; } } private async setupSocketEvents(): Promise { try { if (this.debugLog) console.log(`Setup socket-client (${this.params.name}) Events...`); await DyFM_Array.asyncForEach( this.incomingEvents, async (event: DyFM_SocketEvent) => { event.serviceName = this.params.name; this.socket.on(event.eventKey, async (content: any) => { try { await event.executeEventTasks(content, this.params.service); } catch (error) { DyFM_Log.error(`socket-client (${this.params.name}) Event failed, ERROR:`, error); await this.emitEvent(DyFM_SocketEvent_Key.error, error); } }); } ); } catch (error) { DyFM_Log.error( `socket-client (${this.params.name}) Service Setup Socket Events failed, ` + `ERROR:`, error ); throw error; } } tryingToReconnect = false; protected async connectSocket(): Promise { try { DyFM_Log.info( `< .. > socket-client (${this.params.name}) ` + `connecting to ${this.params.address}:${this.params.port} ...` ); this.socket.connect(); if (this.params.reconnect && !this.tryingToReconnect) { await this.tryReconnectIfNeeded(); } } catch (error) { DyFM_Log.error( `socket-client (${this.params.name}) Service Connect Socket failed, ` + `ERROR:`, error ); throw error; } } private async tryReconnectIfNeeded(): Promise { try { if (this.logFn) console.log(`\nFn:. tryReconnectIfNeeded`); this.tryingToReconnect = true; await DyFM_Async.delay(this._params.reconnectDelay); this.tryingToReconnect = false; if (!this._connected) { await this.connectSocket(); } } catch (error) { DyFM_Log.error( `socket-client (${this.params.name}) Service Try Reconnect failed, ` + `ERROR:`, error ); throw error; } } /** * Emit event to the socket * @param eventType event name * @param content event content */ async emitEvent(eventType: string, content: any): Promise { try { DyFM_Log.log(`<=-- outgoing socket-client (${this.params.name}) event: ${eventType}`); this.socket.emit(eventType, content); } catch (error) { DyFM_Log.error( `socket-client (${this.params.name}) ` + `Service Emit Event failed: ${this.params?.name} (${this.params?.port})`, error ); throw error; } } /** * You must setup params for the service in this function */ abstract getParams(): DyNTS_SocketClientService_Params; /** * You must setup events and required services in this function */ abstract getIncomingEvents(): DyFM_SocketEvent[]; }