Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
Added Event Factory for handoff protocol (#1118)
Browse files Browse the repository at this point in the history
* Added Event Factory for handoff protocol

* Added additional unit test.

Co-authored-by: tracyboehrer <[email protected]>
  • Loading branch information
LeeParrishMSFT and tracyboehrer authored Apr 1, 2021
1 parent 42edeb3 commit 45130e6
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MT License.

package com.microsoft.bot.builder;

import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.UUID;

import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.Attachment;
import com.microsoft.bot.schema.ConversationAccount;
import com.microsoft.bot.schema.Entity;
import com.microsoft.bot.schema.HandoffEventNames;
import com.microsoft.bot.schema.Transcript;

import org.apache.commons.lang3.StringUtils;

/**
* Contains utility methods for creating various event types.
*/
public final class EventFactory {

private EventFactory() {

}

/**
* Create handoff initiation event.
*
* @param turnContext turn context.
* @param handoffContext agent hub-specific context.
*
* @return handoff event.
*/
public static Activity createHandoffInitiation(TurnContext turnContext, Object handoffContext) {
return createHandoffInitiation(turnContext, handoffContext, null);
}


/**
* Create handoff initiation event.
*
* @param turnContext turn context.
* @param handoffContext agent hub-specific context.
* @param transcript transcript of the conversation.
*
* @return handoff event.
*/
public static Activity createHandoffInitiation(TurnContext turnContext, Object handoffContext,
Transcript transcript) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null.");
}

Activity handoffEvent = createHandoffEvent(HandoffEventNames.INITIATEHANDOFF, handoffContext,
turnContext.getActivity().getConversation());

handoffEvent.setFrom(turnContext.getActivity().getFrom());
handoffEvent.setRelatesTo(turnContext.getActivity().getConversationReference());
handoffEvent.setReplyToId(turnContext.getActivity().getId());
handoffEvent.setServiceUrl(turnContext.getActivity().getServiceUrl());
handoffEvent.setChannelId(turnContext.getActivity().getChannelId());

if (transcript != null) {
Attachment attachment = new Attachment();
attachment.setContent(transcript);
attachment.setContentType("application/json");
attachment.setName("Transcript");
handoffEvent.getAttachments().add(attachment);
}

return handoffEvent;
}


/**
* Create handoff status event.
*
* @param conversation Conversation being handed over.
* @param state State, possible values are: "accepted", "failed",
* "completed".
*
* @return handoff event.
*/
public static Activity createHandoffStatus(ConversationAccount conversation, String state) {
return createHandoffStatus(conversation, state, null);
}

/**
* Create handoff status event.
*
* @param conversation Conversation being handed over.
* @param state State, possible values are: "accepted", "failed",
* "completed".
* @param message Additional message for failed handoff.
*
* @return handoff event.
*/
public static Activity createHandoffStatus(ConversationAccount conversation, String state, String message) {
if (conversation == null) {
throw new IllegalArgumentException("conversation cannot be null.");
}

if (state == null) {
throw new IllegalArgumentException("state cannot be null.");
}

ObjectNode handoffContext = JsonNodeFactory.instance.objectNode();
handoffContext.set("state", JsonNodeFactory.instance.textNode(state));
if (StringUtils.isNotBlank(message)) {
handoffContext.set("message", JsonNodeFactory.instance.textNode(message));
}

Activity handoffEvent = createHandoffEvent(HandoffEventNames.HANDOFFSTATUS, handoffContext, conversation);
return handoffEvent;
}

