This commit is contained in:
Tracy Boehrer 2019-09-05 13:31:57 -05:00
Родитель b04c8f052b
Коммит c67a4acf09
58 изменённых файлов: 2562 добавлений и 1034 удалений

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

@ -0,0 +1,262 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ChannelAccount;
import com.microsoft.bot.schema.MessageReaction;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* An implementation of the IBot interface intended for further subclassing.
* Derive from this class to plug in code to handle particular Activity types.
* Pre and post processing of Activities can be plugged in by deriving and calling
* the base class implementation.
*/
public class ActivityHandler implements Bot {
/**
* The OnTurnAsync function is called by the Adapter (for example, the {@link BotFrameworkAdapter} at
* runtime in order to process an inbound Activity.
*
* @param turnContext The context object for this turn. Provides information about the
* incoming activity, and other data needed to process the activity.
* @return
*/
@Override
public CompletableFuture<Void> onTurnAsync(TurnContext turnContext) {
if (turnContext == null) {
throw new IllegalArgumentException("TurnContext cannot be null.");
}
if (turnContext.getActivity() == null) {
throw new IllegalArgumentException("turnContext must have a non-null Activity.");
}
if (turnContext.getActivity().getType() == null) {
throw new IllegalArgumentException("turnContext.getActivity must have a non-null Type.");
}
switch (turnContext.getActivity().getType()) {
case MESSAGE:
return onMessageActivityAsync(turnContext);
case CONVERSATION_UPDATE:
return onConversationUpdateActivityAsync(turnContext);
case MESSAGE_REACTION:
return onMessageReactionActivityAsync(turnContext);
case EVENT:
return onEventActivityAsync(turnContext);
default:
return onUnrecognizedActivityAsync(turnContext);
}
}
/**
* Invoked when a message activity is received from the user when the base behavior of
* {@link #onTurnAsync(TurnContext)} is used.
*
* If overridden, this could potentially contain conversational logic.
* By default, this method does nothing.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> onMessageActivityAsync(TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when a conversation update activity is received from the channel when the base behavior of
* {@link #onTurnAsync(TurnContext)} is used.
*
* Conversation update activities are useful when it comes to responding to users being added to or removed
* from the conversation.
*
* For example, a bot could respond to a user being added by greeting the user.
* By default, this method will call {@link #onMembersAddedAsync(List, TurnContext)} if any users have been added,
* or {@link #onMembersRemovedAsync(List, TurnContext)} if any users have been removed. The method checks the member
* ID so that it only responds to updates regarding members other than the bot itself.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> onConversationUpdateActivityAsync(TurnContext turnContext) {
if (turnContext.getActivity().getMembersAdded() != null) {
if (turnContext.getActivity().getMembersAdded().stream()
.anyMatch(m -> StringUtils.equals(m.getId(), turnContext.getActivity().getId()))) {
return onMembersAddedAsync(turnContext.getActivity().getMembersAdded(), turnContext);
}
} else if (turnContext.getActivity().getMembersRemoved() != null) {
if (turnContext.getActivity().getMembersRemoved().stream()
.anyMatch(m -> StringUtils.equals(m.getId(), turnContext.getActivity().getId()))) {
return onMembersRemovedAsync(turnContext.getActivity().getMembersRemoved(), turnContext);
}
}
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when members other than this bot (like a user) are added to the conversation when the base behavior of
* {@link #onConversationUpdateActivityAsync(TurnContext)} is used.
*
* If overridden, this could potentially send a greeting message to the user instead of waiting for the user to
* send a message first.
*
* By default, this method does nothing.
*
* @param membersAdded A list of all the users that have been added in the conversation update.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onMembersAddedAsync(List<ChannelAccount> membersAdded, TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when members other than this bot (like a user) are removed from the conversation when the base
* behavior of {@link #onConversationUpdateActivityAsync(TurnContext)} is used.
*
* This method could optionally be overridden to perform actions related to users leaving a group conversation.
*
* By default, this method does nothing.
*
* @param membersRemoved A list of all the users that have been removed in the conversation update.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onMembersRemovedAsync(List<ChannelAccount> membersRemoved, TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when an event activity is received from the connector when the base behavior of
* {@link #onTurnAsync(TurnContext)} is used.
*
* Message reactions correspond to the user adding a 'like' or 'sad' etc. (often an emoji) to a
* previously sent activity. Message reactions are only supported by a few channels.
*
* The activity that the message reaction corresponds to is indicated in the replyToId property.
* The value of this property is the activity id of a previously sent activity given back to the
* bot as the response from a send call.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture onMessageReactionActivityAsync(TurnContext turnContext) {
CompletableFuture task = null;
if (turnContext.getActivity().getReactionsAdded() != null) {
task = onReactionsAddedAsync(turnContext.getActivity().getReactionsAdded(), turnContext);
}
if (turnContext.getActivity().getReactionsRemoved() != null) {
if (task != null) {
task.thenApply((result) -> onReactionsRemovedAsync(
turnContext.getActivity().getReactionsRemoved(), turnContext));
} else {
task = onReactionsRemovedAsync(turnContext.getActivity().getReactionsRemoved(), turnContext);
}
}
return task == null ? CompletableFuture.completedFuture(null) : task;
}
/**
* Called when there have been Reactions added that reference a previous Activity.
*
* @param messageReactions The list of reactions added.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture onReactionsAddedAsync(List<MessageReaction> messageReactions,
TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Called when there have been Reactions removed that reference a previous Activity.
*
* @param messageReactions The list of reactions removed.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture onReactionsRemovedAsync(List<MessageReaction> messageReactions,
TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when an event activity is received from the connector when the base behavior of
* {@link #onTurnAsync(TurnContext)} is used.
*
* Event activities can be used to communicate many different things.
*
* By default, this method will call {@link #onTokenResponseEventAsync(TurnContext)} if the
* activity's name is "tokens/response" or {@link #onEventAsync(TurnContext)} otherwise.
* "tokens/response" event can be triggered by an {@link com.microsoft.bot.schema.OAuthCard}.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture onEventActivityAsync(TurnContext turnContext) {
if (StringUtils.equals(turnContext.getActivity().getName(), "tokens/response")) {
return onTokenResponseEventAsync(turnContext);
}
return onEventAsync(turnContext);
}
/**
* Invoked when a "tokens/response" event is received when the base behavior of
* {@link #onEventActivityAsync(TurnContext)} is used.
*
* If using an OAuthPrompt, override this method to forward this {@link Activity} to the current dialog.
*
* By default, this method does nothing.
*
* @param turnContext
* @return
*/
protected CompletableFuture onTokenResponseEventAsync(TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when an event other than <c>tokens/response</c> is received when the base behavior of
* {@link #onEventActivityAsync(TurnContext)} is used.
*
* This method could optionally be overridden if the bot is meant to handle miscellaneous events.
*
* By default, this method does nothing.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture onEventAsync(TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
/**
* Invoked when an activity other than a message, conversation update, or event is received when the base behavior of
* {@link #onTurnAsync(TurnContext)} is used.
*
* If overridden, this could potentially respond to any of the other activity types like
* {@link com.microsoft.bot.schema.ActivityTypes#CONTACT_RELATION_UPDATE} or
* {@link com.microsoft.bot.schema.ActivityTypes#END_OF_CONVERSATION}.
*
* By default, this method does nothing.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture onUnrecognizedActivityAsync(TurnContext turnContext) {
return CompletableFuture.completedFuture(null);
}
}

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

@ -0,0 +1,17 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
/**
* Represents a bot that can operate on incoming activities.
*/
public interface Bot {
/**
* When implemented in a bot, handles an incoming activity.
*
* @param turnContext The context object for this turn. Provides information about the
* incoming activity, and other data needed to process the activity.
* @return A task that represents the work queued to execute.
*/
CompletableFuture onTurnAsync(TurnContext turnContext);
}

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

@ -17,7 +17,7 @@ import java.util.function.Function;
* activities to and receives activities from the Bot Connector Service. When your
* bot receives an activity, the adapter creates a context object, passes it to your
* bot's application logic, and sends responses back to the user's channel.
* <p>Use {@link Use(Middleware)} to add {@link Middleware} objects
* <p>Use {@link #use(Middleware)} to add {@link Middleware} objects
* to your adapters middleware collection. The adapter processes and directs
* incoming activities in through the bot middleware pipeline to your bots logic
* and then back out again. As each activity flows in and out of the bot, each piece
@ -50,7 +50,7 @@ public abstract class BotAdapter {
* Middleware is added to the adapter at initialization time.
* For each turn, the adapter calls middleware in the order in which you added it.
*/
public BotAdapter Use(Middleware middleware) {
public BotAdapter use(Middleware middleware) {
_middlewareSet.Use(middleware);
return this;
}
@ -66,7 +66,8 @@ public abstract class BotAdapter {
* the receiving channel assigned to the activities.
* {@linkalso TurnContext.OnSendActivities(SendActivitiesHandler)}
*/
public abstract ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException;
public abstract CompletableFuture<ResourceResponse[]> sendActivitiesAsync(TurnContext context,
Activity[] activities);
/**
* When overridden in a derived class, replaces an existing activity in the
@ -82,7 +83,8 @@ public abstract class BotAdapter {
* of the activity to replace.</p>
* {@linkalso TurnContext.OnUpdateActivity(UpdateActivityHandler)}
*/
public abstract ResourceResponse UpdateActivity(TurnContext context, Activity activity);
public abstract CompletableFuture<ResourceResponse> updateActivityAsync(TurnContext context,
Activity activity);
/**
* When overridden in a derived class, deletes an existing activity in the
@ -91,11 +93,12 @@ public abstract class BotAdapter {
* @param context The context object for the turn.
* @param reference Conversation reference for the activity to delete.
* @return A task that represents the work queued to execute.
* The {@link ConversationReference.ActivityId} of the conversation
* The {@link ConversationReference#getActivityId} of the conversation
* reference identifies the activity to delete.
* {@linkalso TurnContext.OnDeleteActivity(DeleteActivityHandler)}
*/
public abstract void DeleteActivity(TurnContext context, ConversationReference reference) throws ExecutionException, InterruptedException;
public abstract CompletableFuture<Void> deleteActivityAsync(TurnContext context,
ConversationReference reference);
/**
@ -111,24 +114,24 @@ public abstract class BotAdapter {
* in the pipeline. Once control reaches the end of the pipeline, the adapter calls
* the {@code callback} method. If a middleware component doesnt call
* the next delegate, the adapter does not call any of the subsequent middlewares
* {@link Middleware.OnTurn(TurnContext, MiddlewareSet.NextDelegate)}
* {@link Middleware#onTurnAsync(TurnContext, NextDelegate)}
* methods or the callback method, and the pipeline short circuits.
* <p>When the turn is initiated by a user activity (reactive messaging), the
* callback method will be a reference to the bot's
* {@link Bot.OnTurn(TurnContext)} method. When the turn is
* initiated by a call to {@link ContinueConversation(ConversationReference, Func{TurnContext, Task})}
* {@link Bot#onTurnAsync(TurnContext)} method. When the turn is
* initiated by a call to {@link #continueConversationAsync(String, ConversationReference, BotCallbackHandler)}
* (proactive messaging), the callback method is the callback method that was provided in the call.</p>
*/
protected void RunPipeline(TurnContext context, Consumer<TurnContext> callback) throws Exception {
protected void runPipeline(TurnContext context, BotCallbackHandler callback) throws Exception {
BotAssert.ContextNotNull(context);
// Call any registered Middleware Components looking for ReceiveActivity()
if (context.getActivity() != null) {
_middlewareSet.ReceiveActivityWithStatus(context, callback);
_middlewareSet.receiveActivityWithStatus(context, callback);
} else {
// call back to caller on proactive case
if (callback != null) {
callback.accept(context);
callback.invoke(context);
}
}
}
@ -142,7 +145,8 @@ public abstract class BotAdapter {
* @return A task that represents the work queued to execute.
* @throws UnsupportedOperationException No base implementation is provided.
*/
public CompletableFuture CreateConversation(String channelId, Function<TurnContext, CompletableFuture> callback) {
public CompletableFuture<Void> createConversationAsync(String channelId,
Function<TurnContext, BotCallbackHandler> callback) {
throw new UnsupportedOperationException("Adapter does not support CreateConversation with this arguments");
}
@ -160,13 +164,15 @@ public abstract class BotAdapter {
* before the bot can send activities to the user.
* {@linkalso RunPipeline(TurnContext, Func { TurnContext, Task })}
*/
public void ContinueConversation(String botId, ConversationReference reference, Consumer<TurnContext> callback) throws Exception {
public CompletableFuture<Void> continueConversationAsync(String botId,
ConversationReference reference,
BotCallbackHandler callback) {
ConversationReferenceHelper conv = new ConversationReferenceHelper(reference);
Activity activity = conv.getPostToBotMessage();
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
this.RunPipeline(context, callback);
this.runPipeline(context, callback);
}
}
}

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

@ -0,0 +1,14 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface BotCallbackHandler {
/**
* The callback delegate for application code.
*
* @param turnContext The turn context.
* @return A Task representing the asynchronous operation.
*/
CompletableFuture<Void> invoke(TurnContext turnContext);
}

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

@ -132,10 +132,10 @@ public class BotFrameworkAdapter extends BotAdapter {
claims.put(AuthenticationConstants.APPID_CLAIM, botAppId);
ClaimsIdentity claimsIdentity = new ClaimsIdentity("ExternalBearer", claims);
context.getServices().Add("BotIdentity", claimsIdentity);
context.getTurnState().add("BotIdentity", claimsIdentity);
ConnectorClient connectorClient = this.CreateConnectorClientAsync(reference.getServiceUrl(), claimsIdentity).join();
context.getServices().Add("ConnectorClient", connectorClient);
context.getTurnState().add("ConnectorClient", connectorClient);
RunPipeline(context, callback);
}
return;
@ -218,18 +218,18 @@ public class BotFrameworkAdapter extends BotAdapter {
BotAssert.ActivityNotNull(activity);
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
context.getServices().Add("BotIdentity", identity);
context.getTurnState().add("BotIdentity", identity);
ConnectorClient connectorClient = this.CreateConnectorClientAsync(activity.getServiceUrl(), identity).join();
// TODO: Verify key that C# uses
context.getServices().Add("ConnectorClient", connectorClient);
context.getTurnState().add("ConnectorClient", connectorClient);
super.RunPipeline(context, callback);
// Handle Invoke scenarios, which deviate from the request/response model in that
// the Bot will return a specific body and return code.
if (activity.getType() == ActivityTypes.INVOKE) {
Activity invokeResponse = context.getServices().Get(InvokeReponseKey);
Activity invokeResponse = context.getTurnState().get(InvokeReponseKey);
if (invokeResponse == null) {
// ToDo: Trace Here
throw new IllegalStateException("Bot failed to return a valid 'invokeResponse' activity.");
@ -255,7 +255,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* the receiving channel assigned to the activities.
* {@linkalso TurnContext.OnSendActivities(SendActivitiesHandler)}
*/
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException {
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
if (context == null) {
throw new IllegalArgumentException("context");
}
@ -283,20 +283,20 @@ public class BotFrameworkAdapter extends BotAdapter {
// The Activity Schema doesn't have a delay type build in, so it's simulated
// here in the Bot. This matches the behavior in the Node connector.
int delayMs = (int) activity.getValue();
Thread.sleep(delayMs);
try { Thread.sleep(delayMs); } catch (InterruptedException e) {}
//await(Task.Delay(delayMs));
// No need to create a response. One will be created below.
} else if (activity.getType().toString().equals("invokeResponse")) // Aligning name with Node
{
context.getServices().Add(InvokeReponseKey, activity);
context.getTurnState().add(InvokeReponseKey, activity);
// No need to create a response. One will be created below.
} else if (activity.getType() == ActivityTypes.TRACE && !activity.getChannelId().equals("emulator")) {
// if it is a Trace activity we only send to the channel if it's the emulator.
} else if (!StringUtils.isEmpty(activity.getReplyToId())) {
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
response = connectorClient.getConversations().replyToActivity(activity.getConversation().getId(), activity.getId(), activity);
} else {
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
response = connectorClient.getConversations().sendToConversation(activity.getConversation().getId(), activity);
}
@ -334,7 +334,7 @@ public class BotFrameworkAdapter extends BotAdapter {
*/
@Override
public ResourceResponse UpdateActivity(TurnContext context, Activity activity) {
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
// TODO String conversationId, String activityId, Activity activity)
return connectorClient.getConversations().updateActivity(activity.getConversation().getId(), activity.getId(), activity);
}
@ -348,7 +348,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* {@linkalso TurnContext.OnDeleteActivity(DeleteActivityHandler)}
*/
public void DeleteActivity(TurnContext context, ConversationReference reference) {
RestConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
RestConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
try {
connectorClient.getConversations().deleteConversationMemberAsync(
reference.getConversation().getId(), reference.getActivityId()).join();
@ -373,7 +373,7 @@ public class BotFrameworkAdapter extends BotAdapter {
if (StringUtils.isEmpty(context.getActivity().getConversation().getId()))
throw new IllegalArgumentException("BotFrameworkAdapter.deleteConversationMember(): missing conversation.id");
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
String conversationId = context.getActivity().getConversation().getId();
@ -404,7 +404,7 @@ public class BotFrameworkAdapter extends BotAdapter {
if (StringUtils.isEmpty((context.getActivity().getConversation().getId())))
throw new IllegalArgumentException("BotFrameworkAdapter.GetActivityMembers(): missing conversation.id");
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
String conversationId = context.getActivity().getConversation().getId();
// TODO:
@ -426,7 +426,7 @@ public class BotFrameworkAdapter extends BotAdapter {
if (StringUtils.isEmpty(context.getActivity().getConversation().getId()))
throw new IllegalArgumentException("BotFrameworkAdapter.GetActivityMembers(): missing conversation.id");
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
String conversationId = context.getActivity().getConversation().getId();
// TODO
@ -487,7 +487,7 @@ public class BotFrameworkAdapter extends BotAdapter {
}
public CompletableFuture<ConversationsResult> GetConversations(TurnContextImpl context, String continuationToken) {
ConnectorClient connectorClient = context.getServices().Get("ConnectorClient");
ConnectorClient connectorClient = context.getTurnState().get("ConnectorClient");
// TODO
//ConversationsResult results = await(connectorClient.conversations().getConversationsAsync());
return completedFuture(null);
@ -626,7 +626,7 @@ public class BotFrameworkAdapter extends BotAdapter {
}
protected OAuthClient CreateOAuthApiClient(TurnContext context) throws MalformedURLException, URISyntaxException {
RestConnectorClient client = context.getServices().Get("ConnectorClient");
RestConnectorClient client = context.getTurnState().get("ConnectorClient");
if (client == null) {
throw new IllegalArgumentException("CreateOAuthApiClient: OAuth requires a valid ConnectorClient instance");
}

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

@ -2,164 +2,380 @@
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.StreamSupport;
import static java.util.concurrent.CompletableFuture.completedFuture;
/**
* Abstract Base class which manages details of automatic loading and saving of bot state.
*
* @param TState The type of the bot state object.
* Reads and writes state for your bot to storage.
*/
//public class BotState<TState> : Middleware
// where TState : class, new()
public class BotState<TState> implements Middleware {
private final StateSettings settings;
private final Storage storage;
private final Function<TurnContext, String> keyDelegate;
private final String propertyName;
private final Supplier<? extends TState> ctor;
public abstract class BotState implements PropertyManager {
private String contextServiceKey;
private Storage storage;
/**
* Creates a new {@link BotState{TState}} middleware object.
* Initializes a new instance of the BotState class.
*
* @param name The name to use to load or save the state object.
* @param storage The storage provider to use.
* @param settings The state persistance options to use.
* @param withStorage The storage provider to use.
* @param withContextServiceKey The key for caching on the context services dictionary.
*/
public BotState(Storage storage, String propertyName, Function<TurnContext, String> keyDelegate, Supplier<? extends TState> ctor) {
this(storage, propertyName, keyDelegate, ctor, null);
}
public BotState(Storage withStorage, String withContextServiceKey) {
if (withStorage == null) {
throw new IllegalArgumentException("Storage cannot be null");
}
storage = withStorage;
public BotState(Storage storage, String propertyName, Function<TurnContext, String> keyDelegate, Supplier<? extends TState> ctor, StateSettings settings) {
if (null == storage) {
throw new IllegalArgumentException("Storage");
if (StringUtils.isEmpty(withContextServiceKey)) {
throw new IllegalArgumentException("context service key cannot be empty");
}
if (null == propertyName) {
throw new IllegalArgumentException("String propertyName");
}
if (null == keyDelegate) {
throw new IllegalArgumentException("Key Delegate");
}
if (null == ctor) {
throw new IllegalArgumentException("ctor");
}
this.ctor = ctor;
this.storage = storage;
this.propertyName = propertyName;
this.keyDelegate = keyDelegate;
if (null == settings)
this.settings = new StateSettings();
else
this.settings = settings;
contextServiceKey = withContextServiceKey;
}
/**
* Processess an incoming activity.
* Create a property definition and register it with this BotState.
*
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @param name name of property.
* @param <T> type of property.
* @return The created state property accessor.
*/
public <T extends Object> StatePropertyAccessor<T> createProperty(String name) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("name cannot be empty");
}
return new BotStatePropertyAccessor<>(this, name);
}
/**
* Reads in the current state object and caches it in the context object for this turn.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
* This middleware loads the state object on the leading edge of the middleware pipeline
* and persists the state object on the trailing edge.
*/
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
ReadToContextService(context);
next.next();
WriteFromContextService(context).join();
return;
}
protected void ReadToContextService(TurnContext context) throws IllegalArgumentException, JsonProcessingException {
String key = this.keyDelegate.apply(context);
Map<String, ?> items = null;
try {
CompletableFuture<Map<String, ?>> result = storage.Read(new String[]{key});
items = result.get();
System.out.println(String.format("BotState:OnTurn(tid:%s) ReadToContextService: Found %s items", Thread.currentThread().getId(), items.size()));
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(String.format("Error waiting context storage read: %s", e.toString()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
TState state = StreamSupport.stream(items.entrySet().spliterator(), false)
.filter(entry -> entry.getKey() == key)
.map(Map.Entry::getValue)
.map(entry -> (TState) entry)
.findFirst()
.orElse(null);
//var state = items.Where(entry => entry.Key == key).Select(entry => entry.Value).OfType<TState>().FirstOrDefault();
if (state == null)
state = ctor.get();
context.getServices().Add(this.propertyName, state);
}
protected CompletableFuture WriteFromContextService(TurnContext context) throws Exception {
TState state = context.getServices().Get(this.propertyName);
return Write(context, state);
public CompletableFuture<Void> loadAsync(TurnContext turnContext) {
return loadAsync(turnContext, false);
}
/**
* Reads state from storage.
* Reads in the current state object and caches it in the context object for this turn.
*
* @param TState The type of the bot state object.
* @param context The context object for this turn.
* @param turnContext The context object for this turn.
* @param force True to bypass the cache.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<TState> Read(TurnContext context) throws JsonProcessingException {
String key = this.keyDelegate.apply(context);
Map<String, ?> items = storage.Read(new String[]{key}).join();
TState state = StreamSupport.stream(items.entrySet().spliterator(), false)
.filter(item -> item.getKey() == key)
.map(Map.Entry::getValue)
.map(item -> (TState) item)
.findFirst()
.orElse(null);
//var state = items.Where(entry => entry.Key == key).Select(entry => entry.Value).OfType<TState>().FirstOrDefault();
if (state == null)
state = ctor.get();
return completedFuture(state);
public CompletableFuture<Void> loadAsync(TurnContext turnContext, boolean force) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
String storageKey = getStorageKey(turnContext);
if (force || cachedState == null || cachedState.getState() == null) {
return storage.readAsync(new String[]{storageKey})
.thenApply(val -> {
turnContext.getTurnState().put(contextServiceKey, new CachedBotState(val));
return null;
});
}
return CompletableFuture.completedFuture(null);
}
/**
* Writes state to storage.
* If it has changed, writes to storage the state object that is cached in the current
* context object for this turn.
*
* @param context The context object for this turn.
* @param state The state object.
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture Write(TurnContext context, TState state) throws Exception {
HashMap<String, TState> changes = new HashMap<String, TState>();
//List<Map.Entry<String, ?>> changes = new ArrayList<Map.Entry<String, ?>>();
if (state == null)
state = ctor.get();
String key = keyDelegate.apply(context);
changes.put(key, state);
public CompletableFuture<Void> saveChangesAsync(TurnContext turnContext) {
return saveChangesAsync(turnContext, false);
}
if (this.settings.getLastWriterWins()) {
for (Map.Entry<String, ?> item : changes.entrySet()) {
if (item.getValue() instanceof StoreItem) {
StoreItem valueStoreItem = (StoreItem) item.getValue();
valueStoreItem.seteTag("*");
}
}
/**
* If it has changed, writes to storage the state object that is cached in the current
* context object for this turn.
*
* @param turnContext The context object for this turn.
* @param force True to save state to storage whether or not there are changes.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> saveChangesAsync(TurnContext turnContext, boolean force) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
if (force || (cachedState != null && cachedState.isChanged())) {
String storageKey = getStorageKey(turnContext);
Map<String, Object> changes = new HashMap<String, Object>() {{
put(storageKey, cachedState.state);
}};
return storage.writeAsync(changes)
.thenApply(val -> {
cachedState.setHashCode(cachedState.computeHashCode(cachedState.state));
return null;
});
}
return CompletableFuture.completedFuture(null);
}
/**
* Clears any state currently stored in this state scope.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> clearStateAsync(TurnContext turnContext) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
turnContext.getTurnState().put(contextServiceKey, new CachedBotState(new HashMap<>()));
return CompletableFuture.completedFuture(null);
}
/**
* Delete any state currently stored in this state scope.
*
* @param turnContext The context object for this turn.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> deleteAsync(TurnContext turnContext) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
if (cachedState != null) {
turnContext.getTurnState().remove(contextServiceKey);
}
String storageKey = getStorageKey(turnContext);
return storage.deleteAsync(new String[] {storageKey});
}
/**
* Returns a copy of the raw cached data from the TurnContext, this can be used for tracing scenarios.
*
* @param turnContext The context object for this turn.
* @return A JSON representation of the cached state.
*/
public JsonNode get(TurnContext turnContext) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
String stateKey = getClass().getName();
CachedBotState cachedState = turnContext.getTurnState().get(stateKey);
return new ObjectMapper().valueToTree(cachedState.state);
}
/**
* When overridden in a derived class, gets the key to use when reading and writing state to and from storage.
*
* @param turnContext The context object for this turn.
* @return The storage key.
*/
public abstract String getStorageKey(TurnContext turnContext);
/**
* Gets a property from the state cache in the turn context.
*
* @param turnContext The context object for this turn.
* @param propertyName The name of the property to get.
* @param <T> The property type.
* @return A task that represents the work queued to execute.
*/
protected <T> CompletableFuture<T> getPropertyValueAsync(TurnContext turnContext,
String propertyName) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
if (StringUtils.isEmpty(propertyName)) {
throw new IllegalArgumentException("propertyName cannot be empty");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
return (CompletableFuture<T>) CompletableFuture.completedFuture(cachedState.getState().get(propertyName));
}
/**
* Deletes a property from the state cache in the turn context.
*
* @param turnContext The context object for this turn.
* @param propertyName The name of the property to delete.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> deletePropertyValueAsync(TurnContext turnContext, String propertyName) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
if (StringUtils.isEmpty(propertyName)) {
throw new IllegalArgumentException("propertyName cannot be empty");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
cachedState.getState().remove(propertyName);
return CompletableFuture.completedFuture(null);
}
/**
* Set the value of a property in the state cache in the turn context.
*
* @param turnContext The context object for this turn.
* @param propertyName The name of the property to set.
* @param value The value to set on the property.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> setPropertyValueAsync(TurnContext turnContext,
String propertyName,
Object value) {
if (turnContext == null) {
throw new IllegalArgumentException("turnContext cannot be null");
}
if (StringUtils.isEmpty(propertyName)) {
throw new IllegalArgumentException("propertyName cannot be empty");
}
CachedBotState cachedState = turnContext.getTurnState().get(contextServiceKey);
cachedState.getState().put(propertyName, value);
return CompletableFuture.completedFuture(null);
}
/**
* Internal cached bot state.
*/
private static class CachedBotState {
private Map<String, Object> state;
private int hash;
public CachedBotState(Map<String, Object> withState) {
state = withState;
hash = computeHashCode(withState);
}
public Map<String, Object> getState() {
return state;
}
public void setState(Map<String, Object> withState) {
state = withState;
}
@Override
public int hashCode() {
return hash;
}
public void setHashCode(int witHashCode) {
hash = witHashCode;
}
public boolean isChanged() {
return hash != computeHashCode(state);
}
public int computeHashCode(Object obj) {
//TODO: this may not be the same as in dotnet
return obj.hashCode();
}
}
/**
* Implements IPropertyAccessor for an IPropertyContainer.
*
* Note the semantic of this accessor are intended to be lazy, this means teh Get, Set and Delete
* methods will first call LoadAsync. This will be a no-op if the data is already loaded.
* The implication is you can just use this accessor in the application code directly without first calling LoadAsync
* this approach works with the AutoSaveStateMiddleware which will save as needed at the end of a turn.
*
* @param <T> type of value the propertyAccessor accesses.
*/
private static class BotStatePropertyAccessor<T> implements StatePropertyAccessor<T> {
private String name;
private BotState botState;
public BotStatePropertyAccessor(BotState withState, String withName) {
botState = withState;
name = withName;
}
/**
* Get the property value. The semantics are intended to be lazy, note the use of LoadAsync at the start.
*
* @param turnContext The context object for this turn.
* @param defaultValueFactory Defines the default value. Invoked when no value been set for the requested
* state property. If defaultValueFactory is defined as null,
* the MissingMemberException will be thrown if the underlying property is not set.
* @param <T> type of value the propertyAccessor accesses.
* @return A task that represents the work queued to execute.
*/
@Override
public <T, S> CompletableFuture<T> getAsync(TurnContext turnContext, Supplier<S> defaultValueFactory) {
return botState.loadAsync(turnContext)
.thenCombine(botState.getPropertyValueAsync(turnContext, name), (loadResult, value) -> {
if (value == null) {
value = defaultValueFactory.get();
}
return (T) value;
});
}
/**
* Delete the property. The semantics are intended to be lazy, note the use of LoadAsync at the start.
*
* @param turnContext The turn context.
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<Void> deleteAsync(TurnContext turnContext) {
return botState.loadAsync(turnContext)
.thenCompose(state -> botState.deletePropertyValueAsync(turnContext, name));
}
/**
* Set the property value. The semantics are intended to be lazy, note the use of LoadAsync at the start.
*
* @param turnContext The turn context.
* @param value The value to set.
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<Void> setAsync(TurnContext turnContext, T value) {
return botState.loadAsync(turnContext)
.thenCompose(state -> botState.setPropertyValueAsync(turnContext, name, value));
}
/**
* Gets name of the property.
*
* @return Name of the property.
*/
@Override
public String getName() {
return name;
}
/**
* Sets name of the property.
*
* @param withName Name of the property.
*/
@Override
public void setName(String withName) {
name = withName;
}
return completedFuture(storage.Write(changes).join());
}
}

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

@ -0,0 +1,98 @@
package com.microsoft.bot.builder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
/**
* Manages a collection of botState and provides ability to load and save in parallel.
*/
public class BotStateSet {
private List<BotState> botStates = new ArrayList<>();
/**
* Initializes a new instance of the BotStateSet class.
* @param botStates initial list of {@link BotState} objects to manage.
*/
public BotStateSet(List<BotState> botStates) {
botStates.addAll(botStates);
}
/**
* Gets the BotStates list for the BotStateSet.
* @return The BotState objects managed by this class.
*/
public List<BotState> getBotStates() {
return botStates;
}
/**
* Sets the BotStates list for the BotStateSet.
* @param withBotState The BotState objects managed by this class.
*/
public void setBotStates(List<BotState> withBotState) {
botStates = withBotState;
}
/**
* Adds a bot state object to the set.
*
* @param botState The bot state object to add.
* @return The updated BotStateSet, so you can fluently call add(BotState) multiple times.
*/
public BotStateSet add(BotState botState) {
botStates.add(botState);
return this;
}
/**
* Load all BotState records in parallel.
*
* @param turnContext The TurnContext.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> loadAllAsync(TurnContext turnContext) {
return loadAllAsync(turnContext, false);
}
/**
* Load all BotState records in parallel.
*
* @param turnContext The TurnContext.
* @param force should data be forced into cache.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> loadAllAsync(TurnContext turnContext, boolean force) {
List<CompletableFuture<Void>> loadFutures = botStates.stream()
.map(future -> future.loadAsync(turnContext, force))
.collect(Collectors.toList());
return CompletableFuture.allOf(loadFutures.toArray(new CompletableFuture[loadFutures.size()]));
}
/**
* Save All BotState changes in parallel.
*
* @param turnContext The TurnContext.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> saveAllChangesAsync(TurnContext turnContext) {
return saveAllChangesAsync(turnContext, false);
}
/**
* Save All BotState changes in parallel.
*
* @param turnContext The TurnContext.
* @param force should data be forced to save even if no change were detected.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> saveAllChangesAsync(TurnContext turnContext, boolean force) {
List<CompletableFuture<Void>> saveFutures = botStates.stream()
.map(future -> future.saveChangesAsync(turnContext, force))
.collect(Collectors.toList());
return CompletableFuture.allOf(saveFutures.toArray(new CompletableFuture[saveFutures.size()]));
}
}

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

@ -0,0 +1,124 @@
package com.microsoft.bot.builder;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import java.util.Map;
/**
* Logging client for Bot Telemetry.
*/
public interface BotTelemetryClient {
/**
* Send information about availability of an application.
*
* @param name Availability test name.
* @param timeStamp The time when the availability was captured.
* @param duration The time taken for the availability test to run.
* @param runLocation Name of the location the availability test was run from.
* @param success True if the availability test ran successfully.
*/
default void trackAvailability(String name,
DateTime timeStamp,
Duration duration,
String runLocation,
boolean success) {
trackAvailability(name, timeStamp,duration, runLocation, success, null, null, null);
}
/**
* Send information about availability of an application.
*
* @param name Availability test name.
* @param timeStamp The time when the availability was captured.
* @param duration The time taken for the availability test to run.
* @param runLocation Name of the location the availability test was run from.
* @param success True if the availability test ran successfully.
* @param message Error message on availability test run failure.
* @param properties Named string values you can use to classify and search for this availability telemetry.
* @param metrics Additional values associated with this availability telemetry.
*/
void trackAvailability(String name,
DateTime timeStamp,
Duration duration,
String runLocation,
boolean success,
String message,
Map<String, String> properties,
Map<String, Double> metrics);
/**
* Send information about an external dependency (outgoing call) in the application.
*
* @param dependencyTypeName Name of the command initiated with this dependency call. Low cardinality value.
* Examples are SQL, Azure table, and HTTP.
* @param target External dependency target.
* @param dependencyName Name of the command initiated with this dependency call. Low cardinality value.
* Examples are stored procedure name and URL path template.
* @param data Command initiated by this dependency call. Examples are SQL statement and HTTP URL's with
* all query parameters.
* @param startTime The time when the dependency was called.
* @param duration The time taken by the external dependency to handle the call.
* @param resultCode Result code of dependency call execution.
* @param success True if the dependency call was handled successfully.
*/
void trackDependency(String dependencyTypeName,
String target,
String dependencyName,
String data,
DateTime startTime,
Duration duration,
String resultCode,
boolean success);
/**
* Logs custom events with extensible named fields.
*
* @param eventName A name for the event.
*/
default void trackEvent(String eventName) {
trackEvent(eventName, null, null);
}
/**
* Logs custom events with extensible named fields.
*
* @param eventName A name for the event.
* @param properties Named string values you can use to search and classify events.
* @param metrics Measurements associated with this event.
*/
void trackEvent(String eventName, Map<String, String> properties, Map<String, Double> metrics);
/**
* Logs a system exception.
*
* @param exception The exception to log.
*/
default void trackException(Exception exception) {
trackException(exception, null, null);
}
/**
* Logs a system exception.
*
* @param exception The exception to log.
* @param properties Named string values you can use to classify and search for this exception.
* @param metrics Additional values associated with this exception.
*/
void trackException(Exception exception, Map<String, String> properties, Map<String, Double> metrics);
/**
* Send a trace message.
*
* @param message Message to display.
* @param severityLevel Trace severity level.
* @param properties Named string values you can use to search and classify events.
*/
void trackTrace(String message, Severity severityLevel, Map<String, String> properties);
/**
* Flushes the in-memory buffer and any metrics being pre-aggregated.
*/
void flush();
}

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

@ -1,49 +1,36 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.TurnContext;
import java.util.function.Supplier;
import org.apache.commons.lang3.StringUtils;
/**
* Handles persistence of a conversation state object using the conversation ID as part of the key.
* @param TState The type of the conversation state object.
*/
public class ConversationState<TState> extends BotState<TState>
{
public class ConversationState extends BotState {
/**
* The key to use to read and write this conversation state object to storage.
* Creates a new {@link ConversationState} object.
*/
//
// Note: Hard coded to maintain compatibility with C#
// "ConversationState:{typeof(ConversationState<TState>).Namespace}.{typeof(ConversationState<TState>).Name}"
public static String PropertyName() {
return String.format("ConversationState:Microsoft.Bot.Builder.Core.Extensions.ConversationState`1");
public ConversationState(Storage withStorage) {
super(withStorage, ConversationState.class.getName());
}
/**
* Creates a new {@link ConversationState{TState}} object.
* @param storage The storage provider to use.
* @param settings The state persistance options to use.
*/
public ConversationState(Storage storage, Supplier<? extends TState> ctor) {
this(storage, null, ctor);
}
@Override
public String getStorageKey(TurnContext turnContext) {
if (turnContext.getActivity() == null) {
throw new IllegalArgumentException("invalid activity");
}
public ConversationState(Storage storage, StateSettings settings, Supplier<? extends TState> ctor) {
super(storage, PropertyName(),
(context) -> {
return String.format("conversation/%s/%s", context.getActivity().getChannelId(), context.getActivity().getConversation().getId());
},
ctor,
settings);
}
if (StringUtils.isEmpty(turnContext.getActivity().getChannelId())) {
throw new IllegalArgumentException("invalid activity-missing channelId");
}
/**
* Gets the conversation state object from turn context.
* @param context The context object for this turn.
* @return The coversation state object.
*/
public static <TState> TState Get(TurnContext context) throws IllegalArgumentException {
return context.getServices().Get(PropertyName());
if (turnContext.getActivity().getConversation() == null
|| StringUtils.isEmpty(turnContext.getActivity().getConversation().getId())) {
throw new IllegalArgumentException("invalid activity-missing Conversation.Id");
}
// {channelId}/conversations/{conversationId}
return turnContext.getActivity().getChannelId()
+ "/conversations/"
+ turnContext.getActivity().getConversation().getId();
}
}

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

@ -0,0 +1,93 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationReference;
import com.microsoft.bot.schema.ResourceResponse;
import java.util.concurrent.CompletableFuture;
/**
* A TurnContext that wraps an untyped inner TurnContext.
*/
public class DelegatingTurnContext implements TurnContext {
private TurnContext innerTurnContext;
public DelegatingTurnContext(TurnContext withTurnContext) {
innerTurnContext = withTurnContext;
}
@Override
public BotAdapter getAdapter() {
return innerTurnContext.getAdapter();
}
@Override
public TurnContextStateCollection getTurnState() {
return innerTurnContext.getTurnState();
}
@Override
public Activity getActivity() {
return innerTurnContext.getActivity();
}
@Override
public boolean getResponded() {
return innerTurnContext.getResponded();
}
@Override
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend) {
return innerTurnContext.sendActivityAsync(textReplyToSend);
}
@Override
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak) {
return innerTurnContext.sendActivityAsync(textReplyToSend, speak);
}
@Override
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak, String inputHint) {
return innerTurnContext.sendActivityAsync(textReplyToSend, speak, inputHint);
}
@Override
public CompletableFuture<ResourceResponse> sendActivityAsync(Activity activity) {
return innerTurnContext.sendActivityAsync(activity);
}
@Override
public CompletableFuture<ResourceResponse[]> sendActivitiesAsync(Activity[] activities) {
return innerTurnContext.sendActivitiesAsync(activities);
}
@Override
public CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity) {
return innerTurnContext.updateActivityAsync(activity);
}
@Override
public CompletableFuture<Void> deleteActivityAsync(String activityId) {
return innerTurnContext.deleteActivityAsync(activityId);
}
@Override
public CompletableFuture<Void> deleteActivityAsync(ConversationReference conversationReference) {
return innerTurnContext.deleteActivityAsync(conversationReference);
}
@Override
public TurnContext onSendActivities(SendActivitiesHandler handler) {
return innerTurnContext.onSendActivities(handler);
}
@Override
public TurnContext onUpdateActivity(UpdateActivityHandler handler) {
return innerTurnContext.onUpdateActivity(handler);
}
@Override
public TurnContext onDeleteActivity(DeleteActivityHandler handler) {
return innerTurnContext.onDeleteActivity(handler);
}
}

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

@ -2,24 +2,25 @@ package com.microsoft.bot.builder;
import com.microsoft.bot.schema.ConversationReference;
/**
* A method that can participate in delete activity events for the current turn.
* @param context The context object for the turn.
* @param reference The conversation containing the activity.
* @param next The delegate to call to continue event processing.
* @return A task that represents the work queued to execute.
* A handler calls the {@code next} delegate to pass control to
* the next registered handler. If a handler doesnt call the next delegate,
* the adapter does not call any of the subsequent handlers and does not delete the
*activity.
* <p>The conversation reference's {@link ConversationReference.ActivityId}
* indicates the activity in the conversation to replace.</p>
*
* {@linkalso BotAdapter}
* {@linkalso SendActivitiesHandler}
* {@linkalso UpdateActivityHandler}
*/
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
@FunctionalInterface
public interface DeleteActivityHandler {
void handle(TurnContext context, ConversationReference reference, Runnable next) throws Exception;
/**
* A method that can participate in delete activity events for the current turn.
* @param context The context object for the turn.
* @param reference The conversation containing the activity.
* @param next The delegate to call to continue event processing.
* @return A task that represents the work queued to execute.
* A handler calls the {@code next} delegate to pass control to
* the next registered handler. If a handler doesnt call the next delegate,
* the adapter does not call any of the subsequent handlers and does not delete the
*activity.
* <p>The conversation reference's {@link ConversationReference#getActivityId}
* indicates the activity in the conversation to replace.</p>
*/
CompletableFuture<Void> invoke(TurnContext context,
ConversationReference reference,
Supplier<CompletableFuture<Void>> next);
}

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

@ -0,0 +1,34 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.HashMap;
import java.util.Map;
public class IntentScore {
@JsonProperty
private double score;
private HashMap<String, JsonNode> properties = new HashMap<>();
public double getScore() {
return score;
}
public void setScore(double withScore) {
score = withScore;
}
@JsonAnyGetter
public Map<String, JsonNode> getProperties() {
return this.properties;
}
@JsonAnySetter
public void setProperties(String key, JsonNode value) {
this.properties.put(key, value);
}
}

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

@ -9,29 +9,29 @@ package com.microsoft.bot.builder;
public class InvokeResponse {
/**
* The POST that is generated in response to the incoming Invoke Activity
* will have the HTTP Status code specificied by this field.
* will have the HTTP Status code specified by this field.
*/
private int _status;
private int status;
public int getStatus() {
return this._status;
return status;
}
public void setStatus(int status) {
this._status = status;
public void setStatus(int withStatus) {
this.status = withStatus;
}
/**
* The POST that is generated in response to the incoming Invoke Activity
* will have a body generated by JSON serializing the object in the Body field.
*/
private Object _body;
private Object body;
public Object getBody() {
return _body;
return body;
}
public void setBody(Object body) {
this._body = body;
public void setBody(Object withBody) {
body = withBody;
}
}

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

@ -0,0 +1,135 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class MemoryStorage implements Storage {
private ObjectMapper objectMapper;
private Map<String, JsonNode> memory = new HashMap<>();
private static final String TYPENAMEFORNONENTITY = "__type_name_";
private final Object syncroot = new Object();
private Logger logger = LoggerFactory.getLogger(MemoryStorage.class);
private int _eTag = 0;
public MemoryStorage(Map<String, JsonNode> values) {
objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.findAndRegisterModules();
if (values != null)
memory = values;
}
@Override
public CompletableFuture<Map<String, Object>> readAsync(String[] keys) {
if (keys == null) {
throw new IllegalArgumentException("keys cannot be null");
}
Map<String, Object> storeItems = new HashMap<String, Object>(keys.length);
synchronized (this.syncroot) {
for (String key : keys) {
if (memory.containsKey(key)) {
Object state = memory.get(key);
if (state != null) {
try {
if (!(state instanceof JsonNode))
throw new RuntimeException("DictionaryRead failed: entry not JsonNode");
JsonNode stateNode = (JsonNode) state;
// Check if type info is set for the class
if (!(stateNode.hasNonNull(TYPENAMEFORNONENTITY))) {
logger.error("Read failed: Type info not present for " + key );
throw new RuntimeException(String.format("Read failed: Type info not present for key " + key));
}
String clsName = stateNode.get(TYPENAMEFORNONENTITY).textValue();
// Load the class info
Class<?> cls;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
logger.error("Read failed: Could not load class {}", clsName);
throw new RuntimeException(String.format("Read failed: Could not load class %s", clsName));
}
// Populate dictionary
storeItems.put(key,objectMapper.treeToValue(stateNode, cls ));
} catch (JsonProcessingException e) {
logger.error("Read failed: {}", e.toString());
throw new RuntimeException(String.format("Read failed: %s", e.toString()));
}
}
}
}
}
return CompletableFuture.completedFuture(storeItems);
}
@Override
public CompletableFuture<Void> writeAsync(Map<String, Object> changes) {
synchronized (this.syncroot) {
for (Map.Entry change : changes.entrySet()) {
Object newValue = change.getValue();
String oldStateETag = null;
if (memory.containsValue(change.getKey())) {
Map oldState = (Map) memory.get(change.getKey());
if (oldState.containsValue("eTag")) {
Map.Entry eTagToken = (Map.Entry) oldState.get("eTag");
oldStateETag = (String) eTagToken.getValue();
}
}
// Dictionary stores Key:JsonNode (with type information held within the JsonNode)
JsonNode newState = objectMapper.valueToTree(newValue);
((ObjectNode)newState).put(this.TYPENAMEFORNONENTITY, newValue.getClass().getTypeName());
// Set ETag if applicable
if (newValue instanceof StoreItem) {
StoreItem newStoreItem = (StoreItem) newValue;
if(oldStateETag != null && newStoreItem.getETag() != "*" &&
newStoreItem.getETag() != oldStateETag) {
String msg = String.format("Etag conflict. Original: %s, Current: %s",
newStoreItem.getETag(), oldStateETag);
logger.error(msg);
throw new RuntimeException(msg);
}
Integer newTag = _eTag++;
((ObjectNode)newState).put("eTag", newTag.toString());
}
memory.put((String)change.getKey(), newState);
}
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<Void> deleteAsync(String[] keys) {
if (keys == null) {
throw new IllegalArgumentException("keys cannot be null");
}
synchronized (this.syncroot) {
for (String key : keys) {
Object o = memory.get(key);
memory.remove(o);
}
}
return CompletableFuture.completedFuture(null);
}
}

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

@ -37,7 +37,8 @@ public class MemoryTranscriptStore implements TranscriptStore {
* @param activity The activity to log.
* @return A CompletableFuture that represents the work queued to execute.
*/
public final void LogActivityAsync(Activity activity) {
@Override
public final CompletableFuture<Void> logActivityAsync(Activity activity) {
if (activity == null) {
throw new NullPointerException("activity cannot be null for LogActivity()");
}
@ -53,7 +54,6 @@ public class MemoryTranscriptStore implements TranscriptStore {
ArrayList<Activity> transcript = null;
if (!channel.containsKey(activity.getConversation().getId())) {
transcript = new ArrayList<Activity>();
channel.put(activity.getConversation().getId(), transcript);
@ -64,33 +64,7 @@ public class MemoryTranscriptStore implements TranscriptStore {
transcript.add(activity);
}
}
/**
* Gets from the store activities that match a set of criteria.
*
* @param channelId The ID of the channel the conversation is in.
* @param conversationId The ID of the conversation.
* @param continuationToken
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
public final CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken) {
return GetTranscriptActivitiesAsync(channelId, conversationId, continuationToken, null);
}
/**
* Gets from the store activities that match a set of criteria.
*
* @param channelId The ID of the channel the conversation is in.
* @param conversationId The ID of the conversation.
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
public final CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId) {
return GetTranscriptActivitiesAsync(channelId, conversationId, null, null);
return CompletableFuture.completedFuture(null);
}
/**
@ -103,16 +77,20 @@ public class MemoryTranscriptStore implements TranscriptStore {
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
public final CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken, DateTime startDate) {
@Override
public CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
String conversationId,
String continuationToken,
DateTime startDate) {
if (channelId == null) {
throw new IllegalArgumentException(String.format("missing %1$s", "channelId"));
}
if (conversationId == null) {
throw new IllegalArgumentException(String.format("missing %1$s", "conversationId"));
}
return CompletableFuture.supplyAsync(() -> {
if (channelId == null) {
throw new NullPointerException(String.format("missing %1$s", "channelId"));
}
if (conversationId == null) {
throw new NullPointerException(String.format("missing %1$s", "conversationId"));
}
PagedResult<Activity> pagedResult = new PagedResult<Activity>();
synchronized (channels) {
HashMap<String, ArrayList<Activity>> channel;
@ -165,16 +143,17 @@ public class MemoryTranscriptStore implements TranscriptStore {
* @param conversationId The ID of the conversation to delete.
* @return A task that represents the work queued to execute.
*/
public final CompletableFuture DeleteTranscriptAsync(String channelId, String conversationId) {
@Override
public CompletableFuture<Void> deleteTranscriptAsync(String channelId, String conversationId) {
if (channelId == null) {
throw new IllegalArgumentException(String.format("%1$s should not be null", "channelId"));
}
if (conversationId == null) {
throw new IllegalArgumentException(String.format("%1$s should not be null", "conversationId"));
}
return CompletableFuture.runAsync(() -> {
if (channelId == null) {
throw new NullPointerException(String.format("%1$s should not be null", "channelId"));
}
if (conversationId == null) {
throw new NullPointerException(String.format("%1$s should not be null", "conversationId"));
}
synchronized (this.channels) {
if (!this.channels.containsKey(channelId)) {
return;
@ -187,17 +166,6 @@ public class MemoryTranscriptStore implements TranscriptStore {
}, ExecutorFactory.getExecutor());
}
/**
* Gets the conversations on a channel from the store.
*
* @param channelId The ID of the channel.
* @return A task that represents the work queued to execute.
*/
public final CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId) {
return ListTranscriptsAsync(channelId, null);
}
/**
* Gets the conversations on a channel from the store.
*
@ -205,13 +173,13 @@ public class MemoryTranscriptStore implements TranscriptStore {
* @param continuationToken
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId, String continuationToken) {
if (channelId == null) {
throw new IllegalArgumentException(String.format("missing %1$s", "channelId"));
}
public final CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId, String continuationToken) {
return CompletableFuture.supplyAsync(() -> {
if (channelId == null) {
throw new NullPointerException(String.format("missing %1$s", "channelId"));
}
PagedResult<Transcript> pagedResult = new PagedResult<Transcript>();
synchronized (channels) {

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

@ -0,0 +1,305 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.*;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Contains utility methods for various message types a bot can return.
*
* Create and send a message.
* <code>
* Activity message = MessageFactory.text("Hello World");
* conext.sendActivity(message);
* </code>
*
* <p>The following apply to message actions in general.
* See the channel's documentation for limits imposed upon the contents of
* the text of the message to send.</p>
*
* <p>To control various characteristics of your bot's speech such as voice,
* rate, volume, pronunciation, and pitch, specify test to speak in
* Speech Synthesis Markup Language (SSML) format.</p>
*
* <p>Channels decide how each card action manifests in their user experience.
* In most cases, the cards are clickable. In others, they may be selected by speech
* input. In cases where the channel does not offer an interactive activation
* experience (e.g., when interacting over SMS), the channel may not support
* activation whatsoever. The decision about how to render actions is controlled by
* normative requirements elsewhere in this document (e.g. within the card format,
* or within the suggested actions definition).</p>
*/
public final class MessageFactory {
private MessageFactory() {
}
/**
* Returns a simple text message.
*
* @param text The text of the message to send.
* @return A message activity containing the text.
*/
public static Activity text(String text) {
return text(text);
}
/**
* Returns a simple text message.
*
* @param text The text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enabled channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return A message activity containing the text.
*/
public static Activity text(String text, String ssml, InputHints inputHint) {
Activity activity = Activity.createMessageActivity();
setTextAndSpeech(activity, text, ssml, inputHint);
return activity;
}
/**
* Returns a message that includes a set of suggested actions and optional text.
*
* <code>
* // Create the activity and add suggested actions.
* Activity activity = MessageFactory.suggestedActions(
* new String[] { "red", "green", "blue" },
* "Choose a color");
*
* // Send the activity as a reply to the user.
* context.sendActivity(activity);
* </code>
* @param actions The text of the actions to create.
* @param text Optional. The text of the message to send.
* @return A message activity containing the suggested actions.
*/
public static Activity suggestedActions(List<String> actions, String text) {
return suggestedActions(actions, text, null, null);
}
/**
* Returns a message that includes a set of suggested actions and optional text.
*
* <code>
* // Create the activity and add suggested actions.
* Activity activity = MessageFactory.suggestedActions(
* new String[] { "red", "green", "blue" },
* "Choose a color");
*
* // Send the activity as a reply to the user.
* context.sendActivity(activity);
* </code>
* @param actions The text of the actions to create.
* @param text Optional. The text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return A message activity containing the suggested actions.
*/
public static Activity suggestedActions(List<String> actions, String text, String ssml, InputHints inputHint) {
if (actions == null) {
throw new IllegalArgumentException("actions cannot be null");
}
List<CardAction> cardActions = new ArrayList<>();
for (String action : actions) {
CardAction cardAction = new CardAction() {{
setType(ActionTypes.IM_BACK);
setValue(action);
setTitle(action);
}};
cardActions.add(cardAction);
}
return suggestedCardActions(cardActions, text, ssml, inputHint);
}
/**
* Returns a message that includes a set of suggested actions and optional text.
*
* @param actions The card actions to include.
* @param text Optional, the text of the message to send.
* @return
*/
public static Activity suggestedCardActions(List<CardAction> actions, String text) {
return suggestedCardActions(actions, text, null, null);
}
/**
* Returns a message that includes a set of suggested actions and optional text.
*
* @param actions The card actions to include.
* @param text Optional, the text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return
*/
public static Activity suggestedCardActions(List<CardAction> actions,
String text,
String ssml,
InputHints inputHint) {
if (actions == null) {
throw new IllegalArgumentException("actions cannot be null");
}
Activity activity = Activity.createMessageActivity();
setTextAndSpeech(activity, text, ssml, inputHint);
activity.setSuggestedActions(new SuggestedActions(actions.toArray(new CardAction[actions.size()])));
return activity;
}
/**
* Returns a message activity that contains an attachment.
*
* @param attachment Attachment to include in the message.
* @param text Optional, the text of the message to send.
* @return A message activity containing the attachment.
*/
public static Activity attachment(Attachment attachment, String text) {
return attachment(attachment, text, null, null);
}
/**
* Returns a message activity that contains an attachment.
*
* @param attachment Attachment to include in the message.
* @param text Optional, the text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return A message activity containing the attachment.
*/
public static Activity attachment(Attachment attachment, String text, String ssml, InputHints inputHint) {
if (attachment == null) {
throw new IllegalArgumentException("attachment cannot be null");
}
return attachment(Collections.singletonList(attachment), text, ssml, inputHint);
}
/**
* Returns a message activity that contains an attachment.
*
* @param attachments Attachments to include in the message.
* @param text Optional, the text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return A message activity containing the attachment.
*/
public static Activity attachment(List<Attachment> attachments, String text, String ssml, InputHints inputHint) {
if (attachments == null) {
throw new IllegalArgumentException("attachments cannot be null");
}
return attachmentActivity(AttachmentLayoutTypes.LIST, attachments, text, ssml, inputHint);
}
/**
* Returns a message activity that contains a collection of attachments, in a list.
*
* @param attachments Attachments to include in the message.
* @param text Optional, the text of the message to send.
* @return A message activity containing the attachment.
*/
public static Activity carousel(List<Attachment> attachments, String text) {
return carousel(attachments, text, null, null);
}
/**
* Returns a message activity that contains a collection of attachments, in a list.
*
* @param attachments Attachments to include in the message.
* @param text Optional, the text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return A message activity containing the attachment.
*/
public static Activity carousel(List<Attachment> attachments, String text, String ssml, InputHints inputHint) {
if (attachments == null) {
throw new IllegalArgumentException("attachments cannot be null");
}
return attachmentActivity(AttachmentLayoutTypes.CAROUSEL, attachments, text, ssml, inputHint);
}
/**
* Returns a message activity that contains a single image or video.
*
* @param url The URL of the image or video to send.
* @param contentType The MIME type of the image or video.
* @return A message activity containing the attachment.
*/
public static Activity contentUrl(String url, String contentType) {
return contentUrl(url, contentType, null, null, null, null);
}
/**
* Returns a message activity that contains a single image or video.
*
* @param url The URL of the image or video to send.
* @param contentType The MIME type of the image or video.
* @param name Optional, the name of the image or video file.
* @param text Optional, the text of the message to send.
* @param ssml Optional, text to be spoken by your bot on a speech-enable channel.
* @param inputHint Optional, indicates whether your bot is accepting, expecting, or ignoring user input
* after the message is delivered to the client. Default is {@link InputHints#ACCEPTING_INPUT}.
* @return A message activity containing the attachment.
*/
public static Activity contentUrl(String url,
String contentType,
String name,
String text,
String ssml,
InputHints inputHint) {
if (StringUtils.isEmpty(url)) {
throw new IllegalArgumentException("url cannot be null or empty");
}
if (StringUtils.isEmpty(contentType)) {
throw new IllegalArgumentException("contentType cannot be null or empty");
}
Attachment attachment = new Attachment(){{
setContentType(contentType);
setContentUrl(url);
setName(StringUtils.isEmpty(name) ? null : name);
}};
return attachmentActivity(AttachmentLayoutTypes.LIST, Arrays.asList(new Attachment[]{attachment}),
text, ssml, inputHint);
}
private static Activity attachmentActivity(AttachmentLayoutTypes attachmentLayout,
List<Attachment> attachments,
String text,
String ssml,
InputHints inputHint) {
Activity activity = Activity.createMessageActivity();
activity.setAttachmentLayout(attachmentLayout);
activity.setAttachments(attachments);
setTextAndSpeech(activity, text, ssml, inputHint);
return activity;
}
private static void setTextAndSpeech(Activity activity, String text, String ssml, InputHints inputHint) {
activity.setText(StringUtils.isEmpty(text) ? null : text);
activity.setSpeak(StringUtils.isEmpty(ssml) ? null : ssml);
activity.setInputHint(
inputHint == null ? InputHints.ACCEPTING_INPUT : inputHint);
}
}

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

@ -2,23 +2,25 @@
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
/**
* Represents middleware that can operate on incoming activities.
* A {@link BotAdapter} passes incoming activities from the user's
* channel to the middleware's {@link OnTurn(TurnContext, NextDelegate)}
* channel to the middleware's {@link #onTurn(TurnContext, NextDelegate)}
* method.
* <p>You can add middleware objects to your adapters middleware collection. The
* adapter processes and directs incoming activities in through the bot middleware
* pipeline to your bots logic and then back out again. As each activity flows in
* and out of the bot, each piece of middleware can inspect or act upon the activity,
* adapter processes and directs incoming activities in through the bot middleware
* pipeline to your bots logic and then back out again. As each activity flows in
* and out of the bot, each piece of middleware can inspect or act upon the activity,
* both before and after the bot logic runs.</p>
* <p>For each activity, the adapter calls middleware in the order in which you
* <p>For each activity, the adapter calls middleware in the order in which you
* added it.</p>
*
* <example>
* This defines middleware that sends "before" and "after" messages
* before and after the adapter calls the bot's
* {@link Bot.OnTurn(TurnContext)} method.
* before and after the adapter calls the bot's
* {@link Bot#onTurnAsync(TurnContext)} method.
* <code>
* public class SampleMiddleware : Middleware
* {
@ -33,8 +35,7 @@ package com.microsoft.bot.builder;
* </example>
* {@linkalso Bot}
*/
public interface Middleware
{
public interface Middleware {
/**
* Processess an incoming activity.
* @param context The context object for this turn.
@ -42,15 +43,15 @@ public interface Middleware
* @return A task that represents the work queued to execute.
* Middleware calls the {@code next} delegate to pass control to
* the next middleware in the pipeline. If middleware doesnt call the next delegate,
* the adapter does not call any of the subsequent middlewares request handlers or the
* 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
* <p>The {@code context} provides information about the
* incoming activity, and other data needed to process the activity.</p>
*
* {@linkalso TurnContext}
* {@linkalso Bot.Schema.Activity}
* {@link TurnContext}
* {@link com.microsoft.bot.schema.Activity}
*/
void OnTurn(TurnContext context, NextDelegate next) throws Exception;
CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next);
}

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

@ -1,6 +1,8 @@
package com.microsoft.bot.builder;
@FunctionalInterface
public interface MiddlewareCall {
void requestHandler(TurnContext tc, NextDelegate nd) throws Exception;
}
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface MiddlewareCall {
CompletableFuture<Void> requestHandler(TurnContext tc, NextDelegate nd);
}

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

@ -1,97 +1,79 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
public class MiddlewareSet implements Middleware {
public NextDelegate Next;
private final ArrayList<Middleware> _middleware = new ArrayList<Middleware>();
public MiddlewareSet Use(Middleware middleware) {
BotAssert.MiddlewareNotNull(middleware);
_middleware.add(middleware);
return this;
}
public void ReceiveActivity(TurnContextImpl context)
throws Exception {
ReceiveActivityInternal(context, null);
}
@Override
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
ReceiveActivityInternal((TurnContextImpl) context, null);
try {
next.next();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("MiddlewareSet::OnTurn next delegate: %s", e.toString()));
}
}
public void OnTurn(TurnContextImpl context, CompletableFuture next)
throws ExecutionException, InterruptedException {
return;
}
/**
* Intended to be called from Bot, this method performs exactly the same as the
* standard ReceiveActivity, except that it runs a user-defined delegate returns
* if all Middleware in the receive pipeline was run.
*/
public void ReceiveActivityWithStatus(TurnContext context, Consumer<TurnContext> callback)
throws Exception {
ReceiveActivityInternal(context, callback);
}
private void ReceiveActivityInternal(TurnContext context, Consumer<TurnContext> callback)
throws Exception {
ReceiveActivityInternal(context, callback, 0);
}
private void ReceiveActivityInternal(TurnContext context, Consumer<TurnContext> callback, int nextMiddlewareIndex)
throws Exception {
// Check if we're at the end of the middleware list yet
if (nextMiddlewareIndex == _middleware.size()) {
// If all the Middlware ran, the "leading edge" of the tree is now complete.
// This means it's time to run any developer specified callback.
// Once this callback is done, the "trailing edge" calls are then completed. This
// allows code that looks like:
// Trace.TraceInformation("before");
// await next();
// Trace.TraceInformation("after");
// to run as expected.
// If a callback was provided invoke it now and return its task, otherwise just return the completed task
if (callback == null) {
return ;
} else {
callback.accept(context);
return;
}
}
// Get the next piece of middleware
Middleware nextMiddleware = _middleware.get(nextMiddlewareIndex);
NextDelegate next = new NextDelegate() {
public void next() throws Exception {
ReceiveActivityInternal(context, callback, nextMiddlewareIndex + 1);
}
};
// Execute the next middleware passing a closure that will recurse back into this method at the next piece of middlware as the NextDelegate
nextMiddleware.OnTurn(
context,
next);
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
/**
* Contains an ordered set of {@link Middleware}.
*/
public class MiddlewareSet implements Middleware {
public NextDelegate Next;
private final ArrayList<Middleware> middleware = new ArrayList<>();
/**
* Adds a middleware object to the end of the set.
*
* @param middleware The middleware to add.
* @return The updated middleware set.
*/
public MiddlewareSet use(Middleware middleware) {
BotAssert.MiddlewareNotNull(middleware);
this.middleware.add(middleware);
return this;
}
@Override
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
return receiveActivityInternal((TurnContextImpl) context, null)
.thenCompose((result) -> next.next());
}
/**
* Processes an activity.
*
* @param context The context object for the turn.
* @param callback The delegate to call when the set finishes processing the activity.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> receiveActivityWithStatusAsync(TurnContext context, BotCallbackHandler callback) {
return receiveActivityInternal(context, callback);
}
private CompletableFuture<Void> receiveActivityInternal(TurnContext context, BotCallbackHandler callback) {
return receiveActivityInternal(context, callback, 0);
}
private CompletableFuture<Void> receiveActivityInternal(TurnContext context,
BotCallbackHandler callback,
int nextMiddlewareIndex) {
// Check if we're at the end of the middleware list yet
if (nextMiddlewareIndex == middleware.size()) {
// If all the Middlware ran, the "leading edge" of the tree is now complete.
// This means it's time to run any developer specified callback.
// Once this callback is done, the "trailing edge" calls are then completed. This
// allows code that looks like:
// Trace.TraceInformation("before");
// await next();
// Trace.TraceInformation("after");
// to run as expected.
// If a callback was provided invoke it now and return its task, otherwise just return the completed task
if (callback == null) {
return CompletableFuture.completedFuture(null);
} else {
return callback.invoke(context);
}
}
// Get the next piece of middleware
Middleware nextMiddleware = middleware.get(nextMiddlewareIndex);
// Execute the next middleware passing a closure that will recurse back into this method at the
// next piece of middlware as the NextDelegate
return nextMiddleware.onTurnAsync(context, () ->
receiveActivityInternal(context, callback, nextMiddlewareIndex + 1));
}
}

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

@ -1,6 +1,8 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface NextDelegate {
void next() throws Exception;
CompletableFuture<Void> next();
}

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

@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import java.util.Map;
public class NullBotTelemetryClient implements BotTelemetryClient {
@Override
public void trackAvailability(String name, DateTime timeStamp, Duration duration, String runLocation, boolean success, String message, Map<String, String> properties, Map<String, Double> metrics) {
}
@Override
public void trackDependency(String dependencyTypeName, String target, String dependencyName, String data, DateTime startTime, Duration duration, String resultCode, boolean success) {
}
@Override
public void trackEvent(String eventName, Map<String, String> properties, Map<String, Double> metrics) {
}
@Override
public void trackException(Exception exception, Map<String, String> properties, Map<String, Double> metrics) {
}
@Override
public void trackTrace(String message, Severity severityLevel, Map<String, String> properties) {
}
@Override
public void flush() {
}
}

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

@ -0,0 +1,15 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface OnTurnErrorHandler {
/**
* Gets or sets an error handler that can catch exceptions in the middleware or application.
*
* @param turnContext The context object for this turn.
* @param exception The exception thrown.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> invoke(TurnContext turnContext, Throwable exception);
}

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

@ -1,43 +1,37 @@
package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
/**
Page of results from an enumeration.
<typeparam name="T"></typeparam>
*/
public class PagedResult<T>
{
/**
Page of items.
*/
//C# TO JAVA CONVERTER WARNING: Java does not allow direct instantiation of arrays of generic type parameters:
//ORIGINAL LINE: private T[] Items = new T[0];
private T[] items = (T[])new Object[0];
public final T[] getItems()
{
return this.items;
}
public final void items(T[] value)
{
this.items = value;
}
/**
Token used to page through multiple pages.
*/
private String continuationToken;
public final String continuationToken()
{
return this.continuationToken;
}
public final PagedResult<T> withContinuationToken(String value)
{
this.continuationToken = value;
return this;
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
/**
* Page of results from an enumeration.
* @param <T> The type of items in the results.
*/
public class PagedResult<T> {
/**
* Page of items.
*/
private T[] items = (T[])new Object[0];
/**
Token used to page through multiple pages.
*/
private String continuationToken;
public T[] getItems() {
return this.items;
}
public void setItems(T[] value) {
this.items = value;
}
public String getContinuationToken() {
return continuationToken;
}
public void setContinuationToken(String withValue) {
continuationToken = withValue;
}
}

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

@ -0,0 +1,53 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import org.apache.commons.lang3.StringUtils;
/**
* Handles persistence of a conversation state object using the conversation.Id and from.Id part of an activity.
*/
public class PrivateConversationState extends BotState {
/**
* Initializes a new instance of the PrivateConversationState class.
* @param storage The storage provider to use.
*/
public PrivateConversationState(Storage storage) {
super(storage, PrivateConversationState.class.getName());
}
/**
* Gets the key to use when reading and writing state to and from storage.
*
* @param turnContext The context object for this turn.
* @return The storage key.
*/
@Override
public String getStorageKey(TurnContext turnContext) {
if (turnContext.getActivity() == null) {
throw new IllegalArgumentException("invalid activity");
}
if (StringUtils.isEmpty(turnContext.getActivity().getChannelId())) {
throw new IllegalArgumentException("invalid activity-missing channelId");
}
if (turnContext.getActivity().getConversation() == null
|| StringUtils.isEmpty(turnContext.getActivity().getConversation().getId())) {
throw new IllegalArgumentException("invalid activity-missing Conversation.Id");
}
if (turnContext.getActivity().getFrom() == null
|| StringUtils.isEmpty(turnContext.getActivity().getFrom().getId())) {
throw new IllegalArgumentException("invalid activity-missing From.Id");
}
// {channelId}/conversations/{conversationId}/users/{userId}
return turnContext.getActivity().getChannelId()
+ "/conversations/"
+ turnContext.getActivity().getConversation().getId()
+ "/users/"
+ turnContext.getActivity().getFrom().getId();
}
}

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

@ -0,0 +1,5 @@
package com.microsoft.bot.builder;
public interface PropertyManager {
<T> StatePropertyAccessor<T> createProperty(String name);
}

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

@ -0,0 +1,7 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
public interface Recognizer {
CompletableFuture<RecognizerResult> recognizeAsync(TurnContext turnContext);
}

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

@ -0,0 +1,5 @@
package com.microsoft.bot.builder;
public interface RecognizerConvert {
void convert(Object result);
}

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

@ -0,0 +1,95 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.HashMap;
import java.util.Map;
public class RecognizerResult implements RecognizerConvert {
@JsonProperty
private String text;
@JsonProperty
private String alteredText;
@JsonProperty
private Map<String, IntentScore> intents;
@JsonProperty
JsonNode entities;
private HashMap<String, JsonNode> properties = new HashMap<>();
public IntentScore getTopScoringIntent() {
if (getIntents() == null) {
throw new IllegalArgumentException("RecognizerResult.Intents cannot be null");
}
IntentScore topIntent = new IntentScore();
for (Map.Entry<String, IntentScore> intent : getIntents().entrySet()) {
double score = intent.getValue().getScore();
if (score > topIntent.getScore()) {
topIntent = intent.getValue();
}
}
return topIntent;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getAlteredText() {
return alteredText;
}
public void setAlteredText(String alteredText) {
this.alteredText = alteredText;
}
public Map<String, IntentScore> getIntents() {
return intents;
}
public void setIntents(Map<String, IntentScore> intents) {
this.intents = intents;
}
public JsonNode getEntities() {
return entities;
}
public void setEntities(JsonNode entities) {
this.entities = entities;
}
@JsonAnyGetter
public Map<String, JsonNode> getProperties() {
return this.properties;
}
@JsonAnySetter
public void setProperties(String key, JsonNode value) {
this.properties.put(key, value);
}
@Override
public void convert(Object result) {
setText(((RecognizerResult)result).getText());
setAlteredText((((RecognizerResult)result).getAlteredText()));
setIntents(((RecognizerResult)result).getIntents());
setEntities(((RecognizerResult)result).getEntities());
for (String key : ((RecognizerResult)result).getProperties().keySet()) {
setProperties(key, ((RecognizerResult)result).getProperties().get(key));
}
}
}

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

@ -1,12 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ResourceResponse;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
@FunctionalInterface
public interface SendActivitiesHandler {
ResourceResponse[] handle(TurnContext context, List<Activity> activities, Callable<ResourceResponse[]> next) throws Exception;
/**
* A method that can participate in send activity events for the current turn.
*
* @param context The context object for the turn.
* @param activities The activities to send.
* @param next The delegate to call to continue event processing.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<ResourceResponse[]> invoke(TurnContext context,
List<Activity> activities,
Supplier<CompletableFuture<ResourceResponse[]>> next);
}

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

@ -0,0 +1,19 @@
package com.microsoft.bot.builder;
public enum Severity {
VERBOSE(0),
INFORMATION(1),
WARNING(2),
ERROR(3),
CRITICAL(4);
private int value;
Severity(int witValue) {
value = witValue;
}
public int getSeverity() {
return value;
}
}

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

@ -0,0 +1,12 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
public interface StatePropertyAccessor<T> extends StatePropertyInfo {
<T extends Object, S> CompletableFuture<T> getAsync(TurnContext turnContext, Supplier<S> defaultValueFactory);
CompletableFuture<Void> deleteAsync(TurnContext turnContext);
CompletableFuture<Void> setAsync(TurnContext turnContext, T value);
}

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

@ -0,0 +1,6 @@
package com.microsoft.bot.builder;
public interface StatePropertyInfo {
String getName();
void setName(String withName);
}

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

@ -1,16 +0,0 @@
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
public class StateSettings
{
private boolean lastWriterWins = true;
public boolean getLastWriterWins() {
return this.lastWriterWins;
}
public void setLast(boolean lastWriterWins) {
this.lastWriterWins = lastWriterWins;
}
}

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

@ -1,34 +1,30 @@
package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import com.fasterxml.jackson.core.JsonProcessingException;
package com.microsoft.bot.builder;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public interface Storage
{
public interface Storage {
/**
* Read StoreItems from storage
* @param keys keys of the storeItems to read
* @return StoreItem dictionary
*/
CompletableFuture<Map<String, ? extends Object>> Read(String... keys) throws JsonProcessingException;
CompletableFuture<Map<String, Object>> readAsync(String[] keys);
/**
* Write StoreItems to storage
* @param changes
* @param changes
*/
CompletableFuture Write(Map<String, ? extends Object> changes) throws Exception;
CompletableFuture<Void> writeAsync(Map<String, Object> changes);
/**
* Delete StoreItems from storage
* @param keys keys of the storeItems to delete
*/
CompletableFuture Delete(String... keys);
CompletableFuture<Void> deleteAsync(String[] keys);
}

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

@ -1,11 +1,9 @@
package com.microsoft.bot.builder;
public interface StoreItem
{
public interface StoreItem {
/**
* eTag for concurrency
*/
String geteTag();
void seteTag(String eTag);
String getETag();
void setETag(String eTag);
}

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

@ -1,12 +1,12 @@
package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import java.util.concurrent.CompletableFuture;
/**
* Transcript logger stores activities for conversations for recall.
*/
@ -17,5 +17,5 @@ public interface TranscriptLogger {
* @param activity The activity to transcribe.
* @return A task that represents the work queued to execute.
*/
void LogActivityAsync(Activity activity);
CompletableFuture<Void> logActivityAsync(Activity activity);
}

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

@ -17,6 +17,7 @@ import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
@ -59,7 +60,7 @@ public class TranscriptLoggerMiddleware implements Middleware {
* @return
*/
@Override
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
// log incoming activity at beginning of turn
if (context.getActivity() != null) {
JsonNode role = null;
@ -79,17 +80,12 @@ public class TranscriptLoggerMiddleware implements Middleware {
}
// hook up onSend pipeline
context.OnSendActivities((ctx, activities, nextSend) ->
{
context.onSendActivities((ctx, activities, nextSend) -> {
// run full pipeline
ResourceResponse[] responses = new ResourceResponse[0];
try {
if (nextSend != null) {
responses = nextSend.call();
}
} catch (Exception e) {
e.printStackTrace();
CompletableFuture<ResourceResponse[]> responses = null;
if (nextSend != null) {
responses = nextSend.get();
}
for (Activity activity : activities) {
@ -97,50 +93,32 @@ public class TranscriptLoggerMiddleware implements Middleware {
}
return responses;
});
// hook up update activity pipeline
context.OnUpdateActivity((ctx, activity, nextUpdate) ->
{
context.onUpdateActivity((ctx, activity, nextUpdate) -> {
// run full pipeline
ResourceResponse response = null;
try {
if (nextUpdate != null) {
response = nextUpdate.call();
}
} catch (Exception e) {
e.printStackTrace();
CompletableFuture<ResourceResponse> response = null;
throw new RuntimeException(String.format("Error on Logging.OnUpdateActivity : %s", e.toString()));
if (nextUpdate != null) {
response = nextUpdate.get();
}
// add Message Update activity
Activity updateActivity = Activity.clone(activity);
updateActivity.setType(ActivityTypes.MESSAGE_UPDATE);
LogActivity(updateActivity);
return response;
});
// hook up delete activity pipeline
context.OnDeleteActivity((ctxt, reference, nextDel) -> {
context.onDeleteActivity((ctxt, reference, nextDel) -> {
// run full pipeline
try {
if (nextDel != null) {
logger.error(String.format("Transcript logActivity next delegate: %s)", nextDel));
nextDel.run();
}
} catch (Exception e) {
e.printStackTrace();
logger.error(String.format("Transcript logActivity failed with %s (next delegate: %s)", e.toString(), nextDel));
throw new RuntimeException(String.format("Transcript logActivity failed with %s", e.getMessage()));
if (nextDel != null) {
logger.debug(String.format("Transcript logActivity next delegate: %s)", nextDel));
nextDel.get();
}
// add MessageDelete activity
@ -151,18 +129,13 @@ public class TranscriptLoggerMiddleware implements Middleware {
}};
LogActivity(deleteActivity);
return;
return null;
});
// process bot logic
try {
next.next();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("Error on Logging.next : %s", e.toString()));
}
CompletableFuture<Void> result = next.next();
// flush transcript at end of turn
while (!transcript.isEmpty()) {
@ -174,16 +147,15 @@ public class TranscriptLoggerMiddleware implements Middleware {
}
}
return result;
}
private void LogActivity(Activity activity) {
if (activity.getTimestamp() == null) {
activity.setTimestamp(DateTime.now(DateTimeZone.UTC));
}
transcript.offer(activity);
}
}

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

@ -1,8 +1,7 @@
package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import org.joda.time.DateTime;
@ -13,6 +12,35 @@ import java.util.concurrent.CompletableFuture;
* Transcript logger stores activities for conversations for recall.
*/
public interface TranscriptStore extends TranscriptLogger {
/**
* Gets from the store activities that match a set of criteria.
*
* @param channelId The ID of the channel the conversation is in.
* @param conversationId The ID of the conversation.
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
default CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
String conversationId) {
return getTranscriptActivitiesAsync(channelId, conversationId, null);
}
/**
* Gets from the store activities that match a set of criteria.
*
* @param channelId The ID of the channel the conversation is in.
* @param conversationId The ID of the conversation.
* @param continuationToken
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
default CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
String conversationId,
String continuationToken) {
return getTranscriptActivitiesAsync(channelId, conversationId, continuationToken, DateTime.now());
}
/**
* Gets from the store activities that match a set of criteria.
*
@ -23,14 +51,20 @@ public interface TranscriptStore extends TranscriptLogger {
* @return A task that represents the work queued to execute.
* If the task completes successfully, the result contains the matching activities.
*/
CompletableFuture<PagedResult<Activity>> getTranscriptActivitiesAsync(String channelId,
String conversationId,
String continuationToken,
DateTime startDate);
CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken);
CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId);
//C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are created above:
//ORIGINAL LINE: Task<PagedResult<IActivity>> GetTranscriptActivitiesAsync(string channelId, string conversationId, string continuationToken = null, DateTime startDate = default(DateTime));
CompletableFuture<PagedResult<Activity>> GetTranscriptActivitiesAsync(String channelId, String conversationId, String continuationToken, DateTime localStartDate);
/**
* Gets the conversations on a channel from the store.
*
* @param channelId The ID of the channel.
* @return A task that represents the work queued to execute.
*/
default CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId) {
return listTranscriptsAsync(channelId, null);
}
/**
* Gets the conversations on a channel from the store.
@ -39,12 +73,7 @@ public interface TranscriptStore extends TranscriptLogger {
* @param continuationToken
* @return A task that represents the work queued to execute.
*/
CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId);
//C# TO JAVA CONVERTER NOTE: Java does not support optional parameters. Overloaded method(s) are created above:
//ORIGINAL LINE: Task<PagedResult<Transcript>> ListTranscriptsAsync(string channelId, string continuationToken = null);
CompletableFuture<PagedResult<Transcript>> ListTranscriptsAsync(String channelId, String continuationToken);
CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId, String continuationToken);
/**
* Deletes conversation data from the store.
@ -53,5 +82,5 @@ public interface TranscriptStore extends TranscriptLogger {
* @param conversationId The ID of the conversation to delete.
* @return A task that represents the work queued to execute.
*/
CompletableFuture DeleteTranscriptAsync(String channelId, String conversationId);
CompletableFuture<Void> deleteTranscriptAsync(String channelId, String conversationId);
}

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

@ -2,30 +2,12 @@
// Licensed under the MIT License.
package com.microsoft.bot.builder;
/**
* A method that can participate in send activity events for the current turn.
* @param context The context object for the turn.
* @param activities The activities to send.
* @param next The delegate to call to continue event processing.
* @return A task that represents the work queued to execute.
* A handler calls the {@code next} delegate to pass control to
* the next registered handler. If a handler doesnt call the next delegate,
* the adapter does not call any of the subsequent handlers and does not send the
* {@code activities}.
*
* {@linkalso BotAdapter}
* {@linkalso UpdateActivityHandler}
* {@linkalso DeleteActivityHandler}
*/
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationReference;
import com.microsoft.bot.schema.ResourceResponse;
import java.util.concurrent.CompletableFuture;
//public delegate Task DeleteActivityHandler(TurnContext context, ConversationReference reference, Func<Task> next);
/**
* Provides context for a turn of a bot.
* Context provides information needed to process an incoming activity.
@ -34,8 +16,31 @@ import java.util.concurrent.CompletableFuture;
* {@linkalso Bot}
* {@linkalso Middleware}
*/
public interface TurnContext
{
public interface TurnContext {
/**
* Sends a trace activity to the {@link BotAdapter} for logging purposes.
*
* @param turnContext The context for the current turn.
* @param name The value to assign to the activity's {@link Activity#getName} property.
* @param value The value to assign to the activity's {@link Activity#getValue} property.
* @param valueType The value to assign to the activity's {@link Activity#getValueType} property.
* @param label The value to assign to the activity's {@link Activity#getLabel} property.
* @return A task that represents the work queued to execute. If the adapter is being hosted in the Emulator,
* the task result contains a {@link ResourceResponse} object with the original trace activity's ID; otherwise,
* it contains a {@link ResourceResponse} object containing the ID that the receiving channel assigned to the
* activity.
*/
static CompletableFuture<ResourceResponse> traceActivityAsync(
TurnContext turnContext,
String name,
Object value,
String valueType,
String label) {
return turnContext.sendActivityAsync(turnContext.getActivity().createTrace(name, value, valueType, label));
}
/**
* Gets the bot adapter that created this context object.
*/
@ -44,21 +49,18 @@ public interface TurnContext
/**
* Gets the services registered on this context object.
*/
TurnContextServiceCollection getServices();
TurnContextStateCollection getTurnState();
/**
* Incoming request
*/
Activity getActivity();
/**
* Indicates whether at least one response was sent for the current turn.
* @return {@code true} if at least one response was sent for the current turn.
*/
boolean getResponded();
void setResponded(boolean responded);
/**
* Sends a message activity to the sender of the incoming activity.
@ -74,7 +76,7 @@ public interface TurnContext
* Speech Synthesis Markup Language (SSML) format.</p>
*
*/
ResourceResponse SendActivity(String textReplyToSend) throws Exception;
CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend);
/**
* Sends a message activity to the sender of the incoming activity.
@ -92,8 +94,7 @@ public interface TurnContext
* Speech Synthesis Markup Language (SSML) format.</p>
*
*/
ResourceResponse SendActivity(String textReplyToSend, String speak) throws Exception;
//CompletableFuture<ResourceResponse> SendActivity(String textReplyToSend, String speak = null, String inputHint = InputHints.AcceptingInput);
CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak);
/**
* Sends a message activity to the sender of the incoming activity.
@ -115,7 +116,7 @@ public interface TurnContext
* Speech Synthesis Markup Language (SSML) format.</p>
*
*/
ResourceResponse SendActivity(String textReplyToSend, String speak, String inputHint) throws Exception;
CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak, String inputHint);
/**
* Sends an activity to the sender of the incoming activity.
@ -125,7 +126,7 @@ public interface TurnContext
* a {@link ResourceResponse} object containing the ID that the receiving
* channel assigned to the activity.
*/
ResourceResponse SendActivity(Activity activity) throws Exception;
CompletableFuture<ResourceResponse> sendActivityAsync(Activity activity);
/**
* Sends a set of activities to the sender of the incoming activity.
@ -135,7 +136,7 @@ public interface TurnContext
* an array of {@link ResourceResponse} objects containing the IDs that
* the receiving channel assigned to the activities.
*/
ResourceResponse[] SendActivities(Activity[] activities) throws Exception;
CompletableFuture<ResourceResponse[]> sendActivitiesAsync(Activity[] activities);
/**
* Replaces an existing activity.
@ -147,26 +148,14 @@ public interface TurnContext
* <p>Before calling this, set the ID of the replacement activity to the ID
* of the activity to replace.</p>
*/
ResourceResponse UpdateActivity(Activity activity) throws Exception;
/**
* Replaces an existing activity.
* @param activity New replacement activity.
* @return A task that represents the work queued to execute.
* If the activity is successfully sent, the task result contains
* a {@link ResourceResponse} object containing the ID that the receiving
* channel assigned to the activity.
* <p>Before calling this, set the ID of the replacement activity to the ID
* of the activity to replace.</p>
*/
//CompletableFuture<ResourceResponse> UpdateActivityAsync(Activity activity) throws Exception;
CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity);
/**
* Deletes an existing activity.
* @param activityId The ID of the activity to delete.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> DeleteActivity(String activityId) throws Exception;
CompletableFuture<Void> deleteActivityAsync(String activityId);
/**
* Deletes an existing activity.
@ -175,40 +164,40 @@ public interface TurnContext
* The conversation reference's {@link ConversationReference#getActivityId}
* indicates the activity in the conversation to delete.
*/
void DeleteActivity(ConversationReference conversationReference) throws Exception;
CompletableFuture<Void> deleteActivityAsync(ConversationReference conversationReference);
/**
* Adds a response handler for send activity operations.
* @param handler The handler to add to the context object.
* @return The updated context object.
* When the context's {@link #SendActivity( Activity )}
* or {@link #SendActivities( Activity[])} methods are called,
* When the context's {@link #sendActivityAsync( Activity )}
* or {@link #sendActivitiesAsync( Activity[])} methods are called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*
*/
TurnContext OnSendActivities(SendActivitiesHandler handler);
TurnContext onSendActivities(SendActivitiesHandler handler);
/**
* Adds a response handler for update activity operations.
* @param handler The handler to add to the context object.
* @return The updated context object.
* When the context's {@link #UpdateActivity( Activity )} is called,
* When the context's {@link #updateActivityAsync(Activity)} is called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*
*/
TurnContext OnUpdateActivity(UpdateActivityHandler handler);
TurnContext onUpdateActivity(UpdateActivityHandler handler);
/**
* Adds a response handler for delete activity operations.
* @param handler The handler to add to the context object.
* @return The updated context object.
* @throws NullPointerException {@code handler} is {@code null}.
* When the context's {@link #DeleteActivity(String)} is called,
* When the context's {@link #deleteActivityAsync(String)} is called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*
*/
TurnContext OnDeleteActivity(DeleteActivityHandler handler);
TurnContext onDeleteActivity(DeleteActivityHandler handler);
}

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

@ -15,6 +15,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Supplier;
import static com.microsoft.bot.schema.ActivityTypes.MESSAGE;
import static com.microsoft.bot.schema.ActivityTypes.TRACE;
@ -37,7 +38,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
private final List<UpdateActivityHandler> onUpdateActivity = new ArrayList<UpdateActivityHandler>();
private final List<DeleteActivityHandler> onDeleteActivity = new ArrayList<DeleteActivityHandler>();
private final TurnContextServiceCollection turnServices;
private final TurnContextStateCollection turnState;
/**
* Creates a context object.
@ -57,7 +58,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
throw new IllegalArgumentException("activity");
this.activity = activity;
turnServices = new TurnContextServiceCollectionImpl();
turnState = new TurnContextStateCollection();
}
@ -67,12 +68,13 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @param handler The handler to add to the context object.
* @return The updated context object.
* @throws IllegalArgumentException {@code handler} is {@code null}.
* When the context's {@link #SendActivity( Activity )}
* or {@link #SendActivities( Activity[])} methods are called,
* When the context's {@link #sendActivityAsync( Activity )}
* or {@link #sendActivitiesAsync( Activity[])} methods are called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*/
public TurnContextImpl OnSendActivities(SendActivitiesHandler handler) {
@Override
public TurnContext onSendActivities(SendActivitiesHandler handler) {
if (handler == null)
throw new IllegalArgumentException("handler");
@ -86,11 +88,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @param handler The handler to add to the context object.
* @return The updated context object.
* @throws IllegalArgumentException {@code handler} is {@code null}.
* When the context's {@link #UpdateActivity( Activity )} is called,
* When the context's {@link #updateActivityAsync( Activity )} is called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*/
public TurnContextImpl OnUpdateActivity(UpdateActivityHandler handler) {
@Override
public TurnContext onUpdateActivity(UpdateActivityHandler handler) {
if (handler == null)
throw new IllegalArgumentException("handler");
@ -104,11 +107,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @param handler The handler to add to the context object.
* @return The updated context object.
* @throws IllegalArgumentException {@code handler} is {@code null}.
* When the context's {@link #DeleteActivity(String)} is called,
* When the context's {@link #deleteActivityAsync(String)} is called,
* the adapter calls the registered handlers in the order in which they were
* added to the context object.
*/
public TurnContextImpl OnDeleteActivity(DeleteActivityHandler handler) {
@Override
public TurnContext onDeleteActivity(DeleteActivityHandler handler) {
if (handler == null)
throw new IllegalArgumentException("handler");
@ -126,8 +130,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
/**
* Gets the services registered on this context object.
*/
public TurnContextServiceCollection getServices() {
return this.turnServices;
public TurnContextStateCollection getTurnState() {
return this.turnState;
}
/**
@ -145,11 +149,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @return {@code true} if at least one response was sent for the current turn.
* @throws IllegalArgumentException You attempted to set the value to {@code false}.
*/
@Override
public boolean getResponded() {
return this.responded;
}
public void setResponded(boolean responded) {
private void setResponded(boolean responded) {
if (responded == false) {
throw new IllegalArgumentException("TurnContext: cannot set 'responded' to a value of 'false'.");
}
@ -172,17 +177,17 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* Speech Synthesis Markup Language (SSML) format.</p>
*/
@Override
public ResourceResponse SendActivity(String textReplyToSend) throws Exception {
return SendActivity(textReplyToSend, null, null);
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend) {
return sendActivityAsync(textReplyToSend, null, null);
}
@Override
public ResourceResponse SendActivity(String textReplyToSend, String speak) throws Exception {
return SendActivity(textReplyToSend, speak, null);
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak) {
return sendActivityAsync(textReplyToSend, speak, null);
}
@Override
public ResourceResponse SendActivity(String textReplyToSend, String speak, String inputHint) throws Exception {
public CompletableFuture<ResourceResponse> sendActivityAsync(String textReplyToSend, String speak, String inputHint) {
if (StringUtils.isEmpty(textReplyToSend))
throw new IllegalArgumentException("textReplyToSend");
@ -195,7 +200,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
if (StringUtils.isNotEmpty(inputHint))
activityToSend.setInputHint(InputHints.fromString(inputHint));
return SendActivity(activityToSend);
return sendActivityAsync(activityToSend);
}
/**
@ -209,28 +214,19 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* channel assigned to the activity.
*/
@Override
public ResourceResponse SendActivity(Activity activity) throws Exception {
if (activity == null)
public CompletableFuture<ResourceResponse> sendActivityAsync(Activity activity) {
if (activity == null) {
throw new IllegalArgumentException("activity");
}
System.out.printf("In SENDEACTIVITYASYNC:");
System.out.flush();
Activity[] activities = {activity};
ResourceResponse[] responses;
try {
responses = SendActivities(activities);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("TurnContext:SendActivity fail %s", e.toString()));
}
if (responses == null || responses.length == 0) {
// It's possible an interceptor prevented the activity from having been sent.
// Just return an empty response in that case.
return null;
} else {
return responses[0];
}
return sendActivitiesAsync(activities)
.thenApply(resourceResponses -> {
if (resourceResponses == null || resourceResponses.length == 0) {
return null;
}
return resourceResponses[0];
});
}
/**
@ -243,23 +239,22 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* the receiving channel assigned to the activities.
*/
@Override
public ResourceResponse[] SendActivities(Activity[] activities) throws Exception {
public CompletableFuture<ResourceResponse[]> sendActivitiesAsync(Activity[] activities) {
// Bind the relevant Conversation Reference properties, such as URLs and
// ChannelId's, to the activities we're about to send.
ConversationReference cr = GetConversationReference(this.activity);
ConversationReference cr = getConversationReference(this.activity);
for (Activity a : activities) {
ApplyConversationReference(a, cr);
applyConversationReference(a, cr);
}
// Convert the IActivities to Activies.
// Activity[] activityArray = Array.ConvertAll(activities, (input) => (Activity)input);
List<Activity> activityArray = Arrays.stream(activities).map(input -> input).collect(toList());
// Create the list used by the recursive methods.
List<Activity> activityList = new ArrayList<Activity>(activityArray);
Callable<ResourceResponse[]> ActuallySendStuff = () -> {
Supplier<CompletableFuture<ResourceResponse[]>> actuallySendStuff = () -> {
// Are the any non-trace activities to send?
// The thinking here is that a Trace event isn't user relevant data
// so the "Responded" flag should not be set by Trace messages being
@ -268,6 +263,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
if (!activityList.stream().anyMatch((a) -> a.getType() == TRACE)) {
sentNonTraceActivities = true;
}
// Send from the list, which may have been manipulated via the event handlers.
// Note that 'responses' was captured from the root of the call, and will be
// returned to the original caller.
@ -290,7 +286,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
};
List<Activity> act_list = new ArrayList<>(activityList);
return SendActivitiesInternal(act_list, onSendActivities.iterator(), ActuallySendStuff);
return sendActivitiesInternal(act_list, onSendActivities.iterator(), actuallySendStuff);
}
/**
@ -301,18 +297,14 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @throws com.microsoft.bot.connector.rest.ErrorResponseException The HTTP operation failed and the response contained additional information.
*/
@Override
public ResourceResponse UpdateActivity(Activity activity) throws Exception {
Callable<ResourceResponse> ActuallyUpdateStuff = () -> {
public CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity) {
Supplier<ResourceResponse> ActuallyUpdateStuff = () -> {
return this.getAdapter().UpdateActivity(this, activity);
};
return UpdateActivityInternal(activity, onUpdateActivity.iterator(), ActuallyUpdateStuff);
return updateActivityInternal(activity, onUpdateActivity.iterator(), ActuallyUpdateStuff);
}
/**
* Deletes an existing activity.
*
@ -320,7 +312,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @return A task that represents the work queued to execute.
* @throws Exception The HTTP operation failed and the response contained additional information.
*/
public CompletableFuture<Void> DeleteActivity(String activityId) throws Exception {
public CompletableFuture<Void> deleteActivityAsync(String activityId) {
if (StringUtils.isWhitespace(activityId) || activityId == null)
throw new IllegalArgumentException("activityId");
@ -342,7 +334,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
};
try {
DeleteActivityInternal(cr, onDeleteActivity.iterator(), ActuallyDeleteStuff);
deleteActivityInternal(cr, onDeleteActivity.iterator(), ActuallyDeleteStuff);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("Failed to delete activity %s", e.getMessage()));
@ -350,7 +342,6 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
return;
}, ExecutorFactory.getExecutor());
}
/**
@ -362,7 +353,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* The conversation reference's {@link ConversationReference#getActivityId}
* indicates the activity in the conversation to delete.
*/
public void DeleteActivity(ConversationReference conversationReference) throws Exception {
public CompletableFuture<Void> deleteActivityAsync(ConversationReference conversationReference) {
if (conversationReference == null)
throw new IllegalArgumentException("conversationReference");
@ -378,11 +369,14 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
throw new RuntimeException("DeleteActivity failed");
};
DeleteActivityInternal(conversationReference, onDeleteActivity.iterator(), ActuallyDeleteStuff);
return ;
return deleteActivityInternal(conversationReference, onDeleteActivity.iterator(), ActuallyDeleteStuff);
}
private ResourceResponse[] SendActivitiesInternal(List<Activity> activities, Iterator<SendActivitiesHandler> sendHandlers, Callable<ResourceResponse[]> callAtBottom) throws Exception {
private CompletableFuture<ResourceResponse[]> sendActivitiesInternal(
List<Activity> activities,
Iterator<SendActivitiesHandler> sendHandlers,
Supplier<CompletableFuture<ResourceResponse[]>> callAtBottom) {
if (activities == null)
throw new IllegalArgumentException("activities");
if (sendHandlers == null)
@ -390,24 +384,24 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
if (false == sendHandlers.hasNext()) { // No middleware to run.
if (callAtBottom != null)
return callAtBottom.call();
return callAtBottom.get();
return new ResourceResponse[0];
}
// Default to "No more Middleware after this".
Callable<ResourceResponse[]> next = () -> {
Supplier<CompletableFuture<ResourceResponse[]>> next = () -> {
// Remove the first item from the list of middleware to call,
// so that the next call just has the remaining items to worry about.
//Iterable<SendActivitiesHandler> remaining = sendHandlers.Skip(1);
//Iterator<SendActivitiesHandler> remaining = sendHandlers.iterator();
if (sendHandlers.hasNext())
sendHandlers.next();
return SendActivitiesInternal(activities, sendHandlers, callAtBottom);
return sendActivitiesInternal(activities, sendHandlers, callAtBottom);
};
// Grab the current middleware, which is the 1st element in the array, and execute it
SendActivitiesHandler caller = sendHandlers.next();
return caller.handle(this, activities, next);
return caller.invoke(this, activities, next);
}
// private async Task<ResourceResponse> UpdateActivityInternal(Activity activity,
@ -449,29 +443,29 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
// UpdateActivityHandler toCall = updateHandlers.First();
// return await toCall(this, activity, next);
// }
private ResourceResponse UpdateActivityInternal(Activity activity,
private CompletableFuture<ResourceResponse> updateActivityInternal(Activity activity,
Iterator<UpdateActivityHandler> updateHandlers,
Callable<ResourceResponse> callAtBottom) throws Exception {
Supplier<ResourceResponse> callAtBottom) {
BotAssert.ActivityNotNull(activity);
if (updateHandlers == null)
throw new IllegalArgumentException("updateHandlers");
if (false == updateHandlers.hasNext()) { // No middleware to run.
if (callAtBottom != null) {
return callAtBottom.call();
return callAtBottom.get();
}
return null;
}
// Default to "No more Middleware after this".
Callable<ResourceResponse> next = () -> {
Supplier<CompletableFuture<ResourceResponse>> next = () -> {
// Remove the first item from the list of middleware to call,
// so that the next call just has the remaining items to worry about.
if (updateHandlers.hasNext())
updateHandlers.next();
ResourceResponse result = null;
try {
result = UpdateActivityInternal(activity, updateHandlers, callAtBottom);
result = updateActivityInternal(activity, updateHandlers, callAtBottom);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("Error updating activity: %s", e.toString()));
@ -482,13 +476,13 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
// Grab the current middleware, which is the 1st element in the array, and execute it
UpdateActivityHandler toCall = updateHandlers.next();
return toCall.handle(this, activity, next);
return toCall.invoke(this, activity, next);
}
private void DeleteActivityInternal(ConversationReference cr,
private CompletableFuture<Void> deleteActivityInternal(ConversationReference cr,
Iterator<DeleteActivityHandler> deleteHandlers,
Runnable callAtBottom) throws Exception {
Runnable callAtBottom) {
BotAssert.ConversationReferenceNotNull(cr);
if (deleteHandlers == null)
throw new IllegalArgumentException("deleteHandlers");
@ -501,7 +495,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
}
// Default to "No more Middleware after this".
Runnable next = () -> {
Supplier<CompletableFuture<Void>> next = () -> {
// Remove the first item from the list of middleware to call,
// so that the next call just has the remaining items to worry about.
@ -511,17 +505,18 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
try {
DeleteActivityInternal(cr, deleteHandlers, callAtBottom);
deleteActivityInternal(cr, deleteHandlers, callAtBottom);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("DeleteActivityInternal failed");
}
return;
return null;
};
// Grab the current middleware, which is the 1st element in the array, and execute it.
DeleteActivityHandler toCall = deleteHandlers.next();
toCall.handle(this, cr, next);
toCall.invoke(this, cr, next);
}
/**
@ -531,7 +526,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @return A conversation reference for the conversation that contains the activity.
* @throws IllegalArgumentException {@code activity} is {@code null}.
*/
public static ConversationReference GetConversationReference(Activity activity) {
public static ConversationReference getConversationReference(Activity activity) {
BotAssert.ActivityNotNull(activity);
ConversationReference r = new ConversationReference() {{
@ -553,8 +548,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @param activity The activity to update.
* @param reference The conversation reference.
*/
public static Activity ApplyConversationReference(Activity activity, ConversationReference reference) {
return ApplyConversationReference(activity, reference, false);
public static Activity applyConversationReference(Activity activity, ConversationReference reference) {
return applyConversationReference(activity, reference, false);
}
/**
@ -566,13 +561,16 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @param isIncoming (Optional) {@code true} to treat the activity as an
* incoming activity, where the bot is the recipient; otherwaire {@code false}.
* Default is {@code false}, and the activity will show the bot as the sender.
* Call {@link #GetConversationReference( Activity )} on an incoming
* Call {@link #getConversationReference( Activity )} on an incoming
* activity to get a conversation reference that you can then use to update an
* outgoing activity with the correct delivery information.
* <p>The {@link #SendActivity( Activity )} and {@link #SendActivities( Activity[])}
* <p>The {@link #sendActivityAsync( Activity )} and {@link #sendActivitiesAsync( Activity[])}
* methods do this for you.</p>
*/
public static Activity ApplyConversationReference(Activity activity, ConversationReference reference, boolean isIncoming) {
public static Activity applyConversationReference(Activity activity,
ConversationReference reference,
boolean isIncoming) {
activity.setChannelId(reference.getChannelId());
activity.setServiceUrl(reference.getServiceUrl());
activity.setConversation(reference.getConversation());
@ -592,6 +590,6 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
}
public void close() throws Exception {
turnServices.close();
turnState.close();
}
}

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

@ -1,32 +0,0 @@
package com.microsoft.bot.builder;
import java.util.Map;
/**
* Represents a set of collection of services associated with the {@link TurnContext}.
*
* TODO: add more details on what kind of services can/should be stored here, by whom and what the lifetime semantics are, etc.
*
*/
public interface TurnContextServiceCollection extends Iterable<Map.Entry<String, Object>>, AutoCloseable {
/**
* Add a service with a specified key.
* @param TService The type of service to be added.
* @param key The key to store the service under.
* @param service The service to add.
* @throws IllegalArgumentException Thrown when a service is already registered with the specified {@code key}
*/
<TService extends Object> void Add(String key, TService service) throws IllegalArgumentException;
/**
* Get a service by its key.
* @param TService The type of service to be retrieved.
* @param key The key of the service to get.
* @return The service stored under the specified key.
*/
<TService extends Object> TService Get(String key) throws IllegalArgumentException;
}

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

@ -1,67 +0,0 @@
package com.microsoft.bot.builder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public final class TurnContextServiceCollectionImpl implements TurnContextServiceCollection, AutoCloseable
{
private final HashMap<String, Object> _services = new HashMap<String, Object>();
public TurnContextServiceCollectionImpl() throws IllegalArgumentException {
}
public <TService extends Object> TService Get(String key) throws IllegalArgumentException {
if (key == null)
throw new IllegalArgumentException("key");
TService service = (TService) _services.get(key);
// TODO: log that we didn't find the requested service
return (TService) service;
}
/**
* Get a service by type using its full type name as the key.
* @param TService The type of service to be retrieved.
* @return The service stored under the specified key.
*/
public <TService> TService Get(Class<TService> type) throws IllegalArgumentException {
return this.Get(type.getName());
}
@Override
public <TService extends Object> void Add(String key, TService service) throws IllegalArgumentException {
if (key == null) throw new IllegalArgumentException("key");
if (service == null) throw new IllegalArgumentException("service");
if (_services.containsKey(key))
throw new IllegalArgumentException (String.format("Key %s already exists", key));
_services.put(key, service);
}
/**
* Add a service using its full type name as the key.
* @param TService The type of service to be added.
* @param service The service to add.
*/
public <TService> void Add(TService service, Class<TService> type) throws IllegalArgumentException {
Add(type.getName(), service);
}
public Iterator<Map.Entry<String, Object>> iterator() {
return _services.entrySet().iterator();
}
@Override
public void close() throws Exception {
for (Map.Entry entry : this._services.entrySet()) {
if (entry.getValue() instanceof AutoCloseable) {
((AutoCloseable) entry.getValue()).close();
}
}
}
}

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

@ -0,0 +1,67 @@
package com.microsoft.bot.builder;
import java.util.HashMap;
import java.util.Map;
/**
* Represents a set of collection of services associated with the {@link TurnContext}.
*/
public class TurnContextStateCollection extends HashMap<String, Object> implements AutoCloseable {
public <T> T get(String key) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key");
}
Object service = get(key);
try {
T result = (T) service;
} catch(ClassCastException e) {
return null;
}
return (T) service;
}
/**
* Get a service by type using its full type name as the key.
* @param type The type of service to be retrieved.
* @return The service stored under the specified key.
*/
public <T> T get(Class<T> type) throws IllegalArgumentException {
return get(type.getName());
}
public <T> void add(String key, T value) throws IllegalArgumentException {
if (key == null) {
throw new IllegalArgumentException("key");
}
if (value == null) {
throw new IllegalArgumentException("service");
}
if (containsKey(key))
throw new IllegalArgumentException (String.format("Key %s already exists", key));
put(key, value);
}
/**
* Add a service using its full type name as the key.
* @param value The service to add.
*/
public <T> void add(T value) throws IllegalArgumentException {
add(value.getClass().getName(), value);
}
@Override
public void close() throws Exception {
for (Map.Entry entry : entrySet()) {
if (entry.getValue() instanceof AutoCloseable) {
((AutoCloseable) entry.getValue()).close();
}
}
}
}

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

@ -4,5 +4,5 @@ import java.util.concurrent.CompletableFuture;
@FunctionalInterface
public interface TurnTask {
CompletableFuture invoke(TurnContext context);
CompletableFuture<Void> invoke(TurnContext context);
}

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

@ -1,9 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ResourceResponse;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
@FunctionalInterface
public interface UpdateActivityHandler {
@ -19,10 +23,11 @@ public interface UpdateActivityHandler {
* activity.
* <p>The activity's {@link Activity#getId} indicates the activity in the
* conversation to replace.</p>
*
* {@linkalso BotAdapter}
* {@linkalso SendActivitiesHandler}
* {@linkalso DeleteActivityHandler}
* <p>If the activity is successfully sent, the <paramref name="next"/> delegate returns
* a {@link ResourceResponse} object containing the ID that the receiving channel assigned
* to the activity. Use this response object as the return value of this handler.</p>
*/
ResourceResponse handle(TurnContext context, Activity activity, Callable<ResourceResponse> next);
CompletableFuture<ResourceResponse> invoke(TurnContext context,
Activity activity,
Supplier<CompletableFuture<ResourceResponse>> next);
}

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

@ -1,9 +1,5 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.StateSettings;
import com.microsoft.bot.builder.Storage;
import com.microsoft.bot.builder.TurnContext;
import java.util.function.Supplier;
@ -45,6 +41,6 @@ public class UserState<TState> extends BotState<TState>
* @return The user state object.
*/
public static <TState> TState Get(TurnContext context) throws IllegalArgumentException {
return context.getServices().<TState>Get(PropertyName());
return context.getTurnState().<TState>Get(PropertyName());
}
}

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

@ -0,0 +1,119 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.TokenResponse;
import com.microsoft.bot.schema.TokenStatus;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public interface UserTokenProvider {
/**
* Attempts to retrieve the token for a user that's in a login flow.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param magicCode (Optional) Optional user entered code to validate.
* @return Token Response.
*/
CompletableFuture<TokenResponse> getUserTokenAsync(TurnContext turnContext,
String connectionName,
String magicCode);
/**
* Get the raw signin link to be sent to the user for signin for a connection name.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @return A task that represents the work queued to execute. If the task completes successfully,
* the result contains the raw signin link.
*/
default CompletableFuture<String> getOauthSignInLinkAsync(TurnContext turnContext, String connectionName) {
return getOauthSignInLinkAsync(turnContext, connectionName, null, null);
}
/**
* Get the raw signin link to be sent to the user for signin for a connection name.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId The user id that will be associated with the token.
* @param finalRedirect The final URL that the OAuth flow will redirect to.
* @return A task that represents the work queued to execute. If the task completes successfully,
* the result contains the raw signin link.
*/
CompletableFuture<String> getOauthSignInLinkAsync(TurnContext turnContext,
String connectionName,
String userId,
String finalRedirect);
/**
* Signs the user out with the token server.
*
* @param turnContext Context for the current turn of conversation with the user.
* @return A task that represents the work queued to execute.
*/
default CompletableFuture<Void> signOutUserAsync(TurnContext turnContext) {
return signOutUserAsync(turnContext, null, null);
}
/**
* Signs the user out with the token server.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param connectionName Name of the auth connection to use.
* @param userId User id of user to sign out.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> signOutUserAsync(TurnContext turnContext, String connectionName, String userId);
/**
* Retrieves the token status for each configured connection for the given user.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param userId The user Id for which token status is retrieved.
* @return Array of TokenStatus.
*/
default CompletableFuture<TokenStatus[]> getTokenStatusAsync(TurnContext turnContext, String userId) {
return getTokenStatusAsync(turnContext, userId, null);
}
/**
* Retrieves the token status for each configured connection for the given user.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param userId The user Id for which token status is retrieved.
* @param includeFilter Comma separated list of connection's to include. Blank will return token status
* for all configured connections.
* @return Array of TokenStatus.
*/
CompletableFuture<TokenStatus[]> getTokenStatusAsync(TurnContext turnContext, String userId, String includeFilter);
/**
* Retrieves Azure Active Directory tokens for particular resources on a configured connection.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param connectionName The name of the Azure Active Directory connection configured with this bot.
* @param resourceUrls The list of resource URLs to retrieve tokens for.
* @return Dictionary of resourceUrl to the corresponding TokenResponse.
*/
default CompletableFuture<Map<String, TokenResponse>> getAadTokesAsync(TurnContext turnContext,
String connectionName,
String[] resourceUrls) {
return getAadTokesAsync(turnContext, connectionName, resourceUrls, null);
}
/**
* Retrieves Azure Active Directory tokens for particular resources on a configured connection.
*
* @param turnContext Context for the current turn of conversation with the user.
* @param connectionName The name of the Azure Active Directory connection configured with this bot.
* @param resourceUrls The list of resource URLs to retrieve tokens for.
* @param userId The user Id for which tokens are retrieved. If passing in null the userId is taken from
* the Activity in the ITurnContext.
* @return Dictionary of resourceUrl to the corresponding TokenResponse.
*/
CompletableFuture<Map<String, TokenResponse>> getAadTokesAsync(TurnContext turnContext,
String connectionName,
String[] resourceUrls,
String userId);
}

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

@ -95,7 +95,7 @@ public class TestAdapter extends BotAdapter {
}
@Override
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException {
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
List<ResourceResponse> responses = new LinkedList<ResourceResponse>();
for (Activity activity : activities) {
@ -120,7 +120,7 @@ public class TestAdapter extends BotAdapter {
// to keep the behavior as close as possible to facillitate
// more realistic tests.
int delayMs = (int) activity.getValue();
Thread.sleep(delayMs);
try { Thread.sleep(delayMs); } catch (InterruptedException e) {}
} else {
synchronized (this.botReplies) {
this.botReplies.add(activity);

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

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.
/**
* This package contains the classes for Bot-Builder.
*/
package com.microsoft.bot.builder;

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

@ -1,37 +1,36 @@
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
/**
* Helper class for defining middleware by using a delegate or anonymous method.
*/
public class AnonymousReceiveMiddleware implements Middleware
{
private MiddlewareCall _toCall;
/**
* Creates a middleware object that uses the provided method as its
* process request handler.
* @param anonymousMethod The method to use as the middleware's process
* request handler.
*/
public AnonymousReceiveMiddleware(MiddlewareCall anonymousMethod)
{
if (anonymousMethod == null)
throw new NullPointerException("MiddlewareCall anonymousMethod");
else
_toCall = anonymousMethod;
}
/**
* Uses the method provided in the {@link AnonymousReceiveMiddleware} to
* process an incoming activity.
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return A task that represents the work queued to execute.
*/
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
_toCall.requestHandler(context, next);
}
}
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
/**
* Helper class for defining middleware by using a delegate or anonymous method.
*/
public class AnonymousReceiveMiddleware implements Middleware
{
private MiddlewareCall _toCall;
/**
* Creates a middleware object that uses the provided method as its
* process request handler.
* @param anonymousMethod The method to use as the middleware's process
* request handler.
*/
public AnonymousReceiveMiddleware(MiddlewareCall anonymousMethod)
{
if (anonymousMethod == null)
throw new NullPointerException("MiddlewareCall anonymousMethod");
else
_toCall = anonymousMethod;
}
/**
* Uses the method provided in the {@link AnonymousReceiveMiddleware} to
* process an incoming activity.
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return A task that represents the work queued to execute.
*/
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
return _toCall.requestHandler(context, next);
}
}

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

@ -1,9 +1,9 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.TurnContext;
import java.util.concurrent.CompletableFuture;
public interface CallOnException {
<T> CompletableFuture apply(TurnContext context, T t ) throws Exception;
}
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.TurnContext;
import java.util.concurrent.CompletableFuture;
public interface CallOnException {
<T> CompletableFuture apply(TurnContext context, T t ) throws Exception;
}

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

@ -1,46 +1,48 @@
package com.microsoft.bot.builder;
/**
* This piece of middleware can be added to allow you to handle exceptions when they are thrown
* within your bot's code or middleware further down the pipeline. Using this handler you might
* send an appropriate message to the user to let them know that something has gone wrong.
* You can specify the type of exception the middleware should catch and this middleware can be added
* multiple times to allow you to handle different exception types in different ways.
*
* @param T The type of the exception that you want to catch. This can be 'Exception' to
* catch all or a specific type of exception
*/
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
private final CallOnException _handler;
private final Class<T> _exceptionType;
public CatchExceptionMiddleware(CallOnException callOnException, Class<T> exceptionType) {
_handler = callOnException;
_exceptionType = exceptionType;
}
@Override
public void OnTurn(TurnContext context, NextDelegate next) throws Exception {
Class c = _exceptionType.getDeclaringClass();
try {
// Continue to route the activity through the pipeline
// any errors further down the pipeline will be caught by
// this try / catch
next.next();
} catch (Exception ex) {
if (_exceptionType.isInstance(ex))
// If an error is thrown and the exception is of type T then invoke the handler
_handler.<T>apply(context, (T) ex);
else
throw ex;
}
return;
}
}
package com.microsoft.bot.builder;
import java.util.concurrent.CompletableFuture;
/**
* This piece of middleware can be added to allow you to handle exceptions when they are thrown
* within your bot's code or middleware further down the pipeline. Using this handler you might
* send an appropriate message to the user to let them know that something has gone wrong.
* You can specify the type of exception the middleware should catch and this middleware can be added
* multiple times to allow you to handle different exception types in different ways.
*
* @param T The type of the exception that you want to catch. This can be 'Exception' to
* catch all or a specific type of exception
*/
public class CatchExceptionMiddleware<T extends Exception> implements Middleware {
private final CallOnException _handler;
private final Class<T> _exceptionType;
public CatchExceptionMiddleware(CallOnException callOnException, Class<T> exceptionType) {
_handler = callOnException;
_exceptionType = exceptionType;
}
@Override
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
Class c = _exceptionType.getDeclaringClass();
try {
// Continue to route the activity through the pipeline
// any errors further down the pipeline will be caught by
// this try / catch
next.next();
} catch (Exception ex) {
if (_exceptionType.isInstance(ex))
// If an error is thrown and the exception is of type T then invoke the handler
_handler.<T>apply(context, (T) ex);
else
throw ex;
}
return;
}
}

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

@ -9,7 +9,7 @@ public class CustomKeyState extends BotState<CustomState> {
}
public static CustomState Get(TurnContext context) {
return context.getServices().<CustomState>Get(PropertyName);
return context.getTurnState().<CustomState>Get(PropertyName);
}
}

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

@ -1,130 +0,0 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.bot.connector.ExecutorFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import static java.util.concurrent.CompletableFuture.completedFuture;
/**
* Models IStorage around a dictionary
*/
public class DictionaryStorage implements Storage {
private static ObjectMapper objectMapper;
// TODO: Object needs to be defined
private final Map<String, Object> memory;
private final Object syncroot = new Object();
private int _eTag = 0;
private final String typeNameForNonEntity = "__type_name_";
public DictionaryStorage() {
this(null);
}
public DictionaryStorage(Map<String, Object> dictionary ) {
DictionaryStorage.objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.findAndRegisterModules();
this.memory = (dictionary != null) ? dictionary : new HashMap<String, Object>();
}
public CompletableFuture Delete(String[] keys) {
synchronized (this.syncroot) {
for (String key : keys) {
Object o = this.memory.get(key);
this.memory.remove(o);
}
}
return completedFuture(null);
}
@Override
public CompletableFuture<Map<String, ?>> Read(String[] keys) throws JsonProcessingException {
return CompletableFuture.supplyAsync(() -> {
Map<String, Object> storeItems = new HashMap<String, Object>(keys.length);
synchronized (this.syncroot) {
for (String key : keys) {
if (this.memory.containsKey(key)) {
Object state = this.memory.get(key);
if (state != null) {
try {
if (!(state instanceof JsonNode))
throw new RuntimeException("DictionaryRead failed: entry not JsonNode");
JsonNode stateNode = (JsonNode) state;
// Check if type info is set for the class
if (!(stateNode.hasNonNull(this.typeNameForNonEntity))) {
throw new RuntimeException(String.format("DictionaryRead failed: Type info not present"));
}
String clsName = stateNode.get(this.typeNameForNonEntity).textValue();
// Load the class info
Class<?> cls;
try {
cls = Class.forName(clsName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new RuntimeException(String.format("DictionaryRead failed: Could not load class %s", clsName));
}
// Populate dictionary
storeItems.put(key,DictionaryStorage.objectMapper.treeToValue(stateNode, cls ));
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(String.format("DictionaryRead failed: %s", e.toString()));
}
}
}
}
}
return storeItems;
}, ExecutorFactory.getExecutor());
}
@Override
public CompletableFuture Write(Map<String, ?> changes) throws Exception {
synchronized (this.syncroot) {
for (Map.Entry change : changes.entrySet()) {
Object newValue = change.getValue();
String oldStateETag = null; // default(string);
if (this.memory.containsValue(change.getKey())) {
Map oldState = (Map) this.memory.get(change.getKey());
if (oldState.containsValue("eTag")) {
Map.Entry eTagToken = (Map.Entry) oldState.get("eTag");
oldStateETag = (String) eTagToken.getValue();
}
}
// Dictionary stores Key:JsonNode (with type information held within the JsonNode)
JsonNode newState = DictionaryStorage.objectMapper.valueToTree(newValue);
((ObjectNode)newState).put(this.typeNameForNonEntity, newValue.getClass().getTypeName());
// Set ETag if applicable
if (newValue instanceof StoreItem) {
StoreItem newStoreItem = (StoreItem) newValue;
if(oldStateETag != null && newStoreItem.geteTag() != "*" &&
newStoreItem.geteTag() != oldStateETag) {
throw new Exception(String.format("Etag conflict.\r\n\r\nOriginal: %s\r\nCurrent: %s",
newStoreItem.geteTag(), oldStateETag));
}
Integer newTag = _eTag++;
((ObjectNode)newState).put("eTag", newTag.toString());
}
this.memory.put((String)change.getKey(), newState);
}
}
return completedFuture(null);
}
}

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

@ -1,13 +0,0 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.DictionaryStorage;
/**
* RamStorage stores data in volative dictionary
*/
public class MemoryStorage extends DictionaryStorage {
public MemoryStorage() {
super(null);
}
}

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

@ -61,11 +61,9 @@ public class MiddlewareSetTest extends TestBase
public void NestedSet_OnReceive() throws Exception {
final boolean[] wasCalled = {false};
MiddlewareSet inner = new MiddlewareSet();
inner.Use(new AnonymousReceiveMiddleware(new MiddlewareCall() {
public void requestHandler(TurnContext tc, NextDelegate nd) throws Exception {
wasCalled[0] = true;
nd.next();
}
inner.Use(new AnonymousReceiveMiddleware((MiddlewareCall) (tc, nd) -> {
wasCalled[0] = true;
return nd.next();
}));
MiddlewareSet outer = new MiddlewareSet();
outer.Use(inner);

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

@ -36,7 +36,7 @@ public class SimpleAdapter extends BotAdapter {
@Override
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) throws InterruptedException {
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
Assert.assertNotNull("SimpleAdapter.deleteActivity: missing reference", activities);
Assert.assertTrue("SimpleAdapter.sendActivities: empty activities array.", activities.length > 0);
@ -49,8 +49,6 @@ public class SimpleAdapter extends BotAdapter {
}
ResourceResponse[] result = new ResourceResponse[responses.size()];
return responses.toArray(result);
}
@Override

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

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.schema;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* The status of a particular token.
*/
public class TokenStatus {
/**
* The channelId of the token status pertains to.
*/
@JsonProperty
private String channelId;
/**
* The name of the connection the token status pertains to.
*/
@JsonProperty
private String connectionName;
/**
* True if a token is stored for this ConnectionName.
*/
@JsonProperty
private boolean hasToken;
/**
* The display name of the service provider for which this Token belongs to.
*/
@JsonProperty
private String serviceProviderDisplayName;
/**
* Gets the channelId.
* @return The channelId.
*/
public String getChannelId() {
return channelId;
}
/**
* Sets the channelId.
* @param withChannelId The channelId.
*/
public void setChannelId(String withChannelId) {
channelId = withChannelId;
}
/**
* Gets the connectionName.
* @return The connection name.
*/
public String getConnectionName() {
return connectionName;
}
/**
* Sets the connectionName.
* @param withConnectionName The connection name.
*/
public void setConnectionName(String withConnectionName) {
connectionName = withConnectionName;
}
/**
* Gets the hasToken value.
* @return The hasToken value.
*/
public boolean hasToken() {
return hasToken;
}
/**
* Sets the hasToken value.
* @param withHasToken The hasToken value.
*/
public void setHasToken(boolean withHasToken) {
hasToken = withHasToken;
}
/**
* Gets the serviceProviderDisplayName field.
* @return The service provider display name.
*/
public String getServiceProviderDisplayName() {
return serviceProviderDisplayName;
}
/**
* Sets the serviceProviderDisplayName field.
* @param withServiceProviderDisplayName The service provider display name.
*/
public void setServiceProviderDisplayName(String withServiceProviderDisplayName) {
serviceProviderDisplayName = withServiceProviderDisplayName;
}
}