functions_framework 0.1.0 → 0.3.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.
@@ -16,6 +16,7 @@ require "logger"
16
16
 
17
17
  require "functions_framework/cloud_events"
18
18
  require "functions_framework/function"
19
+ require "functions_framework/legacy_event_converter"
19
20
  require "functions_framework/registry"
20
21
  require "functions_framework/version"
21
22
 
@@ -36,8 +37,8 @@ require "functions_framework/version"
36
37
  # functions framework. Use the {FunctionsFramework.http},
37
38
  # {FunctionsFramework.event}, or {FunctionsFramework.cloud_event} methods to
38
39
  # define functions. To serve functions via a web service, invoke the
39
- # `functions-framework` executable, or use the {FunctionsFramework.start} or
40
- # {FunctionsFramework.run} methods.
40
+ # `functions-framework-ruby` executable, or use the {FunctionsFramework.start}
41
+ # or {FunctionsFramework.run} methods.
41
42
  #
42
43
  # ## Internal modules
43
44
  #
@@ -48,7 +49,7 @@ require "functions_framework/version"
48
49
  # you define an event function, you will receive the event as a
49
50
  # {FunctionsFramework::CloudEvents::Event} object.
50
51
  # * {FunctionsFramework::CLI} is the implementation of the
51
- # `functions-framework` executable. Most apps will not need to interact
52
+ # `functions-framework-ruby` executable. Most apps will not need to interact
52
53
  # with this class directly.
53
54
  # * {FunctionsFramework::Function} is the internal representation of a
54
55
  # function, indicating the type of function (http or cloud event), the
@@ -62,7 +63,7 @@ require "functions_framework/version"
62
63
  # * {FunctionsFramework::Server} is a web server that makes a function
63
64
  # available via HTTP. It wraps the Puma web server and runs a specific
64
65
  # {FunctionsFramework::Function}. Many apps can simply run the
65
- # `functions-framework` executable to spin up a server. However, if you
66
+ # `functions-framework-ruby` executable to spin up a server. However, if you
66
67
  # need closer control over your execution environment, you can use the
67
68
  # {FunctionsFramework::Server} class to run a server. Note that, in most
68
69
  # cases, it is easier to use the {FunctionsFramework.start} or
@@ -139,37 +140,10 @@ module FunctionsFramework
139
140
  end
140
141
 
141
142
  ##
142
- # Define a function that responds to CloudEvents.
143
- #
144
- # You must provide a name for the function, and a block that implemets the
145
- # function. The block should take two arguments: the event _data_ and the
146
- # event _context_. Any return value is ignored.
147
- #
148
- # The event data argument will be one of the following types:
149
- # * A `String` (with encoding `ASCII-8BIT`) if the data is in the form of
150
- # binary data. You may choose to perform additional interpretation of
151
- # the binary data using information in the content type provided by the
152
- # context argument.
153
- # * Any data type that can be represented in JSON (i.e. `String`,
154
- # `Integer`, `Array`, `Hash`, `true`, `false`, or `nil`) if the event
155
- # came with a JSON payload. The content type may also be set in the
156
- # context if the data is a String.
157
- #
158
- # The context argument will be of type {FunctionsFramework::CloudEvents::Event},
159
- # and will contain CloudEvents context attributes such as `id` and `type`.
160
- #
161
- # See also {FunctionsFramework.cloud_event} which defines a function that
162
- # takes a single argument of type {FunctionsFramework::CloudEvents::Event}.
163
- #
164
- # ## Example
143
+ # This is an obsolete interface that defines an event function taking two
144
+ # arguments (data and context) rather than one.
165
145
  #
166
- # FunctionsFramework.event "my-function" do |data, context|
167
- # FunctionsFramework.logger.info "Event data: #{data.inspect}"
168
- # end
169
- #
170
- # @param name [String] The function name. Defaults to {DEFAULT_TARGET}.
171
- # @param block [Proc] The function code as a proc.
172
- # @return [self]
146
+ # @deprecated Use {FunctionsFramework.cloud_event} instead.
173
147
  #
174
148
  def event name = DEFAULT_TARGET, &block
175
149
  global_registry.add_event name, &block
