functions_framework 0.2.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 921150d16d50802b1fa0e5f4f6fb5d3c71aee1dd87e8242ce2e0b63dc8c6320b
4
- data.tar.gz: 9b2fd4967811fc1225319102a6aadb56497d90241f6678933843a88299ea926c
3
+ metadata.gz: 370556a72cd31e4d24499b0715ca7f96a0598cb856fb28fed3d3eaabee0a80ae
4
+ data.tar.gz: 04a24cf1e2f205fe124eb0bda891f647177b93c43ba1c2286ed52ffbad18cb1d
5
5
  SHA512:
6
- metadata.gz: 90ea5d1622b446eb02a84a79717c92b47fadc6b5420f153acd48d0b01716a44dd6dc6cbea6851f446fde70e5a53522569b95cde61c5807563dee5b0bc4a6b50c
7
- data.tar.gz: 0dd19692bfaa5566c4da5151d78c9d233b9241089d186d5461ef13e83529ca9de7f04e8736fbae70fd361a1fc2b605c03947d23867d28c2c9f80995d9cf60d06
6
+ metadata.gz: 34d91fa0417632a253ddba1db7aa85f54697eb645caaaf96c9253a36fca241bca80bf17a40796dd5c8521d2c7bbe04ff23353764491d28b5e4335e0ee022f05a
7
+ data.tar.gz: 0d8d59ef3e45d3980136d7015e8066052bffe3942c442f715368dcf6ac5dd98b0cb282c313fa1a387ac70514747701e75c879c549faa9802444fe6b4d56115c7
@@ -1,8 +1,40 @@
1
1
  # Changelog
2
2
 
3
+ ### v0.5.0 / 2020-07-09
4
+
5
+ * Removed embedded CloudEvents classes and added the official CloudEvents SDK as a dependency. A `FunctionsFramework::CloudEvents` alias provides backward compatibility.
6
+
7
+ ### v0.4.1 / 2020-07-08
8
+
9
+ * Fixed unsupported signal error on Windows.
10
+ * Fixed several edge case errors in legacy event conversion.
11
+ * Generated Content-Type headers now properly quote param values if needed.
12
+ * Minor documentation updates.
13
+
14
+ ### v0.4.0 / 2020-06-29
15
+
16
+ * Dropped the legacy and largely unsupported `:event` function type. All event functions should be of type `:cloud_event`.
17
+ * Define the object context for function execution, and include an extensible context helper.
18
+ * Support for CloudEvents with specversion 0.3.
19
+ * CloudEvents now correct percent-encodes/decodes binary headers.
20
+ * CloudEvents now includes more robust RFC 2045 parsing of the Content-Type header.
21
+ * The CloudEventsError class now properly subclasses StandardError instead of RuntimeError.
22
+ * Removed redundant `_string` accessors from event classes since raw forms are already available via `[]`.
23
+ * A variety of corrections to event-related class documentation.
24
+
25
+ ### v0.3.1 / 2020-06-27
26
+
27
+ * Fixed crash when using "return" directly in a function block.
28
+ * Added a more flexible request generation helper in the testing module.
29
+ * Fixed several typos in the documentation.
30
+
31
+ ### v0.3.0 / 2020-06-26
32
+
33
+ * Updated the CloudEvent data format for converted pubsub events to conform to Cloud Run's conversion.
34
+
3
35
  ### v0.2.1 / 2020-06-25
4
36
 
5
- * The `--signature-type` check recognizes the legacy `event` type.
37
+ * The `--signature-type` check recognizes the legacy `event` type for `:cloud_event` functions.
6
38
 
7
39
  ### v0.2.0 / 2020-06-24
8
40
 
data/README.md CHANGED
@@ -60,7 +60,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
60
60
  ```ruby
61
61
  # Gemfile
62
62
  source "https://rubygems.org"
63
- gem "functions_framework", "~> 0.2"
63
+ gem "functions_framework", "~> 0.5"
64
64
  ```
65
65
 
66
66
  Create a file called `app.rb` and include the following code. This defines a
@@ -88,7 +88,7 @@ bundle exec functions-framework-ruby --target hello
88
88
  In a separate shell, you can send requests to this function using curl:
89
89
 
90
90
  ```sh
91
- curl https://localhost:8080
91
+ curl http://localhost:8080
92
92
  # Output: Hello, world!
93
93
  ```
94
94
 
@@ -30,12 +30,21 @@ Google Cloud Functions is Google's scalable pay-as-you-go Functions-as-a-Service
30
30
  Functions Framework is designed especially for functions that can be hosted on
31
31
  Cloud Functions.
32
32
 
