From e09aadbb1627260b0878c13ff157f83c93185a25 Mon Sep 17 00:00:00 2001 From: GeoffCoxMSFT Date: Fri, 2 Apr 2021 14:32:39 -0700 Subject: [PATCH] Added first take at copying skill manifest URL --- .../src/pages/publish/BotStatusList.tsx | 54 ++++++++++++++++++- .../src/pages/publish/publishPageUtils.tsx | 27 +++++++++- .../packages/client/src/pages/publish/type.ts | 7 ++- .../src/recoilModel/selectors/project.ts | 1 + .../packages/server/src/locales/en-US.json | 5 +- 5 files changed, 89 insertions(+), 5 deletions(-) diff --git a/Composer/packages/client/src/pages/publish/BotStatusList.tsx b/Composer/packages/client/src/pages/publish/BotStatusList.tsx index 48823b1ac8..72ca883f52 100644 --- a/Composer/packages/client/src/pages/publish/BotStatusList.tsx +++ b/Composer/packages/client/src/pages/publish/BotStatusList.tsx @@ -7,15 +7,16 @@ import moment from 'moment'; import formatMessage from 'format-message'; import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox'; import { Icon } from 'office-ui-fabric-react/lib/Icon'; -import React, { useState, Fragment, useMemo } from 'react'; +import React, { useState, Fragment, useMemo, useRef } from 'react'; import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { PublishResult } from '@bfc/shared'; import { CheckboxVisibility, DetailsList } from 'office-ui-fabric-react/lib/DetailsList'; -import { IconButton } from 'office-ui-fabric-react/lib/Button'; +import { ActionButton, IconButton } from 'office-ui-fabric-react/lib/Button'; import { SharedColors } from '@uifabric/fluent-theme'; import { FontSizes } from '@uifabric/styling'; import get from 'lodash/get'; +import { ITextField, TextField } from 'office-ui-fabric-react/lib/TextField'; import { ApiStatus } from '../../utils/publishStatusPollingUpdater'; @@ -48,6 +49,25 @@ 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); + } + } catch (e) { + // eslint-disable-next-line no-console + console.error('Something went wrong when copying to the clipboard.', e, location); + } + }; const displayedItems: BotStatus[] = useMemo(() => { if (currentSort.key !== 'Bot') return botStatusList; @@ -244,6 +264,30 @@ export const BotStatusList: React.FC = ({ }, isPadded: true, }, + { + key: 'SkillManifest', + name: '', + className: 'skillManifest', + fieldName: 'skillManifestUrl', + minWidth: 114, + maxWidth: 134, + data: 'string', + onRender: (item: BotStatus) => { + return ( + item?.skillManifestUrl && ( + { + copyStringToClipboard(item.skillManifestUrl); + }} + > + {formatMessage('Copy Skill Manifest URL')} + + ) + ); + }, + isPadded: true, + }, { key: 'ShowPublishHistory', name: '', @@ -321,6 +365,12 @@ 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 60b31969e3..fa53f1c251 100644 --- a/Composer/packages/client/src/pages/publish/publishPageUtils.tsx +++ b/Composer/packages/client/src/pages/publish/publishPageUtils.tsx @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { PublishTarget, SkillManifestFile } from '@bfc/shared'; + import { ApiStatus } from '../../utils/publishStatusPollingUpdater'; import { Bot, BotStatus, BotPublishHistory, BotProjectType, BotPropertyType } from './type'; @@ -18,6 +20,7 @@ export const generateBotPropertyData = (botProjectData: BotProjectType[]) => { setting: bot.setting, publishTargets, publishTypes: bot.publishTypes, + skillManifests: bot.skillManifests, }; const tmpBot = { id: bot.projectId, name: bot.name, publishTarget: '' }; if (publishTargets.length > 0) { @@ -28,6 +31,18 @@ export const generateBotPropertyData = (botProjectData: BotProjectType[]) => { return { botPropertyData, botList }; }; +const findSkillManifestUrl = (skillManifests: SkillManifestFile[], appId: string) => { + for (const skillManifest of skillManifests || []) { + for (const endpoint of skillManifest?.content?.endpoints || []) { + if (endpoint?.msAppId === appId) { + return endpoint?.endpointUrl; + } + } + } + + return undefined; +}; + export const generateBotStatusList = ( botList: Bot[], botPropertyData: BotPropertyType, @@ -35,7 +50,7 @@ export const generateBotStatusList = ( ): BotStatus[] => { const bots = botList.map((bot) => { const botStatus: BotStatus = Object.assign({}, bot); - const publishTargets = botPropertyData[bot.id].publishTargets; + const publishTargets: PublishTarget[] = botPropertyData[bot.id].publishTargets; const publishHistory = botPublishHistoryList[bot.id]; if (publishTargets.length > 0 && botStatus.publishTarget && publishHistory) { botStatus.publishTargets = publishTargets; @@ -46,7 +61,17 @@ export const generateBotStatusList = ( botStatus.message = history.message; botStatus.status = history.status; } + + const currentPublishTarget = publishTargets.find((pt) => pt.name === botStatus.publishTarget); + if (currentPublishTarget) { + const config = JSON.parse(currentPublishTarget.configuration); + const appId = config?.settings?.MicrosoftAppId; + if (appId) { + botStatus.skillManifestUrl = findSkillManifestUrl(botPropertyData[bot.id].skillManifests, appId); + } + } } + return botStatus; }); return bots; diff --git a/Composer/packages/client/src/pages/publish/type.ts b/Composer/packages/client/src/pages/publish/type.ts index 6df9e962eb..bb537f8e01 100644 --- a/Composer/packages/client/src/pages/publish/type.ts +++ b/Composer/packages/client/src/pages/publish/type.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { DialogSetting, PublishResult, PublishTarget } from '@bfc/shared'; +import { DialogSetting, PublishResult, PublishTarget, SkillManifestFile } from '@bfc/shared'; import { PublishType } from '../../recoilModel/types'; @@ -16,6 +16,10 @@ export type BotStatus = { status?: number; message?: string; comment?: string; + /** + * The skill manifest URL associated with the current publishTarget. + */ + skillManifestUrl?: string; }; export type Bot = { @@ -28,6 +32,7 @@ type BotProperty = { setting: DialogSetting; publishTargets: PublishTarget[]; publishTypes: PublishType[]; + skillManifests: SkillManifestFile[]; }; export type BotProjectType = { diff --git a/Composer/packages/client/src/recoilModel/selectors/project.ts b/Composer/packages/client/src/recoilModel/selectors/project.ts index 0de67fe70f..d248cd1061 100644 --- a/Composer/packages/client/src/recoilModel/selectors/project.ts +++ b/Composer/packages/client/src/recoilModel/selectors/project.ts @@ -181,6 +181,7 @@ export const botProjectSpaceSelector = selector({ buildEssentials, isPvaSchema, publishTypes, + skillManifests, }; }); return result; diff --git a/Composer/packages/server/src/locales/en-US.json b/Composer/packages/server/src/locales/en-US.json index ad01e7e3a4..49dff16822 100644 --- a/Composer/packages/server/src/locales/en-US.json +++ b/Composer/packages/server/src/locales/en-US.json @@ -815,6 +815,9 @@ "copy_project_location_to_clipboard_eb85c474": { "message": "Copy project location to clipboard" }, + "copy_skill_manifest_url_8f9371d0": { + "message": "Copy Skill Manifest URL" + }, "could_not_connect_to_storage_50411de0": { "message": "Could not connect to storage." }, @@ -4025,4 +4028,4 @@ "your_template_requires_qna_maker_to_access_content_a4ca6f76": { "message": "Your template requires QnA Maker to access content for your bot." } -} \ No newline at end of file +}