-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Cloud Event Abstraction #13542
Cloud Event Abstraction #13542
Changes from 11 commits
d111542
a8a01f9
1b62b89
8022a0c
f1e1cbe
db40e9c
a82a463
71ecc66
d38c325
48394c9
1cd55e5
6fb8283
38a76e6
5a91d22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
from msrest.serialization import UTC | ||
import datetime as dt | ||
import uuid | ||
import base64 | ||
rakshith91 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import json | ||
import six | ||
from ._generated import models | ||
|
@@ -50,7 +51,7 @@ def _from_json(event, encode): | |
return event | ||
|
||
|
||
class CloudEvent(InternalCloudEvent, EventMixin): #pylint:disable=too-many-instance-attributes | ||
class CloudEvent(EventMixin): #pylint:disable=too-many-instance-attributes | ||
"""Properties of an event published to an Event Grid topic using the CloudEvent 1.0 Schema. | ||
|
||
All required parameters must be populated in order to send to Azure. | ||
|
@@ -75,35 +76,60 @@ class CloudEvent(InternalCloudEvent, EventMixin): #pylint:disable=too-many-ins | |
unique for each distinct event. | ||
:type id: Optional[str] | ||
""" | ||
|
||
_validation = { | ||
'source': {'required': True}, | ||
'type': {'required': True}, | ||
} | ||
|
||
_attribute_map = { | ||
'additional_properties': {'key': '', 'type': '{object}'}, | ||
'id': {'key': 'id', 'type': 'str'}, | ||
'source': {'key': 'source', 'type': 'str'}, | ||
'data': {'key': 'data', 'type': 'object'}, | ||
'data_base64': {'key': 'data_base64', 'type': 'bytearray'}, | ||
'type': {'key': 'type', 'type': 'str'}, | ||
'time': {'key': 'time', 'type': 'iso-8601'}, | ||
'specversion': {'key': 'specversion', 'type': 'str'}, | ||
'dataschema': {'key': 'dataschema', 'type': 'str'}, | ||
'datacontenttype': {'key': 'datacontenttype', 'type': 'str'}, | ||
'subject': {'key': 'subject', 'type': 'str'}, | ||
} | ||
|
||
def __init__(self, source, type, **kwargs): | ||
# type: (str, str, Any) -> None | ||
kwargs.setdefault('id', uuid.uuid4()) | ||
kwargs.setdefault("source", source) | ||
kwargs.setdefault("type", type) | ||
kwargs.setdefault("time", dt.datetime.now(UTC()).isoformat()) | ||
kwargs.setdefault("specversion", "1.0") | ||
|
||
super(CloudEvent, self).__init__(**kwargs) | ||
self.source = source | ||
self.type = type | ||
self.specversion = kwargs.pop("specversion", "1.0") | ||
self.id = kwargs.pop("id", str(uuid.uuid4())) | ||
self.time = kwargs.pop("time", dt.datetime.now(UTC()).isoformat()) | ||
self.data = kwargs.pop("data", None) | ||
self.datacontenttype = kwargs.pop("datacontenttype", None) | ||
self.dataschema = kwargs.pop("dataschema", None) | ||
self.subject = kwargs.pop("subject", None) | ||
self._extensions = {} | ||
self._extensions.update({k:v for k, v in kwargs.pop('extensions', {}).items()}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How is extensions available to people if we make it private? They should be able to read from it |
||
|
||
@classmethod | ||
def _from_generated(cls, cloud_event, **kwargs): | ||
generated = InternalCloudEvent.deserialize(cloud_event) | ||
if generated.additional_properties: | ||
extensions = {k:v for k, v in generated.additional_properties.items()} | ||
kwargs.setdefault('extensions', extensions) | ||
return cls( | ||
id=generated.id, | ||
source=generated.source, | ||
type=generated.type, | ||
specversion=generated.specversion, | ||
data=generated.data or generated.data_base64, | ||
time=generated.time, | ||
dataschema=generated.dataschema, | ||
datacontenttype=generated.datacontenttype, | ||
subject=generated.subject, | ||
**kwargs | ||
) | ||
|
||
def _to_generated(self, **kwargs): | ||
if isinstance(self.data, six.binary_type): | ||
data_base64 = self.data | ||
data = None | ||
else: | ||
data_base64 = None | ||
data = self.data | ||
return InternalCloudEvent( | ||
id=self.id, | ||
source=self.source, | ||
type=self.type, | ||
specversion=self.specversion, | ||
data=data, | ||
data_base64=data_base64, | ||
time=self.time, | ||
dataschema=self.dataschema, | ||
datacontenttype=self.datacontenttype, | ||
subject=self.subject, | ||
additional_properties=self._extensions, | ||
**kwargs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extensions should be passed part of additional_properties In [3]: c=CloudEvent(id=42, source="source", type="type", specversion="1.0", additional_properties={"foo":True})
In [4]: c.serialize()
Out[4]:
{'foo': True,
'id': '42',
'source': 'source',
'type': 'type',
'specversion': '1.0'} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Apologies if I misremembered our call the other day but did we reach a conclusion about not using kwargs for extensions? My fear being that we've just lost symmetry if someone **'s a serialized cloudevent back into itself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i took the liberty to slightly change the behavior to this
will serialize to
In other words, extensions can be accepted as both kwargs and an explicit extensions dictionary Rationale is
I can disallow kwargs if there is a strong opinion against this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pitfall here is if someone has an additional property called additional_properties, we get "undefined behavior". I seem to recall a mention in the call that we weren't TOO worried about that/could document it/reserved keywords and all that, but it's the one thing that irks me. This may be a good target for getting some broader user input on, having them hit this pitfall and see if it's dig-out-able or understandable to their eyes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
:) What if you do I'm not against it and it has appeal, but right now I want to be sure we have the base minimal behavior, and improve from it first. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed it to accepting only extensions - thank you both for the inputs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also @KieranBrantnerMagee , I'm catching up your comment, |
||
) | ||
|
||
|
||
class EventGridEvent(InternalEventGridEvent, EventMixin): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -62,6 +62,10 @@ def send(self, events, **kwargs): | |
events = [events] | ||
|
||
if all(isinstance(e, CloudEvent) for e in events) or all(_is_cloud_event(e) for e in events): | ||
try: | ||
events = [e._to_generated(**kwargs) for e in events] | ||
except AttributeError: | ||
pass # means it's a dictionary | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rakshith91 still, how could this happen? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry - missed it |
||
kwargs.setdefault("content_type", "application/cloudevents-batch+json; charset=utf-8") | ||
self._client.publish_cloud_event_events(self._topic_hostname, events, **kwargs) | ||
elif all(isinstance(e, EventGridEvent) for e in events) or all(isinstance(e, dict) for e in events): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
interactions: | ||
- request: | ||
body: '[{"reason_code": 204, "extension": "extension", "id": "9da8e4ce-df2c-4480-b809-c7132fb3ee74", | ||
"source": "http://samplesource.dev", "data": "cloudevent", "type": "Sample.Cloud.Event", | ||
"time": "2020-09-03T21:37:15.165652Z", "specversion": "1.0"}]' | ||
headers: | ||
Accept: | ||
- '*/*' | ||
Accept-Encoding: | ||
- gzip, deflate | ||
Connection: | ||
- keep-alive | ||
Content-Length: | ||
- '244' | ||
Content-Type: | ||
- application/cloudevents-batch+json; charset=utf-8 | ||
User-Agent: | ||
- azsdk-python-eventgridpublisherclient/unknown Python/3.7.3 (Windows-10-10.0.18362-SP0) | ||
method: POST | ||
uri: https://cloudeventgridtestegtopic.westus-1.eventgrid.azure.net/api/events?api-version=2018-01-01 | ||
response: | ||
body: | ||
string: '' | ||
headers: | ||
api-supported-versions: | ||
- '2018-01-01' | ||
content-length: | ||
- '0' | ||
date: | ||
- Thu, 03 Sep 2020 21:37:13 GMT | ||
server: | ||
- Microsoft-HTTPAPI/2.0 | ||
strict-transport-security: | ||
- max-age=31536000; includeSubDomains | ||
status: | ||
code: 200 | ||
message: OK | ||
version: 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed anymore