diff --git a/.github/actions/conventional-pr/src/utils.ts b/.github/actions/conventional-pr/src/utils.ts index 407b03953f..4e4f1a484a 100644 --- a/.github/actions/conventional-pr/src/utils.ts +++ b/.github/actions/conventional-pr/src/utils.ts @@ -20,6 +20,7 @@ const validTypes = [ 'chore', 'revert', 'release', + 'a11y', ]; const typeList = validTypes.map(t => ` - ${t}`).join('\n'); diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b3de1d17bf..2fdb51fd8e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Set Node Version uses: actions/setup-node@v1 with: @@ -56,17 +56,17 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Set Dotnet Version uses: actions/setup-dotnet@v1 with: - dotnet-version: "3.0.100" # SDK Version to use. + dotnet-version: "3.1.102" # SDK Version to use. - name: dotnet build run: dotnet build - working-directory: BotProject/CSharp + working-directory: BotProject/Templates/CSharp - name: dotnet test run: dotnet test - working-directory: BotProject/CSharp + working-directory: BotProject/Templates/CSharp docker-build: name: Docker Build @@ -75,7 +75,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: docker-compose build run: docker-compose build - name: Health check diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index b8fbbe8fc3..3d6f84f147 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v1 + uses: actions/checkout@v2 - name: Cache Node Modules uses: actions/cache@preview with: diff --git a/BotProject/CSharp/.dockerignore b/BotProject/CSharp/.dockerignore deleted file mode 100644 index 31db54001a..0000000000 --- a/BotProject/CSharp/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -**/bin -**/obj \ No newline at end of file diff --git a/BotProject/CSharp/.gitignore b/BotProject/CSharp/.gitignore deleted file mode 100644 index 1557a3a3b1..0000000000 --- a/BotProject/CSharp/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ - -!Properties/launchSettings.json -!packages/packages.json diff --git a/BotProject/CSharp/BotManager.cs b/BotProject/CSharp/BotManager.cs deleted file mode 100644 index 89b76a332b..0000000000 --- a/BotProject/CSharp/BotManager.cs +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using Microsoft.Bot.Builder.BotFramework; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; -using Microsoft.Bot.Builder.Integration.AspNet.Core; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Configuration; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class BotManager : IBotManager - { - private static readonly object Locker = new object(); - - public BotManager(IConfiguration config) - { - Config = config; - - // init work dir - WorkDir = ConvertPath("tmp"); - EnsureDirExists(WorkDir); - CleanDir(WorkDir); - - // set init bot - var bot = Config.GetSection("bot").Get(); - SetCurrent(bot); - } - - public IConfiguration Config { get; } - - public IBotFrameworkHttpAdapter CurrentAdapter { get; set; } - - public IBot CurrentBot { get; set; } - - private string WorkDir { get; } - - public static string ConvertPath(string relativePath) - { - var curDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); - return Path.Combine(curDir, relativePath); - } - - public static void EnsureDirExists(string dirPath) - { - var dirInfo = new DirectoryInfo(dirPath); - if (!dirInfo.Exists) - { - dirInfo.Create(); - } - } - - public static void CleanDir(string dirPath) - { - var dir = new DirectoryInfo(dirPath); - dir.GetFiles().ToList().ForEach(f => f.Delete()); - dir.GetDirectories().ToList().ForEach(d => d.Delete(true)); - } - - public void SetCurrent(string botDir) - { - IStorage storage = new MemoryStorage(); - var userState = new UserState(storage); - var conversationState = new ConversationState(storage); - var inspectionState = new InspectionState(storage); - - // manage all bot resources - var resourceExplorer = new ResourceExplorer().AddFolder(botDir); - - var adapter = new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(Config)); - - var credentials = new MicrosoftAppCredentials(Config["MicrosoftAppId"], Config["MicrosoftAppPassword"]); - - adapter - .UseStorage(storage) - .UseState(userState, conversationState) - .UseAdaptiveDialogs() - .UseResourceExplorer(resourceExplorer) - .UseLanguageGeneration(resourceExplorer, "common.lg") - .Use(new RegisterClassMiddleware(Config)) - .Use(new InspectionMiddleware(inspectionState, userState, conversationState, credentials)); - - adapter.OnTurnError = async (turnContext, exception) => - { - await turnContext.SendActivityAsync(exception.Message).ConfigureAwait(false); - - await conversationState.ClearStateAsync(turnContext).ConfigureAwait(false); - await conversationState.SaveChangesAsync(turnContext).ConfigureAwait(false); - }; - CurrentAdapter = adapter; - - CurrentBot = new ComposerBot("Main.dialog", conversationState, userState, resourceExplorer, DebugSupport.SourceMap); - } - - public void SetCurrent(Stream fileStream, string endpointKey = null, string appPwd = null) - { - lock (Locker) - { - // download file as tmp.zip - var downloadPath = SaveFile(fileStream, "tmp.zip").GetAwaiter().GetResult(); - - // extract to bot folder - var extractPath = ExtractFile(downloadPath, GenNewBotDir()); - - RetrieveSettingsFile(extractPath, endpointKey, appPwd); - SetCurrent(extractPath); - } - } - - public void RetrieveSettingsFile(string extractPath, string endpointKey, string appPwd) - { - var settingsPaths = Directory.GetFiles(extractPath, "appsettings.json", SearchOption.AllDirectories); - if (settingsPaths.Length == 0) - { - return; - } - - var settingsPath = settingsPaths.FirstOrDefault(); - - var settings = JsonConvert.DeserializeObject>(File.ReadAllText(settingsPath)); - - foreach (var pair in settings) - { - if (pair.Value is JObject) - { - foreach (var token in pair.Value as JObject) - { - string subkey = token.Key; - JToken subvalue = token.Value; - this.Config[$"{pair.Key}:{subkey}"] = subvalue.Value(); - } - } - else - { - this.Config[pair.Key.ToString()] = pair.Value.ToString(); - } - } - - if (!string.IsNullOrEmpty(endpointKey)) - { - var luconfigFile = JsonConvert.DeserializeObject(settings["luis"].ToString()); - AddLuisConfig(extractPath, luconfigFile, endpointKey); - } - - if (!string.IsNullOrEmpty(appPwd)) - { - AddOAuthConfig(appPwd); - } - } - - public void AddLuisConfig(string extractPath, LuisConfig luisConfig, string endpointKey) - { - var settingsName = $"luis.settings.{luisConfig.Environment}.{luisConfig.AuthoringRegion}.json"; - var luisEndpoint = $"https://{luisConfig.AuthoringRegion}.api.cognitive.microsoft.com"; - this.Config["luis:endpoint"] = luisEndpoint; - - // No luis settings - var luisPaths = Directory.GetFiles(extractPath, settingsName, SearchOption.AllDirectories); - if (luisPaths.Length == 0) - { - return; - } - - var luisPath = luisPaths[0]; - - var luisConfigJson = JsonConvert.DeserializeObject(File.ReadAllText(luisPath)); - - luisConfigJson.Luis.Add("endpointKey", endpointKey); - - foreach (var item in luisConfigJson.Luis) - { - this.Config[$"luis:{item.Key}"] = item.Value; - } - } - - private void AddOAuthConfig(string appPwd) - { - if (string.IsNullOrEmpty(appPwd)) - { - this.Config["MicrosoftAppPassword"] = string.Empty; - } - else - { - this.Config["MicrosoftAppPassword"] = appPwd; - } - } - - private string GenNewBotDir() - { - return System.Guid.NewGuid().ToString("N"); - } - - private async Task SaveFile(Stream fileStream, string fileName) - { - EnsureDirExists(WorkDir); - - var filePath = Path.Combine(WorkDir, fileName); - using (var outStream = new FileStream(filePath, FileMode.Create)) - { - await fileStream.CopyToAsync(outStream); - } - - return filePath; - } - - // Extract file to a dir - private string ExtractFile(string filePath, string dstDirPath) - { - EnsureDirExists(WorkDir); - - var finalDstPath = Path.Combine(WorkDir, dstDirPath); - - ZipFile.ExtractToDirectory(filePath, finalDstPath); - return finalDstPath; - } - } -} \ No newline at end of file diff --git a/BotProject/CSharp/BotProject.csproj b/BotProject/CSharp/BotProject.csproj deleted file mode 100644 index 4f0d7a31df..0000000000 --- a/BotProject/CSharp/BotProject.csproj +++ /dev/null @@ -1,46 +0,0 @@ - - - - netcoreapp2.1 - ade3d9c2-1633-4922-89e5-a4a50ccb3bc8 - - - - BotProject.ruleset - - - - BotProject.ruleset - - - - - - - - - - - - Always - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - - diff --git a/BotProject/CSharp/BotProject.ruleset b/BotProject/CSharp/BotProject.ruleset deleted file mode 100644 index 845cc367f1..0000000000 --- a/BotProject/CSharp/BotProject.ruleset +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/BotProject/CSharp/BotProject.sln b/BotProject/CSharp/BotProject.sln deleted file mode 100644 index db73c02cd8..0000000000 --- a/BotProject/CSharp/BotProject.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.28307.136 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BotProject", "BotProject.csproj", "{80ACF5BE-4A04-46F8-A83E-530FB21948D5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{5AFADEA2-A18F-46DF-8080-2CA418880318}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {80ACF5BE-4A04-46F8-A83E-530FB21948D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {80ACF5BE-4A04-46F8-A83E-530FB21948D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {80ACF5BE-4A04-46F8-A83E-530FB21948D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {80ACF5BE-4A04-46F8-A83E-530FB21948D5}.Release|Any CPU.Build.0 = Release|Any CPU - {5AFADEA2-A18F-46DF-8080-2CA418880318}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5AFADEA2-A18F-46DF-8080-2CA418880318}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5AFADEA2-A18F-46DF-8080-2CA418880318}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5AFADEA2-A18F-46DF-8080-2CA418880318}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {B13FC467-1A63-4C8F-A29E-43B2D8B79B17} - EndGlobalSection -EndGlobal diff --git a/BotProject/CSharp/ComposerBot.cs b/BotProject/CSharp/ComposerBot.cs deleted file mode 100644 index f8d08a5977..0000000000 --- a/BotProject/CSharp/ComposerBot.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Bot.Builder.AI.QnA; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class ComposerBot : ActivityHandler - { - private AdaptiveDialog rootDialog; - private readonly ResourceExplorer resourceExplorer; - private readonly UserState userState; - private DialogManager dialogManager; - private readonly ConversationState conversationState; - private readonly IStatePropertyAccessor dialogState; - private readonly ISourceMap sourceMap; - - public ComposerBot(string rootDialogFile, ConversationState conversationState, UserState userState, ResourceExplorer resourceExplorer, ISourceMap sourceMap) - { - this.conversationState = conversationState; - this.userState = userState; - this.dialogState = conversationState.CreateProperty("DialogState"); - this.sourceMap = sourceMap; - this.resourceExplorer = resourceExplorer; - this.RootDialogFile = rootDialogFile; - DeclarativeTypeLoader.AddComponent(new QnAMakerComponentRegistration()); - - // auto reload dialogs when file changes - this.resourceExplorer.Changed += (resources) => - { - if (resources.Any(resource => resource.Id == ".dialog")) - { - Task.Run(() => this.LoadRootDialogAsync()); - } - }; - LoadRootDialogAsync(); - } - - private string RootDialogFile { get; set; } - - public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) - { - await this.dialogManager.OnTurnAsync(turnContext, cancellationToken: cancellationToken); - await this.conversationState.SaveChangesAsync(turnContext, false, cancellationToken); - await this.userState.SaveChangesAsync(turnContext, false, cancellationToken); - } - - private void LoadRootDialogAsync() - { - var rootFile = resourceExplorer.GetResource(RootDialogFile); - rootDialog = DeclarativeTypeLoader.Load(rootFile, resourceExplorer, sourceMap); - this.dialogManager = new DialogManager(rootDialog); - } - } -} \ No newline at end of file diff --git a/BotProject/CSharp/ComposerDialogs/Main/Main.dialog b/BotProject/CSharp/ComposerDialogs/Main/Main.dialog deleted file mode 100644 index cb675db8c8..0000000000 --- a/BotProject/CSharp/ComposerDialogs/Main/Main.dialog +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$type": "Microsoft.AdaptiveDialog", - "autoEndDialog": true, - "generator": "common.lg", - "$schema": "https://raw.githubusercontent.com/microsoft/BotFramework-Composer/stable/Composer/packages/server/schemas/sdk.schema", - "triggers": [ - { - "$type": "Microsoft.OnBeginDialog", - "actions": [ - { - "$type": "Microsoft.SendActivity", - "activity": "@{bfdactivity-003038()}" - } - ] - } - ] -} diff --git a/BotProject/CSharp/ComposerDialogs/common/common.lg b/BotProject/CSharp/ComposerDialogs/common/common.lg deleted file mode 100644 index 681faf19ad..0000000000 --- a/BotProject/CSharp/ComposerDialogs/common/common.lg +++ /dev/null @@ -1,2 +0,0 @@ -# bfdactivity-003038 -- This is a default bot. diff --git a/BotProject/CSharp/Controllers/BotAdminController.cs b/BotProject/CSharp/Controllers/BotAdminController.cs deleted file mode 100644 index 25a325ed38..0000000000 --- a/BotProject/CSharp/Controllers/BotAdminController.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 - -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - [Route("api/admin")] - [ApiController] - public class BotAdminController : ControllerBase - { - private readonly BotManager botManager; - - public BotAdminController(BotManager botManager) - { - this.botManager = botManager; - } - - [HttpGet] - public IActionResult GetAsync() - { - return Ok(); - } - - [HttpPost] - public IActionResult PostAsync(IFormFile file, [FromForm]string endpointKey = null, [FromForm]string microsoftAppPassword = null) - { - if (file == null) - { - return BadRequest(); - } - - botManager.SetCurrent(file.OpenReadStream(), endpointKey, microsoftAppPassword); - - return Ok(); - } - } -} diff --git a/BotProject/CSharp/Controllers/BotController.cs b/BotProject/CSharp/Controllers/BotController.cs deleted file mode 100644 index dc1ad5c782..0000000000 --- a/BotProject/CSharp/Controllers/BotController.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// -// Generated with Bot Builder V4 SDK Template for Visual Studio EchoBot v4.3.0 - -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Integration.AspNet.Core; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - // This ASP Controller is created to handle a request. Dependency Injection will provide the Adapter and IBot - // implementation at runtime. Multiple different IBot implementations running at different endpoints can be - // achieved by specifying a more specific type for the bot constructor argument. - [Route("api/messages")] - [ApiController] - public class BotController : ControllerBase - { - private readonly BotManager botManager; - - public BotController(BotManager botManager) - { - this.botManager = botManager; - } - - [HttpPost] - [HttpGet] - public async Task PostAsync() - { - // Delegate the processing of the HTTP POST to the adapter. - // The adapter will invoke the bot. - await botManager.CurrentAdapter.ProcessAsync(Request, Response, botManager.CurrentBot); - } - } -} diff --git a/BotProject/CSharp/DeploymentTemplates/new-rg-parameters.json b/BotProject/CSharp/DeploymentTemplates/new-rg-parameters.json deleted file mode 100644 index ead3390932..0000000000 --- a/BotProject/CSharp/DeploymentTemplates/new-rg-parameters.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "groupLocation": { - "value": "" - }, - "groupName": { - "value": "" - }, - "appId": { - "value": "" - }, - "appSecret": { - "value": "" - }, - "botId": { - "value": "" - }, - "botSku": { - "value": "" - }, - "newAppServicePlanName": { - "value": "" - }, - "newAppServicePlanSku": { - "value": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - } - }, - "newAppServicePlanLocation": { - "value": "" - }, - "newWebAppName": { - "value": "" - } - } -} \ No newline at end of file diff --git a/BotProject/CSharp/DeploymentTemplates/preexisting-rg-parameters.json b/BotProject/CSharp/DeploymentTemplates/preexisting-rg-parameters.json deleted file mode 100644 index b6f5114fcc..0000000000 --- a/BotProject/CSharp/DeploymentTemplates/preexisting-rg-parameters.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "appId": { - "value": "" - }, - "appSecret": { - "value": "" - }, - "botId": { - "value": "" - }, - "botSku": { - "value": "" - }, - "newAppServicePlanName": { - "value": "" - }, - "newAppServicePlanSku": { - "value": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - } - }, - "appServicePlanLocation": { - "value": "" - }, - "existingAppServicePlan": { - "value": "" - }, - "newWebAppName": { - "value": "" - } - } -} \ No newline at end of file diff --git a/BotProject/CSharp/DeploymentTemplates/template-with-new-rg.json b/BotProject/CSharp/DeploymentTemplates/template-with-new-rg.json deleted file mode 100644 index 06b8284158..0000000000 --- a/BotProject/CSharp/DeploymentTemplates/template-with-new-rg.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "groupLocation": { - "type": "string", - "metadata": { - "description": "Specifies the location of the Resource Group." - } - }, - "groupName": { - "type": "string", - "metadata": { - "description": "Specifies the name of the Resource Group." - } - }, - "appId": { - "type": "string", - "metadata": { - "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." - } - }, - "appSecret": { - "type": "string", - "metadata": { - "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings." - } - }, - "botId": { - "type": "string", - "metadata": { - "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." - } - }, - "botSku": { - "type": "string", - "metadata": { - "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." - } - }, - "newAppServicePlanName": { - "type": "string", - "metadata": { - "description": "The name of the App Service Plan." - } - }, - "newAppServicePlanSku": { - "type": "object", - "defaultValue": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - }, - "metadata": { - "description": "The SKU of the App Service Plan. Defaults to Standard values." - } - }, - "newAppServicePlanLocation": { - "type": "string", - "metadata": { - "description": "The location of the App Service Plan. Defaults to \"westus\"." - } - }, - "newWebAppName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." - } - } - }, - "variables": { - "appServicePlanName": "[parameters('newAppServicePlanName')]", - "resourcesLocation": "[parameters('newAppServicePlanLocation')]", - "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", - "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", - "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" - }, - "resources": [ - { - "name": "[parameters('groupName')]", - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2018-05-01", - "location": "[parameters('groupLocation')]", - "properties": { - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2018-05-01", - "name": "storageDeployment", - "resourceGroup": "[parameters('groupName')]", - "dependsOn": [ - "[resourceId('Microsoft.Resources/resourceGroups/', parameters('groupName'))]" - ], - "properties": { - "mode": "Incremental", - "template": { - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": {}, - "variables": {}, - "resources": [ - { - "comments": "Create a new App Service Plan", - "type": "Microsoft.Web/serverfarms", - "name": "[variables('appServicePlanName')]", - "apiVersion": "2018-02-01", - "location": "[variables('resourcesLocation')]", - "sku": "[parameters('newAppServicePlanSku')]", - "properties": { - "name": "[variables('appServicePlanName')]" - } - }, - { - "comments": "Create a Web App using the new App Service Plan", - "type": "Microsoft.Web/sites", - "apiVersion": "2015-08-01", - "location": "[variables('resourcesLocation')]", - "kind": "app", - "dependsOn": [ - "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]" - ], - "name": "[variables('webAppName')]", - "properties": { - "name": "[variables('webAppName')]", - "serverFarmId": "[variables('appServicePlanName')]", - "siteConfig": { - "appSettings": [ - { - "name": "WEBSITE_NODE_DEFAULT_VERSION", - "value": "10.14.1" - }, - { - "name": "MicrosoftAppId", - "value": "[parameters('appId')]" - }, - { - "name": "MicrosoftAppPassword", - "value": "[parameters('appSecret')]" - } - ], - "cors": { - "allowedOrigins": [ - "https://botservice.hosting.portal.azure.net", - "https://hosting.onecloud.azure-test.net/" - ] - } - } - } - }, - { - "apiVersion": "2017-12-01", - "type": "Microsoft.BotService/botServices", - "name": "[parameters('botId')]", - "location": "global", - "kind": "bot", - "sku": { - "name": "[parameters('botSku')]" - }, - "properties": { - "name": "[parameters('botId')]", - "displayName": "[parameters('botId')]", - "endpoint": "[variables('botEndpoint')]", - "msaAppId": "[parameters('appId')]", - "developerAppInsightsApplicationId": null, - "developerAppInsightKey": null, - "publishingCredentials": null, - "storageResourceId": null - }, - "dependsOn": [ - "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" - ] - } - ], - "outputs": {} - } - } - } - ] -} \ No newline at end of file diff --git a/BotProject/CSharp/DeploymentTemplates/template-with-preexisting-rg.json b/BotProject/CSharp/DeploymentTemplates/template-with-preexisting-rg.json deleted file mode 100644 index 43943b6581..0000000000 --- a/BotProject/CSharp/DeploymentTemplates/template-with-preexisting-rg.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "appId": { - "type": "string", - "metadata": { - "description": "Active Directory App ID, set as MicrosoftAppId in the Web App's Application Settings." - } - }, - "appSecret": { - "type": "string", - "metadata": { - "description": "Active Directory App Password, set as MicrosoftAppPassword in the Web App's Application Settings. Defaults to \"\"." - } - }, - "botId": { - "type": "string", - "metadata": { - "description": "The globally unique and immutable bot ID. Also used to configure the displayName of the bot, which is mutable." - } - }, - "botSku": { - "defaultValue": "F0", - "type": "string", - "metadata": { - "description": "The pricing tier of the Bot Service Registration. Acceptable values are F0 and S1." - } - }, - "newAppServicePlanName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The name of the new App Service Plan." - } - }, - "newAppServicePlanSku": { - "type": "object", - "defaultValue": { - "name": "S1", - "tier": "Standard", - "size": "S1", - "family": "S", - "capacity": 1 - }, - "metadata": { - "description": "The SKU of the App Service Plan. Defaults to Standard values." - } - }, - "appServicePlanLocation": { - "type": "string", - "metadata": { - "description": "The location of the App Service Plan." - } - }, - "existingAppServicePlan": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "Name of the existing App Service Plan used to create the Web App for the bot." - } - }, - "newWebAppName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The globally unique name of the Web App. Defaults to the value passed in for \"botId\"." - } - } - }, - "variables": { - "defaultAppServicePlanName": "[if(empty(parameters('existingAppServicePlan')), 'createNewAppServicePlan', parameters('existingAppServicePlan'))]", - "useExistingAppServicePlan": "[not(equals(variables('defaultAppServicePlanName'), 'createNewAppServicePlan'))]", - "servicePlanName": "[if(variables('useExistingAppServicePlan'), parameters('existingAppServicePlan'), parameters('newAppServicePlanName'))]", - "resourcesLocation": "[parameters('appServicePlanLocation')]", - "webAppName": "[if(empty(parameters('newWebAppName')), parameters('botId'), parameters('newWebAppName'))]", - "siteHost": "[concat(variables('webAppName'), '.azurewebsites.net')]", - "botEndpoint": "[concat('https://', variables('siteHost'), '/api/messages')]" - }, - "resources": [ - { - "comments": "Create a new App Service Plan if no existing App Service Plan name was passed in.", - "type": "Microsoft.Web/serverfarms", - "condition": "[not(variables('useExistingAppServicePlan'))]", - "name": "[variables('servicePlanName')]", - "apiVersion": "2018-02-01", - "location": "[variables('resourcesLocation')]", - "sku": "[parameters('newAppServicePlanSku')]", - "properties": { - "name": "[variables('servicePlanName')]" - } - }, - { - "comments": "Create a Web App using an App Service Plan", - "type": "Microsoft.Web/sites", - "apiVersion": "2015-08-01", - "location": "[variables('resourcesLocation')]", - "kind": "app", - "dependsOn": [ - "[resourceId('Microsoft.Web/serverfarms/', variables('servicePlanName'))]" - ], - "name": "[variables('webAppName')]", - "properties": { - "name": "[variables('webAppName')]", - "serverFarmId": "[variables('servicePlanName')]", - "siteConfig": { - "appSettings": [ - { - "name": "WEBSITE_NODE_DEFAULT_VERSION", - "value": "10.14.1" - }, - { - "name": "MicrosoftAppId", - "value": "[parameters('appId')]" - }, - { - "name": "MicrosoftAppPassword", - "value": "[parameters('appSecret')]" - } - ], - "cors": { - "allowedOrigins": [ - "https://botservice.hosting.portal.azure.net", - "https://hosting.onecloud.azure-test.net/" - ] - } - } - } - }, - { - "apiVersion": "2017-12-01", - "type": "Microsoft.BotService/botServices", - "name": "[parameters('botId')]", - "location": "global", - "kind": "bot", - "sku": { - "name": "[parameters('botSku')]" - }, - "properties": { - "name": "[parameters('botId')]", - "displayName": "[parameters('botId')]", - "endpoint": "[variables('botEndpoint')]", - "msaAppId": "[parameters('appId')]", - "developerAppInsightsApplicationId": null, - "developerAppInsightKey": null, - "publishingCredentials": null, - "storageResourceId": null - }, - "dependsOn": [ - "[resourceId('Microsoft.Web/sites/', variables('webAppName'))]" - ] - } - ] -} \ No newline at end of file diff --git a/BotProject/CSharp/IBotManager.cs b/BotProject/CSharp/IBotManager.cs deleted file mode 100644 index 99e421c818..0000000000 --- a/BotProject/CSharp/IBotManager.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.IO; -using Microsoft.Bot.Builder.Integration.AspNet.Core; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public interface IBotManager - { - IBotFrameworkHttpAdapter CurrentAdapter { get; } - - IBot CurrentBot { get; } - - void SetCurrent(Stream fileStream, string endpointKey = null, string appPwd = null); - } -} diff --git a/BotProject/CSharp/LuisConfig.cs b/BotProject/CSharp/LuisConfig.cs deleted file mode 100644 index 506d52e343..0000000000 --- a/BotProject/CSharp/LuisConfig.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Collections.Generic; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class LuisConfig - { - public string Name { get; set; } - - public string DefaultLanguage { get; set; } - - public List Models { get; set; } - - public string AuthoringKey { get; set; } - - public bool Dialogs { get; set; } - - public string Environment { get; set; } - - public bool Autodelete { get; set; } - - public string AuthoringRegion { get; set; } - - public string Folder { get; set; } - - public bool Help { get; set; } - - public bool Force { get; set; } - - public string Config { get; set; } - - public string EndpointKeys { get; set; } - } -} diff --git a/BotProject/CSharp/LuisCustomConfig.cs b/BotProject/CSharp/LuisCustomConfig.cs deleted file mode 100644 index 5b468ccdc7..0000000000 --- a/BotProject/CSharp/LuisCustomConfig.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Collections.Generic; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class LuisCustomConfig - { - public Dictionary Luis { get; set; } - } -} diff --git a/BotProject/CSharp/LuisKey.cs b/BotProject/CSharp/LuisKey.cs deleted file mode 100644 index e8564950e1..0000000000 --- a/BotProject/CSharp/LuisKey.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class LuisKey - { - public string Key { get; set; } - } -} diff --git a/BotProject/CSharp/NuGet.Config b/BotProject/CSharp/NuGet.Config deleted file mode 100644 index 11ff1952c1..0000000000 --- a/BotProject/CSharp/NuGet.Config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/BotProject/CSharp/Program.cs b/BotProject/CSharp/Program.cs deleted file mode 100644 index d2fa81364c..0000000000 --- a/BotProject/CSharp/Program.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.IO; -using Microsoft.AspNetCore; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class Program - { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((hostingContext, config) => - { - var env = hostingContext.HostingEnvironment; - var luisAuthoringRegion = Environment.GetEnvironmentVariable("LUIS_AUTHORING_REGION") ?? "westus"; - config - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) - .AddJsonFile($"luis.settings.{env.EnvironmentName}.{luisAuthoringRegion}.json", optional: true, reloadOnChange: true) - .AddJsonFile($"luis.settings.{Environment.UserName}.{luisAuthoringRegion}.json", optional: true, reloadOnChange: true); - - if (env.IsDevelopment()) - { - config.AddUserSecrets(); - } - - config - .AddEnvironmentVariables() - .AddCommandLine(args); - }).UseStartup() - .Build(); - } -} diff --git a/BotProject/CSharp/Properties/launchSettings.json b/BotProject/CSharp/Properties/launchSettings.json deleted file mode 100644 index 33c8346152..0000000000 --- a/BotProject/CSharp/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:3979/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "BotProject": { - "commandName": "Project", - "launchBrowser": true, - "applicationUrl": "http://localhost:3979", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} \ No newline at end of file diff --git a/BotProject/CSharp/README.md b/BotProject/CSharp/README.md deleted file mode 100644 index 7f9cbbefb2..0000000000 --- a/BotProject/CSharp/README.md +++ /dev/null @@ -1,55 +0,0 @@ -## Bot Project -Bot project is the launcher project for the bots written in declarative form (JSON), using the Composer, for the Bot Framework SDK. - -## Instructions for setting up the Bot Project runtime -The Bot Project is a regular Bot Framework SDK V4 project. Before you can launch it from the emulator, you need to make sure you can run the bot. - -### Prerequisite: -* Install .Netcore 2 - -### Commands: - -* from root folder -* cd BotProject -* cd CSharp -* dotnet restore // for the package updates -* dotnet build // build -* dotnet run // start the bot -* It will start a web server and listening at http://localhost:3979. - -### Test bot -* You can set you emulator to connect to http://localhost:3979/api/messages. - -### config your bot -This setup is required for local testing of your Bot Runtime. -* The only thing you need to config is appsetting.json, which has a bot setting to launch the bot: - -``` -appsettings.json: -"bot": { - "provider": "localDisk", - "path": "../../Bots/SampleBot3/bot3.botproj" -} -``` - -## .botproj folder structure -``` -bot.botproj, bot project got the rootDialog from "entry" -{ - "services": [{ - "type": "luis", - "id": "1", - "name": "TodoBotLuis", - "lufile": "todo.lu", - "applicationId": "TodoBotLuis.applicationId", - "endpointKey": "TodoBotLuis.endpointKey", - "endpoint": "TodoBotLuis.endpoint" - }], - "files": [ - "*.dialog", - "*.lg" - ], - "entry": "main.dialog" -} -``` -* Please refer to [Samples](https://github.com/Microsoft/BotFramework-Composer/tree/master/SampleBots) for more samples. diff --git a/BotProject/CSharp/Schemas/sdk.schema b/BotProject/CSharp/Schemas/sdk.schema deleted file mode 100644 index fc90997234..0000000000 --- a/BotProject/CSharp/Schemas/sdk.schema +++ /dev/null @@ -1,6998 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/microsoft/botbuilder-dotnet/4.Future/schemas/component.schema", - "$id": "sdk.schema", - "type": "object", - "title": "Component types", - "description": "These are all of the types that can be created by the loader.", - "oneOf": [ - { - "title": "Microsoft.ActivityTemplate", - "description": "", - "$ref": "#/definitions/Microsoft.ActivityTemplate" - }, - { - "title": "Microsoft.AdaptiveDialog", - "description": "Flexible, data driven dialog that can adapt to the conversation.", - "$ref": "#/definitions/Microsoft.AdaptiveDialog" - }, - { - "title": "Microsoft.AgeEntityRecognizer", - "description": "Recognizer which recognizes age.", - "$ref": "#/definitions/Microsoft.AgeEntityRecognizer" - }, - { - "title": "Microsoft.AttachmentInput", - "description": "Collect information - Ask for a file or image.", - "$ref": "#/definitions/Microsoft.AttachmentInput" - }, - { - "title": "Microsoft.BeginDialog", - "description": "Begin another dialog.", - "$ref": "#/definitions/Microsoft.BeginDialog" - }, - { - "title": "Microsoft.CancelAllDialogs", - "description": "Cancel all active dialogs. All dialogs in the dialog chain will need a trigger to capture the event configured in this action.", - "$ref": "#/definitions/Microsoft.CancelAllDialogs" - }, - { - "title": "Microsoft.ChoiceInput", - "description": "Collect information - Pick from a list of choices", - "$ref": "#/definitions/Microsoft.ChoiceInput" - }, - { - "title": "Microsoft.ConditionalSelector", - "description": "Use a rule selector based on a condition", - "$ref": "#/definitions/Microsoft.ConditionalSelector" - }, - { - "title": "Microsoft.ConfirmInput", - "description": "Collect information - Ask for confirmation (yes or no).", - "$ref": "#/definitions/Microsoft.ConfirmInput" - }, - { - "title": "Microsoft.ConfirmationEntityRecognizer", - "description": "Recognizer which recognizes confirmation choices (yes/no).", - "$ref": "#/definitions/Microsoft.ConfirmationEntityRecognizer" - }, - { - "title": "Microsoft.CurrencyEntityRecognizer", - "description": "Recognizer which recognizes currency.", - "$ref": "#/definitions/Microsoft.CurrencyEntityRecognizer" - }, - { - "title": "Microsoft.DateTimeEntityRecognizer", - "description": "Recognizer which recognizes dates and time fragments.", - "$ref": "#/definitions/Microsoft.DateTimeEntityRecognizer" - }, - { - "title": "Microsoft.DateTimeInput", - "description": "Collect information - Ask for date and/ or time", - "$ref": "#/definitions/Microsoft.DateTimeInput" - }, - { - "title": "Microsoft.DebugBreak", - "description": "If debugger is attached, stop the execution at this point in the conversation.", - "$ref": "#/definitions/Microsoft.DebugBreak" - }, - { - "title": "Microsoft.DeleteProperty", - "description": "Delete a property and any value it holds.", - "$ref": "#/definitions/Microsoft.DeleteProperty" - }, - { - "title": "Microsoft.DimensionEntityRecognizer", - "description": "Recognizer which recognizes dimension.", - "$ref": "#/definitions/Microsoft.DimensionEntityRecognizer" - }, - { - "title": "Microsoft.EditActions", - "description": "Edit the current list of actions.", - "$ref": "#/definitions/Microsoft.EditActions" - }, - { - "title": "Microsoft.EditArray", - "description": "Modify an array in memory", - "$ref": "#/definitions/Microsoft.EditArray" - }, - { - "title": "Microsoft.EmailEntityRecognizer", - "description": "Recognizer which recognizes email.", - "$ref": "#/definitions/Microsoft.EmailEntityRecognizer" - }, - { - "title": "Microsoft.EmitEvent", - "description": "Emit an event. Capture this event with a trigger.", - "$ref": "#/definitions/Microsoft.EmitEvent" - }, - { - "title": "Microsoft.EndDialog", - "description": "End this dialog.", - "$ref": "#/definitions/Microsoft.EndDialog" - }, - { - "title": "Microsoft.EndTurn", - "description": "End the current turn without ending the dialog.", - "$ref": "#/definitions/Microsoft.EndTurn" - }, - { - "title": "Microsoft.FirstSelector", - "description": "Selector for first true rule", - "$ref": "#/definitions/Microsoft.FirstSelector" - }, - { - "title": "Microsoft.Foreach", - "description": "Execute actions on each item in an a collection.", - "$ref": "#/definitions/Microsoft.Foreach" - }, - { - "title": "Microsoft.ForeachPage", - "description": "Execute actions on each page (collection of items) in an array.", - "$ref": "#/definitions/Microsoft.ForeachPage" - }, - { - "title": "Microsoft.GuidEntityRecognizer", - "description": "Recognizer which recognizes guids.", - "$ref": "#/definitions/Microsoft.GuidEntityRecognizer" - }, - { - "title": "Microsoft.HashtagEntityRecognizer", - "description": "Recognizer which recognizes Hashtags.", - "$ref": "#/definitions/Microsoft.HashtagEntityRecognizer" - }, - { - "title": "Microsoft.HttpRequest", - "description": "Make a HTTP request.", - "$ref": "#/definitions/Microsoft.HttpRequest" - }, - { - "title": "Microsoft.IfCondition", - "description": "Two-way branch the conversation flow based on a condition.", - "$ref": "#/definitions/Microsoft.IfCondition" - }, - { - "title": "Microsoft.InitProperty", - "description": "Define and initialize a property to be an array or object.", - "$ref": "#/definitions/Microsoft.InitProperty" - }, - { - "title": "Microsoft.IpEntityRecognizer", - "description": "Recognizer which recognizes internet IP patterns (like 192.1.1.1).", - "$ref": "#/definitions/Microsoft.IpEntityRecognizer" - }, - { - "title": "Microsoft.LanguagePolicy", - "description": "This represents a policy map for locales lookups to use for language", - "$ref": "#/definitions/Microsoft.LanguagePolicy" - }, - { - "title": "Microsoft.LogAction", - "description": "Log a message to the host application. Send a TraceActivity to Bot Framework Emulator (optional).", - "$ref": "#/definitions/Microsoft.LogAction" - }, - { - "title": "Microsoft.LuisRecognizer", - "description": "LUIS recognizer.", - "$ref": "#/definitions/Microsoft.LuisRecognizer" - }, - { - "title": "Microsoft.MentionEntityRecognizer", - "description": "Recognizer which recognizes @Mentions", - "$ref": "#/definitions/Microsoft.MentionEntityRecognizer" - }, - { - "title": "Microsoft.MostSpecificSelector", - "description": "Select most specific true events with optional additional selector", - "$ref": "#/definitions/Microsoft.MostSpecificSelector" - }, - { - "title": "Microsoft.MultiLanguageRecognizer", - "description": "Configure one recognizer per language and the specify the language fallback policy.", - "$ref": "#/definitions/Microsoft.MultiLanguageRecognizer" - }, - { - "title": "Microsoft.NumberEntityRecognizer", - "description": "Recognizer which recognizes numbers.", - "$ref": "#/definitions/Microsoft.NumberEntityRecognizer" - }, - { - "title": "Microsoft.NumberInput", - "description": "Collect information - Ask for a number.", - "$ref": "#/definitions/Microsoft.NumberInput" - }, - { - "title": "Microsoft.NumberRangeEntityRecognizer", - "description": "Recognizer which recognizes ranges of numbers (Example:2 to 5).", - "$ref": "#/definitions/Microsoft.NumberRangeEntityRecognizer" - }, - { - "title": "Microsoft.OAuthInput", - "description": "Collect login information.", - "$ref": "#/definitions/Microsoft.OAuthInput" - }, - { - "title": "Microsoft.OnActivity", - "description": "Actions to perform on receipt of a generic activity.", - "$ref": "#/definitions/Microsoft.OnActivity" - }, - { - "title": "Microsoft.OnBeginDialog", - "description": "Actions to perform when this dialog begins.", - "$ref": "#/definitions/Microsoft.OnBeginDialog" - }, - { - "title": "Microsoft.OnCancelDialog", - "description": "Actions to perform on cancel dialog event.", - "$ref": "#/definitions/Microsoft.OnCancelDialog" - }, - { - "title": "Microsoft.OnCondition", - "description": "Actions to perform when specified condition is true.", - "$ref": "#/definitions/Microsoft.OnCondition" - }, - { - "title": "Microsoft.OnConversationUpdateActivity", - "description": "Actions to perform on receipt of an activity with type 'ConversationUpdate'.", - "$ref": "#/definitions/Microsoft.OnConversationUpdateActivity" - }, - { - "title": "Microsoft.OnCustomEvent", - "description": "Actions to perform when a custom event is detected. Use 'Emit a custom event' action to raise a custom event.", - "$ref": "#/definitions/Microsoft.OnCustomEvent" - }, - { - "title": "Microsoft.OnDialogEvent", - "description": "Actions to perform when a specific dialog event occurs.", - "$ref": "#/definitions/Microsoft.OnDialogEvent" - }, - { - "title": "Microsoft.OnEndOfConversationActivity", - "description": "Actions to perform on receipt of an activity with type 'EndOfConversation'.", - "$ref": "#/definitions/Microsoft.OnEndOfConversationActivity" - }, - { - "title": "Microsoft.OnError", - "description": "Action to perform when an 'Error' dialog event occurs.", - "$ref": "#/definitions/Microsoft.OnError" - }, - { - "title": "Microsoft.OnEventActivity", - "description": "Actions to perform on receipt of an activity with type 'Event'.", - "$ref": "#/definitions/Microsoft.OnEventActivity" - }, - { - "title": "Microsoft.OnHandoffActivity", - "description": "Actions to perform on receipt of an activity with type 'HandOff'.", - "$ref": "#/definitions/Microsoft.OnHandoffActivity" - }, - { - "title": "Microsoft.OnIntent", - "description": "Actions to perform when specified intent is recognized.", - "$ref": "#/definitions/Microsoft.OnIntent" - }, - { - "title": "Microsoft.OnInvokeActivity", - "description": "Actions to perform on receipt of an activity with type 'Invoke'.", - "$ref": "#/definitions/Microsoft.OnInvokeActivity" - }, - { - "title": "Microsoft.OnMessageActivity", - "description": "Actions to perform on receipt of an activity with type 'Message'. Overrides Intent trigger.", - "$ref": "#/definitions/Microsoft.OnMessageActivity" - }, - { - "title": "Microsoft.OnMessageDeleteActivity", - "description": "Actions to perform on receipt of an activity with type 'MessageDelete'.", - "$ref": "#/definitions/Microsoft.OnMessageDeleteActivity" - }, - { - "title": "Microsoft.OnMessageReactionActivity", - "description": "Actions to perform on receipt of an activity with type 'MessageReaction'.", - "$ref": "#/definitions/Microsoft.OnMessageReactionActivity" - }, - { - "title": "Microsoft.OnMessageUpdateActivity", - "description": "Actions to perform on receipt of an activity with type 'MessageUpdate'.", - "$ref": "#/definitions/Microsoft.OnMessageUpdateActivity" - }, - { - "title": "Microsoft.OnRepromptDialog", - "description": "Actions to perform when 'RepromptDialog' event occurs.", - "$ref": "#/definitions/Microsoft.OnRepromptDialog" - }, - { - "title": "Microsoft.OnTypingActivity", - "description": "Actions to perform on receipt of an activity with type 'Typing'.", - "$ref": "#/definitions/Microsoft.OnTypingActivity" - }, - { - "title": "Microsoft.OnUnknownIntent", - "description": "Action to perform when user input is unrecognized and if none of the 'on intent recognition' triggers match recognized intent.", - "$ref": "#/definitions/Microsoft.OnUnknownIntent" - }, - { - "title": "Microsoft.OrdinalEntityRecognizer", - "description": "Recognizer which recognizes ordinals (example: first, second, 3rd).", - "$ref": "#/definitions/Microsoft.OrdinalEntityRecognizer" - }, - { - "title": "Microsoft.PercentageEntityRecognizer", - "description": "Recognizer which recognizes percentages.", - "$ref": "#/definitions/Microsoft.PercentageEntityRecognizer" - }, - { - "title": "Microsoft.PhoneNumberEntityRecognizer", - "description": "Recognizer which recognizes phone numbers.", - "$ref": "#/definitions/Microsoft.PhoneNumberEntityRecognizer" - }, - { - "title": "Microsoft.QnAMakerDialog", - "description": "Dialog which uses QnAMAker knowledge base to answer questions.", - "$ref": "#/definitions/Microsoft.QnAMakerDialog" - }, - { - "title": "Microsoft.RandomSelector", - "description": "Select most specific true rule", - "$ref": "#/definitions/Microsoft.RandomSelector" - }, - { - "title": "Microsoft.RegExEntityRecognizer", - "description": "Recognizer which recognizes patterns of input based on regex.", - "$ref": "#/definitions/Microsoft.RegExEntityRecognizer" - }, - { - "title": "Microsoft.RegexRecognizer", - "description": "Use regular expressions to recognize intents and entities from user input.", - "$ref": "#/definitions/Microsoft.RegexRecognizer" - }, - { - "title": "Microsoft.RepeatDialog", - "description": "Repeat current dialog.", - "$ref": "#/definitions/Microsoft.RepeatDialog" - }, - { - "title": "Microsoft.ReplaceDialog", - "description": "Replace current dialog with another dialog.", - "$ref": "#/definitions/Microsoft.ReplaceDialog" - }, - { - "title": "Microsoft.SendActivity", - "description": "Respond with an activity.", - "$ref": "#/definitions/Microsoft.SendActivity" - }, - { - "title": "Microsoft.SetProperty", - "description": "Set property to a value.", - "$ref": "#/definitions/Microsoft.SetProperty" - }, - { - "title": "Microsoft.StaticActivityTemplate", - "description": "This allows you to define a static Activity object", - "$ref": "#/definitions/Microsoft.StaticActivityTemplate" - }, - { - "title": "Microsoft.SwitchCondition", - "description": "Execute different actions based on the value of a property.", - "$ref": "#/definitions/Microsoft.SwitchCondition" - }, - { - "title": "Microsoft.TemperatureEntityRecognizer", - "description": "Recognizer which recognizes temperatures.", - "$ref": "#/definitions/Microsoft.TemperatureEntityRecognizer" - }, - { - "title": "Microsoft.TextInput", - "description": "Collection information - Ask for a word or sentence.", - "$ref": "#/definitions/Microsoft.TextInput" - }, - { - "title": "Microsoft.TextTemplate", - "description": "Lg tempalte to evaluate to create text", - "$ref": "#/definitions/Microsoft.TextTemplate" - }, - { - "title": "Microsoft.TraceActivity", - "description": "Send a trace activity to the transcript logger and/ or Bot Framework Emulator.", - "$ref": "#/definitions/Microsoft.TraceActivity" - }, - { - "title": "Microsoft.TrueSelector", - "description": "Selector for all true events", - "$ref": "#/definitions/Microsoft.TrueSelector" - }, - { - "title": "Microsoft.UrlEntityRecognizer", - "description": "Recognizer which recognizes urls (example: http://bing.com)", - "$ref": "#/definitions/Microsoft.UrlEntityRecognizer" - } - ], - "definitions": { - "Microsoft.ActivityTemplate": { - "$role": "unionType(Microsoft.IActivityTemplate)", - "title": "Microsoft ActivityTemplate", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ActivityTemplate" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "template": { - "title": "Template", - "Description": "Language Generator template to use to create the activity", - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "template", - "$type" - ] - } - ] - }, - "Microsoft.AdaptiveDialog": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Adaptive Dialog", - "description": "Flexible, data driven dialog that can adapt to the conversation.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.AdaptiveDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional dialog ID." - }, - "autoEndDialog": { - "type": "boolean", - "title": "Auto end dialog", - "description": "If set to true the dialog will automatically end when there are no further actions. If set to false, remember to manually end the dialog using EndDialog action.", - "default": "true" - }, - "defaultResultProperty": { - "type": "string", - "title": "Default result property", - "description": "Value that will be passed back to the parent dialog.", - "default": "dialog.result" - }, - "recognizer": { - "$type": "Microsoft.IRecognizer", - "title": "Recognizer", - "description": "Language Understanding recognizer that interprets user input into intent and entities.", - "$ref": "#/definitions/Microsoft.IRecognizer" - }, - "generator": { - "$type": "Microsoft.ILanguageGenerator", - "title": "Language Generator", - "description": "Language generator that generates bot responses.", - "$ref": "#/definitions/Microsoft.ILanguageGenerator" - }, - "selector": { - "$type": "Microsoft.ITriggerSelector", - "title": "Selector", - "description": "Policy to determine which trigger is executed. Defaults to a 'best match' selector (optional).", - "$ref": "#/definitions/Microsoft.ITriggerSelector" - }, - "triggers": { - "type": "array", - "description": "List of triggers defined for this dialog.", - "title": "Triggers", - "items": { - "$type": "Microsoft.ITriggerCondition", - "$ref": "#/definitions/Microsoft.ITriggerCondition" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.AgeEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Age Entity Recognizer", - "description": "Recognizer which recognizes age.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.AgeEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.AttachmentInput": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Attachment input dialog", - "description": "Collect information - Ask for a file or image.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.AttachmentInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional id for the dialog" - }, - "prompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Initial prompt", - "description": "Message to send to collect information.", - "examples": [ - "What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "unrecognizedPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", - "examples": [ - "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "invalidPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", - "examples": [ - "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "defaultValueResponse": { - "$type": "Microsoft.IActivityTemplate", - "title": "Default value response", - "description": "Message to send when max turn count (if specified) has been exceeded and the default value is selected as the value.", - "examples": [ - "Sorry, I'm having trouble understanding you. I will just use {this.options.defaultValue} for now. You can say 'I'm 36 years old' to change it." - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "maxTurnCount": { - "type": "integer", - "title": "Max turn count", - "description": "Maximum number of re-prompt attempts to collect information.", - "default": 3, - "examples": [ - 3 - ] - }, - "validations": { - "type": "array", - "title": "Validation expressions", - "description": "Expression to validate user input.", - "examples": [ - "int(this.value) > 1 && int(this.value) <= 150", - "count(this.value) < 300" - ], - "items": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - } - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to store collected information. Input will be skipped if property has value (unless 'Always prompt' is true).", - "examples": [ - "$birthday", - "user.name", - "conversation.issueTitle", - "dialog.favColor" - ], - "type": "string" - }, - "defaultValue": { - "$role": "expression", - "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", - "examples": [ - "@userName", - "coalesce(@number, @partySize)" - ], - "type": "string" - }, - "alwaysPrompt": { - "type": "boolean", - "title": "Always prompt", - "description": "Collect information even if the specified 'property' is not empty.", - "default": false, - "examples": [ - false - ] - }, - "allowInterruptions": { - "type": "string", - "title": "Allow Interruptions", - "description": "A boolean expression that determines whether the parent should be allowed to interrupt the input.", - "default": "true", - "examples": [ - "true" - ] - }, - "outputFormat": { - "type": "string", - "enum": [ - "all", - "first" - ], - "title": "Output format", - "description": "Attachment output format.", - "default": "first" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.BeginDialog": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Begin a dialog", - "description": "Begin another dialog.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.BeginDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "dialog": { - "$type": "Microsoft.IDialog", - "title": "Dialog name", - "description": "Name of the dialog to call.", - "examples": [ - "AddToDoDialog" - ], - "$ref": "#/definitions/Microsoft.IDialog" - }, - "options": { - "type": "object", - "title": "Options", - "description": "One or more options that are passed to the dialog that is called.", - "additionalProperties": { - "type": "string", - "title": "Options" - } - }, - "resultProperty": { - "$role": "expression", - "title": "Property", - "description": "Property to store any value returned by the dialog that is called.", - "examples": [ - "dialog.userName" - ], - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.CancelAllDialogs": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Cancel all dialogs", - "description": "Cancel all active dialogs. All dialogs in the dialog chain will need a trigger to capture the event configured in this action.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.CancelAllDialogs" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "eventName": { - "title": "Event name", - "description": "Name of the event to emit.", - "type": "string" - }, - "eventValue": { - "type": "object", - "title": "Event value", - "description": "Value to emit with the event (optional).", - "additionalProperties": true - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.ChoiceInput": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Choice input dialog", - "description": "Collect information - Pick from a list of choices", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ChoiceInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional id for the dialog" - }, - "prompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Initial prompt", - "description": "Message to send to collect information.", - "examples": [ - "What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "unrecognizedPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", - "examples": [ - "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "invalidPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", - "examples": [ - "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "defaultValueResponse": { - "$type": "Microsoft.IActivityTemplate", - "title": "Default value response", - "description": "Message to send when max turn count (if specified) has been exceeded and the default value is selected as the value.", - "examples": [ - "Sorry, I'm having trouble understanding you. I will just use {this.options.defaultValue} for now. You can say 'I'm 36 years old' to change it." - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "maxTurnCount": { - "type": "integer", - "title": "Max turn count", - "description": "Maximum number of re-prompt attempts to collect information.", - "default": 3, - "examples": [ - 3 - ] - }, - "validations": { - "type": "array", - "title": "Validation expressions", - "description": "Expression to validate user input.", - "examples": [ - "int(this.value) > 1 && int(this.value) <= 150", - "count(this.value) < 300" - ], - "items": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - } - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to store collected information. Input will be skipped if property has value (unless 'Always prompt' is true).", - "examples": [ - "$birthday", - "user.name", - "conversation.issueTitle", - "dialog.favColor" - ], - "type": "string" - }, - "defaultValue": { - "$role": "expression", - "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", - "examples": [ - "@userName", - "coalesce(@number, @partySize)" - ], - "type": "string" - }, - "alwaysPrompt": { - "type": "boolean", - "title": "Always prompt", - "description": "Collect information even if the specified 'property' is not empty.", - "default": false, - "examples": [ - false - ] - }, - "allowInterruptions": { - "type": "string", - "title": "Allow Interruptions", - "description": "A boolean expression that determines whether the parent should be allowed to interrupt the input.", - "default": "true", - "examples": [ - "true" - ] - }, - "outputFormat": { - "type": "string", - "enum": [ - "value", - "index" - ], - "title": "Output format", - "description": "Choice output format.", - "default": "value" - }, - "choices": { - "anyOf": [ - { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - }, - { - "type": "array", - "items": [ - { - "type": "string" - } - ] - }, - { - "type": "array", - "items": [ - { - "title": "Choice", - "type": "object", - "properties": { - "value": { - "type": "string", - "title": "Value", - "description": "Value to return when this choice is selected." - }, - "action": { - "type": "object", - "title": "Action", - "description": "Card action for the choice." - }, - "synonyms": { - "type": "array", - "title": "Synonyms", - "description": "List of synonyms to recognize in addition to the value (optional).", - "items": { - "type": "string" - } - } - } - } - ] - } - ] - }, - "appendChoices": { - "type": "boolean", - "title": "Append choices", - "description": "Compose an output activity containing a set of choices", - "default": "true" - }, - "defaultLocale": { - "type": "string", - "title": "Default locale", - "description": "Default locale.", - "default": "en-us" - }, - "style": { - "type": "string", - "enum": [ - "None", - "Auto", - "Inline", - "List", - "SuggestedAction", - "HeroCard" - ], - "title": "List style", - "description": "Style to render choices.", - "default": "Auto" - }, - "choiceOptions": { - "type": "object", - "properties": { - "inlineSeparator": { - "type": "string", - "title": "Inline separator", - "description": "Character used to separate individual choices when there are more than 2 choices", - "default": ", " - }, - "inlineOr": { - "type": "string", - "title": "Inline or", - "description": "Separator inserted between the choices when there are only 2 choices", - "default": " or " - }, - "inlineOrMore": { - "type": "string", - "title": "Inline or more", - "description": "Separator inserted between the last 2 choices when their are more than 2 choices.", - "default": ", or " - }, - "includeNumbers": { - "type": "boolean", - "title": "Include numbers", - "description": "If true, 'inline' and 'list' list style will be prefixed with the index of the choice.", - "default": true - } - } - }, - "recognizerOptions": { - "type": "object", - "properties": { - "noValue": { - "type": "boolean", - "title": "No value", - "description": "If true, the choices value field will NOT be search over", - "default": false - }, - "noAction": { - "type": "boolean", - "title": "No action", - "description": "If true, the the choices action.title field will NOT be searched over", - "default": false - } - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.ConditionalSelector": { - "$role": "unionType(Microsoft.ITriggerSelector)", - "title": "Condtional Trigger Selector", - "description": "Use a rule selector based on a condition", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ConditionalSelector" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - }, - "ifTrue": { - "$type": "Microsoft.ITriggerSelector", - "$ref": "#/definitions/Microsoft.ITriggerSelector" - }, - "ifFalse": { - "$type": "Microsoft.ITriggerSelector", - "$ref": "#/definitions/Microsoft.ITriggerSelector" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "condition", - "ifTrue", - "ifFalse", - "$type" - ] - } - ] - }, - "Microsoft.ConfirmInput": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Confirm input dialog", - "description": "Collect information - Ask for confirmation (yes or no).", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ConfirmInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional id for the dialog" - }, - "prompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Initial prompt", - "description": "Message to send to collect information.", - "examples": [ - "What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "unrecognizedPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", - "examples": [ - "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "invalidPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", - "examples": [ - "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "defaultValueResponse": { - "$type": "Microsoft.IActivityTemplate", - "title": "Default value response", - "description": "Message to send when max turn count (if specified) has been exceeded and the default value is selected as the value.", - "examples": [ - "Sorry, I'm having trouble understanding you. I will just use {this.options.defaultValue} for now. You can say 'I'm 36 years old' to change it." - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "maxTurnCount": { - "type": "integer", - "title": "Max turn count", - "description": "Maximum number of re-prompt attempts to collect information.", - "default": 3, - "examples": [ - 3 - ] - }, - "validations": { - "type": "array", - "title": "Validation expressions", - "description": "Expression to validate user input.", - "examples": [ - "int(this.value) > 1 && int(this.value) <= 150", - "count(this.value) < 300" - ], - "items": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - } - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to store collected information. Input will be skipped if property has value (unless 'Always prompt' is true).", - "examples": [ - "$birthday", - "user.name", - "conversation.issueTitle", - "dialog.favColor" - ], - "type": "string" - }, - "defaultValue": { - "$role": "expression", - "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", - "examples": [ - "@userName", - "coalesce(@number, @partySize)" - ], - "type": "string" - }, - "alwaysPrompt": { - "type": "boolean", - "title": "Always prompt", - "description": "Collect information even if the specified 'property' is not empty.", - "default": false, - "examples": [ - false - ] - }, - "allowInterruptions": { - "type": "string", - "title": "Allow Interruptions", - "description": "A boolean expression that determines whether the parent should be allowed to interrupt the input.", - "default": "true", - "examples": [ - "true" - ] - }, - "defaultLocale": { - "type": "string", - "title": "Default locale", - "description": "Default locale.", - "default": "en-us" - }, - "style": { - "type": "string", - "enum": [ - "None", - "Auto", - "Inline", - "List", - "SuggestedAction", - "HeroCard" - ], - "title": "List style", - "description": "Style to render choices.", - "default": "Auto" - }, - "choiceOptions": { - "type": "object", - "properties": { - "inlineSeparator": { - "type": "string", - "title": "Inline separator", - "description": "Character used to separate individual choices when there are more than 2 choices", - "default": ", " - }, - "inlineOr": { - "type": "string", - "title": "Inline or", - "description": "Separator inserted between the choices when their are only 2 choices", - "default": " or " - }, - "inlineOrMore": { - "type": "string", - "title": "Inline or more", - "description": "Separator inserted between the last 2 choices when their are more than 2 choices.", - "default": ", or " - }, - "includeNumbers": { - "type": "boolean", - "title": "Include numbers", - "description": "If true, inline and list style choices will be prefixed with the index of the choice.", - "default": true - } - } - }, - "confirmChoices": { - "type": "array", - "items": [ - { - "type": "object", - "properties": { - "value": { - "type": "string", - "title": "Value", - "description": "Value to return when this choice is selected." - }, - "action": { - "type": "object", - "title": "Action", - "description": "Card action for the choice" - }, - "synonyms": { - "type": "array", - "title": "Synonyms", - "description": "List of synonyms to recognize in addition to the value (optional)", - "items": { - "type": "string" - } - } - } - } - ] - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.ConfirmationEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Confirmation Entity Recognizer", - "description": "Recognizer which recognizes confirmation choices (yes/no).", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ConfirmationEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.CurrencyEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Currency Entity Recognizer", - "description": "Recognizer which recognizes currency.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.CurrencyEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.DateTimeEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "DateTime Entity Recognizer", - "description": "Recognizer which recognizes dates and time fragments.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.DateTimeEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.DateTimeInput": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Date/time input dialog", - "description": "Collect information - Ask for date and/ or time", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.DateTimeInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional id for the dialog" - }, - "prompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Initial prompt", - "description": "Message to send to collect information.", - "examples": [ - "What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "unrecognizedPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", - "examples": [ - "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "invalidPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", - "examples": [ - "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "defaultValueResponse": { - "$type": "Microsoft.IActivityTemplate", - "title": "Default value response", - "description": "Message to send when max turn count (if specified) has been exceeded and the default value is selected as the value.", - "examples": [ - "Sorry, I'm having trouble understanding you. I will just use {this.options.defaultValue} for now. You can say 'I'm 36 years old' to change it." - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "maxTurnCount": { - "type": "integer", - "title": "Max turn count", - "description": "Maximum number of re-prompt attempts to collect information.", - "default": 3, - "examples": [ - 3 - ] - }, - "validations": { - "type": "array", - "title": "Validation expressions", - "description": "Expression to validate user input.", - "examples": [ - "int(this.value) > 1 && int(this.value) <= 150", - "count(this.value) < 300" - ], - "items": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - } - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to store collected information. Input will be skipped if property has value (unless 'Always prompt' is true).", - "examples": [ - "$birthday", - "user.name", - "conversation.issueTitle", - "dialog.favColor" - ], - "type": "string" - }, - "defaultValue": { - "$role": "expression", - "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", - "examples": [ - "@userName", - "coalesce(@number, @partySize)" - ], - "type": "string" - }, - "alwaysPrompt": { - "type": "boolean", - "title": "Always prompt", - "description": "Collect information even if the specified 'property' is not empty.", - "default": false, - "examples": [ - false - ] - }, - "allowInterruptions": { - "type": "string", - "title": "Allow Interruptions", - "description": "A boolean expression that determines whether the parent should be allowed to interrupt the input.", - "default": "true", - "examples": [ - "true" - ] - }, - "defaultLocale": { - "type": "string", - "title": "Default locale", - "description": "Default locale.", - "default": "en-us" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.DebugBreak": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Debugger break", - "description": "If debugger is attached, stop the execution at this point in the conversation.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.DebugBreak" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.DeleteProperty": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Delete Property", - "description": "Delete a property and any value it holds.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.DeleteProperty" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to delete.", - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "property", - "$type" - ] - } - ] - }, - "Microsoft.DimensionEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Dimension Entity Recognizer", - "description": "Recognizer which recognizes dimension.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.DimensionEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.EditActions": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Edit actions.", - "description": "Edit the current list of actions.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.EditActions" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "changeType": { - "type": "string", - "title": "Type of change", - "description": "Type of change to apply to the current actions.", - "enum": [ - "InsertActions", - "InsertActionsBeforeTags", - "AppendActions", - "EndSequence", - "ReplaceSequence" - ] - }, - "actions": { - "type": "array", - "title": "Actions", - "description": "Actions to apply.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "changeType", - "actions", - "$type" - ] - } - ] - }, - "Microsoft.EditArray": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Edit array", - "description": "Modify an array in memory", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.EditArray" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "changeType": { - "type": "string", - "title": "Type of change", - "description": "Type of change to the array in memory.", - "enum": [ - "Push", - "Pop", - "Take", - "Remove", - "Clear" - ] - }, - "itemsProperty": { - "$role": "expression", - "title": "Items property", - "description": "Property that holds the array to update.", - "type": "string" - }, - "resultProperty": { - "$role": "expression", - "title": "Result Property", - "description": "Property to store the result of this action.", - "type": "string" - }, - "value": { - "$role": "expression", - "title": "Value", - "description": "New value or expression.", - "examples": [ - "'milk'", - "dialog.favColor", - "dialog.favColor == 'red'" - ], - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "changeType", - "itemsProperty", - "$type" - ] - } - ] - }, - "Microsoft.EmailEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Email Entity Recognizer", - "description": "Recognizer which recognizes email.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.EmailEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.EmitEvent": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Emit a custom event", - "description": "Emit an event. Capture this event with a trigger.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.EmitEvent" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "eventName": { - "title": "Event name", - "description": "Name of the event to emit.", - "anyOf": [ - { - "enum": [ - "beginDialog", - "resumeDialog", - "repromptDialog", - "cancelDialog", - "endDialog", - "activityReceived", - "recognizedIntent", - "unknownIntent", - "actionsStarted", - "actionsSaved", - "actionsEnded", - "actionsResumed" - ] - }, - { - "type": "string" - } - ] - }, - "eventValue": { - "type": "object", - "title": "Event value", - "description": "Value to emit with the event (optional).", - "additionalProperties": true - }, - "bubbleEvent": { - "type": "boolean", - "title": "Bubble event", - "description": "If true this event is passed on to parent dialogs." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "eventName", - "$type" - ] - } - ] - }, - "Microsoft.EndDialog": { - "$role": "unionType(Microsoft.IDialog)", - "title": "End dialog", - "description": "End this dialog.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.EndDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "value": { - "$role": "expression", - "title": "Value", - "description": "Result value returned to the parent dialog.", - "examples": [ - "dialog.userName", - "'tomato'" - ], - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.EndTurn": { - "$role": "unionType(Microsoft.IDialog)", - "title": "End turn", - "description": "End the current turn without ending the dialog.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.EndTurn" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.EntityRecognizers": { - "$role": "unionType", - "title": "Entity Recognizers", - "description": "Union of components which derive from EntityRecognizer abstract class.", - "type": "object", - "oneOf": [ - { - "title": "Microsoft.AgeEntityRecognizer", - "description": "Recognizer which recognizes age.", - "$ref": "#/definitions/Microsoft.AgeEntityRecognizer" - }, - { - "title": "Microsoft.ConfirmationEntityRecognizer", - "description": "Recognizer which recognizes confirmation choices (yes/no).", - "$ref": "#/definitions/Microsoft.ConfirmationEntityRecognizer" - }, - { - "title": "Microsoft.CurrencyEntityRecognizer", - "description": "Recognizer which recognizes currency.", - "$ref": "#/definitions/Microsoft.CurrencyEntityRecognizer" - }, - { - "title": "Microsoft.DateTimeEntityRecognizer", - "description": "Recognizer which recognizes dates and time fragments.", - "$ref": "#/definitions/Microsoft.DateTimeEntityRecognizer" - }, - { - "title": "Microsoft.DimensionEntityRecognizer", - "description": "Recognizer which recognizes dimension.", - "$ref": "#/definitions/Microsoft.DimensionEntityRecognizer" - }, - { - "title": "Microsoft.EmailEntityRecognizer", - "description": "Recognizer which recognizes email.", - "$ref": "#/definitions/Microsoft.EmailEntityRecognizer" - }, - { - "title": "Microsoft.GuidEntityRecognizer", - "description": "Recognizer which recognizes guids.", - "$ref": "#/definitions/Microsoft.GuidEntityRecognizer" - }, - { - "title": "Microsoft.HashtagEntityRecognizer", - "description": "Recognizer which recognizes Hashtags.", - "$ref": "#/definitions/Microsoft.HashtagEntityRecognizer" - }, - { - "title": "Microsoft.IpEntityRecognizer", - "description": "Recognizer which recognizes internet IP patterns (like 192.1.1.1).", - "$ref": "#/definitions/Microsoft.IpEntityRecognizer" - }, - { - "title": "Microsoft.MentionEntityRecognizer", - "description": "Recognizer which recognizes @Mentions", - "$ref": "#/definitions/Microsoft.MentionEntityRecognizer" - }, - { - "title": "Microsoft.NumberEntityRecognizer", - "description": "Recognizer which recognizes numbers.", - "$ref": "#/definitions/Microsoft.NumberEntityRecognizer" - }, - { - "title": "Microsoft.NumberRangeEntityRecognizer", - "description": "Recognizer which recognizes ranges of numbers (Example:2 to 5).", - "$ref": "#/definitions/Microsoft.NumberRangeEntityRecognizer" - }, - { - "title": "Microsoft.OrdinalEntityRecognizer", - "description": "Recognizer which recognizes ordinals (example: first, second, 3rd).", - "$ref": "#/definitions/Microsoft.OrdinalEntityRecognizer" - }, - { - "title": "Microsoft.PercentageEntityRecognizer", - "description": "Recognizer which recognizes percentages.", - "$ref": "#/definitions/Microsoft.PercentageEntityRecognizer" - }, - { - "title": "Microsoft.PhoneNumberEntityRecognizer", - "description": "Recognizer which recognizes phone numbers.", - "$ref": "#/definitions/Microsoft.PhoneNumberEntityRecognizer" - }, - { - "title": "Microsoft.RegExEntityRecognizer", - "description": "Recognizer which recognizes patterns of input based on regex.", - "$ref": "#/definitions/Microsoft.RegExEntityRecognizer" - }, - { - "title": "Microsoft.TemperatureEntityRecognizer", - "description": "Recognizer which recognizes temperatures.", - "$ref": "#/definitions/Microsoft.TemperatureEntityRecognizer" - }, - { - "title": "Microsoft.UrlEntityRecognizer", - "description": "Recognizer which recognizes urls (example: http://bing.com)", - "$ref": "#/definitions/Microsoft.UrlEntityRecognizer" - } - ] - }, - "Microsoft.FirstSelector": { - "$role": "unionType(Microsoft.ITriggerSelector)", - "title": "First Trigger Selector", - "description": "Selector for first true rule", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.FirstSelector" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.Foreach": { - "$role": "unionType(Microsoft.IDialog)", - "title": "For each item", - "description": "Execute actions on each item in an a collection.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.Foreach" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "itemsProperty": { - "$role": "expression", - "title": "Items property", - "description": "Property that holds the array.", - "examples": [ - "user.todoList" - ], - "type": "string" - }, - "actions": { - "type": "array", - "title": "Actions", - "description": "Actions to execute for each item. Use '$foreach.value' to access the value of each item. Use '$foreach.index' to access the index of each item.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "itemsProperty", - "actions", - "$type" - ] - } - ] - }, - "Microsoft.ForeachPage": { - "$role": "unionType(Microsoft.IDialog)", - "title": "For each page", - "description": "Execute actions on each page (collection of items) in an array.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ForeachPage" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "itemsProperty": { - "$role": "expression", - "title": "Items property", - "description": "Property that holds the array.", - "examples": [ - "user.todoList" - ], - "type": "string" - }, - "actions": { - "type": "array", - "title": "Actions", - "description": "Actions to execute for each page. Use '$foreach.page' to access each page.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - }, - "pageSize": { - "type": "integer", - "title": "Page size", - "description": "Number of items in each page.", - "default": 10 - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "itemsProperty", - "actions", - "$type" - ] - } - ] - }, - "Microsoft.GuidEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Guid Entity Recognizer", - "description": "Recognizer which recognizes guids.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.GuidEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.HashtagEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Hashtag Entity Recognizer", - "description": "Recognizer which recognizes Hashtags.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.HashtagEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.HttpRequest": { - "$role": "unionType(Microsoft.IDialog)", - "type": "object", - "title": "HTTP request", - "description": "Make a HTTP request.", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.HttpRequest" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "method": { - "type": "string", - "title": "HTTP method", - "description": "HTTP method to use.", - "enum": [ - "GET", - "POST", - "PATCH", - "PUT", - "DELETE" - ], - "examples": [ - "GET", - "POST" - ] - }, - "url": { - "type": "string", - "title": "Url", - "description": "URL to call (supports data binding).", - "examples": [ - "https://contoso.com" - ] - }, - "body": { - "type": "object", - "title": "Body", - "description": "Body to include in the HTTP call (supports data binding).", - "additionalProperties": true - }, - "resultProperty": { - "$role": "expression", - "title": "Result property", - "description": "Property to store the result of this action. The result includes 4 properties from the http response: statusCode, reasonPhrase, content and headers. If the content is json it will be a deserialized object.", - "examples": [ - "dialog.contosodata" - ], - "type": "string" - }, - "headers": { - "type": "object", - "additionProperties": true, - "title": "Headers", - "description": "One or more headers to include in the request (supports data binding)." - }, - "responseType": { - "type": "string", - "title": "Response type", - "description": "Defines the type of HTTP response. Automatically calls the 'Send a response' action if set to 'Activity' or 'Activities'.", - "enum": [ - "None", - "Json", - "Activity", - "Activities" - ], - "default": "Json" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "url", - "method", - "$type" - ] - } - ] - }, - "Microsoft.IActivityTemplate": { - "title": "Microsoft ActivityTemplates", - "description": "Components which are IActivityTemplates", - "$role": "unionType", - "oneOf": [ - { - "title": "Microsoft.ActivityTemplate", - "description": "", - "$ref": "#/definitions/Microsoft.ActivityTemplate" - }, - { - "title": "Microsoft.StaticActivityTemplate", - "description": "This allows you to define a static Activity object", - "$ref": "#/definitions/Microsoft.StaticActivityTemplate" - }, - { - "type": "string", - "title": "string" - } - ] - }, - "Microsoft.IDialog": { - "title": "Microsoft Dialogs", - "description": "Union of components which implement the Dialog contract", - "$role": "unionType", - "oneOf": [ - { - "title": "Microsoft.AdaptiveDialog", - "description": "Flexible, data driven dialog that can adapt to the conversation.", - "$ref": "#/definitions/Microsoft.AdaptiveDialog" - }, - { - "title": "Microsoft.AttachmentInput", - "description": "Collect information - Ask for a file or image.", - "$ref": "#/definitions/Microsoft.AttachmentInput" - }, - { - "title": "Microsoft.BeginDialog", - "description": "Begin another dialog.", - "$ref": "#/definitions/Microsoft.BeginDialog" - }, - { - "title": "Microsoft.CancelAllDialogs", - "description": "Cancel all active dialogs. All dialogs in the dialog chain will need a trigger to capture the event configured in this action.", - "$ref": "#/definitions/Microsoft.CancelAllDialogs" - }, - { - "title": "Microsoft.ChoiceInput", - "description": "Collect information - Pick from a list of choices", - "$ref": "#/definitions/Microsoft.ChoiceInput" - }, - { - "title": "Microsoft.ConfirmInput", - "description": "Collect information - Ask for confirmation (yes or no).", - "$ref": "#/definitions/Microsoft.ConfirmInput" - }, - { - "title": "Microsoft.DateTimeInput", - "description": "Collect information - Ask for date and/ or time", - "$ref": "#/definitions/Microsoft.DateTimeInput" - }, - { - "title": "Microsoft.DebugBreak", - "description": "If debugger is attached, stop the execution at this point in the conversation.", - "$ref": "#/definitions/Microsoft.DebugBreak" - }, - { - "title": "Microsoft.DeleteProperty", - "description": "Delete a property and any value it holds.", - "$ref": "#/definitions/Microsoft.DeleteProperty" - }, - { - "title": "Microsoft.EditActions", - "description": "Edit the current list of actions.", - "$ref": "#/definitions/Microsoft.EditActions" - }, - { - "title": "Microsoft.EditArray", - "description": "Modify an array in memory", - "$ref": "#/definitions/Microsoft.EditArray" - }, - { - "title": "Microsoft.EmitEvent", - "description": "Emit an event. Capture this event with a trigger.", - "$ref": "#/definitions/Microsoft.EmitEvent" - }, - { - "title": "Microsoft.EndDialog", - "description": "End this dialog.", - "$ref": "#/definitions/Microsoft.EndDialog" - }, - { - "title": "Microsoft.EndTurn", - "description": "End the current turn without ending the dialog.", - "$ref": "#/definitions/Microsoft.EndTurn" - }, - { - "title": "Microsoft.Foreach", - "description": "Execute actions on each item in an a collection.", - "$ref": "#/definitions/Microsoft.Foreach" - }, - { - "title": "Microsoft.ForeachPage", - "description": "Execute actions on each page (collection of items) in an array.", - "$ref": "#/definitions/Microsoft.ForeachPage" - }, - { - "title": "Microsoft.HttpRequest", - "description": "Make a HTTP request.", - "$ref": "#/definitions/Microsoft.HttpRequest" - }, - { - "title": "Microsoft.IfCondition", - "description": "Two-way branch the conversation flow based on a condition.", - "$ref": "#/definitions/Microsoft.IfCondition" - }, - { - "title": "Microsoft.InitProperty", - "description": "Define and initialize a property to be an array or object.", - "$ref": "#/definitions/Microsoft.InitProperty" - }, - { - "title": "Microsoft.LogAction", - "description": "Log a message to the host application. Send a TraceActivity to Bot Framework Emulator (optional).", - "$ref": "#/definitions/Microsoft.LogAction" - }, - { - "title": "Microsoft.NumberInput", - "description": "Collect information - Ask for a number.", - "$ref": "#/definitions/Microsoft.NumberInput" - }, - { - "title": "Microsoft.OAuthInput", - "description": "Collect login information.", - "$ref": "#/definitions/Microsoft.OAuthInput" - }, - { - "title": "Microsoft.QnAMakerDialog", - "description": "Dialog which uses QnAMAker knowledge base to answer questions.", - "$ref": "#/definitions/Microsoft.QnAMakerDialog" - }, - { - "title": "Microsoft.RepeatDialog", - "description": "Repeat current dialog.", - "$ref": "#/definitions/Microsoft.RepeatDialog" - }, - { - "title": "Microsoft.ReplaceDialog", - "description": "Replace current dialog with another dialog.", - "$ref": "#/definitions/Microsoft.ReplaceDialog" - }, - { - "title": "Microsoft.SendActivity", - "description": "Respond with an activity.", - "$ref": "#/definitions/Microsoft.SendActivity" - }, - { - "title": "Microsoft.SetProperty", - "description": "Set property to a value.", - "$ref": "#/definitions/Microsoft.SetProperty" - }, - { - "title": "Microsoft.SwitchCondition", - "description": "Execute different actions based on the value of a property.", - "$ref": "#/definitions/Microsoft.SwitchCondition" - }, - { - "title": "Microsoft.TextInput", - "description": "Collection information - Ask for a word or sentence.", - "$ref": "#/definitions/Microsoft.TextInput" - }, - { - "title": "Microsoft.TraceActivity", - "description": "Send a trace activity to the transcript logger and/ or Bot Framework Emulator.", - "$ref": "#/definitions/Microsoft.TraceActivity" - }, - { - "type": "string", - "title": "string" - } - ] - }, - "Microsoft.ILanguageGenerator": { - "title": "Microsoft ILanguageGenerator", - "description": "Union of components which implement the ILanguageGenerator interface", - "$role": "unionType", - "oneOf": [ - { - "type": "string", - "title": "string" - } - ] - }, - "Microsoft.IRecognizer": { - "title": "Microsoft IRecognizer", - "description": "Union of components which implement the IRecognizer interface", - "$role": "unionType", - "oneOf": [ - { - "title": "Microsoft.LuisRecognizer", - "description": "LUIS recognizer.", - "$ref": "#/definitions/Microsoft.LuisRecognizer" - }, - { - "title": "Microsoft.MultiLanguageRecognizer", - "description": "Configure one recognizer per language and the specify the language fallback policy.", - "$ref": "#/definitions/Microsoft.MultiLanguageRecognizer" - }, - { - "title": "Microsoft.RegexRecognizer", - "description": "Use regular expressions to recognize intents and entities from user input.", - "$ref": "#/definitions/Microsoft.RegexRecognizer" - }, - { - "type": "string", - "title": "string" - } - ] - }, - "Microsoft.ITextTemplate": { - "title": "Microsoft TextTemplate", - "description": "Union of components which implement the TextTemplate", - "$role": "unionType", - "oneOf": [ - { - "title": "Microsoft.TextTemplate", - "description": "Lg tempalte to evaluate to create text", - "$ref": "#/definitions/Microsoft.TextTemplate" - }, - { - "type": "string", - "title": "string" - } - ] - }, - "Microsoft.ITriggerCondition": { - "$role": "unionType", - "title": "Microsoft Triggers", - "description": "Union of components which implement the OnCondition", - "oneOf": [ - { - "title": "Microsoft.OnActivity", - "description": "Actions to perform on receipt of a generic activity.", - "$ref": "#/definitions/Microsoft.OnActivity" - }, - { - "title": "Microsoft.OnBeginDialog", - "description": "Actions to perform when this dialog begins.", - "$ref": "#/definitions/Microsoft.OnBeginDialog" - }, - { - "title": "Microsoft.OnCancelDialog", - "description": "Actions to perform on cancel dialog event.", - "$ref": "#/definitions/Microsoft.OnCancelDialog" - }, - { - "title": "Microsoft.OnCondition", - "description": "Actions to perform when specified condition is true.", - "$ref": "#/definitions/Microsoft.OnCondition" - }, - { - "title": "Microsoft.OnConversationUpdateActivity", - "description": "Actions to perform on receipt of an activity with type 'ConversationUpdate'.", - "$ref": "#/definitions/Microsoft.OnConversationUpdateActivity" - }, - { - "title": "Microsoft.OnCustomEvent", - "description": "Actions to perform when a custom event is detected. Use 'Emit a custom event' action to raise a custom event.", - "$ref": "#/definitions/Microsoft.OnCustomEvent" - }, - { - "title": "Microsoft.OnDialogEvent", - "description": "Actions to perform when a specific dialog event occurs.", - "$ref": "#/definitions/Microsoft.OnDialogEvent" - }, - { - "title": "Microsoft.OnEndOfConversationActivity", - "description": "Actions to perform on receipt of an activity with type 'EndOfConversation'.", - "$ref": "#/definitions/Microsoft.OnEndOfConversationActivity" - }, - { - "title": "Microsoft.OnError", - "description": "Action to perform when an 'Error' dialog event occurs.", - "$ref": "#/definitions/Microsoft.OnError" - }, - { - "title": "Microsoft.OnEventActivity", - "description": "Actions to perform on receipt of an activity with type 'Event'.", - "$ref": "#/definitions/Microsoft.OnEventActivity" - }, - { - "title": "Microsoft.OnHandoffActivity", - "description": "Actions to perform on receipt of an activity with type 'HandOff'.", - "$ref": "#/definitions/Microsoft.OnHandoffActivity" - }, - { - "title": "Microsoft.OnIntent", - "description": "Actions to perform when specified intent is recognized.", - "$ref": "#/definitions/Microsoft.OnIntent" - }, - { - "title": "Microsoft.OnInvokeActivity", - "description": "Actions to perform on receipt of an activity with type 'Invoke'.", - "$ref": "#/definitions/Microsoft.OnInvokeActivity" - }, - { - "title": "Microsoft.OnMessageActivity", - "description": "Actions to perform on receipt of an activity with type 'Message'. Overrides Intent trigger.", - "$ref": "#/definitions/Microsoft.OnMessageActivity" - }, - { - "title": "Microsoft.OnMessageDeleteActivity", - "description": "Actions to perform on receipt of an activity with type 'MessageDelete'.", - "$ref": "#/definitions/Microsoft.OnMessageDeleteActivity" - }, - { - "title": "Microsoft.OnMessageReactionActivity", - "description": "Actions to perform on receipt of an activity with type 'MessageReaction'.", - "$ref": "#/definitions/Microsoft.OnMessageReactionActivity" - }, - { - "title": "Microsoft.OnMessageUpdateActivity", - "description": "Actions to perform on receipt of an activity with type 'MessageUpdate'.", - "$ref": "#/definitions/Microsoft.OnMessageUpdateActivity" - }, - { - "title": "Microsoft.OnRepromptDialog", - "description": "Actions to perform when 'RepromptDialog' event occurs.", - "$ref": "#/definitions/Microsoft.OnRepromptDialog" - }, - { - "title": "Microsoft.OnTypingActivity", - "description": "Actions to perform on receipt of an activity with type 'Typing'.", - "$ref": "#/definitions/Microsoft.OnTypingActivity" - }, - { - "title": "Microsoft.OnUnknownIntent", - "description": "Action to perform when user input is unrecognized and if none of the 'on intent recognition' triggers match recognized intent.", - "$ref": "#/definitions/Microsoft.OnUnknownIntent" - } - ] - }, - "Microsoft.ITriggerSelector": { - "$role": "unionType", - "title": "Selectors", - "description": "Union of components which are trigger selectors", - "oneOf": [ - { - "title": "Microsoft.ConditionalSelector", - "description": "Use a rule selector based on a condition", - "$ref": "#/definitions/Microsoft.ConditionalSelector" - }, - { - "title": "Microsoft.FirstSelector", - "description": "Selector for first true rule", - "$ref": "#/definitions/Microsoft.FirstSelector" - }, - { - "title": "Microsoft.MostSpecificSelector", - "description": "Select most specific true events with optional additional selector", - "$ref": "#/definitions/Microsoft.MostSpecificSelector" - }, - { - "title": "Microsoft.RandomSelector", - "description": "Select most specific true rule", - "$ref": "#/definitions/Microsoft.RandomSelector" - }, - { - "title": "Microsoft.TrueSelector", - "description": "Selector for all true events", - "$ref": "#/definitions/Microsoft.TrueSelector" - } - ] - }, - "Microsoft.IfCondition": { - "$role": "unionType(Microsoft.IDialog)", - "title": "If condition", - "description": "Two-way branch the conversation flow based on a condition.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.IfCondition" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Expression to evaluate.", - "examples": [ - "user.age > 3" - ], - "type": "string" - }, - "actions": { - "type": "array", - "title": "Actions", - "description": "Actions to execute if condition is true.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - }, - "elseActions": { - "type": "array", - "title": "Else", - "description": "Actions to execute if condition is false.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "condition", - "actions", - "$type" - ] - } - ] - }, - "Microsoft.InitProperty": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Initialize property", - "description": "Define and initialize a property to be an array or object.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.InitProperty" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property (named location to store information).", - "examples": [ - "user.age" - ], - "type": "string" - }, - "type": { - "type": "string", - "title": "Type", - "description": "Type of value.", - "enum": [ - "object", - "array" - ] - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "property", - "type", - "$type" - ] - } - ] - }, - "Microsoft.IpEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Ip Entity Recognizer", - "description": "Recognizer which recognizes internet IP patterns (like 192.1.1.1).", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.IpEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.LanguagePolicy": { - "title": "Language Policy", - "description": "This represents a policy map for locales lookups to use for language", - "type": "object", - "additionalProperties": false, - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.LanguagePolicy" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.LogAction": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Log to console", - "description": "Log a message to the host application. Send a TraceActivity to Bot Framework Emulator (optional).", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.LogAction" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "text": { - "type": "string", - "title": "Text", - "description": "Information to log." - }, - "traceActivity": { - "type": "boolean", - "title": "Send Trace Activity", - "description": "If true, automatically sends a TraceActivity (view in Bot Framework Emulator).", - "default": false - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "text", - "$type" - ] - } - ] - }, - "Microsoft.LuisRecognizer": { - "$role": "unionType(Microsoft.IRecognizer)", - "title": "LUIS Recognizer", - "description": "LUIS recognizer.", - "type": "object", - "additionalProperties": false, - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.LuisRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "applicationId": { - "type": "string" - }, - "endpoint": { - "type": "string" - }, - "endpointKey": { - "type": "string" - } - }, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "applicationId", - "endpoint", - "endpointKey", - "$type" - ] - } - ] - }, - "Microsoft.MentionEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Mentions Entity Recognizer", - "description": "Recognizer which recognizes @Mentions", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.MentionEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.MostSpecificSelector": { - "$role": "unionType(Microsoft.ITriggerSelector)", - "title": "Most Specific Trigger Selector", - "description": "Select most specific true events with optional additional selector", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.MostSpecificSelector" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "selector": { - "$type": "Microsoft.ITriggerSelector", - "$ref": "#/definitions/Microsoft.ITriggerSelector" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.MultiLanguageRecognizer": { - "$role": "unionType(Microsoft.IRecognizer)", - "title": "Multi-language recognizer", - "description": "Configure one recognizer per language and the specify the language fallback policy.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.MultiLanguageRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "languagePolicy": { - "$type": "Microsoft.LanguagePolicy", - "type": "object", - "title": "Language policy", - "description": "Defines fall back languages to try per user input language.", - "$ref": "#/definitions/Microsoft.LanguagePolicy" - }, - "recognizers": { - "type": "object", - "title": "Recognizers", - "description": "Map of language -> IRecognizer", - "additionalProperties": { - "$type": "Microsoft.IRecognizer", - "$ref": "#/definitions/Microsoft.IRecognizer" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "recognizers", - "$type" - ] - } - ] - }, - "Microsoft.NumberEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Number Entity Recognizer", - "description": "Recognizer which recognizes numbers.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.NumberEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.NumberInput": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Number input dialog", - "description": "Collect information - Ask for a number.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.NumberInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional id for the dialog" - }, - "prompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Initial prompt", - "description": "Message to send to collect information.", - "examples": [ - "What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "unrecognizedPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", - "examples": [ - "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "invalidPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", - "examples": [ - "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "defaultValueResponse": { - "$type": "Microsoft.IActivityTemplate", - "title": "Default value response", - "description": "Message to send when max turn count (if specified) has been exceeded and the default value is selected as the value.", - "examples": [ - "Sorry, I'm having trouble understanding you. I will just use {this.options.defaultValue} for now. You can say 'I'm 36 years old' to change it." - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "maxTurnCount": { - "type": "integer", - "title": "Max turn count", - "description": "Maximum number of re-prompt attempts to collect information.", - "default": 3, - "examples": [ - 3 - ] - }, - "validations": { - "type": "array", - "title": "Validation expressions", - "description": "Expression to validate user input.", - "examples": [ - "int(this.value) > 1 && int(this.value) <= 150", - "count(this.value) < 300" - ], - "items": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - } - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to store collected information. Input will be skipped if property has value (unless 'Always prompt' is true).", - "examples": [ - "$birthday", - "user.name", - "conversation.issueTitle", - "dialog.favColor" - ], - "type": "string" - }, - "defaultValue": { - "$role": "expression", - "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", - "examples": [ - "@userName", - "coalesce(@number, @partySize)" - ], - "type": "string" - }, - "alwaysPrompt": { - "type": "boolean", - "title": "Always prompt", - "description": "Collect information even if the specified 'property' is not empty.", - "default": false, - "examples": [ - false - ] - }, - "allowInterruptions": { - "type": "string", - "title": "Allow Interruptions", - "description": "A boolean expression that determines whether the parent should be allowed to interrupt the input.", - "default": "true", - "examples": [ - "true" - ] - }, - "outputFormat": { - "type": "string", - "enum": [ - "float", - "integer" - ], - "title": "Output format", - "description": "Number output format.", - "default": "float" - }, - "defaultLocale": { - "type": "string", - "title": "Default locale", - "description": "Default locale.", - "default": "en-us" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.NumberRangeEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "NumberRange Entity Recognizer", - "description": "Recognizer which recognizes ranges of numbers (Example:2 to 5).", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.NumberRangeEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.OAuthInput": { - "$role": "unionType(Microsoft.IDialog)", - "title": "OAuthInput Dialog", - "description": "Collect login information.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OAuthInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "connectionName": { - "type": "string", - "title": "Connection name", - "description": "The connection name configured in Azure Web App Bot OAuth settings.", - "examples": [ - "msgraphOAuthConnection" - ] - }, - "text": { - "type": "string", - "title": "Text", - "description": "Text shown in the OAuth signin card.", - "examples": [ - "Please sign in. " - ] - }, - "title": { - "type": "string", - "title": "Title", - "description": "Title shown in the OAuth signin card.", - "examples": [ - "Login" - ] - }, - "timeout": { - "type": "integer", - "title": "Timeout", - "description": "Time out setting for the OAuth signin card.", - "default": "900000" - }, - "tokenProperty": { - "$role": "expression", - "title": "Token property", - "description": "Property to store the OAuth token result.", - "examples": [ - "dialog.token" - ], - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "connectionName", - "$type" - ] - } - ] - }, - "Microsoft.OnActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On activity", - "description": "Actions to perform on receipt of a generic activity.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - }, - "type": { - "type": "string", - "title": "Activity type", - "description": "The Activity.Type to match" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "type", - "$type" - ] - } - ] - }, - "Microsoft.OnBeginDialog": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On begin dialog", - "description": "Actions to perform when this dialog begins.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnBeginDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnCancelDialog": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On cancel dialog", - "description": "Actions to perform on cancel dialog event.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnCancelDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnCondition": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On condition", - "description": "Actions to perform when specified condition is true.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnCondition" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnConversationUpdateActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On ConversationUpdate activity", - "description": "Actions to perform on receipt of an activity with type 'ConversationUpdate'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnConversationUpdateActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnCustomEvent": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On custom event", - "description": "Actions to perform when a custom event is detected. Use 'Emit a custom event' action to raise a custom event.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnCustomEvent" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - }, - "event": { - "type": "string", - "title": "Custom event name", - "description": "Name of the custom event." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "event", - "$type" - ] - } - ] - }, - "Microsoft.OnDialogEvent": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On dialog event", - "description": "Actions to perform when a specific dialog event occurs.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnDialogEvent" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - }, - "event": { - "type": "string", - "title": "Dialog event name", - "description": "Name of dialog event." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "event", - "$type" - ] - } - ] - }, - "Microsoft.OnEndOfConversationActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On EndOfConversation activity", - "description": "Actions to perform on receipt of an activity with type 'EndOfConversation'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnEndOfConversationActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnError": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On Error", - "description": "Action to perform when an 'Error' dialog event occurs.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnError" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnEventActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On Event activity", - "description": "Actions to perform on receipt of an activity with type 'Event'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnEventActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnHandoffActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On Handoff activity", - "description": "Actions to perform on receipt of an activity with type 'HandOff'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnHandoffActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnIntent": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On intent recognition", - "description": "Actions to perform when specified intent is recognized.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnIntent" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - }, - "intent": { - "type": "string", - "title": "Intent", - "description": "Name of intent." - }, - "entities": { - "type": "array", - "title": "Entities", - "description": "Required entities.", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "intent", - "$type" - ] - } - ] - }, - "Microsoft.OnInvokeActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On Invoke activity", - "description": "Actions to perform on receipt of an activity with type 'Invoke'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnInvokeActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnMessageActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On Message activity", - "description": "Actions to perform on receipt of an activity with type 'Message'. Overrides Intent trigger.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnMessageActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnMessageDeleteActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On MessageDelete activity", - "description": "Actions to perform on receipt of an activity with type 'MessageDelete'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnMessageDeleteActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnMessageReactionActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On MessageReaction activity", - "description": "Actions to perform on receipt of an activity with type 'MessageReaction'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnMessageReactionActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnMessageUpdateActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On MessageUpdate activity", - "description": "Actions to perform on receipt of an activity with type 'MessageUpdate'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnMessageUpdateActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnRepromptDialog": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On RepromptDialog", - "description": "Actions to perform when 'RepromptDialog' event occurs.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnRepromptDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnTypingActivity": { - "$role": "unionType(Microsoft.ITriggerCondition)", - "title": "On Typing activity", - "description": "Actions to perform on receipt of an activity with type 'Typing'.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnTypingActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OnUnknownIntent": { - "title": "On unknown intent", - "description": "Action to perform when user input is unrecognized and if none of the 'on intent recognition' triggers match recognized intent.", - "type": "object", - "$role": "unionType(Microsoft.ITriggerCondition)", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OnUnknownIntent" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Condition (expression).", - "examples": [ - "user.vip == true" - ], - "type": "string" - }, - "actions": { - "type": "array", - "description": "Sequence of actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "actions", - "$type" - ] - } - ] - }, - "Microsoft.OrdinalEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Ordinal Entity Recognizer", - "description": "Recognizer which recognizes ordinals (example: first, second, 3rd).", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.OrdinalEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.PercentageEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Percentage Entity Recognizer", - "description": "Recognizer which recognizes percentages.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.PercentageEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.PhoneNumberEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Phone Number Entity Recognizer", - "description": "Recognizer which recognizes phone numbers.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.PhoneNumberEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.QnAMakerDialog": { - "$role": "unionType(Microsoft.IDialog)", - "title": "QnAMaker Dialog", - "description": "Dialog which uses QnAMAker knowledge base to answer questions.", - "type": "object", - "additionalProperties": false, - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.QnAMakerDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "knowledgeBaseId": { - "$role": "expression", - "title": "KnowledgeBase Id", - "description": "KnowledgeBase Id of your QnA Maker KnowledgeBase.", - "default": "settings.qna.knowledgebaseid", - "type": "string" - }, - "endpointKey": { - "$role": "expression", - "title": "Endpoint Key", - "description": "Endpoint key for the QnA Maker KB.", - "default": "settings.qna.endpointkey", - "type": "string" - }, - "hostname": { - "$role": "expression", - "title": "Hostname", - "description": "Hostname for your QnA Maker service.", - "default": "settings.qna.hostname", - "examples": [ - "https://yourserver.azurewebsites.net/qnamaker" - ], - "type": "string" - }, - "noAnswer": { - "$type": "Microsoft.IActivityTemplate", - "title": "Fallback answer", - "description": "Default answer to return when none found in KB.", - "default": "Sorry, I did not find an answer.", - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "threshold": { - "type": "number", - "title": "Threshold", - "description": "Threshold score to filter results.", - "default": 0.3 - }, - "activeLearningCardTitle": { - "type": "string", - "title": "Active learning card title", - "description": "Title for active learning suggestions card.", - "default": "Did you mean:" - }, - "cardNoMatchText": { - "type": "string", - "title": "Card no match text", - "description": "Text for no match option.", - "default": "None of the above." - }, - "cardNoMatchResponse ": { - "$type": "Microsoft.IActivityTemplate", - "title": "Card no match response", - "description": "Custom response when no match option was selected.", - "default": "Thanks for the feedback.", - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "strictFilters": { - "type": "array", - "title": "Strict Filter Property", - "description": "Memory property that holds strict filters to use when calling the QnA Maker KB.", - "items": { - "type": "object" - } - } - }, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "knowledgeBaseId", - "endpointKey", - "hostname", - "$type" - ] - } - ] - }, - "Microsoft.RandomSelector": { - "$role": "unionType(Microsoft.ITriggerSelector)", - "title": "Random rule selector", - "description": "Select most specific true rule", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.RandomSelector" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "seed": { - "type": "integer" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.RegExEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Regex Entity Recognizer", - "description": "Recognizer which recognizes patterns of input based on regex.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.RegExEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "name": { - "type": "string", - "title": "Name", - "description": "Name of the entity" - }, - "pattern": { - "type": "string", - "title": "Pattern", - "description": "Pattern expressed as regular expression." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "name", - "pattern", - "$type" - ] - } - ] - }, - "Microsoft.RegexRecognizer": { - "$role": "unionType(Microsoft.IRecognizer)", - "title": "Regex recognizer", - "description": "Use regular expressions to recognize intents and entities from user input.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.RegexRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "intents": { - "type": "array", - "title": "RegEx patterns to intents", - "description": "Collection of patterns to match for an intent.", - "items": { - "type": "object", - "properties": { - "intent": { - "type": "string", - "title": "Intent", - "description": "The intent name." - }, - "pattern": { - "type": "string", - "title": "Pattern", - "description": "The regular expression pattern." - } - } - } - }, - "entities": { - "type": "array", - "title": "Entity recognizers", - "description": "Collection of entity recognizers to use.", - "items": { - "$type": "Microsoft.EntityRecognizers", - "$ref": "#/definitions/Microsoft.EntityRecognizers" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "intents", - "$type" - ] - } - ] - }, - "Microsoft.RepeatDialog": { - "$role": "unionType(Microsoft.IDialog)", - "type": "object", - "title": "Repeat dialog", - "description": "Repeat current dialog.", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.RepeatDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.ReplaceDialog": { - "$role": "unionType(Microsoft.IDialog)", - "type": "object", - "title": "Replace dialog", - "description": "Replace current dialog with another dialog.", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.ReplaceDialog" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "dialog": { - "$type": "Microsoft.IDialog", - "title": "Dialog name", - "description": "Current dialog will be replaced by this dialog.", - "$ref": "#/definitions/Microsoft.IDialog" - }, - "options": { - "type": "object", - "title": "Options", - "description": "One or more options that are passed to the dialog that is called.", - "additionalProperties": { - "type": "string", - "title": "Options" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.SendActivity": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Send an activity", - "description": "Respond with an activity.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.SendActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "activity": { - "$type": "Microsoft.IActivityTemplate", - "title": "Activity", - "description": "Activity to send.", - "$ref": "#/definitions/Microsoft.IActivityTemplate" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.SetProperty": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Set property", - "description": "Set property to a value.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.SetProperty" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property (named location to store information).", - "examples": [ - "user.age" - ], - "type": "string" - }, - "value": { - "$role": "expression", - "title": "Value", - "description": "New value or expression.", - "examples": [ - "'milk'", - "dialog.favColor", - "dialog.favColor == 'red'" - ], - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "property", - "value", - "$type" - ] - } - ] - }, - "Microsoft.StaticActivityTemplate": { - "$role": "unionType(Microsoft.IActivityTemplate)", - "title": "Microsoft Static Activity Template", - "description": "This allows you to define a static Activity object", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.StaticActivityTemplate" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "activity": { - "type": "object", - "title": "Activity", - "Description": "A static Activity to used" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "activity", - "$type" - ] - } - ] - }, - "Microsoft.SwitchCondition": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Switch condition", - "description": "Execute different actions based on the value of a property.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.SwitchCondition" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "condition": { - "$role": "expression", - "title": "Condition", - "description": "Property to evaluate.", - "examples": [ - "user.favColor" - ], - "type": "string" - }, - "cases": { - "type": "array", - "title": "Cases", - "desc": "Actions for each possible condition.", - "items": { - "type": "object", - "required": [ - "value", - "case" - ], - "properties": { - "value": { - "$role": "expression", - "title": "Value", - "description": "Value.", - "examples": [ - "'red'", - "dialog.colors.red" - ], - "type": "string" - }, - "actions": { - "type": "array", - "title": "Actions", - "description": "Actions to execute.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - } - } - }, - "default": { - "type": "array", - "title": "Default", - "description": "Actions to execute if none of the cases meet the condition.", - "items": { - "$type": "Microsoft.IDialog", - "$ref": "#/definitions/Microsoft.IDialog" - } - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "condition", - "$type" - ] - } - ] - }, - "Microsoft.TemperatureEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Temperature Entity Recognizer", - "description": "Recognizer which recognizes temperatures.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.TemperatureEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.TextInput": { - "$role": "unionType(Microsoft.IDialog)", - "type": "object", - "title": "Text input dialog", - "description": "Collection information - Ask for a word or sentence.", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.TextInput" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "id": { - "type": "string", - "title": "Id", - "description": "Optional id for the dialog" - }, - "prompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Initial prompt", - "description": "Message to send to collect information.", - "examples": [ - "What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "unrecognizedPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", - "examples": [ - "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "invalidPrompt": { - "$type": "Microsoft.IActivityTemplate", - "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", - "examples": [ - "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "defaultValueResponse": { - "$type": "Microsoft.IActivityTemplate", - "title": "Default value response", - "description": "Message to send when max turn count (if specified) has been exceeded and the default value is selected as the value.", - "examples": [ - "Sorry, I'm having trouble understanding you. I will just use {this.options.defaultValue} for now. You can say 'I'm 36 years old' to change it." - ], - "$ref": "#/definitions/Microsoft.IActivityTemplate" - }, - "maxTurnCount": { - "type": "integer", - "title": "Max turn count", - "description": "Maximum number of re-prompt attempts to collect information.", - "default": 3, - "examples": [ - 3 - ] - }, - "validations": { - "type": "array", - "title": "Validation expressions", - "description": "Expression to validate user input.", - "examples": [ - "int(this.value) > 1 && int(this.value) <= 150", - "count(this.value) < 300" - ], - "items": { - "$role": "expression", - "type": "string", - "description": "String must contain an expression." - } - }, - "property": { - "$role": "expression", - "title": "Property", - "description": "Property to store collected information. Input will be skipped if property has value (unless 'Always prompt' is true).", - "examples": [ - "$birthday", - "user.name", - "conversation.issueTitle", - "dialog.favColor" - ], - "type": "string" - }, - "defaultValue": { - "$role": "expression", - "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", - "examples": [ - "@userName", - "coalesce(@number, @partySize)" - ], - "type": "string" - }, - "alwaysPrompt": { - "type": "boolean", - "title": "Always prompt", - "description": "Collect information even if the specified 'property' is not empty.", - "default": false, - "examples": [ - false - ] - }, - "allowInterruptions": { - "type": "string", - "title": "Allow Interruptions", - "description": "A boolean expression that determines whether the parent should be allowed to interrupt the input.", - "default": "true", - "examples": [ - "true" - ] - }, - "outputFormat": { - "type": "string", - "enum": [ - "none", - "trim", - "lowercase", - "uppercase" - ], - "title": "Output format", - "description": "Format of output.", - "default": "none" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.TextTemplate": { - "$role": "unionType(Microsoft.ITextTemplate)", - "title": "Microsoft TextTemplate", - "description": "Lg tempalte to evaluate to create text", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.TextTemplate" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "template": { - "title": "Template", - "Description": "Language Generator template to evaluate to create the text", - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "template", - "$type" - ] - } - ] - }, - "Microsoft.TraceActivity": { - "$role": "unionType(Microsoft.IDialog)", - "title": "Send a TraceActivity", - "description": "Send a trace activity to the transcript logger and/ or Bot Framework Emulator.", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.TraceActivity" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - }, - "name": { - "type": "string", - "title": "Name", - "description": "Name of the trace activity" - }, - "valueType": { - "type": "string", - "title": "Value type", - "description": "Type of value" - }, - "value": { - "$role": "expression", - "title": "Value", - "description": "Property that holds the value to send as trace activity.", - "type": "string" - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.TrueSelector": { - "$role": "unionType(Microsoft.ITriggerSelector)", - "title": "True Trigger Selector", - "description": "Selector for all true events", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.TrueSelector" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - }, - "Microsoft.UrlEntityRecognizer": { - "$role": "unionType(Microsoft.EntityRecognizers)", - "title": "Url Entity Recognizer", - "description": "Recognizer which recognizes urls (example: http://bing.com)", - "type": "object", - "properties": { - "$type": { - "title": "$type", - "description": "Defines the valid properties for the component you are configuring (from a dialog .schema file)", - "type": "string", - "pattern": "^[a-zA-Z][a-zA-Z0-9.]*$", - "const": "Microsoft.UrlEntityRecognizer" - }, - "$copy": { - "title": "$copy", - "description": "Copy the definition by id from a .dialog file.", - "type": "string", - "pattern": "^(([a-zA-Z][a-zA-Z0-9.]*)?(#[a-zA-Z][a-zA-Z0-9.]*)?)$" - }, - "$id": { - "title": "$id", - "description": "Inline id for reuse of an inline definition", - "type": "string", - "pattern": "^([a-zA-Z][a-zA-Z0-9.]*)$" - }, - "$designer": { - "title": "$designer", - "type": "object", - "description": "Extra information for the Bot Framework Designer." - } - }, - "additionalProperties": false, - "patternProperties": { - "^\\$": { - "type": "string" - } - }, - "anyOf": [ - { - "title": "Reference", - "required": [ - "$copy" - ] - }, - { - "title": "Type", - "required": [ - "$type" - ] - } - ] - } - } -} diff --git a/BotProject/CSharp/Scripts/create.ps1 b/BotProject/CSharp/Scripts/create.ps1 deleted file mode 100644 index bd30ddf40c..0000000000 --- a/BotProject/CSharp/Scripts/create.ps1 +++ /dev/null @@ -1,185 +0,0 @@ -Param( - [string] $name, - [string] $location, - [string] $appId, - [string] $appPassword, - [string] $environment, - [string] $projDir = $(Get-Location), - [string] $logFile = $(Join-Path $PSScriptRoot .. "create_log.txt") -) - -if ($PSVersionTable.PSVersion.Major -lt 6){ - Write-Host "! Powershell 6 is required, current version is $($PSVersionTable.PSVersion.Major), please refer following documents for help." - Write-Host "For Windows - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6" - Write-Host "For Mac - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-6" - Break -} - -# Reset log file -if (Test-Path $logFile) { - Clear-Content $logFile -Force | Out-Null -} -else { - New-Item -Path $logFile | Out-Null -} - -if (-not (Test-Path (Join-Path $projDir 'appsettings.json'))) -{ - Write-Host "! Could not find an 'appsettings.json' file in the current directory." -ForegroundColor DarkRed - Write-Host "+ Please re-run this script from your project directory." -ForegroundColor Magenta - Break -} - -# Get mandatory parameters -if (-not $name) { - $name = Read-Host "? Bot Name (used as default name for resource group and deployed resources)" -} - -if (-not $environment) -{ - $environment = Read-Host "? Environment Name (single word, all lowercase)" - $environment = $environment.ToLower().Split(" ") | Select-Object -First 1 -} - -if (-not $location) { - $location = Read-Host "? Azure resource group region" -} - -if (-not $appPassword) { - $appPassword = Read-Host "? Password for MSA app registration (must be at least 16 characters long, contain at least 1 special character, and contain at least 1 numeric character)" -} - -if (-not $appId) { - # Create app registration - $app = (az ad app create ` - --display-name $name ` - --password `"$($appPassword)`" ` - --available-to-other-tenants ` - --reply-urls 'https://token.botframework.com/.auth/web/redirect' ` - --output json) - - # Retrieve AppId - if ($app) { - $appId = ($app | ConvertFrom-Json) | Select-Object -ExpandProperty appId - } - - if(-not $appId) { - Write-Host "! Could not provision Microsoft App Registration automatically. Review the log for more information." -ForegroundColor DarkRed - Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed - Write-Host "+ Provision an app manually in the Azure Portal, then try again providing the -appId and -appPassword arguments. See https://aka.ms/vamanualappcreation for more information." -ForegroundColor Magenta - Break - } -} - -$resourceGroup = "$name-$environment" -$servicePlanName = "$name-$environment" - -# Get timestamp -$timestamp = Get-Date -f MMddyyyyHHmmss - -# Create resource group -Write-Host "> Creating resource group ..." -(az group create --name $resourceGroup --location $location --output json) 2>> $logFile | Out-Null - -# Deploy Azure services -Write-Host "> Validating Azure deployment ..." -$validation = az group deployment validate ` - --resource-group $resourcegroup ` - --template-file "$(Join-Path $PSScriptRoot '..' 'DeploymentTemplates' 'template-with-preexisting-rg.json')" ` - --parameters appId=$appId appSecret="`"$($appPassword)`"" newAppServicePlanName=$servicePlanName appServicePlanLocation=$location botId=$name ` - --output json - -if ($validation) { - $validation >> $logFile - $validation = $validation | ConvertFrom-Json - - if (-not $validation.error) { - Write-Host "> Deploying Azure services (this could take a while)..." -ForegroundColor Yellow - $deployment = az group deployment create ` - --name $timestamp ` - --resource-group $resourceGroup ` - --template-file "$(Join-Path $PSScriptRoot '..' 'DeploymentTemplates' 'template-with-preexisting-rg.json')" ` - --parameters appId=$appId appSecret="`"$($appPassword)`"" newAppServicePlanName=$servicePlanName appServicePlanLocation=$location botId=$name ` - --output json - } - else { - Write-Host "! Template is not valid with provided parameters. Review the log for more information." -ForegroundColor DarkRed - Write-Host "! Error: $($validation.error.message)" -ForegroundColor DarkRed - Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed - Write-Host "+ To delete this resource group, run 'az group delete -g $($resourceGroup) --no-wait'" -ForegroundColor Magenta - Break - } -} - - -# Get deployment outputs -$outputs = az group deployment show ` - --name $timestamp ` - --resource-group $resourceGroup ` - --output json 2>> $logFile - -# If it succeeded then we perform the remainder of the steps -if ($outputs) -{ - # Log and convert to JSON - $outputs >> $logFile - $outputs = $outputs | ConvertFrom-Json - $outputMap = @{} - $outputs.PSObject.Properties | Foreach-Object { $outputMap[$_.Name] = $_.Value } - - # Update appsettings.json - Write-Host "> Updating appsettings.json ..." - if (Test-Path $(Join-Path $projDir appsettings.json)) { - $settings = Get-Content $(Join-Path $projDir appsettings.json) | ConvertFrom-Json - } - else { - $settings = New-Object PSObject - } - - $settings | Add-Member -Type NoteProperty -Force -Name 'microsoftAppId' -Value $appId - $settings | Add-Member -Type NoteProperty -Force -Name 'microsoftAppPassword' -Value $appPassword - $settings | Add-Member -Type NoteProperty -Force -Name 'bot' -Value "RunningInstance" - - foreach ($key in $outputMap.Keys) { $settings | Add-Member -Type NoteProperty -Force -Name $key -Value $outputMap[$key].value } - $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $projDir appsettings.json) - - Write-Host "> Done." - Write-Host "- App Id: $appId" - Write-Host "- App Password: $appPassword" - Write-Host "- Resource Group: $resourceGroup" - Write-Host "- ServicePlan: $servicePlanName" - Write-Host "- Bot Name: $name" - Write-Host "- Web App Name : $name" -} -else -{ - # Check for failed deployments - $operations = az group deployment operation list -g $resourceGroup -n $timestamp --output json 2>> $logFile | Out-Null - - if ($operations) { - $operations = $operations | ConvertFrom-Json - $failedOperations = $operations | Where { $_.properties.statusmessage.error -ne $null } - if ($failedOperations) { - foreach ($operation in $failedOperations) { - switch ($operation.properties.statusmessage.error.code) { - "MissingRegistrationForLocation" { - Write-Host "! Deployment failed for resource of type $($operation.properties.targetResource.resourceType). This resource is not avaliable in the location provided." -ForegroundColor DarkRed - Write-Host "+ Update the .\Deployment\Resources\parameters.template.json file with a valid region for this resource and provide the file path in the -parametersFile parameter." -ForegroundColor Magenta - } - default { - Write-Host "! Deployment failed for resource of type $($operation.properties.targetResource.resourceType)." - Write-Host "! Code: $($operation.properties.statusMessage.error.code)." - Write-Host "! Message: $($operation.properties.statusMessage.error.message)." - } - } - } - } - } - else { - Write-Host "! Deployment failed. Please refer to the log file for more information." -ForegroundColor DarkRed - Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed - } - - Write-Host "+ To delete this resource group, run 'az group delete -g $($resourceGroup) --no-wait'" -ForegroundColor Magenta - Break -} \ No newline at end of file diff --git a/BotProject/CSharp/Scripts/deploy.ps1 b/BotProject/CSharp/Scripts/deploy.ps1 deleted file mode 100644 index 1efad16116..0000000000 --- a/BotProject/CSharp/Scripts/deploy.ps1 +++ /dev/null @@ -1,137 +0,0 @@ -Param( - [string] $name, - [string] $environment, - [string] $luisAuthoringKey, - [string] $luisAuthoringRegion, - [string] $projFolder = $(Get-Location), - [string] $botPath, - [string] $logFile = $(Join-Path $PSScriptRoot .. "deploy_log.txt") -) - -if ($PSVersionTable.PSVersion.Major -lt 6){ - Write-Host "! Powershell 6 is required, current version is $($PSVersionTable.PSVersion.Major), please refer following documents for help." - Write-Host "For Windows - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-6" - Write-Host "For Mac - https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-6" - Break -} - -# Get mandatory parameters -if (-not $name) { - $name = Read-Host "? Bot Web App Name" -} - -if (-not $environment) -{ - $environment = Read-Host "? Environment Name (single word, all lowercase)" - $environment = $environment.ToLower().Split(" ") | Select-Object -First 1 -} - -if (-not $botPath) { - $botPath = Read-Host "? The Reletive Path Of Bot" -} - - -# Reset log file -if (Test-Path $logFile) { - Clear-Content $logFile -Force | Out-Null -} -else { - New-Item -Path $logFile | Out-Null -} - -# Check for existing deployment files -if (-not (Test-Path (Join-Path $projFolder '.deployment'))) { - # Add needed deployment files for az - az bot prepare-deploy --lang Csharp --code-dir $projFolder --proj-file-path BotProject.csproj --output json | Out-Null -} - -# Delete src zip, if it exists -$zipPath = $(Join-Path $projFolder 'code.zip') -if (Test-Path $zipPath) { - Remove-Item $zipPath -Force | Out-Null -} - -# Perform dotnet publish step ahead of zipping up -$publishFolder = $(Join-Path $projFolder 'bin\Release\netcoreapp2.2') -dotnet publish -c release -o $publishFolder -v q > $logFile - -# Copy bot files to running folder -$remoteBotPath = $(Join-Path $publishFolder "RunningInstance") -Remove-Item $remoteBotPath -Recurse -ErrorAction Ignore -Copy-Item -Path $botPath -Recurse -Destination $remoteBotPath -Container -Force - -# Merge from custom config files -$customConfigFiles = Get-ChildItem -Path $remoteBotPath -Include "appsettings.json" -Recurse -Force -if ($customConfigFiles) -{ - if (Test-Path $(Join-Path $publishFolder appsettings.json)) { - $settings = Get-Content $(Join-Path $publishFolder appsettings.json) | ConvertFrom-Json - } - else { - $settings = New-Object PSObject - } - - $customConfig = @{} - $customSetting = Get-Content $customConfigFiles.FullName | ConvertFrom-Json - $customSetting.PSObject.Properties | Foreach-Object { $customConfig[$_.Name] = $_.Value } - foreach ($key in $customConfig.Keys) { $settings | Add-Member -Type NoteProperty -Force -Name $key -Value $customConfig[$key] } - - $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.json) -} - -# Add Luis Config to appsettings -if ($luisAuthoringKey -and $luisAuthoringRegion) -{ - # change setting file in publish folder - if (Test-Path $(Join-Path $publishFolder appsettings.json)) { - $settings = Get-Content $(Join-Path $publishFolder appsettings.json) | ConvertFrom-Json - } - else { - $settings = New-Object PSObject - } - - $luisConfigFiles = Get-ChildItem -Path $publishFolder -Include "luis.settings*" -Recurse -Force - - $luisAppIds = @{} - - foreach ($luisConfigFile in $luisConfigFiles) - { - $luisSetting = Get-Content $luisConfigFile.FullName | ConvertFrom-Json - $luis = $luisSetting.luis - $luis.PSObject.Properties | Foreach-Object { $luisAppIds[$_.Name] = $_.Value } - } - - $luisEndpoint = "https://$luisAuthoringRegion.api.cognitive.microsoft.com" - - $luisConfig = @{} - - $luisConfig["endpointKey"] = $luisAuthoringKey - $luisConfig["endpoint"] = $luisEndpoint - - foreach ($key in $luisAppIds.Keys) { $luisConfig[$key] = $luisAppIds[$key] } - - $settings | Add-Member -Type NoteProperty -Force -Name 'luis' -Value $luisConfig - - $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.json) -} - -$resourceGroup = "$name-$environment" - -if($?) -{ - # Compress source code - Get-ChildItem -Path "$($publishFolder)" | Compress-Archive -DestinationPath "$($zipPath)" -Force | Out-Null - - # Publish zip to Azure - Write-Host "> Publishing to Azure ..." -ForegroundColor Green - (az webapp deployment source config-zip ` - --resource-group $resourceGroup ` - --name $name ` - --src $zipPath ` - --output json) 2>> $logFile | Out-Null -} -else -{ - Write-Host "! Could not deploy automatically to Azure. Review the log for more information." -ForegroundColor DarkRed - Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed -} \ No newline at end of file diff --git a/BotProject/CSharp/Startup.cs b/BotProject/CSharp/Startup.cs deleted file mode 100644 index 12358ce19d..0000000000 --- a/BotProject/CSharp/Startup.cs +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Bot.Builder.BotFramework; -using Microsoft.Bot.Builder.Dialogs.Declarative.Types; -using Microsoft.Bot.Connector.Authentication; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace Microsoft.Bot.Builder.ComposerBot.Json -{ - public class Startup - { - public Startup(IHostingEnvironment env, IConfiguration configuration) - { - this.HostingEnvironment = env; - this.Configuration = configuration; - } - - public IHostingEnvironment HostingEnvironment { get; } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); - - services.AddSingleton(this.Configuration); - - // Create the credential provider to be used with the Bot Framework Adapter. - services.AddSingleton(); - - services.AddSingleton(); - - TypeFactory.Configuration = this.Configuration; - services.AddSingleton((sp) => new BotManager(this.Configuration)); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseHsts(); - } - - app.UseDefaultFiles(); - app.UseStaticFiles(); - app.UseWebSockets(); - - //app.UseHttpsRedirection(); - app.UseMvc(); - } - } -} diff --git a/BotProject/CSharp/Tests/ActionsTests.cs b/BotProject/CSharp/Tests/ActionsTests.cs deleted file mode 100644 index 173086e547..0000000000 --- a/BotProject/CSharp/Tests/ActionsTests.cs +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Adapters; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; -using Microsoft.Bot.Builder.Dialogs.Declarative.Types; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.IO; -using System.Threading.Tasks; - -namespace Tests -{ - [TestClass] - public class ActionsTests - { - private static string getOsPath(string path) => Path.Combine(path.TrimEnd('\\').Split('\\')); - - private static readonly string samplesDirectory = getOsPath(@"..\..\..\..\..\..\Composer\packages\server\assets\projects"); - - private static string getFolderPath(string path) - { - return Path.Combine(samplesDirectory, path); - } - - - [ClassInitialize] - public static void ClassInitialize(TestContext context) - { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); - string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory)); - } - - public TestContext TestContext { get; set; } - - [TestMethod] - public async Task Actions_01Actions() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)) - .Send("01") - .AssertReply("Step 1") - .AssertReply("Step 2") - .AssertReply("Step 3") - .AssertReply("user.age is set to 18") - .AssertReply("user.age is set to null") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_02EndTurn() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)) - .Send("02") - .AssertReply("What's up?") - .Send("Nothing") - .AssertReply("Oh I see!") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_03IfCondition() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("03") - .AssertReply("Hello, I'm Zoidberg. What is your name?") - .Send("Carlos") - .AssertReply("Hello Carlos, nice to talk to you!") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_04EditArray() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("04") - .AssertReply("Here are the index and values in the array.") - .AssertReply("0: 11111") - .AssertReply("1: 40000") - .AssertReply("2: 222222") - .AssertReply("If each page shows two items, here are the index and values") - .AssertReply("0: 11111") - .AssertReply("1: 40000") - .AssertReply("0: 222222") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_05EndDialog() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("05") - .AssertReply("Hello, I'm Zoidberg. What is your name?") - .Send("luhan") - .AssertReply("Hello luhan, nice to talk to you!") - .AssertReply("I'm a joke bot. To get started say \"joke\".") - .Send("joke") - .AssertReply("Why did the chicken cross the road?") - .Send("I don't know") - .AssertReply("To get to the other side!") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_06SwitchCondition() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("07") - .AssertReply("Please select a value from below:\n\n 1. Test1\n 2. Test2\n 3. Test3") - .Send("Test1") - .AssertReply("You select: Test1") - .AssertReply("You select: 1") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_07RepeatDialog() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("08") - .AssertReply("Do you want to repeat this dialog, yes to repeat, no to end this dialog (1) Yes or (2) No") - .Send("Yes") - .AssertReply("Do you want to repeat this dialog, yes to repeat, no to end this dialog (1) Yes or (2) No") - .Send("No") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_08TraceAndLog() - { - await BuildTestFlow(getFolderPath("ActionsSample"), sendTrace: true) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("09") - .Send("luhan") - .AssertReply(activity => - { - var trace = (Activity)activity; - Assert.AreEqual(ActivityTypes.Trace, trace.Type, "should be trace activity"); - }) - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_09EditActions() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("10") - .AssertReply("Hello, I'm Zoidberg. What is your name?") - .Send("luhan") - .AssertReply("Hello luhan, nice to talk to you!") - .AssertReply("Goodbye!") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_10ReplaceDialog() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("11") - .AssertReply("Hello, I'm Zoidberg. What is your name?") - .Send("luhan") - .AssertReply("Hello luhan, nice to talk to you! Please either enter 'joke' or 'fortune' to replace the dialog you want.") - .Send("joke") - .AssertReply("Why did the chicken cross the road?") - .Send("Why?") - .AssertReply("To get to the other side!") - .Send("future") - .AssertReply("Seeing into your future...") - .AssertReply("I see great things in your future!") - .AssertReply("Potentially a successful demo") - .StartTestAsync(); - } - - [TestMethod] - public async Task Actions_11EmitEvent() - { - await BuildTestFlow(getFolderPath("ActionsSample")) - .SendConversationUpdate() - .AssertReply(String.Format("I can show you examples on how to use actions. Enter the number next to the entity that you with to see in action.{0}01 - Actions{0}02 - EndTurn{0}03 - IfCondiftion{0}04 - EditArray, Foreach{0}05 - EndDialog{0}06 - HttpRequest{0}07 - SwitchCondition{0}08 - RepeatDialog{0}09 - TraceAndLog{0}10 - EditActions{0}11 - ReplaceDialog{0}12 - EmitEvent{0}13 - QnAMaker", Environment.NewLine)).Send("12") - .AssertReply("Say moo to get a response, say emit to emit a event.") - .Send("moo") - .AssertReply("Yippee ki-yay!") - .Send("emit") - .AssertReply("CustomEvent Fired.") - .StartTestAsync(); - } - - private TestFlow BuildTestFlow(string folderPath, bool sendTrace = false) - { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); - var storage = new MemoryStorage(); - var convoState = new ConversationState(storage); - var userState = new UserState(storage); - var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName), sendTrace); - var resourceExplorer = new ResourceExplorer(); - resourceExplorer.AddFolder(folderPath); - adapter - .UseStorage(storage) - .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) - .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); - - var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); - - return new TestFlow(adapter, async (turnContext, cancellationToken) => - { - if (dialog is AdaptiveDialog planningDialog) - { - await dm.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false); - } - }); - } - } -} diff --git a/BotProject/CSharp/Tests/ControllingConversationTests.cs b/BotProject/CSharp/Tests/ControllingConversationTests.cs deleted file mode 100644 index ad75bbae45..0000000000 --- a/BotProject/CSharp/Tests/ControllingConversationTests.cs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Adapters; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; -using Microsoft.Bot.Builder.Dialogs.Declarative.Types; -using Microsoft.Bot.Builder.LanguageGeneration; -using Microsoft.Bot.Builder.ComposerBot.Json; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Tests -{ - [TestClass] - public class ControllingConversationTests - { - private static string getOsPath(string path) => Path.Combine(path.TrimEnd('\\').Split('\\')); - - private static readonly string samplesDirectory = getOsPath(@"..\..\..\..\..\..\Composer\packages\server\assets\projects"); - - private static ResourceExplorer resourceExplorer = new ResourceExplorer(); - - - [ClassInitialize] - public static void ClassInitialize(TestContext context) - { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); - string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "ControllingConversationFlowSample")); - resourceExplorer.AddFolder(path); - } - - [ClassCleanup] - public static void ClassCleanup() - { - resourceExplorer.Dispose(); - } - - public TestContext TestContext { get; set; } - - [TestMethod] - public async Task ControllingConversationBotTest() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply(String.Format("Welcome to the Controlling Conversation sample. Choose from the list below to try.{0}You can also type \"Cancel\" to cancel any dialog or \"Endturn\" to explicitly accept an input.", Environment.NewLine)) - .Send("01") - .AssertReply("Hello, What's your age?") - .Send("18") - .AssertReply("Your age is 18 which satisified the condition that was evaluated") - .Send("02") - .AssertReply("Who are your?\n\n 1. Susan\n 2. Nick\n 3. Tom") - .Send("2") - .AssertReply("You selected Nick") - .AssertReply("This is the logic inside the \"Nick\" switch block.") - .Send("03") - .AssertReply("Pushed dialog.id into a list") - .AssertReply("0: 11111") - .AssertReply("1: 40000") - .AssertReply("2: 222222") - .Send("04") - .AssertReply("Pushed dialog.ids into a list") - .AssertReply("0: 11111") - .AssertReply("1: 40000") - .AssertReply("0: 222222") - .Send("06") - .Send("hi") - .Send("07") - .AssertReply("Do you want to repeat this dialog, yes to repeat, no to end this dialog (1) Yes or (2) No") - .Send("Yes") - .AssertReply("Do you want to repeat this dialog, yes to repeat, no to end this dialog (1) Yes or (2) No") - .Send("No") - .Send("05") - .AssertReply("Canceled.") - .StartTestAsync(); - } - - - private TestFlow BuildTestFlow(bool sendTrace = false) - { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); - var storage = new MemoryStorage(); - var convoState = new ConversationState(storage); - var userState = new UserState(storage); - var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName), sendTrace); - adapter - .UseStorage(storage) - .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) - .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); - - var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); - - return new TestFlow(adapter, async (turnContext, cancellationToken) => - { - if (dialog is AdaptiveDialog planningDialog) - { - await dm.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false); - } - }); - } - } -} diff --git a/BotProject/CSharp/Tests/InputsTests.cs b/BotProject/CSharp/Tests/InputsTests.cs deleted file mode 100644 index a893278ca3..0000000000 --- a/BotProject/CSharp/Tests/InputsTests.cs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Adapters; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; -using Microsoft.Bot.Builder.Dialogs.Declarative.Types; -using Microsoft.Bot.Builder.LanguageGeneration; -using Microsoft.Bot.Builder.ComposerBot.Json; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Tests -{ - [TestClass] - public class InputsTests - { - private static string getOsPath(string path) => Path.Combine(path.TrimEnd('\\').Split('\\')); - - private static readonly string samplesDirectory = getOsPath(@"..\..\..\..\..\..\Composer\packages\server\assets\projects"); - - private static ResourceExplorer resourceExplorer = new ResourceExplorer(); - - - [ClassInitialize] - public static void ClassInitialize(TestContext context) - { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); - string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "AskingQuestionsSample")); - resourceExplorer.AddFolder(path); - } - - [ClassCleanup] - public static void ClassCleanup() - { - resourceExplorer.Dispose(); - } - - public TestContext TestContext { get; set; } - - [TestMethod] - public async Task Inputs_01TextInput() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply(String.Format("Welcome to Input Sample Bot.{0}I can show you examples on how to use actions, You can enter number 01-07{0}01 - TextInput{0}02 - NumberInput{0}03 - ConfirmInput{0}04 - ChoiceInput{0}05 - AttachmentInput{0}06 - DateTimeInput{0}07 - OAuthInput{0}", Environment.NewLine)) - .Send("01") - .AssertReply("Hello, I'm Zoidberg. What is your name? (This can't be interrupted)") - .Send("02") - .AssertReply("Hello 02, nice to talk to you!") - .Send("02") - .AssertReply("What is your age?") - .StartTestAsync(); - } - - [TestMethod] - public async Task Inputs_02NumberInput() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply(String.Format("Welcome to Input Sample Bot.{0}I can show you examples on how to use actions, You can enter number 01-07{0}01 - TextInput{0}02 - NumberInput{0}03 - ConfirmInput{0}04 - ChoiceInput{0}05 - AttachmentInput{0}06 - DateTimeInput{0}07 - OAuthInput{0}", Environment.NewLine)) - .Send("02") - .AssertReply("What is your age?") - .Send("18") - .AssertReply("Hello, your age is 18!") - .AssertReply("2 * 2.2 equals?") - .Send("4.4") - .AssertReply("2 * 2.2 equals 4.4, that's right!") - .StartTestAsync(); - } - - [TestMethod] - public async Task Inputs_03ConfirmInput() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply(String.Format("Welcome to Input Sample Bot.{0}I can show you examples on how to use actions, You can enter number 01-07{0}01 - TextInput{0}02 - NumberInput{0}03 - ConfirmInput{0}04 - ChoiceInput{0}05 - AttachmentInput{0}06 - DateTimeInput{0}07 - OAuthInput{0}", Environment.NewLine)) - .Send("03") - .AssertReply("yes or no (1) Yes or (2) No") - .Send("asdasd") - .AssertReply("I need a yes or no. (1) Yes or (2) No") - .Send("yes") - .AssertReply("confirmation: True") - .StartTestAsync(); - } - - [TestMethod] - public async Task Inputs_04ChoiceInput() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply(String.Format("Welcome to Input Sample Bot.{0}I can show you examples on how to use actions, You can enter number 01-07{0}01 - TextInput{0}02 - NumberInput{0}03 - ConfirmInput{0}04 - ChoiceInput{0}05 - AttachmentInput{0}06 - DateTimeInput{0}07 - OAuthInput{0}", Environment.NewLine)).Send("04") - .AssertReply("Please select a value from below:\n\n 1. Test1\n 2. Test2\n 3. Test3") - .Send("Test1") - .AssertReply("You select: Test1") - .StartTestAsync(); - } - - [TestMethod] - public async Task Inputs_06DateTimeInput() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply(String.Format("Welcome to Input Sample Bot.{0}I can show you examples on how to use actions, You can enter number 01-07{0}01 - TextInput{0}02 - NumberInput{0}03 - ConfirmInput{0}04 - ChoiceInput{0}05 - AttachmentInput{0}06 - DateTimeInput{0}07 - OAuthInput{0}", Environment.NewLine)).Send("06") - .AssertReply("Please enter a date.") - .Send("June 1st") - .AssertReply("You entered: 2019-06-01") - .StartTestAsync(); - } - - private TestFlow BuildTestFlow(bool sendTrace = false) - { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); - var storage = new MemoryStorage(); - var convoState = new ConversationState(storage); - var userState = new UserState(storage); - var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName), sendTrace); - adapter - .UseStorage(storage) - .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) - .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); - - var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); - - return new TestFlow(adapter, async (turnContext, cancellationToken) => - { - if (dialog is AdaptiveDialog planningDialog) - { - await dm.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false); - } - }); - } - } -} diff --git a/BotProject/CSharp/Tests/MessageTests.cs b/BotProject/CSharp/Tests/MessageTests.cs deleted file mode 100644 index c077c6a3b7..0000000000 --- a/BotProject/CSharp/Tests/MessageTests.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Adapters; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; -using Microsoft.Bot.Builder.Dialogs.Declarative.Types; -using Microsoft.Bot.Builder.LanguageGeneration; -using Microsoft.Bot.Builder.ComposerBot.Json; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Tests -{ - [TestClass] - public class MessageTests - { - private static string getOsPath(string path) => Path.Combine(path.TrimEnd('\\').Split('\\')); - - private static readonly string samplesDirectory = getOsPath(@"..\..\..\..\..\..\Composer\packages\server\assets\projects"); - - private static ResourceExplorer resourceExplorer = new ResourceExplorer(); - - - [ClassInitialize] - public static void ClassInitialize(TestContext context) - { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); - string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "RespondingWithTextSample")); - resourceExplorer.AddFolder(path); - } - - [ClassCleanup] - public static void ClassCleanup() - { - resourceExplorer.Dispose(); - } - - public TestContext TestContext { get; set; } - - [TestMethod] - public async Task MessageTest() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply("What type of message would you like to send?\n\n 1. Simple Text\n 2. Text With Memory\n 3. LGWithParam\n 4. LGComposition\n 5. Structured LG\n 6. MultiLineText\n 7. IfElseCondition\n 8. SwitchCondition") - .Send("1") - .AssertReplyOneOf(new string[] { "Hi, this is simple text", "Hey, this is simple text", "Hello, this is simple text" }) - .AssertReply("What type of message would you like to send?\n\n 1. Simple Text\n 2. Text With Memory\n 3. LGWithParam\n 4. LGComposition\n 5. Structured LG\n 6. MultiLineText\n 7. IfElseCondition\n 8. SwitchCondition") - .Send("2") - .AssertReply("This is a text saved in memory.") - .AssertReply("What type of message would you like to send?\n\n 1. Simple Text\n 2. Text With Memory\n 3. LGWithParam\n 4. LGComposition\n 5. Structured LG\n 6. MultiLineText\n 7. IfElseCondition\n 8. SwitchCondition") - .Send("3") - .AssertReply("Hello, I'm Zoidberg. What is your name?") - .Send("luhan") - .AssertReply("Hello luhan, nice to talk to you!") - .AssertReply("What type of message would you like to send?\n\n 1. Simple Text\n 2. Text With Memory\n 3. LGWithParam\n 4. LGComposition\n 5. Structured LG\n 6. MultiLineText\n 7. IfElseCondition\n 8. SwitchCondition") - .Send("4") - .AssertReply("luhan nice to talk to you!") - .AssertReply("What type of message would you like to send?\n\n 1. Simple Text\n 2. Text With Memory\n 3. LGWithParam\n 4. LGComposition\n 5. Structured LG\n 6. MultiLineText\n 7. IfElseCondition\n 8. SwitchCondition") - .StartTestAsync(); - } - - private TestFlow BuildTestFlow(bool sendTrace = false) - { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); - var storage = new MemoryStorage(); - var convoState = new ConversationState(storage); - var userState = new UserState(storage); - var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName), sendTrace); - adapter - .UseStorage(storage) - .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) - .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); - - var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); - - return new TestFlow(adapter, async (turnContext, cancellationToken) => - { - if (dialog is AdaptiveDialog planningDialog) - { - await dm.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false); - } - }); - } - } -} diff --git a/BotProject/CSharp/Tests/Tests.csproj b/BotProject/CSharp/Tests/Tests.csproj deleted file mode 100644 index 4ddbb74f07..0000000000 --- a/BotProject/CSharp/Tests/Tests.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - netcoreapp2.1 - - false - - - - ..\BotProject.ruleset - - - - ..\BotProject.ruleset - - - - - - - - - - - - - diff --git a/BotProject/CSharp/Tests/ToDoBotTests.cs b/BotProject/CSharp/Tests/ToDoBotTests.cs deleted file mode 100644 index b2f4fc5ac9..0000000000 --- a/BotProject/CSharp/Tests/ToDoBotTests.cs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using Microsoft.Bot.Builder; -using Microsoft.Bot.Builder.Adapters; -using Microsoft.Bot.Builder.Dialogs; -using Microsoft.Bot.Builder.Dialogs.Adaptive; -using Microsoft.Bot.Builder.Dialogs.Debugging; -using Microsoft.Bot.Builder.Dialogs.Declarative; -using Microsoft.Bot.Builder.Dialogs.Declarative.Resources; -using Microsoft.Bot.Builder.Dialogs.Declarative.Types; -using Microsoft.Bot.Builder.LanguageGeneration; -using Microsoft.Bot.Builder.ComposerBot.Json; -using Microsoft.Bot.Schema; -using Microsoft.Extensions.Configuration; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; - -namespace Tests -{ - [TestClass] - public class ToDoBotTests - { - private static string getOsPath(string path) => Path.Combine(path.TrimEnd('\\').Split('\\')); - - private static readonly string samplesDirectory = getOsPath(@"..\..\..\..\..\..\Composer\packages\server\assets\projects"); - - private static ResourceExplorer resourceExplorer = new ResourceExplorer(); - - - [ClassInitialize] - public static void ClassInitialize(TestContext context) - { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); - string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "TodoSample")); - resourceExplorer.AddFolder(path); - } - - [ClassCleanup] - public static void ClassCleanup() - { - resourceExplorer.Dispose(); - } - - public TestContext TestContext { get; set; } - - [TestMethod] - public async Task ToDoBotTest() - { - await BuildTestFlow() - .SendConversationUpdate() - .AssertReply("Hi! I'm a ToDo bot. Say \"add a todo named first\" to get started.") - .Send("add a todo named first") - .AssertReply("Successfully added a todo named first") - .Send("add a todo named second") - .AssertReply("Successfully added a todo named second") - .Send("add a todo") - .AssertReply("OK, please enter the title of your todo.") - .Send("third") - .AssertReply("Successfully added a todo named third") - .Send("show todos") - .AssertReply(String.Format("Your most recent 3 tasks are{0}* first\n* second\n* third", Environment.NewLine)) - .Send("delete todo named second") - .AssertReply("Successfully removed a todo named second") - .Send("show todos") - .AssertReply(String.Format("Your most recent 2 tasks are{0}* first\n* third", Environment.NewLine)) - .StartTestAsync(); - } - - private TestFlow BuildTestFlow(bool sendTrace = false) - { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); - var storage = new MemoryStorage(); - var convoState = new ConversationState(storage); - var userState = new UserState(storage); - var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName), sendTrace); - adapter - .UseStorage(storage) - .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) - .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); - - var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); - - return new TestFlow(adapter, async (turnContext, cancellationToken) => - { - if (dialog is AdaptiveDialog planningDialog) - { - await dm.OnTurnAsync(turnContext, cancellationToken).ConfigureAwait(false); - } - }); - } - } -} diff --git a/BotProject/CSharp/appsettings.Development.json b/BotProject/CSharp/appsettings.Development.json deleted file mode 100644 index e203e9407e..0000000000 --- a/BotProject/CSharp/appsettings.Development.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } -} diff --git a/BotProject/CSharp/appsettings.json b/BotProject/CSharp/appsettings.json deleted file mode 100644 index e3a8437897..0000000000 --- a/BotProject/CSharp/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "bot": "ComposerDialogs", - "MicrosoftAppId": "", - "MicrosoftAppPassword": "" -} \ No newline at end of file diff --git a/BotProject/Templates/CSharp/BotProject.csproj b/BotProject/Templates/CSharp/BotProject.csproj index 3b888c5943..060d2341fe 100644 --- a/BotProject/Templates/CSharp/BotProject.csproj +++ b/BotProject/Templates/CSharp/BotProject.csproj @@ -1,4 +1,5 @@ - + + netcoreapp3.1 @@ -13,7 +14,6 @@ Always - BotProject.ruleset @@ -21,16 +21,16 @@ BotProject.ruleset - - - - - - - - - - + + + + + + + + + + all @@ -40,4 +40,4 @@ - + \ No newline at end of file diff --git a/BotProject/Templates/CSharp/ComposerBot.cs b/BotProject/Templates/CSharp/ComposerBot.cs index 45cb85659a..0589c862fc 100644 --- a/BotProject/Templates/CSharp/ComposerBot.cs +++ b/BotProject/Templates/CSharp/ComposerBot.cs @@ -14,7 +14,6 @@ namespace Microsoft.Bot.Builder.ComposerBot.Json { public class ComposerBot : ActivityHandler { - private AdaptiveDialog rootDialog; private readonly ResourceExplorer resourceExplorer; private readonly UserState userState; private DialogManager dialogManager; @@ -23,9 +22,7 @@ public class ComposerBot : ActivityHandler private readonly ISourceMap sourceMap; private readonly string rootDialogFile; - private readonly IBotTelemetryClient telemetryClient; - - public ComposerBot(string rootDialogFile, ConversationState conversationState, UserState userState, ResourceExplorer resourceExplorer, ISourceMap sourceMap, IBotTelemetryClient telemetryClient) + public ComposerBot(string rootDialogFile, ConversationState conversationState, UserState userState, ResourceExplorer resourceExplorer, ISourceMap sourceMap) { this.conversationState = conversationState; this.userState = userState; @@ -33,15 +30,11 @@ public ComposerBot(string rootDialogFile, ConversationState conversationState, U this.sourceMap = sourceMap; this.resourceExplorer = resourceExplorer; this.rootDialogFile = rootDialogFile; - this.telemetryClient = telemetryClient; - DeclarativeTypeLoader.AddComponent(new QnAMakerComponentRegistration()); - LoadRootDialogAsync(); } - + public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken)) { - this.telemetryClient.TrackTrace("Activity:" + turnContext.Activity.Text, Severity.Information, null); await this.dialogManager.OnTurnAsync(turnContext, cancellationToken: cancellationToken); await this.conversationState.SaveChangesAsync(turnContext, false, cancellationToken); await this.userState.SaveChangesAsync(turnContext, false, cancellationToken); @@ -50,8 +43,10 @@ public ComposerBot(string rootDialogFile, ConversationState conversationState, U private void LoadRootDialogAsync() { var rootFile = resourceExplorer.GetResource(rootDialogFile); - rootDialog = DeclarativeTypeLoader.Load(rootFile, resourceExplorer, sourceMap); - this.dialogManager = new DialogManager(rootDialog); + var rootDialog = resourceExplorer.LoadType(rootFile); + this.dialogManager = new DialogManager(rootDialog) + .UseResourceExplorer(resourceExplorer) + .UseLanguageGeneration(); } } } diff --git a/BotProject/Templates/CSharp/LuisConfigAdaptor.cs b/BotProject/Templates/CSharp/LuisConfigAdaptor.cs new file mode 100644 index 0000000000..6f23086f3b --- /dev/null +++ b/BotProject/Templates/CSharp/LuisConfigAdaptor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; + +namespace Microsoft.Bot.Builder.ComposerBot.Json +{ + public static class LuisConfigAdaptor + { + public static IConfigurationBuilder UseLuisConfigAdaptor(this IConfigurationBuilder builder) + { + var configuration = builder.Build(); + var settings = new Dictionary(); + settings["environment"] = configuration.GetValue("luis:environment"); + settings["region"] = configuration.GetValue("luis:authoringRegion"); + builder.AddInMemoryCollection(settings); + return builder; + } + } +} diff --git a/BotProject/Templates/CSharp/Program.cs b/BotProject/Templates/CSharp/Program.cs index 01920c40ba..434f5a21d2 100644 --- a/BotProject/Templates/CSharp/Program.cs +++ b/BotProject/Templates/CSharp/Program.cs @@ -1,11 +1,13 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using Microsoft.Bot.Builder.AI.Luis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -15,42 +17,42 @@ public class Program { public static void Main(string[] args) { - BuildWebHost(args).Run(); + CreateHostBuilder(args).Build().Run(); } - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .ConfigureAppConfiguration((hostingContext, config) => + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureAppConfiguration((hostingContext, builder) => { var env = hostingContext.HostingEnvironment; - var luisAuthoringRegion = Environment.GetEnvironmentVariable("LUIS_AUTHORING_REGION") ?? "westus"; - config - .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true) - .AddJsonFile($"ComposerDialogs/settings/appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"luis.settings.{env.EnvironmentName}.{luisAuthoringRegion}.json", optional: true, reloadOnChange: true) - .AddJsonFile($"luis.settings.{Environment.UserName}.{luisAuthoringRegion}.json", optional: true, reloadOnChange: true); - try + + builder.AddJsonFile($"ComposerDialogs/settings/appsettings.json", optional: true, reloadOnChange: true) + .AddJsonFile("appsetting.json", optional: true, reloadOnChange: true) + .UseLuisConfigAdaptor() + .UseLuisSettings(); + + if (env.IsDevelopment()) { - foreach (string filePath in Directory.GetFiles($"ComposerDialogs", "generated/luis.settings.*.json")) - { - config.AddJsonFile(filePath, optional: true, reloadOnChange: true); - } + // Local Debug + builder.AddJsonFile("appsettings.development.json", optional: true, reloadOnChange: true); } - catch (Exception ex) + else { - Trace.WriteLine(ex.Message); + //Azure Deploy + builder.AddJsonFile("appsettings.deployment.json", optional: true, reloadOnChange: true); } - if (env.IsDevelopment()) + if (!env.IsDevelopment()) { - config.AddUserSecrets(); + builder.AddUserSecrets(); } - config - .AddEnvironmentVariables() - .AddCommandLine(args); - }).UseStartup() - .Build(); + builder.AddEnvironmentVariables() + .AddCommandLine(args); + }) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); } } diff --git a/BotProject/Templates/CSharp/README.md b/BotProject/Templates/CSharp/README.md index 7f9cbbefb2..b096ecf323 100644 --- a/BotProject/Templates/CSharp/README.md +++ b/BotProject/Templates/CSharp/README.md @@ -5,14 +5,14 @@ Bot project is the launcher project for the bots written in declarative form (JS The Bot Project is a regular Bot Framework SDK V4 project. Before you can launch it from the emulator, you need to make sure you can run the bot. ### Prerequisite: -* Install .Netcore 2 +* Install .Netcore 3.1 ### Commands: * from root folder * cd BotProject -* cd CSharp -* dotnet restore // for the package updates +* cd Templates/CSharp +* dotnet user-secrets init // init the user secret id * dotnet build // build * dotnet run // start the bot * It will start a web server and listening at http://localhost:3979. @@ -20,36 +20,3 @@ The Bot Project is a regular Bot Framework SDK V4 project. Before you can launch ### Test bot * You can set you emulator to connect to http://localhost:3979/api/messages. -### config your bot -This setup is required for local testing of your Bot Runtime. -* The only thing you need to config is appsetting.json, which has a bot setting to launch the bot: - -``` -appsettings.json: -"bot": { - "provider": "localDisk", - "path": "../../Bots/SampleBot3/bot3.botproj" -} -``` - -## .botproj folder structure -``` -bot.botproj, bot project got the rootDialog from "entry" -{ - "services": [{ - "type": "luis", - "id": "1", - "name": "TodoBotLuis", - "lufile": "todo.lu", - "applicationId": "TodoBotLuis.applicationId", - "endpointKey": "TodoBotLuis.endpointKey", - "endpoint": "TodoBotLuis.endpoint" - }], - "files": [ - "*.dialog", - "*.lg" - ], - "entry": "main.dialog" -} -``` -* Please refer to [Samples](https://github.com/Microsoft/BotFramework-Composer/tree/master/SampleBots) for more samples. diff --git a/BotProject/Templates/CSharp/Schemas/sdk.schema b/BotProject/Templates/CSharp/Schemas/sdk.schema index 1fafc1696f..a1c8661f27 100644 --- a/BotProject/Templates/CSharp/Schemas/sdk.schema +++ b/BotProject/Templates/CSharp/Schemas/sdk.schema @@ -999,7 +999,7 @@ "unrecognizedPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", + "description": "Message to send when the recognizer does not understand the user input.", "examples": [ "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" ], @@ -1008,7 +1008,7 @@ "invalidPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", + "description": "Message to send when the user input does not meet any validation expression.", "examples": [ "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" ], @@ -1072,7 +1072,7 @@ "string" ], "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", + "description": "'Property' will be set to the value of this expression when max turn count is exceeded.", "examples": [ "@userName", "coalesce(@number, @partySize)" @@ -1089,7 +1089,7 @@ "string" ], "title": "Value", - "description": "Gets or sets a value expression which can be used to intialize the input prompt.", + "description": "'Property' will be set to the value of this expression unless it evaluates to null.", "examples": [ "@userName" ] @@ -1527,7 +1527,7 @@ "unrecognizedPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", + "description": "Message to send when the recognizer does not understand the user input.", "examples": [ "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" ], @@ -1536,7 +1536,7 @@ "invalidPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", + "description": "Message to send when the user input does not meet any validation expression.", "examples": [ "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" ], @@ -1600,7 +1600,7 @@ "string" ], "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", + "description": "'Property' will be set to the value of this expression when max turn count is exceeded.", "examples": [ "@userName", "coalesce(@number, @partySize)" @@ -1617,7 +1617,7 @@ "string" ], "title": "Value", - "description": "Gets or sets a value expression which can be used to intialize the input prompt.", + "description": "'Property' will be set to the value of this expression unless it evaluates to null.", "examples": [ "@userName" ] @@ -1939,7 +1939,7 @@ "unrecognizedPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", + "description": "Message to send when the recognizer does not understand the user input.", "examples": [ "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" ], @@ -1948,7 +1948,7 @@ "invalidPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", + "description": "Message to send when the user input does not meet any validation expression.", "examples": [ "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" ], @@ -2012,7 +2012,7 @@ "string" ], "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", + "description": "'Property' will be set to the value of this expression when max turn count is exceeded.", "examples": [ "@userName", "coalesce(@number, @partySize)" @@ -2029,7 +2029,7 @@ "string" ], "title": "Value", - "description": "Gets or sets a value expression which can be used to intialize the input prompt.", + "description": "'Property' will be set to the value of this expression unless it evaluates to null.", "examples": [ "@userName" ] @@ -2552,7 +2552,7 @@ "unrecognizedPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", + "description": "Message to send when the recognizer does not understand the user input.", "examples": [ "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" ], @@ -2561,7 +2561,7 @@ "invalidPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", + "description": "Message to send when the user input does not meet any validation expression.", "examples": [ "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" ], @@ -2625,7 +2625,7 @@ "string" ], "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", + "description": "'Property' will be set to the value of this expression when max turn count is exceeded.", "examples": [ "@userName", "coalesce(@number, @partySize)" @@ -2642,7 +2642,7 @@ "string" ], "title": "Value", - "description": "Gets or sets a value expression which can be used to intialize the input prompt.", + "description": "'Property' will be set to the value of this expression unless it evaluates to null.", "examples": [ "@userName" ] @@ -5763,7 +5763,7 @@ "unrecognizedPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", + "description": "Message to send when the recognizer does not understand the user input.", "examples": [ "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" ], @@ -5772,7 +5772,7 @@ "invalidPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", + "description": "Message to send when the user input does not meet any validation expression.", "examples": [ "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" ], @@ -5836,7 +5836,7 @@ "string" ], "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", + "description": "'Property' will be set to the value of this expression when max turn count is exceeded.", "examples": [ "@userName", "coalesce(@number, @partySize)" @@ -5853,7 +5853,7 @@ "string" ], "title": "Value", - "description": "Gets or sets a value expression which can be used to intialize the input prompt.", + "description": "'Property' will be set to the value of this expression unless it evaluates to null.", "examples": [ "@userName" ] @@ -11520,7 +11520,7 @@ "unrecognizedPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Unrecognized prompt", - "description": "Message to send if user response is not recognized.", + "description": "Message to send when the recognizer does not understand the user input.", "examples": [ "Sorry, I do not understand '{turn.activity.text'}. Let's try again. What is your birth date?" ], @@ -11529,7 +11529,7 @@ "invalidPrompt": { "$kind": "Microsoft.IActivityTemplate", "title": "Invalid prompt", - "description": "Message to send if user response is invalid. Relies on specified validation expressions.", + "description": "Message to send when the user input does not meet any validation expression.", "examples": [ "Sorry, '{this.value}' does not work. I need a number between 1-150. What is your age?" ], @@ -11593,7 +11593,7 @@ "string" ], "title": "Default value", - "description": "Expression to examine on each turn of the conversation as possible value to the property.", + "description": "'Property' will be set to the value of this expression when max turn count is exceeded.", "examples": [ "@userName", "coalesce(@number, @partySize)" @@ -11610,7 +11610,7 @@ "string" ], "title": "Value", - "description": "Gets or sets a value expression which can be used to intialize the input prompt.", + "description": "'Property' will be set to the value of this expression unless it evaluates to null.", "examples": [ "@userName" ] @@ -11648,7 +11648,7 @@ "description": "Expression to format the output.", "examples": [ "=toUpper(this.value)", - "@{toUpper(this.value)}" + "${toUpper(this.value)}" ] } }, @@ -12034,4 +12034,4 @@ ] } } -} \ No newline at end of file +} diff --git a/BotProject/Templates/CSharp/Scripts/create.ps1 b/BotProject/Templates/CSharp/Scripts/create.ps1 index 20ca8ddc86..d3ac6cac73 100644 --- a/BotProject/Templates/CSharp/Scripts/create.ps1 +++ b/BotProject/Templates/CSharp/Scripts/create.ps1 @@ -24,9 +24,9 @@ else { New-Item -Path $logFile | Out-Null } -if (-not (Test-Path (Join-Path $projDir 'appsettings.json'))) +if (-not (Test-Path (Join-Path $projDir 'appsettings.deployment.json'))) { - Write-Host "! Could not find an 'appsettings.json' file in the current directory." -ForegroundColor DarkRed + Write-Host "! Could not find an 'appsettings.deployment.json' file in the current directory." -ForegroundColor DarkRed Write-Host "+ Please re-run this script from your project directory." -ForegroundColor Magenta Break } @@ -152,24 +152,22 @@ if ($outputs) $outputMap = @{} $outputs.PSObject.Properties | Foreach-Object { $outputMap[$_.Name] = $_.Value } - # Update appsettings.json - Write-Host "> Updating appsettings.json ..." - if (Test-Path $(Join-Path $projDir appsettings.json)) { - $settings = Get-Content $(Join-Path $projDir appsettings.json) | ConvertFrom-Json + # Update appsettings.deployment.json + Write-Host "> Updating appsettings.deployment.json ..." + if (Test-Path $(Join-Path $projDir appsettings.deployment.json)) { + $settings = Get-Content $(Join-Path $projDir appsettings.deployment.json) | ConvertFrom-Json } else { $settings = New-Object PSObject } - $settings | Add-Member -Type NoteProperty -Force -Name 'microsoftAppId' -Value $appId - - dotnet user-secrets init --project $projDir - dotnet user-secrets set "MicrosoftAppPassword" $appPassword --project $projDir + $settings | Add-Member -Type NoteProperty -Force -Name 'MicrosoftAppId' -Value $appId + $settings | Add-Member -Type NoteProperty -Force -Name 'MicrosoftAppPassword' -Value $appPassword $settings | Add-Member -Type NoteProperty -Force -Name 'bot' -Value "ComposerDialogs" foreach ($key in $outputMap.Keys) { $settings | Add-Member -Type NoteProperty -Force -Name $key -Value $outputMap[$key].value } - $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $projDir appsettings.json) + $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $projDir appsettings.deployment.json) Write-Host "> Done." Write-Host "- App Id: $appId" @@ -210,4 +208,4 @@ else Write-Host "+ To delete this resource group, run 'az group delete -g $($resourceGroup) --no-wait'" -ForegroundColor Magenta Break -} \ No newline at end of file +} diff --git a/BotProject/Templates/CSharp/Scripts/deploy.ps1 b/BotProject/Templates/CSharp/Scripts/deploy.ps1 index 8d6c86ac60..faab0667c4 100644 --- a/BotProject/Templates/CSharp/Scripts/deploy.ps1 +++ b/BotProject/Templates/CSharp/Scripts/deploy.ps1 @@ -1,4 +1,4 @@ -Param( +Param( [string] $name, [string] $environment, [string] $luisAuthoringKey, @@ -56,7 +56,7 @@ if (Test-Path $zipPath) { dotnet user-secrets init # Perform dotnet publish step ahead of zipping up -$publishFolder = $(Join-Path $projFolder 'bin\Release\netcoreapp2.2') +$publishFolder = $(Join-Path $projFolder 'bin\Release\netcoreapp3.1') dotnet publish -c release -o $publishFolder -v q > $logFile @@ -73,26 +73,8 @@ else { Copy-Item -Path $localBotPath -Recurse -Destination $publishFolder -Container -Force } -# Merge from custom config files -$customConfigFiles = Get-ChildItem -Path $remoteBotPath -Include "appsettings.json" -Recurse -Force -if ($customConfigFiles) { - if (Test-Path $(Join-Path $publishFolder appsettings.json)) { - $settings = Get-Content $(Join-Path $publishFolder appsettings.json) | ConvertFrom-Json - } - else { - $settings = New-Object PSObject - } - - $customConfig = @{ } - $customSetting = Get-Content $customConfigFiles.FullName | ConvertFrom-Json - $customSetting.PSObject.Properties | Foreach-Object { $customConfig[$_.Name] = $_.Value } - foreach ($key in $customConfig.Keys) { $settings | Add-Member -Type NoteProperty -Force -Name $key -Value $customConfig[$key] } - - $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.json) -} - # Try to get luis config from appsettings -$settings = Get-Content $(Join-Path $projFolder appsettings.json) | ConvertFrom-Json +$settings = Get-Content $(Join-Path $projFolder appsettings.deployment.json) | ConvertFrom-Json $luisSettings = $settings.luis if (-not $luisAuthoringKey) { @@ -131,13 +113,17 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { $luconfigjson | ConvertTo-Json -Depth 100 | Out-File $(Join-Path $remoteBotPath luconfig.json) - # Execute lubuild command - if (Get-Command lubuild -errorAction SilentlyContinue) { - lubuild --authoringKey $luisAuthoringKey + # Execute bf luis:build command + if (Get-Command bf -errorAction SilentlyContinue) { + $customizedSettings = Get-Content $(Join-Path $remoteBotPath settings appsettings.json) | ConvertFrom-Json + $customizedEnv = $customizedSettings.luis.environment + bf luis:build --in .\ --botName $name --authoringKey $luisAuthoringKey --dialog --out .\generated --suffix $customizedEnv -f } else { - Write-Host "lubuild does not exist, use the following command to install lubuild:" - Write-Host "npm install -g https://botbuilder.myget.org/F/botbuilder-declarative/npm/lubuild/-/1.0.3-preview.tgz" + Write-Host "bf luis:build does not exist, use the following command to install:" + Write-Host "1. npm config set registry https://botbuilder.myget.org/F/botframework-cli/npm/" + Write-Host "2. npm install -g @microsoft/botframework-cli" + Write-Host "3. npm config set registry http://registry.npmjs.org" Break } @@ -152,8 +138,8 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { Set-Location -Path $projFolder # change setting file in publish folder - if (Test-Path $(Join-Path $publishFolder appsettings.json)) { - $settings = Get-Content $(Join-Path $publishFolder appsettings.json) | ConvertFrom-Json + if (Test-Path $(Join-Path $publishFolder appsettings.deployment.json)) { + $settings = Get-Content $(Join-Path $publishFolder appsettings.deployment.json) | ConvertFrom-Json } else { $settings = New-Object PSObject @@ -180,7 +166,7 @@ if ($luisAuthoringKey -and $luisAuthoringRegion) { $settings | Add-Member -Type NoteProperty -Force -Name 'luis' -Value $luisConfig - $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.json) + $settings | ConvertTo-Json -depth 100 | Out-File $(Join-Path $publishFolder appsettings.deployment.json) $tokenResponse = (az account get-access-token) | ConvertFrom-Json $token = $tokenResponse.accessToken @@ -251,4 +237,4 @@ if ($?) { else { Write-Host "! Could not deploy automatically to Azure. Review the log for more information." -ForegroundColor DarkRed Write-Host "! Log: $($logFile)" -ForegroundColor DarkRed -} \ No newline at end of file +} diff --git a/BotProject/Templates/CSharp/Startup.cs b/BotProject/Templates/CSharp/Startup.cs index f6b769dd96..f4f532942d 100644 --- a/BotProject/Templates/CSharp/Startup.cs +++ b/BotProject/Templates/CSharp/Startup.cs @@ -44,8 +44,6 @@ public void ConfigureServices(IServiceCollection services) // Create the credential provider to be used with the Bot Framework Adapter. services.AddSingleton(); - services.AddSingleton(); - // Load settings var settings = new BotSettings(); Configuration.Bind(settings); @@ -66,18 +64,9 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(storage); var userState = new UserState(storage); var conversationState = new ConversationState(storage); - var inspectionState = new InspectionState(storage); - - // Configure telemetry - services.AddApplicationInsightsTelemetry(); - var telemetryClient = new BotTelemetryClient(new TelemetryClient()); - services.AddSingleton(telemetryClient); - services.AddBotApplicationInsights(telemetryClient); var botFile = Configuration.GetSection("bot").Get(); - TypeFactory.Configuration = this.Configuration; - // manage all bot resources var resourceExplorer = new ResourceExplorer().AddFolder(botFile); @@ -85,15 +74,12 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton((s) => { + HostContext.Current.Set(Configuration); + var adapter = new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration)); adapter .UseStorage(storage) - .UseState(userState, conversationState) - .UseAdaptiveDialogs() - .UseResourceExplorer(resourceExplorer) - .UseLanguageGeneration(resourceExplorer, "common.lg") - .Use(new RegisterClassMiddleware(Configuration)) - .Use(new InspectionMiddleware(inspectionState, userState, conversationState, credentials)); + .UseState(userState, conversationState); if (!string.IsNullOrEmpty(settings.BlobStorage.ConnectionString) && !string.IsNullOrEmpty(settings.BlobStorage.Container)) { @@ -107,28 +93,18 @@ public void ConfigureServices(IServiceCollection services) adapter.OnTurnError = async (turnContext, exception) => { await turnContext.SendActivityAsync(exception.Message).ConfigureAwait(false); - telemetryClient.TrackException(new Exception("Exceptions: " + exception.Message)); await conversationState.ClearStateAsync(turnContext).ConfigureAwait(false); await conversationState.SaveChangesAsync(turnContext).ConfigureAwait(false); }; return adapter; }); - services.AddSingleton((sp) => new ComposerBot("Main.dialog", conversationState, userState, resourceExplorer, DebugSupport.SourceMap, telemetryClient)); + services.AddSingleton((sp) => new ComposerBot("Main.dialog", conversationState, userState, resourceExplorer, DebugSupport.SourceMap)); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseHsts(); - } - app.UseDefaultFiles(); app.UseStaticFiles(); app.UseWebSockets(); diff --git a/BotProject/Templates/CSharp/Tests/ActionsTests.cs b/BotProject/Templates/CSharp/Tests/ActionsTests.cs index fbfedc8690..920cd9a00f 100644 --- a/BotProject/Templates/CSharp/Tests/ActionsTests.cs +++ b/BotProject/Templates/CSharp/Tests/ActionsTests.cs @@ -34,7 +34,6 @@ private static string getFolderPath(string path) [ClassInitialize] public static void ClassInitialize(TestContext context) { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory)); } @@ -156,6 +155,7 @@ await BuildTestFlow(getFolderPath("ActionsSample"), sendTrace: true) } [TestMethod] + [Ignore] public async Task Actions_09EditActions() { await BuildTestFlow(getFolderPath("ActionsSample")) @@ -204,7 +204,6 @@ await BuildTestFlow(getFolderPath("ActionsSample")) private TestFlow BuildTestFlow(string folderPath, bool sendTrace = false) { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); var storage = new MemoryStorage(); var convoState = new ConversationState(storage); var userState = new UserState(storage); @@ -213,15 +212,14 @@ private TestFlow BuildTestFlow(string folderPath, bool sendTrace = false) resourceExplorer.AddFolder(folderPath); adapter .UseStorage(storage) - .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) + .UseState(userState, convoState) .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); + var dialog = resourceExplorer.LoadType(resource); + DialogManager dm = new DialogManager(dialog) + .UseResourceExplorer(resourceExplorer) + .UseLanguageGeneration(); ; return new TestFlow(adapter, async (turnContext, cancellationToken) => { diff --git a/BotProject/Templates/CSharp/Tests/ControllingConversationTests.cs b/BotProject/Templates/CSharp/Tests/ControllingConversationTests.cs index 593d7666f8..a100e4565e 100644 --- a/BotProject/Templates/CSharp/Tests/ControllingConversationTests.cs +++ b/BotProject/Templates/CSharp/Tests/ControllingConversationTests.cs @@ -30,7 +30,6 @@ public class ControllingConversationTests [ClassInitialize] public static void ClassInitialize(TestContext context) { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "ControllingConversationFlowSample")); resourceExplorer.AddFolder(path); } @@ -75,15 +74,12 @@ await BuildTestFlow() .Send("Yes") .AssertReply("Do you want to repeat this dialog, yes to repeat, no to end this dialog (1) Yes or (2) No") .Send("No") - .Send("05") - .AssertReply("Canceled.") .StartTestAsync(); } private TestFlow BuildTestFlow(bool sendTrace = false) { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); var storage = new MemoryStorage(); var convoState = new ConversationState(storage); var userState = new UserState(storage); @@ -91,14 +87,13 @@ private TestFlow BuildTestFlow(bool sendTrace = false) adapter .UseStorage(storage) .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); + var dialog = resourceExplorer.LoadType(resource); + DialogManager dm = new DialogManager(dialog) + .UseResourceExplorer(resourceExplorer) + .UseLanguageGeneration(); return new TestFlow(adapter, async (turnContext, cancellationToken) => { diff --git a/BotProject/Templates/CSharp/Tests/InputsTests.cs b/BotProject/Templates/CSharp/Tests/InputsTests.cs index 0bf735d2fe..fe1ce5e0e0 100644 --- a/BotProject/Templates/CSharp/Tests/InputsTests.cs +++ b/BotProject/Templates/CSharp/Tests/InputsTests.cs @@ -31,7 +31,6 @@ public class InputsTests [ClassInitialize] public static void ClassInitialize(TestContext context) { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "AskingQuestionsSample")); resourceExplorer.AddFolder(path); } @@ -116,7 +115,6 @@ await BuildTestFlow() private TestFlow BuildTestFlow(bool sendTrace = false) { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); var storage = new MemoryStorage(); var convoState = new ConversationState(storage); var userState = new UserState(storage); @@ -124,14 +122,13 @@ private TestFlow BuildTestFlow(bool sendTrace = false) adapter .UseStorage(storage) .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); + var dialog = resourceExplorer.LoadType(resource); + DialogManager dm = new DialogManager(dialog) + .UseResourceExplorer(resourceExplorer) + .UseLanguageGeneration(); return new TestFlow(adapter, async (turnContext, cancellationToken) => { diff --git a/BotProject/Templates/CSharp/Tests/MessageTests.cs b/BotProject/Templates/CSharp/Tests/MessageTests.cs index d5fb6dc42c..d68c119860 100644 --- a/BotProject/Templates/CSharp/Tests/MessageTests.cs +++ b/BotProject/Templates/CSharp/Tests/MessageTests.cs @@ -34,7 +34,6 @@ public class MessageTests [ClassInitialize] public static void ClassInitialize(TestContext context) { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "RespondingWithTextSample")); resourceExplorer.AddFolder(path); } @@ -72,7 +71,6 @@ await BuildTestFlow() private TestFlow BuildTestFlow(bool sendTrace = false) { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); var storage = new MemoryStorage(); var convoState = new ConversationState(storage); var userState = new UserState(storage); @@ -80,14 +78,13 @@ private TestFlow BuildTestFlow(bool sendTrace = false) adapter .UseStorage(storage) .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); + var dialog = resourceExplorer.LoadType(resource); + DialogManager dm = new DialogManager(dialog) + .UseResourceExplorer(resourceExplorer) + .UseLanguageGeneration(); return new TestFlow(adapter, async (turnContext, cancellationToken) => { diff --git a/BotProject/Templates/CSharp/Tests/ToDoBotTests.cs b/BotProject/Templates/CSharp/Tests/ToDoBotTests.cs index b2dca363f0..b60b279307 100644 --- a/BotProject/Templates/CSharp/Tests/ToDoBotTests.cs +++ b/BotProject/Templates/CSharp/Tests/ToDoBotTests.cs @@ -30,7 +30,6 @@ public class ToDoBotTests [ClassInitialize] public static void ClassInitialize(TestContext context) { - TypeFactory.Configuration = new ConfigurationBuilder().AddInMemoryCollection().Build(); string path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, samplesDirectory, "TodoSample")); resourceExplorer.AddFolder(path); } @@ -68,7 +67,6 @@ await BuildTestFlow() private TestFlow BuildTestFlow(bool sendTrace = false) { - TypeFactory.Configuration = new ConfigurationBuilder().Build(); var storage = new MemoryStorage(); var convoState = new ConversationState(storage); var userState = new UserState(storage); @@ -76,14 +74,13 @@ private TestFlow BuildTestFlow(bool sendTrace = false) adapter .UseStorage(storage) .UseState(userState, convoState) - .UseAdaptiveDialogs() - .UseLanguageGeneration(resourceExplorer, "common.lg") - .UseResourceExplorer(resourceExplorer) .Use(new TranscriptLoggerMiddleware(new FileTranscriptLogger())); var resource = resourceExplorer.GetResource("Main.dialog"); - var dialog = DeclarativeTypeLoader.Load(resource, resourceExplorer, DebugSupport.SourceMap); - DialogManager dm = new DialogManager(dialog); + var dialog = resourceExplorer.LoadType(resource); + DialogManager dm = new DialogManager(dialog) + .UseResourceExplorer(resourceExplorer) + .UseLanguageGeneration(); return new TestFlow(adapter, async (turnContext, cancellationToken) => { diff --git a/BotProject/Templates/CSharp/appsettings.Deployment.json b/BotProject/Templates/CSharp/appsettings.Deployment.json new file mode 100644 index 0000000000..1797133380 --- /dev/null +++ b/BotProject/Templates/CSharp/appsettings.Deployment.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/BotProject/Templates/CSharp/appsettings.Development.json b/BotProject/Templates/CSharp/appsettings.Development.json index e203e9407e..0db3279e44 100644 --- a/BotProject/Templates/CSharp/appsettings.Development.json +++ b/BotProject/Templates/CSharp/appsettings.Development.json @@ -1,9 +1,3 @@ { - "Logging": { - "LogLevel": { - "Default": "Debug", - "System": "Information", - "Microsoft": "Information" - } - } + } diff --git a/CHANGELOG.md b/CHANGELOG.md index d0f67e1b60..aee5732f2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,57 @@ ## Releases +### 03-09-2020 + +#### Added + +- feat: Remove input LU when deconstructing prompts ([#2180](https://github.com/microsoft/BotFramework-Composer/pull/2180)) ([@tdurnford](https://github.com/tdurnford)) +- feat: Add cross train before luis publish ([#2069](https://github.com/microsoft/BotFramework-Composer/pull/2069)) ([@cwhitten](https://github.com/cwhitten)) +- feat: Added inline lu to prompts ([#2159](https://github.com/microsoft/BotFramework-Composer/pull/2159)) ([@cwhitten](https://github.com/cwhitten)) +- feat: implement new action design to improve readability ([#2136](https://github.com/microsoft/BotFramework-Composer/pull/2136)) ([@cwhitten](https://github.com/cwhitten)) +- feat: repaint ui for setProperties in visual editor ([#2017](https://github.com/microsoft/BotFramework-Composer/pull/2017)) ([@alanlong9278](https://github.com/alanlong9278)) +- feat: Update package and schema to 200216 ([#1997](https://github.com/microsoft/BotFramework-Composer/pull/1997)) ([@luhan2017](https://github.com/luhan2017)) +- feat: update new trigger modal according to design ([#1786](https://github.com/microsoft/BotFramework-Composer/pull/1786)) ([@liweitian](https://github.com/liweitian)) +- feat: display 6 actions as contentless node ([#2108](https://github.com/microsoft/BotFramework-Composer/pull/2108)) ([@yeze322](https://github.com/yeze322)) +- feat: support multi-line node block in Visual Editor ([#2005](https://github.com/microsoft/BotFramework-Composer/pull/2005)) ([@yeze322](https://github.com/yeze322)) +- feat: add more help links ([#2070](https://github.com/microsoft/BotFramework-Composer/pull/2070)) ([@a-b-r-o-w-n](https://github.com/a-b-r-o-w-n)) +- feat: support inline LU section editing ([#1994](https://github.com/microsoft/BotFramework-Composer/pull/1994)) ([@zhixzhan](https://github.com/zhixzhan)) + +#### Fixed + +- fix: trigger creation bug ([#2151](https://github.com/microsoft/BotFramework-Composer/pull/2151)) ([@liweitian](https://github.com/liweitian)) +- fix: lu build bug when training empty intents ([#2201](https://github.com/microsoft/BotFramework-Composer/pull/2201)) ([@lei9444](https://github.com/lei9444)) +- fix: use nightly build to replace the private bf-lu package ([#2190](https://github.com/microsoft/BotFramework-Composer/pull/2190)) ([@lei9444](https://github.com/lei9444)) +- fix: Moved value field to user tab and removed inline lu from attachment input ([#2194](https://github.com/microsoft/BotFramework-Composer/pull/2194)) ([@tdurnford](https://github.com/tdurnford)) +- fix: load schema files when loading bot project ([#2170](https://github.com/microsoft/BotFramework-Composer/pull/2170)) ([@a-b-r-o-w-n](https://github.com/a-b-r-o-w-n)) +- fix: deployment script, decouple debugging and deployment settings ([#2153](https://github.com/microsoft/BotFramework-Composer/pull/2153)) ([@zidaneymar](https://github.com/zidaneymar)) +- fix: Double scroll bars in dialog's properties pane ([#2163](https://github.com/microsoft/BotFramework-Composer/pull/2163)) ([@alanlong9278](https://github.com/alanlong9278)) +- fix: lg template display wrong in visual & form editor ([#2191](https://github.com/microsoft/BotFramework-Composer/pull/2191)) ([@alanlong9278](https://github.com/alanlong9278)) +- fix: remove edit button in lu all up view ([#2146](https://github.com/microsoft/BotFramework-Composer/pull/2146)) ([@cwhitten](https://github.com/cwhitten)) +- a11y: Use header tag for trigger in visual editor ([#2128](https://github.com/microsoft/BotFramework-Composer/pull/2128)) ([@cwhitten](https://github.com/cwhitten)) +- fix: update lu format link ([#2107](https://github.com/microsoft/BotFramework-Composer/pull/2107)) ([@liweitian](https://github.com/liweitian)) +- fix: resolve known bugs in LU LSP. ([#2098](https://github.com/microsoft/BotFramework-Composer/pull/2098)) ([@cosmicshuai](https://github.com/cosmicshuai)) +- fix: no longer show duplicate lg error notifications ([#2100](https://github.com/microsoft/BotFramework-Composer/pull/2100)) ([@zhixzhan](https://github.com/zhixzhan)) +- fix: replace animated screenshots with a static screenshot ([#2045](https://github.com/microsoft/BotFramework-Composer/pull/2045)) ([@benbrown](https://github.com/benbrown)) +- fix: support copy actions across dialogs ([#2198](https://github.com/microsoft/BotFramework-Composer/pull/2198)) ([@yeze322](https://github.com/yeze322)) +- fix: Default ActivityProcessed to true (bool) ([#2189](https://github.com/microsoft/BotFramework-Composer/pull/2189)) ([@cwhitten](https://github.com/cwhitten)) +- a11y: add name for nodeMenu, arrow and endNode ([#2131](https://github.com/microsoft/BotFramework-Composer/pull/2131)) ([@cwhitten](https://github.com/cwhitten)) +- a11y: add role, name, posinset for paste in edgeMenu ([#2126](https://github.com/microsoft/BotFramework-Composer/pull/2126)) ([@alanlong9278](https://github.com/alanlong9278)) +- fix: fix 'sort()' function of steps and cases in visual editor ([#2166](https://github.com/microsoft/BotFramework-Composer/pull/2166)) ([@yeze322](https://github.com/yeze322)) + +#### Changed + +- style: fix hover state nodes ui ([#2065](https://github.com/microsoft/BotFramework-Composer/pull/2065)) ([@alanlong9278](https://github.com/alanlong9278)) + +#### Other + +- ci: add a11y pr title prefix for accessibility prs ([#2171](https://github.com/microsoft/BotFramework-Composer/pull/2171)) ([@a-b-r-o-w-n](https://github.com/a-b-r-o-w-n)) +- docs: update docs to fix LU file format link ([#2071](https://github.com/microsoft/BotFramework-Composer/pull/2071)) ([@vishwacsena](https://github.com/vishwacsena)) +- chore: merge stable release into master ([@a-b-r-o-w-n](https://github.com/a-b-r-o-w-n)) +- docs: clarify setup ([#2145](https://github.com/microsoft/BotFramework-Composer/pull/2145)) ([@DaraOladapo](https://github.com/DaraOladapo)) +- docs: update dotnet requirement in setup docs ([#2160](https://github.com/microsoft/BotFramework-Composer/pull/2160)) ([@vkacherov](https://github.com/vkacherov)) +- samples: Update HttpRequest sample ([#2161](https://github.com/microsoft/BotFramework-Composer/pull/2161)) ([@luhan2017](https://github.com/luhan2017)) + ### 02-21-2020 #### Added diff --git a/Composer/cypress/integration/LUPage.spec.ts b/Composer/cypress/integration/LUPage.spec.ts index f989d4dce7..950e35157a 100644 --- a/Composer/cypress/integration/LUPage.spec.ts +++ b/Composer/cypress/integration/LUPage.spec.ts @@ -14,10 +14,7 @@ context('LU Page', () => { cy.contains('ToDoBotWithLuisSample.Main'); cy.contains('All'); - cy.get('.toggleEditMode button').as('switchButton'); - - // all multiple file, edit mode button is disabled. - cy.get('@switchButton').should('be.disabled'); + cy.get('.toggleEditMode button').should('not.exist'); // by default is table view cy.findByTestId('LUEditor') @@ -28,7 +25,7 @@ context('LU Page', () => { cy.findByTestId('LUEditor').within(() => { cy.findByText('__TestToDoBotWithLuisSample.Main').click(); }); - + cy.get('.toggleEditMode button').as('switchButton'); // goto edit-mode cy.get('@switchButton').click(); cy.findByTestId('LUEditor') diff --git a/Composer/cypress/integration/NotificationPage.spec.ts b/Composer/cypress/integration/NotificationPage.spec.ts index ba00dc873d..eb7b06dfe8 100644 --- a/Composer/cypress/integration/NotificationPage.spec.ts +++ b/Composer/cypress/integration/NotificationPage.spec.ts @@ -13,7 +13,7 @@ context('Notification Page', () => { cy.get('.toggleEditMode button').as('switchButton'); cy.get('@switchButton').click(); - cy.get('textarea').type('test lg syntax error'); + cy.get('textarea').type('test lg syntax error#'); cy.visitPage('Notifications'); diff --git a/Composer/packages/client/__tests__/components/design.test.js b/Composer/packages/client/__tests__/components/design.test.js index ead441d63f..dbd3ce46fa 100644 --- a/Composer/packages/client/__tests__/components/design.test.js +++ b/Composer/packages/client/__tests__/components/design.test.js @@ -10,6 +10,13 @@ import { dialogs } from '../constants.json'; import { TriggerCreationModal } from './../../src/components/ProjectTree/TriggerCreationModal'; import { ProjectTree } from './../../src/components/ProjectTree'; import { CreateDialogModal } from './../../src/pages/design/createDialogModal'; + +jest.mock('@bfc/code-editor', () => { + return { + LuEditor: () =>
, + }; +}); + describe('', () => { it('should render the ProjectTree', async () => { const dialogId = 'Main'; diff --git a/Composer/packages/client/__tests__/store/reducer/reducer.test.js b/Composer/packages/client/__tests__/store/reducer/reducer.test.js index cc0e7521dd..ea8c9368eb 100644 --- a/Composer/packages/client/__tests__/store/reducer/reducer.test.js +++ b/Composer/packages/client/__tests__/store/reducer/reducer.test.js @@ -31,7 +31,7 @@ describe('test all reducer handlers', () => { payload: { id: 'common.lg', content: ` # bfdactivity-003038 - - You said '@{turn.activity.text}'`, + - You said '\${turn.activity.text}'`, }, } ); diff --git a/Composer/packages/client/package.json b/Composer/packages/client/package.json index c0fcfb6d77..ea80ad3363 100644 --- a/Composer/packages/client/package.json +++ b/Composer/packages/client/package.json @@ -20,12 +20,12 @@ "@bfc/extensions": "*", "@bfc/indexers": "*", "@bfc/shared": "*", - "@bfcomposer/bf-lu": "^1.1.8", + "@microsoft/bf-lu": "4.8.0-preview.111952", "@emotion/core": "^10.0.7", "@reach/router": "^1.2.1", "axios": "^0.18.0", - "botbuilder-lg": "4.7.0-preview.93464", - "botframework-expressions": "4.7.0-preview.93464", + "botbuilder-lg": "^4.8.0-preview.106823", + "botframework-expressions": "^4.8.0-preview.106476", "format-message": "^6.2.3", "immer": "^5.2.0", "jwt-decode": "^2.2.0", diff --git a/Composer/packages/client/public/extensionContainer.html b/Composer/packages/client/public/extensionContainer.html index 1e74464e72..56d4f64b40 100644 --- a/Composer/packages/client/public/extensionContainer.html +++ b/Composer/packages/client/public/extensionContainer.html @@ -47,6 +47,9 @@ width: 100%; height: 100%; } + #root { + overflow: auto; + } diff --git a/Composer/packages/client/src/ShellApi.ts b/Composer/packages/client/src/ShellApi.ts index ddbacb8775..11614ba51a 100644 --- a/Composer/packages/client/src/ShellApi.ts +++ b/Composer/packages/client/src/ShellApi.ts @@ -9,6 +9,7 @@ import get from 'lodash/get'; import { isExpression } from './utils'; import * as lgUtil from './utils/lgUtil'; import * as luUtil from './utils/luUtil'; +import { updateRegExIntent } from './utils/dialogUtil'; import { StoreContext } from './store'; import ApiClient from './messenger/ApiClient'; import { getDialogData, setDialogData, sanitizeDialogData } from './utils'; @@ -217,9 +218,9 @@ export const ShellApi: React.FC = () => { if (!file) throw new Error(`lu file ${id} not found`); if (!intentName) throw new Error(`intentName is missing or empty`); - const newLuContent = luUtil.updateIntent(file.content, intentName, intent); + const content = luUtil.updateIntent(file.content, intentName, intent); - return await updateLuFile({ id, newLuContent }); + return await updateLuFile({ id, content }); } async function addLuIntentHandler({ id, intent }, event) { @@ -227,9 +228,9 @@ export const ShellApi: React.FC = () => { const file = luFiles.find(file => file.id === id); if (!file) throw new Error(`lu file ${id} not found`); - const newLuContent = luUtil.addIntent(file.content, intent); + const content = luUtil.addIntent(file.content, intent); - return await updateLuFile({ id, newLuContent }); + return await updateLuFile({ id, content }); } async function removeLuIntentHandler({ id, intentName }, event) { @@ -238,9 +239,17 @@ export const ShellApi: React.FC = () => { if (!file) throw new Error(`lu file ${id} not found`); if (!intentName) throw new Error(`intentName is missing or empty`); - const newLuContent = luUtil.removeIntent(file.content, intentName); + const content = luUtil.removeIntent(file.content, intentName); - return await updateLuFile({ id, newLuContent }); + return await updateLuFile({ id, content }); + } + + async function updateRegExIntentHandler({ id, intentName, pattern }, event) { + if (isEventSourceValid(event) === false) return false; + const dialog = dialogs.find(dialog => dialog.id === id); + if (!dialog) throw new Error(`dialog ${dialogId} not found`); + const newDialog = updateRegExIntent(dialog, intentName, pattern); + return await updateDialog({ id, content: newDialog.content }); } async function fileHandler(fileTargetType, fileChangeType, { id, content }, event) { @@ -345,6 +354,7 @@ export const ShellApi: React.FC = () => { apiClient.registerApi('addLuIntent', addLuIntentHandler); apiClient.registerApi('updateLuIntent', updateLuIntentHandler); apiClient.registerApi('removeLuIntent', removeLuIntentHandler); + apiClient.registerApi('updateRegExIntent', updateRegExIntentHandler); apiClient.registerApi('navTo', navTo); apiClient.registerApi('onFocusEvent', focusEvent); apiClient.registerApi('onFocusSteps', focusSteps); diff --git a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx index ade8dd9461..3cf5124191 100644 --- a/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx +++ b/Composer/packages/client/src/components/ProjectTree/TriggerCreationModal.tsx @@ -7,14 +7,17 @@ import React, { useState, useContext } from 'react'; import formatMessage from 'format-message'; import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button'; +import { Label } from 'office-ui-fabric-react/lib/Label'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { Dropdown } from 'office-ui-fabric-react/lib/Dropdown'; +import { TextField } from 'office-ui-fabric-react/lib/TextField'; +import { DialogInfo, luIndexer, combineMessage } from '@bfc/indexers'; import get from 'lodash/get'; -import { DialogInfo } from '@bfc/indexers'; +import { LuEditor } from '@bfc/code-editor'; import { - addNewTrigger, + generateNewDialog, getTriggerTypes, TriggerFormData, TriggerFormDataErrors, @@ -27,13 +30,19 @@ import { getMessageTypes, regexRecognizerKey, } from '../../utils/dialogUtil'; +import { addIntent } from '../../utils/luUtil'; import { StoreContext } from '../../store'; -import { styles, dropdownStyles, dialogWindow } from './styles'; +import { styles, dropdownStyles, dialogWindow, intent } from './styles'; -const validateForm = (data: TriggerFormData): TriggerFormDataErrors => { +const nameRegex = /^[a-zA-Z0-9-_.]+$/; +const validateForm = ( + data: TriggerFormData, + isRegEx: boolean, + regExIntents: [{ intent: string; pattern: string }] +): TriggerFormDataErrors => { const errors: TriggerFormDataErrors = {}; - const { $type, specifiedType } = data; + const { $type, specifiedType, intent, triggerPhrases, regexEx } = data; if ($type === eventTypeKey && !specifiedType) { errors.specifiedType = formatMessage('Please select a event type'); @@ -43,38 +52,71 @@ const validateForm = (data: TriggerFormData): TriggerFormDataErrors => { errors.specifiedType = formatMessage('Please select an activity type'); } + if ($type === messageTypeKey && !specifiedType) { + errors.specifiedType = formatMessage('Please select a message type'); + } + if (!$type) { errors.$type = formatMessage('Please select a trigger type'); } + + if ($type === intentTypeKey && (!intent || !nameRegex.test(intent))) { + errors.intent = formatMessage( + 'Spaces and special characters are not allowed. Use letters, numbers, -, or _., numbers, -, and _' + ); + } + + if ($type === intentTypeKey && isRegEx && regExIntents.find(ri => ri.intent === intent)) { + errors.intent = `regEx ${intent} is already defined`; + } + + if ($type === intentTypeKey && isRegEx && !regexEx) { + errors.regexEx = formatMessage('Please input regEx pattern'); + } + + if ($type === intentTypeKey && !isRegEx && !triggerPhrases) { + errors.triggerPhrases = formatMessage('Please input trigger phrases'); + } + if (data.errors.triggerPhrases) { + errors.triggerPhrases = data.errors.triggerPhrases; + } return errors; }; +export interface LuFilePayload { + id: string; + content: string; +} + interface TriggerCreationModalProps { dialogId: string; isOpen: boolean; onDismiss: () => void; - onSubmit: (dialog: DialogInfo) => void; + onSubmit: (dialog: DialogInfo, luFilePayload?: LuFilePayload) => void; } -const initialFormData: TriggerFormData = { - errors: {}, - $type: intentTypeKey, - intent: '', - specifiedType: '', -}; - -const triggerTypeOptions: IDropdownOption[] = getTriggerTypes(); - export const TriggerCreationModal: React.FC = props => { const { isOpen, onDismiss, onSubmit, dialogId } = props; - const [formData, setFormData] = useState(initialFormData); const { state } = useContext(StoreContext); const { dialogs, luFiles } = state; const luFile = luFiles.find(lu => lu.id === dialogId); const dialogFile = dialogs.find(dialog => dialog.id === dialogId); + const isRegEx = get(dialogFile, 'content.recognizer.$type', '') === regexRecognizerKey; + const regexIntents = get(dialogFile, 'content.recognizer.intents', []); + const isNone = !get(dialogFile, 'content.recognizer'); + const initialFormData: TriggerFormData = { + errors: {}, + $type: isNone ? '' : intentTypeKey, + specifiedType: '', + intent: '', + triggerPhrases: '', + regexEx: '', + }; + const [formData, setFormData] = useState(initialFormData); + const onClickSubmitButton = e => { e.preventDefault(); - const errors = validateForm(formData); + const errors = validateForm(formData, isRegEx, regexIntents); if (Object.keys(errors).length) { setFormData({ @@ -83,8 +125,19 @@ export const TriggerCreationModal: React.FC = props = }); return; } - const newDialog = addNewTrigger(dialogs, dialogId, formData); - onSubmit(newDialog); + + const content = get(luFile, 'content', ''); + const newDialog = generateNewDialog(dialogs, dialogId, formData); + if (formData.$type === intentTypeKey && !isRegEx) { + const newContent = addIntent(content, { Name: formData.intent, Body: formData.triggerPhrases }); + const updateLuFile = { + id: dialogId, + content: newContent, + }; + onSubmit(newDialog, updateLuFile); + } else { + onSubmit(newDialog); + } onDismiss(); }; @@ -92,29 +145,36 @@ export const TriggerCreationModal: React.FC = props = setFormData({ ...initialFormData, $type: option.key }); }; - const onSelectIntent = (e, option) => { - setFormData({ ...formData, intent: option.key }); - }; - const onSelectSpecifiedTypeType = (e, option) => { setFormData({ ...formData, specifiedType: option.key }); }; - const eventTypes: IDropdownOption[] = getEventTypes(); - const activityTypes: IDropdownOption[] = getActivityTypes(); - const messageTypes: IDropdownOption[] = getMessageTypes(); - - const isRegEx = get(dialogFile, 'content.recognizer.$type', '') === regexRecognizerKey; + const onNameChange = (e, name) => { + setFormData({ ...formData, intent: name }); + }; - const regexIntents = get(dialogFile, 'content.recognizer.intents', []); - const luisIntents = get(luFile, 'intents', []); - const intents = isRegEx ? regexIntents : luisIntents; + const onChangeRegEx = (e, pattern) => { + setFormData({ ...formData, regexEx: pattern }); + }; - const intentOptions = intents.map(t => { - return { key: t.name || t.Name || t.intent, text: t.name || t.Name || t.intent }; - }); + const onTriggerPhrasesChange = (body: string) => { + const errors = formData.errors; + const content = '#' + formData.intent + '\n' + body; + const { diagnostics } = luIndexer.parse(content); + errors.triggerPhrases = combineMessage(diagnostics); + setFormData({ ...formData, triggerPhrases: body, errors }); + }; - const showIntentDropDown = formData.$type === intentTypeKey; + const eventTypes: IDropdownOption[] = getEventTypes(); + const activityTypes: IDropdownOption[] = getActivityTypes(); + const messageTypes: IDropdownOption[] = getMessageTypes(); + let triggerTypeOptions: IDropdownOption[] = getTriggerTypes(); + if (isNone) { + triggerTypeOptions = triggerTypeOptions.filter(t => t.key !== intentTypeKey); + } + const showIntentName = formData.$type === intentTypeKey; + const showRegExDropDown = formData.$type === intentTypeKey && isRegEx; + const showTriggerPhrase = formData.$type === intentTypeKey && !isRegEx; const showEventDropDown = formData.$type === eventTypeKey; const showActivityDropDown = formData.$type === activityTypeKey; const showMessageDropDown = formData.$type === messageTypeKey; @@ -142,9 +202,8 @@ export const TriggerCreationModal: React.FC = props = onChange={onSelectTriggerType} errorMessage={formData.errors.$type} data-testid={'triggerTypeDropDown'} - defaultSelectedKey={intentTypeKey} + defaultSelectedKey={formData.$type} /> - {showEventDropDown && ( = props = data-testid={'messageTypeDropDown'} /> )} - {showIntentDropDown && ( - + )} + + {showRegExDropDown && ( + + )} + {showTriggerPhrase && } + {showTriggerPhrase && ( + )} diff --git a/Composer/packages/client/src/components/ProjectTree/styles.ts b/Composer/packages/client/src/components/ProjectTree/styles.ts index afb5647bd4..a1c4b050f9 100644 --- a/Composer/packages/client/src/components/ProjectTree/styles.ts +++ b/Composer/packages/client/src/components/ProjectTree/styles.ts @@ -28,6 +28,7 @@ export const root = css` border-right: 1px solid #c4c4c4; box-sizing: border-box; overflow-y: auto; + overflow-x: hidden; .ms-List-cell { min-height: 36px; } @@ -137,7 +138,7 @@ export const dropdownStyles = { fontWeight: FontWeights.semibold, }, dropdown: { - width: '300px', + width: '400px', }, root: { paddingBottom: '20px', @@ -148,7 +149,7 @@ export const dialogWindow = css` display: flex; flex-direction: column; width: 400px; - height: 250px; + min-height: 300px; `; export const textFieldlabel = { @@ -162,11 +163,17 @@ export const textFieldlabel = { }; export const intent = { - fieldGroup: { - width: 200, + root: { + width: '400px', + paddingBottom: '20px', }, +}; + +export const triggerPhrases = { root: { - height: '90px', + width: '400px', + }, + fieldGroup: { + height: 80, }, - subComponentStyles: textFieldlabel, }; diff --git a/Composer/packages/client/src/extension-container/ExtensionContainer.tsx b/Composer/packages/client/src/extension-container/ExtensionContainer.tsx index bb37bc25b1..79fc535df8 100644 --- a/Composer/packages/client/src/extension-container/ExtensionContainer.tsx +++ b/Composer/packages/client/src/extension-container/ExtensionContainer.tsx @@ -98,6 +98,22 @@ const shellApi: ShellApi = { }); }, + addLuIntent: (id, intent) => { + return apiClient.apiCall('addLuIntent', { id, intent }); + }, + + updateLuIntent: (id, intentName, intent) => { + return apiClient.apiCall('updateLuIntent', { id, intentName, intent }); + }, + + removeLuIntent: (id, intentName) => { + return apiClient.apiCall('removeLuIntent', { id, intentName }); + }, + + updateRegExIntent: (id, intentName, pattern) => { + return apiClient.apiCall('updateRegExIntent', { id, intentName, pattern }); + }, + createDialog: () => { return apiClient.apiCall('createDialog'); }, diff --git a/Composer/packages/client/src/pages/design/index.tsx b/Composer/packages/client/src/pages/design/index.tsx index 778be7846f..b0b36a2020 100644 --- a/Composer/packages/client/src/pages/design/index.tsx +++ b/Composer/packages/client/src/pages/design/index.tsx @@ -12,12 +12,13 @@ import { globalHistory } from '@reach/router'; import get from 'lodash/get'; import { PromptTab } from '@bfc/shared'; import { getNewDesigner, seedNewDialog } from '@bfc/shared'; +import { DialogInfo } from '@bfc/indexers'; import { VisualEditorAPI } from '../../messenger/FrameAPI'; import { TestController } from '../../TestController'; import { BASEPATH, DialogDeleting } from '../../constants'; import { createSelectedPath, deleteTrigger, getbreadcrumbLabel } from '../../utils'; -import { TriggerCreationModal } from '../../components/ProjectTree/TriggerCreationModal'; +import { TriggerCreationModal, LuFilePayload } from '../../components/ProjectTree/TriggerCreationModal'; import { Conversation } from '../../components/Conversation'; import { DialogStyle } from '../../components/Modal/styles'; import { OpenConfirmModal } from '../../components/Modal/Confirm'; @@ -171,14 +172,22 @@ function DesignPage(props) { setTriggerModalVisibility(true); }; - const onTriggerCreationSubmit = dialog => { - const payload = { + const onTriggerCreationSubmit = (dialog: DialogInfo, luFile?: LuFilePayload) => { + const dialogPayload = { id: dialog.id, content: dialog.content, }; + if (luFile) { + const luFilePayload = { + id: luFile.id, + content: luFile.content, + }; + actions.updateLuFile(luFilePayload); + } + const index = get(dialog, 'content.triggers', []).length - 1; actions.selectTo(`triggers[${index}]`); - actions.updateDialog(payload); + actions.updateDialog(dialogPayload); }; function handleSelect(id, selected = '') { diff --git a/Composer/packages/client/src/pages/design/styles.js b/Composer/packages/client/src/pages/design/styles.js index 5b267a00eb..8961319754 100644 --- a/Composer/packages/client/src/pages/design/styles.js +++ b/Composer/packages/client/src/pages/design/styles.js @@ -62,6 +62,7 @@ export const editorWrapper = css` display: flex; flex-direction: row; flex-grow: 1; + overflow: hidden; `; export const visualPanel = css` @@ -113,9 +114,9 @@ export const middleTriggerContainer = css` display: flex; justify-content: center; align-items: center; - background: #e5e5e5; + background: #f6f6f6; width: 100%; - margin-top: 48px; + margin-top: 55px; height: calc(100% - 48px); position: absolute; `; diff --git a/Composer/packages/client/src/pages/language-generation/code-editor.tsx b/Composer/packages/client/src/pages/language-generation/code-editor.tsx index e7f591b297..fabd5b42f1 100644 --- a/Composer/packages/client/src/pages/language-generation/code-editor.tsx +++ b/Composer/packages/client/src/pages/language-generation/code-editor.tsx @@ -51,10 +51,10 @@ const CodeEditor: React.FC = props => { useEffect(() => { // reset content with file.content's initial state - if (!file || isEmpty(file)) return; + if (!file || isEmpty(file) || content) return; const value = template ? template.body : file.content; setContent(value); - }, [fileId, templateId]); + }, [file, templateId]); useEffect(() => { const currentDiagnostics = inlineMode && template ? filterTemplateDiagnostics(diagnostics, template) : diagnostics; diff --git a/Composer/packages/client/src/pages/language-generation/index.tsx b/Composer/packages/client/src/pages/language-generation/index.tsx index e97feb30d4..727a2a2878 100644 --- a/Composer/packages/client/src/pages/language-generation/index.tsx +++ b/Composer/packages/client/src/pages/language-generation/index.tsx @@ -3,7 +3,7 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import React, { useContext, Fragment, useMemo, useCallback, Suspense } from 'react'; +import React, { useContext, Fragment, useMemo, useCallback, Suspense, useEffect } from 'react'; import formatMessage from 'format-message'; import { Toggle } from 'office-ui-fabric-react/lib/Toggle'; import { RouteComponentProps, Router } from '@reach/router'; @@ -36,7 +36,7 @@ const LGPage: React.FC = props => { const { dialogs } = state; const path = props.location?.pathname ?? ''; - const { fileId = 'common' } = props; + const { fileId = '' } = props; const edit = /\/edit(\/)?$/.test(path); const navLinks = useMemo(() => { const newDialogLinks = dialogs.map(dialog => { @@ -57,6 +57,13 @@ const LGPage: React.FC = props => { return newDialogLinks; }, [dialogs]); + useEffect(() => { + const activeDialog = dialogs.find(({ id }) => id === fileId); + if (!activeDialog && dialogs.length && fileId !== 'common') { + navigateTo('/language-generation/common'); + } + }, [fileId, dialogs]); + const onSelect = useCallback( id => { const url = `/language-generation/${id}`; diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.js b/Composer/packages/client/src/pages/language-understanding/code-editor.js deleted file mode 100644 index a51ba5b456..0000000000 --- a/Composer/packages/client/src/pages/language-understanding/code-editor.js +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -/* eslint-disable react/display-name */ -import React, { useState, useEffect } from 'react'; -import { PropTypes } from 'prop-types'; -import { LuEditor } from '@bfc/code-editor'; -import get from 'lodash/get'; -import debounce from 'lodash/debounce'; -import isEmpty from 'lodash/isEmpty'; -import { combineMessage, isValid } from '@bfc/indexers'; - -export default function CodeEditor(props) { - const { file, errorMsg: updateErrorMsg } = props; - const onChange = debounce(props.onChange, 500); - const diagnostics = get(file, 'diagnostics', []); - const [content, setContent] = useState(get(file, 'content', '')); - - const fileId = file && file.id; - useEffect(() => { - // reset content with file.content's initial state - if (isEmpty(file)) return; - setContent(file.content); - }, [fileId]); - - // local content maybe invalid and should always sync real-time - // file.content assume to be load from server - const _onChange = value => { - setContent(value); - // TODO: validate before request update server like lg, when luParser is ready - onChange(value); - }; - - // diagnostics is load file error, - // updateErrorMsg is save file return error. - const isInvalid = !isValid(file.diagnostics) || updateErrorMsg !== ''; - const errorMsg = isInvalid ? `${combineMessage(diagnostics)}\n ${updateErrorMsg}` : ''; - - return ( - - ); -} - -CodeEditor.propTypes = { - file: PropTypes.object, - onChange: PropTypes.func, - errorMsg: PropTypes.string, -}; diff --git a/Composer/packages/client/src/pages/language-understanding/code-editor.tsx b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx new file mode 100644 index 0000000000..577b47acdc --- /dev/null +++ b/Composer/packages/client/src/pages/language-understanding/code-editor.tsx @@ -0,0 +1,171 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* eslint-disable react/display-name */ +import React, { useState, useEffect, useMemo, useContext, useCallback } from 'react'; +import { LuEditor } from '@bfc/code-editor'; +import get from 'lodash/get'; +import debounce from 'lodash/debounce'; +import isEmpty from 'lodash/isEmpty'; +import { editor } from '@bfcomposer/monaco-editor/esm/vs/editor/editor.api'; +import { luIndexer, combineMessage, isValid, filterTemplateDiagnostics } from '@bfc/indexers'; +import { RouteComponentProps } from '@reach/router'; +import querystring from 'query-string'; + +import { StoreContext } from '../../store'; +import * as luUtil from '../../utils/luUtil'; + +const { parse } = luIndexer; + +const lspServerPath = '/lu-language-server'; + +interface CodeEditorProps extends RouteComponentProps<{}> { + fileId: string; +} + +const CodeEditor: React.FC = props => { + const { actions, state } = useContext(StoreContext); + const { luFiles } = state; + const { fileId } = props; + const file = luFiles?.find(({ id }) => id === fileId); + const [diagnostics, setDiagnostics] = useState(get(file, 'diagnostics', [])); + const [httpErrorMsg, setHttpErrorMsg] = useState(''); + const [luEditor, setLuEditor] = useState(null); + + const search = props.location?.search ?? ''; + const searchSectionName = querystring.parse(search).t; + const sectionId = Array.isArray(searchSectionName) + ? searchSectionName[0] + : typeof searchSectionName === 'string' + ? searchSectionName + : undefined; + const intent = sectionId && file ? file.intents.find(({ Name }) => Name === sectionId) : undefined; + + const hash = props.location?.hash ?? ''; + const hashLine = querystring.parse(hash).L; + const line = Array.isArray(hashLine) ? +hashLine[0] : typeof hashLine === 'string' ? +hashLine : undefined; + + const inlineMode = !!intent; + const [content, setContent] = useState(intent?.Body || file?.content); + + useEffect(() => { + // reset content with file.content initial state + if (!file || isEmpty(file) || content) return; + const value = intent ? intent.Body : file.content; + setContent(value); + }, [file, sectionId]); + + const errorMsg = useMemo(() => { + const currentDiagnostics = inlineMode && intent ? filterTemplateDiagnostics(diagnostics, intent) : diagnostics; + const isInvalid = !isValid(currentDiagnostics); + return isInvalid ? combineMessage(diagnostics) : httpErrorMsg; + }, [diagnostics, httpErrorMsg]); + + const editorDidMount = (luEditor: editor.IStandaloneCodeEditor) => { + setLuEditor(luEditor); + }; + + useEffect(() => { + if (luEditor && line !== undefined) { + window.requestAnimationFrame(() => { + luEditor.revealLine(line); + luEditor.focus(); + luEditor.setPosition({ lineNumber: line, column: 1 }); + }); + } + }, [line, luEditor]); + + const updateLuIntent = useMemo( + () => + debounce((Body: string) => { + if (!file || !intent) return; + const { Name } = intent; + const payload = { + file, + intentName: Name, + intent: { + Name, + Body, + }, + }; + actions.updateLuIntent(payload); + }, 500), + [file, intent] + ); + + const updateLuFile = useMemo( + () => + debounce((content: string) => { + if (!file) return; + const { id } = file; + const payload = { + id, + content, + }; + actions.updateLuFile(payload); + }, 500), + [file] + ); + + const updateDiagnostics = useMemo( + () => + debounce((value: string) => { + if (!file) return; + const { id } = file; + if (inlineMode) { + if (!intent) return; + const { Name } = intent; + const { content } = file; + try { + const newContent = luUtil.updateIntent(content, Name, { + Name, + Body: value, + }); + const { diagnostics } = parse(newContent, id); + setDiagnostics(diagnostics); + } catch (error) { + setHttpErrorMsg(error.error); + } + } else { + const { diagnostics } = parse(value, id); + setDiagnostics(diagnostics); + } + }, 1000), + [file, intent] + ); + + const _onChange = useCallback( + value => { + setContent(value); + updateDiagnostics(value); + if (!file) return; + if (inlineMode) { + updateLuIntent(value); + } else { + updateLuFile(value); + } + }, + [file, intent] + ); + + const luOption = { + fileId, + sectionId: intent?.Name, + }; + + return ( + + ); +}; + +export default CodeEditor; diff --git a/Composer/packages/client/src/pages/language-understanding/index.tsx b/Composer/packages/client/src/pages/language-understanding/index.tsx index 1d75ce283b..95ee7bd2da 100644 --- a/Composer/packages/client/src/pages/language-understanding/index.tsx +++ b/Composer/packages/client/src/pages/language-understanding/index.tsx @@ -2,9 +2,10 @@ // Licensed under the MIT License. /** @jsx jsx */ import { jsx } from '@emotion/core'; -import React, { useContext, Fragment, useEffect, useState, useMemo, Suspense } from 'react'; +import React, { useContext, Fragment, useMemo, Suspense, useCallback, useEffect } from 'react'; import formatMessage from 'format-message'; import { Toggle } from 'office-ui-fabric-react/lib/Toggle'; +import { RouteComponentProps, Router } from '@reach/router'; import { StoreContext } from '../../store'; import { projectContainer } from '../design/styles'; @@ -26,20 +27,18 @@ import { } from './styles'; const CodeEditor = React.lazy(() => import('./code-editor')); -interface DefineConversationProps { +interface LUPageProps extends RouteComponentProps<{}> { + fileId?: string; path: string; } -const LUPage: React.FC = props => { - const { state, actions } = useContext(StoreContext); - const { luFiles, dialogs } = state; - const [editMode, setEditMode] = useState(false); - const [errorMsg, setErrorMsg] = useState(''); - const fileId = props['*']; - const isRoot = fileId === ''; - const activeDialog = dialogs.find(item => item.id === fileId); - - const luFile = luFiles.length && activeDialog ? luFiles.find(luFile => luFile.id === activeDialog.id) : null; +const LUPage: React.FC = props => { + const { state } = useContext(StoreContext); + const { dialogs } = state; + const path = props.location?.pathname ?? ''; + const { fileId = '' } = props; + const edit = /\/edit(\/)?$/.test(path); + const isRoot = fileId === 'all'; const navLinks = useMemo(() => { const newDialogLinks = dialogs.map(dialog => { @@ -55,49 +54,28 @@ const LUPage: React.FC = props => { }, [dialogs]); useEffect(() => { - // root view merge all lu file into one list, we can't edit multi file. - if (isRoot) { - setEditMode(false); - } - - // fall back to the all-up page if we don't have an active dialog - if (!isRoot && !activeDialog && dialogs.length) { - navigateTo('/language-understanding'); + const activeDialog = dialogs.find(({ id }) => id === fileId); + if (!activeDialog && fileId !== 'all' && dialogs.length) { + navigateTo('/language-understanding/all'); } }, [fileId, dialogs]); - useEffect(() => { - setErrorMsg(''); - }, [luFile]); - - function onSelect(id) { - if (id === '_all') { - navigateTo('/language-understanding'); - } else { - navigateTo(`/language-understanding/${id}`); - } - setEditMode(false); - } - - async function onChange(newContent: string) { - const id = activeDialog ? activeDialog.id : undefined; - const payload = { - id: id, // current opened lu file - content: newContent, - }; - try { - await actions.updateLuFile(payload); - } catch (error) { - setErrorMsg(error.message); - } - } + const onSelect = useCallback( + id => { + const url = `/language-understanding/${id}`; + navigateTo(url); + }, + [edit] + ); - // #TODO: get line number from lu parser, then deep link to code editor this - // Line - function onTableViewClickEdit({ fileId = '' }) { - navigateTo(`language-understanding/${fileId}`); - setEditMode(true); - } + const onToggleEditMode = useCallback( + (_e, checked) => { + let url = `/language-understanding/${fileId}`; + if (checked) url += `/edit`; + navigateTo(url); + }, + [fileId] + ); const toolbarItems = [ { @@ -113,24 +91,26 @@ const LUPage: React.FC = props => {
{formatMessage('User Input')}
- setEditMode(!editMode)} - /> + {(!isRoot || edit) && ( + + )}
{ - onSelect('_all'); + onSelect('all'); }} > {'All'} @@ -138,13 +118,12 @@ const LUPage: React.FC = props => {
- {editMode ? ( - }> - - - ) : ( - - )} + }> + + + + +
diff --git a/Composer/packages/client/src/pages/language-understanding/styles.js b/Composer/packages/client/src/pages/language-understanding/styles.ts similarity index 100% rename from Composer/packages/client/src/pages/language-understanding/styles.js rename to Composer/packages/client/src/pages/language-understanding/styles.ts diff --git a/Composer/packages/client/src/pages/language-understanding/table-view.tsx b/Composer/packages/client/src/pages/language-understanding/table-view.tsx index 6548ee126d..2edbf09e13 100644 --- a/Composer/packages/client/src/pages/language-understanding/table-view.tsx +++ b/Composer/packages/client/src/pages/language-understanding/table-view.tsx @@ -16,15 +16,15 @@ import { ScrollablePane, ScrollbarVisibility } from 'office-ui-fabric-react/lib/ import { Sticky, StickyPositionType } from 'office-ui-fabric-react/lib/Sticky'; import formatMessage from 'format-message'; import { NeutralColors, FontSizes } from '@uifabric/fluent-theme'; -import { isValid, DialogInfo, LuFile } from '@bfc/indexers'; +import { isValid, LuFile } from '@bfc/indexers'; +import { RouteComponentProps } from '@reach/router'; import { StoreContext } from '../../store'; import { navigateTo } from '../../utils'; import { formCell, luPhraseCell } from './styles'; -interface TableViewProps { - activeDialog: DialogInfo | undefined; - onClickEdit: ({ fileId: string }) => void; +interface TableViewProps extends RouteComponentProps<{}> { + fileId: string; } interface Intent { @@ -38,16 +38,34 @@ interface Intent { const TableView: React.FC = props => { const { state } = useContext(StoreContext); const { dialogs, luFiles } = state; - const { activeDialog, onClickEdit } = props; + const { fileId } = props; + const activeDialog = dialogs.find(({ id }) => id === fileId); + const [intents, setIntents] = useState([]); const listRef = useRef(null); + function checkErrors(files: LuFile[]): LuFile[] { + return files.filter(file => !isValid(file.diagnostics)); + } + + function getIntentState(file: LuFile): string { + if (!file.diagnostics) { + return formatMessage('Error'); + } else if (file.status && file.status.lastUpdateTime >= file.status.lastPublishTime) { + return formatMessage('Not yet published'); + } else if (file.status && file.status.lastPublishTime > file.status.lastUpdateTime) { + return formatMessage('Published'); + } else { + return formatMessage('Unknown State'); // It's a bug in most cases. + } + } + useEffect(() => { if (isEmpty(luFiles)) return; const errorFiles = checkErrors(luFiles); if (errorFiles.length !== 0) { - onClickEdit({ fileId: errorFiles[0].id }); + navigateTo(`/language-understanding/${errorFiles[0].id}/edit`); return; } @@ -61,7 +79,7 @@ const TableView: React.FC = props => { name, phrases, fileId: luFile.id, - used: luDialog ? luDialog.luIntents.includes(name) : false, // used by it's dialog or not + used: !!luDialog && luDialog.referredLuIntents.some(lu => lu.name === name), // used by it's dialog or not state, }); }); @@ -76,29 +94,14 @@ const TableView: React.FC = props => { } }, [luFiles, activeDialog]); - function checkErrors(files: LuFile[]): LuFile[] { - return files.filter(file => !isValid(file.diagnostics)); - } - - function getIntentState(file: LuFile): string { - if (!file.diagnostics) { - return formatMessage('Error'); - } else if (file.status && file.status.lastUpdateTime >= file.status.lastPublishTime) { - return formatMessage('Not yet published'); - } else if (file.status && file.status.lastPublishTime > file.status.lastUpdateTime) { - return formatMessage('Published'); - } else { - return formatMessage('Unknown State'); // It's a bug in most cases. - } - } - const getTemplatesMoreButtons = (item, index): IContextualMenuItem[] => { const buttons = [ { key: 'edit', name: 'Edit', onClick: () => { - onClickEdit(intents[index]); + const { name, fileId } = intents[index]; + navigateTo(`/language-understanding/${fileId}/edit?t=${encodeURIComponent(name)}`); }, }, ]; @@ -114,8 +117,13 @@ const TableView: React.FC = props => { minWidth: 100, maxWidth: 150, data: 'string', - onRender: item => { - return
#{item.name}
; + onRender: (item: Intent) => { + let displayName = `#${item.name}`; + if (item.name.includes('/')) { + const [, childName] = item.name.split('/'); + displayName = `##${childName}`; + } + return
{displayName}
; }, }, { diff --git a/Composer/packages/client/src/pages/notifications/index.tsx b/Composer/packages/client/src/pages/notifications/index.tsx index cccac44dc3..093238013a 100644 --- a/Composer/packages/client/src/pages/notifications/index.tsx +++ b/Composer/packages/client/src/pages/notifications/index.tsx @@ -3,49 +3,41 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import { useState, useContext } from 'react'; +import { useState } from 'react'; import { RouteComponentProps } from '@reach/router'; -import { StoreContext } from '../../store'; - import { ToolBar } from './../../components/ToolBar/index'; import useNotifications from './useNotifications'; import { NotificationList } from './NotificationList'; import { NotificationHeader } from './NotificationHeader'; import { root } from './styles'; -import { INotification } from './types'; +import { INotification, NotificationType } from './types'; import { navigateTo } from './../../utils'; -import { convertDialogDiagnosticToUrl, toUrlUtil } from './../../utils/navigation'; +import { convertPathToUrl, toUrlUtil } from './../../utils/navigation'; const Notifications: React.FC = () => { const [filter, setFilter] = useState(''); - const { state } = useContext(StoreContext); - const { dialogs } = state; const notifications = useNotifications(filter); const navigations = { - lg: (item: INotification) => { + [NotificationType.LG]: (item: INotification) => { let url = `/language-generation/${item.id}/edit#L=${item.diagnostic.range?.start.line || 0}`; - const dividerIndex = item.id.indexOf('#'); //the format of item.id is lgFile#inlineTemplateId - if (dividerIndex > -1) { - const templateId = item.id.substring(dividerIndex + 1); - const lgFile = item.id.substring(0, dividerIndex); - const dialog = dialogs.find(d => d.lgFile === lgFile); - const lgTemplate = dialog ? dialog.lgTemplates.find(lg => lg.name === templateId) : null; - const path = lgTemplate ? lgTemplate.path : ''; - if (path && dialog) { - url = toUrlUtil(dialog.id, path); - } + if (item.dialogPath) { + url = toUrlUtil(item.dialogPath); } navigateTo(url); }, - lu: (item: INotification) => { - navigateTo(`/dialogs/${item.id}`); + [NotificationType.LU]: (item: INotification) => { + let uri = `/language-understanding/${item.id}`; + if (item.dialogPath) { + uri = convertPathToUrl(item.id, item.dialogPath); + } + navigateTo(uri); }, - dialog: (item: INotification) => { + [NotificationType.DIALOG]: (item: INotification) => { //path is like main.trigers[0].actions[0] //uri = id?selected=triggers[0]&focused=triggers[0].actions[0] - const uri = convertDialogDiagnosticToUrl(item.diagnostic); + const uri = convertPathToUrl(item.id, item.dialogPath); navigateTo(uri); }, }; diff --git a/Composer/packages/client/src/pages/notifications/types.ts b/Composer/packages/client/src/pages/notifications/types.ts index bce5eef0a0..707e3823da 100644 --- a/Composer/packages/client/src/pages/notifications/types.ts +++ b/Composer/packages/client/src/pages/notifications/types.ts @@ -1,13 +1,84 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { Diagnostic, createSingleMessage, DialogInfo, LuFile, isDiagnosticWithInRange } from '@bfc/indexers'; + +import { replaceDialogDiagnosticLabel } from '../../utils'; +export const DiagnosticSeverity = ['Error', 'Warning']; //'Information', 'Hint' + +export enum NotificationType { + DIALOG, + LG, + LU, + GENERAL, +} export interface INotification { id: string; severity: string; - type: string; + type: NotificationType; location: string; message: string; diagnostic: any; + dialogPath?: string; //the data path in dialog } -export const DiagnosticSeverity = ['Error', 'Warning']; //'Information', 'Hint' +export abstract class Notification implements INotification { + id: string; + severity: string; + type = NotificationType.GENERAL; + location: string; + message = ''; + diagnostic: Diagnostic; + dialogPath?: string; + constructor(id: string, location: string, diagnostic: Diagnostic) { + this.id = id; + this.severity = DiagnosticSeverity[diagnostic.severity] || ''; + this.diagnostic = diagnostic; + this.location = location; + } +} + +export class DialogNotification extends Notification { + type = NotificationType.DIALOG; + constructor(id: string, location: string, diagnostic: Diagnostic) { + super(id, location, diagnostic); + this.message = `In ${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`; + this.dialogPath = diagnostic.path; + } +} + +export class LgNotification extends Notification { + type = NotificationType.LG; + constructor(id: string, lgTemplateName: string, location: string, diagnostic: Diagnostic, dialogs: DialogInfo[]) { + super(id, location, diagnostic); + this.message = createSingleMessage(diagnostic); + this.dialogPath = this.findDialogPath(dialogs, id, lgTemplateName); + } + private findDialogPath(dialogs: DialogInfo[], id: string, lgTemplateName: string) { + if (lgTemplateName) { + const dialog = dialogs.find(d => d.lgFile === id); + const lgTemplate = dialog ? dialog.lgTemplates.find(lg => lg.name === lgTemplateName) : null; + const path = lgTemplate ? lgTemplate.path : ''; + return path; + } + } +} + +export class LuNotification extends Notification { + type = NotificationType.LU; + constructor(id: string, location: string, diagnostic: Diagnostic, luFile: LuFile, dialogs: DialogInfo[]) { + super(id, location, diagnostic); + this.dialogPath = this.findDialogPath(luFile, dialogs, diagnostic); + this.message = createSingleMessage(diagnostic); + } + + private findDialogPath(luFile: LuFile, dialogs: DialogInfo[], d: Diagnostic) { + const intentName = luFile.intents.find(intent => { + const { range } = intent; + if (!range) return false; + return isDiagnosticWithInRange(d, range); + })?.Name; + + return dialogs.find(dialog => dialog.id === luFile.id)?.referredLuIntents.find(lu => lu.name === intentName)?.path; + } +} diff --git a/Composer/packages/client/src/pages/notifications/useNotifications.tsx b/Composer/packages/client/src/pages/notifications/useNotifications.tsx index 529e1005d9..91441951de 100644 --- a/Composer/packages/client/src/pages/notifications/useNotifications.tsx +++ b/Composer/packages/client/src/pages/notifications/useNotifications.tsx @@ -2,74 +2,55 @@ // Licensed under the MIT License. import { useContext, useMemo } from 'react'; -import { createSingleMessage } from '@bfc/indexers'; import get from 'lodash/get'; import { LgNamePattern } from '@bfc/shared'; import { StoreContext } from '../../store'; -import { replaceDialogDiagnosticLabel } from '../../utils'; -import { INotification, DiagnosticSeverity } from './types'; +import { Notification, DialogNotification, LuNotification, LgNotification } from './types'; import { getReferredFiles } from './../../utils/luUtil'; export default function useNotifications(filter?: string) { const { state } = useContext(StoreContext); const { dialogs, luFiles, lgFiles } = state; const memoized = useMemo(() => { - const notifactions: INotification[] = []; + const notifactions: Notification[] = []; dialogs.forEach(dialog => { dialog.diagnostics.map(diagnostic => { const location = `${dialog.id}.dialog`; - notifactions.push({ - type: 'dialog', - location, - message: `In ${replaceDialogDiagnosticLabel(diagnostic.path)} ${diagnostic.message}`, - severity: DiagnosticSeverity[diagnostic.severity] || '', - diagnostic, - id: dialog.id, - }); + notifactions.push(new DialogNotification(dialog.id, location, diagnostic)); }); }); getReferredFiles(luFiles, dialogs).forEach(lufile => { lufile.diagnostics.map(diagnostic => { const location = `${lufile.id}.lu`; - notifactions.push({ - type: 'lu', - location, - message: createSingleMessage(diagnostic), - severity: 'Error', - diagnostic, - id: lufile.id, - }); + notifactions.push(new LuNotification(lufile.id, location, diagnostic, lufile, dialogs)); }); }); lgFiles.forEach(lgFile => { const lgTemplates = get(lgFile, 'templates', []); - lgFile.diagnostics.map(diagnostic => { - const mappedTemplate = lgTemplates.find( - t => - get(diagnostic, 'range.start.line') >= get(t, 'range.startLineNumber') && - get(diagnostic, 'range.end.line') <= get(t, 'range.endLineNumber') - ); - let id = lgFile.id; - const location = `${lgFile.id}.lg`; - if (mappedTemplate && mappedTemplate.name.match(LgNamePattern)) { - //should navigate to design page - id = `${lgFile.id}#${mappedTemplate.name}`; - } - notifactions.push({ - type: 'lg', - severity: DiagnosticSeverity[diagnostic.severity] || '', - location, - message: createSingleMessage(diagnostic), - diagnostic, - id, + lgFile.diagnostics + // only report diagnostics belong to itself. + .filter(({ source, message }) => message.includes(`source: ${source}`)) + .map(diagnostic => { + const mappedTemplate = lgTemplates.find( + t => + get(diagnostic, 'range.start.line') >= get(t, 'range.startLineNumber') && + get(diagnostic, 'range.end.line') <= get(t, 'range.endLineNumber') + ); + const id = lgFile.id; + const location = `${lgFile.id}.lg`; + let lgTemplateName = ''; + if (mappedTemplate && mappedTemplate.name.match(LgNamePattern)) { + //should navigate to design page + lgTemplateName = mappedTemplate.name; + } + notifactions.push(new LgNotification(id, lgTemplateName, location, diagnostic, dialogs)); }); - }); }); return notifactions; }, [dialogs, luFiles, lgFiles]); - const notifications: INotification[] = !filter ? memoized : memoized.filter(x => x.severity === filter); + const notifications: Notification[] = filter ? memoized.filter(x => x.severity === filter) : memoized; return notifications; } diff --git a/Composer/packages/client/src/pages/setting/deployment/deploy-create-output.png b/Composer/packages/client/src/pages/setting/deployment/deploy-create-output.png new file mode 100644 index 0000000000..ed9c82e38f Binary files /dev/null and b/Composer/packages/client/src/pages/setting/deployment/deploy-create-output.png differ diff --git a/Composer/packages/client/src/pages/setting/deployment/deploy-deploy-output.png b/Composer/packages/client/src/pages/setting/deployment/deploy-deploy-output.png new file mode 100644 index 0000000000..b33ea0bfc8 Binary files /dev/null and b/Composer/packages/client/src/pages/setting/deployment/deploy-deploy-output.png differ diff --git a/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getCreate.js b/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getCreate.js index 274651e884..83139f2b7e 100644 --- a/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getCreate.js +++ b/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getCreate.js @@ -12,7 +12,7 @@ import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { StoreContext } from '../../../store'; import { styles } from './styles'; -import processGif from './deploy-create-output.gif'; +import processGif from './deploy-create-output.png'; export const DeployWizardStep2 = props => { const { state } = useContext(StoreContext); @@ -57,7 +57,7 @@ export const DeployWizardStep2 = props => { {formatMessage('Animation diff --git a/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getDeploy.js b/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getDeploy.js index 5697cfc81d..f9b18fbee1 100644 --- a/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getDeploy.js +++ b/Composer/packages/client/src/pages/setting/deployment/deployWizardStep-getDeploy.js @@ -9,7 +9,7 @@ import { DialogFooter } from 'office-ui-fabric-react/lib/Dialog'; import { TextField } from 'office-ui-fabric-react/lib/TextField'; import { styles } from './styles'; -import processGif from './deploy-deploy-output.gif'; +import processGif from './deploy-deploy-output.png'; export const DeployWizardStep3 = props => { const { closeModal, botValues } = props; @@ -50,7 +50,7 @@ export const DeployWizardStep3 = props => { {formatMessage('Animation diff --git a/Composer/packages/client/src/pages/setting/deployment/index.js b/Composer/packages/client/src/pages/setting/deployment/index.js index 33a9064cbf..1e6cb904ec 100644 --- a/Composer/packages/client/src/pages/setting/deployment/index.js +++ b/Composer/packages/client/src/pages/setting/deployment/index.js @@ -5,6 +5,7 @@ import React, { useState, Fragment } from 'react'; import formatMessage from 'format-message'; import { DefaultButton } from 'office-ui-fabric-react/lib/Button'; import { Stack } from 'office-ui-fabric-react/lib/Stack'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { DeployWizard } from './deployWizard.js'; import { styles } from './styles'; @@ -16,6 +17,7 @@ const instructions = { ), button1: formatMessage('Create Azure Resources'), button2: formatMessage('Deploy Bot to Azure'), + helpLink: 'https://aka.ms/bfc-publishing-your-bot', }; export const Deployment = () => { @@ -44,10 +46,13 @@ export const Deployment = () => {
{instructions.title}

{instructions.description}

- + + + {formatMessage('Learn more')} +
diff --git a/Composer/packages/client/src/pages/setting/onboarding-settings/index.tsx b/Composer/packages/client/src/pages/setting/onboarding-settings/index.tsx index f0f394bd61..da2244268a 100644 --- a/Composer/packages/client/src/pages/setting/onboarding-settings/index.tsx +++ b/Composer/packages/client/src/pages/setting/onboarding-settings/index.tsx @@ -6,6 +6,7 @@ import { jsx } from '@emotion/core'; import { useCallback, useContext } from 'react'; import formatMessage from 'format-message'; import { Toggle } from 'office-ui-fabric-react/lib/Toggle'; +import { Link } from 'office-ui-fabric-react/lib/Link'; import { StoreContext } from '../../../store'; @@ -35,6 +36,9 @@ export const OnboardingSettings = () => { onChange={onChange} onText={formatMessage('Enabled')} /> + + {formatMessage('Learn more')} +
); }; diff --git a/Composer/packages/client/src/router.tsx b/Composer/packages/client/src/router.tsx index e8271517fb..a730f121b9 100644 --- a/Composer/packages/client/src/router.tsx +++ b/Composer/packages/client/src/router.tsx @@ -48,7 +48,8 @@ const Routes = props => { - + + diff --git a/Composer/packages/client/src/store/action/lu.ts b/Composer/packages/client/src/store/action/lu.ts index 9c9b3f1ddb..63639811c1 100644 --- a/Composer/packages/client/src/store/action/lu.ts +++ b/Composer/packages/client/src/store/action/lu.ts @@ -1,28 +1,50 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import clonedeep from 'lodash/cloneDeep'; +import debounce from 'lodash/debounce'; -import { ActionCreator } from '../types'; +import * as luUtil from '../../utils/luUtil'; +import { undoable } from '../middlewares/undo'; +import { ActionCreator, State } from '../types'; -import { ActionTypes } from './../../constants/index'; import httpClient from './../../utils/httpUtil'; - -export const updateLuFile: ActionCreator = async ({ dispatch }, { id, content }) => { +import { ActionTypes } from './../../constants/index'; +import { fetchProject } from './project'; +import { setError } from './error'; +//remove editor's debounce and add it to action +export const debouncedUpdateLu = debounce(async (store, id, content) => { try { - const response = await httpClient.put(`/projects/opened/luFiles/${id}`, { id, content }); - dispatch({ - type: ActionTypes.UPDATE_LU_SUCCESS, - payload: { response }, - }); + await httpClient.put(`/projects/opened/luFiles/${id}`, { id, content }); } catch (err) { - dispatch({ - type: ActionTypes.UPDATE_LU_FAILURE, - payload: null, - error: err, + setError(store, { + message: err.response && err.response.data.message ? err.response.data.message : err, + summary: 'UPDATE LU ERROR', }); - throw new Error(err.response.data.message); + //if update lu error, do a full refresh. + fetchProject(store); } +}, 500); + +export const updateLuFile: ActionCreator = async (store, { id, content }) => { + store.dispatch({ type: ActionTypes.UPDATE_LU_SUCCESS, payload: { id, content } }); + debouncedUpdateLu(store, id, content); }; +export const undoableUpdateLuFile = undoable( + updateLuFile, + (state: State, args: any[], isEmpty) => { + if (isEmpty) { + const id = args[0].id; + const content = clonedeep(state.luFiles.find(luFile => luFile.id === id)?.content); + return [{ id, content }]; + } else { + return args; + } + }, + updateLuFile, + updateLuFile +); + export const createLuFile: ActionCreator = async ({ dispatch }, { id, content }) => { try { const response = await httpClient.post(`/projects/opened/luFiles`, { id, content }); @@ -57,6 +79,21 @@ export const removeLuFile: ActionCreator = async ({ dispatch }, { id }) => { } }; +export const updateLuIntent: ActionCreator = async (store, { file, intentName, intent }) => { + const newContent = luUtil.updateIntent(file.content, intentName, intent); + return await undoableUpdateLuFile(store, { id: file.id, content: newContent }); +}; + +export const createLuIntent: ActionCreator = async (store, { file, intent }) => { + const newContent = luUtil.addIntent(file.content, intent); + return await undoableUpdateLuFile(store, { id: file.id, content: newContent }); +}; + +export const removeLuIntent: ActionCreator = async (store, { file, intentName }) => { + const newContent = luUtil.removeIntent(file.content, intentName); + return await undoableUpdateLuFile(store, { id: file.id, content: newContent }); +}; + export const publishLuis: ActionCreator = async ({ dispatch }, authoringKey) => { try { const response = await httpClient.post(`/projects/opened/luFiles/publish`, { authoringKey }); diff --git a/Composer/packages/client/src/store/reducer/index.ts b/Composer/packages/client/src/store/reducer/index.ts index 09177cc313..79cb94e55a 100644 --- a/Composer/packages/client/src/store/reducer/index.ts +++ b/Composer/packages/client/src/store/reducer/index.ts @@ -5,7 +5,7 @@ import get from 'lodash/get'; import set from 'lodash/set'; import { dialogIndexer } from '@bfc/indexers'; import { SensitiveProperties } from '@bfc/shared'; -import { Diagnostic, DiagnosticSeverity, LgTemplate, lgIndexer } from '@bfc/indexers'; +import { Diagnostic, DiagnosticSeverity, LgTemplate, lgIndexer, luIndexer } from '@bfc/indexers'; import { ImportResolverDelegate } from 'botbuilder-lg'; import { ActionTypes, FileTypes } from '../../constants'; @@ -140,8 +140,22 @@ const updateLgTemplate: ReducerFunc = (state, { id, content }) => { return state; }; -const updateLuTemplate: ReducerFunc = (state, { response }) => { - state.luFiles = response.data.luFiles; +const updateLuTemplate: ReducerFunc = (state, { id, content }) => { + const luFiles = state.luFiles.map(luFile => { + if (luFile.id === id) { + luFile.content = content; + return luFile; + } + return luFile; + }); + + state.luFiles = luFiles.map(luFile => { + const { parse } = luIndexer; + const { id, content } = luFile; + const { intents, diagnostics } = parse(content, id); + return { ...luFile, intents, diagnostics, content }; + }); + return state; }; diff --git a/Composer/packages/client/src/utils/dialogUtil.ts b/Composer/packages/client/src/utils/dialogUtil.ts index e388decfb0..de8fb2216d 100644 --- a/Composer/packages/client/src/utils/dialogUtil.ts +++ b/Composer/packages/client/src/utils/dialogUtil.ts @@ -21,14 +21,18 @@ interface DialogsMap { export interface TriggerFormData { errors: TriggerFormDataErrors; $type: string; - intent: string; specifiedType: string; + intent: string; + triggerPhrases: string; + regexEx: string; } export interface TriggerFormDataErrors { $type?: string; intent?: string; specifiedType?: string; + triggerPhrases?: string; + regexEx?: string; } export function getDialog(dialogs: DialogInfo[], dialogId: string) { @@ -58,8 +62,15 @@ export function getFriendlyName(data) { return data.$type; } -export function insert(content, path: string, position: number | undefined, data: TriggerFormData) { +export function insert(content, path: string, position: number | undefined, data: any) { const current = get(content, path, []); + const insertAt = typeof position === 'undefined' ? current.length : position; + current.splice(insertAt, 0, data); + set(content, path, current); + return content; +} + +export function generateNewTrigger(data: TriggerFormData) { const optionalAttributes: { intent?: string; event?: string } = {}; if (data.specifiedType) { data.$type = data.specifiedType; @@ -67,28 +78,68 @@ export function insert(content, path: string, position: number | undefined, data if (data.intent) { optionalAttributes.intent = data.intent; } - const newStep = { $type: data.$type, ...seedNewDialog(data.$type, {}, optionalAttributes), }; + return newStep; +} - const insertAt = typeof position === 'undefined' ? current.length : position; +export function generateRegexExpression(intent: string, pattern: string) { + return { intent, pattern }; +} - current.splice(insertAt, 0, newStep); +export function createTrigger(dialog: DialogInfo, data: TriggerFormData): DialogInfo { + const dialogCopy = cloneDeep(dialog); + const trigger = generateNewTrigger(data); + insert(dialogCopy.content, 'triggers', undefined, trigger); + return dialogCopy; +} - set(content, path, current); +export function createRegExIntent(dialog: DialogInfo, intent: string, pattern: string): DialogInfo { + const regex = generateRegexExpression(intent, pattern); + const dialogCopy = cloneDeep(dialog); + insert(dialogCopy.content, 'recognizer.intents', undefined, regex); + return dialogCopy; +} - return content; +export function updateRegExIntent(dialog: DialogInfo, intent: string, pattern: string): DialogInfo { + let dialogCopy = cloneDeep(dialog); + const regexIntents = get(dialogCopy, 'content.recognizer.intents', []); + const targetIntent = regexIntents.find(ri => ri.intent === intent); + if (!targetIntent) { + dialogCopy = createRegExIntent(dialog, intent, pattern); + } else { + targetIntent.pattern = pattern; + } + return dialogCopy; } -export function addNewTrigger(dialogs: DialogInfo[], dialogId: string, data: TriggerFormData): DialogInfo { - const dialogCopy = getDialog(dialogs, dialogId); - if (!dialogCopy) throw new Error(`dialog ${dialogId} does not exist`); - insert(dialogCopy.content, 'triggers', undefined, data); +//it is possible that we cannot find a RegEx. Because it will clear all regEx when we +//switch to another recognizer type +export function deleteRegExIntent(dialog: DialogInfo, intent: string): DialogInfo { + const dialogCopy = cloneDeep(dialog); + const regexIntents = get(dialogCopy, 'content.recognizer.intents', []); + const index = regexIntents.findIndex(ri => ri.intent === intent); + if (index > -1) { + regexIntents.splice(index, 1); + } return dialogCopy; } +export function generateNewDialog(dialogs: DialogInfo[], dialogId: string, data: TriggerFormData): DialogInfo { + //add new trigger + const dialog = dialogs.find(dialog => dialog.id === dialogId); + if (!dialog) throw new Error(`dialog ${dialogId} does not exist`); + let updatedDialog = createTrigger(dialog, data); + + //add regex expression + if (data.regexEx) { + updatedDialog = createRegExIntent(updatedDialog, data.intent, data.regexEx); + } + return updatedDialog; +} + export function createSelectedPath(selected: number) { return `triggers[${selected}]`; } @@ -98,11 +149,16 @@ export function createFocusedPath(selected: number, focused: number) { } export function deleteTrigger(dialogs: DialogInfo[], dialogId: string, index: number) { - const dialogCopy = getDialog(dialogs, dialogId); + let dialogCopy = getDialog(dialogs, dialogId); if (!dialogCopy) return null; - const content = dialogCopy.content; - content.triggers.splice(index, 1); - return content; + const isRegEx = get(dialogCopy, 'content.recognizer.$type', '') === regexRecognizerKey; + if (isRegEx) { + const regExIntent = get(dialogCopy, `content.triggers[${index}].intent`, ''); + dialogCopy = deleteRegExIntent(dialogCopy, regExIntent); + } + const triggers = get(dialogCopy, 'content.triggers'); + triggers.splice(index, 1); + return dialogCopy.content; } export function getTriggerTypes(): IDropdownOption[] { diff --git a/Composer/packages/client/src/utils/luUtil.ts b/Composer/packages/client/src/utils/luUtil.ts index 14f9ba5951..30023da7a5 100644 --- a/Composer/packages/client/src/utils/luUtil.ts +++ b/Composer/packages/client/src/utils/luUtil.ts @@ -7,146 +7,8 @@ * for more usage detail, please check client/__tests__/utils/luUtil.test.ts */ -import { sectionHandler } from '@bfcomposer/bf-lu/lib/parser'; import { LuFile, DialogInfo } from '@bfc/indexers'; -import isEmpty from 'lodash/isEmpty'; - -const { luParser, sectionOperator } = sectionHandler; - -export interface LuIntent { - Name: string; - Body: string; -} - -const NEWLINE = '\n'; - -export function textFromIntent(intent: LuIntent | null, secondary = false): string { - if (!intent || isEmpty(intent)) return ''; - const { Name, Body } = intent; - const textBuilder: string[] = []; - if (Name && Body) { - if (secondary) { - textBuilder.push(`## ${Name.trim()}`); - } else { - textBuilder.push(`# ${Name.trim()}`); - } - textBuilder.push(Body); - } - return textBuilder.join(NEWLINE); -} - -export function textFromIntents(intents: LuIntent[], secondary = false): string { - return intents.map(intent => textFromIntent(intent, secondary)).join(`${NEWLINE}${NEWLINE}`); -} - -function updateInSections(sections: LuIntent[], intentName: string, updatedIntent: LuIntent | null): LuIntent[] { - // remove - if (!updatedIntent || isEmpty(updatedIntent)) { - return sections.filter(({ Name }) => Name !== intentName); - } - // add - if (sections.findIndex(({ Name }) => Name === intentName) === -1) { - sections.push(updatedIntent); - return sections; - } - // update - return sections.map(section => { - if (section.Name === intentName) { - return updatedIntent; - } - return section; - }); -} - -/** - * - * @param content origin lu file content - * @param intentName intent Name, support subSection naming 'CheckEmail/CheckUnreadEmail'. if #CheckEmail not exist will do recursive add. - * @param {Name, Body} intent the updates. if intent is empty will do remove. - */ -export function updateIntent(content: string, intentName: string, intent: LuIntent | null): string { - let targetSection; - let targetSectionContent; - const updatedSectionContent = textFromIntent(intent); - const resource = luParser.parse(content); - const { Sections } = resource; - - // if intent is null, do remove - // if remove target not exist return origin content; - if (!intent || isEmpty(intent)) { - if (intentName.includes('/')) { - const [parrentName, childName] = intentName.split('/'); - const targetChildSection = Sections.find(({ Name }) => Name === parrentName)?.SimpleIntentSections.find( - ({ Name }) => Name === childName - ); - if (!targetChildSection) { - return content; - } - } else { - const targetSection = Sections.find(({ Name }) => Name === intentName); - if (targetSection) { - return new sectionOperator(resource).deleteSection(targetSection.Id).Content; - } - return content; - } - } - - // nestedSection name path - if (intentName.includes('/')) { - const [parrentName, childName] = intentName.split('/'); - targetSection = Sections.find(({ Name }) => Name === parrentName); - - if (targetSection) { - const updatedSections = updateInSections(targetSection.SimpleIntentSections, childName, intent); - targetSectionContent = textFromIntent({ Name: targetSection.Name, Body: textFromIntents(updatedSections, true) }); - } else { - targetSectionContent = textFromIntent({ Name: parrentName, Body: textFromIntent(intent, true) }); - } - } else { - targetSection = Sections.find(({ Name }) => Name === intentName); - targetSectionContent = updatedSectionContent; - } - - // update - if (targetSection) { - return new sectionOperator(resource).updateSection(targetSection.Id, targetSectionContent).Content; - // add if not exist - } else { - return new sectionOperator(resource).addSection(['', targetSectionContent].join(NEWLINE)).Content; - } -} - -/** - * - * @param content origin lu file content - * @param {Name, Body} intent the adds. Name support subSection naming 'CheckEmail/CheckUnreadEmail', if #CheckEmail not exist will do recursive add. - */ -export function addIntent(content: string, { Name, Body }: LuIntent): string { - const intentName = Name; - if (Name.includes('/')) { - const [, childName] = Name.split('/'); - Name = childName; - } - return updateIntent(content, intentName, { Name, Body }); -} - -/** - * - * @param content origin lu file content - * @param intentName the remove intentName. Name support subSection naming 'CheckEmail/CheckUnreadEmail', if any of them not exist will do nothing. - */ -export function removeIntent(content: string, intentName: string): string { - if (intentName.includes('/')) { - return updateIntent(content, intentName, null); - } - const resource = luParser.parse(content); - const { Sections } = resource; - const targetSection = Sections.find(({ Name }) => Name === intentName); - if (targetSection) { - return new sectionOperator(resource).deleteSection(targetSection.Id).Content; - } - return content; -} +export * from '@bfc/indexers/lib/utils/luUtil'; export function getReferredFiles(luFiles: LuFile[], dialogs: DialogInfo[]) { return luFiles.filter(file => { diff --git a/Composer/packages/client/src/utils/navigation.ts b/Composer/packages/client/src/utils/navigation.ts index 415f6285a1..117734326b 100644 --- a/Composer/packages/client/src/utils/navigation.ts +++ b/Composer/packages/client/src/utils/navigation.ts @@ -3,7 +3,6 @@ import cloneDeep from 'lodash/cloneDeep'; import { navigate, NavigateOptions } from '@reach/router'; -import { Diagnostic } from '@bfc/indexers'; import { BreadcrumbItem, DesignPageLocation } from '../store/types'; @@ -77,13 +76,11 @@ interface NavigationState { breadcrumb: BreadcrumbItem[]; } -export function convertDialogDiagnosticToUrl(diagnostic: Diagnostic): string { +export function convertPathToUrl(id: string, path?: string): string { //path is like main.trigers[0].actions[0] //uri = id?selected=triggers[0]&focused=triggers[0].actions[0] - const { path, source } = diagnostic; - if (!source) return ''; - let uri = `/dialogs/${source}`; + let uri = `/dialogs/${id}`; if (!path) return uri; const items = path.split('#'); @@ -107,8 +104,10 @@ export function convertDialogDiagnosticToUrl(diagnostic: Diagnostic): string { return uri; } -export function toUrlUtil(dialogId: string, path: string): string { +export function toUrlUtil(path: string): string { const tokens = path.split('#'); + const firstDotIndex = tokens[0].indexOf('.'); + const dialogId = tokens[0].substring(0, firstDotIndex); const focusedPath = parsePathToFocused(tokens[0]); const selectedPath = parsePathToSelected(tokens[0]); const type = tokens[1]; diff --git a/Composer/packages/extensions/obiformeditor/demo/src/editorschema.json b/Composer/packages/extensions/obiformeditor/demo/src/editorschema.json index 7875250f47..b41d827cdb 100644 --- a/Composer/packages/extensions/obiformeditor/demo/src/editorschema.json +++ b/Composer/packages/extensions/obiformeditor/demo/src/editorschema.json @@ -63,9 +63,6 @@ "Microsoft.IfCondition": { "title": "Branch: If/Else" }, - "Microsoft.InitProperty": { - "title": "Initialize Property" - }, "Microsoft.OnIntent": { "title": "Intent" }, diff --git a/Composer/packages/extensions/obiformeditor/demo/src/index.tsx b/Composer/packages/extensions/obiformeditor/demo/src/index.tsx index 8897c9d207..5178b5d728 100644 --- a/Composer/packages/extensions/obiformeditor/demo/src/index.tsx +++ b/Composer/packages/extensions/obiformeditor/demo/src/index.tsx @@ -173,6 +173,10 @@ const mockShellApi = [ 'getLgTemplates', 'createLgTemplate', 'updateLgTemplate', + 'addLuIntent', + 'updateLuIntent', + 'removeLuIntent', + 'updateRegExIntent', 'validateExpression', 'onFocusSteps', 'onFocusEvent', diff --git a/Composer/packages/extensions/obiformeditor/package.json b/Composer/packages/extensions/obiformeditor/package.json index a74aae2939..b575a1f5a4 100644 --- a/Composer/packages/extensions/obiformeditor/package.json +++ b/Composer/packages/extensions/obiformeditor/package.json @@ -61,7 +61,7 @@ "@types/react": "16.9.0", "@types/react-dom": "16.9.0", "autoprefixer": "^9.5.1", - "botframework-expressions": "4.7.0-preview.93464", + "botframework-expressions": "^4.8.0-preview.106476", "codemirror": "^5.44.0", "copyfiles": "^2.1.0", "css-loader": "^2.1.1", diff --git a/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/Other.tsx b/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/Other.tsx index 2e36c41bb7..581957d1c4 100644 --- a/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/Other.tsx +++ b/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/Other.tsx @@ -10,7 +10,6 @@ import { MicrosoftInputDialog } from '@bfc/shared'; import { WidgetLabel } from '../../widgets/WidgetLabel'; import { LgEditorWidget } from '../../widgets/LgEditorWidget'; -import { TextWidget } from '../../widgets'; import { Validations } from './Validations'; import { field } from './styles'; @@ -22,7 +21,7 @@ interface OtherProps extends FieldProps { } export const Other: React.FC = props => { - const { onChange, getSchema, idSchema, formData, errorSchema } = props; + const { onChange, getSchema, idSchema, formData } = props; return ( @@ -56,17 +55,6 @@ export const Other: React.FC = props => { height={125} /> -
- -
{ return enumSchema.enum.map(o => ({ label: o as string, value: o as string })); }; +const usesLuisRecognizer = ({ content }: DialogInfo) => { + return typeof content?.recognizer === 'string'; +}; + interface UserInputProps extends FieldProps { onChange: PromptFieldChangeHandler; getSchema: GetSchema; } export const UserInput: React.FC = props => { - const { onChange, getSchema, idSchema, formData, errorSchema } = props; + const { formContext, onChange, getSchema, idSchema, formData, errorSchema } = props; + const { const: type } = getSchema('$type'); + const [, promptType] = (type as string).split('.'); + const intentName = `${promptType}.response-${formData?.$designer?.id}`; return ( @@ -58,6 +67,22 @@ export const UserInput: React.FC = props => { />
)} +
+ +
+ {usesLuisRecognizer(formContext.currentDialog) && type !== SDKTypes.AttachmentInput && ( +
+ +
+ )} {getSchema('defaultLocale') && (
= props => { const { formContext } = props; const { shellApi, focusedTab, focusedSteps } = formContext; - const promptSettingsIdSchema = ({ __id: props.idSchema.__id + 'promptSettings' } as unknown) as IdSchema; const getSchema: GetSchema = field => { const fieldSchema = get(props.schema, ['properties', field]); @@ -43,38 +41,23 @@ export const PromptField: React.FC = props => { return ( -
- - - - - - - - - - - -
- -
-
- - - -
-
+ + + + + + + + + + + +
); }; diff --git a/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/styles.ts b/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/styles.ts index aecd3cf0cf..6cc18679b6 100644 --- a/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/styles.ts +++ b/Composer/packages/extensions/obiformeditor/src/Form/fields/PromptField/styles.ts @@ -21,10 +21,6 @@ export const tabs: Partial = { }, }; -export const tabsContainer = css` - border-bottom: 1px solid #c8c6c4; -`; - export const validationItemInput = css` display: flex; align-items: center; diff --git a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx index 65f989aabf..2652028a8e 100644 --- a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx +++ b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/InlineLuEditor.tsx @@ -20,8 +20,12 @@ const InlineLuEditor: React.FC = props => { setLocalContent(value); onSave(value); }; - - return ; + const luOption = { + fileId: file.id, + }; + return ( + + ); }; export default InlineLuEditor; diff --git a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx index a4ba6f1048..70c27c0f64 100644 --- a/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx +++ b/Composer/packages/extensions/obiformeditor/src/Form/fields/RecognizerField/index.tsx @@ -1,28 +1,21 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import React, { useState, ReactElement, Suspense, useEffect } from 'react'; +import React, { useState, ReactElement } from 'react'; import formatMessage from 'format-message'; import { FieldProps } from '@bfcomposer/react-jsonschema-form'; import { Dropdown, ResponsiveMode, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/Spinner'; import { MicrosoftIRecognizer } from '@bfc/shared'; -import { LuFile, combineMessage } from '@bfc/indexers'; +import { LuFile } from '@bfc/indexers'; import { BaseField } from '../BaseField'; -import { LoadingSpinner } from '../../../LoadingSpinner'; - -import ToggleEditor from './ToggleEditor'; -import RegexEditor from './RegexEditor'; import './styles.css'; -const InlineLuEditor = React.lazy(() => import('./InlineLuEditor')); - export const RecognizerField: React.FC> = props => { const { formData } = props; const [loading, setLoading] = useState(false); - const [errorMsg, setErrorMsg] = useState(''); const { formContext: { luFiles, shellApi, currentDialog }, @@ -32,19 +25,6 @@ export const RecognizerField: React.FC> = props const isRegex = typeof formData === 'object' && formData.$type === 'Microsoft.RegexRecognizer'; const currentDialogId = currentDialog.id; const selectedFile: LuFile | void = luFiles.find(f => f.id === currentDialogId); - const isLuFileSelected = Boolean( - selectedFile && typeof props.formData === 'string' && props.formData.startsWith(selectedFile.id) - ); - - //make the inline editor show error message - useEffect(() => { - if (selectedFile && selectedFile.diagnostics.length > 0) { - const msg = combineMessage(selectedFile.diagnostics); - setErrorMsg(msg); - } else { - setErrorMsg(''); - } - }, [selectedFile]); const handleChange = (_, option?: IDropdownOption): void => { if (option) { @@ -144,28 +124,6 @@ export const RecognizerField: React.FC> = props responsiveMode={ResponsiveMode.large} onRenderTitle={onRenderTitle} /> - - {() => { - if (selectedFile && isLuFileSelected) { - const updateLuFile = (newValue?: string): void => { - shellApi.updateLuFile({ id: selectedFile.id, content: newValue }).catch(setErrorMsg); - }; - - return ( - }> - - - ); - } - if (isRegex) { - return ; - } - }} -
); diff --git a/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx b/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx index dbe9bef687..b659fb552f 100644 --- a/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx +++ b/Composer/packages/extensions/obiformeditor/src/Form/widgets/IntentWidget.tsx @@ -4,83 +4,47 @@ import React from 'react'; import { Dropdown, ResponsiveMode, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown'; import formatMessage from 'format-message'; -import { RegexRecognizer } from '@bfc/shared'; -import { LuFile, DialogInfo } from '@bfc/indexers'; +import { DialogInfo } from '@bfc/indexers'; -import { BFDWidgetProps, FormContext } from '../types'; +import { BFDWidgetProps } from '../types'; import { WidgetLabel } from './WidgetLabel'; +import { LuEditorWidget } from './LuEditorWidget'; +import { RegexEditorWidget } from './RegexEditorWidget'; const EMPTY_OPTION = { key: '', text: '' }; enum RecognizerType { + 'none', 'regex', 'luis', } -function recognizerType(currentDialog: DialogInfo): RecognizerType | null { - const recognizer = currentDialog.content.recognizer; - if (!recognizer) { - return null; - } - - if (typeof recognizer === 'object' && recognizer.$type === 'Microsoft.RegexRecognizer') { - return RecognizerType.regex; - } else if (typeof recognizer === 'string') { - return RecognizerType.luis; - } - - return null; -} +function recognizerType({ content }: DialogInfo): RecognizerType | null { + const { recognizer } = content; -function regexIntentOptions(currentDialog: DialogInfo): IDropdownOption[] { - const recognizer = currentDialog.content.recognizer as RegexRecognizer; - let options: IDropdownOption[] = [EMPTY_OPTION]; - - if (!recognizer) { - return options; - } - - if (recognizer.intents) { - options = options.concat(recognizer.intents.map(i => ({ key: i.intent, text: i.intent }))); - } - - return options; -} - -function luIntentOptions(formContext: FormContext): IDropdownOption[] { - const luFile: LuFile | void = formContext.luFiles.find(f => f.id === formContext.currentDialog.id); - let options: IDropdownOption[] = [EMPTY_OPTION]; - - if (luFile) { - const intents: { name: string }[] = luFile.intents.map(({ Name: name }) => { - return { - name, - }; - }); - - options = options.concat( - intents.map(i => ({ - key: i.name, - text: i.name, - })) - ); + if (recognizer) { + if (typeof recognizer === 'object' && recognizer.$type === 'Microsoft.RegexRecognizer') { + return RecognizerType.regex; + } else if (typeof recognizer === 'string') { + return RecognizerType.luis; + } } - return options; + return RecognizerType.none; } export const IntentWidget: React.FC = props => { const { disabled, onChange, id, onFocus, onBlur, value, formContext, placeholder, label, schema } = props; const { description } = schema; + const { currentDialog } = formContext; + + const type = recognizerType(currentDialog); let options: IDropdownOption[] = []; - switch (recognizerType(formContext.currentDialog)) { + switch (type) { case RecognizerType.regex: - options = regexIntentOptions(formContext.currentDialog); - break; case RecognizerType.luis: - options = luIntentOptions(formContext); break; default: options = [EMPTY_OPTION]; @@ -95,18 +59,24 @@ export const IntentWidget: React.FC = props => { return ( <> - - onBlur && onBlur(id, value)} - onChange={handleChange} - onFocus={() => onFocus && onFocus(id, value)} - options={options} - selectedKey={value || null} - responsiveMode={ResponsiveMode.large} - disabled={disabled || options.length === 1} - placeholder={options.length > 1 ? placeholder : formatMessage('No intents configured for this dialog')} - /> + {type === RecognizerType.none && ( + <> + + onBlur && onBlur(id, value)} + onChange={handleChange} + onFocus={() => onFocus && onFocus(id, value)} + options={options} + selectedKey={value || null} + responsiveMode={ResponsiveMode.large} + disabled={disabled || options.length === 1} + placeholder={options.length > 1 ? placeholder : formatMessage('No intents configured for this dialog')} + /> + + )} + {type === RecognizerType.luis && } + {type === RecognizerType.regex && } ); }; diff --git a/Composer/packages/extensions/obiformeditor/src/Form/widgets/LuEditorWidget.tsx b/Composer/packages/extensions/obiformeditor/src/Form/widgets/LuEditorWidget.tsx new file mode 100644 index 0000000000..658419708e --- /dev/null +++ b/Composer/packages/extensions/obiformeditor/src/Form/widgets/LuEditorWidget.tsx @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import React from 'react'; +import debounce from 'lodash/debounce'; +import formatMessage from 'format-message'; +import { LuEditor } from '@bfc/code-editor'; +import { LuFile, filterSectionDiagnostics } from '@bfc/indexers'; +import { LuIntentSection } from '@bfc/shared'; + +import { FormContext } from '../types'; + +import { WidgetLabel } from './WidgetLabel'; + +interface LuEditorWidgetProps { + formContext: FormContext; + name: string; + height?: number | string; + onChange?: (template?: string) => void; + prompt?: boolean; +} + +export class LuEditorWidget extends React.Component { + constructor(props) { + super(props); + this.debounceUpdate = debounce(this.updateLuIntent, 500); + this.name = this.props.name; + this.formContext = this.props.formContext; + this.luFileId = this.formContext.currentDialog.id; + this.luFile = this.formContext.luFiles.find(f => f.id === this.luFileId); + this.luIntent = (this.luFile && this.luFile.intents.find(intent => intent.Name === this.name)) || { + Name: this.name, + Body: '', + }; + } + + formContext: FormContext; + name: string; + luFileId: string; + luFile?: LuFile; + luIntent: LuIntentSection; + state = { localValue: '' }; + debounceUpdate; + updateLuIntent = (body: string) => { + this.formContext.shellApi.updateLuIntent(this.luFileId, this.name, { Name: this.name, Body: body }).catch(() => {}); + }; + + static getDerivedStateFromProps(nextProps, prevState) { + const name = nextProps.name; + const formContext = nextProps.formContext; + const luFileId = formContext.currentDialog.id; + const luFile = formContext.luFiles.find(f => f.id === luFileId); + const luIntent = (luFile && luFile.intents.find(intent => intent.Name === name)) || { + Name: name, + Body: '', + }; + if (!prevState.localValue) { + return { + localValue: luIntent.Body, + }; + } + return null; + } + + onChange = (body: string) => { + this.setState({ + localValue: body, + }); + if (this.luFileId) { + if (body) { + this.debounceUpdate(body); + } else { + this.formContext.shellApi.removeLuIntent(this.luFileId, this.name); + } + } + }; + + render() { + const { height = 250 } = this.props; + const { luFile, luFileId, luIntent, name } = this; + const diagnostic = luFile && filterSectionDiagnostics(luFile.diagnostics, luIntent)[0]; + + const errorMsg = diagnostic + ? diagnostic.message.split('error message: ')[diagnostic.message.split('error message: ').length - 1] + : ''; + + const label = prompt + ? formatMessage('Expected responses (intent: {name})', { name }) + : formatMessage('Trigger phrases (intent: {name})', { name }); + + return ( + <> + + + + ); + } +} diff --git a/Composer/packages/extensions/obiformeditor/src/Form/widgets/RegexEditorWidget.tsx b/Composer/packages/extensions/obiformeditor/src/Form/widgets/RegexEditorWidget.tsx new file mode 100644 index 0000000000..6909cb0585 --- /dev/null +++ b/Composer/packages/extensions/obiformeditor/src/Form/widgets/RegexEditorWidget.tsx @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import { jsx } from '@emotion/core'; +import formatMessage from 'format-message'; +import { RegexRecognizer } from '@bfc/shared'; +import { DialogInfo } from '@bfc/indexers'; +import { useState } from 'react'; + +import { FormContext } from '../types'; + +import { TextWidget } from '.'; + +interface RegexEditorWidgetProps { + formContext: FormContext; + name: string; + onChange?: (template?: string) => void; +} + +function getRegexIntentPattern(currentDialog: DialogInfo, intent: string): string | null { + const recognizer = currentDialog.content.recognizer as RegexRecognizer; + let pattern: string | null = null; + + if (!recognizer) { + return null; + } + + if (recognizer.intents) { + pattern = recognizer.intents.find(i => i.intent === intent)?.pattern || null; + } + + return pattern; +} +export const RegexEditorWidget: React.FC = props => { + const { formContext, name } = props; + const { currentDialog } = formContext; + const label = formatMessage('Trigger phrases (intent: #{name})', { name }); + const [localValue, setLocalValue] = useState(getRegexIntentPattern(currentDialog, name)); + const handleIntentchange = (pattern): void => { + setLocalValue(pattern); + formContext.shellApi.updateRegExIntent(currentDialog.id, name, pattern); + }; + return ( + + ); +}; diff --git a/Composer/packages/extensions/obiformeditor/src/schema/uischema.ts b/Composer/packages/extensions/obiformeditor/src/schema/uischema.ts index 01fdd5ed71..54b5796491 100644 --- a/Composer/packages/extensions/obiformeditor/src/schema/uischema.ts +++ b/Composer/packages/extensions/obiformeditor/src/schema/uischema.ts @@ -159,7 +159,7 @@ export const uiSchema: { [key in SDKTypes]?: UiSchema } = { [SDKTypes.OnInvokeActivity]: { ...triggerUiSchema, }, - [SDKTypes.OnMessageActivity]: { + [SDKTypes.OnMessageReceivedActivity]: { ...triggerUiSchema, }, [SDKTypes.OnMessageDeleteActivity]: { diff --git a/Composer/packages/extensions/visual-designer/__tests__/components/nodes/FormCard.test.tsx b/Composer/packages/extensions/visual-designer/__tests__/components/nodes/FormCard.test.tsx deleted file mode 100644 index 63d303ae46..0000000000 --- a/Composer/packages/extensions/visual-designer/__tests__/components/nodes/FormCard.test.tsx +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import React from 'react'; -import { render, fireEvent } from 'react-testing-library'; - -import { FormCard } from '../../../src/components/nodes/templates/FormCard'; -import { getElementColor } from '../../../src/utils/obiPropertyResolver'; - -describe('', () => { - let renderResult, header, label, nodeColors, onClick; - - beforeEach(() => { - header = 'InputFormCard'; - label = 'FormCard'; - nodeColors = getElementColor('Microsoft.SendActivity'); - - onClick = jest.fn(); - - renderResult = render( - } onClick={onClick} /> - ); - }); - - it('can be clicked', async () => { - const { findByTestId } = renderResult; - const formCard = await findByTestId('FormCard'); - - fireEvent.click(formCard); - expect(onClick).toHaveBeenCalled(); - }); -}); diff --git a/Composer/packages/extensions/visual-designer/__tests__/layouters/measureJsonBoundary.test.ts b/Composer/packages/extensions/visual-designer/__tests__/layouters/measureJsonBoundary.test.ts index 54f3ca632a..8e1cb2310d 100644 --- a/Composer/packages/extensions/visual-designer/__tests__/layouters/measureJsonBoundary.test.ts +++ b/Composer/packages/extensions/visual-designer/__tests__/layouters/measureJsonBoundary.test.ts @@ -11,6 +11,8 @@ import { ChoiceInputSize, ChoiceInputMarginTop, ChoiceInputMarginBottom, + StandardNodeWidth, + HeaderHeight, } from '../../src/constants/ElementSizes'; describe('measureJsonBoundary', () => { @@ -30,9 +32,7 @@ describe('measureJsonBoundary', () => { expect(measureJsonBoundary({ $type: ObiTypes.LoopIndicator })).toEqual( new Boundary(LoopIconSize.width, LoopIconSize.height) ); - expect(measureJsonBoundary({ $type: ObiTypes.LogAction })).toEqual( - new Boundary(InitNodeSize.width, InitNodeSize.height) - ); + expect(measureJsonBoundary({ $type: ObiTypes.LogAction })).toEqual(new Boundary(StandardNodeWidth, HeaderHeight)); }); it("should return boundary whose size is determined by the data's choices when json.$type is choiceInput", () => { const data1: { [key: string]: any } = { diff --git a/Composer/packages/extensions/visual-designer/__tests__/widgets/ChoiceInput.test.tsx b/Composer/packages/extensions/visual-designer/__tests__/widgets/ChoiceInput.test.tsx deleted file mode 100644 index c7f44f0458..0000000000 --- a/Composer/packages/extensions/visual-designer/__tests__/widgets/ChoiceInput.test.tsx +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -import React from 'react'; -import { render, cleanup } from 'react-testing-library'; - -import { ChoiceInput } from '../../src/widgets/ChoiceInput'; -import { ObiTypes } from '../../src/constants/ObiTypes'; - -describe('', () => { - let renderResult, id, data, onEvent, onResize; - - beforeEach(() => { - onEvent = jest.fn(); - onResize = jest.fn(); - id = 'choiceInput'; - }); - afterEach(cleanup); - - it("should have no choice dom when data's choice is null", () => { - data = { - $type: ObiTypes.ChoiceInput, - choices: null, - }; - - renderResult = render(); - - const { queryByTestId } = renderResult; - expect(queryByTestId('ChoiceInput')).toBeNull(); - }); - - it("should show as many choice items as there are when data's choices length is less than 4", () => { - data = { - $type: ObiTypes.ChoiceInput, - choices: [ - { - value: '1', - }, - { - value: '2', - }, - { - value: '3', - }, - ], - }; - - renderResult = render(); - - const { getAllByRole } = renderResult; - const choices = getAllByRole('choice'); - - expect(choices).toHaveLength(3); - }); - - it("should show three choice items and 'more' dom when data's choices length is more than 3", () => { - data = { - $type: ObiTypes.ChoiceInput, - choices: [ - { - value: '1', - }, - { - value: '2', - }, - { - value: '3', - }, - { - value: '4', - }, - ], - }; - - renderResult = render(); - - const { getAllByRole, getByTestId } = renderResult; - const choices = getAllByRole('choice'); - const hasMore = getByTestId('hasMore'); - - expect(choices).toHaveLength(3); - expect(hasMore).toBeTruthy(); - }); -}); diff --git a/Composer/packages/extensions/visual-designer/demo/src/samples/todo/AddToDo.json b/Composer/packages/extensions/visual-designer/demo/src/samples/todo/AddToDo.json index f6f7be93f3..390fa63365 100644 --- a/Composer/packages/extensions/visual-designer/demo/src/samples/todo/AddToDo.json +++ b/Composer/packages/extensions/visual-designer/demo/src/samples/todo/AddToDo.json @@ -49,7 +49,7 @@ "condition": "user.todos == null", "actions": [ { - "$type": "Microsoft.InitProperty", + "$type": "Microsoft.SetProperty", "$designer": { "createdAt": "2019-07-16T20:01:05.970Z", "updatedAt": "2019-07-16T20:01:13.866Z", diff --git a/Composer/packages/extensions/visual-designer/demo/src/stories/VisualSDKDemo.js b/Composer/packages/extensions/visual-designer/demo/src/stories/VisualSDKDemo.js index c061775c26..1d6e470b23 100644 --- a/Composer/packages/extensions/visual-designer/demo/src/stories/VisualSDKDemo.js +++ b/Composer/packages/extensions/visual-designer/demo/src/stories/VisualSDKDemo.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { seedNewDialog, SDKTypes } from '@bfc/shared'; +import { seedNewDialog, SDKTypes, dialogGroups, DialogGroup } from '@bfc/shared'; import { EdgeMenu } from '../../../src/components/menus/EdgeMenu'; import { JsonBlock } from '../components/json-block'; @@ -18,27 +18,21 @@ export class VisualSDKDemo extends Component { seedInitialActions() { const initialTypes = [ - SDKTypes.SendActivity, - SDKTypes.EditArray, - SDKTypes.InitProperty, - SDKTypes.SetProperties, - SDKTypes.SetProperty, - SDKTypes.DeleteProperties, - SDKTypes.DeleteProperty, - SDKTypes.BeginDialog, - SDKTypes.EndDialog, - SDKTypes.RepeatDialog, - SDKTypes.ReplaceDialog, - SDKTypes.CancelAllDialogs, - SDKTypes.EmitEvent, + ...dialogGroups[DialogGroup.RESPONSE].types, + ...dialogGroups[DialogGroup.INPUT].types, + ...dialogGroups[DialogGroup.BRANCHING].types, + ...dialogGroups[DialogGroup.MEMORY].types, + ...dialogGroups[DialogGroup.STEP].types, + ...dialogGroups[DialogGroup.CODE].types, + ...dialogGroups[DialogGroup.LOG].types, ]; const initalActions = initialTypes.map(t => seedNewDialog(t)); return initalActions; } - appendActionPreview($type) { + insertActionPreview($type) { this.setState({ - actions: [...this.state.actions, seedNewDialog($type)], + actions: [seedNewDialog($type), ...this.state.actions], }); } @@ -52,7 +46,7 @@ export class VisualSDKDemo extends Component {
-
+
{renderUIWidget(uiSchemaPrivider.get(action.$type), { id: `actions[${index}]`, data: action, @@ -79,9 +73,9 @@ export class VisualSDKDemo extends Component { renderActionFactory() { return ( -
+

Create action by $type

- this.appendActionPreview($type)} /> + this.insertActionPreview($type)} />
); } @@ -91,8 +85,8 @@ export class VisualSDKDemo extends Component {

Visual SDK Demo

- {this.state.actions.map((action, index) => this.renderActionPreview(action, index))} {this.renderActionFactory()} + {this.state.actions.map((action, index) => this.renderActionPreview(action, index))}
); diff --git a/Composer/packages/extensions/visual-designer/package.json b/Composer/packages/extensions/visual-designer/package.json index 10909e14cc..736dba2a2a 100644 --- a/Composer/packages/extensions/visual-designer/package.json +++ b/Composer/packages/extensions/visual-designer/package.json @@ -25,7 +25,8 @@ }, "dependencies": { "@bfc/shared": "*", - "@emotion/core": "^10.0.7", + "@emotion/core": "^10.0.27", + "@emotion/styled": "^10.0.27", "@types/react": "16.9.0", "classnames": "^2.2.6", "create-react-class": "^15.6.3", @@ -36,6 +37,7 @@ "lodash": "^4.17.15", "office-ui-fabric-react": "7.62.0", "prop-types": "^15.7.2", + "react-measure": "^2.3.0", "source-map-loader": "^0.2.4" }, "peerDependencies": { diff --git a/Composer/packages/extensions/visual-designer/src/components/common/ListOverview.tsx b/Composer/packages/extensions/visual-designer/src/components/common/ListOverview.tsx new file mode 100644 index 0000000000..26640d92d6 --- /dev/null +++ b/Composer/packages/extensions/visual-designer/src/components/common/ListOverview.tsx @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import { jsx, css } from '@emotion/core'; +import { FC } from 'react'; + +import { SingleLineDiv } from '../elements/styledComponents'; + +export interface ListOverviewProps { + items: T[]; + renderItem: (item: T) => JSX.Element; + maxCount?: number; + itemPadding?: number; +} + +export const ListOverview: FC> = ({ + items, + renderItem, + maxCount = 3, + itemPadding: itemInterval = 4, +}) => { + if (!Array.isArray(items) || !items.length) { + return null; + } + return ( +
+ {items.slice(0, maxCount).map((item, index) => { + return ( +
+ {renderItem(item)} +
+ ); + })} + {items.length > maxCount ? ( + + {`${items.length - maxCount} more`} + + ) : null} +
+ ); +}; diff --git a/Composer/packages/extensions/visual-designer/src/components/elements/sharedCSS.tsx b/Composer/packages/extensions/visual-designer/src/components/elements/sharedCSS.tsx new file mode 100644 index 0000000000..0b31181742 --- /dev/null +++ b/Composer/packages/extensions/visual-designer/src/components/elements/sharedCSS.tsx @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { css } from '@emotion/core'; + +export const StandardFontCSS = css` + font-size: 12px; + line-height: 14px; + font-family: Segoe UI; + color: black; +`; + +export const TruncatedCSS = css` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +`; + +export const MultilineCSS = css` + white-space: initial; + overflow-wrap: break-word; + word-break: break-all; +`; diff --git a/Composer/packages/extensions/visual-designer/src/components/elements/styledComponents.tsx b/Composer/packages/extensions/visual-designer/src/components/elements/styledComponents.tsx new file mode 100644 index 0000000000..e8a3e6b08d --- /dev/null +++ b/Composer/packages/extensions/visual-designer/src/components/elements/styledComponents.tsx @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { css } from '@emotion/core'; +import styled from '@emotion/styled'; +import { Link } from 'office-ui-fabric-react/lib/Link'; + +import { ObiColors } from '../../constants/ElementColors'; + +import { DivProps } from './styledComponents.types'; +import { StandardFontCSS, TruncatedCSS, MultilineCSS } from './sharedCSS'; + +const dynamicStyle = props => + css` + color: ${props.color || ObiColors.Black}; + `; + +export const LinkBtn = styled(Link)(props => ({ + color: props.color || ObiColors.AzureBlue, +})); + +export const Span = styled.span` + ${dynamicStyle} +`; + +export const BorderedDiv = styled.div( + css` + ${StandardFontCSS}; + ${TruncatedCSS}; + padding: 2px 0 0 8px; + border: 1px solid #c4c4c4; + box-sizing: border-box; + `, + props => ({ + color: props.color || ObiColors.Black, + width: props.width, + height: props.height, + }) +); + +export const SingleLineDiv = styled.div` + ${StandardFontCSS}; + ${TruncatedCSS}; + line-height: ${height => (height ? height + 'px' : undefined)}; +`; + +export const TextDiv = styled.div` + ${StandardFontCSS}; + ${MultilineCSS}; + white-space: pre-wrap; + line-height: 16px; + display: inline-block; +`; + +export const Text = styled.span( + css` + ${StandardFontCSS}; + `, + ({ color }) => + css` + color: ${color}; + ` +); + +export const FixedInfo = styled.span` + ${StandardFontCSS}; + color: #757575; +`; diff --git a/Composer/packages/extensions/visual-designer/src/components/elements/styledComponents.types.ts b/Composer/packages/extensions/visual-designer/src/components/elements/styledComponents.types.ts new file mode 100644 index 0000000000..df0e8a9c9e --- /dev/null +++ b/Composer/packages/extensions/visual-designer/src/components/elements/styledComponents.types.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { DetailedHTMLProps, HTMLAttributes } from 'react'; + +export type ElementProps = DetailedHTMLProps, HTMLDivElement>; + +export interface DivProps extends ElementProps { + width?: number; + height?: number; +} diff --git a/Composer/packages/extensions/visual-designer/src/components/groups/StepGroup.tsx b/Composer/packages/extensions/visual-designer/src/components/groups/StepGroup.tsx index b852607f9e..eada5efc13 100644 --- a/Composer/packages/extensions/visual-designer/src/components/groups/StepGroup.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/groups/StepGroup.tsx @@ -3,10 +3,9 @@ /** @jsx jsx */ import { jsx } from '@emotion/core'; -import { useState, useMemo, useEffect, FunctionComponent } from 'react'; +import { useMemo, FunctionComponent } from 'react'; import { GraphNode } from '../../models/GraphNode'; -import { areBoundariesEqual } from '../../models/Boundary'; import { sequentialLayouter } from '../../layouters/sequentialLayouter'; import { ElementInterval, EdgeAddButtonSize } from '../../constants/ElementSizes'; import { NodeEventTypes } from '../../constants/NodeEventTypes'; @@ -18,46 +17,43 @@ import { GraphLayout } from '../../models/GraphLayout'; import { EdgeMenu } from '../menus/EdgeMenu'; import { SVGContainer } from '../lib/SVGContainer'; import { renderEdge } from '../lib/EdgeUtil'; +import { GraphNodeMap, useSmartLayout } from '../../hooks/useSmartLayout'; +import { designerCache } from '../../store/DesignerCache'; const StepInterval = ElementInterval.y; -const calculateNodes = (groupId: string, data): GraphNode[] => { +type StepNodeKey = string; + +const getStepKey = (stepOrder: number): StepNodeKey => `steps[${stepOrder}]`; +const parseStepIndex = (stepKey: string): number => parseInt(stepKey.replace(/steps\[(\d+)\]/, '$1')); + +const calculateNodes = (groupId: string, data): GraphNodeMap => { const steps = transformStepGroup(data, groupId); - return steps.map((x): GraphNode => GraphNode.fromIndexedJson(x)); + const stepNodes = steps.map((x): GraphNode => GraphNode.fromIndexedJson(x)); + return stepNodes.reduce((result, node, index) => { + result[getStepKey(index)] = node; + return result; + }, {} as GraphNodeMap); }; -const calculateLayout = (nodes, boundaryMap): GraphLayout => { - nodes.forEach((x): void => (x.boundary = boundaryMap[x.id] || x.boundary)); +const calculateLayout = (nodeMap: GraphNodeMap): GraphLayout => { + const nodes = Object.keys(nodeMap) + .sort((a, b) => parseStepIndex(a) - parseStepIndex(b)) + .map(stepName => nodeMap[stepName]); return sequentialLayouter(nodes); }; export const StepGroup: FunctionComponent = ({ id, data, onEvent, onResize }: NodeProps): JSX.Element => { - const [boundaryMap, setBoundaryMap] = useState({}); - const initialNodes = useMemo((): GraphNode[] => calculateNodes(id, data), [id, data]); - const layout = useMemo((): GraphLayout => calculateLayout(initialNodes, boundaryMap), [initialNodes, boundaryMap]); - const accumulatedPatches = {}; - - const patchBoundary = (id, boundary): void => { - if (!boundaryMap[id] || !areBoundariesEqual(boundaryMap[id], boundary)) { - accumulatedPatches[id] = boundary; - setBoundaryMap({ - ...boundaryMap, - ...accumulatedPatches, - }); - } - }; + const initialNodes = useMemo(() => calculateNodes(id, data), [id, data]); + const { layout, updateNodeBoundary } = useSmartLayout(initialNodes, calculateLayout, onResize); const { boundary, nodes, edges } = layout; - useEffect(() => { - onResize(layout.boundary); - }, [layout]); - return (
{Array.isArray(edges) ? edges.map(x => renderEdge(x)) : null} {nodes - ? nodes.map(x => ( + ? nodes.map((x, index) => ( = ({ id, data, onEvent, onR data={x.data} onEvent={onEvent} onResize={size => { - patchBoundary(x.id, size); + designerCache.cacheBoundary(x.data, size); + updateNodeBoundary(getStepKey(index), size); }} /> diff --git a/Composer/packages/extensions/visual-designer/src/components/lib/ArrowLine.tsx b/Composer/packages/extensions/visual-designer/src/components/lib/ArrowLine.tsx new file mode 100644 index 0000000000..ae07ba0611 --- /dev/null +++ b/Composer/packages/extensions/visual-designer/src/components/lib/ArrowLine.tsx @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/** @jsx jsx */ +import { jsx } from '@emotion/core'; + +export interface ArrowLineProps { + width: number; + arrowsize: number; + containerHeight?: number; + color?: string; +} +export const ArrowLine: React.FC = ({ width, arrowsize, color }) => { + const points = [ + { x: 0, y: 0 }, + { x: width / 2 - (arrowsize + 2), y: 0 }, + { x: width / 2, y: arrowsize }, + { x: width / 2 + (arrowsize + 2), y: 0 }, + { x: width, y: 0 }, + ]; + const pointsString = points.map(p => `${p.x},${p.y}`).join(' '); + return ( + + + + ); +}; + +ArrowLine.defaultProps = { + width: 200, + arrowsize: 8, + containerHeight: 1, + color: 'black', +}; diff --git a/Composer/packages/extensions/visual-designer/src/components/lib/EdgeUtil.tsx b/Composer/packages/extensions/visual-designer/src/components/lib/EdgeUtil.tsx index 192990b7a5..d4b24335ab 100644 --- a/Composer/packages/extensions/visual-designer/src/components/lib/EdgeUtil.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/lib/EdgeUtil.tsx @@ -126,7 +126,14 @@ export const drawSVGEdge = ( const [p1, p2] = calculateArrowPoints(endPoint, direction); const points = [p1, endPoint, p2].map(p => `${p.x},${p.y}`).join(' '); const arrow = ( - + ); elements.push(arrow); } diff --git a/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx b/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx index 9679033006..fd18129b61 100644 --- a/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx +++ b/Composer/packages/extensions/visual-designer/src/components/menus/EdgeMenu.tsx @@ -46,15 +46,21 @@ const buildEdgeMenuItemsFromClipboardContext = ( ); const enablePaste = Array.isArray(clipboardActions) && clipboardActions.length > 0; + const menuItemCount = menuItems.length; menuItems.unshift( { key: 'Paste', name: 'Paste', + ariaLabel: 'Paste', disabled: !enablePaste, onRender: () => { return (