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.
@@ -12,72 +12,171 @@ module CloudEvents
12
12
  # https://github.com/cloudevents/spec/blob/v1.0/json-format.md.
13
13
  #
14
14
  class JsonFormat
15
+ # @private
16
+ UNSPECIFIED = ::Object.new.freeze
17
+
15
18
  ##
16
- # Decode an event from the given input JSON string.
19
+ # Decode an event or batch from the given input JSON string.
20
+ # See {CloudEvents::Format#decode_event} for a general description.
17
21
  #
18
- # @param json [String] A JSON-formatted string
19
- # @return [CloudEvents::Event]
22
+ # Expects `:content` and `:content_type` arguments, and will decline the
23
+ # request unless both are provided.
24
+ #
25
+ # If decoding succeeded, returns a hash with _one of_ the following keys:
26
+ #
27
+ # * `:event` ({CloudEvents::Event}) A single event decoded from the input.
28
+ # * `:event_batch` (Array of {CloudEvents::Event}) A batch of events
29
+ # decoded from the input.
30
+ #
31
+ # @param content [String] Serialized content to decode.
32
+ # @param content_type [CloudEvents::ContentType] The input content type.
33
+ # @return [Hash] if accepting the request.
34
+ # @return [nil] if declining the request.
35
+ # @raise [CloudEvents::FormatSyntaxError] if the JSON could not be parsed
36
+ # @raise [CloudEvents::SpecVersionError] if an unsupported specversion is
37
+ # found.
20
38
  #
21
- def decode json, **_other_kwargs
22
- structure = ::JSON.parse json
23
- decode_hash_structure structure
39
+ def decode_event content: nil, content_type: nil, **_other_kwargs
40
+ return nil unless content && content_type&.media_type == "application" && content_type&.subtype_format == "json"
41
+ case content_type.subtype_base
42
+ when "cloudevents"
43
+ event = decode_hash_structure ::JSON.parse(content), charset_of(content)
44
+ { event: event }
45
+ when "cloudevents-batch"
46
+ charset = charset_of content
47
+ batch = Array(::JSON.parse(content)).map do |structure|
48
+ decode_hash_structure structure, charset
49
+ end
50
+ { event_batch: batch }
51
+ end
52
+ rescue ::JSON::JSONError
53
+ raise FormatSyntaxError, "JSON syntax error"
24
54
  end
25
55
 
26
56
  ##
27
- # Encode an event to a JSON string.
57
+ # Encode an event or batch to a JSON string. This formatter should be able
58
+ # to handle any event.
59
+ # See {CloudEvents::Format#decode_event} for a general description.
28
60
  #
29
- # @param event [CloudEvents::Event] An input event.
61
+ # Expects _either_ the `:event` _or_ the `:event_batch` argument, but not
62
+ # both, and will decline the request unless exactly one is provided.
63
+ #
64
+ # If encoding succeeded, returns a hash with the following keys:
65
+ #
66
+ # * `:content` (String) The serialized form of the event or batch.
67
+ # * `:content_type` ({CloudEvents::ContentType}) The content type for the
68
+ # output.
69
+ #
70
+ # @param event [CloudEvents::Event] An event to encode.
71
+ # @param event_batch [Array<CloudEvents::Event>] An event batch to encode.
30
72
  # @param sort [boolean] Whether to sort keys of the JSON output.
31
- # @return [String] The JSON representation.
73
+ # @return [Hash] if accepting the request.
74
+ # @return [nil] if declining the request.
32
75
  #
33
- def encode event, sort: false, **_other_kwargs
34
- structure = encode_hash_structure event
35
- structure = sort_keys structure if sort
36
- ::JSON.dump structure
76
+ def encode_event event: nil, event_batch: nil, sort: false, **_other_kwargs
77
+ if event && !event_batch
78
+ structure = encode_hash_structure event
79
+ structure = sort_keys structure if sort
80
+ subtype = "cloudevents"
81
+ elsif event_batch && !event
82
+ structure = event_batch.map do |elem|
83
+ structure_elem = encode_hash_structure elem
84
+ sort ? sort_keys(structure_elem) : structure_elem
85
+ end
86
+ subtype = "cloudevents-batch"
87
+ else
88
+ return nil
89
+ end
90
+ content = ::JSON.dump structure
91
+ content_type = ContentType.new "application/#{subtype}+json; charset=#{charset_of content}"
92
+ { content: content, content_type: content_type }
37
93
  end
38
94
 
39
95
  ##
40
- # Decode a batch of events from the given input string.
96
+ # Decode an event data object from a JSON formatted string.
97
+ # See {CloudEvents::Format#decode_data} for a general description.
98
+ #
99
+ # Expects `:spec_version`, `:content` and `:content_type` arguments, and
100
+ # will decline the request unless all three are provided.
101
+ #
102
+ # If decoding succeeded, returns a hash with the following keys:
103
+ #
104
+ # * `:data` (Object) The payload object to set as the `data` attribute.
105
+ # * `:content_type` ({CloudEvents::ContentType}) The content type to be set
106
+ # as the `datacontenttype` attribute.
41
107
  #
