bot-builder periodic
This commit is contained in:
Родитель
abcecf7ad7
Коммит
3f19e94539
|
@ -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 adapter’s middleware collection. The
|
||||
* adapter processes and directs incoming activities in through the bot middleware
|
||||
* pipeline to your bot’s logic and then back out again. As each activity flows in
|
||||
* and out of the bot, each piece of middleware can inspect or act upon the activity,
|
||||
* 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 doesn’t call the next delegate,
|
||||
* the adapter does not call any of the subsequent middleware’s request handlers or the
|
||||
* bot’s receive handler, and the pipeline short circuits.
|
||||
* <p>The {@code context} provides information about the
|
||||
* 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 adapter’s middleware collection. The
|
||||
* adapter processes and directs incoming activities in through the bot middleware
|
||||
* pipeline to your bot’s logic and then back out again. As each activity flows in
|
||||
* and out of the bot, each piece of middleware can inspect or act upon the activity,
|
||||
* 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 doesn’t call the next delegate,
|
||||
* the adapter does not call any of the subsequent middleware’s request handlers or the
|
||||
* bot’s receive handler, and the pipeline short circuits.
|
||||
* <p>The {@code context} provides information about the
|
||||
* 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:
|
||||
* <at id=\"28:2bc5b54d-5d48-4ff1-bd25-03dcbb5ce918\">botname</at>
|
||||
* But Activity.Text doesn't contain those tags and RemoveMentionText can't remove
|
||||
* the entity from Activity.Text.
|
||||
* This will remove the <at> 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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче