Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@

import org.apache.commons.lang3.StringUtils;

import com.microsoft.bot.connector.ConnectorClient;
import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.ChannelAccount;
import com.microsoft.bot.schema.HealthCheckResponse;
import com.microsoft.bot.schema.MessageReaction;
import com.microsoft.bot.schema.ResourceResponse;
import com.microsoft.bot.schema.SignInConstants;
Expand Down Expand Up @@ -405,6 +407,10 @@ protected CompletableFuture<InvokeResponse> onInvokeActivity(TurnContext turnCon
}
return new InvokeResponse(HttpURLConnection.HTTP_INTERNAL_ERROR, null);
});
} else if (StringUtils.equals(turnContext.getActivity().getName(), "healthCheck")) {
CompletableFuture<InvokeResponse> result = new CompletableFuture<>();
result.complete(new InvokeResponse(HttpURLConnection.HTTP_OK, onHealthCheck(turnContext)));
return result;
}

CompletableFuture<InvokeResponse> result = new CompletableFuture<>();
Expand Down Expand Up @@ -436,6 +442,18 @@ protected CompletableFuture<Void> onSignInInvoke(TurnContext turnContext) {
return result;
}

/**
* Invoked when a 'healthCheck' event is
* received when the base behavior of onInvokeActivity is used.
*
* @param turnContext The current TurnContext.
* @return A task that represents a HealthCheckResponse.
*/
protected CompletableFuture<HealthCheckResponse> onHealthCheck(TurnContext turnContext) {
ConnectorClient client = turnContext.getTurnState().get(BotFrameworkAdapter.CONNECTOR_CLIENT_KEY);
return CompletableFuture.completedFuture(HealthCheck.createHealthCheckResponse(client));
}

/**
* Creates a success InvokeResponse with the specified body.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.bot.builder;

import java.util.concurrent.ExecutionException;

import com.microsoft.bot.connector.ConnectorClient;
import com.microsoft.bot.connector.authentication.AppCredentials;
import com.microsoft.bot.schema.HealthCheckResponse;
import com.microsoft.bot.schema.HealthResults;

/**
* A class to process a HealthCheck request.
*/
public final class HealthCheck {

private HealthCheck() {
// not called
}

/**
* @param connector the ConnectorClient instance for this request
* @return HealthCheckResponse
*/
public static HealthCheckResponse createHealthCheckResponse(ConnectorClient connector) {
HealthResults healthResults = new HealthResults();
healthResults.setSuccess(true);

if (connector != null) {
healthResults.setUserAgent(connector.getUserAgent());
AppCredentials credentials = (AppCredentials) connector.credentials();
try {
healthResults.setAuthorization(credentials.getToken().get());
} catch (InterruptedException | ExecutionException ignored) {
// An exception here may happen when you have a valid appId but invalid or blank secret.
// No callbacks will be possible, although the bot maybe healthy in other respects.
}
}

if (healthResults.getAuthorization() != null) {
healthResults.setMessages(new String[]{"Health check succeeded."});
} else {
healthResults.setMessages(new String[]{"Health check succeeded.", "Callbacks are not authorized."});
}

HealthCheckResponse response = new HealthCheckResponse();
response.setHealthResults(healthResults);
return response;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicReference;

public class ActivityHandlerTests {
@Test
Expand Down Expand Up @@ -346,6 +348,98 @@ public void TestUnrecognizedActivityType() {
Assert.assertEquals("onUnrecognizedActivityType", bot.getRecord().get(0));
}

@Test
public void TestHealthCheckAsyncOverride() {
Activity activity = new Activity() {
{
setType(ActivityTypes.INVOKE);
setName("healthCheck");
}
};

TurnContext turnContext = new TurnContextImpl(new TestInvokeAdapter(), activity);

TestActivityHandler bot = new TestActivityHandler();
bot.onTurn(turnContext).join();

Assert.assertEquals(2, bot.getRecord().size());
Assert.assertEquals("onInvokeActivity", bot.getRecord().get(0));
Assert.assertEquals("onHealthCheck", bot.getRecord().get(1));
}

@Test
public void TestHealthCheckAsync() {
Activity activity = new Activity() {
{
setType(ActivityTypes.INVOKE);
setName("healthCheck");
}
};

AtomicReference<List<Activity>> activitiesToSend = new AtomicReference<>();
TurnContext turnContext = new TurnContextImpl(new SimpleAdapter(activitiesToSend::set), activity);

ActivityHandler bot = new ActivityHandler();
bot.onTurn(turnContext).join();

Assert.assertNotNull(activitiesToSend.get());
Assert.assertEquals(1, activitiesToSend.get().size());
Assert.assertTrue(activitiesToSend.get().get(0).getValue() instanceof InvokeResponse);
Assert.assertEquals(200, ((InvokeResponse) activitiesToSend.get().get(0).getValue()).getStatus());
CompletableFuture future = ((CompletableFuture) ((InvokeResponse) activitiesToSend.get().get(0).getValue())
.getBody());
HealthCheckResponse result = new HealthCheckResponse();
result = (HealthCheckResponse) future.join();
Assert.assertTrue(result.getHealthResults().getSuccess());
String[] messages = result.getHealthResults().getMessages();
Assert.assertEquals(messages[0], "Health check succeeded.");
}

@Test
public void TestHealthCheckWithConnectorAsync() {
Activity activity = new Activity() {
{
setType(ActivityTypes.INVOKE);
setName("healthCheck");
}
};

AtomicReference<List<Activity>> activitiesToSend = new AtomicReference<>();
TurnContext turnContext = new TurnContextImpl(new SimpleAdapter(activitiesToSend::set), activity);
MockConnectorClient mockConnector = new MockConnectorClient("Windows/3.1", new MockAppCredentials("awesome"));
turnContext.getTurnState().add(BotFrameworkAdapter.CONNECTOR_CLIENT_KEY, mockConnector);
ActivityHandler bot = new ActivityHandler();
bot.onTurn(turnContext).join();

Assert.assertNotNull(activitiesToSend.get());
Assert.assertEquals(1, activitiesToSend.get().size());
Assert.assertTrue(activitiesToSend.get().get(0).getValue() instanceof InvokeResponse);
Assert.assertEquals(
200,
((InvokeResponse) activitiesToSend.get().get(0).getValue()).getStatus()
);
CompletableFuture<HealthCheckResponse> future =
((CompletableFuture<HealthCheckResponse>)
((InvokeResponse) activitiesToSend.get().get(0).getValue()).getBody());
HealthCheckResponse result = new HealthCheckResponse();
result = (HealthCheckResponse) future.join();
Assert.assertTrue(result.getHealthResults().getSuccess());
Assert.assertEquals(result.getHealthResults().getAuthorization(), "awesome");
Assert.assertEquals(result.getHealthResults().getUserAgent(), "Windows/3.1");
String[] messages = result.getHealthResults().getMessages();
Assert.assertEquals(messages[0], "Health check succeeded.");
}

private static class TestInvokeAdapter extends NotImplementedAdapter {
@Override
public CompletableFuture<ResourceResponse[]> sendActivities(
TurnContext context,
List<Activity> activities
) {
return CompletableFuture.completedFuture(new ResourceResponse[0]);
}
}

private static class NotImplementedAdapter extends BotAdapter {
@Override
public CompletableFuture<ResourceResponse[]> sendActivities(
Expand Down Expand Up @@ -455,6 +549,18 @@ protected CompletableFuture onEvent(TurnContext turnContext) {
return super.onEvent(turnContext);
}

@Override
protected CompletableFuture<InvokeResponse> onInvokeActivity(TurnContext turnContext) {
record.add("onInvokeActivity");
return super.onInvokeActivity(turnContext);
}

@Override
protected CompletableFuture<HealthCheckResponse> onHealthCheck(TurnContext turnContext) {
record.add("onHealthCheck");
return super.onHealthCheck(turnContext);
}

@Override
protected CompletableFuture onInstallationUpdate(TurnContext turnContext) {
record.add("onInstallationUpdate");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.microsoft.bot.builder;

import java.util.concurrent.CompletableFuture;

import com.microsoft.bot.connector.authentication.AppCredentials;
import com.microsoft.bot.connector.authentication.Authenticator;

public class MockAppCredentials extends AppCredentials {

private String token;

public MockAppCredentials(String token) {
super(null);
this.token = token;
}

@Override
public CompletableFuture<String> getToken() {
CompletableFuture<String> result;

result = new CompletableFuture<String>();
result.complete(this.token);
return result;
}

/**
* Returns an appropriate Authenticator that is provided by a subclass.
*
* @return An Authenticator object.
* @throws MalformedURLException If the endpoint isn't valid.
*/
protected Authenticator buildAuthenticator(){
return null;
};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.microsoft.bot.builder;

import com.microsoft.bot.connector.Attachments;
import com.microsoft.bot.connector.ConnectorClient;
import com.microsoft.bot.connector.Conversations;
import com.microsoft.bot.connector.authentication.AppCredentials;
import com.microsoft.bot.rest.RestClient;
import com.microsoft.bot.rest.credentials.ServiceClientCredentials;

public class MockConnectorClient implements ConnectorClient {

private AppCredentials credentials;
private String userAgent;

public MockConnectorClient(String userAgent, AppCredentials credentials) {
this.userAgent = userAgent;
this.credentials = credentials;
}

private MemoryConversations conversations = new MemoryConversations();

@Override
public RestClient getRestClient() {
return null;
}

@Override
public String baseUrl() {
return null;
}

@Override
public ServiceClientCredentials credentials() {
return credentials;
}

@Override
public String getUserAgent() {
return userAgent;
}

@Override
public String getAcceptLanguage() {
return null;
}

@Override
public void setAcceptLanguage(String acceptLanguage) {

}

@Override
public int getLongRunningOperationRetryTimeout() {
return 0;
}

@Override
public void setLongRunningOperationRetryTimeout(int timeout) {

}

@Override
public boolean getGenerateClientRequestId() {
return false;
}

@Override
public void setGenerateClientRequestId(boolean generateClientRequestId) {

}

@Override
public Attachments getAttachments() {
return null;
}

@Override
public Conversations getConversations() {
return conversations;
}

@Override
public void close() throws Exception {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.microsoft.bot.schema;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
* Defines the structure that is returned as the result of a health check on the bot.
* The health check is sent to the bot as an {@link Activity} of type "invoke" and this class along
* with {@link HealthResults} defines the structure of the body of the response.
* The name of the invoke Activity is "healthCheck".
*/
public class HealthCheckResponse {
/**
* The health check results.
*/
@JsonProperty(value = "healthResults")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private HealthResults healthResults;

/**
* Gets the healthResults value.
*
* @return The healthResults value.
*/
public HealthResults getHealthResults() {
return this.healthResults;
}

/**
* Sets the healthResults value.
*
* @param withHealthResults The healthResults value to set.
*/
public void setHealthResults(HealthResults withHealthResults) {
this.healthResults = withHealthResults;
}
}
Loading