diff --git a/libraries/Microsoft.Bot.Builder/EventFactory.cs b/libraries/Microsoft.Bot.Builder/EventFactory.cs new file mode 100644 index 0000000000..c28fc966c2 --- /dev/null +++ b/libraries/Microsoft.Bot.Builder/EventFactory.cs @@ -0,0 +1,100 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Bot.Schema; + +namespace Microsoft.Bot.Builder +{ + /// + /// Contains utility methods for creating various event types. + /// + public static class EventFactory + { + /// + /// Create handoff initiation event. + /// + /// turn context. + /// agent hub-specific context. + /// transcript of the conversation. + /// handoff event. + public static IEventActivity CreateHandoffInitiation(ITurnContext turnContext, object handoffContext, Transcript transcript = null) + { + if (turnContext == null) + { + throw new ArgumentNullException(nameof(turnContext)); + } + + var handoffEvent = CreateHandoffEvent(HandoffEventNames.InitiateHandoff, handoffContext, turnContext.Activity.Conversation); + + handoffEvent.From = turnContext.Activity.From; + handoffEvent.RelatesTo = turnContext.Activity.GetConversationReference(); + handoffEvent.ReplyToId = turnContext.Activity.Id; + handoffEvent.ServiceUrl = turnContext.Activity.ServiceUrl; + handoffEvent.ChannelId = turnContext.Activity.ChannelId; + + if (transcript != null) + { + var attchment = new Attachment + { + Content = transcript, + ContentType = "application/json", + Name = "Transcript", + }; + handoffEvent.Attachments.Add(attchment); + } + + return handoffEvent; + } + + /// + /// Create handoff status event. + /// + /// Conversation being handed over. + /// State, possible values are: "accepted", "failed", "completed". + /// Additional message for failed handoff. + /// handoff event. + public static IEventActivity CreateHandoffStatus(ConversationAccount conversation, string state, string message = null) + { + if (conversation == null) + { + throw new ArgumentNullException(nameof(conversation)); + } + + if (state == null) + { + throw new ArgumentNullException(nameof(state)); + } + + object value; + + if (string.IsNullOrEmpty(message)) + { + value = new { state }; + } + else + { + value = new { state, message }; + } + + var handoffEvent = CreateHandoffEvent(HandoffEventNames.HandoffStatus, value, conversation); + return handoffEvent; + } + + private static Activity CreateHandoffEvent(string name, object value, ConversationAccount conversation) + { + var handoffEvent = Activity.CreateEventActivity() as Activity; + + handoffEvent.Name = name; + handoffEvent.Value = value; + handoffEvent.Id = Guid.NewGuid().ToString(); + handoffEvent.Timestamp = DateTime.UtcNow; + handoffEvent.Conversation = conversation; + handoffEvent.Attachments = new List(); + handoffEvent.Entities = new List(); + return handoffEvent; + } + } +} diff --git a/libraries/Microsoft.Bot.Builder/HandoffEventNames.cs b/libraries/Microsoft.Bot.Builder/HandoffEventNames.cs new file mode 100644 index 0000000000..cedfb74bb8 --- /dev/null +++ b/libraries/Microsoft.Bot.Builder/HandoffEventNames.cs @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Bot.Schema +{ + /// + /// Defines values for handoff event names. + /// + public static class HandoffEventNames + { + public const string InitiateHandoff = "handoff.initiate"; + public const string HandoffStatus = "handoff.status"; + } +} diff --git a/tests/Microsoft.Bot.Builder.Tests/EventFactoryTests.cs b/tests/Microsoft.Bot.Builder.Tests/EventFactoryTests.cs new file mode 100644 index 0000000000..80848d4e7b --- /dev/null +++ b/tests/Microsoft.Bot.Builder.Tests/EventFactoryTests.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Mime; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Bot.Builder.Adapters; +using Microsoft.Bot.Schema; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace Microsoft.Bot.Builder.Tests +{ + [TestClass] + [TestCategory("Message")] + public class EventFactoryTests + { + public TestContext TestContext { get; set; } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void HandoffInitiationNullTurnContext() + { + EventFactory.CreateHandoffInitiation(null, "some text"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void HandoffStatusNullConversation() + { + EventFactory.CreateHandoffStatus(null, "accepted"); + } + + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void HandoffStatusNullStatus() + { + EventFactory.CreateHandoffStatus(new ConversationAccount(), null); + } + + [TestMethod] + public void TestCreateHandoffInitiation() + { + var adapter = new TestAdapter(TestAdapter.CreateConversation(TestContext.TestName)); + string fromID = "test"; + var activity = new Activity + { + Type = ActivityTypes.Message, + Text = string.Empty, + Conversation = new ConversationAccount(), + Recipient = new ChannelAccount(), + From = new ChannelAccount(fromID), + ChannelId = "testchannel", + ServiceUrl = "http://myservice" + }; + var context = new TurnContext(adapter, activity); + + var transcript = new Transcript(new Activity[] { MessageFactory.Text("hello") }); + + Assert.IsNull(transcript.Activities[0].ChannelId); + Assert.IsNull(transcript.Activities[0].ServiceUrl); + Assert.IsNull(transcript.Activities[0].Conversation); + + var handoffEvent = EventFactory.CreateHandoffInitiation(context, new { Skill = "any" }, transcript); + Assert.AreEqual(handoffEvent.Name, HandoffEventNames.InitiateHandoff); + + Assert.AreEqual(handoffEvent.From.Id, fromID); + } + + [TestMethod] + public void TestCreateHandoffStatus() + { + var state = "failed"; + var message = "timed out"; + var handoffEvent = EventFactory.CreateHandoffStatus(new ConversationAccount(), state, message); + Assert.AreEqual(handoffEvent.Name, HandoffEventNames.HandoffStatus); + string status = JsonConvert.SerializeObject(handoffEvent.Value, Formatting.None); + Assert.AreEqual(status, $"{{\"state\":\"{state}\",\"message\":\"{message}\"}}"); + Assert.IsNotNull((handoffEvent as Activity).Attachments); + Assert.IsNotNull(handoffEvent.Id); + } + } +}