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.
- checksums.yaml +4 -4
- data/.yardopts +6 -2
- data/CHANGELOG.md +38 -0
- data/README.md +57 -137
- data/bin/functions-framework-ruby +19 -0
- data/docs/deploying-functions.md +182 -0
- data/docs/overview.md +142 -0
- data/docs/running-a-functions-server.md +122 -0
- data/docs/testing-functions.md +169 -0
- data/docs/writing-functions.md +261 -0
- data/lib/functions_framework.rb +19 -42
- data/lib/functions_framework/cli.rb +71 -13
- data/lib/functions_framework/cloud_events.rb +9 -109
- data/lib/functions_framework/cloud_events/errors.rb +42 -0
- data/lib/functions_framework/cloud_events/event.rb +51 -249
- data/lib/functions_framework/cloud_events/event/v1.rb +363 -0
- data/lib/functions_framework/cloud_events/http_binding.rb +270 -0
- data/lib/functions_framework/cloud_events/json_format.rb +122 -0
- data/lib/functions_framework/function.rb +7 -11
- data/lib/functions_framework/legacy_event_converter.rb +145 -0
- data/lib/functions_framework/registry.rb +3 -27
- data/lib/functions_framework/server.rb +63 -42
- data/lib/functions_framework/testing.rb +60 -20
- data/lib/functions_framework/version.rb +1 -1
- metadata +16 -6
- data/lib/functions_framework/cloud_events/binary_content.rb +0 -59
- data/lib/functions_framework/cloud_events/json_structure.rb +0 -88
data/lib/functions_framework.rb
CHANGED
@@ -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}
|
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
|
-
#
|
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
|
-
#
|
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
|
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
|
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
|
-
|
216
|
-
|
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
|
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
|
-
|
88
|
+
@logging_level -= 1
|
75
89
|
end
|
76
90
|
op.on "-q", "--quiet", "Decrease log verbosity" do
|
77
|
-
|
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
|
-
|
99
|
-
|
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
|
-
|
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
|
-
|
110
|
-
|
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
|
-
@
|
34
|
-
|
29
|
+
# @private
|
30
|
+
SUPPORTED_SPEC_VERSIONS = ["1.0"].freeze
|
35
31
|
|
36
32
|
class << self
|
37
33
|
##
|
38
|
-
#
|
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
|
-
# @
|
61
|
-
# this handler
|
62
|
-
# @param handler [#decode_batched_content] The handler object
|
63
|
-
# @return [self]
|
36
|
+
# @return [Array<String>]
|
64
37
|
#
|
65
|
-
def
|
66
|
-
|
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
|
-
#
|
23
|
+
# CloudEvent object.
|
22
24
|
#
|
23
|
-
#
|
24
|
-
#
|
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
|
-
#
|
29
|
-
#
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|