#40 - ensure return is a JSON with 'detail' field in case of the error

This commit is contained in:
Stanislav Hejny
2025-05-05 15:55:47 +01:00
parent 687b134326
commit d7b21af028
4 changed files with 79 additions and 22 deletions
+26 -10
View File
@@ -24,7 +24,7 @@ from amqp.adapter.data_response_factory import DataResponseFactory
from amqp.adapter.file_handler import FileHandler, StreamingFileHandler from amqp.adapter.file_handler import FileHandler, StreamingFileHandler
from amqp.adapter.logging_utils import logging_error, logging_debug, logging_info from amqp.adapter.logging_utils import logging_error, logging_debug, logging_info
from amqp.config.amq_configuration import AMQConfiguration from amqp.config.amq_configuration import AMQConfiguration
from amqp.model.model import DataMessage, DataResponse from amqp.model.model import DataMessage, DataResponse, AMQErrorMessage
from amqp.router.router_base import AMQ_REPLY_TO_EXCHANGE from amqp.router.router_base import AMQ_REPLY_TO_EXCHANGE
from amqp.router.utils import await_result from amqp.router.utils import await_result
@@ -208,8 +208,10 @@ class CleverThisServiceAdapter:
else: else:
raise ValueError(f"Unexpected HTTP method: {message.method}") raise ValueError(f"Unexpected HTTP method: {message.method}")
else: else:
_amq_response = DataResponseFactory.of( _amq_response = DataResponseFactory.of_error_message(
message.id, 401, "text/plain", b"Unauthorized", message.trace_info message.id,
AMQErrorMessage(401, "Unauthorized", "Unauthorized"),
message.trace_info,
) )
logging_info( logging_info(
f"ALL DONE in on_message id:{message.id}, resp code:{_amq_response.response_code}" f"ALL DONE in on_message id:{message.id}, resp code:{_amq_response.response_code}"
@@ -244,11 +246,9 @@ class CleverThisServiceAdapter:
await _http_resp.read(), await _http_resp.read(),
message.trace_info, message.trace_info,
) )
return DataResponseFactory.of( return DataResponseFactory.of_error_message(
message.id, message.id,
404, AMQErrorMessage(404, "Not Found", "Destination location does not exists"),
"text/plain",
str("Destination location does not exists").encode("utf-8"),
message.trace_info, message.trace_info,
) )
@@ -399,10 +399,18 @@ class CleverThisServiceAdapter:
message.trace_info, message.trace_info,
) )
except HTTPException as http_error: except HTTPException as http_error:
logging_error(f"Service REST endpoint invocation failed: {http_error}")
_content = str(http_error.detail) _content = str(http_error.detail)
if not is_likely_json(_content): if not is_likely_json(_content):
_content = '{"error": "' + _content + '"}' return DataResponseFactory.of_error_message(
logging_error(f"Service REST endpoint invocation failed: {http_error}") message.id,
AMQErrorMessage(
http_error.status_code,
"Service Invocation Failed",
_content,
),
message.trace_info,
)
return DataResponseFactory.of( return DataResponseFactory.of(
message.id, message.id,
http_error.status_code, http_error.status_code,
@@ -414,7 +422,15 @@ class CleverThisServiceAdapter:
logging_error(f"Service endpoint invocation failed: {error}") logging_error(f"Service endpoint invocation failed: {error}")
_content = str(error) _content = str(error)
if not is_likely_json(_content): if not is_likely_json(_content):
_content = '{"error": "' + _content + '"}' return DataResponseFactory.of_error_message(
message.id,
AMQErrorMessage(
500,
"Service Invocation Failed",
_content,
),
message.trace_info,
)
return DataResponseFactory.of( return DataResponseFactory.of(
message.id, message.id,
500, 500,
+15 -1
View File
@@ -5,7 +5,7 @@ method to create, serialize and deserialize DataResponse message
import base64 import base64
import re import re
from io import BytesIO from io import BytesIO
from amqp.model.model import DataResponse from amqp.model.model import DataResponse, AMQErrorMessage
from amqp.model.snowflake_id import SnowflakeId from amqp.model.snowflake_id import SnowflakeId
from amqp.adapter.serializer import parse_map_string, map_as_string from amqp.adapter.serializer import parse_map_string, map_as_string
@@ -51,6 +51,20 @@ class DataResponseFactory:
trace_info=trace_info, trace_info=trace_info,
) )
@staticmethod
def of_error_message(
id: SnowflakeId,
error_message: AMQErrorMessage,
trace_info: dict[str, str],
) -> DataResponse:
return DataResponseFactory.of(
id,
error_message.response_code,
error_message.content_type,
error_message.serialze(),
trace_info,
)
@staticmethod @staticmethod
def serialize(response: DataResponse) -> bytes: def serialize(response: DataResponse) -> bytes:
""" """
+27
View File
@@ -305,3 +305,30 @@ class ScalingRequest:
data["requestType"] data["requestType"]
), # Convert string to Enum ), # Convert string to Enum
) )
class AMQErrorMessage:
"""
The error response message to RPC style call (note: the request is made with DataMessage)
"""
def __init__(
self,
response_code: int,
error: str,
detail: str,
content_type: str = "application/json",
):
self.response_code = response_code
self.error = error
self.detail = detail
self.content_type = content_type
def serialze(self) -> bytes:
return json.dumps(
{
"response_code": self.response_code,
"error": self.error,
"detail": self.detail,
}
).encode("utf-8")
+11 -11
View File
@@ -26,7 +26,7 @@ from amqp.adapter.data_parser import AMQDataParser
from amqp.adapter.data_response_factory import AMQResponseFactory from amqp.adapter.data_response_factory import AMQResponseFactory
from amqp.adapter.cleverthis_service_adapter import CleverThisServiceAdapter, AMQMessage from amqp.adapter.cleverthis_service_adapter import CleverThisServiceAdapter, AMQMessage
from amqp.config.amq_configuration import AMQConfiguration from amqp.config.amq_configuration import AMQConfiguration
from amqp.model.model import AMQResponse from amqp.model.model import AMQResponse, AMQErrorMessage
from amqp.service.amq_service import AMQService from amqp.service.amq_service import AMQService
@@ -220,12 +220,12 @@ class CleverSwarmAmqpAdapter(CleverThisServiceAdapter):
loop=self.loop, loop=self.loop,
) )
return AMQResponseFactory.of( return AMQResponseFactory.of_error_message(
message.id, message.id,
404, AMQErrorMessage(
"text/plain", 404,
str(f"Destination location [{message.path}] does not exists").encode( "Not Found",
"utf-8" f"Destination location [{message.path}] does not exists",
), ),
message.trace_info, message.trace_info,
) )
@@ -293,12 +293,12 @@ class CleverSwarmAmqpAdapter(CleverThisServiceAdapter):
loop=self.loop, loop=self.loop,
) )
return AMQResponseFactory.of( return AMQResponseFactory.of_error_message(
message.id, message.id,
404, AMQErrorMessage(
"text/plain", 404,
str(f"Destination location [{message.path}] does not exists").encode( "Not Found",
"utf-8" f"Destination location [{message.path}] does not exists",
), ),
message.trace_info, message.trace_info,
) )