Skip to content

Commit

Permalink
Merge c90cdc7 into d9794a0
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeffders committed Nov 20, 2018
2 parents d9794a0 + c90cdc7 commit d98d59e
Show file tree
Hide file tree
Showing 15 changed files with 240 additions and 91 deletions.
@@ -1,7 +1,7 @@
.main {
min-width: 464px;
width: 464px;
max-height: 439px;
max-height: 454px;

a {
color: var(--link-color);
Expand All @@ -25,25 +25,6 @@
position: relative;
}

.checkbox-anchor-container {
display: flex;

> .link {
background-color: transparent;
border: 1px solid transparent;
color: var(--focused-selected-list-item-bg);
cursor: pointer;
font-weight: 600;
padding: 0;
text-decoration: none;
white-space: normal;

&:hover {
text-decoration: underline;
}
}
}

.endpoint-warning {
height: 11px;
color: var(--warning-outline);
Expand Down
Expand Up @@ -31,8 +31,10 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

import { mount, shallow } from 'enzyme';
import { mount } from 'enzyme';
import * as React from 'react';
import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl';
import { ActiveBotHelper } from '../../helpers/activeBotHelper';
import { BotCreationDialog, BotCreationDialogState } from './botCreationDialog';

jest.mock('./botCreationDialog.scss', () => ({}));
Expand All @@ -43,14 +45,23 @@ jest.mock('../../../utils', () => ({
}
}));

jest.mock('../../helpers/activeBotHelper', () => ({
ActiveBotHelper: {
confirmAndCreateBot: async () => true
}
}));

describe('BotCreationDialog tests', () => {
let testWrapper;
beforeEach(() => {
testWrapper = mount(<BotCreationDialog/>);
});

it('should render without throwing an error', () => {
const testWrapper = mount(<BotCreationDialog/>);
expect(testWrapper.find(BotCreationDialog)).not.toBe(null);
});

it('should generate a new bot secret when checkbox is toggled', () => {
const testWrapper = shallow(<BotCreationDialog/>);
const initialState = testWrapper.state as Partial<BotCreationDialogState>;
expect(initialState.secret).toBeFalsy();
// toggle on encryption
Expand Down Expand Up @@ -79,7 +90,6 @@ describe('BotCreationDialog tests', () => {
// });

it('should execute a window copy command when copy is clicked', () => {
const testWrapper = mount(<BotCreationDialog/>);
testWrapper.instance().setState({ encryptKey: true });
// mock window functions
const backupExec = window.document.execCommand;
Expand All @@ -102,7 +112,6 @@ describe('BotCreationDialog tests', () => {
});

it('should set state via input change handlers', () => {
const testWrapper = shallow(<BotCreationDialog/>);
const mockEvent = { target: { value: 'someEndpoint', dataset: { prop: 'endpoint' } } };
(testWrapper.instance() as any).onInputChange(mockEvent as any);

Expand All @@ -125,10 +134,61 @@ describe('BotCreationDialog tests', () => {
expect(state.bot.name).toBe('someName');
});

it('should validate channelService on toggle channelService checkbox', () => {
let state = testWrapper.state() as Partial<BotCreationDialogState>;

// initially undefined
expect((state.endpoint as any).channelService).toBe(undefined);

// checked
const mockCheck = { target: { checked: true } };
(testWrapper.instance() as any).onChannelServiceChange(mockCheck as any);

state = testWrapper.state() as Partial<BotCreationDialogState>;
expect((state.endpoint as any).channelService).toBe('https://botframework.azure.us');

// unchecked
mockCheck.target.checked = false;
(testWrapper.instance() as any).onChannelServiceChange(mockCheck as any);

state = testWrapper.state() as Partial<BotCreationDialogState>;
expect((state.endpoint as any).channelService).toBe('');
});

it('should validate the endpoint', () => {
const testWrapper = shallow(<BotCreationDialog/>);
expect((testWrapper.instance() as any).validateEndpoint('http://localhost:3000/api/messages')).toBe('');
expect((testWrapper.instance() as any).validateEndpoint('http://localhost:3000'))
.toBe(`Please include route if necessary: "/api/messages"`);
});

it('should save and connect', async () => {
const instance = testWrapper.instance();
const remoteCallSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue('some/path');
const confirmAndCreateSpy = jest.spyOn(ActiveBotHelper, 'confirmAndCreateBot').mockResolvedValue(true);
await instance.onSaveAndConnect();
expect(remoteCallSpy).toHaveBeenCalledWith('shell:showExplorer-save-dialog', {
buttonLabel: 'Save',
defaultPath: 'some/path',
filters: [{ 'extensions': ['bot'], 'name': 'Bot Files' }],
showsTagField: false,
title: 'Save as'
});
expect(confirmAndCreateSpy).toHaveBeenCalledWith({
'description': '',
'name': '',
'overrides': null,
'padlock': '',
'path': 'some/path',
'services': [{
'appId': '',
'appPassword': '',
'channelService': undefined,
'endpoint': '',
'id': jasmine.any(String),
'name': '',
'type': 'endpoint'
}],
'version': '2.0'
}, null );
});
});
Expand Up @@ -135,7 +135,10 @@ export class BotCreationDialog extends React.Component<{}, BotCreationDialogStat
type="password"
value={ endpoint.appPassword }/>
</Row>

<Checkbox
label="Azure for US Government"
onChange={ this.onChannelServiceChange }
/>
<Row align={ RowAlignment.Bottom }>
<Checkbox
className={ styles.encryptKeyCheckBox }
Expand Down Expand Up @@ -211,6 +214,12 @@ export class BotCreationDialog extends React.Component<{}, BotCreationDialogStat
}
}

private onChannelServiceChange = (event: ChangeEvent<HTMLInputElement>) => {
const { checked } = event.target;
const channelService = checked ? 'https://botframework.azure.us' : '';
this.setState({ endpoint: { ...this.state.endpoint, ...{ channelService: channelService } } } as any);
}

private onCancel = () => {
DialogService.hideDialog();
}
Expand Down Expand Up @@ -259,11 +268,11 @@ export class BotCreationDialog extends React.Component<{}, BotCreationDialogStat
CommandServiceImpl.remoteCall(SharedConstants.Commands.Electron.OpenExternal, url).catch();
}

private onSaveAndConnect = async (e) => {
private onSaveAndConnect = async () => {
try {
const path = await this.showBotSaveDialog();
if (path) {
this.performCreate(path);
await this.performCreate(path);
} else {
// user cancelled out of the save dialog
console.log('Bot creation save dialog was cancelled.');
Expand All @@ -273,7 +282,7 @@ export class BotCreationDialog extends React.Component<{}, BotCreationDialogStat
}
}

private performCreate = (botPath: string) => {
private performCreate = async (botPath: string) => {
const endpoint: IEndpointService = {
type: this.state.endpoint.type,
name: this.state.endpoint.endpoint.trim(),
Expand All @@ -282,6 +291,7 @@ export class BotCreationDialog extends React.Component<{}, BotCreationDialogStat
appPassword: this.state.endpoint.appPassword.trim(),
endpoint: this.state.endpoint.endpoint.trim()
};
(endpoint as any).channelService = (this.state.endpoint as any).channelService;

const bot: BotConfigWithPath = BotConfigWithPathImpl.fromJSON({
...this.state.bot,
Expand All @@ -293,13 +303,15 @@ export class BotCreationDialog extends React.Component<{}, BotCreationDialogStat

const secret = this.state.encryptKey && this.state.secret ? this.state.secret : null;

ActiveBotHelper.confirmAndCreateBot(bot, secret)
.then(() => DialogService.hideDialog())
.catch(err => {
const errMsg = `Error during confirm and create bot: ${err}`;
const notification = newNotification(errMsg);
store.dispatch(beginAdd(notification));
});
try {
await ActiveBotHelper.confirmAndCreateBot(bot, secret);
} catch (err) {
const errMsg = `Error during confirm and create bot: ${err}`;
const notification = newNotification(errMsg);
store.dispatch(beginAdd(notification));
} finally {
DialogService.hideDialog();
}
}

private showBotSaveDialog = async (): Promise<any> => {
Expand Down
Expand Up @@ -43,7 +43,7 @@
position: relative;
padding-right: 20px;
margin-top: 28px;
display: inline-block;
display: block;
text-decoration: none;
&::before {
content: '';
Expand Down
Expand Up @@ -77,6 +77,25 @@ describe('The EndpointExplorer component should', () => {
expect(instance.state.endpointService.name).toBe('a name');
});

it('should update channelService when toggle us gov checkbox', () => {
const instance = node.instance();

// initially undefined
expect((instance.state.endpointService as any).channelService).toBe(undefined);

// checked
const mockCheck = { target: { checked: true } };
instance.onChannelServiceChange(mockCheck as any);

expect((instance.state.endpointService as any).channelService).toBe('https://botframework.azure.us');

// unchecked
mockCheck.target.checked = false;
instance.onChannelServiceChange(mockCheck as any);

expect((instance.state.endpointService as any).channelService).toBe('');
});

it('should set an error when a required field is null', () => {
const instance = node.instance();
const mockEvent = { target: { hasAttribute: () => true, value: '', dataset: { prop: 'name' } } };
Expand Down
Expand Up @@ -37,7 +37,8 @@ import {
DialogFooter,
PrimaryButton,
Row,
TextField
TextField,
Checkbox
} from '@bfemulator/ui-react';
import { BotService, EndpointService } from 'botframework-config/lib/models';
import { IBotService, IEndpointService } from 'botframework-config/lib/schema';
Expand Down Expand Up @@ -68,8 +69,9 @@ export interface EndpointEditorState {
endpointWarning: string;
}

const title = 'Add a Endpoint for your bot';
const detailedDescription = 'You can add a endpoint that you use to communicate to an instance of your bot';
const title = 'Add an Endpoint for your bot';
const detailedDescription = 'You can add an endpoint that you use to communicate to an instance of your bot';
const usGovernmentAzureChannelService = 'https://botframework.azure.us';

export class EndpointEditor extends Component<EndpointEditorProps, EndpointEditorState> {
public state: EndpointEditorState = {} as EndpointEditorState;
Expand Down Expand Up @@ -106,6 +108,10 @@ export class EndpointEditor extends Component<EndpointEditorProps, EndpointEdito
return controllerRegEx.test(endpoint) ? '' : `Please include route if necessary: "/api/messages"`;
}

private static isUsGov(channelService: string): boolean {
return channelService === usGovernmentAzureChannelService;
}

constructor(props: EndpointEditorProps, state: EndpointEditorState) {
super(props, state);
const { endpointService, botService } = props;
Expand Down Expand Up @@ -138,6 +144,7 @@ export class EndpointEditor extends Component<EndpointEditorProps, EndpointEdito
const { tenantId = '', subscriptionId = '', resourceGroup = '', serviceName = '' } = botService;
const hasBotService = tenantId || subscriptionId || resourceGroup || serviceName;
const valid = !!endpoint && !!name;
const isUsGov = EndpointEditor.isUsGov((endpointService as any).channelService);
return (
<Dialog title={ title } detailedDescription={ detailedDescription }
cancel={ this.onCancelClick }>
Expand Down Expand Up @@ -175,6 +182,11 @@ export class EndpointEditor extends Component<EndpointEditorProps, EndpointEdito
onChange={ this.onEndpointInputChange }
label="Application Password" required={ false }
/>
<Checkbox
label="Azure for US Government"
checked={ isUsGov }
onChange={ this.onChannelServiceChange }
/>
<a href="javascript:void(0)"
className={ `${styles.arrow} ${hasBotService ? styles.arrowExpanded : ''}` }
onClick={ this.onABSLinkClick }>
Expand Down Expand Up @@ -257,10 +269,16 @@ export class EndpointEditor extends Component<EndpointEditorProps, EndpointEdito
}
}

private onChannelServiceChange = (event: ChangeEvent<HTMLInputElement>) => {
const { checked } = event.target;
const { endpointService } = this.state;
(endpointService as any).channelService = checked ? usGovernmentAzureChannelService : '';
this.setState({ endpointService } as any);
}

private onBotInputChange = (event: ChangeEvent<HTMLInputElement>): void => {
const { value } = event.target;
const { prop } = event.target.dataset;

const trimmedValue = value.trim();
let { botService } = this.state;
if (!botService) {
Expand Down
3 changes: 2 additions & 1 deletion packages/app/main/src/commands/botCommands.ts
Expand Up @@ -203,7 +203,8 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) {
botId: endpoint.id,
botUrl: endpoint.endpoint,
msaAppId: endpoint.appId,
msaPassword: endpoint.appPassword
msaPassword: endpoint.appPassword,
channelService: (endpoint as any).channelService
}
);
});
Expand Down
9 changes: 9 additions & 0 deletions packages/emulator/core/src/authEndpoints.ts
Expand Up @@ -37,6 +37,15 @@ export const authentication = {
botTokenAudience: 'https://api.botframework.com',
};

export const usGovernmentAuthentication = {
channelService: 'https://botframework.azure.us',
tokenEndpoint: 'https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/oauth2/v2.0/token',
openIdMetadata: 'https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0/.well-known/openid-configuration',
botTokenAudience: 'https://api.botframework.us',
tokenIssuerV1: 'https://sts.windows.net/cab8a31a-1906-4287-a0d8-4eef66b95f6e/',
tokenIssuerV2: 'https://login.microsoftonline.us/cab8a31a-1906-4287-a0d8-4eef66b95f6e/v2.0'
};

export const v31Authentication = {
tokenIssuer: 'https://sts.windows.net/d6d49420-f39b-4df7-a1dc-d59a935871db/',
};
Expand Down
Expand Up @@ -32,7 +32,7 @@ describe('The botStateMiddleware', () => {
emulator = { facilities: { logger: { logMessage: () => true } } } as any;
emulator.facilities.conversations = new ConversationSet();
user = { id: '321', name: 'a user' };
const endpoint = new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, {});
const endpoint = new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, '', {});
conversation = emulator.facilities.conversations.newConversation(emulator, endpoint, user);
botState = new BotState(emulator, 256);
jest.spyOn(Date, 'now').mockReturnValue({ toString: () => '123456' });
Expand Down
Expand Up @@ -43,7 +43,7 @@ describe('The conversations middleware', () => {
it('should create a new conversation', () => {
const bot = { role: 'bot', name: 'thebot', id: '456' };
const req = {
botEndpoint: new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, {}),
botEndpoint: new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, '', {}),
body: {
members: [{ id: '123', name: 'emulator', role: 'user' }],
bot,
Expand Down Expand Up @@ -73,7 +73,7 @@ describe('The conversations middleware', () => {
// create the conversation with an activity
const bot = { role: 'bot', name: 'thebot', id: '456' };
let req: any = {
botEndpoint: new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, {}),
botEndpoint: new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, '', {}),
body: {
members: [{ id: '123', name: 'emulator', role: 'user' }],
bot,
Expand Down Expand Up @@ -499,7 +499,7 @@ function createConversationUtil(emulator: BotEmulator): Conversation {
// create the conversation with an activity
const bot = { role: 'bot', name: 'thebot', id: '456' };
let req: any = {
botEndpoint: new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, {}),
botEndpoint: new BotEndpoint('12', '123', 'http://localhost:12345', '', '', false, '', {}),
body: {
members: [{ id: '123', name: 'emulator', role: 'user' }],
bot,
Expand Down

0 comments on commit d98d59e

Please sign in to comment.