From e5c2ed83bfe9f6da9ae7fc88f0cdcdc59fa3c66b Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Tue, 23 Mar 2021 17:16:40 +0800 Subject: [PATCH 01/55] add skill select profile component --- .../design/exportSkillModal/constants.tsx | 58 ++++--- .../content/SelectProfile.tsx | 162 ++++++++++++++++++ 2 files changed, 200 insertions(+), 20 deletions(-) create mode 100644 Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index 611914dcbc..e4463a03bc 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -20,6 +20,7 @@ import { SelectManifest, SelectTriggers, } from './content'; +import { SelectProfile } from './content/SelectProfile'; export const VERSION_REGEX = /\d\.\d+\.(\d+|preview-\d+)|\d\.\d+/i; @@ -127,7 +128,7 @@ interface EditorStep { } export enum ManifestEditorSteps { - ENDPOINTS = 'ENDPOINTS', + // ENDPOINTS = 'ENDPOINTS', FETCH_MANIFEST_SCHEMA = 'FETCH_MANIFEST_SCHEMA', MANIFEST_DESCRIPTION = 'MANIFEST_DESCRIPTION', MANIFEST_REVIEW = 'MANIFEST_REVIEW', @@ -135,13 +136,15 @@ export enum ManifestEditorSteps { SELECT_MANIFEST = 'SELECT_MANIFEST', SELECT_DIALOGS = 'SELECT_DIALOGS', SELECT_TRIGGERS = 'SELECT_TRIGGERS', + SELECT_PROFILE = 'SELECT_PROFILE', } export const order: ManifestEditorSteps[] = [ ManifestEditorSteps.SELECT_MANIFEST, ManifestEditorSteps.FETCH_MANIFEST_SCHEMA, ManifestEditorSteps.MANIFEST_DESCRIPTION, - ManifestEditorSteps.ENDPOINTS, + // ManifestEditorSteps.ENDPOINTS, + ManifestEditorSteps.SELECT_PROFILE, ManifestEditorSteps.SELECT_DIALOGS, ManifestEditorSteps.SELECT_TRIGGERS, ManifestEditorSteps.MANIFEST_REVIEW, @@ -207,27 +210,42 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { subText: () => formatMessage('To make your bot available for others as a skill, we need to generate a manifest.'), validate, }, - [ManifestEditorSteps.ENDPOINTS]: { - buttons: [cancelButton, nextButton], - content: Endpoints, - editJson: true, - subText: () => - formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), - title: () => formatMessage('Skill endpoints'), - validate: ({ content, schema }) => { - const { items, minItems } = schema.properties?.endpoints; + // [ManifestEditorSteps.ENDPOINTS]: { + // buttons: [cancelButton, nextButton], + // content: Endpoints, + // editJson: true, + // subText: () => + // formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), + // title: () => formatMessage('Skill endpoints'), + // validate: ({ content, schema }) => { + // const { items, minItems } = schema.properties?.endpoints; - if (!content.endpoints || content.endpoints.length < minItems) { - return { endpoints: formatMessage('Please add at least {minItems} endpoint', { minItems }) }; - } + // if (!content.endpoints || content.endpoints.length < minItems) { + // return { endpoints: formatMessage('Please add at least {minItems} endpoint', { minItems }) }; + // } - const endpointSchema = resolveRef(items, schema.definitions); - const endpoints = (content.endpoints || []).map((endpoint) => - validate({ content: endpoint, schema: endpointSchema }) - ); + // const endpointSchema = resolveRef(items, schema.definitions); + // const endpoints = (content.endpoints || []).map((endpoint) => + // validate({ content: endpoint, schema: endpointSchema }) + // ); - return endpoints.some((endpoint) => Object.keys(endpoint).length) ? { endpoints } : {}; - }, + // return endpoints.some((endpoint) => Object.keys(endpoint).length) ? { endpoints } : {}; + // }, + // }, + [ManifestEditorSteps.SELECT_PROFILE]: { + buttons: [ + cancelButton, + { + primary: true, + text: () => formatMessage('Next'), + onClick: ({ onNext }) => onNext, + }, + ], + editJson: false, + content: SelectProfile, + subText: () => + formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), + title: () => formatMessage('Confirm skill endpoints'), }, [ManifestEditorSteps.MANIFEST_REVIEW]: { buttons: [ diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx new file mode 100644 index 0000000000..bb6960b530 --- /dev/null +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -0,0 +1,162 @@ +import { PublishTarget } from '@bfc/shared'; +import formatMessage from 'format-message'; +import { css, Dropdown, Icon, IDropdownOption, PrimaryButton, TextField, TooltipHost } from 'office-ui-fabric-react'; +import React, { useEffect, useMemo, useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { settingsState } from '../../../../recoilModel'; +import { PublishTargets } from '../../../botProject/PublishTargets'; +import { iconStyle } from '../../../botProject/runtime-settings/style'; +import Publish from '../../../publish/Publish'; +import { ContentProps } from '../constants'; + +const styles = { + container: css` + height: 350px; + overflow: auto; + `, +}; + +const onRenderLabel = (props) => { + return ( +
+
+ {' '} + {props.label}{' '} +
+ + + +
+ ); +}; + +export const SelectProfile: React.FC = ({ errors, value, schema, onChange, projectId }) => { + const [publishingTargets, setPublishingTargets] = useState([]); + const [currentTarget, setCurrentTarget] = useState(); + const [endpointUrl, setEndpointUrl] = useState(); + const [appId, setAppId] = useState(); + const { endpoints, ...rest } = value; + + const updateCurrentProfile = useMemo( + () => (_e, option?: IDropdownOption) => { + const target = publishingTargets.find((t) => { + return t.name === option?.key; + }); + if (target) { + setCurrentTarget(target); + const config = JSON.parse(target.configuration); + setEndpointUrl(`https://${config.hostname}.azurewebsites.net`); + setAppId(config.settings.MicrosoftAppId); + onChange({ + endpoints: [ + { + protocol: 'BotFrameworkV3', + name: option?.key, + endpointUrl: `https://${config.hostname}.azurewebsites.net`, + description: '', + msAppId: config.settings.MicrosoftAppId, + }, + ], + ...rest, + }); + } + }, + [publishingTargets] + ); + + const isProfileValid = useMemo( + () => () => { + if (!publishingTargets) { + return false; + } + const filteredProfile = publishingTargets.filter((item) => { + const config = JSON.parse(item.configuration); + return ( + config.settings.MicrosoftAppId && + config.hostname && + config.settings.MicrosoftAppId.length > 0 && + config.hostname.length > 0 + ); + }); + return filteredProfile.length > 0; + }, + [publishingTargets] + ); + + const publishingOptions = useMemo(() => { + return publishingTargets.map((t) => ({ + key: t.name, + text: t.name, + })); + }, [publishingTargets]); + + const settings = useRecoilValue(settingsState(projectId)); + + const OnEndpointChange = useMemo( + () => (e, newValue) => { + setEndpointUrl(newValue); + }, + [setEndpointUrl] + ); + + const OnAppIdChange = useMemo( + () => (e, newValue) => { + setAppId(newValue); + }, + [setAppId] + ); + + useEffect(() => { + setPublishingTargets(settings.publishTargets || []); + }, [settings]); + + return isProfileValid() ? ( +
+ + + +
+ ) : ( +
+ +
+ ); +}; From c86693e46a65ea0be281f615c2b94d371f739a1c Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 24 Mar 2021 17:55:41 +0800 Subject: [PATCH 02/55] manifest file cud api --- .../client/src/utils/manifestFileUtil.ts | 24 ++++++++++ .../server/src/controllers/project.ts | 48 +++++++++++++++++++ .../server/src/models/bot/botProject.ts | 38 ++++++++++++++- .../server/src/models/bot/botStructure.ts | 9 ++++ Composer/packages/server/src/router/api.ts | 3 ++ 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 Composer/packages/client/src/utils/manifestFileUtil.ts diff --git a/Composer/packages/client/src/utils/manifestFileUtil.ts b/Composer/packages/client/src/utils/manifestFileUtil.ts new file mode 100644 index 0000000000..ece41cd8f3 --- /dev/null +++ b/Composer/packages/client/src/utils/manifestFileUtil.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import httpClient from './httpUtil'; + +export const createManifestFile = async (projectId: string, name: string, content: string) => { + const response = await httpClient.post(`/projects/${projectId}/manifest/files`, { + name, + content, + }); + return response.data; +}; + +export const deleteManifestFile = async (projectId: string, name: string) => { + await httpClient.delete(`/projects/${projectId}/manifest/files/${name}`); +}; + +export const updateManifestFile = async (projectId: string, name: string, content: string) => { + const response = await httpClient.put(`/projects/${projectId}/manifest/files/${name}`, { + name, + content, + }); + return response.data; +}; diff --git a/Composer/packages/server/src/controllers/project.ts b/Composer/packages/server/src/controllers/project.ts index f1a9185fcd..47699352e2 100644 --- a/Composer/packages/server/src/controllers/project.ts +++ b/Composer/packages/server/src/controllers/project.ts @@ -301,6 +301,51 @@ async function removeFile(req: Request, res: Response) { } } +async function updateManifestFile(req: Request, res: Response) { + const projectId = req.params.projectId; + const user = await ExtensionContext.getUserFromRequest(req); + const currentProject = await BotProjectService.getProjectById(projectId, user); + if (currentProject !== undefined) { + const lastModified = await currentProject.updateManifestLuFile(req.body.name, req.body.content); + res.status(200).json({ lastModified: lastModified }); + } else { + res.status(404).json({ + message: 'No such bot project opened', + }); + } +} + +async function createManifestFile(req: Request, res: Response) { + const projectId = req.params.projectId; + const user = await ExtensionContext.getUserFromRequest(req); + + const currentProject = await BotProjectService.getProjectById(projectId, user); + if (currentProject !== undefined) { + const { name, content } = req.body; + + //dir = id + const file = await currentProject.createManifestLuFile(name, content); + res.status(200).json(file); + } else { + res.status(404).json({ + message: 'No such bot project opened', + }); + } +} + +async function removeManifestFile(req: Request, res: Response) { + const projectId = req.params.projectId; + const user = await ExtensionContext.getUserFromRequest(req); + + const currentProject = await BotProjectService.getProjectById(projectId, user); + if (currentProject !== undefined) { + const dialogResources = await currentProject.deleteManifestLuFile(req.params.name); + res.status(200).json(dialogResources); + } else { + res.status(404).json({ error: 'No bot project opened' }); + } +} + async function getSkill(req: Request, res: Response) { const projectId = req.params.projectId; const user = await ExtensionContext.getUserFromRequest(req); @@ -546,6 +591,9 @@ export const ProjectController = { updateFile, createFile, removeFile, + createManifestFile, + updateManifestFile, + removeManifestFile, getSkill, build, setQnASettings, diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index 9904623cc6..d97adf30fd 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -32,7 +32,7 @@ import log from '../../logger'; import { BotProjectService } from '../../services/project'; import AssetService from '../../services/asset'; -import { BotStructureFilesPatterns, isCrossTrainConfig } from './botStructure'; +import { BotStructureFilesPatterns, defaultManifestFilePath, isCrossTrainConfig } from './botStructure'; import { Builder } from './builder'; import { IFileStorage } from './../storage/interface'; import { LocationRef, IBuildConfig } from './interface'; @@ -461,6 +461,42 @@ export class BotProject implements IBotProject { return await this._createFile(relativePath, content); }; + public createManifestLuFile = async (name: string, content = '') => { + const filename = name.trim(); + this.validateFileName(filename); + this._validateFileContent(name, content); + const botName = this.name; + const defaultLocale = this.settings?.defaultLanguage || defaultLanguage; + const relativePath = defaultManifestFilePath(botName, filename, defaultLocale); + const file = this.files.get(filename); + if (file) { + throw new Error(`${filename} dialog already exist`); + } + return await this._createFile(relativePath, content); + }; + + public updateManifestLuFile = async (name: string, content: string): Promise => { + const file = this.files.get(name); + if (file === undefined) { + const { lastModified } = await this.createManifestLuFile(name, content); + return lastModified; + } + + const relativePath = file.relativePath; + this._validateFileContent(name, content); + const lastModified = await this._updateFile(relativePath, content); + return lastModified; + }; + + public deleteManifestLuFile = async (name: string) => { + const file = this.files.get(name); + if (file === undefined) { + throw new Error(`no such file ${name}`); + } + await this._removeFile(file.relativePath); + await this._cleanUp(file.relativePath); + }; + public createFiles = async (files) => { const createdFiles: FileInfo[] = []; for (const { name, content } of files) { diff --git a/Composer/packages/server/src/models/bot/botStructure.ts b/Composer/packages/server/src/models/bot/botStructure.ts index 22814269bb..f6fc6db3da 100644 --- a/Composer/packages/server/src/models/bot/botStructure.ts +++ b/Composer/packages/server/src/models/bot/botStructure.ts @@ -10,6 +10,7 @@ const BotStructureTemplate = { entry: '${BOTNAME}.dialog', lg: 'language-generation/${LOCALE}/${BOTNAME}.${LOCALE}.lg', lu: 'language-understanding/${LOCALE}/${BOTNAME}.${LOCALE}.lu', + manifestLu: 'manifest/${FILENAME}.${LOCALE}.lu', qna: 'knowledge-base/en-us/${BOTNAME}.en-us.qna', sourceQnA: 'knowledge-base/source/${FILENAME}.source.qna', dialogSchema: '${BOTNAME}.dialog.schema', @@ -105,6 +106,14 @@ export const parseFileName = (name: string, defaultLocale: string) => { export const isRecognizer = (fileName: string) => fileName.endsWith('.lu.dialog') || fileName.endsWith('.qna.dialog'); export const isCrossTrainConfig = (fileName: string) => fileName.endsWith('cross-train.config.json'); +export const defaultManifestFilePath = (botName: string, fileName: string, locale: string): string => { + return templateInterpolate(BotStructureTemplate.manifestLu, { + BOTNAME: botName, + FILENAME: fileName, + LOCALE: locale, + }); +}; + export const defaultFilePath = ( botName: string, defaultLocale: string, diff --git a/Composer/packages/server/src/router/api.ts b/Composer/packages/server/src/router/api.ts index f3ae8d7e4b..7c770c4bd9 100644 --- a/Composer/packages/server/src/router/api.ts +++ b/Composer/packages/server/src/router/api.ts @@ -38,6 +38,9 @@ router.delete('/projects/:projectId', ProjectController.removeProject); router.put('/projects/:projectId/files/:name', ProjectController.updateFile); router.delete('/projects/:projectId/files/:name', ProjectController.removeFile); router.post('/projects/:projectId/files', ProjectController.createFile); +router.put('/projects/:projectId/manifest/files/:name', ProjectController.updateManifestFile); +router.delete('/projects/:projectId/manifest/files/:name', ProjectController.removeManifestFile); +router.post('/projects/:projectId/manifest/files', ProjectController.createManifestFile); router.get('/projects/:projectId/skill/retrieveSkillManifest', ProjectController.getSkill); router.post('/projects/:projectId/build', ProjectController.build); router.post('/projects/:projectId/qnaSettings/set', ProjectController.setQnASettings); From 0873dfc1eb203096cc79d2c13687fd5f0cf1e621 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Thu, 25 Mar 2021 11:45:13 +0800 Subject: [PATCH 03/55] add skill select page and publish logic --- .../design/exportSkillModal/constants.tsx | 38 ++++---------- .../content/SelectProfile.tsx | 51 ++++++++++++++++--- .../pages/design/exportSkillModal/index.tsx | 48 ++++++++++++++++- .../src/pages/publish/Notifications.tsx | 16 ++++++ .../client/src/pages/publish/Publish.tsx | 6 ++- .../client/src/recoilModel/atoms/botState.ts | 6 +++ .../src/recoilModel/dispatchers/project.ts | 10 +++- 7 files changed, 137 insertions(+), 38 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index e4463a03bc..c399deed59 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -143,10 +143,9 @@ export const order: ManifestEditorSteps[] = [ ManifestEditorSteps.SELECT_MANIFEST, ManifestEditorSteps.FETCH_MANIFEST_SCHEMA, ManifestEditorSteps.MANIFEST_DESCRIPTION, - // ManifestEditorSteps.ENDPOINTS, - ManifestEditorSteps.SELECT_PROFILE, ManifestEditorSteps.SELECT_DIALOGS, ManifestEditorSteps.SELECT_TRIGGERS, + // ManifestEditorSteps.SELECT_PROFILE, ManifestEditorSteps.MANIFEST_REVIEW, ManifestEditorSteps.SAVE_MANIFEST, ]; @@ -210,35 +209,17 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { subText: () => formatMessage('To make your bot available for others as a skill, we need to generate a manifest.'), validate, }, - // [ManifestEditorSteps.ENDPOINTS]: { - // buttons: [cancelButton, nextButton], - // content: Endpoints, - // editJson: true, - // subText: () => - // formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), - // title: () => formatMessage('Skill endpoints'), - // validate: ({ content, schema }) => { - // const { items, minItems } = schema.properties?.endpoints; - - // if (!content.endpoints || content.endpoints.length < minItems) { - // return { endpoints: formatMessage('Please add at least {minItems} endpoint', { minItems }) }; - // } - - // const endpointSchema = resolveRef(items, schema.definitions); - // const endpoints = (content.endpoints || []).map((endpoint) => - // validate({ content: endpoint, schema: endpointSchema }) - // ); - - // return endpoints.some((endpoint) => Object.keys(endpoint).length) ? { endpoints } : {}; - // }, - // }, [ManifestEditorSteps.SELECT_PROFILE]: { buttons: [ cancelButton, { primary: true, - text: () => formatMessage('Next'), - onClick: ({ onNext }) => onNext, + text: () => formatMessage('Generate and Publish'), + onClick: ({ onNext, generateManifest }) => () => { + onNext({ dismiss: true, save: true }); + generateManifest(); + // onPublish(); + }, }, ], editJson: false, @@ -282,9 +263,8 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { cancelButton, { primary: true, - text: () => formatMessage('Generate'), - onClick: ({ generateManifest, onNext }) => () => { - generateManifest(); + text: () => formatMessage('Next'), + onClick: ({ onNext }) => () => { onNext(); }, }, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index bb6960b530..6d543b8934 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -1,13 +1,19 @@ -import { PublishTarget } from '@bfc/shared'; +import { PublishTarget, SkillManifestFile } from '@bfc/shared'; import formatMessage from 'format-message'; import { css, Dropdown, Icon, IDropdownOption, PrimaryButton, TextField, TooltipHost } from 'office-ui-fabric-react'; import React, { useEffect, useMemo, useState } from 'react'; import { useRecoilValue } from 'recoil'; -import { settingsState } from '../../../../recoilModel'; +import { + botDisplayNameState, + currentTargetState, + dispatcherState, + settingsState, + skillManifestsState, +} from '../../../../recoilModel'; import { PublishTargets } from '../../../botProject/PublishTargets'; import { iconStyle } from '../../../botProject/runtime-settings/style'; import Publish from '../../../publish/Publish'; -import { ContentProps } from '../constants'; +import { ContentProps, VERSION_REGEX } from '../constants'; const styles = { container: css` @@ -42,12 +48,37 @@ const onRenderLabel = (props) => { ); }; -export const SelectProfile: React.FC = ({ errors, value, schema, onChange, projectId }) => { +export const getManifestId = ( + botName: string, + skillManifests: SkillManifestFile[], + { content: { $schema } = {} }: Partial +): string => { + const [version] = VERSION_REGEX.exec($schema) || ['']; + + let fileId = version ? `${botName}-${version.replace(/\./g, '-')}-manifest` : `${botName}-manifest`; + let i = -1; + + while (skillManifests.some(({ id }) => id === fileId)) { + if (i < 0) { + fileId = fileId.concat(`-${++i}`); + } else { + fileId = fileId.substr(0, fileId.lastIndexOf('-')).concat(`-${++i}`); + } + } + + return fileId; +}; + +export const SelectProfile: React.FC = ({ manifest, setSkillManifest, value, onChange, projectId }) => { const [publishingTargets, setPublishingTargets] = useState([]); const [currentTarget, setCurrentTarget] = useState(); + const { updateCurrentTarget } = useRecoilValue(dispatcherState); const [endpointUrl, setEndpointUrl] = useState(); const [appId, setAppId] = useState(); const { endpoints, ...rest } = value; + const { id } = manifest; + const botName = useRecoilValue(botDisplayNameState(projectId)); + const skillManifests = useRecoilValue(skillManifestsState(projectId)); const updateCurrentProfile = useMemo( () => (_e, option?: IDropdownOption) => { @@ -56,20 +87,21 @@ export const SelectProfile: React.FC = ({ errors, value, schema, o }); if (target) { setCurrentTarget(target); + updateCurrentTarget(projectId, target); const config = JSON.parse(target.configuration); setEndpointUrl(`https://${config.hostname}.azurewebsites.net`); setAppId(config.settings.MicrosoftAppId); onChange({ + ...rest, endpoints: [ { protocol: 'BotFrameworkV3', name: option?.key, - endpointUrl: `https://${config.hostname}.azurewebsites.net`, + endpointUrl: `https://${config.hostname}.azurewebsites.net/api/messages`, description: '', msAppId: config.settings.MicrosoftAppId, }, ], - ...rest, }); } }, @@ -122,6 +154,13 @@ export const SelectProfile: React.FC = ({ errors, value, schema, o setPublishingTargets(settings.publishTargets || []); }, [settings]); + useEffect(() => { + if (!id) { + const fileId = getManifestId(botName, skillManifests, manifest); + setSkillManifest({ ...manifest, id: fileId }); + } + }, []); + return isProfileValid() ? (
= ({ onSubmit, onDismiss const dialogSchemas = useRecoilValue(dialogSchemasState(projectId)); const luFiles = useRecoilValue(luFilesState(projectId)); const qnaFiles = useRecoilValue(qnaFilesState(projectId)); + const currentTarget = useRecoilValue(currentTargetState(projectId)); const skillManifests = useRecoilValue(skillManifestsState(projectId)); const { updateSkillManifest } = useRecoilValue(dispatcherState); + const { publishToTarget, addNotification } = useRecoilValue(dispatcherState); + const [showAuthDialog, setShowAuthDialog] = useState(false); + + const pendingNotificationRef = useRef(); + const showNotificationsRef = useRef>({}); const [editingId, setEditingId] = useState(); const [currentStep, setCurrentStep] = useState(0); @@ -77,6 +89,28 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss } }; + const handleTriggerPublish = async () => { + // get token + + let token = ''; + // TODO: this logic needs to be moved into the Azure publish extensions + if (isGetTokenFromUser()) { + token = getTokenFromCache('accessToken'); + } else { + let tenant = getTenantIdFromCache(); + if (!tenant) { + const tenants = await AuthClient.getTenants(); + tenant = tenants?.[0]?.tenantId; + setTenantId(tenant); + } + token = await AuthClient.getARMTokenForTenant(tenant); + } + console.log(token); + const notification = createNotification(getPendingNotificationSkillCardProps()); + addNotification(notification); + await publishToTarget(projectId, currentTarget, {}, null, token); + }; + const handleSave = () => { if (skillManifest.content && skillManifest.id) { updateSkillManifest(skillManifest as SkillManifestFile, projectId); @@ -162,6 +196,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss onDismiss: handleDismiss, onNext: handleNext, onSave: handleSave, + // onPublish: handleTriggerPublish, onSubmit, })} /> @@ -171,6 +206,17 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss {editJson && }
+ {showAuthDialog && ( + { + // setDialogHidden(false); + }} + onDismiss={() => { + setShowAuthDialog(false); + }} + /> + )} ); diff --git a/Composer/packages/client/src/pages/publish/Notifications.tsx b/Composer/packages/client/src/pages/publish/Notifications.tsx index 8802db9da7..0b35dabed5 100644 --- a/Composer/packages/client/src/pages/publish/Notifications.tsx +++ b/Composer/packages/client/src/pages/publish/Notifications.tsx @@ -83,3 +83,19 @@ export const getPendingNotificationCardProps = (items: BotStatus[]): CardProps = ), }; }; + +export const getPendingNotificationSkillCardProps = (): CardProps => { + return { + title: '', + description: formatMessage(`Publishing skill`), + type: 'pending', + onRenderCardContent: (props) => ( +
+ +
+ +
+
+ ), + }; +}; diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index 2f8374350d..d74d87178a 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -29,7 +29,11 @@ import { navigateTo } from '../../utils/navigation'; import { PublishDialog } from './PublishDialog'; import { ContentHeaderStyle, HeaderText, ContentStyle, contentEditor } from './styles'; import { BotStatusList } from './BotStatusList'; -import { getPendingNotificationCardProps, getPublishedNotificationCardProps } from './Notifications'; +import { + getPendingNotificationCardProps, + getPendingNotificationSkillCardProps, + getPublishedNotificationCardProps, +} from './Notifications'; import { PullDialog } from './pullDialog'; import { PublishToolbar } from './PublishToolbar'; import { Bot, BotStatus } from './type'; diff --git a/Composer/packages/client/src/recoilModel/atoms/botState.ts b/Composer/packages/client/src/recoilModel/atoms/botState.ts index 1a6d4b316f..9bc98f08bc 100644 --- a/Composer/packages/client/src/recoilModel/atoms/botState.ts +++ b/Composer/packages/client/src/recoilModel/atoms/botState.ts @@ -17,6 +17,7 @@ import { QnAFile, SkillManifestFile, RecognizerFile, + PublishTarget, } from '@bfc/shared'; import { DirectLineLog } from '@botframework-composer/types/src'; import { atomFamily } from 'recoil'; @@ -312,6 +313,11 @@ export const qnaFilesState = atomFamily({ default: [], }); +export const currentTargetState = atomFamily({ + key: getFullyQualifiedKey('currentTarget'), + default: {} as PublishTarget, +}); + export const jsonSchemaFilesState = atomFamily({ key: getFullyQualifiedKey('jsonSchemaFiles'), default: [], diff --git a/Composer/packages/client/src/recoilModel/dispatchers/project.ts b/Composer/packages/client/src/recoilModel/dispatchers/project.ts index 44072454c3..c957856e51 100644 --- a/Composer/packages/client/src/recoilModel/dispatchers/project.ts +++ b/Composer/packages/client/src/recoilModel/dispatchers/project.ts @@ -4,7 +4,7 @@ import formatMessage from 'format-message'; import findIndex from 'lodash/findIndex'; -import { QnABotTemplateId, RootBotManagedProperties } from '@bfc/shared'; +import { PublishTarget, QnABotTemplateId, RootBotManagedProperties } from '@bfc/shared'; import get from 'lodash/get'; import { CallbackInterface, useRecoilCallback } from 'recoil'; @@ -28,6 +28,7 @@ import { createQnAOnState, creationFlowTypeState, currentProjectIdState, + currentTargetState, dispatcherState, fetchReadMePendingState, filePersistenceState, @@ -464,6 +465,12 @@ export const projectDispatcher = () => { } ); + const updateCurrentTarget = useRecoilCallback<[string, PublishTarget], void>( + ({ set }: CallbackInterface) => async (projectId: string, currentTarget) => { + set(currentTargetState(projectId), currentTarget); + } + ); + const saveTemplateId = useRecoilCallback<[string], void>(({ set }: CallbackInterface) => (templateId) => { if (templateId) { set(templateIdState, templateId); @@ -598,6 +605,7 @@ export const projectDispatcher = () => { saveProjectAs, fetchProjectById, fetchRecentProjects, + updateCurrentTarget, setBotStatus, saveTemplateId, updateBoilerplate, From 4d7cc968bb52c911c232e36a32c4abd33e94ab61 Mon Sep 17 00:00:00 2001 From: Long Alan Date: Thu, 25 Mar 2021 15:33:40 +0800 Subject: [PATCH 04/55] folder rename --- Composer/packages/server/src/models/bot/botStructure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/server/src/models/bot/botStructure.ts b/Composer/packages/server/src/models/bot/botStructure.ts index f6fc6db3da..227f2326b1 100644 --- a/Composer/packages/server/src/models/bot/botStructure.ts +++ b/Composer/packages/server/src/models/bot/botStructure.ts @@ -10,7 +10,7 @@ const BotStructureTemplate = { entry: '${BOTNAME}.dialog', lg: 'language-generation/${LOCALE}/${BOTNAME}.${LOCALE}.lg', lu: 'language-understanding/${LOCALE}/${BOTNAME}.${LOCALE}.lu', - manifestLu: 'manifest/${FILENAME}.${LOCALE}.lu', + manifestLu: 'manifests/${FILENAME}.${LOCALE}.lu', qna: 'knowledge-base/en-us/${BOTNAME}.en-us.qna', sourceQnA: 'knowledge-base/source/${FILENAME}.source.qna', dialogSchema: '${BOTNAME}.dialog.schema', From b51339ee48c0650b4c48866f12718997a13edeab Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Thu, 25 Mar 2021 15:56:22 +0800 Subject: [PATCH 05/55] add lu files writing logic --- .../design/exportSkillModal/constants.tsx | 15 +++--- .../exportSkillModal/generateSkillManifest.ts | 46 +++++++++++++++---- .../pages/design/exportSkillModal/index.tsx | 33 +++++++------ 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index c399deed59..c022cda263 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -145,9 +145,9 @@ export const order: ManifestEditorSteps[] = [ ManifestEditorSteps.MANIFEST_DESCRIPTION, ManifestEditorSteps.SELECT_DIALOGS, ManifestEditorSteps.SELECT_TRIGGERS, - // ManifestEditorSteps.SELECT_PROFILE, - ManifestEditorSteps.MANIFEST_REVIEW, - ManifestEditorSteps.SAVE_MANIFEST, + ManifestEditorSteps.SELECT_PROFILE, + // ManifestEditorSteps.MANIFEST_REVIEW, + // ManifestEditorSteps.SAVE_MANIFEST, ]; const cancelButton: Button = { @@ -215,14 +215,14 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { { primary: true, text: () => formatMessage('Generate and Publish'), - onClick: ({ onNext, generateManifest }) => () => { - onNext({ dismiss: true, save: true }); + onClick: ({ generateManifest, onNext }) => () => { generateManifest(); + onNext({ dismiss: true, save: true }); // onPublish(); }, }, ], - editJson: false, + editJson: true, content: SelectProfile, subText: () => formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), @@ -263,8 +263,9 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { cancelButton, { primary: true, - text: () => formatMessage('Next'), + text: () => formatMessage('Generate'), onClick: ({ onNext }) => () => { + // generateManifest(); onNext(); }, }, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index 79a160e931..bfb531d682 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -2,16 +2,26 @@ // Licensed under the MIT License. import get from 'lodash/get'; -import { DialogInfo, DialogSchemaFile, ITrigger, SDKKinds, SkillManifestFile, LuFile, QnAFile } from '@bfc/shared'; +import { + DialogInfo, + DialogSchemaFile, + ITrigger, + SDKKinds, + SkillManifestFile, + LuFile, + QnAFile, + PublishTarget, +} from '@bfc/shared'; import { JSONSchema7 } from '@bfc/extension-client'; import { Activities, Activity, activityHandlerMap, ActivityTypes, DispatchModels } from './constants'; +import { createManifestFile } from '../../../utils/manifestFileUtil'; const getActivityType = (kind: SDKKinds): ActivityTypes | undefined => activityHandlerMap[kind]; export const isSupportedTrigger = ({ type }: ITrigger) => Object.keys(activityHandlerMap).includes(type as SDKKinds); -export const generateSkillManifest = ( +export const generateSkillManifest = async ( schema: JSONSchema7, skillManifest: Partial, dialogs: DialogInfo[], @@ -19,7 +29,9 @@ export const generateSkillManifest = ( luFiles: LuFile[], qnaFiles: QnAFile[], selectedTriggers: ITrigger[], - selectedDialogs: Partial[] + selectedDialogs: Partial[], + currentTarget: PublishTarget, + projectId: string ) => { const { activities: previousActivities, @@ -40,7 +52,15 @@ export const generateSkillManifest = ( const triggers = selectedTriggers.map((tr) => get(content, tr.id) as ITrigger).filter(Boolean); const activities = generateActivities(dialogSchemas, triggers, resolvedDialogs); - const dispatchModels = generateDispatchModels(schema, dialogs, triggers, luFiles, qnaFiles); + const dispatchModels = await generateDispatchModels( + schema, + dialogs, + triggers, + luFiles, + qnaFiles, + currentTarget, + projectId + ); const definitions = getDefinitions(dialogSchemas, resolvedDialogs); return { @@ -99,12 +119,14 @@ export const generateOtherActivities = (kind: SDKKinds): Activities => { return type ? { [type]: { type } } : {}; }; -export const generateDispatchModels = ( +export const generateDispatchModels = async ( schema: JSONSchema7, dialogs: DialogInfo[], selectedTriggers: any[], luFiles: LuFile[], - qnaFiles: QnAFile[] + qnaFiles: QnAFile[], + target: PublishTarget, + projectId: string ): { dispatchModels?: DispatchModels } => { const intents = selectedTriggers.filter(({ $kind }) => $kind === SDKKinds.OnIntent).map(({ intent }) => intent); const { id: rootId } = dialogs.find((dialog) => dialog?.isRoot) || {}; @@ -123,6 +145,14 @@ export const generateDispatchModels = ( return {}; } + const config = JSON.parse(target.configuration); + const baseEndpointUrl = `https://${config.hostname}.azurewebsites.net/manifest`; + + for (const rootLuFile of rootLuFiles) { + const currentFileName = `skill-${rootLuFile.id}.lu`; + await createManifestFile(projectId, currentFileName, rootLuFile.content); + } + const luLanguages = intents.length ? rootLuFiles.reduce((acc, { empty, id }) => { const [name, locale] = id.split('.'); @@ -140,7 +170,7 @@ export const generateDispatchModels = ( { name, contentType: 'application/lu', - url: `<${id}.lu url>`, + url: `${baseEndpointUrl}/skill-${id}.lu`, description: '', }, ], @@ -164,7 +194,7 @@ export const generateDispatchModels = ( { name, contentType: 'application/qna', - url: `<${id}.qna url>`, + url: `${baseEndpointUrl}/skill-${id}.qna`, description: '', }, ], diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index ff403e4a3c..ccbe6054fd 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -25,7 +25,13 @@ import { import { editorSteps, ManifestEditorSteps, order } from './constants'; import { generateSkillManifest } from './generateSkillManifest'; import { styles } from './styles'; -import { getTenantIdFromCache, getTokenFromCache, isGetTokenFromUser, setTenantId } from '../../../utils/auth'; +import { + getTenantIdFromCache, + getTokenFromCache, + isGetTokenFromUser, + isShowAuthDialog, + setTenantId, +} from '../../../utils/auth'; import { AuthClient } from '../../../utils/authClient'; import { createNotification } from '../../../recoilModel/dispatchers/notification'; import { getPendingNotificationCardProps, getPendingNotificationSkillCardProps } from '../../publish/Notifications'; @@ -48,9 +54,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const { updateSkillManifest } = useRecoilValue(dispatcherState); const { publishToTarget, addNotification } = useRecoilValue(dispatcherState); const [showAuthDialog, setShowAuthDialog] = useState(false); - - const pendingNotificationRef = useRef(); - const showNotificationsRef = useRef>({}); + const [clickPublish, setclickPublish] = useState(false); const [editingId, setEditingId] = useState(); const [currentStep, setCurrentStep] = useState(0); @@ -67,8 +71,8 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const editorStep = order[currentStep]; const { buttons = [], content: Content, editJson, helpLink, subText, title, validate } = editorSteps[editorStep]; - const handleGenerateManifest = () => { - const manifest = generateSkillManifest( + const handleGenerateManifest = async () => { + const manifest = await generateSkillManifest( schema, skillManifest, dialogs, @@ -76,7 +80,9 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss luFiles, qnaFiles, selectedTriggers, - selectedDialogs + selectedDialogs, + currentTarget, + projectId ); setSkillManifest(manifest); }; @@ -91,7 +97,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const handleTriggerPublish = async () => { // get token - + setShowAuthDialog(true); let token = ''; // TODO: this logic needs to be moved into the Azure publish extensions if (isGetTokenFromUser()) { @@ -105,9 +111,8 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss } token = await AuthClient.getARMTokenForTenant(tenant); } - console.log(token); - const notification = createNotification(getPendingNotificationSkillCardProps()); - addNotification(notification); + // const notification = createNotification(getPendingNotificationSkillCardProps()); + // addNotification(notification); await publishToTarget(projectId, currentTarget, {}, null, token); }; @@ -196,7 +201,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss onDismiss: handleDismiss, onNext: handleNext, onSave: handleSave, - // onPublish: handleTriggerPublish, + onPublish: handleTriggerPublish, onSubmit, })} /> @@ -206,9 +211,9 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss {editJson && } - {showAuthDialog && ( + {clickPublish && isShowAuthDialog(false) && ( { // setDialogHidden(false); }} From b92eafa3afe73737823485f5352573b5006bf4c4 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Thu, 25 Mar 2021 16:46:03 +0800 Subject: [PATCH 06/55] add redirect to publishing page action --- .../design/exportSkillModal/constants.tsx | 24 +++++++--- .../exportSkillModal/generateSkillManifest.ts | 16 ++----- .../pages/design/exportSkillModal/index.tsx | 44 ++++++++++--------- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index c022cda263..627c195b73 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -215,10 +215,10 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { { primary: true, text: () => formatMessage('Generate and Publish'), - onClick: ({ generateManifest, onNext }) => () => { + onClick: ({ generateManifest, onNext, onPublish }) => () => { generateManifest(); onNext({ dismiss: true, save: true }); - // onPublish(); + onPublish(); }, }, ], @@ -227,6 +227,20 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { subText: () => formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), title: () => formatMessage('Confirm skill endpoints'), + validate: ({ editingId, id, skillManifests }) => { + if (!id || !nameRegex.test(id)) { + return { id: formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.') }; + } + + if ( + (typeof editingId === 'undefined' || editingId !== id) && + skillManifests.some(({ id: manifestId }) => manifestId === id) + ) { + return { id: formatMessage('{id} already exists. Please enter a unique file name.', { id }) }; + } + + return {}; + }, }, [ManifestEditorSteps.MANIFEST_REVIEW]: { buttons: [ @@ -263,9 +277,9 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { cancelButton, { primary: true, - text: () => formatMessage('Generate'), - onClick: ({ onNext }) => () => { - // generateManifest(); + text: () => formatMessage('Next'), + onClick: ({ onNext, generateManifest }) => () => { + generateManifest(); onNext(); }, }, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index bfb531d682..1e42f5e8b8 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -21,7 +21,7 @@ const getActivityType = (kind: SDKKinds): ActivityTypes | undefined => activityH export const isSupportedTrigger = ({ type }: ITrigger) => Object.keys(activityHandlerMap).includes(type as SDKKinds); -export const generateSkillManifest = async ( +export const generateSkillManifest = ( schema: JSONSchema7, skillManifest: Partial, dialogs: DialogInfo[], @@ -52,15 +52,7 @@ export const generateSkillManifest = async ( const triggers = selectedTriggers.map((tr) => get(content, tr.id) as ITrigger).filter(Boolean); const activities = generateActivities(dialogSchemas, triggers, resolvedDialogs); - const dispatchModels = await generateDispatchModels( - schema, - dialogs, - triggers, - luFiles, - qnaFiles, - currentTarget, - projectId - ); + const dispatchModels = generateDispatchModels(schema, dialogs, triggers, luFiles, qnaFiles, currentTarget, projectId); const definitions = getDefinitions(dialogSchemas, resolvedDialogs); return { @@ -119,7 +111,7 @@ export const generateOtherActivities = (kind: SDKKinds): Activities => { return type ? { [type]: { type } } : {}; }; -export const generateDispatchModels = async ( +export const generateDispatchModels = ( schema: JSONSchema7, dialogs: DialogInfo[], selectedTriggers: any[], @@ -150,7 +142,7 @@ export const generateDispatchModels = async ( for (const rootLuFile of rootLuFiles) { const currentFileName = `skill-${rootLuFile.id}.lu`; - await createManifestFile(projectId, currentFileName, rootLuFile.content); + createManifestFile(projectId, currentFileName, rootLuFile.content); } const luLanguages = intents.length diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index ccbe6054fd..734806bde9 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -36,6 +36,7 @@ import { AuthClient } from '../../../utils/authClient'; import { createNotification } from '../../../recoilModel/dispatchers/notification'; import { getPendingNotificationCardProps, getPendingNotificationSkillCardProps } from '../../publish/Notifications'; import { AuthDialog } from '../../../components/Auth/AuthDialog'; +import { navigate } from '@reach/router'; interface ExportSkillModalProps { isOpen: boolean; @@ -71,8 +72,8 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const editorStep = order[currentStep]; const { buttons = [], content: Content, editJson, helpLink, subText, title, validate } = editorSteps[editorStep]; - const handleGenerateManifest = async () => { - const manifest = await generateSkillManifest( + const handleGenerateManifest = () => { + const manifest = generateSkillManifest( schema, skillManifest, dialogs, @@ -96,24 +97,25 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss }; const handleTriggerPublish = async () => { + navigate(`/bot/${projectId}/publish/all`); // get token - setShowAuthDialog(true); - let token = ''; - // TODO: this logic needs to be moved into the Azure publish extensions - if (isGetTokenFromUser()) { - token = getTokenFromCache('accessToken'); - } else { - let tenant = getTenantIdFromCache(); - if (!tenant) { - const tenants = await AuthClient.getTenants(); - tenant = tenants?.[0]?.tenantId; - setTenantId(tenant); - } - token = await AuthClient.getARMTokenForTenant(tenant); - } - // const notification = createNotification(getPendingNotificationSkillCardProps()); - // addNotification(notification); - await publishToTarget(projectId, currentTarget, {}, null, token); + // setShowAuthDialog(true); + // let token = ''; + // // TODO: this logic needs to be moved into the Azure publish extensions + // if (isGetTokenFromUser()) { + // token = getTokenFromCache('accessToken'); + // } else { + // let tenant = getTenantIdFromCache(); + // if (!tenant) { + // const tenants = await AuthClient.getTenants(); + // tenant = tenants?.[0]?.tenantId; + // setTenantId(tenant); + // } + // token = await AuthClient.getARMTokenForTenant(tenant); + // } + // // const notification = createNotification(getPendingNotificationSkillCardProps()); + // // addNotification(notification); + // await publishToTarget(projectId, currentTarget, {}, null, token); }; const handleSave = () => { @@ -211,7 +213,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss {editJson && } - {clickPublish && isShowAuthDialog(false) && ( + {/* {clickPublish && isShowAuthDialog(false) && ( { @@ -221,7 +223,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss setShowAuthDialog(false); }} /> - )} + )} */} ); From 45be7cd2399bbc5c66ddb236fba4a5ce4d95f971 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Thu, 25 Mar 2021 16:47:22 +0800 Subject: [PATCH 07/55] change folder name --- .../src/pages/design/exportSkillModal/generateSkillManifest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index 1e42f5e8b8..7d97fdb228 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -138,7 +138,7 @@ export const generateDispatchModels = ( } const config = JSON.parse(target.configuration); - const baseEndpointUrl = `https://${config.hostname}.azurewebsites.net/manifest`; + const baseEndpointUrl = `https://${config.hostname}.azurewebsites.net/manifests`; for (const rootLuFile of rootLuFiles) { const currentFileName = `skill-${rootLuFile.id}.lu`; From 97584b1f20f3dc80e603ebff36832415305ef8ba Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Fri, 26 Mar 2021 12:38:29 +0800 Subject: [PATCH 08/55] add lu file parsing and intents filtering --- .../design/exportSkillModal/constants.tsx | 26 ++++++++-------- .../content/SelectProfile.tsx | 30 +++++++++++-------- .../exportSkillModal/generateSkillManifest.ts | 10 ++++++- .../pages/design/exportSkillModal/index.tsx | 22 +++++++++----- 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index 627c195b73..57986d8164 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -227,20 +227,20 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { subText: () => formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), title: () => formatMessage('Confirm skill endpoints'), - validate: ({ editingId, id, skillManifests }) => { - if (!id || !nameRegex.test(id)) { - return { id: formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.') }; - } + // validate: ({ editingId, id, skillManifests }) => { + // if (!id || !nameRegex.test(id)) { + // return { id: formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.') }; + // } - if ( - (typeof editingId === 'undefined' || editingId !== id) && - skillManifests.some(({ id: manifestId }) => manifestId === id) - ) { - return { id: formatMessage('{id} already exists. Please enter a unique file name.', { id }) }; - } + // if ( + // (typeof editingId === 'undefined' || editingId !== id) && + // skillManifests.some(({ id: manifestId }) => manifestId === id) + // ) { + // return { id: formatMessage('{id} already exists. Please enter a unique file name.', { id }) }; + // } - return {}; - }, + // return {}; + // }, }, [ManifestEditorSteps.MANIFEST_REVIEW]: { buttons: [ @@ -279,7 +279,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { primary: true, text: () => formatMessage('Next'), onClick: ({ onNext, generateManifest }) => () => { - generateManifest(); + // generateManifest(); onNext(); }, }, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index 6d543b8934..6d98e76899 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -75,11 +75,11 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife const { updateCurrentTarget } = useRecoilValue(dispatcherState); const [endpointUrl, setEndpointUrl] = useState(); const [appId, setAppId] = useState(); - const { endpoints, ...rest } = value; - const { id } = manifest; + const { id, content } = manifest; const botName = useRecoilValue(botDisplayNameState(projectId)); const skillManifests = useRecoilValue(skillManifestsState(projectId)); + const { ...rest } = content; const updateCurrentProfile = useMemo( () => (_e, option?: IDropdownOption) => { const target = publishingTargets.find((t) => { @@ -91,17 +91,21 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife const config = JSON.parse(target.configuration); setEndpointUrl(`https://${config.hostname}.azurewebsites.net`); setAppId(config.settings.MicrosoftAppId); - onChange({ - ...rest, - endpoints: [ - { - protocol: 'BotFrameworkV3', - name: option?.key, - endpointUrl: `https://${config.hostname}.azurewebsites.net/api/messages`, - description: '', - msAppId: config.settings.MicrosoftAppId, - }, - ], + + setSkillManifest({ + content: { + ...rest, + endpoints: [ + { + protocol: 'BotFrameworkV3', + name: option?.key, + endpointUrl: `https://${config.hostname}.azurewebsites.net/api/messages`, + description: '', + msAppId: config.settings.MicrosoftAppId, + }, + ], + }, + id: id, }); } }, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index 7d97fdb228..6393bb15a4 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -16,6 +16,7 @@ import { JSONSchema7 } from '@bfc/extension-client'; import { Activities, Activity, activityHandlerMap, ActivityTypes, DispatchModels } from './constants'; import { createManifestFile } from '../../../utils/manifestFileUtil'; +import { luIndexer } from '@bfc/indexers'; const getActivityType = (kind: SDKKinds): ActivityTypes | undefined => activityHandlerMap[kind]; @@ -142,7 +143,14 @@ export const generateDispatchModels = ( for (const rootLuFile of rootLuFiles) { const currentFileName = `skill-${rootLuFile.id}.lu`; - createManifestFile(projectId, currentFileName, rootLuFile.content); + const parsedLuFile = luIndexer.parse(rootLuFile.content, rootLuFile.id, {}); + const contents = parsedLuFile.intents.map((x) => { + if (intents.find((intent) => intent == x.Name) != -1) { + return x.Body; + } + }); + const mergedContents = contents.join('\n'); + createManifestFile(projectId, currentFileName, mergedContents); } const luLanguages = intents.length diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 734806bde9..102d43d118 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -3,7 +3,7 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import React, { useRef, useState } from 'react'; +import React, { useEffect, useMemo, useRef, useState } from 'react'; import formatMessage from 'format-message'; import { Dialog, DialogFooter, DialogType } from 'office-ui-fabric-react/lib/Dialog'; import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; @@ -53,9 +53,6 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const currentTarget = useRecoilValue(currentTargetState(projectId)); const skillManifests = useRecoilValue(skillManifestsState(projectId)); const { updateSkillManifest } = useRecoilValue(dispatcherState); - const { publishToTarget, addNotification } = useRecoilValue(dispatcherState); - const [showAuthDialog, setShowAuthDialog] = useState(false); - const [clickPublish, setclickPublish] = useState(false); const [editingId, setEditingId] = useState(); const [currentStep, setCurrentStep] = useState(0); @@ -63,7 +60,6 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const [schema, setSchema] = useState({}); const [skillManifest, setSkillManifest] = useState>({}); - const { content = {}, id } = skillManifest; const [selectedDialogs, setSelectedDialogs] = useState([]); @@ -119,8 +115,20 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss }; const handleSave = () => { - if (skillManifest.content && skillManifest.id) { - updateSkillManifest(skillManifest as SkillManifestFile, projectId); + const manifest = generateSkillManifest( + schema, + skillManifest, + dialogs, + dialogSchemas, + luFiles, + qnaFiles, + selectedTriggers, + selectedDialogs, + currentTarget, + projectId + ); + if (manifest.content && manifest.id) { + updateSkillManifest(manifest as SkillManifestFile, projectId); } }; From 73a802a7844463429f7aa4e71e04f88dbfd9bc5d Mon Sep 17 00:00:00 2001 From: Lu Han <32191031+luhan2017@users.noreply.github.com> Date: Tue, 23 Mar 2021 11:59:31 +0800 Subject: [PATCH 09/55] Support .lu and .qna for static files --- runtime/dotnet/azurewebapp/Startup.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/runtime/dotnet/azurewebapp/Startup.cs b/runtime/dotnet/azurewebapp/Startup.cs index 177bc35b9b..a7b18b6ec0 100644 --- a/runtime/dotnet/azurewebapp/Startup.cs +++ b/runtime/dotnet/azurewebapp/Startup.cs @@ -4,6 +4,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; using Microsoft.Bot.Builder; using Microsoft.Bot.Builder.AI.Luis; using Microsoft.Bot.Builder.AI.QnA; @@ -28,6 +30,7 @@ using Microsoft.BotFramework.Composer.WebAppTemplates.Authorization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.FileProviders; namespace Microsoft.BotFramework.Composer.WebAppTemplates { @@ -223,7 +226,21 @@ public void ConfigureServices(IServiceCollection services) public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseDefaultFiles(); - app.UseStaticFiles(); + + // Set up custom content types - associating file extension to MIME type. + var provider = new FileExtensionContentTypeProvider(); + provider.Mappings[".lu"] = "application/lu"; + provider.Mappings[".qna"] = "application/qna"; + + // Expose static files in manifests folder for skill scenarios. + app.UseStaticFiles(new StaticFileOptions + { + FileProvider = new PhysicalFileProvider( + Path.Combine(env.WebRootPath, "manifests")), + RequestPath = "/manifests", + ContentTypeProvider = provider + }); + app.UseNamedPipes(System.Environment.GetEnvironmentVariable("APPSETTING_WEBSITE_SITE_NAME") + ".directline"); app.UseWebSockets(); app.UseRouting() From b4da411977ba0059bd7ece01635305e652296391 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Tue, 30 Mar 2021 20:33:12 +0800 Subject: [PATCH 10/55] 'notification' --- .../pages/design/exportSkillModal/index.tsx | 5 +- .../src/pages/publish/BotStatusList.tsx | 1 + .../src/pages/publish/Notifications.tsx | 46 +++++++++++++++- .../client/src/pages/publish/Publish.tsx | 43 +++++++++++++-- .../packages/server/src/locales/en-US.json | 55 +++++++++++++++---- 5 files changed, 130 insertions(+), 20 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 49eb6c9292..f6a3d53dbf 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -93,7 +93,10 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss }; const handleTriggerPublish = async () => { - navigate(`/bot/${projectId}/publish/all`); + const filePath = `https://${JSON.parse(currentTarget.configuration).hostname}.azurewebsites.net/manifests/${ + skillManifest.id + }.json`; + navigate(`/bot/${projectId}/publish/all?publishTargetName=${currentTarget.name}&url=${filePath}`); // get token // setShowAuthDialog(true); // let token = ''; diff --git a/Composer/packages/client/src/pages/publish/BotStatusList.tsx b/Composer/packages/client/src/pages/publish/BotStatusList.tsx index 48823b1ac8..7092436f69 100644 --- a/Composer/packages/client/src/pages/publish/BotStatusList.tsx +++ b/Composer/packages/client/src/pages/publish/BotStatusList.tsx @@ -142,6 +142,7 @@ export const BotStatusList: React.FC = ({ return ( ), }; }; -export const getPendingNotificationCardProps = (items: BotStatus[]): CardProps => { + +export const getSkillPublishedNotificationCardProps = ( + item: BotStatus, + endpoint?: string, + manifestName?: string +): CardProps => { + const statusIconStyle = css({ + margin: '12px 0 0 -1px', + width: '12px', + height: '12px', + fontSize: '12px', + color: item.status === 200 ? '#27AE60' : 'rgb(161, 159, 157)', + transform: item.status !== 200 ? 'rotate(45deg)' : '', + }); + return { + title: '', + description: + item.status === 200 + ? formatMessage(`You have successfully published {name} to {publishTarget}`, { + name: item.name, + publishTarget: item.publishTarget, + }) + : formatMessage(`Publishing {name} to {publishTarget} failed.`, { + name: item.name, + publishTarget: item.publishTarget, + }), + type: 'pending', + onRenderCardContent: (props) => ( +
+ + +
+
{props.description}
+
+
+ ), + }; +}; + +export const getPendingNotificationCardProps = (items: BotStatus[], isSkill = false): CardProps => { + const description = isSkill + ? 'Publishing your skill...' + : formatMessage(`Publishing {count} bots`, { count: items.length }); return { title: '', - description: formatMessage(`Publishing {count} bots`, { count: items.length }), + description, type: 'pending', onRenderCardContent: (props) => (
diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index 5a0d84bfa6..0ab8b7a3fe 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -8,6 +8,7 @@ import { RouteComponentProps } from '@reach/router'; import formatMessage from 'format-message'; import { useRecoilValue } from 'recoil'; import { PublishResult, PublishTarget } from '@bfc/shared'; +import querystring from 'query-string'; import { dispatcherState, localBotPublishHistorySelector, localBotsDataSelector } from '../../recoilModel'; import { AuthDialog } from '../../components/Auth/AuthDialog'; @@ -32,8 +33,8 @@ import { ContentHeaderStyle, HeaderText, ContentStyle, contentEditor } from './s import { BotStatusList } from './BotStatusList'; import { getPendingNotificationCardProps, - getPendingNotificationSkillCardProps, getPublishedNotificationCardProps, + getSkillPublishedNotificationCardProps, } from './Notifications'; import { PullDialog } from './pullDialog'; import { PublishToolbar } from './PublishToolbar'; @@ -44,7 +45,15 @@ import { generateBotStatusList, deleteNotificationInterval, } from './publishPageUtils'; - +import { manifestUrl } from '../design/styles'; + +const SKILL_PUBLISH_STATUS = { + INITIAL: 'inital', + WAITING: 'wait for publish', + PUBLISHING: 'publishing', + PUBLISHED: 'published', + CANCEL: 'cancel', +}; const Publish: React.FC> = (props) => { const { projectId = '' } = props; @@ -124,6 +133,20 @@ const Publish: React.FC { + if (publishTargetName && botStatusList.length > 0 && skillPublishStatus === SKILL_PUBLISH_STATUS.INITIAL) { + setSkillPublishStatus(SKILL_PUBLISH_STATUS.WAITING); + const currentBotStatus = botStatusList.find((bot) => bot.id === projectId); + changePublishTarget(publishTargetName, currentBotStatus); + setCheckedSkillIds([projectId]); + setPublishDialogVisiblity(true); + } + }, [publishTargetName, botStatusList]); + useEffect(() => { if (currentBotList.length < botList.length) { // init bot status list for the botProjectData is empty array when first mounted @@ -198,9 +221,11 @@ const Publish: React.FC { deleteNotification(resultNotification.id); @@ -302,10 +327,16 @@ const Publish: React.FC{ botName }" }, @@ -3362,6 +3389,9 @@ "this_page_contains_detailed_information_about_your_b2b3413b": { "message": "This Page contains detailed information about your bot. For security reasons, they are hidden by default. To test your bot or publish to Azure, you may need to provide these settings" }, + "this_publishing_profile_profilename_is_no_longer_s_eee0f447": { + "message": "This publishing profile ({ profileName }) is no longer supported. You are a member of multiple Azure tenants and the profile needs to have a tenant id associated with it. You can either edit the profile by adding the `tenantId` property to it''s configuration or create a new one." + }, "this_trigger_type_is_not_supported_by_the_regex_re_dc3eefa2": { "message": "This trigger type is not supported by the RegEx recognizer. To ensure this trigger is fired, change the recognizer type." }, @@ -3518,6 +3548,9 @@ "unread_notifications_indicator_e2ca00d5": { "message": "Unread notifications Indicator" }, + "unsupported_publishing_profile_ad088e54": { + "message": "Unsupported publishing profile" + }, "unused_8d193e3": { "message": "Unused" }, From 9c56475bb7983e61601b6423945277d3ee41cf7f Mon Sep 17 00:00:00 2001 From: Long Alan Date: Wed, 31 Mar 2021 11:11:35 +0800 Subject: [PATCH 11/55] manifest file name --- Composer/packages/server/src/models/bot/botProject.ts | 3 +-- Composer/packages/server/src/models/bot/botStructure.ts | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Composer/packages/server/src/models/bot/botProject.ts b/Composer/packages/server/src/models/bot/botProject.ts index c877e3f008..14bfdd1ece 100644 --- a/Composer/packages/server/src/models/bot/botProject.ts +++ b/Composer/packages/server/src/models/bot/botProject.ts @@ -469,8 +469,7 @@ export class BotProject implements IBotProject { this.validateFileName(filename); this._validateFileContent(name, content); const botName = this.name; - const defaultLocale = this.settings?.defaultLanguage || defaultLanguage; - const relativePath = defaultManifestFilePath(botName, filename, defaultLocale); + const relativePath = defaultManifestFilePath(botName, filename); const file = this.files.get(filename); if (file) { throw new Error(`${filename} dialog already exist`); diff --git a/Composer/packages/server/src/models/bot/botStructure.ts b/Composer/packages/server/src/models/bot/botStructure.ts index 227f2326b1..608dcf516f 100644 --- a/Composer/packages/server/src/models/bot/botStructure.ts +++ b/Composer/packages/server/src/models/bot/botStructure.ts @@ -10,7 +10,7 @@ const BotStructureTemplate = { entry: '${BOTNAME}.dialog', lg: 'language-generation/${LOCALE}/${BOTNAME}.${LOCALE}.lg', lu: 'language-understanding/${LOCALE}/${BOTNAME}.${LOCALE}.lu', - manifestLu: 'manifests/${FILENAME}.${LOCALE}.lu', + manifestLu: 'manifests/${FILENAME}.lu', qna: 'knowledge-base/en-us/${BOTNAME}.en-us.qna', sourceQnA: 'knowledge-base/source/${FILENAME}.source.qna', dialogSchema: '${BOTNAME}.dialog.schema', @@ -106,11 +106,10 @@ export const parseFileName = (name: string, defaultLocale: string) => { export const isRecognizer = (fileName: string) => fileName.endsWith('.lu.dialog') || fileName.endsWith('.qna.dialog'); export const isCrossTrainConfig = (fileName: string) => fileName.endsWith('cross-train.config.json'); -export const defaultManifestFilePath = (botName: string, fileName: string, locale: string): string => { +export const defaultManifestFilePath = (botName: string, fileName: string): string => { return templateInterpolate(BotStructureTemplate.manifestLu, { BOTNAME: botName, FILENAME: fileName, - LOCALE: locale, }); }; From 6739924b09d61c68e1851f11ab2b2b5df91fc92c Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Wed, 31 Mar 2021 11:58:05 +0800 Subject: [PATCH 12/55] add skill add callers logic --- .../design/exportSkillModal/constants.tsx | 51 ++++++----- .../exportSkillModal/content/AddCallers.tsx | 89 +++++++++++++++++++ .../content/SelectProfile.tsx | 37 +++----- .../exportSkillModal/generateSkillManifest.ts | 9 +- .../pages/design/exportSkillModal/index.tsx | 75 +++++++--------- 5 files changed, 168 insertions(+), 93 deletions(-) create mode 100644 Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index 57986d8164..a3beae3148 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -21,6 +21,7 @@ import { SelectTriggers, } from './content'; import { SelectProfile } from './content/SelectProfile'; +import { AddCallers } from './content/AddCallers'; export const VERSION_REGEX = /\d\.\d+\.(\d+|preview-\d+)|\d\.\d+/i; @@ -100,6 +101,8 @@ export interface ContentProps { value: { [key: string]: any }; onChange: (_: any) => void; projectId: string; + callers: string[]; + setCallers: (callers: string[]) => void; } interface Button { @@ -137,6 +140,7 @@ export enum ManifestEditorSteps { SELECT_DIALOGS = 'SELECT_DIALOGS', SELECT_TRIGGERS = 'SELECT_TRIGGERS', SELECT_PROFILE = 'SELECT_PROFILE', + ADD_CALLERS = 'ADD_CALLERS', } export const order: ManifestEditorSteps[] = [ @@ -145,6 +149,7 @@ export const order: ManifestEditorSteps[] = [ ManifestEditorSteps.MANIFEST_DESCRIPTION, ManifestEditorSteps.SELECT_DIALOGS, ManifestEditorSteps.SELECT_TRIGGERS, + ManifestEditorSteps.ADD_CALLERS, ManifestEditorSteps.SELECT_PROFILE, // ManifestEditorSteps.MANIFEST_REVIEW, // ManifestEditorSteps.SAVE_MANIFEST, @@ -204,7 +209,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.MANIFEST_DESCRIPTION]: { buttons: [cancelButton, nextButton], content: Description, - editJson: true, + editJson: false, title: () => formatMessage('Describe your skill'), subText: () => formatMessage('To make your bot available for others as a skill, we need to generate a manifest.'), validate, @@ -217,30 +222,36 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { text: () => formatMessage('Generate and Publish'), onClick: ({ generateManifest, onNext, onPublish }) => () => { generateManifest(); - onNext({ dismiss: true, save: true }); + onNext(); onPublish(); }, }, ], - editJson: true, + editJson: false, content: SelectProfile, subText: () => formatMessage('We need to define the endpoints for the skill to allow other bots to interact with it.'), title: () => formatMessage('Confirm skill endpoints'), - // validate: ({ editingId, id, skillManifests }) => { - // if (!id || !nameRegex.test(id)) { - // return { id: formatMessage('Spaces and special characters are not allowed. Use letters, numbers, -, or _.') }; - // } - - // if ( - // (typeof editingId === 'undefined' || editingId !== id) && - // skillManifests.some(({ id: manifestId }) => manifestId === id) - // ) { - // return { id: formatMessage('{id} already exists. Please enter a unique file name.', { id }) }; - // } - - // return {}; - // }, + }, + [ManifestEditorSteps.ADD_CALLERS]: { + buttons: [ + cancelButton, + { + primary: true, + text: () => formatMessage('Next'), + onClick: ({ onNext, onSaveSkill }) => () => { + onSaveSkill(); + onNext(); + }, + }, + ], + editJson: false, + content: AddCallers, + subText: () => + formatMessage( + 'Add Microsoft App Ids of bots that can access this skill. You can skip this step and add this information later from the project settings tab.' + ), + title: () => formatMessage('Which bots are allowed to use this skill?'), }, [ManifestEditorSteps.MANIFEST_REVIEW]: { buttons: [ @@ -265,7 +276,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { }, ], content: SelectDialogs, - editJson: true, + editJson: false, subText: () => formatMessage( 'These tasks will be used to generate the manifest and describe the capabilities of this skill to those who may want to use it.' @@ -285,7 +296,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { }, ], content: SelectTriggers, - editJson: true, + editJson: false, subText: () => formatMessage( 'These tasks will be used to generate the manifest and describe the capabilities of this skill to those who may want to use it.' @@ -304,7 +315,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { }, ], content: SaveManifest, - editJson: true, + editJson: false, subText: () => formatMessage('Name and save your skill manifest.'), title: () => formatMessage('Save your skill manifest'), validate: ({ editingId, id, skillManifests }) => { diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx new file mode 100644 index 0000000000..d02831cc9e --- /dev/null +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import { jsx } from '@emotion/core'; +import { SharedColors } from '@uifabric/fluent-theme'; +import formatMessage from 'format-message'; +import { ActionButton, css, FontWeights, TextField } from 'office-ui-fabric-react'; +import React from 'react'; +import { tableColumnHeader, tableRow, tableRowItem } from '../../../botProject/styles'; +import { ContentProps } from '../constants'; + +const header = css` + display: flex; + flex-direction: row; + height: 42px; +`; + +const addNewAllowCallers = { + root: { + fontSize: 12, + fontWeight: FontWeights.regular, + color: SharedColors.cyanBlue10, + paddingLeft: 0, + marginLeft: 5, + }, +}; + +const removeCaller = { + root: { + fontSize: 12, + fontWeight: FontWeights.regular, + color: SharedColors.cyanBlue10, + paddingLeft: 0, + paddingBottom: 5, + }, +}; + +export const AddCallers: React.FC = ({ projectId, callers, setCallers }) => { + const handleRemove = (index) => { + var currentCallers = callers.slice(); + currentCallers?.splice(index, 1); + setCallers(currentCallers); + }; + const handleAdd = () => { + var currentCallers = callers.slice(); + currentCallers?.push('0000-11111-00000-11111'); + setCallers(currentCallers); + }; + + return ( +
+
+
{formatMessage('Allowed Callers')}
+
+ {callers?.map((caller, index) => { + return ( +
+
+ { + var currentCallers = callers.slice(); + currentCallers[index] = newValue ?? ''; + setCallers(currentCallers); + }} + borderless + > +
+
+ handleRemove(index)}> + {formatMessage('Remove')} + +
+
+ ); + })} + { + handleAdd(); + }} + > + {formatMessage('Add allowd callers')} + +
+ ); +}; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index 6d98e76899..0f47a079b7 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -1,18 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import { jsx } from '@emotion/core'; import { PublishTarget, SkillManifestFile } from '@bfc/shared'; import formatMessage from 'format-message'; -import { css, Dropdown, Icon, IDropdownOption, PrimaryButton, TextField, TooltipHost } from 'office-ui-fabric-react'; +import { css, Dropdown, Icon, IDropdownOption, TextField, TooltipHost } from 'office-ui-fabric-react'; import React, { useEffect, useMemo, useState } from 'react'; import { useRecoilValue } from 'recoil'; -import { - botDisplayNameState, - currentTargetState, - dispatcherState, - settingsState, - skillManifestsState, -} from '../../../../recoilModel'; + +import { botDisplayNameState, dispatcherState, settingsState, skillManifestsState } from '../../../../recoilModel'; import { PublishTargets } from '../../../botProject/PublishTargets'; import { iconStyle } from '../../../botProject/runtime-settings/style'; -import Publish from '../../../publish/Publish'; import { ContentProps, VERSION_REGEX } from '../constants'; const styles = { @@ -89,7 +88,7 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife setCurrentTarget(target); updateCurrentTarget(projectId, target); const config = JSON.parse(target.configuration); - setEndpointUrl(`https://${config.hostname}.azurewebsites.net`); + setEndpointUrl(`https://${config.hostname}.azurewebsites.net/api/messages`); setAppId(config.settings.MicrosoftAppId); setSkillManifest({ @@ -140,20 +139,6 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife const settings = useRecoilValue(settingsState(projectId)); - const OnEndpointChange = useMemo( - () => (e, newValue) => { - setEndpointUrl(newValue); - }, - [setEndpointUrl] - ); - - const OnAppIdChange = useMemo( - () => (e, newValue) => { - setAppId(newValue); - }, - [setAppId] - ); - useEffect(() => { setPublishingTargets(settings.publishTargets || []); }, [settings]); @@ -177,22 +162,22 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife onChange={updateCurrentProfile} /> diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index 6393bb15a4..406a08129d 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -13,10 +13,11 @@ import { PublishTarget, } from '@bfc/shared'; import { JSONSchema7 } from '@bfc/extension-client'; +import { luIndexer } from '@bfc/indexers'; -import { Activities, Activity, activityHandlerMap, ActivityTypes, DispatchModels } from './constants'; import { createManifestFile } from '../../../utils/manifestFileUtil'; -import { luIndexer } from '@bfc/indexers'; + +import { Activities, Activity, activityHandlerMap, ActivityTypes, DispatchModels } from './constants'; const getActivityType = (kind: SDKKinds): ActivityTypes | undefined => activityHandlerMap[kind]; @@ -142,11 +143,11 @@ export const generateDispatchModels = ( const baseEndpointUrl = `https://${config.hostname}.azurewebsites.net/manifests`; for (const rootLuFile of rootLuFiles) { - const currentFileName = `skill-${rootLuFile.id}.lu`; + const currentFileName = `skill-${rootLuFile.id}`; const parsedLuFile = luIndexer.parse(rootLuFile.content, rootLuFile.id, {}); const contents = parsedLuFile.intents.map((x) => { if (intents.find((intent) => intent == x.Name) != -1) { - return x.Body; + return [`# ${x.Name}`, x.Body].join('\n'); } }); const mergedContents = contents.join('\n'); diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 49eb6c9292..5909b3328d 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -11,6 +11,7 @@ import { JSONSchema7 } from '@bfc/extension-client'; import { Link } from 'office-ui-fabric-react/lib/components/Link'; import { useRecoilValue } from 'recoil'; import { SkillManifestFile } from '@bfc/shared'; +import { navigate } from '@reach/router'; import { dispatcherState, @@ -20,23 +21,15 @@ import { dialogSchemasState, currentTargetState, luFilesSelectorFamily, + settingsState, + rootBotProjectIdSelector, } from '../../../recoilModel'; -import { editorSteps, ManifestEditorSteps, order } from './constants'; -import { generateSkillManifest } from './generateSkillManifest'; import { styles } from './styles'; -import { - getTenantIdFromCache, - getTokenFromCache, - isGetTokenFromUser, - isShowAuthDialog, - setTenantId, -} from '../../../utils/auth'; -import { AuthClient } from '../../../utils/authClient'; -import { createNotification } from '../../../recoilModel/dispatchers/notification'; -import { getPendingNotificationCardProps, getPendingNotificationSkillCardProps } from '../../publish/Notifications'; -import { AuthDialog } from '../../../components/Auth/AuthDialog'; -import { navigate } from '@reach/router'; +import { generateSkillManifest } from './generateSkillManifest'; +import { editorSteps, ManifestEditorSteps, order } from './constants'; +import { mergePropertiesManagedByRootBot } from '../../../recoilModel/dispatchers/utils/project'; +import { cloneDeep } from 'lodash'; interface ExportSkillModalProps { isOpen: boolean; @@ -68,6 +61,24 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const editorStep = order[currentStep]; const { buttons = [], content: Content, editJson, helpLink, subText, title, validate } = editorSteps[editorStep]; + const settings = useRecoilValue(settingsState(projectId)); + const rootBotProjectId = useRecoilValue(rootBotProjectIdSelector) || ''; + const mergedSettings = mergePropertiesManagedByRootBot(projectId, rootBotProjectId, settings); + const { skillConfiguration } = mergedSettings; + const { setSettings } = useRecoilValue(dispatcherState); + const [callers, setCallers] = useState(skillConfiguration?.allowedCallers ?? []); + + const updateAllowedCallers = React.useCallback( + (allowedCallers: string[] = []) => { + const updatedSetting = { + ...cloneDeep(mergedSettings), + skillConfiguration: { ...skillConfiguration, allowedCallers }, + }; + setSettings(projectId, updatedSetting); + }, + [mergedSettings, projectId, skillConfiguration] + ); + const handleGenerateManifest = () => { const manifest = generateSkillManifest( schema, @@ -94,24 +105,6 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const handleTriggerPublish = async () => { navigate(`/bot/${projectId}/publish/all`); - // get token - // setShowAuthDialog(true); - // let token = ''; - // // TODO: this logic needs to be moved into the Azure publish extensions - // if (isGetTokenFromUser()) { - // token = getTokenFromCache('accessToken'); - // } else { - // let tenant = getTenantIdFromCache(); - // if (!tenant) { - // const tenants = await AuthClient.getTenants(); - // tenant = tenants?.[0]?.tenantId; - // setTenantId(tenant); - // } - // token = await AuthClient.getARMTokenForTenant(tenant); - // } - // // const notification = createNotification(getPendingNotificationSkillCardProps()); - // // addNotification(notification); - // await publishToTarget(projectId, currentTarget, {}, null, token); }; const handleSave = () => { @@ -132,6 +125,10 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss } }; + const onSaveSkill = () => { + updateAllowedCallers(callers); + }; + const handleNext = (options?: { dismiss?: boolean; id?: string; save?: boolean }) => { const validated = typeof validate === 'function' ? validate({ content, editingId, id, schema, skillManifests }) : errors; @@ -188,6 +185,8 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss setSkillManifest={setSkillManifest} skillManifests={skillManifests} value={content} + callers={callers} + setCallers={setCallers} onChange={(manifestContent) => setSkillManifest({ ...skillManifest, content: manifestContent })} />
@@ -213,6 +212,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss onSave: handleSave, onPublish: handleTriggerPublish, onSubmit, + onSaveSkill, })} /> ); @@ -221,17 +221,6 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss {editJson && } - {/* {clickPublish && isShowAuthDialog(false) && ( - { - // setDialogHidden(false); - }} - onDismiss={() => { - setShowAuthDialog(false); - }} - /> - )} */} ); From 5426207fefdacbdde39e2d7b680fe13f2e18ee6e Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 31 Mar 2021 17:42:10 +0800 Subject: [PATCH 13/55] add manifest path to BotStructureFilesPatterns --- Composer/packages/server/src/models/bot/botStructure.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/Composer/packages/server/src/models/bot/botStructure.ts b/Composer/packages/server/src/models/bot/botStructure.ts index 608dcf516f..521b77bbdd 100644 --- a/Composer/packages/server/src/models/bot/botStructure.ts +++ b/Composer/packages/server/src/models/bot/botStructure.ts @@ -62,6 +62,7 @@ export const BotStructureFilesPatterns = [ templateInterpolate(BotStructureTemplate.dialogs.entry, { DIALOGNAME: '*' }), templateInterpolate(BotStructureTemplate.dialogs.dialogSchema, { DIALOGNAME: '*' }), templateInterpolate(BotStructureTemplate.dialogs.recognizer, { DIALOGNAME: '*', RECOGNIZERNAME: '*.dialog' }), + templateInterpolate(BotStructureTemplate.manifestLu, { FILENAME: '*' }), templateInterpolate(BotStructureTemplate.formDialogs, { FORMDIALOGNAME: '*.form' }), templateInterpolate(BotStructureTemplate.skillManifests, { MANIFESTFILENAME: '*.json' }), From 63a04c67f9e92fd34266f0883373a5fd055a8d66 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Wed, 31 Mar 2021 17:43:38 +0800 Subject: [PATCH 14/55] fix e2e --- .../client/src/pages/design/exportSkillModal/constants.tsx | 2 +- .../client/src/pages/design/exportSkillModal/index.tsx | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index a3beae3148..cf6d056b98 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -222,7 +222,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { text: () => formatMessage('Generate and Publish'), onClick: ({ generateManifest, onNext, onPublish }) => () => { generateManifest(); - onNext(); + onNext({ dismiss: true, save: true }); onPublish(); }, }, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 5909b3328d..0da903f76a 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -93,6 +93,9 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss projectId ); setSkillManifest(manifest); + if (manifest.content && manifest.id) { + updateSkillManifest(manifest as SkillManifestFile, projectId); + } }; const handleEditJson = () => { From 11be116e138eca4bd79e84b37e589eac1249b29e Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 31 Mar 2021 18:36:33 +0800 Subject: [PATCH 15/55] notification --- .../src/pages/publish/Notifications.tsx | 108 +++++++++++++----- .../client/src/pages/publish/Publish.tsx | 6 +- 2 files changed, 83 insertions(+), 31 deletions(-) diff --git a/Composer/packages/client/src/pages/publish/Notifications.tsx b/Composer/packages/client/src/pages/publish/Notifications.tsx index 1e9500c7a5..c3dcbc8202 100644 --- a/Composer/packages/client/src/pages/publish/Notifications.tsx +++ b/Composer/packages/client/src/pages/publish/Notifications.tsx @@ -9,6 +9,8 @@ import { Icon } from 'office-ui-fabric-react/lib/Icon'; import { CardProps } from '../../components/Notifications/NotificationCard'; import { BotStatus } from './type'; +import { IconButton } from 'office-ui-fabric-react/lib/Button'; +import { FontSizes } from '@uifabric/fluent-theme'; const cardContent = css` display: flex; @@ -56,11 +58,11 @@ export const getPublishedNotificationCardProps = (item: BotStatus): CardProps => name: item.name, publishTarget: item.publishTarget, }), - type: 'pending', + type: item.status === 200 ? 'success' : 'error', onRenderCardContent: (props) => (
- +
{props.description}
@@ -69,41 +71,91 @@ export const getPublishedNotificationCardProps = (item: BotStatus): CardProps => }; }; -export const getSkillPublishedNotificationCardProps = ( - item: BotStatus, - endpoint?: string, - manifestName?: string -): CardProps => { +export const getSkillPublishedNotificationCardProps = (item: BotStatus, url?: string): CardProps => { + const skillCardContent = css` + display: flex; + padding: 0 16px 16px 12px; + min-height: 64px; + `; const statusIconStyle = css({ margin: '12px 0 0 -1px', width: '12px', height: '12px', fontSize: '12px', - color: item.status === 200 ? '#27AE60' : 'rgb(161, 159, 157)', - transform: item.status !== 200 ? 'rotate(45deg)' : '', + color: '#D92525', }); + const errorType = css` + margin-top: 4px; + color: #d92525; + `; + const successType = css` + margin-top: 4px; + color: #27ae60; + `; + const cardTitle = css` + font-size: ${FontSizes.size16}; + lint-height: 22px; + margin-right: 16px; + `; + + const cardDescription = css` + text-size-adjust: none; + font-size: ${FontSizes.size10}; + margin-top: 8px; + margin-right: 16px; + word-break: break-word; + `; + + const linkButton = css` + color: #0078d4; + float: right; + font-size: 12px; + height: auto; + margin: 10px 8px 0 0; + `; return { - title: '', + title: item.status === 200 ? formatMessage('Your skill is ready to be shared!') : '', description: item.status === 200 - ? formatMessage(`You have successfully published {name} to {publishTarget}`, { - name: item.name, - publishTarget: item.publishTarget, - }) - : formatMessage(`Publishing {name} to {publishTarget} failed.`, { - name: item.name, - publishTarget: item.publishTarget, - }), - type: 'pending', - onRenderCardContent: (props) => ( -
- - -
-
{props.description}
-
-
- ), + ? formatMessage( + 'Keep this URL handy to share it with other developers to use in their bot projects. You can find this URL in the project settings tab.' + ) + : formatMessage(`Your skill could not be published.`), + type: item.status === 200 ? 'success' : 'error', + onRenderCardContent: (props) => { + const { title, description } = props; + if (item.status === 200) { + return ( +
+ +
+
{title}
+ {description &&
{description}
} + {url && ( +
+ {url} + navigator.clipboard.writeText(url)} + /> +
+ )} +
+
+ ); + } else { + return ( +
+ + +
+
{props.description}
+
+
+ ); + } + }, }; }; diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index 0ab8b7a3fe..4eef49cab0 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -45,7 +45,6 @@ import { generateBotStatusList, deleteNotificationInterval, } from './publishPageUtils'; -import { manifestUrl } from '../design/styles'; const SKILL_PUBLISH_STATUS = { INITIAL: 'inital', @@ -222,11 +221,12 @@ const Publish: React.FC { deleteNotification(resultNotification.id); showNotificationsRef.current = { ...displayedNotifications, [botProjectId]: false }; From 9a4ed9985b4dcf2b17e1764bfb5c2fefae2893b2 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Thu, 1 Apr 2021 10:00:07 +0800 Subject: [PATCH 16/55] add publish skill settings update --- extensions/azurePublish/src/node/deploy.ts | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/extensions/azurePublish/src/node/deploy.ts b/extensions/azurePublish/src/node/deploy.ts index bc57c5906f..eca8141c80 100644 --- a/extensions/azurePublish/src/node/deploy.ts +++ b/extensions/azurePublish/src/node/deploy.ts @@ -60,6 +60,9 @@ export class BotProjectDeploy { await this.BindKeyVault(absSettings, hostname); } + const skillManifestPath = path.join(this.projPath, 'ComposerDialogs', 'manifests'); + const msAppId = settings.MicrosoftAppId; + await this.updateSkillSettings(profileName, hostname, msAppId, skillManifestPath); // STEP 1: CLEAN UP PREVIOUS BUILDS // cleanup any previous build if (await fs.pathExists(this.zipPath)) { @@ -146,6 +149,44 @@ export class BotProjectDeploy { } } + /** + * update the skill related settings to skills' manifest + * @param hostname hostname of web app + * @param msAppId microsoft app id + * @param skillSettingsPath the path of skills manifest settings + */ + private async updateSkillSettings(profileName: string, hostname: string, msAppId: string, skillSettingsPath: string) { + const manifestFiles = (await fs.readdir(skillSettingsPath)).filter((x) => x.endsWith('.json')); + if (manifestFiles.length === 0) { + this.logger({ + status: BotProjectDeployLoggerType.DEPLOY_INFO, + message: `The manifest does not exsit on path: ${skillSettingsPath}`, + }); + return; + } + + for (const manifestFile of manifestFiles) { + const hostEndpoint = `https://${hostname}.azurewebsites.net/api/messages`; + + const manifest = await fs.readJson(path.join(skillSettingsPath, manifestFile)); + + const endpointIndex = manifest.endpoints.findIndex((x) => x.name === profileName); + if (endpointIndex > -1) { + // already exists + return; + } + manifest.endpoints.push({ + protocol: 'BotFrameworkV3', + name: profileName, + endpointUrl: hostEndpoint, + description: '', + msAppId: msAppId, + }); + + await fs.writeJson(path.join(skillSettingsPath, manifestFile), manifest); + } + } + private async zipDirectory(source: string, out: string) { try { const archive = archiver('zip', { zlib: { level: 9 } }); From 46e6817322d9f443ef98b13a90d54babe4f1c825 Mon Sep 17 00:00:00 2001 From: Long Alan Date: Thu, 1 Apr 2021 19:05:59 +0800 Subject: [PATCH 17/55] pop auth --- .../client/src/pages/publish/Publish.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index 4eef49cab0..2e306846bb 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -142,7 +142,7 @@ const Publish: React.FC bot.id === projectId); changePublishTarget(publishTargetName, currentBotStatus); setCheckedSkillIds([projectId]); - setPublishDialogVisiblity(true); + onPublish(); } }, [publishTargetName, botStatusList]); @@ -267,6 +267,15 @@ const Publish: React.FC { + if (isShowAuthDialog(false)) { + setShowAuthDialog(true); + } else { + setPublishDialogVisiblity(true); + } + TelemetryClient.track('ToolbarButtonClicked', { name: 'publishSelectedBots' }); + }; + const publish = async (items: BotStatus[]) => { const tenantTokenMap = new Map(); // get token @@ -431,14 +440,7 @@ const Publish: React.FC { - if (isShowAuthDialog(false)) { - setShowAuthDialog(true); - } else { - setPublishDialogVisiblity(true); - } - TelemetryClient.track('ToolbarButtonClicked', { name: 'publishSelectedBots' }); - }} + onPublish={onPublish} onPull={() => { setPullDialogVisiblity(true); TelemetryClient.track('ToolbarButtonClicked', { name: 'pullFromProfile' }); From 87261fb4e2f944caf9bb600ecbe920d21fe2dd28 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Fri, 2 Apr 2021 11:27:28 +0800 Subject: [PATCH 18/55] add skill hostendpoint --- extensions/azurePublish/src/node/deploy.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/extensions/azurePublish/src/node/deploy.ts b/extensions/azurePublish/src/node/deploy.ts index eca8141c80..d3f7b25187 100644 --- a/extensions/azurePublish/src/node/deploy.ts +++ b/extensions/azurePublish/src/node/deploy.ts @@ -62,7 +62,7 @@ export class BotProjectDeploy { const skillManifestPath = path.join(this.projPath, 'ComposerDialogs', 'manifests'); const msAppId = settings.MicrosoftAppId; - await this.updateSkillSettings(profileName, hostname, msAppId, skillManifestPath); + await this.updateSkillSettings(profileName, hostname, msAppId, skillManifestPath, settings); // STEP 1: CLEAN UP PREVIOUS BUILDS // cleanup any previous build if (await fs.pathExists(this.zipPath)) { @@ -155,7 +155,13 @@ export class BotProjectDeploy { * @param msAppId microsoft app id * @param skillSettingsPath the path of skills manifest settings */ - private async updateSkillSettings(profileName: string, hostname: string, msAppId: string, skillSettingsPath: string) { + private async updateSkillSettings( + profileName: string, + hostname: string, + msAppId: string, + skillSettingsPath: string, + settings: any + ) { const manifestFiles = (await fs.readdir(skillSettingsPath)).filter((x) => x.endsWith('.json')); if (manifestFiles.length === 0) { this.logger({ @@ -184,6 +190,11 @@ export class BotProjectDeploy { }); await fs.writeJson(path.join(skillSettingsPath, manifestFile), manifest); + + // update skill host endpoint + if (settings.skillHostEndpoint) { + settings.skillHostEndpoint = `https://${hostname}.azurewebsites.net/api/skills`; + } } } From 62b2be590f3552b6463d2714b7171fe1b3b2fa44 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Tue, 6 Apr 2021 10:41:36 +0800 Subject: [PATCH 19/55] fix comments --- .../src/pages/design/exportSkillModal/constants.tsx | 3 --- .../design/exportSkillModal/content/AddCallers.tsx | 4 ++-- .../design/exportSkillModal/content/SelectProfile.tsx | 10 ++++++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index cf6d056b98..5bcb31744f 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -131,7 +131,6 @@ interface EditorStep { } export enum ManifestEditorSteps { - // ENDPOINTS = 'ENDPOINTS', FETCH_MANIFEST_SCHEMA = 'FETCH_MANIFEST_SCHEMA', MANIFEST_DESCRIPTION = 'MANIFEST_DESCRIPTION', MANIFEST_REVIEW = 'MANIFEST_REVIEW', @@ -151,8 +150,6 @@ export const order: ManifestEditorSteps[] = [ ManifestEditorSteps.SELECT_TRIGGERS, ManifestEditorSteps.ADD_CALLERS, ManifestEditorSteps.SELECT_PROFILE, - // ManifestEditorSteps.MANIFEST_REVIEW, - // ManifestEditorSteps.SAVE_MANIFEST, ]; const cancelButton: Button = { diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx index d02831cc9e..6c81d42d03 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx @@ -60,7 +60,7 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall { - var currentCallers = callers.slice(); + const currentCallers = callers.slice(); currentCallers[index] = newValue ?? ''; setCallers(currentCallers); }} @@ -82,7 +82,7 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall handleAdd(); }} > - {formatMessage('Add allowd callers')} + {formatMessage('Add allowed callers')}
); diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index 0f47a079b7..d3f09d25a6 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -37,8 +37,14 @@ const onRenderLabel = (props) => { fontSize: '14px', }} > - {' '} - {props.label}{' '} +
+ {props.label} +
From f03067ad4ed93844ecaf1b13dde761a3da199bdc Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 7 Apr 2021 11:06:21 +0800 Subject: [PATCH 20/55] title change --- .../client/src/components/ProjectTree/ProjectHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/components/ProjectTree/ProjectHeader.tsx b/Composer/packages/client/src/components/ProjectTree/ProjectHeader.tsx index 8318f52085..e9cf7ce6ee 100644 --- a/Composer/packages/client/src/components/ProjectTree/ProjectHeader.tsx +++ b/Composer/packages/client/src/components/ProjectTree/ProjectHeader.tsx @@ -118,7 +118,7 @@ export const ProjectHeader = (props: ProjectHeaderProps) => { onClick: () => {}, }, { - label: formatMessage('Create/edit skill manifest'), + label: formatMessage('Share as a skill'), onClick: () => { onBotEditManifest(projectId); }, From ebbcd1c3915c5a3f282a6224e2fbbd0a2361ef1c Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 7 Apr 2021 15:20:01 +0800 Subject: [PATCH 21/55] fix copy url wrong --- .../src/pages/publish/BotStatusList.tsx | 22 +++---------------- .../src/pages/publish/publishPageUtils.tsx | 10 ++++++--- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/Composer/packages/client/src/pages/publish/BotStatusList.tsx b/Composer/packages/client/src/pages/publish/BotStatusList.tsx index 443ad91eab..6ace4c58af 100644 --- a/Composer/packages/client/src/pages/publish/BotStatusList.tsx +++ b/Composer/packages/client/src/pages/publish/BotStatusList.tsx @@ -49,20 +49,10 @@ export const BotStatusList: React.FC = ({ }) => { const [expandedBotIds, setExpandedBotIds] = useState>({}); const [currentSort, setSort] = useState({ key: 'Bot', descending: true }); - const [clipboardText, setClipboardText] = useState(''); - const clipboardTextFieldRef = useRef(null); const copyStringToClipboard = (value?: string) => { try { - if (clipboardTextFieldRef.current) { - setClipboardText(value || ''); - setTimeout(() => { - if (clipboardTextFieldRef.current) { - clipboardTextFieldRef.current.select(); - document.execCommand('copy'); - } - }, 10); - } + value && navigator.clipboard.writeText(value); } catch (e) { // eslint-disable-next-line no-console console.error('Something went wrong when copying to the clipboard.', e, location); @@ -270,8 +260,8 @@ export const BotStatusList: React.FC = ({ name: '', className: 'skillManifest', fieldName: 'skillManifestUrl', - minWidth: 114, - maxWidth: 134, + minWidth: 134, + maxWidth: 150, data: 'string', onRender: (item: BotStatus) => { return ( @@ -366,12 +356,6 @@ export const BotStatusList: React.FC = ({ onRenderRow={renderTableRow} /> - ); }; diff --git a/Composer/packages/client/src/pages/publish/publishPageUtils.tsx b/Composer/packages/client/src/pages/publish/publishPageUtils.tsx index fa53f1c251..abd565e6a0 100644 --- a/Composer/packages/client/src/pages/publish/publishPageUtils.tsx +++ b/Composer/packages/client/src/pages/publish/publishPageUtils.tsx @@ -31,11 +31,11 @@ export const generateBotPropertyData = (botProjectData: BotProjectType[]) => { return { botPropertyData, botList }; }; -const findSkillManifestUrl = (skillManifests: SkillManifestFile[], appId: string) => { +const findSkillManifestUrl = (skillManifests: SkillManifestFile[], hostname: string, appId: string) => { for (const skillManifest of skillManifests || []) { for (const endpoint of skillManifest?.content?.endpoints || []) { if (endpoint?.msAppId === appId) { - return endpoint?.endpointUrl; + return `https://${hostname}.azurewebsites.net/manifests/${skillManifest.id}.json`; } } } @@ -67,7 +67,11 @@ export const generateBotStatusList = ( const config = JSON.parse(currentPublishTarget.configuration); const appId = config?.settings?.MicrosoftAppId; if (appId) { - botStatus.skillManifestUrl = findSkillManifestUrl(botPropertyData[bot.id].skillManifests, appId); + botStatus.skillManifestUrl = findSkillManifestUrl( + botPropertyData[bot.id].skillManifests, + config.hostname, + appId + ); } } } From 98e3d5caa0f8a41c1da788c231a015467d812060 Mon Sep 17 00:00:00 2001 From: Qi Kang Date: Wed, 7 Apr 2021 17:10:34 +0800 Subject: [PATCH 22/55] fix filtered lu files --- .../src/pages/design/exportSkillModal/generateSkillManifest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts index 406a08129d..6984908364 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/generateSkillManifest.ts @@ -146,7 +146,7 @@ export const generateDispatchModels = ( const currentFileName = `skill-${rootLuFile.id}`; const parsedLuFile = luIndexer.parse(rootLuFile.content, rootLuFile.id, {}); const contents = parsedLuFile.intents.map((x) => { - if (intents.find((intent) => intent == x.Name) != -1) { + if (intents.findIndex((intent) => intent == x.Name) !== -1) { return [`# ${x.Name}`, x.Body].join('\n'); } }); From 4b1f1286f6193a4d998c261ffaa89d7e605a7507 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 7 Apr 2021 17:18:58 +0800 Subject: [PATCH 23/55] merge selectManifest & fetchManifest & description popup --- .../design/exportSkillModal/constants.tsx | 6 +- .../exportSkillModal/content/Description.tsx | 158 +++++++++++++++--- .../pages/design/exportSkillModal/index.tsx | 5 +- 3 files changed, 134 insertions(+), 35 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index 5bcb31744f..602956daad 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -3,7 +3,6 @@ import formatMessage from 'format-message'; import { JSONSchema7 } from '@bfc/extension-client'; -import { resolveRef } from '@bfc/adaptive-form'; import { SkillManifestFile } from '@bfc/shared'; import startCase from 'lodash/startCase'; import { SDKKinds } from '@bfc/shared'; @@ -12,7 +11,6 @@ import { nameRegex } from '../../../constants'; import { Description, - Endpoints, FetchManifestSchema, ReviewManifest, SaveManifest, @@ -143,8 +141,8 @@ export enum ManifestEditorSteps { } export const order: ManifestEditorSteps[] = [ - ManifestEditorSteps.SELECT_MANIFEST, - ManifestEditorSteps.FETCH_MANIFEST_SCHEMA, + // ManifestEditorSteps.SELECT_MANIFEST, + // ManifestEditorSteps.FETCH_MANIFEST_SCHEMA, ManifestEditorSteps.MANIFEST_DESCRIPTION, ManifestEditorSteps.SELECT_DIALOGS, ManifestEditorSteps.SELECT_TRIGGERS, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx index 5ff2dc68d4..e56c2ca10b 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx @@ -3,7 +3,7 @@ /** @jsx jsx */ import { css, jsx } from '@emotion/core'; -import React, { useMemo, useEffect } from 'react'; +import React, { useMemo, useEffect, Fragment, useState } from 'react'; import AdaptiveForm, { FieldLabel } from '@bfc/adaptive-form'; import { FieldProps, JSONSchema7, UIOptions } from '@bfc/extension-client'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; @@ -12,6 +12,17 @@ import { v4 as uuid } from 'uuid'; import { ContentProps } from '../constants'; import { botDisplayNameState } from '../../../../recoilModel'; +import { Label } from 'office-ui-fabric-react/lib/Label'; +import formatMessage from 'format-message'; +import { Dropdown, IDropdownOption, ResponsiveMode } from 'office-ui-fabric-react/lib/Dropdown'; +import { LoadingSpinner } from '@bfc/ui-shared/lib/components/LoadingSpinner'; + +export const VERSION_REGEX = /\d\.\d+\.(\d+|preview-\d+)|\d\.\d+/i; + +export const SCHEMA_URIS = [ + 'https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json', + 'https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json', +]; const styles = { row: css` @@ -25,6 +36,12 @@ const styles = { `, }; +const chooseVersion = css` + display: flex; + width: 72%; + margin: 10px 18px; + justify-content: space-between; +`; const InlineLabelField: React.FC = (props) => { const { id, placeholder, rawErrors, value = '', onChange } = props; @@ -50,39 +67,86 @@ const InlineLabelField: React.FC = (props) => { ); }; -export const Description: React.FC = ({ errors, value, schema, onChange, projectId }) => { +export const Description: React.FC = ({ + errors, + value, + schema, + skillManifests, + onChange, + projectId, + setSchema, + setSkillManifest, + editJson, +}) => { const botName = useRecoilValue(botDisplayNameState(projectId)); + const [isFetchCompleted, setIsFetchCompleted] = useState(false); const { $schema, ...rest } = value; - const { hidden, properties } = useMemo( + const { hidden, properties } = useMemo(() => { + if (!schema.properties) return { hidden: [], properties: {} } as any; + return Object.entries(schema.properties as JSONSchema7).reduce( + ({ hidden, properties }, [key, property]) => { + if (property.type === 'object' || (property.type === 'array' && property?.items?.type !== 'string')) { + return { hidden: [...hidden, key], properties }; + } + + const itemSchema = property?.items as JSONSchema7; + const serializer = + itemSchema?.type === 'string' + ? { + get: (value) => (Array.isArray(value) ? value.join(',') : value), + set: (value) => (typeof value === 'string' ? value.split(/\s*,\s*/) : value), + } + : null; + + return { + hidden, + properties: { ...properties, [key]: { field: InlineLabelField, hideError: true, serializer } }, + }; + }, + { hidden: [], properties: {} } as any + ); + }, [schema]); + + const options: IDropdownOption[] = useMemo( () => - Object.entries(schema?.properties as JSONSchema7).reduce( - ({ hidden, properties }, [key, property]) => { - if (property.type === 'object' || (property.type === 'array' && property?.items?.type !== 'string')) { - return { hidden: [...hidden, key], properties }; - } - - const itemSchema = property?.items as JSONSchema7; - const serializer = - itemSchema?.type === 'string' - ? { - get: (value) => (Array.isArray(value) ? value.join(',') : value), - set: (value) => (typeof value === 'string' ? value.split(/\s*,\s*/) : value), - } - : null; - - return { - hidden, - properties: { ...properties, [key]: { field: InlineLabelField, hideError: true, serializer } }, - }; - }, - { hidden: [], properties: {} } as any - ), + SCHEMA_URIS.map((key, index) => { + const [version] = VERSION_REGEX.exec(key) || []; + + return { + text: formatMessage('Version {version}', { version }), + key, + selected: !index, + }; + }), [] ); useEffect(() => { - if (!value.$id) { + if (!$schema) { + const skillManifest = skillManifests.find((manifest) => manifest.content.$schema === SCHEMA_URIS[0]) || { + content: { $schema: SCHEMA_URIS[0] }, + }; + setSkillManifest(skillManifest); + } + (async function () { + try { + if ($schema) { + const res = await fetch($schema); + const schema = await res.json(); + setSchema(schema); + setIsFetchCompleted(true); + } else { + editJson(); + } + } catch (error) { + editJson(); + } + })(); + }, [$schema]); + + useEffect(() => { + if (!value.$id && value.$schema) { onChange({ $schema, $id: `${botName}-${uuid()}`, endpoints: [{}], name: botName, ...rest }); } }, []); @@ -96,5 +160,45 @@ export const Description: React.FC = ({ errors, value, schema, onC properties, }; - return ; + const handleChange = (_e: React.FormEvent, option?: IDropdownOption) => { + if (option) { + const skillManifest = skillManifests.find((manifest) => manifest.content.$schema === option.key) || { + content: { $schema: option.key as string }, + }; + setIsFetchCompleted(false); + setSkillManifest(skillManifest); + } + }; + + return ( + +
+ + +
+ {isFetchCompleted ? ( + + ) : ( + + )} +
+ ); }; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index b449c6b28b..c599fc269c 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -47,7 +47,6 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const skillManifests = useRecoilValue(skillManifestsState(projectId)); const { updateSkillManifest } = useRecoilValue(dispatcherState); - const [editingId, setEditingId] = useState(); const [currentStep, setCurrentStep] = useState(0); const [errors, setErrors] = useState({}); const [schema, setSchema] = useState({}); @@ -136,13 +135,11 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss }; const handleNext = (options?: { dismiss?: boolean; id?: string; save?: boolean }) => { - const validated = - typeof validate === 'function' ? validate({ content, editingId, id, schema, skillManifests }) : errors; + const validated = typeof validate === 'function' ? validate({ content, id, schema, skillManifests }) : errors; if (!Object.keys(validated).length) { setCurrentStep((current) => (current + 1 < order.length ? current + 1 : current)); options?.save && handleSave(); - options?.id && setEditingId(options.id); options?.dismiss && handleDismiss(); setErrors({}); } else { From 7a23ae9fefac6103f4675037efa22db6d7cc7f87 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Wed, 7 Apr 2021 18:42:01 +0800 Subject: [PATCH 24/55] next bugfix --- .../exportSkillModal/content/Description.tsx | 26 +++++++++---------- .../pages/design/exportSkillModal/index.tsx | 9 +++++-- .../pages/design/exportSkillModal/styles.ts | 8 +++--- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx index e56c2ca10b..a84976d64c 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx @@ -80,7 +80,7 @@ export const Description: React.FC = ({ }) => { const botName = useRecoilValue(botDisplayNameState(projectId)); const [isFetchCompleted, setIsFetchCompleted] = useState(false); - const { $schema, ...rest } = value; + const { $id, $schema, ...rest } = value; const { hidden, properties } = useMemo(() => { if (!schema.properties) return { hidden: [], properties: {} } as any; @@ -123,12 +123,18 @@ export const Description: React.FC = ({ ); useEffect(() => { - if (!$schema) { - const skillManifest = skillManifests.find((manifest) => manifest.content.$schema === SCHEMA_URIS[0]) || { - content: { $schema: SCHEMA_URIS[0] }, - }; - setSkillManifest(skillManifest); - } + const skillManifest = skillManifests.find( + (manifest) => manifest.content.$schema === ($schema || SCHEMA_URIS[0]) + ) || { + content: { + $schema: $schema || SCHEMA_URIS[0], + $id: `${botName}-${uuid()}`, + endpoints: [{}], + name: botName, + ...rest, + }, + }; + setSkillManifest(skillManifest); (async function () { try { if ($schema) { @@ -145,12 +151,6 @@ export const Description: React.FC = ({ })(); }, [$schema]); - useEffect(() => { - if (!value.$id && value.$schema) { - onChange({ $schema, $id: `${botName}-${uuid()}`, endpoints: [{}], name: botName, ...rest }); - } - }, []); - const required = schema?.required || []; const uiOptions: UIOptions = { diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index c599fc269c..bbeaca8d82 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -3,7 +3,7 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import React, { useEffect, useMemo, useRef, useState } from 'react'; +import React, { useState } from 'react'; import formatMessage from 'format-message'; import { Dialog, DialogFooter, DialogType } from 'office-ui-fabric-react/lib/Dialog'; import { DefaultButton, PrimaryButton } from 'office-ui-fabric-react/lib/Button'; @@ -173,7 +173,12 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss )}

-
+
Date: Thu, 8 Apr 2021 15:49:32 +0800 Subject: [PATCH 25/55] bugfix --- .../exportSkillModal/content/Description.tsx | 9 +-------- .../exportSkillModal/content/SelectItems.tsx | 2 +- .../src/pages/design/exportSkillModal/index.tsx | 15 +++++++++++++-- .../packages/client/src/pages/publish/Publish.tsx | 1 + 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx index a84976d64c..63ef959138 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx @@ -10,20 +10,13 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; import { v4 as uuid } from 'uuid'; -import { ContentProps } from '../constants'; +import { ContentProps, SCHEMA_URIS, VERSION_REGEX } from '../constants'; import { botDisplayNameState } from '../../../../recoilModel'; import { Label } from 'office-ui-fabric-react/lib/Label'; import formatMessage from 'format-message'; import { Dropdown, IDropdownOption, ResponsiveMode } from 'office-ui-fabric-react/lib/Dropdown'; import { LoadingSpinner } from '@bfc/ui-shared/lib/components/LoadingSpinner'; -export const VERSION_REGEX = /\d\.\d+\.(\d+|preview-\d+)|\d\.\d+/i; - -export const SCHEMA_URIS = [ - 'https://schemas.botframework.com/schemas/skills/v2.1/skill-manifest.json', - 'https://schemas.botframework.com/schemas/skills/skill-manifest-2.0.0.json', -]; - const styles = { row: css` display: flex; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectItems.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectItems.tsx index 1e69515a6c..6584e61922 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectItems.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectItems.tsx @@ -77,7 +77,7 @@ export const SelectItems: React.FC = ({ items, selection, tabl diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index bbeaca8d82..2895c1400a 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -27,7 +27,7 @@ import { import { styles } from './styles'; import { generateSkillManifest } from './generateSkillManifest'; -import { editorSteps, ManifestEditorSteps, order } from './constants'; +import { editorSteps, ManifestEditorSteps, order, VERSION_REGEX } from './constants'; import { mergePropertiesManagedByRootBot } from '../../../recoilModel/dispatchers/utils/project'; import { cloneDeep } from 'lodash'; @@ -138,7 +138,18 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const validated = typeof validate === 'function' ? validate({ content, id, schema, skillManifests }) : errors; if (!Object.keys(validated).length) { - setCurrentStep((current) => (current + 1 < order.length ? current + 1 : current)); + setCurrentStep((current) => { + if (current === 1) { + const [version] = VERSION_REGEX.exec(content.$schema) || []; + if (current + 1 === order.length) { + return current; + } else { + return version === '2.0.0' ? current + 2 : current + 1; + } + } else { + return current + 1 < order.length ? current + 1 : current; + } + }); options?.save && handleSave(); options?.dismiss && handleDismiss(); setErrors({}); diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index b60ebe5e08..4b0fed67d6 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -143,6 +143,7 @@ const Publish: React.FC Date: Thu, 8 Apr 2021 17:08:35 +0800 Subject: [PATCH 26/55] merge main & popup err --- .../client/src/pages/design/DebugPanel/TabExtensions/index.ts | 2 +- Composer/packages/client/src/pages/publish/Publish.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts index f17f2ca037..8d33f1ae8c 100644 --- a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts +++ b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts @@ -3,7 +3,7 @@ import { TabExtensionConfig } from './types'; import { DiagnosticsTabConfig } from './DiagnosticsTab'; -import { WebChatLogTabConfig } from './WebChatLog/config'; +import { WebChatLogTabConfig } from './WebchatLog/config'; import { RuntimeOutputTabConfig } from './RuntimeOutputLog'; const implementedDebugExtensions: TabExtensionConfig[] = [ diff --git a/Composer/packages/client/src/pages/publish/Publish.tsx b/Composer/packages/client/src/pages/publish/Publish.tsx index 7925fba78f..a7af062450 100644 --- a/Composer/packages/client/src/pages/publish/Publish.tsx +++ b/Composer/packages/client/src/pages/publish/Publish.tsx @@ -4,7 +4,7 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; import { useState, useEffect, useMemo, Fragment, useRef } from 'react'; -import { RouteComponentProps } from '@reach/router'; +import { navigate, RouteComponentProps } from '@reach/router'; import formatMessage from 'format-message'; import { useRecoilValue } from 'recoil'; import { PublishResult, PublishTarget } from '@bfc/shared'; @@ -149,7 +149,7 @@ const Publish: React.FC Date: Fri, 9 Apr 2021 09:33:59 +0800 Subject: [PATCH 27/55] Fix endpoint not update issue --- extensions/azurePublish/src/node/deploy.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/extensions/azurePublish/src/node/deploy.ts b/extensions/azurePublish/src/node/deploy.ts index 5721ef1832..f3f0c1f298 100644 --- a/extensions/azurePublish/src/node/deploy.ts +++ b/extensions/azurePublish/src/node/deploy.ts @@ -60,9 +60,11 @@ export class BotProjectDeploy { await this.BindKeyVault(absSettings, hostname); } - const skillManifestPath = path.join(this.projPath, 'ComposerDialogs', 'manifests'); - const msAppId = settings.MicrosoftAppId; - await this.updateSkillSettings(profileName, hostname, msAppId, skillManifestPath, settings); + const skillManifestPath = path.join(this.projPath, 'wwwroot', 'manifests'); + if (await fs.pathExists(skillManifestPath)) { + await this.updateSkillSettings(profileName, hostname, settings.MicrosoftAppId, skillManifestPath, settings); + } + // STEP 1: CLEAN UP PREVIOUS BUILDS // cleanup any previous build if (await fs.pathExists(this.zipPath)) { @@ -171,6 +173,11 @@ export class BotProjectDeploy { return; } + // update skill host endpoint + if (settings.skillHostEndpoint) { + settings.skillHostEndpoint = `https://${hostname}.azurewebsites.net/api/skills`; + } + for (const manifestFile of manifestFiles) { const hostEndpoint = `https://${hostname}.azurewebsites.net/api/messages`; @@ -190,11 +197,6 @@ export class BotProjectDeploy { }); await fs.writeJson(path.join(skillSettingsPath, manifestFile), manifest); - - // update skill host endpoint - if (settings.skillHostEndpoint) { - settings.skillHostEndpoint = `https://${hostname}.azurewebsites.net/api/skills`; - } } } From d3a03969cf0aee720f065c2c204f7eb91742dba2 Mon Sep 17 00:00:00 2001 From: Lu Han <32191031+luhan2017@users.noreply.github.com> Date: Fri, 9 Apr 2021 10:24:40 +0800 Subject: [PATCH 28/55] Fix new adaptive runtime copy skill manifests to wwwroot folder --- extensions/azurePublish/src/node/index.ts | 11 +++++++++++ extensions/runtimes/src/index.ts | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/extensions/azurePublish/src/node/index.ts b/extensions/azurePublish/src/node/index.ts index 87543cee36..c4076eef2c 100644 --- a/extensions/azurePublish/src/node/index.ts +++ b/extensions/azurePublish/src/node/index.ts @@ -206,6 +206,17 @@ export default async (composer: IExtensionRegistration): Promise => { // copy bot and runtime into projFolder await copy(srcTemplate, buildFolder); + + let manifestPath; + for (const file of botFiles) { + const pattern = /manifests\/[0-9A-z-]*.json/; + if (file.relativePath.match(pattern)) { + manifestPath = path.dirname(file.path); + } + } + + // save manifest + runtime.setSkillManifest(buildFolder, project.fileStorage, manifestPath, project.fileStorage, mode); } else { const botFolder = this.getBotFolder(resourcekey, mode); const runtimeFolder = this.getRuntimeFolder(resourcekey); diff --git a/extensions/runtimes/src/index.ts b/extensions/runtimes/src/index.ts index 2b91b0b996..a42cacd27a 100644 --- a/extensions/runtimes/src/index.ts +++ b/extensions/runtimes/src/index.ts @@ -450,7 +450,7 @@ export default async (composer: any): Promise => { ) => { // update manifst into runtime wwwroot if (mode === 'azurewebapp') { - const manifestDstDir = path.resolve(dstRuntimePath, 'azurewebapp', 'wwwroot', 'manifests'); + const manifestDstDir = path.resolve(dstRuntimePath, 'wwwroot', 'manifests'); if (await fs.pathExists(manifestDstDir)) { await removeDirAndFiles(manifestDstDir); From 4ba00b2cbdff1575b97c342c05cef4fac3ec93b7 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Fri, 9 Apr 2021 11:19:29 +0800 Subject: [PATCH 29/55] skip step revert --- .../pages/design/DebugPanel/TabExtensions/index.ts | 2 +- .../src/pages/design/exportSkillModal/index.tsx | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts index 8d33f1ae8c..f17f2ca037 100644 --- a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts +++ b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts @@ -3,7 +3,7 @@ import { TabExtensionConfig } from './types'; import { DiagnosticsTabConfig } from './DiagnosticsTab'; -import { WebChatLogTabConfig } from './WebchatLog/config'; +import { WebChatLogTabConfig } from './WebChatLog/config'; import { RuntimeOutputTabConfig } from './RuntimeOutputLog'; const implementedDebugExtensions: TabExtensionConfig[] = [ diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 2895c1400a..39b089e145 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -138,18 +138,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const validated = typeof validate === 'function' ? validate({ content, id, schema, skillManifests }) : errors; if (!Object.keys(validated).length) { - setCurrentStep((current) => { - if (current === 1) { - const [version] = VERSION_REGEX.exec(content.$schema) || []; - if (current + 1 === order.length) { - return current; - } else { - return version === '2.0.0' ? current + 2 : current + 1; - } - } else { - return current + 1 < order.length ? current + 1 : current; - } - }); + setCurrentStep((current) => (current + 1 < order.length ? current + 1 : current)); options?.save && handleSave(); options?.dismiss && handleDismiss(); setErrors({}); From b57aac146d4cf97eba7d37d9f517ff9837bbefaf Mon Sep 17 00:00:00 2001 From: Long Jun Date: Fri, 9 Apr 2021 15:09:52 +0800 Subject: [PATCH 30/55] tooltip --- .../src/pages/publish/BotStatusList.tsx | 37 ++++++++++++------- .../src/pages/publish/publishPageUtils.tsx | 11 +++--- .../packages/client/src/pages/publish/type.ts | 2 +- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/Composer/packages/client/src/pages/publish/BotStatusList.tsx b/Composer/packages/client/src/pages/publish/BotStatusList.tsx index e672837745..ac3c0fc91a 100644 --- a/Composer/packages/client/src/pages/publish/BotStatusList.tsx +++ b/Composer/packages/client/src/pages/publish/BotStatusList.tsx @@ -24,6 +24,8 @@ import { ApiStatus } from '../../utils/publishStatusPollingUpdater'; import { PublishStatusList } from './PublishStatusList'; import { detailList, listRoot, tableView } from './styles'; import { BotPublishHistory, BotStatus } from './type'; +import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; +import { flexContentSpaceBetween } from '../language-understanding/styles'; const copiedCalloutStyles = { root: { @@ -32,25 +34,34 @@ const copiedCalloutStyles = { }; type SkillManifestUrlFieldProps = { - url: string; + urls: string[]; }; -const SkillManifestUrlField = ({ url }: SkillManifestUrlFieldProps) => { - const { isCopiedToClipboard, copyTextToClipboard, resetIsCopiedToClipboard } = useCopyToClipboard(url); +const SkillManifestUrlField = ({ urls }: SkillManifestUrlFieldProps) => { + const { isCopiedToClipboard, copyTextToClipboard, resetIsCopiedToClipboard } = useCopyToClipboard(urls[0]); const calloutTarget = useRef(); return ( - { - calloutTarget.current = e.target as HTMLElement; - copyTextToClipboard(); - }} + ( +
+ {url} + navigator.clipboard.writeText(url)} /> +
+ ))} > - {formatMessage('Copy Skill Manifest URL')} -
+ { + calloutTarget.current = e.target as HTMLElement; + copyTextToClipboard(); + }} + > + {formatMessage('Copy Skill Manifest URL')} + + {isCopiedToClipboard && ( = ({ maxWidth: 150, data: 'string', onRender: (item: BotStatus) => { - return item?.skillManifestUrl && ; + return item?.skillManifestUrls.length > 0 && ; }, isPadded: true, }, diff --git a/Composer/packages/client/src/pages/publish/publishPageUtils.tsx b/Composer/packages/client/src/pages/publish/publishPageUtils.tsx index abd565e6a0..75f00d442b 100644 --- a/Composer/packages/client/src/pages/publish/publishPageUtils.tsx +++ b/Composer/packages/client/src/pages/publish/publishPageUtils.tsx @@ -31,16 +31,17 @@ export const generateBotPropertyData = (botProjectData: BotProjectType[]) => { return { botPropertyData, botList }; }; -const findSkillManifestUrl = (skillManifests: SkillManifestFile[], hostname: string, appId: string) => { +const findSkillManifestUrl = (skillManifests: SkillManifestFile[], hostname: string, appId: string): string[] => { + const urls: string[] = []; for (const skillManifest of skillManifests || []) { for (const endpoint of skillManifest?.content?.endpoints || []) { if (endpoint?.msAppId === appId) { - return `https://${hostname}.azurewebsites.net/manifests/${skillManifest.id}.json`; + urls.push(`https://${hostname}.azurewebsites.net/manifests/${skillManifest.id}.json`); } } } - return undefined; + return urls; }; export const generateBotStatusList = ( @@ -49,7 +50,7 @@ export const generateBotStatusList = ( botPublishHistoryList: BotPublishHistory ): BotStatus[] => { const bots = botList.map((bot) => { - const botStatus: BotStatus = Object.assign({}, bot); + const botStatus: BotStatus = Object.assign({ skillManifestUrls: [] }, bot); const publishTargets: PublishTarget[] = botPropertyData[bot.id].publishTargets; const publishHistory = botPublishHistoryList[bot.id]; if (publishTargets.length > 0 && botStatus.publishTarget && publishHistory) { @@ -67,7 +68,7 @@ export const generateBotStatusList = ( const config = JSON.parse(currentPublishTarget.configuration); const appId = config?.settings?.MicrosoftAppId; if (appId) { - botStatus.skillManifestUrl = findSkillManifestUrl( + botStatus.skillManifestUrls = findSkillManifestUrl( botPropertyData[bot.id].skillManifests, config.hostname, appId diff --git a/Composer/packages/client/src/pages/publish/type.ts b/Composer/packages/client/src/pages/publish/type.ts index bb537f8e01..da3041d94d 100644 --- a/Composer/packages/client/src/pages/publish/type.ts +++ b/Composer/packages/client/src/pages/publish/type.ts @@ -19,7 +19,7 @@ export type BotStatus = { /** * The skill manifest URL associated with the current publishTarget. */ - skillManifestUrl?: string; + skillManifestUrls: string[]; }; export type Bot = { From 52f06ac8b8871f8d2f6b674f40273489a3eabd7d Mon Sep 17 00:00:00 2001 From: Long Jun Date: Fri, 9 Apr 2021 16:59:04 +0800 Subject: [PATCH 31/55] add back button & fix url color --- .../design/exportSkillModal/constants.tsx | 14 +++++++++++ .../exportSkillModal/content/Description.tsx | 9 +++++-- .../content/SelectDialogs.tsx | 24 +++++++++---------- .../content/SelectTriggers.tsx | 23 +++++++++--------- .../pages/design/exportSkillModal/index.tsx | 7 ++++++ .../src/pages/publish/BotStatusList.tsx | 5 ++-- .../src/pages/publish/Notifications.tsx | 4 ++-- 7 files changed, 57 insertions(+), 29 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index 602956daad..d952af7b83 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -95,6 +95,8 @@ export interface ContentProps { setSelectedTriggers: (selectedTriggers: any[]) => void; setSkillManifest: (_: Partial) => void; schema: JSONSchema7; + selectedDialogs: any[]; + selectedTriggers: any[]; skillManifests: SkillManifestFile[]; value: { [key: string]: any }; onChange: (_: any) => void; @@ -161,6 +163,12 @@ const nextButton: Button = { onClick: ({ onNext }) => onNext, }; +const backButton: Button = { + primary: true, + text: () => formatMessage('Back'), + onClick: ({ onBack }) => onBack, +}; + const validate = ({ content, schema }) => { const required = schema?.required || []; @@ -212,6 +220,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.SELECT_PROFILE]: { buttons: [ cancelButton, + backButton, { primary: true, text: () => formatMessage('Generate and Publish'), @@ -231,6 +240,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.ADD_CALLERS]: { buttons: [ cancelButton, + backButton, { primary: true, text: () => formatMessage('Next'), @@ -251,6 +261,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.MANIFEST_REVIEW]: { buttons: [ cancelButton, + backButton, { primary: true, text: () => formatMessage('Next'), @@ -264,6 +275,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.SELECT_DIALOGS]: { buttons: [ cancelButton, + backButton, { primary: true, text: () => formatMessage('Next'), @@ -281,6 +293,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.SELECT_TRIGGERS]: { buttons: [ cancelButton, + backButton, { primary: true, text: () => formatMessage('Next'), @@ -301,6 +314,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { [ManifestEditorSteps.SAVE_MANIFEST]: { buttons: [ cancelButton, + backButton, { primary: true, text: () => formatMessage('Save'), diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx index 63ef959138..2178682c70 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx @@ -105,11 +105,16 @@ export const Description: React.FC = ({ () => SCHEMA_URIS.map((key, index) => { const [version] = VERSION_REGEX.exec(key) || []; - + let selected = false; + if ($schema) { + selected = $schema && key === $schema; + } else { + selected = !index; + } return { text: formatMessage('Version {version}', { version }), key, - selected: !index, + selected, }; }), [] diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx index ee90f5fd68..7c59a879f7 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx @@ -93,7 +93,7 @@ const DescriptionColumn: React.FC = (props) => { ); }; -export const SelectDialogs: React.FC = ({ setSelectedDialogs, projectId }) => { +export const SelectDialogs: React.FC = ({ selectedDialogs, setSelectedDialogs, projectId }) => { const dialogs = useRecoilValue(dialogsSelectorFamily(projectId)); const items = useMemo(() => dialogs.map(({ id, content, displayName }) => ({ id, content, displayName })), [ projectId, @@ -138,16 +138,16 @@ export const SelectDialogs: React.FC = ({ setSelectedDialogs, proj [projectId] ); - const selection = useMemo( - () => - new Selection({ - onSelectionChanged: () => { - const selectedItems = selection.getSelection(); - setSelectedDialogs(selectedItems); - }, - }), - [] - ); - + const selection = useMemo(() => { + const listSelection = new Selection({ + onSelectionChanged: () => { + const selectedItems = listSelection.getSelection(); + setSelectedDialogs(selectedItems); + }, + }); + listSelection.setItems(selectedDialogs); + listSelection.setAllSelected(true); + return listSelection; + }, []); return ; }; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx index f1a5127be7..9275a884a9 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx @@ -20,7 +20,7 @@ const getLabel = (kind: SDKKinds, uiSchema) => { return label || kind.replace('Microsoft.', ''); }; -export const SelectTriggers: React.FC = ({ setSelectedTriggers, projectId }) => { +export const SelectTriggers: React.FC = ({ selectedTriggers, setSelectedTriggers, projectId }) => { const dialogs = useRecoilValue(dialogsSelectorFamily(projectId)); const schemas = useRecoilValue(schemasState(projectId)); @@ -80,16 +80,17 @@ export const SelectTriggers: React.FC = ({ setSelectedTriggers, pr }, ]; - const selection = useMemo( - () => - new Selection({ - onSelectionChanged: () => { - const selectedItems = selection.getSelection(); - setSelectedTriggers(selectedItems); - }, - }), - [] - ); + const selection = useMemo(() => { + const listSelection = new Selection({ + onSelectionChanged: () => { + const selectedItems = listSelection.getSelection(); + setSelectedTriggers(selectedItems); + }, + }); + listSelection.setItems(selectedTriggers); + listSelection.setAllSelected(true); + return listSelection; + }, []); return ; }; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 39b089e145..b6c307b782 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -147,6 +147,10 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss } }; + const handleBack = () => { + setCurrentStep((current) => (current > 0 ? current - 1 : current)); + }; + return ( = ({ onSubmit, onDismiss manifest={skillManifest} projectId={projectId} schema={schema} + selectedDialogs={selectedDialogs} + selectedTriggers={selectedTriggers} setErrors={setErrors} setSchema={setSchema} setSelectedDialogs={setSelectedDialogs} @@ -217,6 +223,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss manifest: skillManifest, onDismiss: handleDismiss, onNext: handleNext, + onBack: handleBack, onSave: handleSave, onPublish: handleTriggerPublish, onSubmit, diff --git a/Composer/packages/client/src/pages/publish/BotStatusList.tsx b/Composer/packages/client/src/pages/publish/BotStatusList.tsx index ac3c0fc91a..71f5cb6fe1 100644 --- a/Composer/packages/client/src/pages/publish/BotStatusList.tsx +++ b/Composer/packages/client/src/pages/publish/BotStatusList.tsx @@ -24,7 +24,7 @@ import { ApiStatus } from '../../utils/publishStatusPollingUpdater'; import { PublishStatusList } from './PublishStatusList'; import { detailList, listRoot, tableView } from './styles'; import { BotPublishHistory, BotStatus } from './type'; -import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; +import { ITooltipHostStyles, ITooltipStyles, TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; import { flexContentSpaceBetween } from '../language-understanding/styles'; const copiedCalloutStyles = { @@ -41,12 +41,13 @@ const SkillManifestUrlField = ({ urls }: SkillManifestUrlFieldProps) => { const { isCopiedToClipboard, copyTextToClipboard, resetIsCopiedToClipboard } = useCopyToClipboard(urls[0]); const calloutTarget = useRef(); + const clipUrl = (url) => url.replace('azurewebsites.net/manifests/', 'azureweb...'); return ( (
- {url} + {clipUrl(url)} navigator.clipboard.writeText(url)} />
))} diff --git a/Composer/packages/client/src/pages/publish/Notifications.tsx b/Composer/packages/client/src/pages/publish/Notifications.tsx index 66df23a214..1a9a93bb79 100644 --- a/Composer/packages/client/src/pages/publish/Notifications.tsx +++ b/Composer/packages/client/src/pages/publish/Notifications.tsx @@ -107,7 +107,7 @@ export const getSkillPublishedNotificationCardProps = (item: BotStatus, url?: st `; const linkButton = css` - color: #0078d4; + color: #323130; float: right; font-size: 12px; height: auto; @@ -136,7 +136,7 @@ export const getSkillPublishedNotificationCardProps = (item: BotStatus, url?: st {url} navigator.clipboard.writeText(url)} />
From 2c51c070b067b7463f4a8b99c1f535cfb5ef6258 Mon Sep 17 00:00:00 2001 From: Lu Han <32191031+luhan2017@users.noreply.github.com> Date: Mon, 12 Apr 2021 10:57:41 +0800 Subject: [PATCH 32/55] Update the logic of updating skill host endpoint --- extensions/azurePublish/src/node/deploy.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/azurePublish/src/node/deploy.ts b/extensions/azurePublish/src/node/deploy.ts index ae7bb2aeba..42b1d1ab4b 100644 --- a/extensions/azurePublish/src/node/deploy.ts +++ b/extensions/azurePublish/src/node/deploy.ts @@ -62,6 +62,12 @@ export class BotProjectDeploy { await this.BindKeyVault(absSettings, hostname); } + // Update skill host endpoint + if (settings.skillHostEndpoint) { + settings.skillHostEndpoint = `https://${hostname}.azurewebsites.net/api/skills`; + } + + // Update endpoints in skill manifests const skillManifestPath = path.join(this.projPath, 'wwwroot', 'manifests'); if (await fs.pathExists(skillManifestPath)) { await this.updateSkillSettings(profileName, hostname, settings.MicrosoftAppId, skillManifestPath, settings); @@ -186,11 +192,6 @@ export class BotProjectDeploy { return; } - // update skill host endpoint - if (settings.skillHostEndpoint) { - settings.skillHostEndpoint = `https://${hostname}.azurewebsites.net/api/skills`; - } - for (const manifestFile of manifestFiles) { const hostEndpoint = `https://${hostname}.azurewebsites.net/api/messages`; From 9314af5f819f26e1652476de9f0131147220848c Mon Sep 17 00:00:00 2001 From: Long Alan Date: Mon, 12 Apr 2021 13:27:21 +0800 Subject: [PATCH 33/55] callers set --- .../design/DebugPanel/TabExtensions/index.ts | 2 +- .../pages/design/exportSkillModal/index.tsx | 23 +++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts index f17f2ca037..8d33f1ae8c 100644 --- a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts +++ b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts @@ -3,7 +3,7 @@ import { TabExtensionConfig } from './types'; import { DiagnosticsTabConfig } from './DiagnosticsTab'; -import { WebChatLogTabConfig } from './WebChatLog/config'; +import { WebChatLogTabConfig } from './WebchatLog/config'; import { RuntimeOutputTabConfig } from './RuntimeOutputLog'; const implementedDebugExtensions: TabExtensionConfig[] = [ diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index b6c307b782..f8caada6b0 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -30,6 +30,7 @@ import { generateSkillManifest } from './generateSkillManifest'; import { editorSteps, ManifestEditorSteps, order, VERSION_REGEX } from './constants'; import { mergePropertiesManagedByRootBot } from '../../../recoilModel/dispatchers/utils/project'; import { cloneDeep } from 'lodash'; +import { isUsingAdaptiveRuntime } from '@bfc/shared'; interface ExportSkillModalProps { isOpen: boolean; @@ -63,19 +64,27 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const settings = useRecoilValue(settingsState(projectId)); const rootBotProjectId = useRecoilValue(rootBotProjectIdSelector) || ''; const mergedSettings = mergePropertiesManagedByRootBot(projectId, rootBotProjectId, settings); - const { skillConfiguration } = mergedSettings; + const { skillConfiguration, runtime, runtimeSettings } = mergedSettings; const { setSettings } = useRecoilValue(dispatcherState); - const [callers, setCallers] = useState(skillConfiguration?.allowedCallers ?? []); + const isAdaptive = isUsingAdaptiveRuntime(runtime); + const [callers, setCallers] = useState( + !isAdaptive ? skillConfiguration?.allowedCallers : runtimeSettings?.skills?.allowedCallers ?? [] + ); const updateAllowedCallers = React.useCallback( (allowedCallers: string[] = []) => { - const updatedSetting = { - ...cloneDeep(mergedSettings), - skillConfiguration: { ...skillConfiguration, allowedCallers }, - }; + const updatedSetting = isAdaptive + ? { + ...cloneDeep(mergedSettings), + runtimeSettings: { skills: { allowedCallers } }, + } + : { + ...cloneDeep(mergedSettings), + skillConfiguration: { ...skillConfiguration, allowedCallers }, + }; setSettings(projectId, updatedSetting); }, - [mergedSettings, projectId, skillConfiguration] + [mergedSettings, projectId, isAdaptive, skillConfiguration, runtimeSettings] ); const handleGenerateManifest = () => { From a634daa8760a66dea9c52a1cfd90885875af41c7 Mon Sep 17 00:00:00 2001 From: Lu Han <32191031+luhan2017@users.noreply.github.com> Date: Mon, 12 Apr 2021 14:10:54 +0800 Subject: [PATCH 34/55] remove the physical file provider --- runtime/dotnet/azurewebapp/Startup.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/runtime/dotnet/azurewebapp/Startup.cs b/runtime/dotnet/azurewebapp/Startup.cs index a7b18b6ec0..2205860ec3 100644 --- a/runtime/dotnet/azurewebapp/Startup.cs +++ b/runtime/dotnet/azurewebapp/Startup.cs @@ -235,9 +235,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) // Expose static files in manifests folder for skill scenarios. app.UseStaticFiles(new StaticFileOptions { - FileProvider = new PhysicalFileProvider( - Path.Combine(env.WebRootPath, "manifests")), - RequestPath = "/manifests", ContentTypeProvider = provider }); From b29bdfba1f6d55dab6cbdfc26ca1120c10539a9e Mon Sep 17 00:00:00 2001 From: Long Alan Date: Mon, 12 Apr 2021 14:13:33 +0800 Subject: [PATCH 35/55] back button --- .../content/SelectDialogs.tsx | 22 +++++++++-------- .../content/SelectTriggers.tsx | 24 ++++++++++--------- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx index 7c59a879f7..bd3e68bf76 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx @@ -138,16 +138,18 @@ export const SelectDialogs: React.FC = ({ selectedDialogs, setSele [projectId] ); - const selection = useMemo(() => { - const listSelection = new Selection({ - onSelectionChanged: () => { - const selectedItems = listSelection.getSelection(); - setSelectedDialogs(selectedItems); - }, - }); - listSelection.setItems(selectedDialogs); - listSelection.setAllSelected(true); - return listSelection; + const selection = new Selection({ + getKey: (item) => item.id, + onSelectionChanged: () => { + const selectedItems = selection.getSelection(); + setSelectedDialogs(selectedItems); + }, + }); + + useEffect(() => { + for (const item of selectedDialogs) { + selection.setKeySelected(selection.getKey(item), true, false); + } }, []); return ; }; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx index 9275a884a9..888d2296aa 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx @@ -3,7 +3,7 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { DialogInfo, ITrigger, SDKKinds, getFriendlyName } from '@bfc/shared'; import { Selection } from 'office-ui-fabric-react/lib/DetailsList'; import { useRecoilValue } from 'recoil'; @@ -80,16 +80,18 @@ export const SelectTriggers: React.FC = ({ selectedTriggers, setSe }, ]; - const selection = useMemo(() => { - const listSelection = new Selection({ - onSelectionChanged: () => { - const selectedItems = listSelection.getSelection(); - setSelectedTriggers(selectedItems); - }, - }); - listSelection.setItems(selectedTriggers); - listSelection.setAllSelected(true); - return listSelection; + const selection = new Selection({ + getKey: (item) => item.id, + onSelectionChanged: () => { + const selectedItems = selection.getSelection(); + setSelectedTriggers(selectedItems); + }, + }); + + useEffect(() => { + for (const item of selectedTriggers) { + selection.setKeySelected(selection.getKey(item), true, false); + } }, []); return ; From 8bd1ff51999a56744314f454a53e5afd8eaa2967 Mon Sep 17 00:00:00 2001 From: Long Alan Date: Mon, 12 Apr 2021 16:03:25 +0800 Subject: [PATCH 36/55] default select publish profie --- .../design/DebugPanel/TabExtensions/index.ts | 2 +- .../exportSkillModal/content/SelectDialogs.tsx | 18 +++++++++++------- .../exportSkillModal/content/SelectProfile.tsx | 3 ++- .../content/SelectTriggers.tsx | 18 +++++++++++------- .../pages/design/exportSkillModal/index.tsx | 2 +- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts index 8d33f1ae8c..f17f2ca037 100644 --- a/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts +++ b/Composer/packages/client/src/pages/design/DebugPanel/TabExtensions/index.ts @@ -3,7 +3,7 @@ import { TabExtensionConfig } from './types'; import { DiagnosticsTabConfig } from './DiagnosticsTab'; -import { WebChatLogTabConfig } from './WebchatLog/config'; +import { WebChatLogTabConfig } from './WebChatLog/config'; import { RuntimeOutputTabConfig } from './RuntimeOutputLog'; const implementedDebugExtensions: TabExtensionConfig[] = [ diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx index bd3e68bf76..3cdc0c97eb 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectDialogs.tsx @@ -138,13 +138,17 @@ export const SelectDialogs: React.FC = ({ selectedDialogs, setSele [projectId] ); - const selection = new Selection({ - getKey: (item) => item.id, - onSelectionChanged: () => { - const selectedItems = selection.getSelection(); - setSelectedDialogs(selectedItems); - }, - }); + const selection = useMemo( + () => + new Selection({ + getKey: (item) => item.id, + onSelectionChanged: () => { + const selectedItems = selection.getSelection(); + setSelectedDialogs(selectedItems); + }, + }), + [] + ); useEffect(() => { for (const item of selectedDialogs) { diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index d3f09d25a6..d0f6f5ff6f 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -137,9 +137,10 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife ); const publishingOptions = useMemo(() => { - return publishingTargets.map((t) => ({ + return publishingTargets.map((t, index) => ({ key: t.name, text: t.name, + selected: !index, })); }, [publishingTargets]); diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx index 888d2296aa..10de552175 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectTriggers.tsx @@ -80,13 +80,17 @@ export const SelectTriggers: React.FC = ({ selectedTriggers, setSe }, ]; - const selection = new Selection({ - getKey: (item) => item.id, - onSelectionChanged: () => { - const selectedItems = selection.getSelection(); - setSelectedTriggers(selectedItems); - }, - }); + const selection = useMemo( + () => + new Selection({ + getKey: (item) => item.id, + onSelectionChanged: () => { + const selectedItems = selection.getSelection(); + setSelectedTriggers(selectedItems); + }, + }), + [] + ); useEffect(() => { for (const item of selectedTriggers) { diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index f8caada6b0..55bf31200e 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -27,7 +27,7 @@ import { import { styles } from './styles'; import { generateSkillManifest } from './generateSkillManifest'; -import { editorSteps, ManifestEditorSteps, order, VERSION_REGEX } from './constants'; +import { editorSteps, ManifestEditorSteps, order } from './constants'; import { mergePropertiesManagedByRootBot } from '../../../recoilModel/dispatchers/utils/project'; import { cloneDeep } from 'lodash'; import { isUsingAdaptiveRuntime } from '@bfc/shared'; From 897e6d71c31de172d3038f391c1046b13d8be268 Mon Sep 17 00:00:00 2001 From: Long Alan Date: Mon, 12 Apr 2021 16:14:55 +0800 Subject: [PATCH 37/55] disabled generated & publish button when no profie --- .../src/pages/design/exportSkillModal/constants.tsx | 1 + .../src/pages/design/exportSkillModal/index.tsx | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index d952af7b83..747864f6f0 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -222,6 +222,7 @@ export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { cancelButton, backButton, { + disabled: ({ publishTargets }) => publishTargets.length === 0, primary: true, text: () => formatMessage('Generate and Publish'), onClick: ({ generateManifest, onNext, onPublish }) => () => { diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx index 55bf31200e..901a89c510 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/index.tsx @@ -64,7 +64,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss const settings = useRecoilValue(settingsState(projectId)); const rootBotProjectId = useRecoilValue(rootBotProjectIdSelector) || ''; const mergedSettings = mergePropertiesManagedByRootBot(projectId, rootBotProjectId, settings); - const { skillConfiguration, runtime, runtimeSettings } = mergedSettings; + const { skillConfiguration, runtime, runtimeSettings, publishTargets } = mergedSettings; const { setSettings } = useRecoilValue(dispatcherState); const isAdaptive = isUsingAdaptiveRuntime(runtime); const [callers, setCallers] = useState( @@ -218,7 +218,16 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss
{buttons.map(({ disabled, primary, text, onClick }, index) => { const Button = primary ? PrimaryButton : DefaultButton; - const isDisabled = typeof disabled === 'function' ? disabled({ manifest: skillManifest }) : !!disabled; + + let isDisabled = false; + if (typeof disabled === 'function') { + isDisabled = + editorStep === ManifestEditorSteps.SELECT_MANIFEST + ? disabled({ manifest: skillManifest }) + : disabled({ publishTargets }); + } else { + isDisabled = !!disabled; + } return (
) : (
- +
); }; From 39c6543b2404e59e2269580a01262968ea115c42 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Tue, 13 Apr 2021 17:28:13 +0800 Subject: [PATCH 42/55] comments --- .../botProject/CreatePublishProfileDialog.tsx | 13 +- .../design/exportSkillModal/constants.tsx | 36 +--- .../exportSkillModal/content/AddCallers.tsx | 10 +- .../content/FetchManifestSchema.tsx | 28 --- .../content/SelectManifest.tsx | 171 ------------------ .../content/SelectProfile.tsx | 82 +++++---- .../design/exportSkillModal/content/index.ts | 2 - extensions/azurePublish/src/node/deploy.ts | 7 +- 8 files changed, 60 insertions(+), 289 deletions(-) delete mode 100644 Composer/packages/client/src/pages/design/exportSkillModal/content/FetchManifestSchema.tsx delete mode 100644 Composer/packages/client/src/pages/design/exportSkillModal/content/SelectManifest.tsx diff --git a/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx index 539444ce6d..7a6719f68d 100644 --- a/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx @@ -17,7 +17,6 @@ import { PublishProfileDialog } from './create-publish-profile/PublishProfileDia import { actionButton } from './styles'; import { useBoolean } from '@uifabric/react-hooks'; import Dialog, { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; -import { styles } from '../design/styles'; // -------------------- CreatePublishProfileDialog -------------------- // @@ -42,7 +41,9 @@ export const CreatePublishProfileDialog: React.FC(null); + const [currentPublishProfile, setCurrentPublishProfile] = useState<{ index: number; item: PublishTarget } | null>( + null + ); useEffect(() => { if (projectId) { @@ -74,13 +75,14 @@ export const CreatePublishProfileDialog: React.FC {formatMessage('Create new publish profile')}
- + {showAuthDialog && ( @@ -99,9 +101,10 @@ export const CreatePublishProfileDialog: React.FC { setDialogHidden(true); // reset current - setCurrent(null); + toggleHideDialog(); + setCurrentPublishProfile(null); }} - current={current} + current={currentPublishProfile} projectId={projectId} setPublishTargets={setPublishTargets} targets={publishTargets || []} diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx index 747864f6f0..a71bcc6815 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/constants.tsx @@ -9,15 +9,7 @@ import { SDKKinds } from '@bfc/shared'; import { nameRegex } from '../../../constants'; -import { - Description, - FetchManifestSchema, - ReviewManifest, - SaveManifest, - SelectDialogs, - SelectManifest, - SelectTriggers, -} from './content'; +import { Description, ReviewManifest, SaveManifest, SelectDialogs, SelectTriggers } from './content'; import { SelectProfile } from './content/SelectProfile'; import { AddCallers } from './content/AddCallers'; @@ -131,11 +123,9 @@ interface EditorStep { } export enum ManifestEditorSteps { - FETCH_MANIFEST_SCHEMA = 'FETCH_MANIFEST_SCHEMA', MANIFEST_DESCRIPTION = 'MANIFEST_DESCRIPTION', MANIFEST_REVIEW = 'MANIFEST_REVIEW', SAVE_MANIFEST = 'SAVE_MANIFEST', - SELECT_MANIFEST = 'SELECT_MANIFEST', SELECT_DIALOGS = 'SELECT_DIALOGS', SELECT_TRIGGERS = 'SELECT_TRIGGERS', SELECT_PROFILE = 'SELECT_PROFILE', @@ -143,8 +133,6 @@ export enum ManifestEditorSteps { } export const order: ManifestEditorSteps[] = [ - // ManifestEditorSteps.SELECT_MANIFEST, - // ManifestEditorSteps.FETCH_MANIFEST_SCHEMA, ManifestEditorSteps.MANIFEST_DESCRIPTION, ManifestEditorSteps.SELECT_DIALOGS, ManifestEditorSteps.SELECT_TRIGGERS, @@ -187,28 +175,6 @@ const validate = ({ content, schema }) => { }; export const editorSteps: { [key in ManifestEditorSteps]: EditorStep } = { - [ManifestEditorSteps.SELECT_MANIFEST]: { - buttons: [ - cancelButton, - { - disabled: ({ manifest }) => !manifest?.id, - primary: true, - text: () => formatMessage('Edit'), - onClick: ({ onNext, manifest }) => () => { - onNext({ id: manifest?.id }); - }, - }, - ], - content: SelectManifest, - editJson: false, - subText: () => formatMessage('Create a new skill manifest or select which one you want to edit'), - title: () => formatMessage('Create or edit skill manifest'), - }, - [ManifestEditorSteps.FETCH_MANIFEST_SCHEMA]: { - content: FetchManifestSchema, - editJson: false, - title: () => formatMessage('Select manifest version'), - }, [ManifestEditorSteps.MANIFEST_DESCRIPTION]: { buttons: [cancelButton, nextButton], content: Description, diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx index 6c81d42d03..9fc59705d3 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx @@ -38,11 +38,10 @@ const removeCaller = { export const AddCallers: React.FC = ({ projectId, callers, setCallers }) => { const handleRemove = (index) => { - var currentCallers = callers.slice(); - currentCallers?.splice(index, 1); + const currentCallers = callers.splice(index, 1); setCallers(currentCallers); }; - const handleAdd = () => { + const handleAddNewAllowedCallerClick = () => { var currentCallers = callers.slice(); currentCallers?.push('0000-11111-00000-11111'); setCallers(currentCallers); @@ -65,7 +64,7 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall setCallers(currentCallers); }} borderless - >
+ />
handleRemove(index)}> @@ -76,10 +75,9 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall ); })} { - handleAdd(); + handleAddNewAllowedCallerClick(); }} > {formatMessage('Add allowed callers')} diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/FetchManifestSchema.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/FetchManifestSchema.tsx deleted file mode 100644 index ff2d1fc9d6..0000000000 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/FetchManifestSchema.tsx +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import React, { useEffect } from 'react'; - -import { LoadingSpinner } from '../../../../components/LoadingSpinner'; -import { ContentProps } from '../constants'; - -export const FetchManifestSchema: React.FC = ({ completeStep, editJson, value, setSchema }) => { - useEffect(() => { - (async function () { - try { - if (value?.$schema) { - const res = await fetch(value.$schema); - const schema = await res.json(); - setSchema(schema); - completeStep(); - } else { - editJson(); - } - } catch (error) { - editJson(); - } - })(); - }, [value]); - - return ; -}; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectManifest.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectManifest.tsx deleted file mode 100644 index 0fb117aeaf..0000000000 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectManifest.tsx +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/** @jsx jsx */ -import { css, jsx } from '@emotion/core'; -import React, { useMemo, useState } from 'react'; -import { Dropdown, IDropdownOption, ResponsiveMode } from 'office-ui-fabric-react/lib/Dropdown'; -import { Label } from 'office-ui-fabric-react/lib/Label'; -import { PrimaryButton } from 'office-ui-fabric-react/lib/Button'; -import { Sticky, StickyPositionType } from 'office-ui-fabric-react/lib/Sticky'; -import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ScrollablePane'; -import { - CheckboxVisibility, - DetailsList, - DetailsListLayoutMode, - SelectionMode, -} from 'office-ui-fabric-react/lib/DetailsList'; -import { TooltipHost } from 'office-ui-fabric-react/lib/Tooltip'; -import formatMessage from 'format-message'; - -import { calculateTimeDiff } from '../../../../utils/fileUtil'; -import { ContentProps, SCHEMA_URIS, VERSION_REGEX } from '../constants'; - -const styles = { - detailListContainer: css` - position: relative; - max-height: 40vh; - padding-top: 10px; - overflow: hidden; - flex-grow: 1; - min-height: 250px; - `, - create: css` - display: flex; - `, -}; - -export const SelectManifest: React.FC = ({ completeStep, skillManifests, setSkillManifest }) => { - const [manifestVersion, setManifestVersion] = useState(SCHEMA_URIS[0]); - const [errors, setErrors] = useState<{ version?: string }>({}); - const [version] = useMemo(() => VERSION_REGEX.exec(manifestVersion) || [''], []); - - const options: IDropdownOption[] = useMemo( - () => - SCHEMA_URIS.map((key, index) => { - const [version] = VERSION_REGEX.exec(key) || []; - - return { - text: formatMessage('Version {version}', { version }), - key, - selected: !index, - }; - }), - [] - ); - - const handleChange = (_e: React.FormEvent, option?: IDropdownOption) => { - if (option) { - setManifestVersion(option.key as string); - } - }; - - const handleCreate = () => { - if (!version) { - setErrors({ version: formatMessage('Please select a version of the manifest schema') }); - return; - } - setSkillManifest({ content: { $schema: manifestVersion } }); - completeStep(); - }; - - // for detail file list in open panel - const tableColumns = [ - { - key: 'column1', - name: formatMessage('Name'), - fieldName: 'id', - minWidth: 300, - maxWidth: 350, - isRowHeader: true, - isResizable: true, - isSorted: true, - isSortedDescending: false, - sortAscendingAriaLabel: formatMessage('Sorted A to Z'), - sortDescendingAriaLabel: formatMessage('Sorted Z to A'), - data: 'string', - onRender: (item) => { - return {item.id}; - }, - isPadded: true, - }, - { - key: 'column2', - name: formatMessage('Date Modified'), - fieldName: 'lastModified', - minWidth: 60, - maxWidth: 70, - isResizable: true, - data: 'number', - onRender: (item) => { - return {calculateTimeDiff(item.lastModified)}; - }, - isPadded: true, - }, - ]; - - function onRenderDetailsHeader(props, defaultRender) { - return ( - - {defaultRender({ - ...props, - onRenderColumnHeaderTooltip: (tooltipHostProps) => , - })} - - ); - } - - return ( -
-
- -
- - - {formatMessage('Create')} - -
-
-
- - item.name} - items={skillManifests} - layoutMode={DetailsListLayoutMode.justified} - selectionMode={SelectionMode.single} - onActiveItemChanged={setSkillManifest} - onRenderDetailsHeader={onRenderDetailsHeader} - /> - -
-
- ); -}; diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx index 1a03a97161..cf92828e5b 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/SelectProfile.tsx @@ -6,7 +6,7 @@ import { jsx } from '@emotion/core'; import { PublishTarget, SkillManifestFile } from '@bfc/shared'; import formatMessage from 'format-message'; import { css, Dropdown, Icon, IDropdownOption, TextField, TooltipHost } from 'office-ui-fabric-react'; -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { botDisplayNameState, dispatcherState, settingsState, skillManifestsState } from '../../../../recoilModel'; @@ -39,8 +39,7 @@ const onRenderLabel = (props) => { >
{props.label} @@ -63,7 +62,7 @@ export const getManifestId = ( let fileId = version ? `${botName}-${version.replace(/\./g, '-')}-manifest` : `${botName}-manifest`; let i = -1; - while (skillManifests.some(({ id }) => id === fileId)) { + while (skillManifests.some(({ id }) => id === fileId) && i < skillManifests.length) { if (i < 0) { fileId = fileId.concat(`-${++i}`); } else { @@ -78,6 +77,7 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife const [publishingTargets, setPublishingTargets] = useState([]); const [currentTarget, setCurrentTarget] = useState(); const { updateCurrentTarget } = useRecoilValue(dispatcherState); + const settings = useRecoilValue(settingsState(projectId)); const [endpointUrl, setEndpointUrl] = useState(); const [appId, setAppId] = useState(); const { id, content } = manifest; @@ -85,7 +85,7 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife const skillManifests = useRecoilValue(skillManifestsState(projectId)); const { ...rest } = content; - const updateCurrentProfile = useMemo( + const handleCurrentProfileChange = useMemo( () => (_e, option?: IDropdownOption) => { const target = publishingTargets.find((t) => { return t.name === option?.key; @@ -96,32 +96,36 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife ); useEffect(() => { - if (currentTarget) { - setCurrentTarget(currentTarget); - updateCurrentTarget(projectId, currentTarget); - const config = JSON.parse(currentTarget.configuration); - setEndpointUrl(`https://${config.hostname}.azurewebsites.net/api/messages`); - setAppId(config.settings.MicrosoftAppId); - - setSkillManifest({ - content: { - ...rest, - endpoints: [ - { - protocol: 'BotFrameworkV3', - name: currentTarget.name, - endpointUrl: `https://${config.hostname}.azurewebsites.net/api/messages`, - description: '', - msAppId: config.settings.MicrosoftAppId, - }, - ], - }, - id: id, - }); + try { + if (currentTarget) { + setCurrentTarget(currentTarget); + updateCurrentTarget(projectId, currentTarget); + const config = JSON.parse(currentTarget.configuration); + setEndpointUrl(`https://${config.hostname}.azurewebsites.net/api/messages`); + setAppId(config.settings.MicrosoftAppId); + + setSkillManifest({ + content: { + ...rest, + endpoints: [ + { + protocol: 'BotFrameworkV3', + name: currentTarget.name, + endpointUrl: `https://${config.hostname}.azurewebsites.net/api/messages`, + description: '', + msAppId: config.settings.MicrosoftAppId, + }, + ], + }, + id: id, + }); + } + } catch (err) { + console.log(err.message); } }, [currentTarget]); - const isProfileValid = useMemo( - () => () => { + const isProfileValid = useMemo(() => { + try { if (!publishingTargets) { return false; } @@ -135,9 +139,11 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife ); }); return filteredProfile.length > 0; - }, - [publishingTargets] - ); + } catch (err) { + console.log(err.message); + return false; + } + }, [publishingTargets]); const publishingOptions = useMemo(() => { return publishingTargets.map((t) => ({ @@ -146,8 +152,6 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife })); }, [publishingTargets]); - const settings = useRecoilValue(settingsState(projectId)); - useEffect(() => { setPublishingTargets(settings.publishTargets || []); setCurrentTarget((settings.publishTargets || [])[0]); @@ -160,24 +164,24 @@ export const SelectProfile: React.FC = ({ manifest, setSkillManife } }, []); - return isProfileValid() ? ( + return isProfileValid ? (
= ({ manifest, setSkillManife required ariaLabel={formatMessage('The app id of your application registration')} label={formatMessage('Microsoft App Id')} - placeholder={'The app id'} + placeholder={formatMessage('The app id')} styles={{ root: { paddingBottom: '8px' } }} value={appId} onRenderLabel={onRenderLabel} diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/index.ts b/Composer/packages/client/src/pages/design/exportSkillModal/content/index.ts index 94ea08db69..d0ea0a1c97 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/index.ts +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/index.ts @@ -3,9 +3,7 @@ export * from './Description'; export * from './Endpoints'; -export * from './FetchManifestSchema'; export * from './ReviewManifest'; export * from './SaveManifest'; -export * from './SelectManifest'; export * from './SelectDialogs'; export * from './SelectTriggers'; diff --git a/extensions/azurePublish/src/node/deploy.ts b/extensions/azurePublish/src/node/deploy.ts index 42b1d1ab4b..943b081b21 100644 --- a/extensions/azurePublish/src/node/deploy.ts +++ b/extensions/azurePublish/src/node/deploy.ts @@ -17,6 +17,7 @@ import { AzurePublishErrors, createCustomizeError, stringifyError } from './util import { copyDir } from './utils/copyDir'; import { KeyVaultApi } from './keyvaultHelper/keyvaultApi'; import { KeyVaultApiConfig } from './keyvaultHelper/keyvaultApiConfig'; +import { DialogSetting } from '@botframework-composer/types'; export class BotProjectDeploy { private accessToken: string; @@ -45,7 +46,7 @@ export class BotProjectDeploy { */ public async deploy( project: any, - settings: any, + settings: DialogSetting, profileName: string, name: string, environment: string, @@ -181,13 +182,13 @@ export class BotProjectDeploy { hostname: string, msAppId: string, skillSettingsPath: string, - settings: any + settings: DialogSetting ) { const manifestFiles = (await fs.readdir(skillSettingsPath)).filter((x) => x.endsWith('.json')); if (manifestFiles.length === 0) { this.logger({ status: BotProjectDeployLoggerType.DEPLOY_INFO, - message: `The manifest does not exsit on path: ${skillSettingsPath}`, + message: `The manifest does not exist on path: ${skillSettingsPath}`, }); return; } From c45e63b7e8519e96e521712d1b23a67b52b1eed4 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Tue, 13 Apr 2021 17:40:30 +0800 Subject: [PATCH 43/55] callers --- .../src/pages/design/exportSkillModal/content/AddCallers.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx index 9fc59705d3..a9fdfc201d 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx @@ -38,8 +38,7 @@ const removeCaller = { export const AddCallers: React.FC = ({ projectId, callers, setCallers }) => { const handleRemove = (index) => { - const currentCallers = callers.splice(index, 1); - setCallers(currentCallers); + setCallers(callers.filter((_, i) => i !== index)); }; const handleAddNewAllowedCallerClick = () => { var currentCallers = callers.slice(); From 517286f995547370d52bebb32fb9035c55b766a0 Mon Sep 17 00:00:00 2001 From: Long Jun Date: Tue, 13 Apr 2021 18:23:09 +0800 Subject: [PATCH 44/55] lint --- .../botProject/CreatePublishProfileDialog.tsx | 6 +++--- .../exportSkillModal/content/AddCallers.tsx | 9 ++++++--- .../exportSkillModal/content/Description.tsx | 8 ++++---- .../content/SelectProfile.tsx | 9 ++++++--- .../pages/design/exportSkillModal/index.tsx | 20 ++++++------------- .../src/pages/publish/BotStatusList.tsx | 6 +++--- .../src/pages/publish/Notifications.tsx | 4 ++-- 7 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx b/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx index 7a6719f68d..6e8649fc06 100644 --- a/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx +++ b/Composer/packages/client/src/pages/botProject/CreatePublishProfileDialog.tsx @@ -8,6 +8,8 @@ import { useRecoilValue } from 'recoil'; import { PublishTarget } from '@bfc/shared'; import formatMessage from 'format-message'; import { ActionButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { useBoolean } from '@uifabric/react-hooks'; +import Dialog, { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; import { dispatcherState, settingsState, publishTypesState } from '../../recoilModel'; import { AuthDialog } from '../../components/Auth/AuthDialog'; @@ -15,8 +17,6 @@ import { isShowAuthDialog } from '../../utils/auth'; import { PublishProfileDialog } from './create-publish-profile/PublishProfileDialog'; import { actionButton } from './styles'; -import { useBoolean } from '@uifabric/react-hooks'; -import Dialog, { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; // -------------------- CreatePublishProfileDialog -------------------- // @@ -82,7 +82,7 @@ export const CreatePublishProfileDialog: React.FC
- + {showAuthDialog && ( diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx index a9fdfc201d..451038eb8b 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/AddCallers.tsx @@ -5,8 +5,11 @@ import { jsx } from '@emotion/core'; import { SharedColors } from '@uifabric/fluent-theme'; import formatMessage from 'format-message'; -import { ActionButton, css, FontWeights, TextField } from 'office-ui-fabric-react'; +import { ActionButton } from 'office-ui-fabric-react/lib/Button'; +import { FontWeights } from 'office-ui-fabric-react/lib/Styling'; +import { TextField } from 'office-ui-fabric-react/lib/TextField'; import React from 'react'; + import { tableColumnHeader, tableRow, tableRowItem } from '../../../botProject/styles'; import { ContentProps } from '../constants'; @@ -41,7 +44,7 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall setCallers(callers.filter((_, i) => i !== index)); }; const handleAddNewAllowedCallerClick = () => { - var currentCallers = callers.slice(); + const currentCallers = callers.slice(); currentCallers?.push('0000-11111-00000-11111'); setCallers(currentCallers); }; @@ -56,13 +59,13 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall
{ const currentCallers = callers.slice(); currentCallers[index] = newValue ?? ''; setCallers(currentCallers); }} - borderless />
diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx index 2178682c70..1f83649d05 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx @@ -9,14 +9,14 @@ import { FieldProps, JSONSchema7, UIOptions } from '@bfc/extension-client'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { useRecoilValue } from 'recoil'; import { v4 as uuid } from 'uuid'; - -import { ContentProps, SCHEMA_URIS, VERSION_REGEX } from '../constants'; -import { botDisplayNameState } from '../../../../recoilModel'; import { Label } from 'office-ui-fabric-react/lib/Label'; import formatMessage from 'format-message'; import { Dropdown, IDropdownOption, ResponsiveMode } from 'office-ui-fabric-react/lib/Dropdown'; import { LoadingSpinner } from '@bfc/ui-shared/lib/components/LoadingSpinner'; +import { botDisplayNameState } from '../../../../recoilModel'; +import { ContentProps, SCHEMA_URIS, VERSION_REGEX } from '../constants'; + const styles = { row: css` display: flex; @@ -180,9 +180,9 @@ export const Description: React.FC = ({ {formatMessage('Manifest Version')} = ({ onSubmit, onDismiss }} > = ({ onSubmit, onDismiss schema={schema} selectedDialogs={selectedDialogs} selectedTriggers={selectedTriggers} + setCallers={setCallers} setErrors={setErrors} setSchema={setSchema} setSelectedDialogs={setSelectedDialogs} @@ -208,8 +210,6 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss setSkillManifest={setSkillManifest} skillManifests={skillManifests} value={content} - callers={callers} - setCallers={setCallers} onChange={(manifestContent) => setSkillManifest({ ...skillManifest, content: manifestContent })} />
@@ -219,15 +219,7 @@ const ExportSkillModal: React.FC = ({ onSubmit, onDismiss {buttons.map(({ disabled, primary, text, onClick }, index) => { const Button = primary ? PrimaryButton : DefaultButton; - let isDisabled = false; - if (typeof disabled === 'function') { - isDisabled = - editorStep === ManifestEditorSteps.SELECT_MANIFEST - ? disabled({ manifest: skillManifest }) - : disabled({ publishTargets }); - } else { - isDisabled = !!disabled; - } + const isDisabled = typeof disabled === 'function' ? disabled({ publishTargets }) : !!disabled; return (
@@ -76,12 +76,7 @@ export const AddCallers: React.FC = ({ projectId, callers, setCall
); })} - { - handleAddNewAllowedCallerClick(); - }} - > + {formatMessage('Add allowed callers')}
diff --git a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx index 1f83649d05..ac6092a281 100644 --- a/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx +++ b/Composer/packages/client/src/pages/design/exportSkillModal/content/Description.tsx @@ -117,7 +117,7 @@ export const Description: React.FC = ({ selected, }; }), - [] + [$schema] ); useEffect(() => { @@ -174,7 +174,7 @@ export const Description: React.FC = ({