From ae7c70e35babd27db11ad2f4c89c9490f2c5db2d Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Wed, 19 Mar 2025 18:26:28 +0530 Subject: [PATCH 01/29] Added v2 changes --- src/App.js | 11 + src/EventInvocation.js | 433 ++++++++++++++------ src/pubsub/handlers/RegisterEventHandler.js | 1 + 3 files changed, 318 insertions(+), 127 deletions(-) diff --git a/src/App.js b/src/App.js index 22b1ec38..38a69d01 100644 --- a/src/App.js +++ b/src/App.js @@ -126,6 +126,7 @@ export default class App extends Base { process.env.PUBSUB_SUBSCRIBE_TOPIC_SUFFIX = new URLSearchParams(appUrl.search).get('pubSubSubscribeSuffix'); process.env.PUBSUB_PUBLISH_TOPIC_SUFFIX = new URLSearchParams(appUrl.search).get('pubSubPublishSuffix'); process.env.REGION = new URLSearchParams(appUrl.search).get('region'); + process.env.FIREBOLT_VERSION = new URLSearchParams(appUrl.search).get('fireboltVersion'); if (platform) { process.env.PLATFORM = platform; @@ -427,6 +428,16 @@ export default class App extends Base { process.env.REGISTERPROVIDER = query.params.registerprovider; } + if (query.params.hasOwnProperty('fireboltVersion')) { + const fireboltVersion = query.params.fireboltVersion; + process.env.FIREBOLT_VERSION = fireboltVersion; + // TODO: Load firebolt 2.0 fca sdk if sdkVersion is 2.0 + // if (fireboltVersion === '2.0') { + // // Load Firebolt 2.0 FCA SDK + // window.location.href = `https:/index.html?sdkVersion=${fireboltVersion}`; + // } + } + // Set the pubSub URL if present if (query.params.pubSubUrl) { process.env.PUB_SUB_URL = query.params.pubSubUrl; diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 40c29421..297081a0 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -117,129 +117,7 @@ class EventHandler { } } -export class EventInvocation { - // This method accepts the message params and return the listener response and schema response - async northBoundEventHandling(message) { - const eventParams = message.params; - const moduleWithEventName = eventParams.event; - const params = eventParams.params; - const [listenerResponse, uniqueListenerKey] = await this.registerEvent(moduleWithEventName, params); - const registrationResponse = {}; - - if (process.env.STANDALONE == true) { - registrationResponse['eventName'] = moduleWithEventName; - registrationResponse['eventListenerId'] = uniqueListenerKey; - if (listenerResponse && Number.isInteger(listenerResponse) && listenerResponse > 0) { - registrationResponse['eventListenerResponse'] = { - listenerResponse: listenerResponse, - error: null, - }; - // Handling not supported api to check error schema if it gives a valid response - let schemaValidationResult = {}; - let schemaValidationStatus = CONSTANTS.PASS; - if (message.params.isNotSupportedApi == true) { - schemaValidationResult = errorSchemaCheck(listenerResponse); - schemaValidationStatus = CONSTANTS.FAIL; - } - registrationResponse['eventListenerSchemaResult'] = { - status: schemaValidationStatus, - eventSchemaResult: schemaValidationResult, - }; - eventHandlerMap.get(uniqueListenerKey).setEventListener(registrationResponse); - } else { - if (CONSTANTS.ERROR_LIST.includes(listenerResponse.message)) { - const responseCode = CONSTANTS.STATUS_CODE[3]; - registrationResponse['responseCode'] = responseCode; - } - registrationResponse['eventListenerResponse'] = { result: null, error: listenerResponse }; - // In case of error, validate error against errorschema - const schemaValidationResult = errorSchemaCheck(listenerResponse); - if (schemaValidationResult && schemaValidationResult.errors && schemaValidationResult.errors.length > 0) { - registrationResponse['eventListenerSchemaResult'] = { - status: CONSTANTS.FAIL, - eventSchemaResult: {}, - }; - } else { - registrationResponse['eventListenerSchemaResult'] = { - status: CONSTANTS.PASS, - eventSchemaResult: schemaValidationResult, - }; - } - } - return registrationResponse; - } else { - registrationResponse['jsonrpc'] = '2.0'; - registrationResponse['id'] = null; - if (listenerResponse && Number.isInteger(listenerResponse) && listenerResponse > 0) { - registrationResponse['id'] = listenerResponse; - registrationResponse['result'] = { - listening: true, - event: moduleWithEventName, - }; - eventHandlerMap.get(uniqueListenerKey).setEventListener(registrationResponse); - } else { - registrationResponse['error'] = listenerResponse; - } - return registrationResponse; - } - } - - // This method will listen to event and capture the event response after triggering - async registerEvent(moduleWithEventName, params) { - const paramlist = []; - const [sdkType, module] = this.getSdkTypeAndModule(moduleWithEventName); - const [schemaList, invokedSdk] = await dereferenceOpenRPC(sdkType); - const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); - const eventName = EventHandlerObject.getEventName(); - let eventRegistrationID; - for (const key in params) { - if (params.hasOwnProperty(key)) { - paramlist.push(params[key]); - } - } - // To prevent uncaught exceptions when it received invalid eventName or module names. - try { - if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { - if (moduleWithEventName.includes('_')) { - moduleWithEventName = moduleWithEventName.split('_')[1]; - } - - const { id, promise } = await this.registerEventInTransport(moduleWithEventName); - const result = await promise; - if (result && result.message) { - throw result.message; - } - - // Recieving Event Response - const emit = (eventId, value) => { - if (id == eventId && !CONSTANTS.EXCLUDED_VALUES.includes(value)) { - EventHandlerObject.handleEvent(value); - } - }; - Transport.addEventEmitter(emit); - eventRegistrationID = id; - } else if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - const resolvedModule = MODULE_MAP[sdkType][module]; - eventRegistrationID = await resolvedModule.listen(eventName, (result) => { - if (!CONSTANTS.EXCLUDED_VALUES.includes(result)) { - EventHandlerObject.handleEvent(result); - } - }); - } - } catch (err) { - logger.error('No listener Id :' + JSON.stringify(err), 'registerEvent'); - return [err, null]; - } - - // Construct unique key for event handler. A UUID can be added to the key to make it more unique. - if (eventRegistrationID) { - const eventNameWithoutSDK = moduleWithEventName.includes('_') ? moduleWithEventName.split('_')[1] : moduleWithEventName; - const uniqueListenerKey = eventNameWithoutSDK + '-' + eventRegistrationID; - eventHandlerMap.set(uniqueListenerKey, EventHandlerObject); - return [eventRegistrationID, uniqueListenerKey]; - } - } - +class EventRegistrationInterface { clearEventListeners(event) { try { const [sdkType, module] = this.getSdkTypeAndModule(event); @@ -322,6 +200,133 @@ export class EventInvocation { getHistory(eventKey, numberOfEvents) { return eventHandlerMap.get(eventKey).getHistory(numberOfEvents); } + // Registering the event in Transport mode + async registerEventInTransport(methodName, params) { + const module = methodName.split('.')[0].toLowerCase(); + const method = methodName.split('.')[1]; + const args = Object.assign({ listen: true }, params); + return await Transport.listen(module, method, args); + } +} + +// 1.0 Implementation +class EventRegistration1_0 extends EventRegistrationInterface { + // This method will listen to event and capture the event response after triggering + async registerEvent(moduleWithEventName, params) { + const paramlist = []; + const [sdkType, module] = this.getSdkTypeAndModule(moduleWithEventName); + const [schemaList, invokedSdk] = await dereferenceOpenRPC(sdkType); + const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); + const eventName = EventHandlerObject.getEventName(); + let eventRegistrationID; + for (const key in params) { + if (params.hasOwnProperty(key)) { + paramlist.push(params[key]); + } + } + // To prevent uncaught exceptions when it received invalid eventName or module names. + try { + if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { + if (moduleWithEventName.includes('_')) { + moduleWithEventName = moduleWithEventName.split('_')[1]; + } + + const { id, promise } = await this.registerEventInTransport(moduleWithEventName); + const result = await promise; + if (result && result.message) { + throw result.message; + } + + // Recieving Event Response + const emit = (eventId, value) => { + if (id == eventId && !CONSTANTS.EXCLUDED_VALUES.includes(value)) { + EventHandlerObject.handleEvent(value); + } + }; + Transport.addEventEmitter(emit); + eventRegistrationID = id; + } else if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { + const resolvedModule = MODULE_MAP[sdkType][module]; + eventRegistrationID = await resolvedModule.listen(eventName, (result) => { + if (!CONSTANTS.EXCLUDED_VALUES.includes(result)) { + EventHandlerObject.handleEvent(result); + } + }); + } + } catch (err) { + logger.error('No listener Id :' + JSON.stringify(err), 'registerEvent'); + return [err, null]; + } + + // Construct unique key for event handler. A UUID can be added to the key to make it more unique. + if (eventRegistrationID) { + const eventNameWithoutSDK = moduleWithEventName.includes('_') ? moduleWithEventName.split('_')[1] : moduleWithEventName; + const uniqueListenerKey = eventNameWithoutSDK + '-' + eventRegistrationID; + eventHandlerMap.set(uniqueListenerKey, EventHandlerObject); + return [eventRegistrationID, uniqueListenerKey]; + } + } + + eventListenerResponseHandler(moduleWithEventName, response) { + const [listenerResponse, uniqueListenerKey] = response; + const registrationResponse = {}; + if (process.env.STANDALONE == true) { + registrationResponse['eventName'] = moduleWithEventName; + registrationResponse['eventListenerId'] = uniqueListenerKey; + if (listenerResponse && Number.isInteger(listenerResponse) && listenerResponse > 0) { + registrationResponse['eventListenerResponse'] = { + listenerResponse: listenerResponse, + error: null, + }; + // Handling not supported api to check error schema if it gives a valid response + let schemaValidationResult = {}; + let schemaValidationStatus = CONSTANTS.PASS; + if (message.params.isNotSupportedApi == true) { + schemaValidationResult = errorSchemaCheck(listenerResponse); + schemaValidationStatus = CONSTANTS.FAIL; + } + registrationResponse['eventListenerSchemaResult'] = { + status: schemaValidationStatus, + eventSchemaResult: schemaValidationResult, + }; + eventHandlerMap.get(uniqueListenerKey).setEventListener(registrationResponse); + } else { + if (CONSTANTS.ERROR_LIST.includes(listenerResponse.message)) { + const responseCode = CONSTANTS.STATUS_CODE[3]; + registrationResponse['responseCode'] = responseCode; + } + registrationResponse['eventListenerResponse'] = { result: null, error: listenerResponse }; + // In case of error, validate error against errorschema + const schemaValidationResult = errorSchemaCheck(listenerResponse); + if (schemaValidationResult && schemaValidationResult.errors && schemaValidationResult.errors.length > 0) { + registrationResponse['eventListenerSchemaResult'] = { + status: CONSTANTS.FAIL, + eventSchemaResult: {}, + }; + } else { + registrationResponse['eventListenerSchemaResult'] = { + status: CONSTANTS.PASS, + eventSchemaResult: schemaValidationResult, + }; + } + } + return registrationResponse; + } else { + registrationResponse['jsonrpc'] = '2.0'; + registrationResponse['id'] = null; + if (listenerResponse && Number.isInteger(listenerResponse) && listenerResponse > 0) { + registrationResponse['id'] = listenerResponse; + registrationResponse['result'] = { + listening: true, + event: moduleWithEventName, + }; + eventHandlerMap.get(uniqueListenerKey).setEventListener(registrationResponse); + } else { + registrationResponse['error'] = listenerResponse; + } + return registrationResponse; + } + } // Return the event response object for the eventName passed as the param getEventResponse(message) { @@ -344,12 +349,186 @@ export class EventInvocation { return { error: { code: 'FCAError', message: 'Event response fetch error: ' + err.message } }; } } +} + +// 2.0 Implementation +class EventRegistration2_0 extends EventRegistrationInterface { + // This method will listen to an event and capture the event response after triggering + async registerEvent(moduleWithEventName, params) { + const paramlist = Object.values(params); // Simplified parameter extraction + const [sdkType, module] = this.getSdkTypeAndModule(moduleWithEventName); + const [schemaList] = await dereferenceOpenRPC(sdkType); + const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); + const eventName = EventHandlerObject.getEventName(); + let eventRegistrationID; + + try { + if (process.env.COMMUNICATION_MODE === CONSTANTS.TRANSPORT) { + eventRegistrationID = await this.handleTransportEvent(moduleWithEventName, eventName); + } else if (process.env.COMMUNICATION_MODE === CONSTANTS.SDK) { + eventRegistrationID = await this.handleSdkEvent(sdkType, module, eventName, moduleWithEventName); + } + } catch (err) { + logger.error(`Error registering event: ${JSON.stringify(err)}`, 'registerEvent'); + return [err, null]; + } + + if (eventRegistrationID) { + firebolt2EventHandler.set(moduleWithEventName, EventHandlerObject); + return null; + } + } + + // Helper method to handle Transport events + async handleTransportEvent(moduleWithEventName, eventName) { + const { id, promise } = await this.registerEventInTransport(moduleWithEventName); + const result = await promise; + + if (result.message) throw new Error(result.message); + + Transport.addEventEmitter((eventId, value) => { + if (id === eventId && !CONSTANTS.EXCLUDED_VALUES.includes(value)) { + this.eventHandler(moduleWithEventName, value); + } + }); + + return id; + } + + // Helper method to handle SDK events + async handleSdkEvent(sdkType, module, eventName, moduleWithEventName) { + const resolvedModule = MODULE_MAP[sdkType][module]; + return await resolvedModule.listen(eventName, (result) => { + if (!CONSTANTS.EXCLUDED_VALUES.includes(result)) { + this.eventHandler(moduleWithEventName, result); + } + }); + } + + // Construct a unique listener key + constructUniqueListenerKey(moduleWithEventName, eventRegistrationID) { + const eventNameWithoutSDK = moduleWithEventName.includes('_') ? moduleWithEventName.split('_')[1] : moduleWithEventName; + return `${eventNameWithoutSDK}-${eventRegistrationID}`; + } + + // Handle event responses + eventHandler(eventName, response) { + eventHistory.push({ + eventName, + eventResponse: response, + eventTime: new Date(), + }); + } + + // Return the event response object for the eventName passed as the param + getEventResponse(message) { + try { + const eventName = message.params.event; + const filteredEvents = eventHistory.filter((event) => event.eventName === eventName); + return filteredEvents.length ? filteredEvents[filteredEvents.length - 1] : { [eventName]: null }; + } catch (err) { + return { error: { code: 'FCAError', message: `Event response fetch error: ${err.message}` } }; + } + } + eventListenerResponseHandler(moduleWithEventName, response) { + let count = 1; + const registrationResponse = {}; + registrationResponse['jsonrpc'] = '2.0'; + registrationResponse['id'] = null; + if (response === null) { + registrationResponse['id'] = count++; + registrationResponse['result'] = response; + firebolt2EventHandler.get(moduleWithEventName).setEventListener(registrationResponse); + } else { + registrationResponse['error'] = response; + } + return registrationResponse; + } +} + +export class EventInvocation { + constructor() { + this.eventRegistration = this.initializeEventRegistration(); + } + + // Initialize Event Registration based on SDK version + initializeEventRegistration() { + const sdkVersion = '2.0'; // process.env.FIREBOLT_VERSION; + if (sdkVersion === '1.0') { + return new EventRegistration1_0(); + } else if (sdkVersion === '2.0') { + return new EventRegistration2_0(); + } else { + throw new Error('Invalid SDK version'); + } + } + + async northBoundEventHandling(message) { + try { + const { event: moduleWithEventName, params } = message.params; + const response = await this.eventRegistration.registerEvent(moduleWithEventName, params); + return this.eventRegistration.eventListenerResponseHandler(moduleWithEventName, response); + } catch (error) { + return this.handleError('northBoundEventHandling', error); + } + } + + clearEventListeners(event) { + try { + return this.eventRegistration.clearEventListeners(event); + } catch (error) { + return this.handleError('clearEventListeners', error); + } + } + + // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA + clearAllListeners() { + try { + return this.eventRegistration.clearAllListeners(); + } catch (error) { + return this.handleError('clearAllListeners', error); + } + } + + // Check and assign SDK type from incoming params + getSdkTypeAndModule(moduleWithEventName) { + try { + return this.eventRegistration.getSdkTypeAndModule(moduleWithEventName); + } catch (error) { + return this.handleError('getSdkTypeAndModule', error); + } + } + + // Return event history for the provided unique key + getHistory(eventKey, numberOfEvents) { + try { + return this.eventRegistration.getHistory(eventKey, numberOfEvents); + } catch (error) { + return this.handleError('getHistory', error); + } + } + + // Return the event response object for the eventName passed as the param + getEventResponse(message) { + try { + return this.eventRegistration.getEventResponse(message); + } catch (error) { + return this.handleError('getEventResponse', error); + } + } // Registering the event in Transport mode async registerEventInTransport(methodName, params) { - const module = methodName.split('.')[0].toLowerCase(); - const method = methodName.split('.')[1]; - const args = Object.assign({ listen: true }, params); - return await Transport.listen(module, method, args); + try { + return await this.eventRegistration.registerEventInTransport(methodName, params); + } catch (error) { + return this.handleError('registerEventInTransport', error); + } + } + + // Centralized error handling + handleError(methodName, error) { + console.error(`Error in ${methodName}:`, error.message); + return { error: { code: 'FCAError', message: `Error in ${methodName}: ${error.message}` } }; } } diff --git a/src/pubsub/handlers/RegisterEventHandler.js b/src/pubsub/handlers/RegisterEventHandler.js index c60cb539..bbbb6e3b 100644 --- a/src/pubsub/handlers/RegisterEventHandler.js +++ b/src/pubsub/handlers/RegisterEventHandler.js @@ -40,6 +40,7 @@ export default class RegisterEventHandler extends BaseHandler { if (message.context) { process.env.COMMUNICATION_MODE = message.context.communicationMode; } + process.env.IS_NOT_SUPPORTED_API = message.isNotSupportedApi; const eventInvokerInfo = new EventInvocation(); let sdkType; if (!message.params.event.includes('_')) { From 59bf7c5735669237d34d060c2f3ccc6b493502a8 Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Thu, 20 Mar 2025 14:55:28 +0530 Subject: [PATCH 02/29] Addressed the comments --- src/App.js | 12 +--- src/EventInvocation.js | 141 ++++++++++++++++++++++++++--------------- 2 files changed, 91 insertions(+), 62 deletions(-) diff --git a/src/App.js b/src/App.js index 38a69d01..46785300 100644 --- a/src/App.js +++ b/src/App.js @@ -126,7 +126,7 @@ export default class App extends Base { process.env.PUBSUB_SUBSCRIBE_TOPIC_SUFFIX = new URLSearchParams(appUrl.search).get('pubSubSubscribeSuffix'); process.env.PUBSUB_PUBLISH_TOPIC_SUFFIX = new URLSearchParams(appUrl.search).get('pubSubPublishSuffix'); process.env.REGION = new URLSearchParams(appUrl.search).get('region'); - process.env.FIREBOLT_VERSION = new URLSearchParams(appUrl.search).get('fireboltVersion'); + process.env.SDK_VERSION = new URLSearchParams(appUrl.search).get('sdkVersion'); if (platform) { process.env.PLATFORM = platform; @@ -428,16 +428,6 @@ export default class App extends Base { process.env.REGISTERPROVIDER = query.params.registerprovider; } - if (query.params.hasOwnProperty('fireboltVersion')) { - const fireboltVersion = query.params.fireboltVersion; - process.env.FIREBOLT_VERSION = fireboltVersion; - // TODO: Load firebolt 2.0 fca sdk if sdkVersion is 2.0 - // if (fireboltVersion === '2.0') { - // // Load Firebolt 2.0 FCA SDK - // window.location.href = `https:/index.html?sdkVersion=${fireboltVersion}`; - // } - } - // Set the pubSub URL if present if (query.params.pubSubUrl) { process.env.PUB_SUB_URL = query.params.pubSubUrl; diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 297081a0..04ddf668 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -31,14 +31,16 @@ import Transport from '@firebolt-js/sdk/dist/lib/Transport/index.mjs'; const Validator = require('jsonschema').Validator; const validator = new Validator(); const eventHandlerMap = new Map(); -const eventHistory = []; +const eventHandlerMapV2 = new Map(); +let eventHistory = []; const logger = require('./utils/Logger')('EventInvocation.js'); class EventHandler { constructor(moduleWithEventName, schemaList) { this.moduleWithEventName = moduleWithEventName; const event = moduleWithEventName.split('.')[1]; - this.eventName = this.parseEventName(event); + this.eventName = moduleWithEventName; + this.event = this.parseEventName(event); if (process.env.STANDALONE == true) { this.eventSchema = this.getSchema(moduleWithEventName, schemaList); } @@ -52,7 +54,7 @@ class EventHandler { } // Return short event name getEventName() { - return this.eventName; + return this.event; } // Fetch schema from dereferenced RPC using event name getSchema(moduleWithEventName, schemaList) { @@ -143,43 +145,6 @@ class EventRegistrationInterface { } } - // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA - clearAllListeners() { - logger.info('Clearing registered listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); - try { - if (eventHandlerMap.size >= 1) { - eventHandlerMap.forEach((EventHandlerObject, uniqueListenerKey) => { - // The key in the eventhHanldermap is in the format SDK_ModuleName- - const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; - const eventName = EventHandlerObject.eventName; - const eventRegistrationID = uniqueListenerKey.split('-')[1]; - const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); - logger.info('Unregister event ' + eventNameWithModuleName + ' registration ID ' + eventRegistrationID, 'clearAllListeners'); - - // Events are cleared using Firebolt SDK - if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - MODULE_MAP[sdkType][module].clear(eventName); - } - // Events are cleared by using Transport layer and thus bypassing SDK - else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { - const args = Object.assign({ listen: false }); - Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); - } - }); - eventHandlerMap.clear(); - logger.info('After clearing listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); - return 'Cleared Listeners'; - } else { - logger.info('No active Listeners', 'clearAllListeners'); - return 'No active listeners'; - } - } catch (err) { - logger.error('Error while clearing all event listeners' + err, 'clearAllListeners'); - const response = { error: { code: 'FCAError', message: 'Error while clearing all event listeners: ' + err.message } }; - return response; - } - } - // Check and assign SDK type from incoming params getSdkTypeAndModule(moduleWithEventName) { let sdkType; @@ -210,7 +175,7 @@ class EventRegistrationInterface { } // 1.0 Implementation -class EventRegistration1_0 extends EventRegistrationInterface { +class EventRegistration extends EventRegistrationInterface { // This method will listen to event and capture the event response after triggering async registerEvent(moduleWithEventName, params) { const paramlist = []; @@ -336,7 +301,7 @@ class EventRegistration1_0 extends EventRegistrationInterface { if (process.env.STANDALONE == true) { filteredEventDataObjectList = eventHistory.filter((element) => element.eventListenerId == eventName); } else { - filteredEventDataObjectList = eventHistory.filter((element) => element.eventListenerId.toString() == eventName.split('-').pop()); + filteredEventDataObjectList = eventHistory.filter((element) => element.eventName == eventName); } if (filteredEventDataObjectList.length) { const eventDataObject = filteredEventDataObjectList[filteredEventDataObjectList.length - 1]; @@ -349,10 +314,47 @@ class EventRegistration1_0 extends EventRegistrationInterface { return { error: { code: 'FCAError', message: 'Event response fetch error: ' + err.message } }; } } + + // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA + clearAllListeners() { + logger.info('Clearing registered listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); + try { + if (eventHandlerMap.size >= 1) { + eventHandlerMap.forEach((EventHandlerObject, uniqueListenerKey) => { + // The key in the eventhHanldermap is in the format SDK_ModuleName- + const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; + const eventName = EventHandlerObject.eventName; + const eventRegistrationID = uniqueListenerKey.split('-')[1]; + const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); + logger.info('Unregister event ' + eventNameWithModuleName + ' registration ID ' + eventRegistrationID, 'clearAllListeners'); + + // Events are cleared using Firebolt SDK + if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { + MODULE_MAP[sdkType][module].clear(eventName); + } + // Events are cleared by using Transport layer and thus bypassing SDK + else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { + const args = Object.assign({ listen: false }); + Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + } + }); + eventHandlerMap.clear(); + logger.info('After clearing listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); + return 'Cleared Listeners'; + } else { + logger.info('No active Listeners', 'clearAllListeners'); + return 'No active listeners'; + } + } catch (err) { + logger.error('Error while clearing all event listeners' + err, 'clearAllListeners'); + const response = { error: { code: 'FCAError', message: 'Error while clearing all event listeners: ' + err.message } }; + return response; + } + } } // 2.0 Implementation -class EventRegistration2_0 extends EventRegistrationInterface { +class EventRegistrationV2 extends EventRegistrationInterface { // This method will listen to an event and capture the event response after triggering async registerEvent(moduleWithEventName, params) { const paramlist = Object.values(params); // Simplified parameter extraction @@ -374,7 +376,7 @@ class EventRegistration2_0 extends EventRegistrationInterface { } if (eventRegistrationID) { - firebolt2EventHandler.set(moduleWithEventName, EventHandlerObject); + eventHandlerMapV2.set(moduleWithEventName, EventHandlerObject); return null; } } @@ -438,12 +440,50 @@ class EventRegistration2_0 extends EventRegistrationInterface { if (response === null) { registrationResponse['id'] = count++; registrationResponse['result'] = response; - firebolt2EventHandler.get(moduleWithEventName).setEventListener(registrationResponse); + eventHandlerMapV2.get(moduleWithEventName).setEventListener(registrationResponse); } else { registrationResponse['error'] = response; } return registrationResponse; } + + // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA + clearAllListeners() { + logger.info('Clearing registered listeners' + JSON.stringify(eventHandlerMapV2), 'clearAllListeners'); + try { + eventHistory = []; + if (eventHandlerMapV2.size >= 1) { + eventHandlerMapV2.forEach((EventHandlerObject, uniqueListenerKey) => { + // The key in the eventhHanldermap is in the format SDK_ModuleName- + const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; + const eventName = EventHandlerObject.event; + const eventRegistrationID = uniqueListenerKey.split('-')[1]; + const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); + logger.info('Unregister event ' + eventNameWithModuleName + ' registration ID ' + eventRegistrationID, 'clearAllListeners'); + + // Events are cleared using Firebolt SDK + if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { + MODULE_MAP[sdkType][module].clear(eventName); + } + // Events are cleared by using Transport layer and thus bypassing SDK + else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { + const args = Object.assign({ listen: false }); + Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + } + }); + eventHandlerMapV2.clear(); + logger.info('After clearing listeners' + JSON.stringify(eventHandlerMapV2), 'clearAllListeners'); + return 'Cleared Listeners'; + } else { + logger.info('No active Listeners', 'clearAllListeners'); + return 'No active listeners'; + } + } catch (err) { + logger.error('Error while clearing all event listeners' + err, 'clearAllListeners'); + const response = { error: { code: 'FCAError', message: 'Error while clearing all event listeners: ' + err.message } }; + return response; + } + } } export class EventInvocation { @@ -453,13 +493,12 @@ export class EventInvocation { // Initialize Event Registration based on SDK version initializeEventRegistration() { - const sdkVersion = '2.0'; // process.env.FIREBOLT_VERSION; - if (sdkVersion === '1.0') { - return new EventRegistration1_0(); - } else if (sdkVersion === '2.0') { - return new EventRegistration2_0(); + const sdkVersion = process.env.SDK_VERSION; + const pattern = /(2|\d{2,})\.\d+\.\d+/; + if (sdkVersion && pattern.test(sdkVersion)) { + return new EventRegistrationV2(); } else { - throw new Error('Invalid SDK version'); + return new EventRegistration(); } } From 45d2bae09c8bb97f8d1c4beed4bd490487a1ead5 Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Thu, 20 Mar 2025 15:27:36 +0530 Subject: [PATCH 03/29] fixed unit testcases --- test/unit/EventInvocation.test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index b30fc89c..ff764a9b 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -766,12 +766,12 @@ describe('EventInvocation', () => { test('should return event object with response - single event fired', () => { currentCallback({ foo: 'bar1' }); - const message = { params: { event: 'accessibility.onClosedCaptionsSettingsChanged-6' } }; + const message = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; const expectedResponse = { - eventName: 'modulechanged', + eventName: 'mocksdk_mockmodule.onmodulechanged', eventListenerId: 6, eventResponse: { foo: 'bar1' }, - eventTime: '2023-05-10T14:27:35.806Z', + eventTime: '2025-03-20T09:45:10.557Z', }; result = eventInvocation.getEventResponse(message); console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); @@ -786,9 +786,9 @@ describe('EventInvocation', () => { test('should return event object with last response - multiple events fired', () => { currentCallback({ foo: 'bar2' }); - const message = { params: { event: 'accessibility.onClosedCaptionsSettingsChanged-6' } }; + const message = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; const expectedResponse = { - eventName: 'modulechanged', + eventName: 'mocksdk_mockmodule.onmodulechanged', eventListenerId: 6, eventResponse: { foo: 'bar2' }, eventTime: '2023-05-10T14:18:18.347Z', From 8309f81bd0dac3f6ab38bb51a02a12322ab4970c Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 20 May 2025 10:27:07 +0530 Subject: [PATCH 04/29] Firebolt v2 event changes --- src/App.js | 2 + src/EventInvocation.js | 66 ++++++++++++++++++++----------- src/FireboltTransportInvoker.js | 11 ++++++ test/unit/EventInvocation.test.js | 59 ++++++++++++++++----------- 4 files changed, 92 insertions(+), 46 deletions(-) diff --git a/src/App.js b/src/App.js index 46785300..975f5434 100644 --- a/src/App.js +++ b/src/App.js @@ -90,6 +90,8 @@ export default class App extends Base { this.accessibilityCheck(voiceAnnouncement); }); this.toastStates = []; + // Setting the default execution to the Firebolt v2 + process.env.FIREBOLT_V2 = true; this.overlayed = false; this.overlayDismissTimer = null; const appUrl = window.location; diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 04ddf668..495a0406 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -357,7 +357,6 @@ class EventRegistration extends EventRegistrationInterface { class EventRegistrationV2 extends EventRegistrationInterface { // This method will listen to an event and capture the event response after triggering async registerEvent(moduleWithEventName, params) { - const paramlist = Object.values(params); // Simplified parameter extraction const [sdkType, module] = this.getSdkTypeAndModule(moduleWithEventName); const [schemaList] = await dereferenceOpenRPC(sdkType); const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); @@ -372,29 +371,29 @@ class EventRegistrationV2 extends EventRegistrationInterface { } } catch (err) { logger.error(`Error registering event: ${JSON.stringify(err)}`, 'registerEvent'); - return [err, null]; + return err; } if (eventRegistrationID) { eventHandlerMapV2.set(moduleWithEventName, EventHandlerObject); - return null; + return eventRegistrationID; } } // Helper method to handle Transport events async handleTransportEvent(moduleWithEventName, eventName) { - const { id, promise } = await this.registerEventInTransport(moduleWithEventName); - const result = await promise; - - if (result.message) throw new Error(result.message); - - Transport.addEventEmitter((eventId, value) => { - if (id === eventId && !CONSTANTS.EXCLUDED_VALUES.includes(value)) { - this.eventHandler(moduleWithEventName, value); + const event = moduleWithEventName.charAt(0).toLowerCase() + moduleWithEventName.slice(1); + const args = { listen: true }; + const emit = (value) => { + console.log('Transport event emitter--------'); + if (!CONSTANTS.EXCLUDED_VALUES.includes(value)) { + this.eventHandler(event, value); } - }); - - return id; + }; + const gateway = await this.loadV2TransportModule(); + console.log('Gateway:------', gateway); + gateway.subscribe(event, emit); + return await gateway.request(event, args); } // Helper method to handle SDK events @@ -433,27 +432,34 @@ class EventRegistrationV2 extends EventRegistrationInterface { } } eventListenerResponseHandler(moduleWithEventName, response) { - let count = 1; const registrationResponse = {}; registrationResponse['jsonrpc'] = '2.0'; - registrationResponse['id'] = null; - if (response === null) { - registrationResponse['id'] = count++; + if (response && Number.isInteger(response) && response > 0) { + registrationResponse['id'] = response; + registrationResponse['result'] = { + listening: true, + event: moduleWithEventName, + }; + eventHandlerMapV2.get(moduleWithEventName).setEventListener(registrationResponse); + } else if (response && response.hasOwnProperty('listening') && response.listening) { + registrationResponse['jsonrpc'] = '2.0'; + registrationResponse['id'] = null; registrationResponse['result'] = response; eventHandlerMapV2.get(moduleWithEventName).setEventListener(registrationResponse); } else { registrationResponse['error'] = response; + registrationResponse['id'] = null; } return registrationResponse; } // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - logger.info('Clearing registered listeners' + JSON.stringify(eventHandlerMapV2), 'clearAllListeners'); + logger.info('Clearing registered listeners v2' + JSON.stringify(Object.fromEntries(eventHandlerMapV2)), 'clearAllListeners'); try { eventHistory = []; if (eventHandlerMapV2.size >= 1) { - eventHandlerMapV2.forEach((EventHandlerObject, uniqueListenerKey) => { + eventHandlerMapV2.forEach(async (EventHandlerObject, uniqueListenerKey) => { // The key in the eventhHanldermap is in the format SDK_ModuleName- const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; const eventName = EventHandlerObject.event; @@ -467,12 +473,13 @@ class EventRegistrationV2 extends EventRegistrationInterface { } // Events are cleared by using Transport layer and thus bypassing SDK else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { + const gateway = await this.loadV2TransportModule(); const args = Object.assign({ listen: false }); - Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + gateway.request(eventNameWithModuleName, args); } }); eventHandlerMapV2.clear(); - logger.info('After clearing listeners' + JSON.stringify(eventHandlerMapV2), 'clearAllListeners'); + logger.info('After clearing listeners' + JSON.stringify(Object.fromEntries(eventHandlerMapV2)), 'clearAllListeners'); return 'Cleared Listeners'; } else { logger.info('No active Listeners', 'clearAllListeners'); @@ -484,6 +491,16 @@ class EventRegistrationV2 extends EventRegistrationInterface { return response; } } + + async loadV2TransportModule() { + try { + const GatewayImport = await import('@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); + return GatewayImport.default; + } catch (error) { + logger.error(`Error loading V2 modules for transport mode : ${JSON.stringify(error.message)}`, loadV2TransportModule); + throw new Error(`Error loading V2 modules for transport mode: ${error.message}`); + } + } } export class EventInvocation { @@ -495,7 +512,8 @@ export class EventInvocation { initializeEventRegistration() { const sdkVersion = process.env.SDK_VERSION; const pattern = /(2|\d{2,})\.\d+\.\d+/; - if (sdkVersion && pattern.test(sdkVersion)) { + process.env.FIREBOLT_V2 = true; + if (process.env.FIREBOLT_V2) { return new EventRegistrationV2(); } else { return new EventRegistration(); @@ -504,8 +522,10 @@ export class EventInvocation { async northBoundEventHandling(message) { try { + console.log('----------------------'); const { event: moduleWithEventName, params } = message.params; const response = await this.eventRegistration.registerEvent(moduleWithEventName, params); + console.log('Event response:', response); return this.eventRegistration.eventListenerResponseHandler(moduleWithEventName, response); } catch (error) { return this.handleError('northBoundEventHandling', error); diff --git a/src/FireboltTransportInvoker.js b/src/FireboltTransportInvoker.js index 1c926ce4..df576ed2 100644 --- a/src/FireboltTransportInvoker.js +++ b/src/FireboltTransportInvoker.js @@ -56,7 +56,18 @@ export default class FireboltTransportInvoker { return await invokeProvider.send(module, method, jsonParams); } else if (invoker == CONSTANTS.INVOKEMANAGER) { return await invokeManager.send(module, method, jsonParams); + } else if (process.env.FIREBOLT_V2) { + try { + const Gateway = await import('@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); + const gatewayRequest = Gateway.default; + const promise = await gatewayRequest.request(`${module}.${method}`, jsonParams); + return promise; + } catch (error) { + console.error('Error importing Gateway:', error); + throw Error('Error importing Gateway:' + error); + } } else { + // Default to transport return await Transport.send(module, method, jsonParams); } } else { diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index ff764a9b..c1016a94 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -445,6 +445,7 @@ jest.mock('../../src/FireboltExampleInvoker', () => { callId++; const id = callId; // a mock ID value eventList.push(eventName + '-' + id); + console.log('eventList----:', eventList); return Promise.resolve(id); } }); @@ -485,6 +486,20 @@ jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs', () }; }); +// jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs', () => { +// return { +// subscribe: jest.fn().mockImplementation((event, emit) => { +// console.log('Subscribe function called'); +// }), +// request: jest.fn().mockImplementation((event, args) => { +// console.log('Returning registration response'); +// const result = { listening: true, event: event }; +// console.log('Returning result: ' + JSON.stringify(result)); +// return result; +// }), +// }; +// }); + jest.mock('@firebolt-js/sdk', () => { return { Lifecycle: { @@ -582,8 +597,9 @@ describe('EventInvocation', () => { const result = await eventInvocation.northBoundEventHandling(eventParams); console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(MODULE_MAP.mocksdk.mockmodule.listen).toHaveBeenCalled(); - expect(result.id).toBe(expectedResponse.id); + expect(result.id).toBeGreaterThan(0); expect(result.result).not.toBeNull(); + expect(result.result).toStrictEqual(expectedResponse.result); }); test('should fail if not supported api returns a valid response and not error object', async () => { @@ -645,24 +661,25 @@ describe('EventInvocation', () => { await eventInvocation.getEventResponse(message); }); - test('validate EventInvocation method with communicationMode Transport', async () => { - process.env.COMMUNICATION_MODE = 'Transport'; - const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; - const expectedResponse = { - jsonrpc: '2.0', - result: { - listening: true, - event: 'mocksdk_mockmodule.onmodulechanged', - }, - id: 1, - }; - - const result = await eventInvocation.northBoundEventHandling(eventParams); - console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); - expect(Transport.listen).toHaveBeenCalled(); - expect(result.id).toBe(expectedResponse.id); - expect(result.result).toStrictEqual(expectedResponse.result); - }); + // Check on how to mock the Gateway from firebolt v2 and use it in the test + // test('validate EventInvocation method with communicationMode Transport', async () => { + // process.env.FIREBOLT_V2 = true; + // process.env.COMMUNICATION_MODE = 'Transport'; + // const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; + // const expectedResponse = { + // jsonrpc: '2.0', + // result: { + // listening: true, + // event: 'mocksdk_mockmodule.onmodulechanged', + // }, + // id: 1, + // }; + // console.log('process.env.FIREBOLT_V2-----:', process.env.FIREBOLT_V2); + // const result = await eventInvocation.northBoundEventHandling(eventParams); + // console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); + // process.env.FIREBOLT_V2 == true ? expect(Transport.listen).toHaveBeenCalled() : expect(gateway.subscribe).toHaveBeenCalled(); + // expect(result.result).toStrictEqual(expectedResponse.result); + // }); }); describe('clearEventListeners', () => { @@ -769,7 +786,6 @@ describe('EventInvocation', () => { const message = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; const expectedResponse = { eventName: 'mocksdk_mockmodule.onmodulechanged', - eventListenerId: 6, eventResponse: { foo: 'bar1' }, eventTime: '2025-03-20T09:45:10.557Z', }; @@ -777,7 +793,6 @@ describe('EventInvocation', () => { console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(result).toMatchObject({ eventName: expectedResponse.eventName, - eventListenerId: expectedResponse.eventListenerId, eventResponse: expectedResponse.eventResponse, }); expect(result.eventTime).toBeDefined(); @@ -789,7 +804,6 @@ describe('EventInvocation', () => { const message = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; const expectedResponse = { eventName: 'mocksdk_mockmodule.onmodulechanged', - eventListenerId: 6, eventResponse: { foo: 'bar2' }, eventTime: '2023-05-10T14:18:18.347Z', }; @@ -797,7 +811,6 @@ describe('EventInvocation', () => { console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(result).toMatchObject({ eventName: expectedResponse.eventName, - eventListenerId: expectedResponse.eventListenerId, eventResponse: expectedResponse.eventResponse, }); expect(result.eventTime).toBeDefined(); From 06a05412257205355a55fec285d8f0e202689069 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Sat, 31 May 2025 15:32:18 +0530 Subject: [PATCH 05/29] V1 event changes --- src/EventInvocation.js | 70 +++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 495a0406..aacb6c95 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -32,7 +32,7 @@ const Validator = require('jsonschema').Validator; const validator = new Validator(); const eventHandlerMap = new Map(); const eventHandlerMapV2 = new Map(); -let eventHistory = []; +const eventHistory = new Map(); const logger = require('./utils/Logger')('EventInvocation.js'); class EventHandler { @@ -79,7 +79,6 @@ class EventHandler { const eventSchemaResponse = this.eventSchemaValidation(eventData); eventDataObject = { eventName: this.eventName, - eventListenerId: this.eventListener.eventListenerId, eventResponse: eventData, eventSchemaResult: eventSchemaResponse, eventTime: new Date(), @@ -87,12 +86,16 @@ class EventHandler { } else { eventDataObject = { eventName: this.eventName, - eventListenerId: this.eventListener.id, eventResponse: eventData, eventTime: new Date(), }; } - eventHistory.push(eventDataObject); + const eventMap = eventHistory.get(this.eventName); + if (!eventMap) { + eventHistory.set(this.eventName, [eventDataObject]); + } else { + eventMap.push(eventDataObject); + } } // Schema validation for resolved event data eventSchemaValidation(eventResponse) { @@ -299,11 +302,11 @@ class EventRegistration extends EventRegistrationInterface { let filteredEventDataObjectList; const eventName = message.params.event; if (process.env.STANDALONE == true) { - filteredEventDataObjectList = eventHistory.filter((element) => element.eventListenerId == eventName); + filteredEventDataObjectList = eventHistory.get(eventName.split('-')[0]); } else { - filteredEventDataObjectList = eventHistory.filter((element) => element.eventName == eventName); + filteredEventDataObjectList = eventHistory.get(eventName); } - if (filteredEventDataObjectList.length) { + if (filteredEventDataObjectList && filteredEventDataObjectList.length) { const eventDataObject = filteredEventDataObjectList[filteredEventDataObjectList.length - 1]; return eventDataObject; } else { @@ -317,8 +320,9 @@ class EventRegistration extends EventRegistrationInterface { // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - logger.info('Clearing registered listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); + logger.info('Clearing registered listeners' + JSON.stringify(Object.fromEntries(eventHandlerMap)), 'clearAllListeners'); try { + eventHistory.clear(); if (eventHandlerMap.size >= 1) { eventHandlerMap.forEach((EventHandlerObject, uniqueListenerKey) => { // The key in the eventhHanldermap is in the format SDK_ModuleName- @@ -365,9 +369,9 @@ class EventRegistrationV2 extends EventRegistrationInterface { try { if (process.env.COMMUNICATION_MODE === CONSTANTS.TRANSPORT) { - eventRegistrationID = await this.handleTransportEvent(moduleWithEventName, eventName); + eventRegistrationID = await this.handleTransportEvent(moduleWithEventName, eventName, EventHandlerObject); } else if (process.env.COMMUNICATION_MODE === CONSTANTS.SDK) { - eventRegistrationID = await this.handleSdkEvent(sdkType, module, eventName, moduleWithEventName); + eventRegistrationID = await this.handleSdkEvent(sdkType, module, eventName, moduleWithEventName, EventHandlerObject); } } catch (err) { logger.error(`Error registering event: ${JSON.stringify(err)}`, 'registerEvent'); @@ -381,27 +385,30 @@ class EventRegistrationV2 extends EventRegistrationInterface { } // Helper method to handle Transport events - async handleTransportEvent(moduleWithEventName, eventName) { + async handleTransportEvent(moduleWithEventName, eventName, EventHandlerObject) { const event = moduleWithEventName.charAt(0).toLowerCase() + moduleWithEventName.slice(1); const args = { listen: true }; const emit = (value) => { console.log('Transport event emitter--------'); if (!CONSTANTS.EXCLUDED_VALUES.includes(value)) { - this.eventHandler(event, value); + EventHandlerObject.handleEvent(value); } }; const gateway = await this.loadV2TransportModule(); + if (!gateway || gateway === 'notFound') { + throw new Error('Gateway module not loaded. Please check your .'); + } console.log('Gateway:------', gateway); gateway.subscribe(event, emit); return await gateway.request(event, args); } // Helper method to handle SDK events - async handleSdkEvent(sdkType, module, eventName, moduleWithEventName) { + async handleSdkEvent(sdkType, module, eventName, moduleWithEventName, EventHandlerObject) { const resolvedModule = MODULE_MAP[sdkType][module]; return await resolvedModule.listen(eventName, (result) => { if (!CONSTANTS.EXCLUDED_VALUES.includes(result)) { - this.eventHandler(moduleWithEventName, result); + EventHandlerObject.handleEvent(result); } }); } @@ -412,25 +419,17 @@ class EventRegistrationV2 extends EventRegistrationInterface { return `${eventNameWithoutSDK}-${eventRegistrationID}`; } - // Handle event responses - eventHandler(eventName, response) { - eventHistory.push({ - eventName, - eventResponse: response, - eventTime: new Date(), - }); - } - // Return the event response object for the eventName passed as the param getEventResponse(message) { try { const eventName = message.params.event; - const filteredEvents = eventHistory.filter((event) => event.eventName === eventName); - return filteredEvents.length ? filteredEvents[filteredEvents.length - 1] : { [eventName]: null }; + const filteredEvents = eventHistory.get(eventName); + return filteredEvents && filteredEvents.length ? filteredEvents[filteredEvents.length - 1] : { [eventName]: null }; } catch (err) { return { error: { code: 'FCAError', message: `Event response fetch error: ${err.message}` } }; } } + eventListenerResponseHandler(moduleWithEventName, response) { const registrationResponse = {}; registrationResponse['jsonrpc'] = '2.0'; @@ -457,7 +456,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { clearAllListeners() { logger.info('Clearing registered listeners v2' + JSON.stringify(Object.fromEntries(eventHandlerMapV2)), 'clearAllListeners'); try { - eventHistory = []; + eventHistory.clear(); if (eventHandlerMapV2.size >= 1) { eventHandlerMapV2.forEach(async (EventHandlerObject, uniqueListenerKey) => { // The key in the eventhHanldermap is in the format SDK_ModuleName- @@ -474,6 +473,9 @@ class EventRegistrationV2 extends EventRegistrationInterface { // Events are cleared by using Transport layer and thus bypassing SDK else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { const gateway = await this.loadV2TransportModule(); + if (!gateway || gateway === 'notFound') { + throw new Error('Gateway module not loaded. Please check your .'); + } const args = Object.assign({ listen: false }); gateway.request(eventNameWithModuleName, args); } @@ -493,13 +495,17 @@ class EventRegistrationV2 extends EventRegistrationInterface { } async loadV2TransportModule() { - try { - const GatewayImport = await import('@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); - return GatewayImport.default; - } catch (error) { - logger.error(`Error loading V2 modules for transport mode : ${JSON.stringify(error.message)}`, loadV2TransportModule); - throw new Error(`Error loading V2 modules for transport mode: ${error.message}`); + let gateway; + if (!gateway) { + try { + const GatewayImport = await import('@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); + return GatewayImport.default; + } catch (error) { + logger.error(`Error loading V2 modules for transport mode : ${JSON.stringify(error.message)}`, loadV2TransportModule); + return 'notFound'; + } } + return 'notFound'; } } From 022acf33de0b52b615559ba51f240f8828a5572c Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 3 Jun 2025 12:51:52 +0530 Subject: [PATCH 06/29] Updated import of Transport for v1 and v2 --- src/App.js | 2 +- src/EventInvocation.js | 7 ++----- src/FireboltTransportInvoker.js | 14 +++----------- test/unit/EventInvocation.test.js | 8 ++++---- webpack.dev.js | 4 ++++ webpack.prod.js | 4 ++++ 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/App.js b/src/App.js index 975f5434..ceacdfd5 100644 --- a/src/App.js +++ b/src/App.js @@ -91,7 +91,7 @@ export default class App extends Base { }); this.toastStates = []; // Setting the default execution to the Firebolt v2 - process.env.FIREBOLT_V2 = true; + process.env.FCA_FIREBOLT_SDK_VERSION = true; this.overlayed = false; this.overlayDismissTimer = null; const appUrl = window.location; diff --git a/src/EventInvocation.js b/src/EventInvocation.js index aacb6c95..d60a6dae 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -26,7 +26,7 @@ import { dereferenceOpenRPC, errorSchemaCheck } from './utils/Utils'; import { MODULE_MAP } from './FireboltExampleInvoker'; import { CONSTANTS } from './constant'; -import Transport from '@firebolt-js/sdk/dist/lib/Transport/index.mjs'; +import Transport from 'Transport'; const Validator = require('jsonschema').Validator; const validator = new Validator(); @@ -516,10 +516,7 @@ export class EventInvocation { // Initialize Event Registration based on SDK version initializeEventRegistration() { - const sdkVersion = process.env.SDK_VERSION; - const pattern = /(2|\d{2,})\.\d+\.\d+/; - process.env.FIREBOLT_V2 = true; - if (process.env.FIREBOLT_V2) { + if (process.env.FCA_FIREBOLT_SDK_VERSION) { return new EventRegistrationV2(); } else { return new EventRegistration(); diff --git a/src/FireboltTransportInvoker.js b/src/FireboltTransportInvoker.js index df576ed2..7394159d 100644 --- a/src/FireboltTransportInvoker.js +++ b/src/FireboltTransportInvoker.js @@ -16,7 +16,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import Transport from '@firebolt-js/sdk/dist/lib/Transport'; +import Transport from 'Transport'; import { CONSTANTS } from './constant'; const logger = require('./utils/Logger')('FireboltTransportInvoker.js'); @@ -56,16 +56,8 @@ export default class FireboltTransportInvoker { return await invokeProvider.send(module, method, jsonParams); } else if (invoker == CONSTANTS.INVOKEMANAGER) { return await invokeManager.send(module, method, jsonParams); - } else if (process.env.FIREBOLT_V2) { - try { - const Gateway = await import('@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); - const gatewayRequest = Gateway.default; - const promise = await gatewayRequest.request(`${module}.${method}`, jsonParams); - return promise; - } catch (error) { - console.error('Error importing Gateway:', error); - throw Error('Error importing Gateway:' + error); - } + } else if (process.env.FCA_FIREBOLT_SDK_VERSION) { + return await Transport.request(`${module}.${method}`, jsonParams); } else { // Default to transport return await Transport.send(module, method, jsonParams); diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index c1016a94..f4e45d89 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -18,7 +18,7 @@ import { EventInvocation } from '../../src/EventInvocation'; import { MODULE_MAP } from '../../src/FireboltExampleInvoker'; -import Transport from '@firebolt-js/sdk/dist/lib/Transport/index.mjs'; +import Transport from 'Transport'; const schemaList = { openrpc: '1.2.4', @@ -663,7 +663,7 @@ describe('EventInvocation', () => { // Check on how to mock the Gateway from firebolt v2 and use it in the test // test('validate EventInvocation method with communicationMode Transport', async () => { - // process.env.FIREBOLT_V2 = true; + // process.env.FCA_FIREBOLT_SDK_VERSION = true; // process.env.COMMUNICATION_MODE = 'Transport'; // const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; // const expectedResponse = { @@ -674,10 +674,10 @@ describe('EventInvocation', () => { // }, // id: 1, // }; - // console.log('process.env.FIREBOLT_V2-----:', process.env.FIREBOLT_V2); + // console.log('process.env.FCA_FIREBOLT_SDK_VERSION-----:', process.env.FCA_FIREBOLT_SDK_VERSION); // const result = await eventInvocation.northBoundEventHandling(eventParams); // console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); - // process.env.FIREBOLT_V2 == true ? expect(Transport.listen).toHaveBeenCalled() : expect(gateway.subscribe).toHaveBeenCalled(); + // process.env.FCA_FIREBOLT_SDK_VERSION == true ? expect(Transport.listen).toHaveBeenCalled() : expect(gateway.subscribe).toHaveBeenCalled(); // expect(result.result).toStrictEqual(expectedResponse.result); // }); }); diff --git a/webpack.dev.js b/webpack.dev.js index 4a89f498..da39e318 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -115,6 +115,10 @@ module.exports = { name: 'RunTestHandler', alias: ['/plugins/runTestHandler.js', '/src/pubsub/handlers/RunTestHandler.js'], }, + { + name: 'Transport', + alias: ['@firebolt-js/sdk/dist/lib/Transport/index.mjs', '@firebolt-js/sdk/dist/lib/Gateway/index.mjs'], + }, ], 'resolve' ), diff --git a/webpack.prod.js b/webpack.prod.js index d1892351..f0361987 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -98,6 +98,10 @@ module.exports = { name: 'RunTestHandler', alias: ['/plugins/runTestHandler.js', '/src/pubsub/handlers/RunTestHandler.js'], }, + { + name: 'Transport', + alias: ['@firebolt-js/sdk/dist/lib/Transport/index.mjs', '@firebolt-js/sdk/dist/lib/Gateway/index.mjs'], + }, ], 'resolve' ), From b52c336fa4a50c3afe2f8749b5af82e23106fd91 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 3 Jun 2025 14:39:55 +0530 Subject: [PATCH 07/29] Alias correction --- webpack.dev.js | 2 +- webpack.prod.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webpack.dev.js b/webpack.dev.js index da39e318..67f78dd3 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -117,7 +117,7 @@ module.exports = { }, { name: 'Transport', - alias: ['@firebolt-js/sdk/dist/lib/Transport/index.mjs', '@firebolt-js/sdk/dist/lib/Gateway/index.mjs'], + alias: ['@firebolt-js/sdk/dist/lib/Gateway/index.mjs', '@firebolt-js/sdk/dist/lib/Transport/index.mjs'], }, ], 'resolve' diff --git a/webpack.prod.js b/webpack.prod.js index f0361987..35183425 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -100,7 +100,7 @@ module.exports = { }, { name: 'Transport', - alias: ['@firebolt-js/sdk/dist/lib/Transport/index.mjs', '@firebolt-js/sdk/dist/lib/Gateway/index.mjs'], + alias: ['@firebolt-js/sdk/dist/lib/Gateway/index.mjs', '@firebolt-js/sdk/dist/lib/Transport/index.mjs'], }, ], 'resolve' From f0ac7c95d3d81d13d3b91337453b9356fabf2fe5 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 3 Jun 2025 16:34:20 +0530 Subject: [PATCH 08/29] Code Optimisation --- src/EventInvocation.js | 136 +++++++++++++---------------------------- 1 file changed, 44 insertions(+), 92 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index d60a6dae..13dcde67 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -175,6 +175,46 @@ class EventRegistrationInterface { const args = Object.assign({ listen: true }, params); return await Transport.listen(module, method, args); } + + clearAllListeners(eventHandlerMap) { + logger.info('Clearing registered listeners' + JSON.stringify(Object.fromEntries(eventHandlerMap)), 'clearAllListeners'); + try { + eventHistory.clear(); + if (eventHandlerMap.size >= 1) { + eventHandlerMap.forEach(async (EventHandlerObject, uniqueListenerKey) => { + // The key in the eventhHanldermap is in the format SDK_ModuleName- + const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; + const eventName = EventHandlerObject.event; + const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); + logger.info('Unregistered event- ' + eventNameWithModuleName, 'clearAllListeners'); + + // Events are cleared using Firebolt SDK + if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { + MODULE_MAP[sdkType][module].clear(eventName); + } + // Events are cleared by using Transport layer and thus bypassing SDK + else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { + const args = { listen: false }; + if (process.env.FCA_FIREBOLT_SDK_VERSION) { + await Transport.request(eventNameWithModuleName, args); + } else { + await Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + } + } + }); + eventHandlerMap.clear(); + logger.info('After clearing listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); + return 'Cleared Listeners'; + } else { + logger.info('No active Listeners', 'clearAllListeners'); + return 'No active listeners'; + } + } catch (err) { + logger.error('Error while clearing all event listeners' + err.message, 'clearAllListeners'); + const response = { error: { code: 'FCAError', message: 'Error while clearing all event listeners: ' + err.message } }; + return response; + } + } } // 1.0 Implementation @@ -320,40 +360,7 @@ class EventRegistration extends EventRegistrationInterface { // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - logger.info('Clearing registered listeners' + JSON.stringify(Object.fromEntries(eventHandlerMap)), 'clearAllListeners'); - try { - eventHistory.clear(); - if (eventHandlerMap.size >= 1) { - eventHandlerMap.forEach((EventHandlerObject, uniqueListenerKey) => { - // The key in the eventhHanldermap is in the format SDK_ModuleName- - const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; - const eventName = EventHandlerObject.eventName; - const eventRegistrationID = uniqueListenerKey.split('-')[1]; - const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); - logger.info('Unregister event ' + eventNameWithModuleName + ' registration ID ' + eventRegistrationID, 'clearAllListeners'); - - // Events are cleared using Firebolt SDK - if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - MODULE_MAP[sdkType][module].clear(eventName); - } - // Events are cleared by using Transport layer and thus bypassing SDK - else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { - const args = Object.assign({ listen: false }); - Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); - } - }); - eventHandlerMap.clear(); - logger.info('After clearing listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); - return 'Cleared Listeners'; - } else { - logger.info('No active Listeners', 'clearAllListeners'); - return 'No active listeners'; - } - } catch (err) { - logger.error('Error while clearing all event listeners' + err, 'clearAllListeners'); - const response = { error: { code: 'FCAError', message: 'Error while clearing all event listeners: ' + err.message } }; - return response; - } + super.clearAllListeners(eventHandlerMap); } } @@ -394,13 +401,9 @@ class EventRegistrationV2 extends EventRegistrationInterface { EventHandlerObject.handleEvent(value); } }; - const gateway = await this.loadV2TransportModule(); - if (!gateway || gateway === 'notFound') { - throw new Error('Gateway module not loaded. Please check your .'); - } console.log('Gateway:------', gateway); - gateway.subscribe(event, emit); - return await gateway.request(event, args); + Transport.subscribe(event, emit); + return await Transport.request(event, args); } // Helper method to handle SDK events @@ -454,58 +457,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - logger.info('Clearing registered listeners v2' + JSON.stringify(Object.fromEntries(eventHandlerMapV2)), 'clearAllListeners'); - try { - eventHistory.clear(); - if (eventHandlerMapV2.size >= 1) { - eventHandlerMapV2.forEach(async (EventHandlerObject, uniqueListenerKey) => { - // The key in the eventhHanldermap is in the format SDK_ModuleName- - const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; - const eventName = EventHandlerObject.event; - const eventRegistrationID = uniqueListenerKey.split('-')[1]; - const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); - logger.info('Unregister event ' + eventNameWithModuleName + ' registration ID ' + eventRegistrationID, 'clearAllListeners'); - - // Events are cleared using Firebolt SDK - if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - MODULE_MAP[sdkType][module].clear(eventName); - } - // Events are cleared by using Transport layer and thus bypassing SDK - else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { - const gateway = await this.loadV2TransportModule(); - if (!gateway || gateway === 'notFound') { - throw new Error('Gateway module not loaded. Please check your .'); - } - const args = Object.assign({ listen: false }); - gateway.request(eventNameWithModuleName, args); - } - }); - eventHandlerMapV2.clear(); - logger.info('After clearing listeners' + JSON.stringify(Object.fromEntries(eventHandlerMapV2)), 'clearAllListeners'); - return 'Cleared Listeners'; - } else { - logger.info('No active Listeners', 'clearAllListeners'); - return 'No active listeners'; - } - } catch (err) { - logger.error('Error while clearing all event listeners' + err, 'clearAllListeners'); - const response = { error: { code: 'FCAError', message: 'Error while clearing all event listeners: ' + err.message } }; - return response; - } - } - - async loadV2TransportModule() { - let gateway; - if (!gateway) { - try { - const GatewayImport = await import('@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); - return GatewayImport.default; - } catch (error) { - logger.error(`Error loading V2 modules for transport mode : ${JSON.stringify(error.message)}`, loadV2TransportModule); - return 'notFound'; - } - } - return 'notFound'; + super.clearAllListeners(eventHandlerMapV2); } } From 87dd09778d934a4435e7faa4acf93da976dff034 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 3 Jun 2025 16:43:05 +0530 Subject: [PATCH 09/29] Removed console logs --- src/EventInvocation.js | 10 +++------- src/constant.js | 2 ++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 13dcde67..0249981a 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -204,10 +204,10 @@ class EventRegistrationInterface { }); eventHandlerMap.clear(); logger.info('After clearing listeners' + JSON.stringify(eventHandlerMap), 'clearAllListeners'); - return 'Cleared Listeners'; + return CONSTANTS.CLEARED_LISTENERS; } else { logger.info('No active Listeners', 'clearAllListeners'); - return 'No active listeners'; + return CONSTANTS.NO_ACTIVE_LISTENERS; } } catch (err) { logger.error('Error while clearing all event listeners' + err.message, 'clearAllListeners'); @@ -266,7 +266,7 @@ class EventRegistration extends EventRegistrationInterface { return [err, null]; } - // Construct unique key for event handler. A UUID can be added to the key to make it more unique. + // Construct unique key for event handler. Using the event registration ID. if (eventRegistrationID) { const eventNameWithoutSDK = moduleWithEventName.includes('_') ? moduleWithEventName.split('_')[1] : moduleWithEventName; const uniqueListenerKey = eventNameWithoutSDK + '-' + eventRegistrationID; @@ -396,12 +396,10 @@ class EventRegistrationV2 extends EventRegistrationInterface { const event = moduleWithEventName.charAt(0).toLowerCase() + moduleWithEventName.slice(1); const args = { listen: true }; const emit = (value) => { - console.log('Transport event emitter--------'); if (!CONSTANTS.EXCLUDED_VALUES.includes(value)) { EventHandlerObject.handleEvent(value); } }; - console.log('Gateway:------', gateway); Transport.subscribe(event, emit); return await Transport.request(event, args); } @@ -477,10 +475,8 @@ export class EventInvocation { async northBoundEventHandling(message) { try { - console.log('----------------------'); const { event: moduleWithEventName, params } = message.params; const response = await this.eventRegistration.registerEvent(moduleWithEventName, params); - console.log('Event response:', response); return this.eventRegistration.eventListenerResponseHandler(moduleWithEventName, response); } catch (error) { return this.handleError('northBoundEventHandling', error); diff --git a/src/constant.js b/src/constant.js index d2c826a6..acf6094f 100644 --- a/src/constant.js +++ b/src/constant.js @@ -193,4 +193,6 @@ export const CONSTANTS = { SLA_VALIDATION_INTENT: 'sla-validation', OPENRPC_URL: 'https://rdkcentral.github.io/firebolt/requirements/latest/specifications/firebolt-open-rpc.json', DEFAULT_SLA: 300, + CLEARED_LISTENERS: 'Cleared Listeners', + NO_ACTIVE_LISTENERS: 'No active listeners', }; From 16e1f20a56f6b023c80f616096b7a793ef0cb0b2 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 3 Jun 2025 16:50:31 +0530 Subject: [PATCH 10/29] Added comments --- src/App.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/App.js b/src/App.js index ceacdfd5..f8cea481 100644 --- a/src/App.js +++ b/src/App.js @@ -90,8 +90,9 @@ export default class App extends Base { this.accessibilityCheck(voiceAnnouncement); }); this.toastStates = []; - // Setting the default execution to the Firebolt v2 - process.env.FCA_FIREBOLT_SDK_VERSION = true; + // Setting the firebolt version by fetching it from open-rpc + // Commented out for now, until the implementation of fetching the version is done + // process.env.FCA_FIREBOLT_SDK_VERSION = true; this.overlayed = false; this.overlayDismissTimer = null; const appUrl = window.location; From c05ec388e4d91e131b9da4c27d5c624378fe2367 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Thu, 5 Jun 2025 10:48:04 +0530 Subject: [PATCH 11/29] Fixed unit testcases --- src/App.js | 6 +- src/EventInvocation.js | 22 ++-- src/FireboltTransportInvoker.js | 2 +- test/jest.config.js | 1 + test/jest.setup.js | 2 + test/jest.transport-resolver.js | 23 ++++ test/unit/EventInvocation.test.js | 200 ++++++++++++++++++++--------- test/unit/LifeCycleHistory.test.js | 16 --- test/unit/test_runner.test.js | 55 +++++++- 9 files changed, 233 insertions(+), 94 deletions(-) create mode 100644 test/jest.transport-resolver.js diff --git a/src/App.js b/src/App.js index f8cea481..307cb133 100644 --- a/src/App.js +++ b/src/App.js @@ -91,8 +91,10 @@ export default class App extends Base { }); this.toastStates = []; // Setting the firebolt version by fetching it from open-rpc - // Commented out for now, until the implementation of fetching the version is done - // process.env.FCA_FIREBOLT_SDK_VERSION = true; + // Added the current firebolt version as default value, until the implementation of fetching the version is done + process.env.FCA_FIREBOLT_SDK_VERSION = '1.5.0'; + const pattern = /(2|\d{2,})\.\d+\.\d+/; + process.env.IS_BIDIRECTIONAL_SDK = pattern.test(process.env.FCA_FIREBOLT_SDK_VERSION); this.overlayed = false; this.overlayDismissTimer = null; const appUrl = window.location; diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 0249981a..e32aac17 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -123,7 +123,7 @@ class EventHandler { } class EventRegistrationInterface { - clearEventListeners(event) { + async clearEventListeners(event) { try { const [sdkType, module] = this.getSdkTypeAndModule(event); let eventName = event.split('.')[1]; @@ -132,8 +132,12 @@ class EventRegistrationInterface { if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { MODULE_MAP[sdkType][module].clear(eventName); } else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { - const args = Object.assign({ listen: false }); - Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + const args = { listen: false }; + if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { + await Transport.request(`${module}.on${eventName[0].toUpperCase()}${eventName.substr(1)}`, args); + } else { + await Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + } } return true; } catch (err) { @@ -195,7 +199,7 @@ class EventRegistrationInterface { // Events are cleared by using Transport layer and thus bypassing SDK else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { const args = { listen: false }; - if (process.env.FCA_FIREBOLT_SDK_VERSION) { + if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { await Transport.request(eventNameWithModuleName, args); } else { await Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); @@ -360,7 +364,7 @@ class EventRegistration extends EventRegistrationInterface { // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - super.clearAllListeners(eventHandlerMap); + return super.clearAllListeners(eventHandlerMap); } } @@ -455,7 +459,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - super.clearAllListeners(eventHandlerMapV2); + return super.clearAllListeners(eventHandlerMapV2); } } @@ -466,7 +470,7 @@ export class EventInvocation { // Initialize Event Registration based on SDK version initializeEventRegistration() { - if (process.env.FCA_FIREBOLT_SDK_VERSION) { + if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { return new EventRegistrationV2(); } else { return new EventRegistration(); @@ -483,9 +487,9 @@ export class EventInvocation { } } - clearEventListeners(event) { + async clearEventListeners(event) { try { - return this.eventRegistration.clearEventListeners(event); + return await this.eventRegistration.clearEventListeners(event); } catch (error) { return this.handleError('clearEventListeners', error); } diff --git a/src/FireboltTransportInvoker.js b/src/FireboltTransportInvoker.js index 7394159d..71f4a7d9 100644 --- a/src/FireboltTransportInvoker.js +++ b/src/FireboltTransportInvoker.js @@ -56,7 +56,7 @@ export default class FireboltTransportInvoker { return await invokeProvider.send(module, method, jsonParams); } else if (invoker == CONSTANTS.INVOKEMANAGER) { return await invokeManager.send(module, method, jsonParams); - } else if (process.env.FCA_FIREBOLT_SDK_VERSION) { + } else if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { return await Transport.request(`${module}.${method}`, jsonParams); } else { // Default to transport diff --git a/test/jest.config.js b/test/jest.config.js index 89d1d76b..63ca2dad 100644 --- a/test/jest.config.js +++ b/test/jest.config.js @@ -36,6 +36,7 @@ module.exports = { '^CensorData$': '/../src/source/censorData.json', '^RunTestHandler$': '/../src/pubsub/handlers/RunTestHandler.js', }, + resolver: '/jest.transport-resolver.js', transform: { '^.+\\.[tj]s$': 'babel-jest', '^.+\\.mjs$': 'babel-jest', diff --git a/test/jest.setup.js b/test/jest.setup.js index a1bc9b1b..9f0bfb2e 100644 --- a/test/jest.setup.js +++ b/test/jest.setup.js @@ -183,6 +183,8 @@ jest.mock('../src/constant', () => { SLA_VALIDATION_INTENT: 'sla-validation', OPENRPC_URL: 'https://rdkcentral.github.io/firebolt/requirements/latest/specifications/firebolt-open-rpc.json', DEFAULT_SLA: 300, + CLEARED_LISTENERS: 'Cleared Listeners', + NO_ACTIVE_LISTENERS: 'No active listeners', }; return { CONSTANTS }; diff --git a/test/jest.transport-resolver.js b/test/jest.transport-resolver.js new file mode 100644 index 00000000..f34bdf29 --- /dev/null +++ b/test/jest.transport-resolver.js @@ -0,0 +1,23 @@ +const fs = require('fs'); +const path = require('path'); + +module.exports = (request, options) => { + const defaultResolve = options.defaultResolver; + if (request === 'Transport') { + try { + const files = fs.readdirSync('node_modules/@firebolt-js/sdk/dist/lib/'); + const hasGateway = files.includes('Gateway'); + if (hasGateway) { + console.log('Using Gateway from @firebolt-js/sdk'); + return path.resolve(__dirname, '../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); + } else { + console.log('Using Transport from @firebolt-js/sdk'); + return path.resolve(__dirname, '../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs'); + } + } catch (err) { + console.error(`Error resolving files: ${err.message}`); + return defaultResolve(request, options); + } + } + return defaultResolve(request, options); +}; diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index f4e45d89..1460858e 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -401,6 +401,8 @@ const schemaList = { ], }; +process.env.IS_BIDIRECTIONAL_SDK = false; + // Mocking $abc library and its functions const mockFireboltExampleInvoker = { @@ -471,35 +473,6 @@ jest.mock('../../src/FireboltExampleInvoker', () => { }; }); -jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { - let callId = 0; - callId++; - return { - listen: jest.fn().mockImplementation(() => { - console.log('Returning promise and id'); - const result = { id: callId, promise: Promise.resolve('success') }; - console.log('Returning result: ' + JSON.stringify(result)); - return result; - }), - send: jest.fn(), - addEventEmitter: jest.fn(), - }; -}); - -// jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs', () => { -// return { -// subscribe: jest.fn().mockImplementation((event, emit) => { -// console.log('Subscribe function called'); -// }), -// request: jest.fn().mockImplementation((event, args) => { -// console.log('Returning registration response'); -// const result = { listening: true, event: event }; -// console.log('Returning result: ' + JSON.stringify(result)); -// return result; -// }), -// }; -// }); - jest.mock('@firebolt-js/sdk', () => { return { Lifecycle: { @@ -577,13 +550,21 @@ describe('EventInvocation', () => { }); }); describe('northBoundEventHandling and registerEvent', () => { - let eventInvocation; + let actualVersion; beforeAll(() => { jest.clearAllMocks(); + jest.resetModules(); console.log('initializing eventInvocation'); process.env.COMMUNICATION_MODE = 'SDK'; eventInvocation = new EventInvocation(); }); + beforeEach(() => { + actualVersion = process.env.IS_BIDIRECTIONAL_SDK; + }); + afterEach(() => { + process.env.IS_BIDIRECTIONAL_SDK = actualVersion; + }); + let eventInvocation; test('validate EventInvocation method with communicationMode SDK', async () => { const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; const expectedResponse = { @@ -662,69 +643,166 @@ describe('EventInvocation', () => { }); // Check on how to mock the Gateway from firebolt v2 and use it in the test - // test('validate EventInvocation method with communicationMode Transport', async () => { - // process.env.FCA_FIREBOLT_SDK_VERSION = true; - // process.env.COMMUNICATION_MODE = 'Transport'; - // const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; - // const expectedResponse = { - // jsonrpc: '2.0', - // result: { - // listening: true, - // event: 'mocksdk_mockmodule.onmodulechanged', - // }, - // id: 1, - // }; - // console.log('process.env.FCA_FIREBOLT_SDK_VERSION-----:', process.env.FCA_FIREBOLT_SDK_VERSION); - // const result = await eventInvocation.northBoundEventHandling(eventParams); - // console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); - // process.env.FCA_FIREBOLT_SDK_VERSION == true ? expect(Transport.listen).toHaveBeenCalled() : expect(gateway.subscribe).toHaveBeenCalled(); - // expect(result.result).toStrictEqual(expectedResponse.result); - // }); + test('validate EventInvocation method with communicationMode Transport', async () => { + process.env.COMMUNICATION_MODE = 'Transport'; + const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; + const expectedResponse = { + jsonrpc: '2.0', + result: { + listening: true, + event: 'mocksdk_mockmodule.onmodulechanged', + }, + id: 1, + }; + let eventtInvocationImport, transport; + if (Transport.request == null) { + // v1 events + jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { + let callId = 0; + callId++; + return { + listen: jest.fn().mockImplementation(() => { + console.log('Returning promise and id'); + const result = { id: callId, promise: Promise.resolve('success') }; + console.log('Returning result: ' + JSON.stringify(result)); + return result; + }), + send: jest.fn(), + addEventEmitter: jest.fn(), + }; + }); + process.env.IS_BIDIRECTIONAL_SDK = false; + // re-import the modules after mocking it + eventtInvocationImport = require('../../src/EventInvocation'); + transport = require('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs'); + } else { + // v2 events + jest.doMock('../../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs', () => { + let callId = 0; + callId++; + return { + request: jest.fn().mockImplementation(() => { + console.log('Returning promise and id'); + const result = { id: callId, promise: Promise.resolve('success') }; + console.log('Returning result: ' + JSON.stringify(result)); + return result; + }), + subscribe: jest.fn().mockImplementation((eventName, callBack) => { + console.log('subscribe function called for event: ' + eventName); + }), + }; + }); + process.env.IS_BIDIRECTIONAL_SDK = true; + // re-import the modules after mocking it + eventtInvocationImport = require('../../src/EventInvocation'); + transport = require('../../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); + } + // re-import the modules after mocking it + const eventInvocation = eventtInvocationImport.EventInvocation; + const eventInvocationTest = new eventInvocation(); + const result = await eventInvocationTest.northBoundEventHandling(eventParams); + if (transport.request == null) { + expect(transport.listen).toHaveBeenCalled(); + } else { + expect(transport.request).toHaveBeenCalled(); + expect(transport.subscribe).toHaveBeenCalled(); + } + }); }); describe('clearEventListeners', () => { let eventInvocation; + let actualVersion; beforeAll(() => { jest.clearAllMocks(); + jest.resetModules(); console.log('initializing eventInvocation'); process.env.COMMUNICATION_MODE = 'SDK'; eventInvocation = new EventInvocation(); }); - test('should call clear on the eventName and return true', () => { + beforeEach(() => { + actualVersion = process.env.IS_BIDIRECTIONAL_SDK; + }); + afterEach(() => { + process.env.IS_BIDIRECTIONAL_SDK = actualVersion; + }); + test('should call clear on the eventName and return true', async () => { const event = 'mocksdk_mockmodule.onmodulechanged'; - const result = eventInvocation.clearEventListeners(event); + const result = await eventInvocation.clearEventListeners(event); console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(result).toBe(true); expect(MODULE_MAP.mocksdk.mockmodule.clear).toHaveBeenCalledWith(event.split('.')[1].slice(2)); }); - test('should return error on issues with event name', () => { + test('should return error on issues with event name', async () => { const event = 'onmodulechanged'; - const result = eventInvocation.clearEventListeners(event); + const result = await eventInvocation.clearEventListeners(event); console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(result.error).toBeDefined(); expect(result.error.code).toBe('FCAError'); expect(result.error.message).toBeDefined(); }); - test('should return error on issues with event name', () => { + test('should return error on issues with event name', async () => { const event = 'mockmodule.modulechanged'; - const result = eventInvocation.clearEventListeners(event); + const result = await eventInvocation.clearEventListeners(event); console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(result.error).toBeDefined(); expect(result.error.code).toBe('FCAError'); expect(result.error.message).toBeDefined(); }); - test('should clear listener by sending listen false when communication mode is transport', () => { + test('should clear listener by sending listen false when communication mode is transport', async () => { const event = 'mocksdk_mockmodule.onmodulechanged'; process.env.COMMUNICATION_MODE = 'Transport'; - const result = eventInvocation.clearEventListeners(event); - console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); - expect(result).toBe(true); - expect(Transport.send).toHaveBeenCalledWith('mockmodule', 'onModulechanged', { - listen: false, - }); + if (Transport.request == null) { + // v1 events + jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { + let callId = 0; + callId++; + return { + send: jest.fn(), + }; + }); + process.env.IS_BIDIRECTIONAL_SDK = false; + // re-import the modules after mocking it + const eventInvocationImport = require('../../src/EventInvocation'); + const Transport = require('Transport'); + const eventInvocation = eventInvocationImport.EventInvocation; + const eventInvocationTest = new eventInvocation(); + const result = await eventInvocationTest.clearEventListeners(event); + console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); + expect(result).toBe(true); + expect(Transport.send).toHaveBeenCalledWith('mockmodule', 'onModulechanged', { + listen: false, + }); + } else { + // v2 events + jest.doMock('../../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs', () => { + let callId = 0; + callId++; + return { + request: jest.fn().mockImplementation(() => { + console.log('Returning promise and id'); + const result = { id: callId, promise: Promise.resolve('success') }; + console.log('Returning result: ' + JSON.stringify(result)); + return result; + }), + }; + }); + process.env.IS_BIDIRECTIONAL_SDK = true; + // re-import the modules after mocking it + const eventInvocationImport = require('../../src/EventInvocation'); + const Transport = require('Transport'); + const eventInvocation = eventInvocationImport.EventInvocation; + const eventInvocationTest = new eventInvocation(); + const result = await eventInvocationTest.clearEventListeners(event); + console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); + expect(result).toBe(true); + expect(Transport.request).toHaveBeenCalledWith('mockmodule.onModulechanged', { + listen: false, + }); + } }); }); diff --git a/test/unit/LifeCycleHistory.test.js b/test/unit/LifeCycleHistory.test.js index 1e9f7c41..e6fe4d05 100644 --- a/test/unit/LifeCycleHistory.test.js +++ b/test/unit/LifeCycleHistory.test.js @@ -34,14 +34,6 @@ jest.mock('../../src/utils/Utils', () => { }; }); -jest.mock('@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { - return { - send: () => { - return {}; - }, - }; -}); - jest.mock('../../src/FireboltExampleInvoker', () => { return { get: () => { @@ -50,14 +42,6 @@ jest.mock('../../src/FireboltExampleInvoker', () => { }; }); -jest.mock('../../src/FireboltTransportInvoker', () => { - return { - get: () => { - return mockFireboltTransportInvoker; - }, - }; -}); - jest.mock('../../src/pubsub/handlers/RegisterProviderHandler', () => { return jest.fn().mockImplementation(() => ({ handle: jest.fn().mockResolvedValue(JSON.stringify({ report: 'registered' })), diff --git a/test/unit/test_runner.test.js b/test/unit/test_runner.test.js index afad3ab4..353d37a0 100644 --- a/test/unit/test_runner.test.js +++ b/test/unit/test_runner.test.js @@ -18,6 +18,7 @@ import { Test_Runner } from '../../src/Test_Runner'; import { CONSTANTS } from '../../src/constant'; +import Transport from 'Transport'; const Validator = require('jsonschema').Validator; /** * This is a moc stucture of the actual OPEN RPC document @@ -508,10 +509,6 @@ jest.mock('../../src/FireboltExampleInvoker', () => ({ get: () => mockFireboltExampleInvoker, })); -jest.mock('@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => ({ - send: jest.fn().mockReturnValue({}), -})); - jest.mock('../../src/FireboltTransportInvoker', () => ({ get: () => mockFireboltTransportInvoker, })); @@ -611,6 +608,8 @@ jest.mock('../../src/MethodFilters', () => ({ })), })); +process.env.IS_BIDIRECTIONAL_SDK = false; + describe('Test_Runner test cases', () => { beforeEach(() => { runner = new Test_Runner(); @@ -625,6 +624,9 @@ describe('Test_Runner test cases', () => { }); describe('northBoundSchemaValidationAndReportGeneration Scenarios', () => { + beforeAll(() => { + jest.resetModules(); + }); test('should return empty result when dereference call fails for SDK', async () => { mockShouldDereferencerFail = true; result = await runner.northBoundSchemaValidationAndReportGeneration('SDK'); @@ -791,7 +793,50 @@ describe('Test_Runner test cases', () => { ], }; process.env.COMMUNICATION_MODE = 'Transport'; - result = await runner.northBoundSchemaValidationAndReportGeneration([CONSTANTS.CORE]); + let runner; + if (Transport.request == null) { + // v1 events + jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { + let callId = 0; + callId++; + return { + listen: jest.fn().mockImplementation(() => { + console.log('Returning promise and id'); + const result = { id: callId, promise: Promise.resolve('success') }; + console.log('Returning result: ' + JSON.stringify(result)); + return result; + }), + send: jest.fn(), + addEventEmitter: jest.fn(), + }; + }); + process.env.IS_BIDIRECTIONAL_SDK = false; + // re-import the modules after mocking it + const testRunner = require('../../src/Test_Runner').Test_Runner; + runner = new testRunner(); + } else { + // v2 events + jest.doMock('Transport', () => { + let callId = 0; + callId++; + return { + request: jest.fn().mockImplementation(() => { + console.log('Returning promise and id'); + const result = { id: callId, promise: Promise.resolve('success') }; + console.log('Returning result: ' + JSON.stringify(result)); + return result; + }), + subscribe: jest.fn().mockImplementation((eventName, callBack) => { + console.log('subscribe function called for event: ' + eventName); + }), + }; + }); + process.env.IS_BIDIRECTIONAL_SDK = true; + // re-import the modules after mocking it + const testRunner = require('../../src/Test_Runner').Test_Runner; + runner = new testRunner(); + } + const result = await runner.northBoundSchemaValidationAndReportGeneration([CONSTANTS.CORE]); const extractedResult = result.find((obj) => obj.title === 'Account.id'); extractedResult.code = JSON.parse(extractedResult.code); expect(extractedResult.code['Schema Validation'].Status).toEqual('passed'); From 984bee24af87054da552039db5ed970c8a7dc6ab Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Thu, 5 Jun 2025 11:14:51 +0530 Subject: [PATCH 12/29] Added comments for unit testcases --- test/jest.transport-resolver.js | 6 ++++-- test/unit/EventInvocation.test.js | 35 +++++++++++++++---------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/test/jest.transport-resolver.js b/test/jest.transport-resolver.js index f34bdf29..f4422eef 100644 --- a/test/jest.transport-resolver.js +++ b/test/jest.transport-resolver.js @@ -3,11 +3,13 @@ const path = require('path'); module.exports = (request, options) => { const defaultResolve = options.defaultResolver; + // Check if the module to resolve is 'Transport' if (request === 'Transport') { try { + // Get the list of files in the specified directory const files = fs.readdirSync('node_modules/@firebolt-js/sdk/dist/lib/'); - const hasGateway = files.includes('Gateway'); - if (hasGateway) { + // If the 'Gateway' file exists, then the sdk version is 2.0.0 or later, so resolve to 'Gateway' path + if (files.includes('Gateway')) { console.log('Using Gateway from @firebolt-js/sdk'); return path.resolve(__dirname, '../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs'); } else { diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index 1460858e..fb3beedd 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -755,6 +755,7 @@ describe('EventInvocation', () => { test('should clear listener by sending listen false when communication mode is transport', async () => { const event = 'mocksdk_mockmodule.onmodulechanged'; process.env.COMMUNICATION_MODE = 'Transport'; + let eventInvocationImport, transportImport; if (Transport.request == null) { // v1 events jest.mock('../../node_modules/@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { @@ -766,16 +767,8 @@ describe('EventInvocation', () => { }); process.env.IS_BIDIRECTIONAL_SDK = false; // re-import the modules after mocking it - const eventInvocationImport = require('../../src/EventInvocation'); - const Transport = require('Transport'); - const eventInvocation = eventInvocationImport.EventInvocation; - const eventInvocationTest = new eventInvocation(); - const result = await eventInvocationTest.clearEventListeners(event); - console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); - expect(result).toBe(true); - expect(Transport.send).toHaveBeenCalledWith('mockmodule', 'onModulechanged', { - listen: false, - }); + eventInvocationImport = require('../../src/EventInvocation'); + transportImport = require('Transport'); } else { // v2 events jest.doMock('../../node_modules/@firebolt-js/sdk/dist/lib/Gateway/index.mjs', () => { @@ -792,14 +785,20 @@ describe('EventInvocation', () => { }); process.env.IS_BIDIRECTIONAL_SDK = true; // re-import the modules after mocking it - const eventInvocationImport = require('../../src/EventInvocation'); - const Transport = require('Transport'); - const eventInvocation = eventInvocationImport.EventInvocation; - const eventInvocationTest = new eventInvocation(); - const result = await eventInvocationTest.clearEventListeners(event); - console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); - expect(result).toBe(true); - expect(Transport.request).toHaveBeenCalledWith('mockmodule.onModulechanged', { + eventInvocationImport = require('../../src/EventInvocation'); + transportImport = require('Transport'); + } + const eventInvocation = eventInvocationImport.EventInvocation; + const eventInvocationTest = new eventInvocation(); + const result = await eventInvocationTest.clearEventListeners(event); + console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); + expect(result).toBe(true); + if (Transport.request == null) { + expect(transportImport.send).toHaveBeenCalledWith('mockmodule', 'onModulechanged', { + listen: false, + }); + } else { + expect(transportImport.request).toHaveBeenCalledWith('mockmodule.onModulechanged', { listen: false, }); } From 11a32dcbe6effc1f25afaf3cda6795c06f8f4683 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Mon, 9 Jun 2025 12:50:55 +0530 Subject: [PATCH 13/29] Changed function names and removed un-necessary maps --- src/EventInvocation.js | 67 +++++++++++++++++-------------- test/unit/EventInvocation.test.js | 22 ++++------ 2 files changed, 43 insertions(+), 46 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index e32aac17..a97a2ee8 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -31,8 +31,7 @@ import Transport from 'Transport'; const Validator = require('jsonschema').Validator; const validator = new Validator(); const eventHandlerMap = new Map(); -const eventHandlerMapV2 = new Map(); -const eventHistory = new Map(); +const eventHistoryMap = new Map(); const logger = require('./utils/Logger')('EventInvocation.js'); class EventHandler { @@ -90,9 +89,9 @@ class EventHandler { eventTime: new Date(), }; } - const eventMap = eventHistory.get(this.eventName); + const eventMap = eventHistoryMap.get(this.eventName); if (!eventMap) { - eventHistory.set(this.eventName, [eventDataObject]); + eventHistoryMap.set(this.eventName, [eventDataObject]); } else { eventMap.push(eventDataObject); } @@ -118,17 +117,14 @@ class EventHandler { } // Return queried number of events from history getEventHistory(numberOfEvents) { - return eventHistory.slice(-numberOfEvents); + return eventHistoryMap.slice(-numberOfEvents); } } class EventRegistrationInterface { async clearEventListeners(event) { try { - const [sdkType, module] = this.getSdkTypeAndModule(event); - let eventName = event.split('.')[1]; - eventName = eventName.slice(2); - eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1); + const [sdkType, module, _eventMethodWithoutModule, eventName] = this.parseEventNameAndModuleAndSDKType(event); if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { MODULE_MAP[sdkType][module].clear(eventName); } else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { @@ -152,8 +148,16 @@ class EventRegistrationInterface { } } - // Check and assign SDK type from incoming params - getSdkTypeAndModule(moduleWithEventName) { + /** + * parseEventNameAndModuleAndSDKType + * This method parses the module with event name to extract SDK type, module, event name without module, and formatted event name. + * @param {String} moduleWithEventName - The module with event name in the format 'sdkType_moduleName.onEventName'. + * @returns - array containing sdkType, module, eventNameWithoutModule, and formatted eventName. + * @example + * parseEventNameAndModuleAndSDKType('firebolt_foo.onExampleEvent') + * returns ['firebolt', 'foo', 'onExampleEvent', 'exampleEvent'] + */ + parseEventNameAndModuleAndSDKType(moduleWithEventName) { let sdkType; let module; if (!moduleWithEventName.includes('_')) { @@ -165,7 +169,10 @@ class EventRegistrationInterface { module = module.split('_')[1]; } sdkType = process.env.SDK_TYPE ? process.env.SDK_TYPE : sdkType; - return [sdkType, module]; + const eventMethodWithoutModule = moduleWithEventName.split('.')[1]; + let eventName = eventMethodWithoutModule.slice(2); + eventName = eventName.charAt(0).toLowerCase() + eventName.slice(1); + return [sdkType, module, eventMethodWithoutModule, eventName]; } // Return event history for the provided unique key @@ -183,13 +190,13 @@ class EventRegistrationInterface { clearAllListeners(eventHandlerMap) { logger.info('Clearing registered listeners' + JSON.stringify(Object.fromEntries(eventHandlerMap)), 'clearAllListeners'); try { - eventHistory.clear(); + eventHistoryMap.clear(); if (eventHandlerMap.size >= 1) { - eventHandlerMap.forEach(async (EventHandlerObject, uniqueListenerKey) => { + eventHandlerMap.forEach(async (EventHandlerObject, _uniqueListenerKey) => { // The key in the eventhHanldermap is in the format SDK_ModuleName- const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; const eventName = EventHandlerObject.event; - const [sdkType, module] = this.getSdkTypeAndModule(eventNameWithModuleName); + const [sdkType, module, eventMethodWithoutModule] = this.parseEventNameAndModuleAndSDKType(eventNameWithModuleName); logger.info('Unregistered event- ' + eventNameWithModuleName, 'clearAllListeners'); // Events are cleared using Firebolt SDK @@ -202,7 +209,7 @@ class EventRegistrationInterface { if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { await Transport.request(eventNameWithModuleName, args); } else { - await Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + await Transport.send(module, eventMethodWithoutModule, args); } } }); @@ -226,15 +233,13 @@ class EventRegistration extends EventRegistrationInterface { // This method will listen to event and capture the event response after triggering async registerEvent(moduleWithEventName, params) { const paramlist = []; - const [sdkType, module] = this.getSdkTypeAndModule(moduleWithEventName); + const [sdkType, module] = this.parseEventNameAndModuleAndSDKType(moduleWithEventName); const [schemaList, invokedSdk] = await dereferenceOpenRPC(sdkType); const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); const eventName = EventHandlerObject.getEventName(); let eventRegistrationID; for (const key in params) { - if (params.hasOwnProperty(key)) { - paramlist.push(params[key]); - } + paramlist.push(params[key]); } // To prevent uncaught exceptions when it received invalid eventName or module names. try { @@ -346,9 +351,9 @@ class EventRegistration extends EventRegistrationInterface { let filteredEventDataObjectList; const eventName = message.params.event; if (process.env.STANDALONE == true) { - filteredEventDataObjectList = eventHistory.get(eventName.split('-')[0]); + filteredEventDataObjectList = eventHistoryMap.get(eventName.split('-')[0]); } else { - filteredEventDataObjectList = eventHistory.get(eventName); + filteredEventDataObjectList = eventHistoryMap.get(eventName); } if (filteredEventDataObjectList && filteredEventDataObjectList.length) { const eventDataObject = filteredEventDataObjectList[filteredEventDataObjectList.length - 1]; @@ -372,7 +377,7 @@ class EventRegistration extends EventRegistrationInterface { class EventRegistrationV2 extends EventRegistrationInterface { // This method will listen to an event and capture the event response after triggering async registerEvent(moduleWithEventName, params) { - const [sdkType, module] = this.getSdkTypeAndModule(moduleWithEventName); + const [sdkType, module] = this.parseEventNameAndModuleAndSDKType(moduleWithEventName); const [schemaList] = await dereferenceOpenRPC(sdkType); const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); const eventName = EventHandlerObject.getEventName(); @@ -390,7 +395,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { } if (eventRegistrationID) { - eventHandlerMapV2.set(moduleWithEventName, EventHandlerObject); + eventHandlerMap.set(moduleWithEventName, EventHandlerObject); return eventRegistrationID; } } @@ -428,7 +433,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { getEventResponse(message) { try { const eventName = message.params.event; - const filteredEvents = eventHistory.get(eventName); + const filteredEvents = eventHistoryMap.get(eventName); return filteredEvents && filteredEvents.length ? filteredEvents[filteredEvents.length - 1] : { [eventName]: null }; } catch (err) { return { error: { code: 'FCAError', message: `Event response fetch error: ${err.message}` } }; @@ -444,12 +449,12 @@ class EventRegistrationV2 extends EventRegistrationInterface { listening: true, event: moduleWithEventName, }; - eventHandlerMapV2.get(moduleWithEventName).setEventListener(registrationResponse); + eventHandlerMap.get(moduleWithEventName).setEventListener(registrationResponse); } else if (response && response.hasOwnProperty('listening') && response.listening) { registrationResponse['jsonrpc'] = '2.0'; registrationResponse['id'] = null; registrationResponse['result'] = response; - eventHandlerMapV2.get(moduleWithEventName).setEventListener(registrationResponse); + eventHandlerMap.get(moduleWithEventName).setEventListener(registrationResponse); } else { registrationResponse['error'] = response; registrationResponse['id'] = null; @@ -459,7 +464,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { // This method will clear the eventListeners and the event hsitory for the listener as a part of FCA clearAllListeners() { - return super.clearAllListeners(eventHandlerMapV2); + return super.clearAllListeners(eventHandlerMap); } } @@ -505,11 +510,11 @@ export class EventInvocation { } // Check and assign SDK type from incoming params - getSdkTypeAndModule(moduleWithEventName) { + parseEventNameAndModuleAndSDKType(moduleWithEventName) { try { - return this.eventRegistration.getSdkTypeAndModule(moduleWithEventName); + return this.eventRegistration.parseEventNameAndModuleAndSDKType(moduleWithEventName); } catch (error) { - return this.handleError('getSdkTypeAndModule', error); + return this.handleError('parseEventNameAndModuleAndSDKType', error); } } diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index fb3beedd..0a6cce79 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -805,7 +805,7 @@ describe('EventInvocation', () => { }); }); - describe('getSdkTypeAndModule', () => { + describe('parseEventName', () => { let eventInvocation; beforeAll(async () => { jest.clearAllMocks(); @@ -813,27 +813,19 @@ describe('EventInvocation', () => { eventInvocation = new EventInvocation(); }); test('should return CORE as sdkType when only module.method is passed', () => { - const [sdkType, module] = eventInvocation.getSdkTypeAndModule('mockModule.mockMethod'); + const [sdkType, module, eventMethodWithoutModule, eventName] = eventInvocation.parseEventNameAndModuleAndSDKType('mockModule.onMockMethod'); expect(sdkType).toBe('core'); expect(module).toBe('mockmodule'); + expect(eventMethodWithoutModule).toBe('onMockMethod'); + expect(eventName).toBe('mockMethod'); }); test('should return provided sdk as sdkType when sdk_module.method is passed', () => { - const [sdkType, module] = eventInvocation.getSdkTypeAndModule('mocksdk_mockModule.mockMethod'); + const [sdkType, module, eventMethodWithoutModule, eventName] = eventInvocation.parseEventNameAndModuleAndSDKType('mocksdk_mockModule.onMockMethod'); expect(sdkType).toBe('mocksdk'); expect(module).toBe('mockmodule'); - }); - - test('will return core as sdktype and module as method if only method is passed', () => { - const [sdkType, module] = eventInvocation.getSdkTypeAndModule('mockMethod'); - expect(sdkType).toBe('core'); - expect(module).toBe('mockmethod'); - }); - - test('will return module as empty and core as sdktype if no input is provided', () => { - const [sdkType, module] = eventInvocation.getSdkTypeAndModule(''); - expect(sdkType).toBe('core'); - expect(module).toBe(''); + expect(eventMethodWithoutModule).toBe('onMockMethod'); + expect(eventName).toBe('mockMethod'); }); }); From 50552d45ebd4727eaa838fe2ca08689d0183497a Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Mon, 9 Jun 2025 13:59:22 +0530 Subject: [PATCH 14/29] Added awaits for async functions --- src/pubsub/handlers/ClearEventListeners.js | 2 +- src/pubsub/handlers/clearEventHandler.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pubsub/handlers/ClearEventListeners.js b/src/pubsub/handlers/ClearEventListeners.js index 10a19de0..fc959143 100644 --- a/src/pubsub/handlers/ClearEventListeners.js +++ b/src/pubsub/handlers/ClearEventListeners.js @@ -28,7 +28,7 @@ export default class ClearEventListeners extends BaseHandler { async handle(message) { const eventInvokerInfo = new EventInvocation(); - const validationReport = eventInvokerInfo.clearAllListeners(); + const validationReport = await eventInvokerInfo.clearAllListeners(); return JSON.stringify({ report: validationReport }); } } diff --git a/src/pubsub/handlers/clearEventHandler.js b/src/pubsub/handlers/clearEventHandler.js index 94082477..7d971fd3 100644 --- a/src/pubsub/handlers/clearEventHandler.js +++ b/src/pubsub/handlers/clearEventHandler.js @@ -38,7 +38,7 @@ export default class clearEventHandler extends BaseHandler { process.env.SDK_TYPE = sdkType; } try { - const validationReport = eventInvokerInfo.clearEventListeners(message.params.event); + const validationReport = await eventInvokerInfo.clearEventListeners(message.params.event); return JSON.stringify({ report: validationReport }); } catch (e) { const result = { From 3bc50f31d269b4c595ad98f1a45600a4e9c5f486 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Thu, 12 Jun 2025 14:05:30 +0530 Subject: [PATCH 15/29] improved condition check --- src/EventInvocation.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index a97a2ee8..a2480bb4 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -129,7 +129,7 @@ class EventRegistrationInterface { MODULE_MAP[sdkType][module].clear(eventName); } else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { const args = { listen: false }; - if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { + if (process.env.IS_BIDIRECTIONAL_SDK === true || (typeof process.env.IS_BIDIRECTIONAL_SDK === 'string' && process.env.IS_BIDIRECTIONAL_SDK.toLowerCase() === 'true')) { await Transport.request(`${module}.on${eventName[0].toUpperCase()}${eventName.substr(1)}`, args); } else { await Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); @@ -206,7 +206,7 @@ class EventRegistrationInterface { // Events are cleared by using Transport layer and thus bypassing SDK else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { const args = { listen: false }; - if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { + if (process.env.IS_BIDIRECTIONAL_SDK === true || (typeof process.env.IS_BIDIRECTIONAL_SDK === 'string' && process.env.IS_BIDIRECTIONAL_SDK.toLowerCase() === 'true')) { await Transport.request(eventNameWithModuleName, args); } else { await Transport.send(module, eventMethodWithoutModule, args); @@ -475,7 +475,7 @@ export class EventInvocation { // Initialize Event Registration based on SDK version initializeEventRegistration() { - if (process.env.IS_BIDIRECTIONAL_SDK === true || process.env.IS_BIDIRECTIONAL_SDK === 'true') { + if (process.env.IS_BIDIRECTIONAL_SDK === true || (typeof process.env.IS_BIDIRECTIONAL_SDK === 'string' && process.env.IS_BIDIRECTIONAL_SDK.toLowerCase() === 'true')) { return new EventRegistrationV2(); } else { return new EventRegistration(); From 10dcea2bb7685b58e6dbade256a6cd2564f8ac03 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Thu, 12 Jun 2025 18:25:10 +0530 Subject: [PATCH 16/29] Added null checks --- src/EventInvocation.js | 85 +++++++++++++++++++++++++------ test/unit/EventInvocation.test.js | 58 +++++++++++++++++++-- 2 files changed, 125 insertions(+), 18 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index a2480bb4..bd00230d 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -124,15 +124,20 @@ class EventHandler { class EventRegistrationInterface { async clearEventListeners(event) { try { - const [sdkType, module, _eventMethodWithoutModule, eventName] = this.parseEventNameAndModuleAndSDKType(event); + checkEventNameFormat(event); + const [sdkType, module, eventMethodWithoutModule, eventName] = this.parseEventNameAndModuleAndSDKType(event); if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - MODULE_MAP[sdkType][module].clear(eventName); + const resolvedModule = this.getModuleMap(sdkType, module); + if (!resolvedModule.clear || typeof resolvedModule.clear !== 'function') { + throw new Error(`Module- ${module} from sdk- ${module} does not support event de-registration.`); + } + resolvedModule.clear(eventName); } else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { const args = { listen: false }; if (process.env.IS_BIDIRECTIONAL_SDK === true || (typeof process.env.IS_BIDIRECTIONAL_SDK === 'string' && process.env.IS_BIDIRECTIONAL_SDK.toLowerCase() === 'true')) { - await Transport.request(`${module}.on${eventName[0].toUpperCase()}${eventName.substr(1)}`, args); + await Transport.request(`${module}.${eventMethodWithoutModule}`, args); } else { - await Transport.send(module, 'on' + eventName[0].toUpperCase() + eventName.substr(1), args); + await Transport.send(module, eventMethodWithoutModule, args); } } return true; @@ -154,7 +159,8 @@ class EventRegistrationInterface { * @param {String} moduleWithEventName - The module with event name in the format 'sdkType_moduleName.onEventName'. * @returns - array containing sdkType, module, eventNameWithoutModule, and formatted eventName. * @example - * parseEventNameAndModuleAndSDKType('firebolt_foo.onExampleEvent') + * parseEventNameAndModuleAndSDKType('sdk_foo.onExampleEvent') + * parseEventNameAndModuleAndSDKType('foo.onExampleEvent') * returns ['firebolt', 'foo', 'onExampleEvent', 'exampleEvent'] */ parseEventNameAndModuleAndSDKType(moduleWithEventName) { @@ -193,7 +199,7 @@ class EventRegistrationInterface { eventHistoryMap.clear(); if (eventHandlerMap.size >= 1) { eventHandlerMap.forEach(async (EventHandlerObject, _uniqueListenerKey) => { - // The key in the eventhHanldermap is in the format SDK_ModuleName- + // The key in the eventHandlermap is in the format SDK_ModuleName- const eventNameWithModuleName = EventHandlerObject.moduleWithEventName; const eventName = EventHandlerObject.event; const [sdkType, module, eventMethodWithoutModule] = this.parseEventNameAndModuleAndSDKType(eventNameWithModuleName); @@ -201,7 +207,11 @@ class EventRegistrationInterface { // Events are cleared using Firebolt SDK if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - MODULE_MAP[sdkType][module].clear(eventName); + const resolvedModule = this.getModuleMap(sdkType, module); + if (!resolvedModule.listen || typeof resolvedModule.listen !== 'function') { + throw new Error(`Module- ${module} from sdk- ${module} does not support event de-registration.`); + } + resolvedModule.clear(eventName); } // Events are cleared by using Transport layer and thus bypassing SDK else if (process.env.COMMUNICATION_MODE == CONSTANTS.TRANSPORT) { @@ -226,6 +236,22 @@ class EventRegistrationInterface { return response; } } + + /** + * getModuleMap + * This method parses the module with event name to extract SDK type, module, event name without module, and formatted event name. + * @param {String} sdkType - Sdk type, e.g., 'core', 'extenal', etc. + * @param {String} module - Type of module, e.g., 'account', 'discovery', etc. + * @example + * getModuleMap('core', 'discovery') + * getModuleMap('core', 'discovery') + */ + getModuleMap(sdkType, module) { + if (!MODULE_MAP[sdkType] || !MODULE_MAP[sdkType][module]) { + throw new Error(`Module ${module} from sdk ${sdkType} does not exist.`); + } + return MODULE_MAP[sdkType][module]; + } } // 1.0 Implementation @@ -263,7 +289,10 @@ class EventRegistration extends EventRegistrationInterface { Transport.addEventEmitter(emit); eventRegistrationID = id; } else if (process.env.COMMUNICATION_MODE == CONSTANTS.SDK) { - const resolvedModule = MODULE_MAP[sdkType][module]; + const resolvedModule = this.getModuleMap(sdkType, module); + if (!resolvedModule.listen || typeof resolvedModule.listen !== 'function') { + throw new Error(`Module ${module} from sdk ${module} does not support event listening.`); + } eventRegistrationID = await resolvedModule.listen(eventName, (result) => { if (!CONSTANTS.EXCLUDED_VALUES.includes(result)) { EventHandlerObject.handleEvent(result); @@ -281,10 +310,16 @@ class EventRegistration extends EventRegistrationInterface { const uniqueListenerKey = eventNameWithoutSDK + '-' + eventRegistrationID; eventHandlerMap.set(uniqueListenerKey, EventHandlerObject); return [eventRegistrationID, uniqueListenerKey]; + } else { + logger.error('No listener Id received from SDK', 'registerEvent'); + return ['No listener Id received from SDK', null]; } } eventListenerResponseHandler(moduleWithEventName, response) { + if (CONSTANTS.EXCLUDED_VALUES.includes(response)) { + response = [`No event listener response received for ${moduleWithEventName}`]; + } const [listenerResponse, uniqueListenerKey] = response; const registrationResponse = {}; if (process.env.STANDALONE == true) { @@ -350,6 +385,9 @@ class EventRegistration extends EventRegistrationInterface { try { let filteredEventDataObjectList; const eventName = message.params.event; + if (!eventName) { + throw new Error('Invalid parameters: event name is required'); + } if (process.env.STANDALONE == true) { filteredEventDataObjectList = eventHistoryMap.get(eventName.split('-')[0]); } else { @@ -397,6 +435,9 @@ class EventRegistrationV2 extends EventRegistrationInterface { if (eventRegistrationID) { eventHandlerMap.set(moduleWithEventName, EventHandlerObject); return eventRegistrationID; + } else { + logger.error('No listener Id received from SDK', 'registerEvent v2'); + return 'No listener Id received from SDK'; } } @@ -415,7 +456,10 @@ class EventRegistrationV2 extends EventRegistrationInterface { // Helper method to handle SDK events async handleSdkEvent(sdkType, module, eventName, moduleWithEventName, EventHandlerObject) { - const resolvedModule = MODULE_MAP[sdkType][module]; + const resolvedModule = this.getModuleMap(sdkType, module); + if (!resolvedModule.listen || typeof resolvedModule.listen !== 'function') { + throw new Error(`Module ${module} from sdk ${module} does not support event listening.`); + } return await resolvedModule.listen(eventName, (result) => { if (!CONSTANTS.EXCLUDED_VALUES.includes(result)) { EventHandlerObject.handleEvent(result); @@ -423,16 +467,13 @@ class EventRegistrationV2 extends EventRegistrationInterface { }); } - // Construct a unique listener key - constructUniqueListenerKey(moduleWithEventName, eventRegistrationID) { - const eventNameWithoutSDK = moduleWithEventName.includes('_') ? moduleWithEventName.split('_')[1] : moduleWithEventName; - return `${eventNameWithoutSDK}-${eventRegistrationID}`; - } - // Return the event response object for the eventName passed as the param getEventResponse(message) { try { const eventName = message.params.event; + if (!eventName) { + throw new Error('Invalid parameters: event name is required'); + } const filteredEvents = eventHistoryMap.get(eventName); return filteredEvents && filteredEvents.length ? filteredEvents[filteredEvents.length - 1] : { [eventName]: null }; } catch (err) { @@ -468,6 +509,16 @@ class EventRegistrationV2 extends EventRegistrationInterface { } } +function checkEventNameFormat(moduleWithEventName) { + if (!moduleWithEventName) { + throw new Error('Invalid parameters: event name is required'); + } + const methodNameRegex = /^(?!.*\.$)[^.].*?\.[^_]*_?[^.]*$/; + if (!methodNameRegex.test(moduleWithEventName)) { + throw new Error(`Invalid event name format: ${moduleWithEventName}, expected format is 'moduleName.onEventName' or sdkType_moduleName.onEventName'`); + } +} + export class EventInvocation { constructor() { this.eventRegistration = this.initializeEventRegistration(); @@ -484,7 +535,11 @@ export class EventInvocation { async northBoundEventHandling(message) { try { + if (!message || !message.params || !message.params.event) { + throw new Error('Invalid parameters: event name is required'); + } const { event: moduleWithEventName, params } = message.params; + checkEventNameFormat(moduleWithEventName); const response = await this.eventRegistration.registerEvent(moduleWithEventName, params); return this.eventRegistration.eventListenerResponseHandler(moduleWithEventName, response); } catch (error) { diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index 0a6cce79..c4b59e98 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -447,7 +447,6 @@ jest.mock('../../src/FireboltExampleInvoker', () => { callId++; const id = callId; // a mock ID value eventList.push(eventName + '-' + id); - console.log('eventList----:', eventList); return Promise.resolve(id); } }); @@ -565,6 +564,59 @@ describe('EventInvocation', () => { process.env.IS_BIDIRECTIONAL_SDK = actualVersion; }); let eventInvocation; + test('validate EventInvocation method and throw error for invalid event', async () => { + const eventParams = { params: { event: 'onmodulechanged' } }; + const error = { + error: { + code: 'FCAError', + message: "Error in northBoundEventHandling: Invalid event name format: onmodulechanged, expected format is 'moduleName.onEventName' or sdkType_moduleName.onEventName'", + }, + }; + const result = await eventInvocation.northBoundEventHandling(eventParams); + expect(result).toStrictEqual(error); + }); + test('validate EventInvocation method and throw error for no event name', async () => { + const eventParams = { params: { event: null } }; + const error = { + error: { + code: 'FCAError', + message: 'Error in northBoundEventHandling: Invalid parameters: event name is required', + }, + }; + const result = await eventInvocation.northBoundEventHandling(eventParams); + expect(result).toStrictEqual(error); + }); + test('validate EventInvocation method and throw error for params', async () => { + const eventParams = { params: null }; + const error = { + error: { + code: 'FCAError', + message: 'Error in northBoundEventHandling: Invalid parameters: event name is required', + }, + }; + const result = await eventInvocation.northBoundEventHandling(eventParams); + expect(result).toStrictEqual(error); + }); + test('validate EventInvocation method and throw error module_map method not found', async () => { + const eventParams = { params: { event: 'mocksdk.mockeventmodule1' } }; + const error = { + jsonrpc: '2.0', + id: null, + error: 'Module mocksdk from sdk core does not exist.', + }; + const result = await eventInvocation.northBoundEventHandling(eventParams); + expect(result.error.message).toBe(error.error); + }); + test('validate EventInvocation method and throw error module_map module not found', async () => { + const eventParams = { params: { event: 'mocksdk1.mockeventmodule' } }; + const error = { + jsonrpc: '2.0', + id: null, + error: 'Module mocksdk1 from sdk core does not exist.', + }; + const result = await eventInvocation.northBoundEventHandling(eventParams); + expect(result.error.message).toBe(error.error); + }); test('validate EventInvocation method with communicationMode SDK', async () => { const eventParams = { params: { event: 'mocksdk_mockmodule.onmodulechanged' } }; const expectedResponse = { @@ -794,11 +846,11 @@ describe('EventInvocation', () => { console.log(expect.getState().currentTestName + ' : ' + JSON.stringify(result)); expect(result).toBe(true); if (Transport.request == null) { - expect(transportImport.send).toHaveBeenCalledWith('mockmodule', 'onModulechanged', { + expect(transportImport.send).toHaveBeenCalledWith('mockmodule', 'onmodulechanged', { listen: false, }); } else { - expect(transportImport.request).toHaveBeenCalledWith('mockmodule.onModulechanged', { + expect(transportImport.request).toHaveBeenCalledWith('mockmodule.onmodulechanged', { listen: false, }); } From fa9952e9c63abb80f1e30ea965e0fa6a8a9bd2e9 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 24 Jun 2025 11:31:45 +0530 Subject: [PATCH 17/29] Fetch sdk version from package.json --- src/App.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 307cb133..4f0d09d1 100644 --- a/src/App.js +++ b/src/App.js @@ -39,6 +39,7 @@ import { withAnnouncer } from '@lightningjs/ui-components'; const Base = withAnnouncer(lng.Application); import Toast, { eventEmitter } from './Toast'; import IntentReader from 'IntentReader'; +const packagejson = require('../package.json'); export default class App extends Base { static _template() { @@ -92,7 +93,8 @@ export default class App extends Base { this.toastStates = []; // Setting the firebolt version by fetching it from open-rpc // Added the current firebolt version as default value, until the implementation of fetching the version is done - process.env.FCA_FIREBOLT_SDK_VERSION = '1.5.0'; + process.env.FCA_FIREBOLT_SDK_VERSION = packagejson.dependencies['@firebolt-js/sdk']; + console.log('process.env.FCA_FIREBOLT_SDK_VERSION-----:', process.env.FCA_FIREBOLT_SDK_VERSION); const pattern = /(2|\d{2,})\.\d+\.\d+/; process.env.IS_BIDIRECTIONAL_SDK = pattern.test(process.env.FCA_FIREBOLT_SDK_VERSION); this.overlayed = false; From dc26a193453d98ec49ed42454284cc3892d89920 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 24 Jun 2025 11:35:05 +0530 Subject: [PATCH 18/29] Removed unnecesary comments --- src/App.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/App.js b/src/App.js index 4f0d09d1..04cfe382 100644 --- a/src/App.js +++ b/src/App.js @@ -91,8 +91,6 @@ export default class App extends Base { this.accessibilityCheck(voiceAnnouncement); }); this.toastStates = []; - // Setting the firebolt version by fetching it from open-rpc - // Added the current firebolt version as default value, until the implementation of fetching the version is done process.env.FCA_FIREBOLT_SDK_VERSION = packagejson.dependencies['@firebolt-js/sdk']; console.log('process.env.FCA_FIREBOLT_SDK_VERSION-----:', process.env.FCA_FIREBOLT_SDK_VERSION); const pattern = /(2|\d{2,})\.\d+\.\d+/; From 23e97f8fe85eae9410f22b89a44bf5585af715b3 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 24 Jun 2025 11:52:58 +0530 Subject: [PATCH 19/29] store sdkversion in variable instead of env --- src/App.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 04cfe382..ce576bf3 100644 --- a/src/App.js +++ b/src/App.js @@ -91,10 +91,9 @@ export default class App extends Base { this.accessibilityCheck(voiceAnnouncement); }); this.toastStates = []; - process.env.FCA_FIREBOLT_SDK_VERSION = packagejson.dependencies['@firebolt-js/sdk']; - console.log('process.env.FCA_FIREBOLT_SDK_VERSION-----:', process.env.FCA_FIREBOLT_SDK_VERSION); + const sdkVersionFromPackageJson = packagejson.dependencies['@firebolt-js/sdk']; const pattern = /(2|\d{2,})\.\d+\.\d+/; - process.env.IS_BIDIRECTIONAL_SDK = pattern.test(process.env.FCA_FIREBOLT_SDK_VERSION); + process.env.IS_BIDIRECTIONAL_SDK = pattern.test(sdkVersionFromPackageJson); this.overlayed = false; this.overlayDismissTimer = null; const appUrl = window.location; From ca774d9a00072600b99d3d9e845b7745716a6295 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Wed, 24 Sep 2025 14:51:15 +0530 Subject: [PATCH 20/29] Update sdk version to 1.8.0 next --- package.json | 6 +++--- src/App.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8278db06..b5464896 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "Reference App to demonstrate Firebolt APIs and Lifecycle", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.9", - "@firebolt-js/discovery-sdk": "1.5.0", - "@firebolt-js/manage-sdk": "1.5.0", - "@firebolt-js/sdk": "1.5.0", + "@firebolt-js/discovery-sdk": "1.8.0-next-major.2", + "@firebolt-js/manage-sdk": "1.8.0-next-major.2", + "@firebolt-js/sdk": "1.8.0-next-major.2", "@lightningjs/core": "2.11.0", "@lightningjs/sdk": "^5.0.1", "@lightningjs/ui-components": "^2.2.2", diff --git a/src/App.js b/src/App.js index ce576bf3..b615323d 100644 --- a/src/App.js +++ b/src/App.js @@ -92,7 +92,7 @@ export default class App extends Base { }); this.toastStates = []; const sdkVersionFromPackageJson = packagejson.dependencies['@firebolt-js/sdk']; - const pattern = /(2|\d{2,})\.\d+\.\d+/; + const pattern = /^([2-9]|\d{2,})\.\d+\.\d+$|^1\.(8|9|\d{2,})\.\d+$/; process.env.IS_BIDIRECTIONAL_SDK = pattern.test(sdkVersionFromPackageJson); this.overlayed = false; this.overlayDismissTimer = null; From 0fd247179fed0d777d1fd47e19cb0ef4124bc869 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Wed, 24 Sep 2025 15:43:26 +0530 Subject: [PATCH 21/29] Update regex pattern --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index b615323d..b9b177ca 100644 --- a/src/App.js +++ b/src/App.js @@ -92,7 +92,7 @@ export default class App extends Base { }); this.toastStates = []; const sdkVersionFromPackageJson = packagejson.dependencies['@firebolt-js/sdk']; - const pattern = /^([2-9]|\d{2,})\.\d+\.\d+$|^1\.(8|9|\d{2,})\.\d+$/; + const pattern = /^([2-9]|\d{2,})\.\d+\.\d+$|^1\.(8|9|\d{2,})\.\d+/; process.env.IS_BIDIRECTIONAL_SDK = pattern.test(sdkVersionFromPackageJson); this.overlayed = false; this.overlayDismissTimer = null; From 588d303b9e5e77c2cfe40d25f3498db295c09b66 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Tue, 30 Sep 2025 11:41:37 +0530 Subject: [PATCH 22/29] Fix unit testcases failures --- test/unit/EventInvocation.test.js | 10 ++++++++++ test/unit/IntentReader.test.js | 10 ++++++++++ test/unit/LifeCycleHistory.test.js | 10 ++++++++++ test/unit/MethodFilters.test.js | 10 ++++++++++ test/unit/MethodInvoker.test.js | 10 ++++++++++ test/unit/RunTestHandler.test.js | 10 ++++++++++ test/unit/Utils.test.js | 11 +++++++++++ test/unit/lifecycleRecordHandler.test.js | 10 ++++++++++ test/unit/test_runner.test.js | 10 ++++++++++ 9 files changed, 91 insertions(+) diff --git a/test/unit/EventInvocation.test.js b/test/unit/EventInvocation.test.js index c4b59e98..f1eee5c5 100644 --- a/test/unit/EventInvocation.test.js +++ b/test/unit/EventInvocation.test.js @@ -409,6 +409,16 @@ const mockFireboltExampleInvoker = { invoke: () => {}, }; +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + jest.mock('../../src/utils/Utils', () => { const originalUtils = jest.requireActual('../../src/utils/Utils'); return { diff --git a/test/unit/IntentReader.test.js b/test/unit/IntentReader.test.js index 1a06e1ff..6fd928fa 100644 --- a/test/unit/IntentReader.test.js +++ b/test/unit/IntentReader.test.js @@ -102,6 +102,16 @@ jest.mock('../../src/FireboltTransportInvoker', () => { }; }); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + jest.mock('../../src/Toast', () => { const eventEmitter = { emit: jest.fn(), diff --git a/test/unit/LifeCycleHistory.test.js b/test/unit/LifeCycleHistory.test.js index e6fe4d05..b6b47fad 100644 --- a/test/unit/LifeCycleHistory.test.js +++ b/test/unit/LifeCycleHistory.test.js @@ -48,6 +48,16 @@ jest.mock('../../src/pubsub/handlers/RegisterProviderHandler', () => { })); }); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + const mockCallBackRes = { state: 'foreground', source: '' }; jest.mock('@firebolt-js/sdk', () => { return { diff --git a/test/unit/MethodFilters.test.js b/test/unit/MethodFilters.test.js index ba53f649..26f127cf 100644 --- a/test/unit/MethodFilters.test.js +++ b/test/unit/MethodFilters.test.js @@ -35,6 +35,16 @@ jest.mock('@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { }; }); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + jest.mock('@firebolt-js/sdk', () => { return { Accessibility: {}, diff --git a/test/unit/MethodInvoker.test.js b/test/unit/MethodInvoker.test.js index 440ba14b..d8ba6487 100644 --- a/test/unit/MethodInvoker.test.js +++ b/test/unit/MethodInvoker.test.js @@ -227,6 +227,16 @@ jest.mock('../../src/FireboltExampleInvoker', () => { }; }); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + jest.mock('../../src/utils/Utils', () => { const originalUtils = jest.requireActual('../../src/utils/Utils'); return { diff --git a/test/unit/RunTestHandler.test.js b/test/unit/RunTestHandler.test.js index 9fa38a66..31d409e5 100644 --- a/test/unit/RunTestHandler.test.js +++ b/test/unit/RunTestHandler.test.js @@ -52,6 +52,16 @@ jest.mock('../../src/utils/Utils', () => { }; }); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + const methodsToBeExcluded = 'Mock.Method,module.mockMethod,event.onMockEvent'; describe('RunTestHandler', () => { diff --git a/test/unit/Utils.test.js b/test/unit/Utils.test.js index b8ae0337..0e2272c1 100644 --- a/test/unit/Utils.test.js +++ b/test/unit/Utils.test.js @@ -37,6 +37,17 @@ jest.mock('@firebolt-js/sdk/dist/lib/Transport/index.mjs', () => { }, }; }); + +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + describe('Utils test cases', () => { test('validate dereferenceOpenRPC', async () => { await dereferenceOpenRPC('core'); diff --git a/test/unit/lifecycleRecordHandler.test.js b/test/unit/lifecycleRecordHandler.test.js index 90df2a87..570edc34 100644 --- a/test/unit/lifecycleRecordHandler.test.js +++ b/test/unit/lifecycleRecordHandler.test.js @@ -31,6 +31,16 @@ jest.mock('../../src/LifeCycleHistory', () => { }; }); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + let LifecycleRecordHandler; describe('LifecycleRecordHandler Test Case', () => { diff --git a/test/unit/test_runner.test.js b/test/unit/test_runner.test.js index 353d37a0..d09a328d 100644 --- a/test/unit/test_runner.test.js +++ b/test/unit/test_runner.test.js @@ -513,6 +513,16 @@ jest.mock('../../src/FireboltTransportInvoker', () => ({ get: () => mockFireboltTransportInvoker, })); +// Mock the logger module +jest.mock('../../src/utils/Logger', () => { + const loggerMock = { + error: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }; + return jest.fn(() => loggerMock); +}); + jest.mock('@firebolt-js/sdk', () => ({ Accessibility: {}, Account: {}, From 6e2a9ac0e6647d31f234ffaa20648f2285a037b4 Mon Sep 17 00:00:00 2001 From: Kummithi Guru Eswar Sainath Reddy Date: Mon, 17 Nov 2025 18:44:55 +0530 Subject: [PATCH 23/29] console log of IS_BIDIRECTIONAL_SDK env --- src/App.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/App.js b/src/App.js index b9b177ca..cdb89940 100644 --- a/src/App.js +++ b/src/App.js @@ -94,6 +94,7 @@ export default class App extends Base { const sdkVersionFromPackageJson = packagejson.dependencies['@firebolt-js/sdk']; const pattern = /^([2-9]|\d{2,})\.\d+\.\d+$|^1\.(8|9|\d{2,})\.\d+/; process.env.IS_BIDIRECTIONAL_SDK = pattern.test(sdkVersionFromPackageJson); + console.log('process.env.IS_BIDIRECTIONAL_SDK----:', process.env.IS_BIDIRECTIONAL_SDK); this.overlayed = false; this.overlayDismissTimer = null; const appUrl = window.location; From a0e9f369c4b1ede86f3d6c8cb4820714440d522b Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Tue, 25 Nov 2025 18:02:58 +0530 Subject: [PATCH 24/29] Updated sdk version --- package.json | 6 +++--- src/EventInvocation.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8fa564cc..9417e2c4 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "Reference App to demonstrate Firebolt APIs and Lifecycle", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.9", - "@firebolt-js/discovery-sdk": "1.8.0-next-major.2", - "@firebolt-js/manage-sdk": "1.8.0-next-major.2", - "@firebolt-js/sdk": "1.8.0-next-major.2", + "@firebolt-js/discovery-sdk": "1.8.0-next-major.3", + "@firebolt-js/manage-sdk": "1.8.0-next-major.3", + "@firebolt-js/sdk": "1.8.0-next-major.3", "@lightningjs/core": "2.11.0", "@lightningjs/sdk": "^5.0.1", "@lightningjs/ui-components": "^2.2.2", diff --git a/src/EventInvocation.js b/src/EventInvocation.js index bd00230d..19253e81 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -274,7 +274,7 @@ class EventRegistration extends EventRegistrationInterface { moduleWithEventName = moduleWithEventName.split('_')[1]; } - const { id, promise } = await this.registerEventInTransport(moduleWithEventName); + const { id, promise } = await this.registerEventInTransport(moduleWithEventName, params); const result = await promise; if (result && result.message) { throw result.message; From ced7f9257a6e63cfb1ce20b6ad500f2283fc6b38 Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Mon, 1 Dec 2025 19:07:43 +0530 Subject: [PATCH 25/29] Added changes in event registration --- src/FireboltExampleInvoker.js | 6 +++++- src/LifeCycleHistory.js | 36 +++++++++++++++++++++++++++++------ src/MediaView.js | 16 ++++++++++++++-- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/FireboltExampleInvoker.js b/src/FireboltExampleInvoker.js index 35967101..61af0da3 100644 --- a/src/FireboltExampleInvoker.js +++ b/src/FireboltExampleInvoker.js @@ -109,8 +109,12 @@ export default class FireboltExampleInvoker { return await methodFn(...params); } else if (method.match(/^on[A-Z][a-zA-Z]+$/) && moduleClass.listen) { let id; + let event = method[2].toLowerCase() + method.substr(3); console.log('params:', params); - const event = method[2].toLowerCase() + method.substr(3); + if (process.env.IS_BIDIRECTIONAL_SDK === true || (typeof process.env.IS_BIDIRECTIONAL_SDK === 'string' && process.env.IS_BIDIRECTIONAL_SDK.toLowerCase() === 'true')) { + event = method; + } + if (params.length == 1 && params[0] === true) { id = await moduleClass.listen(event, (e) => { logger.error(e, 'invoke'); diff --git a/src/LifeCycleHistory.js b/src/LifeCycleHistory.js index 4cd04996..71ae15fc 100644 --- a/src/LifeCycleHistory.js +++ b/src/LifeCycleHistory.js @@ -48,11 +48,35 @@ export default class LifecycleHistory { async init(appInstance = null) { lifecycleValidation = process.env.LIFECYCLE_VALIDATION; const lifecycleModule = await assignModuleCapitalization('Lifecycle'); - await Lifecycle.listen('inactive', this._recordHistory.bind(this, lifecycleModule + '.onInactive')); - await Lifecycle.listen('foreground', this._recordHistory.bind(this, lifecycleModule + '.onForeground')); - Lifecycle.listen('background', this._recordHistory.bind(this, lifecycleModule + '.onBackground')); - Lifecycle.listen('suspended', this._recordHistory.bind(this, lifecycleModule + '.onSuspended')); - Lifecycle.listen('unloading', async (event) => { + const isBidirectionalSdk = String(process.env.IS_BIDIRECTIONAL_SDK).toLowerCase() === 'true'; + const eventNames = isBidirectionalSdk + ? { + inactive: 'onInactive', + foreground: 'onForeground', + background: 'onBackground', + suspended: 'onSuspended', + unloading: 'onUnloading', + navigateTo: 'onNavigateTo', + } + : { + inactive: 'inactive', + foreground: 'foreground', + background: 'background', + suspended: 'suspended', + unloading: 'unloading', + navigateTo: 'navigateTo', + }; + const inactiveEvent = eventNames.inactive; + const foregroundEvent = eventNames.foreground; + const backgroundEvent = eventNames.background; + const suspendedEvent = eventNames.suspended; + const unloadingEvent = eventNames.unloading; + const navigateToEvent = eventNames.navigateTo; + await Lifecycle.listen(inactiveEvent, this._recordHistory.bind(this, lifecycleModule + '.onInactive')); + await Lifecycle.listen(foregroundEvent, this._recordHistory.bind(this, lifecycleModule + '.onForeground')); + Lifecycle.listen(backgroundEvent, this._recordHistory.bind(this, lifecycleModule + '.onBackground')); + Lifecycle.listen(suspendedEvent, this._recordHistory.bind(this, lifecycleModule + '.onSuspended')); + Lifecycle.listen(unloadingEvent, async (event) => { let schemaResult, validationResult; await getschemaValidationDone(lifecycleModule + '.onUnloading', event, 'core').then((res) => { schemaResult = res; @@ -100,7 +124,7 @@ export default class LifecycleHistory { } } // register for Discovery.onNavigateTo event - Discovery.listen('navigateTo', async (event) => { + Discovery.listen(navigateToEvent, async (event) => { logger.info('Printing onNavigate To event received: ' + JSON.stringify(event)); try { diff --git a/src/MediaView.js b/src/MediaView.js index d5e74e5a..81d6e0d1 100644 --- a/src/MediaView.js +++ b/src/MediaView.js @@ -68,7 +68,19 @@ export default class MediaView extends lng.Component { _init() { const p = this.tag('Player'); - Lifecycle.listen('inactive', (event) => { + const isBidirectionalSdk = String(process.env.IS_BIDIRECTIONAL_SDK).toLowerCase() === 'true'; + const eventNames = isBidirectionalSdk + ? { + inactive: 'onInactive', + foreground: 'onForeground', + } + : { + inactive: 'inactive', + foreground: 'foreground', + }; + const inactiveEvent = eventNames.inactive; + const foregroundEvent = eventNames.foreground; + Lifecycle.listen(inactiveEvent, (event) => { if (event.state && this.tag('Row.ToggleInactive').checked) { if (p.isPlaying()) { logger.info('Unpausing video', '_init'); @@ -76,7 +88,7 @@ export default class MediaView extends lng.Component { } } }); - Lifecycle.listen('foreground', (event) => { + Lifecycle.listen(foregroundEvent, (event) => { if (event.state && this.tag('Row.ToggleInactive').checked) { const p = this.tag('Player'); if (!p.isPlaying()) { From 212588be5921b47c18432ee9ec812d7cfe4d2e66 Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Mon, 1 Dec 2025 19:08:43 +0530 Subject: [PATCH 26/29] Added minor changes --- src/EventInvocation.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index 19253e81..e1aa3b1f 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -35,11 +35,11 @@ const eventHistoryMap = new Map(); const logger = require('./utils/Logger')('EventInvocation.js'); class EventHandler { - constructor(moduleWithEventName, schemaList) { + constructor(moduleWithEventName, schemaList, skipParsing = false) { this.moduleWithEventName = moduleWithEventName; const event = moduleWithEventName.split('.')[1]; this.eventName = moduleWithEventName; - this.event = this.parseEventName(event); + this.event = skipParsing ? event : this.parseEventName(event); if (process.env.STANDALONE == true) { this.eventSchema = this.getSchema(moduleWithEventName, schemaList); } @@ -417,7 +417,7 @@ class EventRegistrationV2 extends EventRegistrationInterface { async registerEvent(moduleWithEventName, params) { const [sdkType, module] = this.parseEventNameAndModuleAndSDKType(moduleWithEventName); const [schemaList] = await dereferenceOpenRPC(sdkType); - const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList); + const EventHandlerObject = new EventHandler(moduleWithEventName, schemaList, true); const eventName = EventHandlerObject.getEventName(); let eventRegistrationID; From 5c84ae2ceee79c9a515811e1d6ccbf46c9cbc863 Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Mon, 1 Dec 2025 19:35:31 +0530 Subject: [PATCH 27/29] Added minor change --- src/EventInvocation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EventInvocation.js b/src/EventInvocation.js index e1aa3b1f..e2246917 100644 --- a/src/EventInvocation.js +++ b/src/EventInvocation.js @@ -564,7 +564,7 @@ export class EventInvocation { } } - // Check and assign SDK type from incoming params + // Check and assign SDK type from incoming param parseEventNameAndModuleAndSDKType(moduleWithEventName) { try { return this.eventRegistration.parseEventNameAndModuleAndSDKType(moduleWithEventName); From 889a03c98ac5db342d0673565f8116e30950497c Mon Sep 17 00:00:00 2001 From: Abhishek urs C J Date: Mon, 1 Dec 2025 19:37:21 +0530 Subject: [PATCH 28/29] fixed lint error --- src/Test_Runner.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Test_Runner.js b/src/Test_Runner.js index ecc1cedb..47f52406 100644 --- a/src/Test_Runner.js +++ b/src/Test_Runner.js @@ -771,7 +771,7 @@ export class Test_Runner { createUUID() { let dt = new Date().getTime(); const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { - const r = (dt + Math.random() * 16) % 16 | 0; + const r = ((dt + Math.random() * 16) % 16) | 0; dt = Math.floor(dt / 16); return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16); }); From b7ba8b2b8b597b83553375c05cfa5ce5a104ae9e Mon Sep 17 00:00:00 2001 From: Abhishek urs C J <43801187+Abhishk123@users.noreply.github.com> Date: Mon, 12 Jan 2026 11:14:12 +0530 Subject: [PATCH 29/29] Update package.json --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9417e2c4..ad8d0d4e 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "description": "Reference App to demonstrate Firebolt APIs and Lifecycle", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.0.9", - "@firebolt-js/discovery-sdk": "1.8.0-next-major.3", - "@firebolt-js/manage-sdk": "1.8.0-next-major.3", - "@firebolt-js/sdk": "1.8.0-next-major.3", + "@firebolt-js/discovery-sdk": "1.8.0-next-major.4", + "@firebolt-js/manage-sdk": "1.8.0-next-major.4", + "@firebolt-js/sdk": "1.8.0-next-major.4", "@lightningjs/core": "2.11.0", "@lightningjs/sdk": "^5.0.1", "@lightningjs/ui-components": "^2.2.2",