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

Allow pre loaded AsyncAPISpec in from_spec #160

Merged
merged 8 commits into from
Jan 19, 2022
6 changes: 4 additions & 2 deletions asynction/mock_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -138,7 +139,7 @@ def __init__(
@classmethod
def from_spec(
cls,
spec_path: Path,
spec_path: Union[Path, AsyncApiSpec],
validation: bool = True,
server_name: Optional[str] = None,
docs: bool = True,
Expand All @@ -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 pre loaded AsyncApiSpec object.
:param validation: When set to ``False``, message payloads, channel
bindings and ack callbacks are NOT validated.
Defaults to ``True``.
Expand Down
12 changes: 9 additions & 3 deletions asynction/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -113,7 +114,7 @@ def init_app(self, app: Optional[Flask], **kwargs) -> None:
@classmethod
def from_spec(
cls,
spec_path: Path,
spec_path: Union[Path, AsyncApiSpec],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
spec_path: Union[Path, AsyncApiSpec],
spec_path: Union[Path, JSONMapping],

See the previous comment I made with regards to exposing the AsyncApiSpec type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dedoussis Are you saying to just change the type annotations? how would the implementation change? Would I still be able to pass an AsyncApiSpec or what would I pass?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alex-zywicki This would also be an implementation change.

I propose the from_spec method to accept either a file path or a dict. In the case of dict, the conversion of the given dictionary into an AsyncApiSpec object should happen internally within Asynction. This way we can avoid exposing the type to the user. See point 1 of my comment above:

Instead of passing the typed object, we could have users passing the raw dict of their AsyncAPI spec. Hence, we would have the from_spec factory method supporting file paths as well as raw dict data. The latter will account for use cases such as deep merging multiple spec files into one dict (the use case of yours) or converting class definitions to JSONSchemata.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dedoussis I guess I'm confused about what you meant when you said a while back

@alex-zywicki For the time being, If there is a requirement on your end to ensure that each file has a complete and valid AsyncAPI structure, you can use the existing methods of Asynction, but with the risk of having to modify parts of your code when you upgrade to a new minor version of the package.

validation: bool = True,
server_name: Optional[str] = None,
docs: bool = True,
Expand All @@ -124,7 +125,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 pre loaded AsyncApiSpec object.
:param validation: When set to ``False``, message payloads, channel
bindings and ack callbacks are NOT validated.
Defaults to ``True``.
Expand Down Expand Up @@ -155,7 +157,11 @@ def from_spec(
)

"""
spec = load_spec(spec_path=spec_path)
if isinstance(spec_path, AsyncApiSpec):
spec = spec_path
else:
spec = load_spec(spec_path=spec_path)

server_security: Sequence[SecurityRequirement] = []
if (
server_name is not None
Expand Down
8 changes: 8 additions & 0 deletions tests/unit/test_mock_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from asynction.mock_server import task_scheduler
from asynction.server import AsynctionSocketIO
from asynction.server import _noop_handler
from asynction.server import load_spec
from asynction.types import GLOBAL_NAMESPACE
from asynction.types import AsyncApiSpec
from asynction.types import Channel
Expand Down Expand Up @@ -140,6 +141,13 @@ 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):
spec = load_spec(fixture_paths.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,
Expand Down
6 changes: 6 additions & 0 deletions tests/unit/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ def test_asynction_socketio_from_spec(fixture_paths: FixturePaths):
assert isinstance(asio, AsynctionSocketIO)


def test_asynction_socketio_from_spec_object(fixture_paths: FixturePaths):
spec = load_spec(fixture_paths.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,
):
Expand Down