Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the Emulator user to specify a User ID #1456

Merged
merged 12 commits into from
Apr 26, 2019
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class AppSettingsEditor extends React.Component<AppSettingsEditorProps, A

public componentDidMount(): void {
this.props.getFrameworkSettings();
// this.setState({ ['useCustomId']: this.props.framework.useCustomId });
Aliandi marked this conversation as resolved.
Show resolved Hide resolved
}

public render(): JSX.Element {
Expand Down Expand Up @@ -167,7 +168,7 @@ export class AppSettingsEditor extends React.Component<AppSettingsEditorProps, A
</Row>
</Column>
<Column className={[styles.rightColumn, styles.spacing].join(' ')}>
<SmallHeader>Auth</SmallHeader>
<SmallHeader>User settings</SmallHeader>
<Checkbox
className={styles.checkboxOverrides}
checked={state.use10Tokens}
Expand All @@ -176,7 +177,6 @@ export class AppSettingsEditor extends React.Component<AppSettingsEditorProps, A
label="Use version 1.0 authentication tokens"
name="use10Tokens"
/>
<SmallHeader>Sign-in</SmallHeader>
<Checkbox
className={styles.checkboxOverrides}
checked={state.useCodeValidation}
Expand All @@ -185,6 +185,28 @@ export class AppSettingsEditor extends React.Component<AppSettingsEditorProps, A
label="Use a sign-in verification code for OAuthCards"
name="useCodeValidation"
/>
<Checkbox
className={styles.checkboxOverrides}
checked={state.useCustomId}
onChange={this.onChangeCheckBox}
id="use-custom-id"
label="Use your own user ID to communicate with the bot"
name="useCustomId"
/>
<Row align={RowAlignment.Top}>
<TextField
label="User ID"
className={styles.appSettingsInput}
inputContainerClassName={styles.inputContainer}
readOnly={false}
value={state.userGUID}
name="userGUID"
onChange={this.onInputChange}
disabled={!state.useCustomId}
required={state.useCustomId}
errorMessage={state.userGUID ? '' : 'Enter a user ID'}
/>
</Row>
<SmallHeader>Application Updates</SmallHeader>
<Checkbox
className={styles.checkboxOverrides}
Expand Down Expand Up @@ -227,6 +249,7 @@ export class AppSettingsEditor extends React.Component<AppSettingsEditorProps, A
const change = { [name]: checked };
this.setState(change);
this.updateDirtyFlag(change);
if (name === 'useCustomId' && checked === false) this.setState({ userGUID: '' });
Aliandi marked this conversation as resolved.
Show resolved Hide resolved
};

private onClickBrowse = async (): Promise<void> => {
Expand Down
1 change: 1 addition & 0 deletions packages/app/client/src/ui/editor/emulator/emulator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
}

.restart-icon {
margin-left: 20px;
&::before { -webkit-mask: url(../../media/ic_refresh.svg); }
}
.save-icon {
Expand Down
44 changes: 22 additions & 22 deletions packages/app/client/src/ui/editor/emulator/emulator.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,8 @@ describe('<EmulatorContainer/>', () => {
it('should export a transcript', () => {
instance.onExportTranscriptClick();

expect(mockRemoteCallsMade).toHaveLength(1);
expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.SaveTranscriptToFile);
expect(mockRemoteCallsMade).toHaveLength(3);
expect(mockRemoteCallsMade[2].commandName).toBe(SharedConstants.Commands.Emulator.SaveTranscriptToFile);
expect(mockRemoteCallsMade[0].args).toEqual([16, 'convo1']);
});

Expand All @@ -312,7 +312,7 @@ describe('<EmulatorContainer/>', () => {
};
await instance.startNewConversation(undefined, true, true);

expect(mockRemoteCallsMade).toHaveLength(1);
expect(mockRemoteCallsMade).toHaveLength(4);
expect(initConversationSpy).toHaveBeenCalledWith(instance.props, options);
});

Expand Down Expand Up @@ -349,9 +349,9 @@ describe('<EmulatorContainer/>', () => {
};
await instance.startNewConversation(undefined, false, true);

expect(mockRemoteCallsMade).toHaveLength(1);
expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.SetCurrentUser);
expect(mockRemoteCallsMade[0].args).toEqual([options.userId]);
expect(mockRemoteCallsMade).toHaveLength(4);
expect(mockRemoteCallsMade[3].commandName).toBe(SharedConstants.Commands.Emulator.SetCurrentUser);
expect(mockRemoteCallsMade[3].args).toEqual([options.userId]);
expect(mockInitConversation).toHaveBeenCalledWith(instance.props, options);
});

