Added BotAdapterBracketingTest, BotAdapterTests, BotStateSetTests, and renamed BotFrameworkAdaterTests.
This commit is contained in:
Родитель
13a3e99c1f
Коммит
f148cafff2
|
@ -15,28 +15,31 @@ import java.util.function.Function;
|
||||||
/**
|
/**
|
||||||
* Represents a bot adapter that can connect a bot to a service endpoint.
|
* Represents a bot adapter that can connect a bot to a service endpoint.
|
||||||
* This class is abstract.
|
* This class is abstract.
|
||||||
* The bot adapter encapsulates authentication processes and sends
|
* <p>The bot adapter encapsulates authentication processes and sends
|
||||||
* activities to and receives activities from the Bot Connector Service. When your
|
* activities to and receives activities from the Bot Connector Service. When your
|
||||||
* bot receives an activity, the adapter creates a context object, passes it to your
|
* bot receives an activity, the adapter creates a context object, passes it to your
|
||||||
* bot's application logic, and sends responses back to the user's channel.
|
* bot's application logic, and sends responses back to the user's channel.</p>
|
||||||
* <p>Use {@link #use(Middleware)} to add {@link Middleware} objects
|
* <p>Use {@link #use(Middleware)} to add {@link Middleware} objects
|
||||||
* to your adapter’s middleware collection. The adapter processes and directs
|
* to your adapter’s middleware collection. The adapter processes and directs
|
||||||
* incoming activities in through the bot middleware pipeline to your bot’s logic
|
* incoming activities in through the bot middleware pipeline to your bot’s logic
|
||||||
* and then back out again. As each activity flows in and out of the bot, each piece
|
* and then back out again. As each activity flows in and out of the bot, each piece
|
||||||
* of middleware can inspect or act upon the activity, both before and after the bot
|
* of middleware can inspect or act upon the activity, both before and after the bot
|
||||||
* logic runs.</p>
|
* logic runs.</p>
|
||||||
* <p>
|
*
|
||||||
* {@linkalso TurnContext}
|
* {@link TurnContext}
|
||||||
* {@linkalso Activity}
|
* {@link Activity}
|
||||||
* {@linkalso Bot}
|
* {@link Bot}
|
||||||
* {@linkalso Middleware}
|
* {@link Middleware}
|
||||||
*/
|
*/
|
||||||
public abstract class BotAdapter {
|
public abstract class BotAdapter {
|
||||||
/**
|
/**
|
||||||
* The collection of middleware in the adapter's pipeline.
|
* The collection of middleware in the adapter's pipeline.
|
||||||
*/
|
*/
|
||||||
protected final MiddlewareSet _middlewareSet = new MiddlewareSet();
|
protected final MiddlewareSet middlewareSet = new MiddlewareSet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error handler that can catch exceptions in the middleware or application.
|
||||||
|
*/
|
||||||
private OnTurnErrorHandler onTurnError;
|
private OnTurnErrorHandler onTurnError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,7 +66,7 @@ public abstract class BotAdapter {
|
||||||
* For each turn, the adapter calls middleware in the order in which you added it.
|
* For each turn, the adapter calls middleware in the order in which you added it.
|
||||||
*/
|
*/
|
||||||
public BotAdapter use(Middleware middleware) {
|
public BotAdapter use(Middleware middleware) {
|
||||||
_middlewareSet.use(middleware);
|
middlewareSet.use(middleware);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +79,7 @@ public abstract class BotAdapter {
|
||||||
* If the activities are successfully sent, the task result contains
|
* If the activities are successfully sent, the task result contains
|
||||||
* an array of {@link ResourceResponse} objects containing the IDs that
|
* an array of {@link ResourceResponse} objects containing the IDs that
|
||||||
* the receiving channel assigned to the activities.
|
* the receiving channel assigned to the activities.
|
||||||
* {@linkalso TurnContext.OnSendActivities(SendActivitiesHandler)}
|
* {@link TurnContext#onSendActivities(SendActivitiesHandler)}
|
||||||
*/
|
*/
|
||||||
public abstract CompletableFuture<ResourceResponse[]> sendActivities(TurnContext context, Activity[] activities);
|
public abstract CompletableFuture<ResourceResponse[]> sendActivities(TurnContext context, Activity[] activities);
|
||||||
|
|
||||||
|
@ -92,7 +95,7 @@ public abstract class BotAdapter {
|
||||||
* channel assigned to the activity.
|
* channel assigned to the activity.
|
||||||
* <p>Before calling this, set the ID of the replacement activity to the ID
|
* <p>Before calling this, set the ID of the replacement activity to the ID
|
||||||
* of the activity to replace.</p>
|
* of the activity to replace.</p>
|
||||||
* {@linkalso TurnContext.OnUpdateActivity(UpdateActivityHandler)}
|
* {@link TurnContext#onUpdateActivity(UpdateActivityHandler)}
|
||||||
*/
|
*/
|
||||||
public abstract CompletableFuture<ResourceResponse> updateActivity(TurnContext context, Activity activity);
|
public abstract CompletableFuture<ResourceResponse> updateActivity(TurnContext context, Activity activity);
|
||||||
|
|
||||||
|
@ -105,7 +108,7 @@ public abstract class BotAdapter {
|
||||||
* @return A task that represents the work queued to execute.
|
* @return A task that represents the work queued to execute.
|
||||||
* The {@link ConversationReference#getActivityId} of the conversation
|
* The {@link ConversationReference#getActivityId} of the conversation
|
||||||
* reference identifies the activity to delete.
|
* reference identifies the activity to delete.
|
||||||
* {@linkalso TurnContext.OnDeleteActivity(DeleteActivityHandler)}
|
* {@link TurnContext#onDeleteActivity(DeleteActivityHandler)}
|
||||||
*/
|
*/
|
||||||
public abstract CompletableFuture<Void> deleteActivity(TurnContext context, ConversationReference reference);
|
public abstract CompletableFuture<Void> deleteActivity(TurnContext context, ConversationReference reference);
|
||||||
|
|
||||||
|
@ -113,30 +116,32 @@ public abstract class BotAdapter {
|
||||||
/**
|
/**
|
||||||
* Starts activity processing for the current bot turn.
|
* Starts activity processing for the current bot turn.
|
||||||
*
|
*
|
||||||
|
* The adapter calls middleware in the order in which you added it.
|
||||||
|
* The adapter passes in the context object for the turn and a next delegate,
|
||||||
|
* and the middleware calls the delegate to pass control to the next middleware
|
||||||
|
* in the pipeline. Once control reaches the end of the pipeline, the adapter calls
|
||||||
|
* the {@code callback} method. If a middleware component doesn’t call
|
||||||
|
* the next delegate, the adapter does not call any of the subsequent middleware’s
|
||||||
|
* {@link Middleware#onTurn(TurnContext, NextDelegate)}
|
||||||
|
* methods or the callback method, and the pipeline short circuits.
|
||||||
|
*
|
||||||
|
* <p>When the turn is initiated by a user activity (reactive messaging), the
|
||||||
|
* callback method will be a reference to the bot's
|
||||||
|
* {@link Bot#onTurn(TurnContext)} method. When the turn is
|
||||||
|
* initiated by a call to {@link #continueConversation(String, ConversationReference, BotCallbackHandler)}
|
||||||
|
* (proactive messaging), the callback method is the callback method that was provided in the call.</p>
|
||||||
|
*
|
||||||
* @param context The turn's context object.
|
* @param context The turn's context object.
|
||||||
* @param callback A callback method to run at the end of the pipeline.
|
* @param callback A callback method to run at the end of the pipeline.
|
||||||
* @return A task that represents the work queued to execute.
|
* @return A task that represents the work queued to execute.
|
||||||
* @throws NullPointerException {@code context} is null.
|
* @throws NullPointerException {@code context} is null.
|
||||||
* The adapter calls middleware in the order in which you added it.
|
|
||||||
* The adapter passes in the context object for the turn and a next delegate,
|
|
||||||
* and the middleware calls the delegate to pass control to the next middleware
|
|
||||||
* in the pipeline. Once control reaches the end of the pipeline, the adapter calls
|
|
||||||
* the {@code callback} method. If a middleware component doesn’t call
|
|
||||||
* the next delegate, the adapter does not call any of the subsequent middleware’s
|
|
||||||
* {@link Middleware#onTurn(TurnContext, NextDelegate)}
|
|
||||||
* methods or the callback method, and the pipeline short circuits.
|
|
||||||
* <p>When the turn is initiated by a user activity (reactive messaging), the
|
|
||||||
* callback method will be a reference to the bot's
|
|
||||||
* {@link Bot#onTurn(TurnContext)} method. When the turn is
|
|
||||||
* initiated by a call to {@link #continueConversation(String, ConversationReference, BotCallbackHandler)}
|
|
||||||
* (proactive messaging), the callback method is the callback method that was provided in the call.</p>
|
|
||||||
*/
|
*/
|
||||||
protected CompletableFuture<Void> runPipeline(TurnContext context, BotCallbackHandler callback) {
|
protected CompletableFuture<Void> runPipeline(TurnContext context, BotCallbackHandler callback) {
|
||||||
BotAssert.contextNotNull(context);
|
BotAssert.contextNotNull(context);
|
||||||
|
|
||||||
// Call any registered Middleware Components looking for ReceiveActivity()
|
// Call any registered Middleware Components looking for ReceiveActivity()
|
||||||
if (context.getActivity() != null) {
|
if (context.getActivity() != null) {
|
||||||
return _middlewareSet.receiveActivityWithStatus(context, callback)
|
return middlewareSet.receiveActivityWithStatus(context, callback)
|
||||||
.exceptionally(exception -> {
|
.exceptionally(exception -> {
|
||||||
if (onTurnError != null) {
|
if (onTurnError != null) {
|
||||||
return onTurnError.invoke(context, exception);
|
return onTurnError.invoke(context, exception);
|
||||||
|
|
|
@ -192,7 +192,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
||||||
* @return The updated adapter object.
|
* @return The updated adapter object.
|
||||||
*/
|
*/
|
||||||
public BotFrameworkAdapter use(Middleware middleware) {
|
public BotFrameworkAdapter use(Middleware middleware) {
|
||||||
super._middlewareSet.use(middleware);
|
super.middlewareSet.use(middleware);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +261,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
||||||
|
|
||||||
// For all non-invoke scenarios, the HTTP layers above don't have to mess
|
// For all non-invoke scenarios, the HTTP layers above don't have to mess
|
||||||
// with the Body and return codes.
|
// with the Body and return codes.
|
||||||
return null;
|
return CompletableFuture.completedFuture(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
package com.microsoft.bot.builder;
|
package com.microsoft.bot.builder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -14,6 +16,10 @@ import java.util.stream.Collectors;
|
||||||
public class BotStateSet {
|
public class BotStateSet {
|
||||||
private List<BotState> botStates = new ArrayList<>();
|
private List<BotState> botStates = new ArrayList<>();
|
||||||
|
|
||||||
|
public BotStateSet(BotState... withBotStates) {
|
||||||
|
this(Arrays.asList(withBotStates));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new instance of the BotStateSet class.
|
* Initializes a new instance of the BotStateSet class.
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,9 +22,9 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The memory transcript store stores transcripts in volatile memory in a Dictionary.
|
* The memory transcript store stores transcripts in volatile memory in a Dictionary.
|
||||||
* <p>
|
*
|
||||||
* <p>
|
* Because this uses an unbounded volatile dictionary this should only be used for unit tests or
|
||||||
* Because this uses an unbounded volitile dictionary this should only be used for unit tests or non-production environments.
|
* non-production environments.
|
||||||
*/
|
*/
|
||||||
public class MemoryTranscriptStore implements TranscriptStore {
|
public class MemoryTranscriptStore implements TranscriptStore {
|
||||||
private HashMap<String, HashMap<String, ArrayList<Activity>>> channels = new HashMap<String, HashMap<String, ArrayList<Activity>>>();
|
private HashMap<String, HashMap<String, ArrayList<Activity>>> channels = new HashMap<String, HashMap<String, ArrayList<Activity>>>();
|
||||||
|
|
|
@ -19,6 +19,6 @@ public class InspectionState extends BotState {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStorageKey(TurnContext turnContext) {
|
public String getStorageKey(TurnContext turnContext) {
|
||||||
return InspectionState.class.getName();
|
return InspectionState.class.getSimpleName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
package com.microsoft.bot.builder;
|
||||||
|
|
||||||
|
import com.microsoft.bot.builder.adapters.TestAdapter;
|
||||||
|
import com.microsoft.bot.builder.adapters.TestFlow;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class BotAdapterBracketingTest {
|
||||||
|
@Test
|
||||||
|
public void Middleware_BracketingValidation() {
|
||||||
|
TestAdapter adapter = new TestAdapter()
|
||||||
|
.use(new BeforeAfterMiddleware());
|
||||||
|
|
||||||
|
BotCallbackHandler echo = (turnContext -> {
|
||||||
|
String toEcho = "ECHO:" + turnContext.getActivity().getText();
|
||||||
|
return turnContext.sendActivity(turnContext.getActivity().createReply(toEcho))
|
||||||
|
.thenApply(resourceResponse -> null);
|
||||||
|
});
|
||||||
|
|
||||||
|
new TestFlow(adapter, echo)
|
||||||
|
.send("test")
|
||||||
|
.assertReply("BEFORE")
|
||||||
|
.assertReply("ECHO:test")
|
||||||
|
.assertReply("AFTER")
|
||||||
|
.startTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void Middleware_ThrowException() {
|
||||||
|
String uniqueId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
TestAdapter adapter = new TestAdapter()
|
||||||
|
.use(new CatchExceptionMiddleware());
|
||||||
|
|
||||||
|
BotCallbackHandler echoWithException = (turnContext -> {
|
||||||
|
String toEcho = "ECHO:" + turnContext.getActivity().getText();
|
||||||
|
return turnContext.sendActivity(turnContext.getActivity().createReply(toEcho))
|
||||||
|
.thenApply(resourceResponse -> {
|
||||||
|
throw new RuntimeException(uniqueId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new TestFlow(adapter, echoWithException)
|
||||||
|
.send("test")
|
||||||
|
.assertReply("BEFORE")
|
||||||
|
.assertReply("ECHO:test")
|
||||||
|
.assertReply("CAUGHT:" + uniqueId)
|
||||||
|
.assertReply("AFTER")
|
||||||
|
.startTest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CatchExceptionMiddleware implements Middleware {
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> onTurn(TurnContext turnContext, NextDelegate next) {
|
||||||
|
return turnContext.sendActivity(turnContext.getActivity().createReply("BEFORE"))
|
||||||
|
.thenCompose(resourceResponse -> next.next())
|
||||||
|
.exceptionally(exception -> {
|
||||||
|
turnContext.sendActivity(turnContext.getActivity().createReply("CAUGHT:" + exception.getMessage())).join();
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.thenCompose(result -> turnContext.sendActivity(turnContext.getActivity().createReply("AFTER"))
|
||||||
|
.thenApply(resourceResponse -> null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BeforeAfterMiddleware implements Middleware {
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Void> onTurn(TurnContext turnContext, NextDelegate next) {
|
||||||
|
return turnContext.sendActivity(turnContext.getActivity().createReply("BEFORE"))
|
||||||
|
.thenCompose(result -> next.next())
|
||||||
|
.thenCompose(result -> turnContext.sendActivity(turnContext.getActivity().createReply("AFTER"))
|
||||||
|
.thenApply(resourceResponse -> null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
// Licensed under the MIT License.
|
||||||
|
|
||||||
|
package com.microsoft.bot.builder;
|
||||||
|
|
||||||
|
import com.microsoft.bot.builder.adapters.TestAdapter;
|
||||||
|
import com.microsoft.bot.schema.*;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class BotAdapterTests {
|
||||||
|
@Test
|
||||||
|
public void AdapterSingleUse() {
|
||||||
|
SimpleAdapter a = new SimpleAdapter();
|
||||||
|
a.use(new CallCountingMiddleware());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void AdapterUseChaining() {
|
||||||
|
SimpleAdapter a = new SimpleAdapter();
|
||||||
|
a.use(new CallCountingMiddleware()).use(new CallCountingMiddleware());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void PassResourceResponsesThrough() {
|
||||||
|
Consumer<Activity[]> validateResponse = (activities) -> {
|
||||||
|
// no need to do anything.
|
||||||
|
};
|
||||||
|
|
||||||
|
SimpleAdapter a = new SimpleAdapter(validateResponse);
|
||||||
|
TurnContextImpl c = new TurnContextImpl(a, new Activity(ActivityTypes.MESSAGE));
|
||||||
|
|
||||||
|
String activityId = UUID.randomUUID().toString();
|
||||||
|
Activity activity = TestMessage.Message();
|
||||||
|
activity.setId(activityId);
|
||||||
|
|
||||||
|
ResourceResponse resourceResponse = c.sendActivity(activity).join();
|
||||||
|
Assert.assertTrue("Incorrect response Id returned", StringUtils.equals(resourceResponse.getId(), activityId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ContinueConversation_DirectMsgAsync() {
|
||||||
|
boolean[] callbackInvoked = new boolean[] { false };
|
||||||
|
|
||||||
|
TestAdapter adapter = new TestAdapter();
|
||||||
|
ConversationReference cr = new ConversationReference(){{
|
||||||
|
setActivityId("activityId");
|
||||||
|
setBot(new ChannelAccount(){{
|
||||||
|
setId("channelId");
|
||||||
|
setName("testChannelAccount");
|
||||||
|
setRole(RoleTypes.BOT);
|
||||||
|
}});
|
||||||
|
setChannelId("testChannel");
|
||||||
|
setServiceUrl("testUrl");
|
||||||
|
setConversation(new ConversationAccount() {{
|
||||||
|
setConversationType("");
|
||||||
|
setId("testConversationId");
|
||||||
|
setIsGroup(false);
|
||||||
|
setName("testConversationName");
|
||||||
|
setRole(RoleTypes.USER);
|
||||||
|
}});
|
||||||
|
setUser(new ChannelAccount() {{
|
||||||
|
setId("channelId");
|
||||||
|
setName("testChannelAccount");
|
||||||
|
setRole(RoleTypes.BOT);
|
||||||
|
}});
|
||||||
|
}};
|
||||||
|
|
||||||
|
BotCallbackHandler callback = (turnContext) -> {
|
||||||
|
callbackInvoked[0] = true;
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
adapter.continueConversation("MyBot", cr, callback).join();
|
||||||
|
Assert.assertTrue(callbackInvoked[0]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
package com.microsoft.bot.builder;
|
|
||||||
|
|
||||||
import com.microsoft.bot.schema.Activity;
|
|
||||||
import com.microsoft.bot.schema.ActivityTypes;
|
|
||||||
import com.microsoft.bot.schema.ResourceResponse;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class BotFrameworkAdapterTest {
|
|
||||||
@Test
|
|
||||||
public void AdapterSingleUse() {
|
|
||||||
SimpleAdapter a = new SimpleAdapter();
|
|
||||||
a.use(new CallCountingMiddleware());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void AdapterUseChaining() {
|
|
||||||
SimpleAdapter a = new SimpleAdapter();
|
|
||||||
a.use(new CallCountingMiddleware()).use(new CallCountingMiddleware());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void PassResourceResponsesThrough() throws Exception {
|
|
||||||
Consumer<Activity[]> validateResponse = (activities) -> {
|
|
||||||
// no need to do anything.
|
|
||||||
};
|
|
||||||
|
|
||||||
SimpleAdapter a = new SimpleAdapter(validateResponse);
|
|
||||||
TurnContextImpl c = new TurnContextImpl(a, new Activity(ActivityTypes.MESSAGE));
|
|
||||||
|
|
||||||
String activityId = UUID.randomUUID().toString();
|
|
||||||
Activity activity = TestMessage.Message();
|
|
||||||
activity.setId(activityId);
|
|
||||||
|
|
||||||
ResourceResponse resourceResponse = c.sendActivity(activity).join();
|
|
||||||
Assert.assertTrue("Incorrect response Id returned", StringUtils.equals(resourceResponse.getId(), activityId));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
package com.microsoft.bot.builder;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.microsoft.bot.connector.Channels;
|
||||||
|
import com.microsoft.bot.connector.authentication.ClaimsIdentity;
|
||||||
|
import com.microsoft.bot.connector.authentication.CredentialProvider;
|
||||||
|
import com.microsoft.bot.connector.authentication.SimpleCredentialProvider;
|
||||||
|
import com.microsoft.bot.schema.Activity;
|
||||||
|
import com.microsoft.bot.schema.ConversationAccount;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
public class BotFrameworkAdapterTests {
|
||||||
|
@Test
|
||||||
|
public void TenantIdShouldBeSetInConversationForTeams() {
|
||||||
|
Activity activity = processActivity(Channels.MSTEAMS, "theTenantId", null);
|
||||||
|
Assert.assertEquals("theTenantId", activity.getConversation().getTenantId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TenantIdShouldNotChangeInConversationForTeamsIfPresent() {
|
||||||
|
Activity activity = processActivity(Channels.MSTEAMS, "theTenantId", "shouldNotBeReplaced");
|
||||||
|
Assert.assertEquals("shouldNotBeReplaced", activity.getConversation().getTenantId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void TenantIdShouldNotBeSetInConversationIfNotTeams() {
|
||||||
|
Activity activity = processActivity(Channels.DIRECTLINE, "theTenantId", null);
|
||||||
|
Assert.assertNull(activity.getConversation().getTenantId());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Activity processActivity(String channelId, String channelDataTenantId, String conversationTenantId) {
|
||||||
|
ClaimsIdentity mockClaims = new ClaimsIdentity("anonymous");
|
||||||
|
CredentialProvider mockCredentials = new SimpleCredentialProvider();
|
||||||
|
|
||||||
|
BotFrameworkAdapter sut = new BotFrameworkAdapter(mockCredentials);
|
||||||
|
|
||||||
|
ObjectNode channelData = new ObjectMapper().createObjectNode();
|
||||||
|
ObjectNode tenantId = new ObjectMapper().createObjectNode();
|
||||||
|
tenantId.put("id", channelDataTenantId);
|
||||||
|
channelData.set("tenant", tenantId);
|
||||||
|
|
||||||
|
Activity[] activity = new Activity[] { null };
|
||||||
|
sut.processActivity(
|
||||||
|
mockClaims,
|
||||||
|
new Activity("test") {{
|
||||||
|
setChannelId(channelId);
|
||||||
|
setServiceUrl("https://smba.trafficmanager.net/amer/");
|
||||||
|
setChannelData(channelData);
|
||||||
|
setConversation(new ConversationAccount() {{
|
||||||
|
setTenantId(conversationTenantId);
|
||||||
|
}});
|
||||||
|
}},
|
||||||
|
(context) -> {
|
||||||
|
activity[0] = context.getActivity();
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}).join();
|
||||||
|
|
||||||
|
return activity[0];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.microsoft.bot.builder;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class BotStateSetTests {
|
||||||
|
@Test
|
||||||
|
public void BotStateSet_Properties() {
|
||||||
|
Storage storage = new MemoryStorage();
|
||||||
|
|
||||||
|
UserState userState = new UserState(storage);
|
||||||
|
ConversationState conversationState = new ConversationState(storage);
|
||||||
|
BotStateSet stateSet = new BotStateSet(userState, conversationState);
|
||||||
|
|
||||||
|
Assert.assertEquals(2, stateSet.getBotStates().size());
|
||||||
|
Assert.assertTrue(stateSet.getBotStates().get(0) instanceof UserState);
|
||||||
|
Assert.assertTrue(stateSet.getBotStates().get(1) instanceof ConversationState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void BotStateSet_LoadAsync() {
|
||||||
|
Storage storage = new MemoryStorage();
|
||||||
|
|
||||||
|
TurnContext turnContext = TestUtilities.createEmptyContext();
|
||||||
|
|
||||||
|
{
|
||||||
|
UserState userState = new UserState(storage);
|
||||||
|
StatePropertyAccessor<Integer> userProperty = userState.createProperty("userCount");
|
||||||
|
|
||||||
|
ConversationState convState = new ConversationState(storage);
|
||||||
|
StatePropertyAccessor<Integer> convProperty = convState.createProperty("convCount");
|
||||||
|
|
||||||
|
BotStateSet stateSet = new BotStateSet(userState, convState);
|
||||||
|
|
||||||
|
Assert.assertEquals(2, stateSet.getBotStates().size());
|
||||||
|
|
||||||
|
Integer userCount = userProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(0, userCount.intValue());
|
||||||
|
Integer convCount = convProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(0, convCount.intValue());
|
||||||
|
|
||||||
|
userProperty.set(turnContext, 10).join();
|
||||||
|
convProperty.set(turnContext, 20).join();
|
||||||
|
|
||||||
|
stateSet.saveAllChanges(turnContext).join();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UserState userState = new UserState(storage);
|
||||||
|
StatePropertyAccessor<Integer> userProperty = userState.createProperty("userCount");
|
||||||
|
|
||||||
|
ConversationState convState = new ConversationState(storage);
|
||||||
|
StatePropertyAccessor<Integer> convProperty = convState.createProperty("convCount");
|
||||||
|
|
||||||
|
BotStateSet stateSet = new BotStateSet(userState, convState);
|
||||||
|
|
||||||
|
stateSet.loadAll(turnContext).join();
|
||||||
|
|
||||||
|
Integer userCount = userProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(10, userCount.intValue());
|
||||||
|
Integer convCount = convProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(20, convCount.intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void BotStateSet_SaveAsync() {
|
||||||
|
Storage storage = new MemoryStorage();
|
||||||
|
|
||||||
|
UserState userState = new UserState(storage);
|
||||||
|
StatePropertyAccessor<Integer> userProperty = userState.createProperty("userCount");
|
||||||
|
|
||||||
|
ConversationState convState = new ConversationState(storage);
|
||||||
|
StatePropertyAccessor<Integer> convProperty = convState.createProperty("convCount");
|
||||||
|
|
||||||
|
BotStateSet stateSet = new BotStateSet(userState, convState);
|
||||||
|
|
||||||
|
Assert.assertEquals(2, stateSet.getBotStates().size());
|
||||||
|
|
||||||
|
TurnContext turnContext = TestUtilities.createEmptyContext();
|
||||||
|
stateSet.loadAll(turnContext).join();
|
||||||
|
|
||||||
|
Integer userCount = userProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(0, userCount.intValue());
|
||||||
|
Integer convCount = convProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(0, convCount.intValue());
|
||||||
|
|
||||||
|
userProperty.set(turnContext, 10).join();
|
||||||
|
convProperty.set(turnContext, 20).join();
|
||||||
|
|
||||||
|
stateSet.saveAllChanges(turnContext).join();
|
||||||
|
|
||||||
|
userCount = userProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(10, userCount.intValue());
|
||||||
|
convCount = convProperty.get(turnContext, () -> 0).join();
|
||||||
|
Assert.assertEquals(20, convCount.intValue());
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
|
||||||
public class BotStateTest {
|
public class BotStateTests {
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void State_EmptyName() {
|
public void State_EmptyName() {
|
|
@ -5,6 +5,7 @@ package com.microsoft.bot.builder;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
public interface CallOnException {
|
public interface CallOnException {
|
||||||
<T> CompletableFuture<Void> apply(TurnContext context, T t);
|
<T> CompletableFuture<Void> invoke(TurnContext context, T t);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,26 +14,26 @@ import java.util.concurrent.CompletionException;
|
||||||
* multiple times to allow you to handle different exception types in different ways.
|
* multiple times to allow you to handle different exception types in different ways.
|
||||||
*/
|
*/
|
||||||
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
|
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
|
||||||
private final CallOnException _handler;
|
private CallOnException handler;
|
||||||
private final Class<T> _exceptionType;
|
private Class<T> exceptionType;
|
||||||
|
|
||||||
public CatchExceptionMiddleware(CallOnException callOnException, Class<T> exceptionType) {
|
public CatchExceptionMiddleware(CallOnException withCallOnException, Class<T> withExceptionType) {
|
||||||
_handler = callOnException;
|
handler = withCallOnException;
|
||||||
_exceptionType = exceptionType;
|
exceptionType = withExceptionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> onTurn(TurnContext context, NextDelegate next) {
|
public CompletableFuture<Void> onTurn(TurnContext context, NextDelegate next) {
|
||||||
|
|
||||||
Class c = _exceptionType.getDeclaringClass();
|
Class c = exceptionType.getDeclaringClass();
|
||||||
|
|
||||||
// Continue to route the activity through the pipeline
|
// Continue to route the activity through the pipeline
|
||||||
// any errors further down the pipeline will be caught by
|
// any errors further down the pipeline will be caught by
|
||||||
// this try / catch
|
// this try / catch
|
||||||
return next.next()
|
return next.next()
|
||||||
.exceptionally(exception -> {
|
.exceptionally(exception -> {
|
||||||
if (_exceptionType.isInstance(exception)) {
|
if (exceptionType.isInstance(exception)) {
|
||||||
_handler.apply(context, (T) exception);
|
handler.invoke(context, exception);
|
||||||
} else {
|
} else {
|
||||||
throw new CompletionException(exception);
|
throw new CompletionException(exception);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
// Licensed under the MIT License.
|
|
||||||
|
|
||||||
package com.microsoft.bot.builder;
|
|
||||||
|
|
||||||
import com.microsoft.bot.builder.adapters.TestAdapter;
|
|
||||||
import com.microsoft.bot.builder.adapters.TestFlow;
|
|
||||||
import com.microsoft.bot.connector.ExecutorFactory;
|
|
||||||
import com.microsoft.bot.schema.Activity;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
public class CatchException_MiddlewareTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void CatchException_TestMiddleware_TestStackedErrorMiddleware() throws ExecutionException, InterruptedException {
|
|
||||||
|
|
||||||
TestAdapter adapter = new TestAdapter()
|
|
||||||
.use(new CatchExceptionMiddleware<Exception>(new CallOnException() {
|
|
||||||
@Override
|
|
||||||
public <T> CompletableFuture apply(TurnContext context, T t) {
|
|
||||||
return CompletableFuture.runAsync(() -> {
|
|
||||||
Activity activity = context.getActivity();
|
|
||||||
if (activity instanceof Activity) {
|
|
||||||
try {
|
|
||||||
context.sendActivity(activity.createReply(t.toString())).join();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw new RuntimeException(String.format("CatchException_TestMiddleware_TestStackedErrorMiddleware:SendActivity failed %s", e.toString()));
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
Assert.assertTrue("Test was built for ActivityImpl", false);
|
|
||||||
|
|
||||||
}, ExecutorFactory.getExecutor());
|
|
||||||
|
|
||||||
}
|
|
||||||
}, Exception.class))
|
|
||||||
// Add middleware to catch NullReferenceExceptions before throwing up to the general exception instance
|
|
||||||
.use(new CatchExceptionMiddleware<NullPointerException>(new CallOnException() {
|
|
||||||
@Override
|
|
||||||
public <T> CompletableFuture apply(TurnContext context, T t) {
|
|
||||||
context.sendActivity("Sorry - Null Reference Exception").join();
|
|
||||||
return CompletableFuture.completedFuture(null);
|
|
||||||
}
|
|
||||||
}, NullPointerException.class));
|
|
||||||
|
|
||||||
|
|
||||||
new TestFlow(adapter, (context) -> {
|
|
||||||
if (StringUtils.equals(context.getActivity().getText(), "foo")) {
|
|
||||||
try {
|
|
||||||
context.sendActivity(context.getActivity().getText()).join();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (StringUtils.equals(context.getActivity().getText(), "UnsupportedOperationException")) {
|
|
||||||
throw new UnsupportedOperationException("Test");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
.send("foo")
|
|
||||||
.assertReply("foo", "passthrough")
|
|
||||||
.send("UnsupportedOperationException")
|
|
||||||
.assertReply("Test")
|
|
||||||
.startTest();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @Test
|
|
||||||
// [TestCategory("Middleware")]
|
|
||||||
public void CatchException_TestMiddleware_SpecificExceptionType()
|
|
||||||
{
|
|
||||||
TestAdapter adapter = new TestAdapter()
|
|
||||||
.Use(new CatchExceptionMiddleware<Exception>((context, exception) =>
|
|
||||||
{
|
|
||||||
context.SendActivity("Generic Exception Caught");
|
|
||||||
return CompletableFuture.CompletedTask;
|
|
||||||
}))
|
|
||||||
.Use(new CatchExceptionMiddleware<NullReferenceException>((context, exception) =>
|
|
||||||
{
|
|
||||||
context.SendActivity(exception.Message);
|
|
||||||
return CompletableFuture.CompletedTask;
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
await new TestFlow(adapter, (context) =>
|
|
||||||
{
|
|
||||||
if (context.Activity.AsMessageActivity().Text == "foo")
|
|
||||||
{
|
|
||||||
context.SendActivity(context.Activity.AsMessageActivity().Text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Activity.AsMessageActivity().Text == "NullReferenceException")
|
|
||||||
{
|
|
||||||
throw new NullReferenceException("Test");
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompletableFuture.CompletedTask;
|
|
||||||
})
|
|
||||||
.Send("foo")
|
|
||||||
.AssertReply("foo", "passthrough")
|
|
||||||
.Send("NullReferenceException")
|
|
||||||
.AssertReply("Test")
|
|
||||||
.StartTest();
|
|
||||||
}*/
|
|
||||||
}
|
|
Загрузка…
Ссылка в новой задаче