From 2e8bc558e00575fa9facf4ed59d00f8e992b2891 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Sun, 30 Aug 2020 19:18:12 -0700 Subject: [PATCH 01/22] stable Signed-off-by: Srinaath Ravichandran --- .../src/recoilModel/dispatchers/skill.ts | 6 ++--- .../src/utils/backwardCompatibilityHandler.ts | 24 +++++++++++++++++++ .../packages/lib/shared/src/types/settings.ts | 10 +++++--- .../src/BeginSkillDialogField.tsx | 1 + 4 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 Composer/packages/client/src/utils/backwardCompatibilityHandler.ts diff --git a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts index 9d5e190c47..6dc1c0ceaf 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts @@ -6,6 +6,7 @@ import { CallbackInterface, useRecoilCallback } from 'recoil'; import { SkillManifest } from '@bfc/shared'; import httpClient from '../../utils/httpUtil'; +import { convertSkillsToDictionary } from '../../utils/backwardCompatibilityHandler'; import { skillManifestsState, @@ -74,11 +75,10 @@ export const skillDispatcher = () => { set(showAddSkillDialogModalState, false); set(onAddSkillDialogCompleteState, { func: undefined }); + set(settingsState, (settings) => ({ ...settings, - skill: skills.map(({ manifestUrl, name }) => { - return { manifestUrl, name }; - }), + skill: convertSkillsToDictionary(skills), })); set(skillsState, skills); } catch (err) { diff --git a/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts b/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts new file mode 100644 index 0000000000..654b79565b --- /dev/null +++ b/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import keyBy from 'lodash/keyBy'; +import { Skill } from '@bfc/shared'; + +export const convertSkillsToDictionary = (skills: Skill[]) => { + const map = new Map(); + const mappedSkills = skills.map((skill: any) => { + if (!map.get(skill)) { + return skill; + } else { + let i = 1; + while (map.get(`${skill}-${i}`)) { + i++; + } + return { + ...skill, + name: `${skill}-${i}`, + }; + } + }); + return keyBy(mappedSkills, 'name'); +}; diff --git a/Composer/packages/lib/shared/src/types/settings.ts b/Composer/packages/lib/shared/src/types/settings.ts index 845f70c418..efc1d287cf 100644 --- a/Composer/packages/lib/shared/src/types/settings.ts +++ b/Composer/packages/lib/shared/src/types/settings.ts @@ -33,9 +33,13 @@ export interface DialogSetting { defaultLanguage: string; languages: string[]; skill?: { - name: string; - manifestUrl: string; - }[]; + [skillName: string]: { + name?: string; + manifestUrl: string; + msAppId?: string; + endpoint?: string; + }; + }; botId?: string; skillHostEndpoint?: string; [key: string]: any; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 37f71f84e6..7dc07b77cf 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -11,6 +11,7 @@ import { SkillEndpointField } from './SkillEndpointField'; export const BeginSkillDialogField: React.FC = (props) => { const { depth, id, schema, uiOptions, value, onChange, definitions } = props; + console.log(value); const { projectId, shellApi, skills = [] } = useShellApi(); const { displayManifestModal } = shellApi; From eb3a4e69fe377eda925f7aad551de62e6ae53f96 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Sun, 30 Aug 2020 23:12:53 -0700 Subject: [PATCH 02/22] Skill updates Signed-off-by: Srinaath Ravichandran --- .../pages/notifications/useNotifications.tsx | 4 +-- .../src/recoilModel/dispatchers/skill.ts | 26 ++++++++++++-- .../packages/client/src/shell/useShell.ts | 2 ++ .../packages/lib/indexers/src/botIndexer.ts | 35 +++---------------- .../packages/lib/shared/src/types/shell.ts | 1 + .../server/src/models/bot/botProject.ts | 6 ++-- .../server/src/models/bot/skillManager.ts | 2 +- .../src/BeginSkillDialogField.tsx | 12 ++++--- 8 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx index 77d5383b26..41d06da861 100644 --- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx +++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import get from 'lodash/get'; -import { BotIndexer } from '@bfc/indexers'; +import { checkSkillSetting } from '@bfc/indexers'; import { luFilesState, @@ -55,7 +55,7 @@ export default function useNotifications(filter?: string) { diagnostics.forEach((d) => { notifications.push(new ServerNotification(projectId, '', d.source, d)); }); - const skillDiagnostics = BotIndexer.checkSkillSetting(botAssets); + const skillDiagnostics = checkSkillSetting(botAssets); skillDiagnostics.forEach((item) => { if (item.source.endsWith('.json')) { notifications.push(new SkillNotification(projectId, item.source, item.source, item)); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts index 6dc1c0ceaf..85a0e5c8a9 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts @@ -3,7 +3,8 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { CallbackInterface, useRecoilCallback } from 'recoil'; -import { SkillManifest } from '@bfc/shared'; +import { SkillManifest, DialogSetting } from '@bfc/shared'; +import get from 'lodash/get'; import httpClient from '../../utils/httpUtil'; import { convertSkillsToDictionary } from '../../utils/backwardCompatibilityHandler'; @@ -75,7 +76,6 @@ export const skillDispatcher = () => { set(showAddSkillDialogModalState, false); set(onAddSkillDialogCompleteState, { func: undefined }); - set(settingsState, (settings) => ({ ...settings, skill: convertSkillsToDictionary(skills), @@ -116,6 +116,27 @@ export const skillDispatcher = () => { set(displaySkillManifestState, undefined); }); + const updateSkillsInSetting = useRecoilCallback( + ({ set, snapshot }: CallbackInterface) => async ( + skillName: string, + skillInfo: { endpointUrl: string; msAppId: string } + ) => { + const currentSettings: DialogSetting = await snapshot.getPromise(settingsState); + const matchedSkill = get(currentSettings, `skill[${skillName}]`, {}); + if (matchedSkill) { + set(settingsState, { + ...currentSettings, + skill: { + [skillName]: { + ...matchedSkill, + ...skillInfo, + }, + }, + }); + } + } + ); + return { createSkillManifest, removeSkillManifest, @@ -126,5 +147,6 @@ export const skillDispatcher = () => { addSkillDialogSuccess, displayManifestModal, dismissManifestModal, + updateSkillsInSetting, }; }; diff --git a/Composer/packages/client/src/shell/useShell.ts b/Composer/packages/client/src/shell/useShell.ts index c27156b5dc..0750ebc70d 100644 --- a/Composer/packages/client/src/shell/useShell.ts +++ b/Composer/packages/client/src/shell/useShell.ts @@ -70,6 +70,7 @@ export function useShell(source: EventSource): Shell { updateUserSettings, setMessage, displayManifestModal, + updateSkillsInSetting, } = useRecoilValue(dispatcherState); const lgApi = useLgApi(); const luApi = useLuApi(); @@ -190,6 +191,7 @@ export function useShell(source: EventSource): Shell { announce: setMessage, displayManifestModal: displayManifestModal, updateDialogSchema, + updateSkillsInSetting, }; const currentDialog = useMemo(() => dialogs.find((d) => d.id === dialogId), [dialogs, dialogId]); diff --git a/Composer/packages/lib/indexers/src/botIndexer.ts b/Composer/packages/lib/indexers/src/botIndexer.ts index a85d945c6e..60374516a9 100644 --- a/Composer/packages/lib/indexers/src/botIndexer.ts +++ b/Composer/packages/lib/indexers/src/botIndexer.ts @@ -4,10 +4,9 @@ * Verify bot settings, files meet LUIS/QnA requirments. */ -import { BotAssets, BotInfo, LUISLocales, Diagnostic, DiagnosticSeverity, LuFile } from '@bfc/shared'; +import { BotAssets, LUISLocales, Diagnostic, DiagnosticSeverity } from '@bfc/shared'; import difference from 'lodash/difference'; - -import { getLocale } from './utils/help'; +import map from 'lodash/map'; // Verify bot settings, files meet LUIS/QnA requirments. const checkLUISLocales = (assets: BotAssets): Diagnostic[] => { @@ -29,7 +28,7 @@ const checkLUISLocales = (assets: BotAssets): Diagnostic[] => { // Verify bot skill setting. const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { const { - setting: { skill = [], botId, skillHostEndpoint }, + setting: { skill = {}, botId, skillHostEndpoint }, dialogs, } = assets; const diagnostics: Diagnostic[] = []; @@ -37,8 +36,9 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { let skillUsed = false; dialogs.forEach((dialog) => { // used skill not existed in setting + const manifests: string[] = map(skill, ({ manifestUrl }) => manifestUrl); dialog.skills.forEach((skillId) => { - if (skill.findIndex(({ manifestUrl }) => manifestUrl === skillId) === -1) { + if (manifests.findIndex((manifestUrl) => manifestUrl === skillId) === -1) { diagnostics.push( new Diagnostic(`skill '${skillId}' is not existed in appsettings.json`, dialog.id, DiagnosticSeverity.Error) ); @@ -60,28 +60,3 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { return diagnostics; }; - -const filterLUISFilesToPublish = (luFiles: LuFile[]): LuFile[] => { - return luFiles.filter((file) => { - const locale = getLocale(file.id); - return locale && LUISLocales.includes(locale); - }); -}; - -const index = (name: string, assets: BotAssets): BotInfo => { - const diagnostics: Diagnostic[] = []; - diagnostics.push(...checkLUISLocales(assets), ...checkSkillSetting(assets)); - - return { - name, - assets, - diagnostics, - }; -}; - -export const BotIndexer = { - index, - checkLUISLocales, - checkSkillSetting, - filterLUISFilesToPublish, -}; diff --git a/Composer/packages/lib/shared/src/types/shell.ts b/Composer/packages/lib/shared/src/types/shell.ts index 9613ef7bbf..3a2d0581d5 100644 --- a/Composer/packages/lib/shared/src/types/shell.ts +++ b/Composer/packages/lib/shared/src/types/shell.ts @@ -100,6 +100,7 @@ export interface ShellApi { displayManifestModal: (manifestId: string) => void; updateDialogSchema: (_: DialogSchemaFile) => Promise; createTrigger: (id: string, formData, url?: string) => void; + updateSkillsInSetting: (skillName: string, skillsData: { msAppId: string; endpointUrl: string }) => void; } export interface Shell { diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index c0fd04b492..3aeef5e072 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -8,7 +8,7 @@ import axios from 'axios'; import { autofixReferInDialog } from '@bfc/indexers'; import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting, FileExtensions } from '@bfc/shared'; import { UserIdentity, pluginLoader } from '@bfc/plugin-loader'; -import { FeedbackType, generate } from '@microsoft/bf-generate-library'; +import keyBy from 'lodash/keyBy'; import { Path } from '../../utility/path'; import { copyDir } from '../../utility/storage'; @@ -204,9 +204,7 @@ export class BotProject implements IBotProject { const settings = await this.getEnvSettings(false); const { skillsParsed } = await extractSkillManifestUrl(config); - settings.skill = skillsParsed.map(({ manifestUrl, name }) => { - return { manifestUrl, name }; - }); + settings.skill = keyBy(skillsParsed, 'name'); await this.settingManager.set(settings); this.skills = skillsParsed; diff --git a/Composer/packages/server/src/models/bot/skillManager.ts b/Composer/packages/server/src/models/bot/skillManager.ts index f15d3fceab..7f44579d24 100644 --- a/Composer/packages/server/src/models/bot/skillManager.ts +++ b/Composer/packages/server/src/models/bot/skillManager.ts @@ -47,7 +47,7 @@ export const getSkillByUrl = async (url: string, name?: string): Promise }; export const extractSkillManifestUrl = async ( - skills: any[] + skills: Skill[] ): Promise<{ skillsParsed: Skill[]; diagnostics: Diagnostic[] }> => { const skillsParsed: Skill[] = []; const diagnostics: Diagnostic[] = []; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 7dc07b77cf..9f8bfcec06 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -6,16 +6,16 @@ import { FieldProps, JSONSchema7, useShellApi } from '@bfc/extension'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { ObjectField, SchemaField } from '@bfc/adaptive-form'; import formatMessage from 'format-message'; +import { Skill } from '@bfc/shared'; import { SkillEndpointField } from './SkillEndpointField'; export const BeginSkillDialogField: React.FC = (props) => { const { depth, id, schema, uiOptions, value, onChange, definitions } = props; - console.log(value); const { projectId, shellApi, skills = [] } = useShellApi(); - const { displayManifestModal } = shellApi; + const { displayManifestModal, updateSkillsInSetting } = shellApi; - const manifest = useMemo(() => skills.find(({ manifestUrl }) => manifestUrl === value.id), [skills, value.id]); + const manifest: Skill = useMemo(() => skills.find(({ manifestUrl }) => manifestUrl === value.id), [skills, value.id]); const endpointOptions = useMemo(() => (manifest?.endpoints || []).map(({ name }) => name), [manifest]); const handleIdChange = ({ key }) => { @@ -25,9 +25,11 @@ export const BeginSkillDialogField: React.FC = (props) => { } }; - const handleEndpointChange = (skillEndpoint) => { - const { msAppId } = + const handleEndpointChange = async (skillEndpoint) => { + const { msAppId, endpointUrl } = (manifest?.endpoints || []).find(({ endpointUrl }) => endpointUrl === skillEndpoint) || ({} as any); + + updateSkillsInSetting(manifest.name, { endpointUrl, msAppId }); onChange({ ...value, skillEndpoint, ...(msAppId ? { skillAppId: msAppId } : {}) }); }; From 1e2ce9a664b939f9b006633bfc5613ec41ff19a7 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Mon, 31 Aug 2020 09:37:53 -0700 Subject: [PATCH 03/22] skill manifest Signed-off-by: Srinaath Ravichandran --- .../pages/notifications/useNotifications.tsx | 4 +-- .../packages/lib/indexers/src/botIndexer.ts | 33 +++++++++++++++++-- .../server/src/models/bot/botProject.ts | 6 ++-- .../server/src/models/bot/skillManager.ts | 2 +- .../src/BeginSkillDialogField.tsx | 11 +++++-- 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx index 41d06da861..77d5383b26 100644 --- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx +++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import get from 'lodash/get'; -import { checkSkillSetting } from '@bfc/indexers'; +import { BotIndexer } from '@bfc/indexers'; import { luFilesState, @@ -55,7 +55,7 @@ export default function useNotifications(filter?: string) { diagnostics.forEach((d) => { notifications.push(new ServerNotification(projectId, '', d.source, d)); }); - const skillDiagnostics = checkSkillSetting(botAssets); + const skillDiagnostics = BotIndexer.checkSkillSetting(botAssets); skillDiagnostics.forEach((item) => { if (item.source.endsWith('.json')) { notifications.push(new SkillNotification(projectId, item.source, item.source, item)); diff --git a/Composer/packages/lib/indexers/src/botIndexer.ts b/Composer/packages/lib/indexers/src/botIndexer.ts index 60374516a9..4749723995 100644 --- a/Composer/packages/lib/indexers/src/botIndexer.ts +++ b/Composer/packages/lib/indexers/src/botIndexer.ts @@ -4,10 +4,12 @@ * Verify bot settings, files meet LUIS/QnA requirments. */ -import { BotAssets, LUISLocales, Diagnostic, DiagnosticSeverity } from '@bfc/shared'; +import { BotAssets, BotInfo, LUISLocales, Diagnostic, DiagnosticSeverity, LuFile } from '@bfc/shared'; import difference from 'lodash/difference'; import map from 'lodash/map'; +import { getLocale } from './utils/help'; + // Verify bot settings, files meet LUIS/QnA requirments. const checkLUISLocales = (assets: BotAssets): Diagnostic[] => { const { @@ -36,9 +38,9 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { let skillUsed = false; dialogs.forEach((dialog) => { // used skill not existed in setting - const manifests: string[] = map(skill, ({ manifestUrl }) => manifestUrl); dialog.skills.forEach((skillId) => { - if (manifests.findIndex((manifestUrl) => manifestUrl === skillId) === -1) { + const manifestUrlCollection = map(skill, ({ manifestUrl }) => manifestUrl); + if (manifestUrlCollection.findIndex(({ manifestUrl }) => manifestUrl === skillId) === -1) { diagnostics.push( new Diagnostic(`skill '${skillId}' is not existed in appsettings.json`, dialog.id, DiagnosticSeverity.Error) ); @@ -60,3 +62,28 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { return diagnostics; }; + +const filterLUISFilesToPublish = (luFiles: LuFile[]): LuFile[] => { + return luFiles.filter((file) => { + const locale = getLocale(file.id); + return locale && LUISLocales.includes(locale); + }); +}; + +const index = (name: string, assets: BotAssets): BotInfo => { + const diagnostics: Diagnostic[] = []; + diagnostics.push(...checkLUISLocales(assets), ...checkSkillSetting(assets)); + + return { + name, + assets, + diagnostics, + }; +}; + +export const BotIndexer = { + index, + checkLUISLocales, + checkSkillSetting, + filterLUISFilesToPublish, +}; diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index 3aeef5e072..aa20f85a4f 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -4,6 +4,7 @@ import { promisify } from 'util'; import fs from 'fs'; +import values from 'lodash/values'; import axios from 'axios'; import { autofixReferInDialog } from '@bfc/indexers'; import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting, FileExtensions } from '@bfc/shared'; @@ -133,7 +134,8 @@ export class BotProject implements IBotProject { public init = async () => { this.diagnostics = []; this.settings = await this.getEnvSettings(false); - const { skillsParsed, diagnostics } = await extractSkillManifestUrl(this.settings?.skill || ([] as any)); + const skillsCollection = values(this.settings?.skill); + const { skillsParsed, diagnostics } = await extractSkillManifestUrl(skillsCollection || ([] as any)); this.skills = skillsParsed; this.diagnostics.push(...diagnostics); this.files = await this._getFiles(); @@ -200,7 +202,7 @@ export class BotProject implements IBotProject { }; // update skill in settings - public updateSkill = async (config: Skill[]) => { + public updateSkill = async (config: any[]) => { const settings = await this.getEnvSettings(false); const { skillsParsed } = await extractSkillManifestUrl(config); diff --git a/Composer/packages/server/src/models/bot/skillManager.ts b/Composer/packages/server/src/models/bot/skillManager.ts index 7f44579d24..f15d3fceab 100644 --- a/Composer/packages/server/src/models/bot/skillManager.ts +++ b/Composer/packages/server/src/models/bot/skillManager.ts @@ -47,7 +47,7 @@ export const getSkillByUrl = async (url: string, name?: string): Promise }; export const extractSkillManifestUrl = async ( - skills: Skill[] + skills: any[] ): Promise<{ skillsParsed: Skill[]; diagnostics: Diagnostic[] }> => { const skillsParsed: Skill[] = []; const diagnostics: Diagnostic[] = []; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 9f8bfcec06..7d40a43be0 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -20,17 +20,24 @@ export const BeginSkillDialogField: React.FC = (props) => { const handleIdChange = ({ key }) => { if (!manifest || key !== manifest.manifestUrl) { + console.log(manifest); const { skillEndpoint, skillAppId, ...rest } = value; - onChange({ ...rest, id: key }); + onChange({ ...rest, id: `=settings.skill[${manifest.manifestUrl}].msAppId` }); } }; const handleEndpointChange = async (skillEndpoint) => { + console.log(manifest); + console.log(skills); const { msAppId, endpointUrl } = (manifest?.endpoints || []).find(({ endpointUrl }) => endpointUrl === skillEndpoint) || ({} as any); updateSkillsInSetting(manifest.name, { endpointUrl, msAppId }); - onChange({ ...value, skillEndpoint, ...(msAppId ? { skillAppId: msAppId } : {}) }); + onChange({ + ...value, + skillEndpoint: `=settings.skill[${manifest.name}].endpointUrl`, + ...(msAppId ? { skillAppId: `=settings.skill[${manifest.name}].msAppId` } : {}), + }); }; const handleShowManifestClick = () => { From 81672351e84d111dcb9b0dac8f880bf4cecd554e Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Fri, 4 Sep 2020 21:19:24 -0700 Subject: [PATCH 04/22] Supporting skills through dictionary Signed-off-by: Srinaath Ravichandran --- .../src/recoilModel/dispatchers/project.ts | 4 ++ .../src/recoilModel/dispatchers/setting.ts | 22 ++++++++- .../src/recoilModel/dispatchers/skill.ts | 25 +--------- .../packages/client/src/shell/useShell.ts | 14 +++++- .../src/utils/backwardCompatibilityHandler.ts | 25 ++++------ .../packages/lib/indexers/src/botIndexer.ts | 16 +++---- .../packages/lib/shared/src/types/shell.ts | 9 ++-- .../server/src/models/bot/botProject.ts | 15 ++++-- .../src/BeginSkillDialogField.tsx | 47 +++++++++++-------- .../src/SkillEndpointField.tsx | 6 ++- 10 files changed, 106 insertions(+), 77 deletions(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 094aff011e..ca72216213 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -25,6 +25,7 @@ import languageStorage from '../../utils/languageStorage'; import { projectIdCache } from '../../utils/projectCache'; import { designPageLocationState } from '../atoms/botState'; import { QnABotTemplateId } from '../../constants'; +import { convertSkillsToDictionary } from '../../utils/backwardCompatibilityHandler'; import { skillManifestsState, @@ -193,6 +194,9 @@ export const projectDispatcher = () => { set(projectIdState, projectId); refreshLocalStorage(projectId, settings); const mergedSettings = mergeLocalStorage(projectId, settings); + if (isArray(mergedSettings.skill)) { + mergedSettings.skill = convertSkillsToDictionary(mergedSettings.skill); + } set(settingsState, mergedSettings); }); gotoSnapshot(newSnapshot); diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 2ec099b01f..6fd3200642 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -3,7 +3,7 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { CallbackInterface, useRecoilCallback } from 'recoil'; -import { SensitiveProperties, DialogSetting, PublishTarget } from '@bfc/shared'; +import { SensitiveProperties, DialogSetting, PublishTarget, Skill } from '@bfc/shared'; import get from 'lodash/get'; import has from 'lodash/has'; @@ -89,6 +89,25 @@ export const settingsDispatcher = () => { } ); + const updateSkillsInSetting = useRecoilCallback( + ({ set, snapshot }: CallbackInterface) => async (skillName: string, skillInfo: Partial) => { + const currentSettings: DialogSetting = await snapshot.getPromise(settingsState); + const matchedSkill = get(currentSettings, `skill[${skillName}]`, {}); + if (matchedSkill) { + set(settingsState, { + ...currentSettings, + skill: { + ...currentSettings.skill, + [skillName]: { + ...matchedSkill, + ...skillInfo, + }, + }, + }); + } + } + ); + return { setSettings, setRuntimeSettings, @@ -96,5 +115,6 @@ export const settingsDispatcher = () => { setRuntimeField, setCustomRuntime, setQnASettings, + updateSkillsInSetting, }; }; diff --git a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts index 85a0e5c8a9..cc9c799448 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts @@ -3,8 +3,7 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { CallbackInterface, useRecoilCallback } from 'recoil'; -import { SkillManifest, DialogSetting } from '@bfc/shared'; -import get from 'lodash/get'; +import { SkillManifest } from '@bfc/shared'; import httpClient from '../../utils/httpUtil'; import { convertSkillsToDictionary } from '../../utils/backwardCompatibilityHandler'; @@ -116,27 +115,6 @@ export const skillDispatcher = () => { set(displaySkillManifestState, undefined); }); - const updateSkillsInSetting = useRecoilCallback( - ({ set, snapshot }: CallbackInterface) => async ( - skillName: string, - skillInfo: { endpointUrl: string; msAppId: string } - ) => { - const currentSettings: DialogSetting = await snapshot.getPromise(settingsState); - const matchedSkill = get(currentSettings, `skill[${skillName}]`, {}); - if (matchedSkill) { - set(settingsState, { - ...currentSettings, - skill: { - [skillName]: { - ...matchedSkill, - ...skillInfo, - }, - }, - }); - } - } - ); - return { createSkillManifest, removeSkillManifest, @@ -147,6 +125,5 @@ export const skillDispatcher = () => { addSkillDialogSuccess, displayManifestModal, dismissManifestModal, - updateSkillsInSetting, }; }; diff --git a/Composer/packages/client/src/shell/useShell.ts b/Composer/packages/client/src/shell/useShell.ts index 0750ebc70d..444dfcb0dc 100644 --- a/Composer/packages/client/src/shell/useShell.ts +++ b/Composer/packages/client/src/shell/useShell.ts @@ -5,6 +5,7 @@ import { useMemo, useRef } from 'react'; import { ShellApi, ShellData, Shell } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; import formatMessage from 'format-message'; +import lodashGet from 'lodash/get'; import { updateRegExIntent, renameRegExIntent, updateIntentTrigger } from '../utils/dialogUtil'; import { getDialogData, setDialogData } from '../utils/dialogUtil'; @@ -26,6 +27,7 @@ import { focusPathState, userSettingsState, clipboardActionsState, + settingsState, } from '../recoilModel'; import { validatedDialogsSelector } from '../recoilModel/selectors/validatedDialogs'; @@ -56,6 +58,7 @@ export function useShell(source: EventSource): Shell { const userSettings = useRecoilValue(userSettingsState); const clipboardActions = useRecoilValue(clipboardActionsState); const { undo, redo, commitChanges } = useRecoilValue(undoFunctionState); + const settings = useRecoilValue(settingsState); const { updateDialog, updateDialogSchema, @@ -191,7 +194,16 @@ export function useShell(source: EventSource): Shell { announce: setMessage, displayManifestModal: displayManifestModal, updateDialogSchema, - updateSkillsInSetting, + skillsInSettings: { + get: (path: string) => { + if (path) { + const trimmed = path.replace(/=settings.(.*?)/gi, ''); + return lodashGet(settings, trimmed, ''); + } + return ''; + }, + set: updateSkillsInSetting, + }, }; const currentDialog = useMemo(() => dialogs.find((d) => d.id === dialogId), [dialogs, dialogId]); diff --git a/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts b/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts index 654b79565b..bd6de243bd 100644 --- a/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts +++ b/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts @@ -5,20 +5,15 @@ import keyBy from 'lodash/keyBy'; import { Skill } from '@bfc/shared'; export const convertSkillsToDictionary = (skills: Skill[]) => { - const map = new Map(); - const mappedSkills = skills.map((skill: any) => { - if (!map.get(skill)) { - return skill; - } else { - let i = 1; - while (map.get(`${skill}-${i}`)) { - i++; - } - return { - ...skill, - name: `${skill}-${i}`, - }; - } + const mappedSkills = skills.map(({ msAppId, endpointUrl, manifestUrl, name }: Skill) => { + return { + name, + msAppId, + endpointUrl, + manifestUrl, + }; }); - return keyBy(mappedSkills, 'name'); + + const mapped = keyBy(mappedSkills, 'name'); + return mapped; }; diff --git a/Composer/packages/lib/indexers/src/botIndexer.ts b/Composer/packages/lib/indexers/src/botIndexer.ts index 4749723995..0c226e0084 100644 --- a/Composer/packages/lib/indexers/src/botIndexer.ts +++ b/Composer/packages/lib/indexers/src/botIndexer.ts @@ -40,7 +40,7 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { // used skill not existed in setting dialog.skills.forEach((skillId) => { const manifestUrlCollection = map(skill, ({ manifestUrl }) => manifestUrl); - if (manifestUrlCollection.findIndex(({ manifestUrl }) => manifestUrl === skillId) === -1) { + if (manifestUrlCollection.findIndex((manifestUrl) => manifestUrl === skillId) === -1) { diagnostics.push( new Diagnostic(`skill '${skillId}' is not existed in appsettings.json`, dialog.id, DiagnosticSeverity.Error) ); @@ -63,13 +63,6 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { return diagnostics; }; -const filterLUISFilesToPublish = (luFiles: LuFile[]): LuFile[] => { - return luFiles.filter((file) => { - const locale = getLocale(file.id); - return locale && LUISLocales.includes(locale); - }); -}; - const index = (name: string, assets: BotAssets): BotInfo => { const diagnostics: Diagnostic[] = []; diagnostics.push(...checkLUISLocales(assets), ...checkSkillSetting(assets)); @@ -81,6 +74,13 @@ const index = (name: string, assets: BotAssets): BotInfo => { }; }; +const filterLUISFilesToPublish = (luFiles: LuFile[]): LuFile[] => { + return luFiles.filter((file) => { + const locale = getLocale(file.id); + return locale && LUISLocales.includes(locale); + }); +}; + export const BotIndexer = { index, checkLUISLocales, diff --git a/Composer/packages/lib/shared/src/types/shell.ts b/Composer/packages/lib/shared/src/types/shell.ts index 3a2d0581d5..b8c39f6d81 100644 --- a/Composer/packages/lib/shared/src/types/shell.ts +++ b/Composer/packages/lib/shared/src/types/shell.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. /* eslint-disable @typescript-eslint/no-explicit-any */ -import { DialogInfo, LuFile, LgFile, QnAFile, LuIntentSection, LgTemplate, DialogSchemaFile } from './indexers'; +import { DialogInfo, LuFile, LgFile, QnAFile, LuIntentSection, LgTemplate, DialogSchemaFile, Skill } from './indexers'; import { UserSettings } from './settings'; import { OBISchema } from './schema'; @@ -57,7 +57,7 @@ export interface ShellData { luFiles: LuFile[]; qnaFiles: QnAFile[]; userSettings: UserSettings; - skills: any[]; + skills: Skill[]; // TODO: remove schemas: BotSchemas; } @@ -100,7 +100,10 @@ export interface ShellApi { displayManifestModal: (manifestId: string) => void; updateDialogSchema: (_: DialogSchemaFile) => Promise; createTrigger: (id: string, formData, url?: string) => void; - updateSkillsInSetting: (skillName: string, skillsData: { msAppId: string; endpointUrl: string }) => void; + skillsInSettings: { + get: (path: string) => any; + set: (skillName: string, skillsData: Partial) => Promise; + }; } export interface Shell { diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index aa20f85a4f..136d1f9474 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -205,10 +205,17 @@ export class BotProject implements IBotProject { public updateSkill = async (config: any[]) => { const settings = await this.getEnvSettings(false); const { skillsParsed } = await extractSkillManifestUrl(config); - - settings.skill = keyBy(skillsParsed, 'name'); - await this.settingManager.set(settings); - + const trimmedSkillsForSettings = skillsParsed.map((skill) => { + const { msAppId, endpointUrl, manifestUrl, name } = skill; + return { + msAppId, + endpointUrl, + manifestUrl, + name, + }; + }); + const mapped = keyBy(trimmedSkillsForSettings, 'name'); + settings.skill = await this.settingManager.set(mapped); this.skills = skillsParsed; return skillsParsed; }; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 7d40a43be0..375657f9ea 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useMemo } from 'react'; +import React, { useMemo, useEffect } from 'react'; import { FieldProps, JSONSchema7, useShellApi } from '@bfc/extension'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { ObjectField, SchemaField } from '@bfc/adaptive-form'; @@ -13,33 +13,41 @@ import { SkillEndpointField } from './SkillEndpointField'; export const BeginSkillDialogField: React.FC = (props) => { const { depth, id, schema, uiOptions, value, onChange, definitions } = props; const { projectId, shellApi, skills = [] } = useShellApi(); - const { displayManifestModal, updateSkillsInSetting } = shellApi; + const { displayManifestModal, skillsInSettings } = shellApi; - const manifest: Skill = useMemo(() => skills.find(({ manifestUrl }) => manifestUrl === value.id), [skills, value.id]); - const endpointOptions = useMemo(() => (manifest?.endpoints || []).map(({ name }) => name), [manifest]); + const manifest: Skill | undefined = useMemo(() => skills.find(({ manifestUrl }) => manifestUrl === value.id), [ + skills, + value.id, + ]); - const handleIdChange = ({ key }) => { - if (!manifest || key !== manifest.manifestUrl) { - console.log(manifest); + const endpointOptions = useMemo(() => { + return (manifest?.endpoints || []).map(({ name }) => name); + }, [manifest]); + + const handleIdChange = (props) => { + if (!manifest || props.key !== manifest.manifestUrl) { const { skillEndpoint, skillAppId, ...rest } = value; - onChange({ ...rest, id: `=settings.skill[${manifest.manifestUrl}].msAppId` }); + onChange({ ...rest, id: props.key }); } }; const handleEndpointChange = async (skillEndpoint) => { - console.log(manifest); - console.log(skills); const { msAppId, endpointUrl } = - (manifest?.endpoints || []).find(({ endpointUrl }) => endpointUrl === skillEndpoint) || ({} as any); - - updateSkillsInSetting(manifest.name, { endpointUrl, msAppId }); - onChange({ - ...value, - skillEndpoint: `=settings.skill[${manifest.name}].endpointUrl`, - ...(msAppId ? { skillAppId: `=settings.skill[${manifest.name}].msAppId` } : {}), - }); + (manifest?.endpoints || []).find(({ name }) => name === skillEndpoint) || ({} as any); + if (manifest?.name) { + skillsInSettings.set(manifest.name, { endpointUrl, msAppId }); + onChange({ + ...value, + skillEndpoint: `=settings.skill['${manifest?.name}'].endpointUrl`, + ...(msAppId ? { skillAppId: `=settings.skill['${manifest.name}'].msAppId` } : {}), + }); + } }; + useEffect(() => { + handleEndpointChange(manifest?.endpoints[0].name); + }, [endpointOptions]); + const handleShowManifestClick = () => { value.id && displayManifestModal(value.id); }; @@ -47,7 +55,8 @@ export const BeginSkillDialogField: React.FC = (props) => { const skillEndpointUiSchema = uiOptions.properties?.skillEndpoint || {}; skillEndpointUiSchema.serializer = { get: (value) => { - const endpoint = (manifest?.endpoints || []).find(({ endpointUrl }) => endpointUrl === value); + const url: any = skillsInSettings.get(value); + const endpoint = (manifest?.endpoints || []).find(({ endpointUrl }) => endpointUrl === url); return endpoint?.name; }, set: (value) => { diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/SkillEndpointField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/SkillEndpointField.tsx index debd92409f..10c2495fb2 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/SkillEndpointField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/SkillEndpointField.tsx @@ -26,8 +26,10 @@ export const SkillEndpointField: React.FC = (props) => { const deserializedValue = typeof uiOptions?.serializer?.get === 'function' ? uiOptions.serializer.get(value) : value; const handleChange = (newValue: any) => { - const serializedValue = - typeof uiOptions?.serializer?.set === 'function' ? uiOptions.serializer.set(newValue) : newValue; + const serializedValue = newValue; + if (typeof uiOptions?.serializer?.set === 'function') { + uiOptions.serializer.set(newValue); + } onChange(serializedValue); }; From 154418cdf6e4eb1357f2bec49654bdacc0fc6c4d Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Sat, 5 Sep 2020 00:51:36 -0700 Subject: [PATCH 05/22] Safer manifest Id commit Signed-off-by: Srinaath Ravichandran --- .../src/components/SchemaField.tsx | 1 + .../src/BeginSkillDialogField.tsx | 26 ++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx b/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx index 48274ad898..15d6317d8a 100644 --- a/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx +++ b/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx @@ -67,6 +67,7 @@ const SchemaField: React.FC = (props) => { ); const deserializedValue = typeof uiOptions?.serializer?.get === 'function' ? uiOptions.serializer.get(value) : value; + debugger; const FieldWidget = resolveFieldWidget(schema, uiOptions, formUIOptions); const fieldProps: FieldProps = { diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 375657f9ea..89fe731d7a 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useMemo, useEffect } from 'react'; +import React, { useMemo } from 'react'; import { FieldProps, JSONSchema7, useShellApi } from '@bfc/extension'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { ObjectField, SchemaField } from '@bfc/adaptive-form'; @@ -15,19 +15,25 @@ export const BeginSkillDialogField: React.FC = (props) => { const { projectId, shellApi, skills = [] } = useShellApi(); const { displayManifestModal, skillsInSettings } = shellApi; - const manifest: Skill | undefined = useMemo(() => skills.find(({ manifestUrl }) => manifestUrl === value.id), [ - skills, - value.id, - ]); + const manifest: Skill | undefined = useMemo( + () => + skills.find(({ manifestUrl }) => { + debugger; + console.log(manifestUrl); + return manifestUrl === skillsInSettings.get(value.id); + }), + [skills, value.id] + ); const endpointOptions = useMemo(() => { return (manifest?.endpoints || []).map(({ name }) => name); }, [manifest]); - const handleIdChange = (props) => { - if (!manifest || props.key !== manifest.manifestUrl) { + const handleIdChange = ({ key, text }) => { + if (!manifest || key !== manifest.manifestUrl) { const { skillEndpoint, skillAppId, ...rest } = value; - onChange({ ...rest, id: props.key }); + + onChange({ ...rest, id: `=settings.skill['${text}'].manifestUrl` }); } }; @@ -44,10 +50,6 @@ export const BeginSkillDialogField: React.FC = (props) => { } }; - useEffect(() => { - handleEndpointChange(manifest?.endpoints[0].name); - }, [endpointOptions]); - const handleShowManifestClick = () => { value.id && displayManifestModal(value.id); }; From dec825696901c17b5110a42d3a0c62abb913bfb9 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Sat, 5 Sep 2020 12:50:24 -0700 Subject: [PATCH 06/22] manifest is now an expression Signed-off-by: Srinaath Ravichandran --- .../select-skill-dialog/src/BeginSkillDialogField.tsx | 11 +++-------- .../src/SelectSkillDialogField.tsx | 4 ++-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 89fe731d7a..259c5a7532 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -16,12 +16,7 @@ export const BeginSkillDialogField: React.FC = (props) => { const { displayManifestModal, skillsInSettings } = shellApi; const manifest: Skill | undefined = useMemo( - () => - skills.find(({ manifestUrl }) => { - debugger; - console.log(manifestUrl); - return manifestUrl === skillsInSettings.get(value.id); - }), + () => skills.find(({ manifestUrl }) => manifestUrl === skillsInSettings.get(value.id)), [skills, value.id] ); @@ -51,7 +46,7 @@ export const BeginSkillDialogField: React.FC = (props) => { }; const handleShowManifestClick = () => { - value.id && displayManifestModal(value.id); + value.id && displayManifestModal(skillsInSettings.get(value.id)); }; const skillEndpointUiSchema = uiOptions.properties?.skillEndpoint || {}; @@ -77,7 +72,7 @@ export const BeginSkillDialogField: React.FC = (props) => { rawErrors={{}} schema={(schema?.properties?.id as JSONSchema7) || {}} uiOptions={uiOptions.properties?.id || {}} - value={value?.id} + value={skillsInSettings.get(value?.id)} onChange={handleIdChange} /> = (props) => { const { value, onChange } = props; const { shellApi, skills = [] } = useShellApi(); - const { addSkillDialog } = shellApi; + const { addSkillDialog, skillsInSettings } = shellApi; const [comboboxTitle, setComboboxTitle] = useState(null); const options: IComboBoxOption[] = skills.map(({ name, manifestUrl }) => ({ key: manifestUrl, text: name, - isSelected: value === manifestUrl, + isSelected: skillsInSettings.get(value) === manifestUrl, })); options.push( From 50132ed7c2c46cadcb38781a94c1f067255a429f Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Sat, 5 Sep 2020 20:57:08 -0700 Subject: [PATCH 07/22] Handle notifications for skills Signed-off-by: Srinaath Ravichandran --- .../client/src/recoilModel/dispatchers/project.ts | 11 +++++++++-- .../client/src/recoilModel/dispatchers/skill.ts | 3 +-- Composer/packages/client/src/shell/useShell.ts | 11 ++--------- Composer/packages/client/src/utils/dialogUtil.ts | 2 ++ .../lib/indexers/__tests__/botIndexer.test.ts | 11 +++++++---- Composer/packages/lib/indexers/src/botIndexer.ts | 15 +++++++++++++-- Composer/packages/lib/shared/src/index.ts | 1 + .../shared/src/skillsUtils/index.ts} | 12 +++++++++++- .../packages/server/src/models/bot/botProject.ts | 12 +----------- .../src/BeginSkillDialogField.tsx | 10 ++++++++-- 10 files changed, 55 insertions(+), 33 deletions(-) rename Composer/packages/{client/src/utils/backwardCompatibilityHandler.ts => lib/shared/src/skillsUtils/index.ts} (59%) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index ca72216213..aa2bad8425 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -2,7 +2,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. import { useRecoilCallback, CallbackInterface } from 'recoil'; -import { dereferenceDefinitions, LuFile, QnAFile, DialogInfo, SensitiveProperties, DialogSetting } from '@bfc/shared'; +import { + dereferenceDefinitions, + LuFile, + QnAFile, + DialogInfo, + SensitiveProperties, + DialogSetting, + convertSkillsToDictionary, +} from '@bfc/shared'; import { indexer, validateDialog } from '@bfc/indexers'; import objectGet from 'lodash/get'; import objectSet from 'lodash/set'; @@ -25,7 +33,6 @@ import languageStorage from '../../utils/languageStorage'; import { projectIdCache } from '../../utils/projectCache'; import { designPageLocationState } from '../atoms/botState'; import { QnABotTemplateId } from '../../constants'; -import { convertSkillsToDictionary } from '../../utils/backwardCompatibilityHandler'; import { skillManifestsState, diff --git a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts index cc9c799448..2301a213e3 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/skill.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/skill.ts @@ -3,10 +3,9 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { CallbackInterface, useRecoilCallback } from 'recoil'; -import { SkillManifest } from '@bfc/shared'; +import { SkillManifest, convertSkillsToDictionary } from '@bfc/shared'; import httpClient from '../../utils/httpUtil'; -import { convertSkillsToDictionary } from '../../utils/backwardCompatibilityHandler'; import { skillManifestsState, diff --git a/Composer/packages/client/src/shell/useShell.ts b/Composer/packages/client/src/shell/useShell.ts index 444dfcb0dc..e3d56b17c1 100644 --- a/Composer/packages/client/src/shell/useShell.ts +++ b/Composer/packages/client/src/shell/useShell.ts @@ -2,10 +2,9 @@ // Licensed under the MIT License. import { useMemo, useRef } from 'react'; -import { ShellApi, ShellData, Shell } from '@bfc/shared'; +import { ShellApi, ShellData, Shell, fetchFromSettings } from '@bfc/shared'; import { useRecoilValue } from 'recoil'; import formatMessage from 'format-message'; -import lodashGet from 'lodash/get'; import { updateRegExIntent, renameRegExIntent, updateIntentTrigger } from '../utils/dialogUtil'; import { getDialogData, setDialogData } from '../utils/dialogUtil'; @@ -195,13 +194,7 @@ export function useShell(source: EventSource): Shell { displayManifestModal: displayManifestModal, updateDialogSchema, skillsInSettings: { - get: (path: string) => { - if (path) { - const trimmed = path.replace(/=settings.(.*?)/gi, ''); - return lodashGet(settings, trimmed, ''); - } - return ''; - }, + get: (path: string) => fetchFromSettings(path, settings), set: updateSkillsInSetting, }, }; diff --git a/Composer/packages/client/src/utils/dialogUtil.ts b/Composer/packages/client/src/utils/dialogUtil.ts index 1ef458f90e..912b57576a 100644 --- a/Composer/packages/client/src/utils/dialogUtil.ts +++ b/Composer/packages/client/src/utils/dialogUtil.ts @@ -9,6 +9,7 @@ import { DialogInfo, DialogFactory, ITriggerCondition, + DialogSetting, } from '@bfc/shared'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -16,6 +17,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; import formatMessage from 'format-message'; +import lodashGet from 'lodash/get'; import { getFocusPath } from './navigation'; import { upperCaseName } from './fileUtil'; diff --git a/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts b/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts index 82d9587cb0..2acf4b2ca7 100644 --- a/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts +++ b/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { BotAssets, DialogSetting, DialogInfo, DiagnosticSeverity, LuFile } from '@bfc/shared'; +import { BotAssets, DialogSetting, DialogInfo, DiagnosticSeverity, LuFile, ILuisConfig, IQnAConfig } from '@bfc/shared'; import { BotIndexer } from '../src/botIndexer'; const { checkSkillSetting, checkLUISLocales, filterLUISFilesToPublish } = BotIndexer; @@ -32,12 +32,15 @@ const botAssets: BotAssets = { defaultLanguage: 'en-us', botId: '', skillHostEndpoint: '', - skill: [ - { + skill: { + 'Email Skill': { name: 'Email Skill', manifestUrl: 'skill1', }, - ], + }, + luis: {} as ILuisConfig, + qna: {} as IQnAConfig, + runtime: {} as any, } as DialogSetting, }; diff --git a/Composer/packages/lib/indexers/src/botIndexer.ts b/Composer/packages/lib/indexers/src/botIndexer.ts index 0c226e0084..6534c34ee9 100644 --- a/Composer/packages/lib/indexers/src/botIndexer.ts +++ b/Composer/packages/lib/indexers/src/botIndexer.ts @@ -4,7 +4,15 @@ * Verify bot settings, files meet LUIS/QnA requirments. */ -import { BotAssets, BotInfo, LUISLocales, Diagnostic, DiagnosticSeverity, LuFile } from '@bfc/shared'; +import { + BotAssets, + BotInfo, + LUISLocales, + Diagnostic, + DiagnosticSeverity, + LuFile, + fetchFromSettings, +} from '@bfc/shared'; import difference from 'lodash/difference'; import map from 'lodash/map'; @@ -40,7 +48,10 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { // used skill not existed in setting dialog.skills.forEach((skillId) => { const manifestUrlCollection = map(skill, ({ manifestUrl }) => manifestUrl); - if (manifestUrlCollection.findIndex((manifestUrl) => manifestUrl === skillId) === -1) { + if ( + manifestUrlCollection.findIndex((manifestUrl) => manifestUrl === fetchFromSettings(skillId, assets.setting)) === + -1 + ) { diagnostics.push( new Diagnostic(`skill '${skillId}' is not existed in appsettings.json`, dialog.id, DiagnosticSeverity.Error) ); diff --git a/Composer/packages/lib/shared/src/index.ts b/Composer/packages/lib/shared/src/index.ts index 002cbe6d7e..0b726ece36 100644 --- a/Composer/packages/lib/shared/src/index.ts +++ b/Composer/packages/lib/shared/src/index.ts @@ -21,4 +21,5 @@ export * from './schemaUtils'; export * from './types'; export * from './viewUtils'; export * from './walkerUtils'; +export * from './skillsUtils'; export const DialogUtils = dialogUtils; diff --git a/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts b/Composer/packages/lib/shared/src/skillsUtils/index.ts similarity index 59% rename from Composer/packages/client/src/utils/backwardCompatibilityHandler.ts rename to Composer/packages/lib/shared/src/skillsUtils/index.ts index bd6de243bd..84307c44b0 100644 --- a/Composer/packages/client/src/utils/backwardCompatibilityHandler.ts +++ b/Composer/packages/lib/shared/src/skillsUtils/index.ts @@ -1,8 +1,18 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import get from 'lodash/get'; import keyBy from 'lodash/keyBy'; -import { Skill } from '@bfc/shared'; + +import { DialogSetting, Skill } from '../types'; + +export function fetchFromSettings(path: string, settings: DialogSetting): string { + if (path) { + const trimmed = path.replace(/=settings.(.*?)/gi, ''); + return get(settings, trimmed, ''); + } + return ''; +} export const convertSkillsToDictionary = (skills: Skill[]) => { const mappedSkills = skills.map(({ msAppId, endpointUrl, manifestUrl, name }: Skill) => { diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index 136d1f9474..a4bed65101 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -9,7 +9,6 @@ import axios from 'axios'; import { autofixReferInDialog } from '@bfc/indexers'; import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting, FileExtensions } from '@bfc/shared'; import { UserIdentity, pluginLoader } from '@bfc/plugin-loader'; -import keyBy from 'lodash/keyBy'; import { Path } from '../../utility/path'; import { copyDir } from '../../utility/storage'; @@ -205,16 +204,7 @@ export class BotProject implements IBotProject { public updateSkill = async (config: any[]) => { const settings = await this.getEnvSettings(false); const { skillsParsed } = await extractSkillManifestUrl(config); - const trimmedSkillsForSettings = skillsParsed.map((skill) => { - const { msAppId, endpointUrl, manifestUrl, name } = skill; - return { - msAppId, - endpointUrl, - manifestUrl, - name, - }; - }); - const mapped = keyBy(trimmedSkillsForSettings, 'name'); + const mapped = convertSkillsToDictionary(skillsParsed); settings.skill = await this.settingManager.set(mapped); this.skills = skillsParsed; return skillsParsed; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 259c5a7532..c2c507a01f 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -62,6 +62,12 @@ export const BeginSkillDialogField: React.FC = (props) => { }, }; + const skillIdUiSchema = uiOptions.properties?.id || {}; + skillIdUiSchema.serializer = { + get: (value) => skillsInSettings.get(value), + set: (value) => value, + }; + return ( = (props) => { name="id" rawErrors={{}} schema={(schema?.properties?.id as JSONSchema7) || {}} - uiOptions={uiOptions.properties?.id || {}} - value={skillsInSettings.get(value?.id)} + uiOptions={skillIdUiSchema} + value={value?.id} onChange={handleIdChange} /> Date: Sat, 5 Sep 2020 23:17:46 -0700 Subject: [PATCH 08/22] More unit tests fixed Signed-off-by: Srinaath Ravichandran --- .../notifications/useNotifications.test.tsx | 12 ++--- .../packages/client/src/utils/dialogUtil.ts | 2 - .../src/components/SchemaField.tsx | 1 - .../lib/indexers/__tests__/botIndexer.test.ts | 4 +- .../samplebots/bot1/settings/appsettings.json | 22 ++++++-- .../src/BeginSkillDialogField.tsx | 2 - .../__tests__/BeginSkillDialogField.test.tsx | 53 ++++++++++++++----- .../src/__tests__/SelectSkillDialog.test.tsx | 8 +++ 8 files changed, 72 insertions(+), 32 deletions(-) diff --git a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx index 45228c028f..e285971db6 100644 --- a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx +++ b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx @@ -26,7 +26,7 @@ const state = { content: 'test', luFile: 'test', referredLuIntents: [], - skills: ['https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json'], + skills: [`=settings.skill['Email-Skill'].manifestUrl`], }, ], luFiles: [ @@ -86,12 +86,10 @@ const state = { }, ], settings: { - skill: [ - { - manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', - name: 'Email Skill', - }, - ], + skill: { + manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', + name: 'Email-Skill', + }, }, }; diff --git a/Composer/packages/client/src/utils/dialogUtil.ts b/Composer/packages/client/src/utils/dialogUtil.ts index 912b57576a..1ef458f90e 100644 --- a/Composer/packages/client/src/utils/dialogUtil.ts +++ b/Composer/packages/client/src/utils/dialogUtil.ts @@ -9,7 +9,6 @@ import { DialogInfo, DialogFactory, ITriggerCondition, - DialogSetting, } from '@bfc/shared'; import get from 'lodash/get'; import set from 'lodash/set'; @@ -17,7 +16,6 @@ import cloneDeep from 'lodash/cloneDeep'; import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; import formatMessage from 'format-message'; -import lodashGet from 'lodash/get'; import { getFocusPath } from './navigation'; import { upperCaseName } from './fileUtil'; diff --git a/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx b/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx index 15d6317d8a..48274ad898 100644 --- a/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx +++ b/Composer/packages/extensions/adaptive-form/src/components/SchemaField.tsx @@ -67,7 +67,6 @@ const SchemaField: React.FC = (props) => { ); const deserializedValue = typeof uiOptions?.serializer?.get === 'function' ? uiOptions.serializer.get(value) : value; - debugger; const FieldWidget = resolveFieldWidget(schema, uiOptions, formUIOptions); const fieldProps: FieldProps = { diff --git a/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts b/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts index 2acf4b2ca7..7eee9509c4 100644 --- a/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts +++ b/Composer/packages/lib/indexers/__tests__/botIndexer.test.ts @@ -33,8 +33,8 @@ const botAssets: BotAssets = { botId: '', skillHostEndpoint: '', skill: { - 'Email Skill': { - name: 'Email Skill', + 'Email-Skill': { + name: 'Email-Skill', manifestUrl: 'skill1', }, }, diff --git a/Composer/packages/server/__tests__/mocks/samplebots/bot1/settings/appsettings.json b/Composer/packages/server/__tests__/mocks/samplebots/bot1/settings/appsettings.json index 4f742339ad..e468dd1746 100644 --- a/Composer/packages/server/__tests__/mocks/samplebots/bot1/settings/appsettings.json +++ b/Composer/packages/server/__tests__/mocks/samplebots/bot1/settings/appsettings.json @@ -4,11 +4,25 @@ "name": "", "authoringRegion": "westus", "defaultLanguage": "en-us", - "environment": "composer" + "environment": "composer", + "endpoint": "", + "authoringEndpoint": "", + "authoringKey": "", + "endpointKey": "" }, "qna": { "knowledgebaseid": "", - "endpointkey": "", - "hostname": "" - } + "hostname": "", + "endpointKey": "", + "subscriptionKey": "" + }, + "downsampling": { + "maxImbalanceRatio": 10, + "maxUtteranceAllowed": 15000 + }, + "defaultLanguage": "en-us", + "languages": [ + "en-us" + ], + "MicrosoftAppPassword": "" } \ No newline at end of file diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index c2c507a01f..58fb6f9f2b 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -19,7 +19,6 @@ export const BeginSkillDialogField: React.FC = (props) => { () => skills.find(({ manifestUrl }) => manifestUrl === skillsInSettings.get(value.id)), [skills, value.id] ); - const endpointOptions = useMemo(() => { return (manifest?.endpoints || []).map(({ name }) => name); }, [manifest]); @@ -27,7 +26,6 @@ export const BeginSkillDialogField: React.FC = (props) => { const handleIdChange = ({ key, text }) => { if (!manifest || key !== manifest.manifestUrl) { const { skillEndpoint, skillAppId, ...rest } = value; - onChange({ ...rest, id: `=settings.skill['${text}'].manifestUrl` }); } }; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx index 2a1f54eac5..1d344bb8c0 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx @@ -3,14 +3,30 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import React from 'react'; -import { fireEvent, getAllByRole, render } from '@bfc/test-utils'; +import { fireEvent, getAllByRole, render, act } from '@bfc/test-utils'; import { Extension, JSONSchema7 } from '@bfc/extension'; -import { SDKKinds } from '@bfc/shared'; +import { SDKKinds, fetchFromSettings, convertSkillsToDictionary, Skill } from '@bfc/shared'; import { BeginSkillDialogField } from '../BeginSkillDialogField'; import pluginConfig from '..'; -import { schema, skills } from './constants'; +import { schema } from './constants'; + +const skills: any = [ + { + manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', + name: 'yuesuemailskill0207', + endpoints: [ + { + name: 'production', + protocol: 'BotFrameworkV3', + description: 'Production endpoint for the Email Skill', + endpointUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/api/messages', + msAppId: '79432da8-0f7e-4a16-8c23-ddbba30ae85d', + }, + ], + }, +]; const renderBeginSkillDialog = ({ value = {}, onChange = jest.fn() } = {}) => { const addSkillDialog = jest.fn().mockResolvedValue({ manifestUrl: 'https://' }); @@ -26,13 +42,20 @@ const renderBeginSkillDialog = ({ value = {}, onChange = jest.fn() } = {}) => { name: 'select.skillDialog', }; - const shell: any = { - addSkillDialog, - }; - const shellData: any = { skills, }; + const setting: any = { + skill: convertSkillsToDictionary(skills), + }; + + const shell: any = { + addSkillDialog, + skillsInSettings: { + get: (path: string) => fetchFromSettings(path, setting), + set: () => {}, + }, + }; return render( @@ -46,21 +69,23 @@ describe('Begin Skill Dialog', () => { jest.useFakeTimers(); }); - it('should add a new skill', async () => { + fit('should add a new skill', async () => { const onChange = jest.fn(); - const value = { id: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json' }; + const value = { id: `=settings.skill['yuesuemailskill0207'].manifestUrl` }; const { baseElement, findByRole } = renderBeginSkillDialog({ value, onChange }); const listbox = await findByRole('listbox'); fireEvent.click(listbox); - const endpoints = await getAllByRole(baseElement, 'option'); - fireEvent.click(endpoints[endpoints.length - 1]); + const endpoints = getAllByRole(baseElement, 'option'); + act(() => { + fireEvent.click(endpoints[endpoints.length - 1]); + }); expect(onChange).toHaveBeenCalledWith({ - id: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', - skillAppId: '79432da8-0f7e-4a16-8c23-ddbba30ae85d', - skillEndpoint: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/api/messages', + id: "=settings.skill['yuesuemailskill0207'].manifestUrl", + skillAppId: "=settings.skill['yuesuemailskill0207'].msAppId", + skillEndpoint: "=settings.skill['yuesuemailskill0207'].endpointUrl", }); }); }); diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx index 56fc9e952d..16e83b48eb 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx @@ -6,6 +6,7 @@ import React from 'react'; import { fireEvent, getAllByRole, render, act } from '@bfc/test-utils'; import { Extension } from '@bfc/extension'; +import { fetchFromSettings, convertSkillsToDictionary } from '@bfc/shared'; import { SelectSkillDialog } from '../SelectSkillDialogField'; @@ -48,6 +49,13 @@ const renderSelectSkillDialog = ({ addSkillDialog, onChange } = {}) => { const shell = { addSkillDialog, + skillsInSettings: { + get: (path: string) => + fetchFromSettings(path, { + skill: convertSkillsToDictionary(skills), + }), + set: () => {}, + }, }; const shellData = { From f79ebc9d8156c5b085e794ea207868fa5b881bd7 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Tue, 8 Sep 2020 09:24:24 -0700 Subject: [PATCH 09/22] UseNotification test fixed Signed-off-by: Srinaath Ravichandran --- .../__tests__/pages/notifications/useNotifications.test.tsx | 6 ++++-- .../packages/ui-plugins/select-skill-dialog/jest.config.js | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx index e285971db6..7ab865e38d 100644 --- a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx +++ b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx @@ -87,8 +87,10 @@ const state = { ], settings: { skill: { - manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', - name: 'Email-Skill', + 'Email-Skill': { + manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', + name: 'Email-Skill', + }, }, }, }; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/jest.config.js b/Composer/packages/ui-plugins/select-skill-dialog/jest.config.js index 05fd7d1da6..e1eea01308 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/jest.config.js +++ b/Composer/packages/ui-plugins/select-skill-dialog/jest.config.js @@ -5,5 +5,5 @@ const { createConfig } = require('@bfc/test-utils'); module.exports = createConfig('ui-plugin/select-skill-dialog', 'react', { - testPathIgnorePatterns: ['__tests__/constants.ts'], + testPathIgnorePatterns: ['__tests__/constants.ts', 'lib'], }); From 538120bd970403c14f3615ab64a244918a0c65d8 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Tue, 8 Sep 2020 13:49:12 -0700 Subject: [PATCH 10/22] Handle backward compatibility Signed-off-by: Srinaath Ravichandran --- .../src/recoilModel/dispatchers/project.ts | 10 +++- .../src/BeginSkillDialogField.tsx | 50 ++++++++++++++++--- .../__tests__/BeginSkillDialogField.test.tsx | 19 ++++++- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index aa2bad8425..1dec31f12c 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -202,7 +202,15 @@ export const projectDispatcher = () => { refreshLocalStorage(projectId, settings); const mergedSettings = mergeLocalStorage(projectId, settings); if (isArray(mergedSettings.skill)) { - mergedSettings.skill = convertSkillsToDictionary(mergedSettings.skill); + const skillsArr = mergedSettings.skill.map((skillData) => { + const matchedSkill = skills.find((currentSkill) => currentSkill.name === skillData.name); + return { + ...skillData, + msAppId: matchedSkill.msAppId, + endpointUrl: matchedSkill.endpointUrl, + }; + }); + mergedSettings.skill = convertSkillsToDictionary(skillsArr); } set(settingsState, mergedSettings); }); diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 58fb6f9f2b..ac34f45e84 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -10,15 +10,51 @@ import { Skill } from '@bfc/shared'; import { SkillEndpointField } from './SkillEndpointField'; +const referBySettings = (skillName: string, property: string) => { + return `=settings.skill['${skillName}'].${property}`; +}; + +const handleBackwardCompat = (skills: Skill[], value): { name: string; referredUpdates: any } | undefined => { + const { skillEndpoint } = value; + const foundSkill = skills.find(({ manifestUrl }) => manifestUrl === value.id); + let updates: any = {}; + if (foundSkill) { + updates = { + id: referBySettings(foundSkill.name, 'manifestUrl'), + }; + const matchedEndpoint: any = foundSkill.endpoints.find(({ endpointUrl }) => endpointUrl === skillEndpoint); + if (matchedEndpoint) { + updates.skillEndpoint = referBySettings(foundSkill.name, 'endpointUrl'); + updates.skillAppId = referBySettings(foundSkill.name, 'msAppId'); + } + + return { + name: foundSkill?.name, + referredUpdates: updates, + }; + } +}; + export const BeginSkillDialogField: React.FC = (props) => { const { depth, id, schema, uiOptions, value, onChange, definitions } = props; const { projectId, shellApi, skills = [] } = useShellApi(); const { displayManifestModal, skillsInSettings } = shellApi; - const manifest: Skill | undefined = useMemo( - () => skills.find(({ manifestUrl }) => manifestUrl === skillsInSettings.get(value.id)), - [skills, value.id] - ); + const manifest: Skill | undefined = useMemo(() => { + const matchedSkill = skills.find(({ manifestUrl }) => { + return manifestUrl === skillsInSettings.get(value.id); + }); + // Handle backward compatibility + if (!matchedSkill) { + const { id, skillEndpoint, skillAppId, ...rest } = value; + const result = handleBackwardCompat(skills, value); + if (result) { + onChange({ ...rest, ...result.referredUpdates }); + } + } + return matchedSkill; + }, [skills, value.id]); + const endpointOptions = useMemo(() => { return (manifest?.endpoints || []).map(({ name }) => name); }, [manifest]); @@ -26,7 +62,7 @@ export const BeginSkillDialogField: React.FC = (props) => { const handleIdChange = ({ key, text }) => { if (!manifest || key !== manifest.manifestUrl) { const { skillEndpoint, skillAppId, ...rest } = value; - onChange({ ...rest, id: `=settings.skill['${text}'].manifestUrl` }); + onChange({ ...rest, id: referBySettings(text, 'manifestUrl') }); } }; @@ -37,8 +73,8 @@ export const BeginSkillDialogField: React.FC = (props) => { skillsInSettings.set(manifest.name, { endpointUrl, msAppId }); onChange({ ...value, - skillEndpoint: `=settings.skill['${manifest?.name}'].endpointUrl`, - ...(msAppId ? { skillAppId: `=settings.skill['${manifest.name}'].msAppId` } : {}), + skillEndpoint: referBySettings(manifest?.name, 'endpointUrl'), + ...(msAppId ? { skillAppId: referBySettings(manifest?.name, 'msAppId') } : {}), }); } }; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx index 1d344bb8c0..a02e4da6f0 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { fireEvent, getAllByRole, render, act } from '@bfc/test-utils'; import { Extension, JSONSchema7 } from '@bfc/extension'; -import { SDKKinds, fetchFromSettings, convertSkillsToDictionary, Skill } from '@bfc/shared'; +import { SDKKinds, fetchFromSettings, convertSkillsToDictionary } from '@bfc/shared'; import { BeginSkillDialogField } from '../BeginSkillDialogField'; import pluginConfig from '..'; @@ -69,7 +69,7 @@ describe('Begin Skill Dialog', () => { jest.useFakeTimers(); }); - fit('should add a new skill', async () => { + it('should add a new skill', async () => { const onChange = jest.fn(); const value = { id: `=settings.skill['yuesuemailskill0207'].manifestUrl` }; const { baseElement, findByRole } = renderBeginSkillDialog({ value, onChange }); @@ -88,4 +88,19 @@ describe('Begin Skill Dialog', () => { skillEndpoint: "=settings.skill['yuesuemailskill0207'].endpointUrl", }); }); + + fit('should be backwards compatible', async () => { + const onChange = jest.fn(); + const value = { + id: `https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json`, + skillEndpoint: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/api/messages', + }; + renderBeginSkillDialog({ value, onChange }); + + expect(onChange).toHaveBeenCalledWith({ + id: "=settings.skill['yuesuemailskill0207'].manifestUrl", + skillAppId: "=settings.skill['yuesuemailskill0207'].msAppId", + skillEndpoint: "=settings.skill['yuesuemailskill0207'].endpointUrl", + }); + }); }); From cfd927b4b5383dfe912d4dd1cf743e9f5493c596 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Tue, 8 Sep 2020 13:55:23 -0700 Subject: [PATCH 11/22] conditional checks Signed-off-by: Srinaath Ravichandran --- .../packages/client/src/recoilModel/dispatchers/project.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 1dec31f12c..8190cd143f 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -206,8 +206,8 @@ export const projectDispatcher = () => { const matchedSkill = skills.find((currentSkill) => currentSkill.name === skillData.name); return { ...skillData, - msAppId: matchedSkill.msAppId, - endpointUrl: matchedSkill.endpointUrl, + msAppId: matchedSkill?.msAppId, + endpointUrl: matchedSkill?.endpointUrl, }; }); mergedSettings.skill = convertSkillsToDictionary(skillsArr); From 6168435d9f7c7cb5f78a44314f7a65d91c01c7b9 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 10:40:58 -0700 Subject: [PATCH 12/22] PR feedback fixed Removed id field from Schema --- .../notifications/useNotifications.test.tsx | 3 +- .../src/recoilModel/dispatchers/project.ts | 9 +- .../src/recoilModel/dispatchers/setting.ts | 2 +- .../packages/client/src/shell/useShell.ts | 2 +- .../packages/lib/indexers/src/botIndexer.ts | 7 +- .../lib/indexers/src/dialogIndexer.ts | 4 +- .../lib/shared/src/skillsUtils/index.ts | 3 +- .../packages/lib/shared/src/types/settings.ts | 4 +- .../packages/lib/shared/src/types/shell.ts | 2 +- .../select-dialog/src/ComboBoxField.tsx | 27 ++-- .../src/BeginSkillDialogField.tsx | 126 ++++++++++-------- .../select-skill-dialog/src/ComboBoxField.tsx | 14 +- .../src/SelectSkillDialogField.tsx | 41 ++++-- .../__tests__/BeginSkillDialogField.test.tsx | 7 +- .../src/__tests__/SelectSkillDialog.test.tsx | 6 +- .../select-skill-dialog/src/index.ts | 9 -- 16 files changed, 139 insertions(+), 127 deletions(-) diff --git a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx index 7ab865e38d..c0914b37a9 100644 --- a/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx +++ b/Composer/packages/client/__tests__/pages/notifications/useNotifications.test.tsx @@ -26,7 +26,7 @@ const state = { content: 'test', luFile: 'test', referredLuIntents: [], - skills: [`=settings.skill['Email-Skill'].manifestUrl`], + skills: [`=settings.skill['Email-Skill'].endpointUrl`], }, ], luFiles: [ @@ -89,6 +89,7 @@ const state = { skill: { 'Email-Skill': { manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', + endpointUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/api/messages', name: 'Email-Skill', }, }, diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 8190cd143f..5227b35194 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -14,7 +14,6 @@ import { import { indexer, validateDialog } from '@bfc/indexers'; import objectGet from 'lodash/get'; import objectSet from 'lodash/set'; -import isArray from 'lodash/isArray'; import formatMessage from 'format-message'; import lgWorker from '../parsers/lgWorker'; @@ -201,13 +200,11 @@ export const projectDispatcher = () => { set(projectIdState, projectId); refreshLocalStorage(projectId, settings); const mergedSettings = mergeLocalStorage(projectId, settings); - if (isArray(mergedSettings.skill)) { + if (Array.isArray(mergedSettings.skill)) { const skillsArr = mergedSettings.skill.map((skillData) => { - const matchedSkill = skills.find((currentSkill) => currentSkill.name === skillData.name); + // const matchedSkill = skills.find((currentSkill) => currentSkill.name === skillData.name); return { ...skillData, - msAppId: matchedSkill?.msAppId, - endpointUrl: matchedSkill?.endpointUrl, }; }); mergedSettings.skill = convertSkillsToDictionary(skillsArr); @@ -366,7 +363,7 @@ export const projectDispatcher = () => { const { set } = callbackHelpers; try { const response = await httpClient.get(`/runtime/templates`); - if (isArray(response.data)) { + if (Array.isArray(response.data)) { set(runtimeTemplatesState, [...response.data]); } } catch (ex) { diff --git a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts index 6fd3200642..aa2e75964d 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/setting.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/setting.ts @@ -92,7 +92,7 @@ export const settingsDispatcher = () => { const updateSkillsInSetting = useRecoilCallback( ({ set, snapshot }: CallbackInterface) => async (skillName: string, skillInfo: Partial) => { const currentSettings: DialogSetting = await snapshot.getPromise(settingsState); - const matchedSkill = get(currentSettings, `skill[${skillName}]`, {}); + const matchedSkill = get(currentSettings, `skill[${skillName}]`, undefined); if (matchedSkill) { set(settingsState, { ...currentSettings, diff --git a/Composer/packages/client/src/shell/useShell.ts b/Composer/packages/client/src/shell/useShell.ts index e3d56b17c1..d65310c2d0 100644 --- a/Composer/packages/client/src/shell/useShell.ts +++ b/Composer/packages/client/src/shell/useShell.ts @@ -180,7 +180,7 @@ export function useShell(source: EventSource): Shell { }, addSkillDialog: () => { return new Promise((resolve) => { - addSkillDialogBegin((newSkill: { manifestUrl: string } | null) => { + addSkillDialogBegin((newSkill: { manifestUrl: string; name: string } | null) => { resolve(newSkill); }); }); diff --git a/Composer/packages/lib/indexers/src/botIndexer.ts b/Composer/packages/lib/indexers/src/botIndexer.ts index 6534c34ee9..6e6b8806a3 100644 --- a/Composer/packages/lib/indexers/src/botIndexer.ts +++ b/Composer/packages/lib/indexers/src/botIndexer.ts @@ -47,11 +47,8 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { dialogs.forEach((dialog) => { // used skill not existed in setting dialog.skills.forEach((skillId) => { - const manifestUrlCollection = map(skill, ({ manifestUrl }) => manifestUrl); - if ( - manifestUrlCollection.findIndex((manifestUrl) => manifestUrl === fetchFromSettings(skillId, assets.setting)) === - -1 - ) { + const endpointUrlCollection = map(skill, ({ endpointUrl }) => endpointUrl); + if (!endpointUrlCollection.includes(fetchFromSettings(skillId, assets.setting))) { diagnostics.push( new Diagnostic(`skill '${skillId}' is not existed in appsettings.json`, dialog.id, DiagnosticSeverity.Error) ); diff --git a/Composer/packages/lib/indexers/src/dialogIndexer.ts b/Composer/packages/lib/indexers/src/dialogIndexer.ts index 3f7e96095c..443608cfbf 100644 --- a/Composer/packages/lib/indexers/src/dialogIndexer.ts +++ b/Composer/packages/lib/indexers/src/dialogIndexer.ts @@ -166,8 +166,8 @@ function extractReferredSkills(dialog): string[] { const visitor: VisitorFunc = (path: string, value: any): boolean => { // it's a valid schema dialog node. if (has(value, '$kind') && value.$kind === SDKKinds.BeginSkill) { - const skillId = value.id; - skills.push(skillId); + const skillEndpoint = value.skillEndpoint; + skills.push(skillEndpoint); } return false; }; diff --git a/Composer/packages/lib/shared/src/skillsUtils/index.ts b/Composer/packages/lib/shared/src/skillsUtils/index.ts index 84307c44b0..ee6cd5c727 100644 --- a/Composer/packages/lib/shared/src/skillsUtils/index.ts +++ b/Composer/packages/lib/shared/src/skillsUtils/index.ts @@ -24,6 +24,5 @@ export const convertSkillsToDictionary = (skills: Skill[]) => { }; }); - const mapped = keyBy(mappedSkills, 'name'); - return mapped; + return keyBy(mappedSkills, 'name'); }; diff --git a/Composer/packages/lib/shared/src/types/settings.ts b/Composer/packages/lib/shared/src/types/settings.ts index efc1d287cf..ca58266feb 100644 --- a/Composer/packages/lib/shared/src/types/settings.ts +++ b/Composer/packages/lib/shared/src/types/settings.ts @@ -34,10 +34,10 @@ export interface DialogSetting { languages: string[]; skill?: { [skillName: string]: { - name?: string; + name: string; manifestUrl: string; msAppId?: string; - endpoint?: string; + endpointUrl: string; }; }; botId?: string; diff --git a/Composer/packages/lib/shared/src/types/shell.ts b/Composer/packages/lib/shared/src/types/shell.ts index b8c39f6d81..a077fc39c4 100644 --- a/Composer/packages/lib/shared/src/types/shell.ts +++ b/Composer/packages/lib/shared/src/types/shell.ts @@ -95,7 +95,7 @@ export interface ShellApi { redo: () => void; commitChanges: () => void; updateUserSettings: (settings: AllPartial) => void; - addSkillDialog: () => Promise<{ manifestUrl: string } | null>; + addSkillDialog: () => Promise<{ manifestUrl: string; name: string } | null>; announce: (message: string) => void; displayManifestModal: (manifestId: string) => void; updateDialogSchema: (_: DialogSchemaFile) => Promise; diff --git a/Composer/packages/ui-plugins/select-dialog/src/ComboBoxField.tsx b/Composer/packages/ui-plugins/select-dialog/src/ComboBoxField.tsx index 46ee8d5f00..28ca6e6c1f 100644 --- a/Composer/packages/ui-plugins/select-dialog/src/ComboBoxField.tsx +++ b/Composer/packages/ui-plugins/select-dialog/src/ComboBoxField.tsx @@ -17,19 +17,20 @@ interface ComboBoxFieldProps extends FieldProps { onChange: any; } -export const ComboBoxField: React.FC = ({ - comboboxTitle, - description, - id, - label, - options, - value = '', - required, - uiOptions, - onBlur, - onChange, - onFocus, -}) => { +export const ComboBoxField: React.FC = (props) => { + const { + comboboxTitle, + description, + id, + label, + options, + value = '', + required, + uiOptions, + onBlur, + onChange, + onFocus, + } = props; const onRenderOption: IRenderFunction = (option) => option ? (
diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index ac34f45e84..5cdcce8bb0 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -1,36 +1,38 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useMemo } from 'react'; +import React, { useMemo, useState, useEffect } from 'react'; import { FieldProps, JSONSchema7, useShellApi } from '@bfc/extension'; +import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; import { Link } from 'office-ui-fabric-react/lib/Link'; -import { ObjectField, SchemaField } from '@bfc/adaptive-form'; +import { ObjectField } from '@bfc/adaptive-form'; import formatMessage from 'format-message'; import { Skill } from '@bfc/shared'; +import { SelectSkillDialog } from './SelectSkillDialogField'; import { SkillEndpointField } from './SkillEndpointField'; +const getNameFromSetting = (value: string) => { + const nameRegexp = new RegExp(/\['(.*?)'\]/g); + const matched = nameRegexp.exec(value); + if (matched && matched.length > 1) { + return matched[1]; + } + return ''; +}; + const referBySettings = (skillName: string, property: string) => { return `=settings.skill['${skillName}'].${property}`; }; -const handleBackwardCompat = (skills: Skill[], value): { name: string; referredUpdates: any } | undefined => { +const handleBackwardCompatibility = (skills: Skill[], value): { name: string; endpointName: string } | undefined => { const { skillEndpoint } = value; const foundSkill = skills.find(({ manifestUrl }) => manifestUrl === value.id); - let updates: any = {}; if (foundSkill) { - updates = { - id: referBySettings(foundSkill.name, 'manifestUrl'), - }; const matchedEndpoint: any = foundSkill.endpoints.find(({ endpointUrl }) => endpointUrl === skillEndpoint); - if (matchedEndpoint) { - updates.skillEndpoint = referBySettings(foundSkill.name, 'endpointUrl'); - updates.skillAppId = referBySettings(foundSkill.name, 'msAppId'); - } - return { name: foundSkill?.name, - referredUpdates: updates, + endpointName: matchedEndpoint ? matchedEndpoint.name : '', }; } }; @@ -39,84 +41,92 @@ export const BeginSkillDialogField: React.FC = (props) => { const { depth, id, schema, uiOptions, value, onChange, definitions } = props; const { projectId, shellApi, skills = [] } = useShellApi(); const { displayManifestModal, skillsInSettings } = shellApi; + const [selectedSkill, setSelectedSkill] = useState(''); + const [oldEndpoint, loadEndpointForOldBots] = useState(''); + + useEffect(() => { + const { skillEndpoint } = value; + const skill = skills.find(({ name }) => name === getNameFromSetting(skillEndpoint)); - const manifest: Skill | undefined = useMemo(() => { - const matchedSkill = skills.find(({ manifestUrl }) => { - return manifestUrl === skillsInSettings.get(value.id); - }); - // Handle backward compatibility - if (!matchedSkill) { - const { id, skillEndpoint, skillAppId, ...rest } = value; - const result = handleBackwardCompat(skills, value); + if (skill) { + setSelectedSkill(skill.name); + } else { + const result = handleBackwardCompatibility(skills, value); if (result) { - onChange({ ...rest, ...result.referredUpdates }); + setSelectedSkill(result.name); + if (result.endpointName) { + loadEndpointForOldBots(result.endpointName); + } } } - return matchedSkill; - }, [skills, value.id]); + }, []); - const endpointOptions = useMemo(() => { - return (manifest?.endpoints || []).map(({ name }) => name); - }, [manifest]); + const matchedSkill: Skill | undefined = useMemo(() => { + const skill = skills.find(({ name }) => name === selectedSkill); + return skill; + }, [skills, selectedSkill]); - const handleIdChange = ({ key, text }) => { - if (!manifest || key !== manifest.manifestUrl) { - const { skillEndpoint, skillAppId, ...rest } = value; - onChange({ ...rest, id: referBySettings(text, 'manifestUrl') }); - } - }; + const endpointOptions = useMemo(() => { + return (matchedSkill?.endpoints || []).map(({ name }) => name); + }, [matchedSkill]); const handleEndpointChange = async (skillEndpoint) => { - const { msAppId, endpointUrl } = - (manifest?.endpoints || []).find(({ name }) => name === skillEndpoint) || ({} as any); - if (manifest?.name) { - skillsInSettings.set(manifest.name, { endpointUrl, msAppId }); + if (matchedSkill) { + const { msAppId, endpointUrl } = + (matchedSkill.endpoints || []).find(({ name }) => name === skillEndpoint) || ({} as any); + const schemaUpdate: any = {}; + const settingsUpdate: any = {}; + if (endpointUrl) { + skillsInSettings.set(matchedSkill.name, { endpointUrl }); + schemaUpdate.skillEndpoint = referBySettings(matchedSkill?.name, 'endpointUrl'); + settingsUpdate.endpointUrl = endpointUrl; + } + if (msAppId) { + schemaUpdate.skillAppId = referBySettings(matchedSkill?.name, 'msAppId'); + settingsUpdate.msAppId = msAppId; + } + skillsInSettings.set(matchedSkill.name, { ...settingsUpdate }); onChange({ ...value, - skillEndpoint: referBySettings(manifest?.name, 'endpointUrl'), - ...(msAppId ? { skillAppId: referBySettings(manifest?.name, 'msAppId') } : {}), + ...schemaUpdate, }); } }; + useEffect(() => { + if (oldEndpoint) { + handleEndpointChange(oldEndpoint); + } + }, [oldEndpoint]); + const handleShowManifestClick = () => { - value.id && displayManifestModal(skillsInSettings.get(value.id)); + matchedSkill && displayManifestModal(matchedSkill.manifestUrl); }; const skillEndpointUiSchema = uiOptions.properties?.skillEndpoint || {}; skillEndpointUiSchema.serializer = { get: (value) => { const url: any = skillsInSettings.get(value); - const endpoint = (manifest?.endpoints || []).find(({ endpointUrl }) => endpointUrl === url); + const endpoint = (matchedSkill?.endpoints || []).find(({ endpointUrl }) => endpointUrl === url); return endpoint?.name; }, set: (value) => { - const endpoint = (manifest?.endpoints || []).find(({ name }) => name === value); + const endpoint = (matchedSkill?.endpoints || []).find(({ name }) => name === value); return endpoint?.endpointUrl; }, }; - const skillIdUiSchema = uiOptions.properties?.id || {}; - skillIdUiSchema.serializer = { - get: (value) => skillsInSettings.get(value), - set: (value) => value, + const onSkillSelectionChange = (option: IComboBoxOption | null) => { + if (option) { + setSelectedSkill(option?.text); + } }; return ( - + diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/ComboBoxField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/ComboBoxField.tsx index db01ac6252..ee526c4dab 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/ComboBoxField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/ComboBoxField.tsx @@ -4,18 +4,21 @@ import React from 'react'; import { ComboBox, IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; import { FieldLabel } from '@bfc/adaptive-form'; -import { FieldProps } from '@bfc/extension'; import { Icon } from 'office-ui-fabric-react/lib/Icon'; import { ISelectableOption } from 'office-ui-fabric-react/lib/utilities/selectableOption'; import { IRenderFunction } from 'office-ui-fabric-react/lib/Utilities'; export const ADD_DIALOG = 'ADD_DIALOG'; -interface ComboBoxFieldProps extends FieldProps { +interface ComboBoxFieldProps { comboboxTitle: string | null; options: IComboBoxOption[]; onChange: any; required?: boolean; + description: string; + id: string; + label: string; + value: string; } export const ComboBoxField: React.FC = ({ @@ -26,10 +29,7 @@ export const ComboBoxField: React.FC = ({ options, value = '', required, - uiOptions, - onBlur, onChange, - onFocus, }) => { const onRenderOption: IRenderFunction = (option) => option ? ( @@ -45,15 +45,13 @@ export const ComboBoxField: React.FC = ({ return ( - + onBlur && onBlur(id, value)} - onFocus={() => onFocus && onFocus(id, value)} onItemClick={onChange} onRenderOption={onRenderOption} /> diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx index 779c37ed0e..87ff921523 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx @@ -1,25 +1,32 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +/** @jsx jsx */ +import { jsx } from '@emotion/core'; import React, { useState } from 'react'; import { IComboBoxOption, SelectableOptionMenuItemType } from 'office-ui-fabric-react/lib/ComboBox'; -import { FieldProps, useShellApi } from '@bfc/extension'; +import { useShellApi } from '@bfc/extension'; import formatMessage from 'format-message'; +import { schemaField } from '@bfc/adaptive-form'; import { ComboBoxField } from './ComboBoxField'; const ADD_DIALOG = 'ADD_DIALOG'; -export const SelectSkillDialog: React.FC = (props) => { - const { value, onChange } = props; +export const SelectSkillDialog: React.FC<{ + value: string; + depth: number; + onChange: (option: IComboBoxOption | null) => void; +}> = (props) => { + const { value, onChange, depth } = props; const { shellApi, skills = [] } = useShellApi(); - const { addSkillDialog, skillsInSettings } = shellApi; + const { addSkillDialog } = shellApi; const [comboboxTitle, setComboboxTitle] = useState(null); - const options: IComboBoxOption[] = skills.map(({ name, manifestUrl }) => ({ - key: manifestUrl, + const options: IComboBoxOption[] = skills.map(({ name }) => ({ + key: name, text: name, - isSelected: skillsInSettings.get(value) === manifestUrl, + isSelected: value === name, })); options.push( @@ -39,9 +46,9 @@ export const SelectSkillDialog: React.FC = (props) => { if (option) { if (option.key === ADD_DIALOG) { setComboboxTitle(formatMessage('Add a new Skill Dialog')); - addSkillDialog().then((newSkill) => { - if (newSkill && newSkill?.manifestUrl) { - onChange({ key: newSkill.manifestUrl }); + addSkillDialog().then((skill) => { + if (skill?.manifestUrl && skill?.name) { + onChange({ key: skill?.manifestUrl, text: skill?.name }); } setComboboxTitle(null); }); @@ -53,5 +60,17 @@ export const SelectSkillDialog: React.FC = (props) => { } }; - return ; + return ( +
+ +
+ ); }; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx index a02e4da6f0..f2a2610111 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx @@ -71,7 +71,7 @@ describe('Begin Skill Dialog', () => { it('should add a new skill', async () => { const onChange = jest.fn(); - const value = { id: `=settings.skill['yuesuemailskill0207'].manifestUrl` }; + const value = { skillEndpoint: `=settings.skill['yuesuemailskill0207'].endpointUrl` }; const { baseElement, findByRole } = renderBeginSkillDialog({ value, onChange }); const listbox = await findByRole('listbox'); @@ -83,13 +83,12 @@ describe('Begin Skill Dialog', () => { }); expect(onChange).toHaveBeenCalledWith({ - id: "=settings.skill['yuesuemailskill0207'].manifestUrl", skillAppId: "=settings.skill['yuesuemailskill0207'].msAppId", skillEndpoint: "=settings.skill['yuesuemailskill0207'].endpointUrl", }); }); - fit('should be backwards compatible', async () => { + it('should be backwards compatible', async () => { const onChange = jest.fn(); const value = { id: `https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json`, @@ -98,7 +97,7 @@ describe('Begin Skill Dialog', () => { renderBeginSkillDialog({ value, onChange }); expect(onChange).toHaveBeenCalledWith({ - id: "=settings.skill['yuesuemailskill0207'].manifestUrl", + id: `https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json`, skillAppId: "=settings.skill['yuesuemailskill0207'].msAppId", skillEndpoint: "=settings.skill['yuesuemailskill0207'].endpointUrl", }); diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx index 16e83b48eb..7939537548 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/SelectSkillDialog.test.tsx @@ -74,7 +74,7 @@ describe('Select Skill Dialog', () => { const addSkillDialog = jest.fn().mockImplementation(() => { return { then: (cb) => { - cb({ manifestUrl: 'https://' }); + cb({ manifestUrl: 'https://', name: 'test-skill' }); }, }; }); @@ -88,7 +88,7 @@ describe('Select Skill Dialog', () => { fireEvent.click(dialogs[dialogs.length - 1]); expect(addSkillDialog).toHaveBeenCalled(); - expect(onChange).toHaveBeenCalledWith({ key: 'https://' }); + expect(onChange).toHaveBeenCalledWith({ key: 'https://', text: 'test-skill' }); }); it('should select skill', async () => { @@ -104,7 +104,7 @@ describe('Select Skill Dialog', () => { expect(onChange).toHaveBeenCalledWith({ index: 0, isSelected: false, - key: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json', + key: 'Email Skill', text: 'Email Skill', }); }); diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/index.ts b/Composer/packages/ui-plugins/select-skill-dialog/src/index.ts index 6996e03bd7..6a2b2a9e31 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/index.ts +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/index.ts @@ -2,10 +2,8 @@ // Licensed under the MIT License. import { PluginConfig } from '@bfc/extension'; -import formatMessage from 'format-message'; import { SDKKinds } from '@bfc/shared'; -import { SelectSkillDialog } from './SelectSkillDialogField'; import { BeginSkillDialogField } from './BeginSkillDialogField'; const config: PluginConfig = { @@ -15,13 +13,6 @@ const config: PluginConfig = { order: ['skillAppId', '*', 'resultProperty', 'disabled', 'activityProcessed'], hidden: ['botId', 'skillEndpoint', 'skillAppId', 'skillHostEndpoint'], field: BeginSkillDialogField, - properties: { - id: { - description: () => formatMessage('Name of skill dialog to call'), - label: () => formatMessage('Skill Dialog Name'), - field: SelectSkillDialog, - }, - }, }, }, }, From c5f4d1554f677c83be6b29677dc01a7d63a8ed62 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 14:53:32 -0700 Subject: [PATCH 13/22] Removed comments Signed-off-by: Srinaath Ravichandran --- Composer/packages/client/src/recoilModel/dispatchers/project.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 5227b35194..5c9c7c2a0d 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -202,7 +202,6 @@ export const projectDispatcher = () => { const mergedSettings = mergeLocalStorage(projectId, settings); if (Array.isArray(mergedSettings.skill)) { const skillsArr = mergedSettings.skill.map((skillData) => { - // const matchedSkill = skills.find((currentSkill) => currentSkill.name === skillData.name); return { ...skillData, }; From 0a1470d000a123f634163b6266bf5d6407439275 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 15:29:55 -0700 Subject: [PATCH 14/22] Lock file updated and MC resolved Signed-off-by: Srinaath Ravichandran --- .../packages/server/src/models/bot/botProject.ts | 14 ++++++++++++-- Composer/yarn.lock | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index a4bed65101..baf9d69cee 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -4,11 +4,21 @@ import { promisify } from 'util'; import fs from 'fs'; -import values from 'lodash/values'; import axios from 'axios'; import { autofixReferInDialog } from '@bfc/indexers'; -import { getNewDesigner, FileInfo, Skill, Diagnostic, IBotProject, DialogSetting, FileExtensions } from '@bfc/shared'; +import { + getNewDesigner, + FileInfo, + Skill, + Diagnostic, + IBotProject, + DialogSetting, + FileExtensions, + convertSkillsToDictionary, +} from '@bfc/shared'; import { UserIdentity, pluginLoader } from '@bfc/plugin-loader'; +import { FeedbackType, generate } from '@microsoft/bf-generate-library'; +import values from 'lodash/values'; import { Path } from '../../utility/path'; import { copyDir } from '../../utility/storage'; diff --git a/Composer/yarn.lock b/Composer/yarn.lock index 73fca233f7..38ffa3e36d 100644 --- a/Composer/yarn.lock +++ b/Composer/yarn.lock @@ -18283,10 +18283,10 @@ tr46@^2.0.2: dependencies: punycode "^2.1.1" -tree-kill@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a" - integrity sha512-4hjqbObwlh2dLyW4tcz0Ymw0ggoaVDMveUB9w8kFSQScdRLo0gxO9J7WFcUBo+W3C1TLdFIEwNOWebgZZ0RH9Q== +tree-kill@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" + integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== treeify@^1.1.0: version "1.1.0" From eff8b3d55c742b15cfdb2b0006d07d257c30160d Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 17:12:13 -0700 Subject: [PATCH 15/22] Remove reduntant variable Co-authored-by: TJ Durnford --- Composer/packages/lib/indexers/src/dialogIndexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/lib/indexers/src/dialogIndexer.ts b/Composer/packages/lib/indexers/src/dialogIndexer.ts index 443608cfbf..a0ae0fa507 100644 --- a/Composer/packages/lib/indexers/src/dialogIndexer.ts +++ b/Composer/packages/lib/indexers/src/dialogIndexer.ts @@ -167,7 +167,7 @@ function extractReferredSkills(dialog): string[] { // it's a valid schema dialog node. if (has(value, '$kind') && value.$kind === SDKKinds.BeginSkill) { const skillEndpoint = value.skillEndpoint; - skills.push(skillEndpoint); + skills.push(value.skillEndpoint); } return false; }; From e4af44e6a9d5fc803ab204d0d088a8e3a81a02fb Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 17:14:05 -0700 Subject: [PATCH 16/22] Remove single use variable Co-authored-by: TJ Durnford --- .../select-skill-dialog/src/BeginSkillDialogField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 5cdcce8bb0..99f3bee5f1 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -62,7 +62,7 @@ export const BeginSkillDialogField: React.FC = (props) => { }, []); const matchedSkill: Skill | undefined = useMemo(() => { - const skill = skills.find(({ name }) => name === selectedSkill); + return skills.find(({ name }) => name === selectedSkill); return skill; }, [skills, selectedSkill]); From c9845efa734fc2e0f00f81e814d765e3cd64082f Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 17:59:29 -0700 Subject: [PATCH 17/22] Moved regex evaludation to shared Signed-off-by: Srinaath Ravichandran --- Composer/packages/lib/indexers/src/botIndexer.ts | 8 +++++++- .../packages/lib/shared/src/skillsUtils/index.ts | 8 ++++++++ .../packages/lib/shared/src/types/settings.ts | 2 +- .../src/BeginSkillDialogField.tsx | 15 +++------------ .../src/SelectSkillDialogField.tsx | 5 ++--- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Composer/packages/lib/indexers/src/botIndexer.ts b/Composer/packages/lib/indexers/src/botIndexer.ts index 6e6b8806a3..0e58a8834a 100644 --- a/Composer/packages/lib/indexers/src/botIndexer.ts +++ b/Composer/packages/lib/indexers/src/botIndexer.ts @@ -12,6 +12,7 @@ import { DiagnosticSeverity, LuFile, fetchFromSettings, + getSkillNameFromSetting, } from '@bfc/shared'; import difference from 'lodash/difference'; import map from 'lodash/map'; @@ -49,8 +50,13 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => { dialog.skills.forEach((skillId) => { const endpointUrlCollection = map(skill, ({ endpointUrl }) => endpointUrl); if (!endpointUrlCollection.includes(fetchFromSettings(skillId, assets.setting))) { + const skillName = getSkillNameFromSetting(skillId) || skillId; diagnostics.push( - new Diagnostic(`skill '${skillId}' is not existed in appsettings.json`, dialog.id, DiagnosticSeverity.Error) + new Diagnostic( + `The skill '${skillName}' does not exist in in appsettings.json`, + dialog.id, + DiagnosticSeverity.Error + ) ); } }); diff --git a/Composer/packages/lib/shared/src/skillsUtils/index.ts b/Composer/packages/lib/shared/src/skillsUtils/index.ts index ee6cd5c727..ab79cba7c9 100644 --- a/Composer/packages/lib/shared/src/skillsUtils/index.ts +++ b/Composer/packages/lib/shared/src/skillsUtils/index.ts @@ -26,3 +26,11 @@ export const convertSkillsToDictionary = (skills: Skill[]) => { return keyBy(mappedSkills, 'name'); }; + +export const getSkillNameFromSetting = (value: string) => { + const matched = value.match(/\['(.*?)'\]/); + if (matched && matched.length > 1) { + return matched[1]; + } + return ''; +}; diff --git a/Composer/packages/lib/shared/src/types/settings.ts b/Composer/packages/lib/shared/src/types/settings.ts index ca58266feb..7280eb2844 100644 --- a/Composer/packages/lib/shared/src/types/settings.ts +++ b/Composer/packages/lib/shared/src/types/settings.ts @@ -36,7 +36,7 @@ export interface DialogSetting { [skillName: string]: { name: string; manifestUrl: string; - msAppId?: string; + msAppId: string; endpointUrl: string; }; }; diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 99f3bee5f1..354742fcb3 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -7,20 +7,11 @@ import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { ObjectField } from '@bfc/adaptive-form'; import formatMessage from 'format-message'; -import { Skill } from '@bfc/shared'; +import { Skill, getSkillNameFromSetting } from '@bfc/shared'; import { SelectSkillDialog } from './SelectSkillDialogField'; import { SkillEndpointField } from './SkillEndpointField'; -const getNameFromSetting = (value: string) => { - const nameRegexp = new RegExp(/\['(.*?)'\]/g); - const matched = nameRegexp.exec(value); - if (matched && matched.length > 1) { - return matched[1]; - } - return ''; -}; - const referBySettings = (skillName: string, property: string) => { return `=settings.skill['${skillName}'].${property}`; }; @@ -46,7 +37,7 @@ export const BeginSkillDialogField: React.FC = (props) => { useEffect(() => { const { skillEndpoint } = value; - const skill = skills.find(({ name }) => name === getNameFromSetting(skillEndpoint)); + const skill = skills.find(({ name }) => name === getSkillNameFromSetting(skillEndpoint)); if (skill) { setSelectedSkill(skill.name); @@ -124,7 +115,7 @@ export const BeginSkillDialogField: React.FC = (props) => { return ( - + void; }> = (props) => { - const { value, onChange, depth } = props; + const { value, onChange } = props; const { shellApi, skills = [] } = useShellApi(); const { addSkillDialog } = shellApi; const [comboboxTitle, setComboboxTitle] = useState(null); @@ -61,7 +60,7 @@ export const SelectSkillDialog: React.FC<{ }; return ( -
+
Date: Thu, 10 Sep 2020 18:05:57 -0700 Subject: [PATCH 18/22] Updated depth to 0 Signed-off-by: Srinaath Ravichandran --- .../select-skill-dialog/src/BeginSkillDialogField.tsx | 3 +-- .../select-skill-dialog/src/SelectSkillDialogField.tsx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index 354742fcb3..9265f26771 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -53,8 +53,7 @@ export const BeginSkillDialogField: React.FC = (props) => { }, []); const matchedSkill: Skill | undefined = useMemo(() => { - return skills.find(({ name }) => name === selectedSkill); - return skill; + return skills.find(({ name }) => name === selectedSkill); }, [skills, selectedSkill]); const endpointOptions = useMemo(() => { diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx index 69f5762243..2580a125aa 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/SelectSkillDialogField.tsx @@ -60,7 +60,7 @@ export const SelectSkillDialog: React.FC<{ }; return ( -
+
Date: Thu, 10 Sep 2020 18:09:23 -0700 Subject: [PATCH 19/22] More PR feedback fixed Signed-off-by: Srinaath Ravichandran --- Composer/packages/lib/indexers/src/dialogIndexer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/Composer/packages/lib/indexers/src/dialogIndexer.ts b/Composer/packages/lib/indexers/src/dialogIndexer.ts index a0ae0fa507..d1663a9b60 100644 --- a/Composer/packages/lib/indexers/src/dialogIndexer.ts +++ b/Composer/packages/lib/indexers/src/dialogIndexer.ts @@ -166,7 +166,6 @@ function extractReferredSkills(dialog): string[] { const visitor: VisitorFunc = (path: string, value: any): boolean => { // it's a valid schema dialog node. if (has(value, '$kind') && value.$kind === SDKKinds.BeginSkill) { - const skillEndpoint = value.skillEndpoint; skills.push(value.skillEndpoint); } return false; From 3b0466674cd242806daac56635530c523e359299 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 18:38:57 -0700 Subject: [PATCH 20/22] Fix references Signed-off-by: Srinaath Ravichandran --- .../select-skill-dialog/src/BeginSkillDialogField.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index de38ee7ebf..e271d93b37 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useMemo } from 'react'; +import React, { useMemo, useState, useEffect } from 'react'; import { FieldProps, JSONSchema7, useShellApi } from '@bfc/extension-client'; import { Link } from 'office-ui-fabric-react/lib/Link'; import { ObjectField } from '@bfc/adaptive-form'; import formatMessage from 'format-message'; import { Skill, getSkillNameFromSetting } from '@bfc/shared'; - +import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; import { SelectSkillDialog } from './SelectSkillDialogField'; import { SkillEndpointField } from './SkillEndpointField'; From ad90e1444bb9df854700ea51e407f906697cfe82 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 18:42:45 -0700 Subject: [PATCH 21/22] Tests fixed Signed-off-by: Srinaath Ravichandran --- .../src/__tests__/BeginSkillDialogField.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx index 36e71fc62e..4208e87867 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/__tests__/BeginSkillDialogField.test.tsx @@ -5,7 +5,7 @@ import React from 'react'; import { fireEvent, getAllByRole, render } from '@bfc/test-utils'; import { EditorExtension, JSONSchema7 } from '@bfc/extension-client'; -import { SDKKinds, convertSkillsToDictionary } from '@bfc/shared'; +import { SDKKinds, convertSkillsToDictionary, fetchFromSettings } from '@bfc/shared'; import { act } from '@bfc/test-utils/lib/hooks'; import { BeginSkillDialogField } from '../BeginSkillDialogField'; From b6661f5f0358068bb214818a3d02456e113b5f62 Mon Sep 17 00:00:00 2001 From: Srinaath Ravichandran Date: Thu, 10 Sep 2020 20:19:31 -0700 Subject: [PATCH 22/22] Lint fix Signed-off-by: Srinaath Ravichandran --- .../ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx index e271d93b37..4cb5857c09 100644 --- a/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx +++ b/Composer/packages/ui-plugins/select-skill-dialog/src/BeginSkillDialogField.tsx @@ -8,6 +8,7 @@ import { ObjectField } from '@bfc/adaptive-form'; import formatMessage from 'format-message'; import { Skill, getSkillNameFromSetting } from '@bfc/shared'; import { IComboBoxOption } from 'office-ui-fabric-react/lib/ComboBox'; + import { SelectSkillDialog } from './SelectSkillDialogField'; import { SkillEndpointField } from './SkillEndpointField';