Skip to content

Commit

Permalink
Initial check-in of extensions including ControlForm. (#3297)
Browse files Browse the repository at this point in the history
Co-authored-by: Chris McConnell <chrimc>
  • Loading branch information
chrimc62 committed May 28, 2021
1 parent 560994a commit b1e7a40
Show file tree
Hide file tree
Showing 23 changed files with 13,946 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31229.75
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Form.Extensions", "Microsoft.Form.Extensions\Microsoft.Form.Extensions.csproj", "{8BE9A2AB-47A7-4136-9112-E8BB17BB5BD6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{B77EDC38-A315-438A-8F34-53B3003DAD98}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8BE9A2AB-47A7-4136-9112-E8BB17BB5BD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8BE9A2AB-47A7-4136-9112-E8BB17BB5BD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8BE9A2AB-47A7-4136-9112-E8BB17BB5BD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8BE9A2AB-47A7-4136-9112-E8BB17BB5BD6}.Release|Any CPU.Build.0 = Release|Any CPU
{B77EDC38-A315-438A-8F34-53B3003DAD98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B77EDC38-A315-438A-8F34-53B3003DAD98}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B77EDC38-A315-438A-8F34-53B3003DAD98}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B77EDC38-A315-438A-8F34-53B3003DAD98}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C168C6C9-7BA0-4222-8DCA-E608B1ECDA23}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using AdaptiveExpressions.Properties;
using Microsoft.Bot.Builder.Dialogs;
using Newtonsoft.Json;

