Skip to content

Commit

Permalink
FLEXIN-74: changed endpoints for init and token refresh (#46)
Browse files Browse the repository at this point in the history
* FLEXIN-74: changed endpoints for init and token refresh

* FLEXIN-74: added spaces

* FLEXIN-74: added deploymentKey and region to the apis

* FLEXIN-74: updated deprecated event

* FLEXIN-74: updated test

* FLEXIN-74: updated test

* FLEXIN-74: address PR comments

* FLEXIN-74: added validation

* FLEXIN-74: removed default value for setDeploymentKey()

* FLEXIN-74: added msg to env variable

* FLEXIN-74: replaced console with logger framework

* FLEXIN-74: using each

* FLEXIN-74: POC changes

* FLEXIN-74: POC changes 2

* FLEXIN-74: POC changes 3

* FLEXIN-74: corrections after merge conflicts

* FLEXIN-74: fixing unit tests

* FLEXIN-74: fixing unit tests

* FLEXIN-74: more fixing

* FLEXIN-74: more fixing

* FLEXIN-74: fixing more unit tests

* FLEXIN-74: removing log
  • Loading branch information
ashishkumarTWLO authored Oct 17, 2023
1 parent 90e01cf commit 8696e30
Show file tree
Hide file tree
Showing 17 changed files with 236 additions and 130 deletions.
8 changes: 2 additions & 6 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,9 @@ API_KEY=
API_SECRET=
ADDRESS_SID=
CONVERSATIONS_SERVICE_SID=
# ALLOWED_ORIGINS should be a comma-separated list of origins
ALLOWED_ORIGINS=http://localhost:3000
## base endpoint of your local server. By default it is "http://localhost:3001"
REACT_APP_SERVER_URL=http://localhost:3001
# Deployment key used to boot the webchat UI locally.
# Deployment key used to boot the Webchat UI locally.
# You can also pass this as part of query params to REACT_APP_SERVER_URL
REACT_APP_DEPLOYMENT_KEY=
# region for the host (i.e stage-us1, dev-us1, us1), defaults to us1(prod)
# Region for the host (i.e stage-us1, dev-us1, us1), defaults to us1(prod)
REACT_APP_REGION=''

10 changes: 9 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,13 @@
"import/no-duplicates": ["error"],
"spaced-comment": "warn",
"prefer-named-capture-group": "off"
}
},
"overrides": [
{
"files": ["src/definitions.ts"],
"rules": {
"camelcase": ["off"]
}
}
]
}
47 changes: 36 additions & 11 deletions src/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,22 @@ describe("Index", () => {
);
});

