Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b7d79c4
Added selected state to web chat log items
tonyanziano Apr 7, 2021
689c4df
Latest item in log now selected if nothing is selected.
tonyanziano Apr 8, 2021
299a20e
Merge branch 'main' of https://github.com/microsoft/BotFramework-Comp…
tonyanziano Apr 8, 2021
90f7a58
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 8, 2021
81b47ff
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 8, 2021
0c1e433
Moved log entry sorting to dispatcher
tonyanziano Apr 8, 2021
966a798
Merge branch 'main' into toanzian/log-selection
srinaath Apr 8, 2021
d96b2ea
Merge branch 'toanzian/log-selection' of https://github.com/microsoft…
tonyanziano Apr 8, 2021
f6d4797
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 8, 2021
e7460ec
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
b613c31
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
bda1835
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
33cb931
Made `id` property mandatory
tonyanziano Apr 9, 2021
b52981a
Merge branch 'toanzian/log-selection' of https://github.com/microsoft…
tonyanziano Apr 9, 2021
1f1604e
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
68c6db3
Fixed tests
tonyanziano Apr 9, 2021
c2f5dc7
Merge branch 'toanzian/log-selection' of https://github.com/microsoft…
tonyanziano Apr 9, 2021
5bd9d29
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
6b082e0
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
beb3619
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
74f6c5e
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
0b34b5e
Moved Web Chat log item ID generation to instantiation
tonyanziano Apr 9, 2021
ff3a830
Merge branch 'toanzian/log-selection' of https://github.com/microsoft…
tonyanziano Apr 9, 2021
c7e4a2d
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
5117387
Merge branch 'main' into toanzian/log-selection
hatpick Apr 9, 2021
7f313ed
Merge branch 'main' into toanzian/log-selection
tonyanziano Apr 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from '@botframework-composer/types';
import { AxiosResponse } from 'axios';
import formatMessage from 'format-message';
import { v4 as uuid } from 'uuid';

