Skip to content

Commit 10612eb

Browse files
committed
Leverage marshmallow_dataclass for input dto validation
1 parent 525d029 commit 10612eb

File tree

4 files changed

+27
-23
lines changed

4 files changed

+27
-23
lines changed

auctioning_platform/requirements.txt

+10-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ click==7.1.2 # via flask, rq
1515
dnspython==2.0.0 # via email-validator
1616
email-validator==1.1.1 # via -r ./web_app/requirements.txt
1717
factory-boy==3.1.0 # via -r ./auctions/requirements-dev.txt, -r ./web_app/requirements-dev.txt
18-
faker==4.14.0 # via -r ./shipping_infrastructure/requirements.txt, factory-boy, pytest-faker
18+
faker==4.14.0 # via -r ./shipping_infrastructure/requirements.txt, factory-boy, pytest-faker
1919
flask-babelex==0.9.4 # via flask-security
2020
flask-injector==0.12.0 # via -r ./web_app/requirements.txt
2121
flask-login==0.5.0 # via -r ./web_app/requirements.txt, flask-security
@@ -31,7 +31,9 @@ injector==0.18.4 # via -r ./auctions/requirements.txt, -r ./auctions_in
3131
itsdangerous==1.1.0 # via flask, flask-security, flask-wtf
3232
jinja2==2.11.2 # via flask, flask-babelex
3333
markupsafe==1.1.1 # via jinja2, wtforms
34-
marshmallow==3.8.0 # via -r ./web_app/requirements.txt
34+
marshmallow-dataclass==8.1.0 # via -r ./web_app/requirements.txt
35+
marshmallow==3.8.0 # via -r ./web_app/requirements.txt, marshmallow-dataclass
36+
mypy-extensions==0.4.3 # via typing-inspect
3537
packaging==20.4 # via pytest
3638
passlib==1.7.4 # via flask-security
3739
pluggy==0.13.1 # via pytest
@@ -42,9 +44,9 @@ pyparsing==2.4.7 # via packaging
4244
pytest-faker==2.0.0 # via -r ./auctions/requirements-dev.txt
4345
pytest-freezegun==0.4.2 # via -r ./processes/requirements-dev.txt
4446
pytest-sqlalchemy==0.2.1 # via -r ./db_infrastructure/requirements.txt
45-
pytest==6.1.1 # via -r ./auctions/requirements-dev.txt, -r ./auctions_infrastructure/requirements-dev.txt, -r ./customer_relationship/requirements-dev.txt, -r ./foundation/requirements-dev.txt, -r ./payments/requirements-dev.txt, -r ./processes/requirements-dev.txt, -r ./shipping/requirements-dev.txt, -r ./shipping_infrastructure/requirements-dev.txt, -r ./web_app/requirements-dev.txt, pytest-freezegun, pytest-sqlalchemy
47+
pytest==6.1.2 # via -r ./auctions/requirements-dev.txt, -r ./auctions_infrastructure/requirements-dev.txt, -r ./customer_relationship/requirements-dev.txt, -r ./foundation/requirements-dev.txt, -r ./payments/requirements-dev.txt, -r ./processes/requirements-dev.txt, -r ./shipping/requirements-dev.txt, -r ./shipping_infrastructure/requirements-dev.txt, -r ./web_app/requirements-dev.txt, pytest-freezegun, pytest-sqlalchemy
4648
python-dateutil==2.8.1 # via faker, freezegun
47-
python-dotenv==0.14.0 # via -r ./main/requirements.txt
49+
python-dotenv==0.15.0 # via -r ./main/requirements.txt
4850
pytz==2019.3 # via -r ./auctions_infrastructure/requirements.txt, babel
4951
redis==3.5.3 # via -r ./main/requirements.txt, rq
5052
requests==2.22.0 # via -r ./payments/requirements.txt, stripe
@@ -55,9 +57,10 @@ sqlalchemy-utils==0.36.8 # via pytest-sqlalchemy
5557
sqlalchemy==1.3.19 # via -r ./auctions_infrastructure/requirements.txt, -r ./customer_relationship/requirements.txt, -r ./db_infrastructure/requirements.txt, -r ./main/requirements.txt, -r ./payments/requirements.txt, -r ./processes/requirements.txt, -r ./shipping_infrastructure/requirements.txt, -r ./web_app/requirements.txt, -r ./web_app_models/requirements.txt, pytest-sqlalchemy, sqlalchemy-utils
5658
stripe==2.54.0 # via -r ./payments/requirements.txt
5759
text-unidecode==1.3 # via faker
58-
toml==0.10.1 # via pytest
59-
typing-extensions==3.7.4.3 # via -r ./foundation/requirements.txt, -r ./processes/requirements.txt, injector
60+
toml==0.10.2 # via pytest
61+
typing-extensions==3.7.4.3 # via -r ./foundation/requirements.txt, -r ./processes/requirements.txt, typing-inspect
62+
typing-inspect==0.6.0 # via marshmallow-dataclass
6063
typing==3.7.4.3 # via flask-injector
61-
urllib3==1.25.10 # via requests
64+
urllib3==1.25.11 # via requests
6265
werkzeug==1.0.1 # via flask
6366
wtforms==2.3.3 # via flask-wtf

