forms: make IField.Validate and IStep.Process async Task methods
This commit is contained in:
Родитель
2862b2ea6a
Коммит
2d18fc7447
|
@ -37,9 +37,12 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form.Advanced
|
||||
{
|
||||
#pragma warning disable CS1998
|
||||
|
||||
/// <summary>Base class with declarative implementation of <see cref="IField{T}"/>. </summary>
|
||||
/// <typeparam name="T">Underlying form state.</typeparam>
|
||||
public class Field<T> : IField<T>
|
||||
|
@ -199,7 +202,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
return _prompt;
|
||||
}
|
||||
|
||||
public virtual string Validate(T state, object value)
|
||||
public virtual Task<string> ValidateAsync(T state, object value)
|
||||
{
|
||||
return _validate(state, value);
|
||||
}
|
||||
|
@ -382,7 +385,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
protected bool _keepZero;
|
||||
protected string _description;
|
||||
protected Prompt _help;
|
||||
protected ValidateDelegate<T> _validate = new ValidateDelegate<T>((state, value) => null);
|
||||
protected ValidateDelegate<T> _validate = new ValidateDelegate<T>(async (state, value) => null);
|
||||
protected string[] _terms = new string[0];
|
||||
protected Dictionary<object, string> _valueDescriptions = new Dictionary<object, string>();
|
||||
protected Dictionary<object, string[]> _valueTerms = new Dictionary<object, string[]>();
|
||||
|
|
|
@ -154,7 +154,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
|
||||
IFormBuilder<T> IFormBuilder<T>.Confirm(Prompt prompt, ConditionalDelegate<T> condition, IEnumerable<string> dependencies)
|
||||
{
|
||||
if (condition == null) condition = (state) => true;
|
||||
if (condition == null) condition = state => true;
|
||||
if (dependencies == null)
|
||||
{
|
||||
// Default next steps go from previous field ignoring confirmations back to next confirmation
|
||||
|
@ -200,7 +200,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return this;
|
||||
}
|
||||
|
||||
IFormBuilder<T> IFormBuilder<T>.OnCompletion(CompletionDelegate<T> callback)
|
||||
IFormBuilder<T> IFormBuilder<T>.OnCompletionAsync(CompletionDelegate<T> callback)
|
||||
{
|
||||
_form._completion = callback;
|
||||
return this;
|
||||
|
|
|
@ -43,6 +43,8 @@ using Microsoft.Bot.Builder.Form.Advanced;
|
|||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
#pragma warning disable CS1998
|
||||
|
||||
internal static class FormStatics
|
||||
{
|
||||
#region IForm<T> statics
|
||||
|
@ -175,7 +177,9 @@ namespace Microsoft.Bot.Builder.Form
|
|||
// 1) Go through them while supporting only quit or back and reset
|
||||
// 2) Drop them
|
||||
// 3) Just pick one (found in form.StepState, but that is opaque here)
|
||||
step.Process(context, _state, _formState, input, matches, out feedback, out prompt);
|
||||
var result = await step.ProcessAsync(context, _state, _formState, input, matches);
|
||||
feedback = result.Feedback;
|
||||
prompt = result.Prompt;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -269,7 +273,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
matches = MatchAnalyzer.Coalesce(matches, lastInput).ToArray();
|
||||
if (MatchAnalyzer.IsFullMatch(lastInput, matches))
|
||||
{
|
||||
next = step.Process(context, _state, _formState, lastInput, matches, out feedback, out prompt);
|
||||
var result = await step.ProcessAsync(context, _state, _formState, lastInput, matches);
|
||||
next = result.Next;
|
||||
feedback = result.Feedback;
|
||||
prompt = result.Prompt;
|
||||
|
||||
// 1) Not completed, not valid -> Not require, last
|
||||
// 2) Completed, feedback -> require, not last
|
||||
requirePrompt = (_formState.Phase() == StepPhase.Completed);
|
||||
|
@ -304,7 +312,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
var bestMatch = MatchAnalyzer.BestMatches(matches, commands);
|
||||
if (bestMatch == 0)
|
||||
{
|
||||
next = step.Process(context, _state, _formState, lastInput, matches, out feedback, out prompt);
|
||||
var result = await step.ProcessAsync(context, _state, _formState, lastInput, matches);
|
||||
next = result.Next;
|
||||
feedback = result.Feedback;
|
||||
prompt = result.Prompt;
|
||||
|
||||
requirePrompt = (_formState.Phase() == StepPhase.Completed);
|
||||
useLastPrompt = !requirePrompt;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form.Advanced
|
||||
{
|
||||
|
@ -334,7 +335,7 @@ namespace Microsoft.Bot.Builder.Form.Advanced
|
|||
/// One way to control this is to supply a <see cref="ValidationDelegate"/> to the
|
||||
/// <see cref="IForm<T>.Field"/> or <see cref="IForm<T>.Confirm"/> steps.
|
||||
/// </remarks>
|
||||
string Validate(T state, object value);
|
||||
Task<string> ValidateAsync(T state, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Return the help description for this field.
|
||||
|
|
|
@ -185,6 +185,6 @@ namespace Microsoft.Bot.Builder.Form
|
|||
/// the form state results. In any case the completed form state will be passed
|
||||
/// to the parent dialog.
|
||||
/// </remarks>
|
||||
IFormBuilder<T> OnCompletion(CompletionDelegate<T> callback);
|
||||
IFormBuilder<T> OnCompletionAsync(CompletionDelegate<T> callback);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
|
@ -53,7 +54,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
/// <param name="state">Form state to test.</param>
|
||||
/// <param name="value">Response value to validate.</param>
|
||||
/// <returns>Null if value is valid otherwise feedback on what is wrong.</returns>
|
||||
public delegate string ValidateDelegate<T>(T state, object value);
|
||||
public delegate Task<string> ValidateDelegate<T>(T state, object value);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate called when a form is completed.
|
||||
|
@ -66,7 +67,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
/// such as sending it to your service. It cannot be used to create a new
|
||||
/// dialog or return a value to the parent dialog.
|
||||
/// </remarks>
|
||||
public delegate void CompletionDelegate<T>(IDialogContext context, T state);
|
||||
public delegate Task CompletionDelegate<T>(IDialogContext context, T state);
|
||||
|
||||
/// <summary>
|
||||
/// Interface for controlling the form dialog created.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
//
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
|
||||
|
@ -39,6 +40,21 @@ namespace Microsoft.Bot.Builder.Form
|
|||
{
|
||||
internal enum StepPhase { Ready, Responding, Completed };
|
||||
internal enum StepType { Field, Confirm, Navigation, Message };
|
||||
|
||||
internal struct StepResult
|
||||
{
|
||||
internal StepResult(NextStep next, string feedback, string prompt)
|
||||
{
|
||||
this.Next = next;
|
||||
this.Feedback = feedback;
|
||||
this.Prompt = prompt;
|
||||
}
|
||||
|
||||
internal NextStep Next { get; set; }
|
||||
internal string Feedback { get; set; }
|
||||
internal string Prompt { get; set; }
|
||||
}
|
||||
|
||||
internal interface IStep<T>
|
||||
{
|
||||
string Name { get; }
|
||||
|
@ -53,8 +69,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
|
||||
IEnumerable<TermMatch> Match(IDialogContext context, T state, FormState form, string input, out string lastInput);
|
||||
|
||||
NextStep Process(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches,
|
||||
out string feedback, out string prompt);
|
||||
Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches);
|
||||
|
||||
string NotUnderstood(IDialogContext context, T state, FormState form, string input);
|
||||
|
||||
|
|
|
@ -37,9 +37,12 @@ using System.Diagnostics;
|
|||
using System.Linq;
|
||||
|
||||
using Microsoft.Bot.Builder.Form.Advanced;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Bot.Builder.Form
|
||||
{
|
||||
#pragma warning disable CS1998
|
||||
|
||||
internal class FieldStep<T> : IStep<T>
|
||||
{
|
||||
public FieldStep(string name, IForm<T> form)
|
||||
|
@ -115,11 +118,12 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return matches;
|
||||
}
|
||||
|
||||
public NextStep Process(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches,
|
||||
out string feedback, out string prompt)
|
||||
|
||||
|
||||
public async Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches)
|
||||
{
|
||||
feedback = null;
|
||||
prompt = null;
|
||||
string feedback = null;
|
||||
string prompt = null;
|
||||
var iprompt = _field.Prompt();
|
||||
var fieldState = form.StepState as FieldStepState;
|
||||
object response = null;
|
||||
|
@ -129,7 +133,8 @@ namespace Microsoft.Bot.Builder.Form
|
|||
var firstMatch = matches.FirstOrDefault();
|
||||
if (matches.Count() == 1)
|
||||
{
|
||||
response = SetValue(state, firstMatch.Value, form, out feedback);
|
||||
response = firstMatch.Value;
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
}
|
||||
else if (matches.Count() > 1)
|
||||
{
|
||||
|
@ -187,12 +192,14 @@ namespace Microsoft.Bot.Builder.Form
|
|||
{
|
||||
if (_field.AllowsMultiple())
|
||||
{
|
||||
response = SetValue(state, settled, form, out feedback);
|
||||
response = settled;
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(settled.Count() == 1);
|
||||
response = SetValue(state, settled.First(), form, out feedback);
|
||||
response = settled.First();
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -231,12 +238,14 @@ namespace Microsoft.Bot.Builder.Form
|
|||
// No clarification left, so set the field
|
||||
if (_field.AllowsMultiple())
|
||||
{
|
||||
response = SetValue(state, fieldState.Settled, form, out feedback);
|
||||
response = fieldState.Settled;
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(fieldState.Settled.Count() == 1);
|
||||
response = SetValue(state, fieldState.Settled.First(), form, out feedback);
|
||||
response = fieldState.Settled.First();
|
||||
feedback = await SetValueAsync(state, response, form);
|
||||
}
|
||||
form.SetPhase(StepPhase.Completed);
|
||||
}
|
||||
|
@ -257,7 +266,8 @@ namespace Microsoft.Bot.Builder.Form
|
|||
}
|
||||
}
|
||||
}
|
||||
return _field.Next(response, state);
|
||||
var next = _field.Next(response, state);
|
||||
return new StepResult(next, feedback, prompt);
|
||||
}
|
||||
|
||||
public string NotUnderstood(IDialogContext context, T state, FormState form, string input)
|
||||
|
@ -351,16 +361,16 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return value;
|
||||
}
|
||||
|
||||
private object SetValue(T state, object value, FormState form, out string feedback)
|
||||
private async Task<string> SetValueAsync(T state, object value, FormState form)
|
||||
{
|
||||
var desc = _field.Form.Fields.Field(_name);
|
||||
feedback = desc.Validate(state, value);
|
||||
var feedback = await desc.ValidateAsync(state, value);
|
||||
if (feedback == null)
|
||||
{
|
||||
SetValue(state, value);
|
||||
form.SetPhase(StepPhase.Completed);
|
||||
}
|
||||
return value;
|
||||
return feedback;
|
||||
}
|
||||
|
||||
private IPrompt<T> NextClarifyPrompt(T state, FieldStepState stepState, IRecognize<T> recognizer, out Ambiguous clarify)
|
||||
|
@ -471,16 +481,13 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return prompter.Prompt(state, "", input);
|
||||
}
|
||||
|
||||
public NextStep Process(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches,
|
||||
out string feedback,
|
||||
out string prompt)
|
||||
public async Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches)
|
||||
{
|
||||
feedback = null;
|
||||
prompt = null;
|
||||
var value = matches.First().Value;
|
||||
form.StepState = null;
|
||||
form.SetPhase((bool)value ? StepPhase.Completed : StepPhase.Ready);
|
||||
return _field.Next(value, state);
|
||||
var next = _field.Next(value, state);
|
||||
return new StepResult(next, feedback: null, prompt: null);
|
||||
}
|
||||
|
||||
public string Start(IDialogContext context, T state, FormState form)
|
||||
|
@ -575,14 +582,11 @@ namespace Microsoft.Bot.Builder.Form
|
|||
return new Prompter<T>(template, _form, null).Prompt(state, _name, input);
|
||||
}
|
||||
|
||||
public NextStep Process(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches,
|
||||
out string feedback,
|
||||
out string prompt)
|
||||
public async Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches)
|
||||
{
|
||||
feedback = null;
|
||||
prompt = null;
|
||||
form.Next = null;
|
||||
return new NextStep(new string[] { matches.First().Value as string });
|
||||
var next = new NextStep(new string[] { matches.First().Value as string });
|
||||
return new StepResult(next, feedback: null, prompt: null);
|
||||
}
|
||||
|
||||
public string Start(IDialogContext context, T state, FormState form)
|
||||
|
@ -671,7 +675,7 @@ namespace Microsoft.Bot.Builder.Form
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public NextStep Process(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches, out string feedback, out string prompt)
|
||||
public Task<StepResult> ProcessAsync(IDialogContext context, T state, FormState form, string input, IEnumerable<TermMatch> matches)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ using AnnotatedSandwichOrder = Microsoft.Bot.Sample.AnnotatedSandwichBot.Sandwic
|
|||
|
||||
namespace Microsoft.Bot.Builder.FormTest
|
||||
{
|
||||
#pragma warning disable CS1998
|
||||
|
||||
public enum DebugOptions
|
||||
{
|
||||
None, AnnotationsAndNumbers, AnnotationsAndNoNumbers, NoAnnotations, NoFieldOrder,
|
||||
|
@ -114,7 +116,7 @@ namespace Microsoft.Bot.Builder.FormTest
|
|||
|
||||
.Message("What we have is a {?{Signature} signature pizza} {?{GourmetDelite} gourmet pizza} {?{Stuffed} {&Stuffed}} {?{?{BYO.Crust} {&BYO.Crust}} {?{BYO.Sauce} {&BYO.Sauce}} {?{BYO.Toppings}}} pizza")
|
||||
.Field("DeliveryAddress", validate:
|
||||
(state, value) =>
|
||||
async (state, value) =>
|
||||
{
|
||||
string feedback = null;
|
||||
var str = value as string;
|
||||
|
@ -129,7 +131,7 @@ namespace Microsoft.Bot.Builder.FormTest
|
|||
.Confirm("Would you like a {Size}, {&Signature} {Signature} pizza delivered to {DeliveryAddress}?", isSignature, dependencies: new string[] { "Size", "Kind", "Signature" })
|
||||
.Confirm("Would you like a {Size}, {&GourmetDelite} {GourmetDelite} pizza delivered to {DeliveryAddress}?", isGourmet)
|
||||
.Confirm("Would you like a {Size}, {&Stuffed} {Stuffed} pizza delivered to {DeliveryAddress}?", isStuffed)
|
||||
.OnCompletion((session, pizza) => Console.WriteLine("{0}", pizza))
|
||||
.OnCompletionAsync(async (session, pizza) => Console.WriteLine("{0}", pizza))
|
||||
.Build();
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче