8000 Support originating audience for skills conversations · RobStand/botbuilder-python@838fae6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 838fae6

Browse files
author
Eric Dahlvang
committed
Support originating audience for skills conversations
1 parent 5ed1497 commit 838fae6

File tree

7 files changed

+163
-11
lines changed

7 files changed

+163
-11
lines changed

libraries/botbuilder-core/botbuilder/core/skills/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
from .conversation_id_factory import ConversationIdFactoryBase
1010
from .skill_conversation_id_factory import SkillConversationIdFactory
1111
from .skill_handler import SkillHandler
12+
from .skill_conversation_id_factory_options import SkillConversationIdFactoryOptions
13+
from .skill_conversation_reference import SkillConversationReference
1214

1315
__all__ = [
1416
"BotFrameworkSkill",
1517
"ConversationIdFactoryBase",
1618
"SkillConversationIdFactory",
19+
"SkillConversationIdFactoryOptions",
20+
"SkillConversationReference",
1721
"SkillHandler",
1822
]
Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,66 @@
11
# Copyright (c) Microsoft Corporation. All rights reserved.
22
# Licensed under the MIT License.
3+
34
from abc import ABC, abstractmethod
5+
from typing import Union
46
from botbuilder.schema import ConversationReference
7+
from .skill_conversation_id_factory_options import SkillConversationIdFactoryOptions
8+
from .skill_conversation_reference import SkillConversationReference
59

610

711
class ConversationIdFactoryBase(ABC):
12+
"""
13+
Handles creating conversation ids for skill and should be subclassed.
14+
15+
.. remarks::
16+
Derive from this class to handle creation of conversation ids, retrieval of
17+
SkillConversationReferences and deletion.
18+
"""
19+
820
@abstractmethod
921
async def create_skill_conversation_id(
10-
self, conversation_reference: ConversationReference
22+
self,
23+
options_or_conversation_reference: Union[
24+
SkillConversationIdFactoryOptions, ConversationReference
25+
],
1126
) -> str:
27+
"""
28+
Using the options passed in, creates a conversation id and
29+
SkillConversationReference, storing them for future use.
30+
31+
:param options_or_conversation_reference: The options contain properties useful
32+
for generating a SkillConversationReference and conversation id.
33+
:type options_or_conversation_reference: :class:
34+
`Union[SkillConversationIdFactoryOptions, ConversationReference]`
35+
36+
:returns: A skill conversation id.
37+
38+
.. note::
39+
SkillConversationIdFactoryOptions is the preferred parameter type, while ConversationReference
40+
type is provided for backwards compatability.
41+
"""
1242
raise NotImplementedError()
1343

1444
@abstractmethod
1545
async def get_conversation_reference(
1646
self, skill_conversation_id: str
17-
) -> ConversationReference:
47+
) -> Union[SkillConversationReference, ConversationReference]:
48+
"""
49+
Retrieves a SkillConversationReference using a conversation id passed in.
50+
51+
:param skill_conversation_id: The conversation id for which to retrieve
52+
the SkillConversationReference.
53+
:type skill_conversation_id: str
54+
55+
.. note::
56+
SkillConversationReference is the preferred return type, while ConversationReference
57+
type is provided for backwards compatability.
58+
"""
1859
raise NotImplementedError()
1960

2061
@abstractmethod
2162
async def delete_conversation_reference(self, skill_conversation_id: str):
63+
"""
64+
Removes any reference to objects keyed on the conversation id passed in.
65+
"""
2266
raise NotImplementedError()

libraries/botbuilder-core/botbuilder/core/skills/skill_conversation_id_factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def __init__(self, storage: Storage):
1818
self._forward_x_ref: Dict[str, str] = {}
1919
self._backward_x_ref: Dict[str, Tuple[str, str]] = {}
2020

21-
async def create_skill_conversation_id(
21+
async def create_skill_conversation_id( # pylint: disable=W0221
2222
self, conversation_reference: ConversationReference
2323
) -> str:
2424
if not conversation_reference:
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from botbuilder.schema import Activity
5+
from .bot_framework_skill import BotFrameworkSkill
6+
7+
8+
class SkillConversationIdFactoryOptions:
9+
def __init__(
10+
self,
11+
from_bot_oauth_scope: str,
12+
from_bot_id: str,
13+
activity: Activity,
14+
bot_framework_skill: BotFrameworkSkill,
15+
):
16+
if from_bot_oauth_scope is None:
17+
raise TypeError(
18+
"SkillConversationIdFactoryOptions(): from_bot_oauth_scope cannot be None."
19+
)
20+
21+
if from_bot_id is None:
22+
raise TypeError(
23+
"SkillConversationIdFactoryOptions(): from_bot_id cannot be None."
24+
)
25+
26+
if activity is None:
27+
raise TypeError(
28+
"SkillConversationIdFactoryOptions(): activity cannot be None."
29+
)
30+
31+
if bot_framework_skill is None:
32+
raise TypeError(
33+
"SkillConversationIdFactoryOptions(): bot_framework_skill cannot be None."
34+
)
35+
36+
self._from_bot_oauth_scope = from_bot_oauth_scope
37+
self._from_bot_id = from_bot_id
38+
self._activity = activity
39+
self._bot_framework_skill = bot_framework_skill
40+
41+
@property
42+
def from_bot_oauth_scope(self) -> str:
43+
return self._from_bot_oauth_scope
44+
45+
@property
46+
def from_bot_id(self) -> str:
47+
return self._from_bot_id
48+
49+
@property
50+
def activity(self) -> Activity:
51+
return self._activity
52+
53+
@property
54+
def bot_framework_skill(self) -> BotFrameworkSkill:
55+
return self._bot_framework_skill
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
from botbuilder.schema import ConversationReference
4+
5+
6+
class SkillConversationReference:
7+
"""
8+
ConversationReference implementation for Skills ConversationIdFactory.
9+
"""
10+
11+
def __init__(self, conversation_reference: ConversationReference, oauth_scope: str):
12+
if conversation_reference is None:
13+
raise TypeError(
14+
"SkillConversationReference(): conversation_reference cannot be None."
15+
)
16+
17+
if oauth_scope is None:
18+
raise TypeError("SkillConversationReference(): oauth_scope cannot be None.")
19+
20+
self._conversation_reference = conversation_reference
21+
self._oauth_scope = oauth_scope
22+
23+
@property
24+
def conversation_reference(self) -> ConversationReference:
25+
return self._conversation_reference
26+
27+
@property
28+
def oauth_scope(self) -> str:
29+
return self._oauth_scope

libraries/botbuilder-core/botbuilder/core/skills/skill_handler.py

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
)
1313
from botframework.connector.auth import (
1414
AuthenticationConfiguration,
15+
AuthenticationConstants,
1516
ChannelProvider,
1617
ClaimsIdentity,
1718
CredentialProvider,
19+
GovernmentConstants,
1820
)
19-
20-
from .skill_conversation_id_factory import SkillConversationIdFactory
21+
from .skill_conversation_reference import SkillConversationReference
22+
from .skill_conversation_id_factory import ConversationIdFactoryBase
2123

2224

2325
class SkillHandler(ChannelServiceHandler):
@@ -30,7 +32,7 @@ def __init__(
3032
self,
3133
adapter: BotAdapter,
3234
bot: Bot,
33-
conversation_id_factory: SkillConversationIdFactory,
35+
conversation_id_factory: ConversationIdFactoryBase,
3436
credential_provider: CredentialProvider,
3537
auth_configuration: AuthenticationConfiguration,
3638
channel_provider: ChannelProvider = None,
@@ -118,14 +120,29 @@ async def _process_activity(
118120
reply_to_activity_id: str,
119121
activity: Activity,
120122
) -> ResourceResponse:
121-
conversation_reference = await self._conversation_id_factory.get_conversation_reference(
123+
conversation_reference_result = await self._conversation_id_factory.get_conversation_reference(
122124
conversation_id
123125
)
124126

127+
oauth_scope = None
128+
conversation_reference = None
129+
if isinstance(conversation_reference_result, SkillConversationReference):
130+
oauth_scope = conversation_reference_result.oauth_scope
131+
conversation_reference = (
132+
conversation_reference_result.conversation_reference
133+
)
134+
else:
135+
conversation_reference = conversation_reference_result
136+
oauth_scope = (
137+
GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
138+
if self._channel_provider and self._channel_provider.is_government()
139+
else AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
140+
)
141+
125142
if not conversation_reference:
126143
raise KeyError("ConversationReference not found")
127144

128-
skill_conversation_reference = ConversationReference(
145+
activity_conversation_reference = ConversationReference(
129146
activity_id=activity.id,
130147
user=activity.from_property,
131148
bot=activity.recipient,
@@ -137,7 +154,7 @@ async def _process_activity(
137154
async def callback(context: TurnContext):
138155
context.turn_state[
139156
SkillHandler.SKILL_CONVERSATION_REFERENCE_KEY
140-
] = skill_conversation_reference
157+
] = activity_conversation_reference
141158
TurnContext.apply_conversation_reference(activity, conversation_reference)
142159
context.activity.id = reply_to_activity_id
143160

@@ -154,7 +171,10 @@ async def callback(context: TurnContext):
154171
await context.send_activity(activity)
155172

156173
await self._adapter.continue_conversation(
157-
conversation_reference, callback, claims_identity=claims_identity
174+
conversation_reference,
175+
callback,
176+
claims_identity=claims_identity,
177+
audience=oauth_scope,
158178
)
159179
return ResourceResponse(id=str(uuid4()))
160180

libraries/botbuilder-core/tests/skills/test_skill_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class ConversationIdFactoryForTest(ConversationIdFactoryBase):
3434
def __init__(self):
3535
self._conversation_refs: Dict[str, str] = {}
3636

37-
async def create_skill_conversation_id(
37+
async def create_skill_conversation_id( # pylint: disable=W0221
3838
self, conversation_reference: ConversationReference
3939
) -> str:
4040
cr_json = json.dumps(conversation_reference.serialize())

0 commit comments

Comments
 (0)
0