Drew Short
6 years ago
9 changed files with 164 additions and 2 deletions
-
1.gitignore
-
3server/atheneum/__init__.py
-
1server/atheneum/api/__init__.py
-
25server/atheneum/api/user_api.py
-
20server/atheneum/errors.py
-
52server/atheneum/service/serialization_service.py
-
43server/atheneum/service/user_service.py
-
5server/atheneum/utility/json_utility.py
-
16server/tests/api/test_user_api.py
@ -1,2 +1,3 @@ |
|||
"""API blueprint exports.""" |
|||
from atheneum.api.authentication_api import AUTH_BLUEPRINT |
|||
from atheneum.api.user_api import USER_BLUEPRINT |
@ -0,0 +1,25 @@ |
|||
"""User API blueprint and endpoint definitions.""" |
|||
from flask import Blueprint, abort |
|||
|
|||
from atheneum.api.decorators import return_json |
|||
from atheneum.api.model import APIResponse |
|||
from atheneum.middleware import authentication_middleware |
|||
from atheneum.service import user_service |
|||
|
|||
USER_BLUEPRINT = Blueprint( |
|||
name='user', import_name=__name__, url_prefix='/user') |
|||
|
|||
|
|||
@USER_BLUEPRINT.route('/<name>', methods=['GET']) |
|||
@return_json |
|||
@authentication_middleware.require_token_auth |
|||
def get_user(name: str) -> APIResponse: |
|||
""" |
|||
Get a token for continued authentication. |
|||
|
|||
:return: A login token for continued authentication |
|||
""" |
|||
user = user_service.find_by_name(name) |
|||
if user is not None: |
|||
return APIResponse(user, 200) |
|||
return abort(404) |
@ -0,0 +1,20 @@ |
|||
"""Error definitions for Atheneum.""" |
|||
from typing import Dict |
|||
|
|||
|
|||
class BaseError(RuntimeError): |
|||
"""Atheneum Base Error Class.""" |
|||
|
|||
def __init__( |
|||
self, |
|||
message: str = 'Unknown error', |
|||
extra_fields: Dict[str, str] = None) -> None: |
|||
"""Populate The Error Definition.""" |
|||
super().__init__(message) |
|||
self.extra_fields = extra_fields |
|||
|
|||
|
|||
class ValidationError(BaseError): |
|||
"""Atheneum Validation Error.""" |
|||
|
|||
pass |
@ -0,0 +1,52 @@ |
|||
"""Handle Model Serialization.""" |
|||
from typing import Dict, Callable, Any, List, Optional, Type |
|||
|
|||
from atheneum import errors |
|||
from atheneum.db import db |
|||
|
|||
|
|||
class BaseSerializer: # pylint: disable=too-few-public-methods |
|||
"""Base Model serializer.""" |
|||
|
|||
def __init__(self, model: Type[db.Model]) -> None: |
|||
"""Initialize the base serializer.""" |
|||
self._fields: Dict[str, Callable[[db.Model], Any]] = {} |
|||
self.model = model |
|||
|
|||
def serialize(self, options: Optional[List[str]]) -> Any: |
|||
"""Convert Model field and factories to dicts.""" |
|||
field_factories = self._serializers() |
|||
if not options: |
|||
options = list(field_factories.keys()) |
|||
ret = {} |
|||
for key in options: |
|||
if key not in field_factories: |
|||
raise errors.ValidationError( |
|||
'Invalid key: %r. Valid keys: %r.' % ( |
|||
key, list(sorted(field_factories.keys())))) |
|||
factory = field_factories[key] |
|||
ret[key] = factory() |
|||
return ret |
|||
|
|||
def _serializers(self) -> Dict[str, Callable[[], Any]]: |
|||
"""Field definitions.""" |
|||
raise NotImplementedError() |
|||
|
|||
|
|||
_model_serializers: Dict[str, Type[BaseSerializer]] = {} |
|||
|
|||
|
|||
def register_serializer( |
|||
model_name: str, model_serializer: Type[BaseSerializer]) -> None: |
|||
"""Add a model to the serializer mapping.""" |
|||
_model_serializers[model_name] = model_serializer |
|||
|
|||
|
|||
def serialize_model(model_obj: db.Model) -> Any: |
|||
"""Lookup a Model and hand off to the serializer.""" |
|||
try: |
|||
return _model_serializers[ |
|||
type(model_obj).__name__](model_obj).serialize(None) |
|||
except KeyError: |
|||
raise NotImplementedError( |
|||
'{} has no registered serializers'.format(model_obj.__name__)) |
@ -0,0 +1,16 @@ |
|||
from flask.testing import FlaskClient |
|||
|
|||
from tests.conftest import AuthActions |
|||
|
|||
|
|||
def test_get_user_happy_path(auth: AuthActions, client: FlaskClient): |
|||
auth.login() |
|||
auth_header = auth.get_authorization_header_token() |
|||
result = client.get( |
|||
'/user/{}'.format(client.application.config['test_username']), |
|||
headers={ |
|||
auth_header[0]: auth_header[1] |
|||
}) |
|||
assert result.status_code == 200 |
|||
assert result.json is not None |
|||
assert result.json['name'] == client.application.config['test_username'] |
Write
Preview
Loading…
Cancel
Save
Reference in new issue