From 8aacac1bd72c1c5bef380c49c581d2bff3bae6de Mon Sep 17 00:00:00 2001 From: Lee Parrish <30470292+LeeParrishMSFT@users.noreply.github.com> Date: Fri, 26 Mar 2021 16:43:50 -0500 Subject: [PATCH] Added Action.Execute support --- .../bot/builder/ActivityHandler.java | 89 +++++++++++++++++ .../bot/builder/ActivityHandlerTests.java | 55 +++++++++++ .../schema/AdaptiveCardAuthentication.java | 75 +++++++++++++++ .../bot/schema/AdaptiveCardInvokeAction.java | 95 +++++++++++++++++++ .../schema/AdaptiveCardInvokeResponse.java | 75 +++++++++++++++ .../bot/schema/AdaptiveCardInvokeValue.java | 78 +++++++++++++++ 6 files changed, 467 insertions(+) create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardAuthentication.java create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeAction.java create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeResponse.java create mode 100644 libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeValue.java diff --git a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java index e9c4b6918..74d7f5ed3 100644 --- a/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java +++ b/libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java @@ -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; @@ -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; /** @@ -398,6 +402,16 @@ protected CompletableFuture onEventActivity(TurnContext turnContext) { * @return A task that represents the work queued to execute. */ protected CompletableFuture 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 @@ -612,6 +626,25 @@ protected CompletableFuture 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 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. @@ -662,6 +695,62 @@ protected CompletableFuture 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. */ diff --git a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ActivityHandlerTests.java b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ActivityHandlerTests.java index 66401f54b..b8884b67f 100644 --- a/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ActivityHandlerTests.java +++ b/libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ActivityHandlerTests.java @@ -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; @@ -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); @@ -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 sendActivities( + TurnContext context, + List 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 sendActivities( @@ -619,5 +667,12 @@ protected CompletableFuture onCommandResultActivity(TurnContext turnContext) { return super.onCommandResultActivity(turnContext); } + @Override + protected CompletableFuture onAdaptiveCardInvoke( + TurnContext turnContext, AdaptiveCardInvokeValue invokeValue) { + record.add("onAdaptiveCardInvoke"); + return CompletableFuture.completedFuture(new AdaptiveCardInvokeResponse()); + } + } } diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardAuthentication.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardAuthentication.java new file mode 100644 index 000000000..9acedd10a --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardAuthentication.java @@ -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; + } + +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeAction.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeAction.java new file mode 100644 index 000000000..afa60915a --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeAction.java @@ -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; + } + +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeResponse.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeResponse.java new file mode 100644 index 000000000..d30fdac0f --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeResponse.java @@ -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 is returned as the result of an Invoke activity + * with Name of 'adaptiveCard/action'. + */ +public class AdaptiveCardInvokeResponse { + + @JsonProperty(value = "statusCode") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private int statusCode; + + @JsonProperty(value = "type") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String type; + + @JsonProperty(value = "value") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Object value; + + /** + * Gets the Card Action response getStatusCode(). + * @return the StatusCode value as a int. + */ + public int getStatusCode() { + return this.statusCode; + } + + /** + * Sets the Card Action response getStatusCode(). + * @param withStatusCode The StatusCode value. + */ + public void setStatusCode(int withStatusCode) { + this.statusCode = withStatusCode; + } + + /** + * Gets the Type of this {@link AdaptiveCardInvokeResponse} . + * @return the Type value as a String. + */ + public String getType() { + return this.type; + } + + /** + * Sets the Type of this {@link AdaptiveCardInvokeResponse} . + * @param withType The Type value. + */ + public void setType(String withType) { + this.type = withType; + } + + /** + * Gets the json response Object. + * @return the Value value as a Object. + */ + public Object getValue() { + return this.value; + } + + /** + * Sets the json response Object. + * @param withValue The Value value. + */ + public void setValue(Object withValue) { + this.value = withValue; + } + +} diff --git a/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeValue.java b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeValue.java new file mode 100644 index 000000000..c64764d51 --- /dev/null +++ b/libraries/bot-schema/src/main/java/com/microsoft/bot/schema/AdaptiveCardInvokeValue.java @@ -0,0 +1,78 @@ +// 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.Value for Invoke activity + * with Name of 'adaptiveCard/action'. + */ +public class AdaptiveCardInvokeValue { + + @JsonProperty(value = "action") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private AdaptiveCardInvokeAction action; + + @JsonProperty(value = "authentication") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private AdaptiveCardAuthentication authentication; + + @JsonProperty(value = "state") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private String state; + + /** + * Gets the action of this adaptive card action invoke value. + * @return the Action value as a AdaptiveCardInvokeAction. + */ + public AdaptiveCardInvokeAction getAction() { + return this.action; + } + + /** + * Sets the action of this adaptive card action invoke value. + * @param withAction The Action value. + */ + public void setAction(AdaptiveCardInvokeAction withAction) { + this.action = withAction; + } + + /** + * Gets the {@link AdaptiveCardAuthentication} for this adaptive + * card invoke action value. + * @return the Authentication value as a AdaptiveCardAuthentication. + */ + public AdaptiveCardAuthentication getAuthentication() { + return this.authentication; + } + + /** + * Sets the {@link AdaptiveCardAuthentication} for this adaptive + * card invoke action value. + * @param withAuthentication The Authentication value. + */ + public void setAuthentication(AdaptiveCardAuthentication withAuthentication) { + this.authentication = withAuthentication; + } + + /** + * Gets the 'state' or magic code for an OAuth flow. + * @return the State value as a String. + */ + public String getState() { + return this.state; + } + + /** + * Sets the 'state' or magic code for an OAuth flow. + * @param withState The State value. + */ + public void setState(String withState) { + this.state = withState; + } + +}