Commit a168a012 by Oleksandr Barabash

updates on structures, error handlers added

parent 761d2bd9
""" Message extension bot """ """ Message extension bot """
import asyncio import asyncio
import uuid
from asyncio import Future from asyncio import Future
from typing import Optional from typing import Optional
from urllib.parse import urlparse, parse_qsl, urlencode from urllib.parse import urlparse, parse_qsl, urlencode
...@@ -16,12 +17,11 @@ from marshmallow import EXCLUDE ...@@ -16,12 +17,11 @@ from marshmallow import EXCLUDE
from bots.exceptions import ConversationNotFound from bots.exceptions import ConversationNotFound
from config import TaskModuleConfig, AppConfig from config import TaskModuleConfig, AppConfig
from entities.json.acknowledge import Acknowledge
from entities.json.conversation_reference_schema import ( from entities.json.conversation_reference_schema import (
ConversationReferenceSchema ConversationReferenceSchema
) )
from entities.json.medx import MedX, MXTypes from entities.json.medx import MedX, MXTypes
from entities.json.notification import Notification, NotificationCosmos from entities.json.notification import NotificationCosmos
from utils.card_helper import CardHelper from utils.card_helper import CardHelper
from utils.cosmos_client import CosmosClient, ItemNotFound from utils.cosmos_client import CosmosClient, ItemNotFound
from utils.function import get_first_or_none from utils.function import get_first_or_none
...@@ -83,23 +83,31 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -83,23 +83,31 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
future = Future() future = Future()
# reset parameters # reset parameters
notification.id = None notification.id = uuid.uuid4().__str__()
notification.tenant_id = AppConfig.TENANT_ID notification.tenant_id = AppConfig.TENANT_ID
async def routine(): async def routine():
""" async routine """ """ async routine """
_saved_notification = await self.cosmos_client.create_notification( try:
notification reference = await self.cosmos_client.get_conversation(
) notification.destination
destination = notification.destination )
reference = await self.get_conversation_reference(destination) except ItemNotFound:
future.set_exception(ConversationNotFound("not found"))
except Exception as e:
future.set_exception(e)
return
async def callback(turn_context: TurnContext) -> None: async def callback(turn_context: TurnContext) -> None:
""" Turn Context callback. Kinda awful syntax, I know """ """ Turn Context callback. Kinda awful syntax, I know """
# TODO(s1z): Add exception handler # TODO(s1z): Add exception handler
# (like conversation not found etc...) # (like conversation not found etc...)
try: try:
card = CardHelper.create_notification_card(notification) card = CardHelper.create_notification_card(
await self.cosmos_client.create_notification(
notification
)
)
attachments = [CardFactory.adaptive_card(card)] attachments = [CardFactory.adaptive_card(card)]
message = Activity(type=ActivityTypes.message, message = Activity(type=ActivityTypes.message,
attachments=attachments) attachments=attachments)
...@@ -122,27 +130,9 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler): ...@@ -122,27 +130,9 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
# noinspection PyProtectedMember # noinspection PyProtectedMember
return parsed_url._replace(query=urlencode(params)).geturl() return parsed_url._replace(query=urlencode(params)).geturl()
# noinspection SqlNoDataSourceInspection,SqlDialectInspection
async def get_conversation_reference(self, conversation_id: Optional[str])\
-> ConversationReference:
""" Get conversation reference from DB """
if conversation_id is None:
raise ConversationNotFound("conversation_id is None")
container = await self.cosmos_client.get_conversations_container()
tenant_id = AppConfig.TENANT_ID
item = get_first_or_none(list(container.query_items(
query=f"SELECT * FROM c WHERE c.id=@id",
partition_key=tenant_id,
parameters=[{"name": "@id",
"value": conversation_id}],
)))
if item is None:
raise ConversationNotFound("conversation_id is not Found")
return ConversationReferenceSchema(unknown=EXCLUDE).load(item)
async def on_conversation_update_activity(self, turn_context: TurnContext): async def on_conversation_update_activity(self, turn_context: TurnContext):
""" On update conversation """ """ On update conversation """
print("activity:", turn_context.activity)
await self.cosmos_client.create_conversation_reference(turn_context) await self.cosmos_client.create_conversation_reference(turn_context)
async def handle_submit_action(self, turn_context: TurnContext) -> None: async def handle_submit_action(self, turn_context: TurnContext) -> None:
......
...@@ -24,16 +24,13 @@ class AppConfig: ...@@ -24,16 +24,13 @@ class AppConfig:
""" Bot Configuration """ """ Bot Configuration """
PORT = os.environ.get("HOST_PORT", 8000) PORT = os.environ.get("HOST_PORT", 8000)
# APP_ID = os.environ.get("MS_APP_ID",
# "d472f12a-323b-4058-b89b-7a4b15c48ab7")
# APP_PASSWORD = os.environ.get("MS_APP_PASSWORD",
# "ZdL|zw:]Io_}goFfWs2{w70g+.FXwQ")
TENANT_ID = os.environ.get("TENANT_ID", TENANT_ID = os.environ.get("TENANT_ID",
"5df91ebc-64fa-4aa1-862c-bdc0cba3c656") "5df91ebc-64fa-4aa1-862c-bdc0cba3c656")
APP_PASSWORD = os.environ.get("MS_APP_PASSWORD",
"ahO8Q~tSDvGJUcfN8ACZ51e21hh411vLwkal_dv.")
APP_ID = os.environ.get("MS_APP_ID", APP_ID = os.environ.get("MS_APP_ID",
"82109577-846a-442d-9320-776148d77442") "34b032df-9532-48f8-a8a1-0e864f9e5562")
APP_PASSWORD = os.environ.get("MS_APP_PASSWORD",
"7Ll8Q~XeV3D8vNmM3Q4BNyxYUcMrM1SQtghOndxT")
class CosmosDBConfig: class CosmosDBConfig:
......
""" Conversation reference object """
from dataclasses import dataclass, field
from typing import Optional, Any, Dict
from botbuilder.schema import (
ConversationReference as MSConversationReference, ConversationAccount,
ChannelAccount
)
from entities.json.camel_case_mixin import CamelCaseMixin, timestamp_factory
@dataclass
class Account(CamelCaseMixin):
""" Account Channel object """
id: str
# This is a bullshit, microsoft sends null name!
name: Optional[str] = field(default=None)
# may be null if it's a bot
aad_object_id: Optional[str] = field(default=None)
role: Optional[str] = field(default=None)
@dataclass
class Conversation(CamelCaseMixin):
""" Conversation URL """
id: str
tenant_id: str
conversation_type: str = field(default=None)
is_group: Optional[bool] = field(default=None)
name: Optional[str] = field(default=None)
aad_object_id: Optional[str] = field(default=None)
role: Optional[str] = field(default=None)
# Microsoft does not describe this object at all
properties: Optional[Dict[str, Any]] = field(default=None)
@dataclass
class ConversationReference(CamelCaseMixin):
""" Conversation Reference schema """
bot: Optional[Account]
conversation: Conversation
channel_id: str
service_url: str
# has to be not null but MS sends null
locale: Optional[str] = field(default=None)
activity_id: Optional[str] = field(default=None)
user: Optional[Account] = field(default=None)
def to_ms_reference(self) -> MSConversationReference:
""" Convert dataclass to microsoft conversation reference object """
return MSConversationReference(
activity_id=self.activity_id,
channel_id=self.channel_id,
locale=self.locale,
service_url=self.service_url,
user=ChannelAccount(
id=self.user.id,
name=self.user.name,
aad_object_id=self.user.aad_object_id,
role=self.user.role
),
bot=ChannelAccount(
id=self.bot.id,
name=self.bot.name,
aad_object_id=self.bot.aad_object_id,
role=self.bot.role
),
conversation=ConversationAccount(
is_group=self.conversation.is_group,
conversation_type=self.conversation.conversation_type,
id=self.conversation.id,
name=self.conversation.name,
aad_object_id=self.conversation.aad_object_id,
role=self.conversation.role,
tenant_id=self.conversation.tenant_id,
properties=self.conversation.properties
)
)
...@@ -15,6 +15,7 @@ from marshmallow import EXCLUDE ...@@ -15,6 +15,7 @@ from marshmallow import EXCLUDE
from entities.json.acknowledge import Acknowledge from entities.json.acknowledge import Acknowledge
from entities.json.acknowledge_schema import AcknowledgeSchema from entities.json.acknowledge_schema import AcknowledgeSchema
from entities.json.camel_case_mixin import timestamp_factory from entities.json.camel_case_mixin import timestamp_factory
from entities.json.conversation_reference import ConversationReference
from entities.json.conversation_reference_schema import \ from entities.json.conversation_reference_schema import \
ConversationReferenceSchema ConversationReferenceSchema
from entities.json.initiation import Initiation from entities.json.initiation import Initiation
...@@ -188,7 +189,6 @@ class CosmosClient: ...@@ -188,7 +189,6 @@ class CosmosClient:
body.update(dict(id=uuid.uuid4().__str__())) body.update(dict(id=uuid.uuid4().__str__()))
while tries < max_tries: while tries < max_tries:
try: try:
return await self.execute_blocking(bl) return await self.execute_blocking(bl)
except exceptions.CosmosHttpResponseError as e: except exceptions.CosmosHttpResponseError as e:
...@@ -282,6 +282,17 @@ class CosmosClient: ...@@ -282,6 +282,17 @@ class CosmosClient:
except ItemNotFound: except ItemNotFound:
return None return None
async def get_conversation(self, conversation_id: str)\
-> ConversationReference:
""" Get Conversation Reference """
from config import AppConfig
container = await self.get_conversations_container()
item = await self.get_item(container, conversation_id,
AppConfig.TENANT_ID)
return ConversationReference.get_schema(unknown=EXCLUDE)\
.load(item).to_ms_reference()
async def get_notification(self, notification_id: str)\ async def get_notification(self, notification_id: str)\
-> NotificationCosmos: -> NotificationCosmos:
""" Get Notification """ """ Get Notification """
...@@ -299,15 +310,23 @@ class CosmosClient: ...@@ -299,15 +310,23 @@ class CosmosClient:
activity = turn_context.activity activity = turn_context.activity
reference = TurnContext.get_conversation_reference(activity) reference = TurnContext.get_conversation_reference(activity)
reference_dict = ConversationReferenceSchema().dump(reference) reference_json = ConversationReference.get_schema().dump(reference)
# reference_dict = ConversationReferenceSchema().dump(reference)
container = await self.get_conversations_container() container = await self.get_conversations_container()
reference_dict.update({ reference_json.update({
CosmosDBConfig.Conversations.PK: reference.conversation.id CosmosDBConfig.Conversations.PK: reference.conversation.id
}) })
def bl() -> Dict[str, str]:
""" Potential blocking code """
return container.create_item(body=reference_json)
try: try:
await self.create_item(container, body=reference_dict, max_tries=1) return await self.execute_blocking(bl)
except ItemExists: except exceptions.CosmosHttpResponseError as e:
pass if e.status_code == 409: # Already exists
return
raise SaveItemError(e.http_error_message)
async def create_initiation(self, initiator: str, async def create_initiation(self, initiator: str,
notification_id: str) -> None: notification_id: str) -> None:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment