From 1752d355f149bac33d54a4e8cc05e753961f7216 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Fri, 13 Oct 2023 18:56:18 +0000 Subject: [PATCH] Specification validation as part of shortcuts --- docs/customizations.rst | 13 ++++--- openapi_core/shortcuts.py | 8 +++++ .../unmarshalling/request/protocols.py | 6 ++++ .../unmarshalling/request/unmarshallers.py | 4 +++ .../unmarshalling/response/protocols.py | 6 ++++ openapi_core/unmarshalling/unmarshallers.py | 3 ++ openapi_core/validation/request/protocols.py | 6 ++++ openapi_core/validation/request/validators.py | 17 +++++++++ openapi_core/validation/response/protocols.py | 6 ++++ .../validation/response/validators.py | 11 ++++++ openapi_core/validation/validators.py | 11 ++++++ poetry.lock | 27 +++----------- pyproject.toml | 2 +- tests/unit/conftest.py | 13 +++++-- tests/unit/test_shortcuts.py | 36 +++++++++++++++++++ 15 files changed, 137 insertions(+), 32 deletions(-) diff --git a/docs/customizations.rst b/docs/customizations.rst index 679dedcd..059cc745 100644 --- a/docs/customizations.rst +++ b/docs/customizations.rst @@ -4,18 +4,17 @@ Customizations Specification validation ------------------------ -By default, the provided specification is validated on ``Spec`` object creation time. +By default, the specified specification is also validated. If you know you have a valid specification already, disabling the validator can improve the performance. .. code-block:: python - :emphasize-lines: 5 + :emphasize-lines: 4 - from openapi_core import Spec - - spec = Spec.from_dict( - spec_dict, - validator=None, + validate_request( + request, + spec=spec, + spec_validator_cls=None, ) Media type deserializers diff --git a/openapi_core/shortcuts.py b/openapi_core/shortcuts.py index 42846d6e..50c60103 100644 --- a/openapi_core/shortcuts.py +++ b/openapi_core/shortcuts.py @@ -108,6 +108,7 @@ def unmarshal_apicall_request( if not issubclass(cls, RequestUnmarshaller): raise TypeError("'cls' argument is not type of RequestUnmarshaller") v = cls(spec, base_url=base_url, **unmarshaller_kwargs) + v.check_spec(spec) result = v.unmarshal(request) result.raise_for_errors() return result @@ -134,6 +135,7 @@ def unmarshal_webhook_request( "'cls' argument is not type of WebhookRequestUnmarshaller" ) v = cls(spec, base_url=base_url, **unmarshaller_kwargs) + v.check_spec(spec) result = v.unmarshal(request) result.raise_for_errors() return result @@ -198,6 +200,7 @@ def unmarshal_apicall_response( if not issubclass(cls, ResponseUnmarshaller): raise TypeError("'cls' argument is not type of ResponseUnmarshaller") v = cls(spec, base_url=base_url, **unmarshaller_kwargs) + v.check_spec(spec) result = v.unmarshal(request, response) result.raise_for_errors() return result @@ -227,6 +230,7 @@ def unmarshal_webhook_response( "'cls' argument is not type of WebhookResponseUnmarshaller" ) v = cls(spec, base_url=base_url, **unmarshaller_kwargs) + v.check_spec(spec) result = v.unmarshal(request, response) result.raise_for_errors() return result @@ -378,6 +382,7 @@ def validate_apicall_request( if not issubclass(cls, RequestValidator): raise TypeError("'cls' argument is not type of RequestValidator") v = cls(spec, base_url=base_url, **validator_kwargs) + v.check_spec(spec) return v.validate(request) @@ -402,6 +407,7 @@ def validate_webhook_request( "'cls' argument is not type of WebhookRequestValidator" ) v = cls(spec, base_url=base_url, **validator_kwargs) + v.check_spec(spec) return v.validate(request) @@ -425,6 +431,7 @@ def validate_apicall_response( if not issubclass(cls, ResponseValidator): raise TypeError("'cls' argument is not type of ResponseValidator") v = cls(spec, base_url=base_url, **validator_kwargs) + v.check_spec(spec) return v.validate(request, response) @@ -452,4 +459,5 @@ def validate_webhook_response( "'cls' argument is not type of WebhookResponseValidator" ) v = cls(spec, base_url=base_url, **validator_kwargs) + v.check_spec(spec) return v.validate(request, response) diff --git a/openapi_core/unmarshalling/request/protocols.py b/openapi_core/unmarshalling/request/protocols.py index 388f13c8..c6d0b057 100644 --- a/openapi_core/unmarshalling/request/protocols.py +++ b/openapi_core/unmarshalling/request/protocols.py @@ -15,6 +15,9 @@ class RequestUnmarshaller(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def unmarshal( self, request: Request, @@ -27,6 +30,9 @@ class WebhookRequestUnmarshaller(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def unmarshal( self, request: WebhookRequest, diff --git a/openapi_core/unmarshalling/request/unmarshallers.py b/openapi_core/unmarshalling/request/unmarshallers.py index a003c9db..0b812e1c 100644 --- a/openapi_core/unmarshalling/request/unmarshallers.py +++ b/openapi_core/unmarshalling/request/unmarshallers.py @@ -1,6 +1,7 @@ from typing import Optional from jsonschema_path import SchemaPath +from openapi_spec_validator.validation.types import SpecValidatorType from openapi_core.casting.schemas import schema_casters_factory from openapi_core.casting.schemas.factories import SchemaCastersFactory @@ -88,6 +89,7 @@ def __init__( style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory, media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory, schema_validators_factory: Optional[SchemaValidatorsFactory] = None, + spec_validator_cls: Optional[SpecValidatorType] = None, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, extra_media_type_deserializers: Optional[ @@ -108,6 +110,7 @@ def __init__( style_deserializers_factory=style_deserializers_factory, media_type_deserializers_factory=media_type_deserializers_factory, schema_validators_factory=schema_validators_factory, + spec_validator_cls=spec_validator_cls, format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, @@ -123,6 +126,7 @@ def __init__( style_deserializers_factory=style_deserializers_factory, media_type_deserializers_factory=media_type_deserializers_factory, schema_validators_factory=schema_validators_factory, + spec_validator_cls=spec_validator_cls, format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, diff --git a/openapi_core/unmarshalling/response/protocols.py b/openapi_core/unmarshalling/response/protocols.py index 8666e84d..08c79e9d 100644 --- a/openapi_core/unmarshalling/response/protocols.py +++ b/openapi_core/unmarshalling/response/protocols.py @@ -20,6 +20,9 @@ class ResponseUnmarshaller(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def unmarshal( self, request: Request, @@ -33,6 +36,9 @@ class WebhookResponseUnmarshaller(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def unmarshal( self, request: WebhookRequest, diff --git a/openapi_core/unmarshalling/unmarshallers.py b/openapi_core/unmarshalling/unmarshallers.py index b0a4192b..42c5a6b6 100644 --- a/openapi_core/unmarshalling/unmarshallers.py +++ b/openapi_core/unmarshalling/unmarshallers.py @@ -4,6 +4,7 @@ from typing import Tuple from jsonschema_path import SchemaPath +from openapi_spec_validator.validation.types import SpecValidatorType from openapi_core.casting.schemas import schema_casters_factory from openapi_core.casting.schemas.factories import SchemaCastersFactory @@ -42,6 +43,7 @@ def __init__( style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory, media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory, schema_validators_factory: Optional[SchemaValidatorsFactory] = None, + spec_validator_cls: Optional[SpecValidatorType] = None, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, extra_media_type_deserializers: Optional[ @@ -64,6 +66,7 @@ def __init__( style_deserializers_factory=style_deserializers_factory, media_type_deserializers_factory=media_type_deserializers_factory, schema_validators_factory=schema_validators_factory, + spec_validator_cls=spec_validator_cls, format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, diff --git a/openapi_core/validation/request/protocols.py b/openapi_core/validation/request/protocols.py index e27f5863..8009c50a 100644 --- a/openapi_core/validation/request/protocols.py +++ b/openapi_core/validation/request/protocols.py @@ -15,6 +15,9 @@ class RequestValidator(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def iter_errors( self, request: Request, @@ -33,6 +36,9 @@ class WebhookRequestValidator(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def iter_errors( self, request: WebhookRequest, diff --git a/openapi_core/validation/request/validators.py b/openapi_core/validation/request/validators.py index b1e7ffe0..61a76149 100644 --- a/openapi_core/validation/request/validators.py +++ b/openapi_core/validation/request/validators.py @@ -6,6 +6,9 @@ from typing import Optional from jsonschema_path import SchemaPath +from openapi_spec_validator import OpenAPIV30SpecValidator +from openapi_spec_validator import OpenAPIV31SpecValidator +from openapi_spec_validator.validation.types import SpecValidatorType from openapi_core.casting.schemas import schema_casters_factory from openapi_core.casting.schemas.factories import SchemaCastersFactory @@ -70,6 +73,7 @@ def __init__( style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory, media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory, schema_validators_factory: Optional[SchemaValidatorsFactory] = None, + spec_validator_cls: Optional[SpecValidatorType] = None, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, extra_media_type_deserializers: Optional[ @@ -84,6 +88,7 @@ def __init__( style_deserializers_factory=style_deserializers_factory, media_type_deserializers_factory=media_type_deserializers_factory, schema_validators_factory=schema_validators_factory, + spec_validator_cls=spec_validator_cls, format_validators=format_validators, extra_format_validators=extra_format_validators, extra_media_type_deserializers=extra_media_type_deserializers, @@ -387,53 +392,65 @@ def iter_errors(self, request: WebhookRequest) -> Iterator[Exception]: class V30RequestBodyValidator(APICallRequestBodyValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_write_schema_validators_factory class V30RequestParametersValidator(APICallRequestParametersValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_write_schema_validators_factory class V30RequestSecurityValidator(APICallRequestSecurityValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_write_schema_validators_factory class V30RequestValidator(APICallRequestValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_write_schema_validators_factory class V31RequestBodyValidator(APICallRequestBodyValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31RequestParametersValidator(APICallRequestParametersValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31RequestSecurityValidator(APICallRequestSecurityValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31RequestValidator(APICallRequestValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory path_finder_cls = WebhookPathFinder class V31WebhookRequestBodyValidator(WebhookRequestBodyValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory path_finder_cls = WebhookPathFinder class V31WebhookRequestParametersValidator(WebhookRequestParametersValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory path_finder_cls = WebhookPathFinder class V31WebhookRequestSecurityValidator(WebhookRequestSecurityValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory path_finder_cls = WebhookPathFinder class V31WebhookRequestValidator(WebhookRequestValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory path_finder_cls = WebhookPathFinder diff --git a/openapi_core/validation/response/protocols.py b/openapi_core/validation/response/protocols.py index 7a403d3e..95c4a83d 100644 --- a/openapi_core/validation/response/protocols.py +++ b/openapi_core/validation/response/protocols.py @@ -16,6 +16,9 @@ class ResponseValidator(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def iter_errors( self, request: Request, @@ -36,6 +39,9 @@ class WebhookResponseValidator(Protocol): def __init__(self, spec: SchemaPath, base_url: Optional[str] = None): ... + def check_spec(self, spec: SchemaPath) -> None: + ... + def iter_errors( self, request: WebhookRequest, diff --git a/openapi_core/validation/response/validators.py b/openapi_core/validation/response/validators.py index 5f2ce662..b5ff7088 100644 --- a/openapi_core/validation/response/validators.py +++ b/openapi_core/validation/response/validators.py @@ -7,6 +7,8 @@ from typing import Mapping from jsonschema_path import SchemaPath +from openapi_spec_validator import OpenAPIV30SpecValidator +from openapi_spec_validator import OpenAPIV31SpecValidator from openapi_core.exceptions import OpenAPIError from openapi_core.protocols import Request @@ -330,36 +332,45 @@ def iter_errors( class V30ResponseDataValidator(APICallResponseDataValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_read_schema_validators_factory class V30ResponseHeadersValidator(APICallResponseHeadersValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_read_schema_validators_factory class V30ResponseValidator(APICallResponseValidator): + spec_validator_cls = OpenAPIV30SpecValidator schema_validators_factory = oas30_read_schema_validators_factory class V31ResponseDataValidator(APICallResponseDataValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31ResponseHeadersValidator(APICallResponseHeadersValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31ResponseValidator(APICallResponseValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31WebhookResponseDataValidator(WebhookResponseDataValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31WebhookResponseHeadersValidator(WebhookResponseHeadersValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory class V31WebhookResponseValidator(WebhookResponseValidator): + spec_validator_cls = OpenAPIV31SpecValidator schema_validators_factory = oas31_schema_validators_factory diff --git a/openapi_core/validation/validators.py b/openapi_core/validation/validators.py index 4a864816..a6e549cf 100644 --- a/openapi_core/validation/validators.py +++ b/openapi_core/validation/validators.py @@ -8,6 +8,7 @@ from urllib.parse import urljoin from jsonschema_path import SchemaPath +from openapi_spec_validator.validation.types import SpecValidatorType from openapi_core.casting.schemas import schema_casters_factory from openapi_core.casting.schemas.factories import SchemaCastersFactory @@ -43,6 +44,7 @@ class BaseValidator: schema_validators_factory: SchemaValidatorsFactory = NotImplemented + spec_validator_cls: Optional[SpecValidatorType] = None def __init__( self, @@ -52,6 +54,7 @@ def __init__( style_deserializers_factory: StyleDeserializersFactory = style_deserializers_factory, media_type_deserializers_factory: MediaTypeDeserializersFactory = media_type_deserializers_factory, schema_validators_factory: Optional[SchemaValidatorsFactory] = None, + spec_validator_cls: Optional[SpecValidatorType] = None, format_validators: Optional[FormatValidatorsDict] = None, extra_format_validators: Optional[FormatValidatorsDict] = None, extra_media_type_deserializers: Optional[ @@ -73,10 +76,18 @@ def __init__( raise NotImplementedError( "schema_validators_factory is not assigned" ) + self.spec_validator_cls = spec_validator_cls or self.spec_validator_cls self.format_validators = format_validators self.extra_format_validators = extra_format_validators self.extra_media_type_deserializers = extra_media_type_deserializers + def check_spec(self, spec: SchemaPath) -> None: + if self.spec_validator_cls is None: + return + + validator = self.spec_validator_cls(spec) + validator.validate() + def _find_media_type( self, content: SchemaPath, mimetype: Optional[str] = None ) -> MediaType: diff --git a/poetry.lock b/poetry.lock index e29c0e97..ebf18ac2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1033,23 +1033,6 @@ PyYAML = ">=5.1" referencing = ">=0.28.0,<0.31.0" requests = ">=2.31.0,<3.0.0" -[[package]] -name = "jsonschema-spec" -version = "0.2.4" -description = "JSONSchema Spec with object-oriented paths" -optional = false -python-versions = ">=3.8.0,<4.0.0" -files = [ - {file = "jsonschema_spec-0.2.4-py3-none-any.whl", hash = "sha256:e6dcf7056734ec6854f7888da6c08ce6c421f28aeeddce96bb90de0fb6d711ef"}, - {file = "jsonschema_spec-0.2.4.tar.gz", hash = "sha256:873e396ad1ba6edf9f52d6174c110d4fafb7b5f5894744246a53fe75e5251ec2"}, -] - -[package.dependencies] -pathable = ">=0.4.1,<0.5.0" -PyYAML = ">=5.1" -referencing = ">=0.28.0,<0.31.0" -requests = ">=2.31.0,<3.0.0" - [[package]] name = "jsonschema-specifications" version = "2023.6.1" @@ -1373,19 +1356,19 @@ rfc3339-validator = "*" [[package]] name = "openapi-spec-validator" -version = "0.7.0" +version = "0.7.1" description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator" optional = false python-versions = ">=3.8.0,<4.0.0" files = [ - {file = "openapi_spec_validator-0.7.0-py3-none-any.whl", hash = "sha256:09c1cb83e00537bffdef81a98648869220f7ab03a32981460e0777bc80d63eb8"}, - {file = "openapi_spec_validator-0.7.0.tar.gz", hash = "sha256:03e8cbff36e1cab573e0e2fb51eff3d677f88aba33985d0fffe8753512595f1c"}, + {file = "openapi_spec_validator-0.7.1-py3-none-any.whl", hash = "sha256:3c81825043f24ccbcd2f4b149b11e8231abce5ba84f37065e14ec947d8f4e959"}, + {file = "openapi_spec_validator-0.7.1.tar.gz", hash = "sha256:8577b85a8268685da6f8aa30990b83b7960d4d1117e901d451b5d572605e5ec7"}, ] [package.dependencies] importlib-resources = {version = ">=5.8,<7.0", markers = "python_version < \"3.9\""} jsonschema = ">=4.18.0,<5.0.0" -jsonschema-spec = ">=0.2.3,<0.3.0" +jsonschema-path = ">=0.3.1,<0.4.0" lazy-object-proxy = ">=1.7.1,<2.0.0" openapi-schema-validator = ">=0.6.0,<0.7.0" @@ -2485,4 +2468,4 @@ starlette = ["starlette"] [metadata] lock-version = "2.0" python-versions = "^3.8.0" -content-hash = "c4e8bc0763a0b3f061e7e42a49a0f43cbc4666ef6b31fe748ca83d88e55d9b3c" +content-hash = "54f6f9ffda98506f95c651ddab0fccdd02790773341a193638e07b754aa9426d" diff --git a/pyproject.toml b/pyproject.toml index dcaa6af3..4cb536a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,7 @@ isodate = "*" more-itertools = "*" parse = "*" openapi-schema-validator = "^0.6.0" -openapi-spec-validator = ">=0.6.0,<0.8.0" +openapi-spec-validator = "^0.7.1" requests = {version = "*", optional = true} werkzeug = "*" jsonschema-path = "^0.3.1" diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index 1136240f..736eb9ab 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -4,12 +4,21 @@ @pytest.fixture def spec_v30(): - return SchemaPath.from_dict({"openapi": "3.0"}) + return SchemaPath.from_dict({"openapi": "3.0.0"}) @pytest.fixture def spec_v31(): - return SchemaPath.from_dict({"openapi": "3.1"}) + return SchemaPath.from_dict( + { + "openapi": "3.1.0", + "info": { + "title": "Spec", + "version": "0.0.1", + }, + "paths": {}, + } + ) @pytest.fixture diff --git a/tests/unit/test_shortcuts.py b/tests/unit/test_shortcuts.py index f5fe9c02..170c4cbf 100644 --- a/tests/unit/test_shortcuts.py +++ b/tests/unit/test_shortcuts.py @@ -1,6 +1,7 @@ from unittest import mock import pytest +from openapi_spec_validator import OpenAPIV31SpecValidator from openapi_core import unmarshal_apicall_request from openapi_core import unmarshal_apicall_response @@ -46,6 +47,7 @@ class MockClass: + spec_validator_cls = None schema_validators_factory = None schema_unmarshallers_factory = None @@ -586,6 +588,23 @@ def test_cls_apicall(self, spec_v31): (request,), ] + def test_cls_apicall_with_spec_validator_cls(self, spec_v31): + request = mock.Mock(spec=Request) + TestAPICallReq = type( + "TestAPICallReq", + (MockReqValidator, APICallRequestValidator), + { + "spec_validator_cls": OpenAPIV31SpecValidator, + }, + ) + + result = validate_request(request, spec=spec_v31, cls=TestAPICallReq) + + assert result is None + assert TestAPICallReq.validate_calls == [ + (request,), + ] + def test_cls_webhook(self, spec_v31): request = mock.Mock(spec=Request) TestWebhookReq = type( @@ -601,6 +620,23 @@ def test_cls_webhook(self, spec_v31): (request,), ] + def test_cls_webhook_with_spec_validator_cls(self, spec_v31): + request = mock.Mock(spec=Request) + TestWebhookReq = type( + "TestWebhookReq", + (MockReqValidator, WebhookRequestValidator), + { + "spec_validator_cls": OpenAPIV31SpecValidator, + }, + ) + + result = validate_request(request, spec=spec_v31, cls=TestWebhookReq) + + assert result is None + assert TestWebhookReq.validate_calls == [ + (request,), + ] + def test_webhook_cls(self, spec_v31): request = mock.Mock(spec=WebhookRequest) TestWebhookReq = type(