33
+ You can run Ruby functions on Google Cloud Functions by selecting the `ruby26`
34
+ runtime. This runtime uses a recent release of Ruby 2.6. Support for other
35
+ versions of Ruby may be added in the future.
36
+
37
+ > **Note:** Ruby support on Cloud Functions is currently in limited preview.
38
+ > It is not yet suitable for production workloads, and support is best-effort
39
+ > only. Access is currently limited to selected early-access users.
40
+
33
41
  ### Deploying and updating your function
34
42
 
35
43
  Before you can deploy to Cloud Functions, make sure your bundle, and in
36
44
  particular your `Gemfile.lock` file, is up to date. The easiest way to do this
37
45
  is to `bundle install` or `bundle update` and run your local tests prior to
38
- deploying.
46
+ deploying. Cloud Functions will not accept your function unless an up-to-date
47
+ `Gemfile.lock` is present.
39
48
 
40
49
  Choose a name for your function. This function name is how it will appear in the
41
50
  cloud console, and will also be part of the function's URL. (It's different from
@@ -96,6 +105,12 @@ to adapt it if you have an Anthos installation.
96
105
 
97
106
  ### Building an image for your function
98
107
 
108
+ Before you can deploy to Cloud Run, make sure your bundle, and in
109
+ particular your `Gemfile.lock` file, is up to date. The easiest way to do this
110
+ is to `bundle install` or `bundle update` and run your local tests prior to
111
+ deploying. The configuration used in the Dockerfile below will not accept your
112
+ function unless an up-to-date `Gemfile.lock` is present.
113
+
99
114
  First, build a Docker image containing your function. Following is a simple
100
115
  Dockerfile that you can use as a starting point. Feel free to adjust it to the
101
116
  needs of your project:
@@ -126,6 +141,10 @@ You must use your project ID, but you can choose an app name and build ID. The
126
141
  command may ask you for permission to enable the Cloud Build API for the project
127
142
  if it isn't already enabled.
128
143
 
144
+ Because you provide your own Docker image when deploying to Cloud Run, you can
145
+ use any version of Ruby supported by the Functions Framework, from 2.4 through
146
+ 2.7.
147
+
129
148
  ### Deploying an image to Cloud Run
130
149
 
131
150
  To deploy to Cloud Run, specify the same image URL that you built above. For
@@ -152,7 +171,7 @@ deployed function.
152
171
 
153
172
  Note that our Dockerfile's entrypoint did not pass any source file or target
154
173
  name to the Functions Framework. If these are not specified, the Framework will
155
- use the source `.app.rb` and the target `function` by default. To use different
174
+ use the source `./app.rb` and the target `function` by default. To use different
156
175
  values, you need to set the appropriate environment variables when deploying, as
157
176
  illustrated above with the `FUNCTION_SOURCE` and `FUNCTION_TARGET` variables.
158
177
 
@@ -64,7 +64,7 @@ Create a `Gemfile` listing the Functions Framework as a dependency:
64
64
  ```ruby
65
65
  # Gemfile
66
66
  source "https://rubygems.org"
67
- gem "functions_framework", "~> 0.2"
67
+ gem "functions_framework", "~> 0.5"
68
68
  ```
69
69
 
70
70
  Create a file called `app.rb` and include the following code. This defines a
@@ -92,7 +92,7 @@ bundle exec functions-framework-ruby --target hello
92
92
  In a separate shell, you can send requests to this function using curl:
93
93
 
94
94
  ```sh
95
- curl https://localhost:8080
95
+ curl http://localhost:8080
96
96
  # Output: Hello, world!
97
97
  ```
98
98
 
@@ -30,7 +30,7 @@ that returns a simple message in the HTTP response body:
30
30
  ```ruby
31
31
  require "functions_framework"
32
32
 
33
- FunctionsFramework.http("hello") do |request|
33
+ FunctionsFramework.http "hello" do |request|
34
34
  # Return the response body.
35
35
  "Hello, world!\n"
36
36
  end
@@ -43,7 +43,7 @@ now cover these in a bit more detail.
43
43
 
44
44
  An HTTP function is passed a request, which is an object of type
45
45
  [Rack::Request](https://rubydoc.info/gems/rack/Rack/Request). This object
46
- provides methods methods for obtaining request information such as the method,
46
+ provides methods for obtaining request information such as the method,
47
47
  path, query parameters, body content, and headers. You can also obtain the raw
48
48
  Rack environment using the `env` method. The following example includes some
49
49
  request information in the response:
@@ -51,7 +51,7 @@ request information in the response:
51
51
  ```ruby
52
52
  require "functions_framework"
53
53
 
54
- FunctionsFramework.http("request_info") do |request|
54
+ FunctionsFramework.http "request_info_example" do |request|
55
55
  # Include some request info in the response body.
56
56
  "Received #{request.method} from #{request.url}!\n"
57
57
  end
@@ -66,7 +66,7 @@ hosting environment.
66
66
  ```ruby
67
67
  require "functions_framework"
68
68
 
69
- FunctionsFramework.http("logging_example") do |request|
69
+ FunctionsFramework.http "logging_example" do |request|
70
70
  # Log some request info.
71
71
  request.logger.info "I received #{request.method} from #{request.url}!"
72
72
  # A simple response body.
@@ -106,10 +106,19 @@ framework such as Ruby on Rails, you may want to consider a solution such as
106
106
  Google Cloud Run that is tailored to larger applications. However, a lightweight
107
107
  framework such as Sinatra is sometimes useful when writing HTTP functions.
108
108
 
109
- It is easy to connect an HTTP function to a Sinatra app. Write the Sinatra app
110
- using the "modular" Sinatra interface (i.e. subclass `Sinatra::Base`), and then
111
- simply run the Sinatra app as a Rack handler from the function. Here is a basic
112
- example:
109
+ It is easy to connect an HTTP function to a Sinatra app. First, declare the
110
+ dependency on Sinatra in your `Gemfile`:
111
+
112
+ ```ruby
113
+ # Gemfile
114
+ source "https://rubygems.org"
115
+ gem "functions_framework", "~> 0.5"
116
+ gem "sinatra", "~> 2.0"
117
+ ```
118
+
119
+ Write the Sinatra app using the "modular" Sinatra interface (i.e. subclass
120
+ `Sinatra::Base`), and then run the Sinatra app directly as a Rack handler from
121
+ the function. Here is a basic example:
113
122
 
114
123
  ```ruby
115
124
  require "functions_framework"
@@ -143,20 +152,25 @@ information about it:
143
152
  ```ruby
144
153
  require "functions_framework"
145
154
 
146
- FunctionsFramework.cloud_event("hello") do |event|
155
+ FunctionsFramework.cloud_event "hello" do |event|
147
156
  FunctionsFramework.logger.info "I received an event of type #{event.type}!"
148
157
  end
149
158
  ```
150
159
 
