Unfinished Bot Builder
This commit is contained in:
Родитель
b04c8f052b
Коммит
c67a4acf09
|
@ -0,0 +1,262 @@
|
|||
// 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.ChannelAccount;
|
||||
import com.microsoft.bot.schema.MessageReaction;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* An implementation of the IBot interface intended for further subclassing.
|
||||
* Derive from this class to plug in code to handle particular Activity types.
|
||||
* Pre and post processing of Activities can be plugged in by deriving and calling
|
||||
* the base class implementation.
|
||||
*/
|
||||
public class ActivityHandler implements Bot {
|
||||
/**
|
||||
* The OnTurnAsync function is called by the Adapter (for example, the {@link BotFrameworkAdapter} at
|
||||
* runtime in order to process an inbound Activity.
|
||||
*
|
||||
* @param turnContext The context object for this turn. Provides information about the
|
||||
* incoming activity, and other data needed to process the activity.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> onTurnAsync(TurnContext turnContext) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("TurnContext cannot be null.");
|
||||
}
|
||||
|
||||
if (turnContext.getActivity() == null) {
|
||||
throw new IllegalArgumentException("turnContext must have a non-null Activity.");
|
||||
}
|
||||
|
||||
if (turnContext.getActivity().getType() == null) {
|
||||
throw new IllegalArgumentException("turnContext.getActivity must have a non-null Type.");
|
||||
}
|
||||
|
||||
switch (turnContext.getActivity().getType()) {
|
||||
case MESSAGE:
|
||||
return onMessageActivityAsync(turnContext);
|
||||
case CONVERSATION_UPDATE:
|
||||
return onConversationUpdateActivityAsync(turnContext);
|
||||
case MESSAGE_REACTION:
|
||||
return onMessageReactionActivityAsync(turnContext);
|
||||
case EVENT:
|
||||
return onEventActivityAsync(turnContext);
|
||||
|
||||
default:
|
||||
return onUnrecognizedActivityAsync(turnContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a message activity is received from the user when the base behavior of
|
||||
* {@link #onTurnAsync(TurnContext)} is used.
|
||||
*
|
||||
* If overridden, this could potentially contain conversational logic.
|
||||
* By default, this method does nothing.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> onMessageActivityAsync(TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a conversation update activity is received from the channel when the base behavior of
|
||||
* {@link #onTurnAsync(TurnContext)} is used.
|
||||
*
|
||||
* Conversation update activities are useful when it comes to responding to users being added to or removed
|
||||
* from the conversation.
|
||||
*
|
||||
* For example, a bot could respond to a user being added by greeting the user.
|
||||
* By default, this method will call {@link #onMembersAddedAsync(List, TurnContext)} if any users have been added,
|
||||
* or {@link #onMembersRemovedAsync(List, TurnContext)} if any users have been removed. The method checks the member
|
||||
* ID so that it only responds to updates regarding members other than the bot itself.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> onConversationUpdateActivityAsync(TurnContext turnContext) {
|
||||
if (turnContext.getActivity().getMembersAdded() != null) {
|
||||
if (turnContext.getActivity().getMembersAdded().stream()
|
||||
.anyMatch(m -> StringUtils.equals(m.getId(), turnContext.getActivity().getId()))) {
|
||||
|
||||
return onMembersAddedAsync(turnContext.getActivity().getMembersAdded(), turnContext);
|
||||
}
|
||||
} else if (turnContext.getActivity().getMembersRemoved() != null) {
|
||||
if (turnContext.getActivity().getMembersRemoved().stream()
|
||||
.anyMatch(m -> StringUtils.equals(m.getId(), turnContext.getActivity().getId()))) {
|
||||
|
||||
return onMembersRemovedAsync(turnContext.getActivity().getMembersRemoved(), turnContext);
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when members other than this bot (like a user) are added to the conversation when the base behavior of
|
||||
* {@link #onConversationUpdateActivityAsync(TurnContext)} is used.
|
||||
*
|
||||
* If overridden, this could potentially send a greeting message to the user instead of waiting for the user to
|
||||
* send a message first.
|
||||
*
|
||||
* By default, this method does nothing.
|
||||
*
|
||||
* @param membersAdded A list of all the users that have been added in the conversation update.
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture<Void> onMembersAddedAsync(List<ChannelAccount> membersAdded, TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when members other than this bot (like a user) are removed from the conversation when the base
|
||||
* behavior of {@link #onConversationUpdateActivityAsync(TurnContext)} is used.
|
||||
*
|
||||
* This method could optionally be overridden to perform actions related to users leaving a group conversation.
|
||||
*
|
||||
* By default, this method does nothing.
|
||||
*
|
||||
* @param membersRemoved A list of all the users that have been removed in the conversation update.
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture<Void> onMembersRemovedAsync(List<ChannelAccount> membersRemoved, TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an event activity is received from the connector when the base behavior of
|
||||
* {@link #onTurnAsync(TurnContext)} is used.
|
||||
*
|
||||
* Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
|
||||
* previously sent activity. Message reactions are only supported by a few channels.
|
||||
*
|
||||
* The activity that the message reaction corresponds to is indicated in the replyToId property.
|
||||
* The value of this property is the activity id of a previously sent activity given back to the
|
||||
* bot as the response from a send call.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture onMessageReactionActivityAsync(TurnContext turnContext) {
|
||||
CompletableFuture task = null;
|
||||
|
||||
if (turnContext.getActivity().getReactionsAdded() != null) {
|
||||
task = onReactionsAddedAsync(turnContext.getActivity().getReactionsAdded(), turnContext);
|
||||
}
|
||||
|
||||
if (turnContext.getActivity().getReactionsRemoved() != null) {
|
||||
if (task != null) {
|
||||
task.thenApply((result) -> onReactionsRemovedAsync(
|
||||
turnContext.getActivity().getReactionsRemoved(), turnContext));
|
||||
} else {
|
||||
task = onReactionsRemovedAsync(turnContext.getActivity().getReactionsRemoved(), turnContext);
|
||||
}
|
||||
}
|
||||
|
||||
return task == null ? CompletableFuture.completedFuture(null) : task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there have been Reactions added that reference a previous Activity.
|
||||
*
|
||||
* @param messageReactions The list of reactions added.
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture onReactionsAddedAsync(List<MessageReaction> messageReactions,
|
||||
TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when there have been Reactions removed that reference a previous Activity.
|
||||
*
|
||||
* @param messageReactions The list of reactions removed.
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture onReactionsRemovedAsync(List<MessageReaction> messageReactions,
|
||||
TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an event activity is received from the connector when the base behavior of
|
||||
* {@link #onTurnAsync(TurnContext)} is used.
|
||||
*
|
||||
* Event activities can be used to communicate many different things.
|
||||
*
|
||||
* By default, this method will call {@link #onTokenResponseEventAsync(TurnContext)} if the
|
||||
* activity's name is "tokens/response" or {@link #onEventAsync(TurnContext)} otherwise.
|
||||
* "tokens/response" event can be triggered by an {@link com.microsoft.bot.schema.OAuthCard}.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture onEventActivityAsync(TurnContext turnContext) {
|
||||
if (StringUtils.equals(turnContext.getActivity().getName(), "tokens/response")) {
|
||||
return onTokenResponseEventAsync(turnContext);
|
||||
}
|
||||
|
||||
return onEventAsync(turnContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a "tokens/response" event is received when the base behavior of
|
||||
* {@link #onEventActivityAsync(TurnContext)} is used.
|
||||
*
|
||||
* If using an OAuthPrompt, override this method to forward this {@link Activity} to the current dialog.
|
||||
*
|
||||
* By default, this method does nothing.
|
||||
*
|
||||
* @param turnContext
|
||||
* @return
|
||||
*/
|
||||
protected CompletableFuture onTokenResponseEventAsync(TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an event other than <c>tokens/response</c> is received when the base behavior of
|
||||
* {@link #onEventActivityAsync(TurnContext)} is used.
|
||||
*
|
||||
* This method could optionally be overridden if the bot is meant to handle miscellaneous events.
|
||||
*
|
||||
* By default, this method does nothing.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture onEventAsync(TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when an activity other than a message, conversation update, or event is received when the base behavior of
|
||||
* {@link #onTurnAsync(TurnContext)} is used.
|
||||
*
|
||||
* If overridden, this could potentially respond to any of the other activity types like
|
||||
* {@link com.microsoft.bot.schema.ActivityTypes#CONTACT_RELATION_UPDATE} or
|
||||
* {@link com.microsoft.bot.schema.ActivityTypes#END_OF_CONVERSATION}.
|
||||
*
|
||||
* By default, this method does nothing.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture onUnrecognizedActivityAsync(TurnContext turnContext) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Represents a bot that can operate on incoming activities.
|
||||
*/
|
||||
public interface Bot {
|
||||
/**
|
||||
* When implemented in a bot, handles an incoming activity.
|
||||
*
|
||||
* @param turnContext The context object for this turn. Provides information about the
|
||||
* incoming activity, and other data needed to process the activity.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
CompletableFuture onTurnAsync(TurnContext turnContext);
|
||||
}
|
|
@ -17,7 +17,7 @@ import java.util.function.Function;
|
|||
* 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's application logic, and sends responses back to the user's channel.
|
||||
* <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
|
||||
* 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
|
||||
|
@ -50,7 +50,7 @@ public abstract class BotAdapter {
|
|||
* Middleware is added to the adapter at initialization time.
|
||||
* 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);
|
||||
return this;
|
||||
}
|
||||
|
@ -66,7 +66,8 @@ public abstract class BotAdapter {
|
|||
* the receiving channel assigned to the activities.
|
||||
* {@linkalso TurnContext.OnSendActivities(SendActivitiesHandler)}
|
||||
*/
|
||||
public abstract ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException;
|
||||
public abstract CompletableFuture<ResourceResponse[]> sendActivitiesAsync(TurnContext context,
|
||||
Activity[] activities);
|
||||
|
||||
/**
|
||||
* When overridden in a derived class, replaces an existing activity in the
|
||||
|
@ -82,7 +83,8 @@ public abstract class BotAdapter {
|
|||
* of the activity to replace.</p>
|
||||
* {@linkalso TurnContext.OnUpdateActivity(UpdateActivityHandler)}
|
||||
*/
|
||||
public abstract ResourceResponse UpdateActivity(TurnContext context, Activity activity);
|
||||
public abstract CompletableFuture<ResourceResponse> updateActivityAsync(TurnContext context,
|
||||
Activity activity);
|
||||
|
||||
/**
|
||||
* When overridden in a derived class, deletes an existing activity in the
|
||||
|
@ -91,11 +93,12 @@ public abstract class BotAdapter {
|
|||
* @param context The context object for the turn.
|
||||
* @param reference Conversation reference for the activity to delete.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* The {@link ConversationReference.ActivityId} of the conversation
|
||||
* The {@link ConversationReference#getActivityId} of the conversation
|
||||
* reference identifies the activity to delete.
|
||||
* {@linkalso TurnContext.OnDeleteActivity(DeleteActivityHandler)}
|
||||
*/
|
||||
public abstract void DeleteActivity(TurnContext context, ConversationReference reference) throws ExecutionException, InterruptedException;
|
||||
public abstract CompletableFuture<Void> deleteActivityAsync(TurnContext context,
|
||||
ConversationReference reference);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -111,24 +114,24 @@ public abstract class BotAdapter {
|
|||
* 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, MiddlewareSet.NextDelegate)}
|
||||
* {@link Middleware#onTurnAsync(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(ConversationReference, Func{TurnContext, Task})}
|
||||
* {@link Bot#onTurnAsync(TurnContext)} method. When the turn is
|
||||
* initiated by a call to {@link #continueConversationAsync(String, ConversationReference, BotCallbackHandler)}
|
||||
* (proactive messaging), the callback method is the callback method that was provided in the call.</p>
|
||||
*/
|
||||
protected void RunPipeline(TurnContext context, Consumer<TurnContext> callback) throws Exception {
|
||||
protected void runPipeline(TurnContext context, BotCallbackHandler callback) throws Exception {
|
||||
BotAssert.ContextNotNull(context);
|
||||
|
||||
// Call any registered Middleware Components looking for ReceiveActivity()
|
||||
if (context.getActivity() != null) {
|
||||
_middlewareSet.ReceiveActivityWithStatus(context, callback);
|
||||
_middlewareSet.receiveActivityWithStatus(context, callback);
|
||||
} else {
|
||||
// call back to caller on proactive case
|
||||
if (callback != null) {
|
||||
callback.accept(context);
|
||||
callback.invoke(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +145,8 @@ public abstract class BotAdapter {
|
|||
* @return A task that represents the work queued to execute.
|
||||
* @throws UnsupportedOperationException No base implementation is provided.
|
||||
*/
|
||||
public CompletableFuture CreateConversation(String channelId, Function<TurnContext, CompletableFuture> callback) {
|
||||
public CompletableFuture<Void> createConversationAsync(String channelId,
|
||||
Function<TurnContext, BotCallbackHandler> callback) {
|
||||
throw new UnsupportedOperationException("Adapter does not support CreateConversation with this arguments");
|
||||
}
|
||||
|
||||
|
@ -160,13 +164,15 @@ public abstract class BotAdapter {
|
|||
* before the bot can send activities to the user.
|
||||
* {@linkalso RunPipeline(TurnContext, Func { TurnContext, Task })}
|
||||
*/
|
||||
public void ContinueConversation(String botId, ConversationReference reference, Consumer<TurnContext> callback) throws Exception {
|
||||
public CompletableFuture<Void> continueConversationAsync(String botId,
|
||||
ConversationReference reference,
|
||||
BotCallbackHandler callback) {
|
||||
|
||||
ConversationReferenceHelper conv = new ConversationReferenceHelper(reference);
|
||||
Activity activity = conv.getPostToBotMessage();
|
||||
|
||||
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
|
||||
this.RunPipeline(context, callback);
|
||||
this.runPipeline(context, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BotCallbackHandler {
|
||||
/**
|
||||
* The callback delegate for application code.
|
||||
*
|
||||
* @param turnContext The turn context.
|
||||
* @return A Task representing the asynchronous operation.
|
||||
*/
|
||||
CompletableFuture<Void> invoke(TurnContext turnContext);
|
||||
}
|
|
@ -132,10 +132,10 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
claims.put(AuthenticationConstants.APPID_CLAIM, botAppId);
|
||||
ClaimsIdentity claimsIdentity = new ClaimsIdentity("ExternalBearer", claims);
|
||||
|
||||
context.getServices().Add("BotIdentity", claimsIdentity);
|
||||
context.getTurnState().add("BotIdentity", claimsIdentity);
|
||||
|
||||
ConnectorClient connectorClient = this.CreateConnectorClientAsync(reference.getServiceUrl(), claimsIdentity).join();
|
||||
context.getServices().Add("ConnectorClient", connectorClient);
|
||||
context.getTurnState().add("ConnectorClient", connectorClient);
|
||||
RunPipeline(context, callback);
|
||||
}
|
||||
return;
|
||||
|
@ -218,18 +218,18 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
BotAssert.ActivityNotNull(activity);
|
||||
|
||||
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
|
||||
context.getServices().Add("BotIdentity", identity);
|
||||
context.getTurnState().add("BotIdentity", identity);
|
||||
|
||||
ConnectorClient connectorClient = this.CreateConnectorClientAsync(activity.getServiceUrl(), identity).join();
|
||||
// TODO: Verify key that C# uses
|
||||
context.getServices().Add("ConnectorClient", connectorClient);
|
||||
context.getTurnState().add("ConnectorClient", connectorClient);
|
||||
|
||||
super.RunPipeline(context, callback);
|
||||
|
||||
// Handle Invoke scenarios, which deviate from the request/response model in that
|
||||
// the Bot will return a specific body and return code.
|
||||
if (activity.getType() == ActivityTypes.INVOKE) {
|
||||
Activity invokeResponse = context.getServices().Get(InvokeReponseKey);
|
||||
Activity invokeResponse = context.getTurnState().get(InvokeReponseKey);
|
||||
if (invokeResponse == null) {
|
||||
// ToDo: Trace Here
|
||||
throw new IllegalStateException("Bot failed to return a valid 'invokeResponse' activity.");
|
||||
|
@ -255,7 +255,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
* the receiving channel assigned to the activities.
|
||||
* {@linkalso TurnContext.OnSendActivities(SendActivitiesHandler)}
|
||||
*/
|
||||
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException {
|
||||
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
|
||||
if (context == null) {
|
||||
throw new IllegalArgumentException("context");
|
||||
}
|
||||
|
@ -283,20 +283,20 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
// The Activity Schema doesn't have a delay type build in, so it's simulated
|
||||
// here in the Bot. This matches the behavior in the Node connector.
|
||||
int delayMs = (int) activity.getValue();
|
||||
Thread.sleep(delayMs);
|
||||
try { Thread.sleep(delayMs); } catch (InterruptedException e) {}
|
||||
//await(Task.Delay(delayMs));
|
||||
// No need to create a response. One will be created below.
|
||||
} else if (activity.getType().toString().equals("invokeResponse")) // Aligning name with Node
|
||||
{
|
||||
context.getServices().Add(InvokeReponseKey, activity);
|
||||
context.getTurnState().add(InvokeReponseKey, activity);
|
||||
// No need to create a response. One will be created below.
|
||||
} else if (activity.getType() == ActivityTypes.TRACE && !activity.getChannelId().equals("emulator")) {
|
||||
// if it is a Trace activity we only send to the channel if it's the emulator.
|
||||
} else if (!StringUtils.isEmpty(activity.getReplyToId())) {
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
response = connectorClient.getConversations().replyToActivity(activity.getConversation().getId(), activity.getId(), activity);
|
||||
} else {
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
response = connectorClient.getConversations().sendToConversation(activity.getConversation().getId(), activity);
|
||||
}
|
||||
|
||||
|
@ -334,7 +334,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
*/
|
||||
@Override
|
||||
public ResourceResponse UpdateActivity(TurnContext context, Activity activity) {
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
// TODO String conversationId, String activityId, Activity activity)
|
||||
return connectorClient.getConversations().updateActivity(activity.getConversation().getId(), activity.getId(), activity);
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
* {@linkalso TurnContext.OnDeleteActivity(DeleteActivityHandler)}
|
||||
*/
|
||||
public void DeleteActivity(TurnContext context, ConversationReference reference) {
|
||||
RestConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
RestConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
try {
|
||||
connectorClient.getConversations().deleteConversationMemberAsync(
|
||||
reference.getConversation().getId(), reference.getActivityId()).join();
|
||||
|
@ -373,7 +373,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
if (StringUtils.isEmpty(context.getActivity().getConversation().getId()))
|
||||
throw new IllegalArgumentException("BotFrameworkAdapter.deleteConversationMember(): missing conversation.id");
|
||||
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
|
||||
String conversationId = context.getActivity().getConversation().getId();
|
||||
|
||||
|
@ -404,7 +404,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
if (StringUtils.isEmpty((context.getActivity().getConversation().getId())))
|
||||
throw new IllegalArgumentException("BotFrameworkAdapter.GetActivityMembers(): missing conversation.id");
|
||||
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
String conversationId = context.getActivity().getConversation().getId();
|
||||
|
||||
// TODO:
|
||||
|
@ -426,7 +426,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
if (StringUtils.isEmpty(context.getActivity().getConversation().getId()))
|
||||
throw new IllegalArgumentException("BotFrameworkAdapter.GetActivityMembers(): missing conversation.id");
|
||||
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
String conversationId = context.getActivity().getConversation().getId();
|
||||
|
||||
// TODO
|
||||
|
@ -487,7 +487,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
}
|
||||
|
||||
public CompletableFuture<ConversationsResult> GetConversations(TurnContextImpl context, String continuationToken) {
|
||||
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
|
||||
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
|
||||
// TODO
|
||||
//ConversationsResult results = await(connectorClient.conversations().getConversationsAsync());
|
||||
return completedFuture(null);
|
||||
|
@ -626,7 +626,7 @@ public class BotFrameworkAdapter extends BotAdapter {
|
|||
}
|
||||
|
||||
protected OAuthClient CreateOAuthApiClient(TurnContext context) throws MalformedURLException, URISyntaxException {
|
||||
RestConnectorClient client = context.getServices().Get("ConnectorClient");
|
||||
RestConnectorClient client = context.getTurnState().get("ConnectorClient");
|
||||
if (client == null) {
|
||||
throw new IllegalArgumentException("CreateOAuthApiClient: OAuth requires a valid ConnectorClient instance");
|
||||
}
|
||||
|
|
|
@ -2,164 +2,380 @@
|
|||
// Licensed under the MIT License.
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import static java.util.concurrent.CompletableFuture.completedFuture;
|
||||
|
||||
/**
|
||||
* Abstract Base class which manages details of automatic loading and saving of bot state.
|
||||
*
|
||||
* @param TState The type of the bot state object.
|
||||
* Reads and writes state for your bot to storage.
|
||||
*/
|
||||
//public class BotState<TState> : Middleware
|
||||
// where TState : class, new()
|
||||
public class BotState<TState> implements Middleware {
|
||||
|
||||
private final StateSettings settings;
|
||||
private final Storage storage;
|
||||
private final Function<TurnContext, String> keyDelegate;
|
||||
private final String propertyName;
|
||||
private final Supplier<? extends TState> ctor;
|
||||
public abstract class BotState implements PropertyManager {
|
||||
private String contextServiceKey;
|
||||
private Storage storage;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BotState{TState}} middleware object.
|
||||
* Initializes a new instance of the BotState class.
|
||||
*
|
||||
* @param name The name to use to load or save the state object.
|
||||
* @param storage The storage provider to use.
|
||||
* @param settings The state persistance options to use.
|
||||
* @param withStorage The storage provider to use.
|
||||
* @param withContextServiceKey The key for caching on the context services dictionary.
|
||||
*/
|
||||
public BotState(Storage storage, String propertyName, Function<TurnContext, String> keyDelegate, Supplier<? extends TState> ctor) {
|
||||
this(storage, propertyName, keyDelegate, ctor, null);
|
||||
}
|
||||
public BotState(Storage withStorage, String withContextServiceKey) {
|
||||
if (withStorage == null) {
|
||||
throw new IllegalArgumentException("Storage cannot be null");
|
||||
}
|
||||
storage = withStorage;
|
||||
|
||||
public BotState(Storage storage, String propertyName, Function<TurnContext, String> keyDelegate, Supplier<? extends TState> ctor, StateSettings settings) {
|
||||
if (null == storage) {
|
||||
throw new IllegalArgumentException("Storage");
|
||||
if (StringUtils.isEmpty(withContextServiceKey)) {
|
||||
throw new IllegalArgumentException("context service key cannot be empty");
|
||||
}
|
||||
if (null == propertyName) {
|
||||
throw new IllegalArgumentException("String propertyName");
|
||||
}
|
||||
if (null == keyDelegate) {
|
||||
throw new IllegalArgumentException("Key Delegate");
|
||||
}
|
||||
if (null == ctor) {
|
||||
throw new IllegalArgumentException("ctor");
|
||||
}
|
||||
this.ctor = ctor;
|
||||
this.storage = storage;
|
||||
this.propertyName = propertyName;
|
||||
this.keyDelegate = keyDelegate;
|
||||
if (null == settings)
|
||||
this.settings = new StateSettings();
|
||||
else
|
||||
this.settings = settings;
|
||||
contextServiceKey = withContextServiceKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processess an incoming activity.
|
||||
* Create a property definition and register it with this BotState.
|
||||
*
|
||||
* @param context The context object for this turn.
|
||||
* @param next The delegate to call to continue the bot middleware pipeline.
|
||||
* @param name name of property.
|
||||
* @param <T> type of property.
|
||||
* @return The created state property accessor.
|
||||
*/
|
||||
public <T extends Object> StatePropertyAccessor<T> createProperty(String name) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
throw new IllegalArgumentException("name cannot be empty");
|
||||
}
|
||||
|
||||
return new BotStatePropertyAccessor<>(this, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in the current state object and caches it in the context object for this turn.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* This middleware loads the state object on the leading edge of the middleware pipeline
|
||||
* and persists the state object on the trailing edge.
|
||||
*/
|
||||
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
|
||||
ReadToContextService(context);
|
||||
next.next();
|
||||
WriteFromContextService(context).join();
|
||||
return;
|
||||
}
|
||||
|
||||
protected void ReadToContextService(TurnContext context) throws IllegalArgumentException, JsonProcessingException {
|
||||
String key = this.keyDelegate.apply(context);
|
||||
Map<String, ?> items = null;
|
||||
try {
|
||||
CompletableFuture<Map<String, ?>> result = storage.Read(new String[]{key});
|
||||
items = result.get();
|
||||
System.out.println(String.format("BotState:OnTurn(tid:%s) ReadToContextService: Found %s items", Thread.currentThread().getId(), items.size()));
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("Error waiting context storage read: %s", e.toString()));
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ExecutionException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
TState state = StreamSupport.stream(items.entrySet().spliterator(), false)
|
||||
.filter(entry -> entry.getKey() == key)
|
||||
.map(Map.Entry::getValue)
|
||||
.map(entry -> (TState) entry)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
|
||||
//var state = items.Where(entry => entry.Key == key).Select(entry => entry.Value).OfType<TState>().FirstOrDefault();
|
||||
if (state == null)
|
||||
state = ctor.get();
|
||||
context.getServices().Add(this.propertyName, state);
|
||||
}
|
||||
|
||||
protected CompletableFuture WriteFromContextService(TurnContext context) throws Exception {
|
||||
TState state = context.getServices().Get(this.propertyName);
|
||||
return Write(context, state);
|
||||
public CompletableFuture<Void> loadAsync(TurnContext turnContext) {
|
||||
return loadAsync(turnContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads state from storage.
|
||||
* Reads in the current state object and caches it in the context object for this turn.
|
||||
*
|
||||
* @param TState The type of the bot state object.
|
||||
* @param context The context object for this turn.
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param force True to bypass the cache.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<TState> Read(TurnContext context) throws JsonProcessingException {
|
||||
String key = this.keyDelegate.apply(context);
|
||||
Map<String, ?> items = storage.Read(new String[]{key}).join();
|
||||
TState state = StreamSupport.stream(items.entrySet().spliterator(), false)
|
||||
.filter(item -> item.getKey() == key)
|
||||
.map(Map.Entry::getValue)
|
||||
.map(item -> (TState) item)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
//var state = items.Where(entry => entry.Key == key).Select(entry => entry.Value).OfType<TState>().FirstOrDefault();
|
||||
if (state == null)
|
||||
state = ctor.get();
|
||||
return completedFuture(state);
|
||||
public CompletableFuture<Void> loadAsync(TurnContext turnContext, boolean force) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
|
||||
String storageKey = getStorageKey(turnContext);
|
||||
if (force || cachedState == null || cachedState.getState() == null) {
|
||||
return storage.readAsync(new String[]{storageKey})
|
||||
.thenApply(val -> {
|
||||
turnContext.getTurnState().put(contextServiceKey, new CachedBotState(val));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes state to storage.
|
||||
* If it has changed, writes to storage the state object that is cached in the current
|
||||
* context object for this turn.
|
||||
*
|
||||
* @param context The context object for this turn.
|
||||
* @param state The state object.
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture Write(TurnContext context, TState state) throws Exception {
|
||||
HashMap<String, TState> changes = new HashMap<String, TState>();
|
||||
//List<Map.Entry<String, ?>> changes = new ArrayList<Map.Entry<String, ?>>();
|
||||
if (state == null)
|
||||
state = ctor.get();
|
||||
String key = keyDelegate.apply(context);
|
||||
changes.put(key, state);
|
||||
public CompletableFuture<Void> saveChangesAsync(TurnContext turnContext) {
|
||||
return saveChangesAsync(turnContext, false);
|
||||
}
|
||||
|
||||
if (this.settings.getLastWriterWins()) {
|
||||
for (Map.Entry<String, ?> item : changes.entrySet()) {
|
||||
if (item.getValue() instanceof StoreItem) {
|
||||
StoreItem valueStoreItem = (StoreItem) item.getValue();
|
||||
valueStoreItem.seteTag("*");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If it has changed, writes to storage the state object that is cached in the current
|
||||
* context object for this turn.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param force True to save state to storage whether or not there are changes.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> saveChangesAsync(TurnContext turnContext, boolean force) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
|
||||
if (force || (cachedState != null && cachedState.isChanged())) {
|
||||
String storageKey = getStorageKey(turnContext);
|
||||
Map<String, Object> changes = new HashMap<String, Object>() {{
|
||||
put(storageKey, cachedState.state);
|
||||
}};
|
||||
|
||||
return storage.writeAsync(changes)
|
||||
.thenApply(val -> {
|
||||
cachedState.setHashCode(cachedState.computeHashCode(cachedState.state));
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any state currently stored in this state scope.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> clearStateAsync(TurnContext turnContext) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
turnContext.getTurnState().put(contextServiceKey, new CachedBotState(new HashMap<>()));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete any state currently stored in this state scope.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> deleteAsync(TurnContext turnContext) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
|
||||
if (cachedState != null) {
|
||||
turnContext.getTurnState().remove(contextServiceKey);
|
||||
}
|
||||
|
||||
String storageKey = getStorageKey(turnContext);
|
||||
return storage.deleteAsync(new String[] {storageKey});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of the raw cached data from the TurnContext, this can be used for tracing scenarios.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return A JSON representation of the cached state.
|
||||
*/
|
||||
public JsonNode get(TurnContext turnContext) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
String stateKey = getClass().getName();
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(stateKey);
|
||||
return new ObjectMapper().valueToTree(cachedState.state);
|
||||
}
|
||||
|
||||
/**
|
||||
* When overridden in a derived class, gets the key to use when reading and writing state to and from storage.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return The storage key.
|
||||
*/
|
||||
public abstract String getStorageKey(TurnContext turnContext);
|
||||
|
||||
/**
|
||||
* Gets a property from the state cache in the turn context.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param propertyName The name of the property to get.
|
||||
* @param <T> The property type.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected <T> CompletableFuture<T> getPropertyValueAsync(TurnContext turnContext,
|
||||
String propertyName) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(propertyName)) {
|
||||
throw new IllegalArgumentException("propertyName cannot be empty");
|
||||
}
|
||||
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
|
||||
return (CompletableFuture<T>) CompletableFuture.completedFuture(cachedState.getState().get(propertyName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a property from the state cache in the turn context.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param propertyName The name of the property to delete.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture<Void> deletePropertyValueAsync(TurnContext turnContext, String propertyName) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(propertyName)) {
|
||||
throw new IllegalArgumentException("propertyName cannot be empty");
|
||||
}
|
||||
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
|
||||
cachedState.getState().remove(propertyName);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of a property in the state cache in the turn context.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param propertyName The name of the property to set.
|
||||
* @param value The value to set on the property.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
protected CompletableFuture<Void> setPropertyValueAsync(TurnContext turnContext,
|
||||
String propertyName,
|
||||
Object value) {
|
||||
if (turnContext == null) {
|
||||
throw new IllegalArgumentException("turnContext cannot be null");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(propertyName)) {
|
||||
throw new IllegalArgumentException("propertyName cannot be empty");
|
||||
}
|
||||
|
||||
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
|
||||
cachedState.getState().put(propertyName, value);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal cached bot state.
|
||||
*/
|
||||
private static class CachedBotState {
|
||||
private Map<String, Object> state;
|
||||
private int hash;
|
||||
|
||||
public CachedBotState(Map<String, Object> withState) {
|
||||
state = withState;
|
||||
hash = computeHashCode(withState);
|
||||
}
|
||||
|
||||
public Map<String, Object> getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void setState(Map<String, Object> withState) {
|
||||
state = withState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
public void setHashCode(int witHashCode) {
|
||||
hash = witHashCode;
|
||||
}
|
||||
|
||||
public boolean isChanged() {
|
||||
return hash != computeHashCode(state);
|
||||
}
|
||||
|
||||
public int computeHashCode(Object obj) {
|
||||
//TODO: this may not be the same as in dotnet
|
||||
return obj.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements IPropertyAccessor for an IPropertyContainer.
|
||||
*
|
||||
* Note the semantic of this accessor are intended to be lazy, this means teh Get, Set and Delete
|
||||
* methods will first call LoadAsync. This will be a no-op if the data is already loaded.
|
||||
* The implication is you can just use this accessor in the application code directly without first calling LoadAsync
|
||||
* this approach works with the AutoSaveStateMiddleware which will save as needed at the end of a turn.
|
||||
*
|
||||
* @param <T> type of value the propertyAccessor accesses.
|
||||
*/
|
||||
private static class BotStatePropertyAccessor<T> implements StatePropertyAccessor<T> {
|
||||
private String name;
|
||||
private BotState botState;
|
||||
|
||||
public BotStatePropertyAccessor(BotState withState, String withName) {
|
||||
botState = withState;
|
||||
name = withName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property value. The semantics are intended to be lazy, note the use of LoadAsync at the start.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param defaultValueFactory Defines the default value. Invoked when no value been set for the requested
|
||||
* state property. If defaultValueFactory is defined as null,
|
||||
* the MissingMemberException will be thrown if the underlying property is not set.
|
||||
* @param <T> type of value the propertyAccessor accesses.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
@Override
|
||||
public <T, S> CompletableFuture<T> getAsync(TurnContext turnContext, Supplier<S> defaultValueFactory) {
|
||||
return botState.loadAsync(turnContext)
|
||||
.thenCombine(botState.getPropertyValueAsync(turnContext, name), (loadResult, value) -> {
|
||||
if (value == null) {
|
||||
value = defaultValueFactory.get();
|
||||
}
|
||||
|
||||
return (T) value;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the property. The semantics are intended to be lazy, note the use of LoadAsync at the start.
|
||||
*
|
||||
* @param turnContext The turn context.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteAsync(TurnContext turnContext) {
|
||||
return botState.loadAsync(turnContext)
|
||||
.thenCompose(state -> botState.deletePropertyValueAsync(turnContext, name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the property value. The semantics are intended to be lazy, note the use of LoadAsync at the start.
|
||||
*
|
||||
* @param turnContext The turn context.
|
||||
* @param value The value to set.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<Void> setAsync(TurnContext turnContext, T value) {
|
||||
return botState.loadAsync(turnContext)
|
||||
.thenCompose(state -> botState.setPropertyValueAsync(turnContext, name, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets name of the property.
|
||||
*
|
||||
* @return Name of the property.
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets name of the property.
|
||||
*
|
||||
* @param withName Name of the property.
|
||||
*/
|
||||
@Override
|
||||
public void setName(String withName) {
|
||||
name = withName;
|
||||
}
|
||||
return completedFuture(storage.Write(changes).join());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Manages a collection of botState and provides ability to load and save in parallel.
|
||||
*/
|
||||
public class BotStateSet {
|
||||
private List<BotState> botStates = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Initializes a new instance of the BotStateSet class.
|
||||
* @param botStates initial list of {@link BotState} objects to manage.
|
||||
*/
|
||||
public BotStateSet(List<BotState> botStates) {
|
||||
botStates.addAll(botStates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the BotStates list for the BotStateSet.
|
||||
* @return The BotState objects managed by this class.
|
||||
*/
|
||||
public List<BotState> getBotStates() {
|
||||
return botStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the BotStates list for the BotStateSet.
|
||||
* @param withBotState The BotState objects managed by this class.
|
||||
*/
|
||||
public void setBotStates(List<BotState> withBotState) {
|
||||
botStates = withBotState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a bot state object to the set.
|
||||
*
|
||||
* @param botState The bot state object to add.
|
||||
* @return The updated BotStateSet, so you can fluently call add(BotState) multiple times.
|
||||
*/
|
||||
public BotStateSet add(BotState botState) {
|
||||
botStates.add(botState);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all BotState records in parallel.
|
||||
*
|
||||
* @param turnContext The TurnContext.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> loadAllAsync(TurnContext turnContext) {
|
||||
return loadAllAsync(turnContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all BotState records in parallel.
|
||||
*
|
||||
* @param turnContext The TurnContext.
|
||||
* @param force should data be forced into cache.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> loadAllAsync(TurnContext turnContext, boolean force) {
|
||||
List<CompletableFuture<Void>> loadFutures = botStates.stream()
|
||||
.map(future -> future.loadAsync(turnContext, force))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.allOf(loadFutures.toArray(new CompletableFuture[loadFutures.size()]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save All BotState changes in parallel.
|
||||
*
|
||||
* @param turnContext The TurnContext.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> saveAllChangesAsync(TurnContext turnContext) {
|
||||
return saveAllChangesAsync(turnContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save All BotState changes in parallel.
|
||||
*
|
||||
* @param turnContext The TurnContext.
|
||||
* @param force should data be forced to save even if no change were detected.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> saveAllChangesAsync(TurnContext turnContext, boolean force) {
|
||||
List<CompletableFuture<Void>> saveFutures = botStates.stream()
|
||||
.map(future -> future.saveChangesAsync(turnContext, force))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return CompletableFuture.allOf(saveFutures.toArray(new CompletableFuture[saveFutures.size()]));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Logging client for Bot Telemetry.
|
||||
*/
|
||||
public interface BotTelemetryClient {
|
||||
|
||||
/**
|
||||
* Send information about availability of an application.
|
||||
*
|
||||
* @param name Availability test name.
|
||||
* @param timeStamp The time when the availability was captured.
|
||||
* @param duration The time taken for the availability test to run.
|
||||
* @param runLocation Name of the location the availability test was run from.
|
||||
* @param success True if the availability test ran successfully.
|
||||
*/
|
||||
default void trackAvailability(String name,
|
||||
DateTime timeStamp,
|
||||
Duration duration,
|
||||
String runLocation,
|
||||
boolean success) {
|
||||
trackAvailability(name, timeStamp,duration, runLocation, success, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send information about availability of an application.
|
||||
*
|
||||
* @param name Availability test name.
|
||||
* @param timeStamp The time when the availability was captured.
|
||||
* @param duration The time taken for the availability test to run.
|
||||
* @param runLocation Name of the location the availability test was run from.
|
||||
* @param success True if the availability test ran successfully.
|
||||
* @param message Error message on availability test run failure.
|
||||
* @param properties Named string values you can use to classify and search for this availability telemetry.
|
||||
* @param metrics Additional values associated with this availability telemetry.
|
||||
*/
|
||||
void trackAvailability(String name,
|
||||
DateTime timeStamp,
|
||||
Duration duration,
|
||||
String runLocation,
|
||||
boolean success,
|
||||
String message,
|
||||
Map<String, String> properties,
|
||||
Map<String, Double> metrics);
|
||||
|
||||
/**
|
||||
* Send information about an external dependency (outgoing call) in the application.
|
||||
*
|
||||
* @param dependencyTypeName Name of the command initiated with this dependency call. Low cardinality value.
|
||||
* Examples are SQL, Azure table, and HTTP.
|
||||
* @param target External dependency target.
|
||||
* @param dependencyName Name of the command initiated with this dependency call. Low cardinality value.
|
||||
* Examples are stored procedure name and URL path template.
|
||||
* @param data Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with
|
||||
* all query parameters.
|
||||
* @param startTime The time when the dependency was called.
|
||||
* @param duration The time taken by the external dependency to handle the call.
|
||||
* @param resultCode Result code of dependency call execution.
|
||||
* @param success True if the dependency call was handled successfully.
|
||||
*/
|
||||
void trackDependency(String dependencyTypeName,
|
||||
String target,
|
||||
String dependencyName,
|
||||
String data,
|
||||
DateTime startTime,
|
||||
Duration duration,
|
||||
String resultCode,
|
||||
boolean success);
|
||||
|
||||
/**
|
||||
* Logs custom events with extensible named fields.
|
||||
*
|
||||
* @param eventName A name for the event.
|
||||
*/
|
||||
default void trackEvent(String eventName) {
|
||||
trackEvent(eventName, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs custom events with extensible named fields.
|
||||
*
|
||||
* @param eventName A name for the event.
|
||||
* @param properties Named string values you can use to search and classify events.
|
||||
* @param metrics Measurements associated with this event.
|
||||
*/
|
||||
void trackEvent(String eventName, Map<String, String> properties, Map<String, Double> metrics);
|
||||
|
||||
/**
|
||||
* Logs a system exception.
|
||||
*
|
||||
* @param exception The exception to log.
|
||||
*/
|
||||
default void trackException(Exception exception) {
|
||||
trackException(exception, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a system exception.
|
||||
*
|
||||
* @param exception The exception to log.
|
||||
* @param properties Named string values you can use to classify and search for this exception.
|
||||
* @param metrics Additional values associated with this exception.
|
||||
*/
|
||||
void trackException(Exception exception, Map<String, String> properties, Map<String, Double> metrics);
|
||||
|
||||
/**
|
||||
* Send a trace message.
|
||||
*
|
||||
* @param message Message to display.
|
||||
* @param severityLevel Trace severity level.
|
||||
* @param properties Named string values you can use to search and classify events.
|
||||
*/
|
||||
void trackTrace(String message, Severity severityLevel, Map<String, String> properties);
|
||||
|
||||
/**
|
||||
* Flushes the in-memory buffer and any metrics being pre-aggregated.
|
||||
*/
|
||||
void flush();
|
||||
}
|
|
@ -1,49 +1,36 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Handles persistence of a conversation state object using the conversation ID as part of the key.
|
||||
* @param TState The type of the conversation state object.
|
||||
*/
|
||||
public class ConversationState<TState> extends BotState<TState>
|
||||
{
|
||||
public class ConversationState extends BotState {
|
||||
/**
|
||||
* The key to use to read and write this conversation state object to storage.
|
||||
* Creates a new {@link ConversationState} object.
|
||||
*/
|
||||
//
|
||||
// Note: Hard coded to maintain compatibility with C#
|
||||
// "ConversationState:{typeof(ConversationState<TState>).Namespace}.{typeof(ConversationState<TState>).Name}"
|
||||
public static String PropertyName() {
|
||||
return String.format("ConversationState:Microsoft.Bot.Builder.Core.Extensions.ConversationState`1");
|
||||
public ConversationState(Storage withStorage) {
|
||||
super(withStorage, ConversationState.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConversationState{TState}} object.
|
||||
* @param storage The storage provider to use.
|
||||
* @param settings The state persistance options to use.
|
||||
*/
|
||||
public ConversationState(Storage storage, Supplier<? extends TState> ctor) {
|
||||
this(storage, null, ctor);
|
||||
}
|
||||
@Override
|
||||
public String getStorageKey(TurnContext turnContext) {
|
||||
if (turnContext.getActivity() == null) {
|
||||
throw new IllegalArgumentException("invalid activity");
|
||||
}
|
||||
|
||||
public ConversationState(Storage storage, StateSettings settings, Supplier<? extends TState> ctor) {
|
||||
super(storage, PropertyName(),
|
||||
(context) -> {
|
||||
return String.format("conversation/%s/%s", context.getActivity().getChannelId(), context.getActivity().getConversation().getId());
|
||||
},
|
||||
ctor,
|
||||
settings);
|
||||
}
|
||||
if (StringUtils.isEmpty(turnContext.getActivity().getChannelId())) {
|
||||
throw new IllegalArgumentException("invalid activity-missing channelId");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversation state object from turn context.
|
||||
* @param context The context object for this turn.
|
||||
* @return The coversation state object.
|
||||
*/
|
||||
public static <TState> TState Get(TurnContext context) throws IllegalArgumentException {
|
||||
return context.getServices().Get(PropertyName());
|
||||
if (turnContext.getActivity().getConversation() == null
|
||||
|| StringUtils.isEmpty(turnContext.getActivity().getConversation().getId())) {
|
||||
throw new IllegalArgumentException("invalid activity-missing Conversation.Id");
|
||||
}
|
||||
|
||||
// {channelId}/conversations/{conversationId}
|
||||
return turnContext.getActivity().getChannelId()
|
||||
+ "/conversations/"
|
||||
+ turnContext.getActivity().getConversation().getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.ConversationReference;
|
||||
import com.microsoft.bot.schema.ResourceResponse;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A TurnContext that wraps an untyped inner TurnContext.
|
||||
*/
|
||||
public class DelegatingTurnContext implements TurnContext {
|
||||
private TurnContext innerTurnContext;
|
||||
|
||||
public DelegatingTurnContext(TurnContext withTurnContext) {
|
||||
innerTurnContext = withTurnContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BotAdapter getAdapter() {
|
||||
return innerTurnContext.getAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TurnContextStateCollection getTurnState() {
|
||||
return innerTurnContext.getTurnState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return innerTurnContext.getActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getResponded() {
|
||||
return innerTurnContext.getResponded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend) {
|
||||
return innerTurnContext.sendActivityAsync(textReplyToSend);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak) {
|
||||
return innerTurnContext.sendActivityAsync(textReplyToSend, speak);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak, String inputHint) {
|
||||
return innerTurnContext.sendActivityAsync(textReplyToSend, speak, inputHint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(Activity activity) {
|
||||
return innerTurnContext.sendActivityAsync(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResourceResponse[]> sendActivitiesAsync(Activity[] activities) {
|
||||
return innerTurnContext.sendActivitiesAsync(activities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity) {
|
||||
return innerTurnContext.updateActivityAsync(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteActivityAsync(String activityId) {
|
||||
return innerTurnContext.deleteActivityAsync(activityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteActivityAsync(ConversationReference conversationReference) {
|
||||
return innerTurnContext.deleteActivityAsync(conversationReference);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TurnContext onSendActivities(SendActivitiesHandler handler) {
|
||||
return innerTurnContext.onSendActivities(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TurnContext onUpdateActivity(UpdateActivityHandler handler) {
|
||||
return innerTurnContext.onUpdateActivity(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TurnContext onDeleteActivity(DeleteActivityHandler handler) {
|
||||
return innerTurnContext.onDeleteActivity(handler);
|
||||
}
|
||||
}
|
|
@ -2,24 +2,25 @@ package com.microsoft.bot.builder;
|
|||
|
||||
import com.microsoft.bot.schema.ConversationReference;
|
||||
|
||||
/**
|
||||
* A method that can participate in delete activity events for the current turn.
|
||||
* @param context The context object for the turn.
|
||||
* @param reference The conversation containing the activity.
|
||||
* @param next The delegate to call to continue event processing.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* A handler calls the {@code next} delegate to pass control to
|
||||
* the next registered handler. If a handler doesn’t call the next delegate,
|
||||
* the adapter does not call any of the subsequent handlers and does not delete the
|
||||
*activity.
|
||||
* <p>The conversation reference's {@link ConversationReference.ActivityId}
|
||||
* indicates the activity in the conversation to replace.</p>
|
||||
*
|
||||
* {@linkalso BotAdapter}
|
||||
* {@linkalso SendActivitiesHandler}
|
||||
* {@linkalso UpdateActivityHandler}
|
||||
*/
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DeleteActivityHandler {
|
||||
void handle(TurnContext context, ConversationReference reference, Runnable next) throws Exception;
|
||||
/**
|
||||
* A method that can participate in delete activity events for the current turn.
|
||||
* @param context The context object for the turn.
|
||||
* @param reference The conversation containing the activity.
|
||||
* @param next The delegate to call to continue event processing.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* A handler calls the {@code next} delegate to pass control to
|
||||
* the next registered handler. If a handler doesn’t call the next delegate,
|
||||
* the adapter does not call any of the subsequent handlers and does not delete the
|
||||
*activity.
|
||||
* <p>The conversation reference's {@link ConversationReference#getActivityId}
|
||||
* indicates the activity in the conversation to replace.</p>
|
||||
*/
|
||||
CompletableFuture<Void> invoke(TurnContext context,
|
||||
ConversationReference reference,
|
||||
Supplier<CompletableFuture<Void>> next);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class IntentScore {
|
||||
@JsonProperty
|
||||
private double score;
|
||||
|
||||
private HashMap<String, JsonNode> properties = new HashMap<>();
|
||||
|
||||
public double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public void setScore(double withScore) {
|
||||
score = withScore;
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, JsonNode> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void setProperties(String key, JsonNode value) {
|
||||
this.properties.put(key, value);
|
||||
}
|
||||
}
|
|
@ -9,29 +9,29 @@ package com.microsoft.bot.builder;
|
|||
public class InvokeResponse {
|
||||
/**
|
||||
* The POST that is generated in response to the incoming Invoke Activity
|
||||
* will have the HTTP Status code specificied by this field.
|
||||
* will have the HTTP Status code specified by this field.
|
||||
*/
|
||||
private int _status;
|
||||
private int status;
|
||||
|
||||
public int getStatus() {
|
||||
return this._status;
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this._status = status;
|
||||
public void setStatus(int withStatus) {
|
||||
this.status = withStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* The POST that is generated in response to the incoming Invoke Activity
|
||||
* will have a body generated by JSON serializing the object in the Body field.
|
||||
*/
|
||||
private Object _body;
|
||||
private Object body;
|
||||
|
||||
public Object getBody() {
|
||||
return _body;
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(Object body) {
|
||||
this._body = body;
|
||||
public void setBody(Object withBody) {
|
||||
body = withBody;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class MemoryStorage implements Storage {
|
||||
private ObjectMapper objectMapper;
|
||||
private Map<String, JsonNode> memory = new HashMap<>();
|
||||
private static final String TYPENAMEFORNONENTITY = "__type_name_";
|
||||
private final Object syncroot = new Object();
|
||||
private Logger logger = LoggerFactory.getLogger(MemoryStorage.class);
|
||||
private int _eTag = 0;
|
||||
|
||||
public MemoryStorage(Map<String, JsonNode> values) {
|
||||
objectMapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.findAndRegisterModules();
|
||||
|
||||
if (values != null)
|
||||
memory = values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Map<String, Object>> readAsync(String[] keys) {
|
||||
if (keys == null) {
|
||||
throw new IllegalArgumentException("keys cannot be null");
|
||||
}
|
||||
|
||||
Map<String, Object> storeItems = new HashMap<String, Object>(keys.length);
|
||||
synchronized (this.syncroot) {
|
||||
for (String key : keys) {
|
||||
if (memory.containsKey(key)) {
|
||||
Object state = memory.get(key);
|
||||
if (state != null) {
|
||||
try {
|
||||
if (!(state instanceof JsonNode))
|
||||
throw new RuntimeException("DictionaryRead failed: entry not JsonNode");
|
||||
|
||||
JsonNode stateNode = (JsonNode) state;
|
||||
|
||||
// Check if type info is set for the class
|
||||
if (!(stateNode.hasNonNull(TYPENAMEFORNONENTITY))) {
|
||||
logger.error("Read failed: Type info not present for " + key );
|
||||
throw new RuntimeException(String.format("Read failed: Type info not present for key " + key));
|
||||
}
|
||||
String clsName = stateNode.get(TYPENAMEFORNONENTITY).textValue();
|
||||
|
||||
// Load the class info
|
||||
Class<?> cls;
|
||||
try {
|
||||
cls = Class.forName(clsName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.error("Read failed: Could not load class {}", clsName);
|
||||
throw new RuntimeException(String.format("Read failed: Could not load class %s", clsName));
|
||||
}
|
||||
|
||||
// Populate dictionary
|
||||
storeItems.put(key,objectMapper.treeToValue(stateNode, cls ));
|
||||
} catch (JsonProcessingException e) {
|
||||
logger.error("Read failed: {}", e.toString());
|
||||
throw new RuntimeException(String.format("Read failed: %s", e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(storeItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> writeAsync(Map<String, Object> changes) {
|
||||
synchronized (this.syncroot) {
|
||||
for (Map.Entry change : changes.entrySet()) {
|
||||
Object newValue = change.getValue();
|
||||
|
||||
String oldStateETag = null;
|
||||
if (memory.containsValue(change.getKey())) {
|
||||
Map oldState = (Map) memory.get(change.getKey());
|
||||
if (oldState.containsValue("eTag")) {
|
||||
Map.Entry eTagToken = (Map.Entry) oldState.get("eTag");
|
||||
oldStateETag = (String) eTagToken.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
// Dictionary stores Key:JsonNode (with type information held within the JsonNode)
|
||||
JsonNode newState = objectMapper.valueToTree(newValue);
|
||||
((ObjectNode)newState).put(this.TYPENAMEFORNONENTITY, newValue.getClass().getTypeName());
|
||||
|
||||
// Set ETag if applicable
|
||||
if (newValue instanceof StoreItem) {
|
||||
StoreItem newStoreItem = (StoreItem) newValue;
|
||||
if(oldStateETag != null && newStoreItem.getETag() != "*" &&
|
||||
newStoreItem.getETag() != oldStateETag) {
|
||||
String msg = String.format("Etag conflict. Original: %s, Current: %s",
|
||||
newStoreItem.getETag(), oldStateETag);
|
||||
logger.error(msg);
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
Integer newTag = _eTag++;
|
||||
((ObjectNode)newState).put("eTag", newTag.toString());
|
||||
}
|
||||
|
||||
memory.put((String)change.getKey(), newState);
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteAsync(String[] keys) {
|
||||
if (keys == null) {
|
||||
throw new IllegalArgumentException("keys cannot be null");
|
||||
}
|
||||
|
||||
synchronized (this.syncroot) {
|
||||
for (String key : keys) {
|
||||
Object o = memory.get(key);
|
||||
memory.remove(o);
|
||||
}
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
|
@ -37,7 +37,8 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
* @param activity The activity to log.
|
||||
* @return A CompletableFuture that represents the work queued to execute.
|
||||
*/
|
||||
public final void LogActivityAsync(Activity activity) {
|
||||
@Override
|
||||
public final CompletableFuture<Void> logActivityAsync(Activity activity) {
|
||||
if (activity == null) {
|
||||
throw new NullPointerException("activity cannot be null for LogActivity()");
|
||||
}
|
||||
|
@ -53,7 +54,6 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
|
||||
ArrayList<Activity> transcript = null;
|
||||
|
||||
|
||||
if (!channel.containsKey(activity.getConversation().getId())) {
|
||||
transcript = new ArrayList<Activity>();
|
||||
channel.put(activity.getConversation().getId(), transcript);
|
||||
|
@ -64,33 +64,7 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
transcript.add(activity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets from the store activities that match a set of criteria.
|
||||
*
|
||||
* @param channelId The ID of the channel the conversation is in.
|
||||
* @param conversationId The ID of the conversation.
|
||||
* @param continuationToken
|
||||
* @return A task that represents the work queued to execute.
|
||||
* If the task completes successfully, the result contains the matching activities.
|
||||
*/
|
||||
|
||||
public final CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken) {
|
||||
return GetTranscriptActivitiesAsync(channelId, conversationId, continuationToken, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets from the store activities that match a set of criteria.
|
||||
*
|
||||
* @param channelId The ID of the channel the conversation is in.
|
||||
* @param conversationId The ID of the conversation.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* If the task completes successfully, the result contains the matching activities.
|
||||
*/
|
||||
|
||||
public final CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId) {
|
||||
return GetTranscriptActivitiesAsync(channelId, conversationId, null, null);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,16 +77,20 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
* @return A task that represents the work queued to execute.
|
||||
* If the task completes successfully, the result contains the matching activities.
|
||||
*/
|
||||
public final CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken, DateTime startDate) {
|
||||
@Override
|
||||
public CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
|
||||
String conversationId,
|
||||
String continuationToken,
|
||||
DateTime startDate) {
|
||||
if (channelId == null) {
|
||||
throw new IllegalArgumentException(String.format("missing %1$s", "channelId"));
|
||||
}
|
||||
|
||||
if (conversationId == null) {
|
||||
throw new IllegalArgumentException(String.format("missing %1$s", "conversationId"));
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
if (channelId == null) {
|
||||
throw new NullPointerException(String.format("missing %1$s", "channelId"));
|
||||
}
|
||||
|
||||
if (conversationId == null) {
|
||||
throw new NullPointerException(String.format("missing %1$s", "conversationId"));
|
||||
}
|
||||
|
||||
PagedResult<Activity> pagedResult = new PagedResult<Activity>();
|
||||
synchronized (channels) {
|
||||
HashMap<String, ArrayList<Activity>> channel;
|
||||
|
@ -165,16 +143,17 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
* @param conversationId The ID of the conversation to delete.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public final CompletableFuture DeleteTranscriptAsync(String channelId, String conversationId) {
|
||||
@Override
|
||||
public CompletableFuture<Void> deleteTranscriptAsync(String channelId, String conversationId) {
|
||||
if (channelId == null) {
|
||||
throw new IllegalArgumentException(String.format("%1$s should not be null", "channelId"));
|
||||
}
|
||||
|
||||
if (conversationId == null) {
|
||||
throw new IllegalArgumentException(String.format("%1$s should not be null", "conversationId"));
|
||||
}
|
||||
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
if (channelId == null) {
|
||||
throw new NullPointerException(String.format("%1$s should not be null", "channelId"));
|
||||
}
|
||||
|
||||
if (conversationId == null) {
|
||||
throw new NullPointerException(String.format("%1$s should not be null", "conversationId"));
|
||||
}
|
||||
|
||||
synchronized (this.channels) {
|
||||
if (!this.channels.containsKey(channelId)) {
|
||||
return;
|
||||
|
@ -187,17 +166,6 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
}, ExecutorFactory.getExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversations on a channel from the store.
|
||||
*
|
||||
* @param channelId The ID of the channel.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
|
||||
public final CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId) {
|
||||
return ListTranscriptsAsync(channelId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversations on a channel from the store.
|
||||
*
|
||||
|
@ -205,13 +173,13 @@ public class MemoryTranscriptStore implements TranscriptStore {
|
|||
* @param continuationToken
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
@Override
|
||||
public CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId, String continuationToken) {
|
||||
if (channelId == null) {
|
||||
throw new IllegalArgumentException(String.format("missing %1$s", "channelId"));
|
||||
}
|
||||
|
||||
public final CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId, String continuationToken) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
if (channelId == null) {
|
||||
throw new NullPointerException(String.format("missing %1$s", "channelId"));
|
||||
}
|
||||
|
||||
PagedResult<Transcript> pagedResult = new PagedResult<Transcript>();
|
||||
synchronized (channels) {
|
||||
|
||||
|
|
|
@ -0,0 +1,305 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.schema.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains utility methods for various message types a bot can return.
|
||||
*
|
||||
* Create and send a message.
|
||||
* <code>
|
||||
* Activity message = MessageFactory.text("Hello World");
|
||||
* conext.sendActivity(message);
|
||||
* </code>
|
||||
*
|
||||
* <p>The following apply to message actions in general.
|
||||
* See the channel's documentation for limits imposed upon the contents of
|
||||
* the text of the message to send.</p>
|
||||
*
|
||||
* <p>To control various characteristics of your bot's speech such as voice,
|
||||
* rate, volume, pronunciation, and pitch, specify test to speak in
|
||||
* Speech Synthesis Markup Language (SSML) format.</p>
|
||||
*
|
||||
* <p>Channels decide how each card action manifests in their user experience.
|
||||
* In most cases, the cards are clickable. In others, they may be selected by speech
|
||||
* input. In cases where the channel does not offer an interactive activation
|
||||
* experience (e.g., when interacting over SMS), the channel may not support
|
||||
* activation whatsoever. The decision about how to render actions is controlled by
|
||||
* normative requirements elsewhere in this document (e.g. within the card format,
|
||||
* or within the suggested actions definition).</p>
|
||||
*/
|
||||
public final class MessageFactory {
|
||||
private MessageFactory() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple text message.
|
||||
*
|
||||
* @param text The text of the message to send.
|
||||
* @return A message activity containing the text.
|
||||
*/
|
||||
public static Activity text(String text) {
|
||||
return text(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a simple text message.
|
||||
*
|
||||
* @param text The text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enabled channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return A message activity containing the text.
|
||||
*/
|
||||
public static Activity text(String text, String ssml, InputHints inputHint) {
|
||||
Activity activity = Activity.createMessageActivity();
|
||||
setTextAndSpeech(activity, text, ssml, inputHint);
|
||||
return activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message that includes a set of suggested actions and optional text.
|
||||
*
|
||||
* <code>
|
||||
* // Create the activity and add suggested actions.
|
||||
* Activity activity = MessageFactory.suggestedActions(
|
||||
* new String[] { "red", "green", "blue" },
|
||||
* "Choose a color");
|
||||
*
|
||||
* // Send the activity as a reply to the user.
|
||||
* context.sendActivity(activity);
|
||||
* </code>
|
||||
* @param actions The text of the actions to create.
|
||||
* @param text Optional. The text of the message to send.
|
||||
* @return A message activity containing the suggested actions.
|
||||
*/
|
||||
public static Activity suggestedActions(List<String> actions, String text) {
|
||||
return suggestedActions(actions, text, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message that includes a set of suggested actions and optional text.
|
||||
*
|
||||
* <code>
|
||||
* // Create the activity and add suggested actions.
|
||||
* Activity activity = MessageFactory.suggestedActions(
|
||||
* new String[] { "red", "green", "blue" },
|
||||
* "Choose a color");
|
||||
*
|
||||
* // Send the activity as a reply to the user.
|
||||
* context.sendActivity(activity);
|
||||
* </code>
|
||||
* @param actions The text of the actions to create.
|
||||
* @param text Optional. The text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return A message activity containing the suggested actions.
|
||||
*/
|
||||
public static Activity suggestedActions(List<String> actions, String text, String ssml, InputHints inputHint) {
|
||||
if (actions == null) {
|
||||
throw new IllegalArgumentException("actions cannot be null");
|
||||
}
|
||||
|
||||
List<CardAction> cardActions = new ArrayList<>();
|
||||
for (String action : actions) {
|
||||
CardAction cardAction = new CardAction() {{
|
||||
setType(ActionTypes.IM_BACK);
|
||||
setValue(action);
|
||||
setTitle(action);
|
||||
}};
|
||||
|
||||
cardActions.add(cardAction);
|
||||
}
|
||||
|
||||
return suggestedCardActions(cardActions, text, ssml, inputHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message that includes a set of suggested actions and optional text.
|
||||
*
|
||||
* @param actions The card actions to include.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @return
|
||||
*/
|
||||
public static Activity suggestedCardActions(List<CardAction> actions, String text) {
|
||||
return suggestedCardActions(actions, text, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message that includes a set of suggested actions and optional text.
|
||||
*
|
||||
* @param actions The card actions to include.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return
|
||||
*/
|
||||
public static Activity suggestedCardActions(List<CardAction> actions,
|
||||
String text,
|
||||
String ssml,
|
||||
InputHints inputHint) {
|
||||
if (actions == null) {
|
||||
throw new IllegalArgumentException("actions cannot be null");
|
||||
}
|
||||
|
||||
Activity activity = Activity.createMessageActivity();
|
||||
setTextAndSpeech(activity, text, ssml, inputHint);
|
||||
|
||||
activity.setSuggestedActions(new SuggestedActions(actions.toArray(new CardAction[actions.size()])));
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains an attachment.
|
||||
*
|
||||
* @param attachment Attachment to include in the message.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity attachment(Attachment attachment, String text) {
|
||||
return attachment(attachment, text, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains an attachment.
|
||||
*
|
||||
* @param attachment Attachment to include in the message.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity attachment(Attachment attachment, String text, String ssml, InputHints inputHint) {
|
||||
if (attachment == null) {
|
||||
throw new IllegalArgumentException("attachment cannot be null");
|
||||
}
|
||||
|
||||
return attachment(Collections.singletonList(attachment), text, ssml, inputHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains an attachment.
|
||||
*
|
||||
* @param attachments Attachments to include in the message.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity attachment(List<Attachment> attachments, String text, String ssml, InputHints inputHint) {
|
||||
if (attachments == null) {
|
||||
throw new IllegalArgumentException("attachments cannot be null");
|
||||
}
|
||||
|
||||
return attachmentActivity(AttachmentLayoutTypes.LIST, attachments, text, ssml, inputHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains a collection of attachments, in a list.
|
||||
*
|
||||
* @param attachments Attachments to include in the message.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity carousel(List<Attachment> attachments, String text) {
|
||||
return carousel(attachments, text, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains a collection of attachments, in a list.
|
||||
*
|
||||
* @param attachments Attachments to include in the message.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity carousel(List<Attachment> attachments, String text, String ssml, InputHints inputHint) {
|
||||
if (attachments == null) {
|
||||
throw new IllegalArgumentException("attachments cannot be null");
|
||||
}
|
||||
|
||||
return attachmentActivity(AttachmentLayoutTypes.CAROUSEL, attachments, text, ssml, inputHint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains a single image or video.
|
||||
*
|
||||
* @param url The URL of the image or video to send.
|
||||
* @param contentType The MIME type of the image or video.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity contentUrl(String url, String contentType) {
|
||||
return contentUrl(url, contentType, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a message activity that contains a single image or video.
|
||||
*
|
||||
* @param url The URL of the image or video to send.
|
||||
* @param contentType The MIME type of the image or video.
|
||||
* @param name Optional, the name of the image or video file.
|
||||
* @param text Optional, the text of the message to send.
|
||||
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
|
||||
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
|
||||
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
|
||||
* @return A message activity containing the attachment.
|
||||
*/
|
||||
public static Activity contentUrl(String url,
|
||||
String contentType,
|
||||
String name,
|
||||
String text,
|
||||
String ssml,
|
||||
InputHints inputHint) {
|
||||
if (StringUtils.isEmpty(url)) {
|
||||
throw new IllegalArgumentException("url cannot be null or empty");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(contentType)) {
|
||||
throw new IllegalArgumentException("contentType cannot be null or empty");
|
||||
}
|
||||
|
||||
Attachment attachment = new Attachment(){{
|
||||
setContentType(contentType);
|
||||
setContentUrl(url);
|
||||
setName(StringUtils.isEmpty(name) ? null : name);
|
||||
}};
|
||||
|
||||
return attachmentActivity(AttachmentLayoutTypes.LIST, Arrays.asList(new Attachment[]{attachment}),
|
||||
text, ssml, inputHint);
|
||||
}
|
||||
|
||||
private static Activity attachmentActivity(AttachmentLayoutTypes attachmentLayout,
|
||||
List<Attachment> attachments,
|
||||
String text,
|
||||
String ssml,
|
||||
InputHints inputHint) {
|
||||
Activity activity = Activity.createMessageActivity();
|
||||
activity.setAttachmentLayout(attachmentLayout);
|
||||
activity.setAttachments(attachments);
|
||||
setTextAndSpeech(activity, text, ssml, inputHint);
|
||||
return activity;
|
||||
}
|
||||
|
||||
private static void setTextAndSpeech(Activity activity, String text, String ssml, InputHints inputHint) {
|
||||
activity.setText(StringUtils.isEmpty(text) ? null : text);
|
||||
activity.setSpeak(StringUtils.isEmpty(ssml) ? null : ssml);
|
||||
activity.setInputHint(
|
||||
inputHint == null ? InputHints.ACCEPTING_INPUT : inputHint);
|
||||
}
|
||||
}
|
|
@ -2,23 +2,25 @@
|
|||
// Licensed under the MIT License.
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Represents middleware that can operate on incoming activities.
|
||||
* A {@link BotAdapter} passes incoming activities from the user's
|
||||
* channel to the middleware's {@link OnTurn(TurnContext, NextDelegate)}
|
||||
* channel to the middleware's {@link #onTurn(TurnContext, NextDelegate)}
|
||||
* method.
|
||||
* <p>You can add middleware objects to your adapter’s middleware collection. The
|
||||
* adapter processes and directs 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 of middleware can inspect or act upon the activity,
|
||||
* adapter processes and directs 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 of middleware can inspect or act upon the activity,
|
||||
* both before and after the bot logic runs.</p>
|
||||
* <p>For each activity, the adapter calls middleware in the order in which you
|
||||
* <p>For each activity, the adapter calls middleware in the order in which you
|
||||
* added it.</p>
|
||||
*
|
||||
* <example>
|
||||
* This defines middleware that sends "before" and "after" messages
|
||||
* before and after the adapter calls the bot's
|
||||
* {@link Bot.OnTurn(TurnContext)} method.
|
||||
* before and after the adapter calls the bot's
|
||||
* {@link Bot#onTurnAsync(TurnContext)} method.
|
||||
* <code>
|
||||
* public class SampleMiddleware : Middleware
|
||||
* {
|
||||
|
@ -33,8 +35,7 @@ package com.microsoft.bot.builder;
|
|||
* </example>
|
||||
* {@linkalso Bot}
|
||||
*/
|
||||
public interface Middleware
|
||||
{
|
||||
public interface Middleware {
|
||||
/**
|
||||
* Processess an incoming activity.
|
||||
* @param context The context object for this turn.
|
||||
|
@ -42,15 +43,15 @@ public interface Middleware
|
|||
* @return A task that represents the work queued to execute.
|
||||
* Middleware calls the {@code next} delegate to pass control to
|
||||
* the next middleware in the pipeline. If middleware doesn’t call the next delegate,
|
||||
* the adapter does not call any of the subsequent middleware’s request handlers or the
|
||||
* the adapter does not call any of the subsequent middleware’s request handlers or the
|
||||
* bot’s receive handler, and the pipeline short circuits.
|
||||
* <p>The {@code context} provides information about the
|
||||
* <p>The {@code context} provides information about the
|
||||
* incoming activity, and other data needed to process the activity.</p>
|
||||
*
|
||||
* {@linkalso TurnContext}
|
||||
* {@linkalso Bot.Schema.Activity}
|
||||
* {@link TurnContext}
|
||||
* {@link com.microsoft.bot.schema.Activity}
|
||||
*/
|
||||
void OnTurn(TurnContext context, NextDelegate next) throws Exception;
|
||||
CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MiddlewareCall {
|
||||
void requestHandler(TurnContext tc, NextDelegate nd) throws Exception;
|
||||
}
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface MiddlewareCall {
|
||||
CompletableFuture<Void> requestHandler(TurnContext tc, NextDelegate nd);
|
||||
}
|
||||
|
|
|
@ -1,97 +1,79 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MiddlewareSet implements Middleware {
|
||||
public NextDelegate Next;
|
||||
|
||||
private final ArrayList<Middleware> _middleware = new ArrayList<Middleware>();
|
||||
|
||||
public MiddlewareSet Use(Middleware middleware) {
|
||||
BotAssert.MiddlewareNotNull(middleware);
|
||||
_middleware.add(middleware);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void ReceiveActivity(TurnContextImpl context)
|
||||
throws Exception {
|
||||
ReceiveActivityInternal(context, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
|
||||
ReceiveActivityInternal((TurnContextImpl) context, null);
|
||||
try {
|
||||
next.next();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("MiddlewareSet::OnTurn next delegate: %s", e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void OnTurn(TurnContextImpl context, CompletableFuture next)
|
||||
throws ExecutionException, InterruptedException {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intended to be called from Bot, this method performs exactly the same as the
|
||||
* standard ReceiveActivity, except that it runs a user-defined delegate returns
|
||||
* if all Middleware in the receive pipeline was run.
|
||||
*/
|
||||
public void ReceiveActivityWithStatus(TurnContext context, Consumer<TurnContext> callback)
|
||||
throws Exception {
|
||||
ReceiveActivityInternal(context, callback);
|
||||
}
|
||||
|
||||
private void ReceiveActivityInternal(TurnContext context, Consumer<TurnContext> callback)
|
||||
throws Exception {
|
||||
ReceiveActivityInternal(context, callback, 0);
|
||||
}
|
||||
|
||||
private void ReceiveActivityInternal(TurnContext context, Consumer<TurnContext> callback, int nextMiddlewareIndex)
|
||||
throws Exception {
|
||||
// Check if we're at the end of the middleware list yet
|
||||
if (nextMiddlewareIndex == _middleware.size()) {
|
||||
// If all the Middlware ran, the "leading edge" of the tree is now complete.
|
||||
// This means it's time to run any developer specified callback.
|
||||
// Once this callback is done, the "trailing edge" calls are then completed. This
|
||||
// allows code that looks like:
|
||||
// Trace.TraceInformation("before");
|
||||
// await next();
|
||||
// Trace.TraceInformation("after");
|
||||
// to run as expected.
|
||||
|
||||
// If a callback was provided invoke it now and return its task, otherwise just return the completed task
|
||||
if (callback == null) {
|
||||
return ;
|
||||
} else {
|
||||
callback.accept(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next piece of middleware
|
||||
Middleware nextMiddleware = _middleware.get(nextMiddlewareIndex);
|
||||
NextDelegate next = new NextDelegate() {
|
||||
public void next() throws Exception {
|
||||
ReceiveActivityInternal(context, callback, nextMiddlewareIndex + 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the next middleware passing a closure that will recurse back into this method at the next piece of middlware as the NextDelegate
|
||||
nextMiddleware.OnTurn(
|
||||
context,
|
||||
next);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Contains an ordered set of {@link Middleware}.
|
||||
*/
|
||||
public class MiddlewareSet implements Middleware {
|
||||
public NextDelegate Next;
|
||||
private final ArrayList<Middleware> middleware = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Adds a middleware object to the end of the set.
|
||||
*
|
||||
* @param middleware The middleware to add.
|
||||
* @return The updated middleware set.
|
||||
*/
|
||||
public MiddlewareSet use(Middleware middleware) {
|
||||
BotAssert.MiddlewareNotNull(middleware);
|
||||
this.middleware.add(middleware);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
|
||||
return receiveActivityInternal((TurnContextImpl) context, null)
|
||||
.thenCompose((result) -> next.next());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an activity.
|
||||
*
|
||||
* @param context The context object for the turn.
|
||||
* @param callback The delegate to call when the set finishes processing the activity.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> receiveActivityWithStatusAsync(TurnContext context, BotCallbackHandler callback) {
|
||||
return receiveActivityInternal(context, callback);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> receiveActivityInternal(TurnContext context, BotCallbackHandler callback) {
|
||||
return receiveActivityInternal(context, callback, 0);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> receiveActivityInternal(TurnContext context,
|
||||
BotCallbackHandler callback,
|
||||
int nextMiddlewareIndex) {
|
||||
// Check if we're at the end of the middleware list yet
|
||||
if (nextMiddlewareIndex == middleware.size()) {
|
||||
// If all the Middlware ran, the "leading edge" of the tree is now complete.
|
||||
// This means it's time to run any developer specified callback.
|
||||
// Once this callback is done, the "trailing edge" calls are then completed. This
|
||||
// allows code that looks like:
|
||||
// Trace.TraceInformation("before");
|
||||
// await next();
|
||||
// Trace.TraceInformation("after");
|
||||
// to run as expected.
|
||||
|
||||
// If a callback was provided invoke it now and return its task, otherwise just return the completed task
|
||||
if (callback == null) {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
return callback.invoke(context);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the next piece of middleware
|
||||
Middleware nextMiddleware = middleware.get(nextMiddlewareIndex);
|
||||
|
||||
// Execute the next middleware passing a closure that will recurse back into this method at the
|
||||
// next piece of middlware as the NextDelegate
|
||||
return nextMiddleware.onTurnAsync(context, () ->
|
||||
receiveActivityInternal(context, callback, nextMiddlewareIndex + 1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface NextDelegate {
|
||||
void next() throws Exception;
|
||||
CompletableFuture<Void> next();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class NullBotTelemetryClient implements BotTelemetryClient {
|
||||
@Override
|
||||
public void trackAvailability(String name, DateTime timeStamp, Duration duration, String runLocation, boolean success, String message, Map<String, String> properties, Map<String, Double> metrics) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackDependency(String dependencyTypeName, String target, String dependencyName, String data, DateTime startTime, Duration duration, String resultCode, boolean success) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackEvent(String eventName, Map<String, String> properties, Map<String, Double> metrics) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackException(Exception exception, Map<String, String> properties, Map<String, Double> metrics) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackTrace(String message, Severity severityLevel, Map<String, String> properties) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface OnTurnErrorHandler {
|
||||
/**
|
||||
* Gets or sets an error handler that can catch exceptions in the middleware or application.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @param exception The exception thrown.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
CompletableFuture<Void> invoke(TurnContext turnContext, Throwable exception);
|
||||
}
|
|
@ -1,43 +1,37 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
/**
|
||||
Page of results from an enumeration.
|
||||
|
||||
<typeparam name="T"></typeparam>
|
||||
*/
|
||||
public class PagedResult<T>
|
||||
{
|
||||
/**
|
||||
Page of items.
|
||||
*/
|
||||
|
||||
//C# TO JAVA CONVERTER WARNING: Java does not allow direct instantiation of arrays of generic type parameters:
|
||||
//ORIGINAL LINE: private T[] Items = new T[0];
|
||||
private T[] items = (T[])new Object[0];
|
||||
public final T[] getItems()
|
||||
{
|
||||
return this.items;
|
||||
}
|
||||
public final void items(T[] value)
|
||||
{
|
||||
this.items = value;
|
||||
}
|
||||
|
||||
/**
|
||||
Token used to page through multiple pages.
|
||||
*/
|
||||
private String continuationToken;
|
||||
public final String continuationToken()
|
||||
{
|
||||
return this.continuationToken;
|
||||
}
|
||||
public final PagedResult<T> withContinuationToken(String value)
|
||||
{
|
||||
this.continuationToken = value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
|
||||
/**
|
||||
* Page of results from an enumeration.
|
||||
* @param <T> The type of items in the results.
|
||||
*/
|
||||
public class PagedResult<T> {
|
||||
/**
|
||||
* Page of items.
|
||||
*/
|
||||
private T[] items = (T[])new Object[0];
|
||||
|
||||
/**
|
||||
Token used to page through multiple pages.
|
||||
*/
|
||||
private String continuationToken;
|
||||
|
||||
public T[] getItems() {
|
||||
return this.items;
|
||||
}
|
||||
|
||||
public void setItems(T[] value) {
|
||||
this.items = value;
|
||||
}
|
||||
|
||||
public String getContinuationToken() {
|
||||
return continuationToken;
|
||||
}
|
||||
|
||||
public void setContinuationToken(String withValue) {
|
||||
continuationToken = withValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Handles persistence of a conversation state object using the conversation.Id and from.Id part of an activity.
|
||||
*/
|
||||
public class PrivateConversationState extends BotState {
|
||||
/**
|
||||
* Initializes a new instance of the PrivateConversationState class.
|
||||
* @param storage The storage provider to use.
|
||||
*/
|
||||
public PrivateConversationState(Storage storage) {
|
||||
super(storage, PrivateConversationState.class.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key to use when reading and writing state to and from storage.
|
||||
*
|
||||
* @param turnContext The context object for this turn.
|
||||
* @return The storage key.
|
||||
*/
|
||||
@Override
|
||||
public String getStorageKey(TurnContext turnContext) {
|
||||
if (turnContext.getActivity() == null) {
|
||||
throw new IllegalArgumentException("invalid activity");
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(turnContext.getActivity().getChannelId())) {
|
||||
throw new IllegalArgumentException("invalid activity-missing channelId");
|
||||
}
|
||||
|
||||
if (turnContext.getActivity().getConversation() == null
|
||||
|| StringUtils.isEmpty(turnContext.getActivity().getConversation().getId())) {
|
||||
throw new IllegalArgumentException("invalid activity-missing Conversation.Id");
|
||||
}
|
||||
|
||||
if (turnContext.getActivity().getFrom() == null
|
||||
|| StringUtils.isEmpty(turnContext.getActivity().getFrom().getId())) {
|
||||
throw new IllegalArgumentException("invalid activity-missing From.Id");
|
||||
}
|
||||
|
||||
// {channelId}/conversations/{conversationId}/users/{userId}
|
||||
return turnContext.getActivity().getChannelId()
|
||||
+ "/conversations/"
|
||||
+ turnContext.getActivity().getConversation().getId()
|
||||
+ "/users/"
|
||||
+ turnContext.getActivity().getFrom().getId();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
public interface PropertyManager {
|
||||
<T> StatePropertyAccessor<T> createProperty(String name);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface Recognizer {
|
||||
CompletableFuture<RecognizerResult> recognizeAsync(TurnContext turnContext);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
public interface RecognizerConvert {
|
||||
void convert(Object result);
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RecognizerResult implements RecognizerConvert {
|
||||
@JsonProperty
|
||||
private String text;
|
||||
|
||||
@JsonProperty
|
||||
private String alteredText;
|
||||
|
||||
@JsonProperty
|
||||
private Map<String, IntentScore> intents;
|
||||
|
||||
@JsonProperty
|
||||
JsonNode entities;
|
||||
|
||||
private HashMap<String, JsonNode> properties = new HashMap<>();
|
||||
|
||||
public IntentScore getTopScoringIntent() {
|
||||
if (getIntents() == null) {
|
||||
throw new IllegalArgumentException("RecognizerResult.Intents cannot be null");
|
||||
}
|
||||
|
||||
IntentScore topIntent = new IntentScore();
|
||||
for (Map.Entry<String, IntentScore> intent : getIntents().entrySet()) {
|
||||
double score = intent.getValue().getScore();
|
||||
if (score > topIntent.getScore()) {
|
||||
topIntent = intent.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return topIntent;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String getAlteredText() {
|
||||
return alteredText;
|
||||
}
|
||||
|
||||
public void setAlteredText(String alteredText) {
|
||||
this.alteredText = alteredText;
|
||||
}
|
||||
|
||||
public Map<String, IntentScore> getIntents() {
|
||||
return intents;
|
||||
}
|
||||
|
||||
public void setIntents(Map<String, IntentScore> intents) {
|
||||
this.intents = intents;
|
||||
}
|
||||
|
||||
public JsonNode getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
public void setEntities(JsonNode entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, JsonNode> getProperties() {
|
||||
return this.properties;
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void setProperties(String key, JsonNode value) {
|
||||
this.properties.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void convert(Object result) {
|
||||
setText(((RecognizerResult)result).getText());
|
||||
setAlteredText((((RecognizerResult)result).getAlteredText()));
|
||||
setIntents(((RecognizerResult)result).getIntents());
|
||||
setEntities(((RecognizerResult)result).getEntities());
|
||||
|
||||
for (String key : ((RecognizerResult)result).getProperties().keySet()) {
|
||||
setProperties(key, ((RecognizerResult)result).getProperties().get(key));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,26 @@
|
|||
// 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.ResourceResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SendActivitiesHandler {
|
||||
ResourceResponse[] handle(TurnContext context, List<Activity> activities, Callable<ResourceResponse[]> next) throws Exception;
|
||||
/**
|
||||
* A method that can participate in send activity events for the current turn.
|
||||
*
|
||||
* @param context The context object for the turn.
|
||||
* @param activities The activities to send.
|
||||
* @param next The delegate to call to continue event processing.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
CompletableFuture<ResourceResponse[]> invoke(TurnContext context,
|
||||
List<Activity> activities,
|
||||
Supplier<CompletableFuture<ResourceResponse[]>> next);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
public enum Severity {
|
||||
VERBOSE(0),
|
||||
INFORMATION(1),
|
||||
WARNING(2),
|
||||
ERROR(3),
|
||||
CRITICAL(4);
|
||||
|
||||
private int value;
|
||||
|
||||
Severity(int witValue) {
|
||||
value = witValue;
|
||||
}
|
||||
|
||||
public int getSeverity() {
|
||||
return value;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface StatePropertyAccessor<T> extends StatePropertyInfo {
|
||||
<T extends Object, S> CompletableFuture<T> getAsync(TurnContext turnContext, Supplier<S> defaultValueFactory);
|
||||
|
||||
CompletableFuture<Void> deleteAsync(TurnContext turnContext);
|
||||
|
||||
CompletableFuture<Void> setAsync(TurnContext turnContext, T value);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
public interface StatePropertyInfo {
|
||||
String getName();
|
||||
void setName(String withName);
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
public class StateSettings
|
||||
{
|
||||
private boolean lastWriterWins = true;
|
||||
public boolean getLastWriterWins() {
|
||||
return this.lastWriterWins;
|
||||
}
|
||||
public void setLast(boolean lastWriterWins) {
|
||||
this.lastWriterWins = lastWriterWins;
|
||||
}
|
||||
}
|
|
@ -1,34 +1,30 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface Storage
|
||||
{
|
||||
public interface Storage {
|
||||
/**
|
||||
* Read StoreItems from storage
|
||||
* @param keys keys of the storeItems to read
|
||||
* @return StoreItem dictionary
|
||||
*/
|
||||
CompletableFuture<Map<String, ? extends Object>> Read(String... keys) throws JsonProcessingException;
|
||||
CompletableFuture<Map<String, Object>> readAsync(String[] keys);
|
||||
|
||||
/**
|
||||
* Write StoreItems to storage
|
||||
* @param changes
|
||||
* @param changes
|
||||
*/
|
||||
CompletableFuture Write(Map<String, ? extends Object> changes) throws Exception;
|
||||
CompletableFuture<Void> writeAsync(Map<String, Object> changes);
|
||||
|
||||
/**
|
||||
* Delete StoreItems from storage
|
||||
* @param keys keys of the storeItems to delete
|
||||
*/
|
||||
CompletableFuture Delete(String... keys);
|
||||
CompletableFuture<Void> deleteAsync(String[] keys);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
public interface StoreItem
|
||||
{
|
||||
public interface StoreItem {
|
||||
/**
|
||||
* eTag for concurrency
|
||||
*/
|
||||
|
||||
String geteTag();
|
||||
void seteTag(String eTag);
|
||||
String getETag();
|
||||
void setETag(String eTag);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Transcript logger stores activities for conversations for recall.
|
||||
*/
|
||||
|
@ -17,5 +17,5 @@ public interface TranscriptLogger {
|
|||
* @param activity The activity to transcribe.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
void LogActivityAsync(Activity activity);
|
||||
CompletableFuture<Void> logActivityAsync(Activity activity);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.joda.time.DateTime;
|
|||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
|
||||
|
@ -59,7 +60,7 @@ public class TranscriptLoggerMiddleware implements Middleware {
|
|||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
|
||||
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
|
||||
// log incoming activity at beginning of turn
|
||||
if (context.getActivity() != null) {
|
||||
JsonNode role = null;
|
||||
|
@ -79,17 +80,12 @@ public class TranscriptLoggerMiddleware implements Middleware {
|
|||
}
|
||||
|
||||
// hook up onSend pipeline
|
||||
context.OnSendActivities((ctx, activities, nextSend) ->
|
||||
{
|
||||
|
||||
context.onSendActivities((ctx, activities, nextSend) -> {
|
||||
// run full pipeline
|
||||
ResourceResponse[] responses = new ResourceResponse[0];
|
||||
try {
|
||||
if (nextSend != null) {
|
||||
responses = nextSend.call();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
CompletableFuture<ResourceResponse[]> responses = null;
|
||||
|
||||
if (nextSend != null) {
|
||||
responses = nextSend.get();
|
||||
}
|
||||
|
||||
for (Activity activity : activities) {
|
||||
|
@ -97,50 +93,32 @@ public class TranscriptLoggerMiddleware implements Middleware {
|
|||
}
|
||||
|
||||
return responses;
|
||||
|
||||
|
||||
});
|
||||
|
||||
// hook up update activity pipeline
|
||||
context.OnUpdateActivity((ctx, activity, nextUpdate) ->
|
||||
{
|
||||
|
||||
context.onUpdateActivity((ctx, activity, nextUpdate) -> {
|
||||
// run full pipeline
|
||||
ResourceResponse response = null;
|
||||
try {
|
||||
if (nextUpdate != null) {
|
||||
response = nextUpdate.call();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
CompletableFuture<ResourceResponse> response = null;
|
||||
|
||||
|
||||
throw new RuntimeException(String.format("Error on Logging.OnUpdateActivity : %s", e.toString()));
|
||||
if (nextUpdate != null) {
|
||||
response = nextUpdate.get();
|
||||
}
|
||||
|
||||
// add Message Update activity
|
||||
Activity updateActivity = Activity.clone(activity);
|
||||
updateActivity.setType(ActivityTypes.MESSAGE_UPDATE);
|
||||
LogActivity(updateActivity);
|
||||
|
||||
return response;
|
||||
|
||||
|
||||
});
|
||||
|
||||
// hook up delete activity pipeline
|
||||
context.OnDeleteActivity((ctxt, reference, nextDel) -> {
|
||||
context.onDeleteActivity((ctxt, reference, nextDel) -> {
|
||||
// run full pipeline
|
||||
|
||||
try {
|
||||
if (nextDel != null) {
|
||||
logger.error(String.format("Transcript logActivity next delegate: %s)", nextDel));
|
||||
nextDel.run();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error(String.format("Transcript logActivity failed with %s (next delegate: %s)", e.toString(), nextDel));
|
||||
throw new RuntimeException(String.format("Transcript logActivity failed with %s", e.getMessage()));
|
||||
|
||||
if (nextDel != null) {
|
||||
logger.debug(String.format("Transcript logActivity next delegate: %s)", nextDel));
|
||||
nextDel.get();
|
||||
}
|
||||
|
||||
// add MessageDelete activity
|
||||
|
@ -151,18 +129,13 @@ public class TranscriptLoggerMiddleware implements Middleware {
|
|||
}};
|
||||
|
||||
LogActivity(deleteActivity);
|
||||
return;
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
// process bot logic
|
||||
try {
|
||||
next.next();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("Error on Logging.next : %s", e.toString()));
|
||||
}
|
||||
CompletableFuture<Void> result = next.next();
|
||||
|
||||
// flush transcript at end of turn
|
||||
while (!transcript.isEmpty()) {
|
||||
|
@ -174,16 +147,15 @@ public class TranscriptLoggerMiddleware implements Middleware {
|
|||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private void LogActivity(Activity activity) {
|
||||
if (activity.getTimestamp() == null) {
|
||||
activity.setTimestamp(DateTime.now(DateTimeZone.UTC));
|
||||
}
|
||||
transcript.offer(activity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import org.joda.time.DateTime;
|
||||
|
@ -13,6 +12,35 @@ import java.util.concurrent.CompletableFuture;
|
|||
* Transcript logger stores activities for conversations for recall.
|
||||
*/
|
||||
public interface TranscriptStore extends TranscriptLogger {
|
||||
|
||||
/**
|
||||
* Gets from the store activities that match a set of criteria.
|
||||
*
|
||||
* @param channelId The ID of the channel the conversation is in.
|
||||
* @param conversationId The ID of the conversation.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* If the task completes successfully, the result contains the matching activities.
|
||||
*/
|
||||
default CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
|
||||
String conversationId) {
|
||||
return getTranscriptActivitiesAsync(channelId, conversationId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets from the store activities that match a set of criteria.
|
||||
*
|
||||
* @param channelId The ID of the channel the conversation is in.
|
||||
* @param conversationId The ID of the conversation.
|
||||
* @param continuationToken
|
||||
* @return A task that represents the work queued to execute.
|
||||
* If the task completes successfully, the result contains the matching activities.
|
||||
*/
|
||||
default CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
|
||||
String conversationId,
|
||||
String continuationToken) {
|
||||
return getTranscriptActivitiesAsync(channelId, conversationId, continuationToken, DateTime.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets from the store activities that match a set of criteria.
|
||||
*
|
||||
|
@ -23,14 +51,20 @@ public interface TranscriptStore extends TranscriptLogger {
|
|||
* @return A task that represents the work queued to execute.
|
||||
* If the task completes successfully, the result contains the matching activities.
|
||||
*/
|
||||
CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
|
||||
String conversationId,
|
||||
String continuationToken,
|
||||
DateTime startDate);
|
||||
|
||||
CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken);
|
||||
|
||||
CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId);
|
||||
|
||||
//C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are created above:
|
||||
//ORIGINAL LINE: Task<PagedResult<IActivity>> GetTranscriptActivitiesAsync(string channelId, string conversationId, string continuationToken = null, DateTime startDate = default(DateTime));
|
||||
CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken, DateTime localStartDate);
|
||||
/**
|
||||
* Gets the conversations on a channel from the store.
|
||||
*
|
||||
* @param channelId The ID of the channel.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
default CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId) {
|
||||
return listTranscriptsAsync(channelId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the conversations on a channel from the store.
|
||||
|
@ -39,12 +73,7 @@ public interface TranscriptStore extends TranscriptLogger {
|
|||
* @param continuationToken
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
|
||||
CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId);
|
||||
|
||||
//C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are created above:
|
||||
//ORIGINAL LINE: Task<PagedResult<Transcript>> ListTranscriptsAsync(string channelId, string continuationToken = null);
|
||||
CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId, String continuationToken);
|
||||
CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId, String continuationToken);
|
||||
|
||||
/**
|
||||
* Deletes conversation data from the store.
|
||||
|
@ -53,5 +82,5 @@ public interface TranscriptStore extends TranscriptLogger {
|
|||
* @param conversationId The ID of the conversation to delete.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
CompletableFuture DeleteTranscriptAsync(String channelId, String conversationId);
|
||||
CompletableFuture<Void> deleteTranscriptAsync(String channelId, String conversationId);
|
||||
}
|
||||
|
|
|
@ -2,30 +2,12 @@
|
|||
// Licensed under the MIT License.
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
/**
|
||||
* A method that can participate in send activity events for the current turn.
|
||||
* @param context The context object for the turn.
|
||||
* @param activities The activities to send.
|
||||
* @param next The delegate to call to continue event processing.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* A handler calls the {@code next} delegate to pass control to
|
||||
* the next registered handler. If a handler doesn’t call the next delegate,
|
||||
* the adapter does not call any of the subsequent handlers and does not send the
|
||||
* {@code activities}.
|
||||
*
|
||||
* {@linkalso BotAdapter}
|
||||
* {@linkalso UpdateActivityHandler}
|
||||
* {@linkalso DeleteActivityHandler}
|
||||
*/
|
||||
|
||||
import com.microsoft.bot.schema.Activity;
|
||||
import com.microsoft.bot.schema.ConversationReference;
|
||||
import com.microsoft.bot.schema.ResourceResponse;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
//public delegate Task DeleteActivityHandler(TurnContext context, ConversationReference reference, Func<Task> next);
|
||||
|
||||
/**
|
||||
* Provides context for a turn of a bot.
|
||||
* Context provides information needed to process an incoming activity.
|
||||
|
@ -34,8 +16,31 @@ import java.util.concurrent.CompletableFuture;
|
|||
* {@linkalso Bot}
|
||||
* {@linkalso Middleware}
|
||||
*/
|
||||
public interface TurnContext
|
||||
{
|
||||
public interface TurnContext {
|
||||
/**
|
||||
* Sends a trace activity to the {@link BotAdapter} for logging purposes.
|
||||
*
|
||||
* @param turnContext The context for the current turn.
|
||||
* @param name The value to assign to the activity's {@link Activity#getName} property.
|
||||
* @param value The value to assign to the activity's {@link Activity#getValue} property.
|
||||
* @param valueType The value to assign to the activity's {@link Activity#getValueType} property.
|
||||
* @param label The value to assign to the activity's {@link Activity#getLabel} property.
|
||||
* @return A task that represents the work queued to execute. If the adapter is being hosted in the Emulator,
|
||||
* the task result contains a {@link ResourceResponse} object with the original trace activity's ID; otherwise,
|
||||
* it contains a {@link ResourceResponse} object containing the ID that the receiving channel assigned to the
|
||||
* activity.
|
||||
*/
|
||||
static CompletableFuture<ResourceResponse> traceActivityAsync(
|
||||
TurnContext turnContext,
|
||||
String name,
|
||||
Object value,
|
||||
String valueType,
|
||||
String label) {
|
||||
|
||||
return turnContext.sendActivityAsync(turnContext.getActivity().createTrace(name, value, valueType, label));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the bot adapter that created this context object.
|
||||
*/
|
||||
|
@ -44,21 +49,18 @@ public interface TurnContext
|
|||
/**
|
||||
* Gets the services registered on this context object.
|
||||
*/
|
||||
TurnContextServiceCollection getServices();
|
||||
TurnContextStateCollection getTurnState();
|
||||
|
||||
/**
|
||||
* Incoming request
|
||||
*/
|
||||
Activity getActivity();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Indicates whether at least one response was sent for the current turn.
|
||||
* @return {@code true} if at least one response was sent for the current turn.
|
||||
*/
|
||||
boolean getResponded();
|
||||
void setResponded(boolean responded);
|
||||
|
||||
/**
|
||||
* Sends a message activity to the sender of the incoming activity.
|
||||
|
@ -74,7 +76,7 @@ public interface TurnContext
|
|||
* Speech Synthesis Markup Language (SSML) format.</p>
|
||||
*
|
||||
*/
|
||||
ResourceResponse SendActivity(String textReplyToSend) throws Exception;
|
||||
CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend);
|
||||
|
||||
/**
|
||||
* Sends a message activity to the sender of the incoming activity.
|
||||
|
@ -92,8 +94,7 @@ public interface TurnContext
|
|||
* Speech Synthesis Markup Language (SSML) format.</p>
|
||||
*
|
||||
*/
|
||||
ResourceResponse SendActivity(String textReplyToSend, String speak) throws Exception;
|
||||
//CompletableFuture<ResourceResponse> SendActivity(String textReplyToSend, String speak = null, String inputHint = InputHints.AcceptingInput);
|
||||
CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak);
|
||||
|
||||
/**
|
||||
* Sends a message activity to the sender of the incoming activity.
|
||||
|
@ -115,7 +116,7 @@ public interface TurnContext
|
|||
* Speech Synthesis Markup Language (SSML) format.</p>
|
||||
*
|
||||
*/
|
||||
ResourceResponse SendActivity(String textReplyToSend, String speak, String inputHint) throws Exception;
|
||||
CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak, String inputHint);
|
||||
|
||||
/**
|
||||
* Sends an activity to the sender of the incoming activity.
|
||||
|
@ -125,7 +126,7 @@ public interface TurnContext
|
|||
* a {@link ResourceResponse} object containing the ID that the receiving
|
||||
* channel assigned to the activity.
|
||||
*/
|
||||
ResourceResponse SendActivity(Activity activity) throws Exception;
|
||||
CompletableFuture<ResourceResponse> sendActivityAsync(Activity activity);
|
||||
|
||||
/**
|
||||
* Sends a set of activities to the sender of the incoming activity.
|
||||
|
@ -135,7 +136,7 @@ public interface TurnContext
|
|||
* an array of {@link ResourceResponse} objects containing the IDs that
|
||||
* the receiving channel assigned to the activities.
|
||||
*/
|
||||
ResourceResponse[] SendActivities(Activity[] activities) throws Exception;
|
||||
CompletableFuture<ResourceResponse[]> sendActivitiesAsync(Activity[] activities);
|
||||
|
||||
/**
|
||||
* Replaces an existing activity.
|
||||
|
@ -147,26 +148,14 @@ public interface TurnContext
|
|||
* <p>Before calling this, set the ID of the replacement activity to the ID
|
||||
* of the activity to replace.</p>
|
||||
*/
|
||||
ResourceResponse UpdateActivity(Activity activity) throws Exception;
|
||||
|
||||
/**
|
||||
* Replaces an existing activity.
|
||||
* @param activity New replacement activity.
|
||||
* @return A task that represents the work queued to execute.
|
||||
* If the activity is successfully sent, the task result contains
|
||||
* a {@link ResourceResponse} object containing the ID that the receiving
|
||||
* channel assigned to the activity.
|
||||
* <p>Before calling this, set the ID of the replacement activity to the ID
|
||||
* of the activity to replace.</p>
|
||||
*/
|
||||
//CompletableFuture<ResourceResponse> UpdateActivityAsync(Activity activity) throws Exception;
|
||||
CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity);
|
||||
|
||||
/**
|
||||
* Deletes an existing activity.
|
||||
* @param activityId The ID of the activity to delete.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
CompletableFuture<Void> DeleteActivity(String activityId) throws Exception;
|
||||
CompletableFuture<Void> deleteActivityAsync(String activityId);
|
||||
|
||||
/**
|
||||
* Deletes an existing activity.
|
||||
|
@ -175,40 +164,40 @@ public interface TurnContext
|
|||
* The conversation reference's {@link ConversationReference#getActivityId}
|
||||
* indicates the activity in the conversation to delete.
|
||||
*/
|
||||
void DeleteActivity(ConversationReference conversationReference) throws Exception;
|
||||
CompletableFuture<Void> deleteActivityAsync(ConversationReference conversationReference);
|
||||
|
||||
/**
|
||||
* Adds a response handler for send activity operations.
|
||||
* @param handler The handler to add to the context object.
|
||||
* @return The updated context object.
|
||||
* When the context's {@link #SendActivity( Activity )}
|
||||
* or {@link #SendActivities( Activity[])} methods are called,
|
||||
* When the context's {@link #sendActivityAsync( Activity )}
|
||||
* or {@link #sendActivitiesAsync( Activity[])} methods are called,
|
||||
* the adapter calls the registered handlers in the order in which they were
|
||||
* added to the context object.
|
||||
*
|
||||
*/
|
||||
TurnContext OnSendActivities(SendActivitiesHandler handler);
|
||||
TurnContext onSendActivities(SendActivitiesHandler handler);
|
||||
|
||||
/**
|
||||
* Adds a response handler for update activity operations.
|
||||
* @param handler The handler to add to the context object.
|
||||
* @return The updated context object.
|
||||
* When the context's {@link #UpdateActivity( Activity )} is called,
|
||||
* When the context's {@link #updateActivityAsync(Activity)} is called,
|
||||
* the adapter calls the registered handlers in the order in which they were
|
||||
* added to the context object.
|
||||
*
|
||||
*/
|
||||
TurnContext OnUpdateActivity(UpdateActivityHandler handler);
|
||||
TurnContext onUpdateActivity(UpdateActivityHandler handler);
|
||||
|
||||
/**
|
||||
* Adds a response handler for delete activity operations.
|
||||
* @param handler The handler to add to the context object.
|
||||
* @return The updated context object.
|
||||
* @throws NullPointerException {@code handler} is {@code null}.
|
||||
* When the context's {@link #DeleteActivity(String)} is called,
|
||||
* When the context's {@link #deleteActivityAsync(String)} is called,
|
||||
* the adapter calls the registered handlers in the order in which they were
|
||||
* added to the context object.
|
||||
*
|
||||
*/
|
||||
TurnContext OnDeleteActivity(DeleteActivityHandler handler);
|
||||
TurnContext onDeleteActivity(DeleteActivityHandler handler);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.Arrays;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.microsoft.bot.schema.ActivityTypes.MESSAGE;
|
||||
import static com.microsoft.bot.schema.ActivityTypes.TRACE;
|
||||
|
@ -37,7 +38,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
private final List<UpdateActivityHandler> onUpdateActivity = new ArrayList<UpdateActivityHandler>();
|
||||
private final List<DeleteActivityHandler> onDeleteActivity = new ArrayList<DeleteActivityHandler>();
|
||||
|
||||
private final TurnContextServiceCollection turnServices;
|
||||
private final TurnContextStateCollection turnState;
|
||||
|
||||
/**
|
||||
* Creates a context object.
|
||||
|
@ -57,7 +58,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
throw new IllegalArgumentException("activity");
|
||||
this.activity = activity;
|
||||
|
||||
turnServices = new TurnContextServiceCollectionImpl();
|
||||
turnState = new TurnContextStateCollection();
|
||||
}
|
||||
|
||||
|
||||
|
@ -67,12 +68,13 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @param handler The handler to add to the context object.
|
||||
* @return The updated context object.
|
||||
* @throws IllegalArgumentException {@code handler} is {@code null}.
|
||||
* When the context's {@link #SendActivity( Activity )}
|
||||
* or {@link #SendActivities( Activity[])} methods are called,
|
||||
* When the context's {@link #sendActivityAsync( Activity )}
|
||||
* or {@link #sendActivitiesAsync( Activity[])} methods are called,
|
||||
* the adapter calls the registered handlers in the order in which they were
|
||||
* added to the context object.
|
||||
*/
|
||||
public TurnContextImpl OnSendActivities(SendActivitiesHandler handler) {
|
||||
@Override
|
||||
public TurnContext onSendActivities(SendActivitiesHandler handler) {
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("handler");
|
||||
|
||||
|
@ -86,11 +88,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @param handler The handler to add to the context object.
|
||||
* @return The updated context object.
|
||||
* @throws IllegalArgumentException {@code handler} is {@code null}.
|
||||
* When the context's {@link #UpdateActivity( Activity )} is called,
|
||||
* When the context's {@link #updateActivityAsync( Activity )} is called,
|
||||
* the adapter calls the registered handlers in the order in which they were
|
||||
* added to the context object.
|
||||
*/
|
||||
public TurnContextImpl OnUpdateActivity(UpdateActivityHandler handler) {
|
||||
@Override
|
||||
public TurnContext onUpdateActivity(UpdateActivityHandler handler) {
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("handler");
|
||||
|
||||
|
@ -104,11 +107,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @param handler The handler to add to the context object.
|
||||
* @return The updated context object.
|
||||
* @throws IllegalArgumentException {@code handler} is {@code null}.
|
||||
* When the context's {@link #DeleteActivity(String)} is called,
|
||||
* When the context's {@link #deleteActivityAsync(String)} is called,
|
||||
* the adapter calls the registered handlers in the order in which they were
|
||||
* added to the context object.
|
||||
*/
|
||||
public TurnContextImpl OnDeleteActivity(DeleteActivityHandler handler) {
|
||||
@Override
|
||||
public TurnContext onDeleteActivity(DeleteActivityHandler handler) {
|
||||
if (handler == null)
|
||||
throw new IllegalArgumentException("handler");
|
||||
|
||||
|
@ -126,8 +130,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
/**
|
||||
* Gets the services registered on this context object.
|
||||
*/
|
||||
public TurnContextServiceCollection getServices() {
|
||||
return this.turnServices;
|
||||
public TurnContextStateCollection getTurnState() {
|
||||
return this.turnState;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,11 +149,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @return {@code true} if at least one response was sent for the current turn.
|
||||
* @throws IllegalArgumentException You attempted to set the value to {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public boolean getResponded() {
|
||||
return this.responded;
|
||||
}
|
||||
|
||||
public void setResponded(boolean responded) {
|
||||
private void setResponded(boolean responded) {
|
||||
if (responded == false) {
|
||||
throw new IllegalArgumentException("TurnContext: cannot set 'responded' to a value of 'false'.");
|
||||
}
|
||||
|
@ -172,17 +177,17 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* Speech Synthesis Markup Language (SSML) format.</p>
|
||||
*/
|
||||
@Override
|
||||
public ResourceResponse SendActivity(String textReplyToSend) throws Exception {
|
||||
return SendActivity(textReplyToSend, null, null);
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend) {
|
||||
return sendActivityAsync(textReplyToSend, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceResponse SendActivity(String textReplyToSend, String speak) throws Exception {
|
||||
return SendActivity(textReplyToSend, speak, null);
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak) {
|
||||
return sendActivityAsync(textReplyToSend, speak, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceResponse SendActivity(String textReplyToSend, String speak, String inputHint) throws Exception {
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak, String inputHint) {
|
||||
if (StringUtils.isEmpty(textReplyToSend))
|
||||
throw new IllegalArgumentException("textReplyToSend");
|
||||
|
||||
|
@ -195,7 +200,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
if (StringUtils.isNotEmpty(inputHint))
|
||||
activityToSend.setInputHint(InputHints.fromString(inputHint));
|
||||
|
||||
return SendActivity(activityToSend);
|
||||
return sendActivityAsync(activityToSend);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,28 +214,19 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* channel assigned to the activity.
|
||||
*/
|
||||
@Override
|
||||
public ResourceResponse SendActivity(Activity activity) throws Exception {
|
||||
if (activity == null)
|
||||
public CompletableFuture<ResourceResponse> sendActivityAsync(Activity activity) {
|
||||
if (activity == null) {
|
||||
throw new IllegalArgumentException("activity");
|
||||
}
|
||||
|
||||
System.out.printf("In SENDEACTIVITYASYNC:");
|
||||
System.out.flush();
|
||||
Activity[] activities = {activity};
|
||||
ResourceResponse[] responses;
|
||||
try {
|
||||
responses = SendActivities(activities);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("TurnContext:SendActivity fail %s", e.toString()));
|
||||
}
|
||||
if (responses == null || responses.length == 0) {
|
||||
// It's possible an interceptor prevented the activity from having been sent.
|
||||
// Just return an empty response in that case.
|
||||
return null;
|
||||
} else {
|
||||
return responses[0];
|
||||
}
|
||||
|
||||
return sendActivitiesAsync(activities)
|
||||
.thenApply(resourceResponses -> {
|
||||
if (resourceResponses == null || resourceResponses.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return resourceResponses[0];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,23 +239,22 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* the receiving channel assigned to the activities.
|
||||
*/
|
||||
@Override
|
||||
public ResourceResponse[] SendActivities(Activity[] activities) throws Exception {
|
||||
public CompletableFuture<ResourceResponse[]> sendActivitiesAsync(Activity[] activities) {
|
||||
// Bind the relevant Conversation Reference properties, such as URLs and
|
||||
// ChannelId's, to the activities we're about to send.
|
||||
ConversationReference cr = GetConversationReference(this.activity);
|
||||
ConversationReference cr = getConversationReference(this.activity);
|
||||
for (Activity a : activities) {
|
||||
ApplyConversationReference(a, cr);
|
||||
applyConversationReference(a, cr);
|
||||
}
|
||||
|
||||
// Convert the IActivities to Activies.
|
||||
// Activity[] activityArray = Array.ConvertAll(activities, (input) => (Activity)input);
|
||||
List<Activity> activityArray = Arrays.stream(activities).map(input -> input).collect(toList());
|
||||
|
||||
|
||||
// Create the list used by the recursive methods.
|
||||
List<Activity> activityList = new ArrayList<Activity>(activityArray);
|
||||
|
||||
Callable<ResourceResponse[]> ActuallySendStuff = () -> {
|
||||
Supplier<CompletableFuture<ResourceResponse[]>> actuallySendStuff = () -> {
|
||||
// Are the any non-trace activities to send?
|
||||
// The thinking here is that a Trace event isn't user relevant data
|
||||
// so the "Responded" flag should not be set by Trace messages being
|
||||
|
@ -268,6 +263,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
if (!activityList.stream().anyMatch((a) -> a.getType() == TRACE)) {
|
||||
sentNonTraceActivities = true;
|
||||
}
|
||||
|
||||
// Send from the list, which may have been manipulated via the event handlers.
|
||||
// Note that 'responses' was captured from the root of the call, and will be
|
||||
// returned to the original caller.
|
||||
|
@ -290,7 +286,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
};
|
||||
|
||||
List<Activity> act_list = new ArrayList<>(activityList);
|
||||
return SendActivitiesInternal(act_list, onSendActivities.iterator(), ActuallySendStuff);
|
||||
return sendActivitiesInternal(act_list, onSendActivities.iterator(), actuallySendStuff);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -301,18 +297,14 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @throws com.microsoft.bot.connector.rest.ErrorResponseException The HTTP operation failed and the response contained additional information.
|
||||
*/
|
||||
@Override
|
||||
public ResourceResponse UpdateActivity(Activity activity) throws Exception {
|
||||
|
||||
|
||||
Callable<ResourceResponse> ActuallyUpdateStuff = () -> {
|
||||
public CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity) {
|
||||
Supplier<ResourceResponse> ActuallyUpdateStuff = () -> {
|
||||
return this.getAdapter().UpdateActivity(this, activity);
|
||||
};
|
||||
|
||||
return UpdateActivityInternal(activity, onUpdateActivity.iterator(), ActuallyUpdateStuff);
|
||||
return updateActivityInternal(activity, onUpdateActivity.iterator(), ActuallyUpdateStuff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Deletes an existing activity.
|
||||
*
|
||||
|
@ -320,7 +312,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @return A task that represents the work queued to execute.
|
||||
* @throws Exception The HTTP operation failed and the response contained additional information.
|
||||
*/
|
||||
public CompletableFuture<Void> DeleteActivity(String activityId) throws Exception {
|
||||
public CompletableFuture<Void> deleteActivityAsync(String activityId) {
|
||||
if (StringUtils.isWhitespace(activityId) || activityId == null)
|
||||
throw new IllegalArgumentException("activityId");
|
||||
|
||||
|
@ -342,7 +334,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
};
|
||||
|
||||
try {
|
||||
DeleteActivityInternal(cr, onDeleteActivity.iterator(), ActuallyDeleteStuff);
|
||||
deleteActivityInternal(cr, onDeleteActivity.iterator(), ActuallyDeleteStuff);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("Failed to delete activity %s", e.getMessage()));
|
||||
|
@ -350,7 +342,6 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
return;
|
||||
|
||||
}, ExecutorFactory.getExecutor());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -362,7 +353,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* The conversation reference's {@link ConversationReference#getActivityId}
|
||||
* indicates the activity in the conversation to delete.
|
||||
*/
|
||||
public void DeleteActivity(ConversationReference conversationReference) throws Exception {
|
||||
public CompletableFuture<Void> deleteActivityAsync(ConversationReference conversationReference) {
|
||||
if (conversationReference == null)
|
||||
throw new IllegalArgumentException("conversationReference");
|
||||
|
||||
|
@ -378,11 +369,14 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
throw new RuntimeException("DeleteActivity failed");
|
||||
};
|
||||
|
||||
DeleteActivityInternal(conversationReference, onDeleteActivity.iterator(), ActuallyDeleteStuff);
|
||||
return ;
|
||||
return deleteActivityInternal(conversationReference, onDeleteActivity.iterator(), ActuallyDeleteStuff);
|
||||
}
|
||||
|
||||
private ResourceResponse[] SendActivitiesInternal(List<Activity> activities, Iterator<SendActivitiesHandler> sendHandlers, Callable<ResourceResponse[]> callAtBottom) throws Exception {
|
||||
private CompletableFuture<ResourceResponse[]> sendActivitiesInternal(
|
||||
List<Activity> activities,
|
||||
Iterator<SendActivitiesHandler> sendHandlers,
|
||||
Supplier<CompletableFuture<ResourceResponse[]>> callAtBottom) {
|
||||
|
||||
if (activities == null)
|
||||
throw new IllegalArgumentException("activities");
|
||||
if (sendHandlers == null)
|
||||
|
@ -390,24 +384,24 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
|
||||
if (false == sendHandlers.hasNext()) { // No middleware to run.
|
||||
if (callAtBottom != null)
|
||||
return callAtBottom.call();
|
||||
return callAtBottom.get();
|
||||
return new ResourceResponse[0];
|
||||
}
|
||||
|
||||
// Default to "No more Middleware after this".
|
||||
Callable<ResourceResponse[]> next = () -> {
|
||||
Supplier<CompletableFuture<ResourceResponse[]>> next = () -> {
|
||||
// Remove the first item from the list of middleware to call,
|
||||
// so that the next call just has the remaining items to worry about.
|
||||
//Iterable<SendActivitiesHandler> remaining = sendHandlers.Skip(1);
|
||||
//Iterator<SendActivitiesHandler> remaining = sendHandlers.iterator();
|
||||
if (sendHandlers.hasNext())
|
||||
sendHandlers.next();
|
||||
return SendActivitiesInternal(activities, sendHandlers, callAtBottom);
|
||||
return sendActivitiesInternal(activities, sendHandlers, callAtBottom);
|
||||
};
|
||||
|
||||
// Grab the current middleware, which is the 1st element in the array, and execute it
|
||||
SendActivitiesHandler caller = sendHandlers.next();
|
||||
return caller.handle(this, activities, next);
|
||||
return caller.invoke(this, activities, next);
|
||||
}
|
||||
|
||||
// private async Task<ResourceResponse> UpdateActivityInternal(Activity activity,
|
||||
|
@ -449,29 +443,29 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
// UpdateActivityHandler toCall = updateHandlers.First();
|
||||
// return await toCall(this, activity, next);
|
||||
// }
|
||||
private ResourceResponse UpdateActivityInternal(Activity activity,
|
||||
private CompletableFuture<ResourceResponse> updateActivityInternal(Activity activity,
|
||||
Iterator<UpdateActivityHandler> updateHandlers,
|
||||
Callable<ResourceResponse> callAtBottom) throws Exception {
|
||||
Supplier<ResourceResponse> callAtBottom) {
|
||||
BotAssert.ActivityNotNull(activity);
|
||||
if (updateHandlers == null)
|
||||
throw new IllegalArgumentException("updateHandlers");
|
||||
|
||||
if (false == updateHandlers.hasNext()) { // No middleware to run.
|
||||
if (callAtBottom != null) {
|
||||
return callAtBottom.call();
|
||||
return callAtBottom.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Default to "No more Middleware after this".
|
||||
Callable<ResourceResponse> next = () -> {
|
||||
Supplier<CompletableFuture<ResourceResponse>> next = () -> {
|
||||
// Remove the first item from the list of middleware to call,
|
||||
// so that the next call just has the remaining items to worry about.
|
||||
if (updateHandlers.hasNext())
|
||||
updateHandlers.next();
|
||||
ResourceResponse result = null;
|
||||
try {
|
||||
result = UpdateActivityInternal(activity, updateHandlers, callAtBottom);
|
||||
result = updateActivityInternal(activity, updateHandlers, callAtBottom);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("Error updating activity: %s", e.toString()));
|
||||
|
@ -482,13 +476,13 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
|
||||
// Grab the current middleware, which is the 1st element in the array, and execute it
|
||||
UpdateActivityHandler toCall = updateHandlers.next();
|
||||
return toCall.handle(this, activity, next);
|
||||
return toCall.invoke(this, activity, next);
|
||||
}
|
||||
|
||||
|
||||
private void DeleteActivityInternal(ConversationReference cr,
|
||||
private CompletableFuture<Void> deleteActivityInternal(ConversationReference cr,
|
||||
Iterator<DeleteActivityHandler> deleteHandlers,
|
||||
Runnable callAtBottom) throws Exception {
|
||||
Runnable callAtBottom) {
|
||||
BotAssert.ConversationReferenceNotNull(cr);
|
||||
if (deleteHandlers == null)
|
||||
throw new IllegalArgumentException("deleteHandlers");
|
||||
|
@ -501,7 +495,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
}
|
||||
|
||||
// Default to "No more Middleware after this".
|
||||
Runnable next = () -> {
|
||||
Supplier<CompletableFuture<Void>> next = () -> {
|
||||
// Remove the first item from the list of middleware to call,
|
||||
// so that the next call just has the remaining items to worry about.
|
||||
|
||||
|
@ -511,17 +505,18 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
|
||||
|
||||
try {
|
||||
DeleteActivityInternal(cr, deleteHandlers, callAtBottom);
|
||||
deleteActivityInternal(cr, deleteHandlers, callAtBottom);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("DeleteActivityInternal failed");
|
||||
}
|
||||
return;
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// Grab the current middleware, which is the 1st element in the array, and execute it.
|
||||
DeleteActivityHandler toCall = deleteHandlers.next();
|
||||
toCall.handle(this, cr, next);
|
||||
toCall.invoke(this, cr, next);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -531,7 +526,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @return A conversation reference for the conversation that contains the activity.
|
||||
* @throws IllegalArgumentException {@code activity} is {@code null}.
|
||||
*/
|
||||
public static ConversationReference GetConversationReference(Activity activity) {
|
||||
public static ConversationReference getConversationReference(Activity activity) {
|
||||
BotAssert.ActivityNotNull(activity);
|
||||
|
||||
ConversationReference r = new ConversationReference() {{
|
||||
|
@ -553,8 +548,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @param activity The activity to update.
|
||||
* @param reference The conversation reference.
|
||||
*/
|
||||
public static Activity ApplyConversationReference(Activity activity, ConversationReference reference) {
|
||||
return ApplyConversationReference(activity, reference, false);
|
||||
public static Activity applyConversationReference(Activity activity, ConversationReference reference) {
|
||||
return applyConversationReference(activity, reference, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -566,13 +561,16 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
* @param isIncoming (Optional) {@code true} to treat the activity as an
|
||||
* incoming activity, where the bot is the recipient; otherwaire {@code false}.
|
||||
* Default is {@code false}, and the activity will show the bot as the sender.
|
||||
* Call {@link #GetConversationReference( Activity )} on an incoming
|
||||
* Call {@link #getConversationReference( Activity )} on an incoming
|
||||
* activity to get a conversation reference that you can then use to update an
|
||||
* outgoing activity with the correct delivery information.
|
||||
* <p>The {@link #SendActivity( Activity )} and {@link #SendActivities( Activity[])}
|
||||
* <p>The {@link #sendActivityAsync( Activity )} and {@link #sendActivitiesAsync( Activity[])}
|
||||
* methods do this for you.</p>
|
||||
*/
|
||||
public static Activity ApplyConversationReference(Activity activity, ConversationReference reference, boolean isIncoming) {
|
||||
public static Activity applyConversationReference(Activity activity,
|
||||
ConversationReference reference,
|
||||
boolean isIncoming) {
|
||||
|
||||
activity.setChannelId(reference.getChannelId());
|
||||
activity.setServiceUrl(reference.getServiceUrl());
|
||||
activity.setConversation(reference.getConversation());
|
||||
|
@ -592,6 +590,6 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
|
|||
}
|
||||
|
||||
public void close() throws Exception {
|
||||
turnServices.close();
|
||||
turnState.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a set of collection of services associated with the {@link TurnContext}.
|
||||
*
|
||||
* TODO: add more details on what kind of services can/should be stored here, by whom and what the lifetime semantics are, etc.
|
||||
*
|
||||
*/
|
||||
public interface TurnContextServiceCollection extends Iterable<Map.Entry<String, Object>>, AutoCloseable {
|
||||
/**
|
||||
* Add a service with a specified key.
|
||||
* @param TService The type of service to be added.
|
||||
* @param key The key to store the service under.
|
||||
* @param service The service to add.
|
||||
* @throws IllegalArgumentException Thrown when a service is already registered with the specified {@code key}
|
||||
*/
|
||||
<TService extends Object> void Add(String key, TService service) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Get a service by its key.
|
||||
* @param TService The type of service to be retrieved.
|
||||
* @param key The key of the service to get.
|
||||
* @return The service stored under the specified key.
|
||||
*/
|
||||
<TService extends Object> TService Get(String key) throws IllegalArgumentException;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public final class TurnContextServiceCollectionImpl implements TurnContextServiceCollection, AutoCloseable
|
||||
{
|
||||
private final HashMap<String, Object> _services = new HashMap<String, Object>();
|
||||
|
||||
public TurnContextServiceCollectionImpl() throws IllegalArgumentException {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public <TService extends Object> TService Get(String key) throws IllegalArgumentException {
|
||||
if (key == null)
|
||||
throw new IllegalArgumentException("key");
|
||||
|
||||
TService service = (TService) _services.get(key);
|
||||
// TODO: log that we didn't find the requested service
|
||||
return (TService) service;
|
||||
}
|
||||
/**
|
||||
* Get a service by type using its full type name as the key.
|
||||
* @param TService The type of service to be retrieved.
|
||||
* @return The service stored under the specified key.
|
||||
*/
|
||||
public <TService> TService Get(Class<TService> type) throws IllegalArgumentException {
|
||||
return this.Get(type.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <TService extends Object> void Add(String key, TService service) throws IllegalArgumentException {
|
||||
if (key == null) throw new IllegalArgumentException("key");
|
||||
if (service == null) throw new IllegalArgumentException("service");
|
||||
|
||||
if (_services.containsKey(key))
|
||||
throw new IllegalArgumentException (String.format("Key %s already exists", key));
|
||||
_services.put(key, service);
|
||||
}
|
||||
/**
|
||||
* Add a service using its full type name as the key.
|
||||
* @param TService The type of service to be added.
|
||||
* @param service The service to add.
|
||||
*/
|
||||
|
||||
public <TService> void Add(TService service, Class<TService> type) throws IllegalArgumentException {
|
||||
Add(type.getName(), service);
|
||||
}
|
||||
|
||||
|
||||
public Iterator<Map.Entry<String, Object>> iterator() {
|
||||
return _services.entrySet().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
for (Map.Entry entry : this._services.entrySet()) {
|
||||
if (entry.getValue() instanceof AutoCloseable) {
|
||||
((AutoCloseable) entry.getValue()).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a set of collection of services associated with the {@link TurnContext}.
|
||||
*/
|
||||
public class TurnContextStateCollection extends HashMap<String, Object> implements AutoCloseable {
|
||||
public <T> T get(String key) throws IllegalArgumentException {
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("key");
|
||||
}
|
||||
|
||||
Object service = get(key);
|
||||
try {
|
||||
T result = (T) service;
|
||||
} catch(ClassCastException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (T) service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a service by type using its full type name as the key.
|
||||
* @param type The type of service to be retrieved.
|
||||
* @return The service stored under the specified key.
|
||||
*/
|
||||
public <T> T get(Class<T> type) throws IllegalArgumentException {
|
||||
return get(type.getName());
|
||||
}
|
||||
|
||||
public <T> void add(String key, T value) throws IllegalArgumentException {
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("key");
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("service");
|
||||
}
|
||||
|
||||
if (containsKey(key))
|
||||
throw new IllegalArgumentException (String.format("Key %s already exists", key));
|
||||
put(key, value);
|
||||
}
|
||||
/**
|
||||
* Add a service using its full type name as the key.
|
||||
* @param value The service to add.
|
||||
*/
|
||||
|
||||
public <T> void add(T value) throws IllegalArgumentException {
|
||||
add(value.getClass().getName(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
for (Map.Entry entry : entrySet()) {
|
||||
if (entry.getValue() instanceof AutoCloseable) {
|
||||
((AutoCloseable) entry.getValue()).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4,5 +4,5 @@ import java.util.concurrent.CompletableFuture;
|
|||
|
||||
@FunctionalInterface
|
||||
public interface TurnTask {
|
||||
CompletableFuture invoke(TurnContext context);
|
||||
CompletableFuture<Void> invoke(TurnContext context);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
// 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.ResourceResponse;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface UpdateActivityHandler {
|
||||
|
@ -19,10 +23,11 @@ public interface UpdateActivityHandler {
|
|||
* activity.
|
||||
* <p>The activity's {@link Activity#getId} indicates the activity in the
|
||||
* conversation to replace.</p>
|
||||
*
|
||||
* {@linkalso BotAdapter}
|
||||
* {@linkalso SendActivitiesHandler}
|
||||
* {@linkalso DeleteActivityHandler}
|
||||
* <p>If the activity is successfully sent, the <paramref name="next"/> delegate returns
|
||||
* a {@link ResourceResponse} object containing the ID that the receiving channel assigned
|
||||
* to the activity. Use this response object as the return value of this handler.</p>
|
||||
*/
|
||||
ResourceResponse handle(TurnContext context, Activity activity, Callable<ResourceResponse> next);
|
||||
CompletableFuture<ResourceResponse> invoke(TurnContext context,
|
||||
Activity activity,
|
||||
Supplier<CompletableFuture<ResourceResponse>> next);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.builder.StateSettings;
|
||||
import com.microsoft.bot.builder.Storage;
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -45,6 +41,6 @@ public class UserState<TState> extends BotState<TState>
|
|||
* @return The user state object.
|
||||
*/
|
||||
public static <TState> TState Get(TurnContext context) throws IllegalArgumentException {
|
||||
return context.getServices().<TState>Get(PropertyName());
|
||||
return context.getTurnState().<TState>Get(PropertyName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.schema.TokenResponse;
|
||||
import com.microsoft.bot.schema.TokenStatus;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface UserTokenProvider {
|
||||
/**
|
||||
* Attempts to retrieve the token for a user that's in a login flow.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param connectionName Name of the auth connection to use.
|
||||
* @param magicCode (Optional) Optional user entered code to validate.
|
||||
* @return Token Response.
|
||||
*/
|
||||
CompletableFuture<TokenResponse> getUserTokenAsync(TurnContext turnContext,
|
||||
String connectionName,
|
||||
String magicCode);
|
||||
|
||||
/**
|
||||
* Get the raw signin link to be sent to the user for signin for a connection name.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param connectionName Name of the auth connection to use.
|
||||
* @return A task that represents the work queued to execute. If the task completes successfully,
|
||||
* the result contains the raw signin link.
|
||||
*/
|
||||
default CompletableFuture<String> getOauthSignInLinkAsync(TurnContext turnContext, String connectionName) {
|
||||
return getOauthSignInLinkAsync(turnContext, connectionName, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw signin link to be sent to the user for signin for a connection name.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param connectionName Name of the auth connection to use.
|
||||
* @param userId The user id that will be associated with the token.
|
||||
* @param finalRedirect The final URL that the OAuth flow will redirect to.
|
||||
* @return A task that represents the work queued to execute. If the task completes successfully,
|
||||
* the result contains the raw signin link.
|
||||
*/
|
||||
CompletableFuture<String> getOauthSignInLinkAsync(TurnContext turnContext,
|
||||
String connectionName,
|
||||
String userId,
|
||||
String finalRedirect);
|
||||
|
||||
/**
|
||||
* Signs the user out with the token server.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
default CompletableFuture<Void> signOutUserAsync(TurnContext turnContext) {
|
||||
return signOutUserAsync(turnContext, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs the user out with the token server.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param connectionName Name of the auth connection to use.
|
||||
* @param userId User id of user to sign out.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
CompletableFuture<Void> signOutUserAsync(TurnContext turnContext, String connectionName, String userId);
|
||||
|
||||
/**
|
||||
* Retrieves the token status for each configured connection for the given user.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param userId The user Id for which token status is retrieved.
|
||||
* @return Array of TokenStatus.
|
||||
*/
|
||||
default CompletableFuture<TokenStatus[]> getTokenStatusAsync(TurnContext turnContext, String userId) {
|
||||
return getTokenStatusAsync(turnContext, userId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the token status for each configured connection for the given user.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param userId The user Id for which token status is retrieved.
|
||||
* @param includeFilter Comma separated list of connection's to include. Blank will return token status
|
||||
* for all configured connections.
|
||||
* @return Array of TokenStatus.
|
||||
*/
|
||||
CompletableFuture<TokenStatus[]> getTokenStatusAsync(TurnContext turnContext, String userId, String includeFilter);
|
||||
|
||||
/**
|
||||
* Retrieves Azure Active Directory tokens for particular resources on a configured connection.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param connectionName The name of the Azure Active Directory connection configured with this bot.
|
||||
* @param resourceUrls The list of resource URLs to retrieve tokens for.
|
||||
* @return Dictionary of resourceUrl to the corresponding TokenResponse.
|
||||
*/
|
||||
default CompletableFuture<Map<String, TokenResponse>> getAadTokesAsync(TurnContext turnContext,
|
||||
String connectionName,
|
||||
String[] resourceUrls) {
|
||||
return getAadTokesAsync(turnContext, connectionName, resourceUrls, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves Azure Active Directory tokens for particular resources on a configured connection.
|
||||
*
|
||||
* @param turnContext Context for the current turn of conversation with the user.
|
||||
* @param connectionName The name of the Azure Active Directory connection configured with this bot.
|
||||
* @param resourceUrls The list of resource URLs to retrieve tokens for.
|
||||
* @param userId The user Id for which tokens are retrieved. If passing in null the userId is taken from
|
||||
* the Activity in the ITurnContext.
|
||||
* @return Dictionary of resourceUrl to the corresponding TokenResponse.
|
||||
*/
|
||||
CompletableFuture<Map<String, TokenResponse>> getAadTokesAsync(TurnContext turnContext,
|
||||
String connectionName,
|
||||
String[] resourceUrls,
|
||||
String userId);
|
||||
}
|
|
@ -95,7 +95,7 @@ public class TestAdapter extends BotAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException {
|
||||
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
|
||||
List<ResourceResponse> responses = new LinkedList<ResourceResponse>();
|
||||
|
||||
for (Activity activity : activities) {
|
||||
|
@ -120,7 +120,7 @@ public class TestAdapter extends BotAdapter {
|
|||
// to keep the behavior as close as possible to facillitate
|
||||
// more realistic tests.
|
||||
int delayMs = (int) activity.getValue();
|
||||
Thread.sleep(delayMs);
|
||||
try { Thread.sleep(delayMs); } catch (InterruptedException e) {}
|
||||
} else {
|
||||
synchronized (this.botReplies) {
|
||||
this.botReplies.add(activity);
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for
|
||||
// license information.
|
||||
|
||||
/**
|
||||
* This package contains the classes for Bot-Builder.
|
||||
*/
|
||||
package com.microsoft.bot.builder;
|
|
@ -1,37 +1,36 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Helper class for defining middleware by using a delegate or anonymous method.
|
||||
*/
|
||||
public class AnonymousReceiveMiddleware implements Middleware
|
||||
{
|
||||
private MiddlewareCall _toCall;
|
||||
|
||||
/**
|
||||
* Creates a middleware object that uses the provided method as its
|
||||
* process request handler.
|
||||
* @param anonymousMethod The method to use as the middleware's process
|
||||
* request handler.
|
||||
*/
|
||||
public AnonymousReceiveMiddleware(MiddlewareCall anonymousMethod)
|
||||
{
|
||||
if (anonymousMethod == null)
|
||||
throw new NullPointerException("MiddlewareCall anonymousMethod");
|
||||
else
|
||||
_toCall = anonymousMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the method provided in the {@link AnonymousReceiveMiddleware} to
|
||||
* process an incoming activity.
|
||||
* @param context The context object for this turn.
|
||||
* @param next The delegate to call to continue the bot middleware pipeline.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
|
||||
_toCall.requestHandler(context, next);
|
||||
}
|
||||
|
||||
}
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* Helper class for defining middleware by using a delegate or anonymous method.
|
||||
*/
|
||||
public class AnonymousReceiveMiddleware implements Middleware
|
||||
{
|
||||
private MiddlewareCall _toCall;
|
||||
|
||||
/**
|
||||
* Creates a middleware object that uses the provided method as its
|
||||
* process request handler.
|
||||
* @param anonymousMethod The method to use as the middleware's process
|
||||
* request handler.
|
||||
*/
|
||||
public AnonymousReceiveMiddleware(MiddlewareCall anonymousMethod)
|
||||
{
|
||||
if (anonymousMethod == null)
|
||||
throw new NullPointerException("MiddlewareCall anonymousMethod");
|
||||
else
|
||||
_toCall = anonymousMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the method provided in the {@link AnonymousReceiveMiddleware} to
|
||||
* process an incoming activity.
|
||||
* @param context The context object for this turn.
|
||||
* @param next The delegate to call to continue the bot middleware pipeline.
|
||||
* @return A task that represents the work queued to execute.
|
||||
*/
|
||||
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
|
||||
return _toCall.requestHandler(context, next);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface CallOnException {
|
||||
<T> CompletableFuture apply(TurnContext context, T t ) throws Exception;
|
||||
}
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.builder.TurnContext;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface CallOnException {
|
||||
<T> CompletableFuture apply(TurnContext context, T t ) throws Exception;
|
||||
}
|
|
@ -1,46 +1,48 @@
|
|||
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
/**
|
||||
* This piece of middleware can be added to allow you to handle exceptions when they are thrown
|
||||
* within your bot's code or middleware further down the pipeline. Using this handler you might
|
||||
* send an appropriate message to the user to let them know that something has gone wrong.
|
||||
* You can specify the type of exception the middleware should catch and this middleware can be added
|
||||
* multiple times to allow you to handle different exception types in different ways.
|
||||
*
|
||||
* @param T The type of the exception that you want to catch. This can be 'Exception' to
|
||||
* catch all or a specific type of exception
|
||||
*/
|
||||
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
|
||||
private final CallOnException _handler;
|
||||
private final Class<T> _exceptionType;
|
||||
|
||||
public CatchExceptionMiddleware(CallOnException callOnException, Class<T> exceptionType) {
|
||||
_handler = callOnException;
|
||||
_exceptionType = exceptionType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
|
||||
|
||||
Class c = _exceptionType.getDeclaringClass();
|
||||
|
||||
try {
|
||||
// Continue to route the activity through the pipeline
|
||||
// any errors further down the pipeline will be caught by
|
||||
// this try / catch
|
||||
next.next();
|
||||
} catch (Exception ex) {
|
||||
|
||||
if (_exceptionType.isInstance(ex))
|
||||
// If an error is thrown and the exception is of type T then invoke the handler
|
||||
_handler.<T>apply(context, (T) ex);
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
package com.microsoft.bot.builder;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* This piece of middleware can be added to allow you to handle exceptions when they are thrown
|
||||
* within your bot's code or middleware further down the pipeline. Using this handler you might
|
||||
* send an appropriate message to the user to let them know that something has gone wrong.
|
||||
* You can specify the type of exception the middleware should catch and this middleware can be added
|
||||
* multiple times to allow you to handle different exception types in different ways.
|
||||
*
|
||||
* @param T The type of the exception that you want to catch. This can be 'Exception' to
|
||||
* catch all or a specific type of exception
|
||||
*/
|
||||
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
|
||||
private final CallOnException _handler;
|
||||
private final Class<T> _exceptionType;
|
||||
|
||||
public CatchExceptionMiddleware(CallOnException callOnException, Class<T> exceptionType) {
|
||||
_handler = callOnException;
|
||||
_exceptionType = exceptionType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
|
||||
|
||||
Class c = _exceptionType.getDeclaringClass();
|
||||
|
||||
try {
|
||||
// Continue to route the activity through the pipeline
|
||||
// any errors further down the pipeline will be caught by
|
||||
// this try / catch
|
||||
next.next();
|
||||
} catch (Exception ex) {
|
||||
|
||||
if (_exceptionType.isInstance(ex))
|
||||
// If an error is thrown and the exception is of type T then invoke the handler
|
||||
_handler.<T>apply(context, (T) ex);
|
||||
else
|
||||
throw ex;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,7 @@ public class CustomKeyState extends BotState<CustomState> {
|
|||
}
|
||||
|
||||
public static CustomState Get(TurnContext context) {
|
||||
return context.getServices().<CustomState>Get(PropertyName);
|
||||
return context.getTurnState().<CustomState>Get(PropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.microsoft.bot.connector.ExecutorFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static java.util.concurrent.CompletableFuture.completedFuture;
|
||||
|
||||
/**
|
||||
* Models IStorage around a dictionary
|
||||
*/
|
||||
public class DictionaryStorage implements Storage {
|
||||
private static ObjectMapper objectMapper;
|
||||
|
||||
// TODO: Object needs to be defined
|
||||
private final Map<String, Object> memory;
|
||||
private final Object syncroot = new Object();
|
||||
private int _eTag = 0;
|
||||
private final String typeNameForNonEntity = "__type_name_";
|
||||
|
||||
public DictionaryStorage() {
|
||||
this(null);
|
||||
}
|
||||
public DictionaryStorage(Map<String, Object> dictionary ) {
|
||||
DictionaryStorage.objectMapper = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.findAndRegisterModules();
|
||||
this.memory = (dictionary != null) ? dictionary : new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
public CompletableFuture Delete(String[] keys) {
|
||||
synchronized (this.syncroot) {
|
||||
for (String key : keys) {
|
||||
Object o = this.memory.get(key);
|
||||
this.memory.remove(o);
|
||||
}
|
||||
}
|
||||
return completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Map<String, ?>> Read(String[] keys) throws JsonProcessingException {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Map<String, Object> storeItems = new HashMap<String, Object>(keys.length);
|
||||
synchronized (this.syncroot) {
|
||||
for (String key : keys) {
|
||||
if (this.memory.containsKey(key)) {
|
||||
Object state = this.memory.get(key);
|
||||
if (state != null) {
|
||||
try {
|
||||
if (!(state instanceof JsonNode))
|
||||
throw new RuntimeException("DictionaryRead failed: entry not JsonNode");
|
||||
JsonNode stateNode = (JsonNode) state;
|
||||
// Check if type info is set for the class
|
||||
if (!(stateNode.hasNonNull(this.typeNameForNonEntity))) {
|
||||
throw new RuntimeException(String.format("DictionaryRead failed: Type info not present"));
|
||||
}
|
||||
String clsName = stateNode.get(this.typeNameForNonEntity).textValue();
|
||||
|
||||
// Load the class info
|
||||
Class<?> cls;
|
||||
try {
|
||||
cls = Class.forName(clsName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("DictionaryRead failed: Could not load class %s", clsName));
|
||||
}
|
||||
|
||||
// Populate dictionary
|
||||
storeItems.put(key,DictionaryStorage.objectMapper.treeToValue(stateNode, cls ));
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(String.format("DictionaryRead failed: %s", e.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return storeItems;
|
||||
}, ExecutorFactory.getExecutor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture Write(Map<String, ?> changes) throws Exception {
|
||||
synchronized (this.syncroot) {
|
||||
for (Map.Entry change : changes.entrySet()) {
|
||||
Object newValue = change.getValue();
|
||||
|
||||
String oldStateETag = null; // default(string);
|
||||
if (this.memory.containsValue(change.getKey())) {
|
||||
Map oldState = (Map) this.memory.get(change.getKey());
|
||||
if (oldState.containsValue("eTag")) {
|
||||
Map.Entry eTagToken = (Map.Entry) oldState.get("eTag");
|
||||
oldStateETag = (String) eTagToken.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
// Dictionary stores Key:JsonNode (with type information held within the JsonNode)
|
||||
JsonNode newState = DictionaryStorage.objectMapper.valueToTree(newValue);
|
||||
((ObjectNode)newState).put(this.typeNameForNonEntity, newValue.getClass().getTypeName());
|
||||
|
||||
// Set ETag if applicable
|
||||
if (newValue instanceof StoreItem) {
|
||||
StoreItem newStoreItem = (StoreItem) newValue;
|
||||
if(oldStateETag != null && newStoreItem.geteTag() != "*" &&
|
||||
newStoreItem.geteTag() != oldStateETag) {
|
||||
throw new Exception(String.format("Etag conflict.\r\n\r\nOriginal: %s\r\nCurrent: %s",
|
||||
newStoreItem.geteTag(), oldStateETag));
|
||||
}
|
||||
Integer newTag = _eTag++;
|
||||
((ObjectNode)newState).put("eTag", newTag.toString());
|
||||
}
|
||||
|
||||
this.memory.put((String)change.getKey(), newState);
|
||||
}
|
||||
}
|
||||
return completedFuture(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
package com.microsoft.bot.builder;
|
||||
|
||||
import com.microsoft.bot.builder.DictionaryStorage;
|
||||
|
||||
/**
|
||||
* RamStorage stores data in volative dictionary
|
||||
*/
|
||||
public class MemoryStorage extends DictionaryStorage {
|
||||
|
||||
public MemoryStorage() {
|
||||
super(null);
|
||||
}
|
||||
}
|
|
@ -61,11 +61,9 @@ public class MiddlewareSetTest extends TestBase
|
|||
public void NestedSet_OnReceive() throws Exception {
|
||||
final boolean[] wasCalled = {false};
|
||||
MiddlewareSet inner = new MiddlewareSet();
|
||||
inner.Use(new AnonymousReceiveMiddleware(new MiddlewareCall() {
|
||||
public void requestHandler(TurnContext tc, NextDelegate nd) throws Exception {
|
||||
wasCalled[0] = true;
|
||||
nd.next();
|
||||
}
|
||||
inner.Use(new AnonymousReceiveMiddleware((MiddlewareCall) (tc, nd) -> {
|
||||
wasCalled[0] = true;
|
||||
return nd.next();
|
||||
}));
|
||||
MiddlewareSet outer = new MiddlewareSet();
|
||||
outer.Use(inner);
|
||||
|
|
|
@ -36,7 +36,7 @@ public class SimpleAdapter extends BotAdapter {
|
|||
|
||||
|
||||
@Override
|
||||
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException {
|
||||
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
|
||||
Assert.assertNotNull("SimpleAdapter.deleteActivity: missing reference", activities);
|
||||
Assert.assertTrue("SimpleAdapter.sendActivities: empty activities array.", activities.length > 0);
|
||||
|
||||
|
@ -49,8 +49,6 @@ public class SimpleAdapter extends BotAdapter {
|
|||
}
|
||||
ResourceResponse[] result = new ResourceResponse[responses.size()];
|
||||
return responses.toArray(result);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
package com.microsoft.bot.schema;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* The status of a particular token.
|
||||
*/
|
||||
public class TokenStatus {
|
||||
/**
|
||||
* The channelId of the token status pertains to.
|
||||
*/
|
||||
@JsonProperty
|
||||
private String channelId;
|
||||
|
||||
/**
|
||||
* The name of the connection the token status pertains to.
|
||||
*/
|
||||
@JsonProperty
|
||||
private String connectionName;
|
||||
|
||||
/**
|
||||
* True if a token is stored for this ConnectionName.
|
||||
*/
|
||||
@JsonProperty
|
||||
private boolean hasToken;
|
||||
|
||||
/**
|
||||
* The display name of the service provider for which this Token belongs to.
|
||||
*/
|
||||
@JsonProperty
|
||||
private String serviceProviderDisplayName;
|
||||
|
||||
/**
|
||||
* Gets the channelId.
|
||||
* @return The channelId.
|
||||
*/
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channelId.
|
||||
* @param withChannelId The channelId.
|
||||
*/
|
||||
public void setChannelId(String withChannelId) {
|
||||
channelId = withChannelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the connectionName.
|
||||
* @return The connection name.
|
||||
*/
|
||||
public String getConnectionName() {
|
||||
return connectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the connectionName.
|
||||
* @param withConnectionName The connection name.
|
||||
*/
|
||||
public void setConnectionName(String withConnectionName) {
|
||||
connectionName = withConnectionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hasToken value.
|
||||
* @return The hasToken value.
|
||||
*/
|
||||
public boolean hasToken() {
|
||||
return hasToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hasToken value.
|
||||
* @param withHasToken The hasToken value.
|
||||
*/
|
||||
public void setHasToken(boolean withHasToken) {
|
||||
hasToken = withHasToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serviceProviderDisplayName field.
|
||||
* @return The service provider display name.
|
||||
*/
|
||||
public String getServiceProviderDisplayName() {
|
||||
return serviceProviderDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the serviceProviderDisplayName field.
|
||||
* @param withServiceProviderDisplayName The service provider display name.
|
||||
*/
|
||||
public void setServiceProviderDisplayName(String withServiceProviderDisplayName) {
|
||||
serviceProviderDisplayName = withServiceProviderDisplayName;
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче