From 933176fac43101e700f4fb584c2a98caaaed054c Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 25 Mar 2021 23:15:10 +0800 Subject: [PATCH 01/38] update add remote skill UI --- .../src/components/CreateSkillModal.tsx | 235 +++++++++------- .../src/components/SelectIntentModal.tsx | 251 ++++++++++++++++++ Composer/packages/client/src/constants.ts | 31 ++- .../server/src/controllers/utilities.ts | 21 ++ .../packages/server/src/locales/en-US.json | 12 +- .../server/src/models/utilities/util.ts | 6 + Composer/packages/server/src/router/api.ts | 2 + 7 files changed, 456 insertions(+), 102 deletions(-) create mode 100644 Composer/packages/client/src/components/SelectIntentModal.tsx diff --git a/Composer/packages/client/src/components/CreateSkillModal.tsx b/Composer/packages/client/src/components/CreateSkillModal.tsx index f0f62fd71b..1f753a6010 100644 --- a/Composer/packages/client/src/components/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/CreateSkillModal.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useRef, useState, useMemo } from 'react'; +import React, { Fragment, useRef, useState, useMemo } from 'react'; import formatMessage from 'format-message'; import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { Label } from 'office-ui-fabric-react/lib/Label'; @@ -12,12 +12,16 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; import debounce from 'lodash/debounce'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; +import { Link } from 'office-ui-fabric-react/lib/Link'; +import { Separator } from 'office-ui-fabric-react/lib/Separator'; +import { settingsState, skillsStateSelector, luFilesState } from '../recoilModel'; import { addSkillDialog } from '../constants'; import httpClient from '../utils/httpUtil'; -import { skillsStateSelector } from '../recoilModel'; import TelemetryClient from '../telemetry/TelemetryClient'; +import { SelectIntentModal } from './SelectIntentModal'; + export interface SkillFormDataErrors { endpoint?: string; manifestUrl?: string; @@ -96,9 +100,11 @@ export const validateManifestUrl = async ({ export const CreateSkillModal: React.FC = ({ projectId, onSubmit, onDismiss }) => { const skills = useRecoilValue(skillsStateSelector); + const { publishTargets, languages, luFeatures } = useRecoilValue(settingsState(projectId)); + const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false); const [formData, setFormData] = useState<{ manifestUrl: string; endpointName: string }>({ - manifestUrl: '', + manifestUrl: 'https://luhan0603-dev.azurewebsites.net/manifests/calendar-2-1-manifest.json', endpointName: '', }); const [formDataErrors, setFormDataErrors] = useState({}); @@ -107,19 +113,20 @@ export const CreateSkillModal: React.FC = ({ projectId, o manifestUrl: ValidationState.NotValidated, name: ValidationState.Validated, }); - const [selectedEndpointKey, setSelectedEndpointKey] = useState(null); + // const [selectedEndpointKey, setSelectedEndpointKey] = useState(null); const [skillManifest, setSkillManifest] = useState(null); + const luFiles = useRecoilValue(luFilesState(projectId)); - const endpointOptions = useMemo(() => { - return (skillManifest?.endpoints || [])?.map(({ name, endpointUrl, msAppId }, key) => ({ - key, - text: name, - data: { - endpointUrl, - msAppId, - }, - })); - }, [skillManifest]); + // const endpointOptions = useMemo(() => { + // return (skillManifest?.endpoints || [])?.map(({ name, endpointUrl, msAppId }, key) => ({ + // key, + // text: name, + // data: { + // endpointUrl, + // msAppId, + // }, + // })); + // }, [skillManifest]); const debouncedValidateManifestURl = useRef(debounce(validateManifestUrl, 500)).current; @@ -150,97 +157,135 @@ export const CreateSkillModal: React.FC = ({ projectId, o manifestUrl: currentManifestUrl, }); setSkillManifest(null); - setSelectedEndpointKey(null); + // setSelectedEndpointKey(null); }; - const handleEndpointUrlChange = (_, option?: IDropdownOption) => { - if (option) { - const { data, key } = option; - validateEndpoint({ - formData: { - ...data, - ...formData, - }, - ...validationHelpers, - }); - setFormData({ - ...data, - ...formData, - }); - setSelectedEndpointKey(key as number); - } - }; + // const handleEndpointUrlChange = (_, option?: IDropdownOption) => { + // if (option) { + // const { data, key } = option; + // validateEndpoint({ + // formData: { + // ...data, + // ...formData, + // }, + // ...validationHelpers, + // }); + // setFormData({ + // ...data, + // ...formData, + // }); + // setSelectedEndpointKey(key as number); + // } + // }; const handleSubmit = (event) => { event.preventDefault(); + console.log(formData); + onSubmit(formData.manifestUrl, formData.endpointName); + TelemetryClient.track('AddNewSkillCompleted'); + }; - if ( - Object.values(validationState).every((validation) => validation === ValidationState.Validated) && - !Object.values(formDataErrors).some(Boolean) - ) { - onSubmit(formData.manifestUrl, formData.endpointName); - TelemetryClient.track('AddNewSkillCompleted'); - } + const validateUrl = (event) => { + event.preventDefault(); }; - const isDisabled = - !formData.manifestUrl || - Object.values(formDataErrors).some(Boolean) || - !Object.values(validationState).every((validation) => validation === ValidationState.Validated); + // const isDisabled = + // !formData.manifestUrl || + // Object.values(formDataErrors).some(Boolean) || + // !Object.values(validationState).every((validation) => validation === ValidationState.Validated); return ( - -
- - - - - {validationState.manifestUrl === ValidationState.Validating && ( - - )} - - - - - - - - - -
-
+ + {showIntentSelectDialog ? ( + + ) : ( + + +
+ {addSkillDialog.SKILL_MANIFEST_FORM.preSubText} + + {formatMessage(' Get an overview ')} + + or + + {formatMessage(' learn how to build a skill ')} + + {addSkillDialog.SKILL_MANIFEST_FORM.afterSubText} +
+ + + + + {(!publishTargets || publishTargets.length < 1) && ( +
+ {formatMessage('To add a skill, your bot ToDoBotWithLuisSample must have publish profile')} + + {formatMessage(' Create a publish profile')} + +
+ )} +
+ {skillManifest && ( + + + +
{skillManifest.name}
+
+
+ )} +
+ + + + + + + {skillManifest ? ( + { + if (luFiles.length) { + setShowIntentSelectDialog(true); + } else { + handleSubmit(event); + } + }} + /> + ) : ( + + )} + + +
+
+ )} +
); }; diff --git a/Composer/packages/client/src/components/SelectIntentModal.tsx b/Composer/packages/client/src/components/SelectIntentModal.tsx new file mode 100644 index 0000000000..e23d1f6f8b --- /dev/null +++ b/Composer/packages/client/src/components/SelectIntentModal.tsx @@ -0,0 +1,251 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +/** @jsx jsx */ +import { jsx, css } from '@emotion/core'; +import React, { Fragment, useState, useMemo, useEffect, useCallback } from 'react'; +import formatMessage from 'format-message'; +import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; +import { DetailsList, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList'; +import { Selection } from 'office-ui-fabric-react/lib/Selection'; +import { Separator } from 'office-ui-fabric-react/lib/Separator'; +import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; +import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { Label } from 'office-ui-fabric-react/lib/Label'; +import { LuEditor } from '@bfc/code-editor'; +import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ScrollablePane'; +import { LuFile, LuIntentSection } from '@bfc/shared'; +import { useRecoilValue } from 'recoil'; + +import { selectIntentDialog } from '../constants'; +import httpClient from '../utils/httpUtil'; +import luWorker from '../recoilModel/parsers/luWorker'; +import { localeState, dispatcherState } from '../recoilModel'; + +const detailListContainer = css` + width: 100%; + height: 400px; + position: relative; + overflow: hidden; + flex-grow: 1; + border: 1px solid E5E5E5; +`; + +interface SelectIntentModalProps { + manifest: { + dispatchModels: { + intents: Array | object; + languages: object; + }; + [key: string]: any; + }; + languages: string[]; + projectId: string; + luFeatures: any; + rootLuFiles: LuFile[]; + onSubmit: (event) => void; + onDismiss: () => void; +} + +const columns = [ + { + key: 'Name', + name: 'Name', + minWidth: 300, + isResizable: false, + onRender: (item: string) => { + return
{item}
; + }, + }, +]; + +const getRemoteLuFiles = async (skillLanguages: object, composerLangeages: string[]) => { + const luFilePromise: Promise[] = []; + try { + for (const [key, value] of Object.entries(skillLanguages)) { + if (composerLangeages.includes(key)) { + value.map((item) => { + // get lu file + luFilePromise.push( + httpClient.get(`/skill/retrieveRemoteFile`, { + params: { + url: item.url, + }, + }) + ); + }); + } + } + const responses = await Promise.all(luFilePromise); + const files: { id: string; content: string }[] = responses.map((response) => { + return response.data; + }); + return files; + } catch (e) { + console.log(e); + } +}; + +const getParsedLuFiles = async (files: { id: string; content: string }[], luFeatures) => { + const promises = files?.map((item) => { + return luWorker.parse(item.id, item.content, luFeatures) as Promise; + }); + const luFiles: LuFile[] = await Promise.all(promises); + return luFiles; +}; + +export const SelectIntentModal: React.FC = (props) => { + const { manifest, onSubmit, onDismiss, languages, luFeatures, projectId, rootLuFiles } = props; + const [page, setPage] = useState(0); + const [selectedIntents, setSelectedIntents] = useState>([]); + // luFiles from manifest + const [luFiles, setLufile] = useState>([]); + // current locale Lufile + const [currentLuFile, setCurrentLuFile] = useState(); + // selected current locale intents + const [displayIntents, setDisplayIntent] = useState>([]); + const [displayContent, setDisplayContent] = useState(''); + // const [luFileError, setError] = useState(); + const [title, setTitle] = useState(selectIntentDialog.SELECT_INTENT); + const locale = useRecoilValue(localeState(projectId)); + + const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); + + const selection = useMemo(() => { + return new Selection({ + onSelectionChanged: () => { + const selectedItems = selection.getSelection(); + setSelectedIntents(selectedItems as string[]); + }, + }); + }, []); + + // intents from manifest, intents can be an object or array. + const intentItems = useMemo(() => { + let res; + if (Array.isArray(manifest.dispatchModels?.intents)) { + res = manifest.dispatchModels.intents; + } else { + res = Object.keys(manifest.dispatchModels?.intents); + } + return res; + }, [manifest]); + + const updateLuFile = useCallback( + (content: string) => { + const file = rootLuFiles.find(({ id }) => id.includes(locale)); + if (!file) return; + const { id } = file; + file.content; + const payload = { + projectId: projectId, + id, + content: file.content + '\n' + content, + }; + updateLuFileDispatcher(payload); + }, + [rootLuFiles, projectId, locale] + ); + + useEffect(() => { + if (locale) { + const skillLanguages = manifest.dispatchModels?.languages; + getRemoteLuFiles(skillLanguages, languages) + .then((items) => { + items && + getParsedLuFiles(items, luFeatures).then((files) => { + setLufile(files); + files.map((file) => { + if (file.id.includes(locale)) { + setCurrentLuFile(file); + } + }); + }); + }) + .catch((e) => { + console.log(e); + }); + } + }, [manifest.dispatchModels?.languages, languages, locale]); + + useEffect(() => { + if (selectedIntents.length > 0 && currentLuFile) { + const intents: LuIntentSection[] = []; + currentLuFile.intents.map((intent) => { + if (selectedIntents.includes(intent.Name)) { + intents.push(intent); + } + }); + setDisplayIntent(intents); + + // current locale, selected intent value. + const intentsValue = intents + .map((item) => { + return `# ${item.Name}` + '\n' + item.Body; + }) + .join('\n'); + setDisplayContent(intentsValue); + } + }, [selectedIntents, currentLuFile]); + + return ( + + + {page === 0 ? ( + + +
+ + + +
+
+ ) : ( + + + + )} + + + {page === 1 && ( + { + setPage(page - 1); + setTitle(selectIntentDialog.SELECT_INTENT); + }} + /> + )} + + + {page === 0 ? ( + { + setPage(page + 1); + setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE); + }} + /> + ) : ( + { + updateLuFile(displayContent); + onSubmit(event); + onDismiss(); + }} + /> + )} + + +
+
+ ); +}; diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index 2ede718606..46f3c7aaca 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -294,7 +294,10 @@ export const addSkillDialog = { get SKILL_MANIFEST_FORM() { return { title: formatMessage('Add a skill'), - subText: formatMessage('Enter a manifest url to add a new skill to your bot.'), + preSubText: formatMessage(`Skills extend your bot's conversational capabilities . To know more about skills`), + afterSubText: formatMessage( + `To make sure the skill will work correctly, we perform some validation checks. When you’re ready to add a skill, enter the Skill manifest URL provided to you by the skill author.` + ), }; }, get SKILL_MANIFEST_FORM_EDIT() { @@ -305,6 +308,32 @@ export const addSkillDialog = { }, }; +export const selectIntentDialog = { + get SELECT_INTENT() { + return { + title: formatMessage('Select intents to trigger Bookings skill'), + subText: formatMessage('These intents will trigger this skill from ToDoBotSample'), + }; + }, + get ADD_OR_EDIT_PHRASE() { + return { + title: formatMessage('Add or edit phrases to trigger Bookings skill'), + subText: formatMessage('These phrases will trigger this skill from ToDoBotSample'), + }; + }, +}; + +export const enableOrchestratorDialog = { + get title() { + return formatMessage('Enable Orchestrator'); + }, + get subText() { + return formatMessage( + 'Connecting to skills works best with the Orchestrator recognizer at the root bot (TodoBotSample)' + ); + }, +}; + export const repairSkillDialog = (name: string) => { return { title: formatMessage('Link to this skill has been broken'), diff --git a/Composer/packages/server/src/controllers/utilities.ts b/Composer/packages/server/src/controllers/utilities.ts index 72c76c6187..7fe7f3cb13 100644 --- a/Composer/packages/server/src/controllers/utilities.ts +++ b/Composer/packages/server/src/controllers/utilities.ts @@ -3,6 +3,7 @@ import { Request, Response } from 'express'; import { parseQnAContent } from '../models/utilities/parser'; +import { getRemoteFile as getFile } from '../models/utilities/util'; async function getQnaContent(req: Request, res: Response) { try { @@ -16,6 +17,26 @@ async function getQnaContent(req: Request, res: Response) { } } +async function getRemoteFile(req: Request, res: Response) { + try { + const url: string = req.query.url; + const content = await getFile(url); + const start = decodeURI(url).lastIndexOf('/'); + const end = decodeURI(url).lastIndexOf('.'); + const id = url.substring(start + 1, end); + + res.status(200).json({ + content, + id, + }); + } catch (err) { + res.status(404).json({ + message: err.message, + }); + } +} + export const UtilitiesController = { getQnaContent, + getRemoteFile, }; diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 9bbb489103..b4e03949ca 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -1223,9 +1223,6 @@ "enable_speech_e4a16f1c": { "message": "Enable speech" }, - "enable_the_component_model_including_the_new_creat_f6e5659c": { - "message": "Enable the Component Model including the new creation experience, the Adaptive Runtime, and Package Manager." - }, "enabled_ba7cab66": { "message": "Enabled" }, @@ -2198,6 +2195,9 @@ "new_13daf639": { "message": "New" }, + "new_creation_experience_29591aca": { + "message": "New Creation Experience" + }, "new_template_49e6f0f2": { "message": "New template" }, @@ -2456,6 +2456,9 @@ "preview_features_e279bac5": { "message": "Preview features" }, + "preview_the_new_bot_creation_experience_create_new_e0b7150f": { + "message": "Preview the new bot creation experience. Create new bots that use the Adaptive Runtime, and can be enhanced using Package Manager." + }, "previous_bd2ac015": { "message": "Previous" }, @@ -3161,9 +3164,6 @@ "the_api_messages_endpoint_for_the_skill_f318dc63": { "message": "The /api/messages endpoint for the skill." }, - "the_component_model_febefd93": { - "message": "The Component Model" - }, "the_dialog_you_have_tried_to_delete_is_currently_u_a37c7a02": { "message": "The dialog you have tried to delete is currently used in the below dialog(s). Removing this dialog will cause your Bot to malfunction without additional action." }, diff --git a/Composer/packages/server/src/models/utilities/util.ts b/Composer/packages/server/src/models/utilities/util.ts index 17109e4d94..b099cda2da 100644 --- a/Composer/packages/server/src/models/utilities/util.ts +++ b/Composer/packages/server/src/models/utilities/util.ts @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import axios from 'axios'; export const getDialogNameFromFile = (file: string) => { const tokens = file.split('.'); @@ -18,3 +19,8 @@ export const getDialogNameFromFile = (file: string) => { } return dialogName; }; + +export const getRemoteFile = async (url): Promise => { + const response = await axios.get(url); + return response.data; +}; diff --git a/Composer/packages/server/src/router/api.ts b/Composer/packages/server/src/router/api.ts index f3ae8d7e4b..916f1f8e74 100644 --- a/Composer/packages/server/src/router/api.ts +++ b/Composer/packages/server/src/router/api.ts @@ -96,6 +96,8 @@ router.use('/assets/locales/', express.static(path.join(__dirname, '..', '..', ' //help api router.get('/utilities/qna/parse', UtilitiesController.getQnaContent); +router.get('/skill/retrieveRemoteFile', UtilitiesController.getRemoteFile); + // extensions router.get('/extensions', ExtensionsController.listExtensions); router.post('/extensions', ExtensionsController.addExtension); From db46a2892e2f3ed57b6679271639fbf28de5eb62 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 25 Mar 2021 23:16:55 +0800 Subject: [PATCH 02/38] polish --- Composer/packages/client/src/components/CreateSkillModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/CreateSkillModal.tsx b/Composer/packages/client/src/components/CreateSkillModal.tsx index 1f753a6010..a8c7e05350 100644 --- a/Composer/packages/client/src/components/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/CreateSkillModal.tsx @@ -104,7 +104,7 @@ export const CreateSkillModal: React.FC = ({ projectId, o const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false); const [formData, setFormData] = useState<{ manifestUrl: string; endpointName: string }>({ - manifestUrl: 'https://luhan0603-dev.azurewebsites.net/manifests/calendar-2-1-manifest.json', + manifestUrl: '', endpointName: '', }); const [formDataErrors, setFormDataErrors] = useState({}); From 20d8313570e65d708d7db63b4dd88d3225a3b7b6 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 29 Mar 2021 17:06:01 +0800 Subject: [PATCH 03/38] add trigger after skill created --- .../__tests__/components/skill.test.tsx | 5 +- .../CreateSkillModal.tsx | 87 ++++++++----------- .../SelectIntentModal.tsx | 27 +++--- .../AddRemoteSkillModal/SkillDetail.tsx | 66 ++++++++++++++ Composer/packages/client/src/constants.ts | 8 +- .../client/src/pages/design/Modals.tsx | 5 +- 6 files changed, 126 insertions(+), 72 deletions(-) rename Composer/packages/client/src/components/{ => AddRemoteSkillModal}/CreateSkillModal.tsx (81%) rename Composer/packages/client/src/components/{ => AddRemoteSkillModal}/SelectIntentModal.tsx (88%) create mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx diff --git a/Composer/packages/client/__tests__/components/skill.test.tsx b/Composer/packages/client/__tests__/components/skill.test.tsx index 6855a4d258..1342a19c43 100644 --- a/Composer/packages/client/__tests__/components/skill.test.tsx +++ b/Composer/packages/client/__tests__/components/skill.test.tsx @@ -6,7 +6,10 @@ import { act, fireEvent } from '@botframework-composer/test-utils'; import httpClient from '../../src/utils/httpUtil'; import { renderWithRecoil } from '../testUtils'; -import CreateSkillModal, { validateEndpoint, validateManifestUrl } from '../../src/components/CreateSkillModal'; +import CreateSkillModal, { + validateEndpoint, + validateManifestUrl, +} from '../../src/components/AddRemoteSkillModal/CreateSkillModal'; import { currentProjectIdState, settingsState } from '../../src/recoilModel'; jest.mock('../../src//utils/httpUtil'); diff --git a/Composer/packages/client/src/components/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx similarity index 81% rename from Composer/packages/client/src/components/CreateSkillModal.tsx rename to Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index a8c7e05350..13e282d5b1 100644 --- a/Composer/packages/client/src/components/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -1,10 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { Fragment, useRef, useState, useMemo } from 'react'; +import React, { Fragment, useRef, useState } from 'react'; import formatMessage from 'format-message'; -import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; -import { Label } from 'office-ui-fabric-react/lib/Label'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; @@ -15,12 +13,14 @@ import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; -import { settingsState, skillsStateSelector, luFilesState } from '../recoilModel'; -import { addSkillDialog } from '../constants'; -import httpClient from '../utils/httpUtil'; -import TelemetryClient from '../telemetry/TelemetryClient'; +import { settingsState, skillsStateSelector, luFilesState, designPageLocationState } from '../../recoilModel'; +import { addSkillDialog } from '../../constants'; +import httpClient from '../../utils/httpUtil'; +import TelemetryClient from '../../telemetry/TelemetryClient'; import { SelectIntentModal } from './SelectIntentModal'; +import { SkillDetail } from './SkillDetail'; +import { TriggerFormData } from '../../utils/dialogUtil'; export interface SkillFormDataErrors { endpoint?: string; @@ -34,7 +34,7 @@ export const msAppIdRegex = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A export interface CreateSkillModalProps { projectId: string; - onSubmit: (manifestUrl: string, endpointName: string) => void; + onSubmit: (manifestUrl: string, endpointName: string, dialogId: string, triggerFormData: TriggerFormData) => void; onDismiss: () => void; } @@ -98,11 +98,21 @@ export const validateManifestUrl = async ({ } }; +const getTriggerFormData = (intent: string, content: string): TriggerFormData => ({ + errors: {}, + $kind: 'Microsoft.OnIntent', + event: '', + intent: intent, + triggerPhrases: content, + regEx: '', +}); + export const CreateSkillModal: React.FC = ({ projectId, onSubmit, onDismiss }) => { const skills = useRecoilValue(skillsStateSelector); const { publishTargets, languages, luFeatures } = useRecoilValue(settingsState(projectId)); - const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false); + const { dialogId } = useRecoilValue(designPageLocationState(projectId)); + const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false); const [formData, setFormData] = useState<{ manifestUrl: string; endpointName: string }>({ manifestUrl: '', endpointName: '', @@ -113,21 +123,9 @@ export const CreateSkillModal: React.FC = ({ projectId, o manifestUrl: ValidationState.NotValidated, name: ValidationState.Validated, }); - // const [selectedEndpointKey, setSelectedEndpointKey] = useState(null); const [skillManifest, setSkillManifest] = useState(null); const luFiles = useRecoilValue(luFilesState(projectId)); - // const endpointOptions = useMemo(() => { - // return (skillManifest?.endpoints || [])?.map(({ name, endpointUrl, msAppId }, key) => ({ - // key, - // text: name, - // data: { - // endpointUrl, - // msAppId, - // }, - // })); - // }, [skillManifest]); - const debouncedValidateManifestURl = useRef(debounce(validateManifestUrl, 500)).current; const validationHelpers = { @@ -160,40 +158,22 @@ export const CreateSkillModal: React.FC = ({ projectId, o // setSelectedEndpointKey(null); }; - // const handleEndpointUrlChange = (_, option?: IDropdownOption) => { - // if (option) { - // const { data, key } = option; - // validateEndpoint({ - // formData: { - // ...data, - // ...formData, - // }, - // ...validationHelpers, - // }); - // setFormData({ - // ...data, - // ...formData, - // }); - // setSelectedEndpointKey(key as number); - // } - // }; - - const handleSubmit = (event) => { + const handleSubmit = (event, content) => { event.preventDefault(); - console.log(formData); - onSubmit(formData.manifestUrl, formData.endpointName); + // add a remote skill + console.log(skillManifest.name); + + const triggerFormData = getTriggerFormData(skillManifest.name, content); + onSubmit(formData.manifestUrl, formData.endpointName, dialogId, triggerFormData); TelemetryClient.track('AddNewSkillCompleted'); + // add trigger to root bot + TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); }; const validateUrl = (event) => { event.preventDefault(); }; - // const isDisabled = - // !formData.manifestUrl || - // Object.values(formDataErrors).some(Boolean) || - // !Object.values(validationState).every((validation) => validation === ValidationState.Validated); - return ( {showIntentSelectDialog ? ( @@ -205,6 +185,7 @@ export const CreateSkillModal: React.FC = ({ projectId, o onDismiss={onDismiss} onSubmit={handleSubmit} rootLuFiles={luFiles} + dialogId={dialogId} /> ) : ( = ({ projectId, o - +
= ({ projectId, o
)} -
+ {skillManifest && ( - - -
{skillManifest.name}
-
+ +
+ +
)}
diff --git a/Composer/packages/client/src/components/SelectIntentModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx similarity index 88% rename from Composer/packages/client/src/components/SelectIntentModal.tsx rename to Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx index e23d1f6f8b..bd91347f99 100644 --- a/Composer/packages/client/src/components/SelectIntentModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx @@ -16,10 +16,10 @@ import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ import { LuFile, LuIntentSection } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; -import { selectIntentDialog } from '../constants'; -import httpClient from '../utils/httpUtil'; -import luWorker from '../recoilModel/parsers/luWorker'; -import { localeState, dispatcherState } from '../recoilModel'; +import { selectIntentDialog } from '../../constants'; +import httpClient from '../../utils/httpUtil'; +import luWorker from '../../recoilModel/parsers/luWorker'; +import { localeState, dispatcherState } from '../../recoilModel'; const detailListContainer = css` width: 100%; @@ -42,7 +42,8 @@ interface SelectIntentModalProps { projectId: string; luFeatures: any; rootLuFiles: LuFile[]; - onSubmit: (event) => void; + dialogId: string; + onSubmit: (event, content: string) => void; onDismiss: () => void; } @@ -94,7 +95,7 @@ const getParsedLuFiles = async (files: { id: string; content: string }[], luFeat }; export const SelectIntentModal: React.FC = (props) => { - const { manifest, onSubmit, onDismiss, languages, luFeatures, projectId, rootLuFiles } = props; + const { manifest, onSubmit, onDismiss, languages, luFeatures, projectId, rootLuFiles, dialogId } = props; const [page, setPage] = useState(0); const [selectedIntents, setSelectedIntents] = useState>([]); // luFiles from manifest @@ -105,7 +106,7 @@ export const SelectIntentModal: React.FC = (props) => { const [displayIntents, setDisplayIntent] = useState>([]); const [displayContent, setDisplayContent] = useState(''); // const [luFileError, setError] = useState(); - const [title, setTitle] = useState(selectIntentDialog.SELECT_INTENT); + const [title, setTitle] = useState(selectIntentDialog.SELECT_INTENT(dialogId)); const locale = useRecoilValue(localeState(projectId)); const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); @@ -139,7 +140,7 @@ export const SelectIntentModal: React.FC = (props) => { const payload = { projectId: projectId, id, - content: file.content + '\n' + content, + content: file.content + `\n # ${manifest.name} \n` + content, }; updateLuFileDispatcher(payload); }, @@ -180,7 +181,7 @@ export const SelectIntentModal: React.FC = (props) => { // current locale, selected intent value. const intentsValue = intents .map((item) => { - return `# ${item.Name}` + '\n' + item.Body; + return `> ${item.Name}` + '\n' + item.Body; }) .join('\n'); setDisplayContent(intentsValue); @@ -217,7 +218,7 @@ export const SelectIntentModal: React.FC = (props) => { text={formatMessage('Back')} onClick={() => { setPage(page - 1); - setTitle(selectIntentDialog.SELECT_INTENT); + setTitle(selectIntentDialog.SELECT_INTENT(dialogId)); }} /> )} @@ -229,7 +230,7 @@ export const SelectIntentModal: React.FC = (props) => { text={formatMessage('Next')} onClick={() => { setPage(page + 1); - setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE); + setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId)); }} /> ) : ( @@ -237,8 +238,10 @@ export const SelectIntentModal: React.FC = (props) => { styles={{ root: { marginLeft: '8px' } }} text={formatMessage('Done')} onClick={(event) => { + // merge lu file updateLuFile(displayContent); - onSubmit(event); + onSubmit(event, displayContent); + // add a trigger to root bot onDismiss(); }} /> diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx new file mode 100644 index 0000000000..60b08ba94a --- /dev/null +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +/** @jsx jsx */ +import { jsx, css } from '@emotion/core'; +import React, { Fragment } from 'react'; +import formatMessage from 'format-message'; +import { title } from '../../pages/about/styles'; + +interface SkillDetailProps { + manifest: { + dispatchModels: { + intents: Array | object; + languages: object; + }; + version: string; + activities: object; + publisherName: string; + description: string; + [key: string]: any; + }; +} +const container = css` + width: 100%; + margin: 10px 0px; +`; +const segment = css` + margin: 15px 0px; +`; +const title = css` + font-size: 20px; + line-height: 24px; + font-weight: 600; +`; +const subTitle = css` + font-size: 14px; + line-height: 20px; + font-weight: 600; +`; +const text = css` + font-size: 14px; + line-height: 20px; +`; +export const SkillDetail: React.FC = (props) => { + const { manifest } = props; + return ( +
+
{formatMessage(manifest.name)}
+
+
{formatMessage('Description')}
+
{formatMessage(manifest.description)}
+
+
+
{formatMessage('Version')}
+
{formatMessage(manifest.version)}
+
+
+
{formatMessage('Activities')}
+
{formatMessage(Object.keys(manifest.activities).join(', '))}
+
+
+
{formatMessage('Publisher')}
+
{formatMessage(manifest.publisherName)}
+
+
+ ); +}; diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index 46f3c7aaca..99c590ac9c 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -309,16 +309,16 @@ export const addSkillDialog = { }; export const selectIntentDialog = { - get SELECT_INTENT() { + SELECT_INTENT: (name: string) => { return { title: formatMessage('Select intents to trigger Bookings skill'), - subText: formatMessage('These intents will trigger this skill from ToDoBotSample'), + subText: formatMessage(`These intents will trigger this skill from ${name}`), }; }, - get ADD_OR_EDIT_PHRASE() { + ADD_OR_EDIT_PHRASE: (name: string) => { return { title: formatMessage('Add or edit phrases to trigger Bookings skill'), - subText: formatMessage('These phrases will trigger this skill from ToDoBotSample'), + subText: formatMessage(`These phrases will trigger this skill from ${name}`), }; }, }; diff --git a/Composer/packages/client/src/pages/design/Modals.tsx b/Composer/packages/client/src/pages/design/Modals.tsx index dd8968e656..1e42012b48 100644 --- a/Composer/packages/client/src/pages/design/Modals.tsx +++ b/Composer/packages/client/src/pages/design/Modals.tsx @@ -31,7 +31,7 @@ import { createQnAOnState, exportSkillModalInfoState } from '../../recoilModel/a import CreationModal from './creationModal'; -const CreateSkillModal = React.lazy(() => import('../../components/CreateSkillModal')); +const CreateSkillModal = React.lazy(() => import('../../components/AddRemoteSkillModal/CreateSkillModal')); const RepairSkillModal = React.lazy(() => import('../../components/RepairSkillModal')); const CreateDialogModal = React.lazy(() => import('./createDialogModal')); const DisplayManifestModal = React.lazy(() => import('../../components/Modal/DisplayManifestModal')); @@ -131,9 +131,10 @@ const Modals: React.FC = ({ projectId = '' }) => { onDismiss={() => { setAddSkillDialogModalVisibility(false); }} - onSubmit={(manifestUrl, endpointName) => { + onSubmit={(manifestUrl, endpointName, dialogId, formData) => { setAddSkillDialogModalVisibility(false); addRemoteSkillToBotProject(manifestUrl, endpointName); + createTrigger(projectId, dialogId, formData); }} /> )} From b16038f843f7d2f5476c1d03ba6d8b09e62fc9fe Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Tue, 30 Mar 2021 22:24:34 +0800 Subject: [PATCH 04/38] add action to on intent trigger after add remote skill --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 157 ++++++---------- .../OrchestractorModal.tsx | 53 ++++++ .../AddRemoteSkillModal/SelectIntentModal.tsx | 127 +++++++------ .../AddRemoteSkillModal/SkillDetail.tsx | 5 +- .../AddRemoteSkillModal/createActionHelper.ts | 20 ++ Composer/packages/client/src/constants.ts | 16 +- .../client/src/pages/design/Modals.tsx | 14 +- .../src/recoilModel/dispatchers/trigger.ts | 172 +++++++++++------- .../packages/server/src/locales/en-US.json | 49 ++++- 9 files changed, 361 insertions(+), 252 deletions(-) create mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/OrchestractorModal.tsx create mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 13e282d5b1..060de885e4 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -1,14 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { Fragment, useRef, useState } from 'react'; +import React, { Fragment, useRef, useState, useCallback } from 'react'; import formatMessage from 'format-message'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; -import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; +// import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; import debounce from 'lodash/debounce'; +import { isUsingAdaptiveRuntime } from '@bfc/shared'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; @@ -17,10 +18,11 @@ import { settingsState, skillsStateSelector, luFilesState, designPageLocationSta import { addSkillDialog } from '../../constants'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; +import { TriggerFormData } from '../../utils/dialogUtil'; import { SelectIntentModal } from './SelectIntentModal'; import { SkillDetail } from './SkillDetail'; -import { TriggerFormData } from '../../utils/dialogUtil'; +import { createActionFromManifest } from './createActionHelper'; export interface SkillFormDataErrors { endpoint?: string; @@ -34,45 +36,12 @@ export const msAppIdRegex = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A export interface CreateSkillModalProps { projectId: string; - onSubmit: (manifestUrl: string, endpointName: string, dialogId: string, triggerFormData: TriggerFormData) => void; + addRemoteSkill: (manifestUrl: string, endpointName: string) => void; + addTriggerToRoot: (dialogId: string, triggerFormData: TriggerFormData, action) => void; onDismiss: () => void; } -enum ValidationState { - NotValidated = 'NotValidated', - Validating = 'Validating', - Validated = 'Validated', -} - -export const validateEndpoint = ({ - formData, - formDataErrors, - setFormDataErrors, - setValidationState, - validationState, -}) => { - const { msAppId, endpointUrl } = formData; - const { endpoint: _, ...errors } = formDataErrors; - - if (!msAppId || !endpointUrl) { - setFormDataErrors({ ...errors, endpoint: formatMessage('Please select a valid endpoint') }); - } else if (!urlRegex.test(endpointUrl) || !msAppIdRegex.test(msAppId)) { - setFormDataErrors({ ...errors, endpoint: formatMessage('Skill manifest endpoint is configured improperly') }); - } else { - setFormDataErrors(errors); - setValidationState({ ...validationState, endpoint: ValidationState.Validated }); - } -}; - -export const validateManifestUrl = async ({ - formData, - formDataErrors, - projectId, - setFormDataErrors, - setValidationState, - setSkillManifest, - validationState, -}) => { +export const validateManifestUrl = async ({ formData, formDataErrors, setFormDataErrors }) => { const { manifestUrl } = formData; const { manifestUrl: _, ...errors } = formDataErrors; @@ -81,20 +50,7 @@ export const validateManifestUrl = async ({ } else if (!urlRegex.test(manifestUrl)) { setFormDataErrors({ ...errors, manifestUrl: formatMessage('Url should start with http[s]://') }); } else { - try { - setValidationState({ ...validationState, manifestUrl: ValidationState.Validating }); - const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, { - params: { - url: manifestUrl, - }, - }); - setFormDataErrors(errors); - setSkillManifest(data); - setValidationState({ ...validationState, manifestUrl: ValidationState.Validated }); - } catch (error) { - setFormDataErrors({ ...errors, manifestUrl: formatMessage('Manifest url can not be accessed') }); - setValidationState({ ...validationState, manifestUrl: ValidationState.NotValidated }); - } + setFormDataErrors({}); } }; @@ -107,22 +63,19 @@ const getTriggerFormData = (intent: string, content: string): TriggerFormData => regEx: '', }); -export const CreateSkillModal: React.FC = ({ projectId, onSubmit, onDismiss }) => { - const skills = useRecoilValue(skillsStateSelector); - const { publishTargets, languages, luFeatures } = useRecoilValue(settingsState(projectId)); +export const CreateSkillModal: React.FC = (props) => { + const { projectId, addRemoteSkill, addTriggerToRoot, onDismiss } = props; + const { publishTargets, languages, luFeatures, runtime } = useRecoilValue(settingsState(projectId)); const { dialogId } = useRecoilValue(designPageLocationState(projectId)); const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false); + const [formData, setFormData] = useState<{ manifestUrl: string; endpointName: string }>({ manifestUrl: '', endpointName: '', }); const [formDataErrors, setFormDataErrors] = useState({}); - const [validationState, setValidationState] = useState({ - endpoint: ValidationState.NotValidated, - manifestUrl: ValidationState.NotValidated, - name: ValidationState.Validated, - }); + const [skillManifest, setSkillManifest] = useState(null); const luFiles = useRecoilValue(luFilesState(projectId)); @@ -130,24 +83,14 @@ export const CreateSkillModal: React.FC = ({ projectId, o const validationHelpers = { formDataErrors, - skills, setFormDataErrors, - setValidationState, - setSkillManifest, - validationState, }; const handleManifestUrlChange = (_, currentManifestUrl = '') => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { manifestUrl, ...rest } = formData; - setValidationState((validationState) => ({ - ...validationState, - manifestUrl: ValidationState.NotValidated, - endpoint: ValidationState.NotValidated, - })); debouncedValidateManifestURl({ formData: { manifestUrl: currentManifestUrl }, - projectId, ...validationHelpers, }); setFormData({ @@ -155,37 +98,49 @@ export const CreateSkillModal: React.FC = ({ projectId, o manifestUrl: currentManifestUrl, }); setSkillManifest(null); - // setSelectedEndpointKey(null); }; + const validateUrl = useCallback( + async (event) => { + event.preventDefault(); + try { + const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, { + params: { + url: formData.manifestUrl, + }, + }); + setSkillManifest(data); + } catch (error) { + setFormDataErrors({ ...error, manifestUrl: formatMessage('Manifest url can not be accessed') }); + } + }, + [projectId, formData] + ); + const handleSubmit = (event, content) => { event.preventDefault(); // add a remote skill - console.log(skillManifest.name); - const triggerFormData = getTriggerFormData(skillManifest.name, content); - onSubmit(formData.manifestUrl, formData.endpointName, dialogId, triggerFormData); + addRemoteSkill(formData.manifestUrl, formData.endpointName); TelemetryClient.track('AddNewSkillCompleted'); // add trigger to root bot + const action = createActionFromManifest(content); + addTriggerToRoot(dialogId, triggerFormData, [action]); TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); }; - const validateUrl = (event) => { - event.preventDefault(); - }; - return ( {showIntentSelectDialog ? ( ) : ( = ({ projectId, o value={formData.manifestUrl || ''} onChange={handleManifestUrlChange} /> - {(!publishTargets || publishTargets.length < 1) && ( -
- {formatMessage('To add a skill, your bot ToDoBotWithLuisSample must have publish profile')} - - {formatMessage(' Create a publish profile')} - -
- )} {skillManifest && ( @@ -236,26 +183,30 @@ export const CreateSkillModal: React.FC = ({ projectId, o )} - - - + {skillManifest ? ( - { - if (luFiles.length) { + isUsingAdaptiveRuntime(runtime) && luFiles.length > 0 ? ( + { setShowIntentSelectDialog(true); - } else { - handleSubmit(event); - } - }} - /> + }} + /> + ) : ( + { + addRemoteSkill(formData.manifestUrl, formData.endpointName); + }} + /> + ) ) : ( { + const { onDismiss, dialogId, onSubmit } = props; + const [enable, setEnable] = useState(false); + const onChange = (ev, check) => { + setEnable(check); + }; + return ( + + +
+ {enableOrchestratorDialog.subText}({dialogId}) + + {formatMessage(' learn more about Orchestractor')} + +
+ + + { + // download orchestrator + onSubmit(); + }} + /> + + +
+
+ ); +}; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx index bd91347f99..7e1a40041d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx @@ -21,6 +21,8 @@ import httpClient from '../../utils/httpUtil'; import luWorker from '../../recoilModel/parsers/luWorker'; import { localeState, dispatcherState } from '../../recoilModel'; +import { OrchestractorModal } from './OrchestractorModal'; + const detailListContainer = css` width: 100%; height: 400px; @@ -106,9 +108,9 @@ export const SelectIntentModal: React.FC = (props) => { const [displayIntents, setDisplayIntent] = useState>([]); const [displayContent, setDisplayContent] = useState(''); // const [luFileError, setError] = useState(); - const [title, setTitle] = useState(selectIntentDialog.SELECT_INTENT(dialogId)); + const [title, setTitle] = useState(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); const locale = useRecoilValue(localeState(projectId)); - + const [showOrchestratorDialog, setShowOrchestratorDialog] = useState(false); const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); const selection = useMemo(() => { @@ -189,66 +191,71 @@ export const SelectIntentModal: React.FC = (props) => { }, [selectedIntents, currentLuFile]); return ( - - - {page === 0 ? ( - - -
- - - -
-
- ) : ( - - - - )} - - - {page === 1 && ( - { - setPage(page - 1); - setTitle(selectIntentDialog.SELECT_INTENT(dialogId)); - }} - /> - )} - - + + {showOrchestratorDialog ? ( + { + // append remote lufile into root lu file + updateLuFile(displayContent); + // add trigger to root + onSubmit(ev, displayContent); + }} + /> + ) : ( + + {page === 0 ? ( - { - setPage(page + 1); - setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId)); - }} - /> + + +
+ + + +
+
) : ( - { - // merge lu file - updateLuFile(displayContent); - onSubmit(event, displayContent); - // add a trigger to root bot - onDismiss(); - }} - /> + + + )} -
-
-
-
+ + + {page === 1 && ( + { + setPage(page - 1); + setTitle(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); + }} + /> + )} + + + { + if (page === 1) { + setShowOrchestratorDialog(true); + } else { + setPage(page + 1); + setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); + } + }} + /> + + +
+
+ )} +
); }; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index 60b08ba94a..e058d6b3e0 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -1,10 +1,10 @@ +/* eslint-disable format-message/literal-pattern */ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. /** @jsx jsx */ import { jsx, css } from '@emotion/core'; -import React, { Fragment } from 'react'; +import React from 'react'; import formatMessage from 'format-message'; -import { title } from '../../pages/about/styles'; interface SkillDetailProps { manifest: { @@ -16,6 +16,7 @@ interface SkillDetailProps { activities: object; publisherName: string; description: string; + name: string; [key: string]: any; }; } diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts new file mode 100644 index 0000000000..4820d4e94e --- /dev/null +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { generateDesignerId } from '@bfc/shared'; + +export const createActionFromManifest = (manifest) => { + return { + $kind: 'Microsoft.BeginSkill', + $designer: { + id: `${generateDesignerId()}`, + }, + activityProcessed: true, + botId: '=settings.MicrosoftAppId', + skillHostEndpoint: '=settings.skillHostEndpoint', + connectionName: '=settings.connectionName', + allowInterruptions: true, + skillEndpoint: `=settings.skill['${manifest.name}'].endpointUrl`, + skillAppId: `=settings.skill['${manifest.name}'].msAppId`, + }; +}; diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index 17f5d92f29..f4d9d59750 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -315,15 +315,19 @@ export const addSkillDialog = { }; export const selectIntentDialog = { - SELECT_INTENT: (name: string) => { + SELECT_INTENT: (name: string, skill: string) => { return { - title: formatMessage('Select intents to trigger Bookings skill'), + // eslint-disable-next-line format-message/literal-pattern + title: formatMessage(`Select intents to trigger ${skill} skill`), + // eslint-disable-next-line format-message/literal-pattern subText: formatMessage(`These intents will trigger this skill from ${name}`), }; }, - ADD_OR_EDIT_PHRASE: (name: string) => { + ADD_OR_EDIT_PHRASE: (name: string, skill: string) => { return { - title: formatMessage('Add or edit phrases to trigger Bookings skill'), + // eslint-disable-next-line format-message/literal-pattern + title: formatMessage(`Add or edit phrases to trigger ${skill} skill`), + // eslint-disable-next-line format-message/literal-pattern subText: formatMessage(`These phrases will trigger this skill from ${name}`), }; }, @@ -334,9 +338,7 @@ export const enableOrchestratorDialog = { return formatMessage('Enable Orchestrator'); }, get subText() { - return formatMessage( - 'Connecting to skills works best with the Orchestrator recognizer at the root bot (TodoBotSample)' - ); + return formatMessage('Connecting to skills works best with the Orchestrator recognizer at the root bot'); }, }; diff --git a/Composer/packages/client/src/pages/design/Modals.tsx b/Composer/packages/client/src/pages/design/Modals.tsx index 1e42012b48..feeaa1645b 100644 --- a/Composer/packages/client/src/pages/design/Modals.tsx +++ b/Composer/packages/client/src/pages/design/Modals.tsx @@ -62,6 +62,7 @@ const Modals: React.FC = ({ projectId = '' }) => { createQnAKBFromUrl, createQnAKBFromScratch, createTrigger, + createTriggerWithAction, createQnATrigger, createDialogCancel, } = useRecoilValue(dispatcherState); @@ -127,14 +128,17 @@ const Modals: React.FC = ({ projectId = '' }) => { )} {showAddSkillDialogModal && ( { + addRemoteSkill={(manifestUrl, endpointName) => { setAddSkillDialogModalVisibility(false); + addRemoteSkillToBotProject(manifestUrl, endpointName); + }} + addTriggerToRoot={(dialogId, formData, actions) => { + console.log(actions); + createTriggerWithAction(projectId, dialogId, formData, actions); }} - onSubmit={(manifestUrl, endpointName, dialogId, formData) => { + projectId={projectId} + onDismiss={() => { setAddSkillDialogModalVisibility(false); - addRemoteSkillToBotProject(manifestUrl, endpointName); - createTrigger(projectId, dialogId, formData); }} /> )} diff --git a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts index a1104ae9e4..e869e74bb9 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts @@ -37,6 +37,80 @@ const getDesignerIdFromDialogPath = (dialog, path) => { return designerId; }; +const getNewDialogWithTrigger = async ( + callbackHelpers: CallbackInterface, + projectId: string, + dialogId: string, + formData: TriggerFormData +) => { + const { snapshot } = callbackHelpers; + const dispatcher = await snapshot.getPromise(dispatcherState); + const lgFiles = await snapshot.getPromise(lgFilesSelectorFamily(projectId)); + const luFiles = await snapshot.getPromise(luFilesState(projectId)); + const dialogs = await snapshot.getPromise(dialogsSelectorFamily(projectId)); + const dialog = await snapshot.getPromise(dialogState({ projectId, dialogId })); + const schemas = await snapshot.getPromise(schemasState(projectId)); + const locale = await snapshot.getPromise(localeState(projectId)); + + const { createLuIntent, createLgTemplates } = dispatcher; + + const lgFile = lgFiles.find((file) => file.id === `${dialogId}.${locale}`); + const luFile = luFiles.find((file) => file.id === `${dialogId}.${locale}`); + + if (!luFile) throw new Error(`lu file ${dialogId} not found`); + if (!lgFile) throw new Error(`lg file ${dialogId} not found`); + if (!dialog) throw new Error(`dialog ${dialogId} not found`); + const newDialog = generateNewDialog(dialogs, dialog.id, formData, schemas.sdk?.content); + const index = get(newDialog, 'content.triggers', []).length - 1; + if (formData.$kind === intentTypeKey && formData.triggerPhrases) { + const intent = { Name: formData.intent, Body: formData.triggerPhrases }; + luFile && (await createLuIntent({ id: luFile.id, intent, projectId })); + } else if (formData.$kind === qnaMatcherKey) { + const designerId1 = getDesignerIdFromDialogPath( + newDialog, + `content.triggers[${index}].actions[0].actions[1].prompt` + ); + const designerId2 = getDesignerIdFromDialogPath( + newDialog, + `content.triggers[${index}].actions[0].elseActions[0].activity` + ); + const lgTemplates: LgTemplate[] = [ + LgTemplateSamples.TextInputPromptForQnAMatcher(designerId1) as LgTemplate, + LgTemplateSamples.SendActivityForQnAMatcher(designerId2) as LgTemplate, + ]; + await createLgTemplates({ id: lgFile.id, templates: lgTemplates, projectId }); + } else if (formData.$kind === onChooseIntentKey) { + const designerId1 = getDesignerIdFromDialogPath(newDialog, `content.triggers[${index}].actions[4].prompt`); + const designerId2 = getDesignerIdFromDialogPath( + newDialog, + `content.triggers[${index}].actions[5].elseActions[0].activity` + ); + const lgTemplates1: LgTemplate[] = [ + LgTemplateSamples.TextInputPromptForOnChooseIntent(designerId1) as LgTemplate, + LgTemplateSamples.SendActivityForOnChooseIntent(designerId2) as LgTemplate, + ]; + + let lgTemplates2: LgTemplate[] = [ + LgTemplateSamples.adaptiveCardJson as LgTemplate, + LgTemplateSamples.whichOneDidYouMean as LgTemplate, + LgTemplateSamples.pickOne as LgTemplate, + LgTemplateSamples.getAnswerReadBack as LgTemplate, + LgTemplateSamples.getIntentReadBack as LgTemplate, + ]; + const commonlgFile = lgFiles.find(({ id }) => id === `common.${locale}`); + + lgTemplates2 = lgTemplates2.filter((t) => commonlgFile?.templates.findIndex((clft) => clft.name === t.name) === -1); + + await createLgTemplates({ id: `common.${locale}`, templates: lgTemplates2, projectId }); + await createLgTemplates({ id: lgFile.id, templates: lgTemplates1, projectId }); + } + return { + id: newDialog.id, + projectId, + content: newDialog.content, + }; +}; + export const triggerDispatcher = () => { const createTrigger = useRecoilCallback( (callbackHelpers: CallbackInterface) => async ( @@ -48,75 +122,12 @@ export const triggerDispatcher = () => { try { const { snapshot } = callbackHelpers; const dispatcher = await snapshot.getPromise(dispatcherState); - const lgFiles = await snapshot.getPromise(lgFilesSelectorFamily(projectId)); - const luFiles = await snapshot.getPromise(luFilesState(projectId)); - const dialogs = await snapshot.getPromise(dialogsSelectorFamily(projectId)); - const dialog = await snapshot.getPromise(dialogState({ projectId, dialogId })); - const schemas = await snapshot.getPromise(schemasState(projectId)); - const locale = await snapshot.getPromise(localeState(projectId)); - - const { createLuIntent, createLgTemplates, updateDialog, selectTo } = dispatcher; - - const lgFile = lgFiles.find((file) => file.id === `${dialogId}.${locale}`); - const luFile = luFiles.find((file) => file.id === `${dialogId}.${locale}`); - - if (!luFile) throw new Error(`lu file ${dialogId} not found`); - if (!lgFile) throw new Error(`lg file ${dialogId} not found`); - if (!dialog) throw new Error(`dialog ${dialogId} not found`); - const newDialog = generateNewDialog(dialogs, dialog.id, formData, schemas.sdk?.content); - const index = get(newDialog, 'content.triggers', []).length - 1; - if (formData.$kind === intentTypeKey && formData.triggerPhrases) { - const intent = { Name: formData.intent, Body: formData.triggerPhrases }; - luFile && (await createLuIntent({ id: luFile.id, intent, projectId })); - } else if (formData.$kind === qnaMatcherKey) { - const designerId1 = getDesignerIdFromDialogPath( - newDialog, - `content.triggers[${index}].actions[0].actions[1].prompt` - ); - const designerId2 = getDesignerIdFromDialogPath( - newDialog, - `content.triggers[${index}].actions[0].elseActions[0].activity` - ); - const lgTemplates: LgTemplate[] = [ - LgTemplateSamples.TextInputPromptForQnAMatcher(designerId1) as LgTemplate, - LgTemplateSamples.SendActivityForQnAMatcher(designerId2) as LgTemplate, - ]; - await createLgTemplates({ id: lgFile.id, templates: lgTemplates, projectId }); - } else if (formData.$kind === onChooseIntentKey) { - const designerId1 = getDesignerIdFromDialogPath(newDialog, `content.triggers[${index}].actions[4].prompt`); - const designerId2 = getDesignerIdFromDialogPath( - newDialog, - `content.triggers[${index}].actions[5].elseActions[0].activity` - ); - const lgTemplates1: LgTemplate[] = [ - LgTemplateSamples.TextInputPromptForOnChooseIntent(designerId1) as LgTemplate, - LgTemplateSamples.SendActivityForOnChooseIntent(designerId2) as LgTemplate, - ]; - - let lgTemplates2: LgTemplate[] = [ - LgTemplateSamples.adaptiveCardJson as LgTemplate, - LgTemplateSamples.whichOneDidYouMean as LgTemplate, - LgTemplateSamples.pickOne as LgTemplate, - LgTemplateSamples.getAnswerReadBack as LgTemplate, - LgTemplateSamples.getIntentReadBack as LgTemplate, - ]; - const commonlgFile = lgFiles.find(({ id }) => id === `common.${locale}`); - - lgTemplates2 = lgTemplates2.filter( - (t) => commonlgFile?.templates.findIndex((clft) => clft.name === t.name) === -1 - ); - - await createLgTemplates({ id: `common.${locale}`, templates: lgTemplates2, projectId }); - await createLgTemplates({ id: lgFile.id, templates: lgTemplates1, projectId }); - } - const dialogPayload = { - id: newDialog.id, - projectId, - content: newDialog.content, - }; + const { updateDialog, selectTo } = dispatcher; + const dialogPayload = await getNewDialogWithTrigger(callbackHelpers, projectId, dialogId, formData); await updateDialog(dialogPayload); if (autoSelected) { - await selectTo(projectId, newDialog.id, `triggers[${index}]`); + const index = get(dialogPayload.content, 'triggers', []).length - 1; + await selectTo(projectId, dialogPayload.id, `triggers[${index}]`); } } catch (ex) { setError(callbackHelpers, ex); @@ -178,9 +189,36 @@ export const triggerDispatcher = () => { } ); + const createTriggerWithAction = useRecoilCallback( + (callbackHelpers: CallbackInterface) => async ( + projectId: string, + dialogId: string, + formData: TriggerFormData, + actions: object, + autoSelected = true + ) => { + try { + const { snapshot } = callbackHelpers; + const dispatcher = await snapshot.getPromise(dispatcherState); + const { updateDialog, selectTo } = dispatcher; + const dialogPayload = await getNewDialogWithTrigger(callbackHelpers, projectId, dialogId, formData); + const index = get(dialogPayload.content, 'triggers', []).length - 1; + dialogPayload.content.triggers[index].actions = actions; + console.log(dialogPayload.content); + + await updateDialog(dialogPayload); + if (autoSelected) { + await selectTo(projectId, dialogPayload.id, `triggers[${index}]`); + } + } catch (ex) { + setError(callbackHelpers, ex); + } + } + ); return { createTrigger, deleteTrigger, createQnATrigger, + createTriggerWithAction, }; }; diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index fd6cb7a0cf..2e5a42066a 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -203,6 +203,9 @@ "add_new_variation_e49425ea": { "message": "Add new variation" }, + "add_or_edit_phrases_to_trigger_bookings_skill_8c0147ab": { + "message": "Add or edit phrases to trigger Bookings skill" + }, "add_property_d381eba3": { "message": "Add Property" }, @@ -746,6 +749,9 @@ "connecting_to_b_targetname_b_to_import_bot_content_65d8db95": { "message": "Connecting to { targetName } to import bot content..." }, + "connecting_to_skills_works_best_with_the_orchestra_bb3b2e38": { + "message": "Connecting to skills works best with the Orchestrator recognizer at the root bot (TodoBotSample)" + }, "continue_ac067716": { "message": "Continue" }, @@ -821,6 +827,9 @@ "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, + "create_a_publish_profile_83a61c3f": { + "message": " Create a publish profile" + }, "create_a_skill_in_your_bot_d7659e6b": { "message": "Create a skill in your bot" }, @@ -1256,6 +1265,9 @@ "enable_multi_turn_extraction_8a168892": { "message": "Enable multi-turn extraction" }, + "enable_orchestrator_cdbbd2c5": { + "message": "Enable Orchestrator" + }, "enable_speech_e30d6a2a": { "message": "Enable Speech" }, @@ -1583,6 +1595,9 @@ "get_activity_members_11339605": { "message": "Get activity members" }, + "get_an_overview_ef09e016": { + "message": " Get an overview " + }, "get_conversation_members_71602275": { "message": "Get conversation members" }, @@ -1778,6 +1793,9 @@ "intentname_is_missing_or_empty_e49db2f8": { "message": "intentName is missing or empty" }, + "intents_9b8593e0": { + "message": "Intents" + }, "interpolated_string_c96053f2": { "message": "Interpolated string." }, @@ -1853,6 +1871,9 @@ "learn_about_adaptive_expressions_fb1b6c3c": { "message": "Learn about Adaptive expressions" }, + "learn_how_to_build_a_skill_dbc58063": { + "message": " learn how to build a skill " + }, "learn_more_14816ec": { "message": "Learn More." }, @@ -2498,9 +2519,6 @@ "please_select_a_trigger_type_67417abb": { "message": "Please select a trigger type" }, - "please_select_a_valid_endpoint_bf608af1": { - "message": "Please select a valid endpoint" - }, "please_select_a_version_of_the_manifest_schema_4a3efbb1": { "message": "Please select a version of the manifest schema" }, @@ -2663,6 +2681,9 @@ "published_4bb5209e": { "message": "Published" }, + "publisher_bf6195cf": { + "message": "Publisher" + }, "publishing_count_bots_b2a7f564": { "message": "Publishing { count } bots" }, @@ -2969,6 +2990,9 @@ "select_input_hint_267a6208": { "message": "Select input hint" }, + "select_intents_to_trigger_bookings_skill_fa2bc681": { + "message": "Select intents to trigger Bookings skill" + }, "select_language_to_delete_d1662d3d": { "message": "Select language to delete" }, @@ -3086,12 +3110,15 @@ "skill_host_endpoint_url_e68b65f6": { "message": "Skill host endpoint url" }, - "skill_manifest_endpoint_is_configured_improperly_e083731d": { - "message": "Skill manifest endpoint is configured improperly" + "skill_manifest_url_be7ef8d0": { + "message": "Skill Manifest Url" }, "skillname_manifest_ef3d9fed": { "message": "{ skillName } Manifest" }, + "skills_extend_your_bot_s_conversational_capabiliti_ce5c2384": { + "message": "Skills extend your bot''s conversational capabilities . To know more about skills" + }, "something_happened_while_attempting_to_pull_e_952c7afe": { "message": "Something happened while attempting to pull: { e }" }, @@ -3392,6 +3419,9 @@ "title_msg_ee91458d": { "message": "{ title }. { msg }" }, + "to_add_a_skill_your_bot_todobotwithluissample_must_ece86fa": { + "message": "To add a skill, your bot ToDoBotWithLuisSample must have publish profile" + }, "to_learn_more_a_visit_this_document_a_ce188d8": { "message": "To learn more, visit this document." }, @@ -3410,6 +3440,9 @@ "to_learn_more_about_the_title_a_visit_its_document_c302e9b1": { "message": "To learn more about the { title }, visit its documentation page." }, + "to_make_sure_the_skill_will_work_correctly_we_perf_8de42615": { + "message": "To make sure the skill will work correctly, we perform some validation checks. When you’re ready to add a skill, enter the Skill manifest URL provided to you by the skill author." + }, "to_make_your_bot_available_for_others_as_a_skill_w_f2c19b9c": { "message": "To make your bot available for others as a skill, we need to generate a manifest." }, @@ -3599,15 +3632,15 @@ "user_is_typing_typing_activity_cd938615": { "message": "User is typing (Typing activity)" }, - "validating_35b79a96": { - "message": "Validating..." - }, "validation_b10c677c": { "message": "Validation" }, "validation_rules_efd3144d": { "message": "Validation Rules" }, + "valify_url_d4289305": { + "message": "Valify Url" + }, "value_d842f16d": { "message": "Value" }, From e77a10772f821b7013535a67df6046bedc8bf96d Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 1 Apr 2021 12:00:34 +0800 Subject: [PATCH 05/38] import orchestrator after add remote skill --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 173 +++++++++--------- .../AddRemoteSkillModal/Orchestractor.tsx | 61 ++++++ .../OrchestractorModal.tsx | 53 ------ ...SelectIntentModal.tsx => SelectIntent.tsx} | 134 ++++++++------ .../AddRemoteSkillModal/createActionHelper.ts | 20 -- .../components/AddRemoteSkillModal/helper.ts | 54 ++++++ Composer/packages/client/src/constants.ts | 7 +- .../client/src/pages/design/Modals.tsx | 2 +- 8 files changed, 287 insertions(+), 217 deletions(-) create mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx delete mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/OrchestractorModal.tsx rename Composer/packages/client/src/components/AddRemoteSkillModal/{SelectIntentModal.tsx => SelectIntent.tsx} (67%) delete mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts create mode 100644 Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 060de885e4..7585c37d4a 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -9,21 +9,21 @@ import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; import debounce from 'lodash/debounce'; -import { isUsingAdaptiveRuntime } from '@bfc/shared'; +import { isUsingAdaptiveRuntime, SDKKinds } from '@bfc/shared'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; -import { settingsState, skillsStateSelector, luFilesState, designPageLocationState } from '../../recoilModel'; +import { settingsState, luFilesState, designPageLocationState } from '../../recoilModel'; import { addSkillDialog } from '../../constants'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; import { TriggerFormData } from '../../utils/dialogUtil'; - -import { SelectIntentModal } from './SelectIntentModal'; +import { selectIntentDialog } from '../../constants'; +import { dispatcherState } from '../../recoilModel'; +import { SelectIntent } from './SelectIntent'; import { SkillDetail } from './SkillDetail'; -import { createActionFromManifest } from './createActionHelper'; - +import { createActionFromManifest } from './helper'; export interface SkillFormDataErrors { endpoint?: string; manifestUrl?: string; @@ -65,19 +65,23 @@ const getTriggerFormData = (intent: string, content: string): TriggerFormData => export const CreateSkillModal: React.FC = (props) => { const { projectId, addRemoteSkill, addTriggerToRoot, onDismiss } = props; - const { publishTargets, languages, luFeatures, runtime } = useRecoilValue(settingsState(projectId)); - const { dialogId } = useRecoilValue(designPageLocationState(projectId)); + const [title, setTitle] = useState({ + subText: '', + title: addSkillDialog.SKILL_MANIFEST_FORM.title, + }); const [showIntentSelectDialog, setShowIntentSelectDialog] = useState(false); - const [formData, setFormData] = useState<{ manifestUrl: string; endpointName: string }>({ manifestUrl: '', endpointName: '', }); const [formDataErrors, setFormDataErrors] = useState({}); - const [skillManifest, setSkillManifest] = useState(null); + + const { languages, luFeatures, runtime } = useRecoilValue(settingsState(projectId)); + const { dialogId } = useRecoilValue(designPageLocationState(projectId)); const luFiles = useRecoilValue(luFilesState(projectId)); + const { updateRecognizer } = useRecoilValue(dispatcherState); const debouncedValidateManifestURl = useRef(debounce(validateManifestUrl, 500)).current; @@ -117,107 +121,112 @@ export const CreateSkillModal: React.FC = (props) => { [projectId, formData] ); - const handleSubmit = (event, content) => { + const handleSubmit = (event, content: string, enable: boolean) => { event.preventDefault(); // add a remote skill const triggerFormData = getTriggerFormData(skillManifest.name, content); addRemoteSkill(formData.manifestUrl, formData.endpointName); TelemetryClient.track('AddNewSkillCompleted'); // add trigger to root bot - const action = createActionFromManifest(content); + const action = createActionFromManifest(skillManifest); addTriggerToRoot(dialogId, triggerFormData, [action]); TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); + if (enable) { + // update recognizor type to orchestrator + updateRecognizer(projectId, dialogId, SDKKinds.OrchestratorRecognizer); + } }; return ( - + {showIntentSelectDialog ? ( - { + setTitle({ + subText: '', + title: addSkillDialog.SKILL_MANIFEST_FORM.title, + }); + setShowIntentSelectDialog(false); + }} onDismiss={onDismiss} onSubmit={handleSubmit} /> ) : ( - - -
- {addSkillDialog.SKILL_MANIFEST_FORM.preSubText} - - {formatMessage(' Get an overview ')} - - or - - {formatMessage(' learn how to build a skill ')} - - {addSkillDialog.SKILL_MANIFEST_FORM.afterSubText} + +
+ {addSkillDialog.SKILL_MANIFEST_FORM.preSubText} + + {formatMessage(' Get an overview ')} + + or + + {formatMessage(' learn how to build a skill ')} + + {addSkillDialog.SKILL_MANIFEST_FORM.afterSubText} +
+ + +
+
+ {skillManifest && ( + + +
+ +
+
+ )} +
+ - -
- -
- {skillManifest && ( - - -
- -
-
- )} -
- - - - - {skillManifest ? ( - isUsingAdaptiveRuntime(runtime) && luFiles.length > 0 ? ( - { - setShowIntentSelectDialog(true); - }} - /> - ) : ( - { - addRemoteSkill(formData.manifestUrl, formData.endpointName); - }} - /> - ) + + + {skillManifest ? ( + isUsingAdaptiveRuntime(runtime) && luFiles.length > 0 ? ( + { + setTitle(selectIntentDialog.SELECT_INTENT(dialogId, skillManifest.name)); + setShowIntentSelectDialog(true); + }} + /> ) : ( { + addRemoteSkill(formData.manifestUrl, formData.endpointName); + }} /> - )} - - -
- + ) + ) : ( + + )} + + + )} - + ); }; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx new file mode 100644 index 0000000000..73561e5137 --- /dev/null +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React, { useState, Fragment } 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'; + +export const Orchestractor = (props) => { + const { projectId, onSubmit, onBack } = props; + const [enable, setEnable] = useState(false); + const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); + const onChange = (ev, check) => { + setEnable(check); + }; + return ( + + + +
+ {enableOrchestratorDialog.content} + + {formatMessage('\n learn more about Orchestractor')} + +
+ +
+ + + + + { + onSubmit(event, enable); + if (enable) { + // TODO. show notification + // download orchestrator first + importOrchestractor(projectId, reloadProject, setApplicationLevelError); + // TODO. update notification + } + }} + /> + + +
+
+ ); +}; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/OrchestractorModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/OrchestractorModal.tsx deleted file mode 100644 index 8cca24685d..0000000000 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/OrchestractorModal.tsx +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import React, { useState } from 'react'; -import formatMessage from 'format-message'; -import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; -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 { enableOrchestratorDialog } from '../../constants'; - -export const OrchestractorModal = (props) => { - const { onDismiss, dialogId, onSubmit } = props; - const [enable, setEnable] = useState(false); - const onChange = (ev, check) => { - setEnable(check); - }; - return ( - - -
- {enableOrchestratorDialog.subText}({dialogId}) - - {formatMessage(' learn more about Orchestractor')} - -
- - - { - // download orchestrator - onSubmit(); - }} - /> - - -
-
- ); -}; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx similarity index 67% rename from Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx rename to Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 7e1a40041d..3ab0bcd624 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntentModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -4,7 +4,6 @@ import { jsx, css } from '@emotion/core'; import React, { Fragment, useState, useMemo, useEffect, useCallback } from 'react'; import formatMessage from 'format-message'; -import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { DetailsList, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList'; import { Selection } from 'office-ui-fabric-react/lib/Selection'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; @@ -21,7 +20,7 @@ import httpClient from '../../utils/httpUtil'; import luWorker from '../../recoilModel/parsers/luWorker'; import { localeState, dispatcherState } from '../../recoilModel'; -import { OrchestractorModal } from './OrchestractorModal'; +import { Orchestractor } from './Orchestractor'; const detailListContainer = css` width: 100%; @@ -32,7 +31,7 @@ const detailListContainer = css` border: 1px solid E5E5E5; `; -interface SelectIntentModalProps { +interface SelectIntentProps { manifest: { dispatchModels: { intents: Array | object; @@ -45,8 +44,10 @@ interface SelectIntentModalProps { luFeatures: any; rootLuFiles: LuFile[]; dialogId: string; - onSubmit: (event, content: string) => void; + onSubmit: (event, content: string, enable: boolean) => void; onDismiss: () => void; + setTitle: (value) => void; + onBack: () => void; } const columns = [ @@ -96,8 +97,19 @@ const getParsedLuFiles = async (files: { id: string; content: string }[], luFeat return luFiles; }; -export const SelectIntentModal: React.FC = (props) => { - const { manifest, onSubmit, onDismiss, languages, luFeatures, projectId, rootLuFiles, dialogId } = props; +export const SelectIntent: React.FC = (props) => { + const { + manifest, + onSubmit, + onDismiss, + languages, + luFeatures, + projectId, + rootLuFiles, + dialogId, + setTitle, + onBack, + } = props; const [page, setPage] = useState(0); const [selectedIntents, setSelectedIntents] = useState>([]); // luFiles from manifest @@ -108,7 +120,6 @@ export const SelectIntentModal: React.FC = (props) => { const [displayIntents, setDisplayIntent] = useState>([]); const [displayContent, setDisplayContent] = useState(''); // const [luFileError, setError] = useState(); - const [title, setTitle] = useState(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); const locale = useRecoilValue(localeState(projectId)); const [showOrchestratorDialog, setShowOrchestratorDialog] = useState(false); const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); @@ -193,68 +204,71 @@ export const SelectIntentModal: React.FC = (props) => { return ( {showOrchestratorDialog ? ( - { + { + setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); + setShowOrchestratorDialog(false); + }} + onSubmit={(ev, enable) => { // append remote lufile into root lu file updateLuFile(displayContent); // add trigger to root - onSubmit(ev, displayContent); + onSubmit(ev, displayContent, enable); }} /> ) : ( - - - {page === 0 ? ( - - -
- - - -
-
+ + {page === 0 ? ( + + +
+ + + +
+
+ ) : ( + + + + )} + + + {page === 1 ? ( + { + setPage(page - 1); + setTitle(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); + }} + /> ) : ( - - - + )} - - - {page === 1 && ( - { - setPage(page - 1); - setTitle(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); - }} - /> - )} - - - { - if (page === 1) { - setShowOrchestratorDialog(true); - } else { - setPage(page + 1); - setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); - } - }} - /> - - + + + { + if (page === 1) { + setShowOrchestratorDialog(true); + } else { + setPage(page + 1); + setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); + } + }} + /> + -
+ )}
); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts deleted file mode 100644 index 4820d4e94e..0000000000 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/createActionHelper.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import { generateDesignerId } from '@bfc/shared'; - -export const createActionFromManifest = (manifest) => { - return { - $kind: 'Microsoft.BeginSkill', - $designer: { - id: `${generateDesignerId()}`, - }, - activityProcessed: true, - botId: '=settings.MicrosoftAppId', - skillHostEndpoint: '=settings.skillHostEndpoint', - connectionName: '=settings.connectionName', - allowInterruptions: true, - skillEndpoint: `=settings.skill['${manifest.name}'].endpointUrl`, - skillAppId: `=settings.skill['${manifest.name}'].msAppId`, - }; -}; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts new file mode 100644 index 0000000000..35a50401f3 --- /dev/null +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { generateDesignerId } from '@bfc/shared'; +import formatMessage from 'format-message'; + +import httpClient from '../../utils/httpUtil'; +import TelemetryClient from '../../telemetry/TelemetryClient'; + +export const createActionFromManifest = (manifest) => { + console.log(manifest); + return { + $kind: 'Microsoft.BeginSkill', + $designer: { + id: `${generateDesignerId()}`, + }, + activityProcessed: true, + botId: '=settings.MicrosoftAppId', + skillHostEndpoint: '=settings.skillHostEndpoint', + connectionName: '=settings.connectionName', + allowInterruptions: true, + skillEndpoint: `=settings.skill['${manifest.name}'].endpointUrl`, + skillAppId: `=settings.skill['${manifest.name}'].msAppId`, + }; +}; + +export const importOrchestractor = async (projectId: string, reloadProject, setApplicationLevelError) => { + const reqBody = { + package: 'Microsoft.Bot.Components.Orchestrator', + version: '1.0.0-preview.20210310.a7ff2d0', + source: 'https://api.nuget.org/v3/index.json', + isUpdating: false, + }; + try { + const results = await httpClient.post(`projects/${projectId}/import`, reqBody); + // check to see if there was a conflict that requires confirmation + if (results.data.success === false) { + TelemetryClient.track('PackageInstallConflictFound', { ...reqBody, isUpdate: reqBody.isUpdating }); + } else { + TelemetryClient.track('PackageInstalled', { ...reqBody, isUpdate: reqBody.isUpdating }); + // reload modified content + await reloadProject(projectId); + } + } catch (err) { + TelemetryClient.track('PackageInstallFailed', { ...reqBody, isUpdate: reqBody.isUpdating }); + + console.error(err); + setApplicationLevelError({ + status: err.response.status, + message: err.response && err.response.data.message ? err.response.data.message : err, + summary: formatMessage('Install Error'), + }); + } +}; diff --git a/Composer/packages/client/src/constants.ts b/Composer/packages/client/src/constants.ts index f4d9d59750..17d9c60628 100644 --- a/Composer/packages/client/src/constants.ts +++ b/Composer/packages/client/src/constants.ts @@ -338,7 +338,12 @@ export const enableOrchestratorDialog = { return formatMessage('Enable Orchestrator'); }, get subText() { - return formatMessage('Connecting to skills works best with the Orchestrator recognizer at the root bot'); + return formatMessage('Enable orchestrator as the recognizer at the root dialog to add this skill'); + }, + 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.' + ); }, }; diff --git a/Composer/packages/client/src/pages/design/Modals.tsx b/Composer/packages/client/src/pages/design/Modals.tsx index feeaa1645b..b0d567dd06 100644 --- a/Composer/packages/client/src/pages/design/Modals.tsx +++ b/Composer/packages/client/src/pages/design/Modals.tsx @@ -134,7 +134,7 @@ const Modals: React.FC = ({ projectId = '' }) => { }} addTriggerToRoot={(dialogId, formData, actions) => { console.log(actions); - createTriggerWithAction(projectId, dialogId, formData, actions); + createTriggerWithAction(projectId, dialogId, formData, actions, false); }} projectId={projectId} onDismiss={() => { From 1cf03563af760cd1705a5c908c1476d9e3df447e Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 1 Apr 2021 16:16:08 +0800 Subject: [PATCH 06/38] add loading spinner --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 7 +++-- .../packages/server/src/locales/en-US.json | 30 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 7585c37d4a..07ee9c6e1e 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -13,6 +13,7 @@ import { isUsingAdaptiveRuntime, SDKKinds } from '@bfc/shared'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; +import { LoadingSpinner } from '../../components/LoadingSpinner'; import { settingsState, luFilesState, designPageLocationState } from '../../recoilModel'; import { addSkillDialog } from '../../constants'; @@ -77,6 +78,7 @@ export const CreateSkillModal: React.FC = (props) => { }); const [formDataErrors, setFormDataErrors] = useState({}); const [skillManifest, setSkillManifest] = useState(null); + const [showDetail, setShowDetail] = useState(false); const { languages, luFeatures, runtime } = useRecoilValue(settingsState(projectId)); const { dialogId } = useRecoilValue(designPageLocationState(projectId)); @@ -107,6 +109,7 @@ export const CreateSkillModal: React.FC = (props) => { const validateUrl = useCallback( async (event) => { event.preventDefault(); + setShowDetail(true); try { const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, { params: { @@ -182,11 +185,11 @@ export const CreateSkillModal: React.FC = (props) => { onChange={handleManifestUrlChange} />
- {skillManifest && ( + {showDetail && (
- + {skillManifest ? : }
)} diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 2e5a42066a..c50f092761 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -203,9 +203,6 @@ "add_new_variation_e49425ea": { "message": "Add new variation" }, - "add_or_edit_phrases_to_trigger_bookings_skill_8c0147ab": { - "message": "Add or edit phrases to trigger Bookings skill" - }, "add_property_d381eba3": { "message": "Add Property" }, @@ -749,9 +746,6 @@ "connecting_to_b_targetname_b_to_import_bot_content_65d8db95": { "message": "Connecting to { targetName } to import bot content..." }, - "connecting_to_skills_works_best_with_the_orchestra_bb3b2e38": { - "message": "Connecting to skills works best with the Orchestrator recognizer at the root bot (TodoBotSample)" - }, "continue_ac067716": { "message": "Continue" }, @@ -827,9 +821,6 @@ "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, - "create_a_publish_profile_83a61c3f": { - "message": " Create a publish profile" - }, "create_a_skill_in_your_bot_d7659e6b": { "message": "Create a skill in your bot" }, @@ -1265,6 +1256,9 @@ "enable_multi_turn_extraction_8a168892": { "message": "Enable multi-turn extraction" }, + "enable_orchestrator_as_the_recognizer_at_the_root__1de64c98": { + "message": "Enable orchestrator as the recognizer at the root dialog to add this skill" + }, "enable_orchestrator_cdbbd2c5": { "message": "Enable Orchestrator" }, @@ -1754,6 +1748,9 @@ "insert_template_reference_bb33720e": { "message": "Insert template reference" }, + "install_error_a9319839": { + "message": "Install Error" + }, "install_microsoft_net_core_sdk_2de509f0": { "message": "Install Microsoft .NET Core SDK" }, @@ -1892,6 +1889,9 @@ "learn_more_about_manifests_6e7c364b": { "message": "Learn more about manifests" }, + "learn_more_about_orchestractor_d0c99460": { + "message": "\n learn more about Orchestractor" + }, "learn_more_about_skill_manifests_7708ce2c": { "message": "Learn more about skill manifests" }, @@ -2207,6 +2207,9 @@ "msg_bf173fef": { "message": "{ msg }" }, + "multi_bot_projects_work_best_with_the_orchestrator_8b80e480": { + "message": "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_choice_839b54bb": { "message": "Multi-choice" }, @@ -2990,9 +2993,6 @@ "select_input_hint_267a6208": { "message": "Select input hint" }, - "select_intents_to_trigger_bookings_skill_fa2bc681": { - "message": "Select intents to trigger Bookings skill" - }, "select_language_to_delete_d1662d3d": { "message": "Select language to delete" }, @@ -3119,6 +3119,9 @@ "skills_extend_your_bot_s_conversational_capabiliti_ce5c2384": { "message": "Skills extend your bot''s conversational capabilities . To know more about skills" }, + "skip_bcb86160": { + "message": "Skip" + }, "something_happened_while_attempting_to_pull_e_952c7afe": { "message": "Something happened while attempting to pull: { e }" }, @@ -3419,9 +3422,6 @@ "title_msg_ee91458d": { "message": "{ title }. { msg }" }, - "to_add_a_skill_your_bot_todobotwithluissample_must_ece86fa": { - "message": "To add a skill, your bot ToDoBotWithLuisSample must have publish profile" - }, "to_learn_more_a_visit_this_document_a_ce188d8": { "message": "To learn more, visit this document." }, From 86e819a63782dcd6f31d6539d03519d90b86ea66 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 1 Apr 2021 17:01:22 +0800 Subject: [PATCH 07/38] fix bug --- .../components/AddRemoteSkillModal/SkillDetail.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index e058d6b3e0..1dcc291c41 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -43,24 +43,25 @@ const text = css` `; export const SkillDetail: React.FC = (props) => { const { manifest } = props; + console.log(manifest); return (
-
{formatMessage(manifest.name)}
+
{formatMessage(manifest?.name || '')}
{formatMessage('Description')}
-
{formatMessage(manifest.description)}
+
{formatMessage(manifest?.description || '')}
{formatMessage('Version')}
-
{formatMessage(manifest.version)}
+
{formatMessage(manifest?.version || '')}
{formatMessage('Activities')}
-
{formatMessage(Object.keys(manifest.activities).join(', '))}
+
{formatMessage(Object.keys(manifest?.activities).join(', '))}
{formatMessage('Publisher')}
-
{formatMessage(manifest.publisherName)}
+
{formatMessage(manifest?.publisherName || '')}
); From b17ab63cdbd01fa52bf58c07f46ed695e5ad71b3 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 2 Apr 2021 15:12:56 +0800 Subject: [PATCH 08/38] add skill and alowCalled into appsettings --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 7 ++- .../AddRemoteSkillModal/SelectIntent.tsx | 5 +- .../recoilModel/dispatchers/botProjectFile.ts | 3 + .../src/recoilModel/dispatchers/setting.ts | 61 +++++++++++++++++++ 4 files changed, 73 insertions(+), 3 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 07ee9c6e1e..73733bc6dd 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -13,8 +13,8 @@ import { isUsingAdaptiveRuntime, SDKKinds } from '@bfc/shared'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; -import { LoadingSpinner } from '../../components/LoadingSpinner'; +import { LoadingSpinner } from '../../components/LoadingSpinner'; import { settingsState, luFilesState, designPageLocationState } from '../../recoilModel'; import { addSkillDialog } from '../../constants'; import httpClient from '../../utils/httpUtil'; @@ -22,6 +22,7 @@ import TelemetryClient from '../../telemetry/TelemetryClient'; import { TriggerFormData } from '../../utils/dialogUtil'; import { selectIntentDialog } from '../../constants'; import { dispatcherState } from '../../recoilModel'; + import { SelectIntent } from './SelectIntent'; import { SkillDetail } from './SkillDetail'; import { createActionFromManifest } from './helper'; @@ -83,7 +84,7 @@ export const CreateSkillModal: React.FC = (props) => { const { languages, luFeatures, runtime } = useRecoilValue(settingsState(projectId)); const { dialogId } = useRecoilValue(designPageLocationState(projectId)); const luFiles = useRecoilValue(luFilesState(projectId)); - const { updateRecognizer } = useRecoilValue(dispatcherState); + const { updateRecognizer, setSkillAndAllowCaller } = useRecoilValue(dispatcherState); const debouncedValidateManifestURl = useRef(debounce(validateManifestUrl, 500)).current; @@ -126,6 +127,8 @@ export const CreateSkillModal: React.FC = (props) => { const handleSubmit = (event, content: string, enable: boolean) => { event.preventDefault(); + // add skill to appsetting + setSkillAndAllowCaller(projectId, skillManifest, 0); // add a remote skill const triggerFormData = getTriggerFormData(skillManifest.name, content); addRemoteSkill(formData.manifestUrl, formData.endpointName); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 3ab0bcd624..548f6fe3dd 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -4,7 +4,7 @@ import { jsx, css } from '@emotion/core'; import React, { Fragment, useState, useMemo, useEffect, useCallback } from 'react'; import formatMessage from 'format-message'; -import { DetailsList, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList'; +import { DetailsList, SelectionMode, CheckboxVisibility } from 'office-ui-fabric-react/lib/DetailsList'; import { Selection } from 'office-ui-fabric-react/lib/Selection'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; @@ -198,6 +198,8 @@ export const SelectIntent: React.FC = (props) => { }) .join('\n'); setDisplayContent(intentsValue); + } else { + setDisplayContent(''); } }, [selectedIntents, currentLuFile]); @@ -225,6 +227,7 @@ export const SelectIntent: React.FC = (props) => {
{ skillsData: Skill, selectedEndpointIndex: number ) => { + console.log(skillNameIdentifier); + console.log(skillsData); + console.log(selectedEndpointIndex); const { set, snapshot } = callbackHelpers; const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector); if (!rootBotProjectId) { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index cbd11fb0c3..3b07f3c3d0 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -166,6 +166,66 @@ export const settingsDispatcher = () => { setRuntimeField(projectId, 'customRuntime', isOn); }); + const setSkillAndAllowCaller = useRecoilCallback( + ({ set, snapshot }: CallbackInterface) => async ( + projectId: string, + manifest: any, + selectedEndpointIndex: number + ) => { + const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector); + if (!rootBotProjectId) { + return; + } + + const settings = await snapshot.getPromise(settingsState(rootBotProjectId)); + + let msAppId = '', + endpointUrl = '', + endpointName = ''; + + console.log(manifest); + if (selectedEndpointIndex !== -1 && manifest) { + const data = manifest.endpoints[selectedEndpointIndex]; + msAppId = data.msAppId; + endpointUrl = data.endpointUrl; + endpointName = data.name; + } + // set(botProjectFileState(rootBotProjectId), (current) => { + // const result = produce(current, (draftState) => { + // draftState.content.skills[skillNameIdentifier].endpointName = endpointName; + // }); + // return result; + // }); + + // else { + // set(botProjectFileState(rootBotProjectId), (current) => { + // const result = produce(current, (draftState) => { + // delete draftState.content.skills[skillNameIdentifier].endpointName; + // }); + // return result; + // }); + // } + if (settings.skill) { + set(settingsState(projectId), (currentValue) => ({ + ...currentValue, + skill: { + ...settings.skill, + [manifest.name]: { + endpointUrl, + msAppId, + }, + }, + runtimeSettings: { + ...settings.runtimeSettings, + skills: { + allowedCallers: [...settings.runtimeSettings?.skills?.allowedCallers, msAppId], + }, + }, + })); + } + } + ); + const setQnASettings = useRecoilCallback( (callbackHelpers: CallbackInterface) => async (projectId: string, subscriptionKey: string) => { const { set } = callbackHelpers; @@ -196,5 +256,6 @@ export const settingsDispatcher = () => { setImportedLibraries, setCustomRuntime, setQnASettings, + setSkillAndAllowCaller, }; }; From c814826dfbf0a3f2be03dbeb86033e043b6b49cd Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 2 Apr 2021 22:16:57 +0800 Subject: [PATCH 09/38] fix skill identifier wrong in new action --- .../components/AddRemoteSkillModal/CreateSkillModal.tsx | 7 ++++--- .../client/src/components/AddRemoteSkillModal/helper.ts | 6 +++--- Composer/packages/client/src/pages/design/Modals.tsx | 4 ++-- .../client/src/recoilModel/dispatchers/botProjectFile.ts | 3 --- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 73733bc6dd..729cd2750c 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -38,7 +38,7 @@ export const msAppIdRegex = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A export interface CreateSkillModalProps { projectId: string; - addRemoteSkill: (manifestUrl: string, endpointName: string) => void; + addRemoteSkill: (manifestUrl: string, endpointName: string) => Promise; addTriggerToRoot: (dialogId: string, triggerFormData: TriggerFormData, action) => void; onDismiss: () => void; } @@ -127,14 +127,15 @@ export const CreateSkillModal: React.FC = (props) => { const handleSubmit = (event, content: string, enable: boolean) => { event.preventDefault(); + const skillEndpointIndex = 0; // add skill to appsetting - setSkillAndAllowCaller(projectId, skillManifest, 0); + setSkillAndAllowCaller(projectId, skillManifest, skillEndpointIndex); // add a remote skill const triggerFormData = getTriggerFormData(skillManifest.name, content); addRemoteSkill(formData.manifestUrl, formData.endpointName); TelemetryClient.track('AddNewSkillCompleted'); // add trigger to root bot - const action = createActionFromManifest(skillManifest); + const action = createActionFromManifest(skillManifest, skillEndpointIndex); addTriggerToRoot(dialogId, triggerFormData, [action]); TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); if (enable) { diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 35a50401f3..d048e839bb 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -7,7 +7,7 @@ import formatMessage from 'format-message'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; -export const createActionFromManifest = (manifest) => { +export const createActionFromManifest = (manifest, selectEndpointIndex: number) => { console.log(manifest); return { $kind: 'Microsoft.BeginSkill', @@ -19,8 +19,8 @@ export const createActionFromManifest = (manifest) => { skillHostEndpoint: '=settings.skillHostEndpoint', connectionName: '=settings.connectionName', allowInterruptions: true, - skillEndpoint: `=settings.skill['${manifest.name}'].endpointUrl`, - skillAppId: `=settings.skill['${manifest.name}'].msAppId`, + skillEndpoint: manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].endpointUrl : '', + skillAppId: manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].msAppId : '', }; }; diff --git a/Composer/packages/client/src/pages/design/Modals.tsx b/Composer/packages/client/src/pages/design/Modals.tsx index b0d567dd06..81380eeea0 100644 --- a/Composer/packages/client/src/pages/design/Modals.tsx +++ b/Composer/packages/client/src/pages/design/Modals.tsx @@ -128,9 +128,9 @@ const Modals: React.FC = ({ projectId = '' }) => { )} {showAddSkillDialogModal && ( { + addRemoteSkill={async (manifestUrl, endpointName) => { setAddSkillDialogModalVisibility(false); - addRemoteSkillToBotProject(manifestUrl, endpointName); + await addRemoteSkillToBotProject(manifestUrl, endpointName); }} addTriggerToRoot={(dialogId, formData, actions) => { console.log(actions); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts index 2768f47c6a..fe2416d572 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts @@ -114,9 +114,6 @@ export const botProjectFileDispatcher = () => { skillsData: Skill, selectedEndpointIndex: number ) => { - console.log(skillNameIdentifier); - console.log(skillsData); - console.log(selectedEndpointIndex); const { set, snapshot } = callbackHelpers; const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector); if (!rootBotProjectId) { From 3584a2f42a4e229396215bc055fd3e9ddd647fdd Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Tue, 6 Apr 2021 23:03:09 +0800 Subject: [PATCH 10/38] fix skill identifier --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 46 +++++++++++-------- .../AddRemoteSkillModal/SkillDetail.tsx | 1 - .../components/AddRemoteSkillModal/helper.ts | 19 +------- .../client/src/pages/design/Modals.tsx | 7 ++- .../src/recoilModel/dispatchers/project.ts | 2 + .../src/recoilModel/dispatchers/setting.ts | 46 ++++++------------- .../src/recoilModel/dispatchers/skill.ts | 2 +- .../src/recoilModel/dispatchers/trigger.ts | 15 ++++-- .../client/src/recoilModel/utils/skill.ts | 21 +++++++++ 9 files changed, 80 insertions(+), 79 deletions(-) create mode 100644 Composer/packages/client/src/recoilModel/utils/skill.ts diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 729cd2750c..b99cde826d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { Fragment, useRef, useState, useCallback } from 'react'; +import React, { Fragment, useRef, useState, useCallback, useEffect } from 'react'; import formatMessage from 'format-message'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; // import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; @@ -15,17 +15,16 @@ import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; import { LoadingSpinner } from '../../components/LoadingSpinner'; -import { settingsState, luFilesState, designPageLocationState } from '../../recoilModel'; +import { settingsState, designPageLocationState, dispatcherState, luFilesSelectorFamily } from '../../recoilModel'; import { addSkillDialog } from '../../constants'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; import { TriggerFormData } from '../../utils/dialogUtil'; import { selectIntentDialog } from '../../constants'; -import { dispatcherState } from '../../recoilModel'; import { SelectIntent } from './SelectIntent'; import { SkillDetail } from './SkillDetail'; -import { createActionFromManifest } from './helper'; + export interface SkillFormDataErrors { endpoint?: string; manifestUrl?: string; @@ -39,7 +38,7 @@ export const msAppIdRegex = /^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A export interface CreateSkillModalProps { projectId: string; addRemoteSkill: (manifestUrl: string, endpointName: string) => Promise; - addTriggerToRoot: (dialogId: string, triggerFormData: TriggerFormData, action) => void; + addTriggerToRoot: (dialogId: string, triggerFormData: TriggerFormData, skillId: string) => void; onDismiss: () => void; } @@ -83,8 +82,8 @@ export const CreateSkillModal: React.FC = (props) => { const { languages, luFeatures, runtime } = useRecoilValue(settingsState(projectId)); const { dialogId } = useRecoilValue(designPageLocationState(projectId)); - const luFiles = useRecoilValue(luFilesState(projectId)); - const { updateRecognizer, setSkillAndAllowCaller } = useRecoilValue(dispatcherState); + const luFiles = useRecoilValue(luFilesSelectorFamily(projectId)); + const { updateRecognizer } = useRecoilValue(dispatcherState); const debouncedValidateManifestURl = useRef(debounce(validateManifestUrl, 500)).current; @@ -127,23 +126,34 @@ export const CreateSkillModal: React.FC = (props) => { const handleSubmit = (event, content: string, enable: boolean) => { event.preventDefault(); - const skillEndpointIndex = 0; - // add skill to appsetting - setSkillAndAllowCaller(projectId, skillManifest, skillEndpointIndex); - // add a remote skill - const triggerFormData = getTriggerFormData(skillManifest.name, content); - addRemoteSkill(formData.manifestUrl, formData.endpointName); - TelemetryClient.track('AddNewSkillCompleted'); - // add trigger to root bot - const action = createActionFromManifest(skillManifest, skillEndpointIndex); - addTriggerToRoot(dialogId, triggerFormData, [action]); - TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); + // add a remote skill, add skill identifier into botProj file + addRemoteSkill(formData.manifestUrl, formData.endpointName).then(() => { + TelemetryClient.track('AddNewSkillCompleted'); + const result = location.href.split('/'); + let skillId = ''; + if (result.length > 0) skillId = result[result.length - 1]; + console.log(skillId); + // add trigger with connect to skill action to root bot + const triggerFormData = getTriggerFormData(skillManifest.name, content); + addTriggerToRoot(dialogId, triggerFormData, skillId); + TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); + }); + if (enable) { // update recognizor type to orchestrator updateRecognizer(projectId, dialogId, SDKKinds.OrchestratorRecognizer); } }; + useEffect(() => { + if (skillManifest?.endpoints) { + setFormData({ + ...formData, + endpointName: skillManifest.endpoints[0].name, + }); + } + }, [skillManifest]); + return ( {showIntentSelectDialog ? ( diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index 1dcc291c41..603a611953 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -43,7 +43,6 @@ const text = css` `; export const SkillDetail: React.FC = (props) => { const { manifest } = props; - console.log(manifest); return (
{formatMessage(manifest?.name || '')}
diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 3fde3e75fa..27f8e6cfd1 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -1,32 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { generateDesignerId } from '@bfc/shared'; import formatMessage from 'format-message'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; -export const createActionFromManifest = (manifest, selectEndpointIndex: number) => { - return { - $kind: 'Microsoft.BeginSkill', - $designer: { - id: `${generateDesignerId()}`, - }, - activityProcessed: true, - botId: '=settings.MicrosoftAppId', - skillHostEndpoint: '=settings.skillHostEndpoint', - connectionName: '=settings.connectionName', - allowInterruptions: true, - skillEndpoint: manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].endpointUrl : '', - skillAppId: manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].msAppId : '', - }; -}; - export const importOrchestractor = async (projectId: string, reloadProject, setApplicationLevelError) => { const reqBody = { package: 'Microsoft.Bot.Components.Orchestrator', - version: '1.0.0-preview.20210310.a7ff2d0', + version: '', source: 'https://api.nuget.org/v3/index.json', isUpdating: false, }; diff --git a/Composer/packages/client/src/pages/design/Modals.tsx b/Composer/packages/client/src/pages/design/Modals.tsx index 91e218a4ea..a06f4bc756 100644 --- a/Composer/packages/client/src/pages/design/Modals.tsx +++ b/Composer/packages/client/src/pages/design/Modals.tsx @@ -60,7 +60,7 @@ const Modals: React.FC = ({ projectId = '' }) => { createQnAKBsFromUrls, createQnAKBFromScratch, createTrigger, - createTriggerWithAction, + createTriggerForRemoteSkill, createQnATrigger, createDialogCancel, } = useRecoilValue(dispatcherState); @@ -130,9 +130,8 @@ const Modals: React.FC = ({ projectId = '' }) => { setAddSkillDialogModalVisibility(false); await addRemoteSkillToBotProject(manifestUrl, endpointName); }} - addTriggerToRoot={(dialogId, formData, actions) => { - console.log(actions); - createTriggerWithAction(projectId, dialogId, formData, actions, false); + addTriggerToRoot={(dialogId, formData, skillId) => { + createTriggerForRemoteSkill(projectId, dialogId, formData, skillId, false); }} projectId={projectId} onDismiss={() => { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 0127be9242..2ef68471ff 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -166,6 +166,8 @@ export const projectDispatcher = () => { const { projectId } = await openRemoteSkill(callbackHelpers, manifestUrl); set(botProjectIdsState, (current) => [...current, projectId]); await dispatcher.addRemoteSkillToBotProjectFile(projectId, manifestUrl, endpointName); + // update appsetting + await dispatcher.setSkillAndAllowCaller(rootBotProjectId, projectId, endpointName); navigateToSkillBot(rootBotProjectId, projectId); } catch (ex) { handleProjectFailure(callbackHelpers, ex); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 3b07f3c3d0..ce09506171 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -12,6 +12,8 @@ import cloneDeep from 'lodash/cloneDeep'; import settingStorage from '../../utils/dialogSettingStorage'; import { settingsState } from '../atoms/botState'; import { rootBotProjectIdSelector, botProjectSpaceSelector } from '../selectors/project'; +import { skillsStateSelector } from '../selectors'; +import { botNameIdentifierState } from '../atoms'; import httpClient from './../../utils/httpUtil'; import { setError } from './shared'; @@ -167,50 +169,28 @@ export const settingsDispatcher = () => { }); const setSkillAndAllowCaller = useRecoilCallback( - ({ set, snapshot }: CallbackInterface) => async ( - projectId: string, - manifest: any, - selectedEndpointIndex: number - ) => { + ({ set, snapshot }: CallbackInterface) => async (projectId: string, skillId: string, endpointName: string) => { const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector); if (!rootBotProjectId) { return; } - + const manifestIdentifier = await snapshot.getPromise(botNameIdentifierState(skillId)); const settings = await snapshot.getPromise(settingsState(rootBotProjectId)); - - let msAppId = '', - endpointUrl = '', - endpointName = ''; - - console.log(manifest); - if (selectedEndpointIndex !== -1 && manifest) { - const data = manifest.endpoints[selectedEndpointIndex]; - msAppId = data.msAppId; - endpointUrl = data.endpointUrl; - endpointName = data.name; + const skills = await snapshot.getPromise(skillsStateSelector); + const manifest = skills[manifestIdentifier]?.manifest; + let msAppId, endpointUrl; + + if (manifest?.endpoints) { + const cur = manifest.endpoints.find((item) => item.name === endpointName); + endpointUrl = cur?.endpointUrl || ''; + msAppId = cur?.msAppId || ''; } - // set(botProjectFileState(rootBotProjectId), (current) => { - // const result = produce(current, (draftState) => { - // draftState.content.skills[skillNameIdentifier].endpointName = endpointName; - // }); - // return result; - // }); - - // else { - // set(botProjectFileState(rootBotProjectId), (current) => { - // const result = produce(current, (draftState) => { - // delete draftState.content.skills[skillNameIdentifier].endpointName; - // }); - // return result; - // }); - // } if (settings.skill) { set(settingsState(projectId), (currentValue) => ({ ...currentValue, skill: { ...settings.skill, - [manifest.name]: { + [manifestIdentifier]: { endpointUrl, msAppId, }, diff --git a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts index 137ff6c9d2..cf243384bb 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts @@ -6,7 +6,7 @@ import { CallbackInterface, useRecoilCallback } from 'recoil'; import { SkillManifestFile } from '@bfc/shared'; import produce from 'immer'; -import { rootBotProjectIdSelector, skillsStateSelector } from '../selectors'; +import { rootBotProjectIdSelector, skillsStateSelector, skillNameIdentifierByProjectIdSelector } from '../selectors'; import { skillManifestsState, displaySkillManifestState, diff --git a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts index 57a67d940e..ed67f52599 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts @@ -6,7 +6,7 @@ import { BaseSchema, deleteActions, ITriggerCondition, LgTemplate, LgTemplateSam import get from 'lodash/get'; import { schemasState, dialogState, localeState } from '../atoms/botState'; -import { dialogsSelectorFamily, luFilesSelectorFamily } from '../selectors'; +import { dialogsSelectorFamily, luFilesSelectorFamily, skillNameIdentifierByProjectIdSelector } from '../selectors'; import { onChooseIntentKey, generateNewDialog, @@ -16,6 +16,7 @@ import { } from '../../utils/dialogUtil'; import { lgFilesSelectorFamily } from '../selectors/lg'; import { dispatcherState } from '../atoms'; +import { createActionFromManifest } from '../utils/skill'; import { setError } from './shared'; @@ -195,12 +196,12 @@ export const triggerDispatcher = () => { } ); - const createTriggerWithAction = useRecoilCallback( + const createTriggerForRemoteSkill = useRecoilCallback( (callbackHelpers: CallbackInterface) => async ( projectId: string, dialogId: string, formData: TriggerFormData, - actions: object, + skillId: string, autoSelected = true ) => { try { @@ -216,6 +217,12 @@ export const triggerDispatcher = () => { createLgTemplates ); const index = get(dialogPayload.content, 'triggers', []).length - 1; + const skillsByProjectId = await snapshot.getPromise(skillNameIdentifierByProjectIdSelector); + console.log(skillsByProjectId); + const skillIdentifier = skillsByProjectId[skillId]; + console.log(skillIdentifier); + const actions: any = []; + actions.push(createActionFromManifest(skillIdentifier)); dialogPayload.content.triggers[index].actions = actions; await updateDialog(dialogPayload); @@ -231,6 +238,6 @@ export const triggerDispatcher = () => { createTrigger, deleteTrigger, createQnATrigger, - createTriggerWithAction, + createTriggerForRemoteSkill, }; }; diff --git a/Composer/packages/client/src/recoilModel/utils/skill.ts b/Composer/packages/client/src/recoilModel/utils/skill.ts new file mode 100644 index 0000000000..54b5d57c3f --- /dev/null +++ b/Composer/packages/client/src/recoilModel/utils/skill.ts @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { generateDesignerId } from '@bfc/shared'; + +export const createActionFromManifest = (manifestIdentifier) => { + // get identifier + return { + $kind: 'Microsoft.BeginSkill', + $designer: { + id: `${generateDesignerId()}`, + }, + activityProcessed: true, + botId: '=settings.MicrosoftAppId', + skillHostEndpoint: '=settings.skillHostEndpoint', + connectionName: '=settings.connectionName', + allowInterruptions: true, + skillEndpoint: `=settings.skill['${manifestIdentifier}'].endpointUrl`, // manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].endpointUrl : '', + skillAppId: `=settings.skill['${manifestIdentifier}'].msAppId`, // manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].msAppId : '', + }; +}; From 4cde587798cf668ad7d72ca0c86412e0453ebed3 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 7 Apr 2021 16:16:14 +0800 Subject: [PATCH 11/38] remove allowedCaller when remove skill --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 1 - .../src/recoilModel/dispatchers/botProjectFile.ts | 13 +++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index b99cde826d..bda65e22b4 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -132,7 +132,6 @@ export const CreateSkillModal: React.FC = (props) => { const result = location.href.split('/'); let skillId = ''; if (result.length > 0) skillId = result[result.length - 1]; - console.log(skillId); // add trigger with connect to skill action to root bot const triggerFormData = getTriggerFormData(skillManifest.name, content); addTriggerToRoot(dialogId, triggerFormData, skillId); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts index fe2416d572..f3aaefb03c 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts @@ -76,9 +76,22 @@ export const botProjectFileDispatcher = () => { const rootBotSettings = await snapshot.getPromise(settingsState(rootBotProjectId)); if (rootBotSettings.skill) { const updatedSettings = produce(rootBotSettings, (draftState) => { + let msAppId = ''; if (draftState.skill?.[botNameIdentifier]) { + msAppId = draftState.skill[botNameIdentifier].msAppId; delete draftState.skill[botNameIdentifier]; } + // remove msAppId in allowCallers + if (msAppId && draftState.skillConfiguration?.allowedCallers?.length > 0) { + draftState.skillConfiguration.allowedCallers = draftState.skillConfiguration?.allowedCallers.filter( + (item) => item !== msAppId + ); + } + if (msAppId && draftState.runtimeSettings?.skills?.allowedCallers?.length > 0) { + draftState.runtimeSettings.skills.allowedCallers = draftState.runtimeSettings.skills.allowedCallers.filter( + (item) => item !== msAppId + ); + } }); setRootBotSettingState(callbackHelpers, rootBotProjectId, updatedSettings); } From 518118f118f065c897d8ea6659926ce189390432 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 7 Apr 2021 22:10:29 +0800 Subject: [PATCH 12/38] fix skill not added after add remote skill --- .../src/recoilModel/dispatchers/setting.ts | 61 +++++++++++++------ .../packages/server/src/locales/en-US.json | 18 +++--- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index ce09506171..848a310231 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -3,7 +3,14 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { CallbackInterface, useRecoilCallback } from 'recoil'; -import { SensitiveProperties, RootBotManagedProperties, DialogSetting, PublishTarget, LibraryRef } from '@bfc/shared'; +import { + SensitiveProperties, + RootBotManagedProperties, + DialogSetting, + PublishTarget, + LibraryRef, + isUsingAdaptiveRuntime, +} from '@bfc/shared'; import get from 'lodash/get'; import set from 'lodash/set'; import has from 'lodash/has'; @@ -17,7 +24,6 @@ import { botNameIdentifierState } from '../atoms'; import httpClient from './../../utils/httpUtil'; import { setError } from './shared'; - export const setRootBotSettingState = async ( callbackHelpers: CallbackInterface, projectId: string, @@ -185,24 +191,43 @@ export const settingsDispatcher = () => { endpointUrl = cur?.endpointUrl || ''; msAppId = cur?.msAppId || ''; } - if (settings.skill) { - set(settingsState(projectId), (currentValue) => ({ - ...currentValue, - skill: { - ...settings.skill, - [manifestIdentifier]: { - endpointUrl, - msAppId, + + const v2 = isUsingAdaptiveRuntime(settings.runtime); + set(settingsState(projectId), (currentValue) => { + if (v2) { + return { + ...currentValue, + skill: { + ...settings.skill, + [manifestIdentifier]: { + endpointUrl, + msAppId, + }, }, - }, - runtimeSettings: { - ...settings.runtimeSettings, - skills: { - allowedCallers: [...settings.runtimeSettings?.skills?.allowedCallers, msAppId], + skillConfiguration: { + ...settings.skillConfiguration, + allowedCallers: [...settings.skillConfiguration?.allowedCallers, msAppId], }, - }, - })); - } + }; + } else { + return { + ...currentValue, + skill: { + ...settings.skill, + [manifestIdentifier]: { + endpointUrl, + msAppId, + }, + }, + runtimeSettings: { + ...settings.runtimeSettings, + skills: { + allowedCallers: [...settings.runtimeSettings?.skills?.allowedCallers, msAppId], + }, + }, + }; + } + }); } ); diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 685005a100..055d5db015 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -602,9 +602,6 @@ "cannot_parse_attachment_c3e552a5": { "message": "Cannot parse attachment." }, - "cannot_post_activity_conversation_not_found_c1e26d2d": { - "message": "Cannot post activity. Conversation not found." - }, "cannot_upload_file_conversation_not_found_8a983504": { "message": "Cannot upload file. Conversation not found." }, @@ -1235,9 +1232,6 @@ "downloading_bb6fb34b": { "message": "Downloading..." }, - "downloading_language_model_9d40c817": { - "message": "Downloading Language Model" - }, "due_to_the_following_error_we_were_unable_to_succe_5ca033c7": { "message": "Due to the following error, we were unable to successfully add your selected QnA keys to your bot project:" }, @@ -2507,6 +2501,9 @@ "no_uploads_were_attached_as_a_part_of_the_request_63e92f54": { "message": "No uploads were attached as a part of the request." }, + "no_web_chat_activity_yet_4227dc1a": { + "message": "No Web Chat activity yet." + }, "no_wildcard_ff439e76": { "message": "no wildcard" }, @@ -2642,6 +2639,9 @@ "or_4f7d4edb": { "message": "Or: " }, + "orchestrator_downloading_language_model_e785be44": { + "message": "Orchestrator: Downloading language model" + }, "orchestrator_recognizer_cf38b65a": { "message": "Orchestrator recognizer" }, @@ -2861,6 +2861,9 @@ "published_4bb5209e": { "message": "Published" }, + "publisher_bf6195cf": { + "message": "Publisher" + }, "publishing_count_plural_1_one_bot_other_bots_11edc1e9": { "message": "Publishing { count, plural,\n =1 {one bot}\n other {# bots}\n}" }, @@ -3986,9 +3989,6 @@ "web_chat_c5ca7ab6": { "message": "Web Chat" }, - "webchat_inspector_4d0dfeb7": { - "message": "Webchat Inspector" - }, "webchat_log_b7213a9e": { "message": "Webchat log." }, From 5fc8bf9a5bfca13babee02ec4bf335aec38f44a1 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 8 Apr 2021 09:04:47 +0800 Subject: [PATCH 13/38] fix orchestractor modal not show --- .../AddRemoteSkillModal/SelectIntent.tsx | 7 +- .../src/recoilModel/dispatchers/setting.ts | 16 +-- .../packages/server/src/locales/en-US.json | 97 ++++++++++--------- Composer/yarn.lock | 2 +- 4 files changed, 64 insertions(+), 58 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index ec9b9e8c10..60668e9467 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -12,7 +12,7 @@ import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button' import { Label } from 'office-ui-fabric-react/lib/Label'; import { LuEditor } from '@bfc/code-editor'; import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ScrollablePane'; -import { LuFile, LuIntentSection } from '@bfc/shared'; +import { LuFile, LuIntentSection, SDKKinds } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; import TelemetryClient from '../../telemetry/TelemetryClient'; @@ -127,9 +127,12 @@ export const SelectIntent: React.FC = (props) => { const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); const curRecognizers = useRecoilValue(recognizersSelectorFamily(projectId)); const hasOrchestractor = useMemo(() => { + console.log(curRecognizers); + const fileName = `${dialogId}.${locale}.lu.dialog`; + console.log(fileName); for (const file of curRecognizers) { - if (file.id === fileName) { + if (file.id === fileName && file.content.$kind === SDKKinds.OrchestratorRecognizer) { return true; } } diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 848a310231..9bf434dfc2 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -204,9 +204,11 @@ export const settingsDispatcher = () => { msAppId, }, }, - skillConfiguration: { - ...settings.skillConfiguration, - allowedCallers: [...settings.skillConfiguration?.allowedCallers, msAppId], + runtimeSettings: { + ...settings.runtimeSettings, + skills: { + allowedCallers: [...settings.runtimeSettings?.skills?.allowedCallers, msAppId], + }, }, }; } else { @@ -219,11 +221,9 @@ export const settingsDispatcher = () => { msAppId, }, }, - runtimeSettings: { - ...settings.runtimeSettings, - skills: { - allowedCallers: [...settings.runtimeSettings?.skills?.allowedCallers, msAppId], - }, + skillConfiguration: { + ...settings.skillConfiguration, + allowedCallers: [...settings.skillConfiguration?.allowedCallers, msAppId], }, }; } diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 055d5db015..8968fc766b 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -2,9 +2,6 @@ "0_bytes_a1e1cdb3": { "message": "0 Bytes" }, - "5_minute_intro_7ea06d2b": { - "message": "5 Minute Intro" - }, "ErrorInfo_part1": { "message": "An error occurred in the form editor!" }, @@ -317,9 +314,6 @@ "application_updates_bdf5f8b6": { "message": "Application Updates" }, - "apr_9_2020_3c8b47d7": { - "message": "Apr 9, 2020" - }, "are_you_sure_you_want_to_delete_your_bot_this_acti_214a9e11": { "message": "Are you sure you want to delete your bot? This action cannot be undone and your bot and all related files in the bot project folder will be permanently deleted. Your Azure resources will remain unchanged." }, @@ -440,9 +434,6 @@ "begindialog_a5594562": { "message": "BeginDialog" }, - "ben_brown_99c12d19": { - "message": "Ben Brown" - }, "boolean_6000988a": { "message": "Boolean" }, @@ -503,11 +494,8 @@ "bot_framework_composer_is_a_visual_authoring_canva_c3947d91": { "message": "Bot Framework Composer is a visual authoring canvas for building bots and other types of conversational application with the Microsoft Bot Framework technology stack. With Composer you will find everything you need to build a modern, state-of-the-art conversational experience." }, - "bot_framework_composer_is_an_open_source_visual_au_2be2e02b": { - "message": "Bot Framework Composer is an open-source visual authoring canvas for developers and multi-disciplinary teams to build bots. Composer integrates LUIS and QnA Maker, and allows sophisticated composition of bot replies using language generation." - }, - "bot_framework_provides_the_most_comprehensive_expe_e34a7f5d": { - "message": "Bot Framework provides the most comprehensive experience for building conversational applications." + "bot_framework_emulator_fefd4a59": { + "message": "Bot Framework Emulator" }, "bot_is_botname_c5af0c89": { "message": "Bot is { botName }" @@ -569,9 +557,6 @@ "break_out_of_loop_ab30157c": { "message": "Break out of loop" }, - "build_your_first_bot_f9c3e427": { - "message": "Build your first bot" - }, "building_5e8a3c1d": { "message": "Building" }, @@ -614,6 +599,9 @@ "check_for_updates_and_install_them_automatically_50337340": { "message": "Check for updates and install them automatically." }, + "check_out_the_resources_on_github_4d73792a": { + "message": "Check out the resources on GitHub." + }, "choice_input_f75a2353": { "message": "Choice Input" }, @@ -635,9 +623,6 @@ "choose_subscription_3d78d10d": { "message": "Choose subscription" }, - "chris_whitten_11df1f35": { - "message": "Chris Whitten" - }, "clear_all_da755751": { "message": "Clear all" }, @@ -683,12 +668,12 @@ "composer_cannot_yet_translate_your_bot_automatical_2d54081b": { "message": "Composer cannot yet translate your bot automatically.\nTo create a translation manually, Composer will create a copy of your bot’s content with the name of the additional language. This content can then be translated without affecting the original bot logic or flow and you can switch between languages to ensure the responses are correctly and appropriately translated." }, + "composer_documentation_60a27d37": { + "message": "Composer documentation" + }, "composer_includes_a_telemetry_feature_that_collect_8fd7bfbf": { "message": "Composer includes a telemetry feature that collects usage information. It is important that the Composer team understands how the tool is being used so that it can be improved." }, - "composer_introduction_98a93701": { - "message": "Composer introduction" - }, "composer_is_up_to_date_9118257d": { "message": "Composer is up to date." }, @@ -701,6 +686,9 @@ "composer_needs_net_core_sdk_46e2a8ae": { "message": "Composer needs .NET Core SDK" }, + "composer_on_github_1e3782ef": { + "message": "Composer on GitHub" + }, "composer_settings_31b04099": { "message": "Composer Settings" }, @@ -929,9 +917,6 @@ "create_new_e0946c49": { "message": "Create new" }, - "create_new_empty_bot_21cf0ea3": { - "message": "Create new empty bot" - }, "create_new_folder_19d3faa4": { "message": "Create new folder" }, @@ -1223,6 +1208,9 @@ "done_54e3d4b6": { "message": "Done" }, + "download_emulator_c8fb3403": { + "message": "Download Emulator" + }, "download_icon_2e0d10": { "message": "Download Icon" }, @@ -1379,6 +1367,9 @@ "endpoints_ff946539": { "message": "Endpoints" }, + "engage_with_other_bot_builders_ask_and_answer_ques_550d86bc": { + "message": "Engage with other bot builders. Ask and answer questions." + }, "ensure_your_bot_can_understand_your_users_by_frequ_c3f5c722": { "message": "Ensure your bot can understand your users by frequently training your LUIS model." }, @@ -1610,6 +1601,9 @@ "find_pre_built_adaptive_expressions_b106308e": { "message": "Find pre-built Adaptive expressions" }, + "find_tutorials_step_by_step_guides_discover_what_y_8f3e3863": { + "message": "Find tutorials, step-by-step guides. Discover what you can build with Composer." + }, "firstselector_a3daca5d": { "message": "FirstSelector" }, @@ -1724,15 +1718,18 @@ "getting_help_ab6811b0": { "message": "Getting Help" }, - "getting_started_f45a7e87": { - "message": "Getting Started" - }, "getting_template_910a4116": { "message": "Getting template" }, + "go_to_composer_repository_cba5d3d2": { + "message": "Go to Composer repository" + }, "go_to_qna_all_up_view_page_d475333d": { "message": "Go to QnA all-up view page." }, + "go_to_stack_overflow_e525148": { + "message": "Go to Stack Overflow" + }, "got_it_2c06b54a": { "message": "Got it!" }, @@ -1967,9 +1964,6 @@ "itemcount_plural_0_no_schemas_1_one_schema_other_s_e1aea7f": { "message": "{ itemCount, plural,\n =0 {No schemas}\n =1 {One schema}\n other {# schemas}\n} have been found.\n { itemCount, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" }, - "jan_28_2020_8beb36dc": { - "message": "Jan 28, 2020" - }, "just_add_a_qna_key_and_you_ll_be_ready_to_talk_to__d18758bb": { "message": "Just add a QnA key and you’ll be ready to talk to your bot." }, @@ -2363,9 +2357,6 @@ "ms_teams_15993b97": { "message": "MS Teams" }, - "msft_ignite_ai_show_e131edef": { - "message": "MSFT Ignite AI Show" - }, "msg_bf173fef": { "message": "{ msg }" }, @@ -2489,6 +2480,9 @@ "no_qna_file_with_name_id_7cb89755": { "message": "NO QNA FILE WITH NAME { id }" }, + "no_recent_bots_f4cf7d0a": { + "message": "No recent bots" + }, "no_search_results_1ba50423": { "message": "No search results" }, @@ -2522,9 +2516,6 @@ "notifications_cbfa7704": { "message": "Notifications" }, - "nov_12_2019_96ec5473": { - "message": "Nov 12, 2019" - }, "number_a6dc44e": { "message": "Number" }, @@ -2942,6 +2933,9 @@ "re_prompt_for_input_reprompt_dialog_event_ba028f7": { "message": "Re-prompt for input (Reprompt dialog event)" }, + "read_documentation_d5df5d59": { + "message": "Read documentation" + }, "read_me_4734ca1": { "message": "Read Me" }, @@ -3068,6 +3062,9 @@ "resource_name_817b6e75": { "message": "Resource name" }, + "resources_ccefab27": { + "message": "Resources" + }, "response_is_response_3cd62f8f": { "message": "Response is { response }" }, @@ -3422,6 +3419,9 @@ "ssml_tag_981a8aac": { "message": "SSML tag" }, + "stack_overflow_de80008e": { + "message": "Stack Overflow" + }, "start_and_stop_local_bot_runtimes_individually_901c8d7d": { "message": "Start and stop local bot runtimes individually." }, @@ -3533,6 +3533,9 @@ "terms_of_use_6542769b": { "message": "Terms of Use" }, + "test_and_debug_bots_built_using_the_bot_framework__fa382385": { + "message": "Test and debug bots built using the Bot Framework SDK. Available on GitHub." + }, "test_in_emulator_b1b3c278": { "message": "Test in Emulator" }, @@ -3941,9 +3944,6 @@ "video_card_cda18e03": { "message": "Video card" }, - "video_tutorials_79eb26ca": { - "message": "Video tutorials:" - }, "view_dialog_f5151228": { "message": "View dialog" }, @@ -3959,9 +3959,6 @@ "view_project_readme_ff079d2e": { "message": "View project readme" }, - "vishwac_sena_45910bf0": { - "message": "Vishwac Sena" - }, "visit_a_this_page_a_to_learn_more_about_entity_def_c7c862a9": { "message": "Visit this page to learn more about entity definition." }, @@ -3983,9 +3980,6 @@ "we_need_to_define_the_endpoints_for_the_skill_to_a_5dc98d90": { "message": "We need to define the endpoints for the skill to allow other bots to interact with it." }, - "weather_bot_c38920cd": { - "message": "Weather Bot" - }, "web_chat_c5ca7ab6": { "message": "Web Chat" }, @@ -4025,6 +4019,12 @@ "what_is_the_type_of_this_trigger_d2701744": { "message": "What is the type of this trigger?" }, + "what_s_new_a9752a8e": { + "message": "What''s new" + }, + "what_s_new_list_6fe719cb": { + "message": "What''s new list" + }, "what_you_need_to_know_to_get_started_e2ab837a": { "message": "What you need to know to get started" }, @@ -4097,6 +4097,9 @@ "you_do_not_have_permission_to_save_bots_here_56cc10c7": { "message": "You do not have permission to save bots here" }, + "you_don_t_have_any_bot_yet_start_to_link_create_a__ccacc2d6": { + "message": "You don’t have any bot yet. Start to create a new bot" + }, "you_have_successfully_published_name_to_publishtar_bc81d3c1": { "message": "You have successfully published { name } to { publishTarget }" }, diff --git a/Composer/yarn.lock b/Composer/yarn.lock index 3e667d4112..7ae9c41999 100644 --- a/Composer/yarn.lock +++ b/Composer/yarn.lock @@ -24280,4 +24280,4 @@ zip-stream@^4.0.0: dependencies: archiver-utils "^2.1.0" compress-commons "^4.0.0" - readable-stream "^3.6.0" + readable-stream "^3.6.0" \ No newline at end of file From d85bb7d65f88d65e231c4ad6ec4d40b012065d67 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 8 Apr 2021 11:49:07 +0800 Subject: [PATCH 14/38] remove trigger when remove skill, fix bugs --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 24 +++++++++++-------- .../AddRemoteSkillModal/SelectIntent.tsx | 3 --- .../components/ProjectTree/ProjectTree.tsx | 1 + .../client/src/pages/design/SideBar.tsx | 2 +- .../src/recoilModel/dispatchers/project.ts | 22 ++++++++++++++++- .../src/recoilModel/dispatchers/trigger.ts | 2 -- 6 files changed, 37 insertions(+), 17 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index bda65e22b4..c24385c5ed 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -127,16 +127,20 @@ export const CreateSkillModal: React.FC = (props) => { const handleSubmit = (event, content: string, enable: boolean) => { event.preventDefault(); // add a remote skill, add skill identifier into botProj file - addRemoteSkill(formData.manifestUrl, formData.endpointName).then(() => { - TelemetryClient.track('AddNewSkillCompleted'); - const result = location.href.split('/'); - let skillId = ''; - if (result.length > 0) skillId = result[result.length - 1]; - // add trigger with connect to skill action to root bot - const triggerFormData = getTriggerFormData(skillManifest.name, content); - addTriggerToRoot(dialogId, triggerFormData, skillId); - TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); - }); + addRemoteSkill(formData.manifestUrl, formData.endpointName) + .then(() => { + TelemetryClient.track('AddNewSkillCompleted'); + const result = location.href.split('/'); + let skillId = ''; + if (result.length > 0) skillId = result[result.length - 1]; + // add trigger with connect to skill action to root bot + const triggerFormData = getTriggerFormData(skillManifest.name, content); + addTriggerToRoot(dialogId, triggerFormData, skillId); + TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); + }) + .catch((err) => { + console.log(err); + }); if (enable) { // update recognizor type to orchestrator diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 60668e9467..51b7103100 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -127,10 +127,7 @@ export const SelectIntent: React.FC = (props) => { const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); const curRecognizers = useRecoilValue(recognizersSelectorFamily(projectId)); const hasOrchestractor = useMemo(() => { - console.log(curRecognizers); - const fileName = `${dialogId}.${locale}.lu.dialog`; - console.log(fileName); for (const file of curRecognizers) { if (file.id === fileName && file.content.$kind === SDKKinds.OrchestratorRecognizer) { return true; diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx index fd8e0ff8a5..39df119273 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx @@ -375,6 +375,7 @@ export const ProjectTree: React.FC = ({ label: formatMessage('Remove this trigger'), icon: 'Delete', onClick: (link) => { + console.log(projectId, link.dialogId); onDialogDeleteTrigger?.(projectId, link.dialogId ?? '', link.trigger ?? 0); }, }, diff --git a/Composer/packages/client/src/pages/design/SideBar.tsx b/Composer/packages/client/src/pages/design/SideBar.tsx index d18bf8a549..452d86ef31 100644 --- a/Composer/packages/client/src/pages/design/SideBar.tsx +++ b/Composer/packages/client/src/pages/design/SideBar.tsx @@ -77,7 +77,6 @@ const SideBar: React.FC = React.memo(({ projectId }) => { const undoFunction = useRecoilValue(undoFunctionState(projectId)); const rootProjectId = useRecoilValue(rootBotProjectIdSelector); const { commitChanges } = undoFunction; - const { removeDialog, updateDialog, @@ -247,6 +246,7 @@ const SideBar: React.FC = React.memo(({ projectId }) => { async function handleRemoveSkill(skillId: string) { // check if skill used in current project workspace const usedInBots = skillUsedInBotsMap[skillId]; + const confirmRemove = usedInBots.length ? await OpenConfirmModal(formatMessage('Warning'), removeSkillDialog().subText, { onRenderContent: () => { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 96b89d3ebf..6231f01508 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -38,6 +38,8 @@ import { } from '../atoms'; import { botRuntimeOperationsSelector, rootBotProjectIdSelector } from '../selectors'; import { mergePropertiesManagedByRootBot, postRootBotCreation } from '../../recoilModel/dispatchers/utils/project'; +import { projectDialogsMapSelector } from '../../recoilModel'; +import { deleteTrigger as DialogdeleteTrigger } from '../../utils/dialogUtil'; import { announcementState, boilerplateVersionState, recentProjectsState, templateIdState } from './../atoms'; import { logMessage, setError } from './../dispatchers/shared'; @@ -68,8 +70,25 @@ export const projectDispatcher = () => { const { set, snapshot } = callbackHelpers; const dispatcher = await snapshot.getPromise(dispatcherState); - await dispatcher.removeSkillFromBotProjectFile(projectIdToRemove); + const projectDialogsMap = await snapshot.getPromise(projectDialogsMapSelector); const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector); + const manifestIdentifier = await snapshot.getPromise(botNameIdentifierState(projectIdToRemove)); + const rootDialog = rootBotProjectId && projectDialogsMap[rootBotProjectId].find((dialog) => dialog.isRoot); + // remove the same identifier trigger in root bot + if (rootBotProjectId && rootDialog && rootDialog.triggers.length > 0) { + const index = rootDialog.triggers.findIndex((item) => item.displayName === manifestIdentifier); + const content = DialogdeleteTrigger( + projectDialogsMap[rootBotProjectId], + rootDialog?.id, + index, + async (trigger) => await dispatcher.deleteTrigger(rootBotProjectId, rootDialog?.id, trigger) + ); + if (content) { + await dispatcher.updateDialog({ id: rootDialog?.id, content, projectId: rootBotProjectId }); + } + } + + await dispatcher.removeSkillFromBotProjectFile(projectIdToRemove); const botRuntimeOperations = await snapshot.getPromise(botRuntimeOperationsSelector); set(botProjectIdsState, (currentProjects) => { @@ -172,6 +191,7 @@ export const projectDispatcher = () => { navigateToSkillBot(rootBotProjectId, projectId); } catch (ex) { handleProjectFailure(callbackHelpers, ex); + throw ex; } finally { set(botOpeningState, false); } diff --git a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts index ed67f52599..18544bcf5b 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts @@ -218,9 +218,7 @@ export const triggerDispatcher = () => { ); const index = get(dialogPayload.content, 'triggers', []).length - 1; const skillsByProjectId = await snapshot.getPromise(skillNameIdentifierByProjectIdSelector); - console.log(skillsByProjectId); const skillIdentifier = skillsByProjectId[skillId]; - console.log(skillIdentifier); const actions: any = []; actions.push(createActionFromManifest(skillIdentifier)); dialogPayload.content.triggers[index].actions = actions; From 29577bd65a2f91b6d4ddf7a217242d14bf3380d1 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 8 Apr 2021 18:37:25 +0800 Subject: [PATCH 15/38] polish UI --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 4 ++-- .../AddRemoteSkillModal/Orchestractor.tsx | 3 ++- .../AddRemoteSkillModal/SelectIntent.tsx | 16 ++++++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index c24385c5ed..3d6c221e8a 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -193,7 +193,7 @@ export const CreateSkillModal: React.FC = (props) => {
-
+
= (props) => { )} diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx index 73561e5137..2565624a2c 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx @@ -16,7 +16,7 @@ import { importOrchestractor } from './helper'; export const Orchestractor = (props) => { const { projectId, onSubmit, onBack } = props; - const [enable, setEnable] = useState(false); + const [enable, setEnable] = useState(true); const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); const onChange = (ev, check) => { setEnable(check); @@ -32,6 +32,7 @@ export const Orchestractor = (props) => {
= (props) => { // selected current locale intents const [displayIntents, setDisplayIntent] = useState>([]); const [displayContent, setDisplayContent] = useState(''); - // const [luFileError, setError] = useState(); + // const [diagnostics, setDiagnostics] = useState([]); const locale = useRecoilValue(localeState(projectId)); const [showOrchestratorDialog, setShowOrchestratorDialog] = useState(false); const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); @@ -254,7 +254,14 @@ export const SelectIntent: React.FC = (props) => { ) : ( = (props) => { handleSubmit(ev, true); } else { // show orchestractor + setTitle(enableOrchestratorDialog); setShowOrchestratorDialog(true); } } else { From 9429c20e333ec2b5528d97893799c283980fd755 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 9 Apr 2021 14:56:20 +0800 Subject: [PATCH 16/38] add endpoint dropdown --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 28 ++- .../AddRemoteSkillModal/SelectIntent.tsx | 11 +- .../components/ProjectTree/ProjectTree.tsx | 1 - .../client/src/pages/design/SideBar.tsx | 20 +- .../src/recoilModel/dispatchers/project.ts | 7 +- .../packages/server/src/locales/en-US.json | 184 ++++++++++++------ 6 files changed, 178 insertions(+), 73 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 3d6c221e8a..89bfe470f3 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { Fragment, useRef, useState, useCallback, useEffect } from 'react'; +import React, { Fragment, useRef, useState, useCallback, useEffect, useMemo } from 'react'; import formatMessage from 'format-message'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; // import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; @@ -13,6 +13,7 @@ import { isUsingAdaptiveRuntime, SDKKinds } from '@bfc/shared'; import { DialogWrapper, DialogTypes } from '@bfc/ui-shared'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { Separator } from 'office-ui-fabric-react/lib/Separator'; +import { Dropdown, IDropdownOption, ResponsiveMode } from 'office-ui-fabric-react/lib/Dropdown'; import { LoadingSpinner } from '../../components/LoadingSpinner'; import { settingsState, designPageLocationState, dispatcherState, luFilesSelectorFamily } from '../../recoilModel'; @@ -92,6 +93,15 @@ export const CreateSkillModal: React.FC = (props) => { setFormDataErrors, }; + const options: IDropdownOption[] = useMemo(() => { + return skillManifest?.endpoints?.map((item) => { + return { + key: item.msAppId, + text: formatMessage(item.name), + }; + }); + }, [skillManifest]); + const handleManifestUrlChange = (_, currentManifestUrl = '') => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { manifestUrl, ...rest } = formData; @@ -201,6 +211,22 @@ export const CreateSkillModal: React.FC = (props) => { value={formData.manifestUrl || ''} onChange={handleManifestUrlChange} /> + {skillManifest?.endpoints?.length > 1 && ( + { + if (option) { + setFormData({ + ...formData, + endpointName: option.text, + }); + } + }} + /> + )}
{showDetail && ( diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 468d8a1e1f..cb17733bd6 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -175,10 +175,17 @@ export const SelectIntent: React.FC = (props) => { useEffect(() => { if (locale) { const skillLanguages = manifest.dispatchModels?.languages; + const luFeaturesTemp = { + enableMLEntities: false, + enableListEntities: false, + enableCompositeEntities: false, + enablePrebuiltEntities: false, + enableRegexEntities: false, + }; getRemoteLuFiles(skillLanguages, languages) .then((items) => { items && - getParsedLuFiles(items, luFeatures, []).then((files) => { + getParsedLuFiles(items, luFeaturesTemp, []).then((files) => { setLufile(files); files.map((file) => { if (file.id.includes(locale)) { @@ -260,7 +267,7 @@ export const SelectIntent: React.FC = (props) => { projectId, fileId: dialogId, sectionId: manifest.name, - luFeatures: {}, + luFeatures: luFeatures, }} telemetryClient={TelemetryClient} value={displayContent} diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx index 4df648f33a..dfd4b6c439 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectTree.tsx @@ -364,7 +364,6 @@ export const ProjectTree: React.FC = ({ label: formatMessage('Remove this trigger'), icon: 'Delete', onClick: (link) => { - console.log(projectId, link.dialogId); onDialogDeleteTrigger?.(projectId, link.dialogId ?? '', link.trigger ?? 0); }, }, diff --git a/Composer/packages/client/src/pages/design/SideBar.tsx b/Composer/packages/client/src/pages/design/SideBar.tsx index 452d86ef31..38eb4447d7 100644 --- a/Composer/packages/client/src/pages/design/SideBar.tsx +++ b/Composer/packages/client/src/pages/design/SideBar.tsx @@ -134,9 +134,17 @@ const SideBar: React.FC = React.memo(({ projectId }) => { }; const projectTreeHeaderMenuItems = [ + { + key: 'ConnectRemoteSkill', + label: formatMessage('Add a skill'), + onClick: () => { + setAddSkillDialogModalVisibility(true); + TelemetryClient.track('AddNewSkillStarted', { method: 'remoteSkill' }); + }, + }, { key: 'CreateNewSkill', - label: formatMessage('Create a new skill'), + label: formatMessage('Add a new bot'), onClick: () => { setCreationFlowType('Skill'); setCreationFlowStatus(CreationFlowStatus.NEW); @@ -145,21 +153,13 @@ const SideBar: React.FC = React.memo(({ projectId }) => { }, { key: 'OpenSkill', - label: formatMessage('Open an existing skill'), + label: formatMessage('Add an existing bot'), onClick: () => { setCreationFlowType('Skill'); setCreationFlowStatus(CreationFlowStatus.OPEN); TelemetryClient.track('AddNewSkillStarted', { method: 'existingSkill' }); }, }, - { - key: 'ConnectRemoteSkill', - label: formatMessage('Connect a remote skill'), - onClick: () => { - setAddSkillDialogModalVisibility(true); - TelemetryClient.track('AddNewSkillStarted', { method: 'remoteSkill' }); - }, - }, ]; async function handleDeleteDialog(projectId: string, dialogId: string) { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 6231f01508..496310f3c3 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -38,7 +38,7 @@ import { } from '../atoms'; import { botRuntimeOperationsSelector, rootBotProjectIdSelector } from '../selectors'; import { mergePropertiesManagedByRootBot, postRootBotCreation } from '../../recoilModel/dispatchers/utils/project'; -import { projectDialogsMapSelector } from '../../recoilModel'; +import { projectDialogsMapSelector, botDisplayNameState } from '../../recoilModel'; import { deleteTrigger as DialogdeleteTrigger } from '../../utils/dialogUtil'; import { announcementState, boilerplateVersionState, recentProjectsState, templateIdState } from './../atoms'; @@ -72,11 +72,12 @@ export const projectDispatcher = () => { const dispatcher = await snapshot.getPromise(dispatcherState); const projectDialogsMap = await snapshot.getPromise(projectDialogsMapSelector); const rootBotProjectId = await snapshot.getPromise(rootBotProjectIdSelector); - const manifestIdentifier = await snapshot.getPromise(botNameIdentifierState(projectIdToRemove)); + // const manifestIdentifier = await snapshot.getPromise(botNameIdentifierState(projectIdToRemove)); + const triggerName = await snapshot.getPromise(botDisplayNameState(projectIdToRemove)); const rootDialog = rootBotProjectId && projectDialogsMap[rootBotProjectId].find((dialog) => dialog.isRoot); // remove the same identifier trigger in root bot if (rootBotProjectId && rootDialog && rootDialog.triggers.length > 0) { - const index = rootDialog.triggers.findIndex((item) => item.displayName === manifestIdentifier); + const index = rootDialog.triggers.findIndex((item) => item.displayName === triggerName); const content = DialogdeleteTrigger( projectDialogsMap[rootBotProjectId], rootDialog?.id, diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 551356b557..03cff5212e 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -494,14 +494,8 @@ "bot_framework_composer_is_a_visual_authoring_canva_c3947d91": { "message": "Bot Framework Composer is a visual authoring canvas for building bots and other types of conversational application with the Microsoft Bot Framework technology stack. With Composer you will find everything you need to build a modern, state-of-the-art conversational experience." }, - "bot_framework_composer_is_an_open_source_visual_au_2be2e02b": { - "message": "Bot Framework Composer is an open-source visual authoring canvas for developers and multi-disciplinary teams to build bots. Composer integrates LUIS and QnA Maker, and allows sophisticated composition of bot replies using language generation." - }, - "bot_framework_composer_requires_node_js_in_order_t_a1d3dfb": { - "message": "Bot Framework Composer requires Node.js in order to run. Click “Install Node.js” to install the latest version" - }, - "bot_framework_provides_the_most_comprehensive_expe_e34a7f5d": { - "message": "Bot Framework provides the most comprehensive experience for building conversational applications." + "bot_framework_composer_requires_node_js_in_order_t_9c45c226": { + "message": "Bot Framework Composer requires Node.js in order to create and run a new bot. Click “Install Node.js” to install the latest version" }, "bot_framework_emulator_fefd4a59": { "message": "Bot Framework Emulator" @@ -662,6 +656,9 @@ "collapse_navigation_17228b95": { "message": "Collapse Navigation" }, + "collect_service_level_and_conversation_level_data__d486b519": { + "message": "Collect service-level and conversation-level data to help gauge the performance and efficacy of your bot." + }, "comment_7ef1428e": { "message": "Comment" }, @@ -728,6 +725,9 @@ "configure_ecb97e30": { "message": "Configure" }, + "configure_title_1a4383b7": { + "message": "Configure { title }" + }, "configured_8d0f4dc8": { "message": "Configured" }, @@ -818,6 +818,9 @@ "copy_and_share_this_information_with_your_azure_ad_2ec77eeb": { "message": "Copy and share this information with your Azure admin. After your Luis key is provisioned, you will be ready to test your bot." }, + "copy_and_share_this_information_with_your_azure_ad_a41b99ea": { + "message": "Copy and share this information with your Azure admin. After your Speech key is provisioned, you will be ready to test your bot with speech." + }, "copy_content_for_translation_7affbcbb": { "message": "Copy content for translation" }, @@ -878,6 +881,9 @@ "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, + "create_a_new_speech_resource_c3c8fe60": { + "message": "Create a new Speech resource" + }, "create_a_publishing_profile_a79c6808": { "message": "Create a publishing profile" }, @@ -947,6 +953,9 @@ "create_new_qna_resources_ea2f7b37": { "message": "Create new QNA resources" }, + "create_new_speech_resources_73a921be": { + "message": "Create new Speech resources" + }, "create_or_edit_skill_manifest_8ad98da9": { "message": "Create or edit skill manifest" }, @@ -1091,9 +1100,6 @@ "deleting_dialogid_failed_1d7cc05a": { "message": "Deleting \"{ dialogId }\" failed." }, - "deleting_one_source_file_will_also_delete_qna_file_f3afd698": { - "message": "Deleting one source file will also delete qna files with the same name on other locales" - }, "describe_your_skill_88554792": { "message": "Describe your skill" }, @@ -1139,6 +1145,9 @@ "dialog_cancelled_cancel_dialog_event_3eba3d7e": { "message": "Dialog cancelled (Cancel dialog event)" }, + "dialog_d99c0378": { + "message": "Dialog" + }, "dialog_data_61d5539b": { "message": "Dialog data" }, @@ -1181,8 +1190,8 @@ "dialogfactory_missing_schema_5c3255c4": { "message": "DialogFactory missing schema." }, - "dialognum_plural_0_no_bots_1_one_bot_other_bots_ha_1cf10787": { - "message": "{ dialogNum, plural,\n =0 {No bots}\n =1 {One bot}\n other {# bots}\n} have been found.\n { dialogNum, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" + "dialognum_plural_0_no_bots_have_1_one_bot_has_othe_549c9b69": { + "message": "{ dialogNum, plural,\n =0 {No bots have}\n =1 {One bot has}\n other {# bots have}\n} been found.\n { dialogNum, select,\n 0 {}\n other {Press down arrow key to navigate the search results}\n}" }, "dialogs_triggers_and_actions_8a39ffea": { "message": "Dialogs, triggers, and actions" @@ -1238,6 +1247,9 @@ "due_to_the_following_error_we_were_unable_to_succe_897e7abb": { "message": "Due to the following error, we were unable to successfully add your selected LUIS keys to your bot project:" }, + "due_to_the_following_error_we_were_unable_to_succe_9e2dcd4a": { + "message": "Due to the following error, we were unable to successfully add your selected Speech keys to your bot project:" + }, "duplicate_31cec192": { "message": "Duplicate" }, @@ -1337,6 +1349,9 @@ "enable_6f5d1328": { "message": "Enable" }, + "enable_insights_e339cf40": { + "message": "Enable Insights" + }, "enable_line_numbers_to_refer_to_code_lines_by_numb_e5ba66ea": { "message": "Enable line numbers to refer to code lines by number." }, @@ -1373,9 +1388,6 @@ "endofconversation_activity_4aa21306": { "message": "EndOfConversation activity" }, - "endpoint_key_933b60ae": { - "message": "Endpoint Key" - }, "endpoints_ff946539": { "message": "Endpoints" }, @@ -1385,8 +1397,8 @@ "ensure_your_bot_can_understand_your_users_by_frequ_c3f5c722": { "message": "Ensure your bot can understand your users by frequently training your LUIS model." }, - "enter_a_manifest_url_to_add_a_new_skill_to_your_bo_eb966c95": { - "message": "Enter a manifest URL to add a new skill to your bot." + "enter_a_manifest_url_to_add_a_new_skill_to_your_bo_57e9d660": { + "message": "Enter a manifest url to add a new skill to your bot." }, "enter_a_max_value_14e8ba52": { "message": "Enter a max value" @@ -1415,9 +1427,6 @@ "enter_luis_authoring_key_c59f8f1f": { "message": "Enter LUIS authoring key" }, - "enter_luis_endpoint_key_f8eb30f5": { - "message": "Enter LUIS endpoint key" - }, "enter_luis_region_2316eceb": { "message": "Enter LUIS region" }, @@ -1436,14 +1445,20 @@ "enter_name_for_new_resource_group_96fe8ea8": { "message": "Enter name for new resource group" }, + "enter_name_for_new_speech_resources_d4cd8f5d": { + "message": "Enter name for new Speech resources" + }, "enter_qna_maker_subscription_key_d26b4bad": { "message": "Enter QnA Maker Subscription key" }, "enter_qna_region_4dae87f5": { "message": "Enter QnA region" }, - "enter_skill_host_endpoint_url_7489a83f": { - "message": "Enter Skill host endpoint URL" + "enter_skill_host_endpoint_url_e22eeab5": { + "message": "Enter Skill host endpoint url" + }, + "enter_speech_region_7e86bace": { + "message": "Enter Speech region" }, "entities_ef09392c": { "message": "Entities" @@ -1496,6 +1511,9 @@ "error_provisioning_25835400": { "message": "Error provisioning." }, + "errorscount_plural_0_no_errors_1_one_error_other_e_a8c998bb": { + "message": "{ errorsCount, plural,\n =0 {No errors}\n =1 {One error}\n other {# errors}\n}" + }, "errorsmsg_8f5d3d85": { "message": "{ errorsMsg }" }, @@ -1559,6 +1577,9 @@ "external_services_da7820ce": { "message": "External services" }, + "external_skill_73e16d25": { + "message": "External skill" + }, "extract_question_and_answer_pairs_from_an_online_f_7316548e": { "message": "Extract question-and-answer pairs from an online FAQ, product manuals, or other files. Supported formats are .tsv, .pdf, .doc, .docx, .xlsx, containing questions and answers in sequence. Learn more about knowledge base sources. Skip this step to add questions and answers manually after creation. The number of sources and file size you can add depends on the QnA service SKU you choose. Learn more about QnA Maker SKUs." }, @@ -1652,6 +1673,9 @@ "form_b674666c": { "message": "form" }, + "form_dialog_7327a4ff": { + "message": "Form dialog" + }, "form_dialog_error_ba7c37fe": { "message": "Form dialog error" }, @@ -1664,9 +1688,15 @@ "form_editor_7c2b02f0": { "message": "form editor" }, + "form_field_8566629d": { + "message": "Form field" + }, "form_title_baf85c7e": { "message": "form title" }, + "form_trigger_e8828303": { + "message": "Form trigger" + }, "form_wide_operations_1c1a73eb": { "message": "form-wide operations" }, @@ -1817,14 +1847,14 @@ "import_as_new_35630827": { "message": "Import as new" }, - "import_new_url_and_overwrite_5e463747": { - "message": "Import new URL and overwrite" + "import_new_url_and_overwrite_ef1f405e": { + "message": "Import new url and overwrite" }, "import_schema_75659c5f": { "message": "Import schema" }, - "import_url_62402b7c": { - "message": "Import URL" + "import_url_ccaa2f16": { + "message": "Import Url" }, "import_your_bot_to_new_project_8751d82f": { "message": "Import your bot to new project" @@ -1868,6 +1898,9 @@ "input_your_details_below_to_create_a_new_qna_resou_82b28d39": { "message": "Input your details below to create a new QNA resource. You will be able to manage your new resource in the Azure portal. Learn more" }, + "input_your_details_below_to_create_a_new_speech_re_51b79b16": { + "message": "Input your details below to create a new Speech resource. You will be able to manage your new resource in the Azure portal. Learn more" + }, "insert_a_property_reference_in_memory_95d27746": { "message": "Insert a property reference in memory" }, @@ -2003,8 +2036,8 @@ "knowledge_base_name_7d83bbe4": { "message": "Knowledge base name" }, - "knowledge_qna_1a599dcf": { - "message": "Knowledge (QnA)" + "knowledge_qna_7a4e84f7": { + "message": "Knowledge(QnA)" }, "knowledge_source_dd66f38f": { "message": "Knowledge source" @@ -2036,6 +2069,9 @@ "learn_how_to_build_a_skill_dbc58063": { "message": " learn how to build a skill " }, + "learn_how_to_publish_to_a_dev_ops_pipeline_using_c_9b4577be": { + "message": "Learn how to publish to a Dev Ops pipeline using CI / CD." + }, "learn_more_14816ec": { "message": "Learn More." }, @@ -2075,6 +2111,9 @@ "leave_product_tour_49585718": { "message": "Leave Product Tour?" }, + "lg_e6ee5b4a": { + "message": "LG" + }, "lg_editor_ee0184e6": { "message": "LG editor" }, @@ -2138,6 +2177,9 @@ "loading_bde52856": { "message": "Loading" }, + "loading_keys_22ceedc": { + "message": "Loading keys..." + }, "local_bot_runtime_manager_812cbd0c": { "message": "Local bot runtime manager" }, @@ -2180,6 +2222,9 @@ "looping_ddae56ff": { "message": "Looping" }, + "lu_15572a02": { + "message": "LU" + }, "lu_editor_d09fb2b0": { "message": "LU editor" }, @@ -2219,9 +2264,6 @@ "luis_authoring_region_b142f97b": { "message": "Luis Authoring Region" }, - "luis_endpoint_key_c685e219": { - "message": "LUIS endpoint key" - }, "luis_key_is_required_with_the_current_recognizer_s_66890a29": { "message": "LUIS key is required with the current recognizer setting to start your bot locally, and publish" }, @@ -2258,11 +2300,11 @@ "manifest_editor_1426637": { "message": "manifest editor" }, - "manifest_url_a6250c02": { - "message": "Manifest URL" + "manifest_url_30824e88": { + "message": "Manifest url" }, - "manifest_url_can_not_be_accessed_ba43fc31": { - "message": "Manifest URL can not be accessed" + "manifest_url_can_not_be_accessed_a7f147b2": { + "message": "Manifest url can not be accessed" }, "manifest_version_1edc004a": { "message": "Manifest Version" @@ -2468,6 +2510,9 @@ "no_existing_qna_resource_found_in_this_subscriptio_a547349b": { "message": "No existing QNA resource found in this subscription. Click “Next” to create new." }, + "no_existing_speech_resource_found_in_this_subscrip_4bc27bc3": { + "message": "No existing Speech resource found in this subscription. Click “Next” to create a new one." + }, "no_extensions_installed_4b925277": { "message": "No extensions installed" }, @@ -2693,8 +2738,8 @@ "please_enter_an_event_name_a148275a": { "message": "Please enter an event name" }, - "please_input_a_manifest_url_79cce9d5": { - "message": "Please input a manifest URL" + "please_input_a_manifest_url_d726edbf": { + "message": "Please input a manifest Url" }, "please_input_regex_pattern_5cd659a2": { "message": "Please input regEx pattern" @@ -2723,9 +2768,6 @@ "prebuilt_entity_21ebcdc6": { "message": "Prebuilt entity" }, - "prediction_key_200e158b": { - "message": "Prediction key" - }, "press_enter_to_add_this_item_or_tab_to_move_to_the_6beb8a14": { "message": "press Enter to add this item or Tab to move to the next interactive element" }, @@ -2855,15 +2897,15 @@ "publish_profile_to_configure_ca758e68": { "message": "Publish profile to configure:" }, - "publish_profiles_36fb522d": { - "message": "Publish profiles" - }, "publish_selected_bots_825bc03a": { "message": "Publish selected bots" }, "publish_target_388f6adf": { "message": "Publish target" }, + "publish_to_dev_ops_3b8f5a2f": { + "message": "Publish to Dev Ops" + }, "publish_your_bot_to_azure_and_manage_published_bot_67751ca9": { "message": "Publish your bot to Azure and manage published bots here." }, @@ -2885,6 +2927,9 @@ "publishing_name_to_publishtarget_failed_8677b68d": { "message": "Publishing { name } to { publishTarget } failed." }, + "publishing_profile_6d7064ce": { + "message": "Publishing Profile" + }, "publishing_target_46605bc5": { "message": "Publishing target" }, @@ -3074,6 +3119,9 @@ "resource_group_41fc9245": { "message": "Resource group:" }, + "resource_group_59eac426": { + "message": "Resource group" + }, "resource_group_982beb22": { "message": "Resource Group" }, @@ -3254,6 +3302,9 @@ "select_runtime_version_to_add_d63d383b": { "message": "Select runtime version to add" }, + "select_speech_keys_3a21b942": { + "message": "Select Speech keys" + }, "select_subscription_c5678611": { "message": "Select subscription" }, @@ -3266,6 +3317,9 @@ "select_which_tasks_this_skill_can_perform_172b0eae": { "message": "Select which tasks this skill can perform" }, + "select_your_azure_subscription_and_choose_from_exi_1e05256f": { + "message": "Select your Azure subscription and choose from existing Speech keys, or create a new Speech resource. Learn more" + }, "select_your_azure_subscription_and_choose_from_exi_73b6e4d6": { "message": "Select your Azure subscription and choose from existing QNA keys, or create a new QNA resource. Learn more" }, @@ -3374,8 +3428,8 @@ "skill_host_endpoint_4118a173": { "message": "Skill host endpoint" }, - "skill_host_endpoint_url_702c277c": { - "message": "Skill host endpoint URL" + "skill_host_endpoint_url_e68b65f6": { + "message": "Skill host endpoint url" }, "skill_manifest_url_be7ef8d0": { "message": "Skill Manifest Url" @@ -3437,6 +3491,12 @@ "speech_16063aed": { "message": "Speech" }, + "speech_key_1ab3f84a": { + "message": "Speech key" + }, + "speech_region_864a8a83": { + "message": "Speech region" + }, "spoken_text_used_by_the_channel_to_render_audibly_d07c7427": { "message": "Spoken text used by the channel to render audibly." }, @@ -3602,6 +3662,12 @@ "the_following_qna_key_has_been_successfully_added__7c97bbc0": { "message": "The following QnA key has been successfully added to your bot project:" }, + "the_following_speech_key_has_been_successfully_add_4bf0b587": { + "message": "The following Speech key has been successfully added to your bot project:" + }, + "the_following_speech_resource_was_successfully_cre_c4037861": { + "message": "The following Speech resource was successfully created and added to your bot project:" + }, "the_main_dialog_is_the_foundation_of_every_bot_cre_d4a938ff": { "message": "The main dialog is the foundation of every bot created in Composer. There is only one main dialog and all other dialogs are children of it. It gets initialized every time your bot runs and is the entry point into the bot." }, @@ -3731,6 +3797,9 @@ "tips_80d0da2b": { "message": "tips" }, + "title_connection_eaec11f8": { + "message": "{ title } connection" + }, "title_ee03d132": { "message": "Title" }, @@ -3797,6 +3866,12 @@ "train_your_language_model_ee4cec6a": { "message": "Train your language model" }, + "trigger_f0ee1fbf": { + "message": "Trigger" + }, + "trigger_group_79a00ac6": { + "message": "Trigger group" + }, "trigger_phrases_are_inputs_from_users_that_will_be_f8c61866": { "message": "Trigger phrases are inputs from users that will be used to train your LUIS model. This follows .lu file format." }, @@ -3902,18 +3977,15 @@ "updating_scripts_e17a5722": { "message": "Updating scripts... " }, - "url_22a5f3b8": { - "message": "URL" + "url_8c4ff7d2": { + "message": "Url" }, - "url_should_start_with_http_or_https_c34632bb": { - "message": "URL should start with http:// or https://" + "url_should_start_with_http_s_9ca55d94": { + "message": "Url should start with http[s]://" }, "use_custom_luis_authoring_key_9c71470b": { "message": "Use custom LUIS authoring key" }, - "use_custom_luis_endpoint_key_572e2c29": { - "message": "Use custom LUIS endpoint key" - }, "use_custom_luis_region_49d31dbf": { "message": "Use custom LUIS region" }, @@ -3953,9 +4025,6 @@ "validation_rules_efd3144d": { "message": "Validation Rules" }, - "valify_url_d4289305": { - "message": "Valify Url" - }, "value_d842f16d": { "message": "Value" }, @@ -3998,6 +4067,9 @@ "warning_aacb8c24": { "message": "Warning" }, + "warningscount_plural_0_no_warnings_1_one_warning_o_347cc928": { + "message": "{ warningsCount, plural,\n =0 {No warnings}\n =1 {One warning}\n other {# warnings}\n}" + }, "warningsmsg_e2c04bfe": { "message": "{ warningsMsg }" }, From f8eb9b08f75d7348d2f4f39e7f2953b1a377ff08 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 12 Apr 2021 11:33:59 +0800 Subject: [PATCH 17/38] en-us --- Composer/packages/server/src/locales/en-US.json | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 03cff5212e..fa3ebcf6eb 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -134,6 +134,9 @@ "add_a_luis_key_7702f550": { "message": "Add a LUIS key" }, + "add_a_new_bot_d7af2391": { + "message": "Add a new bot" + }, "add_a_new_key_5c208c29": { "message": "Add a new key" }, @@ -161,6 +164,9 @@ "add_alternative_phrasing_17e0304c": { "message": "+ Add alternative phrasing" }, + "add_an_existing_bot_5a9cc5b1": { + "message": "Add an existing bot" + }, "add_caller_5c12aa5b": { "message": "Add caller" }, @@ -752,9 +758,6 @@ "congratulations_your_model_is_successfully_publish_52ebc297": { "message": "Congratulations! Your model is successfully published." }, - "connect_a_remote_skill_10cf0724": { - "message": "Connect a remote skill" - }, "connect_to_a_skill_53c9dff0": { "message": "Connect to a skill" }, @@ -875,9 +878,6 @@ "create_a_new_qna_maker_resource_8c03ee9e": { "message": "Create a new QnA Maker resource" }, - "create_a_new_skill_e961ff28": { - "message": "Create a new skill" - }, "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, @@ -2642,9 +2642,6 @@ "one_or_more_options_that_are_passed_to_the_dialog__cbcf5d72": { "message": "One or more options that are passed to the dialog that is called." }, - "open_an_existing_skill_fbd87273": { - "message": "Open an existing skill" - }, "open_e0beb7b9": { "message": "Open" }, From 3e262afff6c0acb8a7bdf8b5e7782fe8fce59f82 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 12 Apr 2021 12:05:40 +0800 Subject: [PATCH 18/38] filter duplicate msAppId in allowedCaller --- .../src/components/AddRemoteSkillModal/helper.ts | 2 +- .../client/src/recoilModel/dispatchers/setting.ts | 12 ++++++++++-- Composer/packages/server/src/locales/en-US.json | 15 +++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 27f8e6cfd1..629ca95800 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -10,7 +10,7 @@ export const importOrchestractor = async (projectId: string, reloadProject, setA const reqBody = { package: 'Microsoft.Bot.Components.Orchestrator', version: '', - source: 'https://api.nuget.org/v3/index.json', + source: 'https://botbuilder.myget.org/F/botbuilder-v4-dotnet-daily/api/v3/index.json', isUpdating: false, }; try { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 9bf434dfc2..c379a1b5a0 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -195,6 +195,10 @@ export const settingsDispatcher = () => { const v2 = isUsingAdaptiveRuntime(settings.runtime); set(settingsState(projectId), (currentValue) => { if (v2) { + const callers = [...settings.runtimeSettings?.skills?.allowedCallers]; + if (!callers?.find((item) => item === msAppId)) { + callers.push(msAppId); + } return { ...currentValue, skill: { @@ -207,11 +211,15 @@ export const settingsDispatcher = () => { runtimeSettings: { ...settings.runtimeSettings, skills: { - allowedCallers: [...settings.runtimeSettings?.skills?.allowedCallers, msAppId], + allowedCallers: callers, }, }, }; } else { + const callers = [...settings.skillConfiguration?.allowedCallers]; + if (!callers?.find((item) => item === msAppId)) { + callers.push(msAppId); + } return { ...currentValue, skill: { @@ -223,7 +231,7 @@ export const settingsDispatcher = () => { }, skillConfiguration: { ...settings.skillConfiguration, - allowedCallers: [...settings.skillConfiguration?.allowedCallers, msAppId], + allowedCallers: callers, }, }; } diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index fa3ebcf6eb..01323345a5 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -701,6 +701,9 @@ "composer_on_github_1e3782ef": { "message": "Composer on GitHub" }, + "composer_runtime_error_b0efe05": { + "message": "Composer Runtime Error" + }, "composer_settings_31b04099": { "message": "Composer Settings" }, @@ -1496,12 +1499,18 @@ "error_occurred_5549a6b4": { "message": "Error occurred" }, + "error_occurred_building_the_bot_7425aa09": { + "message": "Error occurred building the bot" + }, "error_occurred_ejecting_runtime_8512129e": { "message": "Error occurred ejecting runtime!" }, "error_occurred_error_event_3e7f8ad0": { "message": "Error occurred (Error event)" }, + "error_occurred_trying_to_fetch_runtime_standard_ou_d0677f2d": { + "message": "Error occurred trying to fetch runtime standard output" + }, "error_please_add_unknown_functions_to_setting_s_cu_14b4abf8": { "message": "{ error } Please add unknown functions to setting''s customFunctions field." }, @@ -2711,9 +2720,6 @@ "output_5023cf84": { "message": "Output" }, - "outputs_e6f0bfbe": { - "message": "Outputs" - }, "page_number_cdee4179": { "message": "Page number" }, @@ -3512,9 +3518,6 @@ "start_bot_25ecad14": { "message": "Start Bot" }, - "start_bot_failed_d75647d5": { - "message": "Start bot failed" - }, "start_command_a085f2ec": { "message": "Start command" }, From be423f67233e565ed1a4fe1a97a90d3f607685ce Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 12 Apr 2021 15:10:54 +0800 Subject: [PATCH 19/38] disable entity in lueditor --- .../AddRemoteSkillModal/SelectIntent.tsx | 11 +++++++++++ .../src/components/AddRemoteSkillModal/helper.ts | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index cb17733bd6..18051c09b2 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -23,6 +23,7 @@ import { localeState, dispatcherState } from '../../recoilModel'; import { recognizersSelectorFamily } from '../../recoilModel/selectors/recognizers'; import { Orchestractor } from './Orchestractor'; +import { getLuDiagnostics } from './helper'; const detailListContainer = css` width: 100%; @@ -126,6 +127,8 @@ export const SelectIntent: React.FC = (props) => { const [showOrchestratorDialog, setShowOrchestratorDialog] = useState(false); const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); const curRecognizers = useRecoilValue(recognizersSelectorFamily(projectId)); + const [triggerErrorMessage, setTriggerErrorMsg] = useState(''); + const hasOrchestractor = useMemo(() => { const fileName = `${dialogId}.${locale}.lu.dialog`; for (const file of curRecognizers) { @@ -222,6 +225,13 @@ export const SelectIntent: React.FC = (props) => { } }, [selectedIntents, currentLuFile]); + useEffect(() => { + if (displayContent) { + const error = getLuDiagnostics('manifestName', displayContent); + setTriggerErrorMsg(error); + } + }, [displayContent]); + const handleSubmit = (ev, enableOchestractor) => { // append remote lufile into root lu file updateLuFile(displayContent); @@ -269,6 +279,7 @@ export const SelectIntent: React.FC = (props) => { sectionId: manifest.name, luFeatures: luFeatures, }} + errorMessage={triggerErrorMessage} telemetryClient={TelemetryClient} value={displayContent} onChange={setDisplayContent} diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 629ca95800..6ca5477eda 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import formatMessage from 'format-message'; - +import { luIndexer, combineMessage } from '@bfc/indexers'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; @@ -34,3 +34,15 @@ export const importOrchestractor = async (projectId: string, reloadProject, setA }); } }; + +export const getLuDiagnostics = (intent: string, triggerPhrases: string) => { + const content = `#${intent}\n${triggerPhrases}`; + const { diagnostics } = luIndexer.parse(content, '', { + enableListEntities: false, + enableCompositeEntities: false, + enableMLEntities: false, + enablePrebuiltEntities: false, + enableRegexEntities: false, + }); + return combineMessage(diagnostics); +}; From 199af754d1a23e2f6a793385a560b7b790c82821 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 12 Apr 2021 15:44:24 +0800 Subject: [PATCH 20/38] polish --- .../client/src/components/AddRemoteSkillModal/SelectIntent.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 18051c09b2..55f42fbd49 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -229,6 +229,8 @@ export const SelectIntent: React.FC = (props) => { if (displayContent) { const error = getLuDiagnostics('manifestName', displayContent); setTriggerErrorMsg(error); + } else { + setTriggerErrorMsg(''); } }, [displayContent]); From 5cfda9c8b842818e46a972efe7931489fe867df7 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Tue, 13 Apr 2021 16:59:22 +0800 Subject: [PATCH 21/38] fix some comments --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 8 +++---- .../AddRemoteSkillModal/SelectIntent.tsx | 22 +++++++++++-------- .../AddRemoteSkillModal/SkillDetail.tsx | 7 +++++- .../components/AddRemoteSkillModal/helper.ts | 4 ++-- .../src/recoilModel/dispatchers/setting.ts | 10 ++++----- .../src/recoilModel/dispatchers/trigger.ts | 5 +++-- 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 89bfe470f3..f99ba7ba9d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -192,12 +192,12 @@ export const CreateSkillModal: React.FC = (props) => {
{addSkillDialog.SKILL_MANIFEST_FORM.preSubText} - - {formatMessage(' Get an overview ')} + + {formatMessage('Get an overview')} or - - {formatMessage(' learn how to build a skill ')} + + {formatMessage('learn how to build a skill')} {addSkillDialog.SKILL_MANIFEST_FORM.afterSubText}
diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 55f42fbd49..214cc9ad84 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -60,7 +60,7 @@ const columns = [ minWidth: 300, isResizable: false, onRender: (item: string) => { - return
{item}
; + return {item}; }, }, ]; @@ -73,11 +73,15 @@ const getRemoteLuFiles = async (skillLanguages: object, composerLangeages: strin value.map((item) => { // get lu file luFilePromise.push( - httpClient.get(`/utilities/retrieveRemoteFile`, { - params: { - url: item.url, - }, - }) + httpClient + .get(`/utilities/retrieveRemoteFile`, { + params: { + url: item.url, + }, + }) + .catch((err) => { + console.error(err); + }) ); }); } @@ -129,7 +133,7 @@ export const SelectIntent: React.FC = (props) => { const curRecognizers = useRecoilValue(recognizersSelectorFamily(projectId)); const [triggerErrorMessage, setTriggerErrorMsg] = useState(''); - const hasOrchestractor = useMemo(() => { + const hasOrchestrator = useMemo(() => { const fileName = `${dialogId}.${locale}.lu.dialog`; for (const file of curRecognizers) { if (file.id === fileName && file.content.$kind === SDKKinds.OrchestratorRecognizer) { @@ -305,10 +309,10 @@ export const SelectIntent: React.FC = (props) => { { if (page === 1) { - if (hasOrchestractor) { + if (hasOrchestrator) { // skip orchestractor modal handleSubmit(ev, true); } else { diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index 603a611953..e4815e0cb2 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -13,7 +13,12 @@ interface SkillDetailProps { languages: object; }; version: string; - activities: object; + activities: { + [key: string]: { + type: string; + name: string; + }; + }; publisherName: string; description: string; name: string; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 6ca5477eda..991d01f764 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -8,8 +8,8 @@ import TelemetryClient from '../../telemetry/TelemetryClient'; export const importOrchestractor = async (projectId: string, reloadProject, setApplicationLevelError) => { const reqBody = { - package: 'Microsoft.Bot.Components.Orchestrator', - version: '', + package: 'Microsoft.Bot.Builder.AI.Orchestrator', + version: '4.14.0-daily.preview.20210410.233802.009f924', source: 'https://botbuilder.myget.org/F/botbuilder-v4-dotnet-daily/api/v3/index.json', isUpdating: false, }; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index c379a1b5a0..8b15823757 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -187,14 +187,14 @@ export const settingsDispatcher = () => { let msAppId, endpointUrl; if (manifest?.endpoints) { - const cur = manifest.endpoints.find((item) => item.name === endpointName); - endpointUrl = cur?.endpointUrl || ''; - msAppId = cur?.msAppId || ''; + const matchedEndpoint = manifest.endpoints.find((item) => item.name === endpointName); + endpointUrl = matchedEndpoint?.endpointUrl || ''; + msAppId = matchedEndpoint?.msAppId || ''; } - const v2 = isUsingAdaptiveRuntime(settings.runtime); + const isAdaptiveRuntime = isUsingAdaptiveRuntime(settings.runtime); set(settingsState(projectId), (currentValue) => { - if (v2) { + if (isAdaptiveRuntime) { const callers = [...settings.runtimeSettings?.skills?.allowedCallers]; if (!callers?.find((item) => item === msAppId)) { callers.push(msAppId); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts index 18544bcf5b..f0c17cffc0 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/trigger.ts @@ -206,8 +206,9 @@ export const triggerDispatcher = () => { ) => { try { const { snapshot } = callbackHelpers; - const dispatcher = await snapshot.getPromise(dispatcherState); - const { createLuIntent, createLgTemplates, updateDialog, selectTo } = dispatcher; + const { createLuIntent, createLgTemplates, updateDialog, selectTo } = await snapshot.getPromise( + dispatcherState + ); const dialogPayload = await getNewDialogWithTrigger( callbackHelpers, projectId, From c58452c8b7b5a7a76305f90921ccce4e9c07ce79 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Tue, 13 Apr 2021 21:44:28 +0800 Subject: [PATCH 22/38] fix lint --- .../components/AddRemoteSkillModal/CreateSkillModal.tsx | 8 ++++---- .../src/components/AddRemoteSkillModal/SelectIntent.tsx | 2 +- .../client/src/components/AddRemoteSkillModal/helper.ts | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index f99ba7ba9d..a154e50490 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -4,7 +4,6 @@ import React, { Fragment, useRef, useState, useCallback, useEffect, useMemo } from 'react'; import formatMessage from 'format-message'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; -// import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; @@ -97,6 +96,7 @@ export const CreateSkillModal: React.FC = (props) => { return skillManifest?.endpoints?.map((item) => { return { key: item.msAppId, + // eslint-disable-next-line format-message/literal-pattern text: formatMessage(item.name), }; }); @@ -192,11 +192,11 @@ export const CreateSkillModal: React.FC = (props) => {
{addSkillDialog.SKILL_MANIFEST_FORM.preSubText} - + {formatMessage('Get an overview')} or - + {formatMessage('learn how to build a skill')} {addSkillDialog.SKILL_MANIFEST_FORM.afterSubText} @@ -213,9 +213,9 @@ export const CreateSkillModal: React.FC = (props) => { /> {skillManifest?.endpoints?.length > 1 && ( { if (option) { diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 214cc9ad84..88c31dbf26 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -278,6 +278,7 @@ export const SelectIntent: React.FC = (props) => { = (props) => { sectionId: manifest.name, luFeatures: luFeatures, }} - errorMessage={triggerErrorMessage} telemetryClient={TelemetryClient} value={displayContent} onChange={setDisplayContent} diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 991d01f764..c8db7a07ee 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -3,6 +3,7 @@ import formatMessage from 'format-message'; import { luIndexer, combineMessage } from '@bfc/indexers'; + import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; From 1c9b20f76d2b3d915f634b42bbab0241635790ee Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 14 Apr 2021 15:46:25 +0800 Subject: [PATCH 23/38] fix comments --- .../__tests__/components/skill.test.tsx | 134 ++---------------- .../AddRemoteSkillModal/CreateSkillModal.tsx | 24 ++-- .../AddRemoteSkillModal/SkillDetail.tsx | 13 +- .../components/AddRemoteSkillModal/helper.ts | 3 +- .../src/recoilModel/dispatchers/project.ts | 1 - .../packages/server/src/locales/en-US.json | 73 +++++----- 6 files changed, 70 insertions(+), 178 deletions(-) diff --git a/Composer/packages/client/__tests__/components/skill.test.tsx b/Composer/packages/client/__tests__/components/skill.test.tsx index 02f316a5e3..e206526c65 100644 --- a/Composer/packages/client/__tests__/components/skill.test.tsx +++ b/Composer/packages/client/__tests__/components/skill.test.tsx @@ -6,10 +6,7 @@ import { act, fireEvent } from '@botframework-composer/test-utils'; import httpClient from '../../src/utils/httpUtil'; import { renderWithRecoil } from '../testUtils'; -import CreateSkillModal, { - validateEndpoint, - validateManifestUrl, -} from '../../src/components/AddRemoteSkillModal/CreateSkillModal'; +import CreateSkillModal, { validateManifestUrl } from '../../src/components/AddRemoteSkillModal/CreateSkillModal'; import { currentProjectIdState, settingsState } from '../../src/recoilModel'; jest.mock('../../src//utils/httpUtil'); @@ -53,9 +50,15 @@ describe('', () => { (httpClient.get as jest.Mock).mockResolvedValue({ endpoints: [] }); const onDismiss = jest.fn(); - const onSubmit = jest.fn(); + const addRemoteSkill = jest.fn(); + const addTriggerToRoot = jest.fn(); const { getByLabelText, getByText } = renderWithRecoil( - , + , recoilInitState ); @@ -74,7 +77,7 @@ describe('', () => { act(() => { fireEvent.click(submitButton); }); - expect(onSubmit).not.toBeCalled(); + expect(addRemoteSkill).not.toBeCalled(); } finally { jest.runOnlyPendingTimers(); jest.useRealTimers(); @@ -102,11 +105,7 @@ describe('', () => { await validateManifestUrl({ formData, formDataErrors, - projectId, setFormDataErrors, - setValidationState, - setSkillManifest, - validationState, }); expect(setFormDataErrors).toBeCalledWith( @@ -124,11 +123,7 @@ describe('', () => { validateManifestUrl({ formData, formDataErrors, - projectId, setFormDataErrors, - setValidationState, - setSkillManifest, - validationState, }); expect(setFormDataErrors).toBeCalledWith(expect.objectContaining({ manifestUrl: 'Please input a manifest Url' })); @@ -143,11 +138,7 @@ describe('', () => { await validateManifestUrl({ formData, formDataErrors, - projectId, setFormDataErrors, - setValidationState, - setSkillManifest, - validationState, }); expect(setValidationState).toBeCalledWith( expect.objectContaining({ @@ -180,11 +171,7 @@ describe('', () => { await validateManifestUrl({ formData, formDataErrors, - projectId, setFormDataErrors, - setValidationState, - setSkillManifest, - validationState, }); expect(setValidationState).toBeCalledWith( expect.objectContaining({ @@ -209,105 +196,4 @@ describe('', () => { ); }); }); - - describe('validateEndpoint', () => { - it('should set an error for missing msAppId', () => { - const formData = { endpointUrl: 'https://skill/api/messages' }; - - validateEndpoint({ - formData, - formDataErrors, - setFormDataErrors, - setValidationState, - validationState, - }); - - expect(setFormDataErrors).toBeCalledWith( - expect.objectContaining({ - endpoint: 'Please select a valid endpoint', - }) - ); - expect(setValidationState).not.toBeCalled(); - }); - - it('should set an error for missing endpointUrl', () => { - const formData = { msAppId: '00000000-0000-0000-0000-000000000000' }; - - validateEndpoint({ - formData, - formDataErrors, - setFormDataErrors, - setValidationState, - validationState, - }); - - expect(setFormDataErrors).toBeCalledWith( - expect.objectContaining({ - endpoint: 'Please select a valid endpoint', - }) - ); - expect(setValidationState).not.toBeCalled(); - }); - - it('should set an error for malformed msAppId', () => { - const formData = { endpointUrl: 'https://skill/api/messages', msAppId: 'malformed app id' }; - - validateEndpoint({ - formData, - formDataErrors, - setFormDataErrors, - setValidationState, - validationState, - }); - - expect(setFormDataErrors).toBeCalledWith( - expect.objectContaining({ - endpoint: 'Skill manifest endpoint is configured improperly', - }) - ); - expect(setValidationState).not.toBeCalled(); - }); - - it('should set an error for malformed endpointUrl', () => { - const formData = { endpointUrl: 'malformed endpoint', msAppId: '00000000-0000-0000-0000-000000000000' }; - - validateEndpoint({ - formData, - formDataErrors, - setFormDataErrors, - setValidationState, - validationState, - }); - - expect(setFormDataErrors).toBeCalledWith( - expect.objectContaining({ - endpoint: 'Skill manifest endpoint is configured improperly', - }) - ); - expect(setValidationState).not.toBeCalled(); - }); - - it('should not set an error', () => { - const formData = { endpointUrl: 'https://skill/api/messages', msAppId: '00000000-0000-0000-0000-000000000000' }; - - validateEndpoint({ - formData, - formDataErrors, - setFormDataErrors, - setValidationState, - validationState, - }); - - expect(setFormDataErrors).toBeCalledWith( - expect.not.objectContaining({ - endpoint: expect.any(String), - }) - ); - expect(setValidationState).toBeCalledWith( - expect.objectContaining({ - endpoint: 'Validated', - }) - ); - }); - }); }); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index a154e50490..274fed25bb 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -64,6 +64,8 @@ const getTriggerFormData = (intent: string, content: string): TriggerFormData => regEx: '', }); +const ButtonStyle = { root: { marginLeft: '8px' } }; + export const CreateSkillModal: React.FC = (props) => { const { projectId, addRemoteSkill, addTriggerToRoot, onDismiss } = props; @@ -137,20 +139,16 @@ export const CreateSkillModal: React.FC = (props) => { const handleSubmit = (event, content: string, enable: boolean) => { event.preventDefault(); // add a remote skill, add skill identifier into botProj file - addRemoteSkill(formData.manifestUrl, formData.endpointName) - .then(() => { - TelemetryClient.track('AddNewSkillCompleted'); - const result = location.href.split('/'); - let skillId = ''; - if (result.length > 0) skillId = result[result.length - 1]; + addRemoteSkill(formData.manifestUrl, formData.endpointName).then(() => { + TelemetryClient.track('AddNewSkillCompleted'); + const skillId = location.href.match(/skill\/([^/]*)/)?.[1]; + if (skillId) { // add trigger with connect to skill action to root bot const triggerFormData = getTriggerFormData(skillManifest.name, content); addTriggerToRoot(dialogId, triggerFormData, skillId); TelemetryClient.track('AddNewTriggerCompleted', { kind: 'Microsoft.OnIntent' }); - }) - .catch((err) => { - console.log(err); - }); + } + }); if (enable) { // update recognizor type to orchestrator @@ -244,7 +242,7 @@ export const CreateSkillModal: React.FC = (props) => { {skillManifest ? ( isUsingAdaptiveRuntime(runtime) && luFiles.length > 0 ? ( { setTitle(selectIntentDialog.SELECT_INTENT(dialogId, skillManifest.name)); @@ -253,7 +251,7 @@ export const CreateSkillModal: React.FC = (props) => { /> ) : ( { addRemoteSkill(formData.manifestUrl, formData.endpointName); @@ -263,7 +261,7 @@ export const CreateSkillModal: React.FC = (props) => { ) : ( diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index e4815e0cb2..27819d14f7 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -5,24 +5,23 @@ import { jsx, css } from '@emotion/core'; import React from 'react'; import formatMessage from 'format-message'; - interface SkillDetailProps { manifest: { dispatchModels: { intents: Array | object; - languages: object; + languages: Record>; }; version: string; - activities: { - [key: string]: { + activities: Record< + string, + { type: string; name: string; - }; - }; + } + >; publisherName: string; description: string; name: string; - [key: string]: any; }; } const container = css` diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index c8db7a07ee..88972d81e5 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -10,9 +10,10 @@ import TelemetryClient from '../../telemetry/TelemetryClient'; export const importOrchestractor = async (projectId: string, reloadProject, setApplicationLevelError) => { const reqBody = { package: 'Microsoft.Bot.Builder.AI.Orchestrator', - version: '4.14.0-daily.preview.20210410.233802.009f924', + version: '', source: 'https://botbuilder.myget.org/F/botbuilder-v4-dotnet-daily/api/v3/index.json', isUpdating: false, + isPreview: true, }; try { const results = await httpClient.post(`projects/${projectId}/import`, reqBody); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 496310f3c3..46e0bbf22c 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -192,7 +192,6 @@ export const projectDispatcher = () => { navigateToSkillBot(rootBotProjectId, projectId); } catch (ex) { handleProjectFailure(callbackHelpers, ex); - throw ex; } finally { set(botOpeningState, false); } diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 2b4340985d..d3abf10a03 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -818,14 +818,8 @@ "copy_9748f9f": { "message": "Copy" }, - "copy_and_share_this_information_with_your_azure_ad_2cb02ba1": { - "message": "Copy and share this information with your Azure admin. After your QNA key is provisioned, you will be ready to test your bot with qna." - }, - "copy_and_share_this_information_with_your_azure_ad_2ec77eeb": { - "message": "Copy and share this information with your Azure admin. After your Luis key is provisioned, you will be ready to test your bot." - }, - "copy_and_share_this_information_with_your_azure_ad_a41b99ea": { - "message": "Copy and share this information with your Azure admin. After your Speech key is provisioned, you will be ready to test your bot with speech." + "copy_and_share_this_information_with_your_azure_ad_68be5396": { + "message": "Copy and share this information with your Azure admin to provision resources on your behalf." }, "copy_content_for_translation_7affbcbb": { "message": "Copy content for translation" @@ -953,8 +947,8 @@ "create_new_luis_resources_a1a8fad5": { "message": "Create new LUIS resources" }, - "create_new_qna_resources_ea2f7b37": { - "message": "Create new QNA resources" + "create_new_qna_resources_f21ea0cf": { + "message": "Create new QnA resources" }, "create_new_speech_resources_73a921be": { "message": "Create new Speech resources" @@ -1730,9 +1724,6 @@ "generate_44e33e72": { "message": "Generate" }, - "generate_a_resource_request_9d5bcacd": { - "message": "Generate a resource request" - }, "generate_dialog_b80a85b2": { "message": "Generate dialog" }, @@ -1754,8 +1745,8 @@ "get_activity_members_11339605": { "message": "Get activity members" }, - "get_an_overview_ef09e016": { - "message": " Get an overview " + "get_an_overview_56c78cc3": { + "message": "Get an overview" }, "get_conversation_members_71602275": { "message": "Get conversation members" @@ -1802,6 +1793,9 @@ "handoff_activity_14363a20": { "message": "Handoff activity" }, + "handoff_to_admin_c104b028": { + "message": "Handoff to admin" + }, "handover_to_human_1a619574": { "message": "Handover to human" }, @@ -1823,9 +1817,21 @@ "home_351838cd": { "message": "Home" }, + "how_would_you_like_to_provision_this_resource_174f805d": { + "message": "How would you like to provision this resource?" + }, "http_request_79847109": { "message": "HTTP Request" }, + "i_am_working_on_a_microsoft_bot_framework_project__12049dd4": { + "message": "I am working on a Microsoft Bot Framework project, and I now require some Azure resources to be created. Please follow the instructions below to create these resources and provide them to me.\n\n1. Using the Azure portal, please create a Language Understanding resource on my behalf.\n2. Once provisioned, securely share the resulting credentials with me as described in the link below.\n\nDetailed instructions:\nhttps://aka.ms/bfcomposerhandoffluis" + }, + "i_am_working_on_a_microsoft_bot_framework_project__1295ee05": { + "message": "I am working on a Microsoft Bot Framework project, and I now require some Azure resources to be created. Please follow the instructions below to create these resources and provide them to me.\n\n1. Using the Azure portal, please create a Speech resource on my behalf.\n2. Once provisioned, securely share the resulting credentials with me as described in the link below.\n\nDetailed instructions:\nhttps://aka.ms/bfcomposerhandoffdls" + }, + "i_am_working_on_a_microsoft_bot_framework_project__9b5f3f78": { + "message": "I am working on a Microsoft Bot Framework project, and I now require some Azure resources to be created. Please follow the instructions below to create these resources and provide them to me.\n\n1. Using the Azure portal, please create a QnAMaker resource on my behalf.\n2. Once provisioned, securely share the resulting credentials with me as described in the link below.\n\nDetailed instructions:\nhttps://aka.ms/bfcomposerhandoffqnamaker" + }, "i_want_to_keep_the_template_content_in_the_file_ju_769331d9": { "message": "I want to keep the template content in the file, just want to dereference from this response (hint: keep the content if you currently, or plan to re-use in another location)" }, @@ -1907,8 +1913,8 @@ "input_your_details_below_to_create_a_new_luis_reso_75ca7d17": { "message": "Input your details below to create a new LUIS resource. You will be able to manage your new resource in the Azure portal. Learn more" }, - "input_your_details_below_to_create_a_new_qna_resou_82b28d39": { - "message": "Input your details below to create a new QNA resource. You will be able to manage your new resource in the Azure portal. Learn more" + "input_your_details_below_to_create_a_new_qna_resou_b32404f2": { + "message": "Input your details below to create a new QnA resource. You will be able to manage your new resource in the Azure portal. Learn more" }, "input_your_details_below_to_create_a_new_speech_re_51b79b16": { "message": "Input your details below to create a new Speech resource. You will be able to manage your new resource in the Azure portal. Learn more" @@ -1958,8 +1964,8 @@ "install_the_update_and_restart_composer_fac30a61": { "message": "Install the update and restart Composer." }, - "instructions_for_your_azure_admin_7da1c5aa": { - "message": "Instructions for your Azure admin:" + "instructions_for_your_azure_admin_aa780628": { + "message": "Instructions for your Azure admin" }, "integer_7f378275": { "message": "integer" @@ -2078,12 +2084,18 @@ "learn_about_adaptive_expressions_fb1b6c3c": { "message": "Learn about Adaptive expressions" }, + "learn_how_to_build_a_skill_b1c39c82": { + "message": "learn how to build a skill" + }, "learn_how_to_publish_to_a_dev_ops_pipeline_using_c_9b4577be": { "message": "Learn how to publish to a Dev Ops pipeline using CI / CD." }, "learn_more_14816ec": { "message": "Learn More." }, + "learn_more_7a8d626": { + "message": "Learn More" + }, "learn_more_a79a7918": { "message": "Learn more" }, @@ -2516,8 +2528,8 @@ "no_existing_luis_resource_found_in_this_subscripti_22642816": { "message": "No existing LUIS resource found in this subscription. Click “Next” to create new." }, - "no_existing_qna_resource_found_in_this_subscriptio_a547349b": { - "message": "No existing QNA resource found in this subscription. Click “Next” to create new." + "no_existing_qna_resource_found_in_this_subscriptio_c70e340": { + "message": "No existing QnA resource found in this subscription. Click “Next” to create new." }, "no_existing_speech_resource_found_in_this_subscrip_4bc27bc3": { "message": "No existing Speech resource found in this subscription. Click “Next” to create a new one." @@ -3209,6 +3221,9 @@ "schema_24739a48": { "message": "Schema" }, + "schema_definition_not_found_in_sdk_schema_1102ce9b": { + "message": "Schema definition not found in sdk.schema." + }, "schemaid_doesn_t_exists_select_an_schema_to_edit_o_9cccc954": { "message": "{ schemaId } doesn''t exists, select an schema to edit or create a new one" }, @@ -3299,8 +3314,8 @@ "select_property_type_45c6e68e": { "message": "Select property type" }, - "select_qna_keys_29abf5cb": { - "message": "Select QNA keys" + "select_qna_keys_e616cc57": { + "message": "Select QnA keys" }, "select_runtime_version_to_add_d63d383b": { "message": "Select runtime version to add" @@ -3323,12 +3338,6 @@ "select_your_azure_subscription_and_choose_from_exi_1e05256f": { "message": "Select your Azure subscription and choose from existing Speech keys, or create a new Speech resource. Learn more" }, - "select_your_azure_subscription_and_choose_from_exi_73b6e4d6": { - "message": "Select your Azure subscription and choose from existing QNA keys, or create a new QNA resource. Learn more" - }, - "select_your_azure_subscription_and_choose_from_exi_ff2b2416": { - "message": "Select your Azure subscription and choose from existing LUIS keys, or create a new LUIS resource. Learn more" - }, "selection_field_86d1dc94": { "message": "selection field" }, @@ -3662,6 +3671,9 @@ "the_following_qna_key_has_been_successfully_added__7c97bbc0": { "message": "The following QnA key has been successfully added to your bot project:" }, + "the_following_qna_resource_was_successfully_create_84173ca4": { + "message": "The following QnA resource was successfully created and added to your bot project:" + }, "the_following_speech_key_has_been_successfully_add_4bf0b587": { "message": "The following Speech key has been successfully added to your bot project:" }, @@ -4016,9 +4028,6 @@ "user_is_typing_typing_activity_cd938615": { "message": "User is typing (Typing activity)" }, - "using_the_azure_portal_create_a_language_understan_15e2f51f": { - "message": "Using the Azure portal, create a Language Understanding resource. Create these in a subscription that the developer has accesss to. This will result in an authoring key and an endpoint key. Provide these keys to the developer in a secure manner." - }, "validation_b10c677c": { "message": "Validation" }, From 918cb9296658c67ebd2634a9f17fa6ea96b6d08e Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 14 Apr 2021 15:47:19 +0800 Subject: [PATCH 24/38] add prerelease --- extensions/packageManager/src/node/index.ts | 4 +++- extensions/runtimes/src/index.ts | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/extensions/packageManager/src/node/index.ts b/extensions/packageManager/src/node/index.ts index c0250bb108..8ce2ad215a 100644 --- a/extensions/packageManager/src/node/index.ts +++ b/extensions/packageManager/src/node/index.ts @@ -311,6 +311,7 @@ export default async (composer: IExtensionRegistration): Promise => { const version = req.body.version; const source = req.body.source; const isUpdating = req.body.isUpdating || false; + const isPreview = req.body.isPreview || false; const mergeErrors: string[] = []; const captureErrors = (msg: string): void => { @@ -328,7 +329,8 @@ export default async (composer: IExtensionRegistration): Promise => { packageName, version, source, - currentProject + currentProject, + isPreview ); const manifestFile = runtime.identifyManifest(runtimePath, currentProject.name); diff --git a/extensions/runtimes/src/index.ts b/extensions/runtimes/src/index.ts index abe25ce9ac..899e674fbc 100644 --- a/extensions/runtimes/src/index.ts +++ b/extensions/runtimes/src/index.ts @@ -49,12 +49,13 @@ export default async (composer: any): Promise => { packageName: string, version: string, source: string, - _project: any + _project: any, + isPreview = false ): Promise => { // run dotnet install on the project const command = `dotnet add package "${packageName}"${version ? ' --version="' + version + '"' : ''}${ source ? ' --source="' + source + '"' : '' - }`; + }${isPreview ? ' --prerelease' : ''}`; composer.log('EXEC:', command); const { stderr: installError, stdout: installOutput } = await execAsync(command, { cwd: path.join(runtimePath, 'azurewebapp'), @@ -348,12 +349,13 @@ export default async (composer: any): Promise => { packageName: string, version: string, source: string, - _project: any + _project: any, + isPreview = false ): Promise => { // run dotnet install on the project const command = `dotnet add ${_project.name}.csproj package "${packageName}"${ version ? ' --version="' + version + '"' : '' - }${source ? ' --source="' + source + '"' : ''}`; + }${source ? ' --source="' + source + '"' : ''}${isPreview ? ' --prerelease' : ''}`; composer.log('EXEC:', command); const { stderr: installError, stdout: installOutput } = await execAsync(command, { cwd: path.join(runtimePath), @@ -458,12 +460,13 @@ export default async (composer: any): Promise => { packageName: string, version: string, source: string, - _project: any + _project: any, + isPreview = false ): Promise => { // run dotnet install on the project const command = `dotnet add ${_project.name}.csproj package "${packageName}"${ version ? ' --version="' + version + '"' : '' - }${source ? ' --source="' + source + '"' : ''}`; + }${source ? ' --source="' + source + '"' : ''}${isPreview ? ' --prerelease' : ''}`; composer.log('EXEC:', command); const { stderr: installError, stdout: installOutput } = await execAsync(command, { cwd: path.join(runtimePath), @@ -568,7 +571,8 @@ export default async (composer: any): Promise => { packageName: string, version: string, source: string, - _project: any + _project: any, + isPreview = false ): Promise => { // run dotnet install on the project const { stderr: installError, stdout: installOutput } = await execAsync( @@ -635,7 +639,8 @@ export default async (composer: any): Promise => { packageName: string, version: string, source: string, - _project: any + _project: any, + isPreview = false ): Promise => { // run dotnet install on the project const { stderr: installError, stdout: installOutput } = await execAsync( From 7b140cb4c24f81179f695fec6f2b771d4ee6cc4a Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Wed, 14 Apr 2021 22:51:14 +0800 Subject: [PATCH 25/38] fix test, fix endpoints bug, append intent to multiLanguage lufile --- .../__tests__/components/skill.test.tsx | 72 ++++------------- .../AddRemoteSkillModal/CreateSkillModal.tsx | 37 +++++---- .../AddRemoteSkillModal/SelectIntent.tsx | 79 +++++++++++++------ .../AddRemoteSkillModal/SkillDetail.tsx | 2 +- .../src/recoilModel/dispatchers/skill.ts | 2 +- 5 files changed, 94 insertions(+), 98 deletions(-) diff --git a/Composer/packages/client/__tests__/components/skill.test.tsx b/Composer/packages/client/__tests__/components/skill.test.tsx index e206526c65..7223b180fa 100644 --- a/Composer/packages/client/__tests__/components/skill.test.tsx +++ b/Composer/packages/client/__tests__/components/skill.test.tsx @@ -6,7 +6,11 @@ import { act, fireEvent } from '@botframework-composer/test-utils'; import httpClient from '../../src/utils/httpUtil'; import { renderWithRecoil } from '../testUtils'; -import CreateSkillModal, { validateManifestUrl } from '../../src/components/AddRemoteSkillModal/CreateSkillModal'; +import CreateSkillModal, { + validateManifestUrl, + getSkillManifest, +} from '../../src/components/AddRemoteSkillModal/CreateSkillModal'; +// import {SelectIntent} from '../../src/components/AddRemoteSkillModal/SelectIntent'; import { currentProjectIdState, settingsState } from '../../src/recoilModel'; jest.mock('../../src//utils/httpUtil'); @@ -54,15 +58,15 @@ describe('', () => { const addTriggerToRoot = jest.fn(); const { getByLabelText, getByText } = renderWithRecoil( , recoilInitState ); - const urlInput = getByLabelText('Manifest url'); + const urlInput = getByLabelText('Skill Manifest Url'); act(() => { fireEvent.change(urlInput, { target: { value: 'https://onenote-dev.azurewebsites.net/manifests/OneNoteSync-2-1-preview-1-manifest.json' }, @@ -73,11 +77,8 @@ describe('', () => { 'https://onenote-dev.azurewebsites.net/manifests/OneNoteSync-2-1-preview-1-manifest.json' ); - const submitButton = getByText('Confirm'); - act(() => { - fireEvent.click(submitButton); - }); - expect(addRemoteSkill).not.toBeCalled(); + const endpoint = getByText('Endpoints'); + expect(endpoint).not.toBeUndefined(); } finally { jest.runOnlyPendingTimers(); jest.useRealTimers(); @@ -85,17 +86,13 @@ describe('', () => { }); let formDataErrors; - let validationState; let setFormDataErrors; let setSkillManifest; - let setValidationState; beforeEach(() => { formDataErrors = {}; - validationState = {}; setFormDataErrors = jest.fn(); setSkillManifest = jest.fn(); - setValidationState = jest.fn(); }); describe('validateManifestUrl', () => { @@ -112,7 +109,6 @@ describe('', () => { expect.objectContaining({ manifestUrl: 'Url should start with http[s]://' }) ); expect(setSkillManifest).not.toBeCalled(); - expect(setValidationState).not.toBeCalled(); }); }); @@ -129,66 +125,32 @@ describe('', () => { expect(setFormDataErrors).toBeCalledWith(expect.objectContaining({ manifestUrl: 'Please input a manifest Url' })); }); - it('should try and retrieve manifest if manifest url meets other criteria', async () => { + it('should try and retrieve manifest', async () => { (httpClient.get as jest.Mock) = jest.fn().mockResolvedValue({ data: 'skill manifest' }); - const formData = { manifestUrl: 'https://skill' }; - const formDataErrors = { manifestUrl: 'error' }; + const manifestUrl = 'https://skill'; - await validateManifestUrl({ - formData, - formDataErrors, - setFormDataErrors, - }); - expect(setValidationState).toBeCalledWith( - expect.objectContaining({ - manifestUrl: 'Validating', - }) - ); + await getSkillManifest(projectId, manifestUrl, setSkillManifest, setFormDataErrors); expect(httpClient.get).toBeCalledWith(`/projects/${projectId}/skill/retrieveSkillManifest`, { params: { - url: formData.manifestUrl, + url: manifestUrl, }, }); expect(setSkillManifest).toBeCalledWith('skill manifest'); - expect(setValidationState).toBeCalledWith( - expect.objectContaining({ - manifestUrl: 'Validated', - }) - ); - expect(setFormDataErrors).toBeCalledWith( - expect.not.objectContaining({ - manifestUrl: 'error', - }) - ); }); it('should show error when it could not retrieve skill manifest', async () => { (httpClient.get as jest.Mock) = jest.fn().mockRejectedValue({ message: 'skill manifest' }); - const formData = { manifestUrl: 'https://skill' }; + const manifestUrl = 'https://skill'; - await validateManifestUrl({ - formData, - formDataErrors, - setFormDataErrors, - }); - expect(setValidationState).toBeCalledWith( - expect.objectContaining({ - manifestUrl: 'Validating', - }) - ); + await getSkillManifest(projectId, manifestUrl, setSkillManifest, setFormDataErrors); expect(httpClient.get).toBeCalledWith(`/projects/${projectId}/skill/retrieveSkillManifest`, { params: { - url: formData.manifestUrl, + url: manifestUrl, }, }); expect(setSkillManifest).not.toBeCalled(); - expect(setValidationState).toBeCalledWith( - expect.objectContaining({ - manifestUrl: 'NotValidated', - }) - ); expect(setFormDataErrors).toBeCalledWith( expect.objectContaining({ manifestUrl: 'Manifest url can not be accessed', diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 274fed25bb..c45572020d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -54,7 +54,21 @@ export const validateManifestUrl = async ({ formData, formDataErrors, setFormDat setFormDataErrors({}); } }; - +export const getSkillManifest = async (projectId: string, manifestUrl: string, setSkillManifest, setFormDataErrors) => { + try { + const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, { + params: { + url: manifestUrl, + }, + }); + setSkillManifest(data); + if (!data.dispatchModels) { + setFormDataErrors({ manifestUrl: formatMessage('Miss dispatch modal') }); + } + } catch (error) { + setFormDataErrors({ ...error, manifestUrl: formatMessage('Manifest url can not be accessed') }); + } +}; const getTriggerFormData = (intent: string, content: string): TriggerFormData => ({ errors: {}, $kind: 'Microsoft.OnIntent', @@ -97,7 +111,7 @@ export const CreateSkillModal: React.FC = (props) => { const options: IDropdownOption[] = useMemo(() => { return skillManifest?.endpoints?.map((item) => { return { - key: item.msAppId, + key: item.name, // eslint-disable-next-line format-message/literal-pattern text: formatMessage(item.name), }; @@ -119,19 +133,10 @@ export const CreateSkillModal: React.FC = (props) => { }; const validateUrl = useCallback( - async (event) => { + (event) => { event.preventDefault(); setShowDetail(true); - try { - const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, { - params: { - url: formData.manifestUrl, - }, - }); - setSkillManifest(data); - } catch (error) { - setFormDataErrors({ ...error, manifestUrl: formatMessage('Manifest url can not be accessed') }); - } + getSkillManifest(projectId, formData.manifestUrl, setSkillManifest, setFormDataErrors); }, [projectId, formData] ); @@ -211,15 +216,16 @@ export const CreateSkillModal: React.FC = (props) => { /> {skillManifest?.endpoints?.length > 1 && ( { if (option) { + console.log(option); setFormData({ ...formData, - endpointName: option.text, + endpointName: option.key as string, }); } }} @@ -244,6 +250,7 @@ export const CreateSkillModal: React.FC = (props) => { { setTitle(selectIntentDialog.SELECT_INTENT(dialogId, skillManifest.name)); setShowIntentSelectDialog(true); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 88c31dbf26..5f9a5f9be9 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -104,6 +104,15 @@ const getParsedLuFiles = async (files: { id: string; content: string }[], luFeat return luFiles; }; +const mergeIntentsContent = (intents: LuIntentSection[]) => { + return ( + intents + ?.map((item) => { + return `> ${item.Name}` + '\n' + item.Body; + }) + ?.join('\n') || '' + ); +}; export const SelectIntent: React.FC = (props) => { const { manifest, @@ -119,12 +128,13 @@ export const SelectIntent: React.FC = (props) => { } = props; const [page, setPage] = useState(0); const [selectedIntents, setSelectedIntents] = useState>([]); - // luFiles from manifest + // luFiles from manifest, language was included in root bot languages const [luFiles, setLufile] = useState>([]); // current locale Lufile const [currentLuFile, setCurrentLuFile] = useState(); - // selected current locale intents - const [displayIntents, setDisplayIntent] = useState>([]); + // selected intents in different languages + const [multiLanguageIntents, setMultiLanguageIntents] = useState>>({}); + // selected current locale intents content const [displayContent, setDisplayContent] = useState(''); // const [diagnostics, setDiagnostics] = useState([]); const locale = useRecoilValue(localeState(projectId)); @@ -163,21 +173,29 @@ export const SelectIntent: React.FC = (props) => { return res; }, [manifest]); - const updateLuFile = useCallback( - (content: string) => { - const file = rootLuFiles.find(({ id }) => id.includes(locale)); - if (!file) return; - const { id } = file; - file.content; + const updateLuFiles = useCallback(() => { + rootLuFiles?.map(async (lufile) => { + const rootId = lufile.id.split('.'); + const language = rootId[rootId.length - 1]; + let append = ''; + if (language === locale) { + append = displayContent; + } else { + const intents = multiLanguageIntents[language]; + if (!intents) { + return; + } + append = mergeIntentsContent(intents); + } const payload = { - projectId: projectId, - id, - content: file.content + `\n # ${manifest.name} \n` + content, + projectId, + id: lufile.id, + content: lufile.content + `\n # ${manifest.name} \n` + append, }; - updateLuFileDispatcher(payload); - }, - [rootLuFiles, projectId, locale] - ); + console.log(payload); + await updateLuFileDispatcher(payload); + }); + }, [rootLuFiles, projectId, locale, displayContent, multiLanguageIntents]); useEffect(() => { if (locale) { @@ -208,26 +226,35 @@ export const SelectIntent: React.FC = (props) => { }, [manifest.dispatchModels?.languages, languages, locale]); useEffect(() => { - if (selectedIntents.length > 0 && currentLuFile) { + if (selectedIntents.length > 0) { const intents: LuIntentSection[] = []; - currentLuFile.intents.map((intent) => { + const multiLanguageIntents: Record = {}; + currentLuFile?.intents?.map((intent) => { if (selectedIntents.includes(intent.Name)) { intents.push(intent); } }); - setDisplayIntent(intents); + luFiles?.map((file) => { + const id = file.id.split('.'); + const language = id[id.length - 1]; + multiLanguageIntents[language] = []; + file.intents?.map((intent) => { + if (selectedIntents.includes(intent.Name)) { + multiLanguageIntents[language].push(intent); + } + }); + }); + console.log(multiLanguageIntents); + setMultiLanguageIntents(multiLanguageIntents); // current locale, selected intent value. - const intentsValue = intents - .map((item) => { - return `> ${item.Name}` + '\n' + item.Body; - }) - .join('\n'); + const intentsValue = mergeIntentsContent(intents); setDisplayContent(intentsValue); } else { setDisplayContent(''); + setMultiLanguageIntents({}); } - }, [selectedIntents, currentLuFile]); + }, [selectedIntents, currentLuFile, luFiles]); useEffect(() => { if (displayContent) { @@ -240,7 +267,7 @@ export const SelectIntent: React.FC = (props) => { const handleSubmit = (ev, enableOchestractor) => { // append remote lufile into root lu file - updateLuFile(displayContent); + updateLuFiles(); // add trigger to root onSubmit(ev, displayContent, enableOchestractor); }; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index 27819d14f7..6fa6040b8f 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -60,7 +60,7 @@ export const SkillDetail: React.FC = (props) => {
{formatMessage('Activities')}
-
{formatMessage(Object.keys(manifest?.activities).join(', '))}
+
{formatMessage(manifest?.activities ? Object.keys(manifest?.activities).join(', ') : '')}
{formatMessage('Publisher')}
diff --git a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts index cf243384bb..137ff6c9d2 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts @@ -6,7 +6,7 @@ import { CallbackInterface, useRecoilCallback } from 'recoil'; import { SkillManifestFile } from '@bfc/shared'; import produce from 'immer'; -import { rootBotProjectIdSelector, skillsStateSelector, skillNameIdentifierByProjectIdSelector } from '../selectors'; +import { rootBotProjectIdSelector, skillsStateSelector } from '../selectors'; import { skillManifestsState, displaySkillManifestState, From ae5f185f990a23c3ebd792412dc0a5961a59bef2 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 15 Apr 2021 10:30:05 +0800 Subject: [PATCH 26/38] polish --- Composer/packages/client/__tests__/components/skill.test.tsx | 1 - .../src/components/AddRemoteSkillModal/CreateSkillModal.tsx | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Composer/packages/client/__tests__/components/skill.test.tsx b/Composer/packages/client/__tests__/components/skill.test.tsx index 7223b180fa..0fb45ad1d7 100644 --- a/Composer/packages/client/__tests__/components/skill.test.tsx +++ b/Composer/packages/client/__tests__/components/skill.test.tsx @@ -10,7 +10,6 @@ import CreateSkillModal, { validateManifestUrl, getSkillManifest, } from '../../src/components/AddRemoteSkillModal/CreateSkillModal'; -// import {SelectIntent} from '../../src/components/AddRemoteSkillModal/SelectIntent'; import { currentProjectIdState, settingsState } from '../../src/recoilModel'; jest.mock('../../src//utils/httpUtil'); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index c45572020d..5d7edb48e7 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -250,7 +250,7 @@ export const CreateSkillModal: React.FC = (props) => { { setTitle(selectIntentDialog.SELECT_INTENT(dialogId, skillManifest.name)); setShowIntentSelectDialog(true); From 6b7980943d80668784a4b9ed1050815b2479b98d Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 15 Apr 2021 11:44:21 +0800 Subject: [PATCH 27/38] disable next if lueditor error --- .../AddRemoteSkillModal/SelectIntent.tsx | 1 + .../electron-server/locales/en-US.json | 6 +++--- .../packages/server/src/locales/en-US.json | 19 +++++++++++-------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 5f9a5f9be9..e8fe85ae3f 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -337,6 +337,7 @@ export const SelectIntent: React.FC = (props) => { { if (page === 1) { if (hasOrchestrator) { diff --git a/Composer/packages/electron-server/locales/en-US.json b/Composer/packages/electron-server/locales/en-US.json index fae3220af4..7203f8cfa2 100644 --- a/Composer/packages/electron-server/locales/en-US.json +++ b/Composer/packages/electron-server/locales/en-US.json @@ -53,8 +53,8 @@ "initializing_4e34f7d": { "message": "Initializing..." }, - "learn_more_about_bot_framework_2daca065": { - "message": "Learn More About Bot Framework" + "learn_more_about_bot_framework_f5fe280b": { + "message": "Learn more about Bot Framework" }, "minimize_4f999e30": { "message": "Minimize" @@ -122,4 +122,4 @@ "zoom_out_dc7d60d2": { "message": "Zoom Out" } -} \ No newline at end of file +} diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index d6c204c4b1..87608da0a4 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -1793,8 +1793,8 @@ "how_would_you_like_to_provision_this_resource_174f805d": { "message": "How would you like to provision this resource?" }, - "http_request_79847109": { - "message": "HTTP Request" + "http_request_b6394895": { + "message": "HTTP request" }, "i_am_working_on_a_microsoft_bot_framework_project__12049dd4": { "message": "I am working on a Microsoft Bot Framework project, and I now require some Azure resources to be created. Please follow the instructions below to create these resources and provide them to me.\n\n1. Using the Azure portal, please create a Language Understanding resource on my behalf.\n2. Once provisioned, securely share the resulting credentials with me as described in the link below.\n\nDetailed instructions:\nhttps://aka.ms/bfcomposerhandoffluis" @@ -2063,9 +2063,6 @@ "learn_more_7a8d626": { "message": "Learn More" }, - "learn_more_7a8d626": { - "message": "Learn More" - }, "learn_more_a79a7918": { "message": "Learn more" }, @@ -2369,6 +2366,9 @@ "minimum_f31b05ab": { "message": "Minimum" }, + "miss_dispatch_modal_cf2d278e": { + "message": "Miss dispatch modal" + }, "missing_definition_for_defname_33f2b594": { "message": "Missing definition for { defName }" }, @@ -3182,9 +3182,6 @@ "schema_24739a48": { "message": "Schema" }, - "schema_definition_not_found_in_sdk_schema_1102ce9b": { - "message": "Schema definition not found in sdk.schema." - }, "schemaid_doesn_t_exists_select_an_schema_to_edit_o_9cccc954": { "message": "{ schemaId } doesn''t exists, select an schema to edit or create a new one" }, @@ -3413,6 +3410,9 @@ "skillname_manifest_ef3d9fed": { "message": "{ skillName } Manifest" }, + "skills_can_be_called_by_external_bots_allow_other__aa203913": { + "message": "Skills can be “called” by external bots. Allow other bots to call your skill by adding their App IDs to the list below." + }, "skills_extend_your_bot_s_conversational_capabiliti_ce5c2384": { "message": "Skills extend your bot''s conversational capabilities . To know more about skills" }, @@ -3821,6 +3821,9 @@ "toolbar_bafd4228": { "message": "toolbar" }, + "topic_e820dbbd": { + "message": "(Topic)" + }, "total_mb_531a3721": { "message": "{ total }MB" }, From 62fe89b1c6ecfb15baa06309cdaf3415991f4bd9 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 15 Apr 2021 15:53:32 +0800 Subject: [PATCH 28/38] fix lint --- .../src/components/AddRemoteSkillModal/CreateSkillModal.tsx | 2 +- .../src/components/AddRemoteSkillModal/SelectIntent.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 25e2372af6..8dec961746 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -248,9 +248,9 @@ export const CreateSkillModal: React.FC = (props) => { {skillManifest ? ( isUsingAdaptiveRuntime(runtime) && luFiles.length > 0 ? ( { setTitle(selectIntentDialog.SELECT_INTENT(dialogId, skillManifest.name)); setShowIntentSelectDialog(true); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index e8fe85ae3f..85aa5d2fb1 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -192,7 +192,7 @@ export const SelectIntent: React.FC = (props) => { id: lufile.id, content: lufile.content + `\n # ${manifest.name} \n` + append, }; - console.log(payload); + console.log(lufile.id, projectId, append); await updateLuFileDispatcher(payload); }); }, [rootLuFiles, projectId, locale, displayContent, multiLanguageIntents]); @@ -335,9 +335,9 @@ export const SelectIntent: React.FC = (props) => { { if (page === 1) { if (hasOrchestrator) { From 782067419dc6d84fca82cc5edd72e60f49180a4f Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 15 Apr 2021 17:48:06 +0800 Subject: [PATCH 29/38] fix multi language --- .../AddRemoteSkillModal/SelectIntent.tsx | 13 ++++----- .../recoilModel/dispatchers/botProjectFile.ts | 8 +++--- .../client/src/recoilModel/dispatchers/lu.ts | 28 +++++++++++++++++++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 85aa5d2fb1..a31d9e832d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -139,7 +139,7 @@ export const SelectIntent: React.FC = (props) => { // const [diagnostics, setDiagnostics] = useState([]); const locale = useRecoilValue(localeState(projectId)); const [showOrchestratorDialog, setShowOrchestratorDialog] = useState(false); - const { updateLuFile: updateLuFileDispatcher } = useRecoilValue(dispatcherState); + const { batchUpdateLuFiles } = useRecoilValue(dispatcherState); const curRecognizers = useRecoilValue(recognizersSelectorFamily(projectId)); const [triggerErrorMessage, setTriggerErrorMsg] = useState(''); @@ -174,6 +174,7 @@ export const SelectIntent: React.FC = (props) => { }, [manifest]); const updateLuFiles = useCallback(() => { + const payloads: { projectId: string; id: string; content: string }[] = []; rootLuFiles?.map(async (lufile) => { const rootId = lufile.id.split('.'); const language = rootId[rootId.length - 1]; @@ -187,14 +188,13 @@ export const SelectIntent: React.FC = (props) => { } append = mergeIntentsContent(intents); } - const payload = { + payloads.push({ projectId, id: lufile.id, content: lufile.content + `\n # ${manifest.name} \n` + append, - }; - console.log(lufile.id, projectId, append); - await updateLuFileDispatcher(payload); + }); }); + batchUpdateLuFiles(payloads); }, [rootLuFiles, projectId, locale, displayContent, multiLanguageIntents]); useEffect(() => { @@ -245,7 +245,6 @@ export const SelectIntent: React.FC = (props) => { } }); }); - console.log(multiLanguageIntents); setMultiLanguageIntents(multiLanguageIntents); // current locale, selected intent value. const intentsValue = mergeIntentsContent(intents); @@ -269,7 +268,7 @@ export const SelectIntent: React.FC = (props) => { // append remote lufile into root lu file updateLuFiles(); // add trigger to root - onSubmit(ev, displayContent, enableOchestractor); + // onSubmit(ev, displayContent, enableOchestractor); }; return ( diff --git a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts index f3aaefb03c..1b9027b49b 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts @@ -77,17 +77,17 @@ export const botProjectFileDispatcher = () => { if (rootBotSettings.skill) { const updatedSettings = produce(rootBotSettings, (draftState) => { let msAppId = ''; - if (draftState.skill?.[botNameIdentifier]) { + if (draftState?.skill?.[botNameIdentifier]) { msAppId = draftState.skill[botNameIdentifier].msAppId; delete draftState.skill[botNameIdentifier]; } // remove msAppId in allowCallers - if (msAppId && draftState.skillConfiguration?.allowedCallers?.length > 0) { - draftState.skillConfiguration.allowedCallers = draftState.skillConfiguration?.allowedCallers.filter( + if (msAppId && draftState?.skillConfiguration?.allowedCallers?.length > 0) { + draftState.skillConfiguration.allowedCallers = draftState.skillConfiguration.allowedCallers.filter( (item) => item !== msAppId ); } - if (msAppId && draftState.runtimeSettings?.skills?.allowedCallers?.length > 0) { + if (msAppId && draftState?.runtimeSettings?.skills?.allowedCallers?.length > 0) { draftState.runtimeSettings.skills.allowedCallers = draftState.runtimeSettings.skills.allowedCallers.filter( (item) => item !== msAppId ); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/lu.ts b/Composer/packages/client/src/recoilModel/dispatchers/lu.ts index 100b09d72d..2ca9a38394 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/lu.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/lu.ts @@ -176,6 +176,33 @@ export const removeLuFileState = async ( }; export const luDispatcher = () => { + const batchUpdateLuFiles = useRecoilCallback( + (callbackHelpers: CallbackInterface) => async ( + payloads: { + id: string; + content: string; + projectId: string; + }[] + ) => { + const { snapshot } = callbackHelpers; + payloads.map(async ({ id, content, projectId }) => { + // const luFiles = await snapshot.getPromise(luFilesSelectorFamily(projectId)); + const { luFeatures } = await snapshot.getPromise(settingsState(projectId)); + try { + const updatedFile = (await luWorker.parse(id, content, luFeatures, [])) as LuFile; + // compare to drop expired change on current id file. + /** + * Why other methods do not need double check content? + * Because this method already did set content before call luFilesAtomUpdater. + */ + updateLuFiles(callbackHelpers, projectId, { updates: [updatedFile] }); + } catch (error) { + setError(callbackHelpers, error); + } + }); + } + ); + const updateLuFile = useRecoilCallback( (callbackHelpers: CallbackInterface) => async ({ id, @@ -330,6 +357,7 @@ export const luDispatcher = () => { ); return { + batchUpdateLuFiles, updateLuFile, updateLuIntent, createLuIntent, From ecf08354361f131092a876a3f816f9906a4368f5 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 15 Apr 2021 18:19:44 +0800 Subject: [PATCH 30/38] show warning msg if remote lufile get fail --- .../src/components/AddRemoteSkillModal/SelectIntent.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index a31d9e832d..8524f92f9d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -65,7 +65,7 @@ const columns = [ }, ]; -const getRemoteLuFiles = async (skillLanguages: object, composerLangeages: string[]) => { +const getRemoteLuFiles = async (skillLanguages: object, composerLangeages: string[], setWarningMsg) => { const luFilePromise: Promise[] = []; try { for (const [key, value] of Object.entries(skillLanguages)) { @@ -81,6 +81,7 @@ const getRemoteLuFiles = async (skillLanguages: object, composerLangeages: strin }) .catch((err) => { console.error(err); + setWarningMsg('get remote file fail'); }) ); }); @@ -142,6 +143,7 @@ export const SelectIntent: React.FC = (props) => { const { batchUpdateLuFiles } = useRecoilValue(dispatcherState); const curRecognizers = useRecoilValue(recognizersSelectorFamily(projectId)); const [triggerErrorMessage, setTriggerErrorMsg] = useState(''); + const [warningMsg, setWarningMsg] = useState(''); const hasOrchestrator = useMemo(() => { const fileName = `${dialogId}.${locale}.lu.dialog`; @@ -207,7 +209,7 @@ export const SelectIntent: React.FC = (props) => { enablePrebuiltEntities: false, enableRegexEntities: false, }; - getRemoteLuFiles(skillLanguages, languages) + getRemoteLuFiles(skillLanguages, languages, setWarningMsg) .then((items) => { items && getParsedLuFiles(items, luFeaturesTemp, []).then((files) => { @@ -221,6 +223,7 @@ export const SelectIntent: React.FC = (props) => { }) .catch((e) => { console.log(e); + setWarningMsg('get remote file fail'); }); } }, [manifest.dispatchModels?.languages, languages, locale]); @@ -314,6 +317,7 @@ export const SelectIntent: React.FC = (props) => { }} telemetryClient={TelemetryClient} value={displayContent} + warningMessage={warningMsg} onChange={setDisplayContent} /> From 065949a17dbadf15a91f84306503d524374baab8 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Thu, 15 Apr 2021 18:53:51 +0800 Subject: [PATCH 31/38] fix typecheck --- .../components/AddRemoteSkillModal/SelectIntent.tsx | 2 +- .../src/recoilModel/dispatchers/botProjectFile.ts | 12 ++++++++++-- .../client/src/recoilModel/dispatchers/setting.ts | 8 ++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 8524f92f9d..7eda0171c2 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -271,7 +271,7 @@ export const SelectIntent: React.FC = (props) => { // append remote lufile into root lu file updateLuFiles(); // add trigger to root - // onSubmit(ev, displayContent, enableOchestractor); + onSubmit(ev, displayContent, enableOchestractor); }; return ( diff --git a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts index 1b9027b49b..67d2480de3 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/botProjectFile.ts @@ -82,12 +82,20 @@ export const botProjectFileDispatcher = () => { delete draftState.skill[botNameIdentifier]; } // remove msAppId in allowCallers - if (msAppId && draftState?.skillConfiguration?.allowedCallers?.length > 0) { + if ( + msAppId && + draftState?.skillConfiguration?.allowedCallers && + draftState?.skillConfiguration?.allowedCallers.length > 0 + ) { draftState.skillConfiguration.allowedCallers = draftState.skillConfiguration.allowedCallers.filter( (item) => item !== msAppId ); } - if (msAppId && draftState?.runtimeSettings?.skills?.allowedCallers?.length > 0) { + if ( + msAppId && + draftState?.runtimeSettings?.skills?.allowedCallers && + draftState?.runtimeSettings?.skills?.allowedCallers.length > 0 + ) { draftState.runtimeSettings.skills.allowedCallers = draftState.runtimeSettings.skills.allowedCallers.filter( (item) => item !== msAppId ); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 8b15823757..9f012a30d4 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -195,7 +195,9 @@ export const settingsDispatcher = () => { const isAdaptiveRuntime = isUsingAdaptiveRuntime(settings.runtime); set(settingsState(projectId), (currentValue) => { if (isAdaptiveRuntime) { - const callers = [...settings.runtimeSettings?.skills?.allowedCallers]; + const callers = settings.runtimeSettings?.skills?.allowedCallers + ? [...settings.runtimeSettings?.skills?.allowedCallers] + : []; if (!callers?.find((item) => item === msAppId)) { callers.push(msAppId); } @@ -216,7 +218,9 @@ export const settingsDispatcher = () => { }, }; } else { - const callers = [...settings.skillConfiguration?.allowedCallers]; + const callers = settings.skillConfiguration?.allowedCallers + ? [...settings.skillConfiguration?.allowedCallers] + : []; if (!callers?.find((item) => item === msAppId)) { callers.push(msAppId); } From 9c08697675689d1b04359ab7b7b3328ffca65ce2 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 16 Apr 2021 11:35:31 +0800 Subject: [PATCH 32/38] fix comments --- .../AddRemoteSkillModal/CreateSkillModal.tsx | 10 +-- .../AddRemoteSkillModal/Orchestractor.tsx | 72 +++++++++---------- .../AddRemoteSkillModal/SelectIntent.tsx | 31 ++++---- .../components/AddRemoteSkillModal/helper.ts | 2 - .../client/src/recoilModel/dispatchers/lu.ts | 1 - 5 files changed, 56 insertions(+), 60 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx index 8dec961746..2240e90a23 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/CreateSkillModal.tsx @@ -78,7 +78,7 @@ const getTriggerFormData = (intent: string, content: string): TriggerFormData => regEx: '', }); -const ButtonStyle = { root: { marginLeft: '8px' } }; +const buttonStyle = { root: { marginLeft: '8px' } }; export const CreateSkillModal: React.FC = (props) => { const { projectId, addRemoteSkill, addTriggerToRoot, onDismiss } = props; @@ -180,7 +180,6 @@ export const CreateSkillModal: React.FC = (props) => { manifest={skillManifest} projectId={projectId} rootLuFiles={luFiles} - setTitle={setTitle} onBack={() => { setTitle({ subText: '', @@ -190,6 +189,7 @@ export const CreateSkillModal: React.FC = (props) => { }} onDismiss={onDismiss} onSubmit={handleSubmit} + onUpdateTitle={setTitle} /> ) : ( @@ -249,7 +249,7 @@ export const CreateSkillModal: React.FC = (props) => { isUsingAdaptiveRuntime(runtime) && luFiles.length > 0 ? ( { setTitle(selectIntentDialog.SELECT_INTENT(dialogId, skillManifest.name)); @@ -258,7 +258,7 @@ export const CreateSkillModal: React.FC = (props) => { /> ) : ( { addRemoteSkill(formData.manifestUrl, formData.endpointName); @@ -268,7 +268,7 @@ export const CreateSkillModal: React.FC = (props) => { ) : ( diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx index 2565624a2c..584f31616c 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx @@ -14,49 +14,49 @@ import { enableOrchestratorDialog } from '../../constants'; import { importOrchestractor } from './helper'; +const learnMoreUrl = 'https://aka.ms/bf-composer-docs-publish-bot'; + export const Orchestractor = (props) => { const { projectId, onSubmit, onBack } = props; - const [enable, setEnable] = useState(true); + const [enableOrchestrator, setEnableOrchestrator] = useState(true); const { setApplicationLevelError, reloadProject } = useRecoilValue(dispatcherState); const onChange = (ev, check) => { - setEnable(check); + setEnableOrchestrator(check); }; return ( - - - -
- {enableOrchestratorDialog.content} - - {formatMessage('\n learn more about Orchestractor')} - -
- + +
+ {enableOrchestratorDialog.content} + +
{formatMessage('Learn more about Orchestractor')}
+ +
+ +
+ + + + + { + onSubmit(event, enableOrchestrator); + if (enableOrchestrator) { + // TODO. show notification + // download orchestrator first + importOrchestractor(projectId, reloadProject, setApplicationLevelError); + // TODO. update notification + } + }} /> -
- - - - - { - onSubmit(event, enable); - if (enable) { - // TODO. show notification - // download orchestrator first - importOrchestractor(projectId, reloadProject, setApplicationLevelError); - // TODO. update notification - } - }} - /> - - +
- + ); }; diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 7eda0171c2..8126e9701d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -12,7 +12,7 @@ import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button' import { Label } from 'office-ui-fabric-react/lib/Label'; import { LuEditor } from '@bfc/code-editor'; import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ScrollablePane'; -import { LuFile, LuIntentSection, SDKKinds } from '@bfc/shared'; +import { LuFile, LuIntentSection, SDKKinds, ILUFeaturesConfig } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; import TelemetryClient from '../../telemetry/TelemetryClient'; @@ -31,27 +31,26 @@ const detailListContainer = css` position: relative; overflow: hidden; flex-grow: 1; - border: 1px solid E5E5E5; + border: 1px solid #e5e5e5; `; -interface SelectIntentProps { +type SelectIntentProps = { manifest: { dispatchModels: { - intents: Array | object; + intents: string[] | object; languages: object; }; - [key: string]: any; - }; + } & Record; languages: string[]; projectId: string; - luFeatures: any; + luFeatures: ILUFeaturesConfig; rootLuFiles: LuFile[]; dialogId: string; - onSubmit: (event, content: string, enable: boolean) => void; + onSubmit: (event: Event, content: string, enable: boolean) => void; onDismiss: () => void; - setTitle: (value) => void; + onUpdateTitle: (title: { title: string; subText: string }) => void; onBack: () => void; -} +} & Record; const columns = [ { @@ -98,7 +97,7 @@ const getRemoteLuFiles = async (skillLanguages: object, composerLangeages: strin }; const getParsedLuFiles = async (files: { id: string; content: string }[], luFeatures, lufiles) => { - const promises = files?.map((item) => { + const promises = files.map((item) => { return luWorker.parse(item.id, item.content, luFeatures, lufiles) as Promise; }); const luFiles: LuFile[] = await Promise.all(promises); @@ -124,7 +123,7 @@ export const SelectIntent: React.FC = (props) => { projectId, rootLuFiles, dialogId, - setTitle, + onUpdateTitle, onBack, } = props; const [page, setPage] = useState(0); @@ -280,7 +279,7 @@ export const SelectIntent: React.FC = (props) => { { - setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); + onUpdateTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); setShowOrchestratorDialog(false); }} onSubmit={handleSubmit} @@ -329,7 +328,7 @@ export const SelectIntent: React.FC = (props) => { text={formatMessage('Back')} onClick={() => { setPage(page - 1); - setTitle(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); + onUpdateTitle(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); }} /> ) : ( @@ -348,13 +347,13 @@ export const SelectIntent: React.FC = (props) => { handleSubmit(ev, true); } else { // show orchestractor - setTitle(enableOrchestratorDialog); + onUpdateTitle(enableOrchestratorDialog); setShowOrchestratorDialog(true); } } else { // show next page setPage(page + 1); - setTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); + onUpdateTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); } }} /> diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 88972d81e5..693cb7e3c1 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -27,8 +27,6 @@ export const importOrchestractor = async (projectId: string, reloadProject, setA } } catch (err) { TelemetryClient.track('PackageInstallFailed', { ...reqBody, isUpdate: reqBody.isUpdating }); - - console.error(err); setApplicationLevelError({ status: err.response.status, message: err.response && err.response.data.message ? err.response.data.message : err, diff --git a/Composer/packages/client/src/recoilModel/dispatchers/lu.ts b/Composer/packages/client/src/recoilModel/dispatchers/lu.ts index 2ca9a38394..7e56c9aa7e 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/lu.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/lu.ts @@ -186,7 +186,6 @@ export const luDispatcher = () => { ) => { const { snapshot } = callbackHelpers; payloads.map(async ({ id, content, projectId }) => { - // const luFiles = await snapshot.getPromise(luFilesSelectorFamily(projectId)); const { luFeatures } = await snapshot.getPromise(settingsState(projectId)); try { const updatedFile = (await luWorker.parse(id, content, luFeatures, [])) as LuFile; From 93b541887c01e1dd9c92449a9eea479750a004ab Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 16 Apr 2021 21:35:52 +0800 Subject: [PATCH 33/38] fix test --- .../__tests__/components/skill.test.tsx | 5 +---- .../AddRemoteSkillModal/Orchestractor.tsx | 2 +- .../packages/server/src/locales/en-US.json | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Composer/packages/client/__tests__/components/skill.test.tsx b/Composer/packages/client/__tests__/components/skill.test.tsx index 63b9b0f49a..fcfe40fb60 100644 --- a/Composer/packages/client/__tests__/components/skill.test.tsx +++ b/Composer/packages/client/__tests__/components/skill.test.tsx @@ -55,7 +55,7 @@ describe('', () => { const onDismiss = jest.fn(); const addRemoteSkill = jest.fn(); const addTriggerToRoot = jest.fn(); - const { getByLabelText, getByText } = renderWithRecoil( + const { getByLabelText } = renderWithRecoil( ', () => { expect(urlInput.getAttribute('value')).toBe( 'https://onenote-dev.azurewebsites.net/manifests/OneNoteSync-2-1-preview-1-manifest.json' ); - - const endpoint = getByText('Endpoints'); - expect(endpoint).not.toBeUndefined(); } finally { jest.runOnlyPendingTimers(); jest.useRealTimers(); diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx index 584f31616c..1c2d137298 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/Orchestractor.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useState, Fragment } from 'react'; +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'; diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index 2a84d55ae4..b6eac8c294 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -2069,8 +2069,8 @@ "learn_more_about_manifests_6e7c364b": { "message": "Learn more about manifests" }, - "learn_more_about_orchestractor_d0c99460": { - "message": "\n learn more about Orchestractor" + "learn_more_about_orchestractor_3bfa61e1": { + "message": "Learn more about Orchestractor" }, "learn_more_about_skill_manifests_7708ce2c": { "message": "Learn more about skill manifests" @@ -2255,6 +2255,9 @@ "make_a_copy_77d1233": { "message": "Make a copy" }, + "make_orchestrator_my_preferred_recognizer_for_mult_2295034b": { + "message": "Make Orchestrator my preferred recognizer for multi-bot projects" + }, "manage_bot_languages_9ec36fd7": { "message": "Manage bot languages" }, @@ -2621,6 +2624,9 @@ "open_e0beb7b9": { "message": "Open" }, + "open_in_power_virtual_agents_fcd881e6": { + "message": "Open in Power Virtual Agents" + }, "open_inline_editor_a5aabcfa": { "message": "Open inline editor" }, @@ -2729,6 +2735,9 @@ "pop_out_editor_5528a187": { "message": "Pop out editor" }, + "power_virtual_agents_topics_count_9043ab47": { + "message": "Power Virtual Agents Topics ({ count })" + }, "powervirtualagents_logo_11858924": { "message": "PowerVirtualAgents Logo" }, @@ -3542,6 +3551,9 @@ "synonyms_optional_afe5cdb1": { "message": "Synonyms (Optional)" }, + "system_topic_44cfbac8": { + "message": "System Topic" + }, "tag_entity_5ae99194": { "message": "Tag entity" }, @@ -3731,6 +3743,9 @@ "this_option_allows_your_users_to_give_multiple_val_d2dd0d58": { "message": "This option allows your users to give multiple values for this property." }, + "this_page_contains_detailed_information_about_your_224a2b04": { + "message": "This Page contains detailed information about your bot. For security reasons, they are hidden by default. To test your bot or publish to Azure, you may need to provide these settings." + }, "this_page_contains_detailed_information_about_your_b2b3413b": { "message": "This Page contains detailed information about your bot. For security reasons, they are hidden by default. To test your bot or publish to Azure, you may need to provide these settings" }, @@ -3977,6 +3992,9 @@ "user_is_typing_typing_activity_cd938615": { "message": "User is typing (Typing activity)" }, + "user_topic_e3978941": { + "message": "User Topic" + }, "validation_b10c677c": { "message": "Validation" }, From 4f36cf930b636b769151c1b5fbed1c43c4ef69c0 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 16 Apr 2021 22:13:38 +0800 Subject: [PATCH 34/38] fix comments --- .../AddRemoteSkillModal/SelectIntent.tsx | 63 +++++++++---------- .../AddRemoteSkillModal/SkillDetail.tsx | 31 +++++---- 2 files changed, 43 insertions(+), 51 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx index 8126e9701d..9b3f64fe75 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SelectIntent.tsx @@ -100,17 +100,16 @@ const getParsedLuFiles = async (files: { id: string; content: string }[], luFeat const promises = files.map((item) => { return luWorker.parse(item.id, item.content, luFeatures, lufiles) as Promise; }); - const luFiles: LuFile[] = await Promise.all(promises); - return luFiles; + return await Promise.all(promises); }; const mergeIntentsContent = (intents: LuIntentSection[]) => { return ( intents - ?.map((item) => { - return `> ${item.Name}` + '\n' + item.Body; + .map((item) => { + return `> ${item.Name}\n${item.Body}`; }) - ?.join('\n') || '' + .join('\n') || '' ); }; export const SelectIntent: React.FC = (props) => { @@ -126,17 +125,16 @@ export const SelectIntent: React.FC = (props) => { onUpdateTitle, onBack, } = props; - const [page, setPage] = useState(0); + const [pageIndex, setPage] = useState(0); const [selectedIntents, setSelectedIntents] = useState>([]); // luFiles from manifest, language was included in root bot languages - const [luFiles, setLufile] = useState>([]); + const [luFiles, setLufiles] = useState>([]); // current locale Lufile const [currentLuFile, setCurrentLuFile] = useState(); // selected intents in different languages const [multiLanguageIntents, setMultiLanguageIntents] = useState>>({}); // selected current locale intents content const [displayContent, setDisplayContent] = useState(''); - // const [diagnostics, setDiagnostics] = useState([]); const locale = useRecoilValue(localeState(projectId)); const [showOrchestratorDialog, setShowOrchestratorDialog] = useState(false); const { batchUpdateLuFiles } = useRecoilValue(dispatcherState); @@ -146,12 +144,7 @@ export const SelectIntent: React.FC = (props) => { const hasOrchestrator = useMemo(() => { const fileName = `${dialogId}.${locale}.lu.dialog`; - for (const file of curRecognizers) { - if (file.id === fileName && file.content.$kind === SDKKinds.OrchestratorRecognizer) { - return true; - } - } - return false; + return curRecognizers.some((f) => f.id === fileName && f.content.$kind === SDKKinds.OrchestratorRecognizer); }, [curRecognizers, dialogId, locale]); const selection = useMemo(() => { @@ -176,7 +169,7 @@ export const SelectIntent: React.FC = (props) => { const updateLuFiles = useCallback(() => { const payloads: { projectId: string; id: string; content: string }[] = []; - rootLuFiles?.map(async (lufile) => { + for (const lufile of rootLuFiles) { const rootId = lufile.id.split('.'); const language = rootId[rootId.length - 1]; let append = ''; @@ -185,16 +178,16 @@ export const SelectIntent: React.FC = (props) => { } else { const intents = multiLanguageIntents[language]; if (!intents) { - return; + continue; } append = mergeIntentsContent(intents); } payloads.push({ projectId, id: lufile.id, - content: lufile.content + `\n # ${manifest.name} \n` + append, + content: `${lufile.content}\n# ${manifest.name}\n${append}`, }); - }); + } batchUpdateLuFiles(payloads); }, [rootLuFiles, projectId, locale, displayContent, multiLanguageIntents]); @@ -212,7 +205,7 @@ export const SelectIntent: React.FC = (props) => { .then((items) => { items && getParsedLuFiles(items, luFeaturesTemp, []).then((files) => { - setLufile(files); + setLufiles(files); files.map((file) => { if (file.id.includes(locale)) { setCurrentLuFile(file); @@ -222,7 +215,7 @@ export const SelectIntent: React.FC = (props) => { }) .catch((e) => { console.log(e); - setWarningMsg('get remote file fail'); + setWarningMsg(formatMessage('get remote file fail')); }); } }, [manifest.dispatchModels?.languages, languages, locale]); @@ -231,22 +224,22 @@ export const SelectIntent: React.FC = (props) => { if (selectedIntents.length > 0) { const intents: LuIntentSection[] = []; const multiLanguageIntents: Record = {}; - currentLuFile?.intents?.map((intent) => { - if (selectedIntents.includes(intent.Name)) { - intents.push(intent); + currentLuFile?.intents?.forEach((cur) => { + if (selectedIntents.includes(cur.Name)) { + intents.push(cur); } }); - - luFiles?.map((file) => { + for (const file of luFiles) { const id = file.id.split('.'); const language = id[id.length - 1]; multiLanguageIntents[language] = []; - file.intents?.map((intent) => { + for (const intent of file.intents) { if (selectedIntents.includes(intent.Name)) { multiLanguageIntents[language].push(intent); } - }); - }); + } + } + setMultiLanguageIntents(multiLanguageIntents); // current locale, selected intent value. const intentsValue = mergeIntentsContent(intents); @@ -286,7 +279,7 @@ export const SelectIntent: React.FC = (props) => { /> ) : ( - {page === 0 ? ( + {pageIndex === 0 ? (
@@ -323,11 +316,11 @@ export const SelectIntent: React.FC = (props) => { )} - {page === 1 ? ( + {pageIndex === 1 ? ( { - setPage(page - 1); + setPage(pageIndex - 1); onUpdateTitle(selectIntentDialog.SELECT_INTENT(dialogId, manifest.name)); }} /> @@ -337,11 +330,11 @@ export const SelectIntent: React.FC = (props) => { { - if (page === 1) { + if (pageIndex === 1) { if (hasOrchestrator) { // skip orchestractor modal handleSubmit(ev, true); @@ -352,7 +345,7 @@ export const SelectIntent: React.FC = (props) => { } } else { // show next page - setPage(page + 1); + setPage(pageIndex + 1); onUpdateTitle(selectIntentDialog.ADD_OR_EDIT_PHRASE(dialogId, manifest.name)); } }} diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx index 6fa6040b8f..de3a82cd67 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/SkillDetail.tsx @@ -1,29 +1,28 @@ -/* eslint-disable format-message/literal-pattern */ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. /** @jsx jsx */ import { jsx, css } from '@emotion/core'; import React from 'react'; import formatMessage from 'format-message'; -interface SkillDetailProps { +type SkillDetailProps = { manifest: { - dispatchModels: { - intents: Array | object; - languages: Record>; + dispatchModels?: { + intents: string[] | object; + languages: Record; }; - version: string; - activities: Record< + version?: string; + activities?: Record< string, { type: string; name: string; } >; - publisherName: string; - description: string; - name: string; + publisherName?: string; + description?: string; + name?: string; }; -} +}; const container = css` width: 100%; margin: 10px 0px; @@ -49,22 +48,22 @@ export const SkillDetail: React.FC = (props) => { const { manifest } = props; return (
-
{formatMessage(manifest?.name || '')}
+
{manifest.name || ''}
{formatMessage('Description')}
-
{formatMessage(manifest?.description || '')}
+
{manifest.description || ''}
{formatMessage('Version')}
-
{formatMessage(manifest?.version || '')}
+
{manifest.version || ''}
{formatMessage('Activities')}
-
{formatMessage(manifest?.activities ? Object.keys(manifest?.activities).join(', ') : '')}
+
{manifest.activities ? Object.keys(manifest.activities).join(', ') : ''}
{formatMessage('Publisher')}
-
{formatMessage(manifest?.publisherName || '')}
+
{manifest.publisherName || ''}
); From f0f2e3f8f3f4f7bf7fb3a336a4e0acea07e17bd4 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Fri, 16 Apr 2021 23:15:17 +0800 Subject: [PATCH 35/38] fix test --- .../src/recoilModel/dispatchers/__tests__/project.test.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/__tests__/project.test.tsx b/Composer/packages/client/src/recoilModel/dispatchers/__tests__/project.test.tsx index d0344008c1..d06896162d 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/__tests__/project.test.tsx +++ b/Composer/packages/client/src/recoilModel/dispatchers/__tests__/project.test.tsx @@ -13,6 +13,9 @@ import httpClient from '../../../utils/httpUtil'; import { projectDispatcher } from '../project'; import { botProjectFileDispatcher } from '../botProjectFile'; import { publisherDispatcher } from '../publisher'; +import { triggerDispatcher } from '../trigger'; +import { settingsDispatcher } from '../setting'; +import { dialogsDispatcher } from '../dialogs'; import { renderRecoilHook } from '../../../../__tests__/testUtils'; import { recentProjectsState, @@ -207,6 +210,9 @@ describe('Project dispatcher', () => { projectDispatcher, botProjectFileDispatcher, publisherDispatcher, + triggerDispatcher, + dialogsDispatcher, + settingsDispatcher, }, }, } From f6229b2999f8e731989cd72a1fa1085f8db76c20 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 19 Apr 2021 15:47:13 +0800 Subject: [PATCH 36/38] fix comments --- .../src/components/AddRemoteSkillModal/helper.ts | 13 +++++++++++++ .../packages/client/src/recoilModel/utils/skill.ts | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts index 693cb7e3c1..fd9f6d141d 100644 --- a/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts +++ b/Composer/packages/client/src/components/AddRemoteSkillModal/helper.ts @@ -3,10 +3,16 @@ import formatMessage from 'format-message'; import { luIndexer, combineMessage } from '@bfc/indexers'; +import { OpenConfirmModal } from '@bfc/ui-shared'; import httpClient from '../../utils/httpUtil'; import TelemetryClient from '../../telemetry/TelemetryClient'; +const conflictConfirmationTitle = formatMessage('Conflicting changes detected'); +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) => { const reqBody = { package: 'Microsoft.Bot.Builder.AI.Orchestrator', @@ -20,6 +26,13 @@ export const importOrchestractor = async (projectId: string, reloadProject, setA // check to see if there was a conflict that requires confirmation if (results.data.success === false) { TelemetryClient.track('PackageInstallConflictFound', { ...reqBody, isUpdate: reqBody.isUpdating }); + const confirmResult = await OpenConfirmModal(conflictConfirmationTitle, conflictConfirmationPrompt); + + if (confirmResult) { + TelemetryClient.track('PackageInstallConflictResolved', { ...reqBody, isUpdate: reqBody.isUpdating }); + // update package, set isUpdating to true + await httpClient.post(`projects/${projectId}/import`, { ...reqBody, isUpdate: true }); + } } else { TelemetryClient.track('PackageInstalled', { ...reqBody, isUpdate: reqBody.isUpdating }); // reload modified content diff --git a/Composer/packages/client/src/recoilModel/utils/skill.ts b/Composer/packages/client/src/recoilModel/utils/skill.ts index 54b5d57c3f..42d951a09d 100644 --- a/Composer/packages/client/src/recoilModel/utils/skill.ts +++ b/Composer/packages/client/src/recoilModel/utils/skill.ts @@ -15,7 +15,7 @@ export const createActionFromManifest = (manifestIdentifier) => { skillHostEndpoint: '=settings.skillHostEndpoint', connectionName: '=settings.connectionName', allowInterruptions: true, - skillEndpoint: `=settings.skill['${manifestIdentifier}'].endpointUrl`, // manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].endpointUrl : '', - skillAppId: `=settings.skill['${manifestIdentifier}'].msAppId`, // manifest.endpoints.length > 0 ? manifest.endpoints?.[selectEndpointIndex].msAppId : '', + skillEndpoint: `=settings.skill['${manifestIdentifier}'].endpointUrl`, + skillAppId: `=settings.skill['${manifestIdentifier}'].msAppId`, }; }; From 4778d9f6f8604444007ebdd90d5f4b21831706d6 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 19 Apr 2021 17:04:54 +0800 Subject: [PATCH 37/38] change sidebar back --- .../client/src/pages/design/SideBar.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Composer/packages/client/src/pages/design/SideBar.tsx b/Composer/packages/client/src/pages/design/SideBar.tsx index 38eb4447d7..452d86ef31 100644 --- a/Composer/packages/client/src/pages/design/SideBar.tsx +++ b/Composer/packages/client/src/pages/design/SideBar.tsx @@ -134,17 +134,9 @@ const SideBar: React.FC = React.memo(({ projectId }) => { }; const projectTreeHeaderMenuItems = [ - { - key: 'ConnectRemoteSkill', - label: formatMessage('Add a skill'), - onClick: () => { - setAddSkillDialogModalVisibility(true); - TelemetryClient.track('AddNewSkillStarted', { method: 'remoteSkill' }); - }, - }, { key: 'CreateNewSkill', - label: formatMessage('Add a new bot'), + label: formatMessage('Create a new skill'), onClick: () => { setCreationFlowType('Skill'); setCreationFlowStatus(CreationFlowStatus.NEW); @@ -153,13 +145,21 @@ const SideBar: React.FC = React.memo(({ projectId }) => { }, { key: 'OpenSkill', - label: formatMessage('Add an existing bot'), + label: formatMessage('Open an existing skill'), onClick: () => { setCreationFlowType('Skill'); setCreationFlowStatus(CreationFlowStatus.OPEN); TelemetryClient.track('AddNewSkillStarted', { method: 'existingSkill' }); }, }, + { + key: 'ConnectRemoteSkill', + label: formatMessage('Connect a remote skill'), + onClick: () => { + setAddSkillDialogModalVisibility(true); + TelemetryClient.track('AddNewSkillStarted', { method: 'remoteSkill' }); + }, + }, ]; async function handleDeleteDialog(projectId: string, dialogId: string) { From 90b268aed06ea7afa65b3e1cd6aa0aba6d0623b0 Mon Sep 17 00:00:00 2001 From: Wenyi Luo Date: Mon, 19 Apr 2021 18:09:58 +0800 Subject: [PATCH 38/38] enus --- .../packages/server/src/locales/en-US.json | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index dd0eb6fd28..985202eb29 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -134,9 +134,6 @@ "add_a_luis_key_7702f550": { "message": "Add a LUIS key" }, - "add_a_new_bot_d7af2391": { - "message": "Add a new bot" - }, "add_a_new_key_5c208c29": { "message": "Add a new key" }, @@ -164,9 +161,6 @@ "add_alternative_phrasing_17e0304c": { "message": "+ Add alternative phrasing" }, - "add_an_existing_bot_5a9cc5b1": { - "message": "Add an existing bot" - }, "add_caller_5c12aa5b": { "message": "Add caller" }, @@ -743,9 +737,15 @@ "confirmation_modal_must_have_a_title_b0816e0b": { "message": "Confirmation modal must have a title." }, + "conflicting_changes_detected_6c282985": { + "message": "Conflicting changes detected" + }, "congratulations_your_model_is_successfully_publish_52ebc297": { "message": "Congratulations! Your model is successfully published." }, + "connect_a_remote_skill_10cf0724": { + "message": "Connect a remote skill" + }, "connect_to_a_skill_53c9dff0": { "message": "Connect to a skill" }, @@ -857,6 +857,9 @@ "create_a_new_qna_maker_resource_8c03ee9e": { "message": "Create a new QnA Maker resource" }, + "create_a_new_skill_e961ff28": { + "message": "Create a new skill" + }, "create_a_new_skill_manifest_or_select_which_one_yo_a97e9616": { "message": "Create a new skill manifest or select which one you want to edit" }, @@ -1262,6 +1265,9 @@ "edit_in_json_75d0d754": { "message": "Edit in JSON" }, + "edit_in_power_virtual_agents_56ee7ac2": { + "message": "Edit in Power Virtual Agents" + }, "edit_kb_name_5e2d8c5b": { "message": "Edit KB name" }, @@ -1724,6 +1730,9 @@ "get_qna_key_583b2548": { "message": "Get QnA key" }, + "get_remote_file_fail_37ef94c5": { + "message": "get remote file fail" + }, "get_started_76ed4cb9": { "message": "Get started" }, @@ -2618,12 +2627,12 @@ "one_or_more_options_that_are_passed_to_the_dialog__cbcf5d72": { "message": "One or more options that are passed to the dialog that is called." }, + "open_an_existing_skill_fbd87273": { + "message": "Open an existing skill" + }, "open_e0beb7b9": { "message": "Open" }, - "open_in_power_virtual_agents_fcd881e6": { - "message": "Open in Power Virtual Agents" - }, "open_inline_editor_a5aabcfa": { "message": "Open inline editor" }, @@ -3737,6 +3746,9 @@ "this_operation_cannot_be_completed_the_skill_is_al_4886d311": { "message": "This operation cannot be completed. The skill is already part of the Bot Project" }, + "this_operation_will_overwrite_changes_made_to_prev_e746d44f": { + "message": "This operation will overwrite changes made to previously imported files. Do you want to proceed?" + }, "this_option_allows_your_users_to_give_multiple_val_d2dd0d58": { "message": "This option allows your users to give multiple values for this property." }, @@ -3992,9 +4004,6 @@ "user_topic_e3978941": { "message": "User Topic" }, - "validating_35b79a96": { - "message": "Validating..." - }, "validation_b10c677c": { "message": "Validation" },