Composable type loader first iteration. Still pending:

+Activities from text
+Lots of negative tests (and their error messages)
+ Dispatch dialog fix (doesn't work as-is)
+FormFlow test
+Change $type to @type
+Plugin model
+Recognizers converter
+further schema validation
This commit is contained in:
Carlos Castro 2019-01-07 10:13:33 -08:00
Родитель 4d0c58fd9e
Коммит 7353c5642d
22 изменённых файлов: 643 добавлений и 7 удалений

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

@ -82,9 +82,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Flows.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Flow.Tests\Microsoft.Bot.Builder.Dialogs.Flows.Tests.csproj", "{0027D023-2E4C-4298-81A0-DC8BA8577116}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Expressions", "libraries\Microsoft.CommonExpressions\Microsoft.Expressions.csproj", "{68899A42-6375-43BC-95BB-2E4798D2C0EB}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Expressions", "libraries\Microsoft.CommonExpressions\Microsoft.Expressions.csproj", "{68899A42-6375-43BC-95BB-2E4798D2C0EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Expressions.Tests", "tests\Microsoft.Expressions.Tests\Microsoft.Expressions.Tests.csproj", "{A7637631-40EF-4E8D-A90A-422A5FD593D1}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Expressions.Tests", "tests\Microsoft.Expressions.Tests\Microsoft.Expressions.Tests.csproj", "{A7637631-40EF-4E8D-A90A-422A5FD593D1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Flow.Loader", "libraries\Microsoft.Bot.Builder.Dialogs.Flow.Loader\Microsoft.Bot.Builder.Dialogs.Flow.Loader.csproj", "{1BC05915-044E-4776-8956-B44BBEFF2F84}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Bot.Builder.Dialogs.Flow.Loader.Tests", "tests\Microsoft.Bot.Builder.Dialogs.Flow.Loader.Tests\Microsoft.Bot.Builder.Dialogs.Flow.Loader.Tests.csproj", "{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -391,6 +395,22 @@ Global
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Documentation|Any CPU.Build.0 = Debug|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D790A4BB-D8AC-4AAE-B3FE-0CF432CA8031}.Release|Any CPU.Build.0 = Release|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Documentation|Any CPU.ActiveCfg = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Documentation|Any CPU.Build.0 = Debug|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1BC05915-044E-4776-8956-B44BBEFF2F84}.Release|Any CPU.Build.0 = Release|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Debug - NuGet Packages|Any CPU.ActiveCfg = Debug|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Debug - NuGet Packages|Any CPU.Build.0 = Debug|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Documentation|Any CPU.ActiveCfg = Debug|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Documentation|Any CPU.Build.0 = Debug|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -437,6 +457,8 @@ Global
{0027D023-2E4C-4298-81A0-DC8BA8577116} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{68899A42-6375-43BC-95BB-2E4798D2C0EB} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{A7637631-40EF-4E8D-A90A-422A5FD593D1} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
{1BC05915-044E-4776-8956-B44BBEFF2F84} = {4269F3C3-6B42-419B-B64A-3E6DC0F1574A}
{CD473E17-DA3C-4E64-9AC6-31B4FB6D69E7} = {AD743B78-D61F-4FBF-B620-FA83CE599A50}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7173C9F3-A7F9-496E-9078-9156E35D6E16}

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

@ -11,7 +11,6 @@
<PackageReference Include="Microsoft.Recognizers.Text.DateTime" Version="1.1.3" />
<PackageReference Include="Microsoft.Recognizers.Text.Number" Version="1.1.3" />
<PackageReference Include="Microsoft.Recognizers.Text.Sequence" Version="1.1.3" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Schema.NET" Version="3.5.0" />
</ItemGroup>

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

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Flow;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Contract
{
public class DialogInfo
{
public string Id { get; set; }
public IDialog Dialog { get; set; }
public List<IDialogCommand> Commands { get; set; }
}
}

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

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Contract
{
public class DialogsInfo
{
[JsonProperty(PropertyName = "initialNodeId")]
public string InitialNodeId { get; set; }
[JsonProperty(PropertyName = "Dialogs")]
public List<DialogInfo> Dialogs { get; set; }
}
}

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

@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Composition;
using Microsoft.Bot.Builder.Dialogs.Composition.Expressions;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Converters
{
public class ActivityConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IActivity);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.ValueType == typeof(string))
{
// If we expect an activity but find text, it is a short expression for a message activity
return new Activity()
{
Type = ActivityTypes.Message,
Text = (string)reader.Value
};
}
return serializer.Deserialize<Activity>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
}

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

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs.Flow;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Converters
{
public class DialogCommandConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IDialogCommand);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var typeName = jsonObject["$type"].ToString();
var dialogCommand = Factory.Build<IDialogCommand>(typeName, jsonObject, serializer);
return dialogCommand;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
}

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

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Composition;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Converters
{
public class DialogConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IDialog);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var dialog = default(IDialog);
var typeName = jsonObject["$type"].ToString();
IDialog dialogCommand = Factory.Build<IDialog>(typeName, jsonObject, serializer);
return dialogCommand;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
}

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

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Dialogs.Composition;
using Microsoft.Bot.Builder.Dialogs.Composition.Expressions;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Converters
{
public class ExpressionConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(IExpressionEval);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.ValueType == typeof(string))
{
return new CommonExpression((string)reader.Value);
}
throw new JsonSerializationException("Expected string expression.");
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
}

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

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Contract;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Converters;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader
{
public static class DialogLoader
{
public static IDialog Load(string json)
{
var flowInfo = JsonConvert.DeserializeObject<DialogsInfo>(
json, new JsonSerializerSettings()
{
Binder = new Binder(),
TypeNameHandling = TypeNameHandling.Auto,
Converters = new List<JsonConverter>()
{
new DialogCommandConverter(),
new DialogConverter(),
new ExpressionConverter(),
new ActivityConverter()
}
});
var rootDialog = new ComponentDialog()
{
Id = Guid.NewGuid().ToString(),
InitialDialogId = flowInfo.InitialNodeId
};
foreach (var flowNodeInfo in flowInfo.Dialogs)
{
rootDialog.AddDialog(flowNodeInfo.Dialog);
var flowDialog = new FlowDialog()
{
Id = flowNodeInfo.Id,
DialogId = flowNodeInfo.Dialog.Id,
OnCompleted = new CommandSet()
{
Commands = flowNodeInfo.Commands
}
};
rootDialog.AddDialog(flowDialog);
}
return rootDialog;
}
}
}

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

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Loaders
{
public class DefaultLoader : ILoader
{
public object Load(JObject obj, JsonSerializer serializer, Type type)
{
return obj.ToObject(type, serializer);
}
}
}

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

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Loaders
{
public interface ILoader
{
object Load(JObject obj, JsonSerializer serializer, Type type);
}
}

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

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs.Composition.Expressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Loaders
{
public class SetVariableCommandLoader : ILoader
{
public object Load(JObject obj, JsonSerializer serializer, Type type)
{
// Allow expressions for the Value property in string format directly
// Example:
// "Name": "Age",
// "Value": "DialogTurnResult.Result"
if (obj["Value"].Type == JTokenType.String)
{
return new SetVariable()
{
Name = obj.Value<string>("Name"),
Value = new CommonExpression(obj.Value<string>("Value"))
};
}
return obj.ToObject(type, serializer);
}
}
}

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

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\libraries\Microsoft.Bot.Builder.Dialogs.Composition\Microsoft.Bot.Builder.Dialogs.Composition.csproj" />
<ProjectReference Include="..\libraries\Microsoft.Bot.Builder.Dialogs.Flow\Microsoft.Bot.Builder.Dialogs.Flow.csproj" />
<ProjectReference Include="..\libraries\Microsoft.Bot.Builder.Dialogs\Microsoft.Bot.Builder.Dialogs.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System">
<HintPath>System</HintPath>
</Reference>
</ItemGroup>
</Project>

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

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json.Serialization;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types
{
public class Binder : DefaultSerializationBinder
{
public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = Factory.NameFromType(serializedType).ToString();
if (string.IsNullOrEmpty(typeName))
{
base.BindToName(serializedType, out assemblyName, out typeName);
}
}
public override Type BindToType(string assemblyName, string typeName)
{
var type = Factory.TypeFromName(typeName);
if (type != default(Type))
{
return type;
}
return base.BindToType(assemblyName, typeName);
}
}
}

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

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Loaders;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Flow.Loader.Types
{
public static class Factory
{
private static Dictionary<Type, ILoader> builders = new Dictionary<Type, ILoader>();
private static Dictionary<string, Type> types = new Dictionary<string, Type>();
private static Dictionary<Type, string> names = new Dictionary<Type, string>();
static Factory()
{
//TODO: we don't want this static initialization, leaving it here for convenience now
// while things are changing rapidly still
// Commands
Register("http://schemas.botframework.com/SetVariable", typeof(SetVariable), new SetVariableCommandLoader());
Register("http://schemas.botframework.com/Switch", typeof(Switch));
Register("http://schemas.botframework.com/CallDialog", typeof(CallDialog));
Register("http://schemas.botframework.com/SendActivity", typeof(SendActivity));
// Dialogs
Register("http://schemas.botframework.com/TextPrompt", typeof(TextPrompt));
Register("http://schemas.botframework.com/IntNumberPrompt", typeof(NumberPrompt<Int32>));
// Recognizers
}
public static void Register(string name, Type type, ILoader loader = null)
{
// Default loader if none specified
if (loader == null)
{
loader = new DefaultLoader();
}
types.Add(name, type);
names.Add(type, name);
builders.Add(type, loader);
}
public static T Build<T>(string name, JObject obj, JsonSerializer serializer) where T : class
{
ILoader builder;
var type = TypeFromName(name);
var found = builders.TryGetValue(type, out builder);
if (!found)
{
// TODO: This is probably too rigid. If there is a type we don't know, consider just ignoring
// or a smarter strategy. Write a test for this scenario and revisit this code.
throw new ArgumentException($"Type {name} not registered in factory.");
}
var built = builder.Load(obj, serializer, type);
var result = built as T;
if (result == null)
{
throw new Exception($"Factory registration for name {name} resulted in type {built.GetType()}, but expected assignable to {typeof(T)}");
}
return result;
}
public static Type TypeFromName(string name)
{
Type type;
return types.TryGetValue(name, out type) ? type : default(Type);
}
public static string NameFromType(Type type)
{
string name;
return names.TryGetValue(type, out name) ? name: default(string);
}
// Plugins
//public static void Register<T>(string friendlyName, Type dotnetType, Assembly assembly, Func<JsonReader, JsonSerializer, T> builder)
//{
//}
}
}

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