@@ -180,12 +154,9 @@ module FunctionsFramework
180
154
  # Define a function that responds to CloudEvents.
181
155
  #
182
156
  # You must provide a name for the function, and a block that implemets the
183
- # function. The block should take _one_ argument: the event object of type
157
+ # function. The block should take one argument: the event object of type
184
158
  # {FunctionsFramework::CloudEvents::Event}. Any return value is ignored.
185
159
  #
186
- # See also {FunctionsFramework.event} which creates a function that takes
187
- # data and context as separate arguments.
188
- #
189
160
  # ## Example
190
161
  #
191
162
  # FunctionsFramework.cloud_event "my-function" do |event|
@@ -205,15 +176,20 @@ module FunctionsFramework
205
176
  # Start the functions framework server in the background. The server will
206
177
  # look up the given target function name in the global registry.
207
178
  #
208
- # @param target [String] The name of the function to run
179
+ # @param target [FunctionsFramework::Function,String] The function to run,
180
+ # or the name of the function to look up in the global registry.
209
181
  # @yield [FunctionsFramework::Server::Config] A config object that can be
210
182
  # manipulated to configure the server.
211
183
  # @return [FunctionsFramework::Server]
212
184
  #
213
185
  def start target, &block
214
186
  require "functions_framework/server"
215
- function = global_registry[target]
216
- raise ::ArgumentError, "Undefined function: #{target.inspect}" if function.nil?
187
+ if target.is_a? ::FunctionsFramework::Function
188
+ function = target
189
+ else
190
+ function = global_registry[target]
191
+ raise ::ArgumentError, "Undefined function: #{target.inspect}" if function.nil?
192
+ end
217
193
  server = Server.new function, &block
218
194
  server.respond_to_signals
219
195
  server.start
@@ -223,7 +199,8 @@ module FunctionsFramework
223
199
  # Run the functions framework server and block until it stops. The server
224
200
  # will look up the given target function name in the global registry.
225
201
  #
226
- # @param target [String] The name of the function to run
202
+ # @param target [FunctionsFramework::Function,String] The function to run,
203
+ # or the name of the function to look up in the global registry.
227
204
  # @yield [FunctionsFramework::Server::Config] A config object that can be
228
205
  # manipulated to configure the server.
229
206
  # @return [self]
@@ -12,15 +12,22 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ require "logger"
15
16
  require "optparse"
16
17
 
17
18
  require "functions_framework"
18
19
 
19
20
  module FunctionsFramework
20
21
  ##
21
- # Implementation of the functions-framework executable.
22
+ # Implementation of the functions-framework-ruby executable.
22
23
  #
23
24
  class CLI
25
+ ##
26
+ # The default logging level, if not given in the environment variable.
27
+ # @return [Integer]
28
+ #
29
+ DEFAULT_LOGGING_LEVEL = ::Logger::Severity::INFO
30
+
24
31
  ##
25
32
  # Create a new CLI, setting arguments to their defaults.
26
33
  #
@@ -33,6 +40,8 @@ module FunctionsFramework
33
40
  @min_threads = nil
34
41
  @max_threads = nil
35
42
  @detailed_errors = nil
43
+ @signature_type = ::ENV["FUNCTION_SIGNATURE_TYPE"]
44
+ @logging_level = init_logging_level
36
45
  end
37
46
 
38
47
  ##
@@ -52,6 +61,11 @@ module FunctionsFramework
52
61
  "Set the source file to load (defaults to #{DEFAULT_SOURCE})" do |val|
53
62
  @source = val
54
63
  end
64
+ op.on "--signature-type TYPE",
65
+ "Asserts that the function has the given signature type." \
66
+ " Supported values are 'http' and 'cloudevent'." do |val|
67
+ @signature_type = val
68
+ end
55
69
  op.on "-p", "--port PORT", "Set the port to listen to (defaults to 8080)" do |val|
56
70
  @port = val.to_i
57
71
  end
@@ -71,10 +85,10 @@ module FunctionsFramework
71
85
  @detailed_errors = val
72
86
  end
73
87
  op.on "-v", "--verbose", "Increase log verbosity" do
74
- ::FunctionsFramework.logger.level -= 1
88
+ @logging_level -= 1
75
89
  end
76
90
  op.on "-q", "--quiet", "Decrease log verbosity" do
77
- ::FunctionsFramework.logger.level += 1
91
+ @logging_level += 1
78
92
  end
79
93
  op.on "--help", "Display help" do
80
94
  puts op
@@ -82,23 +96,51 @@ module FunctionsFramework
82
96
  end
83
97
  end
84
98
  option_parser.parse! argv
85
- unless argv.empty?
86
- warn "Unrecognized arguments: #{argv}"
87
- puts op
88
- exit 1
89
- end
99
+ error "Unrecognized arguments: #{argv}\n#{op}" unless argv.empty?
90
100
  self
91
101
  end
92
102
 
93
103
  ##
94
104
  # Run the configured server, and block until it stops.
105
+ # If a validation error occurs, print a message and exit.
106
+ #
95
107
  # @return [self]
96
108
  #
97
109
  def run
98
- FunctionsFramework.logger.info \
99
- "FunctionsFramework: Loading functions from #{@source.inspect}..."
110
+ begin
111
+ server = start_server
112
+ rescue ::StandardError => e
113
+ error e.message
114
+ end
115
+ server.wait_until_stopped
116
+ self
117
+ end
118
+
119
+ ##
120
+ # Start the configured server and return the running server object.
121
+ # If a validation error occurs, raise an exception.
122
+ # This is used for testing the CLI.
123
+ #
124
+ # @return [FunctionsFramework::Server]
125
+ #
126
+ # @private
127
+ #
128
+ def start_server
129
+ ::FunctionsFramework.logger.level = @logging_level
130
+ ::FunctionsFramework.logger.info "FunctionsFramework v#{VERSION} server starting."
131
+ ::ENV["FUNCTION_TARGET"] = @target
132
+ ::ENV["FUNCTION_SOURCE"] = @source
133
+ ::ENV["FUNCTION_SIGNATURE_TYPE"] = @signature_type
134
+ ::FunctionsFramework.logger.info "FunctionsFramework: Loading functions from #{@source.inspect}..."
100
135
  load @source
101
- server = ::FunctionsFramework.start @target do |config|
136
+ function = ::FunctionsFramework.global_registry[@target]
137
+ raise "Undefined function: #{@target.inspect}" if function.nil?
138
+ unless @signature_type.nil? ||
139
+ @signature_type == "http" && function.type == :http ||
140
+ ["cloudevent", "event"].include?(@signature_type) && function.type == :cloud_event
141
+ raise "Function #{@target.inspect} does not match type #{@signature_type}"
142
+ end
143
+ ::FunctionsFramework.start function do |config|
102
144
  config.rack_env = @env
103
145
  config.port = @port
104
146
  config.bind_addr = @bind
@@ -106,8 +148,24 @@ module FunctionsFramework
106
148
  config.min_threads = @min_threads
107
149
  config.max_threads = @max_threads
108
150
  end
109
- server.wait_until_stopped
110
- self
151
+ end
152
+
153
+ private
154
+
155
+ def init_logging_level
156
+ level_name = ::ENV["FUNCTION_LOGGING_LEVEL"].to_s.upcase.to_sym
157
+ ::Logger::Severity.const_get level_name
158
+ rescue ::NameError
159
+ DEFAULT_LOGGING_LEVEL
160
+ end
161
+
162
+ ##
163
+ # Print the given error message and exit.
164
+ # @param message [String]
165
+ #
166
+ def error message
167
+ warn message
168
+ exit 1
111
169
  end
112
170
  end
113
171
  end
@@ -12,9 +12,11 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require "functions_framework/cloud_events/binary_content"
16
15
  require "functions_framework/cloud_events/content_type"
16
+ require "functions_framework/cloud_events/errors"
17
17
  require "functions_framework/cloud_events/event"
18
+ require "functions_framework/cloud_events/http_binding"
19
+ require "functions_framework/cloud_events/json_format"
18
20
 
19
21
  module FunctionsFramework
20
22
  ##
@@ -22,122 +24,20 @@ module FunctionsFramework
22
24
  #
23
25
  # This is a Ruby implementation of the [CloudEvents](https://cloudevents.io)