namespace Microsoft.Form.Extensions
{
/// <summary>
/// Control what is collected in a form.
/// </summary>
public class ControlForm : Dialog
{
/// <summary>
/// Class identifier.
/// </summary>
[JsonProperty("$kind")]
public const string Kind = "Microsoft.ControlForm";

/// <summary>
/// Initializes a new instance of the <see cref="ControlForm"/> class.
/// </summary>
/// <param name="callerPath">Optional, source file full path.</param>
/// <param name="callerLine">Optional, line number in source file.</param>
public ControlForm([CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0)
{
RegisterSourceLocation(callerPath, callerLine);
}

/// <summary>
/// Gets or sets an optional expression which if is true will disable this action.
/// </summary>
/// <example>
/// "user.age > 18".
/// </example>
/// <value>
/// A boolean expression.
/// </value>
[JsonProperty("disabled")]
public BoolExpression Disabled { get; set; }

/// <summary>
/// Gets or sets a list of property names to push to the front of dialog.requiredPropties.
/// </summary>
/// <value>A list of property names.</value>
[JsonProperty("askFirst")]
public ArrayExpression<string> AskFirst { get; set; }

/// <summary>
/// Gets or sets a list of property names to remove from dialog.requiredPropties.
/// </summary>
/// <value>A list of property names.</value>
[JsonProperty("askLast")]
public ArrayExpression<string> AskLast { get; set; }

/// <summary>
/// Gets or sets a list of property names to remove from dialog.requiredPropties.
/// </summary>
/// <value>A list of property names.</value>
[JsonProperty("noAsk")]
public ArrayExpression<string> NoAsk { get; set; }

/// <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);
}

var askFirst = AskFirst?.GetValue(dc) ?? new List<string>();
var askLast = AskLast?.GetValue(dc) ?? new List<string>();
var noAsk = NoAsk?.GetValue(dc) ?? new List<string>();
var required = dc.State.GetValue<List<string>>(DialogPath.RequiredProperties) ?? new List<string>();

// Ensure there is no overlap
askFirst.RemoveAll(p => noAsk.Contains(p));
askLast.RemoveAll(p => noAsk.Contains(p) || askFirst.Contains(p));
required.RemoveAll(p => noAsk.Contains(p) || askFirst.Contains(p) || askLast.Contains(p));
required = askFirst.Union(required).Union(askLast).ToList();
dc.State.SetValue(DialogPath.RequiredProperties, required);
return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using AdaptiveExpressions.Converters;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs.Declarative;
using Microsoft.Bot.Builder.Dialogs.Declarative.Converters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace Microsoft.Form.Extensions
{
/// <summary>
/// Configure form extensions.
/// </summary>
public class FormExtensionsBotComponent : BotComponent
{
/// <summary>
/// <see cref="BotComponent"/> for adaptive components.
/// </summary>
/// <inheritdoc/>
public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
{
// Declarative types.
services.AddSingleton<DeclarativeType>(sp => new DeclarativeType<ControlForm>(ControlForm.Kind));
services.AddSingleton<JsonConverterFactory, JsonConverterFactory<ArrayExpressionConverter<string>>>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<PackageId>Microsoft.Form.Extensions</PackageId>
<Description>.NET extensions for forms including the action Microsoft.ControlForm and the trigger Microsoft.OnControlForm.</Description>
<Summary>.NET extensions for forms.</Summary>
<ContentTargetFolders>content</ContentTargetFolders>
<PackageTags>msbot-component;msbot-trigger;msbot-action</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
<RepositoryUrl>https://github.com/Microsoft/botbuilder-samples</RepositoryUrl>
<iconUrl>https://raw.githubusercontent.com/microsoft/botframework-sdk/master/icon.png</iconUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>icon.png</PackageIcon>
<Version>1.0.0-preview</Version>
</PropertyGroup>

<ItemGroup>
<Content Include="**/*.schema" />
<Content Include="**/*.uischema" />
<None Include="exported/**/*.*" Pack="true" PackagePath="exported" />
<None Include="icon.png">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="README.md" Condition="Exists('README.md')" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Bot.Builder.Dialogs.Adaptive" Version="4.13.2" />
</ItemGroup>

<PropertyGroup>
<GenerateNuspecDependsOn>$(GenerateNuspecDependsOn);SetPackageVersion</GenerateNuspecDependsOn>
</PropertyGroup>

<Target Name="SetPackageVersion" DependsOnTargets="Build">
<PropertyGroup>
<!-- <PackageVersion>$([System.DateTime]::Now.ToString(&quot;yyyy.MM.dd.HHmmss&quot;))</PackageVersion> -->
<!-- You can customize the format and the rule about how version increases here. -->
<PackageVersion>$([System.DateTime]::Now.ToString("1.MM.dd.HHmmss-preview"))</PackageVersion>
</PropertyGroup>
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "https://schemas.botframework.com/schemas/component/v1.0/component.schema",
"$role": "implements(Microsoft.IDialog)",
"title": "Control form",
"description": "Control properties being asked in form.",
"type": "object",
"required": [],
"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"
]
},
"askFirst": {
"$ref": "schema:#/definitions/arrayExpression",
"title": "Ask first properties",
"description": "List of properties to add to the front of dialog.requiredProperties."
},
"askLast": {
"$ref": "schema:#/definitions/arrayExpression",
"title": "Ask last properties",
"description": "List of properties to put at the end of dialog.requiredProperties."
},
"noAsk": {
"$ref": "schema:#/definitions/arrayExpression",
"title": "No ask properties",
"description": "List of properties to remove from dialog.requiredProperties."
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://schemas.botframework.com/schemas/ui/v1.0/ui.schema",
"form": {
"label": "Control form",
"subtitle": "Control form"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Threading.Tasks;
using Microsoft.Bot.Builder.AI.Luis;
using Microsoft.Bot.Builder.AI.Luis.Testing;
using Microsoft.Extensions.Configuration;
using Xunit;

namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
{
public class FormTests : IClassFixture<ResourceExplorerFixture>
{
private readonly string assignEntityDirectory = PathUtils.NormalizePath(@"..\..\..\..\tests\tests\FormTests\");
private readonly IConfiguration _configuration;
private readonly ResourceExplorerFixture _resourceExplorerFixture;

public FormTests(ResourceExplorerFixture resourceExplorerFixture)
{
_resourceExplorerFixture = resourceExplorerFixture.Initialize(nameof(FormTests));

_configuration = new ConfigurationBuilder()
.UseMockLuisSettings(assignEntityDirectory, "TestBot")
.Build();

_resourceExplorerFixture.ResourceExplorer
.RegisterType(LuisAdaptiveRecognizer.Kind, typeof(MockLuisRecognizer), new MockLuisLoader(_configuration));
}

[Fact]
public async Task ControlForm()
{
await TestUtils.RunTestScript(_resourceExplorerFixture.ResourceExplorer);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;

namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
{
public class ResourceExplorerFixture : IDisposable
{
private string _folderPath = string.Empty;

public ResourceExplorerFixture()
{
ResourceExplorer = new ResourceExplorer();
}

public ResourceExplorer ResourceExplorer { get; private set; }

public ResourceExplorerFixture Initialize(string resourceFolder)
{
if (_folderPath.Length == 0)
{
_folderPath = Path.Combine(TestUtils.GetProjectPath(), "Tests", resourceFolder);
ResourceExplorer = ResourceExplorer.AddFolder(_folderPath, monitorChanges: false);
}

return this;
}

public void Dispose()
{
_folderPath = string.Empty;
ResourceExplorer.Dispose();
}
}
}
55 changes: 55 additions & 0 deletions experimental/generation/Microsoft.Form.Extensions/Tests/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Bot.Builder.AI.Luis;
using Microsoft.Bot.Builder.AI.QnA;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Adaptive;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Testing;
using Microsoft.Bot.Builder.Dialogs.Declarative;
using Microsoft.Bot.Builder.Dialogs.Declarative.Obsolete;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Form.Extensions;
using Newtonsoft.Json.Serialization;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

[assembly: TestFramework("Microsoft.Bot.Builder.Integration.ApplicationInsights.Core.Tests.Startup", "Tests")]

namespace Microsoft.Bot.Builder.Integration.ApplicationInsights.Core.Tests
{
public class Startup : XunitTestFramework
{
public Startup(IMessageSink messageSink)
: base(messageSink)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false);

Configuration = builder.Build();

/* TODO: This does not work until TestAdapter is updated
Services = new ServiceCollection();
Services.AddSingleton(Configuration);
new DialogsBotComponent().ConfigureServices(Services, Configuration);
new AdaptiveBotComponent().ConfigureServices(Services, Configuration);
new LanguageGenerationBotComponent().ConfigureServices(Services, Configuration);
new LuisBotComponent().ConfigureServices(Services, Configuration);
new QnAMakerBotComponent().ConfigureServices(Services, Configuration);
new FormExtensionsBotComponent().ConfigureServices(Services, Configuration);
*/
ComponentRegistration.Add(new DialogsComponentRegistration());
ComponentRegistration.Add(new DeclarativeComponentRegistration());
ComponentRegistration.Add(new AdaptiveComponentRegistration());
ComponentRegistration.Add(new LanguageGenerationComponentRegistration());
ComponentRegistration.Add(new AdaptiveTestingComponentRegistration());
ComponentRegistration.Add(new LuisComponentRegistration());
ComponentRegistration.Add(new QnAMakerComponentRegistration());
ComponentRegistration.Add(new DeclarativeComponentRegistrationBridge<FormExtensionsBotComponent>());
}

public IConfiguration Configuration { get; }
}
}
Loading

0 comments on commit b1e7a40

Please sign in to comment.