Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#6433] Error in AdaptiveDialog.ContinueActionAsync with native dialog SDK #6444

Merged
merged 3 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,6 @@ protected virtual Dialog ResolveDialog(DialogContext dc)
var se = new StringExpression($"={this.Dialog.ExpressionText}");
var dialogId = se.GetValue(dc.State) ?? throw new InvalidOperationException($"{this.Dialog.ToString()} not found.");
var dialog = dc.FindDialog(dialogId);

if (dialog == null)
{
var resourceExplorer = dc.Context.TurnState.Get<ResourceExplorer>();
if (resourceExplorer != null)
{
dialog = resourceExplorer.LoadType<AdaptiveDialog>($"{dialogId}.dialog");
dc.Dialogs.Add(dialog);
}
}

return dialog;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,44 @@ public IEnumerable<Dialog> GetDependencies()
yield break;
}

/// <summary>
/// Finds a child dialog that was previously added to the container.
/// Uses DialogContext as fallback to gather the dialog from the <see cref="ResourceExplorer"/>.
/// </summary>
/// <param name="dialogId">The ID of the dialog to lookup.</param>
/// <param name="dc">The dialog context where to find the dialog.</param>
/// <returns>The Dialog if found; otherwise null.</returns>
/// <remarks>
/// When the Dialog is gathered from the <see cref="ResourceExplorer"/>,
/// automatically will be loaded into the <see cref="DialogContext.Dialogs"/> stack.
/// </remarks>
public override Dialog FindDialog(string dialogId, DialogContext dc = null)
{
var dialog = FindDialog(dialogId);
if (dialog != null)
{
return dialog;
}

if (dc == null)
{
throw new ArgumentNullException(nameof(dc));
}

var resourceExplorer = dc.Context.TurnState.Get<ResourceExplorer>();
var resourceId = $"{dialogId}.dialog";
var foundResource = resourceExplorer?.TryGetResource(resourceId, out _) ?? false;
if (!foundResource)
{
return null;
}

dialog = resourceExplorer.LoadType<AdaptiveDialog>(resourceId);
dialog.Id = dialogId;
dc.Dialogs.Add(dialog);
return dialog;
}

