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.
@@ -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: