Skip to content

Commit 42d30d8

Browse files
authored
Merge pull request #145 from rsocket/cloudevents
Cloudevents
2 parents 9dd4eb5 + 0f07584 commit 42d30d8

File tree

16 files changed

+107
-34
lines changed

16 files changed

+107
-34
lines changed

CHANGELOG.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
Changelog
22
---------
33

4+
v0.4.11
5+
=======
6+
- Breaking change: RequestRouter argument 'payload_mapper' was replaced with 'payload_deserializer' and 'payload_serializer'
7+
- Added CloutEvent serialize/deserialize helpers for use in RequestRouter
8+
49
v0.4.10
5-
======
10+
=======
611
- Code cleanup
712
- Breaking change: Removed deprecated rsocket.routing.helpers module
813
- Added CloudEvents client/server usage example (compatible with java rsocket example from cloudevents/sdk-java)

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
author = 'jellofishi@pm.me'
2525

2626
# The full version, including alpha/beta/rc tags
27-
release = '0.4.10'
27+
release = '0.4.11'
2828

2929
# -- General configuration ---------------------------------------------------
3030

examples/cloudevents/client_cloudevents.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,10 @@ async def main(server_port: int):
2525
'source': 'https://spring.io/foos'
2626
}, data=json.dumps({'value': 'Dave'}))
2727

28-
data = to_json(event)
29-
response = await client.request_response(Payload(data=data, metadata=composite(route('event'))))
28+
response = await client.request_response(Payload(data=to_json(event), metadata=composite(route('event'))))
3029

31-
event = from_json(CloudEvent, response.data)
32-
response_data = json.loads(event.data)
30+
response_event = from_json(CloudEvent, response.data)
31+
response_data = json.loads(response_event.data)
3332

3433
assert response_data['value'] == 'Dave'
3534

examples/cloudevents/server_cloudevents.py

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,24 @@
33
import logging
44
import sys
55

6-
from cloudevents.conversion import to_json, from_json
76
from cloudevents.pydantic import CloudEvent
87

9-
from rsocket.helpers import create_future
10-
from rsocket.payload import Payload
8+
from rsocket.cloudevents.serialize import cloud_event_deserialize, cloud_event_serialize
119
from rsocket.routing.request_router import RequestRouter
1210
from rsocket.routing.routing_request_handler import RoutingRequestHandler
1311
from rsocket.rsocket_server import RSocketServer
1412
from rsocket.transports.tcp import TransportTCP
1513

16-
router = RequestRouter()
14+
router = RequestRouter(cloud_event_deserialize,
15+
cloud_event_serialize)
1716

1817

1918
@router.response('event')
20-
async def single_request_response(payload):
21-
received_event = from_json(CloudEvent, payload.data)
22-
received_data = json.loads(received_event.data)
23-
24-
event = CloudEvent.create(attributes={
19+
async def event_response(event: CloudEvent) -> CloudEvent:
20+
return CloudEvent.create(attributes={
2521
'type': 'io.spring.event.Foo',
2622
'source': 'https://spring.io/foos'
27-
}, data=json.dumps(received_data))
28-
29-
return create_future(Payload(to_json(event)))
23+
}, data=json.dumps(json.loads(event.data)))
3024

3125

3226
def handler_factory():
@@ -39,9 +33,7 @@ async def run_server(server_port):
3933
def session(*connection):
4034
RSocketServer(TransportTCP(*connection), handler_factory=handler_factory)
4135

42-
server = await asyncio.start_server(session, 'localhost', server_port)
43-
44-
async with server:
36+
async with await asyncio.start_server(session, 'localhost', server_port) as server:
4537
await server.serve_forever()
4638

4739

examples/tutorial/reactivex/chat_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def remove(self):
115115
del chat_data.user_session_by_id[self._session.session_id]
116116

117117
def router_factory(self):
118-
router = RequestRouter(payload_mapper=decode_payload)
118+
router = RequestRouter(payload_deserializer=decode_payload)
119119

120120
@router.response('login')
121121
async def login(username: str) -> Observable:

examples/tutorial/step4/chat_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __init__(self):
8989
self._session: Optional[UserSessionData] = None
9090

9191
def router_factory(self):
92-
router = RequestRouter(payload_mapper=decode_payload)
92+
router = RequestRouter(payload_deserializer=decode_payload)
9393

9494
@router.response('login')
9595
async def login(payload: Payload) -> Awaitable[Payload]:

examples/tutorial/step5/chat_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def __init__(self):
9292
self._session: Optional[UserSessionData] = None
9393

9494
def router_factory(self):
95-
router = RequestRouter(payload_mapper=decode_payload)
95+
router = RequestRouter(payload_deserializer=decode_payload)
9696

9797
@router.response('login')
9898
async def login(username: str) -> Awaitable[Payload]:

examples/tutorial/step6/chat_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def __init__(self):
108108
self._requested_statistics = ServerStatisticsRequest()
109109

110110
def router_factory(self):
111-
router = RequestRouter(payload_mapper=decode_payload)
111+
router = RequestRouter(payload_deserializer=decode_payload)
112112

113113
@router.response('login')
114114
async def login(username: str) -> Awaitable[Payload]:

examples/tutorial/step7/chat_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def __init__(self):
107107
self._requested_statistics = ServerStatisticsRequest()
108108

109109
def router_factory(self):
110-
router = RequestRouter(payload_mapper=decode_payload)
110+
router = RequestRouter(payload_deserializer=decode_payload)
111111

112112
@router.response('login')
113113
async def login(username: str) -> Awaitable[Payload]:

examples/tutorial/step8/chat_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ def __init__(self):
110110
self._requested_statistics = ServerStatisticsRequest()
111111