24
26
  # [1.0 specification](https://github.com/cloudevents/spec/blob/master/spec.md).
25
- # It provides for unmarshaling of events from Rack environment data from
26
- # binary (i.e. header-based) format, as well as structured (body-based) and
27
- # batch formats. A standard JSON structure parser is included. It is also
28
- # possible to register handlers for other formats.
29
- #
30
- # TODO: Unmarshaling of events is implemented, but marshaling is not.
31
27
  #
32
28
  module CloudEvents
33
- @structured_formats = {}
34
- @batched_formats = {}
29
+ # @private
30
+ SUPPORTED_SPEC_VERSIONS = ["1.0"].freeze
35
31
 
36
32
  class << self
37
33
  ##
38
- # Register a handler for the given structured format.
39
- # The handler object must respond to the method
40
- # `#decode_structured_content`. See
41
- # {FunctionsFramework::CloudEvents::JsonStructure} for an example.
42
- #
43
- # @param format [String] The subtype format that should be handled by
44
- # this handler
45
- # @param handler [#decode_structured_content] The handler object
46
- # @return [self]
47
- #
48
- def register_structured_format format, handler
49
- handlers = @structured_formats[format.to_s.strip.downcase] ||= []
50
- handlers << handler unless handlers.include? handler
51
- self
52
- end
53
-
54
- ##
55
- # Register a handler for the given batched format.
56
- # The handler object must respond to the method
57
- # `#decode_batched_content`. See
58
- # {FunctionsFramework::CloudEvents::JsonStructure} for an example.
34
+ # The spec versions supported by this implementation.
59
35
  #
60
- # @param format [String] The subtype format that should be handled by
61
- # this handler
62
- # @param handler [#decode_batched_content] The handler object
63
- # @return [self]
36
+ # @return [Array<String>]
64
37
  #
65
- def register_batched_format format, handler
66
- handlers = @batched_formats[format.to_s.strip.downcase] ||= []
67
- handlers << handler unless handlers.include? handler
68
- self
69
- end
70
-
71
- ##
72
- # Decode an event from the given Rack environment hash. Following the
73
- # CloudEvents spec, this chooses a handler based on the Content-Type of
74
- # the request.
75
- #
76
- # @param env [Hash] The Rack environment
77
- # @return [FunctionsFramework::CloudEvents::Event] if the request
78
- # includes a single structured or binary event
79
- # @return [Array<FunctionsFramework::CloudEvents::Event>] if the request
80
- # includes a batch of structured events
81
- #
82
- def decode_rack_env env
83
- content_type_header = env["CONTENT_TYPE"]
84
- raise "Missing content-type header" unless content_type_header
85
- content_type = ContentType.new content_type_header
86
- if content_type.media_type == "application"
87
- case content_type.subtype_prefix
88
- when "cloudevents"
89
- return decode_structured_content env["rack.input"], content_type
90
- when "cloudevents-batch"
91
- return decode_batched_content env["rack.input"], content_type
92
- end
93
- end
94
- BinaryContent.decode_rack_env env, content_type
95
- end
96
-
97
- ##
98
- # Decode a single event from the given content data. This should be
99
- # passed the request body, if the Content-Type is of the form
100
- # `application/cloudevents+format`.
101
- #
102
- # @param input [IO] An IO-like object providing the content
103
- # @param content_type [FunctionsFramework::CloudEvents::ContentType] the
104
- # content type
105
- # @return [FunctionsFramework::CloudEvents::Event]
106
- #
107
- def decode_structured_content input, content_type
108
- handlers = @structured_formats[content_type.subtype_format] || []
109
- handlers.reverse_each do |handler|
110
- event = handler.decode_structured_content input, content_type
111
- return event if event
112
- end
113
- raise "Unknown cloudevents format: #{content_type.subtype_format.inspect}"
114
- end
115
-
116
- ##
117
- # Decode a batch of events from the given content data. This should be
118
- # passed the request body, if the Content-Type is of the form
119
- # `application/cloudevents-batch+format`.
120
- #
121
- # @param input [IO] An IO-like object providing the content
122
- # @param content_type [FunctionsFramework::CloudEvents::ContentType] the
123
- # content type
124
- # @return [Array<FunctionsFramework::CloudEvents::Event>]
125
- #
126
- def decode_batched_content input, content_type
127
- handlers = @batched_formats[content_type.subtype_format] || []
128
- handlers.reverse_each do |handler|
129
- events = handler.decode_batched_content input, content_type
130
- return events if events
131
- end
132
- raise "Unknown cloudevents batch format: #{content_type.subtype_format.inspect}"
38
+ def supported_spec_versions
39
+ SUPPORTED_SPEC_VERSIONS
133
40
  end