private static Activity createHandoffEvent(String name, Object value, ConversationAccount conversation) {
Activity handoffEvent = Activity.createEventActivity();

handoffEvent.setName(name);
handoffEvent.setValue(value);
handoffEvent.setId(UUID.randomUUID().toString());
handoffEvent.setTimestamp(OffsetDateTime.now(ZoneId.of("UTC")));
handoffEvent.setConversation(conversation);
handoffEvent.setAttachments(new ArrayList<Attachment>());
handoffEvent.setEntities(new ArrayList<Entity>());
return handoffEvent;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MT License.

package com.microsoft.bot.builder;

import java.util.ArrayList;
import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.bot.builder.adapters.TestAdapter;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.ChannelAccount;
import com.microsoft.bot.schema.ConversationAccount;
import com.microsoft.bot.schema.HandoffEventNames;
import com.microsoft.bot.schema.Serialization;
import com.microsoft.bot.schema.Transcript;

import org.junit.Assert;
import org.junit.Test;

public class EventFactoryTests {

@Test
public void HandoffInitiationNullTurnContext() {
Assert.assertThrows(IllegalArgumentException.class,
() -> EventFactory.createHandoffInitiation(null, "some text"));
}

@Test
public void HandoffStatusNullConversation() {
Assert.assertThrows(IllegalArgumentException.class, () -> EventFactory.createHandoffStatus(null, "accepted"));
}

@Test
public void HandoffStatusNullStatus() {
Assert.assertThrows(IllegalArgumentException.class,
() -> EventFactory.createHandoffStatus(new ConversationAccount(), null));
}

@Test
public void TestCreateHandoffInitiation() {
TestAdapter adapter = new TestAdapter(
TestAdapter.createConversationReference("TestCreateHandoffInitiation", "User1", "Bot"));
String fromD = "test";
Activity activity = new Activity(ActivityTypes.MESSAGE);
activity.setText("");
activity.setConversation(new ConversationAccount());
activity.setRecipient(new ChannelAccount());
activity.setFrom(new ChannelAccount(fromD));
activity.setChannelId("testchannel");
activity.setServiceUrl("http://myservice");
TurnContext context = new TurnContextImpl(adapter, activity);
List<Activity> activities = new ArrayList<Activity>();
activities.add(MessageFactory.text("hello"));
Transcript transcript = new Transcript();
transcript.setActivities(activities);

Assert.assertNull(transcript.getActivities().get(0).getChannelId());
Assert.assertNull(transcript.getActivities().get(0).getServiceUrl());
Assert.assertNull(transcript.getActivities().get(0).getConversation());

ObjectNode handoffContext = JsonNodeFactory.instance.objectNode();
handoffContext.set("Skill", JsonNodeFactory.instance.textNode("any"));

Activity handoffEvent = EventFactory.createHandoffInitiation(context, handoffContext, transcript);
Assert.assertEquals(handoffEvent.getName(), HandoffEventNames.INITIATEHANDOFF);
ObjectNode node = (ObjectNode) handoffEvent.getValue();
String skill = node.get("Skill").asText();
Assert.assertEquals("any", skill);
Assert.assertEquals(handoffEvent.getFrom().getId(), fromD);
}

@Test
public void TestCreateHandoffInitiationNoTranscript() {
TestAdapter adapter = new TestAdapter(
TestAdapter.createConversationReference("TestCreateHandoffInitiation", "User1", "Bot"));
String fromD = "test";
Activity activity = new Activity(ActivityTypes.MESSAGE);
activity.setText("");
activity.setConversation(new ConversationAccount());
activity.setRecipient(new ChannelAccount());
activity.setFrom(new ChannelAccount(fromD));
activity.setChannelId("testchannel");
activity.setServiceUrl("http://myservice");
TurnContext context = new TurnContextImpl(adapter, activity);
List<Activity> activities = new ArrayList<Activity>();
activities.add(MessageFactory.text("hello"));

ObjectNode handoffContext = JsonNodeFactory.instance.objectNode();
handoffContext.set("Skill", JsonNodeFactory.instance.textNode("any"));

Activity handoffEvent = EventFactory.createHandoffInitiation(context, handoffContext);
Assert.assertEquals(handoffEvent.getName(), HandoffEventNames.INITIATEHANDOFF);
ObjectNode node = (ObjectNode) handoffEvent.getValue();
String skill = node.get("Skill").asText();
Assert.assertEquals("any", skill);
Assert.assertEquals(handoffEvent.getFrom().getId(), fromD);
}

@Test
public void TestCreateHandoffStatus() throws JsonProcessingException {
String state = "failed";
String message = "timed out";
Activity handoffEvent = EventFactory.createHandoffStatus(new ConversationAccount(), state, message);
Assert.assertEquals(handoffEvent.getName(), HandoffEventNames.HANDOFFSTATUS);

ObjectNode node = (ObjectNode) handoffEvent.getValue();

String stateFormEvent = node.get("state").asText();
Assert.assertEquals(stateFormEvent, state);

String messageFormEvent = node.get("message").asText();
Assert.assertEquals(messageFormEvent, message);

String status = Serialization.toString(node);
Assert.assertEquals(status, String.format("{\"state\":\"%s\",\"message\":\"%s\"}", state, message));
Assert.assertNotNull(handoffEvent.getAttachments());
Assert.assertNotNull(handoffEvent.getId());
}

@Test
public void TestCreateHandoffStatusNoMessage() {
String state = "failed";
Activity handoffEvent = EventFactory.createHandoffStatus(new ConversationAccount(), state);

ObjectNode node = (ObjectNode) handoffEvent.getValue();

String stateFormEvent = node.get("state").asText();
Assert.assertEquals(stateFormEvent, state);

JsonNode messageFormEvent = node.get("message");
Assert.assertNull(messageFormEvent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.bot.schema;

/**
* Defines values for handoff event names.
*/
public final class HandoffEventNames {

private HandoffEventNames() {

}

/**
* The value of handoff events for initiate handoff.
*/
public static final String INITIATEHANDOFF = "handoff.initiate";

/**
* The value of handoff events for handoff status.
*/
public static final String HANDOFFSTATUS = "handoff.status";
}

0 comments on commit 45130e6

Please sign in to comment.