cloud_events 0.4.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +11 -9
- data/lib/cloud_events.rb +1 -0
- data/lib/cloud_events/content_type.rb +1 -1
- data/lib/cloud_events/errors.rb +37 -5
- data/lib/cloud_events/event.rb +1 -1
- data/lib/cloud_events/event/opaque.rb +80 -0
- data/lib/cloud_events/format.rb +202 -0
- data/lib/cloud_events/http_binding.rb +295 -155
- data/lib/cloud_events/json_format.rb +162 -51
- data/lib/cloud_events/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d44f430fb349c68608ecb791c8e0727c7c6cef018022a4f28c6dcb140b201d48
|
4
|
+
data.tar.gz: d4f1755b09f71fe1817d227666762c642de19172062a7fbdc29ec2a837a7b31e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0f36a2c0cf43efafbda5b94a7392e5c52ff7974df002d0051013a4db1bd809d62721499c2f0c5f1712907f72dbbea6e2280532d1ac275a0aa33e96e4094eb19
|
7
|
+
data.tar.gz: c18abd09d34d4e7f51ac2f327d10ae99ab7541ee2f13afa2df46c67424e40bc2e99521f64341436858c686261cdab315945173940cd467fa55bc49ead4590096
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v0.5.1 / 2021-06-28
|
4
|
+
|
5
|
+
* ADDED: Add HttpBinding#probable_event?
|
6
|
+
* FIXED: Fixed a NoMethodError when a format declined to decode an http request
|
7
|
+
|
8
|
+
### v0.5.0 / 2021-06-28
|
9
|
+
|
10
|
+
This is a significant update that provides several important spec-related and usability fixes. Some of the behavioral changes are breaking, so to preserve compatibility, new methods were added and old methods deprecated, particularly in the HttpBinding class. Additionally, the formatter interface has been simplified and expanded to support payload formatting.
|
11
|
+
|
12
|
+
* CHANGED: Deprecated HttpBinding#decode_rack_env and replaced with HttpBinding#decode_event. (The old method remains for backward compatibility, but will be removed in version 1.0). The new decode_event method has the following differences:
|
13
|
+
* decode_event raises NotCloudEventError (rather than returning nil) if given an HTTP request that does not seem to have been intended as a CloudEvent.
|
14
|
+
* decode_event takes an allow_opaque argument that, when set to true, returns a CloudEvents::Event::Opaque (rather than raising an exception) if given a structured event with a format not known by the SDK. Opaque event objects cannot have their fields inspected, but can be reserialized for retransmission to another event handler.
|
15
|
+
* decode_event in binary content mode now parses JSON content-types and exposes the data attribute as a JSON value.
|
16
|
+
* CHANGED: Deprecated the HttpBinding encoding entrypoints (encode_structured_content, encode_batched_content, and encode_binary_content) and replaced with a single encode_event entrypoint that handles all cases via the structured_format argument. (The old methods remain for backward compatibility, but will be removed in version 1.0). In addition, the new encode_event method has the following differences:
|
17
|
+
* encode_event in binary content mode now interprets a string-valued data attribute as a JSON string and serializes it (i.e. wraps it in quotes) if the data_content_type has a JSON format. This is for compatibility with the behavior of the JSON structured mode which always treats the data attribute as a JSON value if the data_content_type indicates JSON.
|
18
|
+
* CHANGED: The JsonFormat class interface was reworked to be more generic, combine the structured and batched calls, and add calls to handle data payloads. A Format module has been added to specify the interface used by JsonFormat and future formatters. (Breaking change of internal interfaces)
|
19
|
+
* CHANGED: Renamed HttpContentError to UnsupportedFormatError to better reflect the specific issue being reported, and to eliminate the coupling to http. The old name remains aliased to the new name, but is deprecated and will be removed in version 1.0.
|
20
|
+
* CHANGED: If format-driven parsing (e.g. parsing a JSON document) fails, a FormatSyntaxError will be raised instead of, for example, a JSON-specific error. The lower-level parser error can still be accessed from the exception's "cause".
|
21
|
+
* FIXED: JsonFormat now sets the datacontenttype attribute explicitly to "application/json" if it isn't otherwise set.
|
22
|
+
|
3
23
|
### v0.4.0 / 2021-05-26
|
4
24
|
|
5
25
|
* ADDED: ContentType can take an optional default charset
|
data/README.md
CHANGED
@@ -13,7 +13,7 @@ Features:
|
|
13
13
|
JSON Batch Format.
|
14
14
|
* Support for sending and receiving CloudEvents via HTTP Bindings.
|
15
15
|
* Supports the [CloudEvent 0.3](https://github.com/cloudevents/spec/tree/v0.3)
|
16
|
-
and [CloudEvents 1.0](https://github.com/cloudevents/spec/tree/v1.0)
|
16
|
+
and [CloudEvents 1.0](https://github.com/cloudevents/spec/tree/v1.0.1)
|
17
17
|
specifications.
|
18
18
|
* Extensible to additional formats and protocol bindings, and future
|
19
19
|
specification versions.
|
@@ -35,7 +35,7 @@ A simple [Sinatra](https://sinatrarb.com) app that receives CloudEvents:
|
|
35
35
|
```ruby
|
36
36
|
# examples/server/Gemfile
|
37
37
|
source "https://rubygems.org"
|
38
|
-
gem "cloud_events", "~> 0.
|
38
|
+
gem "cloud_events", "~> 0.5"
|
39
39
|
gem "sinatra", "~> 2.0"
|
40
40
|
```
|
41
41
|
|
@@ -59,7 +59,7 @@ A simple Ruby script that sends a CloudEvent:
|
|
59
59
|
```ruby
|
60
60
|
# examples/client/Gemfile
|
61
61
|
source "https://rubygems.org"
|
62
|
-
gem "cloud_events", "~> 0.
|
62
|
+
gem "cloud_events", "~> 0.5"
|
63
63
|
```
|
64
64
|
|
65
65
|
```ruby
|
@@ -69,14 +69,16 @@ require "net/http"
|
|
69
69
|
require "uri"
|
70
70
|
|
71
71
|
data = { message: "Hello, CloudEvents!" }
|
72
|
-
event = CloudEvents::Event.create
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
event = CloudEvents::Event.create \
|
73
|
+
spec_version: "1.0",
|
74
|
+
id: "1234-1234-1234",
|
75
|
+
source: "/mycontext",
|
76
|
+
type: "com.example.someevent",
|
77
|
+
data_content_type: "application/json",
|
78
|
+
data: data
|
77
79
|
|
78
80
|
cloud_events_http = CloudEvents::HttpBinding.default
|
79
|
-
headers, body = cloud_events_http.
|
81
|
+
headers, body = cloud_events_http.encode_event event
|
80
82
|
Net::HTTP.post URI("http://localhost:4567"), body, headers
|
81
83
|
```
|
82
84
|
|
data/lib/cloud_events.rb
CHANGED
data/lib/cloud_events/errors.rb
CHANGED
@@ -8,21 +8,53 @@ module CloudEvents
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
#
|
12
|
-
#
|
11
|
+
# An error raised when a protocol binding was asked to decode a CloudEvent
|
12
|
+
# from input data, but does not believe that the data was intended to be a
|
13
|
+
# CloudEvent. For example, the HttpBinding might raise this exception if
|
14
|
+
# given a request that has neither the requisite headers for binary content
|
15
|
+
# mode, nor an appropriate content-type for structured content mode.
|
13
16
|
#
|
14
|
-
class
|
17
|
+
class NotCloudEventError < CloudEventsError
|
15
18
|
end
|
16
19
|
|
17
20
|
##
|
18
|
-
#
|
21
|
+
# An error raised when a protocol binding was asked to decode a CloudEvent
|
22
|
+
# from input data, and the data appears to be a CloudEvent, but was encoded
|
23
|
+
# in a format that is not supported. Some protocol bindings can be configured
|
24
|
+
# to return a {CloudEvents::Event::Opaque} object instead of raising this
|
25
|
+
# error.
|
26
|
+
#
|
27
|
+
class UnsupportedFormatError < CloudEventsError
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# An error raised when a protocol binding was asked to decode a CloudEvent
|
32
|
+
# from input data, and the data appears to be intended as a CloudEvent, but
|
33
|
+
# has unrecoverable format or syntax errors. This error _may_ have a `cause`
|
34
|
+
# such as a `JSON::ParserError` with additional information.
|
35
|
+
#
|
36
|
+
class FormatSyntaxError < CloudEventsError
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# An error raised when a `specversion` is set to a value not recognized or
|
41
|
+
# supported by the SDK.
|
19
42
|
#
|
20
43
|
class SpecVersionError < CloudEventsError
|
21
44
|
end
|
22
45
|
|
23
46
|
##
|
24
|
-
#
|
47
|
+
# An error raised when a malformed CloudEvents attribute is encountered,
|
48
|
+
# often because a required attribute is missing, or a value does not match
|
49
|
+
# the attribute's type specification.
|
25
50
|
#
|
26
51
|
class AttributeError < CloudEventsError
|
27
52
|
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Alias of UnsupportedFormatError, for backward compatibility.
|
56
|
+
#
|
57
|
+
# @deprecated Will be removed in version 1.0. Use {UnsupportedFormatError}.
|
58
|
+
#
|
59
|
+
HttpContentError = UnsupportedFormatError
|
28
60
|
end
|
data/lib/cloud_events/event.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CloudEvents
|
4
|
+
module Event
|
5
|
+
##
|
6
|
+
# This object represents opaque event data that arrived in structured
|
7
|
+
# content mode but was not in a recognized format. It may represent a
|
8
|
+
# single event or a batch of events.
|
9
|
+
#
|
10
|
+
# The event data is retained in a form that can be reserialized (in a
|
11
|
+
# structured cotent mode in the same format) but cannot otherwise be
|
12
|
+
# inspected.
|
13
|
+
#
|
14
|
+
# This object is immutable, and Ractor-shareable on Ruby 3.
|
15
|
+
#
|
16
|
+
class Opaque
|
17
|
+
##
|
18
|
+
# Create an opaque object wrapping the given content and a content type.
|
19
|
+
#
|
20
|
+
# @param content [String] The opaque serialized event data.
|
21
|
+
# @param content_type [CloudEvents::ContentType,nil] The content type,
|
22
|
+
# or `nil` if there is no content type.
|
23
|
+
# @param batch [boolean] Whether this represents a batch. If set to `nil`
|
24
|
+
# or not provided, the value will be inferred from the content type
|
25
|
+
# if possible, or otherwise set to `nil` indicating not known.
|
26
|
+
#
|
27
|
+
def initialize content, content_type, batch: nil
|
28
|
+
@content = content.freeze
|
29
|
+
@content_type = content_type
|
30
|
+
if batch.nil? && content_type&.media_type == "application"
|
31
|
+
case content_type.subtype_base
|
32
|
+
when "cloudevents"
|
33
|
+
batch = false
|
34
|
+
when "cloudevents-batch"
|
35
|
+
batch = true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
@batch = batch
|
39
|
+
freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# The opaque serialized event data
|
44
|
+
#
|
45
|
+
# @return [String]
|
46
|
+
#
|
47
|
+
attr_reader :content
|
48
|
+
|
49
|
+
##
|
50
|
+
# The content type, or `nil` if there is no content type.
|
51
|
+
#
|
52
|
+
# @return [CloudEvents::ContentType,nil]
|
53
|
+
#
|
54
|
+
attr_reader :content_type
|
55
|
+
|
56
|
+
##
|
57
|
+
# Whether this represents a batch, or `nil` if not known.
|
58
|
+
#
|
59
|
+
# @return [boolean,nil]
|
60
|
+
#
|
61
|
+
def batch?
|
62
|
+
@batch
|
63
|
+
end
|
64
|
+
|
65
|
+
## @private
|
66
|
+
def == other
|
67
|
+
Opaque === other &&
|
68
|
+
@content == other.content &&
|
69
|
+
@content_type == other.content_type &&
|
70
|
+
@batch == other.batch?
|
71
|
+
end
|
72
|
+
alias eql? ==
|
73
|
+
|
74
|
+
## @private
|
75
|
+
def hash
|
76
|
+
@content.hash ^ @content_type.hash ^ @batch.hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module CloudEvents
|
7
|
+
##
|
8
|
+
# This module documents the method signatures that may be implemented by
|
9
|
+
# formatters.
|
10
|
+
#
|
11
|
+
# A formatter is an object that implenets "structured" event encoding and
|
12
|
+
# decoding strategies for a particular format (such as JSON). In general,
|
13
|
+
# this includes four operations:
|
14
|
+
#
|
15
|
+
# * Decoding an entire event or batch of events from a input source.
|
16
|
+
# This is implemented by the {Format#decode_event} method.
|
17
|
+
# * Encoding an entire event or batch of events to an output sink.
|
18
|
+
# This is implemented by the {Format#encode_event} method.
|
19
|
+
# * Decoding an event payload (i.e. the `data` attribute) Ruby object from a
|
20
|
+
# serialized representation.
|
21
|
+
# This is implemented by the {Format#decode_data} method.
|
22
|
+
# * Encoding an event payload (i.e. the `data` attribute) Ruby object to a
|
23
|
+
# serialized representation.
|
24
|
+
# This is implemented by the {Format#encode_data} method.
|
25
|
+
#
|
26
|
+
# Each method takes a set of keyword arguments, and returns either a `Hash`
|
27
|
+
# or `nil`. A Hash indicates that the formatter understands the request and
|
28
|
+
# is returning its response. A return value of `nil` means the formatter does
|
29
|
+
# not understand the request and is declining to perform the operation. In
|
30
|
+
# such a case, it is possible that a different formatter should handle it.
|
31
|
+
#
|
32
|
+
# Both the keyword arguments recognized and the returned hash members may
|
33
|
+
# vary from formatter to formatter; similarly, the keyword arguments provided
|
34
|
+
# and the resturned hash members recognized may also vary for different
|
35
|
+
# callers. This interface will define a set of common argument and result key
|
36
|
+
# names, but both callers and formatters must gracefully handle the case of
|
37
|
+
# missing or extra information. For example, if a formatter expects a certain
|
38
|
+
# argument but does not receive it, it can assume the caller does not have
|
39
|
+
# the required information, and it may respond by returning `nil` to decline
|
40
|
+
# the request. Similarly, if a caller expects a response key but does not
|
41
|
+
# receive it, it can assume the formatter does not provide it, and it may
|
42
|
+
# respond by trying a different formatter.
|
43
|
+
#
|
44
|
+
# Additionally, any particular formatter need not implement all methods. For
|
45
|
+
# example, an event formatter would generally implement {Format#decode_event}
|
46
|
+
# and {Format#encode_event}, but might not implement {Format#decode_data} or
|
47
|
+
# {Format#encode_data}.
|
48
|
+
#
|
49
|
+
# Finally, this module itself is present primarily for documentation, and
|
50
|
+
# need not be directly included by formatter implementations.
|
51
|
+
#
|
52
|
+
module Format
|
53
|
+
##
|
54
|
+
# Decode an event or batch from the given serialized input. This is
|
55
|
+
# typically called by a protocol binding to deserialize event data from an
|
56
|
+
# input stream.
|
57
|
+
#
|
58
|
+
# Common arguments include:
|
59
|
+
#
|
60
|
+
# * `:content` (String) Serialized content to decode. For example, it could
|
61
|
+
# be from an HTTP request body.
|
62
|
+
# * `:content_type` ({CloudEvents::ContentType}) The content type. For
|
63
|
+
# example, it could be from the `Content-Type` header of an HTTP request.
|
64
|
+
#
|
65
|
+
# The formatter must first determine whether it is able to interpret the
|
66
|
+
# given input. Typically, this is done by inspecting the `content_type`.
|
67
|
+
# If the formatter determines that it is unable to interpret the input, it
|
68
|
+
# should return `nil`. Otherwise, if the formatter determines it can decode
|
69
|
+
# the input, it should return a `Hash`. Common hash keys include:
|
70
|
+
#
|
71
|
+
# * `:event` ({CloudEvents::Event}) A single event decoded from the input.
|
72
|
+
# * `:event_batch` (Array of {CloudEvents::Event}) A batch of events
|
73
|
+
# decoded from the input.
|
74
|
+
#
|
75
|
+
# The formatter may also raise a {CloudEvents::CloudEventsError} subclass
|
76
|
+
# if it understood the request but determines that the input source is
|
77
|
+
# malformed.
|
78
|
+
#
|
79
|
+
# @param _kwargs [keywords] Arguments
|
80
|
+
# @return [Hash] if accepting the request and returning a result
|
81
|
+
# @return [nil] if declining the request.
|
82
|
+
#
|
83
|
+
def decode_event **_kwargs
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Encode an event or batch to a string. This is typically called by a
|
89
|
+
# protocol binding to serialize event data to an output stream.
|
90
|
+
#
|
91
|
+
# Common arguments include:
|
92
|
+
#
|
93
|
+
# * `:event` ({CloudEvents::Event}) A single event to encode.
|
94
|
+
# * `:event_batch` (Array of {CloudEvents::Event}) A batch of events to
|
95
|
+
# encode.
|
96
|
+
#
|
97
|
+
# The formatter must first determine whether it is able to interpret the
|
98
|
+
# given input. Typically, most formatters should be able to handle any
|
99
|
+
# event or event batch, but a specialized formatter that can handle only
|
100
|
+
# certain kinds of events may return `nil` to decline unwanted inputs.
|
101
|
+
# Otherwise, if the formatter determines it can encode the input, it should
|
102
|
+
# return a `Hash`. common hash keys include:
|
103
|
+
#
|
104
|
+
# * `:content` (String) The serialized form of the event. This might, for
|
105
|
+
# example, be written to an HTTP request body. Care should be taken to
|
106
|
+
# set the string's encoding properly. In particular, to output binary
|
107
|
+
# data, the encoding should probably be set to `ASCII_8BIT`.
|
108
|
+
# * `:content_type` ({CloudEvents::ContentType}) The content type for the
|
109
|
+
# output. This might, for example, be written to the `Content-Type`
|
110
|
+
# header of an HTTP request.
|
111
|
+
#
|
112
|
+
# The formatter may also raise a {CloudEvents::CloudEventsError} subclass
|
113
|
+
# if it understood the request but determines that the input source is
|
114
|
+
# malformed.
|
115
|
+
#
|
116
|
+
# @param _kwargs [keywords] Arguments
|
117
|
+
# @return [Hash] if accepting the request and returning a result
|
118
|
+
# @return [nil] if declining the request.
|
119
|
+
#
|
120
|
+
def encode_event **_kwargs
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Decode an event data object from string format. This is typically called
|
126
|
+
# by a protocol binding to deserialize the payload (i.e. `data` attribute)
|
127
|
+
# of an event as part of "binary content mode" decoding.
|
128
|
+
#
|
129
|
+
# Common arguments include:
|
130
|
+
#
|
131
|
+
# * `:spec_version` (String) The `specversion` of the event.
|
132
|
+
# * `:content` (String) Serialized payload to decode. For example, it could
|
133
|
+
# be from an HTTP request body.
|
134
|
+
# * `:content_type` ({CloudEvents::ContentType}) The content type. For
|
135
|
+
# example, it could be from the `Content-Type` header of an HTTP request.
|
136
|
+
#
|
137
|
+
# The formatter must first determine whether it is able to interpret the
|
138
|
+
# given input. Typically, this is done by inspecting the `content_type`.
|
139
|
+
# If the formatter determines that it is unable to interpret the input, it
|
140
|
+
# should return `nil`. Otherwise, if the formatter determines it can decode
|
141
|
+
# the input, it should return a `Hash`. Common hash keys include:
|
142
|
+
#
|
143
|
+
# * `:data` (Object) The payload object to be set as the `data` attribute
|
144
|
+
# in a {CloudEvents::Event} object.
|
145
|
+
# * `:content_type` ({CloudEvents::ContentType}) The content type to be set
|
146
|
+
# as the `datacontenttype` attribute in a {CloudEvents::Event} object.
|
147
|
+
# In many cases, this may simply be copied from the `:content_type`
|
148
|
+
# argument, but a formatter could modify it to provide corrections or
|
149
|
+
# additional information.
|
150
|
+
#
|
151
|
+
# The formatter may also raise a {CloudEvents::CloudEventsError} subclass
|
152
|
+
# if it understood the request but determines that the input source is
|
153
|
+
# malformed.
|
154
|
+
#
|
155
|
+
# @param _kwargs [keywords] Arguments
|
156
|
+
# @return [Hash] if accepting the request and returning a result
|
157
|
+
# @return [nil] if declining the request.
|
158
|
+
#
|
159
|
+
def decode_data **_kwargs
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
|
163
|
+
##
|
164
|
+
# Encode an event data object to string format. This is typically called by
|
165
|
+
# a protocol binding to serialize the payload (i.e. `data` attribute and
|
166
|
+
# corresponding `datacontenttype` attribute) of an event as part of "binary
|
167
|
+
# content mode" encoding.
|
168
|
+
#
|
169
|
+
# Common arguments include:
|
170
|
+
#
|
171
|
+
# * `:spec_version` (String) The `specversion` of the event.
|
172
|
+
# * `:data` (Object) The payload object from an event's `data` attribute.
|
173
|
+
# * `:content_type` ({CloudEvents::ContentType}) The content type from an
|
174
|
+
# event's `datacontenttype` attribute.
|
175
|
+
#
|
176
|
+
# The formatter must first determine whether it is able to interpret the
|
177
|
+
# given input. Typically, this is done by inspecting the `content_type`.
|
178
|
+
# If the formatter determines that it is unable to interpret the input, it
|
179
|
+
# should return `nil`. Otherwise, if the formatter determines it can decode
|
180
|
+
# the input, it should return a `Hash`. Common hash keys include:
|
181
|
+
#
|
182
|
+
# * `:content` (String) The serialized form of the data. This might, for
|
183
|
+
# example, be written to an HTTP request body. Care should be taken to
|
184
|
+
# set the string's encoding properly. In particular, to output binary
|
185
|
+
# data, the encoding should probably be set to `ASCII_8BIT`.
|
186
|
+
# * `:content_type` ({CloudEvents::ContentType}) The content type for the
|
187
|
+
# output. This might, for example, be written to the `Content-Type`
|
188
|
+
# header of an HTTP request.
|
189
|
+
#
|
190
|
+
# The formatter may also raise a {CloudEvents::CloudEventsError} subclass
|
191
|
+
# if it understood the request but determines that the input source is
|
192
|
+
# malformed.
|
193
|
+
#
|
194
|
+
# @param _kwargs [keywords] Arguments
|
195
|
+
# @return [Hash] if accepting the request and returning a result
|
196
|
+
# @return [nil] if declining the request.
|
197
|
+
#
|
198
|
+
def encode_data **_kwargs
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|