Expand All @@ -362,9 +362,9 @@ describe('<EmulatorContainer/>', () => {

expect(mockDispatch).toHaveBeenCalledWith(clearLog('doc1'));
expect(mockDispatch).toHaveBeenCalledWith(setInspectorObjects('doc1', []));
expect(mockRemoteCallsMade).toHaveLength(1);
expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent);
expect(mockRemoteCallsMade[0].args).toEqual(['conversation_restart', { userId: 'new' }]);
expect(mockRemoteCallsMade).toHaveLength(3);
expect(mockRemoteCallsMade[2].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent);
expect(mockRemoteCallsMade[2].args).toEqual(['conversation_restart', { userId: 'new' }]);
expect(mockStartNewConversation).toHaveBeenCalledWith(undefined, true, true);
});

Expand All @@ -375,9 +375,9 @@ describe('<EmulatorContainer/>', () => {

expect(mockDispatch).toHaveBeenCalledWith(clearLog('doc1'));
expect(mockDispatch).toHaveBeenCalledWith(setInspectorObjects('doc1', []));
expect(mockRemoteCallsMade).toHaveLength(1);
expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent);
expect(mockRemoteCallsMade[0].args).toEqual(['conversation_restart', { userId: 'same' }]);
expect(mockRemoteCallsMade).toHaveLength(3);
expect(mockRemoteCallsMade[2].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent);
expect(mockRemoteCallsMade[2].args).toEqual(['conversation_restart', { userId: 'same' }]);
expect(mockStartNewConversation).toHaveBeenCalledWith(undefined, true, false);
});

Expand Down Expand Up @@ -417,11 +417,11 @@ describe('<EmulatorContainer/>', () => {

await instance.startNewConversation(mockProps);

expect(mockRemoteCallsMade).toHaveLength(2);
expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript);
expect(mockRemoteCallsMade[0].args).toEqual(['someUniqueId|transcript']);
expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromMemory);
expect(mockRemoteCallsMade[1].args).toEqual(['someConvoId', 'someBotId', 'someUserId', []]);
expect(mockRemoteCallsMade).toHaveLength(6);
expect(mockRemoteCallsMade[4].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript);
expect(mockRemoteCallsMade[4].args).toEqual(['someUniqueId|transcript']);
expect(mockRemoteCallsMade[5].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromMemory);
expect(mockRemoteCallsMade[5].args).toEqual(['someConvoId', 'someBotId', 'someUserId', []]);
});

it('should start a new conversation from transcript on disk', async () => {
Expand All @@ -441,11 +441,11 @@ describe('<EmulatorContainer/>', () => {

await instance.startNewConversation(mockProps);

expect(mockRemoteCallsMade).toHaveLength(2);
expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript);
expect(mockRemoteCallsMade[0].args).toEqual(['someUniqueId|transcript']);
expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromDisk);
expect(mockRemoteCallsMade[1].args).toEqual(['someConvoId', 'someBotId', 'someUserId', 'someDocId']);
expect(mockRemoteCallsMade).toHaveLength(6);
expect(mockRemoteCallsMade[4].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript);
expect(mockRemoteCallsMade[4].args).toEqual(['someUniqueId|transcript']);
expect(mockRemoteCallsMade[5].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromDisk);
expect(mockRemoteCallsMade[5].args).toEqual(['someConvoId', 'someBotId', 'someUserId', 'someDocId']);
expect(mockDispatch).toHaveBeenCalledWith(updateDocument('someDocId', { meta: 'some file info' }));
});
});
33 changes: 21 additions & 12 deletions packages/app/client/src/ui/editor/emulator/emulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,18 @@

