Initial release: CloudEvents Python SDK 0.0.1a0
Signed-off-by: Denis Makogon <lildee1991@gmail.com>
This commit is contained in:
parent
159332b754
commit
91224583c4
|
@ -0,0 +1,125 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
env/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# dotenv
|
||||
.env
|
||||
|
||||
# virtualenv
|
||||
.venv
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
*.pyc
|
||||
.testrepository
|
||||
.tox/*
|
||||
dist/*
|
||||
build/*
|
||||
html/*
|
||||
*.egg*
|
||||
cover/*
|
||||
.coverage
|
||||
rdserver.txt
|
||||
python-troveclient.iml
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
.coverage.*
|
||||
*.json
|
||||
.cache
|
||||
*.log*
|
||||
*.csv
|
||||
venv
|
||||
.venv
|
||||
ChangeLog
|
||||
AUTHORS
|
||||
.pytest_cache/
|
|
@ -0,0 +1,19 @@
|
|||
# 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.
|
||||
|
||||
from cloudevents.sdk.converters import binary
|
||||
from cloudevents.sdk.converters import structured
|
||||
|
||||
TypeBinary = binary.BinaryHTTPCloudEventConverter.TYPE
|
||||
TypeStructured = structured.JSONHTTPCloudEventConverter.TYPE
|
|
@ -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 typing
|
||||
|
||||
from cloudevents.sdk.event import base
|
||||
|
||||
|
||||
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 can_write(self, media_type: str) -> bool:
|
||||
return media_type in self.supported_media_types
|
||||
|
||||
def read(self, headers: dict, body: typing.IO) -> base.BaseEvent:
|
||||
raise Exception("not implemented")
|
||||
|
||||
def write(self, event: base.BaseEvent,
|
||||
data_marshaller: typing.Callable) -> (dict, typing.IO):
|
||||
raise Exception("not implemented")
|
|
@ -0,0 +1,54 @@
|
|||
# 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 typing
|
||||
|
||||
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
|
||||
|
||||
|
||||
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)
|
||||
|
||||
def read(self,
|
||||
headers: dict, body: typing.IO) -> event_base.BaseEvent:
|
||||
# we ignore headers, since the whole CE is in request body
|
||||
event = self.event
|
||||
event.UnmarshalBinary(headers, body)
|
||||
return event
|
||||
|
||||
def write(self, event: event_base.BaseEvent,
|
||||
data_marshaller: typing.Callable) -> (dict, typing.IO):
|
||||
hs, data = event.MarshalBinary()
|
||||
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)
|
|
@ -0,0 +1,48 @@
|
|||
# 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 typing
|
||||
|
||||
from cloudevents.sdk.converters import base
|
||||
from cloudevents.sdk.event import base as event_base
|
||||
|
||||
|
||||
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,
|
||||
body: typing.IO) -> event_base.BaseEvent:
|
||||
# we ignore headers, since the whole CE is in request body
|
||||
event = self.event
|
||||
event.UnmarshalJSON(body)
|
||||
return event
|
||||
|
||||
def write(self,
|
||||
event: event_base.BaseEvent,
|
||||
data_marshaller: typing.Callable) -> (dict, typing.IO):
|
||||
return {}, event.MarshalJSON()
|
||||
|
||||
|
||||
def NewJSONHTTPCloudEventConverter(
|
||||
event_class: event_base.BaseEvent) -> JSONHTTPCloudEventConverter:
|
||||
media_types = {
|
||||
"application/cloudevents+json": True,
|
||||
}
|
||||
|
||||
return JSONHTTPCloudEventConverter(event_class, media_types)
|
|
@ -0,0 +1,90 @@
|
|||
# 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 io
|
||||
import ujson
|
||||
import typing
|
||||
|
||||
|
||||
class BaseEvent(object):
|
||||
|
||||
def Properties(self) -> dict:
|
||||
props = dict()
|
||||
for name, value in self.__dict__.items():
|
||||
if str(name).startswith("ce__"):
|
||||
props.update(
|
||||
{
|
||||
str(name).replace("ce__", ""): value.get()
|
||||
}
|
||||
)
|
||||
|
||||
return props
|
||||
|
||||
def Extensions(self) -> dict:
|
||||
props = self.Properties()
|
||||
return props.get("extensions")
|
||||
|
||||
def Get(self, key: str) -> (object, bool):
|
||||
formatted_key = "ce__{0}".format(key.lower())
|
||||
ok = hasattr(self, formatted_key)
|
||||
value = getattr(self, formatted_key, None)
|
||||
if not ok:
|
||||
exts = self.Extensions()
|
||||
return exts.get(key), key in exts
|
||||
|
||||
return value.get(), ok
|
||||
|
||||
def Set(self, key: str, value: object):
|
||||
formatted_key = "ce__{0}".format(key)
|
||||
key_exists = hasattr(self, formatted_key)
|
||||
if key_exists:
|
||||
attr = getattr(self, formatted_key)
|
||||
attr.set(value)
|
||||
setattr(self, formatted_key, attr)
|
||||
return
|
||||
|
||||
exts = self.Extensions()
|
||||
exts.update({key: value})
|
||||
self.Set("extensions", exts)
|
||||
|
||||
def MarshalJSON(self) -> typing.IO:
|
||||
return io.StringIO(ujson.dumps(self.Properties()))
|
||||
|
||||
def UnmarshalJSON(self, b: typing.IO):
|
||||
raw_ce = ujson.load(b)
|
||||
for name, value in raw_ce.items():
|
||||
self.Set(name, value)
|
||||
|
||||
def UnmarshalBinary(self, headers: dict, body: typing.IO):
|
||||
props = self.Properties()
|
||||
for key in props:
|
||||
self.Set(key, headers.get("ce-{0}".format(key)))
|
||||
|
||||
data = None
|
||||
if body:
|
||||
data = body.read()
|
||||
|
||||
self.Set("data", data)
|
||||
|
||||
def MarshalBinary(self) -> (dict, object):
|
||||
headers = {}
|
||||
props = self.Properties()
|
||||
for key, value in props.items():
|
||||
if key not in ["data", "extensions"]:
|
||||
headers["ce-{0}".format(key)] = value
|
||||
|
||||
exts = props.get("extensions")
|
||||
headers.update(**exts)
|
||||
data, _ = self.Get("data")
|
||||
return headers, data
|
|
@ -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.
|
||||
|
||||
|
||||
class Option(object):
|
||||
|
||||
def __init__(self, name, value, is_required):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.is_required = is_required
|
||||
|
||||
def set(self, new_value):
|
||||
is_none = new_value is None
|
||||
if self.is_required and is_none:
|
||||
raise ValueError(
|
||||
"Attribute value error: '{0}', "
|
||||
"invalid new value.".format(self.name))
|
||||
|
||||
self.value = new_value
|
||||
|
||||
def get(self):
|
||||
return self.value
|
||||
|
||||
def required(self):
|
||||
return self.is_required
|
|
@ -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.
|
||||
|
||||
from cloudevents.sdk.event import opt
|
||||
from cloudevents.sdk.event import base
|
||||
|
||||
|
||||
class Event(base.BaseEvent):
|
||||
|
||||
def __init__(self):
|
||||
self.ce__specversion = opt.Option("specversion", "0.1", 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)
|
||||
self.ce__time = opt.Option("time", None, True)
|
||||
self.ce__schemaurl = opt.Option("schemaurl", None, False)
|
||||
self.ce__contenttype = opt.Option("contenttype", None, False)
|
||||
self.ce__data = opt.Option("data", None, False)
|
||||
self.ce__extensions = opt.Option("extensions", dict(), False)
|
||||
|
||||
def CloudEventVersion(self) -> str:
|
||||
return self.ce__specversion.get()
|
|
@ -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.
|
||||
|
||||
from cloudevents.sdk.event import opt
|
||||
from cloudevents.sdk.event import base
|
||||
|
||||
|
||||
class Event(base.BaseEvent):
|
||||
|
||||
def __init__(self):
|
||||
self.ce__cloudEventsVersion = opt.Option(
|
||||
"cloudEventsVersion", "0.1", True)
|
||||
self.ce__eventType = opt.Option("eventType", None, True)
|
||||
self.ce__eventTypeVersion = opt.Option(
|
||||
"eventTypeVersion", None, False)
|
||||
self.ce__source = opt.Option("source", None, True)
|
||||
self.ce__eventID = opt.Option("eventID", None, True)
|
||||
self.ce__evenTime = opt.Option("eventTime", None, True)
|
||||
self.ce__schemaURL = opt.Option("schemaURL", None, False)
|
||||
self.ce__contentType = opt.Option("contentType", None, False)
|
||||
self.ce__data = opt.Option("data", None, False)
|
||||
self.ce__extensions = opt.Option("extensions", dict(), False)
|
||||
|
||||
def CloudEventVersion(self) -> str:
|
||||
return self.ce__cloudEventsVersion.get()
|
|
@ -0,0 +1,27 @@
|
|||
# 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.
|
||||
|
||||
|
||||
class InvalidMimeType(Exception):
|
||||
|
||||
def __init__(self, mime_type):
|
||||
super().__init__(
|
||||
"Invalid MIME type: {0}".format(mime_type))
|
||||
|
||||
|
||||
class UnsupportedEvent(Exception):
|
||||
|
||||
def __init__(self, event_class):
|
||||
super().__init__("Invalid CloudEvent class: "
|
||||
"'{0}'".format(event_class))
|
|
@ -0,0 +1,57 @@
|
|||
# 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 typing
|
||||
|
||||
from cloudevents.sdk import exceptions
|
||||
|
||||
from cloudevents.sdk.converters import base
|
||||
from cloudevents.sdk.converters import binary
|
||||
from cloudevents.sdk.converters import structured
|
||||
|
||||
from cloudevents.sdk.event import base as event_base
|
||||
|
||||
|
||||
class HTTPMarshaller(object):
|
||||
|
||||
def __init__(self, converters: typing.List[base.Converter]):
|
||||
self.__converters = converters
|
||||
|
||||
def FromRequest(self, headers: dict, body: typing.IO):
|
||||
mimeType = headers.get("Content-Type")
|
||||
for cnvrtr in self.__converters:
|
||||
if cnvrtr.can_read(mimeType):
|
||||
return cnvrtr.read(headers, body)
|
||||
|
||||
raise exceptions.InvalidMimeType(mimeType)
|
||||
|
||||
def ToRequest(self, event: event_base.BaseEvent,
|
||||
converter_type: str,
|
||||
data_marshaller: typing.Callable) -> (dict, typing.IO):
|
||||
for cnvrtv in self.__converters:
|
||||
if converter_type == cnvrtv.TYPE:
|
||||
return cnvrtv.write(event, data_marshaller)
|
||||
|
||||
|
||||
def NewDefaultHTTPMarshaller(
|
||||
event_class: event_base.BaseEvent) -> HTTPMarshaller:
|
||||
return HTTPMarshaller([
|
||||
structured.NewJSONHTTPCloudEventConverter(event_class),
|
||||
binary.NewBinaryHTTPCloudEventConverter(event_class),
|
||||
])
|
||||
|
||||
|
||||
def NewHTTPMarshaller(
|
||||
converters: typing.List[base.Converter]) -> HTTPMarshaller:
|
||||
return HTTPMarshaller(converters)
|
|
@ -0,0 +1,102 @@
|
|||
# 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 io
|
||||
import ujson
|
||||
|
||||
from cloudevents.sdk import exceptions
|
||||
from cloudevents.sdk import marshaller
|
||||
|
||||
from cloudevents.sdk.event import v01
|
||||
from cloudevents.sdk.event import upstream
|
||||
|
||||
from cloudevents.sdk.converters import binary
|
||||
from cloudevents.sdk.converters import structured
|
||||
|
||||
|
||||
def test_binary_converter_upstream():
|
||||
headers = {
|
||||
"ce-specversion": "0.1",
|
||||
"ce-type": "word.found.exclamation",
|
||||
"ce-id": "16fb5f0b-211e-1102-3dfe-ea6e2806f124",
|
||||
"ce-time": "2018-10-23T12:28:23.3464579Z",
|
||||
"ce-source": "pytest",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
m = marshaller.NewHTTPMarshaller(
|
||||
[
|
||||
binary.NewBinaryHTTPCloudEventConverter(upstream.Event)
|
||||
]
|
||||
)
|
||||
event = m.FromRequest(headers, None)
|
||||
assert event is not None
|
||||
assert event.Get("type") == (headers.get("ce-type"), True)
|
||||
assert event.Get("id") == (headers.get("ce-id"), True)
|
||||
|
||||
|
||||
def test_structured_converter_upstream():
|
||||
ce = {
|
||||
"specversion": "0.1",
|
||||
"type": "word.found.exclamation",
|
||||
"id": "16fb5f0b-211e-1102-3dfe-ea6e2806f124",
|
||||
"time": "2018-10-23T12:28:23.3464579Z",
|
||||
"source": "pytest",
|
||||
"contenttype": "application/json"
|
||||
}
|
||||
m = marshaller.NewHTTPMarshaller(
|
||||
[
|
||||
structured.NewJSONHTTPCloudEventConverter(upstream.Event)
|
||||
]
|
||||
)
|
||||
event = m.FromRequest(
|
||||
{"Content-Type": "application/cloudevents+json"},
|
||||
io.StringIO(ujson.dumps(ce))
|
||||
)
|
||||
|
||||
assert event is not None
|
||||
assert event.Get("type") == (ce.get("type"), True)
|
||||
assert event.Get("id") == (ce.get("id"), True)
|
||||
|
||||
|
||||
# todo: clarify whether spec 0.1 doesn't support binary format
|
||||
def test_binary_converter_v01():
|
||||
pytest.raises(
|
||||
exceptions.UnsupportedEvent,
|
||||
binary.NewBinaryHTTPCloudEventConverter,
|
||||
v01.Event)
|
||||
|
||||
|
||||
def test_structured_converter_v01():
|
||||
ce = {
|
||||
"specversion": "0.1",
|
||||
"type": "word.found.exclamation",
|
||||
"id": "16fb5f0b-211e-1102-3dfe-ea6e2806f124",
|
||||
"time": "2018-10-23T12:28:23.3464579Z",
|
||||
"source": "pytest",
|
||||
"contenttype": "application/json"
|
||||
}
|
||||
m = marshaller.NewHTTPMarshaller(
|
||||
[
|
||||
structured.NewJSONHTTPCloudEventConverter(v01.Event)
|
||||
]
|
||||
)
|
||||
event = m.FromRequest(
|
||||
{"Content-Type": "application/cloudevents+json"},
|
||||
io.StringIO(ujson.dumps(ce))
|
||||
)
|
||||
|
||||
assert event is not None
|
||||
assert event.Get("type") == (ce.get("type"), True)
|
||||
assert event.Get("id") == (ce.get("id"), True)
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
|
||||
git checkout -b v${CLOUDEVENTS_SDK_VERSION}-stable
|
||||
git push origin v${CLOUDEVENTS_SDK_VERSION}-stable
|
||||
PBR_VERSION=${CLOUDEVENTS_SDK_VERSION} python setup.py sdist bdist_wheel
|
||||
twine upload dist/cloudevents-${CLOUDEVENTS_SDK_VERSIONN}*
|
||||
git checkout master
|
|
@ -0,0 +1,69 @@
|
|||
Release process
|
||||
===============
|
||||
|
||||
Run tests on target brunch
|
||||
--------------------------
|
||||
|
||||
Steps:
|
||||
|
||||
tox
|
||||
|
||||
Cut off stable branch
|
||||
---------------------
|
||||
|
||||
Steps:
|
||||
|
||||
git checkout -b vX.X.X-stable
|
||||
git push origin vX.X.X-stable
|
||||
|
||||
|
||||
Create GitHub tag
|
||||
-----------------
|
||||
|
||||
Steps:
|
||||
|
||||
Releases ---> Draft New Release
|
||||
Name: CloudEvents Python SDK version X.X.X stable release
|
||||
|
||||
|
||||
Collect changes from previous version
|
||||
-------------------------------------
|
||||
|
||||
Steps:
|
||||
|
||||
git log --oneline --decorate
|
||||
|
||||
|
||||
Build distribution package
|
||||
--------------------------
|
||||
|
||||
Steps:
|
||||
|
||||
PBR_VERSION=X.X.X python setup.py sdist bdist_wheel
|
||||
|
||||
|
||||
Check install capability for the wheel
|
||||
--------------------------------------
|
||||
|
||||
Steps:
|
||||
|
||||
python3.7 -m venv .test_venv
|
||||
source .test_venv/bin/activate
|
||||
pip install dist/cloudevents-X.X.X-py3-none-any.whl
|
||||
|
||||
|
||||
Submit release to PYPI
|
||||
----------------------
|
||||
|
||||
Steps:
|
||||
|
||||
twine upload dist/cloudevents-X.X.X-py3-none-any.whl
|
||||
|
||||
Verify install capability for the wheel
|
||||
---------------------------------------
|
||||
|
||||
Steps:
|
||||
|
||||
python3.7 -m venv .test_venv
|
||||
source .new_venv/bin/activate
|
||||
pip install cloudevents --upgrade
|
|
@ -0,0 +1,2 @@
|
|||
pbr!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
ujson
|
|
@ -0,0 +1,24 @@
|
|||
[metadata]
|
||||
name = cloudevents
|
||||
summary = CloudEvents SDK Python
|
||||
description-file =
|
||||
README.md
|
||||
author = Denis Makogon
|
||||
author-email = denys.makogon@oracle.com
|
||||
home-page = https://cloudevents.io/
|
||||
classifier =
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
|
||||
[files]
|
||||
packages =
|
||||
cloudevents
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
|
@ -0,0 +1,20 @@
|
|||
# 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 setuptools
|
||||
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr>=2.0.0'],
|
||||
pbr=True)
|
|
@ -0,0 +1,4 @@
|
|||
flake8==2.5.0
|
||||
hacking<0.11,>=0.10.0
|
||||
pytest==4.0.0
|
||||
pytest-cov==2.4.0
|
|
@ -0,0 +1,35 @@
|
|||
[tox]
|
||||
envlist = py{3.6,3.7},pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
basepython =
|
||||
pep8: python3
|
||||
py3.6: python3.6
|
||||
py3.7: python3.7
|
||||
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = find . -type f -name "*.pyc" -delete
|
||||
whitelist_externals = find
|
||||
rm
|
||||
go
|
||||
docker
|
||||
[testenv:pep8]
|
||||
commands = flake8
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:py3.6]
|
||||
commands = pytest -v -s --tb=long --cov=cloudevents {toxinidir}/cloudevents/tests
|
||||
|
||||
[testenv:py3.7]
|
||||
commands = pytest -v -s --tb=long --cov=cloudevents {toxinidir}/cloudevents/tests
|
||||
|
||||
[flake8]
|
||||
ignore = H405,H404,H403,H401,H306
|
||||
show-source = True
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,docs,venv,.venv
|
Loading…
Reference in New Issue