This commit is contained in:
LocalizationBuildProcess 2019-08-22 14:03:26 -07:00
Родитель 9e28c78108 589c98e15f
Коммит 72eb746bb6
63 изменённых файлов: 1118 добавлений и 609 удалений

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

@ -55,12 +55,12 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
{
get
{
return InputBindings.TryGetValue(DialogContextState.DialogNames, out string value) ? value : null;
return InputBindings.TryGetValue(DialogContextState.DIALOG_VALUE, out string value) ? value : null;
}
set
{
InputBindings[DialogContextState.DialogNames] = value;
InputBindings[DialogContextState.DIALOG_VALUE] = value;
OutputBinding = value;
}
}

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

@ -41,7 +41,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
// In-memory property that will contain the current items value. Defaults to `dialog.value`.
[JsonProperty("valueProperty")]
public string ValueProperty { get; set; } = DialogContextState.DialogNames;
public string ValueProperty { get; set; } = DialogContextState.DIALOG_VALUE;
// Actions to be run for each of items.
[JsonProperty("actions")]

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

@ -41,7 +41,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
// In-memory property that will contain the current items value. Defaults to `dialog.value`.
[JsonProperty("valueProperty")]
public string ValueProperty { get; set; } = DialogContextState.DialogNames;
public string ValueProperty { get; set; } = DialogContextState.DIALOG_VALUE;
// Actions to be run for each of items.
[JsonProperty("actions")]

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

@ -2,12 +2,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Cache;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.TraceExtensions;
using Microsoft.Bot.Schema;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
@ -16,18 +21,17 @@ using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
{
/// <summary>
/// Action for HttpRequests.
/// Action for performing an HttpRequest.
/// </summary>
public class HttpRequest : DialogAction
{
private static readonly HttpClient Client = new HttpClient();
public HttpRequest(HttpMethod method, string url, string property, Dictionary<string, string> headers = null, JObject body = null, [CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0)
public HttpRequest(HttpMethod method, string url, string inputProperty, Dictionary<string, string> headers = null, JObject body = null, [CallerFilePath] string callerPath = "", [CallerLineNumber] int callerLine = 0)
{
this.RegisterSourceLocation(callerPath, callerLine);
this.Method = method;
this.Url = url ?? throw new ArgumentNullException(nameof(url));
this.Property = property;
this.Headers = headers;
this.Body = body;
}
@ -111,10 +115,15 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
public ResponseTypes ResponseType { get; set; } = ResponseTypes.Json;
/// <summary>
/// Gets or sets bidirectional property for input and output. Example: user.age will be passed in, and user.age will be set when the dialog completes.
/// Gets or sets the property to store the HTTP response in.
/// </summary>
/// <remarks>
/// The result will have 4 properties from the http response:
/// [statusCode|reasonPhrase|content|headers]
/// If the content is json it will be an deserialized object, otherwise it will be a string.
/// </remarks>
/// <value>
/// Property for input and output.
/// Property from the HTTP response.
/// </value>
public string Property
{
@ -125,7 +134,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
set
{
InputBindings[DialogContextState.DialogNames] = value;
OutputBinding = value;
}
}
@ -173,6 +181,12 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
}
}
dynamic traceInfo = new JObject();
traceInfo.request = new JObject();
traceInfo.request.method = this.Method.ToString();
traceInfo.request.url = instanceUrl;
HttpResponseMessage response = null;
switch (this.Method)
@ -184,7 +198,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
}
else
{
response = await Client.PostAsync(instanceUrl, new StringContent(instanceBody.ToString(), Encoding.UTF8, "application/json"));
var postContent = new StringContent(instanceBody.ToString(), Encoding.UTF8, "application/json");
traceInfo.request.content = instanceBody.ToString();
traceInfo.request.headers = JObject.FromObject(postContent?.Headers.ToDictionary(t => t.Key, t => (object)t.Value?.FirstOrDefault()));
response = await Client.PostAsync(instanceUrl, postContent);
}
break;
@ -199,6 +216,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
{
var request = new HttpRequestMessage(new System.Net.Http.HttpMethod("PATCH"), instanceUrl);
request.Content = new StringContent(instanceBody.ToString(), Encoding.UTF8, "application/json");
traceInfo.request.content = instanceBody.ToString();
traceInfo.request.headers = JObject.FromObject(request.Content.Headers.ToDictionary(t => t.Key, t => (object)t.Value?.FirstOrDefault()));
response = await Client.SendAsync(request);
}
@ -211,7 +230,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
}
else
{
response = await Client.PutAsync(instanceUrl, new StringContent(instanceBody.ToString(), Encoding.UTF8, "application/json"));
var putContent = new StringContent(instanceBody.ToString(), Encoding.UTF8, "application/json");
traceInfo.request.content = instanceBody.ToString();
traceInfo.request.headers = JObject.FromObject(putContent.Headers.ToDictionary(t => t.Key, t => (object)t.Value?.FirstOrDefault()));
response = await Client.PutAsync(instanceUrl, putContent);
}
break;
@ -225,37 +247,54 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
break;
}
object result = (object)await response.Content.ReadAsStringAsync();
Result requestResult = new Result(response.Headers)
{
StatusCode = (int)response.StatusCode,
ReasonPhrase = response.ReasonPhrase,
};
object content = (object)await response.Content.ReadAsStringAsync();
switch (this.ResponseType)
{
case ResponseTypes.Activity:
var activity = JsonConvert.DeserializeObject<Activity>((string)result);
var activity = JsonConvert.DeserializeObject<Activity>((string)content);
requestResult.Content = JObject.FromObject(activity);
await dc.Context.SendActivityAsync(activity, cancellationToken: cancellationToken).ConfigureAwait(false);
return await dc.EndDialogAsync(cancellationToken: cancellationToken);
break;
case ResponseTypes.Activities:
var activities = JsonConvert.DeserializeObject<Activity[]>((string)result);
var activities = JsonConvert.DeserializeObject<Activity[]>((string)content);
requestResult.Content = JObject.FromObject(activities);
await dc.Context.SendActivitiesAsync(activities, cancellationToken: cancellationToken).ConfigureAwait(false);
return await dc.EndDialogAsync(cancellationToken: cancellationToken);
break;
case ResponseTypes.Json:
// Try set with JOjbect for further retreiving
try
{
result = JToken.Parse((string)result);
content = JToken.Parse((string)content);
}
catch
{
result = result.ToString();
content = content.ToString();
}
return await dc.EndDialogAsync(result, cancellationToken: cancellationToken);
requestResult.Content = content;
break;
case ResponseTypes.None:
default:
return await dc.EndDialogAsync(cancellationToken: cancellationToken);
break;
}
traceInfo.response = JObject.FromObject(requestResult);
// Write Trace Activity for the http request and response values
await dc.Context.TraceActivityAsync("HttpRequest", (object)traceInfo, valueType: "Microsoft.HttpRequest", label: this.Id).ConfigureAwait(false);
// return the actionResult as the result of this operation
return await dc.EndDialogAsync(result: requestResult, cancellationToken: cancellationToken);
}
private async Task ReplaceJTokenRecursively(DialogContext dc, JToken token)
@ -301,5 +340,48 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
break;
}
}
/// <summary>
/// Result data of the the http operation.
/// </summary>
public class Result
{
public Result()
{
}
public Result(HttpHeaders headers)
{
this.Headers = headers.ToDictionary(t => t.Key, t => t.Value.First());
}
/// <summary>
/// Gets or sets the status code from the response to the http operation.
/// </summary>
/// <value>Response status code.</value>
[JsonProperty("statusCode")]
public int StatusCode { get; set; }
/// <summary>
/// Gets or sets the reason phrase from the response to the http operation.
/// </summary>
/// <value>Response reason phrase.</value>
[JsonProperty("reasonPhrase")]
public string ReasonPhrase { get; set; }
/// <summary>
/// Gets the headers from the response to the http operation.
/// </summary>
/// <value>Response headers.</value>
[JsonProperty("headers")]
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>();
/// <summary>
/// Gets or sets the content body from the response to the http operation.
/// </summary>
/// <value>Response content body.</value>
[JsonProperty("content")]
public object Content { get; set; }
}
}
}

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

@ -37,7 +37,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Actions
set
{
InputBindings[DialogContextState.DialogNames] = value;
InputBindings[DialogContextState.DIALOG_VALUE] = value;
OutputBinding = value;
}
}

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

