Implement read method
Signed-off-by: Tudor Plugaru <plugaru.tudor@protonmail.com>
This commit is contained in:
parent
fa0ec99bf2
commit
8aeed15f71
|
@ -32,6 +32,7 @@ keywords = [
|
|||
]
|
||||
dependencies = [
|
||||
"ruff>=0.6.8",
|
||||
"python-dateutil>=2.8.2",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
@ -53,6 +54,7 @@ dev-dependencies = [
|
|||
"flake8-print>=5.0.0",
|
||||
"pre-commit>=3.8.0",
|
||||
"pytest-cov>=5.0.0",
|
||||
"types-python-dateutil>=2.9.0.20241003",
|
||||
]
|
||||
|
||||
[tool.uv.pip]
|
||||
|
|
|
@ -18,6 +18,10 @@ from typing import Any, Optional, Protocol, Union
|
|||
|
||||
|
||||
class BaseCloudEvent(Protocol):
|
||||
def __init__(
|
||||
self, attributes: dict[str, Any], data: Optional[Union[dict, str, bytes]] = None
|
||||
) -> None: ...
|
||||
|
||||
def get_id(self) -> str: ...
|
||||
|
||||
def get_source(self) -> str: ...
|
||||
|
|
|
@ -21,4 +21,4 @@ from cloudevents.core.base import BaseCloudEvent
|
|||
class Format(Protocol):
|
||||
def read(self, data: Union[str, bytes]) -> BaseCloudEvent: ...
|
||||
|
||||
def write(self, event: BaseCloudEvent) -> str: ...
|
||||
def write(self, event: BaseCloudEvent) -> bytes: ...
|
||||
|
|
|
@ -16,12 +16,16 @@
|
|||
import base64
|
||||
import re
|
||||
from datetime import datetime
|
||||
from json import JSONEncoder, dumps
|
||||
from typing import Any, Final, Pattern, Union
|
||||
from json import JSONEncoder, dumps, loads
|
||||
from typing import Any, Final, Pattern, Type, TypeVar, Union
|
||||
|
||||
from dateutil.parser import isoparse
|
||||
|
||||
from cloudevents.core.base import BaseCloudEvent
|
||||
from cloudevents.core.formats.base import Format
|
||||
|
||||
T = TypeVar("T", bound=BaseCloudEvent)
|
||||
|
||||
|
||||
class _JSONEncoderWithDatetime(JSONEncoder):
|
||||
"""
|
||||
|
@ -46,10 +50,33 @@ class JSONFormat(Format):
|
|||
r"^(application|text)\\/([a-zA-Z]+\\+)?json(;.*)*$"
|
||||
)
|
||||
|
||||
def read(self, data: Union[str, bytes]) -> BaseCloudEvent:
|
||||
pass
|
||||
def read(self, event_klass: Type[T], data: Union[str, bytes]) -> T:
|
||||
"""
|
||||
Read a CloudEvent from a JSON formatted byte string.
|
||||
|
||||
def write(self, event: BaseCloudEvent) -> bytes:
|
||||
:param data: The JSON formatted byte array.
|
||||
:return: The CloudEvent instance.
|
||||
"""
|
||||
if isinstance(data, bytes):
|
||||
decoded_data: str = data.decode("utf-8")
|
||||
else:
|
||||
decoded_data = data
|
||||
|
||||
event_attributes = loads(decoded_data)
|
||||
|
||||
if "time" in event_attributes:
|
||||
event_attributes["time"] = isoparse(event_attributes["time"])
|
||||
|
||||
event_data: Union[str, bytes] = event_attributes.get("data")
|
||||
if event_data is None:
|
||||
event_data_base64 = event_attributes.get("data_base64")
|
||||
if event_data_base64 is not None:
|
||||
event_data = base64.b64decode(event_data_base64)
|
||||
|
||||
# disable mypy due to https://github.com/python/mypy/issues/9003
|
||||
return event_klass(event_attributes, event_data) # type: ignore
|
||||
|
||||
def write(self, event: T) -> bytes:
|
||||
"""
|
||||
Write a CloudEvent to a JSON formatted byte string.
|
||||
|
||||
|
@ -57,7 +84,7 @@ class JSONFormat(Format):
|
|||
:return: The CloudEvent as a JSON formatted byte array.
|
||||
"""
|
||||
event_data = event.get_data()
|
||||
event_dict: dict[str, Any] = {**event.get_attributes()}
|
||||
event_dict: dict[str, Any] = dict(event.get_attributes())
|
||||
|
||||
if event_data is not None:
|
||||
if isinstance(event_data, (bytes, bytearray)):
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
import re
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from typing import Any, Final, Optional
|
||||
from typing import Any, Final, Optional, Union
|
||||
|
||||
from cloudevents.core.base import BaseCloudEvent
|
||||
from cloudevents.core.v1.exceptions import (
|
||||
|
@ -45,7 +45,9 @@ class CloudEvent(BaseCloudEvent):
|
|||
obliged to follow this contract.
|
||||
"""
|
||||
|
||||
def __init__(self, attributes: dict[str, Any], data: Optional[dict] = None) -> None:
|
||||
def __init__(
|
||||
self, attributes: dict[str, Any], data: Optional[Union[dict, str, bytes]] = None
|
||||
) -> None:
|
||||
"""
|
||||
Create a new CloudEvent instance.
|
||||
|
||||
|
@ -57,7 +59,7 @@ class CloudEvent(BaseCloudEvent):
|
|||
"""
|
||||
self._validate_attribute(attributes=attributes)
|
||||
self._attributes: dict[str, Any] = attributes
|
||||
self._data: Optional[dict] = data
|
||||
self._data: Optional[Union[dict, str, bytes]] = data
|
||||
|
||||
@staticmethod
|
||||
def _validate_attribute(attributes: dict[str, Any]) -> None:
|
||||
|
@ -316,7 +318,7 @@ class CloudEvent(BaseCloudEvent):
|
|||
"""
|
||||
return self._attributes.get(extension_name)
|
||||
|
||||
def get_data(self) -> Optional[dict]:
|
||||
def get_data(self) -> Optional[Union[dict, str, bytes]]:
|
||||
"""
|
||||
Retrieve data of the event.
|
||||
|
||||
|
|
|
@ -155,3 +155,23 @@ def test_write_cloud_event_to_json_with_no_content_type_set_and_data_as_json() -
|
|||
"utf-8"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_read_cloud_event_from_json_with_attributes_only() -> None:
|
||||
data = '{"id": "123", "source": "source", "type": "type", "specversion": "1.0", "time": "2023-10-25T17:09:19.736166Z", "datacontenttype": "application/json", "dataschema": "http://example.com/schema", "subject": "test_subject"}'.encode(
|
||||
"utf-8"
|
||||
)
|
||||
formatter = JSONFormat()
|
||||
result = formatter.read(CloudEvent, data)
|
||||
|
||||
assert result.get_id() == "123"
|
||||
assert result.get_source() == "source"
|
||||
assert result.get_type() == "type"
|
||||
assert result.get_specversion() == "1.0"
|
||||
assert result.get_time() == datetime(
|
||||
2023, 10, 25, 17, 9, 19, 736166, tzinfo=timezone.utc
|
||||
)
|
||||
assert result.get_datacontenttype() == "application/json"
|
||||
assert result.get_dataschema() == "http://example.com/schema"
|
||||
assert result.get_subject() == "test_subject"
|
||||
assert result.get_data() is None
|
||||
|
|
38
uv.lock
38
uv.lock
|
@ -15,6 +15,7 @@ name = "cloudevents"
|
|||
version = "2.0.0a1"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
|
@ -28,10 +29,14 @@ dev = [
|
|||
{ name = "pre-commit" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-cov" },
|
||||
{ name = "types-python-dateutil" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [{ name = "ruff", specifier = ">=0.6.8" }]
|
||||
requires-dist = [
|
||||
{ name = "python-dateutil", specifier = ">=2.8.2" },
|
||||
{ name = "ruff", specifier = ">=0.6.8" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
|
@ -43,6 +48,7 @@ dev = [
|
|||
{ name = "pre-commit", specifier = ">=3.8.0" },
|
||||
{ name = "pytest", specifier = ">=8.3.3" },
|
||||
{ name = "pytest-cov", specifier = ">=5.0.0" },
|
||||
{ name = "types-python-dateutil", specifier = ">=2.9.0.20241003" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -373,6 +379,18 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652", size = 21990 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "six" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
|
@ -451,6 +469,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/d9/bd/a8b0c64945a92eaeeb8d0283f27a726a776a1c9d12734d990c5fc7a1278c/ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc", size = 8669595 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "six"
|
||||
version = "1.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
|
@ -460,6 +487,15 @@ wheels = [
|
|||
{ url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-python-dateutil"
|
||||
version = "2.9.0.20241003"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/31/f8/f6ee4c803a7beccffee21bb29a71573b39f7037c224843eff53e5308c16e/types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446", size = 9210 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/35/d6/ba5f61958f358028f2e2ba1b8e225b8e263053bd57d3a79e2d2db64c807b/types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d", size = 9693 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
|
|
Loading…
Reference in New Issue