Commit a168a012 by Oleksandr Barabash

updates on structures, error handlers added

parent 761d2bd9
""" Message extension bot """
import asyncio
import uuid
from asyncio import Future
from typing import Optional
from urllib.parse import urlparse, parse_qsl, urlencode
......@@ -16,12 +17,11 @@ from marshmallow import EXCLUDE
from bots.exceptions import ConversationNotFound
from config import TaskModuleConfig, AppConfig
from entities.json.acknowledge import Acknowledge
from entities.json.conversation_reference_schema import (
ConversationReferenceSchema
)
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.cosmos_client import CosmosClient, ItemNotFound
from utils.function import get_first_or_none
......@@ -83,23 +83,31 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
future = Future()
# reset parameters
notification.id = None
notification.id = uuid.uuid4().__str__()
notification.tenant_id = AppConfig.TENANT_ID
async def routine():
""" async routine """
_saved_notification = await self.cosmos_client.create_notification(
notification
try:
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:
""" Turn Context callback. Kinda awful syntax, I know """
# TODO(s1z): Add exception handler
# (like conversation not found etc...)
try:
card = CardHelper.create_notification_card(notification)
card = CardHelper.create_notification_card(
await self.cosmos_client.create_notification(
notification
)
)
attachments = [CardFactory.adaptive_card(card)]
message = Activity(type=ActivityTypes.message,
attachments=attachments)
......@@ -122,27 +130,9 @@ class TeamsMessagingExtensionsActionPreviewBot(TeamsActivityHandler):
# noinspection PyProtectedMember
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):
""" On update conversation """
print("activity:", turn_context.activity)
await self.cosmos_client.create_conversation_reference(turn_context)
async def handle_submit_action(self, turn_context: TurnContext) -> None:
......
......@@ -24,16 +24,13 @@ class AppConfig:
""" Bot Configuration """
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",
"5df91ebc-64fa-4aa1-862c-bdc0cba3c656")
APP_PASSWORD = os.environ.get("MS_APP_PASSWORD",
"ahO8Q~tSDvGJUcfN8ACZ51e21hh411vLwkal_dv.")
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:
......
""" 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
from entities.json.acknowledge import Acknowledge
from entities.json.acknowledge_schema import AcknowledgeSchema
from entities.json.camel_case_mixin import timestamp_factory
from entities.json.conversation_reference import ConversationReference
from entities.json.conversation_reference_schema import \
ConversationReferenceSchema
from entities.json.initiation import Initiation
......@@ -188,7 +189,6 @@ class CosmosClient:
body.update(dict(id=uuid.uuid4().__str__()))
while tries < max_tries:
try:
return await self.execute_blocking(bl)
except exceptions.CosmosHttpResponseError as e:
......@@ -282,6 +282,17 @@ class CosmosClient:
except ItemNotFound:
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)\
-> NotificationCosmos:
""" Get Notification """
......@@ -299,15 +310,23 @@ class CosmosClient:
activity = turn_context.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()
reference_dict.update({
reference_json.update({
CosmosDBConfig.Conversations.PK: reference.conversation.id
})
def bl() -> Dict[str, str]:
""" Potential blocking code """
return container.create_item(body=reference_json)
try:
await self.create_item(container, body=reference_dict, max_tries=1)
except ItemExists:
pass
return await self.execute_blocking(bl)
except exceptions.CosmosHttpResponseError as e:
if e.status_code == 409: # Already exists
return
raise SaveItemError(e.http_error_message)
async def create_initiation(self, initiator: str,
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