diff --git a/asynction/mock_server.py b/asynction/mock_server.py index 65ad245..0e69e58 100644 --- a/asynction/mock_server.py +++ b/asynction/mock_server.py @@ -17,6 +17,7 @@ from typing import MutableSequence from typing import Optional from typing import Sequence +from typing import Union from faker import Faker from faker.exceptions import UnsupportedFeature @@ -138,7 +139,7 @@ def __init__( @classmethod def from_spec( cls, - spec_path: Path, + spec_path: Union[Path, JSONMapping], validation: bool = True, server_name: Optional[str] = None, docs: bool = True, @@ -160,7 +161,8 @@ def from_spec( * ``custom_formats_sample_size`` - :param spec_path: The path where the AsyncAPI YAML specification is located. + :param spec_path: The path where the AsyncAPI YAML specification is located, + or a dictionary object of the AsyncAPI data structure :param validation: When set to ``False``, message payloads, channel bindings and ack callbacks are NOT validated. Defaults to ``True``. diff --git a/asynction/server.py b/asynction/server.py index 8397229..53e9de4 100644 --- a/asynction/server.py +++ b/asynction/server.py @@ -7,6 +7,7 @@ from typing import Any from typing import Optional from typing import Sequence +from typing import Union from urllib.parse import urlparse import jsonschema @@ -67,13 +68,15 @@ def resolve_references(raw_spec: JSONMapping) -> JSONMapping: return deep_resolve(raw_spec, resolver) -def load_spec(spec_path: Path) -> AsyncApiSpec: - with open(spec_path) as f: - serialized = f.read() - raw = yaml.safe_load(serialized) - - raw_resolved = resolve_references(raw_spec=raw) +def load_spec(spec_path: Union[Path, JSONMapping]) -> AsyncApiSpec: + if isinstance(spec_path, Path): + with open(spec_path) as f: + serialized = f.read() + spec = yaml.safe_load(serialized) + else: + spec = spec_path + raw_resolved = resolve_references(spec) return AsyncApiSpec.from_dict(raw_resolved) @@ -113,7 +116,7 @@ def init_app(self, app: Optional[Flask], **kwargs) -> None: @classmethod def from_spec( cls, - spec_path: Path, + spec_path: Union[Path, JSONMapping], validation: bool = True, server_name: Optional[str] = None, docs: bool = True, @@ -124,7 +127,8 @@ def from_spec( """Create a Flask-SocketIO server from an AsyncAPI spec. This is the single entrypoint to the Asynction server API. - :param spec_path: The path where the AsyncAPI YAML specification is located. + :param spec_path: The path where the AsyncAPI YAML specification is located, + or a dictionary object of the AsyncAPI data structure :param validation: When set to ``False``, message payloads, channel bindings and ack callbacks are NOT validated. Defaults to ``True``. @@ -156,6 +160,7 @@ def from_spec( """ spec = load_spec(spec_path=spec_path) + server_security: Sequence[SecurityRequirement] = [] if ( server_name is not None diff --git a/tests/unit/test_mock_server.py b/tests/unit/test_mock_server.py index bfafcb9..d5b7b7c 100644 --- a/tests/unit/test_mock_server.py +++ b/tests/unit/test_mock_server.py @@ -12,6 +12,7 @@ import jsonschema import pytest +import yaml from faker import Faker from flask.app import Flask from flask_socketio import SocketIO @@ -140,6 +141,14 @@ def test_mock_asynction_socketio_from_spec(fixture_paths: FixturePaths): assert isinstance(mock_asio.faker, Faker) +def test_mock_asynction_socketio_from_spec_object(fixture_paths: FixturePaths): + with open(fixture_paths.simple, "r") as simple: + spec = yaml.safe_load(simple) + mock_asio = MockAsynctionSocketIO.from_spec(spec_path=spec) + assert isinstance(mock_asio, MockAsynctionSocketIO) + assert isinstance(mock_asio.faker, Faker) + + def new_mock_asynction_socket_io( spec: AsyncApiSpec, app: Optional[Flask] = None, diff --git a/tests/unit/test_server.py b/tests/unit/test_server.py index c1aa5dd..a7ead9b 100644 --- a/tests/unit/test_server.py +++ b/tests/unit/test_server.py @@ -2,6 +2,7 @@ from unittest import mock import pytest +import yaml from faker import Faker from flask import Flask @@ -50,6 +51,13 @@ def test_asynction_socketio_from_spec(fixture_paths: FixturePaths): assert isinstance(asio, AsynctionSocketIO) +def test_asynction_socketio_from_spec_object(fixture_paths: FixturePaths): + with open(fixture_paths.simple, "r") as simple: + spec = yaml.safe_load(simple) + asio = AsynctionSocketIO.from_spec(spec_path=spec) + assert isinstance(asio, AsynctionSocketIO) + + def test_asynction_socketio_from_spec_uses_spec_server_path_as_socketio_path( fixture_paths: FixturePaths, ):