Skip to content

Commit 603e8bb

Browse files
committed
Support for SharePoint (Viva) Adaptive Card Extension
1 parent ef5ecd3 commit 603e8bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2736
-5
lines changed

libraries/botbuilder-core/botbuilder/core/serializer_helper.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import botbuilder.schema as schema
1111
import botbuilder.schema.teams as teams_schema
12+
import botbuilder.schema.sharepoint as sharepoint_schema
1213

1314
DEPENDICIES = [
1415
schema_cls
@@ -20,6 +21,11 @@
2021
for key, schema_cls in getmembers(teams_schema)
2122
if isinstance(schema_cls, type) and issubclass(schema_cls, (Model, Enum))
2223
]
24+
DEPENDICIES += [
25+
schema_cls
26+
for key, schema_cls in getmembers(sharepoint_schema)
27+
if isinstance(schema_cls, type) and issubclass(schema_cls, (Model, Enum))
28+
]
2329
DEPENDICIES_DICT = {dependency.__name__: dependency for dependency in DEPENDICIES}
2430

2531

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# coding=utf-8
2+
# --------------------------------------------------------------------------
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
# Licensed under the MIT License. See License.txt in the project root for
5+
# license information.
6+
# --------------------------------------------------------------------------
7+
8+
from .sharepoint_activity_handler import SharePointActivityHandler
9+
10+
__all__ = ["SharePointActivityHandler"]
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
# pylint: disable=too-many-lines
5+
6+
from http import HTTPStatus
7+
from botbuilder.core import ActivityHandler, InvokeResponse
8+
from botbuilder.core.activity_handler import _InvokeResponseException
9+
from botbuilder.core.turn_context import TurnContext
10+
from botbuilder.schema.sharepoint import (
11+
AceRequest,
12+
BaseHandleActionResponse,
13+
CardViewResponse,
14+
GetPropertyPaneConfigurationResponse,
15+
QuickViewHandleActionResponse,
16+
QuickViewResponse,
17+
)
18+
from ..serializer_helper import deserializer_helper
19+
20+
21+
class SharePointActivityHandler(ActivityHandler):
22+
"""
23+
The SharePointActivityHandler is derived from ActivityHandler. It adds support for
24+
SharePoint-specific events and interactions.
25+
"""
26+
27+
async def on_invoke_activity(self, turn_context: TurnContext) -> InvokeResponse:
28+
"""
29+
Invoked when an invoke activity is received from the connector.
30+
Invoke activities can be used to communicate many different things.
31+
32+
:param turn_context: A context object for this turn.
33+
34+
:returns: An InvokeResponse that represents the work queued to execute.
35+
36+
.. remarks::
37+
Invoke activities communicate programmatic commands from a client or channel to a bot.
38+
The meaning of an invoke activity is defined by the "invoke_activity.name" property,
39+
which is meaningful within the scope of a channel.
40+
"""
41+
try:
42+
if not turn_context.activity.name:
43+
raise NotImplementedError()
44+
45+
if turn_context.activity.name == "cardExtension/getCardView":
46+
print("Printing AceReq", turn_context.activity.value)
47+
return self._create_invoke_response(
48+
await self.on_sharepoint_task_get_card_view(
49+
turn_context,
50+
deserializer_helper(AceRequest, turn_context.activity.value),
51+
)
52+
)
53+
54+
if turn_context.activity.name == "cardExtension/getQuickView":
55+
return self._create_invoke_response(
56+
await self.on_sharepoint_task_get_quick_view(
57+
turn_context,
58+
deserializer_helper(AceRequest, turn_context.activity.value),
59+
)
60+
)
61+
62+
if (
63+
turn_context.activity.name
64+
== "cardExtension/getPropertyPaneConfiguration"
65+
):
66+
return self._create_invoke_response(
67+
await self.on_sharepoint_task_get_property_pane_configuration(
68+
turn_context,
69+
deserializer_helper(AceRequest, turn_context.activity.value),
70+
)
71+
)
72+
73+
if (
74+
turn_context.activity.name
75+
== "cardExtension/setPropertyPaneConfiguration"
76+
):
77+
ace_request = deserializer_helper(
78+
AceRequest, turn_context.activity.value
79+
)
80+
set_prop_pane_config_response = (
81+
await self.on_sharepoint_task_set_property_pane_configuration(
82+
turn_context, ace_request
83+
)
84+
)
85+
self.validate_set_property_pane_configuration_response(
86+
set_prop_pane_config_response
87+
)
88+
return self._create_invoke_response(set_prop_pane_config_response)
89+
90+
if turn_context.activity.name == "cardExtension/handleAction":
91+
return self._create_invoke_response(
92+
await self.on_sharepoint_task_handle_action(
93+
turn_context,
94+
deserializer_helper(AceRequest, turn_context.activity.value),
95+
)
96+
)
97+
98+
if turn_context.activity.name == "cardExtension/token":
99+
await self.on_sign_in_invoke(turn_context)
100+
return self._create_invoke_response()
101+
102+
except _InvokeResponseException as invoke_exception:
103+
return invoke_exception.create_invoke_response()
104+
return await super().on_invoke_activity(turn_context)
105+
106+
async def on_sharepoint_task_get_card_view(
107+
self, turn_context: TurnContext, ace_request: AceRequest
108+
) -> CardViewResponse:
109+
"""
110+
Override this in a derived class to provide logic for when a card view is fetched.
111+
112+
:param turn_context: A context object for this turn.
113+
:param ace_request: The ACE invoke request value payload.
114+
:returns: A Card View Response for the request.
115+
"""
116+
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
117+
118+
async def on_sharepoint_task_get_quick_view(
119+
self, turn_context: TurnContext, ace_request: AceRequest
120+
) -> QuickViewResponse:
121+
"""
122+
Override this in a derived class to provide logic for when a quick view is fetched.
123+
124+
:param turn_context: A strongly-typed context object for this turn.
125+
:param ace_request: The ACE invoke request value payload.
126+
:returns: A Quick View Response for the request
127+
"""
128+
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
129+
130+
async def on_sharepoint_task_get_property_pane_configuration(
131+
self, turn_context: TurnContext, ace_request: AceRequest
132+
) -> GetPropertyPaneConfigurationResponse:
133+
"""
134+
Override this in a derived class to provide logic for getting configuration pane properties.
135+
136+
:param turn_context: A strongly-typed context object for this turn.
137+
:param ace_request: The ACE invoke request value payload.
138+
:returns: A Property Pane Configuration Response for the request.
139+
"""
140+
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
141+
142+
async def on_sharepoint_task_set_property_pane_configuration(
143+
self, turn_context: TurnContext, ace_request: AceRequest
144+
) -> BaseHandleActionResponse:
145+
"""
146+
Override this in a derived class to provide logic for setting configuration pane properties.
147+
148+
:param turn_context: A strongly-typed context object for this turn.
149+
:param ace_request: The ACE invoke request value payload.
150+
:returns: Card view or no-op action response.
151+
"""
152+
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
153+
154+
async def on_sharepoint_task_handle_action(
155+
self, turn_context: TurnContext, ace_request: AceRequest
156+
) -> BaseHandleActionResponse:
157+
"""
158+
Override this in a derived class to provide logic for handling ACE actions.
159+
160+
:param turn_context: A strongly-typed context object for this turn.
161+
:param ace_request: The ACE invoke request value payload.
162+
:returns: A handle action response..
163+
"""
164+
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
165+
166+
def validate_set_property_pane_configuration_response(
167+
self, response: BaseHandleActionResponse
168+
):
169+
"""
170+
Validates the response for SetPropertyPaneConfiguration action.
171+
172+
:param response: The response object.
173+
:raises ValueError: If response is of type QuickViewHandleActionResponse.
174+
"""
175+
if isinstance(response, QuickViewHandleActionResponse):
176+
raise _InvokeResponseException(
177+
HTTPStatus.INTERNAL_SERVER_ERROR,
178+
"Response for SetPropertyPaneConfiguration action can't be of QuickView type.",
179+
)
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
# pylint: disable=too-many-lines
5+
import sys
6+
import os
7+
8+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
9+
10+
from typing import List
11+
import aiounittest
12+
from botbuilder.schema.sharepoint import AceRequest
13+
from botbuilder.core import TurnContext
14+
from botbuilder.core.sharepoint import SharePointActivityHandler
15+
from botbuilder.schema import (
16+
Activity,
17+
ActivityTypes,
18+
)
19+
from simple_adapter import SimpleAdapter
20+
21+
22+
class TestingSharePointActivityHandler(SharePointActivityHandler):
23+
__test__ = False
24+
25+
def __init__(self):
26+
self.record: List[str] = []
27+
28+
async def on_sharepoint_task_get_card_view(
29+
self, turn_context: TurnContext, request: AceRequest
30+
):
31+
self.record.append("on_sharepoint_task_get_card_view")
32+
return await super().on_sharepoint_task_get_card_view(turn_context, request)
33+
34+
async def on_sharepoint_task_get_property_pane_configuration(
35+
self, turn_context: TurnContext, request: AceRequest
36+
):
37+
self.record.append("on_sharepoint_task_get_property_pane_configuration")
38+
return await super().on_sharepoint_task_get_property_pane_configuration(
39+
turn_context, request
40+
)
41+
42+
async def on_sharepoint_task_get_quick_view(
43+
self, turn_context: TurnContext, request: AceRequest
44+
):
45+
self.record.append("on_sharepoint_task_get_quick_view")
46+
return await super().on_sharepoint_task_get_quick_view(turn_context, request)
47+
48+
async def on_sharepoint_task_set_property_pane_configuration(
49+
self, turn_context: TurnContext, request: AceRequest
50+
):
51+
self.record.append("on_sharepoint_task_set_property_pane_configuration")
52+
return await super().on_sharepoint_task_set_property_pane_configuration(
53+
turn_context, request
54+
)
55+
56+
async def on_sharepoint_task_handle_action(
57+
self, turn_context: TurnContext, request: AceRequest
58+
):
59+
self.record.append("on_sharepoint_task_handle_action")
60+
return await super().on_sharepoint_task_handle_action(turn_context, request)
61+
62+
63+
class TestSharePointActivityHandler(aiounittest.AsyncTestCase):
64+
async def test_on_sharepoint_task_get_card_view(self):
65+
# Arrange
66+
activity = Activity(
67+
type=ActivityTypes.invoke,
68+
name="cardExtension/getCardView",
69+
value=AceRequest(),
70+
)
71+
turn_context = TurnContext(SimpleAdapter(), activity)
72+
73+
# Act
74+
bot = TestingSharePointActivityHandler()
75+
await bot.on_turn(turn_context)
76+
77+
# Assert
78+
self.assertEqual(1, len(bot.record))
79+
self.assertEqual(bot.record, ["on_sharepoint_task_get_card_view"])
80+
81+
async def test_on_sharepoint_task_get_property_pane_configuration(self):
82+
# Arrange
83+
activity = Activity(
84+
type=ActivityTypes.invoke,
85+
name="cardExtension/getPropertyPaneConfiguration",
86+
value=AceRequest(),
87+
)
88+
turn_context = TurnContext(SimpleAdapter(), activity)
89+
90+
# Act
91+
bot = TestingSharePointActivityHandler()
92+
await bot.on_turn(turn_context)
93+
94+
# Assert
95+
self.assertEqual(1, len(bot.record))
96+
self.assertEqual(
97+
bot.record, ["on_sharepoint_task_get_property_pane_configuration"]
98+
)
99+
100+
async def test_on_sharepoint_task_get_quick_view(self):
101+
# Arrange
102+
activity = Activity(
103+
type=ActivityTypes.invoke,
104+
name="cardExtension/getQuickView",
105+
value=AceRequest(),
106+
)
107+
turn_context = TurnContext(SimpleAdapter(), activity)
108+
109+
# Act
110+
bot = TestingSharePointActivityHandler()
111+
await bot.on_turn(turn_context)
112+
113+
# Assert
114+
self.assertEqual(1, len(bot.record))
115+
self.assertEqual(bot.record, ["on_sharepoint_task_get_quick_view"])
116+
117+
async def test_on_sharepoint_task_set_property_pane_configuration(self):
118+
# Arrange
119+
activity = Activity(
120+
type=ActivityTypes.invoke,
121+
name="cardExtension/setPropertyPaneConfiguration",
122+
value=AceRequest(),
123+
)
124+
turn_context = TurnContext(SimpleAdapter(), activity)
125+
126+
# Act
127+
bot = TestingSharePointActivityHandler()
128+
await bot.on_turn(turn_context)
129+
130+
# Assert
131+
self.assertEqual(1, len(bot.record))
132+
self.assertEqual(
133+
bot.record, ["on_sharepoint_task_set_property_pane_configuration"]
134+
)
135+
136+
async def test_on_sharepoint_task_handle_action(self):
137+
# Arrange
138+
activity = Activity(
139+
type=ActivityTypes.invoke,
140+
name="cardExtension/handleAction",
141+
value=AceRequest(),
142+
)
143+
turn_context = TurnContext(SimpleAdapter(), activity)
144+
145+
# Act
146+
bot = TestingSharePointActivityHandler()
147+
await bot.on_turn(turn_context)
148+
149+
# Assert
150+
self.assertEqual(1, len(bot.record))
151+
self.assertEqual(bot.record, ["on_sharepoint_task_handle_action"])
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
5+
from .ace_data import AceData
6+
from .ace_request import AceRequest
7+
from .card_view_response import CardViewResponse
8+
from .quick_view_response import QuickViewResponse
9+
from .get_property_pane_configuration_response import (
10+
GetPropertyPaneConfigurationResponse,
11+
)
12+
from .handle_action_response import BaseHandleActionResponse
13+
from .handle_action_response import QuickViewHandleActionResponse
14+
15+
16+
__all__ = [
17+
"AceData",
18+
"AceRequest",
19+
"CardViewResponse",
20+
"QuickViewResponse",
21+
"GetPropertyPaneConfigurationResponse",
22+
"BaseHandleActionResponse",
23+
"QuickViewHandleActionResponse",
24+
]

0 commit comments

Comments
 (0)