This commit is contained in:
Eric Dahlvang 2021-03-08 22:42:00 -08:00
Родитель 27ba6ad42c
Коммит bdcd59956f
13 изменённых файлов: 465 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using AdaptiveExpressions.Properties;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Actions;
using Newtonsoft.Json;
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.BotBuilderSamples.Components.Actions
{
/// <summary>
/// Executes a set of actions while an expression evaluates to true.
/// </summary>
public class DoWhile : ActionScope
{
/// <summary>
/// Class identifier.
/// </summary>
[JsonProperty("$kind")]
public const string Kind = "BotBuilderSamples.DoWhile";
/// <summary>
/// Initializes a new instance of the <see cref="DoWhile"/> class.
/// </summary>
/// <param name="sourceFilePath">Optional, full path of the source file that contains the caller.</param>
/// <param name="sourceLineNumber">optional, line number in the source file at which the method is called.</param>
[JsonConstructor]
public DoWhile([CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0)
: base()
{
RegisterSourceLocation(sourceFilePath, sourceLineNumber);
}
/// <summary>
/// Gets or sets an optional expression which if is true will disable this action.
/// </summary>
/// <value>
/// A boolean expression.
/// </value>
[JsonProperty("disabled")]
public BoolExpression Disabled { get; set; }
/// <summary>
/// Gets or sets the memory expression evaludating to a condition which determines if the DoWhile should continue.
/// </summary>
/// <value>
/// The memory expression which determines if the DoWhile should continue.
/// </value>
[JsonProperty("condition")]
public BoolExpression Condition { get; set; } = false;
/// <summary>
/// Gets or sets an optional expression which will ensure the loop runs once, before checking the condition.
/// </summary>
/// <value>
/// A boolean expression.
/// </value>
[JsonProperty("runOnce")]
public BoolExpression RunOnce { get; set; } = true;
/// <summary>
/// Called when the dialog is started and pushed onto the dialog stack.
/// </summary>
/// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
/// <param name="options">Optional, initial information to pass to the dialog.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public override async Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options = null, CancellationToken cancellationToken = default(CancellationToken))
{
if (options is CancellationToken)
{
throw new ArgumentException($"{nameof(options)} cannot be a cancellation token");
}
if (Disabled != null && Disabled.GetValue(dc.State))
{
return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
if(Condition == null)
{
throw new InvalidOperationException("Condition is required for DoWhile.");
}
// If RunOnce is false, end the dialog if Condition evaluates to true.
if (RunOnce != null && RunOnce.GetValue(dc.State) == false)
{
if (Condition.GetValue(dc.State) == false)
{
return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
return await base.BeginDialogAsync(dc, options, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Called when the dialog's action ends.
/// </summary>
/// <param name="dc">The <see cref="DialogContext"/> for the current turn of conversation.</param>
/// <param name="result">Optional, value returned from the dialog that was called. The type
/// of the value returned is dependent on the child dialog.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task<DialogTurnResult> OnEndOfActionsAsync(DialogContext dc, object result = null, CancellationToken cancellationToken = default)
{
if (Condition.GetValue(dc.State))
{
return await BeginActionAsync(dc, 0, cancellationToken).ConfigureAwait(false);
}
return await base.OnEndOfActionsAsync(dc, result, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Called when a returning control to this dialog with an <see cref="ActionScopeResult"/>
/// with the property ActionCommand set to <c>BreakLoop</c>.
/// </summary>
/// <param name="dc">The dialog context for the current turn of the conversation.</param>
/// <param name="actionScopeResult">Contains the actions scope result.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task<DialogTurnResult> OnBreakLoopAsync(DialogContext dc, ActionScopeResult actionScopeResult, CancellationToken cancellationToken = default)
{
return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Called when a returning control to this dialog with an <see cref="ActionScopeResult"/>
/// with the property ActionCommand set to <c>ContinueLoop</c>.
/// </summary>
/// <param name="dc">The dialog context for the current turn of the conversation.</param>
/// <param name="actionScopeResult">Contains the actions scope result.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
protected override async Task<DialogTurnResult> OnContinueLoopAsync(DialogContext dc, ActionScopeResult actionScopeResult, CancellationToken cancellationToken = default)
{
if (Condition.GetValue(dc.State))
{
return await BeginActionAsync(dc, 0, cancellationToken).ConfigureAwait(false);
}
return await base.OnEndOfActionsAsync(dc, cancellationToken: cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Builds the compute Id for the dialog.
/// </summary>
/// <returns>A string representing the compute Id.</returns>
protected override string OnComputeId()
{
return $"{GetType().Name}({Condition?.ToString()})";
}
}
}

Просмотреть файл

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections.Generic;
using AdaptiveExpressions.Converters;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs.Debugging;
using Microsoft.Bot.Builder.Dialogs.Declarative;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
using Microsoft.BotBuilderSamples.Components.Actions;
using Newtonsoft.Json;
namespace Microsoft.BotBuilderSamples.Components
{
/// <summary>
/// <see cref="ComponentRegistration"/> for DoWhile enabling discovering assets.
/// </summary>
/// <remarks>
/// To make your components available to the system you derive from ComponentRegistration
/// and implement appropriate interfaces which register functionality. These components
/// then are consumed in appropriate places by the systems that need them.
/// </remarks>
public class DoWhileComponentRegistration : ComponentRegistration, IComponentDeclarativeTypes
{
/// <summary>
/// Initializes a new instance of the <see cref="DoWhileComponentRegistration"/> class.
/// </summary>
public DoWhileComponentRegistration()
{
}
/// <summary>
/// Gets adaptive <see cref="DeclarativeType"/> resources.
/// </summary>
/// <param name="resourceExplorer"><see cref="ResourceExplorer"/> with expected path to get all schema resources.</param>
/// <returns>Adaptive <see cref="DeclarativeType"/> resources.</returns>
public virtual IEnumerable<DeclarativeType> GetDeclarativeTypes(ResourceExplorer resourceExplorer)
{
yield return new DeclarativeType<DoWhile>(DoWhile.Kind);
}
/// <summary>
/// Gets adaptive <see cref="JsonConverter"/> resources.
/// </summary>
/// <param name="resourceExplorer">ResourceExplorer to use to resolve references.</param>
/// <param name="sourceContext">SourceContext to build debugger source map.</param>
/// <returns>Adaptive <see cref="JsonConverter"/> resources.</returns>
public virtual IEnumerable<JsonConverter> GetConverters(ResourceExplorer resourceExplorer, SourceContext sourceContext)
{
yield return new ObjectExpressionConverter<object>();
}
}
}

Двоичные данные
samples/csharp_dotnetcore/101.components-dowhile/DoWhileDialog.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 37 KiB

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Runtime.Plugins;
namespace Microsoft.BotBuilderSamples.Components
{
/// <summary>
// Represents a plugin to be utilized within the bot runtime that can provide access
// to additional components that implement well-defined interfaces in the Bot Framework
// SDK.
/// </summary>
public class DoWhilePlugin : IBotPlugin
{
/// <summary>
/// Load the contents of the plugin into the bot runtime.
/// </summary>
/// <param name="context">Load context that provides access to application configuration and service collection.</param>
public void Load(IBotPluginLoadContext context)
{
ComponentRegistration.Add(new DoWhileComponentRegistration());
}
}
}

Просмотреть файл

@ -0,0 +1,23 @@
MIT License
Copyright (c) 2020 - present Microsoft Corporation
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Просмотреть файл

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RepositoryUrl>https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/101.components-dowhile</RepositoryUrl>
<PackageIcon>package-icon.png</PackageIcon>
<PackageLicenseUrl />
<PackageLicenseExpression></PackageLicenseExpression>
<Description>Sample composer action to execute a loop while a condition is true.</Description>
<PackageLicenseFile>LICENSE.md</PackageLicenseFile>
<PackageProjectUrl>https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/csharp_dotnetcore/101.components-dowhile</PackageProjectUrl>
<Version>1.0.1-preview</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageTags>bf-component</PackageTags>
<ContentTargetFolders>content</ContentTargetFolders>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Bot.Builder.Runtime.Plugins" Version="4.12.0-preview" />
<PackageReference Include="Microsoft.Bot.Builder.Dialogs.Adaptive" Version="4.12.0" />
<PackageReference Include="ParallelExtensionsExtras.CrossPlatform" Version="1.0.0" />
</ItemGroup>
<ItemGroup>
<Content Include="**/*.dialog" />
<Content Include="**/*.lg" />
<Content Include="**/*.lu" />
<Content Include="**/*.schema" />
<Content Include="**/*.uischema" />
<Content Include="**/*.qna" />
</ItemGroup>
<ItemGroup>
<None Remove="Schemas\BotBuilderSamples.DoWhile.schema" />
<None Remove="Schemas\BotBuilderSamples.DoWhile.uischema" />
<None Include="LICENSE.md">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="package-icon.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,44 @@
{
"$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema",
"$role": "implements(Microsoft.IDialog)",
"title": "DoWhile",
"description": "Performs a set of actions while a condition is true.",
"type": "object",
"properties": {
"id": {
"type": "string",
"title": "Id",
"description": "Optional id for the dialog"
},
"disabled": {
"$ref": "schema:#/definitions/booleanExpression",
"title": "Disabled",
"description": "Optional condition which if true will disable this action.",
"examples": [
"user.age > 3"
]
},
"condition": {
"$ref": "schema:#/definitions/booleanExpression",
"title": "Condition",
"description": "Name of the property with the array of dialogs to call.",
"examples": [
"dialog.dialogList"
]
},
"runOnce": {
"$ref": "schema:#/definitions/booleanExpression",
"title": "Run once",
"description": "Execute the actions once, before checking the condition.",
"default": true
},
"actions": {
"type": "array",
"items": {
"$kind": "Microsoft.IDialog"
},
"title": "Actions",
"description": "Actions to execute while the condition is true."
}
}
}

Просмотреть файл

@ -0,0 +1,35 @@
{
"$schema": "https://schemas.botframework.com/schemas/ui/v1.0/ui.schema",
"form": {
"label": "Do actions while condition is true",
"subtitle": "DoWhile",
"helpLink": "https://aka.ms/bfc-understanding-dialogs",
"order": [
"condition",
"runOnce",
"*"
],
"hidden": [
"actions"
],
"properties": {
"condition": {
"intellisenseScopes": [
"variable-scopes"
]
}
}
},
"menu": {
"label": "DoWhile loop",
"submenu": [ "Looping" ]
},
"flow": {
"widget": "ForeachWidget",
"nowrap": true,
"loop": {
"widget": "ActionCard",
"body": "=action.condition"
}
}
}

Двоичные данные
samples/csharp_dotnetcore/101.components-dowhile/new-menu.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 19 KiB

Двоичные данные
samples/csharp_dotnetcore/101.components-dowhile/package-feed.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 15 KiB

Двоичные данные
samples/csharp_dotnetcore/101.components-dowhile/package-icon.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.7 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 96 KiB

Просмотреть файл

@ -0,0 +1,74 @@
# DoWhile Component
This sample demonstrates how to extend [Bot Framework Composer](https://docs.microsoft.com/en-us/composer/introduction) with a new DoWhile custom action component.
The following classes and schema files are included in the project:
| File | Description |
| ------ | ------ |
| [DoWhile.cs](#DoWhile) | Inherits from ActionScope and manages execution of a block of actions supporting Break and Continue semantics.. |
| [DoWhilePlugin.cs](#Plugin) | Loaded by the adaptive runtime DependencyInjection Abstractions and calls ComponentRegistration, adding DoWhileComponentRegistration. |
| [DoWhileComponentRegistration.cs](#ComponentRegistration) | Registers the DoWhile component with the adaptive dialog system. |
| [.schema](#schema) | Declarative definition of the DoWhile component. |
| [.uischema](#uischema) | DoWhile component visual interface declarative definition. |
## Prerequisites
- [Bot Framework Composer](https://docs.microsoft.com/en-us/composer/install-composer) version 1.3.1
- [.NET Core SDK](https://dotnet.microsoft.com/download) version 3.1
- [nuget.exe](https://www.nuget.org/downloads) recommended latest
- A local package feed is required. See ([nuget local feed](https://docs.microsoft.com/en-us/nuget/hosting-packages/local-feeds)) for details.
## To try this sample
1) Clone the repository
```bash
git clone https://github.com/Microsoft/botbuilder-samples.git
```
2) If you are using Visual Studio
- Launch Visual Studio
- File -> Open -> Project/Solution
- Navigate to `samples/csharp_dotnetcore/101.components-dowhile` folder
- Select `Microsoft.BotBuilderSamples.Components.DoWhile.csproj` file
- Building the project
3) The .nupkg should now be in the bin folder. Next, run `nuget add`
```bash
nuget add Microsoft.BotBuilderSamples.Components.DoWhile.1.0.0-preview.nupkg -source c:\nuget
```
## Composer
### Package Installation
Add the local package feed to Composer
![Local Package Feed](package-feed.png)
The local package can now be installed from composers Package Manager screen. Just select the package from the list and click install.
![Package Manager](package-manager.png)
### Usage
Once installed you should find the DoWhile action under the "Looping" menu.
![Action Menu](new-menu.png)
#### Do While
Add the DoWhile action to a dialog, and add some child actions. Ensure the condition will eventually resolve to `true`.
![DoWhile Dialog](DoWhileDialog.png)
## Deploy the bot to Azure
To learn more about deploying a bot to Azure, see [Deploy your bot to Azure](https://aka.ms/azuredeployment) for a complete list of deployment instructions.
## Further reading
- [Add custom actions](https://docs.microsoft.com/en-us/composer/how-to-add-custom-action)