@ -11,6 +11,8 @@ using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Events;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Selectors;
using Microsoft.Bot.Builder.Dialogs.Debugging;
using Microsoft.Bot.Builder.Expressions;
using Microsoft.Bot.Builder.Expressions.Parser;
using Microsoft.Bot.Schema;
using Newtonsoft.Json.Linq;
using static Microsoft.Bot.Builder.Dialogs.Debugging.DebugSupport;
@ -22,7 +24,9 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
/// </summary>
public class AdaptiveDialog : DialogContainer
{
private const string AdaptiveKey = "adaptiveDialogState";
#pragma warning disable SA1310 // Field should not contain underscore.
private const string ADAPTIVE_KEY = "adaptiveDialogState";
#pragma warning restore SA1310 // Field should not contain underscore.
private readonly string changeKey = Guid.NewGuid().ToString();
@ -63,14 +67,14 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
public virtual List<IOnEvent> Events { get; set; } = new List<IOnEvent>();
/// <summary>
/// Gets or sets a value indicating whether gets or sets the policty to Automatically end the dialog when there are no actions to execute.
/// Gets or sets a value indicating whether to end the dialog when there are no actions to execute.
/// </summary>
/// <remarks>
/// If true, when there are no actions to execute the current dialog will end
/// If false, when there are no actions to execute the current dialog will simply end the turn and still be active.
/// If true, when there are no actions to execute, the current dialog will end
/// If false, when there are no actions to execute, the current dialog will simply end the turn and still be active.
/// </remarks>
/// <value>
/// The policty to Automatically end the dialog when there are no actions to execute.
/// Whether to end the dialog when there are no actions to execute.
/// </value>
public bool AutoEndDialog { get; set; } = true;
@ -115,8 +119,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
EnsureDependenciesInstalled();
var activeDialogState = dc.ActiveDialog.State as Dictionary<string, object>;
activeDialogState[AdaptiveKey] = new AdaptiveDialogState();
var state = activeDialogState[AdaptiveKey] as AdaptiveDialogState;
activeDialogState[ADAPTIVE_KEY] = new AdaptiveDialogState();
var state = activeDialogState[ADAPTIVE_KEY] as AdaptiveDialogState;
// Persist options to dialog state
state.Options = options ?? new Dictionary<string, object>();
@ -169,7 +173,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
public override async Task RepromptDialogAsync(ITurnContext turnContext, DialogInstance instance, CancellationToken cancellationToken = default(CancellationToken))
{
// Forward to current sequence step
var state = (instance.State as Dictionary<string, object>)[AdaptiveKey] as AdaptiveDialogState;
var state = (instance.State as Dictionary<string, object>)[ADAPTIVE_KEY] as AdaptiveDialogState;
if (state.Actions.Any())
{
@ -205,12 +209,12 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
public override DialogContext CreateChildContext(DialogContext dc)
{
var activeDialogState = dc.ActiveDialog.State as Dictionary<string, object>;
var state = activeDialogState[AdaptiveKey] as AdaptiveDialogState;
var state = activeDialogState[ADAPTIVE_KEY] as AdaptiveDialogState;
if (state == null)
{
state = new AdaptiveDialogState();
activeDialogState[AdaptiveKey] = state;
activeDialogState[ADAPTIVE_KEY] = state;
}
if (state.Actions != null && state.Actions.Any())
@ -242,7 +246,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
protected async Task<bool> ProcessEventAsync(SequenceContext sequenceContext, DialogEvent dialogEvent, bool preBubble, CancellationToken cancellationToken = default(CancellationToken))
{
// Save into turn
sequenceContext.State.SetValue(DialogContextState.TurnDialogEvent, dialogEvent);
sequenceContext.State.SetValue(DialogContextState.TURN_DIALOGEVENT, dialogEvent);
// Look for triggered evt
var handled = await this.QueueFirstMatchAsync(sequenceContext, dialogEvent, preBubble, cancellationToken).ConfigureAwait(false);
@ -272,11 +276,11 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
// Recognize utterance
var recognized = await this.OnRecognize(sequenceContext, cancellationToken).ConfigureAwait(false);
sequenceContext.State.SetValue(DialogContextState.TurnRecognized, recognized);
sequenceContext.State.SetValue(DialogContextState.TURN_RECOGNIZED, recognized);
var (name, score) = recognized.GetTopScoringIntent();
sequenceContext.State.SetValue(DialogContextState.TurnTopIntent, name);
sequenceContext.State.SetValue(DialogContextState.TurnTopScore, score);
sequenceContext.State.SetValue(DialogContextState.TURN_TOPINTENT, name);
sequenceContext.State.SetValue(DialogContextState.TURN_TOPSCORE, score);
if (this.Recognizer != null)
{
@ -380,8 +384,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
// Increment turns step count
// This helps dialogs being resumed from an interruption to determine if they
// should re-prompt or not.
var stepCount = sequenceContext.State.GetValue<int>(DialogContextState.TurnStepCount, 0);
sequenceContext.State.SetValue(DialogContextState.TurnStepCount, stepCount + 1);
var stepCount = sequenceContext.State.GetValue<int>(DialogContextState.TURN_STEPCOUNT, 0);
sequenceContext.State.SetValue(DialogContextState.TURN_STEPCOUNT, stepCount + 1);
// Is the step waiting for input or were we cancelled?
if (result.Status == DialogTurnStatus.Waiting || this.GetUniqueInstanceId(sequenceContext) != instanceId)
@ -448,10 +452,53 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
protected async Task<RecognizerResult> OnRecognize(SequenceContext sequenceContext, CancellationToken cancellationToken = default(CancellationToken))
{
var context = sequenceContext.Context;
var noneIntent = new RecognizerResult()
{
Text = context.Activity.Text ?? string.Empty,
Intents = new Dictionary<string, IntentScore>()
{
{ "None", new IntentScore() { Score = 0.0 } }
},
Entities = JObject.Parse("{}")
};
var text = context.Activity.Text;
if (context.Activity.Value != null)
{
var value = JObject.FromObject(context.Activity.Value);
// Check for submission of an adaptive card
if (string.IsNullOrEmpty(text) && value.Property("intent") != null)
{
// Map submitted values to a recognizer result
var recognized = new RecognizerResult() { Text = string.Empty };
foreach (var property in value.Properties())
{
if (property.Name.ToLower() == "intent")
{
recognized.Intents[property.Value.ToString()] = new IntentScore() { Score = 1.0 };
}
else
{
if (recognized.Entities.Property(property.Name) == null)
{
recognized.Entities[property.Name] = new JArray(property.Value);
}
else
{
((JArray)recognized.Entities[property.Name]).Add(property.Value);
}
}
}
return recognized;
}
}
if (Recognizer != null)
{
var result = await Recognizer.RecognizeAsync(context, cancellationToken).ConfigureAwait(false);
// only allow one intent
var topIntent = result.GetTopScoringIntent();
result.Intents.Clear();
@ -460,15 +507,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
}
else
{
return new RecognizerResult()
{
Text = context.Activity.Text ?? string.Empty,
Intents = new Dictionary<string, IntentScore>()
{
{ "None", new IntentScore() { Score = 0.0 } }
},
Entities = JObject.Parse("{}")
};
return noneIntent;
}
}
@ -552,12 +591,12 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive
private SequenceContext ToSequenceContext(DialogContext dc)
{
var activeDialogState = dc.ActiveDialog.State as Dictionary<string, object>;
var state = activeDialogState[AdaptiveKey] as AdaptiveDialogState;
var state = activeDialogState[ADAPTIVE_KEY] as AdaptiveDialogState;
if (state == null)
{
state = new AdaptiveDialogState();
activeDialogState[AdaptiveKey] = state;
activeDialogState[ADAPTIVE_KEY] = state;
}
if (state.Actions == null)

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

@ -40,7 +40,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
protected override Task<InputState> OnRecognizeInput(DialogContext dc)
{
var input = dc.State.GetValue<List<Attachment>>(InputProperty);
var input = dc.State.GetValue<List<Attachment>>(INPUT_PROPERTY);
var first = input.Count > 0 ? input[0] : null;
if (first == null || (string.IsNullOrEmpty(first.ContentUrl) && first.Content == null))
@ -51,10 +51,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
switch (this.OutputFormat)
{
case AttachmentOutputFormat.All:
dc.State.SetValue(InputProperty, input);
dc.State.SetValue(INPUT_PROPERTY, input);
break;
case AttachmentOutputFormat.First:
dc.State.SetValue(InputProperty, first);
dc.State.SetValue(INPUT_PROPERTY, first);
break;
}

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

@ -141,8 +141,8 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
protected override Task<InputState> OnRecognizeInput(DialogContext dc)
{
var input = dc.State.GetValue<object>(InputProperty);
var options = dc.State.GetValue<ChoiceInputOptions>(DialogContextState.DialogOptions);
var input = dc.State.GetValue<object>(INPUT_PROPERTY);
var options = dc.State.GetValue<ChoiceInputOptions>(DialogContextState.DIALOG_OPTIONS);
var choices = options.Choices;
@ -162,10 +162,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
{
case ChoiceOutputFormat.Value:
default:
dc.State.SetValue(InputProperty, foundChoice.Value);
dc.State.SetValue(INPUT_PROPERTY, foundChoice.Value);
break;
case ChoiceOutputFormat.Index:
dc.State.SetValue(InputProperty, foundChoice.Index);
dc.State.SetValue(INPUT_PROPERTY, foundChoice.Index);
break;
}
}

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

@ -49,7 +49,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
protected override Task<InputState> OnRecognizeInput(DialogContext dc)
{
var input = dc.State.GetValue<object>(InputProperty);
var input = dc.State.GetValue<object>(INPUT_PROPERTY);
if (dc.Context.Activity.Type == ActivityTypes.Message)
{
// Recognize utterance
@ -60,7 +60,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
var first = results[0];
if (bool.TryParse(first.Resolution["value"].ToString(), out var value))
{
dc.State.SetValue(InputProperty, value);
dc.State.SetValue(INPUT_PROPERTY, value);
return Task.FromResult(InputState.Valid);
}
else
@ -83,7 +83,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
if (secondAttemptResults.Count > 0)
{
input = secondAttemptResults[0].Resolution.Index == 0;
dc.State.SetValue(InputProperty, input);
dc.State.SetValue(INPUT_PROPERTY, input);
}
else
{

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

@ -22,7 +22,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
protected override Task<InputState> OnRecognizeInput(DialogContext dc)
{
var input = dc.State.GetValue<object>(InputProperty);
var input = dc.State.GetValue<object>(INPUT_PROPERTY);
var culture = GetCulture(dc);
var results = DateTimeRecognizer.RecognizeDateTime(input.ToString(), culture);
@ -36,7 +36,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
result.Add(ReadResolution(value));
}
dc.State.SetValue(InputProperty, result);
dc.State.SetValue(INPUT_PROPERTY, result);
}
else
{

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

@ -43,30 +43,32 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
public enum AllowInterruptions
{
/**
* always consult parent dialogs before taking the input
*/
/// <summary>
/// Always consult parent dialogs before taking the input.
/// </summary>
Always,
/**
* never consult parent dialogs
*/
/// <summary>
/// Never consult parent dialogs.
/// </summary>
Never,
/**
* recognize the input first, only consult parent dilaogs when notRecognized
*/
/// <summary>
/// Recognize the input first, only consult parent dilaogs when notRecognized.
/// </summary>
NotRecognized
}
public abstract class InputDialog : Dialog
{
#pragma warning disable SA1310 // Field should not contain underscore.
public const string TURN_COUNT_PROPERTY = "dialog.turnCount";
public const string INPUT_PROPERTY = "turn.value";
// This property can be set by user's code to indicate that the input should re-process incoming user utterance.
// Designed to be a bool property. So user's code can set this to 'true' to signal the input to re-process incoming user utterance.
public const string ProcessInputProperty = "turn.processInput";
public const string TurnCountProperty = "dialog.turnCount";
public const string InputProperty = "turn.value";
public const string PROCESS_INPUT_PROPERTY = "turn.processInput";
#pragma warning restore SA1310 // Field should not contain underscore.
private const string PersistedOptions = "options";
private const string PersistedState = "state";
@ -78,7 +80,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
public AllowInterruptions AllowInterruptions { get; set; } = AllowInterruptions.NotRecognized;
/// <summary>
/// Gets or sets initial value for the prompt.
/// Gets or sets the initial value for the prompt.
/// </summary>
/// <value>
/// Initial value for the prompt.
@ -91,7 +93,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
}
/// <summary>
/// Gets or sets activity to send to the user.
/// Gets or sets the activity to send to the user.
/// </summary>
/// <value>
/// Activity to send to the user.
@ -99,7 +101,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
public ITemplate<Activity> Prompt { get; set; }
/// <summary>
/// Gets or sets activity template for retrying prompt.
/// Gets or sets the activity template for retrying prompt.
/// </summary>
/// <value>
/// Activity template for retrying prompt.
@ -107,13 +109,19 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
public ITemplate<Activity> UnrecognizedPrompt { get; set; }
/// <summary>
/// Gets or sets activity template to send to the user whenever the value provided is invalid.
/// Gets or sets the activity template to send to the user whenever the value provided is invalid.
/// </summary>
/// <value>
/// Activity template to send to the user whenever the value provided is invalid.
/// </value>
public ITemplate<Activity> InvalidPrompt { get; set; }
/// <summary>
/// Gets or sets the activity template to send when MaxTurnCount has been reached and the default value is used.
/// </summary>
/// <value>The activity template.</value>
public ITemplate<Activity> DefaultValueResponse { get; set; }
public List<string> Validations { get; set; } = new List<string>();
/// <summary>
@ -125,7 +133,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
public int? MaxTurnCount { get; set; }
/// <summary>
/// Gets or sets default value for the input dialog.
/// Gets or sets the default value for the input dialog when MaxTurnCount is exceeded.
/// </summary>
/// <value>
/// Default value for the input dialog.
@ -161,7 +169,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
set
{
InputBindings[DialogContextState.DialogNames] = value;
InputBindings[DialogContextState.DIALOG_VALUE] = value;
OutputBinding = value;
}
}
@ -174,14 +182,14 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
}
var op = OnInitializeOptions(dc, options);
dc.State.SetValue(DialogContextState.DialogOptions, op);
dc.State.SetValue(TurnCountProperty, 0);
dc.State.SetValue(InputProperty, null);
dc.State.SetValue(DialogContextState.DIALOG_OPTIONS, op);
dc.State.SetValue(TURN_COUNT_PROPERTY, 0);
dc.State.SetValue(INPUT_PROPERTY, null);
var state = this.AlwaysPrompt ? InputState.Missing : await this.RecognizeInput(dc);
if (state == InputState.Valid)
{
var input = dc.State.GetValue<object>(InputProperty);
var input = dc.State.GetValue<object>(INPUT_PROPERTY);
return await dc.EndDialogAsync(input);
}
else
@ -189,7 +197,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
// turnCount should increase here, because you want when nextTurn comes in
// We will set the turn count to 1 so the input will not pick from "dialog.value"
// and instead go with "turn.activity.text"
dc.State.SetValue(TurnCountProperty, 1);
dc.State.SetValue(TURN_COUNT_PROPERTY, 1);
return await this.PromptUser(dc, state);
}
}
@ -202,44 +210,50 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
return Dialog.EndOfTurn;
}
var stepCount = dc.State.GetValue<int>(DialogContextState.TurnStepCount, 0);
var stepCount = dc.State.GetValue<int>(DialogContextState.TURN_STEPCOUNT, 0);
if (stepCount > 0)
{
return await this.PromptUser(dc, InputState.Missing);
return await this.PromptUser(dc, InputState.Missing).ConfigureAwait(false);
}
var turnCount = dc.State.GetValue<int>(TurnCountProperty, 0);
var turnCount = dc.State.GetValue<int>(TURN_COUNT_PROPERTY, 0);
// Perform base recognition
var state = await this.RecognizeInput(dc);
if (state == InputState.Valid)
{
var input = dc.State.GetValue<object>(InputProperty);
return await dc.EndDialogAsync(input);
var input = dc.State.GetValue<object>(INPUT_PROPERTY);
return await dc.EndDialogAsync(input).ConfigureAwait(false);
}
else if (this.MaxTurnCount == null || turnCount < this.MaxTurnCount)
{
// increase the turnCount as last step
dc.State.SetValue(TurnCountProperty, turnCount + 1);
return await this.PromptUser(dc, state);
dc.State.SetValue(TURN_COUNT_PROPERTY, turnCount + 1);
return await this.PromptUser(dc, state).ConfigureAwait(false);
}
else
{
if (this.defaultValue != null)
{
var (value, error) = this.defaultValue.TryEvaluate(dc.State);
return await dc.EndDialogAsync(value);
if (this.DefaultValueResponse != null)
{
var response = await this.DefaultValueResponse.BindToData(dc.Context, dc.State).ConfigureAwait(false);
await dc.Context.SendActivityAsync(response).ConfigureAwait(false);
}
return await dc.EndDialogAsync(value).ConfigureAwait(false);
}
}
return await dc.EndDialogAsync();
return await dc.EndDialogAsync().ConfigureAwait(false);
}
public override async Task<DialogTurnResult> ResumeDialogAsync(DialogContext dc, DialogReason reason, object result = null, CancellationToken cancellationToken = default(CancellationToken))
{
return await this.PromptUser(dc, InputState.Missing);
return await this.PromptUser(dc, InputState.Missing).ConfigureAwait(false);
}
protected abstract Task<InputState> OnRecognizeInput(DialogContext dc);
@ -372,7 +386,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
break;
}
return await this.Prompt.BindToData(dc.Context, dc.State);
return await this.Prompt.BindToData(dc.Context, dc.State).ConfigureAwait(false);
}
private async Task<InputState> RecognizeInput(DialogContext dc)
@ -398,13 +412,13 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
if (input == null)
{
var turnCount = dc.State.GetValue<int>(TurnCountProperty);
var processInput = dc.State.GetValue<bool>(ProcessInputProperty, false);
var turnCount = dc.State.GetValue<int>(TURN_COUNT_PROPERTY);
var processInput = dc.State.GetValue<bool>(PROCESS_INPUT_PROPERTY, false);
// Go down this path only if the user has not requested to re-process user input via turn.processInput = true.
if (turnCount == 0 && !processInput)
{
input = dc.State.GetValue<object>(DialogContextState.DialogNames, null);
input = dc.State.GetValue<object>(DialogContextState.DIALOG_VALUE, null);
}
else
{
@ -419,10 +433,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
}
// reset turn.processInput so subsequent actions are not impacted.
dc.State.SetValue(ProcessInputProperty, false);
dc.State.SetValue(PROCESS_INPUT_PROPERTY, false);
}
dc.State.SetValue(InputProperty, input);
dc.State.SetValue(INPUT_PROPERTY, input);
if (input != null)
{
var state = await this.OnRecognizeInput(dc).ConfigureAwait(false);

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

@ -43,7 +43,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
protected override Task<InputState> OnRecognizeInput(DialogContext dc)
{
var input = dc.State.GetValue<object>(InputProperty);
var input = dc.State.GetValue<object>(INPUT_PROPERTY);
var culture = GetCulture(dc);
var results = NumberRecognizer.RecognizeNumber(input.ToString(), culture);
@ -70,10 +70,10 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
{
case NumberOutputFormat.Float:
default:
dc.State.SetValue(InputProperty, input);
dc.State.SetValue(INPUT_PROPERTY, input);
break;
case NumberOutputFormat.Integer:
dc.State.SetValue(InputProperty, Math.Floor((float)input));
dc.State.SetValue(INPUT_PROPERTY, Math.Floor((float)input));
break;
}

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

@ -63,7 +63,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
set
{
InputBindings[DialogContextState.DialogNames] = value;
InputBindings[DialogContextState.DIALOG_VALUE] = value;
OutputBinding = value;
}
}

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

@ -52,7 +52,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
protected override Task<InputState> OnRecognizeInput(DialogContext dc)
{
var input = dc.State.GetValue<string>(InputProperty);
var input = dc.State.GetValue<string>(INPUT_PROPERTY);
switch (this.OutputFormat)
{
@ -67,7 +67,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Input
break;
}
dc.State.SetValue(InputProperty, input);
dc.State.SetValue(INPUT_PROPERTY, input);
return input.Length > 0 ? Task.FromResult(InputState.Valid) : Task.FromResult(InputState.Unrecognized);
}
}

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

@ -44,7 +44,7 @@
"property": {
"$role": "expression",
"title": "Property",
"description": "The property to store the result of the HTTP call in (as object or string)",
"description": "The property to store the result of the HTTP call in. The result will have 4 properties from the http response: statusCode|reasonPhrase|content|headers. If the content is json it will be an deserialized object, otherwise it will be a string",
"examples": [
"dialog.contosodata"
]

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

@ -31,6 +31,14 @@
"No date was recognized"
]
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
]
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",

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

@ -85,38 +85,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
Exited
}
private ulong EncodeValue(ThreadModel thread, object value)
{
if (dataModel.IsScalar(value))
{
return 0;
}
var threadCode = threads[thread];
var valueCode = thread.ValueCodes.Add(value);
return Identifier.Encode(threadCode, valueCode);
}
private void DecodeValue(ulong variablesReference, out ThreadModel thread, out object value)
{
Identifier.Decode(variablesReference, out var threadCode, out var valueCode);
thread = this.threads[threadCode];
value = thread.ValueCodes[valueCode];
}
private ulong EncodeFrame(ThreadModel thread, ICodePoint frame)
{
var threadCode = threads[thread];
var valueCode = thread.FrameCodes.Add(frame);
return Identifier.Encode(threadCode, valueCode);
}
private void DecodeFrame(ulong frameCode, out ThreadModel thread, out ICodePoint frame)
{
Identifier.Decode(frameCode, out var threadCode, out var valueCode);
thread = this.threads[threadCode];
frame = thread.FrameCodes[valueCode];
}
private int NextSeq => Interlocked.Increment(ref sequence);
public static string Ellipsis(string text, int length)
{
@ -277,6 +246,39 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
return $"{turnContext.Activity.ChannelId}-{turnContext.Activity.Id}";
}
private ulong EncodeValue(ThreadModel thread, object value)
{
if (dataModel.IsScalar(value))
{
return 0;
}
var threadCode = threads[thread];
var valueCode = thread.ValueCodes.Add(value);
return Identifier.Encode(threadCode, valueCode);
}
private void DecodeValue(ulong variablesReference, out ThreadModel thread, out object value)
{
Identifier.Decode(variablesReference, out var threadCode, out var valueCode);
thread = this.threads[threadCode];
value = thread.ValueCodes[valueCode];
}
private ulong EncodeFrame(ThreadModel thread, ICodePoint frame)
{
var threadCode = threads[thread];
var valueCode = thread.FrameCodes.Add(frame);
return Identifier.Encode(threadCode, valueCode);
}
private void DecodeFrame(ulong frameCode, out ThreadModel thread, out ICodePoint frame)
{
Identifier.Decode(frameCode, out var threadCode, out var valueCode);
thread = this.threads[threadCode];
frame = thread.FrameCodes[valueCode];
}
private async Task UpdateBreakpointsAsync(CancellationToken cancellationToken)
{
var breakpoints = this.breakpoints.ApplyUpdates();
@ -368,8 +370,6 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
await SendAsync(Protocol.Event.From(NextSeq, "output", body), cancellationToken).ConfigureAwait(false);
}
private int NextSeq => Interlocked.Increment(ref sequence);
private Protocol.Capabilities MakeCapabilities()
{
// TODO: there is a "capabilities" event for dynamic updates, but exceptionBreakpointFilters does not seem to be dynamically updateable

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

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Microsoft.Bot.Builder.Dialogs.Debugging
{
public interface IBreakpoints
{
bool IsBreakPoint(object item);
object ItemFor(Protocol.Breakpoint breakpoint);
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints);
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(IReadOnlyList<Protocol.FunctionBreakpoint> functionBreakpoints);
IReadOnlyList<Protocol.Breakpoint> ApplyUpdates();
}
}

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

@ -95,9 +95,9 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
string IDataModel.ToString(object context) => ToString((TContext)context);
protected T Coerce<T>(object item) => (T)this.coercion.Coerce(item, typeof(T));
IEnumerable<object> IDataModel.Names(object context) => Names((TContext)context).Cast<object>();
protected T Coerce<T>(object item) => (T)this.coercion.Coerce(item, typeof(T));
}
public sealed class DictionaryDataModel<TKey, TValue> : DataModelBase<IDictionary<TKey, TValue>, TKey, TValue>

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

