diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1e1bcf41..a7a25ae6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -8,6 +8,7 @@ v0.4.3 - Added on_ready callback to RSocketServer. Called when sender/receiver tasks are ready - Implement ReactiveX (3.0, 4.0) server side handler. Allows to define RequestHandler directly using ReactiveX - Added sending_done_event argument to request_channel to allow client to wait until sending to server is complete/canceled +- Added find_by_mimetype to CompositeMetadata class. Returns list of relevant items by mimetype - Breaking Change: Removed RSocketBase class dependency from RequestHandler. It is not longer required as an argument to __init__ v0.4.2 diff --git a/rsocket/extensions/composite_metadata.py b/rsocket/extensions/composite_metadata.py index 521878c5..f46f8a7e 100644 --- a/rsocket/extensions/composite_metadata.py +++ b/rsocket/extensions/composite_metadata.py @@ -1,8 +1,8 @@ -from typing import List, Type +from typing import List, Type, Union from rsocket.extensions.authentication_content import AuthenticationContent from rsocket.extensions.composite_metadata_item import CompositeMetadataItem -from rsocket.extensions.mimetypes import WellKnownMimeTypes +from rsocket.extensions.mimetypes import WellKnownMimeTypes, ensure_encoding_name from rsocket.extensions.routing import RoutingMetadata from rsocket.extensions.stream_data_mimetype import StreamDataMimetype from rsocket.extensions.stream_data_mimetype import StreamDataMimetypes @@ -40,6 +40,10 @@ def append(self, item: CompositeMetadataItem) -> 'CompositeMetadata': self.items.append(item) return self + def find_by_mimetype(self, mimetype: Union[WellKnownMimeTypes, str, bytes]) -> List[CompositeMetadataItem]: + mimetype_name = ensure_encoding_name(mimetype) + return [item for item in self.items if ensure_encoding_name(item.encoding) == mimetype_name] + def extend(self, *items: CompositeMetadataItem) -> 'CompositeMetadata': self.items.extend(items) return self diff --git a/rsocket/extensions/composite_metadata_item.py b/rsocket/extensions/composite_metadata_item.py index 81bd5309..1ea8643a 100644 --- a/rsocket/extensions/composite_metadata_item.py +++ b/rsocket/extensions/composite_metadata_item.py @@ -1,6 +1,6 @@ from typing import Union, Optional -from rsocket.extensions.mimetypes import WellKnownMimeTypes +from rsocket.extensions.mimetypes import WellKnownMimeTypes, WellKnownMimeType, ensure_well_known_encoding_enum_value _default = object() @@ -18,9 +18,9 @@ class CompositeMetadataItem: ) def __init__(self, - encoding: Union[bytes, WellKnownMimeTypes] = _default, + encoding: Union[bytes, WellKnownMimeTypes, WellKnownMimeType] = _default, body: Optional[bytes] = _default): - self.encoding = default_or_value(encoding) + self.encoding = ensure_well_known_encoding_enum_value(default_or_value(encoding)) self.content = default_or_value(body) def parse(self, buffer: bytes): diff --git a/rsocket/extensions/helpers.py b/rsocket/extensions/helpers.py index 6115b8fb..8bded8ef 100644 --- a/rsocket/extensions/helpers.py +++ b/rsocket/extensions/helpers.py @@ -14,7 +14,8 @@ def composite(*items) -> bytes: return metadata.serialize() -def metadata_item(data: bytes, encoding: Union[bytes, WellKnownMimeTypes]) -> CompositeMetadataItem: +def metadata_item(data: bytes, + encoding: Union[bytes, WellKnownMimeTypes, WellKnownMimeType]) -> CompositeMetadataItem: return CompositeMetadataItem(encoding, data) @@ -30,11 +31,11 @@ def route(*paths: str) -> CompositeMetadataItem: return RoutingMetadata(list(paths)) -def data_mime_type(metadata_mime_type: Union[bytes, WellKnownMimeType]) -> CompositeMetadataItem: +def data_mime_type(metadata_mime_type: Union[bytes, WellKnownMimeType]) -> StreamDataMimetype: return StreamDataMimetype(metadata_mime_type) -def data_mime_types(*metadata_mime_types: Union[bytes, WellKnownMimeType]) -> CompositeMetadataItem: +def data_mime_types(*metadata_mime_types: Union[bytes, WellKnownMimeType]) -> StreamDataMimetypes: return StreamDataMimetypes(list(metadata_mime_types)) diff --git a/rsocket/extensions/mimetype.py b/rsocket/extensions/mimetype.py new file mode 100644 index 00000000..724d038e --- /dev/null +++ b/rsocket/extensions/mimetype.py @@ -0,0 +1,19 @@ +class WellKnownType: + __slots__ = ( + 'name', + 'id' + ) + + def __init__(self, name: bytes, id_: int): + self.name = name + self.id = id_ + + def __eq__(self, other): + return self.name == other.name and self.id == other.id + + def __hash__(self): + return hash((self.id, self.name)) + + +class WellKnownMimeType(WellKnownType): + pass diff --git a/rsocket/extensions/mimetypes.py b/rsocket/extensions/mimetypes.py index fba466b3..af5a1340 100644 --- a/rsocket/extensions/mimetypes.py +++ b/rsocket/extensions/mimetypes.py @@ -2,12 +2,9 @@ from typing import Optional, Union from rsocket.exceptions import RSocketUnknownMimetype +from rsocket.extensions.mimetype import WellKnownMimeType from rsocket.frame_helpers import ensure_bytes -from rsocket.helpers import WellKnownType, map_types_by_id, map_types_by_name - - -class WellKnownMimeType(WellKnownType): - pass +from rsocket.helpers import map_types_by_id, map_types_by_name @unique @@ -92,3 +89,10 @@ def ensure_encoding_name(encoding: Union[WellKnownMimeTypes, str, bytes]) -> byt return encoding.value.name return ensure_bytes(encoding) + + +def ensure_well_known_encoding_enum_value(data_encoding): + if isinstance(data_encoding, WellKnownMimeTypes): + data_encoding = data_encoding.value + + return data_encoding diff --git a/rsocket/extensions/stream_data_mimetype.py b/rsocket/extensions/stream_data_mimetype.py index 76d19554..45401913 100644 --- a/rsocket/extensions/stream_data_mimetype.py +++ b/rsocket/extensions/stream_data_mimetype.py @@ -1,17 +1,10 @@ from typing import Optional, List, Union from rsocket.extensions.composite_metadata_item import CompositeMetadataItem -from rsocket.extensions.mimetypes import WellKnownMimeTypes, WellKnownMimeType +from rsocket.extensions.mimetypes import WellKnownMimeTypes, WellKnownMimeType, ensure_well_known_encoding_enum_value from rsocket.helpers import parse_well_known_encoding, serialize_well_known_encoding -def ensure_well_known_encoding_enum_value(data_encoding): - if isinstance(data_encoding, WellKnownMimeTypes): - data_encoding = data_encoding.value - - return data_encoding - - class StreamDataMimetype(CompositeMetadataItem): __slots__ = ( 'data_encoding' diff --git a/rsocket/helpers.py b/rsocket/helpers.py index a4728dbe..e8c527d9 100644 --- a/rsocket/helpers.py +++ b/rsocket/helpers.py @@ -9,6 +9,7 @@ from reactivestreams.subscriber import Subscriber from reactivestreams.subscription import DefaultSubscription from rsocket.exceptions import RSocketTransportError +from rsocket.extensions.mimetype import WellKnownType from rsocket.frame import Frame from rsocket.frame_helpers import serialize_128max_value, parse_type from rsocket.logger import logger @@ -43,23 +44,6 @@ def subscribe(self, subscriber: Subscriber): subscriber.on_subscribe(self) -class WellKnownType: - __slots__ = ( - 'name', - 'id' - ) - - def __init__(self, name: bytes, id_: int): - self.name = name - self.id = id_ - - def __eq__(self, other): - return self.name == other.name and self.id == other.id - - def __hash__(self): - return hash((self.id, self.name)) - - def map_types_by_name(types): return {value.value.name: value.value for value in types} diff --git a/tests/rsocket/test_composite_metadata.py b/tests/rsocket/test_composite_metadata.py index 9c16e318..c4c07725 100644 --- a/tests/rsocket/test_composite_metadata.py +++ b/tests/rsocket/test_composite_metadata.py @@ -1,10 +1,14 @@ +from typing import cast + import pytest from rsocket.exceptions import RSocketError from rsocket.extensions.composite_metadata import CompositeMetadata -from rsocket.extensions.helpers import composite, data_mime_type, data_mime_types +from rsocket.extensions.helpers import composite, data_mime_type, data_mime_types, route, authenticate_simple, \ + metadata_item from rsocket.extensions.mimetypes import WellKnownMimeTypes from rsocket.extensions.routing import RoutingMetadata +from rsocket.extensions.stream_data_mimetype import StreamDataMimetypes, StreamDataMimetype def test_tag_composite_metadata_too_long(): @@ -23,7 +27,8 @@ def test_data_mime_type_composite_metadata(): composite_metadata.parse(data) assert len(composite_metadata.items) == 1 - assert composite_metadata.items[0].data_encoding == b'application/json' + metadata_item_1 = cast(StreamDataMimetype, composite_metadata.items[0]) + assert metadata_item_1.data_encoding == b'application/json' assert composite_metadata.serialize() == data @@ -38,7 +43,36 @@ def test_data_mime_types_composite_metadata(): composite_metadata.parse(data) assert len(composite_metadata.items) == 1 - assert composite_metadata.items[0].data_encodings[0] == b'application/json' - assert composite_metadata.items[0].data_encodings[1] == b'text/xml' + from typing import cast + metadata_item_1 = cast(StreamDataMimetypes, composite_metadata.items[0]) + + assert metadata_item_1.data_encodings[0] == b'application/json' + assert metadata_item_1.data_encodings[1] == b'text/xml' assert composite_metadata.serialize() == data + + +def test_composite_metadata_find_by_mimetype(): + data = composite( + data_mime_types( + WellKnownMimeTypes.APPLICATION_JSON, + WellKnownMimeTypes.TEXT_XML + ), + route('login'), + authenticate_simple('abcd', '1234'), + metadata_item(b'some_data_1', WellKnownMimeTypes.TEXT_PLAIN), + metadata_item(b'some_data_2', WellKnownMimeTypes.TEXT_PLAIN), + metadata_item(b'{"key":1}', WellKnownMimeTypes.APPLICATION_JSON), + ) + + composite_metadata = CompositeMetadata() + composite_metadata.parse(data) + + plain_text = composite_metadata.find_by_mimetype(WellKnownMimeTypes.TEXT_PLAIN) + + assert len(plain_text) == 2 + assert plain_text[0].content == b'some_data_1' + assert plain_text[1].content == b'some_data_2' + + authentication_items = composite_metadata.find_by_mimetype(WellKnownMimeTypes.MESSAGE_RSOCKET_AUTHENTICATION) + assert authentication_items[0].authentication.password == b'1234'