diff --git a/dartfn/templates/cloudevent/README.md b/dartfn/templates/cloudevent/README.md index b66830a6..61afdaac 100644 --- a/dartfn/templates/cloudevent/README.md +++ b/dartfn/templates/cloudevent/README.md @@ -8,17 +8,7 @@ producer. They generally perform some work and print output for logging. The basic shape of the function handler looks like this: ```dart -@CloudFunction() -void function(CloudEvent event, RequestContext context) { -} -``` - -Or like this if it needs to perform work that will complete sometime in the -future: - -```dart -@CloudFunction() -FutureOr function(CloudEvent event, RequestContext context) async { +Future function(CloudEvent event, RequestContext context) async { } ``` @@ -34,8 +24,7 @@ import 'package:functions_framework/functions_framework.dart'; const _encoder = JsonEncoder.withIndent(' '); -@CloudFunction() -void function(CloudEvent event, RequestContext context) { +Future function(CloudEvent event, RequestContext context) async { context.logger.info('event subject: ${event.subject}'); stderr.writeln(_encoder.convert(event)); } diff --git a/dartfn/templates/cloudevent/bin/server.dart b/dartfn/templates/cloudevent/bin/server.dart index c4a1d523..697d18c1 100644 --- a/dartfn/templates/cloudevent/bin/server.dart +++ b/dartfn/templates/cloudevent/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,8 +20,6 @@ Future main(List args) async { } FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => FunctionTarget.cloudEventWithContext( - function_library.function, - ), - _ => null - }; + 'function' => FunctionTarget.cloudEventWithContext(function_library.function), + _ => null, +}; diff --git a/dartfn/templates/cloudevent/lib/functions.dart b/dartfn/templates/cloudevent/lib/functions.dart index 7dcecade..fc7c60b1 100644 --- a/dartfn/templates/cloudevent/lib/functions.dart +++ b/dartfn/templates/cloudevent/lib/functions.dart @@ -5,16 +5,11 @@ import 'package:functions_framework/functions_framework.dart'; const _encoder = JsonEncoder(); -@CloudFunction() -void function(CloudEvent event, RequestContext context) { - context.logger - .info('[CloudEvent] source: ${event.source}, subject: ${event.subject}'); +Future function(CloudEvent event, RequestContext context) async { + context.logger.info( + '[CloudEvent] source: ${event.source}, subject: ${event.subject}', + ); stderr.writeln( - _encoder.convert( - { - 'message': event, - 'severity': LogSeverity.info, - }, - ), + _encoder.convert({'message': event, 'severity': LogSeverity.info}), ); } diff --git a/dartfn/templates/cloudevent/pubspec.yaml b/dartfn/templates/cloudevent/pubspec.yaml index dd66aa0f..9f3793e1 100644 --- a/dartfn/templates/cloudevent/pubspec.yaml +++ b/dartfn/templates/cloudevent/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: dev_dependencies: build_runner: ^2.0.0 - functions_framework_builder: ^0.4.1 http: ^1.0.0 dart_flutter_team_lints: ^3.0.0 test: ^1.16.6 diff --git a/dartfn/templates/helloworld/README.md b/dartfn/templates/helloworld/README.md index ad1ee364..6acecc28 100644 --- a/dartfn/templates/helloworld/README.md +++ b/dartfn/templates/helloworld/README.md @@ -4,11 +4,9 @@ This example handles HTTP GET requests by responding with 'Hello, World!'. ```dart // lib/functions.dart -import 'package:functions_framework/functions_framework.dart'; import 'package:shelf/shelf.dart'; -@CloudFunction() -Response function(Request request) => Response.ok('Hello, World!'); +Future function(Request request) async => Response.ok('Hello, World!'); ``` ## Simulate a hosted environment on your own machine @@ -48,8 +46,7 @@ variable is set to the new function name. For example: ```dart -@CloudFunction() -Response handleGet(Request request) => Response.ok('Hello, World!'); +Future handleGet(Request request) async => Response.ok('Hello, World!'); ``` Run the `build_runner` to regenerate `bin/server.dart` from `lib/functions.dart` diff --git a/dartfn/templates/helloworld/bin/server.dart b/dartfn/templates/helloworld/bin/server.dart index 70da8045..cd6121ca 100644 --- a/dartfn/templates/helloworld/bin/server.dart +++ b/dartfn/templates/helloworld/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,8 +20,6 @@ Future main(List args) async { } FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => FunctionTarget.http( - function_library.function, - ), - _ => null - }; + 'function' => FunctionTarget.http(function_library.function), + _ => null, +}; diff --git a/dartfn/templates/helloworld/lib/functions.dart b/dartfn/templates/helloworld/lib/functions.dart index 8bef26b2..fc045784 100644 --- a/dartfn/templates/helloworld/lib/functions.dart +++ b/dartfn/templates/helloworld/lib/functions.dart @@ -1,5 +1,4 @@ -import 'package:functions_framework/functions_framework.dart'; import 'package:shelf/shelf.dart'; -@CloudFunction() -Response function(Request request) => Response.ok('Hello, World!'); +Future function(Request request) async => + Response.ok('Hello, World!'); diff --git a/dartfn/templates/helloworld/pubspec.yaml b/dartfn/templates/helloworld/pubspec.yaml index 97b61139..3f281974 100644 --- a/dartfn/templates/helloworld/pubspec.yaml +++ b/dartfn/templates/helloworld/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: dev_dependencies: build_runner: ^2.0.0 - functions_framework_builder: ^0.4.1 http: ^1.0.0 dart_flutter_team_lints: ^3.0.0 test: ^1.16.6 diff --git a/dartfn/templates/json/README.md b/dartfn/templates/json/README.md index 2358623a..430c0ca7 100644 --- a/dartfn/templates/json/README.md +++ b/dartfn/templates/json/README.md @@ -5,8 +5,7 @@ This example demonstrates writing a function that accepts and returns JSON. The basic shape of the function handler looks like this: ```dart -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { } ``` @@ -34,21 +33,19 @@ The Functions Framework parses the request data to fit the shape of a the request, the function implementation provides a default name: `World`. ```dart -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { final name = request.name ?? 'World'; } ``` Finally, the function creates an object returns it. The Functions Framework will take this and attempt "do the right" thing. In this case, the function is typed -to return a `FutureOr`, which has a `toJson()` method, so the +to return a `Future`, which has a `toJson()` method, so the framework will invoke this, then set the response body to the stringified result and set the response header (`content-type`) to `application/json`). ```dart -@CloudFunction() -GreetingResponse function(Map request) { +Future function(Map request) async { final name = request['name'] as String ?? 'World'; final json = GreetingResponse(salutation: 'Hello', name: name); return json; @@ -94,8 +91,7 @@ class GreetingResponse { // operator, and therefore also `hashCode`. } -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { final name = request.name ?? 'World'; final json = GreetingResponse(salutation: 'Hello', name: name); return json; diff --git a/dartfn/templates/json/bin/server.dart b/dartfn/templates/json/bin/server.dart index d53f500b..9faffebf 100644 --- a/dartfn/templates/json/bin/server.dart +++ b/dartfn/templates/json/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,27 +20,24 @@ Future main(List args) async { } FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => JsonFunctionTarget( - function_library.function, - (json) { - if (json is Map) { - try { - return function_library.GreetingRequest.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ), - _ => null - }; + 'function' => JsonFunctionTarget(function_library.function, (json) { + if (json is Map) { + try { + return function_library.GreetingRequest.fromJson(json); + } catch (e, stack) { + throw BadRequestException( + 400, + 'There was an error parsing the provided JSON data.', + innerError: e, + innerStack: stack, + ); + } + } + throw BadRequestException( + 400, + 'The provided JSON is not the expected type ' + '`Map`.', + ); + }), + _ => null, +}; diff --git a/dartfn/templates/json/lib/functions.dart b/dartfn/templates/json/lib/functions.dart index 4213df48..ab95241f 100644 --- a/dartfn/templates/json/lib/functions.dart +++ b/dartfn/templates/json/lib/functions.dart @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:functions_framework/functions_framework.dart'; import 'package:json_annotation/json_annotation.dart'; part 'functions.g.dart'; @@ -58,8 +57,7 @@ class GreetingResponse { int get hashCode => salutation.hashCode ^ name.hashCode; } -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { final name = request.name ?? 'World'; final json = GreetingResponse(salutation: 'Hello', name: name); return json; diff --git a/dartfn/templates/json/pubspec.yaml b/dartfn/templates/json/pubspec.yaml index 207d3473..7fb22715 100644 --- a/dartfn/templates/json/pubspec.yaml +++ b/dartfn/templates/json/pubspec.yaml @@ -14,7 +14,6 @@ dependencies: dev_dependencies: build_runner: ^2.2.1 - functions_framework_builder: ^0.4.7 http: ^1.0.0 json_serializable: ^6.6.0 dart_flutter_team_lints: ^3.0.0 diff --git a/docs/01-introduction.md b/docs/01-introduction.md index c0ea733b..1728de27 100644 --- a/docs/01-introduction.md +++ b/docs/01-introduction.md @@ -35,11 +35,9 @@ Here is a simple example of how to write a function that responds to HTTP request events with a "Hello, World!" greeting: ```dart -import 'package:functions_framework/functions_framework.dart'; import 'package:shelf/shelf.dart'; -@CloudFunction() -Response function(Request request) => Response.ok('Hello, World!'); +Future function(Request request) async => Response.ok('Hello, World!'); ``` If you deploy this simple example to [Cloud Run], you can easily choose to make diff --git a/docs/quickstarts/01-quickstart-dart.md b/docs/quickstarts/01-quickstart-dart.md index c9f7e984..fbfc1caa 100644 --- a/docs/quickstarts/01-quickstart-dart.md +++ b/docs/quickstarts/01-quickstart-dart.md @@ -126,8 +126,7 @@ the `FUNCTION_TARGET` environment variable is set to the new function name. For example: ```dart -@CloudFunction() -Response handleGet(Request request) => Response.ok('Hello, World!'); +Future handleGet(Request request) async => Response.ok('Hello, World!'); ``` Run `build_runner` to regenerate `bin/server.dart` from `lib/functions.dart` diff --git a/examples/fullstack/backend/README.md b/examples/fullstack/backend/README.md index 41a9f4c3..4d367f8b 100644 --- a/examples/fullstack/backend/README.md +++ b/examples/fullstack/backend/README.md @@ -8,8 +8,7 @@ The backend demonstrates writing a function that accepts and returns JSON. The function handler looks like this: ```dart -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { final name = request.name ?? 'World'; final json = GreetingResponse(salutation: getSalutation(), name: name); return json; diff --git a/examples/fullstack/backend/bin/server.dart b/examples/fullstack/backend/bin/server.dart index 090451b9..8609cfe9 100644 --- a/examples/fullstack/backend/bin/server.dart +++ b/examples/fullstack/backend/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,31 +16,10 @@ import 'package:backend/functions.dart' as function_library; import 'package:functions_framework/serve.dart'; Future main(List args) async { - await serve(args, _nameToFunctionTarget); + await serve(args, { + 'function': FunctionTarget.jsonable( + function_library.greeting, + function_library.GreetingRequest.fromJson, + ), + }); } - -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => JsonWithContextFunctionTarget( - function_library.function, - (json) { - if (json is Map) { - try { - return function_library.GreetingRequest.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ), - _ => null - }; diff --git a/examples/fullstack/backend/lib/functions.dart b/examples/fullstack/backend/lib/functions.dart index c3f05f5f..69e3ee03 100644 --- a/examples/fullstack/backend/lib/functions.dart +++ b/examples/fullstack/backend/lib/functions.dart @@ -21,11 +21,15 @@ import 'api_types.dart'; // Export api_types so builder can use them when generating `bin/server.dart`. export 'api_types.dart'; -@CloudFunction() -GreetingResponse function(GreetingRequest request, RequestContext context) { +Future greeting( + GreetingRequest request, + RequestContext context, +) async { final name = request.name ?? 'World'; - final response = - GreetingResponse(salutation: _randomSalutation(), name: name); + final response = GreetingResponse( + salutation: _randomSalutation(), + name: name, + ); context.logger.info('greetingResponse: ${response.toJson()}'); return response; } diff --git a/examples/fullstack/backend/pubspec.yaml b/examples/fullstack/backend/pubspec.yaml index 4a0eb53b..bd084428 100644 --- a/examples/fullstack/backend/pubspec.yaml +++ b/examples/fullstack/backend/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: dev_dependencies: build_runner: ^2.2.1 dart_flutter_team_lints: ^3.0.0 - functions_framework_builder: ^0.4.7 http: ^1.0.0 json_serializable: ^6.6.0 test: ^1.21.6 diff --git a/examples/hello/Makefile b/examples/hello/Makefile index a38b5e45..5d75b4e1 100644 --- a/examples/hello/Makefile +++ b/examples/hello/Makefile @@ -3,18 +3,8 @@ FUNCTION_TARGET = function PORT = 8080 -# bin/server.dart is the generated target for lib/functions.dart -bin/server.dart: - dart run build_runner build --delete-conflicting-outputs - -build: bin/server.dart - -test: clean build +test: dart test -clean: - dart run build_runner clean - rm -rf bin/server.dart - -run: build +run: dart run bin/server.dart --port=$(PORT) --target=$(FUNCTION_TARGET) diff --git a/examples/hello/README.md b/examples/hello/README.md index 1d5b0a26..e2cd5daa 100644 --- a/examples/hello/README.md +++ b/examples/hello/README.md @@ -4,10 +4,8 @@ This example handles HTTP GET requests by responding with 'Hello, World!'. ```dart // lib/functions.dart -import 'package:functions_framework/functions_framework.dart'; import 'package:shelf/shelf.dart'; -@CloudFunction() Response function(Request request) => Response.ok('Hello, World!'); ``` @@ -48,7 +46,6 @@ variable is set to the new function name. For example: ```dart -@CloudFunction() Response handleGet(Request request) => Response.ok('Hello, World!'); ``` diff --git a/examples/hello/bin/server.dart b/examples/hello/bin/server.dart index a0f3390b..bdc4b7c3 100644 --- a/examples/hello/bin/server.dart +++ b/examples/hello/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,12 +16,7 @@ import 'package:functions_framework/serve.dart'; import 'package:hello_world_function/functions.dart' as function_library; Future main(List args) async { - await serve(args, _nameToFunctionTarget); + await serve(args, { + 'function': FunctionTarget.http(function_library.function), + }); } - -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => FunctionTarget.http( - function_library.function, - ), - _ => null - }; diff --git a/examples/hello/lib/functions.dart b/examples/hello/lib/functions.dart index 4e6dfbd7..655d0ed8 100644 --- a/examples/hello/lib/functions.dart +++ b/examples/hello/lib/functions.dart @@ -12,14 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:functions_framework/functions_framework.dart'; import 'package:shelf/shelf.dart'; -@CloudFunction() -Response function(Request request) => Response.ok('Hello, World!'); - -// Overriding the default 'function' also works, but you will need -// to ensure to set the FUNCTION_TARGET environment variable for the -// process to 'handleGet' as well. -//@CloudFunction() -//Response handleGet(Request request) => Response.ok('Hello, World!'); +Future function(Request request) async => + Response.ok('Hello, World!'); diff --git a/examples/hello/pubspec.yaml b/examples/hello/pubspec.yaml index 43f7d21f..566b23f6 100644 --- a/examples/hello/pubspec.yaml +++ b/examples/hello/pubspec.yaml @@ -15,7 +15,6 @@ dependencies: dev_dependencies: build_runner: ^2.0.0 dart_flutter_team_lints: ^3.0.0 - functions_framework_builder: ^0.4.1 http: ^1.0.0 test: ^1.16.6 test_process: ^2.0.0 diff --git a/examples/json/Makefile b/examples/json/Makefile index a38b5e45..5d75b4e1 100644 --- a/examples/json/Makefile +++ b/examples/json/Makefile @@ -3,18 +3,8 @@ FUNCTION_TARGET = function PORT = 8080 -# bin/server.dart is the generated target for lib/functions.dart -bin/server.dart: - dart run build_runner build --delete-conflicting-outputs - -build: bin/server.dart - -test: clean build +test: dart test -clean: - dart run build_runner clean - rm -rf bin/server.dart - -run: build +run: dart run bin/server.dart --port=$(PORT) --target=$(FUNCTION_TARGET) diff --git a/examples/json/README.md b/examples/json/README.md index 140a0dc0..71ccf876 100644 --- a/examples/json/README.md +++ b/examples/json/README.md @@ -5,8 +5,7 @@ This example demonstrates writing a function that accepts and returns JSON. The basic shape of the function handler looks like this: ```dart -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { } ``` @@ -34,8 +33,7 @@ The Functions Framework parses the request data to fit the shape of a the request, the function implementation provides a default name: `World`. ```dart -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { final name = request.name ?? 'World'; } ``` @@ -47,8 +45,7 @@ framework will invoke this, then set the response body to the stringified result and set the response header (`content-type`) to `application/json`). ```dart -@CloudFunction() -GreetingResponse function(Map request) { +Future function(Map request) async { final name = request['name'] as String ?? 'World'; final json = GreetingResponse(salutation: 'Hello', name: name); return json; @@ -94,8 +91,7 @@ class GreetingResponse { // operator, and therefore also `hashCode`. } -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future function(GreetingRequest request) async { final name = request.name ?? 'World'; final json = GreetingResponse(salutation: 'Hello', name: name); return json; @@ -104,11 +100,8 @@ GreetingResponse function(GreetingRequest request) { ## Generate project files -The Dart `build_runner` tool generates the following files - -- `lib/functions.g.dart` - the part file for `GreetingResponse` serialization -- `bin/server.dart` - the main entry point for the function server app, which - invokes the function +The Dart `build_runner` tool generates `lib/functions.g.dart` - the part file +for `GreetingResponse` serialization Run the `build_runner` tool, as shown here: diff --git a/examples/json/bin/server.dart b/examples/json/bin/server.dart index 36062b9a..b7357661 100644 --- a/examples/json/bin/server.dart +++ b/examples/json/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,31 +16,10 @@ import 'package:example_json_function/functions.dart' as function_library; import 'package:functions_framework/serve.dart'; Future main(List args) async { - await serve(args, _nameToFunctionTarget); + await serve(args, { + 'function': FunctionTarget.jsonable( + function_library.greeter, + function_library.GreetingRequest.fromJson, + ), + }); } - -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => JsonFunctionTarget( - function_library.function, - (json) { - if (json is Map) { - try { - return function_library.GreetingRequest.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ), - _ => null - }; diff --git a/examples/json/lib/functions.dart b/examples/json/lib/functions.dart index 4213df48..f283ede8 100644 --- a/examples/json/lib/functions.dart +++ b/examples/json/lib/functions.dart @@ -58,8 +58,10 @@ class GreetingResponse { int get hashCode => salutation.hashCode ^ name.hashCode; } -@CloudFunction() -GreetingResponse function(GreetingRequest request) { +Future greeter( + GreetingRequest request, + RequestContext _, +) async { final name = request.name ?? 'World'; final json = GreetingResponse(salutation: 'Hello', name: name); return json; diff --git a/examples/json/lib/functions.g.dart b/examples/json/lib/functions.g.dart index cfae8cc9..3046a216 100644 --- a/examples/json/lib/functions.g.dart +++ b/examples/json/lib/functions.g.dart @@ -1,5 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - part of 'functions.dart'; // ************************************************************************** diff --git a/examples/json/pubspec.yaml b/examples/json/pubspec.yaml index 6eb0cb10..d8e2f1ad 100644 --- a/examples/json/pubspec.yaml +++ b/examples/json/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: dev_dependencies: build_runner: ^2.2.1 dart_flutter_team_lints: ^3.0.0 - functions_framework_builder: ^0.4.7 http: ^1.0.0 json_serializable: ^6.6.0 test: ^1.21.6 diff --git a/examples/protobuf_firestore/README.md b/examples/protobuf_firestore/README.md index b66830a6..1830fa02 100644 --- a/examples/protobuf_firestore/README.md +++ b/examples/protobuf_firestore/README.md @@ -8,8 +8,7 @@ producer. They generally perform some work and print output for logging. The basic shape of the function handler looks like this: ```dart -@CloudFunction() -void function(CloudEvent event, RequestContext context) { +Future function(CloudEvent event, RequestContext context) async { } ``` @@ -17,8 +16,7 @@ Or like this if it needs to perform work that will complete sometime in the future: ```dart -@CloudFunction() -FutureOr function(CloudEvent event, RequestContext context) async { +Future function(CloudEvent event, RequestContext context) async { } ``` @@ -34,7 +32,6 @@ import 'package:functions_framework/functions_framework.dart'; const _encoder = JsonEncoder.withIndent(' '); -@CloudFunction() void function(CloudEvent event, RequestContext context) { context.logger.info('event subject: ${event.subject}'); stderr.writeln(_encoder.convert(event)); @@ -45,23 +42,6 @@ All the function does is log the source and subject of the CloudEvent that triggered it, and then prints out the entire event JSON object for informational purposes. -## Generate project files - -The Dart `build_runner` tool generates `bin/server.dart`, the main entry point -for the function server app, which invokes the function in `lib/functions.dart`. - -Run the `build_runner` tool, as shown here: - -```shell -$ dart run build_runner build -[INFO] Generating build script completed, took 337ms -[INFO] Reading cached asset graph completed, took 48ms -[INFO] Checking for updates since last build completed, took 426ms -[INFO] Running build completed, took 13ms -[INFO] Caching finalized dependency graph completed, took 29ms -[INFO] Succeeded after 51ms with 0 outputs (0 actions) -``` - ## Test the function ```shell diff --git a/examples/protobuf_firestore/bin/server.dart b/examples/protobuf_firestore/bin/server.dart index b333b468..b8fe2550 100644 --- a/examples/protobuf_firestore/bin/server.dart +++ b/examples/protobuf_firestore/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,12 +16,7 @@ import 'package:example_protobuf_firestore/functions.dart' as function_library; import 'package:functions_framework/serve.dart'; Future main(List args) async { - await serve(args, _nameToFunctionTarget); + await serve(args, { + 'function': FunctionTarget.cloudEvent(function_library.function), + }); } - -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => FunctionTarget.cloudEventWithContext( - function_library.function, - ), - _ => null - }; diff --git a/examples/protobuf_firestore/lib/functions.dart b/examples/protobuf_firestore/lib/functions.dart index 08ca9f57..ed3ff7b7 100644 --- a/examples/protobuf_firestore/lib/functions.dart +++ b/examples/protobuf_firestore/lib/functions.dart @@ -19,12 +19,11 @@ import 'package:functions_framework/functions_framework.dart'; import 'src/function_types.dart'; -@CloudFunction() -void function(CloudEvent event, RequestContext context) { +Future function(CloudEvent event, RequestContext context) async { context.logger.info('event subject: ${event.subject}'); context.logger.debug(jsonEncode(context.request.headers)); - context.responseHeaders['x-data-runtime-types'] = - event.data.runtimeType.toString(); + context.responseHeaders['x-data-runtime-types'] = event.data.runtimeType + .toString(); final instance = DocumentEventData.fromBuffer(event.data as List); stderr.writeln(jsonEncode(instance.toProto3Json())); diff --git a/examples/protobuf_firestore/pubspec.yaml b/examples/protobuf_firestore/pubspec.yaml index a19ab3fc..e5073b97 100644 --- a/examples/protobuf_firestore/pubspec.yaml +++ b/examples/protobuf_firestore/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: dev_dependencies: build_runner: ^2.2.1 dart_flutter_team_lints: ^3.0.0 - functions_framework_builder: ^0.4.10 http: ^1.0.0 test: ^1.21.6 test_process: ^2.0.0 diff --git a/examples/protobuf_firestore/test/function_test.dart b/examples/protobuf_firestore/test/function_test.dart index ce2e816f..a1991d7e 100644 --- a/examples/protobuf_firestore/test/function_test.dart +++ b/examples/protobuf_firestore/test/function_test.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -@Timeout(Duration(seconds: 3)) +@Timeout(Duration(seconds: 5)) library; import 'dart:convert'; diff --git a/examples/raw_cloudevent/README.md b/examples/raw_cloudevent/README.md index b66830a6..eb83259e 100644 --- a/examples/raw_cloudevent/README.md +++ b/examples/raw_cloudevent/README.md @@ -8,8 +8,7 @@ producer. They generally perform some work and print output for logging. The basic shape of the function handler looks like this: ```dart -@CloudFunction() -void function(CloudEvent event, RequestContext context) { +Future function(CloudEvent event, RequestContext context) async { } ``` @@ -17,8 +16,7 @@ Or like this if it needs to perform work that will complete sometime in the future: ```dart -@CloudFunction() -FutureOr function(CloudEvent event, RequestContext context) async { +Future function(CloudEvent event, RequestContext context) async { } ``` @@ -34,8 +32,7 @@ import 'package:functions_framework/functions_framework.dart'; const _encoder = JsonEncoder.withIndent(' '); -@CloudFunction() -void function(CloudEvent event, RequestContext context) { +Future function(CloudEvent event, RequestContext context) async { context.logger.info('event subject: ${event.subject}'); stderr.writeln(_encoder.convert(event)); } diff --git a/examples/raw_cloudevent/bin/server.dart b/examples/raw_cloudevent/bin/server.dart index ab6a3a78..7886b909 100644 --- a/examples/raw_cloudevent/bin/server.dart +++ b/examples/raw_cloudevent/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +17,7 @@ import 'package:example_raw_cloudevent_function/functions.dart' import 'package:functions_framework/serve.dart'; Future main(List args) async { - await serve(args, _nameToFunctionTarget); + await serve(args, { + 'function': FunctionTarget.cloudEvent(function_library.function), + }); } - -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => FunctionTarget.cloudEventWithContext( - function_library.function, - ), - _ => null - }; diff --git a/examples/raw_cloudevent/lib/functions.dart b/examples/raw_cloudevent/lib/functions.dart index 1fd90b03..38030bb1 100644 --- a/examples/raw_cloudevent/lib/functions.dart +++ b/examples/raw_cloudevent/lib/functions.dart @@ -19,9 +19,9 @@ import 'package:functions_framework/functions_framework.dart'; const _encoder = JsonEncoder.withIndent(' '); -@CloudFunction() -void function(CloudEvent event, RequestContext context) { - context.logger - .info('[CloudEvent] source: ${event.source}, subject: ${event.subject}'); +Future function(CloudEvent event, RequestContext context) async { + context.logger.info( + '[CloudEvent] source: ${event.source}, subject: ${event.subject}', + ); stderr.writeln(_encoder.convert(event)); } diff --git a/examples/raw_cloudevent/pubspec.yaml b/examples/raw_cloudevent/pubspec.yaml index a1772395..316ae61b 100644 --- a/examples/raw_cloudevent/pubspec.yaml +++ b/examples/raw_cloudevent/pubspec.yaml @@ -12,7 +12,6 @@ dependencies: dev_dependencies: build_runner: ^2.0.0 dart_flutter_team_lints: ^3.0.0 - functions_framework_builder: ^0.4.1 http: ^1.0.0 test: ^1.16.6 test_process: ^2.0.0 diff --git a/functions_framework/README.md b/functions_framework/README.md index abc75afd..10445086 100644 --- a/functions_framework/README.md +++ b/functions_framework/README.md @@ -31,11 +31,9 @@ The framework allows you to go from: [examples/hello/lib/functions.dart] ```dart -import 'package:functions_framework/functions_framework.dart'; import 'package:shelf/shelf.dart'; -@CloudFunction() -Response function(Request request) => Response.ok('Hello, World!'); +Future function(Request request) async => Response.ok('Hello, World!'); ``` To: diff --git a/functions_framework/lib/functions_framework.dart b/functions_framework/lib/functions_framework.dart index 7e796368..a1832723 100644 --- a/functions_framework/lib/functions_framework.dart +++ b/functions_framework/lib/functions_framework.dart @@ -16,11 +16,10 @@ /// /// ```dart /// // lib/functions.dart -/// import 'package:functions_framework/functions_framework.dart'; /// import 'package:shelf/shelf.dart'; /// -/// @CloudFunction() -/// Response function(Request request) => Response.ok('Hello, World!'); +/// Future function(Request request) async +/// => Response.ok('Hello, World!'); /// ``` library; @@ -28,12 +27,6 @@ export 'package:google_cloud/google_cloud.dart' show BadRequestException, LogSeverity, RequestLogger; export 'src/cloud_event.dart' show CloudEvent; -export 'src/cloud_function.dart' show CloudFunction; export 'src/request_context.dart' show RequestContext; export 'src/typedefs.dart' - show - CloudEventHandler, - CloudEventWithContextHandler, - HandlerWithLogger, - JsonHandler, - JsonWithContextHandler; + show CloudEventHandler, HandlerWithLogger, JsonHandler; diff --git a/functions_framework/lib/serve.dart b/functions_framework/lib/serve.dart index 6d05e869..5cb0398e 100644 --- a/functions_framework/lib/serve.dart +++ b/functions_framework/lib/serve.dart @@ -35,8 +35,7 @@ import 'src/run.dart'; export 'package:google_cloud/google_cloud.dart' show BadRequestException; -export 'src/function_target.dart' - show FunctionTarget, JsonFunctionTarget, JsonWithContextFunctionTarget; +export 'src/function_target.dart' show FunctionTarget; /// If there is an invalid configuration, [exitCode] will be set to a non-zero /// value and the returned [Future] will completes quickly. @@ -46,10 +45,10 @@ export 'src/function_target.dart' /// [ProcessSignal.sigint]. Future serve( List args, - FunctionTarget? Function(String) nameToFunctionTarget, + Map functions, ) async { try { - await _serve(args, nameToFunctionTarget); + await _serve(args, (name) => functions[name]); } on BadConfigurationException catch (e) { stderr.writeln(red.wrap(e.message)); if (e.details != null) { @@ -65,10 +64,7 @@ Future _serve( ) async { final configFromEnvironment = FunctionConfig.fromEnv(); - final config = FunctionConfig.fromArgs( - args, - defaults: configFromEnvironment, - ); + final config = FunctionConfig.fromArgs(args, defaults: configFromEnvironment); final functionTarget = nameToFunctionTarget(config.target); if (functionTarget == null) { diff --git a/functions_framework/lib/src/cloud_function.dart b/functions_framework/lib/src/cloud_function.dart deleted file mode 100644 index fdfd539a..00000000 --- a/functions_framework/lib/src/cloud_function.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:meta/meta_meta.dart'; -import 'package:shelf/shelf.dart'; - -/// Use as an annotation on [Function]s that will be exposed as endpoints. -/// -/// Can only be used on public, top-level functions that are compatible with -/// [Handler] from `package:shelf`. -@Target({TargetKind.function}) -class CloudFunction { - /// The name used to register the function in the function framework. - /// - /// If `null`, the name of the [Function] is used. - final String? target; - - const CloudFunction({this.target}); -} diff --git a/functions_framework/lib/src/function_target.dart b/functions_framework/lib/src/function_target.dart index e294bddc..13a44532 100644 --- a/functions_framework/lib/src/function_target.dart +++ b/functions_framework/lib/src/function_target.dart @@ -16,31 +16,66 @@ import 'dart:async'; import 'package:shelf/shelf.dart'; +import '../functions_framework.dart'; import 'function_config.dart'; import 'targets/cloud_event_targets.dart'; import 'targets/http_targets.dart'; -import 'typedefs.dart'; +import 'targets/json_targets.dart'; export 'targets/json_targets.dart'; abstract class FunctionTarget { + FunctionTarget(); + FunctionType get type => FunctionType.http; - FunctionTarget(); + Future handler(Request request); - factory FunctionTarget.http(Handler function) = HttpFunctionTarget; + factory FunctionTarget.cloudEvent(CloudEventHandler function) = + CloudEventFunctionTarget; - factory FunctionTarget.httpWithLogger( - HandlerWithLogger function, - ) = HttpWithLoggerFunctionTarget; + factory FunctionTarget.http(Handler function) = HttpFunctionTarget; - factory FunctionTarget.cloudEvent( - CloudEventHandler function, - ) = CloudEventFunctionTarget; + factory FunctionTarget.httpWithLogger(HandlerWithLogger function) = + HttpWithLoggerFunctionTarget; - factory FunctionTarget.cloudEventWithContext( - CloudEventWithContextHandler function, - ) = CloudEventWithContextFunctionTarget; + static FunctionTarget jsonable( + Future Function(RequestType object, RequestContext context) + handler, + RequestType Function(Map json) fromJson, + ) => + JsonFunctionTarget(handler, (json) { + if (json is Map) { + try { + return fromJson(json); + } catch (e, stack) { + throw BadRequestException( + 400, + 'There was an error parsing the provided JSON data.', + innerError: e, + innerStack: stack, + ); + } + } + throw BadRequestException( + 400, + 'The provided JSON is not the expected type ' + '`Map`.', + ); + }); - FutureOr handler(Request request); + static FunctionTarget json( + Future Function(Map json, RequestContext context) + handler, + ) => + JsonFunctionTarget(handler, (json) { + if (json is Map) { + return json; + } + throw BadRequestException( + 400, + 'The provided JSON is not the expected type ' + '`Map`.', + ); + }); } diff --git a/functions_framework/lib/src/run.dart b/functions_framework/lib/src/run.dart index 7ecfced6..04b31b59 100644 --- a/functions_framework/lib/src/run.dart +++ b/functions_framework/lib/src/run.dart @@ -39,7 +39,8 @@ Future run( const _forbiddenAssets = {'robots.txt', 'favicon.ico'}; -Handler _forbiddenAssetMiddleware(Handler innerHandler) => (Request request) { +Handler _forbiddenAssetMiddleware(Handler innerHandler) => + (Request request) async { if (_forbiddenAssets.contains(request.url.path)) { return Response.notFound('Not found.'); } diff --git a/functions_framework/lib/src/targets/cloud_event_targets.dart b/functions_framework/lib/src/targets/cloud_event_targets.dart index 7ed44911..2d7ea75f 100644 --- a/functions_framework/lib/src/targets/cloud_event_targets.dart +++ b/functions_framework/lib/src/targets/cloud_event_targets.dart @@ -30,24 +30,6 @@ class CloudEventFunctionTarget extends FunctionTarget { @override FunctionType get type => FunctionType.cloudevent; - @override - FutureOr handler(Request request) async { - final event = await _eventFromRequest(request); - - await function(event); - - return Response.ok(''); - } - - CloudEventFunctionTarget(this.function); -} - -class CloudEventWithContextFunctionTarget extends FunctionTarget { - final CloudEventWithContextHandler function; - - @override - FunctionType get type => FunctionType.cloudevent; - @override Future handler(Request request) async { final event = await _eventFromRequest(request); @@ -58,7 +40,7 @@ class CloudEventWithContextFunctionTarget extends FunctionTarget { return Response.ok('', headers: context.responseHeaders); } - CloudEventWithContextFunctionTarget(this.function); + CloudEventFunctionTarget(this.function); } Future _eventFromRequest(Request request) => @@ -71,10 +53,7 @@ Future _decodeStructured(Request request) async { var jsonObject = await request.decodeJson() as Map; if (!jsonObject.containsKey('datacontenttype')) { - jsonObject = { - ...jsonObject, - 'datacontenttype': type.toString(), - }; + jsonObject = {...jsonObject, 'datacontenttype': type.toString()}; } return _decodeValidCloudEvent(jsonObject, 'structured-mode message'); @@ -87,8 +66,9 @@ Future _decodeBinary(Request request) async { final data = await request.decode(); final map = { - for (var e in request.headers.entries - .where((element) => element.key.startsWith(_cloudEventPrefix))) + for (var e in request.headers.entries.where( + (element) => element.key.startsWith(_cloudEventPrefix), + )) e.key.substring(_clientEventPrefixLength): e.value, 'datacontenttype': data.mimeType.toString(), 'data': data.data, diff --git a/functions_framework/lib/src/targets/http_targets.dart b/functions_framework/lib/src/targets/http_targets.dart index f6daddb7..bdc931f2 100644 --- a/functions_framework/lib/src/targets/http_targets.dart +++ b/functions_framework/lib/src/targets/http_targets.dart @@ -24,7 +24,7 @@ class HttpFunctionTarget extends FunctionTarget { final Handler _function; @override - FutureOr handler(Request request) => _function(request); + Future handler(Request request) async => await _function(request); HttpFunctionTarget(this._function); } @@ -33,7 +33,7 @@ class HttpWithLoggerFunctionTarget extends FunctionTarget { final HandlerWithLogger _function; @override - FutureOr handler(Request request) => + Future handler(Request request) => _function(request, currentLogger); HttpWithLoggerFunctionTarget(this._function); diff --git a/functions_framework/lib/src/targets/json_targets.dart b/functions_framework/lib/src/targets/json_targets.dart index 9addf52c..19e9c39e 100644 --- a/functions_framework/lib/src/targets/json_targets.dart +++ b/functions_framework/lib/src/targets/json_targets.dart @@ -46,78 +46,20 @@ abstract class JsonFunctionTarget factory JsonFunctionTarget( JsonHandler function, RequestType Function(Object?) fromJson, - ) = _JsonFunctionTarget; + ) = _JsonWithContextFunctionTarget; factory JsonFunctionTarget.voidResult( JsonHandler function, RequestType Function(Object?) fromJson, - ) = _VoidJsonFunctionTarget; -} - -class _JsonFunctionTarget - extends JsonFunctionTarget { - _JsonFunctionTarget( - super.function, - super.fromJson, - ) : super._(); - - @override - FutureOr handler(Request request) async { - final argument = await _toRequestType(request); - final response = await _function(argument); - final responseJson = jsonEncode(response); - - return Response.ok( - responseJson, - headers: const {contentTypeHeader: jsonContentType}, - ); - } -} - -class _VoidJsonFunctionTarget - extends JsonFunctionTarget { - _VoidJsonFunctionTarget( - super.function, - super.fromJson, - ) : super._(); - - @override - FutureOr handler(Request request) async { - final argument = await _toRequestType(request); - await _function(argument); - return Response.ok(''); - } -} - -abstract class JsonWithContextFunctionTarget - extends _JsonFunctionTargetBase { - final JsonWithContextHandler _function; - - JsonWithContextFunctionTarget._( - this._function, - RequestType Function(Object?) fromJson, - ) : super(fromJson); - - factory JsonWithContextFunctionTarget( - JsonWithContextHandler function, - RequestType Function(Object?) fromJson, - ) = _JsonWithContextFunctionTarget; - - factory JsonWithContextFunctionTarget.voidResult( - JsonWithContextHandler function, - RequestType Function(Object?) fromJson, ) = _VoidJsonWithContextFunctionTarget; } class _JsonWithContextFunctionTarget - extends JsonWithContextFunctionTarget { - _JsonWithContextFunctionTarget( - super.function, - super.fromJson, - ) : super._(); + extends JsonFunctionTarget { + _JsonWithContextFunctionTarget(super.function, super.fromJson) : super._(); @override - FutureOr handler(Request request) async { + Future handler(Request request) async { final argument = await _toRequestType(request); final context = contextForRequest(request); final response = await _function(argument, context); @@ -127,23 +69,18 @@ class _JsonWithContextFunctionTarget responseJson, headers: context.responseHeaders.isEmpty ? const {contentTypeHeader: jsonContentType} - : { - contentTypeHeader: jsonContentType, - ...context.responseHeaders, - }, + : {contentTypeHeader: jsonContentType, ...context.responseHeaders}, ); } } class _VoidJsonWithContextFunctionTarget - extends JsonWithContextFunctionTarget { - _VoidJsonWithContextFunctionTarget( - super.function, - super.fromJson, - ) : super._(); + extends JsonFunctionTarget { + _VoidJsonWithContextFunctionTarget(super.function, super.fromJson) + : super._(); @override - FutureOr handler(Request request) async { + Future handler(Request request) async { final argument = await _toRequestType(request); final context = contextForRequest(request); await _function(argument, context); diff --git a/functions_framework/lib/src/typedefs.dart b/functions_framework/lib/src/typedefs.dart index 9dc7ea0c..9f861235 100644 --- a/functions_framework/lib/src/typedefs.dart +++ b/functions_framework/lib/src/typedefs.dart @@ -20,18 +20,15 @@ import 'package:shelf/shelf.dart'; import 'cloud_event.dart'; import 'request_context.dart'; -/// The shape of a handler for [CloudEvent] types. -typedef CloudEventHandler = FutureOr Function(CloudEvent request); - /// The shape of a handler for [CloudEvent] types while also providing a /// [RequestContext]. -typedef CloudEventWithContextHandler = FutureOr Function( +typedef CloudEventHandler = Future Function( CloudEvent request, RequestContext context, ); /// The shape of a handler that supports a custom [RequestType] and -/// [ResponseType]. +/// [ResponseType] and while also providing a [RequestContext]. /// /// The [RequestType] must be either a type compatible with a JSON literal or /// have a `fromJson` constructor with a single, positional parameter that is @@ -40,24 +37,13 @@ typedef CloudEventWithContextHandler = FutureOr Function( /// The [ResponseType] must be either a type compatible with a JSON literal or /// have a `toJson()` function with a returns type compatible with a JSON /// literal. -typedef JsonHandler = FutureOr - Function(RequestType request); - -/// The shape of a handler that supports a custom [RequestType] and -/// [ResponseType] and while also providing a [RequestContext]. -/// -/// See [JsonHandler] for the type requirements for [RequestType] and -/// [ResponseType]. -typedef JsonWithContextHandler - = FutureOr Function( - RequestType request, - RequestContext context, -); +typedef JsonHandler = Future Function( + RequestType request, RequestContext context); /// The shape of a basic handler that follows the /// [package:shelf](https://pub.dev/packages/shelf) [Handler] pattern while also /// providing a [RequestLogger]. -typedef HandlerWithLogger = FutureOr Function( +typedef HandlerWithLogger = Future Function( Request request, RequestLogger logger, ); diff --git a/functions_framework_builder/.gitignore b/functions_framework_builder/.gitignore deleted file mode 100644 index 02ecb07c..00000000 --- a/functions_framework_builder/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# See https://dart.dev/guides/libraries/private-files - -# Files and directories created by pub -.dart_tool/ -pubspec.lock - -# Standard files for Cloud Functions conformance tests -# https://github.com/GoogleCloudPlatform/functions-framework-conformance -function_output.json -serverlog_stderr.txt -serverlog_stdout.txt diff --git a/functions_framework_builder/CHANGELOG.md b/functions_framework_builder/CHANGELOG.md deleted file mode 100644 index a17eb1ba..00000000 --- a/functions_framework_builder/CHANGELOG.md +++ /dev/null @@ -1,84 +0,0 @@ -## 0.4.11-wip - -- Support the latest versions of `analyzer`, `dart_style` and `source_gen`. -- Require Dart 3.5 - -## 0.4.10 - -- Allow the latest `package:functions_framework`. - -## 0.4.9 - -- Support the latest `package:analyzer`. - -## 0.4.8 - -- Support the latest `package:analyzer`. -- Require Dart 3.0 -- Support `package:http` v1 - -## 0.4.7 - -- Support the latest `package:analyzer`. - -## 0.4.6 - -- Support the latest `package:analyzer`. - -## 0.4.5 - -- Support the latest `package:analyzer`. - -## 0.4.4 - -- Sort directives in generated `bin/server.dart`. - -## 0.4.3 - -- Support the latest `package:analyzer`. - -## 0.4.2 - -- Support classes with `toJson` methods defined via mix-in. -- Allow the latest `package:functions_framework`. - -## 0.4.1 - -- Allow the latest version of `package:build_config`. - -## 0.4.0 - -- Generate null-safe code. -- Migrate implementation to null safety. -- Support the latest `package:functions_framework` version. -- Allow the latest, null-safe versions of a number of packages. - -## 0.3.1 - -- Support the latest `package:functions_framework` version. - -## 0.3.0 - -- Added support for connecting functions that handle and return JSON data. -- Added support for connecting functions that handle the `CloudEvent` type. -- Support `analyzer: '>=0.40.6 <0.42.0'` - -## 0.2.0 - -- **BREAKING** Function definitions now must be in `lib/functions.dart`. -- **BREAKING** Generated server binary is now `bin/server.dart` instead of - `bin/main.dart`. -- Add a number of checks to the builder to make sure the annotations are used - on the correct type of functions. -- Renamed the builder library from `functions_framework_builder.dart` to - `builder.dart`. This is the convention for builders. This is potentially - breaking for anyone why imports this library – but that's not expected. -- Hid the `FunctionsFrameworkGenerator` class. Not meant for external - consumption. - -## 0.1.0 - -This is a preview release of the Functions Framework Builder package for Dart to -demonstrate http functions support only (cloudevent support is not yet -implemented). This is a work in progress and currently does not pass conformance -testing. diff --git a/functions_framework_builder/LICENSE b/functions_framework_builder/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/functions_framework_builder/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/functions_framework_builder/README.md b/functions_framework_builder/README.md deleted file mode 100644 index ed5d0687..00000000 --- a/functions_framework_builder/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Functions Framework Builder for Dart - -An open source FaaS (Function as a Service) framework for writing portable Dart -functions, brought to you by the Google Dart and Cloud Functions teams. - -This is the builder package for the Functions Framework for Dart. Please -see [GoogleCloudPlatform/functions-framework-dart] for more information. - - -[GoogleCloudPlatform/functions-framework-dart]: -https://github.com/GoogleCloudPlatform/functions-framework-dart diff --git a/functions_framework_builder/analysis_options.yaml b/functions_framework_builder/analysis_options.yaml deleted file mode 100644 index a8ee49aa..00000000 --- a/functions_framework_builder/analysis_options.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# https://dart.dev/guides/language/analysis-options -include: ../analysis_options.yaml - -analyzer: - errors: - # analyzer deprecations - deprecated_member_use: ignore diff --git a/functions_framework_builder/build.yaml b/functions_framework_builder/build.yaml deleted file mode 100644 index 7f02cf1d..00000000 --- a/functions_framework_builder/build.yaml +++ /dev/null @@ -1,10 +0,0 @@ -builders: - function_framework_builder: - import: package:functions_framework_builder/builder.dart - builder_factories: - - functionsFrameworkBuilder - build_extensions: - 'lib/functions.dart': - - bin/server.dart - auto_apply: dependents - build_to: source diff --git a/functions_framework_builder/lib/builder.dart b/functions_framework_builder/lib/builder.dart deleted file mode 100644 index a2a3f239..00000000 --- a/functions_framework_builder/lib/builder.dart +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/// Configuration for using `package:build`-compatible build systems. -/// -/// See: -/// * [build_runner](https://pub.dev/packages/build_runner) -/// -/// This library is **not** intended to be imported by typical end-users unless -/// you are creating a custom compilation pipeline. See documentation for -/// details, and `build.yaml` for how this builder is configured by default. -library; - -import 'package:analyzer/dart/element/element.dart'; -import 'package:build/build.dart'; -import 'package:dart_style/dart_style.dart'; -import 'package:functions_framework/functions_framework.dart'; -import 'package:path/path.dart' as path; -import 'package:source_gen/source_gen.dart'; - -import 'src/constants.dart'; -import 'src/function_type_validator.dart'; -import 'src/supported_function_type.dart'; - -Builder functionsFrameworkBuilder([BuilderOptions? options]) => - const _FunctionsFrameworkBuilder(); - -class _FunctionsFrameworkBuilder implements Builder { - const _FunctionsFrameworkBuilder(); - - @override - Map> get buildExtensions => const { - r'lib/functions.dart': ['bin/server.dart'], - }; - - @override - Future build(BuildStep buildStep) async { - final entries = {}; - - final libraryElement = await buildStep.inputLibrary; - final validator = await FunctionTypeValidator.create(buildStep.resolver); - - for (var annotatedElement in _fromLibrary(libraryElement)) { - final element = annotatedElement.element; - if (element is! FunctionElement || element.isPrivate) { - throw InvalidGenerationSourceError( - 'Only top-level, public functions are supported.', - element: element, - ); - } - - final targetReader = annotatedElement.annotation.read('target'); - - final targetName = - targetReader.isNull ? element.name : targetReader.stringValue; - - if (entries.containsKey(targetName)) { - throw InvalidGenerationSourceError( - 'A function has already been annotated with target "$targetName".', - element: element, - ); - } - - final invokeExpression = validator.validate( - libraryElement, - targetName, - element, - ); - - entries[targetName] = invokeExpression; - } - - final cases = [ - for (var e in entries.values) ' ${e.name} => ${e.expression},', - ]; - - final importDirectives = [ - "'package:functions_framework/serve.dart'", - "'${buildStep.inputId.uri}' as $functionsLibraryPrefix", - ]..sort(); - - var output = ''' -// GENERATED CODE - DO NOT MODIFY BY HAND -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// @dart=3.6 - -${importDirectives.map((e) => 'import $e;').join('\n')} - -Future main(List args) async { - await serve(args, _nameToFunctionTarget); -} - -FunctionTarget? _nameToFunctionTarget(String name) => - switch (name) { -${cases.join('\n')} - _ => null - }; -'''; - - try { - output = DartFormatter( - languageVersion: libraryElement.languageVersion.effective, - ).format(output); - } on FormatterException catch (e, stack) { - log.warning('Could not format output.', e, stack); - } - - await buildStep.writeAsString( - AssetId( - buildStep.inputId.package, - path.join('bin', 'server.dart'), - ), - output, - ); - } -} - -Iterable _fromLibrary(LibraryElement library) sync* { - // Merging the `topLevelElements` picks up private elements and fields. - // While neither is supported, it allows us to provide helpful errors if devs - // are using the annotations incorrectly. - final mergedElements = { - ...library.topLevelElements, - ...library.exportNamespace.definedNames.values, - }; - - for (var element in mergedElements) { - final annotations = _checker.annotationsOf(element).toList(); - - if (annotations.isEmpty) { - continue; - } - if (annotations.length > 1) { - throw InvalidGenerationSourceError( - 'Cannot be annotated with `CloudFunction` more than once.', - element: element, - ); - } - yield AnnotatedElement(ConstantReader(annotations.single), element); - } -} - -const _checker = TypeChecker.fromRuntime(CloudFunction); diff --git a/functions_framework_builder/lib/src/analyzer_utils.dart b/functions_framework_builder/lib/src/analyzer_utils.dart deleted file mode 100644 index 1ea67e32..00000000 --- a/functions_framework_builder/lib/src/analyzer_utils.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; - -extension DartTypeExtension on DartType { - String toStringNonNullable() => getDisplayString().dropQuestion(); -} - -extension ElementExtension on Element { - String toStringNonNullable() => getDisplayString().dropQuestion(); -} - -extension on String { - String dropQuestion() { - if (endsWith('?')) return substring(0, length - 1); - return this; - } -} diff --git a/functions_framework_builder/lib/src/constants.dart b/functions_framework_builder/lib/src/constants.dart deleted file mode 100644 index c40e7910..00000000 --- a/functions_framework_builder/lib/src/constants.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -const functionsLibraryPrefix = 'function_library'; diff --git a/functions_framework_builder/lib/src/function_type_validator.dart b/functions_framework_builder/lib/src/function_type_validator.dart deleted file mode 100644 index 584ace92..00000000 --- a/functions_framework_builder/lib/src/function_type_validator.dart +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:build/build.dart'; -import 'package:source_gen/source_gen.dart'; - -import 'generic_function_type.dart'; -import 'supported_function_type.dart'; - -class FunctionTypeValidator { - final List _types; - - FunctionTypeValidator._(this._types); - - FactoryData validate( - LibraryElement library, - String targetName, - FunctionElement element, - ) { - for (var type in _types) { - final reference = type.createReference(library, targetName, element); - if (reference != null) { - return reference; - } - } - throw InvalidGenerationSourceError( - ''' -Not compatible with a supported function shape: -${_types.map((e) => ' ${e.typedefName} [${e.typeDescription}] from ${e.libraryUri}').join('\n')} -''', - element: element, - ); - } - - static Future create(Resolver resolver) async => - FunctionTypeValidator._( - [ - await SupportedFunctionType.create( - resolver, - 'package:functions_framework/functions_framework.dart', - 'HandlerWithLogger', - 'FunctionTarget.httpWithLogger', - ), - await SupportedFunctionType.create( - resolver, - 'package:shelf/shelf.dart', - 'Handler', - 'FunctionTarget.http', - ), - await SupportedFunctionType.create( - resolver, - 'package:functions_framework/functions_framework.dart', - 'CloudEventWithContextHandler', - 'FunctionTarget.cloudEventWithContext', - ), - await SupportedFunctionType.create( - resolver, - 'package:functions_framework/functions_framework.dart', - 'CloudEventHandler', - 'FunctionTarget.cloudEvent', - ), - await GenericFunctionType.createWithContext(resolver), - await GenericFunctionType.create(resolver), - ], - ); -} diff --git a/functions_framework_builder/lib/src/generic_function_type.dart b/functions_framework_builder/lib/src/generic_function_type.dart deleted file mode 100644 index 90ab26be..00000000 --- a/functions_framework_builder/lib/src/generic_function_type.dart +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:build/build.dart'; -import 'package:source_gen/source_gen.dart'; -import 'package:source_helper/source_helper.dart'; - -import 'analyzer_utils.dart'; -import 'constants.dart'; -import 'supported_function_type.dart'; -import 'valid_json_utils.dart'; - -const _libraryUrl = 'package:functions_framework/functions_framework.dart'; -final _libraryUri = Uri.parse(_libraryUrl); -const _typedefName = 'JsonHandler'; -const _typedefWithContextName = 'JsonWithContextHandler'; - -const _constructorName = 'JsonFunctionTarget'; -const _voidConstructorName = '$_constructorName.voidResult'; -const _withContextConstructorName = 'JsonWithContextFunctionTarget'; -const _voidWithContextConstructorName = - '$_withContextConstructorName.voidResult'; - -class GenericFunctionType implements SupportedFunctionType { - @override - String get libraryUri => _libraryUrl; - - @override - String get typedefName => _typedefName; - - @override - String get typeDescription => - _functionTypeAliasElement.aliasedElement!.toStringNonNullable(); - - final TypeAliasElement _functionTypeAliasElement; - final bool _withContext; - - GenericFunctionType._(this._functionTypeAliasElement, this._withContext); - - static Future create(Resolver resolver) async { - final lib = await resolver.libraryFor(AssetId.resolve(_libraryUri)); - - final handlerTypeAlias = - lib.exportNamespace.get(_typedefName) as TypeAliasElement; - - return GenericFunctionType._(handlerTypeAlias, false); - } - - static Future createWithContext( - Resolver resolver, - ) async { - final lib = await resolver.libraryFor(AssetId.resolve(_libraryUri)); - - final handlerTypeAlias = - lib.exportNamespace.get(_typedefWithContextName) as TypeAliasElement; - - return GenericFunctionType._(handlerTypeAlias, true); - } - - @override - FactoryData? createReference( - LibraryElement library, - String targetName, - FunctionElement element, - ) { - if (element.parameters.isEmpty) { - return null; - } - - final firstParamType = element.parameters.first.type; - - final paramInfo = validJsonParamType(firstParamType); - - if (paramInfo == null) { - return null; - } - - final returnKind = validJsonReturnType(element.returnType); - - if (returnKind == JsonReturnKind.notJson) { - return null; - } - - final functionType = _functionTypeAliasElement.instantiate( - typeArguments: [firstParamType, element.returnType], - nullabilitySuffix: NullabilitySuffix.none, - ); - - if (library.typeSystem.isSubtypeOf(element.type, functionType)) { - if (paramInfo.paramType != null) { - if (library.exportNamespace.get(paramInfo.paramType!.element.name) == - null) { - // TODO: add a test for this! - throw InvalidGenerationSourceError( - 'The type `${paramInfo.paramType!.element.name}` is not exposed ' - 'by the function library `${library.source.uri}` so it cannot ' - 'be used.', - ); - } - } - - return _GenericFactoryData( - _withContext, - returnKind == JsonReturnKind.isVoid, - paramInfo, - escapeDartString(targetName), - '$functionsLibraryPrefix.${element.name}', - ); - } - return null; - } -} - -class _GenericFactoryData implements FactoryData { - final String _endpointConstructorName; - final String target; - final String function; - - final String returnType; - final String factoryBody; - - _GenericFactoryData._( - this._endpointConstructorName, - this.target, - this.function, - this.returnType, - this.factoryBody, - ); - - factory _GenericFactoryData( - bool withContext, - bool isVoid, - JsonParamInfo info, - String target, - String function, - ) { - final jsonTypeDisplay = info.jsonType.toStringNonNullable(); - final typeDisplayName = info.paramType == null - ? jsonTypeDisplay - : '$functionsLibraryPrefix.' - '${info.paramType!.toStringNonNullable()}'; - - final returnBlock = info.paramType == null - ? 'return $_jsonParamName;' - : ''' - try { - return $typeDisplayName.$fromJsonFactoryName($_jsonParamName); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - '''; - - final body = ''' - if ($_jsonParamName is $jsonTypeDisplay) { - $returnBlock - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`$jsonTypeDisplay`.', - ); -'''; - - return _GenericFactoryData._( - isVoid - ? withContext - ? _voidWithContextConstructorName - : _voidConstructorName - : withContext - ? _withContextConstructorName - : _constructorName, - target, - function, - typeDisplayName, - body, - ); - } - - @override - String get expression => ''' -$_endpointConstructorName($function, ($_jsonParamName) { - $factoryBody -},) -'''; - - @override - String get name => target; -} - -const _jsonParamName = 'json'; diff --git a/functions_framework_builder/lib/src/supported_function_type.dart b/functions_framework_builder/lib/src/supported_function_type.dart deleted file mode 100644 index d3a071f1..00000000 --- a/functions_framework_builder/lib/src/supported_function_type.dart +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/nullability_suffix.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:build/build.dart'; -import 'package:source_helper/source_helper.dart'; - -import 'analyzer_utils.dart'; -import 'constants.dart'; - -class SupportedFunctionType { - final String libraryUri; - final String typedefName; - final String typeDescription; - - final FunctionType _type; - final String? _constructor; - - SupportedFunctionType._( - this.libraryUri, - this.typedefName, - this._type, { - String? constructor, - }) : _constructor = constructor, - typeDescription = _type.toStringNonNullable(); - - static Future create( - Resolver resolver, - String libraryUri, - String typeDefName, - String constructor, - ) async { - final lib = await resolver.libraryFor( - AssetId.resolve(Uri.parse(libraryUri)), - ); - - final handlerTypeAlias = - lib.exportNamespace.get(typeDefName) as TypeAliasElement; - - final functionType = handlerTypeAlias.instantiate( - typeArguments: [], - nullabilitySuffix: NullabilitySuffix.none, - ); - - return SupportedFunctionType._( - libraryUri, - typeDefName, - functionType as FunctionType, - constructor: constructor, - ); - } - - FactoryData? createReference( - LibraryElement library, - String targetName, - FunctionElement element, - ) { - if (element.library.typeSystem.isSubtypeOf(element.type, _type)) { - return _TrivialFactoryData( - escapeDartString(targetName), - '$_constructor(' - '$functionsLibraryPrefix.${element.name},)', - ); - } - return null; - } -} - -abstract class FactoryData { - String get name; - - String get expression; -} - -class _TrivialFactoryData implements FactoryData { - @override - final String name; - @override - final String expression; - - _TrivialFactoryData(this.name, this.expression); -} diff --git a/functions_framework_builder/lib/src/valid_json_utils.dart b/functions_framework_builder/lib/src/valid_json_utils.dart deleted file mode 100644 index 5119a84b..00000000 --- a/functions_framework_builder/lib/src/valid_json_utils.dart +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:analyzer/dart/element/type.dart'; -import 'package:collection/collection.dart'; - -const fromJsonFactoryName = 'fromJson'; - -bool _validJsonType(DartType type, bool allowComplexMembers) { - bool validCollectionMember(DartType memberType) { - if (allowComplexMembers) { - return memberType is DynamicType || - memberType.isDartCoreObject || - _validJsonReturnTypeCore(memberType) == JsonReturnKind.other; - } - return memberType is DynamicType || memberType.isDartCoreObject; - } - - if (type.isDartCoreBool || - type.isDartCoreNum || - type.isDartCoreDouble || - type.isDartCoreInt || - type.isDartCoreString) { - return true; - } - - if (type is InterfaceType) { - if (type.isDartCoreList) { - final arg = type.typeArguments.single; - return validCollectionMember(arg); - } else if (type.isDartCoreMap) { - final keyArg = type.typeArguments[0]; - final valueArg = type.typeArguments[1]; - return keyArg.isDartCoreString && validCollectionMember(valueArg); - } - } - return false; -} - -typedef JsonParamInfo = ({DartType jsonType, InterfaceType? paramType}); - -JsonParamInfo? validJsonParamType(DartType type) { - // Look for a `fromJson` factory that takes a JSON-able type - if (type is InterfaceType) { - final fromJsonCtor = type.constructors.singleWhereOrNull( - (element) => element.name == fromJsonFactoryName, - ); - if (fromJsonCtor != null) { - final requiredParams = fromJsonCtor.parameters - .where((element) => element.isRequiredPositional) - .toList(); - if (requiredParams.length == 1) { - final paramType = requiredParams.single.type; - if (_validJsonType(paramType, false)) { - return (jsonType: paramType, paramType: type); - } - } - } - } - - if (_validJsonType(type, false)) { - return (jsonType: type, paramType: null); - } - return null; -} - -enum JsonReturnKind { isVoid, notJson, other } - -JsonReturnKind validJsonReturnType(DartType type) { - if (type.isDartAsyncFuture || type.isDartAsyncFutureOr) { - // Unwrap the future value and run again! - return _validJsonReturnTypeCore( - (type as InterfaceType).typeArguments.single, - ); - } - - return _validJsonReturnTypeCore(type); -} - -JsonReturnKind _validJsonReturnTypeCore(DartType type) { - if (type is VoidType) { - return JsonReturnKind.isVoid; - } - - // Look for a `toJson` function that returns a JSON-able type - if (type is InterfaceType) { - final toJsonMethod = type.element.augmented - .lookUpMethod(name: 'toJson', library: type.element.library); - if (toJsonMethod != null && - toJsonMethod.parameters.every((element) => element.isOptional)) { - type = toJsonMethod.returnType; - } - } - - return _validJsonType(type, true) - ? JsonReturnKind.other - : JsonReturnKind.notJson; -} diff --git a/functions_framework_builder/mono_pkg.yaml b/functions_framework_builder/mono_pkg.yaml deleted file mode 100644 index 4491d87a..00000000 --- a/functions_framework_builder/mono_pkg.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# https://github.com/google/mono_repo.dart -sdk: -- pubspec -- dev - -stages: -- analyze_format: - - group: - - format - - analyze: --fatal-infos - sdk: dev - - analyze: - sdk: pubspec -- unit_test: - - test diff --git a/functions_framework_builder/pubspec.yaml b/functions_framework_builder/pubspec.yaml deleted file mode 100644 index e1b844bd..00000000 --- a/functions_framework_builder/pubspec.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: functions_framework_builder -version: 0.4.11-wip -description: Builder for package:functions_framework -repository: https://github.com/GoogleCloudPlatform/functions-framework-dart - -resolution: workspace -environment: - sdk: ^3.6.0 - -dependencies: - analyzer: '>=6.9.0 <8.0.0' - build: ^2.3.1 - build_config: ^1.0.0 - collection: ^1.17.0 - dart_style: '>=2.3.7 <4.0.0' - # There is a tight version constraint because the builder has a strict - # dependency on all features exposed. - functions_framework: '>=0.4.0 <0.4.5' - glob: ^2.0.0 - meta: ^1.7.0 - path: ^1.8.0 - shelf: ^1.0.0 - source_gen: '>=1.4.0 <3.0.0' - source_helper: ^1.3.3 - -dev_dependencies: - build_test: ^2.1.2 - dart_flutter_team_lints: ^3.0.0 - package_config: ^2.0.0 - stream_transform: ^2.0.0 - test: ^1.21.6 diff --git a/functions_framework_builder/test/builder_test.dart b/functions_framework_builder/test/builder_test.dart deleted file mode 100644 index 09c574fe..00000000 --- a/functions_framework_builder/test/builder_test.dart +++ /dev/null @@ -1,688 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:async'; -import 'dart:io'; - -import 'package:build_test/build_test.dart'; -import 'package:functions_framework_builder/builder.dart'; -import 'package:source_gen/source_gen.dart'; -import 'package:test/test.dart'; - -void main() { - tearDown(() { - // Increment this after each test so the next test has it's own package - _pkgCacheCount++; - }); - - test('Simple Generator test', () async { - await _generateTest( - r''' -import 'package:functions_framework/functions_framework.dart'; -import 'package:shelf/shelf.dart'; - -@CloudFunction() -Response handleGet(Request request) => Response.ok('Hello, World!'); -''', - ''' -$_outputHeader -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'handleGet' => FunctionTarget.http( - function_library.handleGet, - ), - _ => null - }; -''', - ); - }); - - test('Populate the target param', () async { - await _generateTest( - r''' -import 'package:functions_framework/functions_framework.dart'; -import 'package:shelf/shelf.dart'; - -@CloudFunction(target: 'some function') -Response handleGet(Request request) => Response.ok('Hello, World!'); -''', - ''' -$_outputHeader -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'some function' => FunctionTarget.http( - function_library.handleGet, - ), - _ => null - }; -''', - ); - }); - - test('Valid shelf function shapes are supported', () async { - final file = File('test/test_examples/valid_shelf_handlers.dart'); - - final lines = [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - 'customResponse', - 'customResponseAsync', - 'customResponseFutureOr', - ] - .map( - (e) => """ - '$e' => FunctionTarget.http( - function_library.$e, - ),""", - ) - .join('\n'); - await _generateTest( - file.readAsStringSync(), - ''' -$_outputHeader -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { -$lines - _ => null - }; -''', - ); - }); - - test('Valid CloudEvent function shapes are supported', () async { - final file = File('test/test_examples/valid_cloud_event_handlers.dart'); - - final lines = [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'optionalParam', - 'objectParam', - ] - .map( - (e) => """ - '$e' => FunctionTarget.cloudEvent( - function_library.$e, - ),""", - ) - .join('\n'); - await _generateTest( - file.readAsStringSync(), - ''' -$_outputHeader -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { -$lines - _ => null - }; -''', - ); - }); - - group('Valid custom type function shapes are supported', () { - final inputContent = - File('test/test_examples/valid_custom_type_handlers.dart') - .readAsStringSync(); - - test('void return type', () async { - await _testItems( - inputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget.voidResult( - function_library.$e, - (json) { - if (json is Map) { - try { - return function_library.JsonType.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ),""", - ); - }); - - test('void return type with context', () async { - final newInputContent = inputContent - .replaceAll( - '(JsonType request) ', - '(JsonType request, RequestContext context)', - ) - .replaceAll( - 'int? other', - 'RequestContext context, int? other', - ); - - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonWithContextFunctionTarget.voidResult( - function_library.$e, - (json) { - if (json is Map) { - try { - return function_library.JsonType.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ),""", - ); - }); - - test('simple return type', () async { - final newInputContent = inputContent - .replaceAll('void ', 'int ') - .replaceAll('', ''); - - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget( - function_library.$e, - (json) { - if (json is Map) { - try { - return function_library.JsonType.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ),""", - ); - }); - - test('JSON return type', () async { - final newInputContent = inputContent - .replaceAll('void ', 'int ') - .replaceAll('', ''); - - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget( - function_library.$e, - (json) { - if (json is Map) { - try { - return function_library.JsonType.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ),""", - ); - }); - - test('complex return type', () async { - final newInputContent = inputContent - .replaceAll('void ', 'Map> ') - .replaceAll('', '>>'); - - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget( - function_library.$e, - (json) { - if (json is Map) { - try { - return function_library.JsonType.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ),""", - ); - }); - }); - - group('Valid JSON type function shapes are supported', () { - final inputContent = - File('test/test_examples/valid_json_type_handlers.dart') - .readAsStringSync(); - - test('void return type', () async { - await _testItems( - inputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget.voidResult( - function_library.$e, - (json) { - if (json is num) { - return json; - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`num`.', - ); - }, - ),""", - ); - }); - - test('simple return type', () async { - final newInputContent = inputContent - .replaceAll('void ', 'int ') - .replaceAll('', ''); - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget( - function_library.$e, - (json) { - if (json is num) { - return json; - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`num`.', - ); - }, - ),""", - ); - }); - - test('complex return type', () async { - final newInputContent = inputContent - .replaceAll('void ', 'Map> ') - .replaceAll('', '>>'); - - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonFunctionTarget( - function_library.$e, - (json) { - if (json is num) { - return json; - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`num`.', - ); - }, - ),""", - ); - }); - - test('void with context', () async { - final newInputContent = inputContent - .replaceAll( - '(num request)', - '(num request, RequestContext context)', - ) - .replaceAll( - 'int? other', - 'RequestContext context, int? other', - ); - - await _testItems( - newInputContent, - [ - 'syncFunction', - 'asyncFunction', - 'futureOrFunction', - 'extraParam', - 'optionalParam', - ], - (e) => """ - '$e' => JsonWithContextFunctionTarget.voidResult( - function_library.$e, - (json) { - if (json is num) { - return json; - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`num`.', - ); - }, - ),""", - ); - }); - }); - - test('duplicate target names fails', () async { - await _generateThrows( - r''' -import 'package:functions_framework/functions_framework.dart'; -import 'package:shelf/shelf.dart'; - -@CloudFunction() -Response function(Request request) => Response.ok('Hello, World!'); - -@CloudFunction(target: 'function') -Response function2(Request request) => Response.ok('Hello, World!'); -''', - isA().having( - (e) => e.toString(), - 'toString()', - ''' -A function has already been annotated with target "function". -package:$_pkgName/functions.dart:8:10 - ╷ -8 │ Response function2(Request request) => Response.ok('Hello, World!'); - │ ^^^^^^^^^ - ╵''', - ), - ); - }); - - test('freezed style mixed in json types are allowed', () async { - final inputContent = - File('test/test_examples/freezed_style_json_mixin_handler.dart') - .readAsStringSync(); - - final srcs = {'$_pkgName|lib/functions.dart': inputContent}; - - await testBuilder( - functionsFrameworkBuilder(), - srcs, - reader: await PackageAssetReader.currentIsolate(), - ); - }); - - group('invalid function shapes are not allowed', () { - final onlyFunctionMatcher = - startsWith('Only top-level, public functions are supported.'); - final notCompatibleMatcher = startsWith( - 'Not compatible with a supported function shape:', - ); - final invalidShapes = { - // - // Not functions - // - 'class AClass{}': onlyFunctionMatcher, - 'int field = 5;': onlyFunctionMatcher, - 'int get getter => 5;': onlyFunctionMatcher, - - // - // Double-annotated functions are not allowed - // - '@CloudFunction() Response function(Request request) => null;': - startsWith( - 'Cannot be annotated with `CloudFunction` more than once.', - ), - - // - // pkg:shelf handlers - // - - // Not enough params - 'Response handleGet() => null;': notCompatibleMatcher, - // First param is not positional - 'Response handleGet({Request request}) => null;': notCompatibleMatcher, - // Too many required params - 'Response handleGet(Request request, int other) => null;': - notCompatibleMatcher, - // Param is wrong type - 'Response handleGet(int request) => null;': notCompatibleMatcher, - // Return type is wrong - 'int handleGet(Request request) => null;': notCompatibleMatcher, - // Return type is wrong - 'Future handleGet(Request request) => null;': notCompatibleMatcher, - // Return type is wrong - 'FutureOr handleGet(Request request) => null;': notCompatibleMatcher, - // Private functions do not work - 'Response _function(Request request) => null;': onlyFunctionMatcher, - - // - // CloudEvent types handlers - // - - // TODO: exclude return non-void return types? - - // - // Custom and JSON event types - // - 'Duration handleGet(DateTime request) => null;': notCompatibleMatcher, - }; - - for (var shape in invalidShapes.entries) { - test('"${shape.key}"', () async { - await _generateThrows( - ''' -import 'dart:async'; - -import 'package:functions_framework/functions_framework.dart'; -import 'package:shelf/shelf.dart'; - -@CloudFunction() -${shape.key} -''', - isA().having( - (e) => e.toString(), - 'toString()', - shape.value, - ), - ); - }); - } - }); -} - -Future _generateThrows(String inputLibrary, Object matcher) async { - await expectLater( - () => _generateTest(inputLibrary, null, validateLog: false), - throwsA(matcher), - ); -} - -Future _testItems( - String inputContent, - List functions, - String Function(String entry) entryFactory, -) async { - final entries = functions.map((e) => entryFactory(e)).join('\n'); - - await _generateTest( - inputContent, - ''' -$_outputHeader -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { -$entries - _ => null - }; -''', - ); -} - -Future _generateTest( - String inputLibrary, - String? expectedContent, { - bool validateLog = true, -}) async { - final srcs = {'$_pkgName|lib/functions.dart': inputLibrary}; - - await testBuilder( - functionsFrameworkBuilder(), - srcs, - generateFor: { - ...srcs.keys, - '$_pkgName|\$package\$', - }, - outputs: expectedContent == null - ? null - : { - '$_pkgName|bin/server.dart': decodedMatches(expectedContent), - }, - onLog: (log) { - if (!validateLog) { - return; - } - if (_ignoredLogMessages.any(log.message.contains)) { - return; - } - addTearDown(() { - var output = 'Unexpected log message: "${log.message}"'; - if (log.message.isEmpty && log.error != null) { - output = '$output (${log.error})'; - } - fail(output); - }); - }, - reader: await PackageAssetReader.currentIsolate(), - ); -} - -const _ignoredLogMessages = { - 'Generating SDK summary', - 'The latest `analyzer` version may not fully support your current SDK ' - 'version.', -}; - -// Ensure every test gets its own unique package name -String get _pkgName => 'pkg$_pkgCacheCount'; -int _pkgCacheCount = 1; - -String get _outputHeader => ''' -// GENERATED CODE - DO NOT MODIFY BY HAND -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// @dart=3.6 - -import 'package:functions_framework/serve.dart'; -import 'package:$_pkgName/functions.dart' as function_library; - -Future main(List args) async { - await serve(args, _nameToFunctionTarget); -} -'''; diff --git a/functions_framework_builder/test/test_examples/complex_target_name_handlers.dart b/functions_framework_builder/test/test_examples/complex_target_name_handlers.dart deleted file mode 100644 index 4edeee6d..00000000 --- a/functions_framework_builder/test/test_examples/complex_target_name_handlers.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'package:functions_framework/functions_framework.dart'; - -@CloudFunction(target: "'single quotes'") -void function1(CloudEvent request) => throw UnimplementedError(); - -@CloudFunction(target: '"double quotes"') -void function2(CloudEvent request) => throw UnimplementedError(); - -@CloudFunction(target: r'$dollar signs$') -void function3(CloudEvent request) => throw UnimplementedError(); - -@CloudFunction(target: 'white space\t\n\r\nall over') -void function4(CloudEvent request) => throw UnimplementedError(); diff --git a/functions_framework_builder/test/test_examples/freezed_style_json_mixin_handler.dart b/functions_framework_builder/test/test_examples/freezed_style_json_mixin_handler.dart deleted file mode 100644 index 4b3206f3..00000000 --- a/functions_framework_builder/test/test_examples/freezed_style_json_mixin_handler.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:functions_framework/functions_framework.dart'; - -mixin class JsonType { - Map toJson() => throw UnimplementedError(); -} - -class MixedInType with JsonType { - // ignore: avoid_unused_constructor_parameters - factory MixedInType.fromJson(Map json) => - throw UnimplementedError(); -} - -@CloudFunction() -MixedInType syncFunction(MixedInType request) => throw UnimplementedError(); diff --git a/functions_framework_builder/test/test_examples/valid_cloud_event_handlers.dart b/functions_framework_builder/test/test_examples/valid_cloud_event_handlers.dart deleted file mode 100644 index 0fdf082f..00000000 --- a/functions_framework_builder/test/test_examples/valid_cloud_event_handlers.dart +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:async'; - -import 'package:functions_framework/functions_framework.dart'; - -@CloudFunction() -void syncFunction(CloudEvent request) => throw UnimplementedError(); - -@CloudFunction() -Future asyncFunction(CloudEvent request) => throw UnimplementedError(); - -@CloudFunction() -FutureOr futureOrFunction(CloudEvent request) => - throw UnimplementedError(); - -@CloudFunction() -void optionalParam([CloudEvent? request]) => throw UnimplementedError(); - -@CloudFunction() -void objectParam(Object request) => throw UnimplementedError(); diff --git a/functions_framework_builder/test/test_examples/valid_custom_type_handlers.dart b/functions_framework_builder/test/test_examples/valid_custom_type_handlers.dart deleted file mode 100644 index 04df83b1..00000000 --- a/functions_framework_builder/test/test_examples/valid_custom_type_handlers.dart +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:async'; - -import 'package:functions_framework/functions_framework.dart'; - -@CloudFunction() -void syncFunction(JsonType request) => throw UnimplementedError(); - -@CloudFunction() -Future asyncFunction(JsonType request) => throw UnimplementedError(); - -@CloudFunction() -FutureOr futureOrFunction(JsonType request) => throw UnimplementedError(); - -@CloudFunction() -void extraParam(JsonType request, [int? other]) => throw UnimplementedError(); - -@CloudFunction() -void optionalParam([JsonType? request, int? other]) => - throw UnimplementedError(); - -class JsonType { - // ignore: avoid_unused_constructor_parameters - factory JsonType.fromJson(Map json) => - throw UnimplementedError(); - - Map toJson() => throw UnimplementedError(); -} diff --git a/functions_framework_builder/test/test_examples/valid_json_type_handlers.dart b/functions_framework_builder/test/test_examples/valid_json_type_handlers.dart deleted file mode 100644 index ee2f5b17..00000000 --- a/functions_framework_builder/test/test_examples/valid_json_type_handlers.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import 'dart:async'; - -import 'package:functions_framework/functions_framework.dart'; - -@CloudFunction() -void syncFunction(num request) => throw UnimplementedError(); - -@CloudFunction() -Future asyncFunction(num request) => throw UnimplementedError(); - -@CloudFunction() -FutureOr futureOrFunction(num request) => throw UnimplementedError(); - -@CloudFunction() -void extraParam(num request, [int? other]) => throw UnimplementedError(); - -@CloudFunction() -void optionalParam([num? request, int? other]) => throw UnimplementedError(); diff --git a/functions_framework_builder/test/test_examples/valid_shelf_handlers.dart b/functions_framework_builder/test/test_examples/valid_shelf_handlers.dart deleted file mode 100644 index adaa2a96..00000000 --- a/functions_framework_builder/test/test_examples/valid_shelf_handlers.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// ignore_for_file: library_private_types_in_public_api - -import 'dart:async'; - -import 'package:functions_framework/functions_framework.dart'; -import 'package:shelf/shelf.dart'; - -@CloudFunction() -Response syncFunction(Request request) => throw UnimplementedError(); - -@CloudFunction() -Future asyncFunction(Request request) => throw UnimplementedError(); - -@CloudFunction() -FutureOr futureOrFunction(Request request) => - throw UnimplementedError(); - -@CloudFunction() -Response extraParam(Request request, [int? other]) => - throw UnimplementedError(); - -@CloudFunction() -Response optionalParam([Request? request, int? other]) => - throw UnimplementedError(); - -@CloudFunction() -_MyResponse customResponse(Request request) => throw UnimplementedError(); - -@CloudFunction() -Future<_MyResponse> customResponseAsync(Request request) => - throw UnimplementedError(); - -@CloudFunction() -FutureOr<_MyResponse> customResponseFutureOr(Request request) => - throw UnimplementedError(); - -abstract class _MyResponse extends Response { - _MyResponse(super.statusCode); -} diff --git a/integration_test/bin/server.dart b/integration_test/bin/server.dart index 65b0baff..00bffb0e 100644 --- a/integration_test/bin/server.dart +++ b/integration_test/bin/server.dart @@ -1,4 +1,3 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND // Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,63 +17,30 @@ import 'package:functions_framework/serve.dart'; import 'package:hello_world_function_test/functions.dart' as function_library; -Future main(List args) async { - await serve(args, _nameToFunctionTarget); -} - -FunctionTarget? _nameToFunctionTarget(String name) => switch (name) { - 'function' => FunctionTarget.http( - function_library.function, - ), - 'loggingHandler' => FunctionTarget.httpWithLogger( - function_library.loggingHandler, - ), - 'basicCloudEventHandler' => FunctionTarget.cloudEventWithContext( - function_library.basicCloudEventHandler, - ), - 'protoEventHandler' => FunctionTarget.cloudEventWithContext( - function_library.protoEventHandler, - ), - 'conformanceHttp' => FunctionTarget.http( - function_library.conformanceHttp, - ), - 'conformanceCloudEvent' => FunctionTarget.cloudEvent( - function_library.conformanceCloudEvent, - ), - 'pubSubHandler' => JsonWithContextFunctionTarget.voidResult( - function_library.pubSubHandler, - (json) { - if (json is Map) { - try { - return function_library.PubSub.fromJson(json); - } catch (e, stack) { - throw BadRequestException( - 400, - 'There was an error parsing the provided JSON data.', - innerError: e, - innerStack: stack, - ); - } - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ), - 'jsonHandler' => JsonWithContextFunctionTarget( - function_library.jsonHandler, - (json) { - if (json is Map) { - return json; - } - throw BadRequestException( - 400, - 'The provided JSON is not the expected type ' - '`Map`.', - ); - }, - ), - _ => null - }; +Future main(List args) async => serve(args, { + 'function': FunctionTarget.http( + function_library.function, + ), + 'loggingHandler': FunctionTarget.httpWithLogger( + function_library.loggingHandler, + ), + 'basicCloudEventHandler': FunctionTarget.cloudEvent( + function_library.basicCloudEventHandler, + ), + 'protoEventHandler': FunctionTarget.cloudEvent( + function_library.protoEventHandler, + ), + 'conformanceHttp': FunctionTarget.http( + function_library.conformanceHttp, + ), + 'conformanceCloudEvent': FunctionTarget.cloudEvent( + function_library.conformanceCloudEvent, + ), + 'pubSubHandler': FunctionTarget.jsonable( + function_library.pubSubHandler, + function_library.PubSub.fromJson, + ), + 'jsonHandler': FunctionTarget.json( + function_library.jsonHandler, + ), + }); diff --git a/integration_test/lib/functions.dart b/integration_test/lib/functions.dart index 5d64a2c9..2de5e40a 100644 --- a/integration_test/lib/functions.dart +++ b/integration_test/lib/functions.dart @@ -32,7 +32,6 @@ int _requestCount = 0; final _watch = Stopwatch(); -@CloudFunction() Future function(Request request) async { _watch.start(); _requestCount++; @@ -73,10 +72,7 @@ Future function(Request request) async { 'environment': Platform.environment, }; - return Response.ok( - encodeJsonPretty(output), - headers: _jsonHeaders, - ); + return Response.ok(encodeJsonPretty(output), headers: _jsonHeaders); } if (urlPath.startsWith('exception')) { @@ -111,8 +107,7 @@ Future function(Request request) async { } } -@CloudFunction() -Response loggingHandler(Request request, RequestLogger logger) { +Future loggingHandler(Request request, RequestLogger logger) async { logger ..log('default', LogSeverity.defaultSeverity) ..debug('debug') @@ -127,8 +122,10 @@ Response loggingHandler(Request request, RequestLogger logger) { return Response.ok(''); } -@CloudFunction() -void basicCloudEventHandler(CloudEvent event, RequestContext context) { +Future basicCloudEventHandler( + CloudEvent event, + RequestContext context, +) async { context.logger.info('event subject: ${event.subject}'); final pubSub = PubSub.fromJson(event.data as Map); @@ -139,8 +136,7 @@ void basicCloudEventHandler(CloudEvent event, RequestContext context) { stderr.writeln(encodeJsonPretty(event)); } -@CloudFunction() -void protoEventHandler(CloudEvent event, RequestContext context) { +Future protoEventHandler(CloudEvent event, RequestContext context) async { context.logger.info('event subject: ${event.subject}'); context.logger.debug(context.request.headers); diff --git a/integration_test/lib/src/conformance_handlers.dart b/integration_test/lib/src/conformance_handlers.dart index 85617de8..4d74fca6 100644 --- a/integration_test/lib/src/conformance_handlers.dart +++ b/integration_test/lib/src/conformance_handlers.dart @@ -18,13 +18,10 @@ import 'package:shelf/shelf.dart'; import 'utils.dart'; -@CloudFunction() Future conformanceHttp(Request request) async { final content = await request.readAsString(); - File('function_output.json').writeAsStringSync( - content, - ); + File('function_output.json').writeAsStringSync(content); final buffer = StringBuffer() ..writeln('Hello, conformance test!') @@ -38,12 +35,9 @@ Future conformanceHttp(Request request) async { return Response.ok(output); } -@CloudFunction() -void conformanceCloudEvent(CloudEvent event) { +Future conformanceCloudEvent(CloudEvent event, RequestContext _) async { final eventEncoded = encodeJsonPretty(event); - File('function_output.json').writeAsStringSync( - eventEncoded, - ); + File('function_output.json').writeAsStringSync(eventEncoded); final buffer = StringBuffer() ..writeln('Hello, conformance test!') diff --git a/integration_test/lib/src/json_handlers.dart b/integration_test/lib/src/json_handlers.dart index 7e7e9db0..b69fedce 100644 --- a/integration_test/lib/src/json_handlers.dart +++ b/integration_test/lib/src/json_handlers.dart @@ -16,19 +16,17 @@ import 'package:functions_framework/functions_framework.dart'; import 'pub_sub_types.dart'; -@CloudFunction() -void pubSubHandler(PubSub pubSub, RequestContext context) { +Future pubSubHandler(PubSub pubSub, RequestContext context) async { print('subscription: ${pubSub.subscription}'); context.logger.info('subscription: ${pubSub.subscription}'); context.responseHeaders['subscription'] = pubSub.subscription; context.responseHeaders['multi'] = ['item1', 'item2']; } -@CloudFunction() -FutureOr jsonHandler( +Future jsonHandler( Map request, RequestContext context, -) { +) async { print('Keys: ${request.keys.join(', ')}'); context.responseHeaders['key_count'] = request.keys.length.toString(); context.responseHeaders['multi'] = ['item1', 'item2']; diff --git a/integration_test/pubspec.yaml b/integration_test/pubspec.yaml index e07b4211..5bcc7df0 100644 --- a/integration_test/pubspec.yaml +++ b/integration_test/pubspec.yaml @@ -14,7 +14,6 @@ dev_dependencies: build_runner: ^2.2.1 build_verify: ^3.0.0 dart_flutter_team_lints: ^3.0.0 - functions_framework_builder: ^0.4.10 google_cloud: ^0.2.1-wip http: ^1.0.0 http_parser: ^4.0.0 diff --git a/integration_test/test/cloud_behavior_test.dart b/integration_test/test/cloud_behavior_test.dart index 9c2d08cd..b1577da9 100644 --- a/integration_test/test/cloud_behavior_test.dart +++ b/integration_test/test/cloud_behavior_test.dart @@ -154,7 +154,7 @@ void main() { group('cloud event', () { setUp(() async { await doSetup( - FunctionTarget.cloudEventWithContext( + FunctionTarget.cloudEvent( basicCloudEventHandler, ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 8e3d8f2f..53661eff 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,6 +15,5 @@ workspace: - examples/protobuf_firestore - examples/raw_cloudevent - functions_framework - - functions_framework_builder - google_cloud - integration_test