Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DaprInternalError.as_json_safe_dict for actors #765

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions dapr/clients/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
See the License for the specific language governing permissions and
limitations under the License.
"""
import base64
import json
from typing import Optional

Expand Down Expand Up @@ -44,6 +45,17 @@ def as_dict(self):
'raw_response_bytes': self._raw_response_bytes,
}

def as_json_safe_dict(self):
error_dict = self.as_dict()

if self._raw_response_bytes is not None:
# Encode bytes to base64 for JSON compatibility
error_dict['raw_response_bytes'] = base64.b64encode(self._raw_response_bytes).decode(
'utf-8'
)

return error_dict


class StatusDetails:
def __init__(self):
Expand Down
15 changes: 7 additions & 8 deletions ext/dapr-ext-fastapi/dapr/ext/fastapi/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@

from typing import Any, Optional, Type, List

from dapr.actor import Actor, ActorRuntime
from dapr.clients.exceptions import ERROR_CODE_UNKNOWN, DaprInternalError
from dapr.serializers import DefaultJSONSerializer
from fastapi import FastAPI, APIRouter, Request, Response, status # type: ignore
from fastapi.logger import logger
from fastapi.responses import JSONResponse

from dapr.actor import Actor, ActorRuntime
from dapr.clients.exceptions import DaprInternalError, ERROR_CODE_UNKNOWN
from dapr.serializers import DefaultJSONSerializer

DEFAULT_CONTENT_TYPE = 'application/json; utf-8'
DAPR_REENTRANCY_ID_HEADER = 'Dapr-Reentrancy-Id'

Expand Down Expand Up @@ -72,7 +71,7 @@ async def actor_deactivation(actor_type_name: str, actor_id: str):
try:
await ActorRuntime.deactivate(actor_type_name, actor_id)
except DaprInternalError as ex:
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_dict())
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_json_safe_dict())
except Exception as ex:
return _wrap_response(
status.HTTP_500_INTERNAL_SERVER_ERROR, repr(ex), ERROR_CODE_UNKNOWN
Expand All @@ -96,7 +95,7 @@ async def actor_method(
actor_type_name, actor_id, method_name, req_body, reentrancy_id
)
except DaprInternalError as ex:
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_dict())
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_json_safe_dict())
except Exception as ex:
return _wrap_response(
status.HTTP_500_INTERNAL_SERVER_ERROR, repr(ex), ERROR_CODE_UNKNOWN
Expand All @@ -117,7 +116,7 @@ async def actor_timer(
req_body = await request.body()
await ActorRuntime.fire_timer(actor_type_name, actor_id, timer_name, req_body)
except DaprInternalError as ex:
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_dict())
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_json_safe_dict())
except Exception as ex:
return _wrap_response(
status.HTTP_500_INTERNAL_SERVER_ERROR, repr(ex), ERROR_CODE_UNKNOWN
Expand All @@ -139,7 +138,7 @@ async def actor_reminder(
req_body = await request.body()
await ActorRuntime.fire_reminder(actor_type_name, actor_id, reminder_name, req_body)
except DaprInternalError as ex:
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_dict())
return _wrap_response(status.HTTP_500_INTERNAL_SERVER_ERROR, ex.as_json_safe_dict())
except Exception as ex:
return _wrap_response(
status.HTTP_500_INTERNAL_SERVER_ERROR, repr(ex), ERROR_CODE_UNKNOWN
Expand Down
10 changes: 5 additions & 5 deletions ext/flask_dapr/flask_dapr/actor.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from flask import jsonify, make_response, request

from dapr.actor import Actor, ActorRuntime
from dapr.clients.exceptions import DaprInternalError, ERROR_CODE_UNKNOWN
from dapr.clients.exceptions import ERROR_CODE_UNKNOWN, DaprInternalError
from dapr.serializers import DefaultJSONSerializer

DEFAULT_CONTENT_TYPE = 'application/json; utf-8'
Expand Down Expand Up @@ -80,7 +80,7 @@ def _deactivation_handler(self, actor_type_name, actor_id):
try:
asyncio.run(ActorRuntime.deactivate(actor_type_name, actor_id))
except DaprInternalError as ex:
return wrap_response(500, ex.as_dict())
return wrap_response(500, ex.as_json_safe_dict())
except Exception as ex:
return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

Expand All @@ -99,7 +99,7 @@ def _method_handler(self, actor_type_name, actor_id, method_name):
)
)
except DaprInternalError as ex:
return wrap_response(500, ex.as_dict())
return wrap_response(500, ex.as_json_safe_dict())
except Exception as ex:
return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

Expand All @@ -113,7 +113,7 @@ def _timer_handler(self, actor_type_name, actor_id, timer_name):
req_body = request.stream.read()
asyncio.run(ActorRuntime.fire_timer(actor_type_name, actor_id, timer_name, req_body))
except DaprInternalError as ex:
return wrap_response(500, ex.as_dict())
return wrap_response(500, ex.as_json_safe_dict())
except Exception as ex:
return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

Expand All @@ -129,7 +129,7 @@ def _reminder_handler(self, actor_type_name, actor_id, reminder_name):
ActorRuntime.fire_reminder(actor_type_name, actor_id, reminder_name, req_body)
)
except DaprInternalError as ex:
return wrap_response(500, ex.as_dict())
return wrap_response(500, ex.as_json_safe_dict())
except Exception as ex:
return wrap_response(500, repr(ex), ERROR_CODE_UNKNOWN)

Expand Down
31 changes: 30 additions & 1 deletion tests/clients/test_exceptions.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import base64
import json
import unittest

import grpc
Expand All @@ -6,7 +8,7 @@
from google.protobuf.duration_pb2 import Duration

from dapr.clients import DaprGrpcClient
from dapr.clients.exceptions import DaprGrpcError
from dapr.clients.exceptions import DaprGrpcError, DaprInternalError
from dapr.conf import settings

from .fake_dapr_server import FakeDaprSidecar
Expand Down Expand Up @@ -216,3 +218,30 @@ def test_error_code(self):
dapr_error = context.exception

self.assertEqual(dapr_error.error_code(), 'UNKNOWN')

def test_dapr_internal_error_as_json_safe_dict_no_bytes(self):
message = 'Test DaprInternalError.as_json_safe_dict with no raw bytes'
dapr_error = DaprInternalError(message=message)

safe_dict = dapr_error.as_json_safe_dict()
self.assertEqual(safe_dict['message'], message)
self.assertEqual(safe_dict['errorCode'], 'UNKNOWN')
self.assertIsNone(safe_dict['raw_response_bytes'])

# Also check that the safe dict can be serialised to JSON
_ = json.dumps(safe_dict)

def test_dapr_internal_error_as_json_safe_dict_bytes_are_encoded(self):
message = 'Test DaprInternalError.as_json_safe_dict with encoded raw bytes'
raw_bytes = message.encode('utf-8')
dapr_error = DaprInternalError(message=message, raw_response_bytes=raw_bytes)

safe_dict = dapr_error.as_json_safe_dict()
self.assertEqual(safe_dict['message'], message)
self.assertEqual(safe_dict['errorCode'], 'UNKNOWN')

decoded_bytes = base64.b64decode(safe_dict['raw_response_bytes'])
self.assertEqual(decoded_bytes, raw_bytes)

# Also check that the safe dict can be serialised to JSON
_ = json.dumps(safe_dict)
Loading