From f5c30f3329dfe98005d50f133797370e6659dd5d Mon Sep 17 00:00:00 2001 From: TJ Durnford Date: Tue, 15 Sep 2020 09:17:59 -1000 Subject: [PATCH] feat: Add QnA files to dispatch model in skill manifest (#3985) * [feat]: Add QnA files to dispatch model in skill manifest * conditionally add lu files Co-authored-by: Chris Whitten Co-authored-by: Andy Brown --- .../__tests__/generateSkillManifest.test.ts | 43 +++++++++---- .../exportSkillModal/generateSkillManifest.ts | 60 +++++++++++++++---- .../pages/design/exportSkillModal/index.tsx | 3 + 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/__tests__/generateSkillManifest.test.ts b/Composer/packages/client/src/pages/design/exportSkillModal/__tests__/generateSkillManifest.test.ts index ce0aa923dc..47a857c5e7 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/__tests__/generateSkillManifest.test.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/__tests__/generateSkillManifest.test.ts @@ -187,16 +187,21 @@ describe('generateDispatchModels', () => { const dialogs: any = [{ id: 'test', content: { recognizer: 'test.lu' } }]; const selectedTriggers = []; const luFiles = []; - const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles); + const qnaFiles = []; + const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles, qnaFiles); expect(result).toEqual({}); }); it("should return empty object if the schema doesn't include dispatchModels", () => { const schema = { properties: {} }; - const dialogs: any = [{ id: 'test', content: { recognizer: 'test.lu' } }]; + const dialogs: any = [{ id: 'test', content: { recognizer: 'test.lu.qna' } }]; const selectedTriggers = [{ $kind: SDKKinds.OnIntent, intent: 'testIntent' }]; - const luFiles: any = [{ id: 'test.en-us' }, { id: 'test.fr-FR' }]; - const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles); + const luFiles: any = [ + { id: 'test.en-us', empty: false }, + { id: 'test.fr-FR', empty: false }, + ]; + const qnaFiles = []; + const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles, qnaFiles); expect(result).toEqual({}); }); @@ -204,17 +209,25 @@ describe('generateDispatchModels', () => { const schema = { properties: {} }; const dialogs: any = [{ id: 'test', content: {} }]; const selectedTriggers = [{ $kind: SDKKinds.OnIntent, intent: 'testIntent' }]; - const luFiles: any = [{ id: 'test.en-us' }, { id: 'test.fr-FR' }]; - const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles); + const luFiles: any = [ + { id: 'test.en-us', empty: false }, + { id: 'test.fr-FR', empty: false }, + ]; + const qnaFiles = []; + const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles, qnaFiles); expect(result).toEqual({}); }); it('should return dispatch models', () => { const schema = { properties: { dispatchModels: {} } }; - const dialogs: any = [{ id: 'test', content: { recognizer: 'test.lu' }, isRoot: true }]; + const dialogs: any = [{ id: 'test', content: { recognizer: 'test.lu.qna' }, isRoot: true }]; const selectedTriggers = [{ $kind: SDKKinds.OnIntent, intent: 'testIntent' }]; - const luFiles: any = [{ id: 'test.en-us' }, { id: 'test.fr-FR' }]; - const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles); + const luFiles: any = [ + { id: 'test.en-us', empty: false }, + { id: 'test.fr-FR', empty: false }, + ]; + const qnaFiles: any = [{ id: 'test.es-es', empty: false }]; + const result = generateDispatchModels(schema, dialogs, selectedTriggers, luFiles, qnaFiles); expect(result).toEqual( expect.objectContaining({ dispatchModels: { @@ -223,7 +236,7 @@ describe('generateDispatchModels', () => { { name: 'test', contentType: 'application/lu', - url: ``, + url: ``, description: '', }, ], @@ -231,7 +244,15 @@ describe('generateDispatchModels', () => { { name: 'test', contentType: 'application/lu', - url: ``, + url: ``, + description: '', + }, + ], + 'es-es': [ + { + name: 'test', + contentType: 'application/qna', + url: ``, description: '', }, ], diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index 7880a97f62..767006135c 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import get from 'lodash/get'; -import { DialogInfo, DialogSchemaFile, ITrigger, SDKKinds, SkillManifest, LuFile } from '@bfc/shared'; +import { DialogInfo, DialogSchemaFile, ITrigger, SDKKinds, SkillManifest, LuFile, QnAFile } from '@bfc/shared'; import { JSONSchema7 } from '@bfc/extension-client'; import { Activities, Activity, activityHandlerMap, ActivityTypes, DispatchModels } from './constants'; @@ -17,6 +17,7 @@ export const generateSkillManifest = ( dialogs: DialogInfo[], dialogSchemas: DialogSchemaFile[], luFiles: LuFile[], + qnaFiles: QnAFile[], selectedTriggers: ITrigger[], selectedDialogs: Partial[] ) => { @@ -41,7 +42,7 @@ export const generateSkillManifest = ( }, []); const activities = generateActivities(dialogSchemas, triggers, resolvedDialogs); - const dispatchModels = generateDispatchModels(schema, dialogs, triggers, luFiles); + const dispatchModels = generateDispatchModels(schema, dialogs, triggers, luFiles, qnaFiles); const definitions = getDefinitions(dialogSchemas, resolvedDialogs); return { @@ -104,7 +105,8 @@ export const generateDispatchModels = ( schema: JSONSchema7, dialogs: DialogInfo[], selectedTriggers: any[], - luFiles: LuFile[] + luFiles: LuFile[], + qnaFiles: QnAFile[] ): { dispatchModels?: DispatchModels } => { const intents = selectedTriggers.filter(({ $kind }) => $kind === SDKKinds.OnIntent).map(({ intent }) => intent); const { id: rootId } = dialogs.find((dialog) => dialog?.isRoot) || {}; @@ -114,16 +116,46 @@ export const generateDispatchModels = ( return luId === rootId; }); - if (!intents.length || !schema.properties?.dispatchModels) { + const rootQnAFiles = qnaFiles.filter(({ id: qnaFileId }) => { + const [qnaId] = qnaFileId.split('.'); + return qnaId === rootId; + }); + + if (!schema.properties?.dispatchModels) { return {}; } - const languages = rootLuFiles.reduce((acc, { id }) => { + const luLanguages = intents.length + ? rootLuFiles.reduce((acc, { empty, id }) => { + const [name, locale] = id.split('.'); + const { content = {} } = dialogs.find(({ id }) => id === name) || {}; + const { recognizer = '' } = content; + + if (!recognizer.includes('.lu') || empty) { + return acc; + } + + return { + ...acc, + [locale]: [ + ...(acc[locale] ?? []), + { + name, + contentType: 'application/lu', + url: `<${id}.lu url>`, + description: '', + }, + ], + }; + }, {}) + : {}; + + const languages = rootQnAFiles.reduce((acc, { empty, id }) => { const [name, locale] = id.split('.'); const { content = {} } = dialogs.find(({ id }) => id === name) || {}; const { recognizer = '' } = content; - if (!''.endsWith.call(recognizer, '.lu')) { + if (!recognizer.includes('.qna') || empty) { return acc; } @@ -133,19 +165,21 @@ export const generateDispatchModels = ( ...(acc[locale] ?? []), { name, - contentType: 'application/lu', - url: `<${id} url>`, + contentType: 'application/qna', + url: `<${id}.qna url>`, description: '', }, ], }; - }, {}); + }, luLanguages); + + const dispatchModels = { + ...(Object.keys(languages).length ? { languages } : {}), + ...(intents.length ? { intents } : {}), + }; return { - dispatchModels: { - ...(Object.keys(languages).length ? { languages } : {}), - ...(intents.length ? { intents } : {}), - }, + ...(Object.keys(dispatchModels).length ? { dispatchModels } : {}), }; }; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 72770b5497..df49f376c0 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -18,6 +18,7 @@ import { dispatcherState, luFilesState, skillManifestsState, + qnaFilesState, } from '../../../recoilModel'; import { editorSteps, ManifestEditorSteps, order } from './constants'; @@ -34,6 +35,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const dialogs = useRecoilValue(dialogsState); const dialogSchemas = useRecoilValue(dialogSchemasState); const luFiles = useRecoilValue(luFilesState); + const qnaFiles = useRecoilValue(qnaFilesState); const skillManifests = useRecoilValue(skillManifestsState); const { updateSkillManifest } = useRecoilValue(dispatcherState); @@ -59,6 +61,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss dialogs, dialogSchemas, luFiles, + qnaFiles, selectedTriggers, selectedDialogs );