/// <summary>
/// Gets the internal version string.
/// </summary>
Expand Down Expand Up @@ -680,20 +718,6 @@ protected async Task<DialogTurnResult> ContinueActionsAsync(DialogContext dc, ob
var actionDC = CreateChildContext(actionContext);
while (actionDC != null)
{
if (actionDC.ActiveDialog != null)
{
var dialog = FindDialog(actionDC.ActiveDialog.Id);
if (dialog == null)
{
var resourceExplorer = actionDC.Context.TurnState.Get<ResourceExplorer>();
if (resourceExplorer != null)
{
dialog = resourceExplorer.LoadType<AdaptiveDialog>($"{actionDC.ActiveDialog.Id}.dialog");
actionDC.Dialogs.Add(dialog);
}
}
}

// DEBUG: To debug step execution set a breakpoint on line below and add a watch
// statement for actionContext.Actions.
DialogTurnResult result;
Expand Down
11 changes: 11 additions & 0 deletions libraries/Microsoft.Bot.Builder.Dialogs/DialogContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,17 @@ public virtual Dialog FindDialog(string dialogId)
return this.Dialogs.Find(dialogId);
}

/// <summary>
/// Finds a child dialog that was previously added to the container. Uses DialogContext as fallback to gather the dialog.
/// </summary>
/// <param name="dialogId">The ID of the dialog to lookup.</param>
/// <param name="dc">The dialog context fallback where to find the dialog.</param>
/// <returns>The Dialog if found; otherwise null.</returns>
public virtual Dialog FindDialog(string dialogId, DialogContext dc = null)
{
return Dialogs.Find(dialogId) ?? dc?.Dialogs?.Find(dialogId);
}

/// <summary>
/// Called when an event has been raised, using `DialogContext.emitEvent()`, by either the current dialog or a dialog that the current dialog started.
/// </summary>
Expand Down
14 changes: 13 additions & 1 deletion libraries/Microsoft.Bot.Builder.Dialogs/DialogContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,19 @@ public Dialog FindDialog(string dialogId)

if (this.Parent != null)
{
return this.Parent.FindDialog(dialogId);
var dialog = Parent.FindDialog(dialogId);

if (dialog != null)
{
return dialog;
}

var parentDialog = Parent.ActiveDialog?.Id != null ? Parent.FindDialog(Parent.ActiveDialog.Id) : null;
if (parentDialog is DialogContainer)
{
dialog = (parentDialog as DialogContainer).FindDialog(dialogId, this);
return dialog;
}
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Threading;
using System.Runtime.CompilerServices;
using System.Linq;
using System.IO;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Actions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Templates;
Expand All @@ -22,6 +23,7 @@
using Microsoft.Bot.Builder.Dialogs.Adaptive.Testing.TestActions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Generators;
using Moq;
using Microsoft.Bot.Builder.Dialogs.Declarative.Resources;

namespace Microsoft.Bot.Builder.Dialogs.Adaptive.Tests
{
Expand Down Expand Up @@ -1006,6 +1008,126 @@ public async Task AdaptiveDialog_ReplaceParentComplex_VerifyPostReplace()
.StartTestAsync();
}

[Fact]
public async Task AdaptiveDialog_BeginDialog_With_ComponentDialog()
{
var storage = new MemoryStorage();
var adapter = new TestAdapter()
.UseStorage(storage)
.UseBotState(new UserState(storage), new ConversationState(storage));

var rootDialog = new AdaptiveDialog("root")
{
AutoEndDialog = false,
Triggers = new List<OnCondition>()
{
new OnBeginDialog()
{
Actions = new List<Dialog>()
{
new BeginDialog(nameof(ForeachItemsDialog))
}
}
},
};

var dialogManager = new DialogManager(rootDialog)
.UseResourceExplorer(_resourceExplorerFixture.ResourceExplorer);
dialogManager.Dialogs.Add(new ForeachItemsDialog(1));

await new TestFlow((TestAdapter)adapter, async (turnContext, cancellationToken) =>
{
await dialogManager.OnTurnAsync(turnContext, cancellationToken);
})
.SendConversationUpdate()
.Send("hi")
.AssertReply("Send me some text.")
.StartTestAsync();
}

[Fact]
public async Task AdaptiveDialog_LoadDialogFromProperty_With_BotRestart()
{
var storage = new MemoryStorage();
var adapter = new TestAdapter()
.UseStorage(storage)
.UseBotState(new UserState(storage), new ConversationState(storage));

var rootDialog = new AdaptiveDialog("root")
{
AutoEndDialog = false,
Recognizer = new RegexRecognizer
{
Intents = new List<IntentPattern>
{
new IntentPattern
{
Intent = "Start",
Pattern = "start"
}
}
},
Triggers = new List<OnCondition>()
{
new OnIntent()
{
Intent = "Start",
Actions = new List<Dialog>()
{
new SetProperty
{
Property = "turn.dialogToStart",
Value = "AskNameDialog"
},
new BeginDialog("=turn.dialogToStart")
}
},
new OnDialogEvent(DialogEvents.VersionChanged)
{
Actions = new List<Dialog>()
{
new SetProperty
{
Property = "user.name",
Value = $"John Doe ({DialogEvents.VersionChanged})"
},
}
}
},
};

var resourceExplorer = new ResourceExplorer();
var folderPath = Path.Combine(TestUtils.GetProjectPath(), "Tests", "ActionTests");
resourceExplorer = resourceExplorer.AddFolder(folderPath, monitorChanges: false);

var dialogManager = new DialogManager(rootDialog)
.UseResourceExplorer(resourceExplorer);

await new TestFlow((TestAdapter)adapter, async (turnContext, cancellationToken) =>
{
await dialogManager.OnTurnAsync(turnContext, cancellationToken);
})
.Send("start")
.AssertReply("Hello, what is your name?")
.StartTestAsync();

// Simulate bot restart, maintaining storage information.
adapter = new TestAdapter()
.UseStorage(storage)
.UseBotState(new UserState(storage), new ConversationState(storage));

dialogManager = new DialogManager(rootDialog)
.UseResourceExplorer(resourceExplorer);

await new TestFlow((TestAdapter)adapter, async (turnContext, cancellationToken) =>
{
await dialogManager.OnTurnAsync(turnContext, cancellationToken);
})
.Send("John Doe")
.AssertReply($"Hello John Doe ({DialogEvents.VersionChanged}), nice to meet you!")
.StartTestAsync();
}

private static AdaptiveDialog CreateDialog(string custom)
{
return new AdaptiveDialog()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,23 @@
"$kind": "Microsoft.AdaptiveDialog",
"id": "outer",
"autoEndDialog": false,
"recognizer": {
"$kind": "Microsoft.RegexRecognizer",
"intents": [
{
"intent": "TellJokeDialog",
"pattern": "joke"
},
{
"intent": "UnknownDialog",
"pattern": "unknown"
}
]
},
"triggers": [
{
"$kind": "Microsoft.OnBeginDialog",
"$kind": "Microsoft.OnIntent",
"intent": "TellJokeDialog",
"actions": [
{
"$kind": "Microsoft.SetProperty",
Expand All @@ -19,17 +33,40 @@
"dialog": "=turn.dialogToStart"
}
]
},
{
"$kind": "Microsoft.OnIntent",
"intent": "UnknownDialog",
"actions": [
{
"$kind": "Microsoft.SetProperty",
"value": "UnknownDialog",
"property": "turn.dialogToStart"
},
{
"$kind": "Microsoft.BeginDialog",
"dialog": "=turn.dialogToStart"
}
]
}
]
},
"script": [
{
"$kind": "Microsoft.Test.UserSays",
"text": "hi"
"text": "joke"
},
{
"$kind": "Microsoft.Test.AssertReply",
"text": "Why did the chicken cross the road?"
},
{
"$kind": "Microsoft.Test.UserSays",
"text": "unknown"
},
{
"$kind": "Microsoft.Test.AssertReply",
"text": "Object reference not set to an instance of an object."
}
]
}