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

Added Event Factory for handoff protocol #1118

Merged
merged 3 commits into from
Apr 1, 2021
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
@@ -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";
}