Skip to content
This repository has been archived by the owner on Dec 4, 2023. It is now read-only.

Commit

Permalink
Added Action.Execute support (#1104)
Browse files Browse the repository at this point in the history
Co-authored-by: tracyboehrer <[email protected]>
  • Loading branch information
LeeParrishMSFT and tracyboehrer authored Mar 29, 2021
1 parent e01aef1 commit d4c8cb7
Show file tree
Hide file tree
Showing 6 changed files with 467 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.microsoft.bot.builder;

import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.bot.connector.Async;
import java.net.HttpURLConnection;
import java.util.List;
Expand All @@ -13,9 +14,12 @@

import com.microsoft.bot.schema.Activity;
import com.microsoft.bot.schema.ActivityTypes;
import com.microsoft.bot.schema.AdaptiveCardInvokeResponse;
import com.microsoft.bot.schema.AdaptiveCardInvokeValue;
import com.microsoft.bot.schema.ChannelAccount;
import com.microsoft.bot.schema.MessageReaction;
import com.microsoft.bot.schema.ResourceResponse;
import com.microsoft.bot.schema.Serialization;
import com.microsoft.bot.schema.SignInConstants;

/**
Expand Down Expand Up @@ -398,6 +402,16 @@ protected CompletableFuture<Void> onEventActivity(TurnContext turnContext) {
* @return A task that represents the work queued to execute.
*/
protected CompletableFuture<InvokeResponse> onInvokeActivity(TurnContext turnContext) {
if (StringUtils.equals(turnContext.getActivity().getName(), "adaptiveCard/action")) {
AdaptiveCardInvokeValue invokeValue = null;
try {
invokeValue = getAdaptiveCardInvokeValue(turnContext.getActivity());
} catch (InvokeResponseException e) {
return Async.completeExceptionally(e);
}
return onAdaptiveCardInvoke(turnContext, invokeValue).thenApply(result -> createInvokeResponse(result));
}

if (
StringUtils.equals(
turnContext.getActivity().getName(), SignInConstants.VERIFY_STATE_OPERATION_NAME
Expand Down Expand Up @@ -612,6 +626,25 @@ protected CompletableFuture<Void> onInstallationUpdateRemove(TurnContext turnCon
return CompletableFuture.completedFuture(null);
}

/**
* Invoked when the bot is sent an Adaptive Card Action Execute.
*
* @param turnContext A strongly-typed context Object for this
* turn.
* @param invokeValue A stringly-typed Object from the incoming
* activity's Value.
*
* @return A task that represents the work queued to execute.
*
* When the {@link OnInvokeActivity(TurnContext{InvokeActivity})} method
* receives an Invoke with a {@link InvokeActivity#name} of
* `adaptiveCard/action`, it calls this method.
*/
protected CompletableFuture<AdaptiveCardInvokeResponse> onAdaptiveCardInvoke(
TurnContext turnContext, AdaptiveCardInvokeValue invokeValue) {
return Async.completeExceptionally(new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED));
}

/**
* Override this in a derived class to provide logic specific to
* ActivityTypes.END_OF_CONVERSATION activities.
Expand Down Expand Up @@ -662,6 +695,62 @@ protected CompletableFuture<Void> onUnrecognizedActivityType(TurnContext turnCon
return CompletableFuture.completedFuture(null);
}

private AdaptiveCardInvokeValue getAdaptiveCardInvokeValue(Activity activity) throws InvokeResponseException {
if (activity.getValue() == null) {
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Missing value property");
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
}

Object obj = activity.getValue();
JsonNode node = null;
if (obj instanceof JsonNode) {
node = (JsonNode) obj;
} else {
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Value property instanceof not properly formed");
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
}

AdaptiveCardInvokeValue invokeValue = Serialization.treeToValue(node, AdaptiveCardInvokeValue.class);
if (invokeValue == null) {
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Value property instanceof not properly formed");
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
}

if (invokeValue.getAction() == null) {
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Missing action property");
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
}

if (!invokeValue.getAction().getType().equals("Action.Execute")) {
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
HttpURLConnection.HTTP_BAD_REQUEST, "NotSupported",
String.format("The action '%s'is not supported.", invokeValue.getAction().getType()));
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
}

return invokeValue;
}

private AdaptiveCardInvokeResponse createAdaptiveCardInvokeErrorResponse(
Integer statusCode,
String code,
String message
) {
AdaptiveCardInvokeResponse adaptiveCardInvokeResponse = new AdaptiveCardInvokeResponse();
adaptiveCardInvokeResponse.setStatusCode(statusCode);
adaptiveCardInvokeResponse.setType("application/vnd.getmicrosoft().error");
com.microsoft.bot.schema.Error error = new com.microsoft.bot.schema.Error();
error.setCode(code);
error.setMessage(message);
adaptiveCardInvokeResponse.setValue(error);
return adaptiveCardInvokeResponse;
}


/**
* InvokeResponse Exception.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package com.microsoft.bot.builder;

import com.fasterxml.jackson.databind.JsonNode;
import com.microsoft.bot.connector.Async;
import com.microsoft.bot.schema.*;
import org.junit.Assert;
Expand Down Expand Up @@ -97,6 +98,33 @@ public void TestInstallationUpdateRemoveUpgrade() {
Assert.assertEquals("onInstallationUpdateRemove", bot.getRecord().get(1));
}

@Test
public void TestOnAdaptiveCardInvoke() {

AdaptiveCardInvokeValue adaptiveCardInvokeValue = new AdaptiveCardInvokeValue();
AdaptiveCardInvokeAction adaptiveCardInvokeAction = new AdaptiveCardInvokeAction();
adaptiveCardInvokeAction.setType("Action.Execute");
adaptiveCardInvokeValue.setAction(adaptiveCardInvokeAction);

JsonNode node = Serialization.objectToTree(adaptiveCardInvokeValue);
Activity activity = new Activity() {
{
setType(ActivityTypes.INVOKE);
setName("adaptiveCard/action");
setValue(node);
}
};

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("onAdaptiveCardInvoke", bot.getRecord().get(1));
}

@Test
public void TestOnTypingActivity() {
Activity activity = new Activity(ActivityTypes.TYPING);
Expand Down Expand Up @@ -375,6 +403,26 @@ public void TestUnrecognizedActivityType() {
Assert.assertEquals("onUnrecognizedActivityType", bot.getRecord().get(0));
}

private class TestInvokeAdapter extends NotImplementedAdapter {

private Activity activity;

public Activity getActivity() {
return activity;
}

public CompletableFuture<ResourceResponse[]> sendActivities(
TurnContext context,
List<Activity> activities
) {
activity = activities.stream()
.filter(x -> x.getType().equals(ActivityTypes.INVOKE_RESPONSE))
.findFirst()
.get();
return CompletableFuture.completedFuture(new ResourceResponse[0]);
}
}

private static class NotImplementedAdapter extends BotAdapter {
@Override
public CompletableFuture<ResourceResponse[]> sendActivities(
Expand Down Expand Up @@ -532,5 +580,12 @@ protected CompletableFuture onCommandResultActivity(TurnContext turnContext) {
return super.onCommandResultActivity(turnContext);
}

@Override
protected CompletableFuture<AdaptiveCardInvokeResponse> onAdaptiveCardInvoke(
TurnContext turnContext, AdaptiveCardInvokeValue invokeValue) {
record.add("onAdaptiveCardInvoke");
return CompletableFuture.completedFuture(new AdaptiveCardInvokeResponse());
}

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

package com.microsoft.bot.schema;

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

/**
* Defines the structure that arrives in the Activity.getValue().Authentication
* for Invoke activity with Name of 'adaptiveCard/action'.
*/
public class AdaptiveCardAuthentication {

@JsonProperty(value = "id")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String id;

@JsonProperty(value = "connectionName")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String connectionName;

@JsonProperty(value = "token")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String token;

/**
* Gets the Id of the adaptive card invoke authentication.
* @return the Id value as a String.
*/
public String getId() {
return this.id;
}

/**
* Sets the Id of the adaptive card invoke authentication.
* @param withId The Id value.
*/
public void setId(String withId) {
this.id = withId;
}

/**
* Gets the connection name of the adaptive card authentication.
* @return the ConnectionName value as a String.
*/
public String getConnectionName() {
return this.connectionName;
}

/**
* Sets the connection name of the adaptive card authentication.
* @param withConnectionName The ConnectionName value.
*/
public void setConnectionName(String withConnectionName) {
this.connectionName = withConnectionName;
}

/**
* Gets the token of the adaptive card authentication.
* @return the Token value as a String.
*/
public String getToken() {
return this.token;
}

/**
* Sets the token of the adaptive card authentication.
* @param withToken The Token value.
*/
public void setToken(String withToken) {
this.token = withToken;
}

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

package com.microsoft.bot.schema;

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

/**
* Defines the structure that arrives in the Activity.getValue().Action for
* Invoke activity with Name of 'adaptiveCard/action'.
*/
public class AdaptiveCardInvokeAction {

@JsonProperty(value = "type")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String type;

@JsonProperty(value = "id")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String id;

@JsonProperty(value = "verb")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String verb;

@JsonProperty(value = "data")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private Object data;

/**
* Gets the Type of this adaptive card action invoke.
* @return the Type value as a String.
*/
public String getType() {
return this.type;
}

/**
* Sets the Type of this adaptive card action invoke.
* @param withType The Type value.
*/
public void setType(String withType) {
this.type = withType;
}

/**
* Gets the Id of this adaptive card action invoke.
* @return the Id value as a String.
*/
public String getId() {
return this.id;
}

/**
* Sets the Id of this adaptive card action invoke.
* @param withId The Id value.
*/
public void setId(String withId) {
this.id = withId;
}

/**
* Gets the Verb of this adaptive card action invoke.
* @return the Verb value as a String.
*/
public String getVerb() {
return this.verb;
}

/**
* Sets the Verb of this adaptive card action invoke.
* @param withVerb The Verb value.
*/
public void setVerb(String withVerb) {
this.verb = withVerb;
}

/**
* Gets the Data of this adaptive card action invoke.
* @return the Data value as a Object.
*/
public Object getData() {
return this.data;
}

/**
* Sets the Data of this adaptive card action invoke.
* @param withData The Data value.
*/
public void setData(Object withData) {
this.data = withData;
}

}
Loading

0 comments on commit d4c8cb7

Please sign in to comment.