@ -123,13 +123,18 @@ namespace Microsoft.Bot.Builder.Dialogs
await OnEndDialogAsync(turnContext, instance, reason, cancellationToken).ConfigureAwait(false);
}
public ComponentDialog AddDialog(Dialog dialog)
{
return AddDialog(dialog as IDialog);
}
/// <summary>
/// Adds a dialog to the component dialog.
/// </summary>
/// <param name="dialog">The dialog to add.</param>
/// <returns>The updated <see cref="ComponentDialog"/>.</returns>
/// <remarks>Adding a new dialog will inherit the <see cref="IBotTelemetryClient"/> of the ComponentDialog.</remarks>
public ComponentDialog AddDialog(Dialog dialog)
public ComponentDialog AddDialog(IDialog dialog)
{
if (string.IsNullOrEmpty(dialog.Id))
{

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

@ -15,7 +15,7 @@ namespace Microsoft.Bot.Builder.Dialogs
where T : struct
{
public NumberPrompt(string dialogId = nameof(NumberPrompt<T>), PromptValidator<T> validator = null, string defaultLocale = null)
: base(dialogId, validator)
: base(dialogId ?? nameof(NumberPrompt<T>), validator)
{
DefaultLocale = defaultLocale;
}

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

@ -12,7 +12,7 @@ namespace Microsoft.Bot.Builder.Dialogs
public class TextPrompt : Prompt<string>
{
public TextPrompt(string dialogId = nameof(TextPrompt), PromptValidator<string> validator = null)
: base(dialogId, validator)
: base(dialogId ?? nameof(TextPrompt), validator)
{
}

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

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
using Microsoft.Bot.Builder.Dialogs.Composition.Expressions;
using Microsoft.Bot.Builder.Dialogs.Flow;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Converters;
using Microsoft.Bot.Builder.Dialogs.Flow.Loader.Contract;
using Microsoft.Bot.Schema;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
namespace Microsoft.Bot.Builder.Dialogs.Loader.Tests
{
[TestClass]
public class JsonLoadTests
{
[TestMethod]
public async Task JsonDialogLoad_ChainedPromptsAndSelfLoop()
{
string json = File.ReadAllText("TestFlows/ChainedPromptsAndSwitch.json");
var dialog = DialogLoader.Load(json);
var convoState = new ConversationState(new MemoryStorage());
var dialogState = convoState.CreateProperty<DialogState>("dialogState");
var adapter = new TestAdapter()
.Use(new TranscriptLoggerMiddleware(new TraceTranscriptLogger()))
.Use(new AutoSaveStateMiddleware(convoState));
var dialogs = new DialogSet(dialogState);
dialogs.Add(dialog);
await new TestFlow(adapter, async (turnContext, cancellationToken) =>
{
var state = await dialogState.GetAsync(turnContext, () => new DialogState());
var dialogContext = await dialogs.CreateContextAsync(turnContext, cancellationToken);
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
if (results.Status == DialogTurnStatus.Empty)
results = await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
})
.Send("hello")
.AssertReply("What is your name?")
.Send("x")
.AssertReply("What is your name?")
.Send("Joe")
.AssertReply("What is your age?")
.Send("64")
.AssertReply("Done")
.StartTestAsync();
}
}
}

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

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
<PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Composition\Microsoft.Bot.Builder.Dialogs.Composition.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Flow.Loader\Microsoft.Bot.Builder.Dialogs.Flow.Loader.csproj" />
<ProjectReference Include="..\..\libraries\Microsoft.Bot.Builder.Dialogs.Flow\Microsoft.Bot.Builder.Dialogs.Flow.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="TestFlows\ChainedPromptsAndSwitch.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

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

@ -0,0 +1,81 @@
{
"initialNodeId": "NameFlowNode",
"Dialogs": [
{
"Id": "NameFlowNode",
"Dialog": {
"$type": "http://schemas.botframework.com/TextPrompt",
"Id": "NamePrompt",
"DefaultOptions": {
"Prompt": {
"type": "message",
"text": "What is your name?"
},
"RetryPrompt": {
"type": "message",
"text": "Reprompt: What is your name?"
}
}
},
"Commands": [
{
"$type": "http://schemas.botframework.com/SetVariable",
"Name": "Name",
"Value": "DialogTurnResult.Result"
},
{
"$type": "http://schemas.botframework.com/Switch",
"IgnoreCase": false,
"Condition": "Name.Length > 2",
"Cases": {
"true": {
"$type": "http://schemas.botframework.com/CallDialog",
"DialogId": "NumberFlowNode"
},
"false": {
"$type": "http://schemas.botframework.com/CallDialog",
"DialogId": "NameFlowNode"
}
},
"DefaultAction": {
"$type": "http://schemas.botframework.com/SendActivity",
"Text": "default"
}
}
]
},
{
"Id": "NumberFlowNode",
"Dialog": {
"$type": "http://schemas.botframework.com/IntNumberPrompt",
"Id": "NumberPrompt",
"DefaultOptions": {
"Prompt": {
"type": "message",
"text": "What is your age?"
},
"RetryPrompt": {
"type": "message",
"text": "Reprompt: What is your age?"
}
}
},
"Commands": [
{
"$type": "http://schemas.botframework.com/SetVariable",
"Name": "Age",
"Value": "DialogTurnResult.Result"
},
{
"$type": "http://schemas.botframework.com/SetVariable",
"Name": "IsChild",
"Value": "Age < 18"
},
{
"$type": "http://schemas.botframework.com/SendActivity",
"Text": "Done"
}
]
}
]
}

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

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp2.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>