@ -212,6 +212,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
public class Event : Message
{
#pragma warning disable SA1300 // Should begin with an uppercase letter.
public Event(int seq, string @event)
{
this.Seq = seq;
@ -220,6 +221,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
}
public string @event { get; set; }
#pragma warning restore SA1300 // Should begin with an uppercase letter.
public static Event<TBody> From<TBody>(int seq, string @event, TBody body) => new Event<TBody>(seq, @event) { Body = body };
}

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

@ -284,17 +284,4 @@ namespace Microsoft.Bot.Builder.Dialogs.Debugging
public object Item { get; set; }
}
}
public interface IBreakpoints
{
bool IsBreakPoint(object item);
object ItemFor(Protocol.Breakpoint breakpoint);
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(Protocol.Source source, IReadOnlyList<Protocol.SourceBreakpoint> sourceBreakpoints);
IReadOnlyList<Protocol.Breakpoint> SetBreakpoints(IReadOnlyList<Protocol.FunctionBreakpoint> functionBreakpoints);
IReadOnlyList<Protocol.Breakpoint> ApplyUpdates();
}
}

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

@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Dialogs.Adaptive;
using Microsoft.Bot.Builder.Dialogs.Debugging;
@ -21,27 +23,37 @@ namespace Microsoft.Bot.Builder.Dialogs.Declarative
{
IRefResolver refResolver = new IdRefResolver(resourceExplorer, registry);
string id = resource.Id;
var paths = new Stack<string>();
paths.Push(resource.Id);
if (resource is FileResource fileResource)
{
id = fileResource.FullName;
paths.Push(fileResource.FullName);
}
var json = await resource.ReadTextAsync();
try
{
var json = await resource.ReadTextAsync();
return Load<T>(registry, refResolver, paths, json);
return Load<T>(registry, refResolver, paths, json);
}
catch (Exception err)
{
if (err.InnerException is SyntaxErrorException)
{
throw new SyntaxErrorException(err.InnerException.Message)
{
Source = $"{id}{err.InnerException.Source}"
};
}
throw new Exception($"{id} error: {err.Message}\n{err.InnerException?.Message}");
}
}
public static T Load<T>(IResource resource, ResourceExplorer resourceExplorer, Source.IRegistry registry)
{
IRefResolver refResolver = new IdRefResolver(resourceExplorer, registry);
var paths = new Stack<string>();
if (resource is FileResource fileResource)
{
paths.Push(fileResource.FullName);
}
var json = resource.ReadTextAsync().GetAwaiter().GetResult();
return Load<T>(registry, refResolver, paths, json);
return LoadAsync<T>(resource, resourceExplorer, registry).GetAwaiter().GetResult();
}
/// <summary>

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

@ -48,7 +48,7 @@ namespace Microsoft.Bot.Builder.Dialogs
}
State = new DialogContextState(this, settings: settings, userState: userState, conversationState: conversationState, turnState: turnState as Dictionary<string, object>);
State.SetValue(DialogContextState.TurnActivity, Context.Activity);
State.SetValue(DialogContextState.TURN_ACTIVITY, Context.Activity);
}
public DialogContext(DialogSet dialogs, ITurnContext turnContext, DialogState state, IDictionary<string, object> conversationState = null, IDictionary<string, object> userState = null, IDictionary<string, object> settings = null)
@ -67,7 +67,7 @@ namespace Microsoft.Bot.Builder.Dialogs
}
State = new DialogContextState(this, settings: settings, userState: userState, conversationState: conversationState, turnState: turnState as Dictionary<string, object>);
State.SetValue(DialogContextState.TurnActivity, Context.Activity);
State.SetValue(DialogContextState.TURN_ACTIVITY, Context.Activity);
}
/// <summary>
@ -330,11 +330,11 @@ namespace Microsoft.Bot.Builder.Dialogs
// set dialog result
if (ShouldInheritState(dialog))
{
State.SetValue(DialogContextState.StepOptionsProperty, options);
State.SetValue(DialogContextState.STEP_OPTIONS_PROPERTY, options);
}
else
{
State.SetValue(DialogContextState.DialogOptions, options);
State.SetValue(DialogContextState.DIALOG_OPTIONS, options);
}
// Call dialogs BeginAsync() method.

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

