Skip to content

Make SDK compliant with CloudEvents SDK spec #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,4 @@ venv
ChangeLog
AUTHORS
.pytest_cache/
.idea
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Package **cloudevents** provides primitives to work with CloudEvents specificati

Parsing upstream Event from HTTP Request:
```python
from cloudevents.sdk.event import upstream
from cloudevents.sdk.event import v02
from cloudevents.sdk import marshaller

data = "<this is where your CloudEvent comes from>"
m = marshaller.NewDefaultHTTPMarshaller(upstream.Event)
m = marshaller.NewDefaultHTTPMarshaller(v02.Event)
event = m.FromRequest(
{"Content-Type": "application/cloudevents+json"},
data,
Expand All @@ -25,12 +25,12 @@ from cloudevents.sdk.event import v01

event = (
v01.Event().
WithContentType("application/json").
WithData('{"name":"john"}').
WithEventID("my-id").
WithSource("from-galaxy-far-far-away").
WithEventTime("tomorrow").
WithEventType("cloudevent.greet.you")
SetContentType("application/json").
SetData('{"name":"john"}').
SetEventID("my-id").
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason this isn't just SetID to align with the property name change? Same for Time and Type below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That’s what’s in SDK spec. Verbose API is a better option here, less things seem to be ambiguous.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok

SetSource("from-galaxy-far-far-away").
SetEventTime("tomorrow").
SetEventType("cloudevent.greet.you")
)

```
Expand All @@ -44,12 +44,12 @@ from cloudevents.sdk.event import v01