42
- # @param json [String] A JSON-formatted string
43
- # @return [Array<CloudEvents::Event>]
108
+ # @param content [String] Serialized content to decode.
109
+ # @param content_type [CloudEvents::ContentType] The input content type.
110
+ # @return [Hash] if accepting the request.
111
+ # @return [nil] if declining the request.
112
+ # @raise [CloudEvents::FormatSyntaxError] if the JSON could not be parsed.
113
+ # @raise [CloudEvents::SpecVersionError] if an unsupported specversion is
114
+ # found.
44
115
  #
45
- def decode_batch json, **_other_kwargs
46
- structure_array = Array(::JSON.parse(json))
47
- structure_array.map do |structure|
48
- decode_hash_structure structure
116
+ def decode_data spec_version: nil, content: nil, content_type: nil, **_other_kwargs
117
+ return nil unless spec_version
118
+ return nil unless content
119
+ return nil unless content_type&.subtype_base == "json" || content_type&.subtype_format == "json"
120
+ unless spec_version =~ /^0\.3|1(\.|$)/
121
+ raise SpecVersionError, "Unrecognized specversion: #{spec_version}"
49
122
  end
123
+ data = ::JSON.parse content
124
+ { data: data, content_type: content_type }
125
+ rescue ::JSON::JSONError
126
+ raise FormatSyntaxError, "JSON syntax error"
50
127
  end
51
128
 
52
129
  ##
53
- # Encode a batch of event to a JSON string.
130
+ # Encode an event data object to a JSON formatted string.
131
+ # See {CloudEvents::Format#encode_data} for a general description.
132
+ #
133
+ # Expects `:spec_version`, `:data` and `:content_type` arguments, and will
134
+ # decline the request unless all three are provided.
135
+ # The `:data` object can be any Ruby object that can be interpreted as
136
+ # JSON. Most Ruby objects will work, but normally it will be a JSON value
137
+ # type comprising hashes, arrays, strings, numbers, booleans, or nil.
138
+ #
139
+ # If decoding succeeded, returns a hash with the following keys:
54
140
  #
55
- # @param events [Array<CloudEvents::Event>] An array of input events.
141
+ # * `:content` (String) The serialized form of the data.
142
+ # * `:content_type` ({CloudEvents::ContentType}) The content type for the
143
+ # output.
144
+ #
145
+ # @param data [Object] A data object to encode.
146
+ # @param content_type [CloudEvents::ContentType] The input content type
56
147
  # @param sort [boolean] Whether to sort keys of the JSON output.
57
- # @return [String] The JSON representation.
148
+ # @return [Hash] if accepting the request.
149
+ # @return [nil] if declining the request.
58
150
  #
59
- def encode_batch events, sort: false, **_other_kwargs
60
- structure_array = Array(events).map do |event|
61
- structure = encode_hash_structure event
62
- sort ? sort_keys(structure) : structure
151
+ def encode_data spec_version: nil, data: UNSPECIFIED, content_type: nil, sort: false, **_other_kwargs
152
+ return nil unless spec_version
153
+ return nil if data == UNSPECIFIED
154
+ return nil unless content_type&.subtype_base == "json" || content_type&.subtype_format == "json"
155
+ unless spec_version =~ /^0\.3|1(\.|$)/
156
+ raise SpecVersionError, "Unrecognized specversion: #{spec_version}"
63
157
  end
64
- ::JSON.dump structure_array
158
+ data = sort_keys data if sort
159
+ content = ::JSON.dump data
160
+ { content: content, content_type: content_type }
65
161
  end
66
162
 
67
163
  ##
68
164
  # Decode a single event from a hash data structure with keys and types
69
165
  # conforming to the JSON envelope.
70
166
  #
167
+ # @private
168
+ #
71
169
  # @param structure [Hash] An input hash.
170
+ # @param charset [String] The charset.
72
171
  # @return [CloudEvents::Event]
73
172
  #
74
- def decode_hash_structure structure
173
+ def decode_hash_structure structure, charset = nil
75
174
  spec_version = structure["specversion"].to_s
76
175
  case spec_version
77
176
  when "0.3"
78
- decode_hash_structure_v0 structure
177
+ decode_hash_structure_v0 structure, charset
79
178
  when /^1(\.|$)/
80
- decode_hash_structure_v1 structure
179
+ decode_hash_structure_v1 structure, charset
81
180
  else
82
181
  raise SpecVersionError, "Unrecognized specversion: #{spec_version}"
83
182
  end
@@ -87,6 +186,8 @@ module CloudEvents
87
186
  # Encode a single event to a hash data structure with keys and types
88
187
  # conforming to the JSON envelope.
89
188
  #
189
+ # @private
190
+ #
90
191
  # @param event [CloudEvents::Event] An input event.
91
192
  # @return [String] The hash structure.
92
193
  #
@@ -103,44 +204,51 @@ module CloudEvents
103
204
 
104
205
  private
105
206
 