auctioning_platform/web_app/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ Flask-Injector==0.12.0
44
Flask==1.1.2
55
bcrypt==3.1.7
66
marshmallow==3.8.0
7+
marshmallow-dataclass==8.1.0
78
Flask-Login==0.5.0
89
SQLAlchemy==1.3.19

auctioning_platform/web_app/web_app/blueprints/auctions.py

+2-12
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import flask_injector
33
from flask_login import current_user
44
import injector
5-
from marshmallow import Schema, post_load
65

76
from auctions import (
87
AuctionId,
@@ -13,8 +12,7 @@
1312
PlacingBidOutputBoundary,
1413
PlacingBidOutputDto,
1514
)
16-
from web_app.serialization.dto import get_input_dto
17-
from web_app.serialization.fields import Dollars
15+
from web_app.serialization.dto import get_dto
1816

1917
auctions_blueprint = Blueprint("auctions_blueprint", __name__)
2018

@@ -41,20 +39,12 @@ def place_bid(auction_id: AuctionId, placing_bid_uc: PlacingBid, presenter: Plac
4139
if not current_user.is_authenticated:
4240
abort(403)
4341

44-
dto = get_input_dto(request, PlacingBidSchema, context={"auction_id": auction_id, "bidder_id": current_user.id})
42+
dto = get_dto(request, PlacingBidInputDto, context={"auction_id": auction_id, "bidder_id": current_user.id})
4543

4644
placing_bid_uc.execute(dto)
4745
return presenter.response # type: ignore
4846

4947

50-
class PlacingBidSchema(Schema):
51-
amount = Dollars()
52-
53-
@post_load
54-
def make_dto(self, data: dict, **_kwargs: dict) -> PlacingBidInputDto:
55-
return PlacingBidInputDto(**self.context, **data)
56-
57-
5848
class PlacingBidPresenter(PlacingBidOutputBoundary):
5949
response: Response
6050

auctioning_platform/web_app/web_app/serialization/dto.py

+14-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,23 @@
22

33
from flask import Request, abort, jsonify, make_response
44
from marshmallow import Schema, exceptions
5+
from marshmallow_dataclass import class_schema
56

6-
TSchema = TypeVar("TSchema", bound=Schema)
7+
from foundation.value_objects import Money
78

9+
from web_app.serialization.fields import Dollars
810

9-
def get_input_dto(request: Request, schema_cls: Type[TSchema], context: dict) -> TSchema:
10-
schema = schema_cls(context=context)
11+
TDto = TypeVar("TDto")
12+
13+
14+
class BaseSchema(Schema):
15+
TYPE_MAPPING = {Money: Dollars}
16+
17+
18+
def get_dto(request: Request, dto_cls: Type[TDto], context: dict) -> TDto:
19+
schema_cls = class_schema(dto_cls, base_schema=BaseSchema)
20+
schema = schema_cls()
1121
try:
12-
return cast(TSchema, schema.load(request.json))
22+
return cast(TDto, schema.load(dict(context, **request.json)))
1323
except exceptions.ValidationError as exc:
1424
abort(make_response(jsonify(exc.messages), 400))

0 commit comments

Comments
 (0)