import TelemetryClient from '../../telemetry/TelemetryClient';
import { BotStatus } from '../../constants';
Expand Down Expand Up @@ -79,6 +80,7 @@ export const WebChatPanel: React.FC<WebChatPanelProps> = ({
projectId,
data.activities.map((a) => ({
activity: a,
id: uuid(),
timestamp: new Date(a.timestamp || Date.now()).getTime(),
trafficType: data.trafficType,
}))
Expand All @@ -104,6 +106,7 @@ export const WebChatPanel: React.FC<WebChatPanelProps> = ({
error: {
message: formatMessage('An error occurred connecting initializing the DirectLine server'),
},
id: uuid(),
request: { route: 'conversations/ws/port', method: 'GET', payload: {} },
response: { payload: response.data, statusCode: response.status },
timestamp: Date.now(),
Expand Down Expand Up @@ -211,6 +214,7 @@ export const WebChatPanel: React.FC<WebChatPanelProps> = ({
error: {
message: formatMessage('An error occurred saving transcripts'),
},
id: uuid(),
request: { route: 'saveTranscripts/', method: '', payload: {} },
response: { payload: ex, statusCode: 400 },
timestamp: Date.now(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import React, { useMemo, useEffect, useState, useRef, useCallback } from 'react'
import { useRecoilValue } from 'recoil';
import { ConversationTrafficItem } from '@botframework-composer/types/src';
import formatMessage from 'format-message';
import debounce from 'lodash/debounce';

import {
dispatcherState,
Expand Down Expand Up @@ -42,6 +43,10 @@ const logPane = css`
box-sizing: border-box;
`;

const itemIsSelected = (item: ConversationTrafficItem, currentInspectionData?: WebChatInspectionData) => {
return item.id === currentInspectionData?.item?.id;
};

// R12: We are showing Errors from the root bot only.
export const WebChatLogContent: React.FC<DebugPanelTabHeaderProps> = ({ isActive }) => {
const currentProjectId = useRecoilValue(rootBotProjectIdSelector);
Expand All @@ -59,9 +64,33 @@ export const WebChatLogContent: React.FC<DebugPanelTabHeaderProps> = ({ isActive
}
};

const performInspection = useRef(
debounce((trafficItem: ConversationTrafficItem) => {
if (currentProjectId) {
if (trafficItem?.trafficType === 'network') {
// default to inspecting the request body
setWebChatInspectionData(currentProjectId, { item: trafficItem, mode: 'request' });
} else {
setWebChatInspectionData(currentProjectId, { item: trafficItem });
}
}
}, 500)
).current;

const inspectLatestLogMessage = () => {
// inspect latest log message if nothing is being inspected
if (!inspectionData && currentProjectId) {
const latestTrafficItem = [...rawWebChatTraffic].pop();
if (latestTrafficItem) {
performInspection(latestTrafficItem);
}
}
};

useEffect(() => {
if (navigateToLatestEntry && isActive) {
navigateToNewestLogEntry();
inspectLatestLogMessage();
navigateToLatestEntryWhenActive(false);
}
}, [isActive, navigateToLatestEntry]);
Expand All @@ -80,16 +109,37 @@ export const WebChatLogContent: React.FC<DebugPanelTabHeaderProps> = ({ isActive
);

const renderLogItem = useCallback(
(item: ConversationTrafficItem, index: number) => {
(item: ConversationTrafficItem, index: number, inspectionData?: WebChatInspectionData) => {
switch (item.trafficType) {
case 'activity':
return <WebChatActivityLogItem index={index} item={item} onClickTraffic={onClickTraffic} />;
return (
<WebChatActivityLogItem
index={index}
isSelected={itemIsSelected(item, inspectionData)}
item={item}
onClickTraffic={onClickTraffic}
/>
);

case 'network':
return <WebChatNetworkLogItem index={index} item={item} onClickTraffic={onClickTraffic} />;
return (
<WebChatNetworkLogItem
index={index}
isSelected={itemIsSelected(item, inspectionData)}
item={item}
onClickTraffic={onClickTraffic}
/>
);

case 'networkError':
return <WebChatNetworkLogItem index={index} item={item} onClickTraffic={onClickTraffic} />;
return (
<WebChatNetworkLogItem
index={index}
isSelected={itemIsSelected(item, inspectionData)}
item={item}
onClickTraffic={onClickTraffic}
/>
);

default:
return null;
Expand All @@ -99,12 +149,10 @@ export const WebChatLogContent: React.FC<DebugPanelTabHeaderProps> = ({ isActive
);

const displayedTraffic = useMemo(() => {
const sortedTraffic = [...rawWebChatTraffic]
.sort((t1, t2) => t1.timestamp - t2.timestamp)
.map((t, i) => renderLogItem(t, i));
setLogItemCount(sortedTraffic.length);
return sortedTraffic;
}, [rawWebChatTraffic, renderLogItem]);
const renderedTraffic = [...rawWebChatTraffic].map((t, i) => renderLogItem(t, i, inspectionData));
setLogItemCount(renderedTraffic.length);
return renderedTraffic;
}, [inspectionData, rawWebChatTraffic, renderLogItem]);

const setInspectionData = (data: WebChatInspectionData) => {
if (currentProjectId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ describe('<DebugPanel />', () => {
});
set(webChatTrafficState(rootBotId), [
{
trafficType: 'activity',
activity: {} as any,
id: '',
trafficType: 'activity',
timestamp: Date.now(),
},
]);
Expand All @@ -49,8 +50,9 @@ describe('<DebugPanel />', () => {
});
set(webChatTrafficState(rootBotId), [
{
trafficType: 'activity',
activity: {} as any,
id: '',
trafficType: 'activity',
timestamp: Date.now(),
},
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('<DebugPanel />', () => {
error: {
message: 'Error validating Microsoft App ID and Password',
},
id: '',
timestamp: Date.now(),
trafficType: 'networkError',
request: {
Expand All @@ -61,6 +62,7 @@ describe('<DebugPanel />', () => {
error: {
message: 'Error validating Microsoft App ID and Password',
},
id: '',
timestamp: Date.now(),
trafficType: 'networkError',
request: {
Expand Down Expand Up @@ -88,6 +90,7 @@ describe('<DebugPanel />', () => {
error: {
message: 'Error validating Microsoft App ID and Password',
},
id: '',
timestamp: Date.now(),
trafficType: 'networkError',
request: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ describe('web chat dispatcher', () => {
it('should append a single web chat traffic item to the log', async () => {
const trafficItem = {
activity: {} as any,
id: '',
timestamp: Date.now(),
trafficType: 'activity' as 'activity',
};
Expand All @@ -64,11 +65,13 @@ describe('web chat dispatcher', () => {
it('should append multiple web chat traffic items to the log', async () => {
const trafficItem1 = {
activity: {} as any,
id: '',
timestamp: Date.now(),
trafficType: 'activity' as 'activity',
};
const trafficItem2 = {
activity: {} as any,
id: '',
timestamp: Date.now() + 5,
trafficType: 'activity' as 'activity',
};
Expand All @@ -82,6 +85,7 @@ describe('web chat dispatcher', () => {
it('should clear traffic from the log', async () => {
const trafficItem = {
activity: {} as any,
id: '',
timestamp: Date.now(),
trafficType: 'activity' as 'activity',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export const webChatLogDispatcher = () => {
const clearWebChatLogs = useRecoilCallback((callbackHelpers: CallbackInterface) => (projectId: string) => {
const { set } = callbackHelpers;
set(webChatTrafficState(projectId), []);
set(webChatInspectionDataState(projectId), undefined); // clear the inspection panel
});

const setWebChatPanelVisibility = useRecoilCallback((callbackHelpers: CallbackInterface) => (value: boolean) => {
Expand All @@ -27,9 +28,9 @@ export const webChatLogDispatcher = () => {
const { set } = callbackHelpers;
set(webChatTrafficState(projectId), (currentTraffic) => {
if (Array.isArray(traffic)) {
return [...currentTraffic, ...traffic];
return [...currentTraffic, ...traffic].sort((t1, t2) => t1.timestamp - t2.timestamp);
} else {
return [...currentTraffic, traffic];
return [...currentTraffic, traffic].sort((t1, t2) => t1.timestamp - t2.timestamp);
}
});
}
Expand Down
3 changes: 3 additions & 0 deletions Composer/packages/server/src/middleware/logNetworkTraffic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { NextFunction, Request, Response } from 'express';
import { ConversationNetworkErrorItem, ConversationNetworkTrafficItem } from '@botframework-composer/types';
import { v4 as uuid } from 'uuid';

import { WebSocketServer } from '../directline/utils/webSocketServer';

Expand All @@ -25,6 +26,7 @@ export function logNetworkTraffic(req: Request, res: Response, next?: NextFuncti
details: error.details,
message: error.message,
},
id: uuid(),
request: { method: req.method, payload: req.body, route: req.originalUrl },
response: {
payload: JSON.parse((res as any).sentData || '{}'),
Expand All @@ -36,6 +38,7 @@ export function logNetworkTraffic(req: Request, res: Response, next?: NextFuncti
} else {
// a successful response was sent to the client
data = {
id: uuid(),
request: { method: req.method, payload: req.body, route: req.originalUrl },
response: {
payload: JSON.parse((res as any).sentData || '{}'),
Expand Down
3 changes: 3 additions & 0 deletions Composer/packages/types/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export type NetworkTrafficResponse = {
};

export type ConversationNetworkTrafficItem = {
id: string;
request: NetworkTrafficRequest;
response: NetworkTrafficResponse;
timestamp: number;
Expand All @@ -78,11 +79,13 @@ export type ConversationActivityTraffic = {

export type ConversationActivityTrafficItem = {
activity: Activity;
id: string;
timestamp: number;
trafficType: 'activity';
};

export type ConversationNetworkErrorItem = {
id: string;
error: {
message: string;
details?: string;
Expand Down