event = (
v01.Event().
WithContentType("application/json").
WithData('{"name":"john"}').
WithEventID("my-id").
WithSource("from-galaxy-far-far-away").
WithEventTime("tomorrow").
WithEventType("cloudevent.greet.you")
SetContentType("application/json").
SetData('{"name":"john"}').
SetEventID("my-id").
SetSource("from-galaxy-far-far-away").
SetEventTime("tomorrow").
SetEventType("cloudevent.greet.you")
)
m = marshaller.NewHTTPMarshaller(
[
Expand Down
11 changes: 1 addition & 10 deletions cloudevents/sdk/converters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,7 @@ class Converter(object):

TYPE = None

def __init__(
self, event_class: base.BaseEvent,
supported_media_types: typing.Mapping[str, bool]):
self.event = event_class()
self.supported_media_types = supported_media_types

def can_read(self, media_type: str) -> bool:
return media_type in self.supported_media_types

def read(self, headers: dict, body: typing.IO,
def read(self, event, headers: dict, body: typing.IO,
data_unmarshaller: typing.Callable) -> base.BaseEvent:
raise Exception("not implemented")

Expand Down
25 changes: 7 additions & 18 deletions cloudevents/sdk/converters/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,20 @@
from cloudevents.sdk import exceptions
from cloudevents.sdk.converters import base
from cloudevents.sdk.event import base as event_base
from cloudevents.sdk.event import v01
from cloudevents.sdk.event import v02


class BinaryHTTPCloudEventConverter(base.Converter):

TYPE = "binary"

def __init__(self, event_class: event_base.BaseEvent,
supported_media_types: typing.Mapping[str, bool]):
if event_class == v01.Event:
raise exceptions.UnsupportedEvent(event_class)

super().__init__(event_class, supported_media_types)
SUPPORTED_VERSIONS = [v02.Event, ]

def read(self,
event: event_base.BaseEvent,
headers: dict, body: typing.IO,
data_unmarshaller: typing.Callable) -> event_base.BaseEvent:
# we ignore headers, since the whole CE is in request body
event = self.event
if type(event) not in self.SUPPORTED_VERSIONS:
raise exceptions.UnsupportedEvent(type(event))
event.UnmarshalBinary(headers, body, data_unmarshaller)
return event

Expand All @@ -48,11 +43,5 @@ def write(self, event: event_base.BaseEvent,
return hs, data_marshaller(data)


def NewBinaryHTTPCloudEventConverter(
event_class: event_base.BaseEvent) -> BinaryHTTPCloudEventConverter:
media_types = {
"application/json": True,
"application/xml": True,
"application/octet-stream": True,
}
return BinaryHTTPCloudEventConverter(event_class, media_types)
def NewBinaryHTTPCloudEventConverter() -> BinaryHTTPCloudEventConverter:
return BinaryHTTPCloudEventConverter()
18 changes: 4 additions & 14 deletions cloudevents/sdk/converters/structured.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,10 @@ class JSONHTTPCloudEventConverter(base.Converter):

TYPE = "structured"

def __init__(self, event_class: event_base.BaseEvent,
supported_media_types: typing.Mapping[str, bool]):
super().__init__(event_class, supported_media_types)

def read(self, headers: dict,
def read(self, event: event_base.BaseEvent,
headers: dict,
body: typing.IO,
data_unmarshaller: typing.Callable) -> event_base.BaseEvent:
# we ignore headers, since the whole CE is in request body
event = self.event
event.UnmarshalJSON(body, data_unmarshaller)
return event

Expand All @@ -44,10 +39,5 @@ def write(self,
return {}, event.MarshalJSON(data_marshaller)


def NewJSONHTTPCloudEventConverter(
event_class: event_base.BaseEvent) -> JSONHTTPCloudEventConverter:
media_types = {
"application/cloudevents+json": True,
}

return JSONHTTPCloudEventConverter(event_class, media_types)
def NewJSONHTTPCloudEventConverter() -> JSONHTTPCloudEventConverter:
return JSONHTTPCloudEventConverter()
16 changes: 8 additions & 8 deletions cloudevents/sdk/event/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,28 @@ def ContentType(self) -> str:
# CloudEvent attribute constructors
# Each setter return an instance of its class
# in order to build a pipeline of setter
def WithEventType(self, eventType: str) -> object:
def SetEventType(self, eventType: str) -> object:
raise Exception("not implemented")

def WithSource(self, source: str) -> object:
def SetSource(self, source: str) -> object:
raise Exception("not implemented")

def WithEventID(self, eventID: str) -> object:
def SetEventID(self, eventID: str) -> object:
raise Exception("not implemented")

def WithEventTime(self, eventTime: str) -> object:
def SetEventTime(self, eventTime: str) -> object:
raise Exception("not implemented")

def WithSchemaURL(self, schemaURL: str) -> object:
def SetSchemaURL(self, schemaURL: str) -> object:
raise Exception("not implemented")

def WithData(self, data: object) -> object:
def SetData(self, data: object) -> object:
raise Exception("not implemented")

def WithExtensions(self, extensions: dict) -> object:
def SetExtensions(self, extensions: dict) -> object:
raise Exception("not implemented")

def WithContentType(self, contentType: str) -> object:
def SetContentType(self, contentType: str) -> object:
raise Exception("not implemented")


Expand Down
16 changes: 8 additions & 8 deletions cloudevents/sdk/event/v01.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,35 +59,35 @@ def Extensions(self) -> dict:
def ContentType(self) -> str:
return self.ce__contentType.get()

def WithEventType(self, eventType: str) -> base.BaseEvent:
def SetEventType(self, eventType: str) -> base.BaseEvent:
self.Set("eventType", eventType)
return self

def WithSource(self, source: str) -> base.BaseEvent:
def SetSource(self, source: str) -> base.BaseEvent:
self.Set("source", source)
return self

def WithEventID(self, eventID: str) -> base.BaseEvent:
def SetEventID(self, eventID: str) -> base.BaseEvent:
self.Set("eventID", eventID)
return self

def WithEventTime(self, eventTime: str) -> base.BaseEvent:
def SetEventTime(self, eventTime: str) -> base.BaseEvent:
self.Set("eventTime", eventTime)
return self

def WithSchemaURL(self, schemaURL: str) -> base.BaseEvent:
def SetSchemaURL(self, schemaURL: str) -> base.BaseEvent:
self.Set("schemaURL", schemaURL)
return self

def WithData(self, data: object) -> base.BaseEvent:
def SetData(self, data: object) -> base.BaseEvent:
self.Set("data", data)
return self

def WithExtensions(self, extensions: dict) -> base.BaseEvent:
def SetExtensions(self, extensions: dict) -> base.BaseEvent:
self.Set("extension", extensions)
return self

def WithContentType(self, contentType: str) -> base.BaseEvent:
def SetContentType(self, contentType: str) -> base.BaseEvent:
self.Set("contentType", contentType)
return self

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
class Event(base.BaseEvent):

def __init__(self):
self.ce__specversion = opt.Option("specversion", "0.1", True)
self.ce__specversion = opt.Option("specversion", "0.2", True)
self.ce__type = opt.Option("type", None, True)
self.ce__source = opt.Option("source", None, True)
self.ce__id = opt.Option("id", None, True)
Expand Down Expand Up @@ -56,34 +56,34 @@ def Extensions(self) -> dict:
def ContentType(self) -> str:
return self.ce__contenttype.get()

def WithEventType(self, eventType: str) -> base.BaseEvent:
def SetEventType(self, eventType: str) -> base.BaseEvent:
self.Set("type", eventType)
return self

def WithSource(self, source: str) -> base.BaseEvent:
def SetSource(self, source: str) -> base.BaseEvent:
self.Set("source", source)
return self

def WithEventID(self, eventID: str) -> base.BaseEvent:
def SetEventID(self, eventID: str) -> base.BaseEvent:
self.Set("id", eventID)
return self

def WithEventTime(self, eventTime: str) -> base.BaseEvent:
def SetEventTime(self, eventTime: str) -> base.BaseEvent:
self.Set("time", eventTime)
return self

def WithSchemaURL(self, schemaURL: str) -> base.BaseEvent:
def SetSchemaURL(self, schemaURL: str) -> base.BaseEvent:
self.Set("schemaurl", schemaURL)
return self

def WithData(self, data: object) -> base.BaseEvent:
def SetData(self, data: object) -> base.BaseEvent:
self.Set("data", data)
return self

def WithExtensions(self, extensions: dict) -> base.BaseEvent:
def SetExtensions(self, extensions: dict) -> base.BaseEvent:
self.Set("extension", extensions)
return self

def WithContentType(self, contentType: str) -> base.BaseEvent:
def SetContentType(self, contentType: str) -> base.BaseEvent:
self.Set("contenttype", contentType)
return self
8 changes: 0 additions & 8 deletions cloudevents/sdk/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@
# under the License.


class InvalidMimeTypeFromRequest(Exception):

def __init__(self, mime_type):
super().__init__(
"Unable to read CloudEvent from request, "
"invalid MIME type: {0}".format(mime_type))


class UnsupportedEvent(Exception):

def __init__(self, event_class):
Expand Down
20 changes: 8 additions & 12 deletions cloudevents/sdk/marshaller.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ def __init__(self, converters: typing.List[base.Converter]):
"""
self.__converters = {c.TYPE: c for c in converters}

def FromRequest(self, headers: dict,
def FromRequest(self, event: event_base.BaseEvent,
headers: dict,
body: typing.IO,
data_unmarshaller:
typing.Callable) -> event_base.BaseEvent:
"""
Reads a CloudEvent from an HTTP headers and request body
:param event: CloudEvent placeholder
:type event: cloudevents.sdk.event.base.BaseEvent
:param headers: a dict-like HTTP headers
:type headers: dict
:param body: a stream-like HTTP request body
Expand All @@ -52,12 +55,8 @@ def FromRequest(self, headers: dict,
:return: a CloudEvent
:rtype: event_base.BaseEvent
"""
mimeType = headers.get("Content-Type")
for _, cnvrtr in self.__converters.items():
if cnvrtr.can_read(mimeType):
return cnvrtr.read(headers, body, data_unmarshaller)

raise exceptions.InvalidMimeTypeFromRequest(mimeType)
return cnvrtr.read(event, headers, body, data_unmarshaller)

def ToRequest(self, event: event_base.BaseEvent,
converter_type: str,
Expand All @@ -80,19 +79,16 @@ def ToRequest(self, event: event_base.BaseEvent,
raise exceptions.NoSuchConverter(converter_type)


def NewDefaultHTTPMarshaller(
event_class: event_base.BaseEvent) -> HTTPMarshaller:
def NewDefaultHTTPMarshaller() -> HTTPMarshaller:
"""
Creates the default HTTP marshaller with both structured
and binary converters
:param event_class: CloudEvent spec class
:type event_class: event_base.BaseEvent
:return: an instance of HTTP marshaller
:rtype: cloudevents.sdk.marshaller.HTTPMarshaller
"""
return HTTPMarshaller([
structured.NewJSONHTTPCloudEventConverter(event_class),
binary.NewBinaryHTTPCloudEventConverter(event_class),
structured.NewJSONHTTPCloudEventConverter(),
binary.NewBinaryHTTPCloudEventConverter(),
])


Expand Down
2 changes: 1 addition & 1 deletion cloudevents/tests/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
ce_type = "word.found.exclamation"
ce_id = "16fb5f0b-211e-1102-3dfe-ea6e2806f124"
source = "pytest"
specversion = "0.1"
specversion = "0.2"
eventTime = "2018-10-23T12:28:23.3464579Z"
body = '{"name":"john"}'
headers = {
Expand Down
Loading