Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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'].endpointUrl`],
},
],
luFiles: [
Expand Down Expand Up @@ -86,12 +86,13 @@ const state = {
},
],
settings: {
skill: [
{
skill: {
'Email-Skill': {
manifestUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/manifest/manifest-1.0.json',
name: 'Email Skill',
endpointUrl: 'https://yuesuemailskill0207-gjvga67.azurewebsites.net/api/messages',
name: 'Email-Skill',
},
],
},
},
};

Expand Down
21 changes: 18 additions & 3 deletions Composer/packages/client/src/recoilModel/dispatchers/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@
// 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';
import isArray from 'lodash/isArray';
import formatMessage from 'format-message';

import lgWorker from '../parsers/lgWorker';
Expand Down Expand Up @@ -193,6 +200,14 @@ export const projectDispatcher = () => {
set(projectIdState, projectId);
refreshLocalStorage(projectId, settings);
const mergedSettings = mergeLocalStorage(projectId, settings);
if (Array.isArray(mergedSettings.skill)) {
const skillsArr = mergedSettings.skill.map((skillData) => {
return {
...skillData,
};
});
mergedSettings.skill = convertSkillsToDictionary(skillsArr);
}
set(settingsState, mergedSettings);
});
gotoSnapshot(newSnapshot);
Expand Down Expand Up @@ -347,7 +362,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) {
Expand Down
22 changes: 21 additions & 1 deletion Composer/packages/client/src/recoilModel/dispatchers/setting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -89,12 +89,32 @@ export const settingsDispatcher = () => {
}
);

const updateSkillsInSetting = useRecoilCallback(
({ set, snapshot }: CallbackInterface) => async (skillName: string, skillInfo: Partial<Skill>) => {
const currentSettings: DialogSetting = await snapshot.getPromise(settingsState);
const matchedSkill = get(currentSettings, `skill[${skillName}]`, undefined);
if (matchedSkill) {
set(settingsState, {
...currentSettings,
skill: {
...currentSettings.skill,
[skillName]: {
...matchedSkill,
...skillInfo,
},
},
});
}
}
);

return {
setSettings,
setRuntimeSettings,
setPublishTargets,
setRuntimeField,
setCustomRuntime,
setQnASettings,
updateSkillsInSetting,
};
};
6 changes: 2 additions & 4 deletions Composer/packages/client/src/recoilModel/dispatchers/skill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/* 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';

Expand Down Expand Up @@ -76,9 +76,7 @@ export const skillDispatcher = () => {
set(onAddSkillDialogCompleteState, { func: undefined });
set(settingsState, (settings) => ({
...settings,
skill: skills.map(({ manifestUrl, name }) => {
return { manifestUrl, name };
}),
skill: convertSkillsToDictionary(skills),
}));
set(skillsState, skills);
} catch (err) {
Expand Down
11 changes: 9 additions & 2 deletions Composer/packages/client/src/shell/useShell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// 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';

Expand All @@ -26,6 +26,7 @@ import {
focusPathState,
userSettingsState,
clipboardActionsState,
settingsState,
} from '../recoilModel';
import { validatedDialogsSelector } from '../recoilModel/selectors/validatedDialogs';

Expand Down Expand Up @@ -56,6 +57,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,
Expand All @@ -70,6 +72,7 @@ export function useShell(source: EventSource): Shell {
updateUserSettings,
setMessage,
displayManifestModal,
updateSkillsInSetting,
} = useRecoilValue(dispatcherState);
const lgApi = useLgApi();
const luApi = useLuApi();
Expand Down Expand Up @@ -177,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);
});
});
Expand All @@ -190,6 +193,10 @@ export function useShell(source: EventSource): Shell {
announce: setMessage,
displayManifestModal: displayManifestModal,
updateDialogSchema,
skillsInSettings: {
get: (path: string) => fetchFromSettings(path, settings),
set: updateSkillsInSetting,
},
};

const currentDialog = useMemo(() => dialogs.find((d) => d.id === dialogId), [dialogs, dialogId]);
Expand Down
13 changes: 8 additions & 5 deletions Composer/packages/lib/indexers/__tests__/botIndexer.test.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -32,12 +32,15 @@ const botAssets: BotAssets = {
defaultLanguage: 'en-us',
botId: '',
skillHostEndpoint: '',
skill: [
{
name: 'Email Skill',
skill: {
'Email-Skill': {
name: 'Email-Skill',
manifestUrl: 'skill1',
},
],
},
luis: {} as ILuisConfig,
qna: {} as IQnAConfig,
runtime: {} as any,
} as DialogSetting,
};

Expand Down
30 changes: 20 additions & 10 deletions Composer/packages/lib/indexers/src/botIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@
* 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';

import { getLocale } from './utils/help';

Expand All @@ -29,7 +38,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[] = [];
Expand All @@ -38,7 +47,8 @@ const checkSkillSetting = (assets: BotAssets): Diagnostic[] => {
dialogs.forEach((dialog) => {
// used skill not existed in setting
dialog.skills.forEach((skillId) => {
if (skill.findIndex(({ manifestUrl }) => manifestUrl === skillId) === -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)
);
Expand All @@ -61,13 +71,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));
Expand All @@ -79,6 +82,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,
Expand Down
4 changes: 2 additions & 2 deletions Composer/packages/lib/indexers/src/dialogIndexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
1 change: 1 addition & 0 deletions Composer/packages/lib/shared/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ export * from './schemaUtils';
export * from './types';
export * from './viewUtils';
export * from './walkerUtils';
export * from './skillsUtils';
export const DialogUtils = dialogUtils;
28 changes: 28 additions & 0 deletions Composer/packages/lib/shared/src/skillsUtils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import get from 'lodash/get';
import keyBy from 'lodash/keyBy';

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) => {
return {
name,
msAppId,
endpointUrl,
manifestUrl,
};
});

return keyBy(mappedSkills, 'name');
};
10 changes: 7 additions & 3 deletions Composer/packages/lib/shared/src/types/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ export interface DialogSetting {
defaultLanguage: string;
languages: string[];
skill?: {
name: string;
manifestUrl: string;
}[];
[skillName: string]: {
name: string;
manifestUrl: string;
msAppId?: string;
endpointUrl: string;
};
};
botId?: string;
skillHostEndpoint?: string;
[key: string]: any;
Expand Down
10 changes: 7 additions & 3 deletions Composer/packages/lib/shared/src/types/shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -57,7 +57,7 @@ export interface ShellData {
luFiles: LuFile[];
qnaFiles: QnAFile[];
userSettings: UserSettings;
skills: any[];
skills: Skill[];
// TODO: remove
schemas: BotSchemas;
}
Expand Down Expand Up @@ -95,11 +95,15 @@ export interface ShellApi {
redo: () => void;
commitChanges: () => void;
updateUserSettings: (settings: AllPartial<UserSettings>) => void;
addSkillDialog: () => Promise<{ manifestUrl: string } | null>;
addSkillDialog: () => Promise<{ manifestUrl: string; name: string } | null>;
announce: (message: string) => void;
displayManifestModal: (manifestId: string) => void;
updateDialogSchema: (_: DialogSchemaFile) => Promise<void>;
createTrigger: (id: string, formData, url?: string) => void;
skillsInSettings: {
get: (path: string) => any;
set: (skillName: string, skillsData: Partial<Skill>) => Promise<void>;
};
}

export interface Shell {
Expand Down
Loading