Skip to content

Commit

Permalink
Added new exception types, added utility classes, added some test cas…
Browse files Browse the repository at this point in the history
…es for userApi
  • Loading branch information
alexbalakirev committed Apr 17, 2024
1 parent b4ef150 commit 54a8ec1
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 35 deletions.
6 changes: 5 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ per-file-ignores =
tests/*:S101,
#no docstrings
DC101,
DC102
DC102,
DAR201,
DAR101,
#ignore random generators
S311,
2 changes: 1 addition & 1 deletion src/corbado_python_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
from .config import Config
from .corbado_sdk import CorbadoSDK
from .services import *
from .util import *
from .utils import *
4 changes: 2 additions & 2 deletions src/corbado_python_sdk/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
class Config(BaseModel):
"""Configuration class"""

# Make sure that field assignments are also validated, use "config.model_config["validate_assignment"] =
# False" to be able to use invalid assignments
# Make sure that field assignments are also validated, use "set_assignment_validation(False)"
# to be able to use invalid assignments
model_config = ConfigDict(validate_assignment=True)

# Fields
Expand Down
2 changes: 2 additions & 0 deletions src/corbado_python_sdk/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .server_exception import ServerException
from .standard_exception import StandardException
43 changes: 43 additions & 0 deletions src/corbado_python_sdk/exceptions/server_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Any, Dict, List, Optional


class ServerException(Exception):
"""
Custom exception class for server-related errors.
"""

def __init__(
self,
http_status_code: int,
message: str,
request_data: Optional[Dict[str, Any]] = None,
runtime: Optional[float] = None,
error: Optional[Dict[str, Any]] = None,
) -> None:
"""Initialize ServerException with specific attributes.
Args:
http_status_code (int): HTTP status code associated with the error
message (str): Error message
request_data (Optional[Dict[str, Any]], optional): Additional data associated with the request (optional)
runtime (Optional[float], optional): Runtime information (optional). Defaults to None.
error (Optional[Dict[str, Any]], optional): Specific error information (optional). Defaults to None.
"""
self.http_status_code: int = http_status_code
self.request_data: Dict[str, Any] = request_data or {}
self.runtime: float | None = runtime
self.error: Dict[str, Any] = error or {}

validation_messages: List[str] = self.get_validation_messages()
message += f' (HTTP status code: {http_status_code}, validation messages: {"; ".join(validation_messages)})'

super().__init__(message)

def get_validation_messages(self) -> List[str]:
"""Get the validation messages from the error information.
Returns:
List[str]: List of validation messages
"""
validation: List[Dict[str, Any]] = self.error.get("validation", [])
return [f"{item['field']}: {item['message']}" for item in validation]
6 changes: 6 additions & 0 deletions src/corbado_python_sdk/exceptions/standard_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class StandardException(Exception):
"""
Custom exception class for standard exceptions.
"""

pass
71 changes: 51 additions & 20 deletions src/corbado_python_sdk/services/implementation/user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

from pydantic import BaseModel, ConfigDict

from corbado_python_sdk.exceptions.server_exception import ServerException
from corbado_python_sdk.generated.api.user_api import UserApi
from corbado_python_sdk.generated.exceptions import ApiException
from corbado_python_sdk.generated.models.generic_rsp import GenericRsp
from corbado_python_sdk.generated.models.user_create_req import UserCreateReq
from corbado_python_sdk.generated.models.user_create_rsp import UserCreateRsp
from corbado_python_sdk.generated.models.user_delete_req import UserDeleteReq
from corbado_python_sdk.generated.models.user_get_rsp import UserGetRsp
from corbado_python_sdk.generated.models.user_list_rsp import UserListRsp

from ..interface.user_interface import UserInterface
from corbado_python_sdk.services.interface.user_interface import UserInterface
from corbado_python_sdk.utils import Util


class UserService(
Expand All @@ -30,11 +32,19 @@ def create(self, request: UserCreateReq) -> UserCreateRsp:
Args:
request (UserCreateReq): User create request
Raises:
ServerException: If any server side error occurs.
Returns:
UserCreateRsp: Response
"""
response: UserCreateRsp = self.client.user_create(user_create_req=request)
print(f"{response.http_status_code}")

try:
response: UserCreateRsp = self.client.user_create(user_create_req=request)
except ApiException as e:
exception: ServerException = Util.convert_to_server_exception(e)
raise exception

return response

def list_users(
Expand All @@ -56,34 +66,50 @@ def list_users(
page (int, optional): Page. Defaults to 1.
page_size (int, optional): Page Size. Defaults to 10.
Raises:
ServerException: If any server side error occurs.
Returns:
UserListRsp: Response
"""

print("listing users")
return self.client.user_list(
user_agent=user_agent,
remote_address=remote_addr,
sort=sort,
filter=filter_args,
page=page,
page_size=page_size,
)

def get(
self, user_id: Optional[str] = None, remote_addr: Optional[str] = None, user_agent: Optional[str] = None
) -> UserGetRsp:
try:
responce = self.client.user_list(
user_agent=user_agent,
remote_address=remote_addr,
sort=sort,
filter=filter_args,
page=page,
page_size=page_size,
)
except ApiException as e:
exception: ServerException = Util.convert_to_server_exception(e)
raise exception

return responce

def get(self, user_id: str, remote_addr: Optional[str] = None, user_agent: Optional[str] = None) -> UserGetRsp:
"""Get user
Args:
user_id (str): User Id
remote_addr (str): Remote address
user_agent (str): User agent
Raises:
ServerException: If any server side error occurs.
Returns:
UserGetRsp: Response
"""
return self.client.user_get(remote_address=remote_addr, user_agent=user_agent, user_id=user_id)
try:
responce: UserGetRsp = self.client.user_get(
remote_address=remote_addr, user_agent=user_agent, user_id=user_id
)
except ApiException as e:
exception: ServerException = Util.convert_to_server_exception(e)
raise exception

return responce

def delete(self, user_id: str, request: UserDeleteReq) -> GenericRsp:
"""Delete user
Expand All @@ -95,4 +121,9 @@ def delete(self, user_id: str, request: UserDeleteReq) -> GenericRsp:
Returns:
GenericRsp: Response
"""
return self.client.user_delete(user_delete_req=request, user_id=user_id)
try:
responce: GenericRsp = self.client.user_delete(user_delete_req=request, user_id=user_id)
except ApiException as e:
exception: ServerException = Util.convert_to_server_exception(e)
raise exception
return responce
1 change: 0 additions & 1 deletion src/corbado_python_sdk/util/__init__.py

This file was deleted.

1 change: 1 addition & 0 deletions src/corbado_python_sdk/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .util import Util
163 changes: 163 additions & 0 deletions src/corbado_python_sdk/utils/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import json
from typing import Any, Dict

from corbado_python_sdk.exceptions import ServerException, StandardException
from corbado_python_sdk.generated import ApiException, GenericRsp, RequestData


class Util:
"""
Helper class containing various utility methods.
"""

@staticmethod
def json_encode(data: Any) -> str:
"""JSON encode.
Args:
data (Any): Data to be encoded.
Raises:
StandardException: If encoding fails.
Returns:
str: Encoded JSON string.
"""
try:
json_str: str = json.dumps(data)
except json.JSONDecodeError as e:
raise StandardException(f"json.dumps() failed: {e}")
return json_str

@staticmethod
def json_decode(data: str) -> Dict[str, Any]:
"""
JSON decode.
Args:
data (str): Data to be decoded.
Raises:
StandardException: If decoding fails.
Returns:
Dict[str, Any]: Decoded data as a dictionary.
"""
try:
decoded_data = json.loads(data)
except json.JSONDecodeError as e:
raise StandardException(f"json.loads() failed: {e}")
return decoded_data

@staticmethod
def is_error_http_status_code(status_code: int) -> bool:
"""
Check if the status code indicates an error.
Args:
status_code (int): Status code.
Returns:
bool: True if status code indicates an error, False otherwise.
"""
return status_code >= 300

@staticmethod
def throw_server_exception_old(data: Dict[str, Any]) -> None:
"""
Throw a ServerException based on the provided data.
Args:
data (Dict[str, Any]): Data.
Raises:
StandardException: If required keys are missing in the data.
ServerException: Transformed server exception.
"""
# Check for error data, not existent for 404 for example
data["error"] = data.get("error", {})
keys_to_check: list[str] = ["httpStatusCode", "message", "requestData", "runtime"]
for key in keys_to_check:
if key not in data:
raise StandardException(f"Key '{key}' not found in data")
raise ServerException(
http_status_code=data["httpStatusCode"],
message=data["message"],
request_data=data["requestData"],
runtime=data["runtime"],
error=data["error"],
)

@staticmethod
def convert_to_server_exception(e: ApiException) -> ServerException:
# TODO: move to server exception constructor
"""
Convert ApiException to ServerException.
Args:
e (ApiException): Exception to be converted.
Raises:
StandardException: If response body is not a string.
Returns:
ServerException: ServerException instance.
"""
print(e)
body = e.body
if not isinstance(body, str):
raise StandardException("Response body is not a string")
data: Dict[str, Any] = Util.json_decode(body)
return ServerException(
http_status_code=data["httpStatusCode"],
message=data["message"],
request_data=data["requestData"],
runtime=data["runtime"],
error=data["error"],
)

@staticmethod
def hydrate_request_data(data: Dict[str, Any]) -> RequestData:
"""
Hydrate RequestData object from dictionary.
Args:
data (Dict[str, Any]): Data.
Raises:
StandardException: If required keys are missing in the data.
Returns:
RequestData: RequestData object.
"""
keys_to_check: list[str] = ["requestID", "link"]
for key in keys_to_check:
if key not in data:
raise StandardException(f"Key '{key}' not found in data")
return RequestData(requestID=data["requestID"], link=data["link"])

@staticmethod
def hydrate_response(data: Dict[str, Any]) -> GenericRsp:
"""
Hydrate GenericRsp object from dictionary.
Args:
data (Dict[str, Any]): Data.
Raises:
StandardException: If required keys are missing in the data.
Returns:
GenericRsp: GenericRsp object.
"""
keys_to_check: list[str] = ["httpStatusCode", "message", "requestData", "runtime"]
for key in keys_to_check:
if key not in data:
raise StandardException(f"Key '{key}' not found in data")
request_data: RequestData = Util.hydrate_request_data(data["requestData"])
return GenericRsp(
httpStatusCode=data["httpStatusCode"],
message=data["message"],
requestData=request_data,
runtime=data["runtime"],
)
File renamed without changes.
Loading

0 comments on commit 54a8ec1

Please sign in to comment.