Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@
ScheduledRequestContent,
ScheduledResponseException,
)
from .specs import (
AsyncAPIService,
OpenAPIService,
)
from .system import (
SystemService,
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from .asyncapi import (
AsyncAPIService,
)
from .openapi import (
OpenAPIService,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from itertools import (
chain,
)

from minos.common import (
Config,
)

from ..decorators import (
EnrouteCollector,
enroute,
)
from ..requests import (
Request,
Response,
)


class AsyncAPIService:
def __init__(self, config: Config):
self.config = config
self.spec = SPECIFICATION_SCHEMA.copy()

@enroute.rest.command("/spec/asyncapi", "GET")
def generate_specification(self, request: Request) -> Response:
events = self.get_events()

for event in events:
topic: str = event["topic"]
event_spec = {}

self.spec["channels"][topic] = event_spec

return Response(self.spec)

def get_events(self) -> list[dict]:
events = list()
for name in self.config.get_services():
decorators = EnrouteCollector(name, self.config).get_broker_event()
events += [{"topic": decorator.topic} for decorator in set(chain(*decorators.values()))]

return events


SPECIFICATION_SCHEMA = {
"asyncapi": "2.3.0",
"info": {"title": "", "version": ""},
"channels": {},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from itertools import (
chain,
)
from operator import (
itemgetter,
)

from minos.common import (
Config,
)

from ..decorators import (
EnrouteCollector,
enroute,
)
from ..requests import (
Request,
Response,
)


class OpenAPIService:
def __init__(self, config: Config):
self.config = config
self.spec = SPECIFICATION_SCHEMA.copy()

# noinspection PyUnusedLocal
@enroute.rest.command("/spec/openapi", "GET")
def generate_specification(self, request: Request) -> Response:
for endpoint in self.endpoints:
url = endpoint["url"]
method = endpoint["method"].lower()

if url in self.spec["paths"]:
self.spec["paths"][url][method] = PATH_SCHEMA
else:
self.spec["paths"][url] = {method: PATH_SCHEMA}

return Response(self.spec)

@property
def endpoints(self) -> list[dict]:
endpoints = list()
for name in self.config.get_services():
decorators = EnrouteCollector(name, self.config).get_rest_command_query()
endpoints += [
{"url": decorator.url, "method": decorator.method} for decorator in set(chain(*decorators.values()))
]

endpoints.sort(key=itemgetter("url", "method"))

return endpoints


SPECIFICATION_SCHEMA = {
"openapi": "3.0.0",
"info": {
"version": "1.0.0",
"title": "Minos OpenAPI Spec",
"description": "Minos OpenAPI Spec",
},
"paths": {},
}

PATH_SCHEMA = {
"responses": {"200": {"description": ""}},
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ class CommandService:
def get_order_rest(self, request: Request) -> Response:
return Response("get_order")

@enroute.rest.command(path="/order", method="DELETE")
def delete_order_rest(self, request: Request) -> Response:
return Response("delete_order")

@enroute.broker.command("GetOrder")
def get_order_command(self, request: Request) -> Response:
return BrokerResponse("get_order")
Expand All @@ -28,7 +32,7 @@ def update_order(self, request: Request) -> Response:
return BrokerResponse("update_order")

@enroute.broker.event("TicketAdded")
def ticket_added(self, request: Request) -> None:
def ticket_added(self, request: Request) -> str:
return "command_service_ticket_added"

@enroute.periodic.event("@daily")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ async def test_subscription(self):
await self.discovery.subscribe()
self.assertEqual(1, mock.call_count)
expected = call(
self.ip, 8080, "Order", [{"url": "/order", "method": "GET"}, {"url": "/ticket", "method": "POST"}]
self.ip,
8080,
"Order",
[
{"url": "/order", "method": "DELETE"},
{"url": "/order", "method": "GET"},
{"url": "/ticket", "method": "POST"},
],
)
self.assertEqual(expected, mock.call_args)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import unittest

from minos.common import (
Config,
)
from minos.networks import (
AsyncAPIService,
EnrouteCollector,
InMemoryRequest,
RestCommandEnrouteDecorator,
)
from tests.utils import (
CONFIG_FILE_PATH,
)


class TestAsyncAPIService(unittest.IsolatedAsyncioTestCase):
def setUp(self) -> None:
super().setUp()
self.config = Config(CONFIG_FILE_PATH)

def test_constructor(self):
service = AsyncAPIService(self.config)
self.assertIsInstance(service, AsyncAPIService)
self.assertEqual(self.config, service.config)

def test_get_enroute(self):
service = AsyncAPIService(self.config)
expected = {
service.generate_specification.__name__: {RestCommandEnrouteDecorator("/spec/asyncapi", "GET")},
}
observed = EnrouteCollector(service, self.config).get_all()
self.assertEqual(expected, observed)

async def test_generate_spec(self):
service = AsyncAPIService(self.config)

request = InMemoryRequest()
response = service.generate_specification(request)

expected = {
"asyncapi": "2.3.0",
"info": {"title": "", "version": ""},
"channels": {"TicketAdded": {}, "TicketDeleted": {}},
}

self.assertEqual(expected, await response.content())


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import unittest

from minos.common import (
Config,
)
from minos.networks import (
EnrouteCollector,
InMemoryRequest,
OpenAPIService,
RestCommandEnrouteDecorator,
)
from tests.utils import (
CONFIG_FILE_PATH,
)


class TestOpenAPIService(unittest.IsolatedAsyncioTestCase):
def setUp(self) -> None:
super().setUp()
self.config = Config(CONFIG_FILE_PATH)

def test_constructor(self):
service = OpenAPIService(self.config)
self.assertIsInstance(service, OpenAPIService)
self.assertEqual(self.config, service.config)

def test_get_enroute(self):
service = OpenAPIService(self.config)
expected = {
service.generate_specification.__name__: {RestCommandEnrouteDecorator("/spec/openapi", "GET")},
}
observed = EnrouteCollector(service, self.config).get_all()
self.assertEqual(expected, observed)

async def test_generate_spec(self):
service = OpenAPIService(self.config)

request = InMemoryRequest()
response = service.generate_specification(request)

expected = {
"openapi": "3.0.0",
"info": {"version": "1.0.0", "title": "Minos OpenAPI Spec", "description": "Minos OpenAPI Spec"},
"paths": {
"/order": {
"delete": {"responses": {"200": {"description": ""}}},
"get": {"responses": {"200": {"description": ""}}},
},
"/ticket": {"post": {"responses": {"200": {"description": ""}}}},
},
}

self.assertEqual(expected, await response.content())


if __name__ == "__main__":
unittest.main()