Skip to content

Commit e570ff2

Browse files
committed
now body is embed, add tests coverage
1 parent 3c4a444 commit e570ff2

File tree

6 files changed

+346
-29
lines changed

6 files changed

+346
-29
lines changed

fastapi_jsonapi/api.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
Union,
1515
)
1616

17-
from fastapi import APIRouter, Path, Query, Request, status
17+
from fastapi import APIRouter, Body, Path, Query, Request, status
1818
from pydantic import BaseModel as PydanticBaseModel
1919

2020
from fastapi_jsonapi.data_typing import TypeModel
@@ -118,8 +118,18 @@ def __init__(
118118
schema_in_post=schema_in_post,
119119
schema_in_patch=schema_in_patch,
120120
)
121+
# we need to save post_data and patch_data
122+
# and set dependency `data` as `embed=True`
123+
# because if there's more than one Body dependency,
124+
# FastAPI makes them all `embed=True` and validation breaks!
125+
# doc url
126+
# https://fastapi.tiangolo.com/tutorial/body-multiple-params/#embed-a-single-body-parameter
127+
# code:
128+
# https://github.com/tiangolo/fastapi/blob/831b5d5402a65ee9f415670f4116522c8e874ed3/fastapi/dependencies/utils.py#L768
121129
self.schema_in_post = dto.schema_in_post
130+
self.schema_in_post_data = dto.schema_in_post_data
122131
self.schema_in_patch = dto.schema_in_patch
132+
self.schema_in_patch_data = dto.schema_in_patch_data
123133
self.detail_response_schema = dto.detail_response_schema
124134
self.list_response_schema = dto.list_response_schema
125135

@@ -486,16 +496,21 @@ def _create_post_resource_list_view(self):
486496
487497
:return:
488498
"""
489-
schema_in = self.schema_in_post
499+
# `data` as embed Body param
500+
schema_in = self.schema_in_post_data
490501

491-
async def wrapper(request: Request, data_create: schema_in, **extra_view_deps):
502+
async def wrapper(
503+
request: Request,
504+
data: schema_in = Body(embed=True),
505+
**extra_view_deps,
506+
):
492507
resource = self.list_view_resource(
493508
request=request,
494509
jsonapi=self,
495510
)
496511

497512
response = await resource.handle_post_resource_list(
498-
data_create=data_create.data,
513+
data_create=data,
499514
**extra_view_deps,
500515
)
501516
return response
@@ -576,11 +591,12 @@ def _create_patch_resource_detail_view(self):
576591
577592
:return:
578593
"""
579-
schema_in = self.schema_in_patch
594+
# `data` as embed Body param
595+
schema_in = self.schema_in_patch_data
580596

