From 09218f7a1ca497ce00286be18c7b3d7bd3e1c496 Mon Sep 17 00:00:00 2001 From: Spencer Lepine <60903378+spencerlepine@users.noreply.github.com> Date: Tue, 5 Mar 2024 09:59:45 -0800 Subject: [PATCH] fix: enable message receipts by default (#194) https://github.com/amazon-connect/amazon-connect-chatjs/issues/132 --- src/constants.js | 2 + src/core/chatController.spec.js | 8 ++-- src/core/chatSession.js | 14 ++++--- src/globalConfig.js | 16 ++++++- src/globalConfig.spec.js | 74 ++++++++++++++++++--------------- 5 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/constants.js b/src/constants.js index a5e796b..ccbd394 100644 --- a/src/constants.js +++ b/src/constants.js @@ -6,6 +6,8 @@ export const CHAT_CONFIGURATIONS = { export const PARTICIPANT_TOKEN_HEADER = "x-amzn-connect-participant-token"; export const AUTH_HEADER = "X-Amz-Bearer"; +export const DEFAULT_MESSAGE_RECEIPTS_THROTTLE_MS = 5000; + export const FEATURES = { MESSAGE_RECEIPTS_ENABLED: "MESSAGE_RECEIPTS_ENABLED" }; diff --git a/src/core/chatController.spec.js b/src/core/chatController.spec.js index e29021d..c16482d 100644 --- a/src/core/chatController.spec.js +++ b/src/core/chatController.spec.js @@ -42,10 +42,10 @@ describe("ChatController", () => { let endResponse; function getChatController(shouldSendMessageReceipts = true) { - GlobalConfig.update({ - features: shouldSendMessageReceipts ? [FEATURES.MESSAGE_RECEIPTS_ENABLED] : [], - throttleTime: 1000 - }); + if (!shouldSendMessageReceipts) { + GlobalConfig.removeFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); + } + GlobalConfig.updateThrottleTime(1000); return new ChatController({ sessionType: SESSION_TYPES.AGENT, diff --git a/src/core/chatSession.js b/src/core/chatSession.js index 95b59c9..25ba743 100644 --- a/src/core/chatSession.js +++ b/src/core/chatSession.js @@ -196,11 +196,15 @@ var setGlobalConfig = config => { if (csmConfig) { csmService.updateCsmConfig(csmConfig); } - //Message Receipts enabled by default - if (!(config.features?.messageReceipts?.shouldSendMessageReceipts === false)) { - logger.warn("enabling message-receipts by default; to disable set config.features.messageReceipts.shouldSendMessageReceipts = false"); - setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); - GlobalConfig.updateThrottleTime(config.features?.messageReceipts?.throttleTime); + /** + * Handle setting message receipts feature in Global Config. If no values are given will default to: + * - Message receipts enabled + * - Throttle = 5000 ms + */ + logger.warn("enabling message-receipts by default; to disable set config.features.messageReceipts.shouldSendMessageReceipts = false"); + GlobalConfig.updateThrottleTime(config.features?.messageReceipts?.throttleTime); + if (config.features?.messageReceipts?.shouldSendMessageReceipts === false) { + GlobalConfig.removeFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); } }; diff --git a/src/globalConfig.js b/src/globalConfig.js index 9e0d73e..960d0ce 100644 --- a/src/globalConfig.js +++ b/src/globalConfig.js @@ -1,3 +1,4 @@ +import { FEATURES, DEFAULT_MESSAGE_RECEIPTS_THROTTLE_MS } from "./constants"; import { LogManager } from "./log"; class GlobalConfigImpl { @@ -32,6 +33,8 @@ class GlobalConfigImpl { return true; } }); + this.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); // message receipts enabled by default + this.messageReceiptThrottleTime = DEFAULT_MESSAGE_RECEIPTS_THROTTLE_MS; this.featureChangeListeners = []; } update(configInput) { @@ -42,7 +45,8 @@ class GlobalConfigImpl { this.endpointOverride = config.endpoint || this.endpointOverride; this.reconnect = config.reconnect === false ? false : this.reconnect; this.messageReceiptThrottleTime = config.throttleTime ? config.throttleTime : 5000; - this.features["values"] = Array.isArray(config.features) ? [...config.features] : new Array(); + const features = config.features || this.features.values; + this.features["values"] = Array.isArray(features) ? [...features] : new Array(); } updateStageRegionCell(config) { @@ -58,7 +62,7 @@ class GlobalConfigImpl { } updateThrottleTime(throttleTime) { - this.messageReceiptThrottleTime = throttleTime ? throttleTime : this.messageReceiptThrottleTime; + this.messageReceiptThrottleTime = throttleTime || this.messageReceiptThrottleTime; } getMessageReceiptsThrottleTime() { @@ -77,6 +81,14 @@ class GlobalConfigImpl { return this.endpointOverride; } + removeFeatureFlag(feature) { + if (!this.isFeatureEnabled(feature)) { + return; + } + const index = this.features["values"].indexOf(feature); + this.features["values"].splice(index, 1); + } + setFeatureFlag(feature) { if(this.isFeatureEnabled(feature)) { return; diff --git a/src/globalConfig.spec.js b/src/globalConfig.spec.js index 5dc0d01..0a5ef2d 100644 --- a/src/globalConfig.spec.js +++ b/src/globalConfig.spec.js @@ -59,7 +59,7 @@ describe("globalConfig", () => { expect(GlobalConfig.getRegion()).toEqual(configInput.region); expect(GlobalConfig.getCell()).toEqual(configInput.cell); expect(GlobalConfig.getEndpointOverride()).toEqual(configInput.endpoint); - expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED)).toEqual(false); + expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED)).toEqual(true); }); it("should update stage, region and cell and fetch correct config", () => { GlobalConfig.updateStageRegionCell(stageRegionCell); @@ -136,14 +136,14 @@ describe("globalConfig", () => { setConfig(LogLevel.ERROR); var logger = LogManager.getLogger({ prefix: "prefix " }); logger.error("error", 3); - expect(messages[0]).toEqual(["ERROR [2022-04-12T23:12:36.677Z] prefix : error 3 "]); + expect(messages[1]).toEqual(["ERROR [2022-04-12T23:12:36.677Z] prefix : error 3 "]); }); it("should match log format in advanced_log level", () => { console.error = mockFn; setConfig(LogLevel.ADVANCED_LOG); var logger = LogManager.getLogger({ prefix: "prefix " }); logger.advancedLog("info", 3); - expect(messages[0]).toEqual(["ADVANCED_LOG [2022-04-12T23:12:36.677Z] prefix : info 3 "]); + expect(messages[1]).toEqual(["ADVANCED_LOG [2022-04-12T23:12:36.677Z] prefix : info 3 "]); }); test("set region", () => { ChatSessionObject.setGlobalConfig({ @@ -156,21 +156,21 @@ describe("globalConfig", () => { setConfig(LogLevel.INFO); var logger = LogManager.getLogger({ prefix: "prefix ", logMetaData }); logger.info("info", 3); - expect(messages[1]).toEqual(["INFO [2022-04-12T23:12:36.677Z] prefix : info 3 {\"contactId\":\"abc\"}"]); + expect(messages[2]).toEqual(["INFO [2022-04-12T23:12:36.677Z] prefix : info 3 {\"contactId\":\"abc\"}"]); }); it("should match log format when there is no prefix and logMetaData", () => { console.info = mockFn; setConfig(LogLevel.INFO); var logger = LogManager.getLogger({ logMetaData: {contactId: "abc"}}); logger.info("info", 3); - expect(messages[1]).toEqual(["INFO [2022-04-12T23:12:36.677Z] info 3 {\"contactId\":\"abc\"}"]); + expect(messages[2]).toEqual(["INFO [2022-04-12T23:12:36.677Z] info 3 {\"contactId\":\"abc\"}"]); }); it("should match log format when there is no prefix, but logMetaData is included", () => { console.info = mockFn; setConfig(LogLevel.INFO); var logger = LogManager.getLogger({ logMetaData }); logger.info("info", 3); - expect(messages[1]).toEqual(["INFO [2022-04-12T23:12:36.677Z] info 3 {\"contactId\":\"abc\"}"]); + expect(messages[2]).toEqual(["INFO [2022-04-12T23:12:36.677Z] info 3 {\"contactId\":\"abc\"}"]); }); }); @@ -196,7 +196,7 @@ describe("globalConfig", () => { logger.error("error", 4); logger.error("error", 5); - expect(testLogger.warn.mock.calls[0][0]).toEqual("WARN [\"warn\",3] "); + expect(testLogger.warn.mock.calls[1][0]).toEqual("WARN [\"warn\",3] "); expect(testLogger.error.mock.calls[0][0]).toEqual("ERROR [\"error\",4] "); expect(testLogger.error.mock.calls[1][0]).toEqual("ERROR [\"error\",5] "); }); @@ -214,7 +214,7 @@ describe("globalConfig", () => { logger.error("error", 4); logger.error("error", 5); - expect(testLogger.warn.mock.calls[0][0]).toEqual("WARN [\"warn\",3] "); + expect(testLogger.warn.mock.calls[1][0]).toEqual("WARN [\"warn\",3] "); expect(testLogger.error.mock.calls[0][0]).toEqual("ERROR [\"error\",4] "); expect(testLogger.error.mock.calls[1][0]).toEqual("ERROR [\"error\",5] "); }); @@ -292,55 +292,61 @@ describe("globalConfig", () => { }); describe("feature flag test", () => { - it("should update feature Flag", () => { + it("Should have message receipts feature enabled by default", () => { GlobalConfig.update({ features: [FEATURES.MESSAGE_RECEIPTS_ENABLED] }); expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED)).toEqual(true); }); - it("should update feature Flag and call the registered listeners only once", () => { - GlobalConfig.update({ - features: [] - }); - const handler = jest.fn(); - expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler)).toEqual(false); - GlobalConfig.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); - expect(handler).toHaveBeenCalled(); - GlobalConfig.update({ + + it("Should update feature flags according to setGlobalConfig input", () => { + ChatSessionObject.setGlobalConfig({ features: { messageReceipts: { - shouldSendMessageReceipts: false + shouldSendMessageReceipts: false, + throttleTime: 4000, } } }); + expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED)).toEqual(false); + expect(GlobalConfig.getMessageReceiptsThrottleTime()).toEqual(4000); + }); + + it("Should be able to remove feature flag", () => { + GlobalConfig.removeFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); + expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED)).toEqual(false); + }); + + it("Should be able to add feature flag", () => { + GlobalConfig.setFeatureFlag(FEATURES.PARTICIPANT_CONN_ACK); + expect(GlobalConfig.isFeatureEnabled(FEATURES.PARTICIPANT_CONN_ACK)).toEqual(true); + }); + + it("Should update feature flag and call the registered listeners only once", () => { + GlobalConfig.removeFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); + const handler = jest.fn(); + expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler)).toEqual(false); + GlobalConfig.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); + expect(handler).toHaveBeenCalled(); GlobalConfig.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); GlobalConfig.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); expect(handler).toHaveBeenCalledTimes(1); }); - it("should update feature Flag and call multiple registered listeners only once", () => { - GlobalConfig.update({ - features: { - messageReceipts: { - shouldSendMessageReceipts: true - } - } - }); + it("should update feature flag and call multiple registered listeners only once", () => { const handler = jest.fn().mockReturnValue(true); const handler2 = jest.fn(); - expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler)).toEqual(false); - GlobalConfig.update({ - features: [] - }); + expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler)).toEqual(true); + GlobalConfig.removeFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler)).toEqual(false); expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler2)).toEqual(false); GlobalConfig.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); + GlobalConfig.setFeatureFlag(FEATURES.PARTICIPANT_CONN_ACK); expect(GlobalConfig.isFeatureEnabled(FEATURES.MESSAGE_RECEIPTS_ENABLED, handler)).toEqual(true); expect(handler).toHaveBeenCalled(); expect(handler2).toHaveBeenCalled(); - GlobalConfig.update({ - features: [] - }); + GlobalConfig.removeFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); + GlobalConfig.removeFeatureFlag(FEATURES.PARTICIPANT_CONN_ACK); GlobalConfig.setFeatureFlag(FEATURES.MESSAGE_RECEIPTS_ENABLED); expect(handler).toHaveBeenCalledTimes(3); expect(handler2).toHaveBeenCalledTimes(1);