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

Bsr bump spectree #7617

Merged
merged 8 commits into from
Aug 14, 2023
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
2 changes: 1 addition & 1 deletion api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ schwifty==2022.9.0
semver==2.13.0
sentry-sdk==1.14.0
sib-api-v3-sdk
spectree==0.7.2
spectree==1.2.*
# FIXME (dbaty, 2023-01-04): do not use 1.4.46 that has a new
# deprecation warning for which we're not ready
# (https://docs.sqlalchemy.org/en/20/changelog/changelog_14.html#change-e67bfa1efbe52ae40aa842124bc40c51).
Expand Down
42 changes: 16 additions & 26 deletions api/src/pcapi/routes/public/collective/endpoints/categories.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from collections import defaultdict
from typing import cast

from pcapi.core.categories import categories
from pcapi.core.categories import subcategories_v2
from pcapi.routes.public import blueprints
from pcapi.routes.public.collective.serialization import offers as offers_serialization
from pcapi.routes.serialization import BaseModel
from pcapi.serialization.decorator import spectree_serialize
from pcapi.serialization.spec_tree import ExtendResponse as SpectreeResponse
from pcapi.validation.routes.users_authentifications import api_key_required
Expand All @@ -16,18 +14,14 @@
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
{
"HTTP_200": (
offers_serialization.CollectiveOffersListCategoriesResponseModel,
"La liste des catégories éligibles existantes.",
),
"HTTP_401": (
cast(BaseModel, offers_serialization.AuthErrorResponseModel),
"Authentification nécessaire",
),
}
)
HTTP_200=(
offers_serialization.CollectiveOffersListCategoriesResponseModel,
"La liste des catégories éligibles existantes.",
),
HTTP_401=(
offers_serialization.AuthErrorResponseModel,
"Authentification nécessaire",
),
),
)
@api_key_required
Expand All @@ -48,18 +42,14 @@ def list_categories() -> offers_serialization.CollectiveOffersListCategoriesResp
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
{
"HTTP_200": (
offers_serialization.CollectiveOffersListSubCategoriesResponseModel,
"La liste des sous-catégories éligibles existantes.",
),
"HTTP_401": (
cast(BaseModel, offers_serialization.AuthErrorResponseModel),
"Authentification nécessaire",
),
}
)
HTTP_200=(
offers_serialization.CollectiveOffersListSubCategoriesResponseModel,
"La liste des sous-catégories éligibles existantes.",
),
HTTP_401=(
offers_serialization.AuthErrorResponseModel,
"Authentification nécessaire",
),
),
)
@api_key_required
Expand Down
23 changes: 8 additions & 15 deletions api/src/pcapi/routes/public/collective/endpoints/domains.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from typing import cast

from pcapi.core.educational import repository as educational_repository
from pcapi.routes.public import blueprints
from pcapi.routes.public.collective.serialization import domains as domains_serialization
from pcapi.routes.public.collective.serialization import offers as offers_serialization
from pcapi.routes.serialization import BaseModel
from pcapi.serialization.decorator import spectree_serialize
from pcapi.serialization.spec_tree import ExtendResponse as SpectreeResponse
from pcapi.utils.cache import cached_view
Expand All @@ -16,18 +13,14 @@
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
{
"HTTP_200": (
domains_serialization.CollectiveOffersListDomainsResponseModel,
"La liste des domaines d'éducation.",
),
"HTTP_401": (
cast(BaseModel, offers_serialization.AuthErrorResponseModel),
"Authentification nécessaire",
),
}
)
HTTP_200=(
domains_serialization.CollectiveOffersListDomainsResponseModel,
"La liste des domaines d'éducation.",
),
HTTP_401=(
offers_serialization.AuthErrorResponseModel,
"Authentification nécessaire",
),
),
)
@api_key_required
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from typing import cast

from pcapi.core.educational.api.institution import search_educational_institution
from pcapi.routes.public import blueprints
from pcapi.routes.public.collective.serialization import institutions as institutions_serialization
from pcapi.routes.public.collective.serialization import offers as offers_serialization
from pcapi.routes.serialization import BaseModel
from pcapi.serialization.decorator import spectree_serialize
from pcapi.serialization.spec_tree import ExtendResponse as SpectreeResponse
from pcapi.validation.routes.users_authentifications import api_key_required
Expand All @@ -15,22 +12,18 @@
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
{
"HTTP_200": (
institutions_serialization.CollectiveOffersListEducationalInstitutionResponseModel,
"La liste des établissement scolaires éligibles.",
),
"HTTP_400": (
cast(BaseModel, offers_serialization.ErrorResponseModel),
"Requête malformée",
),
"HTTP_401": (
cast(BaseModel, offers_serialization.AuthErrorResponseModel),
"Authentification nécessaire",
),
}
)
HTTP_200=(
institutions_serialization.CollectiveOffersListEducationalInstitutionResponseModel,
"La liste des établissement scolaires éligibles.",
),
HTTP_400=(
offers_serialization.ErrorResponseModel,
"Requête malformée",
),
HTTP_401=(
offers_serialization.AuthErrorResponseModel,
"Authentification nécessaire",
),
),
)
@api_key_required
Expand Down
28 changes: 10 additions & 18 deletions api/src/pcapi/routes/public/collective/endpoints/offers.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,11 @@
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
BASE_CODE_DESCRIPTIONS
| {
"HTTP_200": (
offers_serialization.CollectiveOffersListResponseModel,
"L'offre collective existe",
),
}
)
**(BASE_CODE_DESCRIPTIONS),
HTTP_200=(
offers_serialization.CollectiveOffersListResponseModel,
"L'offre collective existe",
),
),
)
@api_key_required
Expand Down Expand Up @@ -76,15 +72,11 @@ def get_collective_offers_public(
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
BASE_CODE_DESCRIPTIONS
| {
"HTTP_200": (
offers_serialization.GetPublicCollectiveOfferResponseModel,
"L'offre collective existe",
),
}
)
**(BASE_CODE_DESCRIPTIONS),
HTTP_200=(
offers_serialization.GetPublicCollectiveOfferResponseModel,
"L'offre collective existe",
),
),
)
@api_key_required
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
from typing import cast

from pcapi.core.educational import models as educational_models
from pcapi.routes.public import blueprints
from pcapi.routes.public.collective.serialization import offers as offers_serialization
from pcapi.routes.public.collective.serialization import students_levels as students_levels_serialization
from pcapi.routes.serialization import BaseModel
from pcapi.serialization.decorator import spectree_serialize
from pcapi.serialization.spec_tree import ExtendResponse as SpectreeResponse
from pcapi.validation.routes.users_authentifications import api_key_required
Expand All @@ -15,18 +12,14 @@
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
{
"HTTP_200": (
students_levels_serialization.CollectiveOffersListStudentLevelsResponseModel,
"La liste des domaines d'éducation.",
),
"HTTP_401": (
cast(BaseModel, offers_serialization.AuthErrorResponseModel),
"Authentification nécessaire",
),
}
)
HTTP_200=(
students_levels_serialization.CollectiveOffersListStudentLevelsResponseModel,
"La liste des domaines d'éducation.",
),
HTTP_401=(
offers_serialization.AuthErrorResponseModel,
"Authentification nécessaire",
),
),
)
@api_key_required
Expand Down
23 changes: 8 additions & 15 deletions api/src/pcapi/routes/public/collective/endpoints/venues.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
from typing import cast

from pcapi.core.offerers import repository as offerers_repository
from pcapi.core.providers import repository as providers_repository
from pcapi.models.feature import FeatureToggle
from pcapi.routes.public import blueprints
from pcapi.routes.public.collective.serialization import offers as offers_serialization
from pcapi.routes.public.collective.serialization import venues as venues_serialization
from pcapi.routes.serialization import BaseModel
from pcapi.serialization.decorator import spectree_serialize
from pcapi.serialization.spec_tree import ExtendResponse as SpectreeResponse
from pcapi.validation.routes.users_authentifications import api_key_required
Expand All @@ -18,18 +15,14 @@
api=blueprints.v2_prefixed_public_api_schema,
tags=["API offres collectives"],
resp=SpectreeResponse(
**(
{
"HTTP_200": (
venues_serialization.CollectiveOffersListVenuesResponseModel,
"La liste des lieux ou vous pouvez créer une offre.",
),
"HTTP_401": (
cast(BaseModel, offers_serialization.AuthErrorResponseModel),
"Authentification nécessaire",
),
}
)
HTTP_200=(
venues_serialization.CollectiveOffersListVenuesResponseModel,
"La liste des lieux ou vous pouvez créer une offre.",
),
HTTP_401=(
offers_serialization.AuthErrorResponseModel,
"Authentification nécessaire",
),
),
)
@api_key_required
Expand Down
35 changes: 15 additions & 20 deletions api/src/pcapi/serialization/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@
import logging
from typing import Any
from typing import Callable
from typing import Iterable
from typing import Sequence
from typing import Type

