Skip to content

Commit

Permalink
V4.10.0 (#2180)
Browse files Browse the repository at this point in the history
* Webchat Upgrade and Version number change

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Handle change to middleware (#2177)

* Handle change to middleware

* Updated babel preset env

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Reverted change to babel

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Locks updated to pre webchat change

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Post wechat check

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Prevent hoisting bf-chatdown

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Updated babel preset env

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* fix: Webchat socket instantiation delay (#2179)

* Updated websocket server code to backup messages if it is not connected
* Refactored more occurances of socket send
* Renaming variables
* Added unit test to make sure backedup messages are cleared before connection starts
* Lint fix

* Updated release date

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* All tests working

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Co-authored-by: Srinaath Ravichandran <srravich@microsoft.com>
  • Loading branch information
srinaath and Srinaath Ravichandran committed Aug 31, 2020
1 parent 9042e38 commit 7829dbd
Show file tree
Hide file tree
Showing 24 changed files with 11,686 additions and 6,555 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -4,14 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased
## v4.10.0 - 2020 - 08 - 21
## Added
- [client] Added a log panel entry at the start of a conversation that displays the bot endpoint in PR [2149](https://github.com/microsoft/BotFramework-Emulator/pull/2149)
- [client] - Bumped `botframework-webchat` to v4.10.0 in PR [2177](https://github.com/microsoft/BotFramework-Emulator/pull/2177)

## Fixed
- [client] Added missing content to signed in view of Cosmos DB service dialog and fixed product page link in PR [2150](https://github.com/microsoft/BotFramework-Emulator/pull/2150)
- [docs] Modified CONTRIBUTING.md to include updated information about global dependencies required to build from source in PR [2153](https://github.com/microsoft/BotFramework-Emulator/pull/2153)
- [client] Fixed a bug where trying to open the sign-in link on an OAuth card would open the file explorer if ngrok was not configured in PR [2155](https://github.com/microsoft/BotFramework-Emulator/pull/2155)
- [client] Change to a warning message in inspector when clicking on LUIS trace [2160](https://github.com/microsoft/BotFramework-Emulator/pull/2160)
- [client] Handle result from webchat middleware gracefully [2177](https://github.com/microsoft/BotFramework-Emulator/pull/2177)
- [client] Handle Webchat socket instantiation delay [2179](https://github.com/microsoft/BotFramework-Emulator/pull/2179)

## v4.9.0 - 2020 - 05 - 11
## Added
Expand Down
7 changes: 7 additions & 0 deletions lerna.json
Expand Up @@ -14,5 +14,12 @@
"packages/extensions/json",
"packages/tools"
],
"command": {
"bootstrap": {
"nohoist": [
"@microsoft/bf-chatdown"
]
}
},
"version": "independent"
}
17,978 changes: 11,489 additions & 6,489 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions packages/app/client/package.json
Expand Up @@ -48,7 +48,7 @@
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-react-jsx": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-env": "^7.11.0",
"@babel/preset-typescript": "^7.1.0",
"@types/enzyme": "^3.1.10",
"@types/jest": "24.0.13",
Expand Down Expand Up @@ -93,10 +93,10 @@
"url-loader": "^1.0.1",
"webpack": "^4.32.2",
"webpack-cli": "^3.3.2",
"webpack-dev-server": "^3.4.1"
"webpack-dev-server": "^3.4.1",
"@babel/runtime": "^7.1.5"
},
"dependencies": {
"@babel/runtime": "^7.1.5",
"@bfemulator/app-shared": "^1.0.0",
"@bfemulator/sdk-client": "^1.0.0",
"@bfemulator/sdk-shared": "^1.0.0",
Expand All @@ -107,8 +107,8 @@
"base64url": "3.0.0",
"botframework-config": "4.4.0",
"botframework-schema": "^4.3.4",
"botframework-webchat": "4.9.0",
"botframework-webchat-core": "4.9.0",
"botframework-webchat": "4.10.0",
"botframework-webchat-core": "4.10.0",
"eslint-plugin-react": "^7.12.3",
"markdown-it": "^8.4.2",
"react": "16.8.6",
Expand Down
Expand Up @@ -32,7 +32,6 @@
//

import { ValueTypes, RestartConversationStatus } from '@bfemulator/app-shared';
import { User } from '@bfemulator/sdk-shared';
import { Activity, ActivityTypes } from 'botframework-schema';
import ReactWebChat, { createStyleSet } from 'botframework-webchat';
import * as React from 'react';
Expand Down Expand Up @@ -139,6 +138,11 @@ export class Chat extends PureComponent<ChatProps, ChatState> {
}

private activityWrapper(next, card, children): ReactNode {
let childrenContents = null;
const middlewareResult = next(card);
if (middlewareResult) {
childrenContents = middlewareResult(children);
}
return (
<OuterActivityWrapperContainer
card={card}
Expand All @@ -147,7 +151,7 @@ export class Chat extends PureComponent<ChatProps, ChatState> {
onItemRendererClick={this.onItemRendererClick}
onItemRendererKeyDown={this.onItemRendererKeyDown}
>
{next(card)(children)}
{childrenContents}
</OuterActivityWrapperContainer>
);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/app/main/package.json
@@ -1,7 +1,7 @@
{
"name": "@bfemulator/main",
"packagename": "BotFramework-Emulator",
"version": "4.9.0",
"version": "4.10.0",
"private": true,
"description": "Development tool for the Microsoft Bot Framework. Allows developers to test and debug bots on localhost.",
"main": "./app/server/main.js",
Expand Down Expand Up @@ -79,7 +79,7 @@
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-transform-react-jsx": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.4.4",
"@babel/preset-env": "^7.1.0",
"@babel/preset-env": "^7.11.0",
"@babel/preset-typescript": "^7.1.0",
"@types/async": "^2.0.47",
"@types/chokidar": "^1.7.5",
Expand Down
Expand Up @@ -42,13 +42,17 @@ jest.mock('../../../../utils/oauthLinkEncoder', () => ({
}),
}));

jest.mock('../../../../webSocketServer', () => ({
WebSocketServer: {
getSocketByConversationId: () => ({
send: jest.fn(),
}),
},
}));
const mockSocket = {
send: jest.fn(),
};

jest.mock('../../../../webSocketServer', () => {
return {
WebSocketServer: {
sendToSubscribers: (...args) => mockSocket.send(...args),
},
};
});

describe('replyToActivity route middleware', () => {
const mockReq: any = {
Expand Down
Expand Up @@ -58,10 +58,7 @@ export function createReplyToActivityHandler(emulatorServer: EmulatorRestServer)

// post activity
activity = conversation.prepActivityToBeSentToUser(conversation.user.id, activity);
const payload = { activities: [activity] };
const socket = WebSocketServer.getSocketByConversationId(conversationId);
socket && socket.send(JSON.stringify(payload));

WebSocketServer.sendToSubscribers(conversation.conversationId, activity);
res.send(HttpStatus.OK, { id: activity.id });
res.end();
};
Expand Down
Expand Up @@ -35,13 +35,17 @@ import * as HttpStatus from 'http-status-codes';

import { sendActivityToConversation } from './sendActivityToConversation';

jest.mock('../../../../webSocketServer', () => ({
WebSocketServer: {
getSocketByConversationId: () => ({
send: jest.fn(),
}),
},
}));
const mockSocket = {
send: jest.fn(),
};

jest.mock('../../../../webSocketServer', () => {
return {
WebSocketServer: {
sendToSubscribers: (...args) => mockSocket.send(...args),
},
};
});

const mockSendErrorResponse = jest.fn();
jest.mock('../../../../utils/sendErrorResponse', () => ({
Expand Down
Expand Up @@ -48,10 +48,7 @@ export function sendActivityToConversation(req: Request, res: Response, next: Ne

// post activity
activity = conversation.prepActivityToBeSentToUser(conversation.user.id, activity);
const payload = { activities: [activity] };
const socket = WebSocketServer.getSocketByConversationId(conversation.conversationId);
socket && socket.send(JSON.stringify(payload));

WebSocketServer.sendToSubscribers(conversation.conversationId, activity);
res.send(HttpStatus.OK, { id: activity.id });
res.end();
} catch (err) {
Expand Down
Expand Up @@ -47,9 +47,7 @@ export function sendHistoryToConversation(req: Request, res: Response, next: Nex
for (const activity of activities) {
try {
const updatedActivity = conversation.prepActivityToBeSentToUser(conversation.user.id, activity);
const payload = { activities: [updatedActivity] };
const socket = WebSocketServer.getSocketByConversationId(conversation.conversationId);
socket && socket.send(JSON.stringify(payload));
WebSocketServer.sendToSubscribers(conversation.conversationId, updatedActivity);
successCount++;
} catch (err) {
if (firstErrorMessage === '') {
Expand Down
Expand Up @@ -43,11 +43,14 @@ jest.mock('../../../utils/sendErrorResponse', () => ({
const mockSocket = {
send: jest.fn(),
};
jest.mock('../../../webSocketServer', () => ({
WebSocketServer: {
getSocketByConversationId: () => mockSocket,
},
}));

jest.mock('../../../webSocketServer', () => {
return {
WebSocketServer: {
sendToSubscribers: (...args) => mockSocket.send(...args),
},
};
});

describe('postActivity handler', () => {
beforeEach(() => {
Expand All @@ -56,14 +59,16 @@ describe('postActivity handler', () => {
});

it('should return a 200 and the id of the posted activity', async () => {
const activity = {
id: 'activity1',
};

const mockEmulatorServer: any = {
logger: {
logMessage: jest.fn(),
},
};
const activity = {
id: 'activity1',
};

const req: any = {
body: activity,
conversation: {
Expand All @@ -72,6 +77,7 @@ describe('postActivity handler', () => {
response: {},
statusCode: HttpStatus.OK,
}),
conversationId: 'convo1',
},
params: {
conversationId: 'convo1',
Expand All @@ -88,11 +94,7 @@ describe('postActivity handler', () => {
expect(res.send).toHaveBeenCalledWith(HttpStatus.OK, activity);
expect(res.end).toHaveBeenCalled();
expect(next).toHaveBeenCalled();
expect(mockSocket.send).toHaveBeenCalledWith(
JSON.stringify({
activities: [{ ...req.body, id: 'activity1' }],
})
);
expect(mockSocket.send).toHaveBeenCalledWith(req.params.conversationId, activity);
});

it('should return a 200 but not send the /INSPECT open command over the web socket', async () => {
Expand Down
Expand Up @@ -85,9 +85,7 @@ export function createPostActivityHandler(emulatorServer: EmulatorRestServer) {
res.end();
return next();
}
const payload = { activities: [{ ...activity, id: activity.id }] };
const socket = WebSocketServer.getSocketByConversationId(conversation.conversationId);
socket && socket.send(JSON.stringify(payload));
WebSocketServer.sendToSubscribers(conversation.conversationId, activity);
}
} catch (err) {
sendErrorResponse(req, res, next, err);
Expand Down
Expand Up @@ -38,11 +38,14 @@ import { createFeedActivitiesAsTranscriptHandler } from './feedActivitiesAsTrans
const mockSocket = {
send: jest.fn(),
};
jest.mock('../../../webSocketServer', () => ({
WebSocketServer: {
getSocketByConversationId: () => mockSocket,
},
}));

jest.mock('../../../webSocketServer', () => {
return {
WebSocketServer: {
sendToSubscribers: (...args) => mockSocket.send(...args),
},
};
});

describe('feedActivitiesAsTranscript handler', () => {
it('should send a 200 after sending all activities over the web socket connection', () => {
Expand Down
Expand Up @@ -52,9 +52,7 @@ export function createFeedActivitiesAsTranscriptHandler(emulatorServer: Emulator
}
activities = conversation.prepTranscriptActivities(activities);
activities.forEach(activity => {
const payload = { activities: [activity] };
const socket = WebSocketServer.getSocketByConversationId(conversation.conversationId);
socket && socket.send(JSON.stringify(payload));
WebSocketServer.sendToSubscribers(conversation.conversationId, activity);
emulatorServer.logger.logActivity(conversation.conversationId, activity, activity.recipient.role);
});
} catch (e) {
Expand Down
82 changes: 82 additions & 0 deletions packages/app/main/src/server/webSocketServer.spec.ts
Expand Up @@ -31,6 +31,8 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { Activity } from 'botframework-schema';

import { WebSocketServer } from './webSocketServer';

const mockWSServer = {
Expand Down Expand Up @@ -157,4 +159,84 @@ describe('WebSocketServer', () => {
expect(Object.keys((WebSocketServer as any)._servers)).toEqual([mockConversationId]);
expect(mockWSServer.handleUpgrade).toHaveBeenCalledTimes(1);
});

it('should clear the messages backed up before websocket connection is started', async () => {
let onConnectionFunction = null;
let websocketHandler = null;

(WebSocketServer as any)._restServer = undefined;
(WebSocketServer as any)._servers = {};
(WebSocketServer as any)._sockets = {};

mockWSServer.on.mockImplementation((event, implementation) => {
if (event === 'connection') {
onConnectionFunction = implementation;
}
});

mockCreateServer.mockReturnValueOnce({
address: () => ({ port: 55523 }),
get: (route, handler) => {
websocketHandler = handler;
},
listen: jest.fn((_port, cb) => {
cb();
}),
once: jest.fn(),
});
await WebSocketServer.init();

WebSocketServer.queueActivities('conv-123', { id: 'activity-1' } as Activity);
WebSocketServer.queueActivities('conv-234', { id: 'activity-1' } as Activity);

WebSocketServer.queueActivities('conv-123', { id: 'activity-2' } as Activity);
WebSocketServer.queueActivities('conv-234', { id: 'activity-2' } as Activity);
websocketHandler(
{
params: {
conversationId: 'conv-234',
},
},
{
claimUpgrade: jest.fn(() => {
return {
head: jest.fn(),
socket: jest.fn(),
};
}),
}
);
const socketSendMock = jest.fn();
onConnectionFunction({
send: socketSendMock,
on: jest.fn(),
});
expect(socketSendMock).toHaveBeenCalledTimes(2);
expect(socketSendMock).toHaveBeenNthCalledWith(1, JSON.stringify({ activities: [{ id: 'activity-1' }] }));
expect(socketSendMock).toHaveBeenNthCalledWith(2, JSON.stringify({ activities: [{ id: 'activity-2' }] }));
socketSendMock.mockClear();

websocketHandler(
{
params: {
conversationId: 'conv-123',
},
},
{
claimUpgrade: jest.fn(() => {
return {
head: jest.fn(),
socket: jest.fn(),
};
}),
}
);
onConnectionFunction({
send: socketSendMock,
on: jest.fn(),
});
expect(socketSendMock).toHaveBeenCalledTimes(2);
expect(socketSendMock).toHaveBeenNthCalledWith(1, JSON.stringify({ activities: [{ id: 'activity-1' }] }));
expect(socketSendMock).toHaveBeenNthCalledWith(2, JSON.stringify({ activities: [{ id: 'activity-2' }] }));
});
});

0 comments on commit 7829dbd

Please sign in to comment.