* Refactor for Issue 1046

* Update to push the build again.
This commit is contained in:
Lee Parrish 2021-03-16 08:24:17 -05:00 коммит произвёл GitHub
Родитель 44969e6b71
Коммит 3736b2656a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 702 добавлений и 699 удалений

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -5,6 +5,7 @@ package com.microsoft.bot.dialogs;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.bot.builder.BotAdapter;
import com.microsoft.bot.builder.BotTelemetryClient;
import com.microsoft.bot.builder.NullBotTelemetryClient;
@ -12,14 +13,17 @@ import com.microsoft.bot.builder.StatePropertyAccessor;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.skills.SkillConversationReference;
import com.microsoft.bot.builder.skills.SkillHandler;
import com.microsoft.bot.connector.Async;
import com.microsoft.bot.connector.authentication.AuthenticationConstants;
import com.microsoft.bot.connector.authentication.ClaimsIdentity;
import com.microsoft.bot.connector.authentication.GovernmentAuthenticationConstants;
import com.microsoft.bot.connector.authentication.SkillValidation;
import com.microsoft.bot.dialogs.memory.DialogStateManager;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.EndOfConversationCodes;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import org.apache.commons.lang3.StringUtils;
@ -29,11 +33,10 @@ import org.apache.commons.lang3.StringUtils;
public abstract class Dialog {
/**
* A {@link DialogTurnResult} that indicates that the current dialog is still active and waiting
* for input from the user next turn.
* A {@link DialogTurnResult} that indicates that the current dialog is still
* active and waiting for input from the user next turn.
*/
public static final DialogTurnResult END_OF_TURN = new DialogTurnResult(
DialogTurnStatus.WAITING);
public static final DialogTurnResult END_OF_TURN = new DialogTurnResult(DialogTurnStatus.WAITING);
@JsonIgnore
private BotTelemetryClient telemetryClient;
@ -94,8 +97,8 @@ public abstract class Dialog {
* Called when the dialog is started and pushed onto the dialog stack.
*
* @param dc The {@link DialogContext} for the current turn of conversation.
* @return If the task is successful, the result indicates whether the dialog is still active
* after the turn has been processed by the dialog.
* @return If the task is successful, the result indicates whether the dialog is
* still active after the turn has been processed by the dialog.
*/
public CompletableFuture<DialogTurnResult> beginDialog(DialogContext dc) {
return beginDialog(dc, null);
@ -104,25 +107,27 @@ public abstract class Dialog {
/**
* Called when the dialog is started and pushed onto the dialog stack.
*
* @param dc The {@link DialogContext} for the current turn of conversation.
* @param dc The {@link DialogContext} for the current turn of
* conversation.
* @param options Initial information to pass to the dialog.
* @return If the task is successful, the result indicates whether the dialog is still active
* after the turn has been processed by the dialog.
* @return If the task is successful, the result indicates whether the dialog is
* still active after the turn has been processed by the dialog.
*/
public abstract CompletableFuture<DialogTurnResult> beginDialog(
DialogContext dc, Object options
);
public abstract CompletableFuture<DialogTurnResult> beginDialog(DialogContext dc, Object options);
/**
* Called when the dialog is _continued_, where it is the active dialog and the user replies
* with a new activity.
* Called when the dialog is _continued_, where it is the active dialog and the
* user replies with a new activity.
*
* <p>If this method is *not* overridden, the dialog automatically ends when the user
* replies.</p>
* <p>
* If this method is *not* overridden, the dialog automatically ends when the
* user replies.
* </p>
*
* @param dc The {@link DialogContext} for the current turn of conversation.
* @return If the task is successful, the result indicates whether the dialog is still active
* after the turn has been processed by the dialog. The result may also contain a return value.
* @return If the task is successful, the result indicates whether the dialog is
* still active after the turn has been processed by the dialog. The
* result may also contain a return value.
*/
public CompletableFuture<DialogTurnResult> continueDialog(DialogContext dc) {
// By default just end the current dialog.
@ -130,46 +135,54 @@ public abstract class Dialog {
}
/**
* Called when a child dialog completed this turn, returning control to this dialog.
* Called when a child dialog completed this turn, returning control to this
* dialog.
*
* <p>Generally, the child dialog was started with a call to
* {@link #beginDialog(DialogContext, Object)} However, if the {@link
* DialogContext#replaceDialog(String)} method is called, the logical child dialog may be
* different than the original.</p>
* <p>
* Generally, the child dialog was started with a call to
* {@link #beginDialog(DialogContext, Object)} However, if the
* {@link DialogContext#replaceDialog(String)} method is called, the logical
* child dialog may be different than the original.
* </p>
*
* <p>If this method is *not* overridden, the dialog automatically ends when the user
* replies.</p>
* <p>
* If this method is *not* overridden, the dialog automatically ends when the
* user replies.
* </p>
*
* @param dc The dialog context for the current turn of the conversation.
* @param reason Reason why the dialog resumed.
* @return If the task is successful, the result indicates whether this dialog is still active
* after this dialog turn has been processed.
* @return If the task is successful, the result indicates whether this dialog
* is still active after this dialog turn has been processed.
*/
public CompletableFuture<DialogTurnResult> resumeDialog(DialogContext dc, DialogReason reason) {
return resumeDialog(dc, reason, null);
}
/**
* Called when a child dialog completed this turn, returning control to this dialog.
* Called when a child dialog completed this turn, returning control to this
* dialog.
*
* <p>Generally, the child dialog was started with a call to
* {@link #beginDialog(DialogContext, Object)} However, if the {@link
* DialogContext#replaceDialog(String, Object)} method is called, the logical child dialog may
* be different than the original.</p>
* <p>
* Generally, the child dialog was started with a call to
* {@link #beginDialog(DialogContext, Object)} However, if the
* {@link DialogContext#replaceDialog(String, Object)} method is called, the
* logical child dialog may be different than the original.
* </p>
*
* <p>If this method is *not* overridden, the dialog automatically ends when the user
* replies.</p>
* <p>
* If this method is *not* overridden, the dialog automatically ends when the
* user replies.
* </p>
*
* @param dc The dialog context for the current turn of the conversation.
* @param reason Reason why the dialog resumed.
* @param result Optional, value returned from the dialog that was called. The type of the value
* returned is dependent on the child dialog.
* @return If the task is successful, the result indicates whether this dialog is still active
* after this dialog turn has been processed.
* @param result Optional, value returned from the dialog that was called. The
* type of the value returned is dependent on the child dialog.
* @return If the task is successful, the result indicates whether this dialog
* is still active after this dialog turn has been processed.
*/
public CompletableFuture<DialogTurnResult> resumeDialog(
DialogContext dc, DialogReason reason, Object result
) {
public CompletableFuture<DialogTurnResult> resumeDialog(DialogContext dc, DialogReason reason, Object result) {
// By default just end the current dialog and return result to parent.
return dc.endDialog(result);
}
@ -181,9 +194,7 @@ public abstract class Dialog {
* @param instance State information for this dialog.
* @return A CompletableFuture representing the asynchronous operation.
*/
public CompletableFuture<Void> repromptDialog(
TurnContext turnContext, DialogInstance instance
) {
public CompletableFuture<Void> repromptDialog(TurnContext turnContext, DialogInstance instance) {
// No-op by default
return CompletableFuture.completedFuture(null);
}
@ -192,24 +203,23 @@ public abstract class Dialog {
* Called when the dialog is ending.
*
* @param turnContext The context object for this turn.
* @param instance State information associated with the instance of this dialog on the
* dialog stack.
* @param instance State information associated with the instance of this
* dialog on the dialog stack.
* @param reason Reason why the dialog ended.
* @return A CompletableFuture representing the asynchronous operation.
*/
public CompletableFuture<Void> endDialog(
TurnContext turnContext, DialogInstance instance, DialogReason reason
) {
public CompletableFuture<Void> endDialog(TurnContext turnContext, DialogInstance instance, DialogReason reason) {
// No-op by default
return CompletableFuture.completedFuture(null);
}
/**
* Gets a unique String which represents the version of this dialog. If the version changes
* between turns the dialog system will emit a DialogChanged event.
* Gets a unique String which represents the version of this dialog. If the
* version changes between turns the dialog system will emit a DialogChanged
* event.
*
* @return Unique String which should only change when dialog has changed in a way that should
* restart the dialog.
* @return Unique String which should only change when dialog has changed in a
* way that should restart the dialog.
*/
@JsonIgnore
public String getVersion() {
@ -217,46 +227,47 @@ public abstract class Dialog {
}
/**
* Called when an event has been raised, using `DialogContext.emitEvent()`, by either the
* current dialog or a dialog that the current dialog started.
* Called when an event has been raised, using `DialogContext.emitEvent()`, by
* either the current dialog or a dialog that the current dialog started.
*
* @param dc The dialog context for the current turn of conversation.
* @param e The event being raised.
* @return True if the event is handled by the current dialog and bubbling should stop.
* @return True if the event is handled by the current dialog and bubbling
* should stop.
*/
public CompletableFuture<Boolean> onDialogEvent(DialogContext dc, DialogEvent e) {
// Before bubble
return onPreBubbleEvent(dc, e)
.thenCompose(handled -> {
// Bubble as needed
if (!handled && e.shouldBubble() && dc.getParent() != null) {
return dc.getParent().emitEvent(e.getName(), e.getValue(), true, false);
}
return onPreBubbleEvent(dc, e).thenCompose(handled -> {
// Bubble as needed
if (!handled && e.shouldBubble() && dc.getParent() != null) {
return dc.getParent().emitEvent(e.getName(), e.getValue(), true, false);
}
// just pass the handled value to the next stage
return CompletableFuture.completedFuture(handled);
})
.thenCompose(handled -> {
if (!handled) {
// Post bubble
return onPostBubbleEvent(dc, e);
}
// just pass the handled value to the next stage
return CompletableFuture.completedFuture(handled);
}).thenCompose(handled -> {
if (!handled) {
// Post bubble
return onPostBubbleEvent(dc, e);
}
return CompletableFuture.completedFuture(handled);
});
return CompletableFuture.completedFuture(handled);
});
}
/**
* Called before an event is bubbled to its parent.
*
* <p>This is a good place to perform interception of an event as returning `true` will prevent
* any further bubbling of the event to the dialogs parents and will also prevent any child
* dialogs from performing their default processing.</p>
* <p>
* This is a good place to perform interception of an event as returning `true`
* will prevent any further bubbling of the event to the dialogs parents and
* will also prevent any child dialogs from performing their default processing.
* </p>
*
* @param dc The dialog context for the current turn of conversation.
* @param e The event being raised.
* @return Whether the event is handled by the current dialog and further processing should
* stop.
* @return Whether the event is handled by the current dialog and further
* processing should stop.
*/
protected CompletableFuture<Boolean> onPreBubbleEvent(DialogContext dc, DialogEvent e) {
return CompletableFuture.completedFuture(false);
@ -265,14 +276,15 @@ public abstract class Dialog {
/**
* Called after an event was bubbled to all parents and wasn't handled.
*
* <p>This is a good place to perform default processing logic for an event. Returning `true`
* will
* prevent any processing of the event by child dialogs.</p>
* <p>
* This is a good place to perform default processing logic for an event.
* Returning `true` will prevent any processing of the event by child dialogs.
* </p>
*
* @param dc The dialog context for the current turn of conversation.
* @param e The event being raised.
* @return Whether the event is handled by the current dialog and further processing should
* stop.
* @return Whether the event is handled by the current dialog and further
* processing should stop.
*/
protected CompletableFuture<Boolean> onPostBubbleEvent(DialogContext dc, DialogEvent e) {
return CompletableFuture.completedFuture(false);
@ -292,82 +304,139 @@ public abstract class Dialog {
*
* @param dialog The dialog to start.
* @param turnContext The context for the current turn of the conversation.
* @param accessor The StatePropertyAccessor accessor with which to manage the state of the
* dialog stack.
* @param accessor The StatePropertyAccessor accessor with which to manage
* the state of the dialog stack.
* @return A Task representing the asynchronous operation.
*/
public static CompletableFuture<Void> run(
Dialog dialog,
TurnContext turnContext,
StatePropertyAccessor<DialogState> accessor
) {
public static CompletableFuture<Void> run(Dialog dialog, TurnContext turnContext,
StatePropertyAccessor<DialogState> accessor) {
DialogSet dialogSet = new DialogSet(accessor);
dialogSet.add(dialog);
dialogSet.setTelemetryClient(dialog.getTelemetryClient());
// return dialogSet.createContext(turnContext)
// .thenCompose(dialogContext -> continueOrStart(dialogContext, dialog,
// turnContext))
// .thenApply(result -> null);
return dialogSet.createContext(turnContext)
.thenCompose(dialogContext -> continueOrStart(dialogContext, dialog, turnContext))
.thenApply(result -> null);
.thenAccept(dialogContext -> innerRun(turnContext, dialog.getId(), dialogContext));
}
private static CompletableFuture<Void> continueOrStart(
DialogContext dialogContext, Dialog dialog, TurnContext turnContext
) {
/**
* Shared implementation of run with Dialog and DialogManager.
*
* @param turnContext The turnContext.
* @param dialogId The Id of the Dialog.
* @param dialogContext The DialogContext.
* @return A DialogTurnResult.
*/
protected static CompletableFuture<DialogTurnResult> innerRun(TurnContext turnContext, String dialogId,
DialogContext dialogContext) {
for (Entry<String, Object> entry : turnContext.getTurnState().getTurnStateServices().entrySet()) {
dialogContext.getServices().replace(entry.getKey(), entry.getValue());
}
DialogStateManager dialogStateManager = new DialogStateManager(dialogContext);
return dialogStateManager.loadAllScopes().thenCompose(result -> {
dialogContext.getContext().getTurnState().add(dialogStateManager);
DialogTurnResult dialogTurnResult = null;
boolean endOfTurn = false;
while (!endOfTurn) {
try {
dialogTurnResult = continueOrStart(dialogContext, dialogId, turnContext).join();
endOfTurn = true;
} catch (Exception err) {
// fire error event, bubbling from the leaf.
boolean handled = dialogContext.emitEvent(DialogEvents.ERROR, err, true, true).join();
if (!handled) {
// error was NOT handled, return a result that signifies that the call was unsuccssfull
// (This will trigger the Adapter.OnError handler and end the entire dialog stack)
return Async.completeExceptionally(err);
}
}
}
return CompletableFuture.completedFuture(dialogTurnResult);
});
}
private static CompletableFuture<DialogTurnResult> continueOrStart(DialogContext dialogContext, String dialogId,
TurnContext turnContext) {
if (DialogCommon.isFromParentToSkill(turnContext)) {
// Handle remote cancellation request from parent.
if (turnContext.getActivity().getType().equals(ActivityTypes.END_OF_CONVERSATION)) {
if (dialogContext.getStack().size() == 0) {
// No dialogs to cancel, just return.
return CompletableFuture.completedFuture(null);
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.EMPTY));
}
DialogContext activeDialogContext = getActiveDialogContext(dialogContext);
// Send cancellation message to the top dialog in the stack to ensure all the parents
// Send cancellation message to the top dialog in the stack to ensure all the
// parents
// are canceled in the right order.
return activeDialogContext.cancelAllDialogs(true, null, null).thenApply(result -> null);
return activeDialogContext.cancelAllDialogs(true, null, null);
}
// Handle a reprompt event sent from the parent.
if (turnContext.getActivity().getType().equals(ActivityTypes.EVENT)
&& turnContext.getActivity().getName().equals(DialogEvents.REPROMPT_DIALOG)) {
&& turnContext.getActivity().getName().equals(DialogEvents.REPROMPT_DIALOG)) {
if (dialogContext.getStack().size() == 0) {
// No dialogs to reprompt, just return.
return CompletableFuture.completedFuture(null);
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.EMPTY));
}
return dialogContext.repromptDialog();
return dialogContext.repromptDialog()
.thenApply(result -> new DialogTurnResult(DialogTurnStatus.WAITING));
}
}
return dialogContext.continueDialog()
.thenCompose(result -> {
if (result.getStatus() == DialogTurnStatus.EMPTY) {
return dialogContext.beginDialog(dialog.getId(), null).thenCompose(finalResult -> {
return processEOC(finalResult, turnContext);
});
}
return processEOC(result, turnContext);
});
return dialogContext.continueDialog().thenCompose(result -> {
if (result.getStatus() == DialogTurnStatus.EMPTY) {
return dialogContext.beginDialog(dialogId, null).thenCompose(finalResult -> {
return processEOC(dialogContext, finalResult, turnContext);
});
}
return processEOC(dialogContext, result, turnContext);
});
}
private static CompletableFuture<Void> processEOC(DialogTurnResult result, TurnContext turnContext) {
if (result.getStatus() == DialogTurnStatus.COMPLETE
|| result.getStatus() == DialogTurnStatus.CANCELLED
&& sendEoCToParent(turnContext)) {
private static CompletableFuture<DialogTurnResult> processEOC(DialogContext dialogContext, DialogTurnResult result,
TurnContext turnContext) {
return sendStateSnapshotTrace(dialogContext).thenCompose(snapshotResult -> {
if ((result.getStatus() == DialogTurnStatus.COMPLETE
|| result.getStatus() == DialogTurnStatus.CANCELLED) && sendEoCToParent(turnContext)) {
EndOfConversationCodes code = result.getStatus() == DialogTurnStatus.COMPLETE
? EndOfConversationCodes.COMPLETED_SUCCESSFULLY
: EndOfConversationCodes.USER_CANCELLED;
? EndOfConversationCodes.COMPLETED_SUCCESSFULLY
: EndOfConversationCodes.USER_CANCELLED;
Activity activity = new Activity(ActivityTypes.END_OF_CONVERSATION);
activity.setValue(result.getResult());
activity.setLocale(turnContext.getActivity().getLocale());
activity.setCode(code);
return turnContext.sendActivity(activity).thenApply(finalResult -> null);
turnContext.sendActivity(activity).join();
}
return CompletableFuture.completedFuture(result);
});
}
private static CompletableFuture<Void> sendStateSnapshotTrace(DialogContext dialogContext) {
String traceLabel = "";
Object identity = dialogContext.getContext().getTurnState().get(BotAdapter.BOT_IDENTITY_KEY);
if (identity instanceof ClaimsIdentity) {
traceLabel = SkillValidation.isSkillClaim(((ClaimsIdentity) identity).claims()) ? "Skill State"
: "Bot State";
}
return CompletableFuture.completedFuture(null);
// send trace of memory
JsonNode snapshot = getActiveDialogContext(dialogContext).getState().getMemorySnapshot();
Activity traceActivity = Activity.createTraceActivity("BotState",
"https://www.botframework.com/schemas/botState", snapshot, traceLabel);
return dialogContext.getContext().sendActivity(traceActivity).thenApply(result -> null);
}
/**
* Helper to determine if we should send an EoC to the parent or not.
*
* @param turnContext
* @return
*/
@ -376,17 +445,19 @@ public abstract class Dialog {
ClaimsIdentity claimsIdentity = turnContext.getTurnState().get(BotAdapter.BOT_IDENTITY_KEY);
if (claimsIdentity != null && SkillValidation.isSkillClaim(claimsIdentity.claims())) {
// EoC Activities returned by skills are bounced back to the bot by SkillHandler.
// EoC Activities returned by skills are bounced back to the bot by
// SkillHandler.
// In those cases we will have a SkillConversationReference instance in state.
SkillConversationReference skillConversationReference =
turnContext.getTurnState().get(SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY);
SkillConversationReference skillConversationReference = turnContext.getTurnState()
.get(SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY);
if (skillConversationReference != null) {
// If the skillConversationReference.OAuthScope is for one of the supported channels,
// If the skillConversationReference.OAuthScope is for one of the supported
// channels,
// we are at the root and we should not send an EoC.
return skillConversationReference.getOAuthScope()
!= AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
&& skillConversationReference.getOAuthScope()
!= GovernmentAuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE;
return skillConversationReference
.getOAuthScope() != AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
&& skillConversationReference
.getOAuthScope() != GovernmentAuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE;
}
return true;
}

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

@ -9,26 +9,14 @@ import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.bot.builder.BotAdapter;
import com.microsoft.bot.builder.BotStateSet;
import com.microsoft.bot.builder.ConversationState;
import com.microsoft.bot.builder.StatePropertyAccessor;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.TurnContextStateCollection;
import com.microsoft.bot.builder.UserState;
import com.microsoft.bot.builder.skills.SkillConversationReference;
import com.microsoft.bot.builder.skills.SkillHandler;
import com.microsoft.bot.connector.Async;
import com.microsoft.bot.connector.authentication.AuthenticationConstants;
import com.microsoft.bot.connector.authentication.ClaimsIdentity;
import com.microsoft.bot.connector.authentication.GovernmentAuthenticationConstants;
import com.microsoft.bot.connector.authentication.SkillValidation;
import com.microsoft.bot.dialogs.memory.DialogStateManager;
import com.microsoft.bot.dialogs.memory.DialogStateManagerConfiguration;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.EndOfConversationCodes;
/**
* Class which runs the dialog system.
@ -266,175 +254,13 @@ public class DialogManager {
// Create DialogContext
DialogContext dc = new DialogContext(dialogs, context, dialogState);
dc.getServices().getTurnStateServices().forEach((key, value) -> {
dc.getServices().add(key, value);
return Dialog.innerRun(context, rootDialogId, dc).thenCompose(turnResult -> {
return botStateSet.saveAllChanges(dc.getContext(), false).thenCompose(saveResult -> {
DialogManagerResult result = new DialogManagerResult();
result.setTurnResult(turnResult);
return CompletableFuture.completedFuture(result);
});
});
// map TurnState into root dialog context.services
context.getTurnState().getTurnStateServices().forEach((key, value) -> {
dc.getServices().add(key, value);
});
// get the DialogStateManager configuration
DialogStateManager dialogStateManager = new DialogStateManager(dc, stateManagerConfiguration);
dialogStateManager.loadAllScopesAsync().join();
dc.getContext().getTurnState().add(dialogStateManager);
DialogTurnResult turnResult = null;
// Loop as long as we are getting valid OnError handled we should continue
// executing the
// actions for the turn.
// NOTE: We loop around this block because each pass through we either complete
// the turn and
// break out of the loop
// or we have had an exception AND there was an OnError action which captured
// the error.
// We need to continue the turn based on the actions the OnError handler
// introduced.
Boolean endOfTurn = false;
while (!endOfTurn) {
try {
ClaimsIdentity claimIdentity = context.getTurnState().get(BotAdapter.BOT_IDENTITY_KEY);
if (claimIdentity != null && SkillValidation.isSkillClaim(claimIdentity.claims())) {
// The bot is running as a skill.
turnResult = handleSkillOnTurn(dc).join();
} else {
// The bot is running as root bot.
turnResult = handleBotOnTurn(dc).join();
}
// turn successfully completed, break the loop
endOfTurn = true;
} catch (Exception err) {
// fire error event, bubbling from the leaf.
Boolean handled = dc.emitEvent(DialogEvents.ERROR, err, true, true).join();
if (!handled) {
// error was NOT handled, throw the exception and end the turn. (This will
// trigger
// the Adapter.OnError handler and end the entire dialog stack)
return Async.completeExceptionally(new RuntimeException(err));
}
}
}
// save all state scopes to their respective botState locations.
dialogStateManager.saveAllChanges();
// save BotState changes
botStateSet.saveAllChanges(dc.getContext(), false);
DialogManagerResult result = new DialogManagerResult();
result.setTurnResult(turnResult);
return CompletableFuture.completedFuture(result);
}
/// <summary>
/// Helper to send a trace activity with a memory snapshot of the active dialog
/// DC.
/// </summary>
private static CompletableFuture<Void> sendStateSnapshotTrace(DialogContext dc, String traceLabel) {
// send trace of memory
JsonNode snapshot = getActiveDialogContext(dc).getState().getMemorySnapshot();
Activity traceActivity = (Activity) Activity.createTraceActivity("Bot State",
"https://www.botframework.com/schemas/botState", snapshot, traceLabel);
dc.getContext().sendActivity(traceActivity).join();
return CompletableFuture.completedFuture(null);
}
/**
* Recursively walk up the DC stack to find the active DC.
*/
private static DialogContext getActiveDialogContext(DialogContext dialogContext) {
DialogContext child = dialogContext.getChild();
if (child == null) {
return dialogContext;
} else {
return getActiveDialogContext(child);
}
}
private CompletableFuture<DialogTurnResult> handleSkillOnTurn(DialogContext dc) {
// the bot instanceof running as a skill.
TurnContext turnContext = dc.getContext();
// Process remote cancellation
if (turnContext.getActivity().getType().equals(ActivityTypes.END_OF_CONVERSATION)
&& dc.getActiveDialog() != null
&& DialogCommon.isFromParentToSkill(turnContext)) {
// Handle remote cancellation request from parent.
DialogContext activeDialogContext = getActiveDialogContext(dc);
// Send cancellation message to the top dialog in the stack to ensure all the
// parents are canceled in the right order.
return activeDialogContext.cancelAllDialogs();
}
// Handle reprompt
// Process a reprompt event sent from the parent.
if (turnContext.getActivity().getType().equals(ActivityTypes.EVENT)
&& turnContext.getActivity().getName().equals(DialogEvents.REPROMPT_DIALOG)) {
if (dc.getActiveDialog() == null) {
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.EMPTY));
}
dc.repromptDialog();
return CompletableFuture.completedFuture(new DialogTurnResult(DialogTurnStatus.WAITING));
}
// Continue execution
// - This will apply any queued up interruptions and execute the current/next step(s).
DialogTurnResult turnResult = dc.continueDialog().join();
if (turnResult.getStatus().equals(DialogTurnStatus.EMPTY)) {
// restart root dialog
turnResult = dc.beginDialog(rootDialogId).join();
}
sendStateSnapshotTrace(dc, "Skill State");
if (shouldSendEndOfConversationToParent(turnContext, turnResult)) {
// Send End of conversation at the end.
EndOfConversationCodes code = turnResult.getStatus().equals(DialogTurnStatus.COMPLETE)
? EndOfConversationCodes.COMPLETED_SUCCESSFULLY
: EndOfConversationCodes.USER_CANCELLED;
Activity activity = new Activity(ActivityTypes.END_OF_CONVERSATION);
activity.setValue(turnResult.getResult());
activity.setLocale(turnContext.getActivity().getLocale());
activity.setCode(code);
turnContext.sendActivity(activity).join();
}
return CompletableFuture.completedFuture(turnResult);
}
/**
* Helper to determine if we should send an EndOfConversation to the parent
* or not.
*/
private static boolean shouldSendEndOfConversationToParent(TurnContext context, DialogTurnResult turnResult) {
if (!(turnResult.getStatus().equals(DialogTurnStatus.COMPLETE)
|| turnResult.getStatus().equals(DialogTurnStatus.CANCELLED))) {
// The dialog instanceof still going, don't return EoC.
return false;
}
ClaimsIdentity claimsIdentity = context.getTurnState().get(BotAdapter.BOT_IDENTITY_KEY);
if (claimsIdentity != null && SkillValidation.isSkillClaim(claimsIdentity.claims())) {
// EoC Activities returned by skills are bounced back to the bot by SkillHandler.
// In those cases we will have a SkillConversationReference instance in state.
SkillConversationReference skillConversationReference =
context.getTurnState().get(SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY);
if (skillConversationReference != null) {
// If the skillConversationReference.OAuthScope instanceof for one of the supported channels,
// we are at the root and we should not send an EoC.
return skillConversationReference.getOAuthScope()
!= AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
&& skillConversationReference.getOAuthScope()
!= GovernmentAuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE;
}
return true;
}
return false;
}
/**
@ -459,28 +285,4 @@ public class DialogManager {
}
}
}
private CompletableFuture<DialogTurnResult> handleBotOnTurn(DialogContext dc) {
DialogTurnResult turnResult;
// the bot is running as a root bot.
if (dc.getActiveDialog() == null) {
// start root dialog
turnResult = dc.beginDialog(rootDialogId).join();
} else {
// Continue execution
// - This will apply any queued up interruptions and execute the current/next
// step(s).
turnResult = dc.continueDialog().join();
if (turnResult.getStatus().equals(DialogTurnStatus.EMPTY)) {
// restart root dialog
turnResult = dc.beginDialog(rootDialogId).join();
}
}
sendStateSnapshotTrace(dc, "BotState").join();
return CompletableFuture.completedFuture(turnResult);
}
}

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

@ -54,6 +54,17 @@ public class DialogStateManager implements Map<String, Object> {
private ObjectMapper mapper = new ObjectMapper().findAndRegisterModules();
/**
* Initializes a new instance of the
* {@link com.microsoft.bot.dialogs.memory.DialogStateManager} class.
*
* @param dc The dialog context for the current turn of the
* conversation.
*/
public DialogStateManager(DialogContext dc) {
this(dc, null);
}
/**
* Initializes a new instance of the
* {@link com.microsoft.bot.dialogs.memory.DialogStateManager} class.
@ -455,7 +466,7 @@ public class DialogStateManager implements Map<String, Object> {
*
* @return A Completed Future.
*/
public CompletableFuture<Void> loadAllScopesAsync() {
public CompletableFuture<Void> loadAllScopes() {
configuration.getMemoryScopes().forEach((scope) -> {
scope.load(dialogContext, false).join();
});

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

@ -104,7 +104,7 @@ public class DialogManagerTests {
Dialog adaptiveDialog = CreateTestDialog("conversation.name");
CreateFlow(adaptiveDialog, storage, firstConversationId, "dialogState", null, null)
CreateFlow(adaptiveDialog, storage, firstConversationId, "DialogState", null, null)
.send("hi")
.assertReply("Hello, what is your name?")
.send("Carlos")