from flask import Response
from flask import make_response
from flask import request
import pydantic
import spectree
from werkzeug.exceptions import BadRequest

from pcapi.models.api_errors import ApiErrors
from pcapi.routes.apis import api as default_api
from pcapi.routes.serialization import BaseModel
from pcapi.serialization.spec_tree import ExtendResponse as SpectreeResponse
from pcapi.serialization.spec_tree import ExtendedSpecTree


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -64,15 +63,15 @@ def spectree_serialize(
headers: Type[BaseModel] = None,
cookies: Type[BaseModel] = None,
response_model: Type[BaseModel] = None,
tags: Iterable = (),
tags: Sequence = (),
before: Callable = None,
after: Callable = None,
response_by_alias: bool = True,
exclude_none: bool = False,
on_success_status: int = 200,
on_empty_status: int | None = None,
on_error_statuses: list[int] | None = None,
api: ExtendedSpecTree = default_api,
api: spectree.SpecTree = default_api,
json_format: bool = True,
response_headers: dict[str, str] | None = None,
resp: SpectreeResponse | None = None,
Expand Down Expand Up @@ -123,15 +122,16 @@ def decorate_validation(route: Callable[..., Any]) -> Callable[[Any], Any]:

@wraps(route)
@api.validate(
query=query_in_kwargs,
headers=headers,
cookies=cookies,
resp=spectree_response,
tags=tags,
before=before,
after=after,
before=before,
cookies=cookies,
form=form_in_kwargs,
headers=headers,
json=body_in_kwargs,
query=query_in_kwargs,
resp=spectree_response,
security=security,
tags=tags,
)
def sync_validate(*args: Any, **kwargs: Any) -> Response:
try:
Expand All @@ -142,8 +142,9 @@ def sync_validate(*args: Any, **kwargs: Any) -> Response:
# not throw error when some invalid json in provided for V2 bookings api.
body_params = None
else:
# Since pydantic validator is applied before this method and use a silent json parser, # the only case we should end here is with an PATCH/POST with no validator for body params
# or a GET request with a invalid body. raise
# Since pydantic validator is applied before this method and use a silent json parser,
# the only case we should end here is with an PATCH/POST with no validator for body params
# or a GET request with a invalid body.
raise
query_params = request.args
form = request.form
Expand All @@ -152,13 +153,7 @@ def sync_validate(*args: Any, **kwargs: Any) -> Response:
if query_in_kwargs:
kwargs["query"] = query_in_kwargs(**query_params)
if form_in_kwargs:
try:
kwargs["form"] = form_in_kwargs(**form)
except pydantic.ValidationError as validation_errors:
error_dict = {}
for errors in validation_errors.errors():
error_dict[errors["loc"][0]] = errors["msg"]
raise ApiErrors(error_dict)
kwargs["form"] = form_in_kwargs(**form)
lixxday marked this conversation as resolved.
Show resolved Hide resolved

result = route(*args, **kwargs)
if json_format:
Expand Down
14 changes: 2 additions & 12 deletions api/src/pcapi/serialization/spec_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from pydantic import BaseModel # pylint: disable=wrong-pydantic-base-model-import
from spectree import Response
from spectree import SpecTree
from spectree.utils import parse_code

from pcapi import settings

Expand Down Expand Up @@ -71,14 +70,5 @@ def _get_model_definitions(self) -> dict:


class ExtendResponse(Response):
def generate_spec(self) -> Dict[str, Any]:
responses: Dict[str, Any] = {}
for code in self.codes:
responses[parse_code(code)] = {"description": self.get_code_description(code)}
for code, model in self.code_models.items():
model_name = get_model_key(model=model)
responses[parse_code(code)] = {
"description": self.get_code_description(code),
"content": {"application/json": {"schema": {"$ref": f"#/components/schemas/{model_name}"}}},
}
return responses
def generate_spec(self, _naming_strategy: Callable[[Type[BaseModel]], str] = None) -> Dict[str, Any]:
return super().generate_spec(naming_strategy=get_model_key)
Loading
Loading