112112
def router_factory(self):
113-
router = RequestRouter(payload_mapper=decode_payload)
113+
router = RequestRouter(payload_deserializer=decode_payload)
114114

115115
@router.response('login')
116116
async def login(username: str) -> Awaitable[Payload]:

rsocket/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '0.4.10'
1+
__version__ = '0.4.11'

rsocket/cloudevents/__init__.py

Whitespace-only changes.

rsocket/cloudevents/serialize.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import Any
2+
3+
from cloudevents.conversion import to_json, from_json
4+
from cloudevents.pydantic import CloudEvent
5+
6+
from rsocket.payload import Payload
7+
8+
9+
def cloud_event_deserialize(cls, payload: Payload) -> Any:
10+
if cls == CloudEvent:
11+
return from_json(CloudEvent, payload.data)
12+
13+
return payload
14+
15+
16+
def cloud_event_serialize(cls, value: Any) -> Payload:
17+
if cls == CloudEvent:
18+
return Payload(to_json(value))
19+
20+
return value

rsocket/routing/request_router.py

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from asyncio import Future
12
from dataclasses import dataclass
23
from inspect import signature
34
from typing import Callable, Any, Dict
@@ -6,6 +7,7 @@
67
from rsocket.extensions.composite_metadata import CompositeMetadata
78
from rsocket.frame import FrameType
89
from rsocket.frame_helpers import safe_len
10+
from rsocket.helpers import create_future
911
from rsocket.payload import Payload
1012
from rsocket.rsocket import RSocket
1113

@@ -58,12 +60,16 @@ class RequestRouter:
5860
'_fnf_routes',
5961
'_metadata_push',
6062
'_route_map_by_frame_type',
61-
'_payload_mapper',
63+
'_payload_deserializer',
64+
'_payload_serializer',
6265
'_unknown'
6366
)
6467

65-
def __init__(self, payload_mapper=lambda cls, _: _):
66-
self._payload_mapper = payload_mapper
68+
def __init__(self,
69+
payload_deserializer=lambda cls, _: _,
70+
payload_serializer=lambda cls, _: _):
71+
self._payload_serializer = payload_serializer
72+
self._payload_deserializer = payload_deserializer
6773
self._channel_routes: Dict[str, RouteInfo] = {}
6874
self._stream_routes: Dict[str, RouteInfo] = {}
6975
self._response_routes: Dict[str, RouteInfo] = {}
@@ -148,7 +154,15 @@ async def route(self,
148154
payload,
149155
composite_metadata)
150156

151-
return await route_info.method(**route_kwargs)
157+
result = await route_info.method(**route_kwargs)
158+
159+
if frame_type == FrameType.REQUEST_RESPONSE and not isinstance(result, Future):
160+
if not isinstance(result, Payload):
161+
result = self._payload_serializer(route_info.signature.return_annotation, result)
162+
163+
return create_future(result)
164+
165+
return result
152166

153167
def _collect_route_arguments(self,
154168
route_info: RouteInfo,
@@ -163,10 +177,12 @@ def _collect_route_arguments(self,
163177
if 'composite_metadata' == parameter or parameter_type is CompositeMetadata:
164178
route_kwargs['composite_metadata'] = composite_metadata
165179
else:
180+
payload_data = payload
181+
166182
if parameter_type.annotation not in (Payload, parameter_type.empty):
167-
payload = self._payload_mapper(parameter_type.annotation, payload)
183+
payload_data = self._payload_deserializer(parameter_type.annotation, payload)
168184

169-
route_kwargs[parameter] = payload
185+
route_kwargs[parameter] = payload_data
170186

171187
return route_kwargs
172188

tests/rsocket/cloudevents/__init__.py

Whitespace-only changes.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import json
2+
3+
from cloudevents.conversion import to_json, from_json
4+
from cloudevents.pydantic import CloudEvent
5+
6+
from rsocket.cloudevents.serialize import cloud_event_deserialize, cloud_event_serialize
7+
from rsocket.extensions.helpers import composite, route
8+
from rsocket.extensions.mimetypes import WellKnownMimeTypes
9+
from rsocket.payload import Payload
10+
from rsocket.routing.request_router import RequestRouter
11+
from rsocket.routing.routing_request_handler import RoutingRequestHandler
12+
13+
14+
async def test_routed_cloudevents(lazy_pipe):
15+
router = RequestRouter(cloud_event_deserialize,
16+
cloud_event_serialize)
17+
18+
def handler_factory():
19+
return RoutingRequestHandler(router)
20+
21+
@router.response('cloud_event')
22+
async def response_request(value: CloudEvent) -> CloudEvent:
23+
return CloudEvent.create(attributes={
24+
'type': 'io.spring.event.Foo',
25+
'source': 'https://spring.io/foos'
26+
}, data=json.dumps(json.loads(value.data)))
27+
28+
async with lazy_pipe(
29+
client_arguments={'metadata_encoding': WellKnownMimeTypes.MESSAGE_RSOCKET_COMPOSITE_METADATA},
30+
server_arguments={'handler_factory': handler_factory}) as (server, client):
31+
event = CloudEvent.create(attributes={
32+
'type': 'io.spring.event.Foo',
33+
'source': 'https://spring.io/foos'
34+
}, data=json.dumps({'value': 'Dave'}))
35+
36+
response = await client.request_response(Payload(data=to_json(event), metadata=composite(route('cloud_event'))))
37+
38+
response_event = from_json(CloudEvent, response.data)
39+
response_data = json.loads(response_event.data)
40+
41+
assert response_data['value'] == 'Dave'

0 commit comments

Comments
 (0)