From ab7d0f3f87ffeb7eebdd448fe1537ef95e532c8a Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Mon, 21 Mar 2022 19:06:27 +0000 Subject: [PATCH 1/2] doc: examples use declarative configuration 2/N Change the How-to guides and associated examples to use declarative configuration for the signature type. --- ci/build-examples.yaml | 3 - ci/generate-build-examples.sh | 3 + .../site/hello_world_http/hello_world_http.cc | 9 +- .../hello_world_pubsub/hello_world_pubsub.cc | 10 +- .../hello_world_storage.cc | 10 +- .../site/howto_create_container/README.md | 32 ++--- .../site/howto_deploy_cloud_event/README.md | 23 ++-- .../site/howto_deploy_to_cloud_run/README.md | 32 ++--- .../howto_local_development/CMakeLists.txt | 2 +- .../howto_local_development/local_server.cc | 14 +-- .../testing_http/http_integration_server.cc | 4 +- examples/site/testing_http/http_unit_test.cc | 8 +- examples/site/testing_pubsub/README.md | 22 ++-- .../pubsub_integration_server.cc | 4 +- .../site/testing_pubsub/pubsub_unit_test.cc | 4 +- examples/site/testing_storage/README.md | 32 ++--- .../storage_integration_server.cc | 4 +- .../site/testing_storage/storage_unit_test.cc | 4 +- examples/site_test.cc | 116 +++++++++++++----- 19 files changed, 202 insertions(+), 134 deletions(-) diff --git a/ci/build-examples.yaml b/ci/build-examples.yaml index fbce570c..059d5e0d 100644 --- a/ci/build-examples.yaml +++ b/ci/build-examples.yaml @@ -234,7 +234,6 @@ steps: waitFor: ['gcf-builder-ready'] id: 'site-hello_world_http' args: ['build', - '--env', 'GOOGLE_FUNCTION_SIGNATURE_TYPE=http', '--env', 'GOOGLE_FUNCTION_TARGET=hello_world_http', '--path', 'examples/site/hello_world_http', 'site-hello_world_http', @@ -243,7 +242,6 @@ steps: waitFor: ['gcf-builder-ready'] id: 'site-hello_world_pubsub' args: ['build', - '--env', 'GOOGLE_FUNCTION_SIGNATURE_TYPE=cloudevent', '--env', 'GOOGLE_FUNCTION_TARGET=hello_world_pubsub', '--path', 'examples/site/hello_world_pubsub', 'site-hello_world_pubsub', @@ -252,7 +250,6 @@ steps: waitFor: ['gcf-builder-ready'] id: 'site-hello_world_storage' args: ['build', - '--env', 'GOOGLE_FUNCTION_SIGNATURE_TYPE=cloudevent', '--env', 'GOOGLE_FUNCTION_TARGET=hello_world_storage', '--path', 'examples/site/hello_world_storage', 'site-hello_world_storage', diff --git a/ci/generate-build-examples.sh b/ci/generate-build-examples.sh index 5b8c06e4..33483354 100755 --- a/ci/generate-build-examples.sh +++ b/ci/generate-build-examples.sh @@ -113,6 +113,9 @@ site_example() { if grep -E -q 'gcf::CloudEvent|google::cloud::functions::CloudEvent' ${example}/*; then signature="cloudevent" fi + if grep -E -q 'gcf::Function|google::cloud::functions::Function' ${example}/*; then + signature="declarative" + fi local container="site-${function}" cat <<_EOF_ diff --git a/examples/site/hello_world_http/hello_world_http.cc b/examples/site/hello_world_http/hello_world_http.cc index 26ce9ca3..eae24855 100644 --- a/examples/site/hello_world_http/hello_world_http.cc +++ b/examples/site/hello_world_http/hello_world_http.cc @@ -13,13 +13,12 @@ // limitations under the License. // [START functions_helloworld_http] -#include -#include +#include #include namespace gcf = ::google::cloud::functions; -gcf::HttpResponse hello_world_http(gcf::HttpRequest request) { +gcf::HttpResponse hello_world_http_impl(gcf::HttpRequest request) { auto greeting = [r = std::move(request)] { auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, /*allow_exceptions=*/false); @@ -33,4 +32,8 @@ gcf::HttpResponse hello_world_http(gcf::HttpRequest request) { .set_header("content-type", "text/plain") .set_payload(greeting()); } + +gcf::Function hello_world_http() { + return gcf::MakeFunction(hello_world_http_impl); +} // [END functions_helloworld_http] diff --git a/examples/site/hello_world_pubsub/hello_world_pubsub.cc b/examples/site/hello_world_pubsub/hello_world_pubsub.cc index 779ef27d..7eaf20c0 100644 --- a/examples/site/hello_world_pubsub/hello_world_pubsub.cc +++ b/examples/site/hello_world_pubsub/hello_world_pubsub.cc @@ -13,16 +13,14 @@ // limitations under the License. // [START functions_helloworld_pubsub] -#include +#include #include #include #include namespace gcf = ::google::cloud::functions; -// Though not used in this example, the event is passed by value to support -// applications that move-out its data. -void hello_world_pubsub(gcf::CloudEvent event) { // NOLINT +void hello_world_pubsub_impl(gcf::CloudEvent const& event) { if (event.data_content_type().value_or("") != "application/json") { BOOST_LOG_TRIVIAL(error) << "expected application/json data"; return; @@ -32,4 +30,8 @@ void hello_world_pubsub(gcf::CloudEvent event) { // NOLINT payload["message"]["data"].get()); BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); } + +gcf::Function hello_world_pubsub() { + return gcf::MakeFunction(hello_world_pubsub_impl); +} // [END functions_helloworld_pubsub] diff --git a/examples/site/hello_world_storage/hello_world_storage.cc b/examples/site/hello_world_storage/hello_world_storage.cc index 4687288a..428bca64 100644 --- a/examples/site/hello_world_storage/hello_world_storage.cc +++ b/examples/site/hello_world_storage/hello_world_storage.cc @@ -13,15 +13,13 @@ // limitations under the License. // [START functions_helloworld_storage] -#include +#include #include #include namespace gcf = ::google::cloud::functions; -// Though not used in this example, the event is passed by value to support -// applications that move-out its data. -void hello_world_storage(gcf::CloudEvent event) { // NOLINT +void hello_world_storage_impl(gcf::CloudEvent const& event) { if (event.data_content_type().value_or("") != "application/json") { BOOST_LOG_TRIVIAL(error) << "expected application/json data"; return; @@ -36,4 +34,8 @@ void hello_world_storage(gcf::CloudEvent event) { // NOLINT BOOST_LOG_TRIVIAL(info) << "Created: " << payload.value("timeCreated", ""); BOOST_LOG_TRIVIAL(info) << "Updated: " << payload.value("updated", ""); } + +gcf::Function hello_world_storage() { + return gcf::MakeFunction(hello_world_storage_impl); +} // [END functions_helloworld_storage] diff --git a/examples/site/howto_create_container/README.md b/examples/site/howto_create_container/README.md index 41000677..dcbe2d26 100644 --- a/examples/site/howto_create_container/README.md +++ b/examples/site/howto_create_container/README.md @@ -30,25 +30,26 @@ In this guide we will be using this [function][snippet source]: [snippet source]: /examples/site/hello_world_http/hello_world_http.cc ```cc -#include -#include +#include #include namespace gcf = ::google::cloud::functions; -gcf::HttpResponse hello_world_http(gcf::HttpRequest request) { - auto greeting = [r = std::move(request)] { - auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, - /*allow_exceptions=*/false); - if (request_json.count("name") && request_json["name"].is_string()) { - return "Hello " + request_json.value("name", "World") + "!"; - } - return std::string("Hello World!"); - }; - - return gcf::HttpResponse{} - .set_header("content-type", "text/plain") - .set_payload(greeting()); +gcf::Function hello_world_http() { + return gcf::MakeFunction([](gcf::HttpRequest const& request) { + auto greeting = [r = std::move(request)] { + auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, + /*allow_exceptions=*/false); + if (request_json.count("name") && request_json["name"].is_string()) { + return "Hello " + request_json.value("name", "World") + "!"; + } + return std::string("Hello World!"); + }; + + return gcf::HttpResponse{} + .set_header("content-type", "text/plain") + .set_payload(greeting()); + }); } ``` @@ -85,7 +86,6 @@ containing your function: ```shell pack build \ --builder gcr.io/buildpacks/builder:latest \ - --env GOOGLE_FUNCTION_SIGNATURE_TYPE=http \ --env GOOGLE_FUNCTION_TARGET=hello_world_http \ --path examples/site/hello_world_http \ gcf-cpp-hello-world-http diff --git a/examples/site/howto_deploy_cloud_event/README.md b/examples/site/howto_deploy_cloud_event/README.md index fb7369aa..c7224e28 100644 --- a/examples/site/howto_deploy_cloud_event/README.md +++ b/examples/site/howto_deploy_cloud_event/README.md @@ -33,22 +33,24 @@ In this guide we will be using this [function][snippet source]: [snippet source]: /examples/site/hello_world_pubsub/hello_world_pubsub.cc ```cc -#include +#include #include #include #include namespace gcf = ::google::cloud::functions; -void hello_world_pubsub(gcf::CloudEvent event) { - if (event.data_content_type().value_or("") != "application/json") { - BOOST_LOG_TRIVIAL(error) << "expected application/json data"; - return; - } - auto const payload = nlohmann::json::parse(event.data().value_or("{}")); - auto const name = cppcodec::base64_rfc4648::decode( - payload["message"]["data"].get()); - BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); +gcf::Function hello_world_pubsub() { + return gcf::MakeFunction([](gcf::CloudEvent const& event) { + if (event.data_content_type().value_or("") != "application/json") { + BOOST_LOG_TRIVIAL(error) << "expected application/json data"; + return; + } + auto const payload = nlohmann::json::parse(event.data().value_or("{}")); + auto const name = cppcodec::base64_rfc4648::decode( + payload["message"]["data"].get()); + BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); + }); } ``` @@ -87,7 +89,6 @@ containing your function: GOOGLE_CLOUD_PROJECT=... # put the right value here pack build \ --builder gcr.io/buildpacks/builder:latest \ - --env GOOGLE_FUNCTION_SIGNATURE_TYPE=cloudevent \ --env GOOGLE_FUNCTION_TARGET=hello_world_pubsub \ --path examples/site/hello_world_pubsub \ "gcr.io/${GOOGLE_CLOUD_PROJECT}/gcf-cpp-hello-world-pubsub" diff --git a/examples/site/howto_deploy_to_cloud_run/README.md b/examples/site/howto_deploy_to_cloud_run/README.md index c05e45e8..d6c0e15f 100644 --- a/examples/site/howto_deploy_to_cloud_run/README.md +++ b/examples/site/howto_deploy_to_cloud_run/README.md @@ -43,25 +43,26 @@ In this guide we will be using this [function][snippet source]: [snippet source]: /examples/site/hello_world_http/hello_world_http.cc ```cc -#include -#include +#include #include namespace gcf = ::google::cloud::functions; -gcf::HttpResponse hello_world_http(gcf::HttpRequest request) { - auto greeting = [r = std::move(request)] { - auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, - /*allow_exceptions=*/false); - if (request_json.count("name") && request_json["name"].is_string()) { - return "Hello " + request_json.value("name", "World") + "!"; - } - return std::string("Hello World!"); - }; - - return gcf::HttpResponse{} - .set_header("content-type", "text/plain") - .set_payload(greeting()); +gcf::Function hello_world_http() { + return gcf::MakeFunction([](gcf::HttpRequest const& request) { + auto greeting = [r = std::move(request)] { + auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, + /*allow_exceptions=*/false); + if (request_json.count("name") && request_json["name"].is_string()) { + return "Hello " + request_json.value("name", "World") + "!"; + } + return std::string("Hello World!"); + }; + + return gcf::HttpResponse{} + .set_header("content-type", "text/plain") + .set_payload(greeting()); + }); } ``` @@ -100,7 +101,6 @@ containing your function: GOOGLE_CLOUD_PROJECT=... # put the right value here pack build \ --builder gcr.io/buildpacks/builder:latest \ - --env GOOGLE_FUNCTION_SIGNATURE_TYPE=http \ --env GOOGLE_FUNCTION_TARGET=hello_world_http \ --path examples/site/hello_world_http \ "gcr.io/${GOOGLE_CLOUD_PROJECT}/gcf-cpp-hello-world-http" diff --git a/examples/site/howto_local_development/CMakeLists.txt b/examples/site/howto_local_development/CMakeLists.txt index 455e2e99..8cfdd3e5 100644 --- a/examples/site/howto_local_development/CMakeLists.txt +++ b/examples/site/howto_local_development/CMakeLists.txt @@ -18,7 +18,7 @@ # CMake-based projects. cmake_minimum_required(VERSION 3.5) -project(functions-framework-cpp-howto-local-development CXX C) +project(functions-framework-cpp-howto-local-development CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/examples/site/howto_local_development/local_server.cc b/examples/site/howto_local_development/local_server.cc index 9a511e17..0cd00951 100644 --- a/examples/site/howto_local_development/local_server.cc +++ b/examples/site/howto_local_development/local_server.cc @@ -19,14 +19,14 @@ namespace gcf = ::google::cloud::functions; namespace { -gcf::HttpResponse HelloWithShutdown(gcf::HttpRequest const& /*request*/) { - return gcf::HttpResponse{} - .set_header("Content-Type", "text/plain") - .set_payload("Hello World\n"); +gcf::Function HelloLocal() { + return gcf::MakeFunction([](gcf::HttpRequest const& /*request*/) { + return gcf::HttpResponse{} + .set_header("Content-Type", "text/plain") + .set_payload("Hello World\n"); + }); } } // namespace -int main(int argc, char* argv[]) { - return ::google::cloud::functions::Run(argc, argv, HelloWithShutdown); -} +int main(int argc, char* argv[]) { return gcf::Run(argc, argv, HelloLocal()); } diff --git a/examples/site/testing_http/http_integration_server.cc b/examples/site/testing_http/http_integration_server.cc index 5d8e3abd..09727aec 100644 --- a/examples/site/testing_http/http_integration_server.cc +++ b/examples/site/testing_http/http_integration_server.cc @@ -15,8 +15,8 @@ #include namespace gcf = ::google::cloud::functions; -extern gcf::HttpResponse hello_world_http(gcf::HttpRequest request); +extern gcf::Function hello_world_http(); int main(int argc, char* argv[]) { - return gcf::Run(argc, argv, hello_world_http); + return gcf::Run(argc, argv, hello_world_http()); } diff --git a/examples/site/testing_http/http_unit_test.cc b/examples/site/testing_http/http_unit_test.cc index 040592e3..ce13067c 100644 --- a/examples/site/testing_http/http_unit_test.cc +++ b/examples/site/testing_http/http_unit_test.cc @@ -18,20 +18,20 @@ #include namespace gcf = ::google::cloud::functions; -extern gcf::HttpResponse hello_world_http(gcf::HttpRequest request); +extern gcf::HttpResponse hello_world_http_impl(gcf::HttpRequest request); namespace { TEST(HttpUnitTest, Success) { - auto actual = hello_world_http( + auto actual = hello_world_http_impl( gcf::HttpRequest{}.set_payload(R"js({ "name": "Foo" })js")); EXPECT_EQ(actual.payload(), "Hello Foo!"); - actual = hello_world_http( + actual = hello_world_http_impl( gcf::HttpRequest{}.set_payload(R"js({ "unused": 7 })js")); EXPECT_EQ(actual.payload(), "Hello World!"); - actual = hello_world_http(gcf::HttpRequest{}.set_payload("Bar")); + actual = hello_world_http_impl(gcf::HttpRequest{}.set_payload("Bar")); EXPECT_EQ(actual.payload(), "Hello World!"); } diff --git a/examples/site/testing_pubsub/README.md b/examples/site/testing_pubsub/README.md index 542f1531..f4e78fcf 100644 --- a/examples/site/testing_pubsub/README.md +++ b/examples/site/testing_pubsub/README.md @@ -12,22 +12,24 @@ We will use this [function][snippet source] throughput this guide: [snippet source]: /examples/site/hello_world_pubsub/hello_world_pubsub.cc ```cc -#include +#include #include #include #include namespace gcf = ::google::cloud::functions; -void hello_world_pubsub(gcf::CloudEvent event) { - if (event.data_content_type().value_or("") != "application/json") { - BOOST_LOG_TRIVIAL(error) << "expected application/json data"; - return; - } - auto const payload = nlohmann::json::parse(event.data().value_or("{}")); - auto const name = cppcodec::base64_rfc4648::decode( - payload["message"]["data"].get()); - BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); +gcf::Function hello_world_pubsub() { + return gcf::MakeFunction([](gcf::CloudEvent const& event) { + if (event.data_content_type().value_or("") != "application/json") { + BOOST_LOG_TRIVIAL(error) << "expected application/json data"; + return; + } + auto const payload = nlohmann::json::parse(event.data().value_or("{}")); + auto const name = cppcodec::base64_rfc4648::decode( + payload["message"]["data"].get()); + BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); + }); } ``` diff --git a/examples/site/testing_pubsub/pubsub_integration_server.cc b/examples/site/testing_pubsub/pubsub_integration_server.cc index d8f4a21b..2d71cef4 100644 --- a/examples/site/testing_pubsub/pubsub_integration_server.cc +++ b/examples/site/testing_pubsub/pubsub_integration_server.cc @@ -15,8 +15,8 @@ #include namespace gcf = ::google::cloud::functions; -extern void hello_world_pubsub(gcf::CloudEvent event); +extern gcf::Function hello_world_pubsub(); int main(int argc, char* argv[]) { - return gcf::Run(argc, argv, hello_world_pubsub); + return gcf::Run(argc, argv, hello_world_pubsub()); } diff --git a/examples/site/testing_pubsub/pubsub_unit_test.cc b/examples/site/testing_pubsub/pubsub_unit_test.cc index a0dd1b07..140d83b0 100644 --- a/examples/site/testing_pubsub/pubsub_unit_test.cc +++ b/examples/site/testing_pubsub/pubsub_unit_test.cc @@ -24,7 +24,7 @@ #include namespace gcf = ::google::cloud::functions; -extern void hello_world_pubsub(gcf::CloudEvent event); +extern void hello_world_pubsub_impl(gcf::CloudEvent const& event); namespace { @@ -65,7 +65,7 @@ TEST(PubsubUnitTest, Basic) { event.set_data_content_type("application/json"); event.set_data(test.data); stream->str({}); - EXPECT_NO_THROW(hello_world_pubsub(event)); + EXPECT_NO_THROW(hello_world_pubsub_impl(event)); auto log_lines = stream->str(); EXPECT_THAT(log_lines, HasSubstr(test.expected)); } diff --git a/examples/site/testing_storage/README.md b/examples/site/testing_storage/README.md index ba3beea0..3c83003c 100644 --- a/examples/site/testing_storage/README.md +++ b/examples/site/testing_storage/README.md @@ -13,26 +13,28 @@ We will use this [function][snippet source] throughout this guide: [snippet source]: /examples/site/hello_world_storage/hello_world_storage.cc ```cc -#include +#include #include #include namespace gcf = ::google::cloud::functions; -void hello_world_storage(gcf::CloudEvent event) { - if (event.data_content_type().value_or("") != "application/json") { - BOOST_LOG_TRIVIAL(error) << "expected application/json data"; - return; - } - auto const payload = nlohmann::json::parse(event.data().value_or("{}")); - BOOST_LOG_TRIVIAL(info) << "Event: " << event.id(); - BOOST_LOG_TRIVIAL(info) << "Event Type: " << event.type(); - BOOST_LOG_TRIVIAL(info) << "Bucket: " << payload.value("bucket", ""); - BOOST_LOG_TRIVIAL(info) << "Object: " << payload.value("name", ""); - BOOST_LOG_TRIVIAL(info) << "Metageneration: " - << payload.value("metageneration", ""); - BOOST_LOG_TRIVIAL(info) << "Created: " << payload.value("timeCreated", ""); - BOOST_LOG_TRIVIAL(info) << "Updated: " << payload.value("updated", ""); +gcf::Function hello_world_storage() { + return gcf::MakeFunction([](gcf::CloudEvent const& event) { + if (event.data_content_type().value_or("") != "application/json") { + BOOST_LOG_TRIVIAL(error) << "expected application/json data"; + return; + } + auto const payload = nlohmann::json::parse(event.data().value_or("{}")); + BOOST_LOG_TRIVIAL(info) << "Event: " << event.id(); + BOOST_LOG_TRIVIAL(info) << "Event Type: " << event.type(); + BOOST_LOG_TRIVIAL(info) << "Bucket: " << payload.value("bucket", ""); + BOOST_LOG_TRIVIAL(info) << "Object: " << payload.value("name", ""); + BOOST_LOG_TRIVIAL(info) + << "Metageneration: " << payload.value("metageneration", ""); + BOOST_LOG_TRIVIAL(info) << "Created: " << payload.value("timeCreated", ""); + BOOST_LOG_TRIVIAL(info) << "Updated: " << payload.value("updated", ""); + }); } ``` diff --git a/examples/site/testing_storage/storage_integration_server.cc b/examples/site/testing_storage/storage_integration_server.cc index f8781910..ee480736 100644 --- a/examples/site/testing_storage/storage_integration_server.cc +++ b/examples/site/testing_storage/storage_integration_server.cc @@ -15,8 +15,8 @@ #include namespace gcf = ::google::cloud::functions; -extern void hello_world_storage(gcf::CloudEvent event); +extern gcf::Function hello_world_storage(); int main(int argc, char* argv[]) { - return gcf::Run(argc, argv, hello_world_storage); + return gcf::Run(argc, argv, hello_world_storage()); } diff --git a/examples/site/testing_storage/storage_unit_test.cc b/examples/site/testing_storage/storage_unit_test.cc index af1393ab..77781680 100644 --- a/examples/site/testing_storage/storage_unit_test.cc +++ b/examples/site/testing_storage/storage_unit_test.cc @@ -24,7 +24,7 @@ #include namespace gcf = ::google::cloud::functions; -extern void hello_world_storage(gcf::CloudEvent event); +extern void hello_world_storage_impl(gcf::CloudEvent const& event); namespace { @@ -74,7 +74,7 @@ TEST(StorageUnitTest, Basic) { data["name"] = test.name; event.set_data(data.dump()); stream->str({}); - EXPECT_NO_THROW(hello_world_storage(event)); + EXPECT_NO_THROW(hello_world_storage_impl(event)); auto log_lines = stream->str(); EXPECT_THAT(log_lines, HasSubstr(test.expected)); } diff --git a/examples/site_test.cc b/examples/site_test.cc index b7f18cb6..852a503a 100644 --- a/examples/site_test.cc +++ b/examples/site_test.cc @@ -12,9 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "google/cloud/functions/internal/function_impl.h" +#include "google/cloud/functions/internal/http_message_types.h" #include "google/cloud/functions/internal/parse_cloud_event_json.h" #include "google/cloud/functions/internal/setenv.h" #include "google/cloud/functions/cloud_event.h" +#include "google/cloud/functions/function.h" #include "google/cloud/functions/http_request.h" #include "google/cloud/functions/http_response.h" #include @@ -26,6 +29,8 @@ #include namespace gcf = ::google::cloud::functions; +namespace gcf_internal = ::google::cloud::functions_internal; + extern gcf::HttpResponse bearer_token(gcf::HttpRequest request); extern gcf::HttpResponse concepts_after_response(gcf::HttpRequest request); extern gcf::HttpResponse concepts_after_timeout(gcf::HttpRequest request); @@ -35,9 +40,9 @@ extern gcf::HttpResponse concepts_stateless(gcf::HttpRequest request); extern gcf::HttpResponse env_vars(gcf::HttpRequest request); extern gcf::HttpResponse hello_world_error(gcf::HttpRequest request); extern gcf::HttpResponse hello_world_get(gcf::HttpRequest request); -extern gcf::HttpResponse hello_world_http(gcf::HttpRequest request); -extern void hello_world_pubsub(gcf::CloudEvent event); -extern void hello_world_storage(gcf::CloudEvent event); +extern gcf::Function hello_world_http(); +extern gcf::Function hello_world_pubsub(); +extern gcf::Function hello_world_storage(); extern gcf::HttpResponse http_content(gcf::HttpRequest request); extern gcf::HttpResponse http_cors(gcf::HttpRequest request); extern gcf::HttpResponse http_cors_auth(gcf::HttpRequest request); @@ -59,6 +64,18 @@ using ::testing::AllOf; using ::testing::HasSubstr; using ::testing::IsEmpty; +auto TriggerFunctionHttp(gcf::Function const& function, + gcf::HttpRequest const& r) { + gcf_internal::BeastRequest request; + request.target(r.target()); + request.body() = r.payload(); + for (auto const& [k, v] : r.headers()) request.insert(k, v); + + auto handler = + gcf_internal::FunctionImpl::GetImpl(function)->GetHandler("unused"); + return handler(std::move(request)); +} + TEST(ExamplesSiteTest, BearerToken) { google::cloud::functions_internal::SetEnv("TARGET_URL", std::nullopt); google::cloud::functions_internal::SetEnv("GOOGLE_APPLICATION_CREDENTIALS", @@ -190,16 +207,17 @@ TEST(ExamplesSiteTest, HelloWorldGet) { } TEST(ExamplesSiteTest, HelloWorlHttp) { - auto actual = hello_world_http( - gcf::HttpRequest{}.set_payload(R"js({ "name": "Foo" })js")); - EXPECT_EQ(actual.payload(), "Hello Foo!"); + auto function = hello_world_http(); + auto actual = TriggerFunctionHttp( + function, gcf::HttpRequest{}.set_payload(R"js({ "name": "Foo" })js")); + EXPECT_EQ(actual.body(), "Hello Foo!"); - actual = hello_world_http( - gcf::HttpRequest{}.set_payload(R"js({ "unused": 7 })js")); - EXPECT_EQ(actual.payload(), "Hello World!"); + actual = TriggerFunctionHttp( + function, gcf::HttpRequest{}.set_payload(R"js({ "unused": 7 })js")); + EXPECT_EQ(actual.body(), "Hello World!"); - actual = hello_world_http(gcf::HttpRequest{}.set_payload("Bar")); - EXPECT_EQ(actual.payload(), "Hello World!"); + actual = TriggerFunctionHttp(function, gcf::HttpRequest{}.set_payload("Bar")); + EXPECT_EQ(actual.body(), "Hello World!"); } TEST(ExamplesSiteTest, HelloWorldPubSub) { @@ -228,12 +246,17 @@ TEST(ExamplesSiteTest, HelloWorldPubSub) { for (auto const* data : {"dGVzdCBtZXNzYWdlIDM=", "YWJjZA==", ""}) { auto json = base; json["data"]["message"]["data"] = data; - EXPECT_NO_THROW(hello_world_pubsub( - google::cloud::functions_internal::ParseCloudEventJson(json.dump()))); + auto response = + TriggerFunctionHttp(hello_world_pubsub(), + gcf::HttpRequest{} + .set_payload(json.dump()) + .add_header("ce-type", "com.example.someevent") + .add_header("ce-source", "/mycontext") + .add_header("ce-id", "A234-1234-1234")); + EXPECT_EQ(response.result_int(), 200); } - EXPECT_NO_THROW(hello_world_pubsub( - google::cloud::functions_internal::ParseCloudEventJson(R"js({ + auto constexpr kBodyDataText = R"js({ "specversion": "1.0", "type": "test.invalid.invalid", "source": "//pubsub.googleapis.com/projects/sample-project/topics/gcf-test", @@ -241,10 +264,8 @@ TEST(ExamplesSiteTest, HelloWorldPubSub) { "time": "2020-09-29T11:32:00.000Z", "datacontenttype": "text/plain", "data": "some data" - })js"))); - - EXPECT_THROW(hello_world_pubsub( - google::cloud::functions_internal::ParseCloudEventJson(R"js({ + })js"; + auto constexpr kBodyDataJson = R"js({ "specversion": "1.0", "type": "google.cloud.pubsub.topic.v1.messagePublished", "source": "//pubsub.googleapis.com/projects/sample-project/topics/gcf-test", @@ -255,8 +276,27 @@ TEST(ExamplesSiteTest, HelloWorldPubSub) { "subscription": "projects/sample-project/subscriptions/sample-subscription" } } - })js")), - std::exception); + })js"; + + struct TestCase { + std::string name; + std::string body; + } cases[] = { + {"text", kBodyDataText}, + {"json", kBodyDataJson}, + }; + + for (auto const& [name, body] : cases) { + SCOPED_TRACE("Testing for " + name); + auto response = + TriggerFunctionHttp(hello_world_storage(), + gcf::HttpRequest{} + .set_payload(body) + .add_header("ce-type", "com.example.someevent") + .add_header("ce-source", "/mycontext") + .add_header("ce-id", "A234-1234-1234")); + EXPECT_EQ(response.result_int(), 200); + } } TEST(ExamplesSiteTest, HelloWorldStorage) { @@ -291,11 +331,7 @@ TEST(ExamplesSiteTest, HelloWorldStorage) { } })js"); - EXPECT_NO_THROW(hello_world_storage( - google::cloud::functions_internal::ParseCloudEventJson(base.dump()))); - - EXPECT_NO_THROW(hello_world_storage( - google::cloud::functions_internal::ParseCloudEventJson(R"js({ + auto constexpr kBodyDataText = R"js({ "specversion": "1.0", "type": "test.invalid.invalid", "source": "//pubsub.googleapis.com/projects/sample-project/topics/gcf-test", @@ -303,17 +339,37 @@ TEST(ExamplesSiteTest, HelloWorldStorage) { "time": "2020-09-29T11:32:00.000Z", "datacontenttype": "text/plain", "data": "some data" - })js"))); + })js"; - EXPECT_NO_THROW(hello_world_storage( - google::cloud::functions_internal::ParseCloudEventJson(R"js({ + auto constexpr kBodyDataJson = R"js({ "specversion": "1.0", "type": "test.invalid.invalid", "source": "//pubsub.googleapis.com/projects/sample-project/topics/gcf-test", "id": "aaaaaa-1111-bbbb-2222-cccccccccccc", "time": "2020-09-29T11:32:00.000Z", "datacontenttype": "application/json" - })js"))); + })js"; + + struct TestCase { + std::string name; + std::string body; + } cases[] = { + {"base", base.dump()}, + {"text", kBodyDataText}, + {"json", kBodyDataJson}, + }; + + for (auto const& [name, body] : cases) { + SCOPED_TRACE("Testing for " + name); + auto response = + TriggerFunctionHttp(hello_world_storage(), + gcf::HttpRequest{} + .set_payload(body) + .add_header("ce-type", "com.example.someevent") + .add_header("ce-source", "/mycontext") + .add_header("ce-id", "A234-1234-1234")); + EXPECT_EQ(response.result_int(), 200); + } } TEST(ExamplesSiteTest, HttpContent) { From c8d567046a20880d7c48dc69bc0cd9bf256c95a6 Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Tue, 4 Jul 2023 17:06:44 +0000 Subject: [PATCH 2/2] Update markdown --- .../site/howto_create_container/README.md | 30 +++++++++-------- .../site/howto_deploy_cloud_event/README.md | 22 +++++++------ .../site/howto_deploy_to_cloud_run/README.md | 30 +++++++++-------- examples/site/testing_pubsub/README.md | 22 +++++++------ examples/site/testing_storage/README.md | 32 ++++++++++--------- 5 files changed, 73 insertions(+), 63 deletions(-) diff --git a/examples/site/howto_create_container/README.md b/examples/site/howto_create_container/README.md index dcbe2d26..c0e22ba0 100644 --- a/examples/site/howto_create_container/README.md +++ b/examples/site/howto_create_container/README.md @@ -35,21 +35,23 @@ In this guide we will be using this [function][snippet source]: namespace gcf = ::google::cloud::functions; +gcf::HttpResponse hello_world_http_impl(gcf::HttpRequest request) { + auto greeting = [r = std::move(request)] { + auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, + /*allow_exceptions=*/false); + if (request_json.count("name") && request_json["name"].is_string()) { + return "Hello " + request_json.value("name", "World") + "!"; + } + return std::string("Hello World!"); + }; + + return gcf::HttpResponse{} + .set_header("content-type", "text/plain") + .set_payload(greeting()); +} + gcf::Function hello_world_http() { - return gcf::MakeFunction([](gcf::HttpRequest const& request) { - auto greeting = [r = std::move(request)] { - auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, - /*allow_exceptions=*/false); - if (request_json.count("name") && request_json["name"].is_string()) { - return "Hello " + request_json.value("name", "World") + "!"; - } - return std::string("Hello World!"); - }; - - return gcf::HttpResponse{} - .set_header("content-type", "text/plain") - .set_payload(greeting()); - }); + return gcf::MakeFunction(hello_world_http_impl); } ``` diff --git a/examples/site/howto_deploy_cloud_event/README.md b/examples/site/howto_deploy_cloud_event/README.md index c7224e28..4f234d49 100644 --- a/examples/site/howto_deploy_cloud_event/README.md +++ b/examples/site/howto_deploy_cloud_event/README.md @@ -40,17 +40,19 @@ In this guide we will be using this [function][snippet source]: namespace gcf = ::google::cloud::functions; +void hello_world_pubsub_impl(gcf::CloudEvent const& event) { + if (event.data_content_type().value_or("") != "application/json") { + BOOST_LOG_TRIVIAL(error) << "expected application/json data"; + return; + } + auto const payload = nlohmann::json::parse(event.data().value_or("{}")); + auto const name = cppcodec::base64_rfc4648::decode( + payload["message"]["data"].get()); + BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); +} + gcf::Function hello_world_pubsub() { - return gcf::MakeFunction([](gcf::CloudEvent const& event) { - if (event.data_content_type().value_or("") != "application/json") { - BOOST_LOG_TRIVIAL(error) << "expected application/json data"; - return; - } - auto const payload = nlohmann::json::parse(event.data().value_or("{}")); - auto const name = cppcodec::base64_rfc4648::decode( - payload["message"]["data"].get()); - BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); - }); + return gcf::MakeFunction(hello_world_pubsub_impl); } ``` diff --git a/examples/site/howto_deploy_to_cloud_run/README.md b/examples/site/howto_deploy_to_cloud_run/README.md index d6c0e15f..e6e4bc44 100644 --- a/examples/site/howto_deploy_to_cloud_run/README.md +++ b/examples/site/howto_deploy_to_cloud_run/README.md @@ -48,21 +48,23 @@ In this guide we will be using this [function][snippet source]: namespace gcf = ::google::cloud::functions; +gcf::HttpResponse hello_world_http_impl(gcf::HttpRequest request) { + auto greeting = [r = std::move(request)] { + auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, + /*allow_exceptions=*/false); + if (request_json.count("name") && request_json["name"].is_string()) { + return "Hello " + request_json.value("name", "World") + "!"; + } + return std::string("Hello World!"); + }; + + return gcf::HttpResponse{} + .set_header("content-type", "text/plain") + .set_payload(greeting()); +} + gcf::Function hello_world_http() { - return gcf::MakeFunction([](gcf::HttpRequest const& request) { - auto greeting = [r = std::move(request)] { - auto request_json = nlohmann::json::parse(r.payload(), /*cb=*/nullptr, - /*allow_exceptions=*/false); - if (request_json.count("name") && request_json["name"].is_string()) { - return "Hello " + request_json.value("name", "World") + "!"; - } - return std::string("Hello World!"); - }; - - return gcf::HttpResponse{} - .set_header("content-type", "text/plain") - .set_payload(greeting()); - }); + return gcf::MakeFunction(hello_world_http_impl); } ``` diff --git a/examples/site/testing_pubsub/README.md b/examples/site/testing_pubsub/README.md index f4e78fcf..2bcd6958 100644 --- a/examples/site/testing_pubsub/README.md +++ b/examples/site/testing_pubsub/README.md @@ -19,17 +19,19 @@ We will use this [function][snippet source] throughput this guide: namespace gcf = ::google::cloud::functions; +void hello_world_pubsub_impl(gcf::CloudEvent const& event) { + if (event.data_content_type().value_or("") != "application/json") { + BOOST_LOG_TRIVIAL(error) << "expected application/json data"; + return; + } + auto const payload = nlohmann::json::parse(event.data().value_or("{}")); + auto const name = cppcodec::base64_rfc4648::decode( + payload["message"]["data"].get()); + BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); +} + gcf::Function hello_world_pubsub() { - return gcf::MakeFunction([](gcf::CloudEvent const& event) { - if (event.data_content_type().value_or("") != "application/json") { - BOOST_LOG_TRIVIAL(error) << "expected application/json data"; - return; - } - auto const payload = nlohmann::json::parse(event.data().value_or("{}")); - auto const name = cppcodec::base64_rfc4648::decode( - payload["message"]["data"].get()); - BOOST_LOG_TRIVIAL(info) << "Hello " << (name.empty() ? "World" : name); - }); + return gcf::MakeFunction(hello_world_pubsub_impl); } ``` diff --git a/examples/site/testing_storage/README.md b/examples/site/testing_storage/README.md index 3c83003c..edb0b78a 100644 --- a/examples/site/testing_storage/README.md +++ b/examples/site/testing_storage/README.md @@ -19,22 +19,24 @@ We will use this [function][snippet source] throughout this guide: namespace gcf = ::google::cloud::functions; +void hello_world_storage_impl(gcf::CloudEvent const& event) { + if (event.data_content_type().value_or("") != "application/json") { + BOOST_LOG_TRIVIAL(error) << "expected application/json data"; + return; + } + auto const payload = nlohmann::json::parse(event.data().value_or("{}")); + BOOST_LOG_TRIVIAL(info) << "Event: " << event.id(); + BOOST_LOG_TRIVIAL(info) << "Event Type: " << event.type(); + BOOST_LOG_TRIVIAL(info) << "Bucket: " << payload.value("bucket", ""); + BOOST_LOG_TRIVIAL(info) << "Object: " << payload.value("name", ""); + BOOST_LOG_TRIVIAL(info) << "Metageneration: " + << payload.value("metageneration", ""); + BOOST_LOG_TRIVIAL(info) << "Created: " << payload.value("timeCreated", ""); + BOOST_LOG_TRIVIAL(info) << "Updated: " << payload.value("updated", ""); +} + gcf::Function hello_world_storage() { - return gcf::MakeFunction([](gcf::CloudEvent const& event) { - if (event.data_content_type().value_or("") != "application/json") { - BOOST_LOG_TRIVIAL(error) << "expected application/json data"; - return; - } - auto const payload = nlohmann::json::parse(event.data().value_or("{}")); - BOOST_LOG_TRIVIAL(info) << "Event: " << event.id(); - BOOST_LOG_TRIVIAL(info) << "Event Type: " << event.type(); - BOOST_LOG_TRIVIAL(info) << "Bucket: " << payload.value("bucket", ""); - BOOST_LOG_TRIVIAL(info) << "Object: " << payload.value("name", ""); - BOOST_LOG_TRIVIAL(info) - << "Metageneration: " << payload.value("metageneration", ""); - BOOST_LOG_TRIVIAL(info) << "Created: " << payload.value("timeCreated", ""); - BOOST_LOG_TRIVIAL(info) << "Updated: " << payload.value("updated", ""); - }); + return gcf::MakeFunction(hello_world_storage_impl); } ```