functions_framework 0.1.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|