cloud_events 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -15,14 +15,21 @@ module CloudEvents
|
|
15
15
|
#
|
16
16
|
class HttpBinding
|
17
17
|
##
|
18
|
-
#
|
18
|
+
# The name of the JSON decoder/encoder
|
19
|
+
# @return [String]
|
20
|
+
#
|
21
|
+
JSON_FORMAT = "json"
|
22
|
+
|
23
|
+
##
|
24
|
+
# Returns a default HTTP binding, including support for JSON format.
|
25
|
+
#
|
26
|
+
# @return [HttpBinding]
|
19
27
|
#
|
20
28
|
def self.default
|
21
29
|
@default ||= begin
|
22
30
|
http_binding = new
|
23
|
-
|
24
|
-
http_binding.
|
25
|
-
http_binding.register_batched_formatter "json", json_format
|
31
|
+
http_binding.register_formatter JsonFormat.new, JSON_FORMAT
|
32
|
+
http_binding.default_encoder_name = JSON_FORMAT
|
26
33
|
http_binding
|
27
34
|
end
|
28
35
|
end
|
@@ -31,230 +38,278 @@ module CloudEvents
|
|
31
38
|
# Create an empty HTTP binding.
|
32
39
|
#
|
33
40
|
def initialize
|
34
|
-
@
|
35
|
-
@
|
41
|
+
@event_decoders = []
|
42
|
+
@event_encoders = {}
|
43
|
+
@data_decoders = [DefaultDataFormat]
|
44
|
+
@data_encoders = [DefaultDataFormat]
|
45
|
+
@default_encoder_name = nil
|
36
46
|
end
|
37
47
|
|
38
48
|
##
|
39
|
-
# Register a formatter for
|
49
|
+
# Register a formatter for all operations it supports, based on which
|
50
|
+
# methods are implemented by the formatter object. See
|
51
|
+
# {CloudEvents::Format} for a list of possible methods.
|
40
52
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
# this formatter.
|
46
|
-
# @param formatter [Object] The formatter object.
|
53
|
+
# @param formatter [Object] The formatter
|
54
|
+
# @param name [String] The encoder name under which this formatter will
|
55
|
+
# register its encode operations. Optional. If not specified, any event
|
56
|
+
# encoder will _not_ be registered.
|
47
57
|
# @return [self]
|
48
58
|
#
|
49
|
-
def
|
50
|
-
|
51
|
-
|
59
|
+
def register_formatter formatter, name = nil
|
60
|
+
name = name.to_s.strip.downcase if name
|
61
|
+
decode_event = formatter.respond_to? :decode_event
|
62
|
+
encode_event = name if formatter.respond_to? :encode_event
|
63
|
+
decode_data = formatter.respond_to? :decode_data
|
64
|
+
encode_data = formatter.respond_to? :encode_data
|
65
|
+
register_formatter_methods formatter,
|
66
|
+
decode_event: decode_event,
|
67
|
+
encode_event: encode_event,
|
68
|
+
decode_data: decode_data,
|
69
|
+
encode_data: encode_data
|
52
70
|
self
|
53
71
|
end
|
54
72
|
|
55
73
|
##
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
# `#decode_batch`. See {CloudEvents::JsonFormat} for an example.
|
74
|
+
# Registers the given formatter for the given operations. Some arguments
|
75
|
+
# are activated by passing `true`, whereas those that rely on a format name
|
76
|
+
# are activated by passing in a name string.
|
60
77
|
#
|
61
|
-
# @param
|
62
|
-
#
|
63
|
-
#
|
78
|
+
# @param formatter [Object] The formatter
|
79
|
+
# @param decode_event [boolean] If true, register the formatter's
|
80
|
+
# {CloudEvents::Format#decode_event} method.
|
81
|
+
# @param encode_event [String] If set to a string, use the formatter's
|
82
|
+
# {CloudEvents::Format#encode_event} method when that name is requested.
|
83
|
+
# @param decode_data [boolean] If true, register the formatter's
|
84
|
+
# {CloudEvents::Format#decode_data} method.
|
85
|
+
# @param encode_data [boolean] If true, register the formatter's
|
86
|
+
# {CloudEvents::Format#encode_data} method.
|
64
87
|
# @return [self]
|
65
88
|
#
|
66
|
-
def
|
67
|
-
|
68
|
-
|
89
|
+
def register_formatter_methods formatter,
|
90
|
+
decode_event: false,
|
91
|
+
encode_event: nil,
|
92
|
+
decode_data: false,
|
93
|
+
encode_data: false
|
94
|
+
@event_decoders << formatter if decode_event
|
95
|
+
if encode_event
|
96
|
+
formatters = @event_encoders[encode_event] ||= []
|
97
|
+
formatters << formatter unless formatters.include? formatter
|
98
|
+
end
|
99
|
+
@data_decoders << formatter if decode_data
|
100
|
+
@data_encoders << formatter if encode_data
|
69
101
|
self
|
70
102
|
end
|
71
103
|
|
104
|
+
##
|
105
|
+
# The name of the encoder to use if none is specified
|
106
|
+
# @return [String,nil]
|
107
|
+
#
|
108
|
+
attr_accessor :default_encoder_name
|
109
|
+
|
110
|
+
##
|
111
|
+
# Analyze a Rack environment hash and determine whether it is _probably_
|
112
|
+
# a CloudEvent. This is done by examining headers only, and does not read
|
113
|
+
# or parse the request body. The result is a best guess: false negatives or
|
114
|
+
# false positives are possible for edge cases, but the logic should
|
115
|
+
# generally detect canonically-formatted events.
|
116
|
+
#
|
117
|
+
# @param env [Hash] The Rack environment.
|
118
|
+
# @return [boolean] Whether the request is likely a CloudEvent.
|
119
|
+
#
|
120
|
+
def probable_event? env
|
121
|
+
return true if env["HTTP_CE_SPECVERSION"]
|
122
|
+
content_type = ContentType.new env["CONTENT_TYPE"].to_s
|
123
|
+
content_type.media_type == "application" &&
|
124
|
+
["cloudevents", "cloudevents-batch"].include?(content_type.subtype_base)
|
125
|
+
end
|
126
|
+
|
72
127
|
##
|
73
128
|
# Decode an event from the given Rack environment hash. Following the
|
74
129
|
# CloudEvents spec, this chooses a handler based on the Content-Type of
|
75
130
|
# the request.
|
76
131
|
#
|
132
|
+
# Note that this method will read the body (i.e. `rack.input`) stream.
|
133
|
+
# If you need to access the body after calling this method, you will need
|
134
|
+
# to rewind the stream. To determine whether the request is a CloudEvent
|
135
|
+
# without reading the body, use {#probable_event?}.
|
136
|
+
#
|
77
137
|
# @param env [Hash] The Rack environment.
|
138
|
+
# @param allow_opaque [boolean] If true, returns opaque event objects if
|
139
|
+
# the input is not in a recognized format. If false, raises
|
140
|
+
# {CloudEvents::UnsupportedFormatError} in that case. Default is false.
|
78
141
|
# @param format_args [keywords] Extra args to pass to the formatter.
|
79
142
|
# @return [CloudEvents::Event] if the request includes a single structured
|
80
143
|
# or binary event.
|
81
144
|
# @return [Array<CloudEvents::Event>] if the request includes a batch of
|
82
145
|
# structured events.
|
83
|
-
# @
|
146
|
+
# @raise [CloudEvents::CloudEventsError] if an event could not be decoded
|
147
|
+
# from the request.
|
84
148
|
#
|
85
|
-
def
|
86
|
-
|
87
|
-
content_type = ContentType.new
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
when "cloudevents-batch"
|
95
|
-
content = read_with_charset input, content_type.charset
|
96
|
-
return decode_batched_content content, content_type.subtype_format, **format_args
|
97
|
-
end
|
149
|
+
def decode_event env, allow_opaque: false, **format_args
|
150
|
+
content_type_string = env["CONTENT_TYPE"]
|
151
|
+
content_type = ContentType.new content_type_string if content_type_string
|
152
|
+
content = read_with_charset env["rack.input"], content_type&.charset
|
153
|
+
result = decode_binary_content(content, content_type, env) ||
|
154
|
+
decode_structured_content(content, content_type, allow_opaque, **format_args)
|
155
|
+
if result.nil?
|
156
|
+
content_type_string = content_type_string ? content_type_string.inspect : "not present"
|
157
|
+
raise NotCloudEventError, "Content-Type is #{content_type_string}, and CE-SpecVersion is not present"
|
98
158
|
end
|
99
|
-
|
159
|
+
result
|
100
160
|
end
|
101
161
|
|
102
162
|
##
|
103
|
-
#
|
104
|
-
# passed the request body, if the Content-Type is of the form
|
105
|
-
# `application/cloudevents+format`.
|
163
|
+
# Encode an event or batch of events into HTTP headers and body.
|
106
164
|
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
# @param format_args [keywords] Extra args to pass to the formatter.
|
110
|
-
# @return [CloudEvents::Event]
|
165
|
+
# You may provide an event, an array of events, or an opaque event. You may
|
166
|
+
# also specify what content mode and format to use.
|
111
167
|
#
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
##
|
122
|
-
# Decode a batch of events from the given content data. This should be
|
123
|
-
# passed the request body, if the Content-Type is of the form
|
124
|
-
# `application/cloudevents-batch+format`.
|
168
|
+
# The result is a two-element array where the first element is a headers
|
169
|
+
# list (as defined in the Rack specification) and the second is a string
|
170
|
+
# containing the HTTP body content. When using structured content mode, the
|
171
|
+
# headers list will contain only a `Content-Type` header and the body will
|
172
|
+
# contain the serialized event. When using binary mode, the header list
|
173
|
+
# will contain the serialized event attributes and the body will contain
|
174
|
+
# the serialized event data.
|
125
175
|
#
|
126
|
-
# @param
|
127
|
-
#
|
176
|
+
# @param event [CloudEvents::Event,Array<CloudEvents::Event>,CloudEvents::Event::Opaque]
|
177
|
+
# The event, batch, or opaque event.
|
178
|
+
# @param structured_format [boolean,String] If given, the data will be
|
179
|
+
# encoded in structured content mode. You can pass a string to select
|
180
|
+
# a format name, or pass `true` to use the default format. If set to
|
181
|
+
# `false` (the default), the data will be encoded in binary mode.
|
128
182
|
# @param format_args [keywords] Extra args to pass to the formatter.
|
129
|
-
# @return [Array
|
183
|
+
# @return [Array(headers,String)]
|
130
184
|
#
|
131
|
-
def
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
185
|
+
def encode_event event, structured_format: false, **format_args
|
186
|
+
if event.is_a? Event::Opaque
|
187
|
+
[{ "Content-Type" => event.content_type.to_s }, event.content]
|
188
|
+
elsif !structured_format
|
189
|
+
if event.is_a? ::Array
|
190
|
+
raise ::ArgumentError, "Encoding a batch requires structured_format"
|
191
|
+
end
|
192
|
+
encode_binary_content event, legacy_data_encode: false, **format_args
|
193
|
+
else
|
194
|
+
structured_format = default_encoder_name if structured_format == true
|
195
|
+
raise ::ArgumentError, "Format name not specified, and no default is set" unless structured_format
|
196
|
+
case event
|
197
|
+
when ::Array
|
198
|
+
encode_batched_content event, structured_format, **format_args
|
199
|
+
when Event
|
200
|
+
encode_structured_content event, structured_format, **format_args
|
201
|
+
else
|
202
|
+
raise ::ArgumentError, "Unknown event type: #{event.class}"
|
203
|
+
end
|
136
204
|
end
|
137
|
-
raise HttpContentError, "Unknown cloudevents batch format: #{format.inspect}"
|
138
205
|
end
|
139
206
|
|
140
207
|
##
|
141
|
-
# Decode an event from the given Rack environment
|
208
|
+
# Decode an event from the given Rack environment hash. Following the
|
209
|
+
# CloudEvents spec, this chooses a handler based on the Content-Type of
|
210
|
+
# the request.
|
142
211
|
#
|
143
|
-
# @
|
144
|
-
# @param content_type [CloudEvents::ContentType] the content type from the
|
145
|
-
# Rack environment.
|
146
|
-
# @return [CloudEvents::Event] if a CloudEvent could be decoded from the
|
147
|
-
# Rack environment.
|
148
|
-
# @return [nil] if the Rack environment does not indicate a CloudEvent
|
212
|
+
# @deprecated Will be removed in version 1.0. Use {#decode_event} instead.
|
149
213
|
#
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
214
|
+
# @param env [Hash] The Rack environment.
|
215
|
+
# @param format_args [keywords] Extra args to pass to the formatter.
|
216
|
+
# @return [CloudEvents::Event] if the request includes a single structured
|
217
|
+
# or binary event.
|
218
|
+
# @return [Array<CloudEvents::Event>] if the request includes a batch of
|
219
|
+
# structured events.
|
220
|
+
# @return [nil] if the request does not appear to be a CloudEvent.
|
221
|
+
# @raise [CloudEvents::CloudEventsError] if the request appears to be a
|
222
|
+
# CloudEvent but decoding failed.
|
223
|
+
#
|
224
|
+
def decode_rack_env env, **format_args
|
225
|
+
content_type_string = env["CONTENT_TYPE"]
|
226
|
+
content_type = ContentType.new content_type_string if content_type_string
|
227
|
+
content = read_with_charset env["rack.input"], content_type&.charset
|
228
|
+
env["rack.input"].rewind rescue nil
|
229
|
+
decode_binary_content(content, content_type, env, legacy_data_decode: true) ||
|
230
|
+
decode_structured_content(content, content_type, false, **format_args)
|
166
231
|
end
|
167
232
|
|
168
233
|
##
|
169
|
-
# Encode a single event
|
234
|
+
# Encode a single event in structured content mode in the given format.
|
170
235
|
#
|
171
|
-
#
|
172
|
-
# list (as defined in the Rack specification) and the second is a string
|
173
|
-
# containing the HTTP body content. The headers list will contain only
|
174
|
-
# one header, a `Content-Type` whose value is of the form
|
175
|
-
# `application/cloudevents+format`.
|
236
|
+
# @deprecated Will be removed in version 1.0. Use {#encode_event} instead.
|
176
237
|
#
|
177
238
|
# @param event [CloudEvents::Event] The event.
|
178
|
-
# @param
|
239
|
+
# @param format_name [String] The format name.
|
179
240
|
# @param format_args [keywords] Extra args to pass to the formatter.
|
180
241
|
# @return [Array(headers,String)]
|
181
242
|
#
|
182
|
-
def encode_structured_content event,
|
183
|
-
|
184
|
-
|
185
|
-
content
|
186
|
-
|
187
|
-
content_type = "application/cloudevents+#{format}; charset=#{charset_of content}"
|
188
|
-
return [{ "Content-Type" => content_type }, content]
|
243
|
+
def encode_structured_content event, format_name, **format_args
|
244
|
+
Array(@event_encoders[format_name]).reverse_each do |handler|
|
245
|
+
result = handler.encode_event event: event, **format_args
|
246
|
+
if result&.key?(:content) && result&.key?(:content_type)
|
247
|
+
return [{ "Content-Type" => result[:content_type].to_s }, result[:content]]
|
189
248
|
end
|
190
249
|
end
|
191
|
-
raise
|
250
|
+
raise ::ArgumentError, "Unknown format name: #{format_name.inspect}"
|
192
251
|
end
|
193
252
|
|
194
253
|
##
|
195
|
-
# Encode a batch of events
|
254
|
+
# Encode a batch of events in structured content mode in the given format.
|
196
255
|
#
|
197
|
-
#
|
198
|
-
# list (as defined in the Rack specification) and the second is a string
|
199
|
-
# containing the HTTP body content. The headers list will contain only
|
200
|
-
# one header, a `Content-Type` whose value is of the form
|
201
|
-
# `application/cloudevents-batch+format`.
|
256
|
+
# @deprecated Will be removed in version 1.0. Use {#encode_event} instead.
|
202
257
|
#
|
203
|
-
# @param
|
204
|
-
# @param
|
258
|
+
# @param event_batch [Array<CloudEvents::Event>] The batch of events.
|
259
|
+
# @param format_name [String] The format name.
|
205
260
|
# @param format_args [keywords] Extra args to pass to the formatter.
|
206
261
|
# @return [Array(headers,String)]
|
207
262
|
#
|
208
|
-
def encode_batched_content
|
209
|
-
|
210
|
-
|
211
|
-
content
|
212
|
-
|
213
|
-
content_type = "application/cloudevents-batch+#{format}; charset=#{charset_of content}"
|
214
|
-
return [{ "Content-Type" => content_type }, content]
|
263
|
+
def encode_batched_content event_batch, format_name, **format_args
|
264
|
+
Array(@event_encoders[format_name]).reverse_each do |handler|
|
265
|
+
result = handler.encode_event event_batch: event_batch, **format_args
|
266
|
+
if result&.key?(:content) && result&.key?(:content_type)
|
267
|
+
return [{ "Content-Type" => result[:content_type].to_s }, result[:content]]
|
215
268
|
end
|
216
269
|
end
|
217
|
-
raise
|
270
|
+
raise ::ArgumentError, "Unknown format name: #{format_name.inspect}"
|
218
271
|
end
|
219
272
|
|
220
273
|
##
|
221
|
-
# Encode an event
|
274
|
+
# Encode an event in binary content mode.
|
222
275
|
#
|
223
|
-
#
|
224
|
-
# list (as defined in the Rack specification) and the second is a string
|
225
|
-
# containing the HTTP body content.
|
276
|
+
# @deprecated Will be removed in version 1.0. Use {#encode_event} instead.
|
226
277
|
#
|
227
278
|
# @param event [CloudEvents::Event] The event.
|
279
|
+
# @param format_args [keywords] Extra args to pass to the formatter.
|
228
280
|
# @return [Array(headers,String)]
|
229
281
|
#
|
230
|
-
def encode_binary_content event
|
282
|
+
def encode_binary_content event, legacy_data_encode: true, **format_args
|
231
283
|
headers = {}
|
232
|
-
body = nil
|
233
284
|
event.to_h.each do |key, value|
|
234
|
-
|
235
|
-
when "data"
|
236
|
-
body = value
|
237
|
-
when "datacontenttype"
|
238
|
-
headers["Content-Type"] = value
|
239
|
-
else
|
285
|
+
unless ["data", "datacontenttype"].include? key
|
240
286
|
headers["CE-#{key}"] = percent_encode value
|
241
287
|
end
|
242
288
|
end
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
289
|
+
if legacy_data_encode
|
290
|
+
body = event.data
|
291
|
+
content_type = event.data_content_type&.to_s
|
292
|
+
case body
|
293
|
+
when ::String
|
294
|
+
content_type ||= string_content_type body
|
295
|
+
when nil
|
296
|
+
content_type = nil
|
297
|
+
else
|
298
|
+
body = ::JSON.dump body
|
299
|
+
content_type ||= "application/json; charset=#{body.encoding.name.downcase}"
|
300
|
+
end
|
248
301
|
else
|
249
|
-
body =
|
250
|
-
headers["Content-Type"] ||= "application/json; charset=#{body.encoding.name.downcase}"
|
302
|
+
body, content_type = encode_data event.spec_version, event.data, event.data_content_type, **format_args
|
251
303
|
end
|
304
|
+
headers["Content-Type"] = content_type.to_s if content_type
|
252
305
|
[headers, body]
|
253
306
|
end
|
254
307
|
|
255
308
|
##
|
256
309
|
# Decode a percent-encoded string to a UTF-8 string.
|
257
310
|
#
|
311
|
+
# @private
|
312
|
+
#
|
258
313
|
# @param str [String] Incoming ascii string from an HTTP header, with one
|
259
314
|
# cycle of percent-encoding.
|
260
315
|
# @return [String] Resulting decoded string in UTF-8.
|
@@ -270,6 +325,8 @@ module CloudEvents
|
|
270
325
|
# non-printing and non-ascii characters to result in an ASCII string
|
271
326
|
# suitable for setting as an HTTP header value.
|
272
327
|
#
|
328
|
+
# @private
|
329
|
+
#
|
273
330
|
# @param str [String] Incoming arbitrary string that can be represented
|
274
331
|
# in UTF-8.
|
275
332
|
# @return [String] Resulting encoded string in ASCII.
|
@@ -293,7 +350,87 @@ module CloudEvents
|
|
293
350
|
|
294
351
|
private
|
295
352
|
|
353
|
+
def add_named_formatter collection, formatter, name
|
354
|
+
return unless name
|
355
|
+
formatters = collection[name] ||= []
|
356
|
+
formatters << formatter unless formatters.include? formatter
|
357
|
+
end
|
358
|
+
|
359
|
+
##
|
360
|
+
# Decode a single event from the given request body and content type in
|
361
|
+
# structured mode.
|
362
|
+
#
|
363
|
+
def decode_structured_content content, content_type, allow_opaque, **format_args
|
364
|
+
@event_decoders.reverse_each do |decoder|
|
365
|
+
result = decoder.decode_event content: content, content_type: content_type, **format_args
|
366
|
+
event = result[:event] || result[:event_batch] if result
|
367
|
+
return event if event
|
368
|
+
end
|
369
|
+
if content_type&.media_type == "application" &&
|
370
|
+
["cloudevents", "cloudevents-batch"].include?(content_type.subtype_base)
|
371
|
+
return Event::Opaque.new content, content_type if allow_opaque
|
372
|
+
raise UnsupportedFormatError, "Unknown cloudevents content type: #{content_type}"
|
373
|
+
end
|
374
|
+
nil
|
375
|
+
end
|
376
|
+
|
377
|
+
##
|
378
|
+
# Decode an event from the given Rack environment in binary content mode.
|
379
|
+
#
|
380
|
+
# TODO: legacy_data_decode is deprecated and can be removed when
|
381
|
+
# decode_rack_env is removed.
|
382
|
+
#
|
383
|
+
def decode_binary_content content, content_type, env, legacy_data_decode: false
|
384
|
+
spec_version = env["HTTP_CE_SPECVERSION"]
|
385
|
+
return nil unless spec_version
|
386
|
+
unless spec_version =~ /^0\.3|1(\.|$)/
|
387
|
+
raise SpecVersionError, "Unrecognized specversion: #{spec_version}"
|
388
|
+
end
|
389
|
+
if legacy_data_decode
|
390
|
+
data = content
|
391
|
+
else
|
392
|
+
data, content_type = decode_data spec_version, content, content_type
|
393
|
+
end
|
394
|
+
attributes = { "spec_version" => spec_version, "data" => data }
|
395
|
+
attributes["data_content_type"] = content_type if content_type
|
396
|
+
omit_names = ["specversion", "spec_version", "data", "datacontenttype", "data_content_type"]
|
397
|
+
env.each do |key, value|
|
398
|
+
match = /^HTTP_CE_(\w+)$/.match key
|
399
|
+
next unless match
|
400
|
+
attr_name = match[1].downcase
|
401
|
+
attributes[attr_name] = percent_decode value unless omit_names.include? attr_name
|
402
|
+
end
|
403
|
+
Event.create spec_version: spec_version, attributes: attributes
|
404
|
+
end
|
405
|
+
|
406
|
+
def decode_data spec_version, content, content_type, **format_args
|
407
|
+
@data_decoders.reverse_each do |handler|
|
408
|
+
result = handler.decode_data spec_version: spec_version,
|
409
|
+
content: content,
|
410
|
+
content_type: content_type,
|
411
|
+
**format_args
|
412
|
+
if result&.key?(:data) && result&.key?(:content_type)
|
413
|
+
return [result[:data], result[:content_type]]
|
414
|
+
end
|
415
|
+
end
|
416
|
+
raise "Should not get here"
|
417
|
+
end
|
418
|
+
|
419
|
+
def encode_data spec_version, data_obj, content_type, **format_args
|
420
|
+
@data_encoders.reverse_each do |handler|
|
421
|
+
result = handler.encode_data spec_version: spec_version,
|
422
|
+
data: data_obj,
|
423
|
+
content_type: content_type,
|
424
|
+
**format_args
|
425
|
+
if result&.key?(:content) && result&.key?(:content_type)
|
426
|
+
return [result[:content], result[:content_type]]
|
427
|
+
end
|
428
|
+
end
|
429
|
+
raise "Should not get here"
|
430
|
+
end
|
431
|
+
|
296
432
|
def read_with_charset io, charset
|
433
|
+
return nil if io.nil?
|
297
434
|
str = io.read
|
298
435
|
if charset
|
299
436
|
begin
|
@@ -306,20 +443,23 @@ module CloudEvents
|
|
306
443
|
str
|
307
444
|
end
|
308
445
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
446
|
+
# @private
|
447
|
+
module DefaultDataFormat
|
448
|
+
# @private
|
449
|
+
def self.decode_data content: nil, content_type: nil, **_extra_kwargs
|
450
|
+
{ data: content, content_type: content_type }
|
314
451
|
end
|
315
|
-
end
|
316
452
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
453
|
+
# @private
|
454
|
+
def self.encode_data data: nil, content_type: nil, **_extra_kwargs
|
455
|
+
content = data.to_s
|
456
|
+
content_type ||=
|
457
|
+
if content.encoding == ::Encoding::ASCII_8BIT
|
458
|
+
"application/octet-stream"
|
459
|
+
else
|
460
|
+
"text/plain; charset=#{content.encoding.name.downcase}"
|
461
|
+
end
|
462
|
+
{ content: content, content_type: content_type }
|
323
463
|
end
|
324
464
|
end
|
325
465
|
end
|