restructure to fit around python event-loop
This commit is contained in:
@@ -9,12 +9,12 @@ import logging
|
||||
import sys
|
||||
import traceback
|
||||
import pathlib
|
||||
from asyncio import Future
|
||||
from asyncio import Future, AbstractEventLoop
|
||||
from typing import Dict, List, Tuple, Callable, Coroutine, Any
|
||||
from urllib.parse import parse_qs, urlparse
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from aio_pika.abc import AbstractRobustChannel
|
||||
from aio_pika.abc import AbstractRobustChannel, AbstractRobustConnection, AbstractRobustExchange, AbstractExchange
|
||||
from aiohttp import ClientSession, ClientResponse
|
||||
from dataclasses import dataclass
|
||||
from fastapi import HTTPException
|
||||
@@ -28,6 +28,8 @@ from amqp.adapter.file_uploader import publish_file_to_rabbitmq
|
||||
|
||||
from IPython.core import debugger
|
||||
|
||||
from amqp.router.router_base import AMQ_REPLY_TO_EXCHANGE
|
||||
|
||||
debug = debugger.Pdb().set_trace
|
||||
|
||||
|
||||
@@ -38,7 +40,7 @@ class AMQMessage(DataMessage):
|
||||
in case the response should initiate file download.
|
||||
"""
|
||||
|
||||
def __init__(self, data_message: DataMessage, channel: AbstractRobustChannel):
|
||||
def __init__(self, data_message: DataMessage, connection: AbstractRobustConnection):
|
||||
# Copy all fields from DataMessage
|
||||
self.magic = data_message.magic
|
||||
self.id = data_message.id
|
||||
@@ -49,7 +51,7 @@ class AMQMessage(DataMessage):
|
||||
self.trace_info = data_message.trace_info
|
||||
self.base64_body = data_message.base64_body
|
||||
# Add the new field
|
||||
self.channel = channel
|
||||
self.connection = connection
|
||||
|
||||
|
||||
def parse_request_body(message: DataMessage):
|
||||
@@ -115,11 +117,35 @@ def parse_request_body(message: DataMessage):
|
||||
raise ValueError(f"Unsupported Content-Type: {content_type}")
|
||||
|
||||
|
||||
async def convert_file_response(obj: FileResponse, channel: AbstractRobustChannel) -> str:
|
||||
async def convert_file_response(
|
||||
obj: FileResponse,
|
||||
connection: AbstractRobustConnection,
|
||||
loop: AbstractEventLoop | None
|
||||
) -> str:
|
||||
"""
|
||||
Converts a FileResponse object to a JSON string containing the filename and stream.
|
||||
"""
|
||||
(_stream_name, _file_size) = await publish_file_to_rabbitmq(channel, obj.path)
|
||||
async def wrap_connection_channel(connection: AbstractRobustConnection) -> tuple[AbstractRobustChannel, AbstractExchange, str]:
|
||||
""" Wrap call to channel() to make it Awaitable """
|
||||
_channel = await connection.channel()
|
||||
exchange_name: str = AMQ_REPLY_TO_EXCHANGE
|
||||
_exchange = await _channel.get_exchange(name=exchange_name, ensure=True)
|
||||
_queue = await _channel.declare_queue(exclusive=True) # Declare a unique, exclusive queue
|
||||
_queue_name = _queue.name
|
||||
await _queue.bind(
|
||||
exchange=exchange_name,
|
||||
routing_key=_queue_name, # Use queue name as routing key
|
||||
)
|
||||
|
||||
logging.debug(f" [*] Publishing to exchange: {exchange_name}, q: {_queue_name}")
|
||||
return _channel, _exchange, _queue_name
|
||||
|
||||
_future = asyncio.run_coroutine_threadsafe(wrap_connection_channel(connection), loop)
|
||||
while not _future.done():
|
||||
await asyncio.sleep(0.1) # allow coroutine to execute
|
||||
_channel, _exchange, _routing_key = _future.result()
|
||||
(_stream_name, _file_size) = await publish_file_to_rabbitmq(_exchange, obj.path, _routing_key, loop)
|
||||
await _channel.close()
|
||||
return json.dumps({'files': [
|
||||
{'filename': obj.filename,
|
||||
'mediaType': 'application/octet-stream',
|
||||
@@ -133,7 +159,8 @@ async def call_cleverthis_api(
|
||||
message: AMQMessage,
|
||||
args: Any,
|
||||
api_method: Callable[[Any, Any], Coroutine],
|
||||
success_code: int
|
||||
success_code: int,
|
||||
loop: AbstractEventLoop | None = None
|
||||
) -> DataResponse:
|
||||
"""
|
||||
Handles POST and PUT requests, which typically contain possibly complex payload data, passed as a dictionary.
|
||||
@@ -162,7 +189,7 @@ async def call_cleverthis_api(
|
||||
|
||||
if isinstance(_result, FileResponse):
|
||||
_json_result, _content_type = await convert_file_response(
|
||||
_result, message.channel
|
||||
_result, message.connection, loop
|
||||
), 'application/octet-stream'
|
||||
else:
|
||||
_json_result = _result
|
||||
@@ -224,6 +251,7 @@ class CleverThisServiceAdapter:
|
||||
self.session = session
|
||||
self.service_host = self.amq_configuration.dispatch.service_host
|
||||
self.service_port = self.amq_configuration.dispatch.service_port
|
||||
self.loop: AbstractEventLoop | None = None
|
||||
self.require_authenticated_user = eval(
|
||||
self.amq_configuration.amq_adapter.require_authenticated_user.capitalize()) if self.amq_configuration.amq_adapter.require_authenticated_user.strip() else True
|
||||
|
||||
@@ -247,7 +275,7 @@ class CleverThisServiceAdapter:
|
||||
"""
|
||||
return None
|
||||
|
||||
async def on_message(self, message: AMQMessage, future: Future[DataResponse] = None) -> DataResponse:
|
||||
async def on_message(self, message: AMQMessage) -> DataResponse:
|
||||
"""
|
||||
Called when a message is received from the AMQP.
|
||||
:param message:
|
||||
@@ -292,9 +320,6 @@ class CleverThisServiceAdapter:
|
||||
raise ValueError(f"Unexpected HTTP method: {message.method}")
|
||||
else:
|
||||
amq_response = DataResponseFactory.of(message.id, 401, "text/plain", b"Unauthorized", message.trace_info)
|
||||
|
||||
if future:
|
||||
future.set_result(amq_response)
|
||||
return amq_response
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
Reference in New Issue
Block a user