@ -1,31 +1,51 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Expressions;
using Microsoft.Bot.Builder.Expressions.Parser;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
namespace Microsoft.Bot.Builder.Dialogs
{
/// <summary>
/// Defines the shape of the state object returned by calling DialogContext.State.ToJson().
/// </summary>
public class DialogContextVisibleState
{
[JsonProperty(PropertyName = "user")]
public IDictionary<string, object> User { get; set; }
[JsonProperty(PropertyName = "conversation")]
public IDictionary<string, object> Conversation { get; set; }
[JsonProperty(PropertyName = "dialog")]
public IDictionary<string, object> Dialog { get; set; }
}
public class DialogContextState : IDictionary<string, object>
{
/// <summary>
/// Common state properties paths.
/// </summary>
public const string DialogOptions = "dialog.options";
public const string DialogNames = "dialog.value";
#pragma warning disable SA1310 // Field should not contain underscore.
public const string DIALOG_OPTIONS = "dialog.options";
public const string DIALOG_VALUE = "dialog.value";
public const string TurnActivity = "turn.activity";
public const string TurnRecognized = "turn.recognized";
public const string TurnTopIntent = "turn.recognized.intent";
public const string TurnTopScore = "turn.recognized.score";
public const string TurnStepCount = "turn.stepCount";
public const string TurnDialogEvent = "turn.dialogEvent";
public const string TURN_ACTIVITY = "turn.activity";
public const string TURN_RECOGNIZED = "turn.recognized";
public const string TURN_TOPINTENT = "turn.recognized.intent";
public const string TURN_TOPSCORE = "turn.recognized.score";
public const string TURN_STEPCOUNT = "turn.stepCount";
public const string TURN_DIALOGEVENT = "turn.dialogEvent";
public const string StepOptionsProperty = "dialog.step.options";
public const string STEP_OPTIONS_PROPERTY = "dialog.step.options";
#pragma warning restore SA1310 // Field should not contain underscore.
private const string PrefixCallBack = "callstackScope('";
@ -424,19 +444,4 @@ namespace Microsoft.Bot.Builder.Dialogs
throw new NotImplementedException();
}
}
/// <summary>
/// Defines the shape of the state object returned by calling DialogContext.State.ToJson().
/// </summary>
public class DialogContextVisibleState
{
[JsonProperty(PropertyName = "user")]
public IDictionary<string, object> User { get; set; }
[JsonProperty(PropertyName = "conversation")]
public IDictionary<string, object> Conversation { get; set; }
[JsonProperty(PropertyName = "dialog")]
public IDictionary<string, object> Dialog { get; set; }
}
}

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

@ -2,6 +2,8 @@
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Data;
using Antlr4.Runtime;
using Antlr4.Runtime.Misc;
@ -11,6 +13,9 @@ namespace Microsoft.Bot.Builder.Expressions
{
public static readonly ErrorListener Instance = new ErrorListener();
public override void SyntaxError([NotNull] IRecognizer recognizer, [Nullable] IToken offendingSymbol, int line, int charPositionInLine, [NotNull] string msg, [Nullable] RecognitionException e) => throw new Exception($"syntax error at line {line}:{charPositionInLine} {msg}");
public override void SyntaxError([NotNull] IRecognizer recognizer, [Nullable] IToken offendingSymbol, int line, int charPositionInLine, [NotNull] string msg, [Nullable] RecognitionException e)
{
throw new SyntaxErrorException(msg) { Source = $"({line}:{charPositionInLine})", };
}
}
}

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

@ -30,8 +30,6 @@ namespace Microsoft.Bot.Builder.Expressions.Parser
{ "~", $"dialog.instance" },
};
private readonly EvaluatorLookup _lookup;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionEngine"/> class.
/// Constructor.
@ -39,15 +37,17 @@ namespace Microsoft.Bot.Builder.Expressions.Parser
/// <param name="lookup">If present delegate to lookup evaluation information from type string.</param>
public ExpressionEngine(EvaluatorLookup lookup = null)
{
_lookup = lookup ?? BuiltInFunctions.Lookup;
EvaluatorLookup = lookup ?? BuiltInFunctions.Lookup;
}
public EvaluatorLookup EvaluatorLookup { get; }
/// <summary>
/// Parse the input into an expression.
/// </summary>
/// <param name="expression">Expression to parse.</param>
/// <returns>Expresion tree.</returns>
public Expression Parse(string expression) => new ExpressionTransformer(_lookup).Transform(AntlrParse(expression));
public Expression Parse(string expression) => new ExpressionTransformer(EvaluatorLookup).Transform(AntlrParse(expression));
protected static IParseTree AntlrParse(string expression)
{

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

@ -3,6 +3,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Reflection;
@ -666,7 +667,7 @@ namespace Microsoft.Bot.Builder.Expressions
{
if (!_functions.TryGetValue(type, out var eval))
{
throw new Exception($"{type} does not have an evaluator, it's not a built-in function or a customized function");
throw new SyntaxErrorException($"{type} does not have an evaluator, it's not a built-in function or a customized function");
}
return eval;
}

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

@ -17,11 +17,14 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
private Stack<EvaluationTarget> evaluationTargetStack = new Stack<EvaluationTarget>();
public Analyzer(List<LGTemplate> templates)
public Analyzer(List<LGTemplate> templates, ExpressionEngine expressionEngine)
{
Templates = templates;
templateMap = templates.ToDictionary(t => t.Name);
_expressionParser = new ExpressionEngine(new GetMethodExtensions(new Evaluator(this.Templates, null)).GetMethodX);
// create an evaluator to leverage it's customized function look up for checking
var evaluator = new Evaluator(Templates, expressionEngine);
this._expressionParser = evaluator.ExpressionEngine;
}
public List<LGTemplate> Templates { get; }

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

@ -6,24 +6,28 @@ using System.Text;
using System.Text.RegularExpressions;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Tree;
using Microsoft.Bot.Builder.Expressions;
using Microsoft.Bot.Builder.Expressions.Parser;
namespace Microsoft.Bot.Builder.LanguageGeneration
{
public class Evaluator : LGFileParserBaseVisitor<string>
{
private readonly IGetMethod getMethodX;
private readonly Stack<EvaluationTarget> evaluationTargetStack = new Stack<EvaluationTarget>();
public Evaluator(List<LGTemplate> templates, IGetMethod getMethod)
public Evaluator(List<LGTemplate> templates, ExpressionEngine expressionEngine)
{
Templates = templates;
TemplateMap = templates.ToDictionary(x => x.Name);
getMethodX = getMethod ?? new GetMethodExtensions(this);
// generate a new customzied expression engine by injecting the template as functions
ExpressionEngine = new ExpressionEngine(CustomizedEvaluatorLookup(expressionEngine.EvaluatorLookup));
}
public List<LGTemplate> Templates { get; }
public ExpressionEngine ExpressionEngine { get; }
public Dictionary<string, LGTemplate> TemplateMap { get; }
public string EvaluateTemplate(string templateName, object scope)
@ -255,9 +259,53 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
private (object value, string error) EvalByExpressionEngine(string exp, object scope)
{
var parse = new ExpressionEngine(getMethodX.GetMethodX).Parse(exp);
var parse = this.ExpressionEngine.Parse(exp);
return parse.TryEvaluate(scope);
}
// Genearte a new lookup function based on one lookup function
private EvaluatorLookup CustomizedEvaluatorLookup(EvaluatorLookup baseLookup)
=> (string name) =>
{
var builtInPrefix = "builtin.";
if (name.StartsWith(builtInPrefix))
{
return baseLookup(name.Substring(builtInPrefix.Length));
}
if (this.TemplateMap.ContainsKey(name))
{
return new ExpressionEvaluator(name, BuiltInFunctions.Apply(this.TemplateEvaluator(name)), ReturnType.String, this.ValidTemplateReference);
}
return baseLookup(name);
};
private Func<IReadOnlyList<object>, object> TemplateEvaluator(string templateName)
=> (IReadOnlyList<object> args) =>
{
var newScope = this.ConstructScope(templateName, args.ToList());
return this.EvaluateTemplate(templateName, newScope);
};
private void ValidTemplateReference(Expression expression)
{
var templateName = expression.Type;
if (!this.TemplateMap.ContainsKey(templateName))
{
throw new Exception($"no such template '{templateName}' to call in {expression}");
}
var expectedArgsCount = this.TemplateMap[templateName].Parameters.Count();
var actualArgsCount = expression.Children.Length;
if (expectedArgsCount != actualArgsCount)
{
throw new Exception($"arguments mismatch for template {templateName}, expect {expectedArgsCount} actual {actualArgsCount}");
}
}
}
internal class EvaluationTarget

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

@ -13,14 +13,16 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
{
public class Expander : LGFileParserBaseVisitor<List<string>>
{
private readonly IGetMethod getMethodX;
private readonly ExpressionEngine expressionEngine;
private readonly Stack<EvaluationTarget> evaluationTargetStack = new Stack<EvaluationTarget>();
public Expander(List<LGTemplate> templates, IGetMethod getMethod)
public Expander(List<LGTemplate> templates, ExpressionEngine expressionEngine)
{
Templates = templates;
TemplateMap = templates.ToDictionary(x => x.Name);
getMethodX = getMethod ?? new GetExpanderMethod(this);
// generate a new customzied expression engine by injecting the template as functions
this.expressionEngine = new ExpressionEngine(CustomizedEvaluatorLookup(expressionEngine.EvaluatorLookup));
}
public List<LGTemplate> Templates { get; }
@ -252,10 +254,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
return EvalExpression(exp);
}
private EvaluationTarget CurrentTarget() =>
// just don't want to write evaluationTargetStack.Peek() everywhere
evaluationTargetStack.Peek();
// just don't want to write evaluationTargetStack.Peek() everywhere
private EvaluationTarget CurrentTarget() => evaluationTargetStack.Peek();
private List<string> EvalMultiLineText(string exp)
{
@ -292,7 +292,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
private (object value, string error) EvalByExpressionEngine(string exp, object scope)
{
var parse = new ExpressionEngine(getMethodX.GetMethodX).Parse(exp);
var parse = this.expressionEngine.Parse(exp);
return parse.TryEvaluate(scope);
}
@ -309,63 +309,43 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
return result;
}
}
internal class GetExpanderMethod : IGetMethod
{
// Hold an evaluator instance to make sure all functions have access
// This ensentially make all functions as closure
// This is perticularly used for using templateName as lambda
// Such as {foreach(alarms, ShowAlarm)}
private readonly Expander _expander;
public GetExpanderMethod(Expander expander)
// Genearte a new lookup function based on one lookup function
private EvaluatorLookup CustomizedEvaluatorLookup(EvaluatorLookup baseLookup)
=> (string name) =>
{
_expander = expander;
}
public ExpressionEvaluator GetMethodX(string name)
{
// user can always choose to use builtin.xxx to disambiguate with template xxx
var builtInPrefix = "builtin.";
if (name.StartsWith(builtInPrefix))
{
return BuiltInFunctions.Lookup(name.Substring(builtInPrefix.Length));
return baseLookup(name.Substring(builtInPrefix.Length));
}
// TODO: Should add verifiers and validators
switch (name)
if (this.TemplateMap.ContainsKey(name))
{
case "join":
return new ExpressionEvaluator("join", BuiltInFunctions.Apply(this.Join));
return new ExpressionEvaluator(name, BuiltInFunctions.Apply(this.TemplateEvaluator(name)), ReturnType.String, this.ValidTemplateReference);
}
if (_expander.TemplateMap.ContainsKey(name))
{
return new ExpressionEvaluator($"{name}", BuiltInFunctions.Apply(this.TemplateEvaluator(name)), ReturnType.String, this.ValidTemplateReference);
}
return baseLookup(name);
};
return BuiltInFunctions.Lookup(name);
}
public Func<IReadOnlyList<object>, object> TemplateEvaluator(string templateName) =>
private Func<IReadOnlyList<object>, object> TemplateEvaluator(string templateName) =>
(IReadOnlyList<object> args) =>
{
var newScope = _expander.ConstructScope(templateName, args.ToList());
return _expander.EvaluateTemplate(templateName, newScope);
var newScope = this.ConstructScope(templateName, args.ToList());
return this.EvaluateTemplate(templateName, newScope);
};
public void ValidTemplateReference(Expression expression)
private void ValidTemplateReference(Expression expression)
{
var templateName = expression.Type;
if (!_expander.TemplateMap.ContainsKey(templateName))
if (!this.TemplateMap.ContainsKey(templateName))
{
throw new Exception($"no such template '{templateName}' to call in {expression}");
}
var expectedArgsCount = _expander.TemplateMap[templateName].Parameters.Count();
var expectedArgsCount = this.TemplateMap[templateName].Parameters.Count();
var actualArgsCount = expression.Children.Length;
if (expectedArgsCount != actualArgsCount)
@ -373,36 +353,5 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
throw new Exception($"arguments mismatch for template {templateName}, expect {expectedArgsCount} actual {actualArgsCount}");
}
}
public object Join(IReadOnlyList<object> parameters)
{
object result = null;
if (parameters.Count == 2 &&
BuiltInFunctions.TryParseList(parameters[0], out var p0) &&
parameters[1] is string sep)
{
var p = p0.OfType<object>().Select(x => BuiltInFunctions.TryParseList(x, out var p1) ? p1[0].ToString() : x.ToString());
result = string.Join(sep, p);
}
else if (parameters.Count == 3 &&
BuiltInFunctions.TryParseList(parameters[0], out var li) &&
parameters[1] is string sep1 &&
parameters[2] is string sep2)
{
var p = li.OfType<object>().Select(x => BuiltInFunctions.TryParseList(x, out var p1) ? p1[0].ToString() : x.ToString());
if (li.Count < 3)
{
result = string.Join(sep2, p);
}
else
{
var firstPart = string.Join(sep1, p.TakeWhile(o => o != null && o != p.LastOrDefault()));
result = firstPart + sep2 + p.Last().ToString();
}
}
return result;
}
}
}

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

@ -6,13 +6,21 @@ using System.Text.RegularExpressions;
using Antlr4.Runtime;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Tree;
using Microsoft.Bot.Builder.Expressions;
using Microsoft.Bot.Builder.Expressions.Parser;
namespace Microsoft.Bot.Builder.LanguageGeneration
{
public class StaticChecker
{
public static List<Diagnostic> CheckFiles(IEnumerable<string> filePaths, ImportResolverDelegate importResolver = null)
private readonly ExpressionEngine expressionEngine;
public StaticChecker(ExpressionEngine expressionEngine = null)
{
this.expressionEngine = expressionEngine ?? new ExpressionEngine();
}
public List<Diagnostic> CheckFiles(IEnumerable<string> filePaths, ImportResolverDelegate importResolver = null)
{
var result = new List<Diagnostic>();
var templates = new List<LGTemplate>();
@ -51,9 +59,9 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
return result;
}
public static List<Diagnostic> CheckFile(string filePath, ImportResolverDelegate importResolver = null) => CheckFiles(new List<string>() { filePath }, importResolver);
public List<Diagnostic> CheckFile(string filePath, ImportResolverDelegate importResolver = null) => CheckFiles(new List<string>() { filePath }, importResolver);
public static List<Diagnostic> CheckText(string content, string id = "", ImportResolverDelegate importResolver = null)
public List<Diagnostic> CheckText(string content, string id = "", ImportResolverDelegate importResolver = null)
{
if (importResolver == null)
{
@ -92,21 +100,40 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
return result;
}
public static List<Diagnostic> CheckTemplates(List<LGTemplate> templates) => new StaticCheckerInner(templates).Check();
public List<Diagnostic> CheckTemplates(List<LGTemplate> templates) => new StaticCheckerInner(templates, expressionEngine).Check();
private class StaticCheckerInner : LGFileParserBaseVisitor<List<Diagnostic>>
{
private Dictionary<string, LGTemplate> templateMap = new Dictionary<string, LGTemplate>();
private string currentSource = string.Empty;
private ExpressionEngine baseExpressionEngine;
public StaticCheckerInner(List<LGTemplate> templates)
private IExpressionParser _expressionParser;
public StaticCheckerInner(List<LGTemplate> templates, ExpressionEngine expressionEngine)
{
Templates = templates;
baseExpressionEngine = expressionEngine;
}
public List<LGTemplate> Templates { get; }
// Create a property because we want this to be lazy loaded
private IExpressionParser ExpressionParser
{
get
{
if (_expressionParser == null)
{
// create an evaluator to leverage it's customized function look up for checking
var evaluator = new Evaluator(Templates, baseExpressionEngine);
_expressionParser = evaluator.ExpressionEngine;
}
return _expressionParser;
}
}
/// <summary>
/// Return error messaages list.
/// </summary>
@ -408,7 +435,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
try
{
new ExpressionEngine(new GetMethodExtensions(new Evaluator(this.Templates, null)).GetMethodX).Parse(expression);
ExpressionParser.Parse(expression);
}
catch (Exception e)
{
@ -454,7 +481,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
try
{
new ExpressionEngine(new GetMethodExtensions(new Evaluator(this.Templates, null)).GetMethodX).Parse(exp);
ExpressionParser.Parse(exp);
}
catch (Exception e)
{

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

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Bot.Builder.Expressions.Parser;
namespace Microsoft.Bot.Builder.LanguageGeneration
{
@ -10,13 +11,17 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
/// </summary>
public class TemplateEngine
{
private readonly ExpressionEngine expressionEngine;
/// <summary>
/// Initializes a new instance of the <see cref="TemplateEngine"/> class.
/// Return an empty engine, you can then use AddFile\AddFiles to add files to it,
/// or you can just use this empty engine to evaluate inline template.
/// </summary>
public TemplateEngine()
/// <param name="expressionEngine">The expression engine this template engine based on.</param>
public TemplateEngine(ExpressionEngine expressionEngine = null)
{
this.expressionEngine = expressionEngine ?? new ExpressionEngine();
}
/// <summary>
@ -88,11 +93,10 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
/// </summary>
/// <param name="templateName">Template name to be evaluated.</param>
/// <param name="scope">The state visible in the evaluation.</param>
/// <param name="methodBinder">Optional methodBinder to extend or override functions.</param>
/// <returns>Evaluate result.</returns>
public string EvaluateTemplate(string templateName, object scope = null, IGetMethod methodBinder = null)
public string EvaluateTemplate(string templateName, object scope = null)
{
var evaluator = new Evaluator(Templates, methodBinder);
var evaluator = new Evaluator(Templates, this.expressionEngine);
return evaluator.EvaluateTemplate(templateName, scope);
}
@ -102,17 +106,16 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
/// </summary>
/// <param name="templateName">Template name to be evaluated.</param>
/// <param name="scope">The state visible in the evaluation.</param>
/// <param name="methodBinder">Optional methodBinder to extend or override functions.</param>
/// <returns>Expand result.</returns>
public List<string> ExpandTemplate(string templateName, object scope = null, IGetMethod methodBinder = null)
public List<string> ExpandTemplate(string templateName, object scope = null)
{
var expander = new Expander(Templates, methodBinder);
var expander = new Expander(Templates, this.expressionEngine);
return expander.EvaluateTemplate(templateName, scope);
}
public AnalyzerResult AnalyzeTemplate(string templateName)
{
var analyzer = new Analyzer(Templates);
var analyzer = new Analyzer(Templates, this.expressionEngine);
return analyzer.AnalyzeTemplate(templateName);
}
@ -121,9 +124,8 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
/// </summary>
/// <param name="inlineStr">inline string which will be evaluated.</param>
/// <param name="scope">scope object or JToken.</param>
/// <param name="methodBinder">input method.</param>
/// <returns>Evaluate result.</returns>
public string Evaluate(string inlineStr, object scope = null, IGetMethod methodBinder = null)
public string Evaluate(string inlineStr, object scope = null)
{
// wrap inline string with "# name and -" to align the evaluation process
var fakeTemplateId = "__temp__";
@ -135,7 +137,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
var templates = Templates.Concat(lgsource.Templates).ToList();
RunStaticCheck(templates);
var evaluator = new Evaluator(templates, methodBinder);
var evaluator = new Evaluator(templates, this.expressionEngine);
return evaluator.EvaluateTemplate(fakeTemplateId, scope);
}
@ -146,7 +148,7 @@ namespace Microsoft.Bot.Builder.LanguageGeneration
private void RunStaticCheck(List<LGTemplate> templates = null)
{
var teamplatesToCheck = templates ?? this.Templates;
var diagnostics = StaticChecker.CheckTemplates(teamplatesToCheck);
var diagnostics = new StaticChecker(this.expressionEngine).CheckTemplates(teamplatesToCheck);
var errors = diagnostics.Where(u => u.Severity == DiagnosticSeverity.Error).ToList();
if (errors.Count != 0)

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

@ -490,6 +490,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -869,6 +878,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -1237,6 +1255,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -1476,6 +1503,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -2524,7 +2560,7 @@
"property": {
"$role": "expression",
"title": "Property",
"description": "The property to store the result of the HTTP call in (as object or string)",
"description": "The property to store the result of the HTTP call in. The result will have 4 properties from the http response: statusCode|reasonPhrase|content|headers. If the content is json it will be an deserialized object, otherwise it will be a string",
"examples": [
"dialog.contosodata"
],
@ -3524,6 +3560,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -3702,6 +3747,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -5713,6 +5767,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",

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

@ -17,7 +17,7 @@ set branch=4.Future
rem Update .schema
:update
echo Updating .schema files and building sdk.schema for branch %branch%
call dialogSchema ../libraries/**/*.schema -u -b %branch% -o sdk.schema
bf dialog:merge ../libraries/**/*.schema -u -b %branch% -o sdk.schema
echo *** Schema files will not be available until branch %current% is merged into %branch% ***
:done

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

@ -541,6 +541,7 @@ namespace Microsoft.Bot.Builder.AI.TriggerTrees.Tests
return result;
}
public class WeightedChoice<T>
{
public double Weight = 0.0;

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

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.Adapters;
@ -10,11 +11,13 @@ using Microsoft.Bot.Builder.Dialogs.Adaptive.Input;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Recognizers;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
using Microsoft.Bot.Builder.Dialogs.Declarative.Types;
using Microsoft.Bot.Builder.Expressions;
using Microsoft.Bot.Builder.Expressions.Parser;
using Microsoft.Bot.Builder.LanguageGeneration;
using Microsoft.Bot.Schema;
using Microsoft.Extensions.Configuration;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Schema.NET;
namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
{
@ -291,6 +294,84 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
.StartTestAsync();
}
[TestMethod]
public async Task AdaptiveDialog_TextInputDefaultValueResponse()
{
var ruleDialog = new AdaptiveDialog("planningTest")
{
Events = new List<IOnEvent>()
{
new OnBeginDialog()
{
Actions = new List<IDialog>()
{
new NumberInput()
{
Prompt = new ActivityTemplate("Hello, what is your age?"),
Property = "user.age",
DefaultValue = "10",
MaxTurnCount = 2,
DefaultValueResponse = new ActivityTemplate("I am going to say you are 10.")
},
new SendActivity()
{
Activity = new ActivityTemplate("Your age is {user.age}.")
}
}
}
}
};
await CreateFlow(ruleDialog)
.SendConversationUpdate()
.AssertReply("Hello, what is your age?")
.Send("Why do you want to know")
.AssertReply("Hello, what is your age?")
.Send("Why do you want to know")
.AssertReply("I am going to say you are 10.")
.AssertReply("Your age is 10.")
.StartTestAsync();
}
[TestMethod]
public async Task AdaptiveDialog_TextInputNoMaxTurnCount()
{
var ruleDialog = new AdaptiveDialog("planningTest")
{
Events = new List<IOnEvent>()
{
new OnBeginDialog()
{
Actions = new List<IDialog>()
{
new NumberInput()
{
Prompt = new ActivityTemplate("Hello, what is your age?"),
Property = "user.age",
DefaultValue = "10",
DefaultValueResponse = new ActivityTemplate("I am going to say you are 10.")
},
new SendActivity()
{
Activity = new ActivityTemplate("Your age is {user.age}.")
}
}
}
}
};
await CreateFlow(ruleDialog)
.SendConversationUpdate()
.AssertReply("Hello, what is your age?")
.Send("Why do you want to know")
.AssertReply("Hello, what is your age?")
.Send("Why do you want to know")
.AssertReply("Hello, what is your age?")
.Send("Why do you want to know")
.AssertReply("Hello, what is your age?")
.StartTestAsync();
}
[TestMethod]
public async Task AdaptiveDialog_DoActions()
{
@ -965,7 +1046,7 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
},
new BeginDialog("ageDialog")
{
Options = new
Options = new
{
userAge = "$age"
}
@ -1896,6 +1977,44 @@ namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
.StartTestAsync();
}
[TestMethod]
public async Task AdaptiveDialog_AdaptiveCardSubmit()
{
var ruleDialog = new AdaptiveDialog("planningTest")
{
AutoEndDialog = false,
Recognizer = new RegexRecognizer()
{
Intents = new Dictionary<string, string>()
{
{ "SubmitIntent", "123123123" }
}
},
Events = new List<IOnEvent>()
{
new OnIntent(intent: "SubmitIntent")
{
Actions = new List<IDialog>()
{
new SendActivity("The city is {@city}!")
}
},
},
};
var submitActivity = Activity.CreateMessageActivity();
submitActivity.Value = new
{
intent = "SubmitIntent",
city = "Seattle"
};
await CreateFlow(ruleDialog)
.Send(submitActivity)
.AssertReply("The city is Seattle!")
.StartTestAsync();
}
private TestFlow CreateFlow(AdaptiveDialog ruleDialog)
{
TypeFactory.Configuration = new ConfigurationBuilder().Build();

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

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using Microsoft.Bot.Builder.Expressions.Parser;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@ -10,7 +12,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
{
private TestContext testContextInstance;
public static IEnumerable<object[]> InvalidExpressions => new[]
public static IEnumerable<object[]> SyntaxErrorExpressions => new[]
{
Test("a+"),
Test("a+b*"),
@ -22,7 +24,16 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("user.lists.{dialog.listName}")
};
public static object[] Test(string input) => new object[] { input };
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
/// <value>The TestContext.</value>
public TestContext TestContext
{
get { return testContextInstance; }
set { testContextInstance = value; }
}
public static IEnumerable<object[]> BadExpressions => new[]
{
@ -106,8 +117,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("lessOrEquals(one)"), // function need two parameters
Test("equals(one)"), // equals must accept two parameters
Test("exists(1, 2)"), // function need one parameter
// Test("if(!exists(one), one, hello)"), // the second and third parameters of if must the same type
//Test("if(!exists(one), one, hello)"), // the second and third parameters of if must the same type
Test("not(false, one)"), // function need one parameter
#endregion
@ -121,23 +131,23 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("array(hello,world)"), // shold have 1 param
Test("array(one)"), // shold have 1 param
Test("DataUri(hello, world)"), // shoule have 1 param
Test("DataUri(false)"), // should have string param
Test("DataUri(false)"), //should have string param
Test("uriComponent(hello, world)"), // shoule have 1 param
Test("uriComponent(false)"), // should have string param
Test("uriComponent(false)"), //should have string param
Test("uriComponentToString(hello, world)"), // shoule have 1 param
Test("uriComponentToString(false)"), // should have string param
Test("uriComponentToString(false)"), //should have string param
Test("dataUriToBinary(hello, world)"), // shoule have 1 param
Test("dataUriToBinary(false)"), // should have string param
Test("dataUriToBinary(false)"), //should have string param
Test("dataUriToString(hello, world)"), // shoule have 1 param
Test("dataUriToString(false)"), // should have string param
Test("dataUriToString(false)"), //should have string param
Test("binary(hello, world)"), // shoule have 1 param
Test("binary(one)"), // should have string param
Test("binary(one)"), //should have string param
Test("base64(hello, world)"), // shoule have 1 param
Test("base64(one)"), // should have string param
Test("base64(one)"), //should have string param
Test("base64ToBinary(hello, world)"), // shoule have 1 param
Test("base64ToBinary(one)"), // should have string param
Test("base64ToBinary(one)"), //should have string param
Test("base64ToString(hello, world)"), // shoule have 1 param
Test("base64ToString(false)"), // should have string param
Test("base64ToString(false)"), //should have string param
#endregion
#region Math functions test
@ -168,7 +178,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("rand(7, 6)"), // minvalue cannot be greater than maxValue
Test("sum(items)"), // should have number parameters
Test("range(hello,one)"), // params should be integer
Test("range(one,0)"), // the second param should be more than 0
Test("range(one,0)"), //the second param should be more than 0
#endregion
#region Date and time function test
@ -193,19 +203,19 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("addSeconds(timestamp, 1,'yyyy', 2)"), // should have 2 or 3 params
Test("addSeconds(notISOTimestamp, 1)"), // not ISO datetime format
Test("dayOfMonth('errortime')"), // error datetime format
Test("dayOfMonth(timestamp, 1)"), // should have 1 param
Test("dayOfMonth(timestamp, 1)"), //should have 1 param
Test("dayOfMonth(notISOTimestamp)"), // not ISO datetime format
Test("dayOfWeek('errortime')"), // error datetime format
Test("dayOfWeek(timestamp, 1)"), // should have 1 param
Test("dayOfWeek(timestamp, 1)"), //should have 1 param
Test("dayOfWeek(notISOTimestamp)"), // not ISO datetime format
Test("dayOfYear('errortime')"), // error datetime format
Test("dayOfYear(timestamp, 1)"), // should have 1 param
Test("dayOfYear(timestamp, 1)"), //should have 1 param
Test("dayOfYear(notISOTimestamp)"), // not ISO datetime format
Test("month('errortime')"), // error datetime format
Test("month(timestamp, 1)"), // should have 1 param
Test("month(timestamp, 1)"), //should have 1 param
Test("month(notISOTimestamp)"), // not ISO datetime format
Test("date('errortime')"), // error datetime format
Test("date(timestamp, 1)"), // should have 1 param
Test("date(timestamp, 1)"), //should have 1 param
Test("date(notISOTimestamp)"), // not ISO datetime format
Test("year('errortime')"), // error datetime format
Test("year(timestamp, 1)"), // should have 1 param
@ -245,9 +255,9 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("convertToUTC(timestamp, timezone, 'D', hello)"), // should have 2 or 3 params
Test("addToTime(notValidTimeStamp, one, 'day')"), // not valid timestamp
Test("addToTime(timeStamp, hello, 'day')"), // interval should be integer
Test("addToTime(timeStamp, one, 'decade', 'D')"), // not valid time unit
Test("addToTime(timeStamp, one, 'week', 'A')"), // not valid format
Test("addToTime(timeStamp, one, 'week', 'A', one)"), // should have 3 or 4 params
Test("addToTime(timeStamp, one, 'decade', 'D')"), //not valid time unit
Test("addToTime(timeStamp, one, 'week', 'A')"), //not valid format
Test("addToTime(timeStamp, one, 'week', 'A', one)"), //should have 3 or 4 params
Test("convertTimeZone(notValidTimeStamp, 'UTC', timezone)"), // not valid timestamp
Test("convertTimeZone(timestamp2, invalidTimezone, timezone, 'D')"), // not valid source timezone
Test("convertTimeZone(timestamp2, timezone, invalidTimezone, 'D')"), // not valid destination timezone
@ -262,72 +272,72 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
#endregion
#region uri parsing function test
Test("uriHost(relativeUri)"),
Test("uriPath(relativeUri)"),
Test("uriPathAndQuery(relativeUri)"),
Test("uriPort(relativeUri)"),
Test("uriQuery(relativeUri)"),
Test("uriScheme(relativeUri)"),
Test("uriHost(relatibeUri)"),
Test("uriPath(relatibeUri)"),
Test("uriPathAndQuery(relatibeUri)"),
Test("uriPort(relatibeUri)"),
Test("uriQuery(relatibeUri)"),
Test("uriScheme(relatibeUri)"),
#endregion
#region collection functions test
Test("sum(items, 'hello')"), // should have 1 parameter
Test("sum('hello')"), // first param should be list
Test("average(items, 'hello')"), // should have 1 parameter
Test("average('hello')"), // first param should be list
Test("average(hello)"), // first param should be list
Test("contains('hello world', 'hello', 'new')"), // should have 2 parameter
Test("count(items, 1)"), // should have 1 parameter
Test("count(1)"), // first param should be list or string
Test("empty(1,2)"), // should have two params
Test("first(items,2)"), // should have 1 param
Test("last(items,2)"), // should have 1 param
Test("join(items, 'p1', 'p2','p3')"), // builtin function should have 2-3 params,
Test("sum(items, 'hello')"), //should have 1 parameter
Test("sum('hello')"), //first param should be list
Test("average(items, 'hello')"), //should have 1 parameter
Test("average('hello')"), //first param should be list
Test("average(hello)"), //first param should be list
Test("contains('hello world', 'hello', 'new')"), //should have 2 parameter
Test("count(items, 1)"), //should have 1 parameter
Test("count(1)"), //first param should be list or string
Test("empty(1,2)"), //should have two params
Test("first(items,2)"), //should have 1 param
Test("last(items,2)"), //should have 1 param
Test("join(items, 'p1', 'p2','p3')"), //builtin function should have 2-3 params,
Test("join(hello, 'hi')"), // first param must list
Test("join(items, 1)"), // second param must string
Test("join(items, '1', 2)"), // third param must string
Test("foreach(hello, item, item)"), // first arg is not list
Test("foreach(items, item)"), // should have three parameters
Test("foreach(items, item, item2, item3)"), // should have three parameters
Test("foreach(items, item)"), //should have three parameters
Test("foreach(items, item, item2, item3)"), //should have three parameters
Test("foreach(items, add(1), item)"), // Second paramter of foreach is not an identifier
Test("foreach(items, 1, item)"), // Second paramter error
Test("foreach(items, x, sum(x))"), // third paramter error
Test("select(hello, item, item)"), // first arg is not list
Test("select(items, item)"), // should have three parameters
Test("select(items, item, item2, item3)"), // should have three parameters
Test("select(items, item)"), //should have three parameters
Test("select(items, item, item2, item3)"), //should have three parameters
Test("select(items, add(1), item)"), // Second paramter of foreach is not an identifier
Test("select(items, 1, item)"), // Second paramter error
Test("select(items, x, sum(x))"), // third paramter error
Test("where(hello, item, item)"), // first arg is not list
Test("where(items, item)"), // should have three parameters
Test("where(items, item, item2, item3)"), // should have three parameters
Test("where(items, item)"), //should have three parameters
Test("where(items, item, item2, item3)"), //should have three parameters
Test("where(items, add(1), item)"), // Second paramter of where is not an identifier
Test("where(items, 1, item)"), // Second paramter error
Test("where(items, x, sum(x))"), // third paramter error
Test("union(one, two)"), // should have collection param
Test("intersection(one, two)"), // should have collection param
Test("skip(one, two)"), // should have collection param
Test("skip(items,-1)"), // the second parameter shoule not less than zero
Test("skip(items,3)"), // the second parameter shoule less than the length of the collection
Test("take(one, two)"), // should have collection param
Test("skip(one, two)"), //should have collection param
Test("skip(items,-1)"), //the second parameter shoule not less than zero
Test("skip(items,3)"), //the second parameter shoule less than the length of the collection
Test("take(one, two)"), //should have collection param
Test("take(createArray('H','e','l','l','0'),items[5])"), // the second param expr is wrong
Test("take(items,-1)"), // the second parameter shoule not less than zero
Test("take(items,4)"), // the second parameter shoule less than the length of the collection
Test("subArray(one,1,4)"), // should have collection param
Test("subArray(items,-1,4)"), // the second parameter shoule not less than zero
Test("subArray(items,1,4)"), // the second parameter shoule less than the length of the collection
Test("subArray(createArray('H','e','l','l','o'),items[5],5)"), // the second parameter expression is invalid
Test("subArray(createArray('H','e','l','l','o'),2,items[5])"), // the second parameter expression is invalid
Test("take(items,-1)"), //the second parameter shoule not less than zero
Test("take(items,4)"), //the second parameter shoule less than the length of the collection
Test("subArray(one,1,4)"), //should have collection param
Test("subArray(items,-1,4)"), //the second parameter shoule not less than zero
Test("subArray(items,1,4)"), //the second parameter shoule less than the length of the collection
Test("subArray(createArray('H','e','l','l','o'),items[5],5)"), //the second parameter expression is invalid
Test("subArray(createArray('H','e','l','l','o'),2,items[5])"), //the second parameter expression is invalid
#endregion
#region Object manipulation and construction functions test
Test("json(1,2)"), // should have 1 parameter
Test("json(1)"), // should be string parameter
Test("json(1,2)"), //should have 1 parameter
Test("json(1)"), //should be string parameter
Test("json('{\"key1\":value1\"}')"), // invalid json format string
Test("addProperty(json('{\"key1\":\"value1\"}'), 'key2','value2','key3')"), // should have 3 parameter
Test("addProperty(json('{\"key1\":\"value1\"}'), 'key2','value2','key3')"), //should have 3 parameter
Test("addProperty(json('{\"key1\":\"value1\"}'), 1,'value2')"), // second param should be string
Test("addProperty(json('{\"key1\":\"value1\"}'), 'key1', 3)"), // cannot add existing property
Test("setProperty(json('{\"key1\":\"value1\"}'), 'key2','value2','key3')"), // should have 3 parameter
Test("setProperty(json('{\"key1\":\"value1\"}'), 'key2','value2','key3')"), //should have 3 parameter
Test("setProperty(json('{\"key1\":\"value1\"}'), 1,'value2')"), // second param should be string
Test("removeProperty(json('{\"key1\":\"value1\",\"key2\":\"value2\"}'), 1))"), // second param should be string
Test("removeProperty(json('{\"key1\":\"value1\",\"key2\":\"value2\"}'), '1', '2'))"), // should have 2 parameters
@ -369,24 +379,12 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
#endregion
};
/// <summary>
/// Gets or sets the test context which provides
/// information about and functionality for the current test run.
/// </summary>
/// <value>
/// The test context which provides
/// information about and functionality for the current test run.
/// </value>
public TestContext TestContext
{
get { return testContextInstance; }
set { testContextInstance = value; }
}
public static object[] Test(string input) => new object[] { input };
[DataTestMethod]
[DynamicData(nameof(InvalidExpressions))]
[ExpectedException(typeof(Exception))]
public void Parse(string exp)
[DynamicData(nameof(SyntaxErrorExpressions))]
[ExpectedException(typeof(SyntaxErrorException))]
public void ParseSyntaxErrors(string exp)
{
try
{
@ -395,7 +393,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
catch (Exception e)
{
TestContext.WriteLine(e.Message);
throw e;
throw;
}
}

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

@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using Microsoft.Bot.Builder.Expressions.Parser;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Microsoft.Bot.Builder.Expressions.Tests
@ -308,6 +309,12 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("count(concat(hello,world))", 10),
Test("replace('hello', 'l', 'k')", "hekko"),
Test("replace('hello', 'L', 'k')", "hello"),
Test("replace(\"hello'\", \"'\", '\"')", "hello\""),
Test("replace('hello\"', '\"', \"'\")", "hello'"),
Test("replace('hello\"', '\"', '\n')", "hello\n"),
Test("replace('hello\n', '\n', '\\\\')", "hello\\"),
Test(@"replace('hello\\', '\\', '\\\\')", @"hello\\"),
Test(@"replace('hello\n', '\n', '\\\\')", @"hello\\"),
Test("replaceIgnoreCase('hello', 'L', 'k')", "hekko"),
Test("split('hello','e')", new string[] { "h", "llo" }),
Test("substring('hello', 0, 5)", "hello"),
@ -335,21 +342,21 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("addOrdinal(11 + 11)", "22nd"),
Test("addOrdinal(11 + 12)", "23rd"),
Test("addOrdinal(11 + 13)", "24th"),
Test("addOrdinal(-1)", "-1"), // original string value
Test("addOrdinal(-1)", "-1"), //original string value
#endregion
#region Logical comparison functions test
Test("and(1 == 1, 1 < 2, 1 > 2)", false),
Test("and(!true, !!true)", false), // false && true
Test("and(!!true, !!true)", true), // true && true
Test("and(hello != 'world', bool('true'))", true), // true && true
Test("and(hello == 'world', bool('true'))", false), // false && true
Test("or(!exists(one), !!exists(one))", true), // false && true
Test("or(!exists(one), !exists(one))", false), // false && false
Test("and(!true, !!true)", false), //false && true
Test("and(!!true, !!true)", true), //true && true
Test("and(hello != 'world', bool('true'))", true), //true && true
Test("and(hello == 'world', bool('true'))", false), //false && true
Test("or(!exists(one), !!exists(one))", true), //false && true
Test("or(!exists(one), !exists(one))", false), //false && false
Test("greater(one, two)", false, OneTwo),
Test("greater(one , 0.5) && less(two , 2.5)", true), // true && true
Test("greater(one , 0.5) || less(two , 1.5)", true), // true || false
Test("greater(one , 0.5) || less(two , 1.5)", true), //true || false
Test("greater(5, 2)", true),
Test("greater(2, 2)", false),
Test("greater(one, two)", false),
@ -376,13 +383,18 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("equals(hello, 'hello')", true),
Test("equals(bag.index, 3)", true),
Test("equals(bag.index, 2)", false),
Test("equals(hello == 'world', bool('true'))", false), // false, true
Test("equals(hello == 'world', bool(0))", false), // false, true
Test("if(!exists(one), 'r1', 'r2')", "r2"), // false
Test("if(!!exists(one), 'r1', 'r2')", "r1"), // true
Test("if(0, 'r1', 'r2')", "r1"), // true
Test("if(bool('true'), 'r1', 'r2')", "r1"), // true
Test("if(istrue, 'r1', 'r2')", "r1"), // true
Test("equals(hello == 'world', bool('true'))", false), //false, true
Test("equals(hello == 'world', bool(0))", false), //false, true
Test("if(!exists(one), 'r1', 'r2')", "r2"), //false
Test("if(!!exists(one), 'r1', 'r2')", "r1"), //true
Test("if(0, 'r1', 'r2')", "r1"), //true
Test("if(bool('true'), 'r1', 'r2')", "r1"), //true
Test("if(istrue, 'r1', 'r2')", "r1"), //true
Test("if(bag.name == null, \"hello\", bag.name)", "mybag"),
Test("if(user.name == null, \"hello\", user.name)", "hello"), // user.name don't exist
Test("if(user.name == null, '', user.name)", string.Empty), // user.name don't exist
Test("if(one > 0, one, two)", 1),
Test("if(one < 0, one, two)", 2),
Test("exists(one)", true),
Test("exists(xxx)", false),
Test("exists(one.xxx)", false),
@ -465,8 +477,7 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
#endregion
#region Date and time function test
// init dateTime: 2018-03-15T13:00:00Z
//init dateTime: 2018-03-15T13:00:00Z
Test("addDays(timestamp, 1)", "2018-03-16T13:00:00.000Z"),
Test("addDays(timestamp, 1,'MM-dd-yy')", "03-16-18"),
Test("addHours(timestamp, 1)", "2018-03-15T14:00:00.000Z"),
@ -476,10 +487,10 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("addSeconds(timestamp, 1)", "2018-03-15T13:00:01.000Z"),
Test("addSeconds(timestamp, 1, 'MM-dd-yy hh-mm-ss')", "03-15-18 01-00-01"),
Test("dayOfMonth(timestamp)", 15),
Test("dayOfWeek(timestamp)", 4), // Thursday
Test("dayOfWeek(timestamp)", 4), //Thursday
Test("dayOfYear(timestamp)", 74),
Test("month(timestamp)", 3),
Test("date(timestamp)", "3/15/2018"), // Default. TODO
Test("date(timestamp)", "3/15/2018"), //Default. TODO
Test("year(timestamp)", 2018),
Test("length(utcNow())", 24),
Test("utcNow('MM-DD-YY')", DateTime.UtcNow.ToString("MM-DD-YY")),
@ -624,7 +635,6 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
Test("$x", 3),
Test("$y", 2),
Test("$z", 1),
// Test("^x", 3),
// Test("^y", 2),
// Test("^z", 1),
@ -683,17 +693,17 @@ namespace Microsoft.Bot.Builder.Expressions.Tests
public static object[] Test(string input, object value, HashSet<string> paths = null) => new object[] { input, value, paths };
public static bool IsNumber(object value) =>
value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
[DataTestMethod]
[DynamicData(nameof(Data))]

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

@ -5,3 +5,35 @@
# otherEscape
- Hi \y \
# escapeInExpression
- Hi {replace('hello\\', '\\', '\\\\')}
# escapeInExpression2
- Hi {replace('hello\"', '\"', "'")}
# escapeInExpression3
- Hi {replace("hello'", "'", '\"')}
# escapeInExpression4
- Hi {replace("hello\n", "\n", '\"')}
# escapeInExpression5
- Hi {replace('hello\"', '\"', '\n')}
# escapeInExpression6
- Hi {replace("hello'", "'", '\n')}
# showTodo(todos)
- IF: {count(todos) > 0}
- ```
Your most recent @{count(todos)} tasks are
@{join(foreach(todos, x, showSingleTodo(x)), '\n')}
```
- ELSE:
- ```
You don't have any "@{replace(replace('t\\odo\"', '\"', "'"), '\\', '\\\\')}".
```
# showSingleTodo(x)
- * {x}

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

@ -275,6 +275,31 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
evaled = engine.EvaluateTemplate("otherEscape", null);
Assert.AreEqual(evaled, @"Hi \y \");
evaled = engine.EvaluateTemplate("escapeInExpression", null);
Assert.AreEqual(evaled, "Hi hello\\\\");
evaled = engine.EvaluateTemplate("escapeInExpression2", null);
Assert.AreEqual(evaled, "Hi hello'");
evaled = engine.EvaluateTemplate("escapeInExpression3", null);
Assert.AreEqual(evaled, "Hi hello\"");
evaled = engine.EvaluateTemplate("escapeInExpression4", null);
Assert.AreEqual(evaled, "Hi hello\"");
evaled = engine.EvaluateTemplate("escapeInExpression5", null);
Assert.AreEqual(evaled, "Hi hello\n");
evaled = engine.EvaluateTemplate("escapeInExpression6", null);
Assert.AreEqual(evaled, "Hi hello\n");
var todos = new[] { "A", "B", "C" };
evaled = engine.EvaluateTemplate("showTodo", new { todos });
Assert.AreEqual(evaled, Environment.NewLine + " Your most recent 3 tasks are" + Environment.NewLine + " * A\n* B\n* C" + Environment.NewLine + " ");
evaled = engine.EvaluateTemplate("showTodo", null);
Assert.AreEqual(evaled, Environment.NewLine + " You don't have any \"t\\\\odo'\"." + Environment.NewLine + " ");
}
[TestMethod]
@ -564,30 +589,6 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
Assert.AreEqual("You have 2 alarms, they are 8 pm of tomorrow", evaled[1]);
}
[TestMethod]
public void TestExpandTemplateWithRefInForeach()
{
var engine = new TemplateEngine().AddFile(GetExampleFilePath("Expand.lg"));
var alarms = new[]
{
new
{
time = "7 am",
date = "tomorrow"
},
new
{
time = "8 pm",
date = "tomorrow"
}
};
var evaled = engine.ExpandTemplate("ShowAlarmsWithForeach", new { alarms = alarms });
Assert.AreEqual(1, evaled.Count);
Assert.AreEqual("You have 2 alarms, 7 am at tomorrow and 8 pm at tomorrow", evaled[0]);
}
[TestMethod]
public void TestExpandTemplateWithRefInMultiLine()
{

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

@ -91,7 +91,7 @@ namespace Microsoft.Bot.Builder.AI.LanguageGeneration.Tests
{
var engine = new TemplateEngine().AddFile(GetExampleFilePath(input));
var report = StaticChecker.CheckTemplates(engine.Templates);
var report = new StaticChecker().CheckTemplates(engine.Templates);
TestContext.WriteLine(string.Join("\n", report));
}

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

@ -50,7 +50,7 @@
},
{
"$type": "Microsoft.SendActivity",
"activity": "Done! You have added a pet named \"{dialog.postResponse.name}\" with id \"{dialog.postResponse.id}\""
"activity": "Done! You have added a pet named \"{dialog.postResponse.content.name}\" with id \"{dialog.postResponse.content.id}\""
},
{
"$type": "Microsoft.TextInput",
@ -65,7 +65,7 @@
},
{
"$type": "Microsoft.SendActivity",
"activity": "Great! I found your pet named \"{dialog.getResponse.name}\""
"activity": "Great! I found your pet named \"{dialog.getResponse.content.name}\""
}
]
}

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

@ -1,5 +1,5 @@
# Show
- You have following issues : \n {join(foreach(dialog.getResponse, x, showSingleUrl(x)), '\n')}
- You have following issues : \n {join(foreach(dialog.getResponse.content, x, showSingleUrl(x)), '\n')}
# showSingleUrl(x)
- ```
@ -14,7 +14,7 @@
"type": "TextBlock",
"size": "Medium",
"weight": "Bolder",
"text": "@{x.title}"
"text": "@{replace(replace(x.title, '\"', "'"), '\\', '\\\\')}"
},
{
"type": "ColumnSet",

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

@ -16,7 +16,7 @@
"activity": "Your age is: {user.age}"
},
{
"$type": "Testbot.JavascriptStep",
"$type": "Testbot.JavascriptAction",
"script": "Samples/13 - CustomStep/dogyears.js",
"inputBindings": {
"age": "user.age"

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

@ -12,7 +12,7 @@
},
{
"$type": "Microsoft.IfCondition",
"condition": "!contains({user.email}, '@')",
"condition": "!contains(user.email, '@')",
"actions": [
"CalendarFindSingleContact"
]

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

@ -6,7 +6,7 @@
"$type": "Microsoft.OnBeginDialog",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",

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

@ -10,7 +10,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
@ -30,7 +30,7 @@
}
//{
// "$type": "Microsoft.SendActivity",
// "activity": "response: {user.acceptResponse}"
// "activity": "response: {user.acceptResponse.content}"
//}
]
}

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

@ -10,7 +10,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
@ -58,7 +58,7 @@
}
//{
// "$type": "Microsoft.SendActivity",
// "activity": "response: {user.createResponse}"
// "activity": "response: {user.createResponse.content}"
//}
]
}

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

@ -10,7 +10,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
@ -30,7 +30,7 @@
},
{
"$type": "Microsoft.SendActivity",
"activity": "response: {user.declineResponse}"
"activity": "response: {user.declineResponse.content}"
}
]
}

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

@ -1,42 +1,42 @@
{
"$schema": "../../../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"events": [
{
"$type": "Microsoft.OnBeginDialog",
"actions": [
"$schema": "../../../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"events": [
{
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"connectionName": "msgraph",
"title": "Log in",
"text": "Please log in to your Microsoft account",
"property": "dialog.token"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/contacts?$filter=startswith(displayName,'{user.email}')",
"method": "GET",
"headers": {
"Authorization": "Bearer {dialog.token.Token}"
},
"property": "dialog.getResponse"
},
{
"$type": "Microsoft.SendActivity",
"activity": "The contact you find is {dialog.getResponse.value[0].displayName}, email address is {dialog.getResponse.value[0].emailAddresses[0].address}."
},
{
"$type": "Microsoft.SetProperty",
"value": "dialog.getResponse.value[0].emailAddresses[0].address",
"property": "user.email"
"$type": "Microsoft.OnBeginDialog",
"actions": [
{
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthInput",
"connectionName": "msgraph",
"title": "Log in",
"text": "Please log in to your Microsoft account",
"property": "dialog.token"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/contacts?$filter=startswith(displayName,'{user.email}')",
"method": "GET",
"headers": {
"Authorization": "Bearer {dialog.token.Token}"
},
"property": "dialog.getResponse"
},
{
"$type": "Microsoft.SendActivity",
"activity": "The contact you find is {dialog.getResponse.content.value[0].displayName}, email address is {dialog.getResponse.content.value[0].emailAddresses[0].address}."
},
{
"$type": "Microsoft.SetProperty",
"value": "dialog.getResponse.content.value[0].emailAddresses[0].address",
"property": "user.email"
}
]
}
]
}
]
]
}

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

@ -1,32 +1,32 @@
{
"$schema": "../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"actions": [
{
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
"$schema": "../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
"Property": "dialog.token"
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
"Property": "dialog.token"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/calendarview?startdatetime={utcNow()}&enddatetime={addDays(utcNow(), 1)}",
"method": "GET",
"header": {
"Authorization": "Bearer {dialog.token.Token}"
},
"Property": "user.getGraphMeetings"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/calendarview?startdatetime={utcNow()}&enddatetime={addDays(utcNow(), 1)}",
"method": "GET",
"header": {
"Authorization": "Bearer {dialog.token.Token}"
},
"Property": "user.getGraphMeetings"
}
//,{
// "$type": "Microsoft.SendActivity",
// "activity": "{user.getGraphMeetings}"
//}
]
//,{
// "$type": "Microsoft.SendActivity",
// "activity": "{user.getGraphMeetings.content}"
//}
]
}

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

@ -1,44 +1,44 @@
{
"$schema": "../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"actions": [
"actions": [
{
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
"Property": "dialog.token"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/events/{user.focusedMeeting.id}",
"method": "PATCH",
"header": {
"Authorization": "Bearer {dialog.token.Token}"
},
"body": {
"start": {
"dateTime": "{user.startDateTime}",
"timeZone": "UTC"
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your calendar account",
"Property": "dialog.token"
}
]
},
"end": {
"dateTime": "{user.endDateTime}",
"timeZone": "UTC"
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/events/{user.focusedMeeting.id}",
"method": "PATCH",
"header": {
"Authorization": "Bearer {dialog.token.Token}"
},
"body": {
"start": {
"dateTime": "{user.startDateTime}",
"timeZone": "UTC"
},
"end": {
"dateTime": "{user.endDateTime}",
"timeZone": "UTC"
}
},
"Property": "user.updateResponse"
}
},
"Property": "user.updateResponse"
}
//{
// "$type": "Microsoft.SendActivity",
// "activity": "response: {user.updateResponse}"
//}
]
//{
// "$type": "Microsoft.SendActivity",
// "activity": "response: {user.updateResponse.content}"
//}
]
}

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

@ -7,7 +7,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your email account",

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

@ -1,37 +1,37 @@
{
"$schema": "../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"actions": [
{
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
"$schema": "../../app.schema",
"$type": "Microsoft.AdaptiveDialog",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your email account",
"Property": "dialog.token"
"$type": "Microsoft.IfCondition",
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your email account",
"Property": "dialog.token"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/contacts?$filter=startswith(displayName,'{user.email}')",
"method": "GET",
"header": {
"Authorization": "Bearer {dialog.token.Token}"
},
"Property": "dialog.getResponse"
},
{
"$type": "Microsoft.SendActivity",
"activity": "The contact you find is {dialog.getResponse.content.value[0].displayName}, email address is {dialog.getResponse.content.value[0].emailAddresses[0].address}."
},
{
"$type": "Microsoft.SetProperty",
"value": "dialog.getResponse.content.value[0].emailAddresses[0].address",
"property": "user.email"
}
]
},
{
"$type": "Microsoft.HttpRequest",
"url": "https://graph.microsoft.com/v1.0/me/contacts?$filter=startswith(displayName,'{user.email}')",
"method": "GET",
"header": {
"Authorization": "Bearer {dialog.token.Token}"
},
"Property": "dialog.getResponse"
},
{
"$type": "Microsoft.SendActivity",
"activity": "The contact you find is {dialog.getResponse.value[0].displayName}, email address is {dialog.getResponse.value[0].emailAddresses[0].address}."
},
{
"$type": "Microsoft.SetProperty",
"value": "dialog.getResponse.value[0].emailAddresses[0].address",
"property": "user.email"
}
]
]
}

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

@ -7,7 +7,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your email account",

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

@ -7,7 +7,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your email account",

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

@ -7,7 +7,7 @@
"condition": "dialog.token == null",
"actions": [
{
"$type": "Microsoft.OAuthPrompt",
"$type": "Microsoft.OAuthInput",
"ConnectionName": "msgraph",
"Title": "Log in",
"Text": "Please log in to your email account",

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

@ -100,7 +100,7 @@
"actions": [
{
"$type": "Microsoft.InitProperty",
"property": "user.lists.{dialog.listName}",
"property": "user.lists[dialog.listName]",
"type": "array"
}
]
@ -108,7 +108,7 @@
{
"$type": "Microsoft.EditArray",
"changeType": "Push",
"arrayProperty": "user.lists.{dialog.listName}",
"arrayProperty": "user.lists[dialog.listName]",
"value": "dialog.item"
},
{

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

@ -100,7 +100,7 @@
"actions": [
{
"$type": "Microsoft.InitProperty",
"property": "user.lists.{dialog.listName}",
"property": "user.lists[dialog.listName]",
"type": "array"
}
]
@ -108,7 +108,7 @@
{
"$type": "Microsoft.EditArray",
"changeType": "Remove",
"arrayProperty": "user.lists.{dialog.listName}",
"arrayProperty": "user.lists[dialog.listName]",
"value": "dialog.item"
},
{

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

@ -1,7 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
@ -11,10 +14,14 @@ using Microsoft.Bot.Builder.Dialogs.Adaptive;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Actions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Events;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Input;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Recognizers;
using Microsoft.Bot.Builder.Dialogs.Choices;
using Microsoft.Bot.Builder.Dialogs.Debugging;
using Microsoft.Bot.Builder.Dialogs.Declarative;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;
using Microsoft.Bot.Builder.Expressions.Parser;
using Microsoft.Bot.Schema;
using static Microsoft.Bot.Builder.Dialogs.Debugging.Source;
namespace Microsoft.Bot.Builder.TestBot.Json
{
@ -69,10 +76,21 @@ namespace Microsoft.Bot.Builder.TestBot.Json
foreach (var resource in this.resourceExplorer.GetResources(".dialog").Where(r => r.Id.EndsWith(".main.dialog")))
{
var name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(resource.Id));
choiceInput.Choices.Add(new Choice(name));
var dialog = DeclarativeTypeLoader.Load<IDialog>(resource, this.resourceExplorer, DebugSupport.SourceRegistry);
handleChoice.Cases.Add(new Case($"{name}", new List<IDialog>() { dialog }));
try
{
var name = Path.GetFileNameWithoutExtension(Path.GetFileNameWithoutExtension(resource.Id));
choiceInput.Choices.Add(new Choice(name));
var dialog = DeclarativeTypeLoader.Load<IDialog>(resource, this.resourceExplorer, DebugSupport.SourceRegistry);
handleChoice.Cases.Add(new Case($"{name}", new List<IDialog>() { dialog }));
}
catch (SyntaxErrorException err)
{
Trace.TraceError($"{err.Source}: Error: {err.Message}");
}
catch (Exception err)
{
Trace.TraceError(err.Message);
}
}
choiceInput.Style = ListStyle.Auto;

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

@ -281,9 +281,9 @@
"$ref": "#/definitions/Microsoft.TrueSelector"
},
{
"title": "Testbot.JavascriptStep",
"title": "Testbot.JavascriptAction",
"description": "This gives you the ability to execute javascript to manipulate memory",
"$ref": "#/definitions/Testbot.JavascriptStep"
"$ref": "#/definitions/Testbot.JavascriptAction"
},
{
"title": "Testbot.Multiply",
@ -500,6 +500,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -879,6 +888,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -1247,6 +1265,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -1486,6 +1513,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -2534,7 +2570,7 @@
"property": {
"$role": "expression",
"title": "Property",
"description": "The property to store the result of the HTTP call in (as object or string)",
"description": "The property to store the result of the HTTP call in. The result will have 4 properties from the http response: statusCode|reasonPhrase|content|headers. If the content is json it will be an deserialized object, otherwise it will be a string",
"examples": [
"dialog.contosodata"
],
@ -2755,9 +2791,9 @@
"title": "string"
},
{
"title": "Testbot.JavascriptStep",
"title": "Testbot.JavascriptAction",
"description": "This gives you the ability to execute javascript to manipulate memory",
"$ref": "#/definitions/Testbot.JavascriptStep"
"$ref": "#/definitions/Testbot.JavascriptAction"
},
{
"title": "Testbot.Multiply",
@ -3544,6 +3580,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -3722,6 +3767,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -5733,6 +5787,15 @@
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"defaultValueResponse": {
"$type": "Microsoft.IActivityTemplate",
"title": "Default Value Response",
"description": "The message to send to when max turn count has been exceeded and the default value is selected as the value.",
"examples": [
"I didn't understand your responses, so I will just use the default value of 10. Let me know if you want to change it."
],
"$ref": "#/definitions/Microsoft.IActivityTemplate"
},
"maxTurnCount": {
"type": "integer",
"title": "Max Turn Count",
@ -5970,7 +6033,7 @@
}
]
},
"Testbot.JavascriptStep": {
"Testbot.JavascriptAction": {
"$role": "unionType(Microsoft.IDialog)",
"title": "Javascript Action",
"description": "This gives you the ability to execute javascript to manipulate memory",
@ -5982,7 +6045,7 @@
"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": "Testbot.JavascriptStep"
"const": "Testbot.JavascriptAction"
},
"$copy": {
"title": "$copy",

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

@ -1,4 +1,4 @@
@echo off
erase app.schema
dialogSchema ../../libraries/**/*.schema ./**/*.schema -o app.schema
dialogschema ../../libraries/**/*.schema ./**/*.schema -o app.schema