134
41
  end
135
42
  end
136
43
  end
137
-
138
- require "functions_framework/cloud_events/json_structure"
139
-
140
- FunctionsFramework::CloudEvents.register_structured_format \
141
- "json", FunctionsFramework::CloudEvents::JsonStructure
142
- FunctionsFramework::CloudEvents.register_batched_format \
143
- "json", FunctionsFramework::CloudEvents::JsonStructure
@@ -0,0 +1,42 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module FunctionsFramework
16
+ module CloudEvents
17
+ ##
18
+ # Base class for all CloudEvents errors.
19
+ #
20
+ class CloudEventsError < ::RuntimeError
21
+ end
22
+
23
+ ##
24
+ # Errors indicating unsupported or incorrectly formatted HTTP content or
25
+ # headers.
26
+ #
27
+ class HttpContentError < CloudEventsError
28
+ end
29
+
30
+ ##
31
+ # Errors indicating an unsupported or incorrect spec version.
32
+ #
33
+ class SpecVersionError < CloudEventsError
34
+ end
35
+
36
+ ##
37
+ # Errors related to CloudEvent attributes.
38
+ #
39
+ class AttributeError < CloudEventsError
40
+ end
41
+ end
42
+ end
@@ -15,262 +15,64 @@
15
15
  require "date"
16
16
  require "uri"
17
17
 
18
+ require "functions_framework/cloud_events/event/v1"
19
+
18
20
  module FunctionsFramework
19
21
  module CloudEvents
20
22
  ##
21
- # A cloud event data type.
23
+ # CloudEvent object.
22
24
  #
23
- # This object represents both the event data and the context attributes.
24
- # It is immutable. The data and attribute values can be retrieved but not
25
- # modified. To obtain an event with modifications, use the {#with} method
26
- # to create a copy with the desired changes.
25
+ # An Event object represents a complete event, including both its data and
26
+ # its context attributes. The following are true of all event objects:
27
27
  #
28
- # See https://github.com/cloudevents/spec/blob/master/spec.md for
29
- # descriptions of the various attributes.
28
+ # * Event classes are defined within this module. For example, events
29
+ # conforming to the CloudEvents 1.0 specification are of type
30
+ # {FunctionsFramework::CloudEvents::Event::V1}.
31
+ # * All event classes include this module, so you can use
32
+ # `is_a? FunctionsFramework::CloudEvents::Event` to test whether an
33
+ # object is an event.
34
+ # * All event objects are immutable. Data and atribute values can be
35
+ # retrieved but not modified. To "modify" an event, make a copy with
36
+ # the desired changes. Generally, event classes will provide a helper
37
+ # method for this purpose.
38
+ # * All event objects have a `spec_version` method that returns the
39
+ # version of the CloudEvents spec implemented by that event. (Other
40
+ # methods may be different, depending on the spec version.)
30
41
  #
