diff --git a/libraries/botbuilder-dialogs/src/dialogHelper.ts b/libraries/botbuilder-dialogs/src/dialogHelper.ts index e344fe46a7..474aac2f9a 100644 --- a/libraries/botbuilder-dialogs/src/dialogHelper.ts +++ b/libraries/botbuilder-dialogs/src/dialogHelper.ts @@ -6,6 +6,22 @@ import { DialogSet } from './dialogSet'; import { isSkillClaim } from './prompts/skillsHelpers'; export async function runDialog(dialog: Dialog, context: TurnContext, accessor: StatePropertyAccessor): Promise { + if (!dialog) { + throw new Error('runDialog(): missing dialog'); + } + + if (!context) { + throw new Error('runDialog(): missing context'); + } + + if (!context.activity) { + throw new Error('runDialog(): missing context.activity'); + } + + if (!accessor) { + throw new Error('runDialog(): missing accessor'); + } + const dialogSet = new DialogSet(accessor); dialogSet.telemetryClient = dialog.telemetryClient; dialogSet.add(dialog); @@ -16,11 +32,11 @@ export async function runDialog(dialog: Dialog, context: TurnContext, accessor: const identity = context.turnState.get(context.adapter.BotIdentityKey); if (identity && isSkillClaim(identity.claims)) { // The bot is running as a skill. - if (context.activity.type === ActivityTypes.EndOfConversation && dialogContext.stack.length > 0) { + if (context.activity.type === ActivityTypes.EndOfConversation && dialogContext.stack.length > 0 && isEocComingFromParent(context)) { // Handle remote cancellation request if we have something in the stack. const activeDialogContext = getActiveDialogContext(dialogContext); - const remoteCancelText = 'Skill was canceled by a request from the host.'; + const remoteCancelText = 'Skill was canceled through an EndOfConversation activity from the parent.'; await context.sendTraceActivity(telemetryEventName, undefined, undefined, `${ remoteCancelText }`); // Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the right order. @@ -68,3 +84,12 @@ function getActiveDialogContext(dialogContext: DialogContext): DialogContext { return getActiveDialogContext(child); } + +// We should only cancel the current dialog stack if the EoC activity is coming from a parent (a root bot or another skill). +// When the EoC is coming back from a child, we should just process that EoC normally through the +// dialog stack and let the child dialogs handle that. +function isEocComingFromParent(context: TurnContext): boolean { + // To determine the direction we check callerId property which is set to the parent bot + // by the BotFrameworkHttpClient on outgoing requests. + return !(!!context.activity.callerId); +} diff --git a/libraries/botbuilder-dialogs/tests/dialogHelper.test.js b/libraries/botbuilder-dialogs/tests/dialogHelper.test.js new file mode 100644 index 0000000000..05531b60f5 --- /dev/null +++ b/libraries/botbuilder-dialogs/tests/dialogHelper.test.js @@ -0,0 +1,40 @@ +const { strictEqual } = require('assert'); +const { runDialog } = require('../'); + +describe('runDialog()', function() { + this.timeout(300); + + describe('parameter validation', () => { + it('should throw if missing dialog parameter', (done) => { + runDialog().then( + () => done(new Error('should have throw error')), + (err) => { + done(strictEqual(err.message, 'runDialog(): missing dialog')); + }); + }); + + it('should throw if missing context parameter', (done) => { + runDialog({}).then( + () => done(new Error('should have throw error')), + (err) => { + done(strictEqual(err.message, 'runDialog(): missing context')); + }); + }); + + it('should throw if missing context.activity', (done) => { + runDialog({}, {}).then( + () => done(new Error('should have throw error')), + (err) => { + done(strictEqual(err.message, 'runDialog(): missing context.activity')); + }); + }); + + it('should throw if missing accessor parameter', (done) => { + runDialog({}, { activity: {} }).then( + () => done(new Error('should have throw error')), + (err) => { + done(strictEqual(err.message, 'runDialog(): missing accessor')); + }); + }); + }); +}); diff --git a/libraries/botbuilder/src/botFrameworkHttpClient.ts b/libraries/botbuilder/src/botFrameworkHttpClient.ts index 1cc46248ee..f19127927c 100644 --- a/libraries/botbuilder/src/botFrameworkHttpClient.ts +++ b/libraries/botbuilder/src/botFrameworkHttpClient.ts @@ -90,7 +90,7 @@ export class BotFrameworkHttpClient extends BotFrameworkClient { }; activity.conversation.id = conversationId; activity.serviceUrl = serviceUrl; - activity.callerId = fromBotId; + activity.callerId = `urn:botframework:aadappid:${ fromBotId }`; const config = { headers: { Accept: 'application/json',