581597
async def wrapper(
582598
request: Request,
583-
data_update: schema_in,
599+
data: schema_in = Body(embed=True),
584600
obj_id: str = Path(...),
585601
**extra_view_deps,
586602
):
@@ -592,7 +608,7 @@ async def wrapper(
592608
# TODO: pass obj_id as kwarg (get name from DetailView class)
593609
response = await resource.handle_update_resource(
594610
obj_id=obj_id,
595-
data_update=data_update.data,
611+
data_update=data,
596612
**extra_view_deps,
597613
)
598614
return response

fastapi_jsonapi/schema_builder.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ def included_schemas_list(self) -> List[Type[JSONAPIObjectSchema]]:
8383
@dataclass(frozen=True)
8484
class BuiltSchemasDTO:
8585
schema_in_post: Type[BaseJSONAPIDataInSchema]
86+
schema_in_post_data: Type[BaseJSONAPIItemInSchema]
8687
schema_in_patch: Type[BaseJSONAPIDataInSchema]
88+
schema_in_patch_data: Type[BaseJSONAPIItemInSchema]
8789
detail_response_schema: Type[JSONAPIResultDetailSchema]
8890
list_response_schema: Type[JSONAPIResultListSchema]
8991

@@ -150,21 +152,23 @@ def create_schemas(
150152
if any(schema_in_patch is cmp_schema for cmp_schema in [schema, schema_in_post]):
151153
schema_name_in_patch_suffix = "InPatch"
152154

153-
schemas_in_post = self.build_schema_in(
155+
schema_in_post, schema_in_post_data = self.build_schema_in(
154156
schema_in=schema_in_post,
155157
schema_name_suffix=schema_name_in_post_suffix,
156158
non_optional_relationships=True,
157159
)
158160

159-
schemas_in_patch = self.build_schema_in(
161+
schema_in_patch, schema_in_patch_data = self.build_schema_in(
160162
schema_in=schema_in_patch,
161163
schema_name_suffix=schema_name_in_patch_suffix,
162164
id_field_required=True,
163165
)
164166

165167
return BuiltSchemasDTO(
166-
schema_in_post=schemas_in_post,
167-
schema_in_patch=schemas_in_patch,
168+
schema_in_post=schema_in_post,
169+
schema_in_post_data=schema_in_post_data,
170+
schema_in_patch=schema_in_patch,
171+
schema_in_patch_data=schema_in_patch_data,
168172
list_response_schema=self._create_schemas_objects_list(schema),
169173
detail_response_schema=self._create_schemas_object_detail(schema),
170174
)
@@ -175,7 +179,7 @@ def build_schema_in(
175179
schema_name_suffix: str = "",
176180
non_optional_relationships: bool = False,
177181
id_field_required: bool = False,
178-
) -> Type[BaseJSONAPIDataInSchema]:
182+
) -> Tuple[Type[BaseJSONAPIDataInSchema], Type[BaseJSONAPIItemInSchema]]:
179183
base_schema_name = schema_in.__name__.removesuffix("Schema") + schema_name_suffix
180184

181185
dto = self._get_info_from_schema_for_building(
@@ -202,7 +206,7 @@ def build_schema_in(
202206
__base__=BaseJSONAPIDataInSchema,
203207
)
204208

205-
return wrapped_object_jsonapi_schema
209+
return wrapped_object_jsonapi_schema, object_jsonapi_schema
206210

207211
def _build_schema(
208212
self,

tests/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
workplace_1,
4747
workplace_2,
4848
)
49+
from tests.fixtures.user import ( # noqa
50+
user_attributes,
51+
)
4952
from tests.fixtures.views import ( # noqa
5053
DetailViewBaseGeneric,
5154
ListViewBaseGeneric,

tests/fixtures/app.py

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from pathlib import Path
22
from typing import Type
33

4-
import uvicorn
4+
import pytest
55
from fastapi import APIRouter, FastAPI
6-
from pytest import fixture # noqa PT013
76

87
from fastapi_jsonapi import RoutersJSONAPI, init
98
from fastapi_jsonapi.atomic import AtomicOperations
@@ -172,30 +171,18 @@ def add_routers(app_plain: FastAPI):
172171
return app_plain
173172

174173

175-
@fixture(scope="session")
174+
@pytest.fixture(scope="session")
176175
def app_plain() -> FastAPI:
177176
return build_app_plain()
178177

179178

180-
@fixture(scope="session")
179+
@pytest.fixture(scope="session")
181180
def app(app_plain: FastAPI):
182181
add_routers(app_plain)
183182

184183
return app_plain
185184

186185

187-
if __name__ == "__main__":
188-
fastapi_app = build_app_plain()
189-
add_routers(fastapi_app)
190-
uvicorn.run(
191-
"asgi:app",
192-
host="0.0.0.0",
193-
port=8082,
194-
reload=True,
195-
app_dir=str(CURRENT_DIR),
196-
)
197-
198-
199186
def build_app_custom(
200187
model,
201188
schema,

tests/fixtures/user.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import pytest
2+
3+
from tests.misc.utils import fake
4+
from tests.schemas import UserAttributesBaseSchema
5+
6+
7+
@pytest.fixture()
8+
def user_attributes():
9+
user_attributes = UserAttributesBaseSchema(
10+
name=fake.name(),
11+
age=fake.pyint(),
12+
)
13+
return user_attributes

0 commit comments

Comments
 (0)