-
Notifications
You must be signed in to change notification settings - Fork 378
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add runtime settings page and eject (#2572)
* add new runtime settings page * add popup modal for pickign runtime * add ability for plugins to specify a runtime template * add sample runtime to localPublish add actual file copy mechanism * handle success and failure of runtime injection * respect enableCustomRuntime * pull real runtime code * eject real code update the readme file included in the runtime * change the path of the declarative assets relative to the runtime * update code copying to reflect new asset locations * cleanup * allow parameters to be included in start command * update readme with new info about additional plugin APIs * change schema of settings * little bit more error correction * fix issue with field binding * fix form behaviors * address comments from andy Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
- Loading branch information
Showing
26 changed files
with
721 additions
and
100 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,44 @@ | ||
## Bot Project | ||
## Bot Runtime | ||
Bot project is the launcher project for the bots written in declarative form (JSON), using the Composer, for the Bot Framework SDK. | ||
This same code is used by Composer to start the bot locally for testing. | ||
|
||
## Instructions for setting up the Bot Project runtime | ||
The Bot Project is a regular Bot Framework SDK V4 project. Before you can launch it from the emulator, you need to make sure you can run the bot. | ||
## Instructions for using and customizing the bot runtime | ||
|
||
Composer can be configured to use a customized copy of this runtime. | ||
A copy of it can be added to your project automatically by using the "runtime settings" page in Composer. | ||
|
||
The Bot Project is a regular Bot Framework SDK V4 project. You can modify the code of this project | ||
and continue to use it with Composer. | ||
|
||
* Add additional middleware | ||
* Customize the state storage system | ||
* Add custom dialog classes | ||
|
||
### Prerequisite: | ||
* Install .Netcore 3.1 | ||
|
||
### Commands: | ||
### Build: | ||
|
||
* from root folder | ||
* cd BotProject | ||
* cd Templates/CSharp | ||
* cd [my bot folder]/runtime | ||
* dotnet user-secrets init // init the user secret id | ||
* dotnet build // build | ||
|
||
|
||
### Run from Command line: | ||
* cd [my bot folder]/runtime | ||
* dotnet run // start the bot | ||
* It will start a web server and listening at http://localhost:3979. | ||
|
||
### Run with Composer | ||
|
||
Open your bot project in Composer. Navigate to the runtime settings tab. | ||
|
||
Set the path to runtime to the full path to your runtime code. Customize the start command as necessary. | ||
|
||
The "Start Bot" button will now use your customized runtime. | ||
|
||
Note: the application code must be built and ready to run before Composer can manage it. | ||
|
||
### Test bot | ||
* You can set you emulator to connect to http://localhost:3979/api/messages. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
Composer/packages/client/src/pages/setting/runtime-settings/ejectModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
/** @jsx jsx */ | ||
import { jsx } from '@emotion/core'; | ||
import { useEffect, useMemo, useState, useContext } from 'react'; | ||
import { Dialog, DialogType } from 'office-ui-fabric-react/lib/Dialog'; | ||
import formatMessage from 'format-message'; | ||
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; | ||
import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; | ||
import { ChoiceGroup, IChoiceGroupOption } from 'office-ui-fabric-react/lib/ChoiceGroup'; | ||
|
||
import { StoreContext } from '../../../store'; | ||
|
||
import { modalControlGroup } from './style'; | ||
|
||
export interface EjectModalProps { | ||
ejectRuntime: (templateKey: string) => void; | ||
hidden: boolean; | ||
closeModal: () => void; | ||
} | ||
|
||
export const EjectModal: React.FC<EjectModalProps> = props => { | ||
const [selectedTemplate, setSelectedTemplate] = useState<string | undefined>(); | ||
const { state, actions } = useContext(StoreContext); | ||
const { runtimeTemplates } = state; | ||
|
||
useEffect(() => { | ||
actions.getRuntimeTemplates(); | ||
}, []); | ||
|
||
const availableRuntimeTemplates = useMemo(() => { | ||
return runtimeTemplates.map(t => { | ||
return { | ||
text: t.name, | ||
key: t.key, | ||
}; | ||
}); | ||
}, [runtimeTemplates]); | ||
|
||
const selectTemplate = (ev, item?: IChoiceGroupOption) => { | ||
if (item) { | ||
setSelectedTemplate(item.key); | ||
} | ||
}; | ||
|
||
const doEject = () => { | ||
if (selectedTemplate) { | ||
props.ejectRuntime(selectedTemplate); | ||
} | ||
}; | ||
|
||
return ( | ||
<Dialog | ||
hidden={props.hidden} | ||
onDismiss={props.closeModal} | ||
dialogContentProps={{ | ||
type: DialogType.normal, | ||
title: formatMessage('Add custom runtime'), | ||
subText: formatMessage('Select runtime version to add'), | ||
}} | ||
modalProps={{ | ||
isBlocking: false, | ||
}} | ||
> | ||
<div css={modalControlGroup}> | ||
<ChoiceGroup options={availableRuntimeTemplates} onChange={selectTemplate} required={true} /> | ||
</div> | ||
<DialogFooter> | ||
<DefaultButton onClick={props.closeModal}>Cancel</DefaultButton> | ||
<PrimaryButton onClick={doEject} disabled={!selectedTemplate}> | ||
{formatMessage('Okay')} | ||
</PrimaryButton> | ||
</DialogFooter> | ||
</Dialog> | ||
); | ||
}; |
123 changes: 123 additions & 0 deletions
123
Composer/packages/client/src/pages/setting/runtime-settings/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
/** @jsx jsx */ | ||
import { jsx } from '@emotion/core'; | ||
import { useState, useContext } from 'react'; | ||
import formatMessage from 'format-message'; | ||
import { Toggle } from 'office-ui-fabric-react/lib/Toggle'; | ||
import { TextField } from 'office-ui-fabric-react/lib/TextField'; | ||
import { Link } from 'office-ui-fabric-react/lib/Link'; | ||
import { RouteComponentProps } from '@reach/router'; | ||
|
||
import { LoadingSpinner } from '../../../components/LoadingSpinner'; | ||
import { StoreContext } from '../../../store'; | ||
|
||
import { EjectModal } from './ejectModal'; | ||
import { | ||
breathingSpace, | ||
runtimeSettingsStyle, | ||
runtimeControls, | ||
runtimeControlsTitle, | ||
runtimeToggle, | ||
controlGroup, | ||
} from './style'; | ||
|
||
export const RuntimeSettings: React.FC<RouteComponentProps> = () => { | ||
const { state, actions } = useContext(StoreContext); | ||
const { botName, settings, projectId } = state; | ||
const [formDataErrors, setFormDataErrors] = useState({ command: '', path: '' }); | ||
const [ejectModalVisible, setEjectModalVisible] = useState(false); | ||
|
||
const changeEnabled = (_, on) => { | ||
actions.setSettings(projectId, botName, { ...settings, runtime: { ...settings.runtime, customRuntime: on } }); | ||
}; | ||
|
||
const updateSetting = field => (e, newValue) => { | ||
let valid = true; | ||
let error = 'There was an error'; | ||
if (newValue === '') { | ||
valid = false; | ||
error = 'This is a required field.'; | ||
} | ||
|
||
actions.setSettings(projectId, botName, { ...settings, runtime: { ...settings.runtime, [field]: newValue } }); | ||
|
||
if (valid) { | ||
setFormDataErrors({ ...formDataErrors, [field]: '' }); | ||
} else { | ||
setFormDataErrors({ ...formDataErrors, [field]: error }); | ||
} | ||
}; | ||
|
||
const header = () => ( | ||
<div css={runtimeControls}> | ||
<h1 css={runtimeControlsTitle}>{formatMessage('Bot runtime settings')}</h1> | ||
<p>{formatMessage('Configure Composer to start your bot using runtime code you can customize and control.')}</p> | ||
</div> | ||
); | ||
|
||
const toggle = () => ( | ||
<div css={runtimeToggle}> | ||
<Toggle | ||
label={formatMessage('Use custom runtime')} | ||
inlineLabel | ||
onChange={changeEnabled} | ||
checked={settings.runtime && settings.runtime.customRuntime === true} | ||
/> | ||
</div> | ||
); | ||
|
||
const showEjectModal = () => { | ||
setEjectModalVisible(true); | ||
}; | ||
const closeEjectModal = () => { | ||
setEjectModalVisible(false); | ||
}; | ||
|
||
const ejectRuntime = async (templateKey: string) => { | ||
await actions.ejectRuntime(projectId, templateKey); | ||
closeEjectModal(); | ||
}; | ||
|
||
return botName ? ( | ||
<div css={runtimeSettingsStyle}> | ||
{header()} | ||
{toggle()} | ||
<div css={controlGroup}> | ||
<TextField | ||
label={formatMessage('Runtime code location')} | ||
value={settings.runtime ? settings.runtime.path : ''} | ||
styles={name} | ||
required | ||
onChange={updateSetting('path')} | ||
errorMessage={formDataErrors.path} | ||
data-testid="runtimeCodeLocation" | ||
disabled={!settings.runtime || !settings.runtime.customRuntime} | ||
/> | ||
{formatMessage('Or: ')} | ||
<Link | ||
onClick={showEjectModal} | ||
disabled={!settings.runtime || !settings.runtime.customRuntime} | ||
css={breathingSpace} | ||
> | ||
{formatMessage('Get a new copy of the runtime code')} | ||
</Link> | ||
|
||
<TextField | ||
label={formatMessage('Start command')} | ||
value={settings.runtime ? settings.runtime.command : ''} | ||
styles={name} | ||
required | ||
onChange={updateSetting('command')} | ||
errorMessage={formDataErrors.command} | ||
data-testid="runtimeCommand" | ||
disabled={!settings.runtime || !settings.runtime.customRuntime} | ||
/> | ||
</div> | ||
<EjectModal hidden={!ejectModalVisible} closeModal={closeEjectModal} ejectRuntime={ejectRuntime} /> | ||
</div> | ||
) : ( | ||
<LoadingSpinner /> | ||
); | ||
}; |
51 changes: 51 additions & 0 deletions
51
Composer/packages/client/src/pages/setting/runtime-settings/style.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { css } from '@emotion/core'; | ||
import { FontWeights, FontSizes } from 'office-ui-fabric-react/lib/Styling'; | ||
export const runtimeSettingsStyle = css` | ||
position: absolute; | ||
top: 0; | ||
bottom: 0; | ||
left: 0; | ||
right: 0; | ||
padding: 1rem; | ||
display: flex; | ||
flex-direction: column; | ||
box-sizing: border-box; | ||
`; | ||
|
||
export const runtimeControls = css` | ||
margin-bottom: 18px; | ||
& > h1 { | ||
margin-top: 0; | ||
} | ||
`; | ||
|
||
export const runtimeToggle = css` | ||
display: flex; | ||
& > * { | ||
margin-right: 2rem; | ||
} | ||
`; | ||
|
||
export const controlGroup = css` | ||
border: 1px solid rgb(237, 235, 233); | ||
padding: 0.5rem 1rem 1rem 1rem; | ||
`; | ||
|
||
export const modalControlGroup = css` | ||
border: 1px solid rgb(237, 235, 233); | ||
padding: 0.5rem 1rem 1rem 1rem; | ||
`; | ||
|
||
export const runtimeControlsTitle = css` | ||
font-size: ${FontSizes.xLarge}; | ||
font-weight: ${FontWeights.semibold}; | ||
`; | ||
|
||
export const breathingSpace = css` | ||
margin-bottom: 1rem; | ||
`; |
Oops, something went wrong.