V1.1.0 dev (#114)
* 100% test-coverage rule added to tox (#109) * version bump Signed-off-by: Curtis Mason <cumason@google.com> * adding tests for marshaller Signed-off-by: Curtis Mason <cumason@google.com> * marshaller 100% test-coverage Signed-off-by: Curtis Mason <cumason@bu.edu> * bricked some tests Signed-off-by: Curtis Mason <cumason@bu.edu> * additional error handling Signed-off-by: Curtis Mason <cumason@bu.edu> * 100% test-coverage Signed-off-by: Curtis Mason <cumason@bu.edu> * handles empty data and capitalized headers Signed-off-by: Curtis Mason <cumason@bu.edu> * 1.1.0 version bump Signed-off-by: Curtis Mason <cumason@bu.edu> * Removed _http suffix from http_methods (#108) * Removed _http suffix from http_methods to_binary_http renamed to_binary, and to_structured_http renamed to_structured. These functions are inside of cloudevents.http thus the _http part should be implicitly understood. Signed-off-by: Curtis Mason <cumason@google.com> * version bump Signed-off-by: Curtis Mason <cumason@google.com> * deprecated instead of removal Signed-off-by: Curtis Mason <cumason@bu.edu> * Update setup.py Co-authored-by: Dustin Ingram <di@users.noreply.github.com> Signed-off-by: Curtis Mason <cumason@bu.edu> * 1.1.0 version bump Signed-off-by: Curtis Mason <cumason@bu.edu> Co-authored-by: Dustin Ingram <di@users.noreply.github.com> * swapped args for from_http (#110) Signed-off-by: Curtis Mason <cumason@bu.edu> * exception names shortened (#111) * exception names shortened Signed-off-by: Curtis Mason <cumason@google.com> * to_structured documentation Signed-off-by: Curtis Mason <cumason@google.com> * adjusted readme and changelog (#113) * adjusted readme and changelog Signed-off-by: Curtis Mason <cumason@google.com> * readme adjustment Signed-off-by: Curtis Mason <cumason@google.com> * structured content mode Signed-off-by: Curtis Mason <cumason@google.com> Co-authored-by: Dustin Ingram <di@users.noreply.github.com>
This commit is contained in:
parent
d95b1303a9
commit
14c76188d1
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.1.0]
|
||||||
|
### Changed
|
||||||
|
- Changed from_http to now expect headers argument before data ([#110])
|
||||||
|
- Renamed exception names ([#111])
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
- Renamed to_binary_http and to_structured_http. ([#108])
|
||||||
|
|
||||||
## [1.0.1]
|
## [1.0.1]
|
||||||
### Added
|
### Added
|
||||||
- CloudEvent exceptions and event type checking in http module ([#96])
|
- CloudEvent exceptions and event type checking in http module ([#96])
|
||||||
|
@ -93,4 +101,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
[#71]: https://github.com/cloudevents/sdk-python/pull/71
|
[#71]: https://github.com/cloudevents/sdk-python/pull/71
|
||||||
[#72]: https://github.com/cloudevents/sdk-python/pull/72
|
[#72]: https://github.com/cloudevents/sdk-python/pull/72
|
||||||
[#96]: https://github.com/cloudevents/sdk-python/pull/96
|
[#96]: https://github.com/cloudevents/sdk-python/pull/96
|
||||||
[#98]: https://github.com/cloudevents/sdk-python/pull/98
|
[#98]: https://github.com/cloudevents/sdk-python/pull/98
|
||||||
|
[#108]: https://github.com/cloudevents/sdk-python/pull/108
|
||||||
|
[#110]: https://github.com/cloudevents/sdk-python/pull/110
|
||||||
|
[#111]: https://github.com/cloudevents/sdk-python/pull/111
|
||||||
|
|
23
README.md
23
README.md
|
@ -24,19 +24,20 @@ Below we will provide samples on how to send cloudevents using the popular
|
||||||
### Binary HTTP CloudEvent
|
### Binary HTTP CloudEvent
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from cloudevents.http import CloudEvent, to_binary_http
|
from cloudevents.http import CloudEvent, to_binary
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
# Create a CloudEvent
|
||||||
# This data defines a binary cloudevent
|
# - The CloudEvent "id" is generated if omitted. "specversion" defaults to "1.0".
|
||||||
attributes = {
|
attributes = {
|
||||||
"type": "com.example.sampletype1",
|
"type": "com.example.sampletype1",
|
||||||
"source": "https://example.com/event-producer",
|
"source": "https://example.com/event-producer",
|
||||||
}
|
}
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
|
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body = to_binary_http(event)
|
|
||||||
|
# Creates the HTTP request representation of the CloudEvent in binary content mode
|
||||||
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
# POST
|
# POST
|
||||||
requests.post("<some-url>", data=body, headers=headers)
|
requests.post("<some-url>", data=body, headers=headers)
|
||||||
|
@ -45,18 +46,20 @@ requests.post("<some-url>", data=body, headers=headers)
|
||||||
### Structured HTTP CloudEvent
|
### Structured HTTP CloudEvent
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from cloudevents.http import CloudEvent, to_structured_http
|
from cloudevents.http import CloudEvent, to_structured
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
# Create a CloudEvent
|
||||||
# This data defines a structured cloudevent
|
# - The CloudEvent "id" is generated if omitted. "specversion" defaults to "1.0".
|
||||||
attributes = {
|
attributes = {
|
||||||
"type": "com.example.sampletype2",
|
"type": "com.example.sampletype2",
|
||||||
"source": "https://example.com/event-producer",
|
"source": "https://example.com/event-producer",
|
||||||
}
|
}
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body = to_structured_http(event)
|
|
||||||
|
# Creates the HTTP request representation of the CloudEvent in structured content mode
|
||||||
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
# POST
|
# POST
|
||||||
requests.post("<some-url>", data=body, headers=headers)
|
requests.post("<some-url>", data=body, headers=headers)
|
||||||
|
@ -81,7 +84,7 @@ app = Flask(__name__)
|
||||||
@app.route("/", methods=["POST"])
|
@app.route("/", methods=["POST"])
|
||||||
def home():
|
def home():
|
||||||
# create a CloudEvent
|
# create a CloudEvent
|
||||||
event = from_http(request.get_data(), request.headers)
|
event = from_http(request.headers, request.get_data())
|
||||||
|
|
||||||
# you can access cloudevent fields as seen below
|
# you can access cloudevent fields as seen below
|
||||||
print(
|
print(
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = "1.0.1"
|
__version__ = "1.1.0"
|
||||||
|
|
|
@ -11,9 +11,17 @@
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
class CloudEventMissingRequiredFields(Exception):
|
class MissingRequiredFields(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CloudEventTypeErrorRequiredFields(Exception):
|
class InvalidRequiredFields(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidStructuredJSON(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidHeadersFormat(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -18,7 +18,9 @@ from cloudevents.http.event import CloudEvent
|
||||||
from cloudevents.http.event_type import is_binary, is_structured
|
from cloudevents.http.event_type import is_binary, is_structured
|
||||||
from cloudevents.http.http_methods import (
|
from cloudevents.http.http_methods import (
|
||||||
from_http,
|
from_http,
|
||||||
|
to_binary,
|
||||||
to_binary_http,
|
to_binary_http,
|
||||||
|
to_structured,
|
||||||
to_structured_http,
|
to_structured_http,
|
||||||
)
|
)
|
||||||
from cloudevents.http.json_methods import from_json, to_json
|
from cloudevents.http.json_methods import from_json, to_json
|
||||||
|
|
|
@ -58,15 +58,15 @@ class CloudEvent:
|
||||||
).isoformat()
|
).isoformat()
|
||||||
|
|
||||||
if self._attributes["specversion"] not in _required_by_version:
|
if self._attributes["specversion"] not in _required_by_version:
|
||||||
raise cloud_exceptions.CloudEventMissingRequiredFields(
|
raise cloud_exceptions.MissingRequiredFields(
|
||||||
f"Invalid specversion: {self._attributes['specversion']}"
|
f"Invalid specversion: {self._attributes['specversion']}. "
|
||||||
)
|
)
|
||||||
# There is no good way to default 'source' and 'type', so this
|
# There is no good way to default 'source' and 'type', so this
|
||||||
# checks for those (or any new required attributes).
|
# checks for those (or any new required attributes).
|
||||||
required_set = _required_by_version[self._attributes["specversion"]]
|
required_set = _required_by_version[self._attributes["specversion"]]
|
||||||
if not required_set <= self._attributes.keys():
|
if not required_set <= self._attributes.keys():
|
||||||
raise cloud_exceptions.CloudEventMissingRequiredFields(
|
raise cloud_exceptions.MissingRequiredFields(
|
||||||
f"Missing required keys: {required_set - attributes.keys()}"
|
f"Missing required keys: {required_set - self._attributes.keys()}. "
|
||||||
)
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import json
|
import json
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
from deprecation import deprecated
|
||||||
|
|
||||||
import cloudevents.exceptions as cloud_exceptions
|
import cloudevents.exceptions as cloud_exceptions
|
||||||
from cloudevents.http.event import CloudEvent
|
from cloudevents.http.event import CloudEvent
|
||||||
from cloudevents.http.event_type import is_binary, is_structured
|
from cloudevents.http.event_type import is_binary, is_structured
|
||||||
|
@ -10,20 +12,30 @@ from cloudevents.sdk import converters, marshaller, types
|
||||||
|
|
||||||
|
|
||||||
def from_http(
|
def from_http(
|
||||||
data: typing.Union[str, bytes],
|
|
||||||
headers: typing.Dict[str, str],
|
headers: typing.Dict[str, str],
|
||||||
|
data: typing.Union[str, bytes, None],
|
||||||
data_unmarshaller: types.UnmarshallerType = None,
|
data_unmarshaller: types.UnmarshallerType = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Unwrap a CloudEvent (binary or structured) from an HTTP request.
|
Unwrap a CloudEvent (binary or structured) from an HTTP request.
|
||||||
:param data: the HTTP request body
|
|
||||||
:type data: typing.IO
|
|
||||||
:param headers: the HTTP headers
|
:param headers: the HTTP headers
|
||||||
:type headers: typing.Dict[str, str]
|
:type headers: typing.Dict[str, str]
|
||||||
|
:param data: the HTTP request body
|
||||||
|
:type data: typing.IO
|
||||||
:param data_unmarshaller: Callable function to map data to a python object
|
:param data_unmarshaller: Callable function to map data to a python object
|
||||||
e.g. lambda x: x or lambda x: json.loads(x)
|
e.g. lambda x: x or lambda x: json.loads(x)
|
||||||
:type data_unmarshaller: types.UnmarshallerType
|
:type data_unmarshaller: types.UnmarshallerType
|
||||||
"""
|
"""
|
||||||
|
if data is None:
|
||||||
|
data = ""
|
||||||
|
|
||||||
|
if not isinstance(data, (str, bytes, bytearray)):
|
||||||
|
raise cloud_exceptions.InvalidStructuredJSON(
|
||||||
|
"Expected json of type (str, bytes, bytearray), "
|
||||||
|
f"but instead found {type(data)}. "
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = {key.lower(): value for key, value in headers.items()}
|
||||||
if data_unmarshaller is None:
|
if data_unmarshaller is None:
|
||||||
data_unmarshaller = _json_or_string
|
data_unmarshaller = _json_or_string
|
||||||
|
|
||||||
|
@ -32,19 +44,25 @@ def from_http(
|
||||||
if is_binary(headers):
|
if is_binary(headers):
|
||||||
specversion = headers.get("ce-specversion", None)
|
specversion = headers.get("ce-specversion", None)
|
||||||
else:
|
else:
|
||||||
raw_ce = json.loads(data)
|
try:
|
||||||
|
raw_ce = json.loads(data)
|
||||||
|
except json.decoder.JSONDecodeError:
|
||||||
|
raise cloud_exceptions.InvalidStructuredJSON(
|
||||||
|
"Failed to read fields from structured event. "
|
||||||
|
f"The following can not be parsed as json: {data}. "
|
||||||
|
)
|
||||||
specversion = raw_ce.get("specversion", None)
|
specversion = raw_ce.get("specversion", None)
|
||||||
|
|
||||||
if specversion is None:
|
if specversion is None:
|
||||||
raise cloud_exceptions.CloudEventMissingRequiredFields(
|
raise cloud_exceptions.MissingRequiredFields(
|
||||||
"could not find specversion in HTTP request"
|
"Failed to find specversion in HTTP request. "
|
||||||
)
|
)
|
||||||
|
|
||||||
event_handler = _obj_by_version.get(specversion, None)
|
event_handler = _obj_by_version.get(specversion, None)
|
||||||
|
|
||||||
if event_handler is None:
|
if event_handler is None:
|
||||||
raise cloud_exceptions.CloudEventTypeErrorRequiredFields(
|
raise cloud_exceptions.InvalidRequiredFields(
|
||||||
f"found invalid specversion {specversion}"
|
f"Found invalid specversion {specversion}. "
|
||||||
)
|
)
|
||||||
|
|
||||||
event = marshall.FromRequest(
|
event = marshall.FromRequest(
|
||||||
|
@ -77,8 +95,8 @@ def _to_http(
|
||||||
data_marshaller = _marshaller_by_format[format]
|
data_marshaller = _marshaller_by_format[format]
|
||||||
|
|
||||||
if event._attributes["specversion"] not in _obj_by_version:
|
if event._attributes["specversion"] not in _obj_by_version:
|
||||||
raise cloud_exceptions.CloudEventTypeErrorRequiredFields(
|
raise cloud_exceptions.InvalidRequiredFields(
|
||||||
f"Unsupported specversion: {event._attributes['specversion']}"
|
f"Unsupported specversion: {event._attributes['specversion']}. "
|
||||||
)
|
)
|
||||||
|
|
||||||
event_handler = _obj_by_version[event._attributes["specversion"]]()
|
event_handler = _obj_by_version[event._attributes["specversion"]]()
|
||||||
|
@ -91,11 +109,13 @@ def _to_http(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_structured_http(
|
def to_structured(
|
||||||
event: CloudEvent, data_marshaller: types.MarshallerType = None,
|
event: CloudEvent, data_marshaller: types.MarshallerType = None,
|
||||||
) -> (dict, typing.Union[bytes, str]):
|
) -> (dict, typing.Union[bytes, str]):
|
||||||
"""
|
"""
|
||||||
Returns a tuple of HTTP headers/body dicts representing this cloudevent
|
Returns a tuple of HTTP headers/body dicts representing this cloudevent. If
|
||||||
|
event.data is a byte object, body will have a data_base64 field instead of
|
||||||
|
data.
|
||||||
|
|
||||||
:param event: CloudEvent to cast into http data
|
:param event: CloudEvent to cast into http data
|
||||||
:type event: CloudEvent
|
:type event: CloudEvent
|
||||||
|
@ -107,7 +127,7 @@ def to_structured_http(
|
||||||
return _to_http(event=event, data_marshaller=data_marshaller)
|
return _to_http(event=event, data_marshaller=data_marshaller)
|
||||||
|
|
||||||
|
|
||||||
def to_binary_http(
|
def to_binary(
|
||||||
event: CloudEvent, data_marshaller: types.MarshallerType = None,
|
event: CloudEvent, data_marshaller: types.MarshallerType = None,
|
||||||
) -> (dict, typing.Union[bytes, str]):
|
) -> (dict, typing.Union[bytes, str]):
|
||||||
"""
|
"""
|
||||||
|
@ -125,3 +145,17 @@ def to_binary_http(
|
||||||
format=converters.TypeBinary,
|
format=converters.TypeBinary,
|
||||||
data_marshaller=data_marshaller,
|
data_marshaller=data_marshaller,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated(deprecated_in="1.0.2", details="Use to_binary function instead")
|
||||||
|
def to_binary_http(
|
||||||
|
event: CloudEvent, data_marshaller: types.MarshallerType = None,
|
||||||
|
) -> (dict, typing.Union[bytes, str]):
|
||||||
|
return to_binary(event, data_marshaller)
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated(deprecated_in="1.0.2", details="Use to_structured function instead")
|
||||||
|
def to_structured_http(
|
||||||
|
event: CloudEvent, data_marshaller: types.MarshallerType = None,
|
||||||
|
) -> (dict, typing.Union[bytes, str]):
|
||||||
|
return to_structured(event, data_marshaller)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from cloudevents.http.event import CloudEvent
|
from cloudevents.http.event import CloudEvent
|
||||||
from cloudevents.http.http_methods import from_http, to_structured_http
|
from cloudevents.http.http_methods import from_http, to_structured
|
||||||
from cloudevents.sdk import types
|
from cloudevents.sdk import types
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ def to_json(
|
||||||
:type data_marshaller: typing.Callable
|
:type data_marshaller: typing.Callable
|
||||||
:returns: json object representing the given event
|
:returns: json object representing the given event
|
||||||
"""
|
"""
|
||||||
return to_structured_http(event, data_marshaller=data_marshaller)[1]
|
return to_structured(event, data_marshaller=data_marshaller)[1]
|
||||||
|
|
||||||
|
|
||||||
def from_json(
|
def from_json(
|
||||||
|
@ -33,4 +33,4 @@ def from_json(
|
||||||
:type data_unmarshaller: typing.Callable
|
:type data_unmarshaller: typing.Callable
|
||||||
:returns: CloudEvent representing given cloudevent json object
|
:returns: CloudEvent representing given cloudevent json object
|
||||||
"""
|
"""
|
||||||
return from_http(data=data, headers={}, data_unmarshaller=data_unmarshaller)
|
return from_http(headers={}, data=data, data_unmarshaller=data_unmarshaller)
|
||||||
|
|
|
@ -12,7 +12,7 @@ def default_marshaller(content: any):
|
||||||
|
|
||||||
|
|
||||||
def _json_or_string(content: typing.Union[str, bytes]):
|
def _json_or_string(content: typing.Union[str, bytes]):
|
||||||
if len(content) == 0:
|
if content is None or len(content) == 0:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
return json.loads(content)
|
return json.loads(content)
|
||||||
|
|
|
@ -22,7 +22,7 @@ from cloudevents.sdk import types
|
||||||
# TODO(slinkydeveloper) is this really needed?
|
# TODO(slinkydeveloper) is this really needed?
|
||||||
|
|
||||||
|
|
||||||
class EventGetterSetter(object):
|
class EventGetterSetter(object): # pragma: no cover
|
||||||
|
|
||||||
# ce-specversion
|
# ce-specversion
|
||||||
def CloudEventVersion(self) -> str:
|
def CloudEventVersion(self) -> str:
|
||||||
|
@ -220,7 +220,7 @@ class BaseEvent(EventGetterSetter):
|
||||||
|
|
||||||
missing_fields = self._ce_required_fields - raw_ce.keys()
|
missing_fields = self._ce_required_fields - raw_ce.keys()
|
||||||
if len(missing_fields) > 0:
|
if len(missing_fields) > 0:
|
||||||
raise cloud_exceptions.CloudEventMissingRequiredFields(
|
raise cloud_exceptions.MissingRequiredFields(
|
||||||
f"Missing required attributes: {missing_fields}"
|
f"Missing required attributes: {missing_fields}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -246,7 +246,7 @@ class BaseEvent(EventGetterSetter):
|
||||||
missing_fields = required_binary_fields - headers.keys()
|
missing_fields = required_binary_fields - headers.keys()
|
||||||
|
|
||||||
if len(missing_fields) > 0:
|
if len(missing_fields) > 0:
|
||||||
raise cloud_exceptions.CloudEventMissingRequiredFields(
|
raise cloud_exceptions.MissingRequiredFields(
|
||||||
f"Missing required attributes: {missing_fields}"
|
f"Missing required attributes: {missing_fields}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -75,10 +75,6 @@ class Event(base.BaseEvent):
|
||||||
def ContentEncoding(self) -> str:
|
def ContentEncoding(self) -> str:
|
||||||
return self.ce__datacontentencoding.get()
|
return self.ce__datacontentencoding.get()
|
||||||
|
|
||||||
@property
|
|
||||||
def datacontentencoding(self):
|
|
||||||
return self.ContentEncoding()
|
|
||||||
|
|
||||||
def SetEventType(self, eventType: str) -> base.BaseEvent:
|
def SetEventType(self, eventType: str) -> base.BaseEvent:
|
||||||
self.Set("type", eventType)
|
self.Set("type", eventType)
|
||||||
return self
|
return self
|
||||||
|
@ -119,6 +115,26 @@ class Event(base.BaseEvent):
|
||||||
self.Set("datacontentencoding", contentEncoding)
|
self.Set("datacontentencoding", contentEncoding)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def datacontentencoding(self):
|
||||||
|
return self.ContentEncoding()
|
||||||
|
|
||||||
@datacontentencoding.setter
|
@datacontentencoding.setter
|
||||||
def datacontentencoding(self, value: str):
|
def datacontentencoding(self, value: str):
|
||||||
self.SetContentEncoding(value)
|
self.SetContentEncoding(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subject(self) -> str:
|
||||||
|
return self.Subject()
|
||||||
|
|
||||||
|
@subject.setter
|
||||||
|
def subject(self, value: str):
|
||||||
|
self.SetSubject(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schema_url(self) -> str:
|
||||||
|
return self.SchemaURL()
|
||||||
|
|
||||||
|
@schema_url.setter
|
||||||
|
def schema_url(self, value: str):
|
||||||
|
self.SetSchemaURL(value)
|
||||||
|
|
|
@ -98,3 +98,19 @@ class Event(base.BaseEvent):
|
||||||
def SetExtensions(self, extensions: dict) -> base.BaseEvent:
|
def SetExtensions(self, extensions: dict) -> base.BaseEvent:
|
||||||
self.Set("extensions", extensions)
|
self.Set("extensions", extensions)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schema(self) -> str:
|
||||||
|
return self.Schema()
|
||||||
|
|
||||||
|
@schema.setter
|
||||||
|
def schema(self, value: str):
|
||||||
|
self.SetSchema(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def subject(self) -> str:
|
||||||
|
return self.Subject()
|
||||||
|
|
||||||
|
@subject.setter
|
||||||
|
def subject(self, value: str):
|
||||||
|
self.SetSubject(value)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import cloudevents.exceptions as cloud_exceptions
|
||||||
|
from cloudevents.sdk.event import v1, v03
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("event_class", [v1.Event, v03.Event])
|
||||||
|
def test_unmarshall_binary_missing_fields(event_class):
|
||||||
|
event = event_class()
|
||||||
|
with pytest.raises(cloud_exceptions.MissingRequiredFields) as e:
|
||||||
|
event.UnmarshalBinary({}, "", lambda x: x)
|
||||||
|
assert "Missing required attributes: " in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("event_class", [v1.Event, v03.Event])
|
||||||
|
def test_get_nonexistent_optional(event_class):
|
||||||
|
event = event_class()
|
||||||
|
event.SetExtensions({"ext1": "val"})
|
||||||
|
res = event.Get("ext1")
|
||||||
|
assert res[0] == "val" and res[1] == True
|
|
@ -0,0 +1,41 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cloudevents.sdk import exceptions
|
||||||
|
from cloudevents.sdk.converters import base, binary, structured
|
||||||
|
|
||||||
|
|
||||||
|
def test_binary_converter_raise_unsupported():
|
||||||
|
with pytest.raises(exceptions.UnsupportedEvent):
|
||||||
|
cnvtr = binary.BinaryHTTPCloudEventConverter()
|
||||||
|
cnvtr.read(None, {}, None, None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_base_converters_raise_exceptions():
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
cnvtr = base.Converter()
|
||||||
|
cnvtr.event_supported(None)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
cnvtr = base.Converter()
|
||||||
|
cnvtr.can_read(None)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
cnvtr = base.Converter()
|
||||||
|
cnvtr.write(None, None)
|
||||||
|
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
cnvtr = base.Converter()
|
||||||
|
cnvtr.read(None, None, None, None)
|
|
@ -92,7 +92,6 @@ def test_general_structured_properties(event_class):
|
||||||
if key == "content-type":
|
if key == "content-type":
|
||||||
assert new_headers[key] == http_headers[key]
|
assert new_headers[key] == http_headers[key]
|
||||||
continue
|
continue
|
||||||
assert key in copy_of_ce
|
|
||||||
|
|
||||||
# Test setters
|
# Test setters
|
||||||
new_type = str(uuid4())
|
new_type = str(uuid4())
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cloudevents.http import (
|
||||||
|
CloudEvent,
|
||||||
|
to_binary,
|
||||||
|
to_binary_http,
|
||||||
|
to_structured,
|
||||||
|
to_structured_http,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def event():
|
||||||
|
return CloudEvent({"source": "s", "type": "t"}, None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_binary_http_deprecated(event):
|
||||||
|
with pytest.deprecated_call():
|
||||||
|
assert to_binary(event) == to_binary_http(event)
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_structured_http_deprecated(event):
|
||||||
|
with pytest.deprecated_call():
|
||||||
|
assert to_structured(event) == to_structured_http(event)
|
|
@ -15,12 +15,7 @@ import json
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from cloudevents.http import (
|
from cloudevents.http import CloudEvent, from_http, to_binary, to_structured
|
||||||
CloudEvent,
|
|
||||||
from_http,
|
|
||||||
to_binary_http,
|
|
||||||
to_structured_http,
|
|
||||||
)
|
|
||||||
|
|
||||||
test_data = json.dumps({"data-key": "val"})
|
test_data = json.dumps({"data-key": "val"})
|
||||||
test_attributes = {
|
test_attributes = {
|
||||||
|
@ -39,7 +34,7 @@ def test_cloudevent_access_extensions(specversion):
|
||||||
@pytest.mark.parametrize("specversion", ["0.3", "1.0"])
|
@pytest.mark.parametrize("specversion", ["0.3", "1.0"])
|
||||||
def test_to_binary_extensions(specversion):
|
def test_to_binary_extensions(specversion):
|
||||||
event = CloudEvent(test_attributes, test_data)
|
event = CloudEvent(test_attributes, test_data)
|
||||||
headers, body = to_binary_http(event)
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
assert "ce-ext1" in headers
|
assert "ce-ext1" in headers
|
||||||
assert headers.get("ce-ext1") == test_attributes["ext1"]
|
assert headers.get("ce-ext1") == test_attributes["ext1"]
|
||||||
|
@ -56,7 +51,7 @@ def test_from_binary_extensions(specversion):
|
||||||
"ce-ext2": "test2",
|
"ce-ext2": "test2",
|
||||||
}
|
}
|
||||||
body = json.dumps({"data-key": "val"})
|
body = json.dumps({"data-key": "val"})
|
||||||
event = from_http(body, headers)
|
event = from_http(headers, body)
|
||||||
|
|
||||||
assert headers["ce-ext1"] == event["ext1"]
|
assert headers["ce-ext1"] == event["ext1"]
|
||||||
assert headers["ce-ext2"] == event["ext2"]
|
assert headers["ce-ext2"] == event["ext2"]
|
||||||
|
@ -65,7 +60,7 @@ def test_from_binary_extensions(specversion):
|
||||||
@pytest.mark.parametrize("specversion", ["0.3", "1.0"])
|
@pytest.mark.parametrize("specversion", ["0.3", "1.0"])
|
||||||
def test_to_structured_extensions(specversion):
|
def test_to_structured_extensions(specversion):
|
||||||
event = CloudEvent(test_attributes, test_data)
|
event = CloudEvent(test_attributes, test_data)
|
||||||
headers, body = to_structured_http(event)
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
body = json.loads(body)
|
body = json.loads(body)
|
||||||
|
|
||||||
|
@ -86,7 +81,7 @@ def test_from_structured_extensions(specversion):
|
||||||
}
|
}
|
||||||
|
|
||||||
data = json.dumps(body)
|
data = json.dumps(body)
|
||||||
event = from_http(data, headers)
|
event = from_http(headers, data)
|
||||||
|
|
||||||
assert body["ext1"] == event["ext1"]
|
assert body["ext1"] == event["ext1"]
|
||||||
assert body["ext2"] == event["ext2"]
|
assert body["ext2"] == event["ext2"]
|
||||||
|
|
|
@ -61,4 +61,3 @@ def test_structured_event_to_request_upstream(event_class):
|
||||||
if key == "content-type":
|
if key == "content-type":
|
||||||
assert new_headers[key] == http_headers[key]
|
assert new_headers[key] == http_headers[key]
|
||||||
continue
|
continue
|
||||||
assert key in copy_of_ce
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
import cloudevents.exceptions as cloud_exceptions
|
||||||
from cloudevents.http import CloudEvent
|
from cloudevents.http import CloudEvent
|
||||||
|
|
||||||
|
|
||||||
|
@ -69,3 +70,47 @@ def test_http_cloudevent_mutates_equality(specversion):
|
||||||
event3.data = '{"name":"paul"}'
|
event3.data = '{"name":"paul"}'
|
||||||
assert event2 == event3
|
assert event2 == event3
|
||||||
assert event1 != event2 and event3 != event1
|
assert event1 != event2 and event3 != event1
|
||||||
|
|
||||||
|
|
||||||
|
def test_cloudevent_missing_specversion():
|
||||||
|
attributes = {"specversion": "0.2", "source": "s", "type": "t"}
|
||||||
|
with pytest.raises(cloud_exceptions.MissingRequiredFields) as e:
|
||||||
|
event = CloudEvent(attributes, None)
|
||||||
|
assert "Invalid specversion: 0.2" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cloudevent_missing_minimal_required_fields():
|
||||||
|
attributes = {"type": "t"}
|
||||||
|
with pytest.raises(cloud_exceptions.MissingRequiredFields) as e:
|
||||||
|
event = CloudEvent(attributes, None)
|
||||||
|
assert f"Missing required keys: {set(['source'])}" in str(e.value)
|
||||||
|
|
||||||
|
attributes = {"source": "s"}
|
||||||
|
with pytest.raises(cloud_exceptions.MissingRequiredFields) as e:
|
||||||
|
event = CloudEvent(attributes, None)
|
||||||
|
assert f"Missing required keys: {set(['type'])}" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cloudevent_general_overrides():
|
||||||
|
event = CloudEvent(
|
||||||
|
{
|
||||||
|
"source": "my-source",
|
||||||
|
"type": "com.test.overrides",
|
||||||
|
"subject": "my-subject",
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
expected_attributes = [
|
||||||
|
"time",
|
||||||
|
"source",
|
||||||
|
"id",
|
||||||
|
"specversion",
|
||||||
|
"type",
|
||||||
|
"subject",
|
||||||
|
]
|
||||||
|
|
||||||
|
assert len(event) == 6
|
||||||
|
for attribute in expected_attributes:
|
||||||
|
assert attribute in event
|
||||||
|
del event[attribute]
|
||||||
|
assert len(event) == 0
|
|
@ -25,7 +25,10 @@ from cloudevents.http import (
|
||||||
CloudEvent,
|
CloudEvent,
|
||||||
from_http,
|
from_http,
|
||||||
is_binary,
|
is_binary,
|
||||||
|
is_structured,
|
||||||
|
to_binary,
|
||||||
to_binary_http,
|
to_binary_http,
|
||||||
|
to_structured,
|
||||||
to_structured_http,
|
to_structured_http,
|
||||||
)
|
)
|
||||||
from cloudevents.sdk import converters
|
from cloudevents.sdk import converters
|
||||||
|
@ -69,17 +72,13 @@ test_data = {"payload-content": "Hello World!"}
|
||||||
app = Sanic(__name__)
|
app = Sanic(__name__)
|
||||||
|
|
||||||
|
|
||||||
def post(url, headers, data):
|
|
||||||
return app.test_client.post(url, headers=headers, data=data)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/event", ["POST"])
|
@app.route("/event", ["POST"])
|
||||||
async def echo(request):
|
async def echo(request):
|
||||||
decoder = None
|
decoder = None
|
||||||
if "binary-payload" in request.headers:
|
if "binary-payload" in request.headers:
|
||||||
decoder = lambda x: x
|
decoder = lambda x: x
|
||||||
event = from_http(
|
event = from_http(
|
||||||
request.body, headers=dict(request.headers), data_unmarshaller=decoder
|
dict(request.headers), request.body, data_unmarshaller=decoder
|
||||||
)
|
)
|
||||||
data = (
|
data = (
|
||||||
event.data
|
event.data
|
||||||
|
@ -91,25 +90,24 @@ async def echo(request):
|
||||||
|
|
||||||
@pytest.mark.parametrize("body", invalid_cloudevent_request_body)
|
@pytest.mark.parametrize("body", invalid_cloudevent_request_body)
|
||||||
def test_missing_required_fields_structured(body):
|
def test_missing_required_fields_structured(body):
|
||||||
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields):
|
with pytest.raises(cloud_exceptions.MissingRequiredFields):
|
||||||
# CloudEvent constructor throws TypeError if missing required field
|
# CloudEvent constructor throws TypeError if missing required field
|
||||||
# and NotImplementedError because structured calls aren't
|
# and NotImplementedError because structured calls aren't
|
||||||
# implemented. In this instance one of the required keys should have
|
# implemented. In this instance one of the required keys should have
|
||||||
# prefix e-id instead of ce-id therefore it should throw
|
# prefix e-id instead of ce-id therefore it should throw
|
||||||
_ = from_http(
|
_ = from_http(
|
||||||
json.dumps(body),
|
{"Content-Type": "application/cloudevents+json"}, json.dumps(body),
|
||||||
headers={"Content-Type": "application/cloudevents+json"},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("headers", invalid_test_headers)
|
@pytest.mark.parametrize("headers", invalid_test_headers)
|
||||||
def test_missing_required_fields_binary(headers):
|
def test_missing_required_fields_binary(headers):
|
||||||
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields):
|
with pytest.raises(cloud_exceptions.MissingRequiredFields):
|
||||||
# CloudEvent constructor throws TypeError if missing required field
|
# CloudEvent constructor throws TypeError if missing required field
|
||||||
# and NotImplementedError because structured calls aren't
|
# and NotImplementedError because structured calls aren't
|
||||||
# implemented. In this instance one of the required keys should have
|
# implemented. In this instance one of the required keys should have
|
||||||
# prefix e-id instead of ce-id therefore it should throw
|
# prefix e-id instead of ce-id therefore it should throw
|
||||||
_ = from_http(json.dumps(test_data), headers=headers)
|
_ = from_http(headers, json.dumps(test_data))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("specversion", ["1.0", "0.3"])
|
@pytest.mark.parametrize("specversion", ["1.0", "0.3"])
|
||||||
|
@ -177,9 +175,9 @@ def test_roundtrip_non_json_event(converter, specversion):
|
||||||
event = CloudEvent(attrs, compressed_data)
|
event = CloudEvent(attrs, compressed_data)
|
||||||
|
|
||||||
if converter == converters.TypeStructured:
|
if converter == converters.TypeStructured:
|
||||||
headers, data = to_structured_http(event, data_marshaller=lambda x: x)
|
headers, data = to_structured(event, data_marshaller=lambda x: x)
|
||||||
elif converter == converters.TypeBinary:
|
elif converter == converters.TypeBinary:
|
||||||
headers, data = to_binary_http(event, data_marshaller=lambda x: x)
|
headers, data = to_binary(event, data_marshaller=lambda x: x)
|
||||||
|
|
||||||
headers["binary-payload"] = "true" # Decoding hint for server
|
headers["binary-payload"] = "true" # Decoding hint for server
|
||||||
_, r = app.test_client.post("/event", headers=headers, data=data)
|
_, r = app.test_client.post("/event", headers=headers, data=data)
|
||||||
|
@ -204,12 +202,12 @@ def test_missing_ce_prefix_binary_event(specversion):
|
||||||
# breaking prefix e.g. e-id instead of ce-id
|
# breaking prefix e.g. e-id instead of ce-id
|
||||||
prefixed_headers[key[1:]] = headers[key]
|
prefixed_headers[key[1:]] = headers[key]
|
||||||
|
|
||||||
with pytest.raises(cloud_exceptions.CloudEventMissingRequiredFields):
|
with pytest.raises(cloud_exceptions.MissingRequiredFields):
|
||||||
# CloudEvent constructor throws TypeError if missing required field
|
# CloudEvent constructor throws TypeError if missing required field
|
||||||
# and NotImplementedError because structured calls aren't
|
# and NotImplementedError because structured calls aren't
|
||||||
# implemented. In this instance one of the required keys should have
|
# implemented. In this instance one of the required keys should have
|
||||||
# prefix e-id instead of ce-id therefore it should throw
|
# prefix e-id instead of ce-id therefore it should throw
|
||||||
_ = from_http(json.dumps(test_data), headers=prefixed_headers)
|
_ = from_http(prefixed_headers, json.dumps(test_data))
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("specversion", ["1.0", "0.3"])
|
@pytest.mark.parametrize("specversion", ["1.0", "0.3"])
|
||||||
|
@ -226,7 +224,7 @@ def test_valid_binary_events(specversion):
|
||||||
"ce-specversion": specversion,
|
"ce-specversion": specversion,
|
||||||
}
|
}
|
||||||
data = {"payload": f"payload-{i}"}
|
data = {"payload": f"payload-{i}"}
|
||||||
events_queue.append(from_http(json.dumps(data), headers=headers))
|
events_queue.append(from_http(headers, json.dumps(data)))
|
||||||
|
|
||||||
for i, event in enumerate(events_queue):
|
for i, event in enumerate(events_queue):
|
||||||
data = event.data
|
data = event.data
|
||||||
|
@ -247,7 +245,7 @@ def test_structured_to_request(specversion):
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
|
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body_bytes = to_structured_http(event)
|
headers, body_bytes = to_structured(event)
|
||||||
assert isinstance(body_bytes, bytes)
|
assert isinstance(body_bytes, bytes)
|
||||||
body = json.loads(body_bytes)
|
body = json.loads(body_bytes)
|
||||||
|
|
||||||
|
@ -267,7 +265,7 @@ def test_binary_to_request(specversion):
|
||||||
}
|
}
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body_bytes = to_binary_http(event)
|
headers, body_bytes = to_binary(event)
|
||||||
body = json.loads(body_bytes)
|
body = json.loads(body_bytes)
|
||||||
|
|
||||||
for key in data:
|
for key in data:
|
||||||
|
@ -289,7 +287,7 @@ def test_empty_data_structured_event(specversion):
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = from_http(
|
_ = from_http(
|
||||||
json.dumps(attributes), {"content-type": "application/cloudevents+json"}
|
{"content-type": "application/cloudevents+json"}, json.dumps(attributes)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -304,7 +302,7 @@ def test_empty_data_binary_event(specversion):
|
||||||
"ce-time": "2018-10-23T12:28:22.4579346Z",
|
"ce-time": "2018-10-23T12:28:22.4579346Z",
|
||||||
"ce-source": "<source-url>",
|
"ce-source": "<source-url>",
|
||||||
}
|
}
|
||||||
_ = from_http("", headers)
|
_ = from_http(headers, "")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("specversion", ["1.0", "0.3"])
|
@pytest.mark.parametrize("specversion", ["1.0", "0.3"])
|
||||||
|
@ -322,8 +320,8 @@ def test_valid_structured_events(specversion):
|
||||||
}
|
}
|
||||||
events_queue.append(
|
events_queue.append(
|
||||||
from_http(
|
from_http(
|
||||||
json.dumps(event),
|
|
||||||
{"content-type": "application/cloudevents+json"},
|
{"content-type": "application/cloudevents+json"},
|
||||||
|
json.dumps(event),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -344,7 +342,7 @@ def test_structured_no_content_type(specversion):
|
||||||
"specversion": specversion,
|
"specversion": specversion,
|
||||||
"data": test_data,
|
"data": test_data,
|
||||||
}
|
}
|
||||||
event = from_http(json.dumps(data), {},)
|
event = from_http({}, json.dumps(data))
|
||||||
|
|
||||||
assert event["id"] == "id"
|
assert event["id"] == "id"
|
||||||
assert event["source"] == "source.com.test"
|
assert event["source"] == "source.com.test"
|
||||||
|
@ -382,7 +380,7 @@ def test_cloudevent_repr(specversion):
|
||||||
"ce-time": "2018-10-23T12:28:22.4579346Z",
|
"ce-time": "2018-10-23T12:28:22.4579346Z",
|
||||||
"ce-source": "<source-url>",
|
"ce-source": "<source-url>",
|
||||||
}
|
}
|
||||||
event = from_http("", headers)
|
event = from_http(headers, "")
|
||||||
# Testing to make sure event is printable. I could runevent. __repr__() but
|
# Testing to make sure event is printable. I could runevent. __repr__() but
|
||||||
# we had issues in the past where event.__repr__() could run but
|
# we had issues in the past where event.__repr__() could run but
|
||||||
# print(event) would fail.
|
# print(event) would fail.
|
||||||
|
@ -398,5 +396,79 @@ def test_none_data_cloudevent(specversion):
|
||||||
"specversion": specversion,
|
"specversion": specversion,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
to_binary_http(event)
|
to_binary(event)
|
||||||
to_structured_http(event)
|
to_structured(event)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrong_specversion():
|
||||||
|
headers = {"Content-Type": "application/cloudevents+json"}
|
||||||
|
data = json.dumps(
|
||||||
|
{
|
||||||
|
"specversion": "0.2",
|
||||||
|
"type": "word.found.name",
|
||||||
|
"id": "96fb5f0b-001e-0108-6dfe-da6e2806f124",
|
||||||
|
"source": "<my-source>",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
with pytest.raises(cloud_exceptions.InvalidRequiredFields) as e:
|
||||||
|
from_http(headers, data)
|
||||||
|
assert "Found invalid specversion 0.2" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_data_format_structured_from_http():
|
||||||
|
headers = {"Content-Type": "application/cloudevents+json"}
|
||||||
|
data = 20
|
||||||
|
with pytest.raises(cloud_exceptions.InvalidStructuredJSON) as e:
|
||||||
|
from_http(headers, data)
|
||||||
|
assert "Expected json of type (str, bytes, bytearray)" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_wrong_specversion_to_request():
|
||||||
|
event = CloudEvent({"source": "s", "type": "t"}, None)
|
||||||
|
with pytest.raises(cloud_exceptions.InvalidRequiredFields) as e:
|
||||||
|
event["specversion"] = "0.2"
|
||||||
|
to_binary(event)
|
||||||
|
assert "Unsupported specversion: 0.2" in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_structured():
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/cloudevents+json",
|
||||||
|
}
|
||||||
|
assert is_structured(headers)
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"ce-id": "my-id",
|
||||||
|
"ce-source": "<event-source>",
|
||||||
|
"ce-type": "cloudevent.event.type",
|
||||||
|
"ce-specversion": "1.0",
|
||||||
|
"Content-Type": "text/plain",
|
||||||
|
}
|
||||||
|
assert not is_structured(headers)
|
||||||
|
|
||||||
|
|
||||||
|
def test_empty_json_structured():
|
||||||
|
headers = {"Content-Type": "application/cloudevents+json"}
|
||||||
|
data = ""
|
||||||
|
with pytest.raises(cloud_exceptions.InvalidStructuredJSON) as e:
|
||||||
|
from_http(
|
||||||
|
headers, data,
|
||||||
|
)
|
||||||
|
assert "Failed to read fields from structured event. " in str(e.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_uppercase_headers_with_none_data_binary():
|
||||||
|
headers = {
|
||||||
|
"Ce-Id": "my-id",
|
||||||
|
"Ce-Source": "<event-source>",
|
||||||
|
"Ce-Type": "cloudevent.event.type",
|
||||||
|
"Ce-Specversion": "1.0",
|
||||||
|
}
|
||||||
|
event = from_http(headers, None)
|
||||||
|
|
||||||
|
for key in headers:
|
||||||
|
assert event[key.lower()[3:]] == headers[key]
|
||||||
|
assert event.data == None
|
||||||
|
|
||||||
|
_, new_data = to_binary(event)
|
||||||
|
assert new_data == None
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cloudevents.sdk import converters, exceptions, marshaller
|
||||||
|
from cloudevents.sdk.converters import binary, structured
|
||||||
|
from cloudevents.sdk.event import v1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def headers():
|
||||||
|
return {
|
||||||
|
"ce-specversion": "1.0",
|
||||||
|
"ce-source": "1.0",
|
||||||
|
"ce-type": "com.marshaller.test",
|
||||||
|
"ce-id": "1234-1234-1234",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_request_wrong_unmarshaller():
|
||||||
|
with pytest.raises(exceptions.InvalidDataUnmarshaller):
|
||||||
|
m = marshaller.NewDefaultHTTPMarshaller()
|
||||||
|
_ = m.FromRequest(v1.Event(), {}, "", None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_request_wrong_marshaller():
|
||||||
|
with pytest.raises(exceptions.InvalidDataMarshaller):
|
||||||
|
m = marshaller.NewDefaultHTTPMarshaller()
|
||||||
|
_ = m.ToRequest(v1.Event(), data_marshaller="")
|
||||||
|
|
||||||
|
|
||||||
|
def test_from_request_cannot_read(headers):
|
||||||
|
with pytest.raises(exceptions.UnsupportedEventConverter):
|
||||||
|
m = marshaller.HTTPMarshaller(
|
||||||
|
[binary.NewBinaryHTTPCloudEventConverter(),]
|
||||||
|
)
|
||||||
|
m.FromRequest(v1.Event(), {}, "")
|
||||||
|
|
||||||
|
with pytest.raises(exceptions.UnsupportedEventConverter):
|
||||||
|
m = marshaller.HTTPMarshaller(
|
||||||
|
[structured.NewJSONHTTPCloudEventConverter()]
|
||||||
|
)
|
||||||
|
m.FromRequest(v1.Event(), headers, "")
|
||||||
|
|
||||||
|
|
||||||
|
def test_to_request_invalid_converter():
|
||||||
|
with pytest.raises(exceptions.NoSuchConverter):
|
||||||
|
m = marshaller.HTTPMarshaller(
|
||||||
|
[structured.NewJSONHTTPCloudEventConverter()]
|
||||||
|
)
|
||||||
|
m.ToRequest(v1.Event(), "")
|
|
@ -0,0 +1,36 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cloudevents.sdk.event.opt import Option
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_raise_error():
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
o = Option("test", "value", True)
|
||||||
|
o.set(None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_options_eq_override():
|
||||||
|
o = Option("test", "value", True)
|
||||||
|
assert o.required()
|
||||||
|
|
||||||
|
o2 = Option("test", "value", True)
|
||||||
|
assert o2.required()
|
||||||
|
|
||||||
|
assert o == o2
|
||||||
|
o.set("setting to new value")
|
||||||
|
|
||||||
|
assert o != o2
|
|
@ -0,0 +1,64 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cloudevents.sdk.event import v03
|
||||||
|
|
||||||
|
|
||||||
|
def test_v03_time_property():
|
||||||
|
event = v03.Event()
|
||||||
|
|
||||||
|
time1 = "1234"
|
||||||
|
event.time = time1
|
||||||
|
assert event.EventTime() == time1
|
||||||
|
|
||||||
|
time2 = "4321"
|
||||||
|
event.SetEventTime(time2)
|
||||||
|
assert event.time == time2
|
||||||
|
|
||||||
|
|
||||||
|
def test_v03_subject_property():
|
||||||
|
event = v03.Event()
|
||||||
|
|
||||||
|
subject1 = "<my-subject>"
|
||||||
|
event.subject = subject1
|
||||||
|
assert event.Subject() == subject1
|
||||||
|
|
||||||
|
subject2 = "<my-subject2>"
|
||||||
|
event.SetSubject(subject2)
|
||||||
|
assert event.subject == subject2
|
||||||
|
|
||||||
|
|
||||||
|
def test_v03_schema_url_property():
|
||||||
|
event = v03.Event()
|
||||||
|
|
||||||
|
schema_url1 = "<my-schema>"
|
||||||
|
event.schema_url = schema_url1
|
||||||
|
assert event.SchemaURL() == schema_url1
|
||||||
|
|
||||||
|
schema_url2 = "<my-schema2>"
|
||||||
|
event.SetSchemaURL(schema_url2)
|
||||||
|
assert event.schema_url == schema_url2
|
||||||
|
|
||||||
|
|
||||||
|
def test_v03_datacontentencoding_property():
|
||||||
|
event = v03.Event()
|
||||||
|
|
||||||
|
datacontentencoding1 = "<my-datacontentencoding>"
|
||||||
|
event.datacontentencoding = datacontentencoding1
|
||||||
|
assert event.ContentEncoding() == datacontentencoding1
|
||||||
|
|
||||||
|
datacontentencoding2 = "<my-datacontentencoding2>"
|
||||||
|
event.SetContentEncoding(datacontentencoding2)
|
||||||
|
assert event.datacontentencoding == datacontentencoding2
|
|
@ -0,0 +1,53 @@
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from cloudevents.sdk.event import v1
|
||||||
|
|
||||||
|
|
||||||
|
def test_v1_time_property():
|
||||||
|
event = v1.Event()
|
||||||
|
|
||||||
|
time1 = "1234"
|
||||||
|
event.time = time1
|
||||||
|
assert event.EventTime() == time1
|
||||||
|
|
||||||
|
time2 = "4321"
|
||||||
|
event.SetEventTime(time2)
|
||||||
|
assert event.time == time2
|
||||||
|
|
||||||
|
|
||||||
|
def test_v1_subject_property():
|
||||||
|
event = v1.Event()
|
||||||
|
|
||||||
|
subject1 = "<my-subject>"
|
||||||
|
event.subject = subject1
|
||||||
|
assert event.Subject() == subject1
|
||||||
|
|
||||||
|
subject2 = "<my-subject2>"
|
||||||
|
event.SetSubject(subject2)
|
||||||
|
assert event.subject == subject2
|
||||||
|
|
||||||
|
|
||||||
|
def test_v1_schema_property():
|
||||||
|
event = v1.Event()
|
||||||
|
|
||||||
|
schema1 = "<my-schema>"
|
||||||
|
event.schema = schema1
|
||||||
|
assert event.Schema() == schema1
|
||||||
|
|
||||||
|
schema2 = "<my-schema2>"
|
||||||
|
event.SetSchema(schema2)
|
||||||
|
assert event.schema == schema2
|
|
@ -15,7 +15,7 @@ import sys
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from cloudevents.http import CloudEvent, to_binary_http, to_structured_http
|
from cloudevents.http import CloudEvent, to_binary, to_structured
|
||||||
|
|
||||||
resp = requests.get(
|
resp = requests.get(
|
||||||
"https://raw.githubusercontent.com/cncf/artwork/master/projects/cloudevents/horizontal/color/cloudevents-horizontal-color.png"
|
"https://raw.githubusercontent.com/cncf/artwork/master/projects/cloudevents/horizontal/color/cloudevents-horizontal-color.png"
|
||||||
|
@ -33,7 +33,7 @@ def send_binary_cloud_event(url: str):
|
||||||
event = CloudEvent(attributes, image_bytes)
|
event = CloudEvent(attributes, image_bytes)
|
||||||
|
|
||||||
# Create cloudevent HTTP headers and content
|
# Create cloudevent HTTP headers and content
|
||||||
headers, body = to_binary_http(event)
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
# Send cloudevent
|
# Send cloudevent
|
||||||
requests.post(url, headers=headers, data=body)
|
requests.post(url, headers=headers, data=body)
|
||||||
|
@ -50,10 +50,10 @@ def send_structured_cloud_event(url: str):
|
||||||
event = CloudEvent(attributes, image_bytes)
|
event = CloudEvent(attributes, image_bytes)
|
||||||
|
|
||||||
# Create cloudevent HTTP headers and content
|
# Create cloudevent HTTP headers and content
|
||||||
# Note that to_structured_http will create a data_base64 data field in
|
# Note that to_structured will create a data_base64 data field in
|
||||||
# specversion 1.0 (default specversion) if given
|
# specversion 1.0 (default specversion) if given
|
||||||
# an event whose data field is of type bytes.
|
# an event whose data field is of type bytes.
|
||||||
headers, body = to_structured_http(event)
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
# Send cloudevent
|
# Send cloudevent
|
||||||
requests.post(url, headers=headers, data=body)
|
requests.post(url, headers=headers, data=body)
|
||||||
|
|
|
@ -26,8 +26,8 @@ def home():
|
||||||
# Create a CloudEvent.
|
# Create a CloudEvent.
|
||||||
# data_unmarshaller will cast event.data into an io.BytesIO object
|
# data_unmarshaller will cast event.data into an io.BytesIO object
|
||||||
event = from_http(
|
event = from_http(
|
||||||
request.get_data(),
|
|
||||||
request.headers,
|
request.headers,
|
||||||
|
request.get_data(),
|
||||||
data_unmarshaller=lambda x: io.BytesIO(x),
|
data_unmarshaller=lambda x: io.BytesIO(x),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -7,12 +7,7 @@ from client import image_bytes
|
||||||
from image_sample_server import app
|
from image_sample_server import app
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from cloudevents.http import (
|
from cloudevents.http import CloudEvent, from_http, to_binary, to_structured
|
||||||
CloudEvent,
|
|
||||||
from_http,
|
|
||||||
to_binary_http,
|
|
||||||
to_structured_http,
|
|
||||||
)
|
|
||||||
|
|
||||||
image_fileobj = io.BytesIO(image_bytes)
|
image_fileobj = io.BytesIO(image_bytes)
|
||||||
image_expected_shape = (1880, 363)
|
image_expected_shape = (1880, 363)
|
||||||
|
@ -35,11 +30,11 @@ def test_create_binary_image():
|
||||||
event = CloudEvent(attributes, image_bytes)
|
event = CloudEvent(attributes, image_bytes)
|
||||||
|
|
||||||
# Create http headers/body content
|
# Create http headers/body content
|
||||||
headers, body = to_binary_http(event)
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
# Unmarshall CloudEvent and re-create image
|
# Unmarshall CloudEvent and re-create image
|
||||||
reconstruct_event = from_http(
|
reconstruct_event = from_http(
|
||||||
body, headers, data_unmarshaller=lambda x: io.BytesIO(x)
|
headers, body, data_unmarshaller=lambda x: io.BytesIO(x)
|
||||||
)
|
)
|
||||||
|
|
||||||
# reconstruct_event.data is an io.BytesIO object due to data_unmarshaller
|
# reconstruct_event.data is an io.BytesIO object due to data_unmarshaller
|
||||||
|
@ -62,7 +57,7 @@ def test_create_structured_image():
|
||||||
event = CloudEvent(attributes, image_bytes)
|
event = CloudEvent(attributes, image_bytes)
|
||||||
|
|
||||||
# Create http headers/body content
|
# Create http headers/body content
|
||||||
headers, body = to_structured_http(event)
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
# Structured has cloudevent attributes marshalled inside the body. For this
|
# Structured has cloudevent attributes marshalled inside the body. For this
|
||||||
# reason we must load the byte object to create the python dict containing
|
# reason we must load the byte object to create the python dict containing
|
||||||
|
@ -75,7 +70,7 @@ def test_create_structured_image():
|
||||||
|
|
||||||
# Unmarshall CloudEvent and re-create image
|
# Unmarshall CloudEvent and re-create image
|
||||||
reconstruct_event = from_http(
|
reconstruct_event = from_http(
|
||||||
body, headers, data_unmarshaller=lambda x: io.BytesIO(x)
|
headers, body, data_unmarshaller=lambda x: io.BytesIO(x)
|
||||||
)
|
)
|
||||||
|
|
||||||
# reconstruct_event.data is an io.BytesIO object due to data_unmarshaller
|
# reconstruct_event.data is an io.BytesIO object due to data_unmarshaller
|
||||||
|
@ -92,10 +87,10 @@ def test_server_structured(client):
|
||||||
event = CloudEvent(attributes, image_bytes)
|
event = CloudEvent(attributes, image_bytes)
|
||||||
|
|
||||||
# Create cloudevent HTTP headers and content
|
# Create cloudevent HTTP headers and content
|
||||||
# Note that to_structured_http will create a data_base64 data field in
|
# Note that to_structured will create a data_base64 data field in
|
||||||
# specversion 1.0 (default specversion) if given
|
# specversion 1.0 (default specversion) if given
|
||||||
# an event whose data field is of type bytes.
|
# an event whose data field is of type bytes.
|
||||||
headers, body = to_structured_http(event)
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
# Send cloudevent
|
# Send cloudevent
|
||||||
r = client.post("/", headers=headers, data=body)
|
r = client.post("/", headers=headers, data=body)
|
||||||
|
@ -113,7 +108,7 @@ def test_server_binary(client):
|
||||||
event = CloudEvent(attributes, image_bytes)
|
event = CloudEvent(attributes, image_bytes)
|
||||||
|
|
||||||
# Create cloudevent HTTP headers and content
|
# Create cloudevent HTTP headers and content
|
||||||
headers, body = to_binary_http(event)
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
# Send cloudevent
|
# Send cloudevent
|
||||||
r = client.post("/", headers=headers, data=body)
|
r = client.post("/", headers=headers, data=body)
|
||||||
|
|
|
@ -16,7 +16,7 @@ import sys
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from cloudevents.http import CloudEvent, to_binary_http, to_structured_http
|
from cloudevents.http import CloudEvent, to_binary, to_structured
|
||||||
|
|
||||||
|
|
||||||
def send_binary_cloud_event(url):
|
def send_binary_cloud_event(url):
|
||||||
|
@ -28,7 +28,7 @@ def send_binary_cloud_event(url):
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
|
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body = to_binary_http(event)
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
# send and print event
|
# send and print event
|
||||||
requests.post(url, headers=headers, data=body)
|
requests.post(url, headers=headers, data=body)
|
||||||
|
@ -44,7 +44,7 @@ def send_structured_cloud_event(url):
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
|
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body = to_structured_http(event)
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
# send and print event
|
# send and print event
|
||||||
requests.post(url, headers=headers, data=body)
|
requests.post(url, headers=headers, data=body)
|
||||||
|
|
|
@ -22,7 +22,7 @@ app = Flask(__name__)
|
||||||
@app.route("/", methods=["POST"])
|
@app.route("/", methods=["POST"])
|
||||||
def home():
|
def home():
|
||||||
# create a CloudEvent
|
# create a CloudEvent
|
||||||
event = from_http(request.get_data(), request.headers)
|
event = from_http(request.headers, request.get_data())
|
||||||
|
|
||||||
# you can access cloudevent fields as seen below
|
# you can access cloudevent fields as seen below
|
||||||
print(
|
print(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import pytest
|
import pytest
|
||||||
from json_sample_server import app
|
from json_sample_server import app
|
||||||
|
|
||||||
from cloudevents.http import CloudEvent, to_binary_http, to_structured_http
|
from cloudevents.http import CloudEvent, to_binary, to_structured
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -19,7 +19,7 @@ def test_binary_request(client):
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
|
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body = to_binary_http(event)
|
headers, body = to_binary(event)
|
||||||
|
|
||||||
r = client.post("/", headers=headers, data=body)
|
r = client.post("/", headers=headers, data=body)
|
||||||
assert r.status_code == 204
|
assert r.status_code == 204
|
||||||
|
@ -34,7 +34,7 @@ def test_structured_request(client):
|
||||||
data = {"message": "Hello World!"}
|
data = {"message": "Hello World!"}
|
||||||
|
|
||||||
event = CloudEvent(attributes, data)
|
event = CloudEvent(attributes, data)
|
||||||
headers, body = to_structured_http(event)
|
headers, body = to_structured(event)
|
||||||
|
|
||||||
r = client.post("/", headers=headers, data=body)
|
r = client.post("/", headers=headers, data=body)
|
||||||
assert r.status_code == 204
|
assert r.status_code == 204
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -41,4 +41,5 @@ setup(
|
||||||
],
|
],
|
||||||
packages=find_packages(exclude=["cloudevents.tests"]),
|
packages=find_packages(exclude=["cloudevents.tests"]),
|
||||||
version=pypi_config["version_target"],
|
version=pypi_config["version_target"],
|
||||||
|
install_requires=["deprecation>=2.0,<3.0"],
|
||||||
)
|
)
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -9,7 +9,7 @@ deps =
|
||||||
-r{toxinidir}/requirements/docs.txt
|
-r{toxinidir}/requirements/docs.txt
|
||||||
-r{toxinidir}/requirements/publish.txt
|
-r{toxinidir}/requirements/publish.txt
|
||||||
setenv =
|
setenv =
|
||||||
PYTESTARGS = -v -s --tb=long --cov=cloudevents
|
PYTESTARGS = -v -s --tb=long --cov=cloudevents --cov-report term-missing --cov-fail-under=100
|
||||||
commands = pytest {env:PYTESTARGS} {posargs}
|
commands = pytest {env:PYTESTARGS} {posargs}
|
||||||
|
|
||||||
[testenv:reformat]
|
[testenv:reformat]
|
||||||
|
|
Loading…
Reference in New Issue