106
- def sort_keys hash
207
+ def sort_keys obj
208
+ return obj unless obj.is_a? ::Hash
107
209
  result = {}
108
- hash.keys.sort.each do |key|
109
- result[key] = hash[key]
210
+ obj.keys.sort.each do |key|
211
+ result[key] = sort_keys obj[key]
110
212
  end
111
213
  result
112
214
  end
113
215
 
114
- def decode_hash_structure_v0 structure
115
- data = structure["data"]
116
- content_type = structure["datacontenttype"]
117
- if data.is_a?(::String) && content_type.is_a?(::String)
118
- content_type = ContentType.new content_type
119
- if content_type.subtype == "json" || content_type.subtype_format == "json"
120
- structure = structure.dup
121
- structure["data"] = ::JSON.parse data rescue data
122
- structure["datacontenttype"] = content_type
123
- end
216
+ def charset_of str
217
+ encoding = str.encoding
218
+ if encoding == ::Encoding::ASCII_8BIT
219
+ "binary"
220
+ else
221
+ encoding.name.downcase
222
+ end
223
+ end
224
+
225
+ def decode_hash_structure_v0 structure, charset
226
+ unless structure.key? "datacontenttype"
227
+ structure = structure.dup
228
+ content_type = "application/json"
229
+ content_type = "#{content_type}; charset=#{charset}" if charset
230
+ structure["datacontenttype"] = content_type
124
231
  end
125
232
  Event::V0.new attributes: structure
126
233
  end
127
234
 
128
- def decode_hash_structure_v1 structure
235
+ def decode_hash_structure_v1 structure, charset
129
236
  if structure.key? "data_base64"
130
237
  structure = structure.dup
131
238
  structure["data"] = ::Base64.decode64 structure.delete "data_base64"
239
+ structure["datacontenttype"] ||= "application/octet-stream"
240
+ elsif !structure.key? "datacontenttype"
241
+ structure = structure.dup
242
+ content_type = "application/json"
243
+ content_type = "#{content_type}; charset=#{charset}" if charset
244
+ structure["datacontenttype"] = content_type
132
245
  end
133
246
  Event::V1.new attributes: structure
134
247
  end
135
248
 
136
249
  def encode_hash_structure_v0 event
137
250
  structure = event.to_h
138
- data = event.data
139
- content_type = event.data_content_type
140
- if data.is_a?(::String) && !content_type.nil? &&
141
- (content_type.subtype == "json" || content_type.subtype_format == "json")
142
- structure["data"] = ::JSON.parse data rescue data
143
- end
251
+ structure["datacontenttype"] ||= "application/json"
144
252
  structure
145
253
  end
146
254
 
@@ -150,6 +258,9 @@ module CloudEvents
150
258
  if data.is_a?(::String) && data.encoding == ::Encoding::ASCII_8BIT
151
259
  structure.delete "data"
152
260
  structure["data_base64"] = ::Base64.encode64 data
261
+ structure["datacontenttype"] ||= "application/octet-stream"
262
+ else
263
+ structure["datacontenttype"] ||= "application/json"
153
264
  end
154
265
  structure
155
266
  end
@@ -5,5 +5,5 @@ module CloudEvents
5
5
  # Version of the Ruby CloudEvents SDK
6
6
  # @return [String]
7
7
  #
8
- VERSION = "0.4.0"
8
+ VERSION = "0.5.1"
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloud_events
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Azuma
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2021-06-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: The official Ruby implementation of the CloudEvents Specification. Provides
14
14
  data types for events, and HTTP/JSON bindings for marshalling and unmarshalling
@@ -28,9 +28,11 @@ files:
28
28
  - lib/cloud_events/errors.rb
29
29
  - lib/cloud_events/event.rb
30
30
  - lib/cloud_events/event/field_interpreter.rb
31
+ - lib/cloud_events/event/opaque.rb
31
32
  - lib/cloud_events/event/utils.rb
32
33
  - lib/cloud_events/event/v0.rb
33
34
  - lib/cloud_events/event/v1.rb
35
+ - lib/cloud_events/format.rb
34
36
  - lib/cloud_events/http_binding.rb
35
37
  - lib/cloud_events/json_format.rb
36
38
  - lib/cloud_events/version.rb
@@ -38,10 +40,10 @@ homepage: https://github.com/cloudevents/sdk-ruby
38
40
  licenses:
39
41
  - Apache-2.0
40
42
  metadata:
41
- changelog_uri: https://cloudevents.github.io/sdk-ruby/v0.4.0/file.CHANGELOG.html
43
+ changelog_uri: https://cloudevents.github.io/sdk-ruby/v0.5.1/file.CHANGELOG.html
42
44
  source_code_uri: https://github.com/cloudevents/sdk-ruby
43
45
  bug_tracker_uri: https://github.com/cloudevents/sdk-ruby/issues
44
- documentation_uri: https://cloudevents.github.io/sdk-ruby/v0.4.0
46
+ documentation_uri: https://cloudevents.github.io/sdk-ruby/v0.5.1
45
47
  post_install_message:
46
48
  rdoc_options: []
47
49
  require_paths: