functions_framework 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE +201 -0
- data/README.md +218 -0
- data/bin/functions-framework +19 -0
- data/lib/functions_framework.rb +237 -0
- data/lib/functions_framework/cli.rb +113 -0
- data/lib/functions_framework/cloud_events.rb +143 -0
- data/lib/functions_framework/cloud_events/binary_content.rb +59 -0
- data/lib/functions_framework/cloud_events/content_type.rb +139 -0
- data/lib/functions_framework/cloud_events/event.rb +277 -0
- data/lib/functions_framework/cloud_events/json_structure.rb +88 -0
- data/lib/functions_framework/function.rb +75 -0
- data/lib/functions_framework/registry.rb +137 -0
- data/lib/functions_framework/server.rb +423 -0
- data/lib/functions_framework/testing.rb +244 -0
- data/lib/functions_framework/version.rb +21 -0
- metadata +187 -0
@@ -0,0 +1,237 @@
|
|
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
|
+
require "logger"
|
16
|
+
|
17
|
+
require "functions_framework/cloud_events"
|
18
|
+
require "functions_framework/function"
|
19
|
+
require "functions_framework/registry"
|
20
|
+
require "functions_framework/version"
|
21
|
+
|
22
|
+
##
|
23
|
+
# The Functions Framework for Ruby.
|
24
|
+
#
|
25
|
+
# Functions Framework is an open source framework for writing lightweight,
|
26
|
+
# portable Ruby functions that run in a serverless environment. For general
|
27
|
+
# information about the Functions Framework, see
|
28
|
+
# https://github.com/GoogleCloudPlatform/functions-framework.
|
29
|
+
# To get started with the functions framework for Ruby, see
|
30
|
+
# https://github.com/GoogleCloudPlatform/functions-framework-ruby for basic
|
31
|
+
# examples.
|
32
|
+
#
|
33
|
+
# ## Inside the FunctionsFramework module
|
34
|
+
#
|
35
|
+
# The FunctionsFramework module includes the main entry points for the
|
36
|
+
# functions framework. Use the {FunctionsFramework.http},
|
37
|
+
# {FunctionsFramework.event}, or {FunctionsFramework.cloud_event} methods to
|
38
|
+
# 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.
|
41
|
+
#
|
42
|
+
# ## Internal modules
|
43
|
+
#
|
44
|
+
# Here is a roadmap to the internal modules in the Ruby functions framework.
|
45
|
+
#
|
46
|
+
# * {FunctionsFramework::CloudEvents} provides an implementation of the
|
47
|
+
# [CloudEvents](https://cloudevents.io) specification. In particular, if
|
48
|
+
# you define an event function, you will receive the event as a
|
49
|
+
# {FunctionsFramework::CloudEvents::Event} object.
|
50
|
+
# * {FunctionsFramework::CLI} is the implementation of the
|
51
|
+
# `functions-framework` executable. Most apps will not need to interact
|
52
|
+
# with this class directly.
|
53
|
+
# * {FunctionsFramework::Function} is the internal representation of a
|
54
|
+
# function, indicating the type of function (http or cloud event), the
|
55
|
+
# name of the function, and the block of code implementing it. Most apps
|
56
|
+
# do not need to interact with this class directly.
|
57
|
+
# * {FunctionsFramework::Registry} looks up functions by name. When you
|
58
|
+
# define a set of named functions, they are added to a registry, and when
|
59
|
+
# you start a server and specify the target function by name, it is looked
|
60
|
+
# up from the registry. Most apps do not need to interact with this class
|
61
|
+
# directly.
|
62
|
+
# * {FunctionsFramework::Server} is a web server that makes a function
|
63
|
+
# available via HTTP. It wraps the Puma web server and runs a specific
|
64
|
+
# {FunctionsFramework::Function}. Many apps can simply run the
|
65
|
+
# `functions-framework` executable to spin up a server. However, if you
|
66
|
+
# need closer control over your execution environment, you can use the
|
67
|
+
# {FunctionsFramework::Server} class to run a server. Note that, in most
|
68
|
+
# cases, it is easier to use the {FunctionsFramework.start} or
|
69
|
+
# {FunctionsFramework.run} wrapper methods rather than instantiate a
|
70
|
+
# {FunctionsFramework::Server} class directly.
|
71
|
+
# * {FunctionsFramework::Testing} provides helpers that are useful when
|
72
|
+
# writing unit tests for functions.
|
73
|
+
#
|
74
|
+
module FunctionsFramework
|
75
|
+
@global_registry = Registry.new
|
76
|
+
@logger = ::Logger.new ::STDERR
|
77
|
+
@logger.level = ::Logger::INFO
|
78
|
+
|
79
|
+
##
|
80
|
+
# The default target function name. If you define a function without
|
81
|
+
# specifying a name, or run the framework without giving a target, this name
|
82
|
+
# is used.
|
83
|
+
#
|
84
|
+
# @return [String]
|
85
|
+
#
|
86
|
+
DEFAULT_TARGET = "function".freeze
|
87
|
+
|
88
|
+
##
|
89
|
+
# The default source file path. The CLI loads functions from this file if no
|
90
|
+
# source file is given explicitly.
|
91
|
+
#
|
92
|
+
# @return [String]
|
93
|
+
#
|
94
|
+
DEFAULT_SOURCE = "./app.rb".freeze
|
95
|
+
|
96
|
+
class << self
|
97
|
+
##
|
98
|
+
# The "global" registry that holds events defined by the
|
99
|
+
# {FunctionsFramework} class methods.
|
100
|
+
#
|
101
|
+
# @return [FunctionsFramework::Registry]
|
102
|
+
#
|
103
|
+
attr_accessor :global_registry
|
104
|
+
|
105
|
+
##
|
106
|
+
# A "global" logger that is used by the framework's web server, and can
|
107
|
+
# also be used by functions.
|
108
|
+
#
|
109
|
+
# @return [Logger]
|
110
|
+
#
|
111
|
+
attr_accessor :logger
|
112
|
+
|
113
|
+
##
|
114
|
+
# Define a function that response to HTTP requests.
|
115
|
+
#
|
116
|
+
# You must provide a name for the function, and a block that implemets the
|
117
|
+
# function. The block should take a single `Rack::Request` argument. It
|
118
|
+
# should return one of the following:
|
119
|
+
# * A standard 3-element Rack response array. See
|
120
|
+
# https://github.com/rack/rack/blob/master/SPEC
|
121
|
+
# * A `Rack::Response` object.
|
122
|
+
# * A simple String that will be sent as the response body.
|
123
|
+
# * A Hash object that will be encoded as JSON and sent as the response
|
124
|
+
# body.
|
125
|
+
#
|
126
|
+
# ## Example
|
127
|
+
#
|
128
|
+
# FunctionsFramework.http "my-function" do |request|
|
129
|
+
# "I received a request for #{request.url}"
|
130
|
+
# end
|
131
|
+
#
|
132
|
+
# @param name [String] The function name. Defaults to {DEFAULT_TARGET}.
|
133
|
+
# @param block [Proc] The function code as a proc.
|
134
|
+
# @return [self]
|
135
|
+
#
|
136
|
+
def http name = DEFAULT_TARGET, &block
|
137
|
+
global_registry.add_http name, &block
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
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
|
165
|
+
#
|
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]
|
173
|
+
#
|
174
|
+
def event name = DEFAULT_TARGET, &block
|
175
|
+
global_registry.add_event name, &block
|
176
|
+
self
|
177
|
+
end
|
178
|
+
|
179
|
+
##
|
180
|
+
# Define a function that responds to CloudEvents.
|
181
|
+
#
|
182
|
+
# 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
|
184
|
+
# {FunctionsFramework::CloudEvents::Event}. Any return value is ignored.
|
185
|
+
#
|
186
|
+
# See also {FunctionsFramework.event} which creates a function that takes
|
187
|
+
# data and context as separate arguments.
|
188
|
+
#
|
189
|
+
# ## Example
|
190
|
+
#
|
191
|
+
# FunctionsFramework.cloud_event "my-function" do |event|
|
192
|
+
# FunctionsFramework.logger.info "Event data: #{event.data.inspect}"
|
193
|
+
# end
|
194
|
+
#
|
195
|
+
# @param name [String] The function name. Defaults to {DEFAULT_TARGET}.
|
196
|
+
# @param block [Proc] The function code as a proc.
|
197
|
+
# @return [self]
|
198
|
+
#
|
199
|
+
def cloud_event name = DEFAULT_TARGET, &block
|
200
|
+
global_registry.add_cloud_event name, &block
|
201
|
+
self
|
202
|
+
end
|
203
|
+
|
204
|
+
##
|
205
|
+
# Start the functions framework server in the background. The server will
|
206
|
+
# look up the given target function name in the global registry.
|
207
|
+
#
|
208
|
+
# @param target [String] The name of the function to run
|
209
|
+
# @yield [FunctionsFramework::Server::Config] A config object that can be
|
210
|
+
# manipulated to configure the server.
|
211
|
+
# @return [FunctionsFramework::Server]
|
212
|
+
#
|
213
|
+
def start target, &block
|
214
|
+
require "functions_framework/server"
|
215
|
+
function = global_registry[target]
|
216
|
+
raise ::ArgumentError, "Undefined function: #{target.inspect}" if function.nil?
|
217
|
+
server = Server.new function, &block
|
218
|
+
server.respond_to_signals
|
219
|
+
server.start
|
220
|
+
end
|
221
|
+
|
222
|
+
##
|
223
|
+
# Run the functions framework server and block until it stops. The server
|
224
|
+
# will look up the given target function name in the global registry.
|
225
|
+
#
|
226
|
+
# @param target [String] The name of the function to run
|
227
|
+
# @yield [FunctionsFramework::Server::Config] A config object that can be
|
228
|
+
# manipulated to configure the server.
|
229
|
+
# @return [self]
|
230
|
+
#
|
231
|
+
def run target, &block
|
232
|
+
server = start target, &block
|
233
|
+
server.wait_until_stopped
|
234
|
+
self
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,113 @@
|
|
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
|
+
require "optparse"
|
16
|
+
|
17
|
+
require "functions_framework"
|
18
|
+
|
19
|
+
module FunctionsFramework
|
20
|
+
##
|
21
|
+
# Implementation of the functions-framework executable.
|
22
|
+
#
|
23
|
+
class CLI
|
24
|
+
##
|
25
|
+
# Create a new CLI, setting arguments to their defaults.
|
26
|
+
#
|
27
|
+
def initialize
|
28
|
+
@target = ::ENV["FUNCTION_TARGET"] || ::FunctionsFramework::DEFAULT_TARGET
|
29
|
+
@source = ::ENV["FUNCTION_SOURCE"] || ::FunctionsFramework::DEFAULT_SOURCE
|
30
|
+
@env = nil
|
31
|
+
@port = nil
|
32
|
+
@bind = nil
|
33
|
+
@min_threads = nil
|
34
|
+
@max_threads = nil
|
35
|
+
@detailed_errors = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Parse the given command line arguments.
|
40
|
+
# Exits if argument parsing failed.
|
41
|
+
#
|
42
|
+
# @param argv [Array<String>]
|
43
|
+
# @return [self]
|
44
|
+
#
|
45
|
+
def parse_args argv # rubocop:disable Metrics/MethodLength
|
46
|
+
option_parser = ::OptionParser.new do |op| # rubocop:disable Metrics/BlockLength
|
47
|
+
op.on "-t", "--target TARGET",
|
48
|
+
"Set the name of the function to execute (defaults to #{DEFAULT_TARGET})" do |val|
|
49
|
+
@target = val
|
50
|
+
end
|
51
|
+
op.on "-s", "--source SOURCE",
|
52
|
+
"Set the source file to load (defaults to #{DEFAULT_SOURCE})" do |val|
|
53
|
+
@source = val
|
54
|
+
end
|
55
|
+
op.on "-p", "--port PORT", "Set the port to listen to (defaults to 8080)" do |val|
|
56
|
+
@port = val.to_i
|
57
|
+
end
|
58
|
+
op.on "-b", "--bind BIND", "Set the address to bind to (defaults to 0.0.0.0)" do |val|
|
59
|
+
@bind = val
|
60
|
+
end
|
61
|
+
op.on "-e", "--environment ENV", "Set the Rack environment" do |val|
|
62
|
+
@env = val
|
63
|
+
end
|
64
|
+
op.on "--min-threads NUM", "Set the minimum threead pool size" do |val|
|
65
|
+
@min_threads = val
|
66
|
+
end
|
67
|
+
op.on "--max-threads NUM", "Set the maximum threead pool size" do |val|
|
68
|
+
@max_threads = val
|
69
|
+
end
|
70
|
+
op.on "--[no-]detailed-errors", "Set whether to show error details" do |val|
|
71
|
+
@detailed_errors = val
|
72
|
+
end
|
73
|
+
op.on "-v", "--verbose", "Increase log verbosity" do
|
74
|
+
::FunctionsFramework.logger.level -= 1
|
75
|
+
end
|
76
|
+
op.on "-q", "--quiet", "Decrease log verbosity" do
|
77
|
+
::FunctionsFramework.logger.level += 1
|
78
|
+
end
|
79
|
+
op.on "--help", "Display help" do
|
80
|
+
puts op
|
81
|
+
exit
|
82
|
+
end
|
83
|
+
end
|
84
|
+
option_parser.parse! argv
|
85
|
+
unless argv.empty?
|
86
|
+
warn "Unrecognized arguments: #{argv}"
|
87
|
+
puts op
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Run the configured server, and block until it stops.
|
95
|
+
# @return [self]
|
96
|
+
#
|
97
|
+
def run
|
98
|
+
FunctionsFramework.logger.info \
|
99
|
+
"FunctionsFramework: Loading functions from #{@source.inspect}..."
|
100
|
+
load @source
|
101
|
+
server = ::FunctionsFramework.start @target do |config|
|
102
|
+
config.rack_env = @env
|
103
|
+
config.port = @port
|
104
|
+
config.bind_addr = @bind
|
105
|
+
config.show_error_details = @detailed_errors
|
106
|
+
config.min_threads = @min_threads
|
107
|
+
config.max_threads = @max_threads
|
108
|
+
end
|
109
|
+
server.wait_until_stopped
|
110
|
+
self
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,143 @@
|
|
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
|
+
require "functions_framework/cloud_events/binary_content"
|
16
|
+
require "functions_framework/cloud_events/content_type"
|
17
|
+
require "functions_framework/cloud_events/event"
|
18
|
+
|
19
|
+
module FunctionsFramework
|
20
|
+
##
|
21
|
+
# CloudEvents implementation.
|
22
|
+
#
|
23
|
+
# This is a Ruby implementation of the [CloudEvents](https://cloudevents.io)
|
24
|
+
# [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
|
+
#
|
32
|
+
module CloudEvents
|
33
|
+
@structured_formats = {}
|
34
|
+
@batched_formats = {}
|
35
|
+
|
36
|
+
class << self
|
37
|
+
##
|
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.
|
59
|
+
#
|
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]
|
64
|
+
#
|
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}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
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
|