diff --git a/connexion/operations/openapi.py b/connexion/operations/openapi.py index 8ace3f372..783a31187 100644 --- a/connexion/operations/openapi.py +++ b/connexion/operations/openapi.py @@ -7,7 +7,7 @@ from connexion.datastructures import MediaTypeDict from connexion.operations.abstract import AbstractOperation from connexion.uri_parsing import OpenAPIURIParser -from connexion.utils import deep_get +from connexion.utils import deep_get, generate_example logger = logging.getLogger("connexion.operations.openapi3") @@ -210,6 +210,10 @@ def _nested_example(self, schema): try: # Recurse if schema is an array return [self._nested_example(schema["items"])] + except KeyError: + pass + try: + return generate_example(schema) except KeyError: raise diff --git a/connexion/operations/swagger2.py b/connexion/operations/swagger2.py index 3dba7aa38..8b4f23ba6 100644 --- a/connexion/operations/swagger2.py +++ b/connexion/operations/swagger2.py @@ -8,7 +8,7 @@ from connexion.exceptions import InvalidSpecification from connexion.operations.abstract import AbstractOperation from connexion.uri_parsing import Swagger2URIParser -from connexion.utils import deep_get +from connexion.utils import deep_get, generate_example logger = logging.getLogger("connexion.operations.swagger2") @@ -232,6 +232,10 @@ def _nested_example(self, schema): try: # Recurse if schema is an array return [self._nested_example(schema["items"])] + except KeyError: + pass + try: + return generate_example(schema) except KeyError: raise diff --git a/connexion/utils.py b/connexion/utils.py index 259e45753..a3b18aad6 100644 --- a/connexion/utils.py +++ b/connexion/utils.py @@ -12,6 +12,7 @@ import typing as t import yaml +from jsf import JSF from starlette.routing import compile_path from connexion.exceptions import TypeValidationError @@ -512,3 +513,8 @@ def sort_apis_by_basepath(apis: t.List["API"]) -> t.List["API"]: :return: List of APIs sorted by basepath """ return sort_routes(apis, key=lambda api: api.base_path or "/") + + +def generate_example(schema): + faker = JSF(schema) + return faker.generate() diff --git a/pyproject.toml b/pyproject.toml index 2ac6c6057..91a1abb41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -61,11 +61,13 @@ a2wsgi = { version = ">= 1.7", optional = true } flask = { version = ">= 2.2", extras = ["async"], optional = true } swagger-ui-bundle = { version = ">= 1.1.0", optional = true } uvicorn = { version = ">= 0.17.6", extras = ["standard"], optional = true } +jsf = { version = ">=0.10.0", optional = true } [tool.poetry.extras] flask = ["a2wsgi", "flask"] swagger-ui = ["swagger-ui-bundle"] uvicorn = ["uvicorn"] +mock = ["jsf"] [tool.poetry.group.tests.dependencies] pre-commit = "~2.21.0" @@ -106,4 +108,4 @@ exclude_lines = [ [[tool.mypy.overrides]] module = "referencing.jsonschema.*" -follow_imports = "skip" \ No newline at end of file +follow_imports = "skip" diff --git a/tests/test_mock.py b/tests/test_mock.py index 1307f9298..3a2e20a3e 100644 --- a/tests/test_mock.py +++ b/tests/test_mock.py @@ -224,7 +224,8 @@ def test_mock_resolver_no_example_nested_in_object(): response, status_code = resolver.mock_operation(operation) assert status_code == 200 - assert response == "No example response was defined." + assert isinstance(response, dict) + assert isinstance(response["foo"], str) def test_mock_resolver_no_example_nested_in_list_openapi(): @@ -256,7 +257,8 @@ def test_mock_resolver_no_example_nested_in_list_openapi(): response, status_code = resolver.mock_operation(operation) assert status_code == 202 - assert response == "No example response was defined." + assert isinstance(response, list) + assert all(isinstance(c, str) for c in response) def test_mock_resolver_no_examples(): diff --git a/tests/test_mock2.py b/tests/test_mock2.py new file mode 100644 index 000000000..e591bf338 --- /dev/null +++ b/tests/test_mock2.py @@ -0,0 +1,144 @@ +from re import fullmatch + +from connexion.utils import generate_example + + +def test_generate_example_string(): + + schema = { + "type": "string", + } + + example = generate_example(schema) + + assert isinstance(example, str) + + +def test_generate_example_integer(): + + schema = { + "type": "integer", + } + + example = generate_example(schema) + + assert isinstance(example, int) + + +def test_generate_example_number(): + + schema = { + "type": "number", + } + + example = generate_example(schema) + + assert isinstance(example, float) + + +def test_generate_example_boolean(): + + schema = { + "type": "boolean", + } + + example = generate_example(schema) + + assert isinstance(example, bool) + + +def test_generate_example_integer_minimum(): + + schema = { + "type": "integer", + "minimum": 4, + } + + example = generate_example(schema) + + assert example >= schema["minimum"] and isinstance(example, int) + + +def test_generate_example_integer_maximum(): + + schema = { + "type": "integer", + "maximum": 17, + } + + example = generate_example(schema) + + assert example <= schema["maximum"] and isinstance(example, int) + + +def test_generate_example_integer_exclusive_minimum(): + + schema = { + "type": "integer", + "minimum": 4, + "exclusiveMinimum": True, + } + example = generate_example(schema) + + assert example > schema["minimum"] and isinstance(example, int) + + +def test_generate_example_integer_exclusive_maximum(): + + schema = { + "type": "integer", + "maximum": 17, + "exclusiveMaximum": True, + } + + example = generate_example(schema) + + assert example < schema["maximum"] and isinstance(example, int) + + +def test_generate_example_string_regular_expression(): + + pattern = "^\d{3}-\d{2}-\d{4}$" + + schema = { + "type": "string", + "pattern": pattern, + } + + example = generate_example(schema) + + assert fullmatch(pattern, example) != None and isinstance(example, str) + + +def test_generate_example_string_maximum(): + + schema = { + "type": "string", + "maxLength": 20, + } + + example = generate_example(schema) + + assert isinstance(example, str) and len(example) <= schema["maxLength"] + + +def test_generate_example_string_minimum(): + + schema = { + "type": "string", + "minLength": 20, + } + + example = generate_example(schema) + + assert isinstance(example, str) and len(example) >= schema["minLength"] + + +def test_generate_example_enum(): + + schema = {"type": "string", "enum": ["asc", "desc"]} + + example = generate_example(schema) + + assert isinstance(example, str) + assert example == "asc" or example == "desc"