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

Commit 45130e6

Browse files
Added Event Factory for handoff protocol (#1118)
* Added Event Factory for handoff protocol * Added additional unit test. Co-authored-by: tracyboehrer <tracyboehrer@users.noreply.github.com>
1 parent 42edeb3 commit 45130e6

File tree

3 files changed

+297
-0
lines changed

3 files changed

+297
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MT License.
3+
4+
package com.microsoft.bot.builder;
5+
6+
import java.time.OffsetDateTime;
7+
import java.time.ZoneId;
8+
import java.util.ArrayList;
9+
import java.util.UUID;
10+
11+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
12+
import com.fasterxml.jackson.databind.node.ObjectNode;
13+
import com.microsoft.bot.schema.Activity;
14+
import com.microsoft.bot.schema.Attachment;
15+
import com.microsoft.bot.schema.ConversationAccount;
16+
import com.microsoft.bot.schema.Entity;
17+
import com.microsoft.bot.schema.HandoffEventNames;
18+
import com.microsoft.bot.schema.Transcript;
19+
20+
import org.apache.commons.lang3.StringUtils;
21+
22+
/**
23+
* Contains utility methods for creating various event types.
24+
*/
25+
public final class EventFactory {
26+
27+
private EventFactory() {
28+
29+
}
30+
31+
/**
32+
* Create handoff initiation event.
33+
*
34+
* @param turnContext turn context.
35+
* @param handoffContext agent hub-specific context.
36+
*
37+
* @return handoff event.
38+
*/
39+
public static Activity createHandoffInitiation(TurnContext turnContext, Object handoffContext) {
40+
return createHandoffInitiation(turnContext, handoffContext, null);
41+
}
42+
43+
44+
/**
45+
* Create handoff initiation event.
46+
*
47+
* @param turnContext turn context.
48+
* @param handoffContext agent hub-specific context.
49+
* @param transcript transcript of the conversation.
50+
*
51+
* @return handoff event.
52+
*/
53+
public static Activity createHandoffInitiation(TurnContext turnContext, Object handoffContext,
54+
Transcript transcript) {
55+
if (turnContext == null) {
56+
throw new IllegalArgumentException("turnContext cannot be null.");
57+
}
58+
59+
Activity handoffEvent = createHandoffEvent(HandoffEventNames.INITIATEHANDOFF, handoffContext,
60+
turnContext.getActivity().getConversation());
61+
62+
handoffEvent.setFrom(turnContext.getActivity().getFrom());
63+
handoffEvent.setRelatesTo(turnContext.getActivity().getConversationReference());
64+
handoffEvent.setReplyToId(turnContext.getActivity().getId());
65+
handoffEvent.setServiceUrl(turnContext.getActivity().getServiceUrl());
66+
handoffEvent.setChannelId(turnContext.getActivity().getChannelId());
67+
68+
if (transcript != null) {
69+
Attachment attachment = new Attachment();
70+
attachment.setContent(transcript);
71+
attachment.setContentType("application/json");
72+
attachment.setName("Transcript");
73+
handoffEvent.getAttachments().add(attachment);
74+
}
75+
76+
return handoffEvent;
77+
}
78+
79+
80+
/**
81+
* Create handoff status event.
82+
*
83+
* @param conversation Conversation being handed over.
84+
* @param state State, possible values are: "accepted", "failed",
85+
* "completed".
86+
*
87+
* @return handoff event.
88+
*/
89+
public static Activity createHandoffStatus(ConversationAccount conversation, String state) {
90+
return createHandoffStatus(conversation, state, null);
91+
}
92+
93+
/**
94+
* Create handoff status event.
95+
*
96+
* @param conversation Conversation being handed over.
97+
* @param state State, possible values are: "accepted", "failed",
98+
* "completed".
99+
* @param message Additional message for failed handoff.
100+
*
101+
* @return handoff event.
102+
*/
103+
public static Activity createHandoffStatus(ConversationAccount conversation, String state, String message) {
104+
if (conversation == null) {
105+
throw new IllegalArgumentException("conversation cannot be null.");
106+
}
107+
108+
if (state == null) {
109+
throw new IllegalArgumentException("state cannot be null.");
110+
}
111+
112+
ObjectNode handoffContext = JsonNodeFactory.instance.objectNode();
113+
handoffContext.set("state", JsonNodeFactory.instance.textNode(state));
114+
if (StringUtils.isNotBlank(message)) {
115+
handoffContext.set("message", JsonNodeFactory.instance.textNode(message));
116+
}
117+
118+
Activity handoffEvent = createHandoffEvent(HandoffEventNames.HANDOFFSTATUS, handoffContext, conversation);
119+
return handoffEvent;
120+
}
121+
122+
private static Activity createHandoffEvent(String name, Object value, ConversationAccount conversation) {
123+
Activity handoffEvent = Activity.createEventActivity();
124+
125+
handoffEvent.setName(name);
126+
handoffEvent.setValue(value);
127+
handoffEvent.setId(UUID.randomUUID().toString());
128+
handoffEvent.setTimestamp(OffsetDateTime.now(ZoneId.of("UTC")));
129+
handoffEvent.setConversation(conversation);
130+
handoffEvent.setAttachments(new ArrayList<Attachment>());
131+
handoffEvent.setEntities(new ArrayList<Entity>());
132+
return handoffEvent;
133+
}
134+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MT License.
3+
4+
package com.microsoft.bot.builder;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
import com.fasterxml.jackson.core.JsonProcessingException;
10+
import com.fasterxml.jackson.databind.JsonNode;
11+
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
12+
import com.fasterxml.jackson.databind.node.ObjectNode;
13+
import com.microsoft.bot.builder.adapters.TestAdapter;
14+
import com.microsoft.bot.schema.Activity;
15+
import com.microsoft.bot.schema.ActivityTypes;
16+
import com.microsoft.bot.schema.ChannelAccount;
17+
import com.microsoft.bot.schema.ConversationAccount;
18+
import com.microsoft.bot.schema.HandoffEventNames;
19+
import com.microsoft.bot.schema.Serialization;
20+
import com.microsoft.bot.schema.Transcript;
21+
22+
import org.junit.Assert;
23+
import org.junit.Test;
24+
25+
public class EventFactoryTests {
26+
27+
@Test
28+
public void HandoffInitiationNullTurnContext() {
29+
Assert.assertThrows(IllegalArgumentException.class,
30+
() -> EventFactory.createHandoffInitiation(null, "some text"));
31+
}
32+
33+
@Test
34+
public void HandoffStatusNullConversation() {
35+
Assert.assertThrows(IllegalArgumentException.class, () -> EventFactory.createHandoffStatus(null, "accepted"));
36+
}
37+
38+
@Test
39+
public void HandoffStatusNullStatus() {
40+
Assert.assertThrows(IllegalArgumentException.class,
41+
() -> EventFactory.createHandoffStatus(new ConversationAccount(), null));
42+
}
43+
44+
@Test
45+
public void TestCreateHandoffInitiation() {
46+
TestAdapter adapter = new TestAdapter(
47+
TestAdapter.createConversationReference("TestCreateHandoffInitiation", "User1", "Bot"));
48+
String fromD = "test";
49+
Activity activity = new Activity(ActivityTypes.MESSAGE);
50+
activity.setText("");
51+
activity.setConversation(new ConversationAccount());
52+
activity.setRecipient(new ChannelAccount());
53+
activity.setFrom(new ChannelAccount(fromD));
54+
activity.setChannelId("testchannel");
55+
activity.setServiceUrl("http://myservice");
56+
TurnContext context = new TurnContextImpl(adapter, activity);
57+
List<Activity> activities = new ArrayList<Activity>();
58+
activities.add(MessageFactory.text("hello"));
59+
Transcript transcript = new Transcript();
60+
transcript.setActivities(activities);
61+
62+
Assert.assertNull(transcript.getActivities().get(0).getChannelId());
63+
Assert.assertNull(transcript.getActivities().get(0).getServiceUrl());
64+
Assert.assertNull(transcript.getActivities().get(0).getConversation());
65+
66+
ObjectNode handoffContext = JsonNodeFactory.instance.objectNode();
67+
handoffContext.set("Skill", JsonNodeFactory.instance.textNode("any"));
68+
69+
Activity handoffEvent = EventFactory.createHandoffInitiation(context, handoffContext, transcript);
70+
Assert.assertEquals(handoffEvent.getName(), HandoffEventNames.INITIATEHANDOFF);
71+
ObjectNode node = (ObjectNode) handoffEvent.getValue();
72+
String skill = node.get("Skill").asText();
73+
Assert.assertEquals("any", skill);
74+
Assert.assertEquals(handoffEvent.getFrom().getId(), fromD);
75+
}
76+
77+
@Test
78+
public void TestCreateHandoffInitiationNoTranscript() {
79+
TestAdapter adapter = new TestAdapter(
80+
TestAdapter.createConversationReference("TestCreateHandoffInitiation", "User1", "Bot"));
81+
String fromD = "test";
82+
Activity activity = new Activity(ActivityTypes.MESSAGE);
83+
activity.setText("");
84+
activity.setConversation(new ConversationAccount());
85+
activity.setRecipient(new ChannelAccount());
86+
activity.setFrom(new ChannelAccount(fromD));
87+
activity.setChannelId("testchannel");
88+
activity.setServiceUrl("http://myservice");
89+
TurnContext context = new TurnContextImpl(adapter, activity);
90+
List<Activity> activities = new ArrayList<Activity>();
91+
activities.add(MessageFactory.text("hello"));
92+
93+
ObjectNode handoffContext = JsonNodeFactory.instance.objectNode();
94+
handoffContext.set("Skill", JsonNodeFactory.instance.textNode("any"));
95+
96+
Activity handoffEvent = EventFactory.createHandoffInitiation(context, handoffContext);
97+
Assert.assertEquals(handoffEvent.getName(), HandoffEventNames.INITIATEHANDOFF);
98+
ObjectNode node = (ObjectNode) handoffEvent.getValue();
99+
String skill = node.get("Skill").asText();
100+
Assert.assertEquals("any", skill);
101+
Assert.assertEquals(handoffEvent.getFrom().getId(), fromD);
102+
}
103+
104+
@Test
105+
public void TestCreateHandoffStatus() throws JsonProcessingException {
106+
String state = "failed";
107+
String message = "timed out";
108+
Activity handoffEvent = EventFactory.createHandoffStatus(new ConversationAccount(), state, message);
109+
Assert.assertEquals(handoffEvent.getName(), HandoffEventNames.HANDOFFSTATUS);
110+
111+
ObjectNode node = (ObjectNode) handoffEvent.getValue();
112+
113+
String stateFormEvent = node.get("state").asText();
114+
Assert.assertEquals(stateFormEvent, state);
115+
116+
String messageFormEvent = node.get("message").asText();
117+
Assert.assertEquals(messageFormEvent, message);
118+
119+
String status = Serialization.toString(node);
120+
Assert.assertEquals(status, String.format("{\"state\":\"%s\",\"message\":\"%s\"}", state, message));
121+
Assert.assertNotNull(handoffEvent.getAttachments());
122+
Assert.assertNotNull(handoffEvent.getId());
123+
}
124+
125+
@Test
126+
public void TestCreateHandoffStatusNoMessage() {
127+
String state = "failed";
128+
Activity handoffEvent = EventFactory.createHandoffStatus(new ConversationAccount(), state);
129+
130+
ObjectNode node = (ObjectNode) handoffEvent.getValue();
131+
132+
String stateFormEvent = node.get("state").asText();
133+
Assert.assertEquals(stateFormEvent, state);
134+
135+
JsonNode messageFormEvent = node.get("message");
136+
Assert.assertNull(messageFormEvent);
137+
}
138+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
// Copyright (c) Microsoft Corporation. All rights reserved.
3+
// Licensed under the MIT License.
4+
5+
package com.microsoft.bot.schema;
6+
7+
/**
8+
* Defines values for handoff event names.
9+
*/
10+
public final class HandoffEventNames {
11+
12+
private HandoffEventNames() {
13+
14+
}
15+
16+
/**
17+
* The value of handoff events for initiate handoff.
18+
*/
19+
public static final String INITIATEHANDOFF = "handoff.initiate";
20+
21+
/**
22+
* The value of handoff events for handoff status.
23+
*/
24+
public static final String HANDOFFSTATUS = "handoff.status";
25+
}

0 commit comments

Comments
 (0)