import { createDirectLine } from 'botframework-webchat';
import { uniqueId, uniqueIdv4 } from '@bfemulator/sdk-shared';
import { SplitButton, Splitter } from '@bfemulator/ui-react';
import { Splitter, SplitButton } from '@bfemulator/ui-react';
import base64Url from 'base64url';
import { IEndpointService } from 'botframework-config/lib/schema';
import * as React from 'react';
import { DebugMode, newNotification, Notification, SharedConstants, ValueTypesMask } from '@bfemulator/app-shared';
import {
DebugMode,
FrameworkSettings,
newNotification,
Notification,
SharedConstants,
ValueTypesMask,
} from '@bfemulator/app-shared';

import { Document } from '../../../data/reducer/editor';
import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl';
Expand Down Expand Up @@ -121,14 +128,11 @@ export class Emulator extends React.Component<EmulatorProps, {}> {
const { document = {} } = props;
const { document: nextDocument = {} } = nextProps;

const documentOrUserIdChanged =
(!nextDocument.directLine && document.documentId !== nextDocument.documentId) ||
document.userId !== nextDocument.userId;
const documentIdChanged = !nextDocument.directLine && document.documentId !== nextDocument.documentId;

if (documentOrUserIdChanged) {
if (documentIdChanged) {
startNewConversation(nextProps).catch();
}

const switchedDocuments = props.activeDocumentId !== nextProps.activeDocumentId;
const switchedToThisDocument = nextProps.activeDocumentId === props.documentId;

Expand All @@ -152,10 +156,13 @@ export class Emulator extends React.Component<EmulatorProps, {}> {
? `${uniqueId()}|${props.mode}`
: props.document.conversationId || `${uniqueId()}|${props.mode}`;

const userId = requireNewUserId ? uniqueIdv4() : props.document.userId;
if (requireNewUserId) {
await CommandServiceImpl.remoteCall(SharedConstants.Commands.Emulator.SetCurrentUser, userId);
}
const framework: FrameworkSettings = await CommandServiceImpl.remoteCall(
SharedConstants.Commands.Settings.LoadAppSettings
);
const stableId = framework.userGUID || props.document.userId;
const userId = requireNewUserId ? uniqueIdv4() : stableId;

await CommandServiceImpl.remoteCall(SharedConstants.Commands.Emulator.SetCurrentUser, userId);

const options = {
conversationId,
Expand Down Expand Up @@ -276,6 +283,7 @@ export class Emulator extends React.Component<EmulatorProps, {}> {
onClick={this.onStartOverClick}
/>
)}

<button
className={`${styles.saveIcon} ${styles.toolbarIcon || ''}`}
onClick={this.onExportTranscriptClick}
Expand Down Expand Up @@ -368,13 +376,14 @@ export class Emulator extends React.Component<EmulatorProps, {}> {
break;
}

case SameUserId:
case SameUserId: {
this.props.trackEvent('conversation_restart', {
userId: 'same',
});
// start conversation with new convo id
await this.startNewConversation(undefined, true, false);
break;
}

default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
flex-wrap: nowrap;
align-items: center;
height: 29px;
padding-left: 12px;
background-color: var(--toolbar-bg);
border-top: var(--toolbar-border);
border-bottom: var(--toolbar-border-bottom);
Expand Down
6 changes: 6 additions & 0 deletions packages/app/shared/src/types/serverSettingsTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export interface FrameworkSettings {
collectUsageData?: boolean;
// Digest of k/v pairs for integrity
hash?: string;
// GUID set by the user
userGUID?: string;
// use custom user id
useCustomId?: boolean;
}

export interface WindowStateSettings {
Expand Down Expand Up @@ -138,6 +142,8 @@ export const frameworkDefault: FrameworkSettings = {
usePrereleases: false,
autoUpdate: true,
collectUsageData: false,
userGUID: '',
useCustomId: false,
};

export const windowStateDefault: WindowStateSettings = {
Expand Down