151
- The event parameter is a
152
- [CloudEvents V1 Event](https://rubydoc.info/gems/functions_framework/FunctionsFramework/CloudEvents/Event/V1)
153
- object. You can find detailed information about the fields of a CloudEvent from
154
- the [CloudEvents spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md).
160
+ The event parameter will be either a
161
+ [CloudEvents V0.3 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V0)
162
+ object ([see spec](https://github.com/cloudevents/spec/blob/v0.3/spec.md)) or a
163
+ [CloudEvents V1.0 Event](https://rubydoc.info/gems/cloud_events/CloudEvents/Event/V1)
164
+ object ([see spec](https://github.com/cloudevents/spec/blob/v1.0/spec.md)).
155
165
 
156
166
  Some Google Cloud services send events in a legacy event format that was defined
157
167
  prior to CloudEvents. The Functions Framework will convert these legacy events
158
- to an equivalent CloudEvents type, so your function will always receive a
159
- CloudEvent when it is sent an event from Google Cloud.
168
+ to an equivalent CloudEvents V1 type, so your function will always receive a
169
+ CloudEvent object when it is sent an event from Google Cloud. The precise
170
+ mapping between legacy events and CloudEvents is not specified in detail here,
171
+ but in general, the _data_ from the legacy event will be mapped to the `data`
172
+ field in the CloudEvent, and the _context_ from the legacy event will be mapped
173
+ to equivalent CloudEvent attributes.
160
174
 
161
175
  ## Error handling
162
176
 
@@ -175,7 +189,7 @@ HTTP response yourself. For example:
175
189
  ```ruby
176
190
  require "functions_framework"
177
191
 
178
- FunctionsFramework.http("error_reporter") do |request|
192
+ FunctionsFramework.http "error_reporter" do |request|
179
193
  begin
180
194
  raise "whoops!"
181
195
  rescue RuntimeError => e
@@ -188,9 +202,10 @@ end
188
202
 
189
203
  A Functions Framework based "project" or "application" is a typical Ruby
190
204
  application. It should include a `Gemfile` that specifies the gem dependencies
191
- (including the `functions_framework` gem itself), and at least one Ruby source
192
- file that defines functions. It can also include additional Ruby files defining
193
- classes and methods that assist in the function implementation.
205
+ (including the `functions_framework` gem itself), and any other dependencies
206
+ needed by the function. It must include at least one Ruby source file that
207
+ defines functions, and can also include additional Ruby files defining classes
208
+ and methods that assist in the function implementation.
194
209
 
195
210
  The "entrypoint" to the project, also called the "source", is a Ruby file. It
196
211
  can define any number of functions (with distinct names), although it is often
@@ -221,7 +236,7 @@ A simple project might look like this:
221
236
  ```ruby
222
237
  # Gemfile
223
238
  source "https://rubygems.org"
224
- gem "functions_framework", "~> 0.2"
239
+ gem "functions_framework", "~> 0.5"
225
240
  ```
226
241
 
227
242
  ```ruby
@@ -229,7 +244,7 @@ gem "functions_framework", "~> 0.2"
229
244
  require "functions_framework"
230
245
  require_relative "lib/hello"
231
246
 
232
- FunctionsFramework.http("hello") do |request|
247
+ FunctionsFramework.http "hello" do |request|
233
248
  Hello.new(request).build_response
234
249
  end
235
250
  ```
@@ -237,7 +252,7 @@ end
237
252
  ```ruby
238
253
  # lib/hello.rb
239
254
  class Hello
240
- def initialize(request)
255
+ def initialize request
241
256
  @request = request
242
257
  end
243
258
 
@@ -14,7 +14,8 @@
14
14
 
15
15
  require "logger"
16
16
 
17
- require "functions_framework/cloud_events"
17
+ require "cloud_events"
18
+
18
19
  require "functions_framework/function"
19
20
  require "functions_framework/legacy_event_converter"
20
21
  require "functions_framework/registry"
@@ -44,10 +45,6 @@ require "functions_framework/version"
44
45
  #
45
46
  # Here is a roadmap to the internal modules in the Ruby functions framework.
46
47
  #
47
- # * {FunctionsFramework::CloudEvents} provides an implementation of the
48
- # [CloudEvents](https://cloudevents.io) specification. In particular, if
49
- # you define an event function, you will receive the event as a
50
- # {FunctionsFramework::CloudEvents::Event} object.
51
48
  # * {FunctionsFramework::CLI} is the implementation of the
52
49
  # `functions-framework-ruby` executable. Most apps will not need to interact
53
50
  # with this class directly.
@@ -94,6 +91,12 @@ module FunctionsFramework
94
91
  #
95
92
  DEFAULT_SOURCE = "./app.rb".freeze
96
93
 
94
+ ##
95
+ # The CloudEvents implementation was extracted to become the official
96
+ # CloudEvents SDK. This alias is left here for backward compatibility.
97
+ #
98
+ CloudEvents = ::CloudEvents
99
+
97
100
  class << self
98
101
  ##
99
102
  # The "global" registry that holds events defined by the
@@ -139,23 +142,13 @@ module FunctionsFramework
139
142
  self
140
143
  end
141
144
 
142
- ##
143
- # This is an obsolete interface that defines an event function taking two
144
- # arguments (data and context) rather than one.
145
- #
146
- # @deprecated Use {FunctionsFramework.cloud_event} instead.
147
- #
148
- def event name = DEFAULT_TARGET, &block
149
- global_registry.add_event name, &block
150
- self
151
- end
152
-
153
145
  ##
154
146
  # Define a function that responds to CloudEvents.
155
147
  #
156
148
  # You must provide a name for the function, and a block that implemets the
157
149
  # function. The block should take one argument: the event object of type
158
- # {FunctionsFramework::CloudEvents::Event}. Any return value is ignored.
150
+ # [`CloudEvents::Event`](https://rubydoc.info/gems/cloud_events/CloudEvents/Event).
151
+ # Any return value is ignored.
159
152
  #
160
153
  # ## Example
161
154
  #
@@ -16,21 +16,60 @@ module FunctionsFramework
16
16
  ##
17
17
  # Representation of a function.
18
18
  #
19
- # A function has a name, a type, and a code definition.
19
+ # A function has a name, a type, and an implementation.
20
+ #
21
+ # The implementation in general is an object that responds to the `call`
22
+ # method. For a function of type `:http`, the `call` method takes a single
23
+ # `Rack::Request` argument and returns one of various HTTP response types.
24
+ # See {FunctionsFramework::Registry.add_http}. For a function of type
25
+ # `:cloud_event`, the `call` method takes a single
26
+ # [CloudEvent](https://rubydoc.info/gems/cloud_events/CloudEvents/Event)
27
+ # argument, and does not return a value.
28
+ # See {FunctionsFramework::Registry.add_cloud_event}.
29
+ #
30
+ # If a callable object is provided directly, its `call` method is invoked for
31
+ # every function execution. Note that this means it may be called multiple
32
+ # times concurrently in separate threads.
33
+ #
34
+ # Alternately, the implementation may be provided as a class that should be
35
+ # instantiated to produce a callable object. If a class is provided, it should
36
+ # either subclass {FunctionsFramework::Function::CallBase} or respond to the
37
+ # same constructor interface, i.e. accepting arbitrary keyword arguments. A
38
+ # separate callable object will be instantiated from this class for every
39
+ # function invocation, so each instance will be used for only one invocation.
40
+ #
41
+ # Finally, an implementation can be provided as a block. If a block is
42
+ # provided, it will be recast as a `call` method in an anonymous subclass of
43
+ # {FunctionsFramework::Function::CallBase}. Thus, providing a block is really
44
+ # just syntactic sugar for providing a class. (This means, for example, that
45
+ # the `return` keyword will work within the block because it is treated as a
46
+ # method.)
20
47
  #
21
48
  class Function
22
49
  ##
23
50
  # Create a new function definition.
24
51
  #
25
52
  # @param name [String] The function name
26
- # @param type [Symbol] The type of function. Valid types are
27
- # `:http`, `:event`, and `:cloud_event`.
28
- # @param block [Proc] The function code as a proc
53
+ # @param type [Symbol] The type of function. Valid types are `:http` and
54
+ # `:cloud_event`.
55
+ # @param callable [Class,#call] A callable object or class.
56
+ # @param block [Proc] The function code as a block.
29
57
  #
30
- def initialize name, type, &block
58
+ def initialize name, type, callable = nil, &block
31
59
  @name = name
32
60
  @type = type
33
- @block = block
61
+ @callable = @callable_class = nil
62
+ if callable.respond_to? :call
63
+ @callable = callable
64
+ elsif callable.is_a? ::Class
65
+ @callable_class = callable
66
+ elsif block_given?
67
+ @callable_class = ::Class.new CallBase do
68
+ define_method :call, &block
69
+ end
70
+ else
71
+ raise ::ArgumentError, "No callable given for function"
72
+ end
34
73
  end
35
74
 
36
75
  ##
@@ -44,30 +83,48 @@ module FunctionsFramework
44
83
  attr_reader :type
45
84
 
46
85
  ##
47
- # @return [Proc] The function code as a proc
86
+ # Get a callable for performing a function invocation. This will either
87
+ # return the singleton callable object, or instantiate a new callable from
88
+ # the configured class.
48
89
  #
49
- attr_reader :block
90
+ # @param logger [::Logger] The logger for use by function executions. This
91
+ # may or may not be used by the callable.
92
+ # @return [#call]
93
+ #
94
+ def new_call logger: nil
95
+ return @callable unless @callable.nil?
96
+ logger ||= FunctionsFramework.logger
97
+ @callable_class.new logger: logger, function_name: name, function_type: type
98
+ end
50
99
 
51
100
  ##
52
- # Call the function. You must pass an argument appropriate to the type
53
- # of function.
54
- #
55
- # * A `:http` type function takes a `Rack::Request` argument, and returns
56
- # a Rack response type. See {FunctionsFramework::Registry.add_http}.
57
- # * A `:cloud_event` type function takes a
58
- # {FunctionsFramework::CloudEvents::Event} argument, and does not
59
- # return a value. See {FunctionsFramework::Registry.add_cloud_event}.
101
+ # A base class for a callable object that provides calling context.
60
102
  #
61
- # @param argument [Rack::Request,FunctionsFramework::CloudEvents::Event]
62
- # @return [Object]
103
+ # An object of this class is `self` while a function block is running.
63
104
  #
64
- def call argument
65
- case type
66
- when :event
67
- block.call argument.data, argument
68
- else
69
- block.call argument
105
+ class CallBase
106
+ ##
107
+ # Create a callable object with the given context.
108
+ #
109
+ # @param context [keywords] A set of context arguments. See {#context} for
110
+ # a list of keys that will generally be passed in. However,
111
+ # implementations should be prepared to accept any abritrary keys.
112
+ #
113
+ def initialize **context
114
+ @context = context
70
115
  end
116
+
117
+ ##
118
+ # A keyed hash of context information. Common context keys include:
119
+ #
120
+ # * **:logger** (`Logger`) A logger for use by this function call.
121
+ # * **:function_name** (`String`) The name of the running function.
122
+ # * **:function_type** (`Symbol`) The type of the running function,
123
+ # either `:http` or `:cloud_event`.
124
+ #
125
+ # @return [Hash]
126
+ #
127
+ attr_reader :context
71
128
  end
72
129
  end
73
130
  end