From 577d22a77c0c8b7796d95db03eeb8e5c13c44f44 Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Thu, 29 Apr 2021 22:38:44 -0700 Subject: [PATCH 01/10] First pass to add OrchSkillDialog to local skills --- .../AddRemoteSkillModal/Orchestractor.tsx | 51 ++++++++----- .../OrchestratorForSkillsDialog.tsx | 76 +++++++++++++++++++ .../client/src/pages/design/Modals.tsx | 3 + .../client/src/recoilModel/atoms/appState.ts | 5 ++ .../src/recoilModel/dispatchers/project.ts | 2 + 5 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx index 1c2d137298..0b88ac06b8 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx @@ -16,7 +16,14 @@ import { importOrchestractor } from './helper'; const learnMoreUrl = 'https://aka.ms/bf-composer-docs-publish-bot'; -export const Orchestractor = (props) => { +interface OrchestractorProps { + projectId: string; + onSubmit: (event: any, userSelected?: boolean) => Promise; + onBack: (event: any) => void; + hideBackButton?: boolean; +} + +const Orchestractor: React.FC = (props) => { const { projectId, onSubmit, onBack } = props; const [enableOrchestrator, setEnableOrchestrator] = useState(true); const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); @@ -40,23 +47,33 @@ export const Orchestractor = (props) => { /> - - - - { - onSubmit(event, enableOrchestrator); - if (enableOrchestrator) { - // TODO. show notification - // download orchestrator first - importOrchestractor(projectId, reloadProject, setApplicationLevelError); - // TODO. update notification - } - }} - /> - + + {!props.hideBackButton && } + + + + + { + onSubmit(event, enableOrchestrator); + if (enableOrchestrator) { + // TODO. show notification + // download orchestrator first + importOrchestractor(projectId, reloadProject, setApplicationLevelError); + // TODO. update notification + } + }} + /> + + ); }; + +Orchestractor.defaultProps = { + hideBackButton: false, +}; + +export { OrchestractorProps, Orchestractor }; diff --git a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx new file mode 100644 index 0000000000..a7072216c3 --- /dev/null +++ b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { DialogTypes, DialogWrapper } from '@bfc/ui-shared/lib/components/DialogWrapper'; +import { SDKKinds } from '@botframework-composer/types'; +import formatMessage from 'format-message'; +import React, { useMemo } from 'react'; +import { useRecoilState, useRecoilValue } from 'recoil'; + +import { + designPageLocationState, + dispatcherState, + localeState, + orchestratorForSkillsDialogState, + rootBotProjectIdSelector, +} from '../../recoilModel'; +import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; +import { Orchestractor } from '../AddRemoteSkillModal/Orchestractor'; + +export const OrchestratorForSkillsDialog = (props) => { + const [showOrchestratorDialog, setShowOrchestratorDialog] = useRecoilState(orchestratorForSkillsDialogState); + const rootProjectId = useRecoilValue(rootBotProjectIdSelector) || ''; + const { dialogId } = useRecoilValue(designPageLocationState(rootProjectId)); + const locale = useRecoilValue(localeState(rootProjectId)); + const curRecognizers = useRecoilValue(recognizersSelectorFamily(rootProjectId)); + + const { updateRecognizer } = useRecoilValue(dispatcherState); + + const hasOrchestrator = useMemo(() => { + const fileName = `${dialogId}.${locale}.lu.dialog`; + return curRecognizers.some((f) => f.id === fileName && f.content.$kind === SDKKinds.OrchestratorRecognizer); + }, [curRecognizers, dialogId, locale]); + + const handleOrchestratorSubmit = async (event: any, enable?: boolean) => { + event.preventDefault(); + if (enable) { + // update recognizor type to orchestrator + await updateRecognizer(rootProjectId, dialogId, SDKKinds.OrchestratorRecognizer); + } + setShowOrchestratorDialog(false); + }; + + const isVisible = () => { + if (showOrchestratorDialog) { + if (hasOrchestrator) { + setShowOrchestratorDialog(false); + return false; + } + return true; + } + return false; + }; + + const onDismissHandler = (event: any) => { + setShowOrchestratorDialog(false); + }; + + return ( + + { + setShowOrchestratorDialog(false); + }} + onSubmit={handleOrchestratorSubmit} + /> + + ); +}; diff --git a/Composer/packages/client/src/pages/design/Modals.tsx b/Composer/packages/client/src/pages/design/Modals.tsx index b8414a72f5..6b99ab5176 100644 --- a/Composer/packages/client/src/pages/design/Modals.tsx +++ b/Composer/packages/client/src/pages/design/Modals.tsx @@ -27,6 +27,7 @@ import { undoFunctionState } from '../../recoilModel/undo/history'; import { CreationFlowStatus } from '../../constants'; import { RepairSkillModalOptionKeys } from '../../components/RepairSkillModal'; import { createQnAOnState, exportSkillModalInfoState } from '../../recoilModel/atoms/appState'; +import { OrchestratorForSkillsDialog } from '../../components/Orchestrator/OrchestratorForSkillsDialog'; import CreationModal from './creationModal'; @@ -170,6 +171,8 @@ const Modals: React.FC = ({ projectId = '' }) => { onSubmit={handleCreateQnA} /> + + {displaySkillManifestNameIdentifier && ( ({ key: getFullyQualifiedKey('warnAboutDotNetState'), default: false, }); + +export const orchestratorForSkillsDialogState = atom({ + key: getFullyQualifiedKey('orchestratorForSkillsDialogState'), + default: false, +}); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 41ff17f022..74153894a5 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -38,6 +38,7 @@ import { showCreateQnAFromUrlDialogState, warnAboutDotNetState, settingsState, + orchestratorForSkillsDialogState, } from '../atoms'; import { botRuntimeOperationsSelector, rootBotProjectIdSelector } from '../selectors'; import { mergePropertiesManagedByRootBot, postRootBotCreation } from '../../recoilModel/dispatchers/utils/project'; @@ -165,6 +166,7 @@ export const projectDispatcher = () => { set(botProjectIdsState, (current) => [...current, projectId]); await dispatcher.addLocalSkillToBotProjectFile(projectId); navigateToSkillBot(rootBotProjectId, projectId, mainDialog); + callbackHelpers.set(orchestratorForSkillsDialogState, true); } catch (ex) { handleProjectFailure(callbackHelpers, ex); } finally { From 5e182d7fa9bf5fe25c0c3cf35844c16f2f5ac25e Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Fri, 30 Apr 2021 09:23:00 -0700 Subject: [PATCH 02/10] Rename Orchestractor to Orchestrator --- .../{Orchestractor.tsx => Orchestrator.tsx} | 158 +++++++++--------- .../AddRemoteSkillModal/SelectIntent.tsx | 8 +- .../components/AddRemoteSkillModal/helper.ts | 2 +- .../OrchestratorForSkillsDialog.tsx | 4 +- 4 files changed, 86 insertions(+), 86 deletions(-) rename Composer/packages/client/src/components/AddRemoteSkillModal/{Orchestractor.tsx => Orchestrator.tsx} (86%) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx similarity index 86% rename from Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx rename to Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx index 0b88ac06b8..1f2616f09c 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx @@ -1,79 +1,79 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import React, { useState } from 'react'; -import formatMessage from 'format-message'; -import { Link } from 'office-ui-fabric-react/lib/Link'; -import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox'; -import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; -import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; -import { useRecoilValue } from 'recoil'; - -import { dispatcherState } from '../../recoilModel'; -import { enableOrchestratorDialog } from '../../constants'; - -import { importOrchestractor } from './helper'; - -const learnMoreUrl = 'https://aka.ms/bf-composer-docs-publish-bot'; - -interface OrchestractorProps { - projectId: string; - onSubmit: (event: any, userSelected?: boolean) => Promise; - onBack: (event: any) => void; - hideBackButton?: boolean; -} - -const Orchestractor: React.FC = (props) => { - const { projectId, onSubmit, onBack } = props; - const [enableOrchestrator, setEnableOrchestrator] = useState(true); - const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); - const onChange = (ev, check) => { - setEnableOrchestrator(check); - }; - return ( - - -
- {enableOrchestratorDialog.content} - -
{formatMessage('Learn more about Orchestractor')}
- -
- -
- - - {!props.hideBackButton && } - - - - - { - onSubmit(event, enableOrchestrator); - if (enableOrchestrator) { - // TODO. show notification - // download orchestrator first - importOrchestractor(projectId, reloadProject, setApplicationLevelError); - // TODO. update notification - } - }} - /> - - - -
- ); -}; - -Orchestractor.defaultProps = { - hideBackButton: false, -}; - -export { OrchestractorProps, Orchestractor }; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React, { useState } from 'react'; +import formatMessage from 'format-message'; +import { Link } from 'office-ui-fabric-react/lib/Link'; +import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox'; +import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; +import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { useRecoilValue } from 'recoil'; + +import { dispatcherState } from '../../recoilModel'; +import { enableOrchestratorDialog } from '../../constants'; + +import { importOrchestrator } from './helper'; + +const learnMoreUrl = 'https://aka.ms/bf-composer-docs-publish-bot'; + +interface OrchestratorProps { + projectId: string; + onSubmit: (event: any, userSelected?: boolean) => Promise; + onBack: (event: any) => void; + hideBackButton?: boolean; +} + +const Orchestrator: React.FC = (props) => { + const { projectId, onSubmit, onBack } = props; + const [enableOrchestrator, setEnableOrchestrator] = useState(true); + const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); + const onChange = (ev, check) => { + setEnableOrchestrator(check); + }; + return ( + + +
+ {enableOrchestratorDialog.content} + +
{formatMessage('Learn more about Orchestractor')}
+ +
+ +
+ + + {!props.hideBackButton && } + + + + + { + onSubmit(event, enableOrchestrator); + if (enableOrchestrator) { + // TODO. show notification + // download orchestrator first + importOrchestrator(projectId, reloadProject, setApplicationLevelError); + // TODO. update notification + } + }} + /> + + + +
+ ); +}; + +Orchestrator.defaultProps = { + hideBackButton: false, +}; + +export { OrchestratorProps, Orchestrator }; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 5299b9ccb8..66a2701c6d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -22,7 +22,7 @@ import luWorker from '../../recoilModel/parsers/luWorker'; import { localeState, dispatcherState } from '../../recoilModel'; import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; -import { Orchestractor } from './Orchestractor'; +import { Orchestrator } from './Orchestrator'; const detailListContainer = css` width: 100%; @@ -248,7 +248,7 @@ export const SelectIntent: React.FC = (props) => { return ( {showOrchestratorDialog ? ( - { onUpdateTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); @@ -313,10 +313,10 @@ export const SelectIntent: React.FC = (props) => { onClick={(ev) => { if (pageIndex === 1) { if (hasOrchestrator) { - // skip orchestractor modal + // skip orchestrator modal handleSubmit(ev, true); } else { - // show orchestractor + // show orchestrator onUpdateTitle(enableOrchestratorDialog); setShowOrchestratorDialog(true); } diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 2e4f5787e7..54abbef5ee 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -13,7 +13,7 @@ const conflictConfirmationPrompt = formatMessage( 'This operation will overwrite changes made to previously imported files. Do you want to proceed?' ); -export const importOrchestractor = async (projectId: string, reloadProject, setApplicationLevelError) => { +export const importOrchestrator = async (projectId: string, reloadProject, setApplicationLevelError) => { const reqBody = { package: 'Microsoft.Bot.Builder.AI.Orchestrator', version: '4.13.0', diff --git a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx index a7072216c3..1f7c104667 100644 --- a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx +++ b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx @@ -15,7 +15,7 @@ import { rootBotProjectIdSelector, } from '../../recoilModel'; import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; -import { Orchestractor } from '../AddRemoteSkillModal/Orchestractor'; +import { Orchestrator } from '../AddRemoteSkillModal/Orchestrator'; export const OrchestratorForSkillsDialog = (props) => { const [showOrchestratorDialog, setShowOrchestratorDialog] = useRecoilState(orchestratorForSkillsDialogState); @@ -63,7 +63,7 @@ export const OrchestratorForSkillsDialog = (props) => { title={formatMessage('Enable Orchestrator')} onDismiss={onDismissHandler} > - { From f8d973d4aa95d2590319bab4c2544d5c8a4ac8ad Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Fri, 30 Apr 2021 10:45:11 -0700 Subject: [PATCH 03/10] update package to 4.13.1 --- .../client/src/components/AddRemoteSkillModal/helper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 54abbef5ee..dd6f6a8aeb 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -16,7 +16,7 @@ const conflictConfirmationPrompt = formatMessage( export const importOrchestrator = async (projectId: string, reloadProject, setApplicationLevelError) => { const reqBody = { package: 'Microsoft.Bot.Builder.AI.Orchestrator', - version: '4.13.0', + version: '4.13.1', source: 'https://api.nuget.org/v3/index.json', isUpdating: false, isPreview: false, From ed9fe929855dad6dd6e9338f8fea040b37d75ee6 Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Fri, 30 Apr 2021 12:09:29 -0700 Subject: [PATCH 04/10] Address PR comments --- .../AddRemoteSkillModal/Orchestrator.tsx | 46 ++++++++----------- .../OrchestratorForSkillsDialog.tsx | 15 ++---- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx index 1f2616f09c..08e0a66aac 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx @@ -6,7 +6,7 @@ import formatMessage from 'format-message'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox'; import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; -import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { PrimaryButton, DefaultButton, Button } from 'office-ui-fabric-react/lib/Button'; import { useRecoilValue } from 'recoil'; import { dispatcherState } from '../../recoilModel'; @@ -16,15 +16,15 @@ import { importOrchestrator } from './helper'; const learnMoreUrl = 'https://aka.ms/bf-composer-docs-publish-bot'; -interface OrchestratorProps { +type OrchestratorProps = { projectId: string; - onSubmit: (event: any, userSelected?: boolean) => Promise; - onBack: (event: any) => void; + onSubmit: (event: React.MouseEvent, userSelected?: boolean) => Promise; + onBack?: (event: React.MouseEvent) => void; hideBackButton?: boolean; -} +}; const Orchestrator: React.FC = (props) => { - const { projectId, onSubmit, onBack } = props; + const { projectId, onSubmit, onBack, hideBackButton = false } = props; const [enableOrchestrator, setEnableOrchestrator] = useState(true); const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); const onChange = (ev, check) => { @@ -47,33 +47,23 @@ const Orchestrator: React.FC = (props) => { /> - - {!props.hideBackButton && } - + {!hideBackButton && } - - - { - onSubmit(event, enableOrchestrator); - if (enableOrchestrator) { - // TODO. show notification - // download orchestrator first - importOrchestrator(projectId, reloadProject, setApplicationLevelError); - // TODO. update notification - } - }} - /> - + + { + onSubmit(event, enableOrchestrator); + if (enableOrchestrator) { + // TODO: Block UI from doing any work until import is complete. Item #7531 + importOrchestrator(projectId, reloadProject, setApplicationLevelError); + } + }} + /> ); }; -Orchestrator.defaultProps = { - hideBackButton: false, -}; - export { OrchestratorProps, Orchestrator }; diff --git a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx index 1f7c104667..0403670bdf 100644 --- a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx +++ b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx @@ -17,7 +17,7 @@ import { import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; import { Orchestrator } from '../AddRemoteSkillModal/Orchestrator'; -export const OrchestratorForSkillsDialog = (props) => { +export const OrchestratorForSkillsDialog = () => { const [showOrchestratorDialog, setShowOrchestratorDialog] = useRecoilState(orchestratorForSkillsDialogState); const rootProjectId = useRecoilValue(rootBotProjectIdSelector) || ''; const { dialogId } = useRecoilValue(designPageLocationState(rootProjectId)); @@ -40,7 +40,7 @@ export const OrchestratorForSkillsDialog = (props) => { setShowOrchestratorDialog(false); }; - const isVisible = () => { + const setVisibility = () => { if (showOrchestratorDialog) { if (hasOrchestrator) { setShowOrchestratorDialog(false); @@ -58,19 +58,12 @@ export const OrchestratorForSkillsDialog = (props) => { return ( - { - setShowOrchestratorDialog(false); - }} - onSubmit={handleOrchestratorSubmit} - /> + ); }; From 507b0e4337b68f0f7990ff4f4c3fda34447bb7a7 Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Fri, 30 Apr 2021 12:20:00 -0700 Subject: [PATCH 05/10] Use string constants where possible --- .../Orchestrator/OrchestratorForSkillsDialog.tsx | 11 ++++++----- Composer/packages/client/src/constants.ts | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx index 0403670bdf..c1707d84f3 100644 --- a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx +++ b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx @@ -3,10 +3,11 @@ import { DialogTypes, DialogWrapper } from '@bfc/ui-shared/lib/components/DialogWrapper'; import { SDKKinds } from '@botframework-composer/types'; -import formatMessage from 'format-message'; +import { Button } from 'office-ui-fabric-react/lib/components/Button/Button'; import React, { useMemo } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; +import { enableOrchestratorDialog } from '../../constants'; import { designPageLocationState, dispatcherState, @@ -31,7 +32,7 @@ export const OrchestratorForSkillsDialog = () => { return curRecognizers.some((f) => f.id === fileName && f.content.$kind === SDKKinds.OrchestratorRecognizer); }, [curRecognizers, dialogId, locale]); - const handleOrchestratorSubmit = async (event: any, enable?: boolean) => { + const handleOrchestratorSubmit = async (event: React.MouseEvent, enable?: boolean) => { event.preventDefault(); if (enable) { // update recognizor type to orchestrator @@ -51,7 +52,7 @@ export const OrchestratorForSkillsDialog = () => { return false; }; - const onDismissHandler = (event: any) => { + const onDismissHandler = (event: React.MouseEvent | undefined) => { setShowOrchestratorDialog(false); }; @@ -59,8 +60,8 @@ export const OrchestratorForSkillsDialog = () => { diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index 561cf2769c..97547ad5bc 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -359,7 +359,7 @@ export const enableOrchestratorDialog = { return formatMessage('Enable Orchestrator'); }, get subText() { - return formatMessage('Enable orchestrator as the recognizer at the root dialog to add this skill'); + return formatMessage('Enable Orchestrator as the recognizer at the root dialog to add this skill'); }, get content() { return formatMessage( From d9808942b99cf3e493d15d2950abce65ac3090d4 Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Fri, 30 Apr 2021 12:42:30 -0700 Subject: [PATCH 06/10] Rename Orchestrator component --- .../{Orchestrator.tsx => EnableOrchestrator.tsx} | 4 ++-- .../src/components/AddRemoteSkillModal/SelectIntent.tsx | 4 ++-- .../components/Orchestrator/OrchestratorForSkillsDialog.tsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename Composer/packages/client/src/components/AddRemoteSkillModal/{Orchestrator.tsx => EnableOrchestrator.tsx} (95%) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx similarity index 95% rename from Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx rename to Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx index 08e0a66aac..dbbf503136 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestrator.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx @@ -23,7 +23,7 @@ type OrchestratorProps = { hideBackButton?: boolean; }; -const Orchestrator: React.FC = (props) => { +const EnableOrchestrator: React.FC = (props) => { const { projectId, onSubmit, onBack, hideBackButton = false } = props; const [enableOrchestrator, setEnableOrchestrator] = useState(true); const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); @@ -66,4 +66,4 @@ const Orchestrator: React.FC = (props) => { ); }; -export { OrchestratorProps, Orchestrator }; +export { OrchestratorProps, EnableOrchestrator }; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 66a2701c6d..7d0e86281c 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -22,7 +22,7 @@ import luWorker from '../../recoilModel/parsers/luWorker'; import { localeState, dispatcherState } from '../../recoilModel'; import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; -import { Orchestrator } from './Orchestrator'; +import { EnableOrchestrator } from './EnableOrchestrator'; const detailListContainer = css` width: 100%; @@ -248,7 +248,7 @@ export const SelectIntent: React.FC = (props) => { return ( {showOrchestratorDialog ? ( - { onUpdateTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); diff --git a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx index c1707d84f3..0a8325f2fe 100644 --- a/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx +++ b/Composer/packages/client/src/components/Orchestrator/OrchestratorForSkillsDialog.tsx @@ -16,7 +16,7 @@ import { rootBotProjectIdSelector, } from '../../recoilModel'; import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; -import { Orchestrator } from '../AddRemoteSkillModal/Orchestrator'; +import { EnableOrchestrator } from '../AddRemoteSkillModal/EnableOrchestrator'; export const OrchestratorForSkillsDialog = () => { const [showOrchestratorDialog, setShowOrchestratorDialog] = useRecoilState(orchestratorForSkillsDialogState); @@ -64,7 +64,7 @@ export const OrchestratorForSkillsDialog = () => { title={enableOrchestratorDialog.title} onDismiss={onDismissHandler} > - + ); }; From 53df210562f19a159aafd89712d4b4575468c02f Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Fri, 30 Apr 2021 13:05:36 -0700 Subject: [PATCH 07/10] Fix another typo --- .../src/components/AddRemoteSkillModal/EnableOrchestrator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx index dbbf503136..d4fa9c69a9 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx @@ -36,7 +36,7 @@ const EnableOrchestrator: React.FC = (props) => {
{enableOrchestratorDialog.content} -
{formatMessage('Learn more about Orchestractor')}
+
{formatMessage('Learn more about Orchestrator')}
Date: Fri, 30 Apr 2021 14:30:45 -0700 Subject: [PATCH 08/10] Change link to bf-orchestrator --- .../src/components/AddRemoteSkillModal/EnableOrchestrator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx index d4fa9c69a9..5772763686 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx @@ -14,7 +14,7 @@ import { enableOrchestratorDialog } from '../../constants'; import { importOrchestrator } from './helper'; -const learnMoreUrl = 'https://aka.ms/bf-composer-docs-publish-bot'; +const learnMoreUrl = 'https://aka.ms/bf-orchestrator'; type OrchestratorProps = { projectId: string; From 82558e57736800a1a922f37672a861e7b6600ade Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Sat, 1 May 2021 11:26:32 -0700 Subject: [PATCH 09/10] Link and copy changes per PM and Design --- .../components/AddRemoteSkillModal/EnableOrchestrator.tsx | 2 +- Composer/packages/client/src/constants.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx index 5772763686..7580a8c639 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx @@ -14,7 +14,7 @@ import { enableOrchestratorDialog } from '../../constants'; import { importOrchestrator } from './helper'; -const learnMoreUrl = 'https://aka.ms/bf-orchestrator'; +const learnMoreUrl = 'https://aka.ms/LearnMoreOrchestrator'; type OrchestratorProps = { projectId: string; diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index 97547ad5bc..cb650526bf 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -356,14 +356,14 @@ export const selectIntentDialog = { export const enableOrchestratorDialog = { get title() { - return formatMessage('Enable Orchestrator'); + return formatMessage('Enable Orchestrator Recognizer'); }, get subText() { - return formatMessage('Enable Orchestrator as the recognizer at the root dialog to add this skill'); + return formatMessage('Enable Orchestrator as the recognizer for routing to other skills'); }, get content() { return formatMessage( - 'Multi-bot projects work best with the Orchestrator recognizer set at the root dialog. Orchestrator helps identify and dispatch user intents from the root dialog to the respective skill that can handle the intent. Orchestrator does not support entity extraction at the root dialog level.' + 'Multi-bot projects work best with the Orchestrator recognizer set at the dispatching dialog (typically the root dialog). Orchestrator helps identify and dispatch user intents from the root dialog to the respective skill that handles the intent. Orchestrator does not support entity extraction. If you plan to combine entity extraction and routing at the root dialog, use LUIS instead.' ); }, }; From 0a5517818c12c023c174bee20037b45347671ad4 Mon Sep 17 00:00:00 2001 From: Tai Chou Date: Sat, 1 May 2021 23:33:43 -0700 Subject: [PATCH 10/10] add unit test --- .../EnableOrchestrator.tsx | 3 +- .../OrchestratorForSkillsDialog.test.tsx | 98 +++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 Composer/packages/client/src/components/Orchestrator/__tests__/OrchestratorForSkillsDialog.test.tsx diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx index 7580a8c639..4925f50927 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/EnableOrchestrator.tsx @@ -31,7 +31,7 @@ const EnableOrchestrator: React.FC = (props) => { setEnableOrchestrator(check); }; return ( - +
{enableOrchestratorDialog.content} @@ -51,6 +51,7 @@ const EnableOrchestrator: React.FC = (props) => { { onSubmit(event, enableOrchestrator); diff --git a/Composer/packages/client/src/components/Orchestrator/__tests__/OrchestratorForSkillsDialog.test.tsx b/Composer/packages/client/src/components/Orchestrator/__tests__/OrchestratorForSkillsDialog.test.tsx new file mode 100644 index 0000000000..bdd6ef5181 --- /dev/null +++ b/Composer/packages/client/src/components/Orchestrator/__tests__/OrchestratorForSkillsDialog.test.tsx @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { act, getQueriesForElement, within } from '@botframework-composer/test-utils'; +import { SDKKinds } from '@botframework-composer/types'; +import * as React from 'react'; +import userEvent from '@testing-library/user-event'; + +import { renderWithRecoil } from '../../../../__tests__/testUtils/renderWithRecoil'; +import { + botProjectFileState, + botProjectIdsState, + designPageLocationState, + localeState, + orchestratorForSkillsDialogState, + projectMetaDataState, +} from '../../../recoilModel'; +import { recognizersSelectorFamily } from '../../../recoilModel/selectors/recognizers'; +import { OrchestratorForSkillsDialog } from '../OrchestratorForSkillsDialog'; +import { importOrchestrator } from '../../AddRemoteSkillModal/helper'; + +jest.mock('../../AddRemoteSkillModal/helper'); + +// mimick a project setup with a rootbot and dialog files, and provide conditions for orchestrator skill dialog to be visible +const makeInitialState = (set: any) => { + set(orchestratorForSkillsDialogState, true); + set(botProjectIdsState, ['rootBotId']); + set(botProjectFileState('rootBotId'), { content: { name: 'rootBot', skills: {} }, id: 'rootBot', lastModified: '' }); + set(projectMetaDataState('rootBotId'), { isRootBot: true, isRemote: false }); + set(designPageLocationState('rootBotId'), { dialogId: 'rootBotRootDialogId', focused: 'na', selected: 'na' }); + set(localeState('rootBotId'), 'en-us'); + set(recognizersSelectorFamily('rootBotId'), [ + { id: 'rootBotRootDialogId.en-us.lu.dialog', content: { $kind: SDKKinds.LuisRecognizer } }, + ]); +}; + +const orchestratorTestId = 'orchestrator-skill'; + +describe('', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should not open OrchestratorForSkillsDialog if orchestratorForSkillsDialogState is false', () => { + const { baseElement } = renderWithRecoil(, ({ set }) => { + makeInitialState(set); + set(orchestratorForSkillsDialogState, false); + }); + const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId); + expect(dialog).toBeNull(); + }); + + it('should not open OrchestratorForSkillsDialog if orchestrator already being used in root', () => { + const { baseElement } = renderWithRecoil(, ({ set }) => { + makeInitialState(set); + set(recognizersSelectorFamily('rootBotId'), [ + { id: 'rootBotRootDialogId.en-us.lu.dialog', content: { $kind: SDKKinds.OrchestratorRecognizer } }, + ]); + }); + const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId); + expect(dialog).toBeNull(); + }); + + it('open OrchestratorForSkillsDialog if orchestratorForSkillsDialogState and Orchestrator not used in Root Bot Root Dialog', () => { + const { baseElement } = renderWithRecoil(, ({ set }) => { + makeInitialState(set); + }); + const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId); + expect(dialog).toBeTruthy(); + }); + + it('should install Orchestrator package when user clicks Continue', async () => { + const { baseElement } = renderWithRecoil(, ({ set }) => { + makeInitialState(set); + }); + + await act(async () => { + userEvent.click(within(baseElement).getByTestId('import-orchestrator')); + }); + + expect(importOrchestrator).toBeCalledWith('rootBotId', expect.anything(), expect.anything()); + }); + + it('should not install Orchestrator package when user clicks skip', async () => { + const { baseElement } = renderWithRecoil(, ({ set }) => { + makeInitialState(set); + }); + + await act(async () => { + userEvent.click(await within(baseElement).findByText('Skip')); + }); + + const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId); + expect(dialog).toBeNull(); + + expect(importOrchestrator).toBeCalledTimes(0); + }); +});