From 2c7e62a9246453e17f30a5d905cfaf232feb5210 Mon Sep 17 00:00:00 2001 From: Federico Bernal <64086728+FedericoBernal@users.noreply.github.com> Date: Fri, 12 Mar 2021 14:10:17 -0300 Subject: [PATCH 1/8] Create main structure --- .../com/microsoft/bot/applicationinsights/BotTelemetryClient.java | 0 .../bot/applicationinsights/BotTelemetryClientTests.java | 0 .../microsoft/bot/applicationinsights/MyBotTelemetryClient.java | 0 .../bot/applicationinsights/TelemetryWaterfallTests.java | 0 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClient.java create mode 100644 libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java create mode 100644 libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java create mode 100644 libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClient.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClient.java new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java new file mode 100644 index 000000000..e69de29bb From bf9849a9fa7ab1e90f820bb439c042d79ef3e1a0 Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:31:31 -0300 Subject: [PATCH 2/8] Add pom file --- libraries/bot-applicationinsights/pom.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libraries/bot-applicationinsights/pom.xml b/libraries/bot-applicationinsights/pom.xml index c7bfde216..1dc45c1cf 100644 --- a/libraries/bot-applicationinsights/pom.xml +++ b/libraries/bot-applicationinsights/pom.xml @@ -60,9 +60,22 @@ 2.4.1 + + com.microsoft.bot + bot-dialogs + + + org.mockito + mockito-core + test + + com.microsoft.bot bot-builder + ${project.version} + test-jar + test From d87cac3e34512dd603f95c4a3424bf61c46d86ed Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:33:25 -0300 Subject: [PATCH 3/8] Add main classes --- .../AvailabilityTelemetry.java | 217 +++++++++++++++ .../BotTelemetryClient.java | 0 .../BotTelemetryClientImpl.java | 247 ++++++++++++++++++ 3 files changed, 464 insertions(+) create mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/AvailabilityTelemetry.java delete mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClient.java create mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClientImpl.java diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/AvailabilityTelemetry.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/AvailabilityTelemetry.java new file mode 100644 index 000000000..80911aa58 --- /dev/null +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/AvailabilityTelemetry.java @@ -0,0 +1,217 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.applicationinsights; + +import com.microsoft.applicationinsights.internal.schemav2.AvailabilityData; +import com.microsoft.applicationinsights.internal.util.LocalStringsUtils; +import com.microsoft.applicationinsights.internal.util.Sanitizer; +import com.microsoft.applicationinsights.telemetry.BaseSampleSourceTelemetry; +import com.microsoft.applicationinsights.telemetry.Duration; +import java.util.Date; +import java.util.concurrent.ConcurrentMap; + +/** + * We took this class from https://github.com/microsoft/ApplicationInsights-Java/issues/1099 + * as this is not already migrated in ApplicationInsights-Java library. + */ +public final class AvailabilityTelemetry extends BaseSampleSourceTelemetry { + private Double samplingPercentage; + private final AvailabilityData data; + + public static final String ENVELOPE_NAME = "Availability"; + + public static final String BASE_TYPE = "AvailabilityData"; + + + /** + * Initializes a new instance of the AvailabilityTelemetry class. + */ + public AvailabilityTelemetry() { + this.data = new AvailabilityData(); + initialize(this.data.getProperties()); + setId(LocalStringsUtils.generateRandomIntegerId()); + + // Setting mandatory fields. + setTimestamp(new Date()); + setSuccess(true); + } + + /** + * Initializes a new instance of the AvailabilityTelemetry class with the given name, + * time stamp, duration, HTTP response code and success property values. + * @param name A user-friendly name for the request. + * @param duration The time of the request. + * @param runLocation The duration, in milliseconds, of the request processing. + * @param message The HTTP response code. + * @param success 'true' if the request was a success, 'false' otherwise. + * @param measurements The measurements. + * @param properties The corresponding properties. + */ + public AvailabilityTelemetry(String name, Duration duration, String runLocation, String message, + boolean success, ConcurrentMap measurements, + ConcurrentMap properties) { + + this.data = new AvailabilityData(); + + this.data.setProperties(properties); + this.data.setMeasurements(measurements); + this.data.setMessage(message); + + initialize(this.data.getProperties()); + + setId(LocalStringsUtils.generateRandomIntegerId()); + + setTimestamp(new Date()); + + setName(name); + setRunLocation(runLocation); + setDuration(duration); + setSuccess(success); + } + + + /** + * Gets the ver value from the data object. + * @return The ver value. + */ + @Override + public int getVer() { + return getData().getVer(); + } + + /** + * Gets a map of application-defined request metrics. + * @return The map of metrics + */ + public ConcurrentMap getMetrics() { + return data.getMeasurements(); + } + + /** + * Sets the StartTime. Uses the default behavior and sets the property on the 'data' start time. + * @param timestamp The timestamp as Date. + */ + @Override + public void setTimestamp(Date timestamp) { + if (timestamp == null) { + timestamp = new Date(); + } + + super.setTimestamp(timestamp); + } + + /** + * Gets or human-readable name of the requested page. + * @return A human-readable name. + */ + public String getName() { + return data.getName(); + } + + /** + * Sets or human-readable name of the requested page. + * @param name A human-readable name. + */ + public void setName(String name) { + data.setName(name); + } + + /** + * Gets or human-readable name of the run location. + * @return A human-readable name. + */ + public String getRunLocation() { + return data.getRunLocation(); + } + + /** + * Sets or human-readable name of the run location. + * @param runLocation A human-readable name + */ + public void setRunLocation(String runLocation) { + data.setRunLocation(runLocation); + } + + /** + * Gets the unique identifier of the request. + * @return Unique identifier. + */ + public String getId() { + return data.getId(); + } + + /** + * Sets the unique identifier of the request. + * @param id Unique identifier. + */ + public void setId(String id) { + data.setId(id); + } + + /** + * Gets a value indicating whether application handled the request successfully. + * @return Success indication. + */ + public boolean isSuccess() { + return data.getSuccess(); + } + + /** + * Sets a value indicating whether application handled the request successfully. + * @param success Success indication. + */ + public void setSuccess(boolean success) { + data.setSuccess(success); + } + + /** + * Gets the amount of time it took the application to handle the request. + * @return Amount of time in milliseconds. + */ + public Duration getDuration() { + return data.getDuration(); + } + + /** + * Sets the amount of time it took the application to handle the request. + * @param duration Amount of time in captured in a {@link com.microsoft.applicationinsights.telemetry.Duration}. + */ + public void setDuration(Duration duration) { + data.setDuration(duration); + } + + @Override + public Double getSamplingPercentage() { + return samplingPercentage; + } + + @Override + public void setSamplingPercentage(Double samplingPercentage) { + this.samplingPercentage = samplingPercentage; + } + + @Override + @Deprecated + protected void additionalSanitize() { + data.setName(Sanitizer.sanitizeName(data.getName())); + data.setId(Sanitizer.sanitizeName(data.getId())); + Sanitizer.sanitizeMeasurements(getMetrics()); + } + + @Override + protected AvailabilityData getData() { + return data; + } + + @Override + public String getEnvelopName() { + return ENVELOPE_NAME; + } + + @Override + public String getBaseTypeName() { + return BASE_TYPE; + } +} + diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClient.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClient.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClientImpl.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClientImpl.java new file mode 100644 index 000000000..5a574164b --- /dev/null +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/BotTelemetryClientImpl.java @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.applicationinsights; + +import com.microsoft.applicationinsights.TelemetryClient; +import com.microsoft.applicationinsights.telemetry.EventTelemetry; +import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry; +import com.microsoft.applicationinsights.telemetry.PageViewTelemetry; +import com.microsoft.applicationinsights.telemetry.RemoteDependencyTelemetry; +import com.microsoft.applicationinsights.telemetry.SeverityLevel; +import com.microsoft.applicationinsights.telemetry.TraceTelemetry; +import com.microsoft.bot.builder.BotTelemetryClient; +import com.microsoft.bot.builder.Severity; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * A logging client for bot telemetry. + */ +public class BotTelemetryClientImpl implements BotTelemetryClient { + + private final TelemetryClient telemetryClient; + + /** + * Initializes a new instance of the {@link BotTelemetryClient}. + * + * @param withTelemetryClient The telemetry client to forward bot events to. + */ + public BotTelemetryClientImpl(TelemetryClient withTelemetryClient) { + if (withTelemetryClient == null) { + throw new IllegalArgumentException("withTelemetry should be provided"); + } + this.telemetryClient = withTelemetryClient; + } + + /** + * Send information about availability of an application. + * + * @param name Availability test name. + * @param timeStamp The time when the availability was captured. + * @param duration The time taken for the availability test to run. + * @param runLocation Name of the location the availability test was run from. + * @param success True if the availability test ran successfully. + * @param message Error message on availability test run failure. + * @param properties Named string values you can use to classify and search for this availability telemetry. + * @param metrics Additional values associated with this availability telemetry. + */ + @SuppressWarnings("checkstyle:ParameterNumber") + @Override + public void trackAvailability(String name, + OffsetDateTime timeStamp, + Duration duration, + String runLocation, + boolean success, + String message, + Map properties, + Map metrics) { + com.microsoft.applicationinsights.telemetry.Duration durationTelemetry = + new com.microsoft.applicationinsights.telemetry.Duration(duration.toNanos()); + ConcurrentMap concurrentProperties = new ConcurrentHashMap<>(properties); + ConcurrentMap concurrentMetrics = new ConcurrentHashMap<>(metrics); + AvailabilityTelemetry telemetry = new AvailabilityTelemetry( + name, + durationTelemetry, + runLocation, + message, + success, + concurrentMetrics, + concurrentProperties); + if (properties != null) { + for (Map.Entry pair: properties.entrySet()) { + telemetry.getProperties().put(pair.getKey(), pair.getValue()); + } + } + + if (metrics != null) { + for (Map.Entry pair: metrics.entrySet()) { + telemetry.getMetrics().put(pair.getKey(), pair.getValue()); + } + } + + /** + * This should be telemetryClient.trackAvailability(telemetry). + * However, it is not present in TelemetryClient class + */ + telemetryClient.track(telemetry); + } + + /** + * Send information about an external dependency (outgoing call) in the application. + * + * @param dependencyTypeName Name of the command initiated with this dependency call. Low cardinality value. + * Examples are SQL, Azure table, and HTTP. + * @param target External dependency target. + * @param dependencyName Name of the command initiated with this dependency call. Low cardinality value. + * Examples are stored procedure name and URL path template. + * @param data Command initiated by this dependency call. Examples are SQL statement and HTTP + * URL's with all query parameters. + * @param startTime The time when the dependency was called. + * @param duration The time taken by the external dependency to handle the call. + * @param resultCode Result code of dependency call execution. + * @param success True if the dependency call was handled successfully. + */ + @SuppressWarnings("checkstyle:ParameterNumber") + @Override + public void trackDependency(String dependencyTypeName, + String target, + String dependencyName, + String data, + OffsetDateTime startTime, + Duration duration, + String resultCode, + boolean success) { + com.microsoft.applicationinsights.telemetry.Duration durationTelemetry = + new com.microsoft.applicationinsights.telemetry.Duration(duration.toNanos()); + + RemoteDependencyTelemetry telemetry = + new RemoteDependencyTelemetry(dependencyName, data, durationTelemetry, success); + + telemetry.setType(dependencyTypeName); + telemetry.setTarget(target); + telemetry.setTimestamp(new Date(startTime.toInstant().toEpochMilli())); + telemetry.setResultCode(resultCode); + + telemetryClient.trackDependency(telemetry); + } + + /** + * Logs custom events with extensible named fields. + * + * @param eventName A name for the event. + * @param properties Named string values you can use to search and classify events. + * @param metrics Measurements associated with this event. + */ + @Override + public void trackEvent(String eventName, Map properties, Map metrics) { + EventTelemetry telemetry = new EventTelemetry(eventName); + if (properties != null) { + for (Map.Entry pair: properties.entrySet()) { + telemetry.getProperties().put(pair.getKey(), pair.getValue()); + } + } + + if (metrics != null) { + for (Map.Entry pair: metrics.entrySet()) { + telemetry.getMetrics().put(pair.getKey(), pair.getValue()); + } + } + + telemetryClient.trackEvent(telemetry); + } + + /** + * Logs a system exception. + * + * @param exception The exception to log. + * @param properties Named string values you can use to classify and search for this exception. + * @param metrics Additional values associated with this exception + */ + @Override + public void trackException(Exception exception, Map properties, Map metrics) { + ExceptionTelemetry telemetry = new ExceptionTelemetry(exception); + if (properties != null) { + for (Map.Entry pair: properties.entrySet()) { + telemetry.getProperties().put(pair.getKey(), pair.getValue()); + } + } + + if (metrics != null) { + for (Map.Entry pair: metrics.entrySet()) { + telemetry.getMetrics().put(pair.getKey(), pair.getValue()); + } + } + + telemetryClient.trackException(telemetry); + } + + /** + * Send a trace message. + * + * @param message Message to display. + * @param severityLevel Trace severity level {@link Severity}. + * @param properties Named string values you can use to search and classify events. + */ + @Override + public void trackTrace(String message, Severity severityLevel, Map properties) { + TraceTelemetry telemetry = new TraceTelemetry(message); + telemetry.setSeverityLevel(SeverityLevel.values()[severityLevel.ordinal()]); + + if (properties != null) { + for (Map.Entry pair: properties.entrySet()) { + telemetry.getProperties().put(pair.getKey(), pair.getValue()); + } + } + + telemetryClient.trackTrace(telemetry); + } + + /** + * We implemented this method calling the tracePageView method from {@link BotTelemetryClientImpl} as the + * IBotPageViewTelemetryClient has not been implemented. + * {@inheritDoc} + */ + @Override + public void trackDialogView(String dialogName, Map properties, Map metrics) { + trackPageView(dialogName, properties, metrics); + } + + /** + * Logs a dialog entry / as an Application Insights page view. + * + * @param dialogName The name of the dialog to log the entry / start for. + * @param properties Named string values you can use to search and classify events. + * @param metrics Measurements associated with this event. + */ + public void trackPageView(String dialogName, Map properties, Map metrics) { + PageViewTelemetry telemetry = new PageViewTelemetry(dialogName); + + if (properties != null) { + for (Map.Entry pair: properties.entrySet()) { + telemetry.getProperties().put(pair.getKey(), pair.getValue()); + } + } + + if (metrics != null) { + for (Map.Entry pair: metrics.entrySet()) { + telemetry.getMetrics().put(pair.getKey(), pair.getValue()); + } + } + + telemetryClient.trackPageView(telemetry); + } + + /** + * Flushes the in-memory buffer and any metrics being pre-aggregated. + */ + @Override + public void flush() { + telemetryClient.flush(); + } +} From ff02f3d120e000dec09964023029fe06a058f3e4 Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:33:56 -0300 Subject: [PATCH 4/8] Add TelemetryInitializerMiddleware in core folder --- .../core/TelemetryInitializerMiddleware.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/TelemetryInitializerMiddleware.java diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/TelemetryInitializerMiddleware.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/TelemetryInitializerMiddleware.java new file mode 100644 index 000000000..4126e9369 --- /dev/null +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/TelemetryInitializerMiddleware.java @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for +// license information. + +package com.microsoft.bot.applicationinsights.core; + +import com.microsoft.applicationinsights.core.dependencies.http.client.protocol.HttpClientContext; +import com.microsoft.applicationinsights.core.dependencies.http.protocol.HttpContext; +import com.microsoft.bot.builder.BotAssert; +import com.microsoft.bot.builder.Middleware; +import com.microsoft.bot.builder.NextDelegate; +import com.microsoft.bot.builder.TelemetryLoggerMiddleware; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.schema.Activity; + +import java.util.concurrent.CompletableFuture; + +/** + * Middleware for storing incoming activity on the HttpContext. + */ +public class TelemetryInitializerMiddleware implements Middleware { + + private HttpContext httpContext; + private final String botActivityKey = "BotBuilderActivity"; + private final TelemetryLoggerMiddleware telemetryLoggerMiddleware; + private final Boolean logActivityTelemetry; + + /** + * Initializes a new instance of the {@link TelemetryInitializerMiddleware}. + * @param withTelemetryLoggerMiddleware The TelemetryLoggerMiddleware to use. + * @param withLogActivityTelemetry Boolean determining if you want to log telemetry activity + */ + public TelemetryInitializerMiddleware(TelemetryLoggerMiddleware withTelemetryLoggerMiddleware, + Boolean withLogActivityTelemetry) { + telemetryLoggerMiddleware = withTelemetryLoggerMiddleware; + if (withLogActivityTelemetry == null) { + withLogActivityTelemetry = true; + } + logActivityTelemetry = withLogActivityTelemetry; + } + + /** + * Stores the incoming activity as JSON in the items collection on the HttpContext. + * @param context The incoming TurnContext + * @param next Delegate to run next on + * @return Returns a CompletableFuture with Void value + */ + public CompletableFuture onTurn(TurnContext context, NextDelegate next) { + BotAssert.contextNotNull(context); + + if (context.getActivity() != null) { + Activity activity = context.getActivity(); + + if (this.httpContext == null) { + this.httpContext = HttpClientContext.create(); + } + + Object item = httpContext.getAttribute(botActivityKey); + + if (item != null) { + httpContext.removeAttribute(botActivityKey); + } + + httpContext.setAttribute(botActivityKey, activity); + } + + if (logActivityTelemetry) { + return telemetryLoggerMiddleware.onTurn(context, next); + } else { + return next.next(); + } + } +} + From fb422888a7d3284565d3acf024eb152ed191e773 Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:34:21 -0300 Subject: [PATCH 5/8] Add package-info files --- .../bot/applicationinsights/core/package-info.java | 8 ++++++++ .../microsoft/bot/applicationinsights/package-info.java | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java create mode 100644 libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java new file mode 100644 index 000000000..300ffc6bb --- /dev/null +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/core/package-info.java @@ -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-applicationinsights. + */ +package com.microsoft.bot.applicationinsights.core; diff --git a/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java new file mode 100644 index 000000000..4bf130b60 --- /dev/null +++ b/libraries/bot-applicationinsights/src/main/java/com/microsoft/bot/applicationinsights/package-info.java @@ -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-applicationinsights. + */ +package com.microsoft.bot.applicationinsights; From c9764aafd80c62473b52f8b1f99006ade20629da Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:35:04 -0300 Subject: [PATCH 6/8] Add MyBotTelemetryClient class in test folder --- .../MyBotTelemetryClient.java | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java index e69de29bb..5558ca0d0 100644 --- a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java +++ b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/MyBotTelemetryClient.java @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.applicationinsights; + +import com.microsoft.applicationinsights.TelemetryClient; +import com.microsoft.bot.builder.Severity; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.Map; + +public class MyBotTelemetryClient extends BotTelemetryClientImpl { + public MyBotTelemetryClient(TelemetryClient telemetryClient) { + super(telemetryClient); + } + + @Override + public void trackDependency( + String dependencyTypeName, + String target, + String dependencyName, + String data, + OffsetDateTime startTime, + Duration duration, + String resultCode, + boolean success) + { + super.trackDependency(dependencyName, target, dependencyName, data, startTime, duration, resultCode, success); + } + + @Override + public void trackAvailability( + String name, + OffsetDateTime timeStamp, + Duration duration, + String runLocation, + boolean success, + String message, + Map properties, + Map metrics) + { + super.trackAvailability(name, timeStamp, duration, runLocation, success, message, properties, metrics); + } + + @Override + public void trackEvent( + String eventName, + Map properties, + Map metrics) + { + super.trackEvent(eventName, properties, metrics); + } + + @Override + public void trackException( + Exception exception, + Map properties, + Map metrics) + { + super.trackException(exception, properties, metrics); + } + + @Override + public void trackTrace( + String message, + Severity severityLevel, + Map properties) + { + super.trackTrace(message, severityLevel, properties); + } + + @Override + public void trackPageView( + String name, + Map properties, + Map metrics) + { + super.trackPageView(name, properties, metrics); + } + + @Override + public void flush() + { + super.flush(); + } +} From 86b7aa2b31f3717d2a9ccdd7e427ee6d22d2f7e1 Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:35:45 -0300 Subject: [PATCH 7/8] Add unit tests --- .../BotTelemetryClientTests.java | 183 ++++++++++ .../TelemetryInitializerTests.java | 132 +++++++ .../TelemetryWaterfallTests.java | 328 ++++++++++++++++++ 3 files changed, 643 insertions(+) create mode 100644 libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryInitializerTests.java diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java index e69de29bb..5ae1436a7 100644 --- a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java +++ b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/BotTelemetryClientTests.java @@ -0,0 +1,183 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.applicationinsights; + +import com.microsoft.applicationinsights.TelemetryClient; +import com.microsoft.applicationinsights.TelemetryConfiguration; +import com.microsoft.applicationinsights.channel.TelemetryChannel; +import com.microsoft.applicationinsights.telemetry.EventTelemetry; +import com.microsoft.applicationinsights.telemetry.RemoteDependencyTelemetry; +import com.microsoft.applicationinsights.telemetry.PageViewTelemetry; +import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry; +import com.microsoft.applicationinsights.telemetry.TraceTelemetry; +import com.microsoft.applicationinsights.telemetry.SeverityLevel; +import com.microsoft.bot.builder.BotTelemetryClient; +import com.microsoft.bot.builder.Severity; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.time.Duration; +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Map; + +public class BotTelemetryClientTests { + + private BotTelemetryClient botTelemetryClient; + private TelemetryChannel mockTelemetryChannel; + + @Before + public void initialize() { + mockTelemetryChannel = Mockito.mock(TelemetryChannel.class); + + TelemetryConfiguration telemetryConfiguration = new TelemetryConfiguration(); + telemetryConfiguration.setInstrumentationKey("UNITTEST-INSTRUMENTATION-KEY"); + telemetryConfiguration.setChannel(mockTelemetryChannel); + TelemetryClient telemetryClient = new TelemetryClient(telemetryConfiguration); + + botTelemetryClient = new BotTelemetryClientImpl(telemetryClient); + } + + @Test + public void nullTelemetryClientThrows() { + Assert.assertThrows(IllegalArgumentException.class, () -> { + new BotTelemetryClientImpl(null); + }); + } + + @Test + public void nonNullTelemetryClientSucceeds() { + TelemetryClient telemetryClient = new TelemetryClient(); + + BotTelemetryClient botTelemetryClient = new BotTelemetryClientImpl(telemetryClient); + } + + @Test + public void overrideTest() { + TelemetryClient telemetryClient = new TelemetryClient(); + MyBotTelemetryClient botTelemetryClient = new MyBotTelemetryClient(telemetryClient); + } + + @Test + public void trackAvailabilityTest() { + Map properties = new HashMap<>(); + Map metrics = new HashMap<>(); + properties.put("hello", "value"); + metrics.put("metric", 0.6); + + botTelemetryClient.trackAvailability( + "test", + OffsetDateTime.now(), + Duration.ofNanos(1000), + "run location", + true, + "message", + properties, + metrics); + + Mockito.verify(mockTelemetryChannel, invocations -> { + AvailabilityTelemetry availabilityTelemetry = invocations.getAllInvocations().get(0).getArgument(0); + Assert.assertEquals("test", availabilityTelemetry.getName()); + Assert.assertEquals("message", availabilityTelemetry.getData().getMessage()); + Assert.assertEquals("value", availabilityTelemetry.getProperties().get("hello")); + Assert.assertEquals(0, Double.compare(0.6, availabilityTelemetry.getMetrics().get("metric"))); + }).send(Mockito.any(AvailabilityTelemetry.class)); + + } + + @Test + public void trackEventTest() { + Map properties = new HashMap<>(); + properties.put("hello", "value"); + Map metrics = new HashMap<>(); + metrics.put("metric", 0.6); + + botTelemetryClient.trackEvent("test", properties, metrics); + + Mockito.verify(mockTelemetryChannel, invocations -> { + EventTelemetry eventTelemetry = invocations.getAllInvocations().get(0).getArgument(0); + + Assert.assertEquals("test", eventTelemetry.getName()); + Assert.assertEquals("value", eventTelemetry.getProperties().get("hello")); + Assert.assertEquals(0, Double.compare(0.6, eventTelemetry.getMetrics().get("metric"))); + }).send(Mockito.any(AvailabilityTelemetry.class)); + } + + @Test + public void trackDependencyTest() { + botTelemetryClient.trackDependency( + "test", + "target", + "dependencyname", + "data", + OffsetDateTime.now(), + Duration.ofNanos(1000), + "result", false); + + Mockito.verify(mockTelemetryChannel, invocations -> { + RemoteDependencyTelemetry remoteDependencyTelemetry = invocations.getAllInvocations().get(0).getArgument(0); + + Assert.assertEquals("test", remoteDependencyTelemetry.getType()); + Assert.assertEquals("target", remoteDependencyTelemetry.getTarget()); + Assert.assertEquals("dependencyname", remoteDependencyTelemetry.getName()); + Assert.assertEquals("result", remoteDependencyTelemetry.getResultCode()); + Assert.assertFalse(remoteDependencyTelemetry.getSuccess()); + }).send(Mockito.any(AvailabilityTelemetry.class)); + } + + @Test + public void trackExceptionTest() { + Exception expectedException = new Exception("test-exception"); + Map properties = new HashMap<>(); + properties.put("foo", "bar"); + Map metrics = new HashMap<>(); + metrics.put("metric", 0.6); + + botTelemetryClient.trackException(expectedException, properties, metrics); + + Mockito.verify(mockTelemetryChannel, invocations -> { + ExceptionTelemetry exceptionTelemetry = invocations.getAllInvocations().get(0).getArgument(0); + + Assert.assertEquals(expectedException, exceptionTelemetry.getException()); + Assert.assertEquals("bar", exceptionTelemetry.getProperties().get("foo")); + Assert.assertEquals(0, Double.compare(0.6, exceptionTelemetry.getMetrics().get("metric"))); + }).send(Mockito.any(ExceptionTelemetry.class)); + } + + @Test + public void trackTraceTest() { + Map properties = new HashMap<>(); + properties.put("foo", "bar"); + + botTelemetryClient.trackTrace("hello", Severity.CRITICAL, properties); + + Mockito.verify(mockTelemetryChannel, invocations -> { + TraceTelemetry traceTelemetry = invocations.getAllInvocations().get(0).getArgument(0); + + Assert.assertEquals("hello", traceTelemetry.getMessage()); + Assert.assertEquals(SeverityLevel.Critical, traceTelemetry.getSeverityLevel()); + Assert.assertEquals("bar", traceTelemetry.getProperties().get("foo")); + }).send(Mockito.any(TraceTelemetry.class)); + } + + @Test + public void trackPageViewTest() { + Map properties = new HashMap<>(); + properties.put("hello", "value"); + Map metrics = new HashMap<>(); + metrics.put("metric", 0.6); + + botTelemetryClient.trackDialogView("test", properties, metrics); + + Mockito.verify(mockTelemetryChannel, invocations -> { + PageViewTelemetry pageViewTelemetry = invocations.getAllInvocations().get(0).getArgument(0); + + Assert.assertEquals("test", pageViewTelemetry.getName()); + Assert.assertEquals("value", pageViewTelemetry.getProperties().get("hello")); + Assert.assertEquals(0, Double.compare(0.6, pageViewTelemetry.getMetrics().get("metric"))); + }).send(Mockito.any(PageViewTelemetry.class)); + } +} diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryInitializerTests.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryInitializerTests.java new file mode 100644 index 000000000..06592eef9 --- /dev/null +++ b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryInitializerTests.java @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.applicationinsights; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mockito; + +import com.microsoft.bot.applicationinsights.core.TelemetryInitializerMiddleware; +import com.microsoft.bot.builder.BotTelemetryClient; +import com.microsoft.bot.builder.TelemetryLoggerMiddleware; +import com.microsoft.bot.builder.adapters.TestAdapter; +import com.microsoft.bot.builder.adapters.TestFlow; +import com.microsoft.bot.schema.Activity; +import com.microsoft.bot.schema.ActivityTypes; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class TelemetryInitializerTests { + + @Captor + ArgumentCaptor eventNameCaptor; + + @Captor + ArgumentCaptor> propertiesCaptor; + + @Test + public void telemetryInitializerMiddlewareLogActivitiesEnabled() { + + // Arrange + BotTelemetryClient mockTelemetryClient = Mockito.mock(BotTelemetryClient.class); + TelemetryLoggerMiddleware telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(mockTelemetryClient, false); + + TestAdapter testAdapter = new TestAdapter() + .use(new TelemetryInitializerMiddleware(telemetryLoggerMiddleware, true)); + + // Act + // Default case logging Send/Receive Activities + new TestFlow(testAdapter, turnContext -> { + Activity typingActivity = new Activity(ActivityTypes.TYPING); + typingActivity.setRelatesTo(turnContext.getActivity().getRelatesTo()); + + turnContext.sendActivity(typingActivity).join(); + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException e) { + // Empty error + } + turnContext.sendActivity(String.format("echo:%s", turnContext.getActivity().getText())).join(); + return CompletableFuture.completedFuture(null); + }) + .send("foo") + .assertReply(activity -> { + Assert.assertTrue(activity.isType(ActivityTypes.TYPING)); + }) + .assertReply("echo:foo") + .send("bar") + .assertReply(activity -> { + Assert.assertTrue(activity.isType(ActivityTypes.TYPING)); + }) + .assertReply("echo:bar") + .startTest().join(); + + // Verify + verify(mockTelemetryClient, times(6)).trackEvent( + eventNameCaptor.capture(), + propertiesCaptor.capture() + ); + + List eventNames = eventNameCaptor.getAllValues(); + Assert.assertEquals(6, eventNames.size()); + + } + + @Test + public void telemetryInitializerMiddlewareNotLogActivitiesDisabled() { + + // Arrange + BotTelemetryClient mockTelemetryClient = Mockito.mock(BotTelemetryClient.class); + TelemetryLoggerMiddleware telemetryLoggerMiddleware = new TelemetryLoggerMiddleware(mockTelemetryClient, false); + + TestAdapter testAdapter = new TestAdapter() + .use(new TelemetryInitializerMiddleware(telemetryLoggerMiddleware, false)); + + // Act + // Default case logging Send/Receive Activities + new TestFlow(testAdapter, (turnContext) -> { + Activity typingActivity = new Activity(ActivityTypes.TYPING); + typingActivity.setRelatesTo(turnContext.getActivity().getRelatesTo()); + + turnContext.sendActivity(typingActivity).join(); + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException e) { + // Empty error + } + turnContext.sendActivity(String.format("echo:%s", turnContext.getActivity().getText())).join(); + return CompletableFuture.completedFuture(null); + }) + .send("foo") + .assertReply(activity -> { + Assert.assertTrue(activity.isType(ActivityTypes.TYPING)); + }) + .assertReply("echo:foo") + .send("bar") + .assertReply(activity -> { + Assert.assertTrue(activity.isType(ActivityTypes.TYPING)); + }) + .assertReply("echo:bar") + .startTest().join(); + + // Verify + verify(mockTelemetryClient, times(0)).trackEvent( + eventNameCaptor.capture(), + propertiesCaptor.capture() + ); + List eventNames = eventNameCaptor.getAllValues(); + Assert.assertEquals(0, eventNames.size()); + } +} diff --git a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java index e69de29bb..70825410b 100644 --- a/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java +++ b/libraries/bot-applicationinsights/src/test/java/com/microsoft/bot/applicationinsights/TelemetryWaterfallTests.java @@ -0,0 +1,328 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.bot.applicationinsights; + +import com.microsoft.bot.builder.AutoSaveStateMiddleware; +import com.microsoft.bot.builder.BotTelemetryClient; +import com.microsoft.bot.builder.StatePropertyAccessor; +import com.microsoft.bot.builder.TurnContext; +import com.microsoft.bot.builder.adapters.TestAdapter; +import com.microsoft.bot.builder.ConversationState; +import com.microsoft.bot.builder.MemoryStorage; +import com.microsoft.bot.builder.adapters.TestFlow; +import com.microsoft.bot.dialogs.Dialog; +import com.microsoft.bot.dialogs.DialogContext; +import com.microsoft.bot.dialogs.DialogInstance; +import com.microsoft.bot.dialogs.DialogReason; +import com.microsoft.bot.dialogs.DialogSet; +import com.microsoft.bot.dialogs.DialogState; +import com.microsoft.bot.dialogs.WaterfallDialog; +import com.microsoft.bot.dialogs.WaterfallStep; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +public class TelemetryWaterfallTests { + + @Test + public void waterfall() { + ConversationState convoState = new ConversationState(new MemoryStorage()); + TestAdapter adapter = new TestAdapter(TestAdapter.createConversationReference("Waterfall", "User1", "Bot")) + .use(new AutoSaveStateMiddleware(convoState)); + + BotTelemetryClient telemetryClient = Mockito.mock(BotTelemetryClient.class); + StatePropertyAccessor dialogState = convoState.createProperty("dialogState"); + DialogSet dialogs = new DialogSet(dialogState); + + dialogs.add(new WaterfallDialog("test", newWaterfall())); + dialogs.setTelemetryClient(telemetryClient); + + new TestFlow(adapter, turnContext -> { + DialogContext dc = dialogs.createContext(turnContext).join(); + dc.continueDialog().join(); + if (!turnContext.getResponded()) { + dc.beginDialog("test", null).join(); + } + return CompletableFuture.completedFuture(null); + }) + .send("hello") + .assertReply("step1") + .send("hello") + .assertReply("step2") + .send("hello") + .assertReply("step3") + .startTest() + .join(); + + // C#'s trackEvent method of BotTelemetryClient has nullable parameters, + // therefore it always calls the same method. + // On the other hand, Java's BotTelemetryClient overloads the trackEvent method, + // so instead of calling the same method, it calls a method with less parameters. + // In this particular test, WaterfallDialog's beginDialog calls the method with only two parameters + Mockito.verify(telemetryClient, Mockito.times(4)).trackEvent( + Mockito.anyString(), + Mockito.anyMap() + ); + System.out.printf("Complete"); + } + + @Test + public void waterfallWithCallback() { + ConversationState convoState = new ConversationState(new MemoryStorage()); + TestAdapter adapter = new TestAdapter(TestAdapter.createConversationReference("WaterfallWithCallback", "User1", "Bot")) + .use(new AutoSaveStateMiddleware(convoState)); + + BotTelemetryClient telemetryClient = Mockito.mock(BotTelemetryClient.class); + StatePropertyAccessor dialogState = convoState.createProperty("dialogState"); + DialogSet dialogs = new DialogSet(dialogState); + WaterfallDialog waterfallDialog = new WaterfallDialog("test", newWaterfall()); + + dialogs.add(waterfallDialog); + dialogs.setTelemetryClient(telemetryClient); + + new TestFlow(adapter, turnContext -> { + DialogContext dc = dialogs.createContext(turnContext).join(); + dc.continueDialog().join(); + if (!turnContext.getResponded()) { + dc.beginDialog("test", null).join(); + } + return CompletableFuture.completedFuture(null); + }) + .send("hello") + .assertReply("step1") + .send("hello") + .assertReply("step2") + .send("hello") + .assertReply("step3") + .startTest() + .join(); + + // C#'s trackEvent method of BotTelemetryClient has nullable parameters, + // therefore it always calls the same method. + // On the other hand, Java's BotTelemetryClient overloads the trackEvent method, + // so instead of calling the same method, it calls a method with less parameters. + // In this particular test, WaterfallDialog's beginDialog calls the method with only two parameters + Mockito.verify(telemetryClient, Mockito.times(4)).trackEvent( + Mockito.anyString(), + Mockito.anyMap() + ); + } + + @Test + public void waterfallWithActionsNull() { + Assert.assertThrows(IllegalArgumentException.class, () -> { + BotTelemetryClient telemetryClient = Mockito.mock(BotTelemetryClient.class); + WaterfallDialog waterfall = new WaterfallDialog("test", null); + waterfall.setTelemetryClient(telemetryClient); + waterfall.addStep(null); + }); + } + + @Test + public void ensureEndDialogCalled() { + ConversationState convoState = new ConversationState(new MemoryStorage()); + TestAdapter adapter = new TestAdapter(TestAdapter.createConversationReference("EnsureEndDialogCalled", "User1", "Bot")) + .use(new AutoSaveStateMiddleware(convoState)); + + StatePropertyAccessor dialogState = convoState.createProperty("dialogState"); + DialogSet dialogs = new DialogSet(dialogState); + HashMap> saved_properties = new HashMap<>(); + final int[] counter = {0}; + + // Set up the client to save all logged property names and associated properties (in "saved_properties"). + BotTelemetryClient telemetryClient = Mockito.mock(BotTelemetryClient.class); + Mockito.doAnswer(invocation -> { + String eventName = invocation.getArgument(0); + Map properties = invocation.getArgument(1); + + StringBuilder sb = new StringBuilder(eventName).append("_").append(counter[0]++); + saved_properties.put(sb.toString(), properties); + + return null; + }).when(telemetryClient).trackEvent(Mockito.anyString(), Mockito.anyMap()); + + MyWaterfallDialog waterfallDialog = new MyWaterfallDialog("test", newWaterfall()); + + dialogs.add(waterfallDialog); + dialogs.setTelemetryClient(telemetryClient); + + new TestFlow(adapter, turnContext -> { + DialogContext dc = dialogs.createContext(turnContext).join(); + dc.continueDialog().join(); + if (!turnContext.getResponded()) { + dc.beginDialog("test", null).join(); + } + return CompletableFuture.completedFuture(null); + }) + .send("hello") + .assertReply("step1") + .send("hello") + .assertReply("step2") + .send("hello") + .assertReply("step3") + .send("hello") + .assertReply("step1") + .startTest() + .join(); + + Mockito.verify(telemetryClient, Mockito.times(7)).trackEvent( + Mockito.anyString(), + Mockito.anyMap() + ); + + // Verify: + // Event name is "WaterfallComplete" + // Event occurs on the 4th event logged + // Event contains DialogId + // Event DialogId is set correctly. + Assert.assertTrue(saved_properties.get("WaterfallComplete_4").containsKey("DialogId")); + Assert.assertEquals("test", saved_properties.get("WaterfallComplete_4").get("DialogId")); + Assert.assertTrue(saved_properties.get("WaterfallComplete_4").containsKey("InstanceId")); + Assert.assertTrue(saved_properties.get("WaterfallStep_1").containsKey("InstanceId")); + + // Verify naming on lambda's is "StepXofY" + Assert.assertTrue(saved_properties.get("WaterfallStep_1").containsKey("StepName")); + Assert.assertEquals("Step1of3", saved_properties.get("WaterfallStep_1").get("StepName")); + Assert.assertTrue(saved_properties.get("WaterfallStep_1").containsKey("InstanceId")); + Assert.assertTrue(waterfallDialog.getEndDialogCalled()); + } + + @Test + public void ensureCancelDialogCalled() { + ConversationState convoState = new ConversationState(new MemoryStorage()); + TestAdapter adapter = new TestAdapter(TestAdapter.createConversationReference("EnsureCancelDialogCalled", "User1", "Bot")) + .use(new AutoSaveStateMiddleware(convoState)); + + StatePropertyAccessor dialogState = convoState.createProperty("dialogState"); + DialogSet dialogs = new DialogSet(dialogState); + HashMap> saved_properties = new HashMap<>(); + final int[] counter = {0}; + + // Set up the client to save all logged property names and associated properties (in "saved_properties"). + BotTelemetryClient telemetryClient = Mockito.mock(BotTelemetryClient.class); + Mockito.doAnswer(invocation -> { + String eventName = invocation.getArgument(0); + Map properties = invocation.getArgument(1); + + StringBuilder sb = new StringBuilder(eventName).append("_").append(counter[0]++); + saved_properties.put(sb.toString(), properties); + + return null; + }).when(telemetryClient).trackEvent(Mockito.anyString(), Mockito.anyMap()); + + List steps = new ArrayList<>(); + steps.add(step -> { + step.getContext().sendActivity("step1").join(); + return CompletableFuture.completedFuture(Dialog.END_OF_TURN); + }); + steps.add(step -> { + step.getContext().sendActivity("step2").join(); + return CompletableFuture.completedFuture(Dialog.END_OF_TURN); + }); + steps.add(step -> { + step.cancelAllDialogs().join(); + return CompletableFuture.completedFuture(Dialog.END_OF_TURN); + }); + + MyWaterfallDialog waterfallDialog = new MyWaterfallDialog("test", steps); + + dialogs.add(waterfallDialog); + dialogs.setTelemetryClient(telemetryClient); + + new TestFlow(adapter, turnContext -> { + DialogContext dc = dialogs.createContext(turnContext).join(); + dc.continueDialog().join(); + if (!turnContext.getResponded()) { + dc.beginDialog("test", null).join(); + } + return CompletableFuture.completedFuture(null); + }) + .send("hello") + .assertReply("step1") + .send("hello") + .assertReply("step2") + .send("hello") + .assertReply("step1") + .startTest() + .join(); + + Mockito.verify(telemetryClient, Mockito.times(7)).trackEvent( + Mockito.anyString(), + Mockito.anyMap() + ); + + // Verify: + // Event name is "WaterfallCancel" + // Event occurs on the 4th event logged + // Event contains DialogId + // Event DialogId is set correctly. + Assert.assertTrue(saved_properties.get("WaterfallStart_0").containsKey("DialogId")); + Assert.assertTrue(saved_properties.get("WaterfallStart_0").containsKey("InstanceId")); + Assert.assertTrue(saved_properties.get("WaterfallCancel_4").containsKey("DialogId")); + Assert.assertEquals("test", saved_properties.get("WaterfallCancel_4").get("DialogId")); + Assert.assertTrue(saved_properties.get("WaterfallCancel_4").containsKey("StepName")); + Assert.assertTrue(saved_properties.get("WaterfallCancel_4").containsKey("InstanceId")); + + // Event contains "StepName" + // Event naming on lambda's is "StepXofY" + Assert.assertEquals("Step3of3", saved_properties.get("WaterfallCancel_4").get("StepName")); + Assert.assertTrue(waterfallDialog.getCancelDialogCalled()); + Assert.assertFalse(waterfallDialog.getEndDialogCalled()); + } + + private static List newWaterfall() { + List waterfall = new ArrayList<>(); + + waterfall.add(step -> { + step.getContext().sendActivity("step1").join(); + return CompletableFuture.completedFuture(Dialog.END_OF_TURN); + }); + + waterfall.add(step -> { + step.getContext().sendActivity("step2").join(); + return CompletableFuture.completedFuture(Dialog.END_OF_TURN); + }); + + waterfall.add(step -> { + step.getContext().sendActivity("step3").join(); + return CompletableFuture.completedFuture(Dialog.END_OF_TURN); + }); + + return waterfall; + } + + private class MyWaterfallDialog extends WaterfallDialog { + private Boolean endDialogCalled = false; + private Boolean cancelDialogCalled = false; + + public MyWaterfallDialog(String id, List actions) { + super(id, actions); + } + + @Override + public CompletableFuture endDialog(TurnContext turnContext, DialogInstance instance, DialogReason reason) { + if (reason == DialogReason.END_CALLED) { + endDialogCalled = true; + } else if (reason == DialogReason.CANCEL_CALLED) { + cancelDialogCalled = true; + } + + return super.endDialog(turnContext, instance, reason); + } + + public Boolean getEndDialogCalled() { + return endDialogCalled; + } + + public Boolean getCancelDialogCalled() { + return cancelDialogCalled; + } + } +} From 356d79b1d035a026ce125cf1f18a2e707df4e582 Mon Sep 17 00:00:00 2001 From: Franco Alvarez Date: Tue, 23 Mar 2021 14:40:31 -0300 Subject: [PATCH 8/8] Fix issue in WaterfallDialog class --- .../main/java/com/microsoft/bot/dialogs/WaterfallDialog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java index 597f71ad9..28e0b8bd9 100644 --- a/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java +++ b/libraries/bot-dialogs/src/main/java/com/microsoft/bot/dialogs/WaterfallDialog.java @@ -267,8 +267,8 @@ private String waterfallStepName(int index) { String stepName = steps.get(index).getClass().getSimpleName(); // Default stepname for lambdas - if (StringUtils.isAllBlank(stepName) || stepName.contains("<")) { - stepName = String.format("Step%0of%1", index + 1, steps.size()); + if (StringUtils.isAllBlank(stepName) || stepName.contains("$Lambda$")) { + stepName = String.format("Step%dof%d", index + 1, steps.size()); } return stepName;