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

Added Action.Execute support #1104

Merged
merged 2 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -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 @@ -113,6 +114,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 @@ -462,6 +490,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 @@ -619,5 +667,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