This commit is contained in:
Tracy Boehrer 2019-09-09 14:04:11 -05:00
Родитель abcecf7ad7
Коммит 3f19e94539
24 изменённых файлов: 832 добавлений и 333 удалений

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

@ -6,8 +6,7 @@ package com.microsoft.bot.builder;
import com.microsoft.bot.schema.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
/**
@ -35,6 +34,16 @@ public abstract class BotAdapter {
*/
protected final MiddlewareSet _middlewareSet = new MiddlewareSet();
private OnTurnErrorHandler onTurnError;
public OnTurnErrorHandler getOnTurnError() {
return onTurnError;
}
public void setOnTurnError(OnTurnErrorHandler withTurnError) {
onTurnError = withTurnError;
}
/**
* Creates a default adapter.
*/
@ -51,7 +60,7 @@ public abstract class BotAdapter {
* For each turn, the adapter calls middleware in the order in which you added it.
*/
public BotAdapter use(Middleware middleware) {
_middlewareSet.Use(middleware);
_middlewareSet.use(middleware);
return this;
}
@ -122,17 +131,26 @@ public abstract class BotAdapter {
* 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, BotCallbackHandler callback) throws Exception {
BotAssert.ContextNotNull(context);
protected CompletableFuture<Void> runPipelineAsync(TurnContext context, BotCallbackHandler callback) {
BotAssert.contextNotNull(context);
// Call any registered Middleware Components looking for ReceiveActivity()
if (context.getActivity() != null) {
_middlewareSet.receiveActivityWithStatus(context, callback);
return _middlewareSet.receiveActivityWithStatusAsync(context, callback)
.exceptionally(exception -> {
if (onTurnError != null) {
return onTurnError.invoke(context, exception);
}
throw new CompletionException(exception);
});
} else {
// call back to caller on proactive case
if (callback != null) {
callback.invoke(context);
return callback.invoke(context);
}
return CompletableFuture.completedFuture(null);
}
}
@ -172,7 +190,7 @@ public abstract class BotAdapter {
Activity activity = conv.getPostToBotMessage();
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
this.runPipeline(context, callback);
return runPipelineAsync(context, callback);
}
}
}

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