31
- class Event
32
- ##
33
- # Create a new cloud event object with the given data and attributes.
34
- #
35
- # @param id [String] The required `id` field
36
- # @param source [String,URI] The required `source` field
37
- # @param type [String] The required `type` field
38
- # @param spec_version [String] The required `specversion` field
39
- # @param data [String,Boolean,Integer,Array,Hash] The optional `data`
40
- # field
41
- # @param data_content_type [String,FunctionsFramework::CloudEvents::ContentType]
42
- # The optional `datacontenttype` field
43
- # @param data_schema [String,URI] The optional `dataschema` field
44
- # @param subject [String] The optional `subject` field
45
- # @param time [String,DateTime] The optional `time` field
46
- #
47
- def initialize \
48
- id:,
49
- source:,
50
- type:,
51
- spec_version:,
52
- data: nil,
53
- data_content_type: nil,
54
- data_schema: nil,
55
- subject: nil,
56
- time: nil
57
- @id = interpret_string "id", id, true
58
- @source, @source_string = interpret_uri "source", source, true
59
- @type = interpret_string "type", type, true
60
- @spec_version = interpret_string "spec_version", spec_version, true
61
- @data = data
62
- @data_content_type, @data_content_type_string =
63
- interpret_content_type "data_content_type", data_content_type
64
- @data_schema, @data_schema_string = interpret_uri "data_schema", data_schema
65
- @subject = interpret_string "subject", subject
66
- @time, @time_string = interpret_date_time "time", time
67
- end
68
-
69
- ##
70
- # Create and return a copy of this event with the given changes. See the
71
- # constructor for the parameters that can be passed.
72
- #
73
- # @param changes [keywords] See {#initialize} for a list of arguments.
74
- # @return [FunctionFramework::CloudEvents::Event]
75
- #
76
- def with **changes
77
- params = {
78
- id: id,
79
- source: source,
80
- type: type,
81
- spec_version: spec_version,
82
- data: data,
83
- data_content_type: data_content_type,
84
- data_schema: data_schema,
85
- subject: subject,
86
- time: time
87
- }
88
- params.merge! changes
89
- Event.new(**params)
90
- end
91
-
92
- ##
93
- # The `id` field
94
- # @return [String]
95
- #
96
- attr_reader :id
97
-
98
- ##
99
- # The `source` field as a `URI` object
100
- # @return [URI]
101
- #
102
- attr_reader :source
103
-
104
- ##
105
- # The string representation of the `source` field
106
- # @return [String]
107
- #
108
- attr_reader :source_string
109
-
110
- ##
111
- # The `type` field
112
- # @return [String]
113
- #
114
- attr_reader :type
115
-
116
- ##
117
- # The `specversion` field
118
- # @return [String]
119
- #
120
- attr_reader :spec_version
121
- alias specversion spec_version
122
-
123
- ##
124
- # The event-specific data, or `nil` if there is no data.
125
- #
126
- # Data may be one of the following types:
127
- # * Binary data, represented by a `String` using `ASCII-8BIT` encoding
128
- # * A string in some other encoding such as `UTF-8` or `US-ASCII`
129
- # * Any JSON data type, such as String, boolean, Integer, Array, or Hash
130
- #
131
- # @return [Object]
132
- #
133
- attr_reader :data
134
-
135
- ##
136
- # The optional `datacontenttype` field as a
137
- # {FunctionsFramework::CloudEvents::ContentType} object, or `nil` if the
138
- # field is absent
139
- #
140
- # @return [FunctionsFramework::CloudEvents::ContentType,nil]
141
- #
142
- attr_reader :data_content_type
143
- alias datacontenttype data_content_type
144
-
145
- ##
146
- # The string representation of the optional `datacontenttype` field, or
147
- # `nil` if the field is absent
148
- #
149
- # @return [String,nil]
150
- #
151
- attr_reader :data_content_type_string
152
- alias datacontenttype_string data_content_type_string
153
-
154
- ##
155
- # The optional `dataschema` field as a `URI` object, or `nil` if the
156
- # field is absent
157
- #
158
- # @return [URI,nil]
159
- #
160
- attr_reader :data_schema
161
- alias dataschema data_schema
162
-
163
- ##
164
- # The string representation of the optional `dataschema` field, or `nil`
165
- # if the field is absent
166
- #
167
- # @return [String,nil]
168
- #
169
- attr_reader :data_schema_string
170
- alias dataschema_string data_schema_string
171
-
172
- ##
173
- # The optional `subject` field, or `nil` if the field is absent
174
- #
175
- # @return [String,nil]
176
- #
177
- attr_reader :subject
178
-
179
- ##
180
- # The optional `time` field as a `DateTime` object, or `nil` if the field
181
- # is absent
182
- #
183
- # @return [DateTime,nil]
184
- #
185
- attr_reader :time
186
-
187
- ##
188
- # The string representation of the optional `time` field, or `nil` if the
189
- # field is absent
190
- #
191
- # @return [String,nil]
192
- #
193
- attr_reader :time_string
194
-
195
- ## @private
196
- def == other
197
- other.is_a?(ContentType) &&
198
- id == other.id &&
199
- source == other.source &&
200
- type == other.type &&
201
- spec_version == other.spec_version &&
202
- data_content_type == other.data_content_type &&
203
- data_schema == other.data_schema &&
204
- subject == other.subject &&
205
- time == other.time &&
206
- data == other.data
207
- end
208
- alias eql? ==
209
-
210
- ## @private
211
- def hash
212
- @hash ||=
213
- [id, source, type, spec_version, data_content_type, data_schema, subject, time, data].hash
214
- end
215
-
216
- private
217
-
218
- def interpret_string name, input, required = false
219
- case input
220
- when ::String
221
- raise ::ArgumentError, "The #{name} field cannot be empty" if input.empty?
222
- input
223
- when nil
224
- raise ::ArgumentError, "The #{name} field is required" if required
225
- nil
226
- else
227
- raise ::ArgumentError, "Illegal type for #{name} field: #{input.inspect}"
228
- end
229
- end
230
-
231
- def interpret_uri name, input, required = false
232
- case input
233
- when ::String
234
- raise ::ArgumentError, "The #{name} field cannot be empty" if input.empty?
235
- [::URI.parse(input), input]
236
- when ::URI::Generic
237
- [input, input.to_s]
238
- when nil
239
- raise ::ArgumentError, "The #{name} field is required" if required
240
- [nil, nil]
241
- else
242
- raise ::ArgumentError, "Illegal type for #{name} field: #{input.inspect}"
243
- end
244
- end
245
-
246
- def interpret_date_time name, input, required = false
247
- case input
248
- when ::String
249
- raise ::ArgumentError, "The #{name} field cannot be empty" if input.empty?
250
- [::DateTime.rfc3339(input), input]
251
- when ::DateTime
252
- [input, input.rfc3339]
253
- when nil
254
- raise ::ArgumentError, "The #{name} field is required" if required
255
- [nil, nil]
256
- else
257
- raise ::ArgumentError, "Illegal type for #{name} field: #{input.inspect}"
258
- end
259
- end
260
-
261
- def interpret_content_type name, input, required = false
262
- case input
263
- when ::String
264
- raise ::ArgumentError, "The #{name} field cannot be empty" if input.empty?
265
- [ContentType.new(input), input]
266
- when ContentType
267
- [input, input.to_s]
268
- when nil
269
- raise ::ArgumentError, "The #{name} field is required" if required
270
- [nil, nil]
271
- else
272
- raise ::ArgumentError, "Illegal type for #{name} field: #{input.inspect}"
42
+ # To create an event, you may either:
43
+ #
44
+ # * Construct an instance of the event class directly, for example by
45
+ # calling {Event::V1.new} and passing a set of attributes.
46
+ # * Call {Event.create} and pass a spec version and a set of attributes.
47
+ # This will choose the appropriate event class based on the version.
48
+ # * Decode an event from another representation. For example, use
49
+ # {CloudEvents::JsonFormat} to decode an event from JSON, or use
50
+ # {CloudEvents::HttpBinding} to decode an event from an HTTP request.
51
+ #
52
+ # See https://github.com/cloudevents/spec/blob/master/spec.md for more
53
+ # information about CloudEvents.
54
+ #
55
+ module Event
56
+ class << self
57
+ ##
58
+ # Create a new cloud event object with the given version. Generally,
59
+ # you must also pass additional keyword arguments providing the event's
60
+ # data and attributes. For example, if you pass `1.0` as the
61
+ # `spec_version`, the remaining keyword arguments will be passed
62
+ # through to the {Event::V1.new} constructor.
63
+ #
64
+ # @param spec_version [String] The required `specversion` field.
65
+ # @param kwargs [keywords] Additional parameters for the event.
66
+ #
67
+ def create spec_version:, **kwargs
68
+ case spec_version
69
+ when /^1(\.|$)/
70
+ V1.new spec_version: spec_version, **kwargs
71
+ else
72
+ raise SpecVersionError, "Unrecognized specversion: #{spec_version}"
73
+ end
273
74
  end
75
+ alias new create
274
76
  end
275
77
  end
276
78
  end