This commit is contained in:
Tracy Boehrer 2019-09-26 17:21:08 -05:00
Родитель a708abdcf9
Коммит 08589b677f
35 изменённых файлов: 1028 добавлений и 400 удалений

Просмотреть файл

@ -13,19 +13,28 @@ 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.
* An implementation of the {@link Bot} interface intended for further subclassing.
* Derive from this class to plug in code to handle particular {@link 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.
* Called by the adapter (for example, a {@link BotFrameworkAdapter}) at runtime in order
* to process an inbound {@link Activity}.
*
* <p>This method calls other methods in this class based on the type of the activity to
* process, which allows a derived class to provide type-specific logic in a controlled way.</p>
*
* <p>In a derived class, override this method to add logic that applies to all activity types.
* Add logic to apply before the type-specific logic before the call to the base class
* {@link Bot#onTurn(TurnContext)} method.
* Add logic to apply after the type-specific logic after the call to the base class
* {@link Bot#onTurn(TurnContext)} method.</p>
*
* @param turnContext The context object for this turn. Provides information about the
* incoming activity, and other data needed to process the activity.
* @return
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<Void> onTurn(TurnContext turnContext) {
@ -57,11 +66,10 @@ public class ActivityHandler implements Bot {
}
/**
* Invoked when a message activity is received from the user when the base behavior of
* {@link #onTurn(TurnContext)} is used.
* <p>
* If overridden, this could potentially contain conversational logic.
* By default, this method does nothing.
* Override this in a derived class to provide logic specific to {@link ActivityTypes#MESSAGE}
* activities, such as the conversational logic.
*
* When the {@link #onTurn(TurnContext)} method receives a message activity, it calls this method.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -73,14 +81,15 @@ public class ActivityHandler implements Bot {
/**
* Invoked when a conversation update activity is received from the channel when the base behavior of
* {@link #onTurn(TurnContext)} is used.
* <p>
* Conversation update activities are useful when it comes to responding to users being added to or removed
* from the conversation.
* <p>
*
* 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 #onMembersAdded(List, TurnContext)} if any users have been added,
* or {@link #onMembersRemoved(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.
* By default, this method will call {@link #onMembersAdded(List, TurnContext)} if any users have
* been added or {@link #onMembersRemoved(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.
@ -106,15 +115,15 @@ public class ActivityHandler implements Bot {
}
/**
* Invoked when members other than this bot (like a user) are added to the conversation when the base behavior of
* {@link #onConversationUpdateActivity(TurnContext)} is used.
* <p>
* If overridden, this could potentially send a greeting message to the user instead of waiting for the user to
* send a message first.
* <p>
* By default, this method does nothing.
* Override this in a derived class to provide logic for when members other than the bot
* join the conversation, such as your bot's welcome logic.
*
* @param membersAdded A list of all the users that have been added in the conversation update.
* <p>When the {@link #onConversationUpdateActivity(TurnContext)} method receives a conversation
* update activity that indicates one or more users other than the bo are joining the conversation,
* it calls this method.</p>
*
* @param membersAdded A list of all the members added to the conversation, as described by
* the conversation update activity.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
@ -123,14 +132,15 @@ public class ActivityHandler implements Bot {
}
/**
* Invoked when members other than this bot (like a user) are removed from the conversation when the base
* behavior of {@link #onConversationUpdateActivity(TurnContext)} is used.
* <p>
* This method could optionally be overridden to perform actions related to users leaving a group conversation.
* <p>
* By default, this method does nothing.
* Override this in a derived class to provide logic for when members other than the bot
* leave the conversation, such as your bot's good-bye logic.
*
* @param membersRemoved A list of all the users that have been removed in the conversation update.
* <p>When the {@link #onConversationUpdateActivity(TurnContext)} method receives a conversation
* update activity that indicates one or more users other than the bot are leaving the conversation,
* it calls this method.</p>
*
* @param membersRemoved A list of all the members removed from the conversation, as described
* by the conversation update activity.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
@ -141,13 +151,22 @@ public class ActivityHandler implements Bot {
/**
* Invoked when an event activity is received from the connector when the base behavior of
* {@link #onTurn(TurnContext)} is used.
* <p>
* 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.
* <p>
* The activity that the message reaction corresponds to is indicated in the replyToId property.
*
* <p>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.</p>
*
* <p>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.
* bot as the response from a send call.</p>
*
* <p>When the {@link #onTurn(TurnContext)} method receives a message reaction activity, it calls this
* method. If the message reaction indicates that reactions were added to a message, it calls
* {@link #onReactionsAdded(List, TurnContext)}. If the message reaction indicates that reactions were
* removed from a message, it calls {@link #onReactionsRemoved(List, TurnContext)}.</p>
*
* <p>In a derived class, override this method to add logic that applies to all message reaction activities.
* Add logic to apply before the reactions added or removed logic before the call to the base class
* method. Add logic to apply after the reactions added or removed logic after the call to the base class.</p>
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -172,7 +191,15 @@ public class ActivityHandler implements Bot {
}
/**
* Called when there have been Reactions added that reference a previous Activity.
* Override this in a derived class to provide logic for when reactions to a previous activity
* are added to the conversation.
*
* <p>Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
* previously sent message on the conversation. Message reactions are supported by only a few channels.
* The activity that the message is in reaction to is identified by the activity's
* {@link Activity#getReplyToId()} property. The value of this property is the activity ID
* of a previously sent activity. When the bot sends an activity, the channel assigns an ID to it,
* which is available in the {@link com.microsoft.bot.schema.ResourceResponse#getId} of the result.</p>
*
* @param messageReactions The list of reactions added.
* @param turnContext The context object for this turn.
@ -184,7 +211,15 @@ public class ActivityHandler implements Bot {
}
/**
* Called when there have been Reactions removed that reference a previous Activity.
* Override this in a derived class to provide logic for when reactions to a previous activity
* are removed from the conversation.
*
* <p>Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
* previously sent message on the conversation. Message reactions are supported by only a few channels.
* The activity that the message is in reaction to is identified by the activity's
* {@link Activity#getReplyToId()} property. The value of this property is the activity ID
* of a previously sent activity. When the bot sends an activity, the channel assigns an ID to it,
* which is available in the {@link com.microsoft.bot.schema.ResourceResponse#getId} of the result.</p>
*
* @param messageReactions The list of reactions removed.
* @param turnContext The context object for this turn.
@ -198,12 +233,28 @@ public class ActivityHandler implements Bot {
/**
* Invoked when an event activity is received from the connector when the base behavior of
* {@link #onTurn(TurnContext)} is used.
* <p>
* Event activities can be used to communicate many different things.
* <p>
* By default, this method will call {@link #onTokenResponseEvent(TurnContext)} if the
*
* <p>Event activities can be used to communicate many different things.</p>
*
* <p>By default, this method will call {@link #onTokenResponseEvent(TurnContext)} if the
* activity's name is "tokens/response" or {@link #onEvent(TurnContext)} otherwise.
* "tokens/response" event can be triggered by an {@link com.microsoft.bot.schema.OAuthCard}.
* "tokens/response" event can be triggered by an {@link com.microsoft.bot.schema.OAuthCard}.</p>
*
* <p>When the {@link #onTurn(TurnContext)} method receives an event activity, it calls this method.</p>
*
* <p>If the event {@link Activity#getName} is `tokens/response`, it calls
* {@link #onTokenResponseEvent(TurnContext)} otherwise, it calls {@link #onEvent(TurnContext)}.</p>
*
* <p>In a derived class, override this method to add logic that applies to all event activities.
* Add logic to apply before the specific event-handling logic before the call to the base class
* method. Add logic to apply after the specific event-handling logic after the call to the base class
* method.</p>
*
* <p>Event activities communicate programmatic information from a client or channel to a bot.
* The meaning of an event activity is defined by the {@link Activity#getName} property,
* which is meaningful within the scope of a channel.
* A `tokens/response` event can be triggered by an {@link com.microsoft.bot.schema.OAuthCard} or
* an OAuth prompt.</p>
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -219,10 +270,14 @@ public class ActivityHandler implements Bot {
/**
* Invoked when a "tokens/response" event is received when the base behavior of
* {@link #onEventActivity(TurnContext)} is used.
* <p>
* If using an OAuthPrompt, override this method to forward this {@link Activity} to the current dialog.
* <p>
* By default, this method does nothing.
*
* <p>If using an OAuthPrompt, override this method to forward this {@link Activity} to the
* current dialog.</p>
*
* <p>By default, this method does nothing.</p>
*
* When the {@link #onEventActivity(TurnContext)} method receives an event with a {@link Activity#getName()}
* of `tokens/response`, it calls this method.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -234,10 +289,13 @@ public class ActivityHandler implements Bot {
/**
* Invoked when an event other than tokens/response is received when the base behavior of
* {@link #onEventActivity(TurnContext)} is used.
* <p>
* This method could optionally be overridden if the bot is meant to handle miscellaneous events.
* <p>
* By default, this method does nothing.
*
* <p>This method could optionally be overridden if the bot is meant to handle miscellaneous events.</p>
*
* <p>By default, this method does nothing.</p>
*
* When the {@link #onEventActivity(TurnContext)} method receives an event with a {@link Activity#getName()}
* other than `tokens/response`, it calls this method.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -250,11 +308,14 @@ public class ActivityHandler implements Bot {
* Invoked when an activity other than a message, conversation update, or event is received
* when the base behavior of {@link #onTurn(TurnContext)} is used.
*
* If overridden, this could potentially respond to any of the other activity types like
* <p>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}.
* <p>
* By default, this method does nothing.
* {@link com.microsoft.bot.schema.ActivityTypes#END_OF_CONVERSATION}.</p>
*
* <p>By default, this method does nothing.</p>
*
* <p>When the {@link #onTurn(TurnContext)} method receives an activity that is not a message,
* conversation update, message reaction, or event activity, it calls this method.</p>
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.

Просмотреть файл

@ -10,24 +10,53 @@ import java.util.concurrent.CompletableFuture;
* Middleware to automatically call .SaveChanges() at the end of the turn for all BotState class it is managing.
*/
public class AutoSaveStateMiddleware implements Middleware {
/**
* The list of state management objects managed by this object.
*/
private BotStateSet botStateSet;
public AutoSaveStateMiddleware(BotState ... botStates) {
/**
* Initializes a new instance of the AutoSaveStateMiddleware class.
*
* @param botStates Initial list of {@link BotState} objects to manage.
*/
public AutoSaveStateMiddleware(BotState... botStates) {
botStateSet = new BotStateSet(Arrays.asList(botStates));
}
/**
* Initializes a new instance of the AutoSaveStateMiddleware class.
*
* @param withBotStateSet Initial {@link BotStateSet} object to manage.
*/
public AutoSaveStateMiddleware(BotStateSet withBotStateSet) {
botStateSet = withBotStateSet;
}
/**
* Gets the list of state management objects managed by this object.
*
* @return The state management objects managed by this object.
*/
public BotStateSet getBotStateSet() {
return botStateSet;
}
/**
* Gets the list of state management objects managed by this object.
*
* @param withBotStateSet The state management objects managed by this object.
*/
public void setBotStateSet(BotStateSet withBotStateSet) {
botStateSet = withBotStateSet;
}
/**
* Add a BotState to the list of sources to load.
*
* @param botState botState to manage.
* @return botstateset for chaining more .use().
*/
public AutoSaveStateMiddleware add(BotState botState) {
if (botState == null) {
throw new IllegalArgumentException("botState cannot be null");

Просмотреть файл

@ -5,13 +5,11 @@ package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationReference;
import com.microsoft.bot.schema.ConversationReferenceHelper;
import com.microsoft.bot.schema.ResourceResponse;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
/**
* Represents a bot adapter that can connect a bot to a service endpoint.
@ -36,7 +34,7 @@ public abstract class BotAdapter {
/**
* The collection of middleware in the adapter's pipeline.
*/
protected final MiddlewareSet middlewareSet = new MiddlewareSet();
private final MiddlewareSet middlewareSet = new MiddlewareSet();
/**
* Error handler that can catch exceptions in the middleware or application.
@ -44,20 +42,32 @@ public abstract class BotAdapter {
private OnTurnErrorHandler onTurnError;
/**
* Creates a default adapter.
* Gets the error handler that can catch exceptions in the middleware or application.
*
* @return An error handler that can catch exceptions in the middleware or application.
*/
public BotAdapter() {
super();
}
public OnTurnErrorHandler getOnTurnError() {
return onTurnError;
}
/**
* Sets the error handler that can catch exceptions in the middleware or application.
*
* @param withTurnError An error handler that can catch exceptions in the middleware or application.
*/
public void setOnTurnError(OnTurnErrorHandler withTurnError) {
onTurnError = withTurnError;
}
/**
* Gets the collection of middleware in the adapter's pipeline.
*
* @return The middleware collection for the pipeline.
*/
protected MiddlewareSet getMiddlewareSet() {
return middlewareSet;
}
/**
* Adds middleware to the adapter's pipeline.
*
@ -82,7 +92,8 @@ public abstract class BotAdapter {
* the receiving channel assigned to the activities.
* {@link TurnContext#onSendActivities(SendActivitiesHandler)}
*/
public abstract CompletableFuture<ResourceResponse[]> sendActivities(TurnContext context, List<Activity> activities);
public abstract CompletableFuture<ResourceResponse[]> sendActivities(TurnContext context,
List<Activity> activities);
/**
* When overridden in a derived class, replaces an existing activity in the
@ -98,7 +109,8 @@ public abstract class BotAdapter {
* of the activity to replace.</p>
* {@link TurnContext#onUpdateActivity(UpdateActivityHandler)}
*/
public abstract CompletableFuture<ResourceResponse> updateActivity(TurnContext context, Activity activity);
public abstract CompletableFuture<ResourceResponse> updateActivity(TurnContext context,
Activity activity);
/**
* When overridden in a derived class, deletes an existing activity in the
@ -113,7 +125,6 @@ public abstract class BotAdapter {
*/
public abstract CompletableFuture<Void> deleteActivity(TurnContext context, ConversationReference reference);
/**
* Starts activity processing for the current bot turn.
*
@ -121,7 +132,7 @@ public abstract class BotAdapter {
* The adapter passes in the context object for the turn and a next delegate,
* and the middleware calls the delegate to pass control to the next middleware
* in the pipeline. Once control reaches the end of the pipeline, the adapter calls
* the {@code callback} method. If a middleware component doesnt call
* the {@code callback} method. If a middleware component does not call
* the next delegate, the adapter does not call any of the subsequent middlewares
* {@link Middleware#onTurn(TurnContext, NextDelegate)}
* methods or the callback method, and the pipeline short circuits.
@ -163,24 +174,22 @@ public abstract class BotAdapter {
/**
* Sends a proactive message to a conversation.
*
* @param botId The application ID of the bot. This paramter is ignored in
* single tenant the Adpters (Console, Test, etc) but is critical to the BotFrameworkAdapter
* @param botId The application ID of the bot. This parameter is ignored in
* single tenant the Adapters (Console, Test, etc) but is critical to the BotFrameworkAdapter
* which is multi-tenant aware.
* @param reference A reference to the conversation to continue.
* @param callback The method to call for the resulting bot turn.
* @return A task that represents the work queued to execute.
* Call this method to proactively send a message to a conversation.
* Most channels require a user to initaiate a conversation with a bot
* Most channels require a user to initiate a conversation with a bot
* before the bot can send activities to the user.
* {@linkalso RunPipeline(TurnContext, Func { TurnContext, Task })}
*
* {@link #runPipeline(TurnContext, BotCallbackHandler)}
*/
public CompletableFuture<Void> continueConversation(String botId,
ConversationReference reference,
BotCallbackHandler callback) {
ConversationReferenceHelper conv = new ConversationReferenceHelper(reference);
Activity activity = conv.getPostToBotMessage();
return runPipeline(new TurnContextImpl(this, activity), callback);
return runPipeline(new TurnContextImpl(this, reference.getContinuationActivity()), callback);
}
}

Просмотреть файл

@ -6,6 +6,7 @@ package com.microsoft.bot.builder;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.BaseEncoding;
import com.microsoft.bot.builder.integration.AdapterIntegration;
import com.microsoft.bot.connector.*;
import com.microsoft.bot.connector.authentication.*;
import com.microsoft.bot.connector.rest.RestConnectorClient;
@ -31,6 +32,7 @@ import static java.util.concurrent.CompletableFuture.completedFuture;
/**
* A bot adapter that can connect a bot to a service endpoint.
*
* The bot adapter encapsulates authentication processes and sends
* 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
@ -47,15 +49,35 @@ import static java.util.concurrent.CompletableFuture.completedFuture;
* {@link Bot}
* {@link Middleware}
*/
public class BotFrameworkAdapter extends BotAdapter {
private final static String InvokeResponseKey = "BotFrameworkAdapter.InvokeResponse";
private final static String BotIdentityKey = "BotIdentity";
public class BotFrameworkAdapter extends BotAdapter implements AdapterIntegration, UserTokenProvider {
private static final String INVOKE_RESPONSE_KEY = "BotFrameworkAdapter.InvokeResponse";
private static final String BOT_IDENTITY_KEY = "BotIdentity";
private static final String CONNECTOR_CLIENT_KEY = "ConnectorClient";
/**
* The credential provider.
*/
private final CredentialProvider credentialProvider;
/**
* The channel provider.
*/
private ChannelProvider channelProvider;
/**
* The authentication configuration.
*/
private AuthenticationConfiguration authConfiguration;
/**
* Rest RetryStrategy.
*/
private final RetryStrategy connectorClientRetryStrategy;
private Map<String, MicrosoftAppCredentials> appCredentialMap = new ConcurrentHashMap<String, MicrosoftAppCredentials>();
/**
* AppCredentials dictionary.
*/
private Map<String, MicrosoftAppCredentials> appCredentialMap = new ConcurrentHashMap<>();
/**
* Initializes a new instance of the {@link BotFrameworkAdapter} class,
@ -173,11 +195,11 @@ public class BotFrameworkAdapter extends BotAdapter {
}};
ClaimsIdentity claimsIdentity = new ClaimsIdentity("ExternalBearer", claims);
context.getTurnState().add(TurnContextStateNames.BOT_IDENTITY, claimsIdentity);
context.getTurnState().add(BOT_IDENTITY_KEY, claimsIdentity);
return createConnectorClient(reference.getServiceUrl(), claimsIdentity)
.thenCompose(connectorClient -> {
context.getTurnState().add(TurnContextStateNames.CONNECTOR_CLIENT, connectorClient);
context.getTurnState().add(CONNECTOR_CLIENT_KEY, connectorClient);
return runPipeline(context, callback);
});
}
@ -192,7 +214,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return The updated adapter object.
*/
public BotFrameworkAdapter use(Middleware middleware) {
super.middlewareSet.use(middleware);
getMiddlewareSet().use(middleware);
return this;
}
@ -213,8 +235,8 @@ public class BotFrameworkAdapter extends BotAdapter {
BotCallbackHandler callback) {
BotAssert.activityNotNull(activity);
return JwtTokenValidation.authenticateRequest(activity,
authHeader, credentialProvider, channelProvider, authConfiguration)
return JwtTokenValidation.authenticateRequest(
activity, authHeader, credentialProvider, channelProvider, authConfiguration)
.thenCompose(claimsIdentity -> processActivity(claimsIdentity, activity, callback));
}
@ -237,21 +259,21 @@ public class BotFrameworkAdapter extends BotAdapter {
BotAssert.activityNotNull(activity);
TurnContextImpl context = new TurnContextImpl(this, activity);
context.getTurnState().add(TurnContextStateNames.BOT_IDENTITY, identity);
context.getTurnState().add(BOT_IDENTITY_KEY, identity);
return createConnectorClient(activity.getServiceUrl(), identity)
// run pipeline
.thenCompose(connectorClient -> {
context.getTurnState().add(TurnContextStateNames.CONNECTOR_CLIENT, connectorClient);
context.getTurnState().add(CONNECTOR_CLIENT_KEY, connectorClient);
return 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.
.thenCompose(result -> {
// Handle Invoke scenarios, which deviate from the request/response model in that
// the Bot will return a specific body and return code.
if (activity.isType(ActivityTypes.INVOKE)) {
Activity invokeResponse = context.getTurnState().get(InvokeResponseKey);
Activity invokeResponse = context.getTurnState().get(INVOKE_RESPONSE_KEY);
if (invokeResponse == null) {
throw new IllegalStateException("Bot failed to return a valid 'invokeResponse' activity.");
} else {
@ -311,22 +333,20 @@ public class BotFrameworkAdapter extends BotAdapter {
try {
Thread.sleep(delayMs);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//await(Task.Delay(delayMs));
// No need to create a response. One will be created below.
} else if (activity.isType(ActivityTypes.INVOKE_RESPONSE)) {
context.getTurnState().add(InvokeResponseKey, activity);
context.getTurnState().add(INVOKE_RESPONSE_KEY, activity);
// No need to create a response. One will be created below.
} else if (activity.isType(ActivityTypes.TRACE)
&& !StringUtils.equals(activity.getChannelId(), Channels.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.getTurnState().get(
TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
response = connectorClient.getConversations().replyToActivity(activity).join();
} else {
ConnectorClient connectorClient = context.getTurnState().get(
TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
response = connectorClient.getConversations().sendToConversation(activity).join();
}
@ -365,7 +385,7 @@ public class BotFrameworkAdapter extends BotAdapter {
*/
@Override
public CompletableFuture<ResourceResponse> updateActivity(TurnContext context, Activity activity) {
ConnectorClient connectorClient = context.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
return connectorClient.getConversations().updateActivity(activity);
}
@ -379,7 +399,7 @@ public class BotFrameworkAdapter extends BotAdapter {
*/
@Override
public CompletableFuture<Void> deleteActivity(TurnContext context, ConversationReference reference) {
ConnectorClient connectorClient = context.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
return connectorClient.getConversations().deleteActivity(
reference.getConversation().getId(), reference.getActivityId());
}
@ -402,7 +422,7 @@ public class BotFrameworkAdapter extends BotAdapter {
"BotFrameworkAdapter.deleteConversationMember(): missing conversation.id");
}
ConnectorClient connectorClient = context.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
String conversationId = context.getActivity().getConversation().getId();
return connectorClient.getConversations().deleteConversationMember(conversationId, memberId);
}
@ -438,7 +458,7 @@ public class BotFrameworkAdapter extends BotAdapter {
throw new IllegalArgumentException("BotFrameworkAdapter.GetActivityMembers(): missing conversation.id");
}
ConnectorClient connectorClient = context.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
String conversationId = context.getActivity().getConversation().getId();
return connectorClient.getConversations().getActivityMembers(conversationId, activityId);
@ -459,7 +479,7 @@ public class BotFrameworkAdapter extends BotAdapter {
throw new IllegalArgumentException("BotFrameworkAdapter.GetActivityMembers(): missing conversation.id");
}
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
String conversationId = context.getActivity().getConversation().getId();
return connectorClient.getConversations().getConversationMembers(conversationId);
@ -532,7 +552,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return List of Members of the current conversation
*/
public CompletableFuture<ConversationsResult> getConversations(TurnContextImpl context) {
ConnectorClient connectorClient = context.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
return connectorClient.getConversations().getConversations();
}
@ -550,7 +570,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return List of Members of the current conversation
*/
public CompletableFuture<ConversationsResult> getConversations(TurnContextImpl context, String continuationToken) {
ConnectorClient connectorClient = context.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
ConnectorClient connectorClient = context.getTurnState().get(CONNECTOR_CLIENT_KEY);
return connectorClient.getConversations().getConversations(continuationToken);
}
@ -562,7 +582,8 @@ public class BotFrameworkAdapter extends BotAdapter {
* @param magicCode (Optional) Optional user entered code to validate.
* @return Token Response
*/
public CompletableFuture<TokenResponse> getUserToken(TurnContextImpl context, String connectionName, String magicCode) {
@Override
public CompletableFuture<TokenResponse> getUserToken(TurnContext context, String connectionName, String magicCode) {
BotAssert.contextNotNull(context);
if (context.getActivity().getFrom() == null || StringUtils.isEmpty(context.getActivity().getFrom().getId())) {
@ -594,7 +615,8 @@ public class BotFrameworkAdapter extends BotAdapter {
* @param connectionName Name of the auth connection to use.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<String> getOauthSignInLink(TurnContextImpl context, String connectionName) {
@Override
public CompletableFuture<String> getOauthSignInLink(TurnContext context, String connectionName) {
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(connectionName)) {
throw new IllegalArgumentException("connectionName");
@ -642,9 +664,15 @@ public class BotFrameworkAdapter extends BotAdapter {
* @param context 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
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<String> getOauthSignInLink(TurnContextImpl context, String connectionName, String userId) {
@Override
public CompletableFuture<String> getOauthSignInLink(TurnContext context,
String connectionName,
String userId,
String finalRedirect) {
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(connectionName)) {
throw new IllegalArgumentException("connectionName");
@ -699,7 +727,8 @@ public class BotFrameworkAdapter extends BotAdapter {
* @param connectionName Name of the auth connection to use.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> signOutUser(TurnContextImpl context, String connectionName) {
@Override
public CompletableFuture<Void> signOutUser(TurnContext context, String connectionName, String userId) {
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(connectionName)) {
throw new IllegalArgumentException("connectionName");
@ -708,8 +737,8 @@ public class BotFrameworkAdapter extends BotAdapter {
CompletableFuture<Void> result = new CompletableFuture<>();
try {
ConnectorClient client = getOrCreateConnectorClient(context.getActivity().getServiceUrl());
// TODO: signoutUser
//ConnectorClient client = getOrCreateConnectorClient(context.getActivity().getServiceUrl());
//return client.signOutUser(context.getActivity().getFrom().getId(), connectionName);
result.completeExceptionally(new NotImplementedException("signOutUser"));
return result;
@ -724,11 +753,20 @@ public class BotFrameworkAdapter extends BotAdapter {
*
* @param context Context for the current turn of conversation with the user.
* @param userId The user Id for which token status is retrieved.
* @param includeFilter
* @return Array of {@link TokenStatus}.
*/
public CompletableFuture<TokenStatus[]> getTokenStatus(TurnContext context, String userId) {
@Override
public CompletableFuture<TokenStatus[]> getTokenStatus(TurnContext context, String userId, String includeFilter) {
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(userId)) {
throw new IllegalArgumentException("userId");
}
// TODO: getTokenStatus
throw new NotImplementedException("getTokenStatus");
CompletableFuture<TokenStatus[]> result = new CompletableFuture<>();
result.completeExceptionally(new NotImplementedException("getTokenStatus"));
return result;
}
/**
@ -737,13 +775,35 @@ public class BotFrameworkAdapter extends BotAdapter {
* @param context 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 TurnContext.
* @return Map of resourceUrl to the corresponding {@link TokenResponse}.
*/
@Override
public CompletableFuture<Map<String, TokenResponse>> getAadTokens(TurnContext context,
String connectionName,
String[] resourceUrls) {
String[] resourceUrls,
String userId) {
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(connectionName)) {
throw new IllegalArgumentException("connectionName");
}
if (resourceUrls == null) {
throw new IllegalArgumentException("resourceUrls");
}
String effectiveUserId = userId;
if (StringUtils.isEmpty(effectiveUserId)) {
if (context.getActivity() != null && context.getActivity().getFrom() != null) {
effectiveUserId = context.getActivity().getFrom().getId();
}
}
// TODO: getAadTokens
throw new NotImplementedException("getAadTokens");
CompletableFuture<Map<String, TokenResponse>> result = new CompletableFuture<>();
result.completeExceptionally(new NotImplementedException("getAadTokens"));
return result;
}
/**
@ -772,11 +832,9 @@ public class BotFrameworkAdapter extends BotAdapter {
MicrosoftAppCredentials credentials,
ConversationParameters conversationParameters,
BotCallbackHandler callback) {
// Validate serviceUrl - can throw
// TODO: all these joins are gross
return CompletableFuture.supplyAsync(() -> {
//URI uri = new URI(serviceUrl);
ConnectorClient connectorClient = null;
ConnectorClient connectorClient;
try {
connectorClient = getOrCreateConnectorClient(serviceUrl, credentials);
} catch (Throwable t) {
@ -795,7 +853,9 @@ public class BotFrameworkAdapter extends BotAdapter {
eventActivity.setId((conversationResourceResponse.getActivityId() != null)
? conversationResourceResponse.getActivityId()
: UUID.randomUUID().toString());
eventActivity.setConversation(new ConversationAccount(conversationResourceResponse.getId()));
eventActivity.setConversation(new ConversationAccount(conversationResourceResponse.getId()) {{
setTenantId(conversationParameters.getTenantId());
}});
eventActivity.setRecipient(conversationParameters.getBot());
TurnContextImpl context = new TurnContextImpl(this, eventActivity);
@ -807,8 +867,8 @@ public class BotFrameworkAdapter extends BotAdapter {
}};
ClaimsIdentity claimsIdentity = new ClaimsIdentity("anonymous", claims);
context.getTurnState().add(TurnContextStateNames.BOT_IDENTITY, claimsIdentity);
context.getTurnState().add(TurnContextStateNames.CONNECTOR_CLIENT, connectorClient);
context.getTurnState().add(BOT_IDENTITY_KEY, claimsIdentity);
context.getTurnState().add(CONNECTOR_CLIENT_KEY, connectorClient);
return runPipeline(context, callback).join();
}, ExecutorFactory.getExecutor());
@ -848,7 +908,6 @@ public class BotFrameworkAdapter extends BotAdapter {
}
if (!StringUtils.isEmpty(reference.getConversation().getTenantId())) {
// TODO: Not sure this is doing the same as dotnet. Test.
// Putting tenantId in channelData is a temporary solution while we wait for the Teams API to be updated
conversationParameters.setChannelData(new Object() {
private String tenantId;
@ -867,29 +926,31 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return An OAuth client for the bot.
*/
protected CompletableFuture<OAuthClient> createOAuthClient(TurnContext turnContext) {
return CompletableFuture.supplyAsync(() -> {
if (!OAuthClientConfig.emulateOAuthCards
&& StringUtils.equalsIgnoreCase(turnContext.getActivity().getChannelId(), Channels.EMULATOR)
&& credentialProvider.isAuthenticationDisabled().join()) {
if (!OAuthClientConfig.emulateOAuthCards
&& StringUtils.equalsIgnoreCase(turnContext.getActivity().getChannelId(), Channels.EMULATOR)
&& credentialProvider.isAuthenticationDisabled().join()) {
OAuthClientConfig.emulateOAuthCards = true;
}
OAuthClientConfig.emulateOAuthCards = true;
}
ConnectorClient connectorClient = turnContext.getTurnState().get(TurnContextStateNames.CONNECTOR_CLIENT);
if (connectorClient == null) {
throw new RuntimeException("An ConnectorClient is required in TurnState for this operation.");
}
ConnectorClient connectorClient = turnContext.getTurnState().get(CONNECTOR_CLIENT_KEY);
if (connectorClient == null) {
CompletableFuture<OAuthClient> result = new CompletableFuture<>();
result.completeExceptionally(
new RuntimeException("An ConnectorClient is required in TurnState for this operation."));
return result;
}
if (OAuthClientConfig.emulateOAuthCards) {
// do not join task - we want this to run in the background.
OAuthClient oAuthClient = new RestOAuthClient(
turnContext.getActivity().getServiceUrl(), connectorClient.getRestClient().credentials());
OAuthClientConfig.sendEmulateOAuthCards(oAuthClient, OAuthClientConfig.emulateOAuthCards);
return oAuthClient;
}
if (OAuthClientConfig.emulateOAuthCards) {
// do not join task - we want this to run in the background.
OAuthClient oAuthClient = new RestOAuthClient(
turnContext.getActivity().getServiceUrl(), connectorClient.getRestClient().credentials());
return OAuthClientConfig.sendEmulateOAuthCards(oAuthClient, OAuthClientConfig.emulateOAuthCards)
.thenApply(result -> oAuthClient);
}
return new RestOAuthClient(OAuthClientConfig.OAUTHENDPOINT, connectorClient.getRestClient().credentials());
});
return CompletableFuture.completedFuture(
new RestOAuthClient(OAuthClientConfig.OAUTHENDPOINT, connectorClient.getRestClient().credentials()));
}
/**
@ -922,12 +983,12 @@ public class BotFrameworkAdapter extends BotAdapter {
// For anonymous requests (requests with no header) appId is not set in claims.
Map.Entry<String, String> botAppIdClaim = claimsIdentity.claims().entrySet().stream()
.filter(claim -> claim.getKey() == AuthenticationConstants.AUDIENCE_CLAIM)
.filter(claim -> StringUtils.equals(claim.getKey(), AuthenticationConstants.AUDIENCE_CLAIM))
.findFirst()
.orElse(null);
if (botAppIdClaim == null) {
botAppIdClaim = claimsIdentity.claims().entrySet().stream()
.filter(claim -> claim.getKey() == AuthenticationConstants.APPID_CLAIM)
.filter(claim -> StringUtils.equals(claim.getKey(), AuthenticationConstants.APPID_CLAIM))
.findFirst()
.orElse(null);
}
@ -957,7 +1018,7 @@ public class BotFrameworkAdapter extends BotAdapter {
private ConnectorClient getOrCreateConnectorClient(String serviceUrl, MicrosoftAppCredentials appCredentials)
throws MalformedURLException, URISyntaxException {
RestConnectorClient connectorClient = null;
RestConnectorClient connectorClient;
if (appCredentials != null) {
connectorClient = new RestConnectorClient(
new URI(serviceUrl).toURL().toString(), appCredentials);
@ -989,6 +1050,9 @@ public class BotFrameworkAdapter extends BotAdapter {
return CompletableFuture.completedFuture(appCredentialMap.get(appId));
}
// If app credentials were provided, use them as they are the preferred choice moving forward
//TODO use AppCredentials
return credentialProvider.getAppPassword(appId)
.thenApply(appPassword -> {
MicrosoftAppCredentials appCredentials = new MicrosoftAppCredentials(appId, appPassword);

Просмотреть файл

@ -15,17 +15,30 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
/**
* Reads and writes state for your bot to storage.
* Defines a state management object and automates the reading and writing of associated state
* properties to a storage layer.
*
* <p>Each state management object defines a scope for a storage layer. State properties are
* created within a state management scope, and the Bot Framework defines these scopes:
* {@link ConversationState}, {@link UserState}, and {@link PrivateConversationState}.
* You can define additional scopes for your bot.</p>
*/
public abstract class BotState implements PropertyManager {
/**
* The key for the state cache.
*/
private String contextServiceKey;
/**
* The storage layer this state management object will use.
*/
private Storage storage;
/**
* Initializes a new instance of the BotState class.
*
* @param withStorage The storage provider to use.
* @param withContextServiceKey The key for caching on the context services dictionary.
* @param withContextServiceKey The key for the state cache for this BotState.
*/
public BotState(Storage withStorage, String withContextServiceKey) {
if (withStorage == null) {
@ -40,11 +53,12 @@ public abstract class BotState implements PropertyManager {
}
/**
* Create a property definition and register it with this BotState.
* Creates a named state property within the scope of a BotState and returns
* an accessor for the property.
*
* @param name name of property.
* @param <T> type of property.
* @return The created state property accessor.
* @return A {@link StatePropertyAccessor} for the property.
*/
public <T extends Object> StatePropertyAccessor<T> createProperty(String name) {
if (StringUtils.isEmpty(name)) {
@ -55,7 +69,7 @@ public abstract class BotState implements PropertyManager {
}
/**
* Reads in the current state object and caches it in the context object for this turn.
* Populates the state cache for this BotState from the storage layer.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -68,7 +82,8 @@ public abstract class BotState implements PropertyManager {
* Reads in the current state object and caches it in the context object for this turn.
*
* @param turnContext The context object for this turn.
* @param force True to bypass the cache.
* @param force true to overwrite any existing state cache; or false to load state from
* storage only if the cache doesn't already exist.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> load(TurnContext turnContext, boolean force) {
@ -90,8 +105,7 @@ public abstract class BotState implements PropertyManager {
}
/**
* If it has changed, writes to storage the state object that is cached in the current
* context object for this turn.
* Writes the state cache for this BotState to the storage layer.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -101,11 +115,11 @@ public abstract class BotState implements PropertyManager {
}
/**
* If it has changed, writes to storage the state object that is cached in the current
* context object for this turn.
* Writes the state cache for this BotState to the storage layer.
*
* @param turnContext The context object for this turn.
* @param force True to save state to storage whether or not there are changes.
* @param force true to save the state cache to storage; or false to save state to storage
* only if a property in the cache has changed.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> saveChanges(TurnContext turnContext, boolean force) {
@ -131,7 +145,11 @@ public abstract class BotState implements PropertyManager {
}
/**
* Clears any state currently stored in this state scope.
* Clears the state cache for this BotState.
*
* <p>This method clears the state cache in the turn context. Call
* {@link #saveChanges(TurnContext, boolean)} to persist this
* change in the storage layer.</p>
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
@ -169,7 +187,7 @@ public abstract class BotState implements PropertyManager {
}
/**
* Returns a copy of the raw cached data from the TurnContext, this can be used for tracing scenarios.
* Gets a copy of the raw cached data for this BotState from the turn context.
*
* @param turnContext The context object for this turn.
* @return A JSON representation of the cached state.
@ -185,7 +203,8 @@ public abstract class BotState implements PropertyManager {
}
/**
* When overridden in a derived class, gets the key to use when reading and writing state to and from storage.
* 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.
@ -193,12 +212,13 @@ public abstract class BotState implements PropertyManager {
public abstract String getStorageKey(TurnContext turnContext);
/**
* Gets a property from the state cache in the turn context.
* Gets the value of a property from the state cache for this BotState.
*
* @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.
* @return A task that represents the work queued to execute. If the task is successful, the
* result contains the property value.
*/
protected <T> CompletableFuture<T> getPropertyValue(TurnContext turnContext,
String propertyName) {
@ -215,7 +235,7 @@ public abstract class BotState implements PropertyManager {
}
/**
* Deletes a property from the state cache in the turn context.
* Deletes a property from the state cache for this BotState.
*
* @param turnContext The context object for this turn.
* @param propertyName The name of the property to delete.
@ -236,7 +256,7 @@ public abstract class BotState implements PropertyManager {
}
/**
* Set the value of a property in the state cache in the turn context.
* Sets the value of a property in the state cache for this BotState.
*
* @param turnContext The context object for this turn.
* @param propertyName The name of the property to set.
@ -263,8 +283,19 @@ public abstract class BotState implements PropertyManager {
* Internal cached bot state.
*/
private static class CachedBotState {
/**
* In memory cache of BotState properties.
*/
private Map<String, Object> state;
/**
* Used to compute the hash of the state.
*/
private String hash;
/**
* Object-JsonNode converter.
*/
private ObjectMapper mapper = new ObjectMapper();
public CachedBotState() {
@ -310,9 +341,9 @@ public abstract class BotState implements PropertyManager {
}
/**
* Implements IPropertyAccessor for an IPropertyContainer.
* <p>
* Note the semantic of this accessor are intended to be lazy, this means teh Get, Set and Delete
* Implements StatePropertyAccessor for an PropertyContainer.
*
* <p>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.
@ -320,16 +351,30 @@ public abstract class BotState implements PropertyManager {
* @param <T> type of value the propertyAccessor accesses.
*/
private static class BotStatePropertyAccessor<T> implements StatePropertyAccessor<T> {
/**
* The name of the property.
*/
private String name;
/**
* The parent BotState.
*/
private BotState botState;
/**
* StatePropertyAccessor constructor.
*
* @param withState The parent BotState.
* @param withName The property name.
*/
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.
* Get the property value. The semantics are intended to be lazy, note the use of
* {@link BotState#load(TurnContext)} 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
@ -356,7 +401,8 @@ public abstract class BotState implements PropertyManager {
}
/**
* Delete the property. The semantics are intended to be lazy, note the use of LoadAsync at the start.
* Delete the property. The semantics are intended to be lazy, note the use of
* {@link BotState#load(TurnContext)} at the start.
*
* @param turnContext The turn context.
* @return A task that represents the work queued to execute.
@ -368,7 +414,8 @@ public abstract class BotState implements PropertyManager {
}
/**
* Set the property value. The semantics are intended to be lazy, note the use of LoadAsync at the start.
* Set the property value. The semantics are intended to be lazy, note the use of
* {@link BotState#load(TurnContext)} at the start.
*
* @param turnContext The turn context.
* @param value The value to set.
@ -395,7 +442,6 @@ public abstract class BotState implements PropertyManager {
*
* @param withName Name of the property.
*/
@Override
public void setName(String withName) {
name = withName;
}

Просмотреть файл

@ -5,17 +5,23 @@ package com.microsoft.bot.builder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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 {
/**
* List of BotStates managed by this BotStateSet.
*/
private List<BotState> botStates = new ArrayList<>();
/**
* Initializes a new instance of the BotStateSet class.
*
* @param withBotStates vArgs list of {@link BotState} objects to manage.
*/
public BotStateSet(BotState... withBotStates) {
this(Arrays.asList(withBotStates));
}
@ -54,6 +60,10 @@ public class BotStateSet {
* @return The updated BotStateSet, so you can fluently call add(BotState) multiple times.
*/
public BotStateSet add(BotState botState) {
if (botState == null) {
throw new IllegalArgumentException("botState");
}
botStates.add(botState);
return this;
}
@ -76,11 +86,8 @@ public class BotStateSet {
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> loadAll(TurnContext turnContext, boolean force) {
List<CompletableFuture<Void>> loadFutures = botStates.stream()
.map(future -> future.load(turnContext, force))
.collect(Collectors.toList());
return CompletableFuture.allOf(loadFutures.toArray(new CompletableFuture[loadFutures.size()]));
return CompletableFuture.allOf(botStates.stream()
.map(future -> future.load(turnContext, force)).toArray(CompletableFuture[]::new));
}
/**
@ -101,10 +108,7 @@ public class BotStateSet {
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> saveAllChanges(TurnContext turnContext, boolean force) {
List<CompletableFuture<Void>> saveFutures = botStates.stream()
.map(botState -> botState.saveChanges(turnContext, force))
.collect(Collectors.toList());
return CompletableFuture.allOf(saveFutures.toArray(new CompletableFuture[saveFutures.size()]));
return CompletableFuture.allOf(botStates.stream()
.map(botState -> botState.saveChanges(turnContext, force)).toArray(CompletableFuture[]::new));
}
}

Просмотреть файл

@ -41,6 +41,7 @@ public interface BotTelemetryClient {
* @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.
*/
@SuppressWarnings("checkstyle:ParameterNumber")
void trackAvailability(String name,
OffsetDateTime timeStamp,
Duration duration,
@ -65,6 +66,7 @@ public interface BotTelemetryClient {
* @param resultCode Result code of dependency call execution.
* @param success True if the dependency call was handled successfully.
*/
@SuppressWarnings("checkstyle:ParameterNumber")
void trackDependency(String dependencyTypeName,
String target,
String dependencyName,

Просмотреть файл

@ -6,16 +6,24 @@ package com.microsoft.bot.builder;
import org.apache.commons.lang3.StringUtils;
/**
* Handles persistence of a conversation state object using the conversation ID as part of the key.
* Defines a state management object for conversation state.
*/
public class ConversationState extends BotState {
/**
* Creates a new {@link ConversationState} object.
*
* @param withStorage The storage layer to use.
*/
public ConversationState(Storage withStorage) {
super(withStorage, ConversationState.class.getSimpleName());
}
/**
* 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) {

Просмотреть файл

@ -15,82 +15,118 @@ import java.util.concurrent.CompletableFuture;
* A TurnContext that wraps an untyped inner TurnContext.
*/
public class DelegatingTurnContext implements TurnContext {
/**
* The TurnContext being wrapped.
*/
private TurnContext innerTurnContext;
/**
* Initializes a new instance of the DelegatingTurnContext class.
* @param withTurnContext The TurnContext to wrap.
*/
public DelegatingTurnContext(TurnContext withTurnContext) {
innerTurnContext = withTurnContext;
}
/**
* Gets the inner context's activity.
* @return The inner {@link TurnContext#getAdapter()}.
*/
@Override
public BotAdapter getAdapter() {
return innerTurnContext.getAdapter();
}
/**
* Gets the inner context's activity.
* @return The inner {@link TurnContext#getTurnState()}.
*/
@Override
public TurnContextStateCollection getTurnState() {
return innerTurnContext.getTurnState();
}
/**
* Gets the inner context's activity.
* @return The inner {@link TurnContext#getActivity()}.
*/
@Override
public Activity getActivity() {
return innerTurnContext.getActivity();
}
/**
* Gets the inner context's responded value.
* @return The inner {@link TurnContext#getResponded()}.
*/
@Override
public boolean getResponded() {
return innerTurnContext.getResponded();
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend) {
return innerTurnContext.sendActivity(textReplyToSend);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend, String speak) {
return innerTurnContext.sendActivity(textReplyToSend, speak);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend, String speak, InputHints inputHint) {
public CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend,
String speak,
InputHints inputHint) {
return innerTurnContext.sendActivity(textReplyToSend, speak, inputHint);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<ResourceResponse> sendActivity(Activity activity) {
return innerTurnContext.sendActivity(activity);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<ResourceResponse[]> sendActivities(List<Activity> activities) {
return innerTurnContext.sendActivities(activities);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<ResourceResponse> updateActivity(Activity activity) {
return innerTurnContext.updateActivity(activity);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<Void> deleteActivity(String activityId) {
return innerTurnContext.deleteActivity(activityId);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public CompletableFuture<Void> deleteActivity(ConversationReference conversationReference) {
return innerTurnContext.deleteActivity(conversationReference);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public TurnContext onSendActivities(SendActivitiesHandler handler) {
return innerTurnContext.onSendActivities(handler);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public TurnContext onUpdateActivity(UpdateActivityHandler handler) {
return innerTurnContext.onUpdateActivity(handler);
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
public TurnContext onDeleteActivity(DeleteActivityHandler handler) {
return innerTurnContext.onDeleteActivity(handler);

Просмотреть файл

@ -5,31 +5,59 @@ package com.microsoft.bot.builder;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.HashMap;
import java.util.Map;
/**
* Score plus any extra information about an intent.
*/
public class IntentScore {
/**
* Confidence in an intent.
*/
@JsonProperty
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private double score;
/**
* Extra properties to include in the results.
*/
private HashMap<String, JsonNode> properties = new HashMap<>();
/**
* Gets confidence in an intent.
* @return Confidence in an intent.
*/
public double getScore() {
return score;
}
/**
* Sets confidence in an intent.
* @param withScore Confidence in an intent.
*/
public void setScore(double withScore) {
score = withScore;
}
/**
* Gets extra properties to include in the results.
* @return Any extra properties to include in the results.
*/
@JsonAnyGetter
public Map<String, JsonNode> getProperties() {
return this.properties;
}
/**
* Sets extra properties to include in the results.
* @param key The key of the property.
* @param value The JsonNode value of the property.
*/
@JsonAnySetter
public void setProperties(String key, JsonNode value) {
this.properties.put(key, value);

Просмотреть файл

@ -21,18 +21,34 @@ public class InvokeResponse {
*/
private Object body;
/**
* Gets the HTTP status code for the response.
* @return The HTTP status code.
*/
public int getStatus() {
return status;
}
/**
* Sets the HTTP status code for the response.
* @param withStatus The HTTP status code.
*/
public void setStatus(int withStatus) {
this.status = withStatus;
}
/**
* Gets the body content for the response.
* @return The body content.
*/
public Object getBody() {
return body;
}
/**
* Sets the body content for the response.
* @param withBody The body content.
*/
public void setBody(Object withBody) {
body = withBody;
}

Просмотреть файл

@ -16,18 +16,52 @@ import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
/**
* A storage layer that uses an in-memory dictionary.
*/
public class MemoryStorage implements Storage {
/**
* Special field for holding the type information for the top level
* object being stored.
*/
private static final String TYPENAMEFORNONENTITY = "__type_name_";
private final Object syncroot = new Object();
private ObjectMapper objectMapper;
private Map<String, JsonNode> memory;
private Logger logger = LoggerFactory.getLogger(MemoryStorage.class);
private int _eTag = 0;
/**
* Concurrency sync.
*/
private final Object syncroot = new Object();
/**
* To/From JSON.
*/
private ObjectMapper objectMapper;
/**
* The internal map for storage.
*/
private Map<String, JsonNode> memory;
/**
* The... ummm... logger.
*/
private Logger logger = LoggerFactory.getLogger(MemoryStorage.class);
/**
* eTag counter.
*/
private int eTag = 0;
/**
* Initializes a new instance of the MemoryStorage class.
*/
public MemoryStorage() {
this(null);
}
/**
* Initializes a new instance of the MemoryStorage class.
* @param values A pre-existing dictionary to use; or null to use a new one.
*/
public MemoryStorage(Map<String, JsonNode> values) {
objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
@ -37,6 +71,13 @@ public class MemoryStorage implements Storage {
memory = values != null ? values : new ConcurrentHashMap<>();
}
/**
* Reads storage items from storage.
*
* @param keys keys of the items to read
* @return A task that represents the work queued to execute. If the activities
* are successfully sent, the task result contains the items read, indexed by key.
*/
@Override
public CompletableFuture<Map<String, Object>> read(String[] keys) {
if (keys == null) {
@ -47,18 +88,14 @@ public class MemoryStorage implements Storage {
synchronized (this.syncroot) {
for (String key : keys) {
if (memory.containsKey(key)) {
Object state = memory.get(key);
if (state != null) {
JsonNode stateNode = memory.get(key);
if (stateNode != 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));
throw new RuntimeException(
String.format("Read failed: Type info not present for key " + key));
}
String clsName = stateNode.get(TYPENAMEFORNONENTITY).textValue();
@ -68,7 +105,8 @@ public class MemoryStorage implements Storage {
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));
throw new RuntimeException(
String.format("Read failed: Could not load class %s", clsName));
}
// Populate dictionary
@ -85,6 +123,12 @@ public class MemoryStorage implements Storage {
return CompletableFuture.completedFuture(storeItems);
}
/**
* Writes storage items to storage.
*
* @param changes The items to write, indexed by key.
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<Void> write(Map<String, Object> changes) {
synchronized (this.syncroot) {
@ -116,8 +160,8 @@ public class MemoryStorage implements Storage {
logger.error(msg);
throw new RuntimeException(msg);
}
Integer newTag = _eTag++;
((ObjectNode) newState).put("eTag", newTag.toString());
int newTag = eTag++;
((ObjectNode) newState).put("eTag", Integer.toString(newTag));
}
memory.put(change.getKey(), newState);
@ -127,6 +171,12 @@ public class MemoryStorage implements Storage {
return CompletableFuture.completedFuture(null);
}
/**
* Deletes storage items from storage.
*
* @param keys keys of the items to delete
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<Void> delete(String[] keys) {
if (keys == null) {

Просмотреть файл

@ -3,11 +3,16 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.*;
import com.microsoft.bot.schema.ActionTypes;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.Attachment;
import com.microsoft.bot.schema.AttachmentLayoutTypes;
import com.microsoft.bot.schema.CardAction;
import com.microsoft.bot.schema.InputHints;
import com.microsoft.bot.schema.SuggestedActions;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -131,7 +136,7 @@ public final class MessageFactory {
*
* @param actions The card actions to include.
* @param text Optional, the text of the message to send.
* @return
* @return A message activity that contains the suggested actions.
*/
public static Activity suggestedCardActions(List<CardAction> actions, String text) {
return suggestedCardActions(actions, text, null, null);
@ -145,7 +150,7 @@ public final class MessageFactory {
* @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
* @return A message activity that contains the suggested actions.
*/
public static Activity suggestedCardActions(List<CardAction> actions,
String text,
@ -158,7 +163,7 @@ public final class MessageFactory {
Activity activity = Activity.createMessageActivity();
setTextAndSpeech(activity, text, ssml, inputHint);
activity.setSuggestedActions(new SuggestedActions(actions.toArray(new CardAction[actions.size()])));
activity.setSuggestedActions(new SuggestedActions(actions.toArray(new CardAction[0])));
return activity;
}
@ -282,7 +287,7 @@ public final class MessageFactory {
setName(StringUtils.isEmpty(name) ? null : name);
}};
return attachmentActivity(AttachmentLayoutTypes.LIST, Arrays.asList(attachment),
return attachmentActivity(AttachmentLayoutTypes.LIST, Collections.singletonList(attachment),
text, ssml, inputHint);
}

Просмотреть файл

@ -18,27 +18,26 @@ import java.util.concurrent.CompletableFuture;
* <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.
* <code>
* This defines middleware that sends "before" and "after" messages before and after
* the adapter calls the bot's {@link Bot#onTurn(TurnContext)} method.
*
* {@code
* public class SampleMiddleware : Middleware
* {
* public async Task OnTurn(TurnContext context, MiddlewareSet.NextDelegate next)
* {
* context.SendActivity("before");
* await next().ConfigureAwait(false);
* context.SendActivity("after");
* public async Task OnTurn(TurnContext context, MiddlewareSet.NextDelegate next)
* {
* context.SendActivity("before");
* await next().ConfigureAwait(false);
* context.SendActivity("after");
* }
* }
* }
* </code>
* </example>
* {@linkalso Bot}
*
* {@link Bot}
*/
public interface Middleware {
/**
* Processess an incoming activity.
* Processes an incoming activity.
*
* @param turnContext The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.

Просмотреть файл

@ -4,13 +4,17 @@
package com.microsoft.bot.builder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* Contains an ordered set of {@link Middleware}.
*/
public class MiddlewareSet implements Middleware {
private final ArrayList<Middleware> middleware = new ArrayList<>();
/**
* List of {@link Middleware} objects this class manages.
*/
private final List<Middleware> middlewareList = new ArrayList<>();
/**
* Adds a middleware object to the end of the set.
@ -20,13 +24,29 @@ public class MiddlewareSet implements Middleware {
*/
public MiddlewareSet use(Middleware middleware) {
BotAssert.middlewareNotNull(middleware);
this.middleware.add(middleware);
this.middlewareList.add(middleware);
return this;
}
/**
* Processes an incoming activity.
*
* @param turnContext 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.
* Middleware calls the {@code next} delegate to pass control to
* the next middleware in the pipeline. If middleware doesnt call the next delegate,
* the adapter does not call any of the subsequent middlewares request handlers or the
* bots receive handler, and the pipeline short circuits.
* <p>The {@code context} provides information about the
* incoming activity, and other data needed to process the activity.</p>
* <p>
* {@link TurnContext}
* {@link com.microsoft.bot.schema.Activity}
*/
@Override
public CompletableFuture<Void> onTurn(TurnContext context, NextDelegate next) {
return receiveActivityInternal(context, null)
public CompletableFuture<Void> onTurn(TurnContext turnContext, NextDelegate next) {
return receiveActivityInternal(turnContext, null)
.thenCompose((result) -> next.next());
}
@ -49,7 +69,7 @@ public class MiddlewareSet implements Middleware {
BotCallbackHandler callback,
int nextMiddlewareIndex) {
// Check if we're at the end of the middleware list yet
if (nextMiddlewareIndex == middleware.size()) {
if (nextMiddlewareIndex == middlewareList.size()) {
// If all the Middleware 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
@ -68,7 +88,7 @@ public class MiddlewareSet implements Middleware {
}
// Get the next piece of middleware
Middleware nextMiddleware = middleware.get(nextMiddlewareIndex);
Middleware nextMiddleware = middlewareList.get(nextMiddlewareIndex);
// Execute the next middleware passing a closure that will recurse back into this method at the
// next piece of middleware as the NextDelegate

Просмотреть файл

@ -8,13 +8,29 @@ import java.time.OffsetDateTime;
import java.util.Map;
public class NullBotTelemetryClient implements BotTelemetryClient {
@SuppressWarnings("checkstyle:ParameterNumber")
@Override
public void trackAvailability(String name, OffsetDateTime timeStamp, Duration duration, String runLocation, boolean success, String message, Map<String, String> properties, Map<String, Double> metrics) {
public void trackAvailability(String name,
OffsetDateTime timeStamp,
Duration duration,
String runLocation,
boolean success,
String message,
Map<String, String> properties,
Map<String, Double> metrics) {
}
@SuppressWarnings("checkstyle:ParameterNumber")
@Override
public void trackDependency(String dependencyTypeName, String target, String dependencyName, String data, OffsetDateTime startTime, Duration duration, String resultCode, boolean success) {
public void trackDependency(String dependencyTypeName,
String target,
String dependencyName,
String data,
OffsetDateTime startTime,
Duration duration,
String resultCode,
boolean success) {
}

Просмотреть файл

@ -23,18 +23,34 @@ public class PagedResult<T> {
*/
private String continuationToken;
/**
* Gets the page of items.
* @return The List of items.
*/
public List<T> getItems() {
return this.items;
}
/**
* Sets the page of items.
* @param value The List of items.
*/
public void setItems(List<T> value) {
this.items = value;
}
/**
* Gets the token for retrieving the next page of results.
* @return The Continuation Token to pass to get the next page of results.
*/
public String getContinuationToken() {
return continuationToken;
}
/**
* Sets the token for retrieving the next page of results.
* @param withValue The Continuation Token to pass to get the next page of results.
*/
public void setContinuationToken(String withValue) {
continuationToken = withValue;
}

Просмотреть файл

@ -3,6 +3,16 @@
package com.microsoft.bot.builder;
/**
* PropertyManager defines implementation of a source of named properties.
*/
public interface PropertyManager {
/**
* Creates a managed state property accessor for a property.
*
* @param name The name of the property accessor.
* @param <T> The property value type.
* @return A state property accessor for the property.
*/
<T> StatePropertyAccessor<T> createProperty(String name);
}

Просмотреть файл

@ -5,6 +5,15 @@ package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
/**
* Interface for Recognizers.
*/
public interface Recognizer {
/**
* Runs an utterance through a recognizer and returns a generic recognizer result.
*
* @param turnContext Turn context.
* @return Analysis of utterance.
*/
CompletableFuture<RecognizerResult> recognize(TurnContext turnContext);
}

Просмотреть файл

@ -3,6 +3,13 @@
package com.microsoft.bot.builder;
/**
* Can convert from a generic recognizer result to a strongly typed one.
*/
public interface RecognizerConvert {
/**
* Convert recognizer result.
* @param result Result to convert.
*/
void convert(Object result);
}

Просмотреть файл

@ -5,23 +5,51 @@ package com.microsoft.bot.builder;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.HashMap;
import java.util.Map;
/**
* Contains recognition results generated by an {@link Recognizer}.
*/
public class RecognizerResult implements RecognizerConvert {
@JsonProperty
/**
* Object with each top-level recognized entity as a key.
*/
@JsonProperty(value = "entities")
JsonNode entities;
@JsonProperty
/**
* Original text to recognizer.
*/
@JsonProperty(value = "text")
private String text;
@JsonProperty
/**
* Text modified by recognizer.
*/
@JsonProperty(value = "alteredText")
private String alteredText;
@JsonProperty
/**
* Mapping from intent to information about the intent.
*/
@JsonProperty(value = "intents")
private Map<String, IntentScore> intents;
/**
* Additional properties.
*/
private HashMap<String, JsonNode> properties = new HashMap<>();
/**
* Return the top scoring intent and its score.
* @return The top scoring intent and score.
*/
@JsonIgnore
public IntentScore getTopScoringIntent() {
if (getIntents() == null) {
throw new IllegalArgumentException("RecognizerResult.Intents cannot be null");
@ -38,43 +66,91 @@ public class RecognizerResult implements RecognizerConvert {
return topIntent;
}
/**
* Gets the input text to recognize.
* @return The original text.
*/
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
/**
* Sets the input text to recognize.
* @param withText The text to recognize.
*/
public void setText(String withText) {
text = withText;
}
/**
* Gets the input text as modified by the recognizer, for example for spelling correction.
* @return Text modified by recognizer.
*/
public String getAlteredText() {
return alteredText;
}
public void setAlteredText(String alteredText) {
this.alteredText = alteredText;
/**
* Sets the input text as modified by the recognizer, for example for spelling correction.
* @param withAlteredText Text modified by recognizer.
*/
public void setAlteredText(String withAlteredText) {
alteredText = withAlteredText;
}
/**
* Gets the recognized intents, with the intent as key and the confidence as value.
* @return Mapping from intent to information about the intent.
*/
public Map<String, IntentScore> getIntents() {
return intents;
}
public void setIntents(Map<String, IntentScore> intents) {
this.intents = intents;
/**
* Sets the recognized intents, with the intent as key and the confidence as value.
* @param withIntents Mapping from intent to information about the intent.
*/
public void setIntents(Map<String, IntentScore> withIntents) {
intents = withIntents;
}
/**
* Gets the recognized top-level entities.
* @return Object with each top-level recognized entity as a key.
*/
public JsonNode getEntities() {
return entities;
}
public void setEntities(JsonNode entities) {
this.entities = entities;
/**
* Sets the recognized top-level entities.
* @param withEntities Object with each top-level recognized entity as a key.
*/
public void setEntities(JsonNode withEntities) {
entities = withEntities;
}
/**
* Gets properties that are not otherwise defined by the RecognizerResult type but that
* might appear in the REST JSON object.
* @return The extended properties for the object.
*/
@JsonAnyGetter
public Map<String, JsonNode> getProperties() {
return this.properties;
}
/**
* Sets properties that are not otherwise defined by the RecognizerResult type but that
* might appear in the REST JSON object.
*
* <p>With this, properties not represented in the defined type are not dropped when
* the JSON object is deserialized, but are instead stored in this property. Such properties
* will be written to a JSON object when the instance is serialized.</p>
*
* @param key The property key.
* @param value The property value.
*/
@JsonAnySetter
public void setProperties(String key, JsonNode value) {
this.properties.put(key, value);

Просмотреть файл

@ -3,19 +3,49 @@
package com.microsoft.bot.builder;
/**
* This enumeration is used by TrackTrace to identify severity level.
*/
public enum Severity {
/**
* Verbose severity level.
*/
VERBOSE(0),
/**
* Information severity level.
*/
INFORMATION(1),
/**
* Warning severity level.
*/
WARNING(2),
/**
* Error severity level.
*/
ERROR(3),
/**
* Critical severity level.
*/
CRITICAL(4);
private int value;
/**
* Constructs with an in value.
* @param witValue
*/
Severity(int witValue) {
value = witValue;
}
/**
* For converion to int.
* @return The int value of this enum.
*/
public int getSeverity() {
return value;
}

Просмотреть файл

@ -6,14 +6,40 @@ package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
/**
* Interface which defines methods for how you can get data from a property source such as BotState.
* @param <T> type of the property.
*/
public interface StatePropertyAccessor<T> extends StatePropertyInfo {
/**
* Get the property value from the source.
* @param turnContext TurnContext.
* @return A task representing the result of the asynchronous operation.
*/
default CompletableFuture<T> get(TurnContext turnContext) {
return get(turnContext, null);
}
/**
* Get the property value from the source.
* @param turnContext TurnContext.
* @param defaultValueFactory Function which defines the property value to be returned if no value has been set.
* @return A task representing the result of the asynchronous operation.
*/
CompletableFuture<T> get(TurnContext turnContext, Supplier<T> defaultValueFactory);
/**
* Delete the property from the source.
* @param turnContext TurnContext.
* @return A task representing the result of the asynchronous operation.
*/
CompletableFuture<Void> delete(TurnContext turnContext);
/**
* Set the property value on the source.
* @param turnContext TurnContext.
* @param value The value to set.
* @return A task representing the result of the asynchronous operation.
*/
CompletableFuture<Void> set(TurnContext turnContext, T value);
}

Просмотреть файл

@ -3,8 +3,13 @@
package com.microsoft.bot.builder;
/**
* This is metadata about the property including policy info.
*/
public interface StatePropertyInfo {
/**
* Gets the name of the property.
* @return The name of the property.
*/
String getName();
void setName(String withName);
}

Просмотреть файл

@ -6,26 +6,32 @@ package com.microsoft.bot.builder;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* Defines the interface for a storage layer.
*/
public interface Storage {
/**
* Read StoreItems from storage
* Reads storage items from storage.
*
* @param keys keys of the storeItems to read
* @return StoreItem dictionary
* @param keys keys of the items to read
* @return A task that represents the work queued to execute. If the activities
* are successfully sent, the task result contains the items read, indexed by key.
*/
CompletableFuture<Map<String, Object>> read(String[] keys);
/**
* Write StoreItems to storage
* Writes storage items to storage.
*
* @param changes
* @param changes The items to write, indexed by key.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> write(Map<String, Object> changes);
/**
* Delete StoreItems from storage
* Deletes storage items from storage.
*
* @param keys keys of the storeItems to delete
* @param keys keys of the items to delete
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> delete(String[] keys);
}

Просмотреть файл

@ -37,6 +37,10 @@ public class TelemetryLoggerMiddleware implements Middleware {
logPersonalInformation = withLogPersonalInformation;
}
/**
* Gets the currently configured BotTelemetryClient that logs the event.
* @return The {@link BotTelemetryClient} being used to log events.
*/
public BotTelemetryClient getTelemetryClient() {
return telemetryClient;
}
@ -46,7 +50,7 @@ public class TelemetryLoggerMiddleware implements Middleware {
*
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<Void> onTurn(TurnContext context, NextDelegate next) {
@ -67,7 +71,8 @@ public class TelemetryLoggerMiddleware implements Middleware {
// hook up update activity pipeline
context.onUpdateActivity((updateContext, updateActivity, updateNext) -> updateNext.get()
.thenCombine(onUpdateActivity(updateActivity), (resourceResponse, updateResult) -> resourceResponse));
.thenCombine(
onUpdateActivity(updateActivity), (resourceResponse, updateResult) -> resourceResponse));
// hook up delete activity pipeline
context.onDeleteActivity((deleteContext, deleteReference, deleteNext) -> deleteNext.get()

Просмотреть файл

@ -20,7 +20,7 @@ public class TranscriptInfo {
/**
* Date conversation was started.
*/
private OffsetDateTime created = OffsetDateTime.now();
private OffsetDateTime created;
public TranscriptInfo(String withId, String withChannelId, OffsetDateTime withCreated) {
id = withId;
@ -28,27 +28,51 @@ public class TranscriptInfo {
created = withCreated;
}
/**
* Gets the ID of the channel in which the conversation occurred.
* @return The ID of the channel in which the conversation occurred.
*/
public String channelId() {
return this.channelId;
return channelId;
}
public void setChannelId(String withValue) {
channelId = withValue;
/**
* Sets the ID of the channel in which the conversation occurred.
* @param withChannelId The ID of the channel in which the conversation occurred.
*/
public void setChannelId(String withChannelId) {
channelId = withChannelId;
}
/**
* Gets the ID of the conversation.
* @return The ID of the conversation.
*/
public String getId() {
return id;
}
public void setId(String witValue) {
id = witValue;
/**
* Sets the ID of the conversation.
* @param withId The ID of the conversation.
*/
public void setId(String withId) {
id = withId;
}
/**
* Gets the date the conversation began.
* @return The date then conversation began.
*/
public OffsetDateTime getCreated() {
return created;
}
public void setCreated(OffsetDateTime withValue) {
created = withValue;
/**
* Sets the date the conversation began.
* @param withCreated The date then conversation began.
*/
public void setCreated(OffsetDateTime withCreated) {
created = withCreated;
}
}

Просмотреть файл

@ -25,16 +25,26 @@ import java.util.concurrent.ConcurrentLinkedQueue;
*/
public class TranscriptLoggerMiddleware implements Middleware {
private static final Logger logger = LoggerFactory.getLogger(TranscriptLoggerMiddleware.class);
// https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features
/**
* To/From JSON.
*/
private static ObjectMapper mapper;
static {
mapper = new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT);
mapper.findAndRegisterModules();
.enable(SerializationFeature.INDENT_OUTPUT)
.findAndRegisterModules();
}
/**
* The TranscriptLogger to log to.
*/
private TranscriptLogger transcriptLogger;
/**
* Activity queue.
*/
private Queue<Activity> transcript = new ConcurrentLinkedQueue<Activity>();
/**
@ -52,11 +62,11 @@ public class TranscriptLoggerMiddleware implements Middleware {
}
/**
* initialization for middleware turn.
* Records incoming and outgoing activities to the conversation store.
*
* @param context
* @param next
* @return
* @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.
*/
@Override
public CompletableFuture<Void> onTurn(TurnContext context, NextDelegate next) {

Просмотреть файл

@ -31,7 +31,7 @@ public interface TranscriptStore extends TranscriptLogger {
*
* @param channelId The ID of the channel the conversation is in.
* @param conversationId The ID of the conversation.
* @param continuationToken
* @param continuationToken The continuation token (if available).
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
@ -46,7 +46,7 @@ public interface TranscriptStore extends TranscriptLogger {
*
* @param channelId The ID of the channel the conversation is in.
* @param conversationId The ID of the conversation.
* @param continuationToken
* @param continuationToken The continuation token (if available).
* @param startDate A cutoff date. Activities older than this date are not included.
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
@ -70,7 +70,7 @@ public interface TranscriptStore extends TranscriptLogger {
* Gets the conversations on a channel from the store.
*
* @param channelId The ID of the channel.
* @param continuationToken
* @param continuationToken The continuation token (if available).
* @return A task that represents the work queued to execute.
*/
CompletableFuture<PagedResult<TranscriptInfo>> listTranscripts(String channelId, String continuationToken);

Просмотреть файл

@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
@ -12,11 +13,13 @@ import java.util.concurrent.CompletableFuture;
/**
* Provides context for a turn of a bot.
* Context provides information needed to process an incoming activity.
*
* <p>Context provides information needed to process an incoming activity.
* The context object is created by a {@link BotAdapter} and persists for the
* length of the turn.
* {@linkalso Bot}
* {@linkalso Middleware}
* length of the turn.</p>
*
* {@link Bot}
* {@link Middleware}
*/
public interface TurnContext {
/**
@ -42,66 +45,80 @@ public interface TurnContext {
return turnContext.sendActivity(turnContext.getActivity().createTrace(name, value, valueType, label));
}
/**
* Gets the bot adapter that created this context object.
* @return The bot adapter that created this context object.
*/
BotAdapter getAdapter();
/**
* Gets the services registered on this context object.
* Gets the collection of values cached with the context object for the lifetime of the turn.
* @return The collection of services registered on this context object.
*/
TurnContextStateCollection getTurnState();
/**
* Incoming request
* Gets the activity for this turn of the bot.
* @return The activity for this turn of the bot.
*/
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.
* Gets a value indicating 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; otherwise, {@code false}.
*/
boolean getResponded();
/**
* Sends a message activity to the sender of the incoming activity.
*
* @param textReplyToSend The text of the message to send.
* @return A task that represents the work queued to execute.
* If the activity is successfully sent, the task result contains
* <p>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.
* channel assigned to the activity.</p>
*
* <p>See the channel's documentation for limits imposed upon the contents of
* {@code textReplyToSend}.</p>
* <p>To control various characteristics of your bot's speech such as voice,
* rate, volume, pronunciation, and pitch, specify {@code speak} in
* Speech Synthesis Markup Language (SSML) format.</p>
*
* @param textReplyToSend The text of the message to send.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend);
/**
* Sends a message activity to the sender of the incoming activity.
*
* <p>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>
*
* <p>See the channel's documentation for limits imposed upon the contents of
* {@code textReplyToSend}.</p>
*
* <p>To control various characteristics of your bot's speech such as voice,
* rate, volume, pronunciation, and pitch, specify {@code speak} in
* Speech Synthesis Markup Language (SSML) format.</p>
*
* @param textReplyToSend The text of the message to send.
* @param speak Optional, text to be spoken by your bot on a speech-enabled
* channel.
* @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>See the channel's documentation for limits imposed upon the contents of
* {@code textReplyToSend}.</p>
* <p>To control various characteristics of your bot's speech such as voice,
* rate, volume, pronunciation, and pitch, specify {@code speak} in
* Speech Synthesis Markup Language (SSML) format.</p>
*/
CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend, String speak);
/**
* Sends a message activity to the sender of the incoming activity.
*
* <p>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>
*
* <p>See the channel's documentation for limits imposed upon the contents of
* {@code textReplyToSend}.</p>
*
* <p>To control various characteristics of your bot's speech such as voice,
* rate, volume, pronunciation, and pitch, specify {@code speak} in
* Speech Synthesis Markup Language (SSML) format.</p>
*
* @param textReplyToSend The text of the message to send.
* @param speak Optional, text to be spoken by your bot on a speech-enabled
* channel.
@ -110,14 +127,6 @@ public interface TurnContext {
* One of: "acceptingInput", "ignoringInput", or "expectingInput".
* Default is "acceptingInput".
* @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>See the channel's documentation for limits imposed upon the contents of
* {@code textReplyToSend}.</p>
* <p>To control various characteristics of your bot's speech such as voice,
* rate, volume, pronunciation, and pitch, specify {@code speak} in
* Speech Synthesis Markup Language (SSML) format.</p>
*/
CompletableFuture<ResourceResponse> sendActivity(String textReplyToSend, String speak, InputHints inputHint);
@ -133,26 +142,29 @@ public interface TurnContext {
CompletableFuture<ResourceResponse> sendActivity(Activity activity);
/**
* Sends a set of activities to the sender of the incoming activity.
* Sends a list of activities to the sender of the incoming activity.
*
* <p>If the activities are successfully sent, the task result contains
* an array of {@link ResourceResponse} objects containing the IDs that
* the receiving channel assigned to the activities.</p>
*
* @param activities The activities to send.
* @return A task that represents the work queued to execute.
* If the activities are successfully sent, the task result contains
* an array of {@link ResourceResponse} objects containing the IDs that
* the receiving channel assigned to the activities.
*/
CompletableFuture<ResourceResponse[]> sendActivities(List<Activity> activities);
/**
* Replaces an existing activity.
*
* @param withActivity New replacement activity.
* @return A task that represents the work queued to execute.
* If the activity is successfully sent, the task result contains
* <p>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.
* channel assigned to the activity.</p>
*
* <p>Before calling this, set the ID of the replacement activity to the ID
* of the activity to replace.</p>
*
* @param withActivity New replacement activity.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<ResourceResponse> updateActivity(Activity withActivity);
@ -177,35 +189,38 @@ public interface TurnContext {
/**
* Adds a response handler for send activity operations.
*
* <p>When the context's {@link #sendActivity(Activity)}
* or {@link #sendActivities(List)} methods are called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.</p>
*
* @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(List<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);
/**
* Adds a response handler for update activity operations.
*
* <p>When the context's {@link #updateActivity(Activity)} is called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.</p>
*
* @param handler The handler to add to the context object.
* @return The updated context object.
* When the context's {@link #updateActivity(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);
/**
* Adds a response handler for delete activity operations.
*
* <p>When the context's {@link #deleteActivity(String)} is called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.</p>
*
* @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,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*/
TurnContext onDeleteActivity(DeleteActivityHandler handler);
}

Просмотреть файл

@ -40,6 +40,13 @@ public class TurnContextStateCollection implements AutoCloseable {
return get(type.getSimpleName());
}
/**
* Adds a value to the turn's context.
* @param key The name of the value.
* @param value The value to add.
* @param <T> The type of the value.
* @throws IllegalArgumentException
*/
public <T> void add(String key, T value) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key");
@ -69,10 +76,19 @@ public class TurnContextStateCollection implements AutoCloseable {
add(value.getClass().getSimpleName(), value);
}
/**
* Removes a value.
* @param key The name of the value to remove.
*/
public void remove(String key) {
state.remove(key);
}
/**
* Replaces a value.
* @param key The name of the value to replace.
* @param value The new value.
*/
public void replace(String key, Object value) {
state.remove(key);
add(key, value);

Просмотреть файл

@ -1,13 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
public final class TurnContextStateNames {
public static final String BOT_IDENTITY = "BotIdentity";
public static final String CONNECTOR_CLIENT = "ConnectorClient";
private TurnContextStateNames() {
}
}

Просмотреть файл

@ -19,8 +19,8 @@ public interface UserTokenProvider {
* @return Token Response.
*/
CompletableFuture<TokenResponse> getUserToken(TurnContext turnContext,
String connectionName,
String magicCode);
String connectionName,
String magicCode);
/**
* Get the raw signin link to be sent to the user for signin for a connection name.
@ -30,9 +30,7 @@ public interface UserTokenProvider {
* @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> getOauthSignInLink(TurnContext turnContext, String connectionName) {
return getOauthSignInLink(turnContext, connectionName, null, null);
}
CompletableFuture<String> getOauthSignInLink(TurnContext turnContext, String connectionName);
/**
* Get the raw signin link to be sent to the user for signin for a connection name.
@ -45,9 +43,9 @@ public interface UserTokenProvider {
* the result contains the raw signin link.
*/
CompletableFuture<String> getOauthSignInLink(TurnContext turnContext,
String connectionName,
String userId,
String finalRedirect);
String connectionName,
String userId,
String finalRedirect);
/**
* Signs the user out with the token server.
@ -100,8 +98,8 @@ public interface UserTokenProvider {
* @return Dictionary of resourceUrl to the corresponding TokenResponse.
*/
default CompletableFuture<Map<String, TokenResponse>> getAadTokens(TurnContext turnContext,
String connectionName,
String[] resourceUrls) {
String connectionName,
String[] resourceUrls) {
return getAadTokens(turnContext, connectionName, resourceUrls, null);
}
@ -116,7 +114,7 @@ public interface UserTokenProvider {
* @return Dictionary of resourceUrl to the corresponding TokenResponse.
*/
CompletableFuture<Map<String, TokenResponse>> getAadTokens(TurnContext turnContext,
String connectionName,
String[] resourceUrls,
String userId);
String connectionName,
String[] resourceUrls,
String userId);
}

Просмотреть файл

@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.integration;
import com.microsoft.bot.builder.BotCallbackHandler;
import com.microsoft.bot.builder.InvokeResponse;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationReference;
import java.util.concurrent.CompletableFuture;
/**
* An interface that defines the contract between web service integration pieces and the bot adapter.
*/
public interface AdapterIntegration {
/**
* Creates a turn context and runs the middleware pipeline for an incoming activity.
*
* @param authHeader The HTTP authentication header of the request.
* @param activity The incoming activity.
* @param callback The code to run at the end of the adapter's middleware pipeline.
* @return A task that represents the work queued to execute. If the activity type
* was 'Invoke' and the corresponding key (channelId + activityId) was found
* then an InvokeResponse is returned, otherwise null is returned.
*/
CompletableFuture<InvokeResponse> processActivity(
String authHeader,
Activity activity,
BotCallbackHandler callback);
/**
* Sends a proactive message to a conversation.
*
* <p>Call this method to proactively send a message to a conversation.
* Most _channels require a user to initiate a conversation with a bot
* before the bot can send activities to the user.</p>
*
* @param botId The application ID of the bot. This parameter is ignored in single tenant
* the Adapters (Console, Test, etc) but is critical to the BotFrameworkAdapter
* which is multi-tenant aware.
* @param reference A reference to the conversation to continue.
* @param callback The method to call for the resulting bot turn.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> continueConversation(
String botId,
ConversationReference reference,
BotCallbackHandler callback);
}

Просмотреть файл

@ -1,55 +0,0 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/
package com.microsoft.bot.schema;
import java.util.UUID;
public class ConversationReferenceHelper {
private ConversationReference reference;
public ConversationReferenceHelper(ConversationReference withReference) {
this.reference = withReference;
}
/**
* Creates {@link Activity} from conversation reference as it is posted to bot.
*/
public Activity getPostToBotMessage() {
Activity activity = new Activity();
activity.setType(ActivityTypes.MESSAGE);
activity.setId(UUID.randomUUID().toString());
activity.setRecipient(new ChannelAccount(
reference.getBot().getId(),
reference.getBot().getName()));
activity.setChannelId(reference.getChannelId());
activity.setServiceUrl(reference.getServiceUrl());
activity.setConversation(new ConversationAccount(
reference.getConversation().isGroup(),
reference.getConversation().getId(),
reference.getConversation().getName()));
activity.setFrom(new ChannelAccount(
reference.getUser().getId(),
reference.getUser().getName()));
return activity;
}
/**
* Creates {@link Activity} from conversation reference that can be posted to user as reply.
*/
public Activity getPostToUserMessage() {
Activity msg = this.getPostToBotMessage();
// swap from and recipient
msg.setFrom(msg.getRecipient());
msg.setRecipient(msg.getFrom());
return msg;
}
}