@ -11,76 +11,69 @@ import java.util.List;
/**
* Provides methods for debugging Bot Builder code.
*/
public class BotAssert
{
public class BotAssert {
/**
* Checks that an activity object is not {@code null}.
*
* @param activity The activity object.
* @throws NullPointerException
* {@code activity} is {@code null}.
* @throws NullPointerException {@code activity} is {@code null}.
*/
public static void ActivityNotNull(Activity activity)
{
public static void activityNotNull(Activity activity) {
if (activity == null)
throw new IllegalArgumentException ("Activity");
throw new IllegalArgumentException("Activity");
}
/**
* Checks that a context object is not {@code null}.
*
* @param context The context object.
* @throws NullPointerException
* {@code context} is {@code null}.
* @throws NullPointerException {@code context} is {@code null}.
*/
public static void ContextNotNull(TurnContext context)
{
public static void contextNotNull(TurnContext context) {
if (context == null)
throw new IllegalArgumentException ("TurnContext");
throw new IllegalArgumentException("TurnContext");
}
/**
* Checks that a conversation reference object is not {@code null}.
*
* @param reference The conversation reference object.
* @throws NullPointerException
* {@code reference} is {@code null}.
* @throws NullPointerException {@code reference} is {@code null}.
*/
public static void ConversationReferenceNotNull(ConversationReference reference)
{
public static void conversationReferenceNotNull(ConversationReference reference) {
if (reference == null)
throw new IllegalArgumentException ("ConversationReference");
throw new IllegalArgumentException("ConversationReference");
}
/**
* Checks that an activity collection is not {@code null}.
*
* @param activities The activities.
* @throws NullPointerException
* {@code activities} is {@code null}.
* @throws NullPointerException {@code activities} is {@code null}.
*/
public static void ActivityListNotNull(List<Activity> activities)
{
public static void activityListNotNull(List<Activity> activities) {
if (activities == null)
throw new NullPointerException("List<Activity>");
}
/**
* Checks that a middleware object is not {@code null}.
*
* @param middleware The middleware object.
* @throws NullPointerException
* {@code middleware} is {@code null}.
* @throws NullPointerException {@code middleware} is {@code null}.
*/
public static void MiddlewareNotNull(Middleware middleware)
{
public static void middlewareNotNull(Middleware middleware) {
if (middleware == null)
throw new NullPointerException("Middleware");
}
/**
* Checks that a middleware collection is not {@code null}.
*
* @param middleware The middleware.
* @throws NullPointerException
* {@code middleware} is {@code null}.
* @throws NullPointerException {@code middleware} is {@code null}.
*/
public static void MiddlewareNotNull(ArrayList<Middleware> middleware)
{
public static void middlewareNotNull(ArrayList<Middleware> middleware) {
if (middleware == null)
throw new NullPointerException("List<Middleware>");
}

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

@ -206,7 +206,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* {@linkalso BotAdapter.RunPipeline(TurnContext, Func { TurnContext, Task })}
*/
public CompletableFuture<InvokeResponse> ProcessActivity(String authHeader, Activity activity, Function<TurnContextImpl, CompletableFuture> callback) throws Exception {
BotAssert.ActivityNotNull(activity);
BotAssert.activityNotNull(activity);
//ClaimsIdentity claimsIdentity = await(JwtTokenValidation.validateAuthHeader(activity, authHeader, _credentialProvider));
@ -215,7 +215,7 @@ public class BotFrameworkAdapter extends BotAdapter {
}
public CompletableFuture<InvokeResponse> ProcessActivity(ClaimsIdentity identity, Activity activity, Consumer<TurnContext> callback) throws Exception {
BotAssert.ActivityNotNull(activity);
BotAssert.activityNotNull(activity);
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
context.getTurnState().add("BotIdentity", identity);
@ -503,7 +503,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return Token Response
*/
public CompletableFuture<TokenResponse> GetUserToken(TurnContextImpl context, String connectionName, String magicCode) {
BotAssert.ContextNotNull(context);
BotAssert.contextNotNull(context);
if (context.getActivity().getFrom() == null || StringUtils.isEmpty(context.getActivity().getFrom().getId()))
throw new IllegalArgumentException("BotFrameworkAdapter.GetuserToken(): missing from or from.id");
@ -523,7 +523,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return
*/
public CompletableFuture<String> GetOauthSignInLink(TurnContextImpl context, String connectionName) {
BotAssert.ContextNotNull(context);
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(connectionName))
throw new IllegalArgumentException("connectionName");
@ -540,7 +540,7 @@ public class BotFrameworkAdapter extends BotAdapter {
* @return
*/
public CompletableFuture SignOutUser(TurnContextImpl context, String connectionName) {
BotAssert.ContextNotNull(context);
BotAssert.contextNotNull(context);
if (StringUtils.isEmpty(connectionName))
throw new IllegalArgumentException("connectionName");

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

@ -81,6 +81,16 @@ public interface BotTelemetryClient {
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.
*/
default void trackEvent(String eventName, Map<String, String> properties) {
trackEvent(eventName, properties, null);
}
/**
* Logs custom events with extensible named fields.
*

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

@ -174,13 +174,13 @@ public class MemoryTranscriptStore implements TranscriptStore {
* @return A task that represents the work queued to execute.
*/
@Override
public CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId, String continuationToken) {
public CompletableFuture<PagedResult<TranscriptInfo>> listTranscriptsAsync(String channelId, String continuationToken) {
if (channelId == null) {
throw new IllegalArgumentException(String.format("missing %1$s", "channelId"));
}
return CompletableFuture.supplyAsync(() -> {
PagedResult<Transcript> pagedResult = new PagedResult<Transcript>();
PagedResult<TranscriptInfo> pagedResult = new PagedResult<TranscriptInfo>();
synchronized (channels) {
if (!channels.containsKey(channelId)) {
@ -189,7 +189,7 @@ public class MemoryTranscriptStore implements TranscriptStore {
HashMap<String, ArrayList<Activity>> channel = channels.get(channelId);
if (continuationToken != null) {
List<Transcript> items = channel.entrySet().stream()
List<TranscriptInfo> items = channel.entrySet().stream()
.map(c -> {
OffsetDateTime offsetDateTime = null;
if (c.getValue().stream().findFirst().isPresent()) {
@ -201,24 +201,24 @@ public class MemoryTranscriptStore implements TranscriptStore {
} else {
offsetDateTime = OffsetDateTime.now();
}
return new Transcript()
return new TranscriptInfo()
.withChannelId(channelId)
.withId(c.getKey())
.withCreated(offsetDateTime);
}
)
.sorted(Comparator.comparing(Transcript::getCreated))
.sorted(Comparator.comparing(TranscriptInfo::getCreated))
.filter(skipwhile(c -> !c.getId().equals(continuationToken)))
.skip(1)
.limit(20)
.collect(Collectors.toList());
pagedResult.items(items.toArray(new Transcript[items.size()]));
pagedResult.items(items.toArray(new TranscriptInfo[items.size()]));
if (items.size() == 20) {
pagedResult.withContinuationToken(items.get(items.size() - 1).getId());
}
} else {
List<Transcript> items = channel.entrySet().stream()
List<TranscriptInfo> items = channel.entrySet().stream()
.map(c -> {
OffsetDateTime offsetDateTime = null;
if (c.getValue().stream().findFirst().isPresent()) {
@ -230,16 +230,16 @@ public class MemoryTranscriptStore implements TranscriptStore {
} else {
offsetDateTime = OffsetDateTime.now();
}
return new Transcript()
return new TranscriptInfo()
.withChannelId(channelId)
.withId(c.getKey())
.withCreated(offsetDateTime);
}
)
.sorted(Comparator.comparing(Transcript::getCreated))
.sorted(Comparator.comparing(TranscriptInfo::getCreated))
.limit(20)
.collect(Collectors.toList());
pagedResult.items(items.toArray(new Transcript[items.size()]));
pagedResult.items(items.toArray(new TranscriptInfo[items.size()]));
if (items.size() == 20) {
pagedResult.withContinuationToken(items.get(items.size() - 1).getId());
}

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

@ -1,58 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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)}
* 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,
* both before and after the bot logic runs.</p>
* <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#onTurnAsync(TurnContext)} method.
* <code>
* public class SampleMiddleware : Middleware
* {
* public async Task OnTurn(TurnContext context, MiddlewareSet.NextDelegate next)
* {
* context.SendActivity("before");
* await next().ConfigureAwait(false);
* context.SendActivity("after");
* }
* }
* </code>
* </example>
* {@linkalso Bot}
*/
public interface Middleware {
/**
* Processess 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.
* Middleware calls the {@code next} delegate to pass control to
* the next middleware in the pipeline. If middleware doesnt call the next delegate,
* the adapter does not call any of the subsequent middlewares request handlers or the
* bots receive handler, and the pipeline short circuits.
* <p>The {@code context} provides information about the
* incoming activity, and other data needed to process the activity.</p>
*
* {@link TurnContext}
* {@link com.microsoft.bot.schema.Activity}
*/
CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next);
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// 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)}
* 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,
* both before and after the bot logic runs.</p>
* <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#onTurnAsync(TurnContext)} method.
* <code>
* public class SampleMiddleware : Middleware
* {
* public async Task OnTurn(TurnContext context, MiddlewareSet.NextDelegate next)
* {
* context.SendActivity("before");
* await next().ConfigureAwait(false);
* context.SendActivity("after");
* }
* }
* </code>
* </example>
* {@linkalso Bot}
*/
public interface Middleware {
/**
* Processess an incoming activity.
* @param turnContext The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return A task that represents the work queued to execute.
* Middleware calls the {@code next} delegate to pass control to
* the next middleware in the pipeline. If middleware doesnt call the next delegate,
* the adapter does not call any of the subsequent middlewares request handlers or the
* bots receive handler, and the pipeline short circuits.
* <p>The {@code context} provides information about the
* incoming activity, and other data needed to process the activity.</p>
*
* {@link TurnContext}
* {@link com.microsoft.bot.schema.Activity}
*/
CompletableFuture<Void> onTurnAsync(TurnContext turnContext, NextDelegate next);
}

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

@ -20,7 +20,7 @@ public class MiddlewareSet implements Middleware {
* @return The updated middleware set.
*/
public MiddlewareSet use(Middleware middleware) {
BotAssert.MiddlewareNotNull(middleware);
BotAssert.middlewareNotNull(middleware);
this.middleware.add(middleware);
return this;
}

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

@ -11,5 +11,5 @@ public interface OnTurnErrorHandler {
* @param exception The exception thrown.
* @return A task that represents the work queued to execute.
*/
CompletableFuture<Void> invoke(TurnContext turnContext, Throwable exception);
Void invoke(TurnContext turnContext, Throwable exception);
}

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

@ -0,0 +1,60 @@
package com.microsoft.bot.builder;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.microsoft.bot.connector.Channels;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.Entity;
import org.apache.commons.lang3.StringUtils;
import java.util.concurrent.CompletableFuture;
/**
* Middleware to patch mention Entities from Skype since they don't conform to expected values.
* Bots that interact with Skype should use this middleware if mentions are used.
*
* A Skype mention "text" field is of the format:
* &lt;at id=\"28:2bc5b54d-5d48-4ff1-bd25-03dcbb5ce918\">botname&lt;/at&gt;
* But Activity.Text doesn't contain those tags and RemoveMentionText can't remove
* the entity from Activity.Text.
* This will remove the &lt;at&gt; nodes, leaving just the name.
*/
public class SkypeMentionNormalizeMiddleware implements Middleware {
public static void normalizeSkypMentionText(Activity activity) {
if (StringUtils.equals(activity.getChannelId(), Channels.SKYPE)
&& activity.getType() == ActivityTypes.MESSAGE) {
for (Entity entity : activity.getEntities()) {
if (StringUtils.equals(entity.getType(), "mention")) {
String text = entity.getProperties().get("text").asText();
int closingBracket = text.indexOf(">");
if (closingBracket != -1) {
int openingBracket = text.indexOf("<", closingBracket);
if (openingBracket != -1) {
String mention = text.substring(closingBracket + 1, openingBracket);
// create new JsonNode with new mention value
ObjectNode node = JsonNodeFactory.instance.objectNode();
node.put("text", mention);
entity.setProperties("text", node);
}
}
}
}
}
}
/**
* Middleware implementation which corrects Enity.Mention.Text to a value RemoveMentionText can work with.
*
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return
*/
@Override
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
normalizeSkypMentionText(context.getActivity());
return next.next();
}
}

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

@ -1,32 +0,0 @@
package com.microsoft.bot.builder;
import com.microsoft.bot.builder.ConversationState;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.UserState;
/**
* Provides helper methods for getting state objects from the turn context.
*/
public class StateTurnContextExtensions
{
/**
* Gets a conversation state object from the turn context.
* @param TState The type of the state object to get.
* @param context The context object for this turn.
* @return The state object.
*/
public static <TState extends Object> TState GetConversationState(TurnContext context) throws IllegalArgumentException {
return ConversationState.<TState>Get(context);
}
/**
* Gets a user state object from the turn context.
* @param TState The type of the state object to get.
* @param context The context object for this turn.
* @return The state object.
*/
public static <TState> TState GetUserState(TurnContext context) throws IllegalArgumentException {
return UserState.<TState>Get(context);
}
}

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

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
/**
* Telemetry logger property names.
*/
public class TelemetryConstants {
public static final String CHANNELIDPROPERTY = "channelId";
public static final String CONVERSATIONIDPROPERTY = "conversationId";
public static final String CONVERSATIONNAMEPROPERTY = "conversationName";
public static final String DIALOGIDPROPERTY = "dialogId";
public static final String FROMIDPROPERTY = "fromId";
public static final String FROMNAMEPROPERTY = "fromName";
public static final String LOCALEPROPERTY = "locale";
public static final String RECIPIENTIDPROPERTY = "recipientId";
public static final String RECIPIENTNAMEPROPERTY = "recipientName";
public static final String REPLYACTIVITYIDPROPERTY = "replyActivityId";
public static final String TEXTPROPERTY = "text";
public static final String SPEAKPROPERTY = "speak";
public static final String USERIDPROPERTY = "userId";
}

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

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
/**
* The Telemetry Logger Event names.
*/
public class TelemetryLoggerConstants {
/**
* The name of the event when a new message is received from the user.
*/
public static final String BOTMSGRECEIVEEVENT = "BotMessageReceived";
/**
* The name of the event when logged when a message is sent from the bot to the user.
*/
public static final String BOTMSGSENDEVENT = "BotMessageSend";
/**
* The name of the event when a message is updated by the bot.
*/
public static final String BOTMSGUPDATEEVENT = "BotMessageUpdate";
/**
* The name of the event when a message is deleted by the bot.
*/
public static final String BOTMSGDELETEEVENT = "BotMessageDelete";
}

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

@ -0,0 +1,295 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* Middleware for logging incoming, outgoing, updated or deleted Activity messages.
* Uses the {@link BotTelemetryClient} interface.
*/
public class TelemetryLoggerMiddleware implements Middleware {
/**
* Indicates whether determines whether to log personal information that came from the user.
*/
private boolean logPersonalInformation;
/**
* The currently configured {@link BotTelemetryClient} that logs the QnaMessage event.
*/
private BotTelemetryClient telemetryClient;
/**
* Initializes a new instance of the class.
*
* @param withTelemetryClient The IBotTelemetryClient implementation used for registering telemetry events.
* @param withLogPersonalInformation TRUE to include personally identifiable information.
*/
public TelemetryLoggerMiddleware(BotTelemetryClient withTelemetryClient, boolean withLogPersonalInformation) {
telemetryClient = withTelemetryClient == null ? new NullBotTelemetryClient() : withTelemetryClient;
logPersonalInformation = withLogPersonalInformation;
}
/**
* Logs events based on incoming and outgoing activities using the {@link BotTelemetryClient} interface.
*
* @param context The context object for this turn.
* @param next The delegate to call to continue the bot middleware pipeline.
* @return
*/
@Override
public CompletableFuture<Void> onTurnAsync(TurnContext context, NextDelegate next) {
BotAssert.contextNotNull(context);
// log incoming activity at beginning of turn
return onReceiveActivityAsync(context.getActivity())
.thenCompose(receiveResult -> {
// hook up onSend pipeline
context.onSendActivities((sendContext, sendActivities, sendNext) -> sendNext.get()
.thenApply(responses -> {
for (Activity sendActivity : sendActivities) {
onSendActivityAsync(sendActivity);
}
return responses;
}));
// hook up update activity pipeline
context.onUpdateActivity((updateContext, updateActivity, updateNext) -> updateNext.get()
.thenCombine(onUpdateActivityAsync(updateActivity), (resourceResponse, updateResult) -> resourceResponse));
// hook up delete activity pipeline
context.onDeleteActivity((deleteContext, deleteReference, deleteNext) -> deleteNext.get()
.thenCompose(nextResult -> {
Activity deleteActivity = new Activity(ActivityTypes.MESSAGE_DELETE) {{
setId(deleteReference.getActivityId());
applyConversationReference(deleteReference, false);
}};
return onDeleteActivityAsync(deleteActivity);
}));
if (next != null) {
return next.next();
}
return CompletableFuture.completedFuture(null);
});
}
/**
* Invoked when a message is received from the user.
* Performs logging of telemetry data using the {@link BotTelemetryClient#trackEvent} method.
* This event name used is "BotMessageReceived".
*
* @param activity Current activity sent from user.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onReceiveActivityAsync(Activity activity) {
if (activity == null) {
return CompletableFuture.completedFuture(null);
}
return fillReceiveEventPropertiesAsync(activity, null)
.thenAccept(properties -> {
telemetryClient.trackEvent(TelemetryLoggerConstants.BOTMSGRECEIVEEVENT, properties);
});
}
/**
* Invoked when the bot sends a message to the user.
* Performs logging of telemetry data using the {@link BotTelemetryClient#trackEvent} method.
* This event name used is "BotMessageSend".
*
* @param activity Current activity sent from user.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onSendActivityAsync(Activity activity) {
return fillSendEventPropertiesAsync(activity, null)
.thenAccept(properties -> {
telemetryClient.trackEvent(TelemetryLoggerConstants.BOTMSGSENDEVENT, properties);
});
}
/**
* Invoked when the bot updates a message.
* Performs logging of telemetry data using the {@link BotTelemetryClient#trackEvent} method.
* This event name used is "BotMessageUpdate".
*
* @param activity Current activity sent from user.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onUpdateActivityAsync(Activity activity) {
return fillUpdateEventPropertiesAsync(activity, null)
.thenAccept(properties -> {
telemetryClient.trackEvent(TelemetryLoggerConstants.BOTMSGUPDATEEVENT, properties);
});
}
/**
* Invoked when the bot deletes a message.
* Performs logging of telemetry data using the {@link BotTelemetryClient#trackEvent} method.
* This event name used is "BotMessageDelete".
*
* @param activity Current activity sent from user.
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<Void> onDeleteActivityAsync(Activity activity) {
return fillDeleteEventPropertiesAsync(activity, null)
.thenAccept(properties -> {
telemetryClient.trackEvent(TelemetryLoggerConstants.BOTMSGDELETEEVENT, properties);
});
}
/**
* Fills the event properties for the BotMessageReceived.
* Adheres to the LogPersonalInformation flag to filter Name, Text and Speak properties.
*
* @param activity Last activity sent from user.
* @param additionalProperties Additional properties to add to the event.
* @return A dictionary that is sent as "Properties" to {@link BotTelemetryClient#trackEvent} method for
* the BotMessageReceived event.
*/
protected CompletableFuture<Map<String, String>> fillReceiveEventPropertiesAsync(
Activity activity, Map<String, String> additionalProperties) {
Map<String, String> properties = new HashMap<String, String>() {{
put(TelemetryConstants.FROMIDPROPERTY, activity.getFrom().getId());
put(TelemetryConstants.CONVERSATIONNAMEPROPERTY, activity.getConversation().getName());
put(TelemetryConstants.LOCALEPROPERTY, activity.getLocale());
put(TelemetryConstants.RECIPIENTIDPROPERTY, activity.getRecipient().getId());
put(TelemetryConstants.RECIPIENTNAMEPROPERTY, activity.getRecipient().getName());
}};
// Use the LogPersonalInformation flag to toggle logging PII data, text and user name are common examples
if (logPersonalInformation) {
if (!StringUtils.isEmpty(activity.getFrom().getName())) {
properties.put(TelemetryConstants.FROMNAMEPROPERTY, activity.getFrom().getName());
}
if (!StringUtils.isEmpty(activity.getText())) {
properties.put(TelemetryConstants.TEXTPROPERTY, activity.getText());
}
if (!StringUtils.isEmpty(activity.getSpeak())) {
properties.put(TelemetryConstants.SPEAKPROPERTY, activity.getSpeak());
}
}
// Additional Properties can override "stock" properties.
if (additionalProperties != null) {
properties.putAll(additionalProperties);
}
return CompletableFuture.completedFuture(properties);
}
/**
* Fills the event properties for BotMessageSend.
* These properties are logged when an activity message is sent by the Bot to the user.
*
* @param activity Last activity sent from user.
* @param additionalProperties Additional properties to add to the event.
* @return A dictionary that is sent as "Properties" to {@link BotTelemetryClient#trackEvent} method for
* the BotMessageSend event.
*/
protected CompletableFuture<Map<String, String>> fillSendEventPropertiesAsync(
Activity activity, Map<String, String> additionalProperties) {
Map<String, String> properties = new HashMap<String, String>() {{
put(TelemetryConstants.REPLYACTIVITYIDPROPERTY, activity.getReplyToId());
put(TelemetryConstants.RECIPIENTIDPROPERTY, activity.getRecipient().getId());
put(TelemetryConstants.CONVERSATIONNAMEPROPERTY, activity.getConversation().getName());
put(TelemetryConstants.LOCALEPROPERTY, activity.getLocale());
}};
// Use the LogPersonalInformation flag to toggle logging PII data, text and user name are common examples
if (logPersonalInformation) {
if (!StringUtils.isEmpty(activity.getRecipient().getName())) {
properties.put(TelemetryConstants.RECIPIENTNAMEPROPERTY, activity.getRecipient().getName());
}
if (!StringUtils.isEmpty(activity.getText())) {
properties.put(TelemetryConstants.TEXTPROPERTY, activity.getText());
}
if (!StringUtils.isEmpty(activity.getSpeak())) {
properties.put(TelemetryConstants.SPEAKPROPERTY, activity.getSpeak());
}
}
// Additional Properties can override "stock" properties.
if (additionalProperties != null) {
properties.putAll(additionalProperties);
}
return CompletableFuture.completedFuture(properties);
}
/**
* Fills the event properties for BotMessageUpdate.
* These properties are logged when an activity message is sent by the Bot to the user.
*
* @param activity Last activity sent from user.
* @param additionalProperties Additional properties to add to the event.
* @return A dictionary that is sent as "Properties" to {@link BotTelemetryClient#trackEvent} method for
* the BotMessageUpdate event.
*/
protected CompletableFuture<Map<String, String>> fillUpdateEventPropertiesAsync(
Activity activity, Map<String, String> additionalProperties) {
Map<String, String> properties = new HashMap<String, String>() {{
put(TelemetryConstants.RECIPIENTIDPROPERTY, activity.getRecipient().getId());
put(TelemetryConstants.CONVERSATIONIDPROPERTY, activity.getConversation().getId());
put(TelemetryConstants.CONVERSATIONNAMEPROPERTY, activity.getConversation().getName());
put(TelemetryConstants.LOCALEPROPERTY, activity.getLocale());
}};
// Use the LogPersonalInformation flag to toggle logging PII data, text is a common example
if (logPersonalInformation) {
if (!StringUtils.isEmpty(activity.getText())) {
properties.put(TelemetryConstants.TEXTPROPERTY, activity.getText());
}
}
// Additional Properties can override "stock" properties.
if (additionalProperties != null) {
properties.putAll(additionalProperties);
}
return CompletableFuture.completedFuture(properties);
}
/**
* Fills the event properties for BotMessageDelete.
* These properties are logged when an activity message is sent by the Bot to the user.
*
* @param activity Last activity sent from user.
* @param additionalProperties Additional properties to add to the event.
* @return A dictionary that is sent as "Properties" to {@link BotTelemetryClient#trackEvent} method for
* the BotMessageDelete event.
*/
protected CompletableFuture<Map<String, String>> fillDeleteEventPropertiesAsync(
Activity activity, Map<String, String> additionalProperties) {
Map<String, String> properties = new HashMap<String, String>() {{
put(TelemetryConstants.RECIPIENTIDPROPERTY, activity.getRecipient().getId());
put(TelemetryConstants.CONVERSATIONIDPROPERTY, activity.getConversation().getId());
put(TelemetryConstants.CONVERSATIONNAMEPROPERTY, activity.getConversation().getName());
}};
// Additional Properties can override "stock" properties.
if (additionalProperties != null) {
properties.putAll(additionalProperties);
}
return CompletableFuture.completedFuture(properties);
}
}

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

@ -1,10 +1,7 @@
package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@ -12,9 +9,7 @@ import com.microsoft.bot.schema.Activity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.CompletableFuture;
/**
* Represents a transcript logger that writes activites to a <see cref="Trace"/> object.
@ -32,14 +27,16 @@ public class TraceTranscriptLogger implements TranscriptLogger {
* @return A task that represents the work queued to execute.
*/
@Override
public void LogActivityAsync(Activity activity) {
BotAssert.ActivityNotNull(activity);
String event = null;
try {
event = mapper.writeValueAsString(activity);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
this.logger.info(event);
public CompletableFuture<Void> logActivityAsync(Activity activity) {
BotAssert.activityNotNull(activity);
String event = null;
try {
event = mapper.writeValueAsString(activity);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
this.logger.info(event);
return CompletableFuture.completedFuture(null);
}
}

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

@ -1,54 +1,50 @@
package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import java.time.OffsetDateTime;
/**
* Transcript store item.
*/
public class Transcript {
/**
* channelId that the transcript was taken from.
*/
private String channelId;
public String channelId() {
return this.channelId;
}
public Transcript withChannelId(String value) {
this.channelId = value;
return this;
}
/**
* Conversation id.
*/
private String id;
public String getId() {
return this.id;
}
public Transcript withId(String value) {
this.id = value;
return this;
}
/**
* Date conversation was started.
*/
private OffsetDateTime created = OffsetDateTime.now();
public OffsetDateTime getCreated() {
return this.created;
}
public Transcript withCreated(OffsetDateTime value) {
this.created = value;
return this;
}
}
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder;
import java.time.OffsetDateTime;
/**
* Represents a copy of a conversation.
*/
public class TranscriptInfo {
/**
* channelId that the transcript was taken from.
*/
private String channelId;
public String channelId() {
return this.channelId;
}
public void setChannelId(String withValue) {
channelId = withValue;
}
/**
* Conversation id.
*/
private String id;
public String getId() {
return id;
}
public void setId(String witValue) {
id = witValue;
}
/**
* Date conversation was started.
*/
private OffsetDateTime created = OffsetDateTime.now();
public OffsetDateTime getCreated() {
return created;
}
public void setCreated(OffsetDateTime withValue) {
created = withValue;
}
}

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

@ -113,7 +113,7 @@ public class TranscriptLoggerMiddleware implements Middleware {
});
// hook up delete activity pipeline
context.onDeleteActivity((ctxt, reference, nextDel) -> {
context.onDeleteActivity((ctx, reference, nextDel) -> {
// run full pipeline
if (nextDel != null) {
@ -141,7 +141,7 @@ public class TranscriptLoggerMiddleware implements Middleware {
while (!transcript.isEmpty()) {
Activity activity = transcript.poll();
try {
this.transcriptLogger.LogActivityAsync(activity);
this.transcriptLogger.logActivityAsync(activity);
} catch (RuntimeException err) {
logger.error(String.format("Transcript poll failed : %1$s", err));
}

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

@ -62,7 +62,7 @@ public interface TranscriptStore extends TranscriptLogger {
* @param channelId The ID of the channel.
* @return A task that represents the work queued to execute.
*/
default CompletableFuture<PagedResult<Transcript>> listTranscriptsAsync(String channelId) {
default CompletableFuture<PagedResult<TranscriptInfo>> listTranscriptsAsync(String channelId) {
return listTranscriptsAsync(channelId, null);
}
@ -73,7 +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, String continuationToken);
CompletableFuture<PagedResult<TranscriptInfo>> listTranscriptsAsync(String channelId, String continuationToken);
/**
* Deletes conversation data from the store.

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

@ -3,7 +3,6 @@ package com.microsoft.bot.builder;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import com.microsoft.bot.connector.ExecutorFactory;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationReference;
import com.microsoft.bot.schema.InputHints;
@ -247,42 +246,36 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
applyConversationReference(a, cr);
}
// Convert the IActivities to Activies.
// Activity[] activityArray = Array.ConvertAll(activities, (input) => (Activity)input);
// Convert the IActivities to Activities.
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);
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
// sent out.
boolean sentNonTraceActivities = false;
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.
ResourceResponse[] responses = new ResourceResponse[0];
responses = this.getAdapter().SendActivities(this, activityList.toArray(new Activity[activityList.size()]));
if (responses != null && responses.length == activityList.size()) {
// stitch up activity ids
for (int i = 0; i < responses.length; i++) {
ResourceResponse response = responses[i];
Activity activity = activityList.get(i);
activity.setId(response.getId());
}
}
return getAdapter().sendActivitiesAsync(this, activityList.toArray(new Activity[activityList.size()]))
.thenApply(responses -> {
if (responses != null && responses.length == activityList.size()) {
// stitch up activity ids
for (int i = 0; i < responses.length; i++) {
ResourceResponse response = responses[i];
Activity activity = activityList.get(i);
activity.setId(response.getId());
}
}
// If we actually sent something (that's not Trace), set the flag.
if (sentNonTraceActivities) {
this.setResponded(true);
}
return responses;
// 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
// sent out.
if (activityList.stream().anyMatch((a) -> a.getType() == TRACE)) {
this.setResponded(true);
}
return responses;
});
};
List<Activity> act_list = new ArrayList<>(activityList);
@ -298,8 +291,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
*/
@Override
public CompletableFuture<ResourceResponse> updateActivityAsync(Activity activity) {
Supplier<ResourceResponse> ActuallyUpdateStuff = () -> {
return this.getAdapter().UpdateActivity(this, activity);
Supplier<CompletableFuture<ResourceResponse>> ActuallyUpdateStuff = () -> {
return getAdapter().updateActivityAsync(this, activity);
};
return updateActivityInternal(activity, onUpdateActivity.iterator(), ActuallyUpdateStuff);
@ -313,35 +306,17 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @throws Exception The HTTP operation failed and the response contained additional information.
*/
public CompletableFuture<Void> deleteActivityAsync(String activityId) {
if (StringUtils.isWhitespace(activityId) || activityId == null)
if (StringUtils.isWhitespace(activityId) || activityId == null) {
throw new IllegalArgumentException("activityId");
}
return CompletableFuture.runAsync(() -> {
ConversationReference cr = this.GetConversationReference(this.getActivity());
cr.setActivityId(activityId);
ConversationReference cr = getConversationReference(getActivity());
cr.setActivityId(activityId);
Runnable ActuallyDeleteStuff = () -> {
try {
this.getAdapter().DeleteActivity(this, cr);
} catch (ExecutionException e) {
e.printStackTrace();
throw new RuntimeException(String.format("Failed to delete activity %s", e.toString()));
} catch (InterruptedException e) {
e.printStackTrace();
throw new RuntimeException(String.format("Failed to delete activity %s", e.toString()));
}
return;
};
Supplier<CompletableFuture<Void>> ActuallyDeleteStuff = () ->
getAdapter().deleteActivityAsync(this, cr);
try {
deleteActivityInternal(cr, onDeleteActivity.iterator(), ActuallyDeleteStuff);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("Failed to delete activity %s", e.getMessage()));
}
return;
}, ExecutorFactory.getExecutor());
return deleteActivityInternal(cr, onDeleteActivity.iterator(), ActuallyDeleteStuff);
}
/**
@ -357,17 +332,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
if (conversationReference == null)
throw new IllegalArgumentException("conversationReference");
Runnable ActuallyDeleteStuff = () -> {
try {
this.getAdapter().DeleteActivity(this, conversationReference);
return;
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException("DeleteActivity failed");
};
Supplier<CompletableFuture<Void>> ActuallyDeleteStuff = () ->
getAdapter().deleteActivityAsync(this, conversationReference);
return deleteActivityInternal(conversationReference, onDeleteActivity.iterator(), ActuallyDeleteStuff);
}
@ -377,15 +343,18 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
Iterator<SendActivitiesHandler> sendHandlers,
Supplier<CompletableFuture<ResourceResponse[]>> callAtBottom) {
if (activities == null)
if (activities == null) {
throw new IllegalArgumentException("activities");
if (sendHandlers == null)
}
if (sendHandlers == null) {
throw new IllegalArgumentException("sendHandlers");
}
if (false == sendHandlers.hasNext()) { // No middleware to run.
if (callAtBottom != null)
if (!sendHandlers.hasNext()) { // No middleware to run.
if (callAtBottom != null) {
return callAtBottom.get();
return new ResourceResponse[0];
}
return CompletableFuture.completedFuture(new ResourceResponse[0]);
}
// Default to "No more Middleware after this".
@ -445,8 +414,8 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
// }
private CompletableFuture<ResourceResponse> updateActivityInternal(Activity activity,
Iterator<UpdateActivityHandler> updateHandlers,
Supplier<ResourceResponse> callAtBottom) {
BotAssert.ActivityNotNull(activity);
Supplier<CompletableFuture<ResourceResponse>> callAtBottom) {
BotAssert.activityNotNull(activity);
if (updateHandlers == null)
throw new IllegalArgumentException("updateHandlers");
@ -463,15 +432,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
// 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);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(String.format("Error updating activity: %s", e.toString()));
}
activity.setId(result.getId());
return result;
return updateActivityInternal(activity, updateHandlers, callAtBottom)
.thenApply(resourceResponse -> {
activity.setId(resourceResponse.getId());
return resourceResponse;
});
};
// Grab the current middleware, which is the 1st element in the array, and execute it
@ -482,16 +448,16 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
private CompletableFuture<Void> deleteActivityInternal(ConversationReference cr,
Iterator<DeleteActivityHandler> deleteHandlers,
Runnable callAtBottom) {
BotAssert.ConversationReferenceNotNull(cr);
Supplier<CompletableFuture<Void>> callAtBottom) {
BotAssert.conversationReferenceNotNull(cr);
if (deleteHandlers == null)
throw new IllegalArgumentException("deleteHandlers");
if (deleteHandlers.hasNext() == false) { // No middleware to run.
if (!deleteHandlers.hasNext()) { // No middleware to run.
if (callAtBottom != null) {
callAtBottom.run();
return callAtBottom.get();
}
return;
return CompletableFuture.completedFuture(null);
}
// Default to "No more Middleware after this".
@ -503,20 +469,12 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
if (deleteHandlers.hasNext())
deleteHandlers.next();
try {
deleteActivityInternal(cr, deleteHandlers, callAtBottom);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("DeleteActivityInternal failed");
}
return null;
return deleteActivityInternal(cr, deleteHandlers, callAtBottom);
};
// Grab the current middleware, which is the 1st element in the array, and execute it.
DeleteActivityHandler toCall = deleteHandlers.next();
toCall.invoke(this, cr, next);
return toCall.invoke(this, cr, next);
}
/**
@ -527,7 +485,7 @@ public class TurnContextImpl implements TurnContext, AutoCloseable {
* @throws IllegalArgumentException {@code activity} is {@code null}.
*/
public static ConversationReference getConversationReference(Activity activity) {
BotAssert.ActivityNotNull(activity);
BotAssert.activityNotNull(activity);
ConversationReference r = new ConversationReference() {{
setActivityId(activity.getId());

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

@ -1,4 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.inspection;
public class InspectionActivityExtensions {
import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ConversationReference;
public final class InspectionActivityExtensions {
private InspectionActivityExtensions() {
}
public static Activity makeCommandActivity(String command) {
return Activity.createTraceActivity("Command", "https://www.botframework.com/schemas/command", command, "Command");
}
public static Activity traceActivity(JsonNode state) {
return Activity.createTraceActivity("BotState", "https://www.botframework.com/schemas/botState", state, "Bot State");
}
public static Activity traceActivity(Activity activity, String name, String label) {
return Activity.createTraceActivity(name, "https://www.botframework.com/schemas/activity", activity, label);
}
public static Activity traceActivity(ConversationReference conversationReference) {
return Activity.createTraceActivity("MessageDelete", "https://www.botframework.com/schemas/conversationReference", conversationReference, "Deleted Message");
}
public static Activity traceActivity(Throwable exception) {
return Activity.createTraceActivity("TurnError", "https://www.botframework.com/schemas/error", exception.getMessage(), "Turn Error");
}
}

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

@ -0,0 +1,112 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.bot.builder.inspection;
import com.microsoft.bot.builder.Middleware;
import com.microsoft.bot.builder.NextDelegate;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.schema.Activity;
import org.slf4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
public abstract class InterceptionMiddleware implements Middleware {
private Logger logger;
public static class Intercept {
public Intercept(boolean forward, boolean intercept) {
shouldForwardToApplication = forward;
shouldIntercept = intercept;
}
public boolean shouldForwardToApplication;
public boolean shouldIntercept;
}
public InterceptionMiddleware(Logger withLogger) {
logger = withLogger;
}
@Override
public CompletableFuture<Void> onTurnAsync(TurnContext turnContext, NextDelegate next) {
return invokeInboundAsync(turnContext, InspectionActivityExtensions
.traceActivity(turnContext.getActivity(),"ReceivedActivity","Received Activity"))
.thenCompose(intercept -> {
if (intercept.shouldIntercept) {
turnContext.onSendActivities((sendContext, sendActivities, sendNext) -> {
List<Activity> traceActivities = sendActivities.stream()
.map(a ->
InspectionActivityExtensions.traceActivity(a, "SentActivity", "Sent Activity"))
.collect(Collectors.toList());
return invokeOutboundAsync(sendContext, traceActivities)
.thenCompose(response -> {
return sendNext.get();
});
});
}
if (intercept.shouldForwardToApplication) {
next.next()
.exceptionally(exception -> {
Activity traceActivity = InspectionActivityExtensions.traceActivity(exception);
invokeTraceExceptionAsync(turnContext, traceActivity).join();
throw new CompletionException(exception);
}).join();
}
if (intercept.shouldIntercept) {
return invokeTraceStateAsync(turnContext);
}
return null;
});
}
protected abstract CompletableFuture<Intercept> inboundAsync(TurnContext turnContext, Activity activity);
protected abstract CompletableFuture<Void> outboundAsync(TurnContext turnContext, List<Activity> clonedActivities);
protected abstract CompletableFuture<Void> traceStateAsync(TurnContext turnContext);
private CompletableFuture<Intercept> invokeInboundAsync(TurnContext turnContext, Activity traceActivity) {
return inboundAsync(turnContext, traceActivity)
.exceptionally(exception -> {
logger.warn("Exception in inbound interception {}", exception.getMessage());
return new Intercept(true, false);
});
}
private CompletableFuture<Void> invokeOutboundAsync(TurnContext turnContext, List<Activity> traceActivities) {
return outboundAsync(turnContext, traceActivities)
.exceptionally(exception -> {
logger.warn("Exception in outbound interception {}", exception.getMessage());
return null;
});
}
private CompletableFuture<Void> invokeOutboundAsync(TurnContext turnContext, Activity activity) {
return invokeOutboundAsync(turnContext, Collections.singletonList(activity));
}
private CompletableFuture<Void> invokeTraceStateAsync(TurnContext turnContext) {
return traceStateAsync(turnContext)
.exceptionally(exception -> {
logger.warn("Exception in state interception {}", exception.getMessage());
return null;
});
}
private CompletableFuture<Void> invokeTraceExceptionAsync(TurnContext turnContext, Activity traceActivity) {
return outboundAsync(turnContext, Collections.singletonList(Activity.createContactRelationUpdateActivity()))
.exceptionally(exception -> {
logger.warn("Exception in exception interception {}", exception.getMessage());
return null;
});
}
}

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

@ -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-Inspection.
*/
package com.microsoft.bot.builder.inspection;

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

@ -3,10 +3,7 @@ package com.microsoft.bot.builder.adapters;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import com.microsoft.bot.builder.BotAdapter;
import com.microsoft.bot.builder.Middleware;
import com.microsoft.bot.builder.TurnContext;
import com.microsoft.bot.builder.TurnContextImpl;
import com.microsoft.bot.builder.*;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.*;
import org.apache.commons.lang3.StringUtils;
@ -57,12 +54,12 @@ public class TestAdapter extends BotAdapter {
}
public TestAdapter Use(Middleware middleware) {
super.Use(middleware);
super.use(middleware);
return this;
}
public void ProcessActivity(Activity activity,
Consumer<TurnContext> callback
BotCallbackHandler callback
) throws Exception {
synchronized (this.conversationReference()) {
// ready for next reply
@ -81,7 +78,7 @@ public class TestAdapter extends BotAdapter {
activity.setTimestamp(DateTime.now());
try (TurnContextImpl context = new TurnContextImpl(this, activity)) {
super.RunPipeline(context, callback);
super.runPipelineAsync(context, callback);
}
return;
}
@ -95,7 +92,7 @@ public class TestAdapter extends BotAdapter {
}
@Override
public ResourceResponse[] SendActivities(TurnContext context, Activity[] activities) {
public CompletableFuture<ResourceResponse[]> sendActivitiesAsync(TurnContext context, Activity[] activities) {
List<ResourceResponse> responses = new LinkedList<ResourceResponse>();
for (Activity activity : activities) {
@ -127,12 +124,12 @@ public class TestAdapter extends BotAdapter {
}
}
}
return responses.toArray(new ResourceResponse[responses.size()]);
return CompletableFuture.completedFuture(responses.toArray(new ResourceResponse[responses.size()]));
}
@Override
public ResourceResponse UpdateActivity(TurnContext context, Activity activity) {
public CompletableFuture<ResourceResponse> updateActivityAsync(TurnContext context, Activity activity) {
synchronized (this.botReplies) {
List<Activity> replies = new ArrayList<>(botReplies);
for (int i = 0; i < this.botReplies.size(); i++) {
@ -143,15 +140,15 @@ public class TestAdapter extends BotAdapter {
for (Activity item : replies) {
this.botReplies.add(item);
}
return new ResourceResponse(activity.getId());
return CompletableFuture.completedFuture(new ResourceResponse(activity.getId()));
}
}
}
return new ResourceResponse();
return CompletableFuture.completedFuture(new ResourceResponse());
}
@Override
public void DeleteActivity(TurnContext context, ConversationReference reference) {
public CompletableFuture<Void> deleteActivityAsync(TurnContext context, ConversationReference reference) {
synchronized (this.botReplies) {
ArrayList<Activity> replies = new ArrayList<>(this.botReplies);
for (int i = 0; i < this.botReplies.size(); i++) {
@ -165,7 +162,7 @@ public class TestAdapter extends BotAdapter {
}
}
}
return;
return CompletableFuture.completedFuture(null);
}
/**
@ -229,7 +226,7 @@ public class TestAdapter extends BotAdapter {
* @param userSays
* @return
*/
public void SendTextToBot(String userSays, Consumer<TurnContext> callback) throws Exception {
public void SendTextToBot(String userSays, BotCallbackHandler callback) throws Exception {
this.ProcessActivity(this.MakeActivity(userSays), callback);
}
}

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

@ -29,7 +29,7 @@ public abstract class Dialog
}
public CompletableFuture<DialogCompletion> Begin(TurnContext context, HashMap<String, Object> state, HashMap<String, Object> options)
{
BotAssert.ContextNotNull(context);
BotAssert.contextNotNull(context);
if (state == null)
throw new NullPointerException("HashMap<String, Object> state");
@ -74,7 +74,7 @@ public abstract class Dialog
*/
public CompletableFuture<DialogCompletion> Continue(TurnContext context, HashMap<String, Object> state)
{
BotAssert.ContextNotNull(context);
BotAssert.contextNotNull(context);
if (state == null)
throw new NullPointerException("HashMap<String, Object>");

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

@ -315,8 +315,13 @@ public class Activity {
*
* @param withName Name of the operation
* @param withValueType valueType if helpful to identify the value schema (default is value.GetType().Name)
* @param withValue The content for this trace operation.
* @param withLabel A descriptive label for this trace operation.
*/
public static Activity createTraceActivity(String withName, Object withValue, String withValueType, String withLabel) {
public static Activity createTraceActivity(String withName,
String withValueType,
Object withValue,
String withLabel) {
return new Activity(ActivityTypes.TRACE) {{
setName(withName);
setLabel(withLabel);