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

Commit d4c8cb7

Browse files
Added Action.Execute support (#1104)
Co-authored-by: tracyboehrer <tracyboehrer@users.noreply.github.com>
1 parent e01aef1 commit d4c8cb7

File tree

6 files changed

+467
-0
lines changed

6 files changed

+467
-0
lines changed

libraries/bot-builder/src/main/java/com/microsoft/bot/builder/ActivityHandler.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package com.microsoft.bot.builder;
55

6+
import com.fasterxml.jackson.databind.JsonNode;
67
import com.microsoft.bot.connector.Async;
78
import java.net.HttpURLConnection;
89
import java.util.List;
@@ -13,9 +14,12 @@
1314

1415
import com.microsoft.bot.schema.Activity;
1516
import com.microsoft.bot.schema.ActivityTypes;
17+
import com.microsoft.bot.schema.AdaptiveCardInvokeResponse;
18+
import com.microsoft.bot.schema.AdaptiveCardInvokeValue;
1619
import com.microsoft.bot.schema.ChannelAccount;
1720
import com.microsoft.bot.schema.MessageReaction;
1821
import com.microsoft.bot.schema.ResourceResponse;
22+
import com.microsoft.bot.schema.Serialization;
1923
import com.microsoft.bot.schema.SignInConstants;
2024

2125
/**
@@ -398,6 +402,16 @@ protected CompletableFuture<Void> onEventActivity(TurnContext turnContext) {
398402
* @return A task that represents the work queued to execute.
399403
*/
400404
protected CompletableFuture<InvokeResponse> onInvokeActivity(TurnContext turnContext) {
405+
if (StringUtils.equals(turnContext.getActivity().getName(), "adaptiveCard/action")) {
406+
AdaptiveCardInvokeValue invokeValue = null;
407+
try {
408+
invokeValue = getAdaptiveCardInvokeValue(turnContext.getActivity());
409+
} catch (InvokeResponseException e) {
410+
return Async.completeExceptionally(e);
411+
}
412+
return onAdaptiveCardInvoke(turnContext, invokeValue).thenApply(result -> createInvokeResponse(result));
413+
}
414+
401415
if (
402416
StringUtils.equals(
403417
turnContext.getActivity().getName(), SignInConstants.VERIFY_STATE_OPERATION_NAME
@@ -612,6 +626,25 @@ protected CompletableFuture<Void> onInstallationUpdateRemove(TurnContext turnCon
612626
return CompletableFuture.completedFuture(null);
613627
}
614628

629+
/**
630+
* Invoked when the bot is sent an Adaptive Card Action Execute.
631+
*
632+
* @param turnContext A strongly-typed context Object for this
633+
* turn.
634+
* @param invokeValue A stringly-typed Object from the incoming
635+
* activity's Value.
636+
*
637+
* @return A task that represents the work queued to execute.
638+
*
639+
* When the {@link OnInvokeActivity(TurnContext{InvokeActivity})} method
640+
* receives an Invoke with a {@link InvokeActivity#name} of
641+
* `adaptiveCard/action`, it calls this method.
642+
*/
643+
protected CompletableFuture<AdaptiveCardInvokeResponse> onAdaptiveCardInvoke(
644+
TurnContext turnContext, AdaptiveCardInvokeValue invokeValue) {
645+
return Async.completeExceptionally(new InvokeResponseException(HttpURLConnection.HTTP_NOT_IMPLEMENTED));
646+
}
647+
615648
/**
616649
* Override this in a derived class to provide logic specific to
617650
* ActivityTypes.END_OF_CONVERSATION activities.
@@ -662,6 +695,62 @@ protected CompletableFuture<Void> onUnrecognizedActivityType(TurnContext turnCon
662695
return CompletableFuture.completedFuture(null);
663696
}
664697

698+
private AdaptiveCardInvokeValue getAdaptiveCardInvokeValue(Activity activity) throws InvokeResponseException {
699+
if (activity.getValue() == null) {
700+
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
701+
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Missing value property");
702+
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
703+
}
704+
705+
Object obj = activity.getValue();
706+
JsonNode node = null;
707+
if (obj instanceof JsonNode) {
708+
node = (JsonNode) obj;
709+
} else {
710+
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
711+
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Value property instanceof not properly formed");
712+
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
713+
}
714+
715+
AdaptiveCardInvokeValue invokeValue = Serialization.treeToValue(node, AdaptiveCardInvokeValue.class);
716+
if (invokeValue == null) {
717+
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
718+
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Value property instanceof not properly formed");
719+
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
720+
}
721+
722+
if (invokeValue.getAction() == null) {
723+
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
724+
HttpURLConnection.HTTP_BAD_REQUEST, "BadRequest", "Missing action property");
725+
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
726+
}
727+
728+
if (!invokeValue.getAction().getType().equals("Action.Execute")) {
729+
AdaptiveCardInvokeResponse response = createAdaptiveCardInvokeErrorResponse(
730+
HttpURLConnection.HTTP_BAD_REQUEST, "NotSupported",
731+
String.format("The action '%s'is not supported.", invokeValue.getAction().getType()));
732+
throw new InvokeResponseException(HttpURLConnection.HTTP_BAD_REQUEST, response);
733+
}
734+
735+
return invokeValue;
736+
}
737+
738+
private AdaptiveCardInvokeResponse createAdaptiveCardInvokeErrorResponse(
739+
Integer statusCode,
740+
String code,
741+
String message
742+
) {
743+
AdaptiveCardInvokeResponse adaptiveCardInvokeResponse = new AdaptiveCardInvokeResponse();
744+
adaptiveCardInvokeResponse.setStatusCode(statusCode);
745+
adaptiveCardInvokeResponse.setType("application/vnd.getmicrosoft().error");
746+
com.microsoft.bot.schema.Error error = new com.microsoft.bot.schema.Error();
747+
error.setCode(code);
748+
error.setMessage(message);
749+
adaptiveCardInvokeResponse.setValue(error);
750+
return adaptiveCardInvokeResponse;
751+
}
752+
753+
665754
/**
666755
* InvokeResponse Exception.
667756
*/

libraries/bot-builder/src/test/java/com/microsoft/bot/builder/ActivityHandlerTests.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
package com.microsoft.bot.builder;
55

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

101+
@Test
102+
public void TestOnAdaptiveCardInvoke() {
103+
104+
AdaptiveCardInvokeValue adaptiveCardInvokeValue = new AdaptiveCardInvokeValue();
105+
AdaptiveCardInvokeAction adaptiveCardInvokeAction = new AdaptiveCardInvokeAction();
106+
adaptiveCardInvokeAction.setType("Action.Execute");
107+
adaptiveCardInvokeValue.setAction(adaptiveCardInvokeAction);
108+
109+
JsonNode node = Serialization.objectToTree(adaptiveCardInvokeValue);
110+
Activity activity = new Activity() {
111+
{
112+
setType(ActivityTypes.INVOKE);
113+
setName("adaptiveCard/action");
114+
setValue(node);
115+
}
116+
};
117+
118+
TurnContext turnContext = new TurnContextImpl(new TestInvokeAdapter(), activity);
119+
120+
TestActivityHandler bot = new TestActivityHandler();
121+
bot.onTurn(turnContext).join();
122+
123+
Assert.assertEquals(2, bot.getRecord().size());
124+
Assert.assertEquals("onInvokeActivity", bot.getRecord().get(0));
125+
Assert.assertEquals("onAdaptiveCardInvoke", bot.getRecord().get(1));
126+
}
127+
100128
@Test
101129
public void TestOnTypingActivity() {
102130
Activity activity = new Activity(ActivityTypes.TYPING);
@@ -375,6 +403,26 @@ public void TestUnrecognizedActivityType() {
375403
Assert.assertEquals("onUnrecognizedActivityType", bot.getRecord().get(0));
376404
}
377405

406+
private class TestInvokeAdapter extends NotImplementedAdapter {
407+
408+
private Activity activity;
409+
410+
public Activity getActivity() {
411+
return activity;
412+
}
413+
414+
public CompletableFuture<ResourceResponse[]> sendActivities(
415+
TurnContext context,
416+
List<Activity> activities
417+
) {
418+
activity = activities.stream()
419+
.filter(x -> x.getType().equals(ActivityTypes.INVOKE_RESPONSE))
420+
.findFirst()
421+
.get();
422+
return CompletableFuture.completedFuture(new ResourceResponse[0]);
423+
}
424+
}
425+
378426
private static class NotImplementedAdapter extends BotAdapter {
379427
@Override
380428
public CompletableFuture<ResourceResponse[]> sendActivities(
@@ -532,5 +580,12 @@ protected CompletableFuture onCommandResultActivity(TurnContext turnContext) {
532580
return super.onCommandResultActivity(turnContext);
533581
}
534582

583+
@Override
584+
protected CompletableFuture<AdaptiveCardInvokeResponse> onAdaptiveCardInvoke(
585+
TurnContext turnContext, AdaptiveCardInvokeValue invokeValue) {
586+
record.add("onAdaptiveCardInvoke");
587+
return CompletableFuture.completedFuture(new AdaptiveCardInvokeResponse());
588+
}
589+
535590
}
536591
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MT License.
3+
4+
package com.microsoft.bot.schema;
5+
6+
import com.fasterxml.jackson.annotation.JsonInclude;
7+
import com.fasterxml.jackson.annotation.JsonProperty;
8+
9+
/**
10+
* Defines the structure that arrives in the Activity.getValue().Authentication
11+
* for Invoke activity with Name of 'adaptiveCard/action'.
12+
*/
13+
public class AdaptiveCardAuthentication {
14+
15+
@JsonProperty(value = "id")
16+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
17+
private String id;
18+
19+
@JsonProperty(value = "connectionName")
20+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
21+
private String connectionName;
22+
23+
@JsonProperty(value = "token")
24+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
25+
private String token;
26+
27+
/**
28+
* Gets the Id of the adaptive card invoke authentication.
29+
* @return the Id value as a String.
30+
*/
31+
public String getId() {
32+
return this.id;
33+
}
34+
35+
/**
36+
* Sets the Id of the adaptive card invoke authentication.
37+
* @param withId The Id value.
38+
*/
39+
public void setId(String withId) {
40+
this.id = withId;
41+
}
42+
43+
/**
44+
* Gets the connection name of the adaptive card authentication.
45+
* @return the ConnectionName value as a String.
46+
*/
47+
public String getConnectionName() {
48+
return this.connectionName;
49+
}
50+
51+
/**
52+
* Sets the connection name of the adaptive card authentication.
53+
* @param withConnectionName The ConnectionName value.
54+
*/
55+
public void setConnectionName(String withConnectionName) {
56+
this.connectionName = withConnectionName;
57+
}
58+
59+
/**
60+
* Gets the token of the adaptive card authentication.
61+
* @return the Token value as a String.
62+
*/
63+
public String getToken() {
64+
return this.token;
65+
}
66+
67+
/**
68+
* Sets the token of the adaptive card authentication.
69+
* @param withToken The Token value.
70+
*/
71+
public void setToken(String withToken) {
72+
this.token = withToken;
73+
}
74+
75+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MT License.
3+
4+
package com.microsoft.bot.schema;
5+
6+
import com.fasterxml.jackson.annotation.JsonInclude;
7+
import com.fasterxml.jackson.annotation.JsonProperty;
8+
9+
/**
10+
* Defines the structure that arrives in the Activity.getValue().Action for
11+
* Invoke activity with Name of 'adaptiveCard/action'.
12+
*/
13+
public class AdaptiveCardInvokeAction {
14+
15+
@JsonProperty(value = "type")
16+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
17+
private String type;
18+
19+
@JsonProperty(value = "id")
20+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
21+
private String id;
22+
23+
@JsonProperty(value = "verb")
24+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
25+
private String verb;
26+
27+
@JsonProperty(value = "data")
28+
@JsonInclude(JsonInclude.Include.NON_EMPTY)
29+
private Object data;
30+
31+
/**
32+
* Gets the Type of this adaptive card action invoke.
33+
* @return the Type value as a String.
34+
*/
35+
public String getType() {
36+
return this.type;
37+
}
38+
39+
/**
40+
* Sets the Type of this adaptive card action invoke.
41+
* @param withType The Type value.
42+
*/
43+
public void setType(String withType) {
44+
this.type = withType;
45+
}
46+
47+
/**
48+
* Gets the Id of this adaptive card action invoke.
49+
* @return the Id value as a String.
50+
*/
51+
public String getId() {
52+
return this.id;
53+
}
54+
55+
/**
56+
* Sets the Id of this adaptive card action invoke.
57+
* @param withId The Id value.
58+
*/
59+
public void setId(String withId) {
60+
this.id = withId;
61+
}
62+
63+
/**
64+
* Gets the Verb of this adaptive card action invoke.
65+
* @return the Verb value as a String.
66+
*/
67+
public String getVerb() {
68+
return this.verb;
69+
}
70+
71+
/**
72+
* Sets the Verb of this adaptive card action invoke.
73+
* @param withVerb The Verb value.
74+
*/
75+
public void setVerb(String withVerb) {
76+
this.verb = withVerb;
77+
}
78+
79+
/**
80+
* Gets the Data of this adaptive card action invoke.
81+
* @return the Data value as a Object.
82+
*/
83+
public Object getData() {
84+
return this.data;
85+
}
86+
87+
/**
88+
* Sets the Data of this adaptive card action invoke.
89+
* @param withData The Data value.
90+
*/
91+
public void setData(Object withData) {
92+
this.data = withData;
93+
}
94+
95+
}

0 commit comments

Comments
 (0)