it("sets endpoint correctly", () => {
const setEndpointSpy = jest.spyOn(sessionDataHandler, "setEndpoint");
it("sets region correctly", () => {
const setRegionSpy = jest.spyOn(sessionDataHandler, "setRegion");

const serverUrl = "serverUrl";
initWebchat({ serverUrl, deploymentKey: "CV000000" });
const region = "Foo";
initWebchat({ deploymentKey: "CV000000", region });

expect(setEndpointSpy).toBeCalledWith(serverUrl);
expect(setRegionSpy).toBeCalledWith(region);
});

it("sets deployment key correctly", () => {
const setDeploymentKeySpy = jest.spyOn(sessionDataHandler, "setDeploymentKey");

const deploymentKey = "Foo";
initWebchat({ deploymentKey });

expect(setDeploymentKeySpy).toBeCalledWith(deploymentKey);
});

it("initializes config", () => {
Expand All @@ -59,27 +68,43 @@ describe("Index", () => {
it("initializes config with provided config merged with default config", () => {
const initConfigSpy = jest.spyOn(initActions, "initConfig");

const serverUrl = "serverUrl";
initWebchat({ serverUrl, deploymentKey: "CV000000" });
const deploymentKey = "CV000000";
initWebchat({ deploymentKey });

expect(initConfigSpy).toBeCalledWith(expect.objectContaining({ deploymentKey, theme: { isLight: true } }));
});

it("gives error when deploymentKey is missing", () => {
const logger = window.Twilio.getLogger("InitWebChat");
const errorLoggerSpy = jest.spyOn(logger, "error");
initWebchat();
expect(errorLoggerSpy).toBeCalledTimes(1);
expect(errorLoggerSpy).toHaveBeenCalledWith("deploymentKey must exist to connect to Webchat servers");
});

expect(initConfigSpy).toBeCalledWith(expect.objectContaining({ serverUrl, theme: { isLight: true } }));
it("gives warning when unsupported params are passed", () => {
const logger = window.Twilio.getLogger("InitWebChat");
const warningSpy = jest.spyOn(logger, "warn");
initWebchat({ deploymentKey: "xyz", someKey: "abc" });
expect(warningSpy).toBeCalledTimes(1);
expect(warningSpy).toHaveBeenCalledWith("someKey is not supported.");
});

it("triggers expaneded true if appStatus is open", () => {
it("triggers expanded true if appStatus is open", () => {
const changeExpandedStatusSpy = jest.spyOn(genericActions, "changeExpandedStatus");

initWebchat({ deploymentKey: "CV000000", appStatus: "open" });
expect(changeExpandedStatusSpy).toHaveBeenCalledWith({ expanded: true });
});

it("triggers expaneded false if appStatus is closed", () => {
it("triggers expanded false if appStatus is closed", () => {
const changeExpandedStatusSpy = jest.spyOn(genericActions, "changeExpandedStatus");

initWebchat({ deploymentKey: "CV000000", appStatus: "closed" });
expect(changeExpandedStatusSpy).toHaveBeenCalledWith({ expanded: false });
});

it("triggers expaneded false with default appStatus", () => {
it("triggers expanded false with default appStatus", () => {
const changeExpandedStatusSpy = jest.spyOn(genericActions, "changeExpandedStatus");

initWebchat({ deploymentKey: "CV000000" });
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/logger.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initLogger , getLogger } from "../logger";
import { initLogger, getLogger } from "../logger";

describe("loggerManager", () => {
it("should show a proper message if an invalid log level `DEBUG` is selected", () => {
Expand Down
50 changes: 39 additions & 11 deletions src/__tests__/sessionDataHandler.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fetchMock from "fetch-mock-jest";

import { sessionDataHandler } from "../sessionDataHandler";
import { sessionDataHandler, contactBackend } from "../sessionDataHandler";
import WebChatLogger from "../logger";

jest.mock("../logger");
Expand All @@ -26,9 +26,44 @@ describe("session data handler", () => {
jest.clearAllMocks();
});

it("should set an endpoint", () => {
sessionDataHandler.setEndpoint("foo");
expect(sessionDataHandler.getEndpoint()).toBe("foo");
it("should set the region", () => {
sessionDataHandler.setRegion("Foo");
expect(sessionDataHandler.getRegion()).toBe("Foo");
});

it("should set the deployment key", () => {
sessionDataHandler.setDeploymentKey("key1");
expect(sessionDataHandler.getDeploymentKey()).toBe("key1");
});

describe("contactBackend", () => {
beforeEach(() => () => {
sessionDataHandler.setRegion("");
});

afterEach(() => {
sessionDataHandler.setRegion("");
fetchMock.reset();
});

it("should call correct stage url", async () => {
const mockFetch = Promise.resolve({ ok: true, json: async () => Promise.resolve("okay") });
const fetchSpy = jest
.spyOn(window, "fetch")
.mockImplementation(async (): Promise<never> => mockFetch as Promise<never>);
sessionDataHandler.setRegion("stage");
await contactBackend("/Webchat/Tokens/Refresh", { formData: {} });
expect(fetchSpy.mock.calls[0][0]).toEqual("https://flex-api.stage.twilio.com/v2/Webchat/Tokens/Refresh");
});

it("should call correct prod url", async () => {
const mockFetch = Promise.resolve({ ok: true, json: async () => Promise.resolve("okay") });
const fetchSpy = jest
.spyOn(window, "fetch")
.mockImplementation(async (): Promise<never> => mockFetch as Promise<never>);
await contactBackend("/Webchat/Tokens/Refresh", { formData: {} });
expect(fetchSpy.mock.calls[0][0]).toEqual("https://flex-api.twilio.com/v2/Webchat/Tokens/Refresh");
});
});

describe("fetch and store new session", () => {
Expand Down Expand Up @@ -196,13 +231,6 @@ describe("session data handler", () => {
expect(spyRemove).toHaveBeenCalled();
});

describe("endpoint", () => {
it("should be able to store endpoint", () => {
sessionDataHandler.setEndpoint("http://localhost:4000");
expect(sessionDataHandler.getEndpoint()).toEqual("http://localhost:4000");
});
});

describe("contactBackend", () => {
it("should", async () => {
jest.spyOn(window, "fetch").mockRejectedValueOnce("ForcedFailure");
Expand Down
4 changes: 2 additions & 2 deletions src/components/PreEngagementFormPhase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const PreEngagementFormPhase = () => {
dispatch(
initSession({
token: data.token,
conversationSid: data.conversationSid
conversationSid: data.conversation_sid
})
);
} catch (err) {
Expand Down Expand Up @@ -97,7 +97,7 @@ export const PreEngagementFormPhase = () => {
data-test="pre-engagement-chat-form-query-textarea"
value={query}
onChange={(e) => dispatch(updatePreEngagementData({ query: e.target.value }))}
onKeyPress={handleKeyPress}
onKeyDown={handleKeyPress}
required
/>
</Box>
Expand Down
9 changes: 2 additions & 7 deletions src/components/WebchatWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@ export function WebchatWidget() {
if (data) {
try {
logger.info("Initializing session.");
dispatch(
initSession({
token: data.token,
conversationSid: data.conversationSid
})
);
dispatch(initSession({ token: data.token, conversationSid: data.conversation_sid }));
} catch (e) {
// if initSession fails, go to changeEngagement phase - most likely there's something wrong with the store token or conversation sis
logger.error("Something wrong with the store token or conversation sis. Changing engagement phase.");
logger.error("Something wrong with the store token or conversation_sid. Changing engagement phase.");
dispatch(changeEngagementPhase({ phase: EngagementPhase.PreEngagementForm }));
}
} else {
Expand Down
10 changes: 5 additions & 5 deletions src/components/__tests__/PreEngagementFormPhase.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { sessionDataHandler } from "../../sessionDataHandler";
import { store } from "../../store/store";

const token = "token";
const conversationSid = "sid";
const conversation_sid = "sid";
jest.mock("../../sessionDataHandler", () => ({
sessionDataHandler: {
fetchAndStoreNewSession: () => ({ token, conversationSid }),
fetchAndStoreNewSession: () => ({ token, conversation_sid }),
getRegion: jest.fn()
}
}));
Expand Down Expand Up @@ -91,7 +91,7 @@ describe("Pre Engagement Form Phase", () => {
fireEvent.submit(formBox);

await waitFor(() => {
expect(initAction.initSession).toHaveBeenCalledWith({ token, conversationSid });
expect(initAction.initSession).toHaveBeenCalledWith({ token, conversationSid: conversation_sid });
});
});

Expand Down Expand Up @@ -144,7 +144,7 @@ describe("Pre Engagement Form Phase", () => {
const { container } = render(withStore(<PreEngagementFormPhase />));

const textArea = container.querySelector("textarea") as Element;
fireEvent.keyPress(textArea, { key: "Enter", code: "Enter", charCode: 13, shiftKey: false });
fireEvent.keyDown(textArea, { key: "Enter", code: "Enter", charCode: 13, shiftKey: false });

expect(fetchAndStoreNewSessionSpy).toHaveBeenCalled();
});
Expand All @@ -154,7 +154,7 @@ describe("Pre Engagement Form Phase", () => {
const { container } = render(withStore(<PreEngagementFormPhase />));

const textArea = container.querySelector("textarea") as Element;
fireEvent.keyPress(textArea, { key: "Enter", code: "Enter", charCode: 13, shiftKey: true });
fireEvent.keyDown(textArea, { key: "Enter", code: "Enter", charCode: 13, shiftKey: true });

expect(fetchAndStoreNewSessionSpy).not.toHaveBeenCalled();
});
Expand Down
7 changes: 5 additions & 2 deletions src/components/__tests__/WebchatWidget.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ afterEach(() => {
describe("Webchat Lite", () => {
const sessionData = {
token: "token",
conversationSid: "sid"
conversation_sid: "sid"
};
const region = "stage";

Expand All @@ -77,7 +77,10 @@ describe("Webchat Lite", () => {

render(<WebchatWidget />);

expect(initSessionSpy).toHaveBeenCalledWith(sessionData);
expect(initSessionSpy).toHaveBeenCalledWith({
token: sessionData.token,
conversationSid: sessionData.conversation_sid
});
});

it("start pre-engagement form if no pre-existing session data", () => {
Expand Down
2 changes: 1 addition & 1 deletion src/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export type TokenResponse = {
token: string;
conversationSid: string;
conversation_sid: string;
identity: string;
expiration: string;
};
Expand Down
23 changes: 9 additions & 14 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,24 @@ const defaultConfig: ConfigState = {
};

const initWebchat = async (userConfig: UserConfig) => {
// eslint-disable-next-line no-warning-comments
// TODO: serverUrl needs to be removed with PR #74
const validKeys = ["deploymentKey", "region", "theme", "serverUrl", "appStatus"];
const validKeys = ["deploymentKey", "region", "theme", "appStatus"];
const logger = window.Twilio.getLogger(`InitWebChat`);

// eslint-disable-next-line no-warning-comments
// TODO: Returning from here if no deployment key with PR #74
if (!userConfig?.deploymentKey) {
logger.error(`deploymentKey must exist to connect to webchat servers`);
if (!userConfig || !userConfig.deploymentKey) {
logger.error(`deploymentKey must exist to connect to Webchat servers`);
}

Object.keys(userConfig).forEach((userConfigKey) => {
if (!validKeys.includes(userConfigKey)) {
logger.warn(`${userConfigKey} is not supported.`);
for (const key in userConfig) {
if (!validKeys.includes(key)) {
logger.warn(`${key} is not supported.`);
}
});
}

store.dispatch(changeExpandedStatus({ expanded: userConfig.appStatus === "open" }));
delete userConfig.appStatus;
store.dispatch(changeExpandedStatus({ expanded: userConfig?.appStatus === "open" }));
delete userConfig?.appStatus;

const webchatConfig = merge({}, defaultConfig, userConfig);

sessionDataHandler.setEndpoint(webchatConfig.serverUrl);
sessionDataHandler.setRegion(webchatConfig.region);
sessionDataHandler.setDeploymentKey(webchatConfig.deploymentKey);

Expand Down
Loading

0 comments on commit 8696e30

Please sign in to comment.