diff --git a/CODEOWNERS b/CODEOWNERS
index bfba7f9e..e6885e9e 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -31,10 +31,11 @@
/tools/apigee-x-trial-provision @yuriylesyuk @danistrebel
/tools/decrypt-hybrid-assets @yuriylesyuk
/tools/endpoints-oas-importer @danistrebel
+/tools/grpc-http-gateway-generator @danistrebel @omidtahouri
/tools/hybrid-quickstart @danistrebel
/tools/oas-apigee-mock @markjkelly
/tools/pipeline-linter @seymen @danistrebel
/tools/pipeline-runner @seymen @danistrebel
-/tools/sf-dependency-list @yuriylesyuk
/tools/proxy-endpoint-unifier @anaik91
-/tools/target-server-validator @anaik91
+/tools/sf-dependency-list @yuriylesyuk
+/tools/target-server-validator @anaik91
\ No newline at end of file
diff --git a/README.md b/README.md
index 92920071..51f17bb4 100644
--- a/README.md
+++ b/README.md
@@ -92,7 +92,8 @@ Apigee products.
A tool to unify/split proxy endpoints based on API basepath.
- [Apigee Target Server Validator](tools/target-server-validator) -
A tool to validate all targets in Target Servers & Apigee API Proxy Bundles.
-
+- [gRPC to HTTP Gateway Generator](tools/grpc-http-gateway-generator) -
+ Generate gateways to expose gRPC services with HTTP API management.
## Labs
This folder contains raw assets used to generate content to teach a particular
diff --git a/tools/grpc-http-gateway-generator/README.md b/tools/grpc-http-gateway-generator/README.md
new file mode 100644
index 00000000..6493d81e
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/README.md
@@ -0,0 +1,76 @@
+# gRPC to HTTP Gateway Generator
+
+The purpose of this tool is to generate a gRPC to HTTP gateway based on a protocol buffer.
+It leverages the [gRPC Gateway](https://github.com/grpc-ecosystem/grpc-gateway) project to generate the gateway code.
+
+The generated gRPC gateway can then be used to create HTTP adapters for gRPC services and use them in a classical HTTP REST-based API management environment.
+
+![Architecture Overview](./img/architecture.png)
+
+## Prerequisites
+
+The generator assumed you have the protobuf compiler `protoc` installed.
+
+Note that `protoc` is pre-installed on Cloud Shell.
+
+To install `protoc` with `apt`, run the following command:
+
+```sh
+apt install -y protobuf-compiler
+```
+
+## Generate the Gateway Code
+
+The gateway is generated based on a protocol buffer file that you supply with the `--proto-path` flag:
+
+```sh
+./generate-gateway.sh --proto-path ./examples/currency.proto
+```
+
+Optionally you can also specify the desired output directory with the `--output` flag. If no output directory is specified the output will be generated in the `./generated/gateway` folder.
+
+## Run the Gateway
+
+You can run the gateway locally with the following commands and point to a gRPC server endpoint that runs on localhost port 9090:
+
+```sh
+(cd generated/gateway && go run main.go --grpc-server-endpoint localhost:9090)
+```
+
+If you preffer to run the gateway as a container use the following commands to build and run the gateway container:
+
+```sh
+(cd generated/gateway && docker build . -t gateway:latest)
+docker run -p 8080:8080 -e GRPC_SERVER_ENDPOINT=localhost:9090 gateway:latest
+```
+
+## Try it out locally
+
+With the gateway running you can call the automatically generated API endpoints of the gateway to consume your gRPC service.
+
+With the gRPC Gateway pointing to an exposed currency service of the [microservice demo](https://github.com/GoogleCloudPlatform/microservices-demo) the currency conversion request would look like this:
+
+```sh
+curl -X POST localhost:8080/hipstershop.CurrencyService/Convert -d '{"from": {"units": 3, "currency_code": "USD", "nanos": 0}, "to_code": "CHF"}'
+```
+
+Whist the code to request supported currencies looks like this:
+
+```sh
+curl -X POST localhost:8080/hipstershop.CurrencyService/GetSupportedCurrencies
+```
+
+Note that the automatically generated API doesn't yet come in a RESTful format. This is one of the aspects that can be changed in an API management layer to improve the overall usability and security of the API.
+
+### Deploy to Cloud Run
+
+Once we've tested the gateway locally we can deploy it to Cloud Run for production use.
+
+```sh
+(cd generated/gateway && gcloud run deploy currency-grpc-gw --source . --allow-unauthenticated --region europe-west1 --project $PROJECT_ID)
+```
+
+## Expose the Cloud Run Service via Apigee
+
+With the Gateway running in Cloud Run we can now expose it via Apigee.
+For detailed instructions on how to expose a cloud run service in Apigee please see [this reference implementation](https://github.com/apigee/devrel/tree/main/references/cloud-run).
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/buf.gen.yaml b/tools/grpc-http-gateway-generator/buf.gen.yaml
new file mode 100644
index 00000000..a3133c24
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/buf.gen.yaml
@@ -0,0 +1,24 @@
+# Copyright 2024 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.
+
+version: v1
+plugins:
+ - plugin: go
+ out: gen/go
+ opt:
+ - paths=source_relative
+ - plugin: go-grpc
+ out: gen/go
+ opt:
+ - paths=source_relative
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/currency-v1.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/currency-v1.xml
new file mode 100644
index 00000000..f0acd847
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/currency-v1.xml
@@ -0,0 +1,18 @@
+
+
+
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/AM.SetConversionPath.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/AM.SetConversionPath.xml
new file mode 100644
index 00000000..5d745abd
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/AM.SetConversionPath.xml
@@ -0,0 +1,21 @@
+
+
+
+ flow.target.pathsuffix
+ /hipstershop.CurrencyService/Convert
+
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/AM.SetCurrenciesPath.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/AM.SetCurrenciesPath.xml
new file mode 100644
index 00000000..43cf6ef4
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/AM.SetCurrenciesPath.xml
@@ -0,0 +1,21 @@
+
+
+
+ flow.target.pathsuffix
+ /hipstershop.CurrencyService/GetSupportedCurrencies
+
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/HM.SetTargetMethod.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/HM.SetTargetMethod.xml
new file mode 100644
index 00000000..ff59942a
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/HM.SetTargetMethod.xml
@@ -0,0 +1,20 @@
+
+
+
+ POST
+
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/JS.SetTargetPath.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/JS.SetTargetPath.xml
new file mode 100644
index 00000000..b7fd841d
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/policies/JS.SetTargetPath.xml
@@ -0,0 +1,18 @@
+
+
+ jsc://set-target-path.js
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/proxies/default.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/proxies/default.xml
new file mode 100644
index 00000000..5195c438
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/proxies/default.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ request.verb = "GET" and proxy.pathsuffix = "/currencies"
+
+
+ AM.SetCurrenciesPath
+
+
+
+
+ request.verb = "POST" and proxy.pathsuffix = "/convert"
+
+
+ AM.SetConversionPath
+
+
+
+
+
+
+
+ HM.SetTargetMethod
+
+
+
+
+ /currency/v1
+
+
+ default
+
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/resources/jsc/set-target-path.js b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/resources/jsc/set-target-path.js
new file mode 100644
index 00000000..1e426c44
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/resources/jsc/set-target-path.js
@@ -0,0 +1,29 @@
+/*
+ Copyright 2024 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.
+*/
+
+context.setVariable("target.copy.pathsuffix", false)
+
+var existingTargetUrl = context.getVariable("target.url");
+var targetPath = context.getVariable("flow.target.pathsuffix") || ""; // retrieve custom path, set by AM policies
+var queryString = context.getVariable("request.querystring") || "";
+
+var updatedTargetUrl = existingTargetUrl + targetPath + (queryString ? "?"+queryString : "");
+
+context.setVariable("target.url",
+ existingTargetUrl + // use target base URL
+ targetPath + // append custom path
+ (queryString ? "?"+queryString : "") // conditionally append query params
+);
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/targets/default.xml b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/targets/default.xml
new file mode 100644
index 00000000..064848df
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/apiproxy/targets/default.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ JS.SetTargetPath
+
+
+
+
+
+
+ CLOUD_RUN_URL
+
+
+ CLOUD_RUN_URL
+
+
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/examples/currency-v1/cloud-run-service.yaml b/tools/grpc-http-gateway-generator/examples/currency-v1/cloud-run-service.yaml
new file mode 100644
index 00000000..6d9ff4f9
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency-v1/cloud-run-service.yaml
@@ -0,0 +1,53 @@
+# Copyright 2024 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.
+
+apiVersion: serving.knative.dev/v1
+kind: Service
+metadata:
+ name: currency-service
+spec:
+ template:
+ spec:
+ containers:
+ - name: grpc-gateway
+ image: GRPC_GATEWAY_IMAGE
+ ports:
+ - name: http1
+ containerPort: 8080
+ resources:
+ limits:
+ cpu: 1000m
+ memory: 512Mi
+ startupProbe:
+ timeoutSeconds: 240
+ periodSeconds: 240
+ failureThreshold: 1
+ tcpSocket:
+ port: 8080
+ - name: currencyservice-1
+ image: gcr.io/google-samples/microservices-demo/currencyservice@sha256:e08a1f5d4e4b74fc3d6222d535a11615e8201e7075a090c1ba4436ef1f1cbe7b
+ env:
+ - name: PORT
+ value: "9090"
+ resources:
+ limits:
+ cpu: 1000m
+ memory: 512Mi
+ startupProbe:
+ timeoutSeconds: 1
+ periodSeconds: 10
+ failureThreshold: 3
+ grpc:
+ port: 9090
+ service: currencyservice-v1
diff --git a/tools/grpc-http-gateway-generator/examples/currency.proto b/tools/grpc-http-gateway-generator/examples/currency.proto
new file mode 100644
index 00000000..d6b2f3ee
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/examples/currency.proto
@@ -0,0 +1,57 @@
+// 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.
+
+syntax = "proto3";
+
+package hipstershop;
+
+// -----------------Currency service-----------------
+
+service CurrencyService {
+ rpc GetSupportedCurrencies(Empty) returns (GetSupportedCurrenciesResponse) {}
+ rpc Convert(CurrencyConversionRequest) returns (Money) {}
+}
+
+message Empty {
+}
+
+// Represents an amount of money with its currency type.
+message Money {
+ // The 3-letter currency code defined in ISO 4217.
+ string currency_code = 1;
+
+ // The whole units of the amount.
+ // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar.
+ int64 units = 2;
+
+ // Number of nano (10^-9) units of the amount.
+ // The value must be between -999,999,999 and +999,999,999 inclusive.
+ // If `units` is positive, `nanos` must be positive or zero.
+ // If `units` is zero, `nanos` can be positive, zero, or negative.
+ // If `units` is negative, `nanos` must be negative or zero.
+ // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000.
+ int32 nanos = 3;
+}
+
+message GetSupportedCurrenciesResponse {
+ // The 3-letter currency code defined in ISO 4217.
+ repeated string currency_codes = 1;
+}
+
+message CurrencyConversionRequest {
+ Money from = 1;
+
+ // The 3-letter currency code defined in ISO 4217.
+ string to_code = 2;
+}
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/generate-gateway.sh b/tools/grpc-http-gateway-generator/generate-gateway.sh
new file mode 100755
index 00000000..b3feb84c
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/generate-gateway.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+# Copyright 2024 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.
+
+
+set -e
+
+# check if protoc is installed
+if ! command -v protoc &> /dev/null; then
+ echo "[ERROR] protoc is not installed. Please install protoc before running this script."
+ exit 1
+fi
+
+proto_path=""
+out=""
+
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --proto-path)
+ proto_path="$2"
+ shift 2
+ ;;
+ --out)
+ out="$2"
+ shift 2
+ ;;
+ --*|-*)
+ echo "Unknown option $1"
+ exit 1
+ ;;
+ esac
+done
+
+# check if proto path is provided
+if [ -z "$proto_path" ]; then
+ echo "[ERROR] Proto path is required supply via variable --proto-path"
+ exit 1
+fi
+
+# check if proto path is a file
+if [ ! -f "$proto_path" ]; then
+ echo "[ERROR] Proto path is not a file"
+ exit 1
+fi
+
+# check if output directory is provided
+if [ -z "$out" ]; then
+ echo "[WARN] Output directory is not provided via variable --out. Using the default value ./generated/gateway"
+ out="$(pwd)/generated/gateway"
+else
+ mkdir -p "$out"
+ out="$(cd "$out"; pwd -P)"
+fi
+
+script_dir="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+
+temp_dir=$(mktemp -d)
+proto_file_name=$(basename "$proto_path")
+cp "$proto_path" "$temp_dir/$proto_file_name"
+
+
+# install tooling dependencies
+pushd "$script_dir/tools" &> /dev/null
+go mod tidy
+go install \
+ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
+ github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
+ google.golang.org/protobuf/cmd/protoc-gen-go \
+ google.golang.org/grpc/cmd/protoc-gen-go-grpc
+PATH="$PATH:$(go env GOPATH)/bin"
+export PATH
+popd &> /dev/null
+
+# Generate the gRPC adapter
+mkdir -p "$out/adapter"
+
+pushd "$temp_dir" &> /dev/null
+protoc -I . \
+ --go_out "$out/adapter" \
+ --go_opt "M$proto_file_name=.;adapter" \
+ --go-grpc_out "$out/adapter" \
+ --go-grpc_opt "M$proto_file_name=.;adapter" \
+ "./$proto_file_name"
+
+protoc -I . --grpc-gateway_out "$out/adapter" \
+ --grpc-gateway_opt "M$proto_file_name=.;adapter" \
+ --grpc-gateway_opt generate_unbound_methods=true \
+ "./$proto_file_name"
+popd &> /dev/null
+
+cp "$script_dir/templates/main.go" "$out/main.go"
+cp "$script_dir/templates/Dockerfile" "$out/Dockerfile"
+
+(cd "$out/adapter" && go mod init adapter &> /dev/null && go mod tidy &> /dev/null)
+(cd "$out" && go mod init gateway &> /dev/null && go mod edit -replace adapter=./adapter && go mod tidy &> /dev/null)
diff --git a/tools/grpc-http-gateway-generator/img/architecture.png b/tools/grpc-http-gateway-generator/img/architecture.png
new file mode 100644
index 00000000..26c63b92
Binary files /dev/null and b/tools/grpc-http-gateway-generator/img/architecture.png differ
diff --git a/tools/grpc-http-gateway-generator/pipeline.sh b/tools/grpc-http-gateway-generator/pipeline.sh
new file mode 100755
index 00000000..3e4c7a44
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/pipeline.sh
@@ -0,0 +1,90 @@
+#!/bin/bash
+
+# Copyright 2024 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.
+
+set -e # exit on first error
+
+SCRIPTPATH="$( cd "$(dirname "$0")" || exit >/dev/null 2>&1 ; pwd -P )"
+export PATH="$PATH:$SCRIPTPATH/../../tools/apigee-sackmesser/bin"
+
+PROJECT_ID=$(gcloud config get-value project)
+GCP_REGION=europe-west2
+
+# Generate the gRCP Gateway based on the proto file
+rm -rdf generated || true
+./generate-gateway.sh --proto-path ./examples/currency.proto
+
+# Build the gRPC Gateway
+(cd generated/gateway && CGO_ENABLED=0 go build -o grpcgateway .)
+
+# Build the grpc-gateway container and push it to Artifact Registry
+(cd generated/gateway && docker build -t grpc-gateway:latest .)
+
+DOCKER_REPO="devrel"
+REPO_LOCATION="europe"
+
+if [ -z "$(gcloud artifacts repositories describe $DOCKER_REPO \
+ --location=$REPO_LOCATION \
+ --project "$PROJECT_ID" \
+ --format='get(name)')" ]; then \
+
+ gcloud artifacts repositories create $DOCKER_REPO \
+ --repository-format=docker \
+ --location=$REPO_LOCATION \
+ --project="$PROJECT_ID"
+fi
+
+IMAGE_PATH="$REPO_LOCATION-docker.pkg.dev/$PROJECT_ID/$DOCKER_REPO/grpc-gateway"
+docker tag grpc-gateway:latest "$IMAGE_PATH:latest"
+docker push "$IMAGE_PATH"
+
+# Deploy grpc-gateway container to Cloud Run
+sed -i.bak "s|GRPC_GATEWAY_IMAGE|$IMAGE_PATH|g" "examples/currency-v1/cloud-run-service.yaml"
+
+gcloud run services replace examples/currency-v1/cloud-run-service.yaml \
+ --project "$PROJECT_ID" --region $GCP_REGION \
+ --platform managed
+
+# Generate and deploy an Apigee API proxy for the currency-service
+SA_EMAIL="apigee-test-cloudrun@$APIGEE_X_ORG.iam.gserviceaccount.com"
+
+if [ -z "$(gcloud iam service-accounts list --filter "$SA_EMAIL" --format="value(email)" --project "$APIGEE_X_ORG")" ]; then
+ gcloud iam service-accounts create apigee-test-cloudrun \
+ --description="Apigee Test Cloud Run" --project "$APIGEE_X_ORG"
+fi
+
+gcloud run services add-iam-policy-binding currency-service \
+ --member="serviceAccount:$SA_EMAIL" \
+ --role='roles/run.invoker' \
+ --region=$GCP_REGION \
+ --platform=managed --project "$PROJECT_ID"
+
+CLOUD_RUN_URL=$(gcloud run services list --filter currency-service --format="value(status.url)" --limit 1)
+sed -i "s|CLOUD_RUN_URL|$CLOUD_RUN_URL|g" "examples/currency-v1/apiproxy/targets/default.xml"
+
+TOKEN="$(gcloud config config-helper --force-auth-refresh --format json | jq -r '.credential.access_token')"
+sackmesser deploy -d "$SCRIPTPATH/examples/currency-v1" -o "$APIGEE_X_ORG" -e "$APIGEE_X_ENV" -t "$TOKEN" --deployment-sa "$SA_EMAIL"
+
+# Test the Apigee API
+curl -X GET "https://$APIGEE_X_HOSTNAME/currency/v1/currencies"
+
+curl -X POST "https://$APIGEE_X_HOSTNAME/currency/v1/convert" \
+ -d '{"from": {"units": 3, "currency_code": "USD", "nanos": 0}, "to_code": "CHF"}'
+
+# Clean up
+ # undeploy and delete proxy
+ # delete CR service
+ # delete SA
+ # delete AR registry
diff --git a/tools/grpc-http-gateway-generator/templates/Dockerfile b/tools/grpc-http-gateway-generator/templates/Dockerfile
new file mode 100644
index 00000000..959ba5af
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/templates/Dockerfile
@@ -0,0 +1,25 @@
+# Copyright 2024 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.
+
+FROM golang:alpine as app-builder
+
+WORKDIR /go/src/app
+COPY . .
+
+RUN CGO_ENABLED=0 go build -o grpcgateway .
+
+FROM scratch
+COPY --from=app-builder /go/src/app/grpcgateway /gateway
+
+ENTRYPOINT ["/gateway"]
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/templates/main.go b/tools/grpc-http-gateway-generator/templates/main.go
new file mode 100644
index 00000000..937d90ed
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/templates/main.go
@@ -0,0 +1,76 @@
+// Copyright 2024 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.
+
+package main
+
+import (
+ "context"
+ "flag"
+ "net/http"
+ "os"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
+ "google.golang.org/grpc/grpclog"
+
+ gw "adapter" // needs to be replaced
+)
+
+var (
+ // command-line options:
+ // gRPC server endpoint
+ grpcServerEndpointFlag = flag.String("grpc-server-endpoint", "", "gRPC server endpoint")
+)
+
+func run() error {
+ ctx := context.Background()
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ // Register gRPC server endpoint
+ mux := runtime.NewServeMux()
+ opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
+
+ grpcServerEndpoint := *grpcServerEndpointFlag
+ if grpcServerEndpoint == "" {
+ grpcServerEndpoint = os.Getenv("GRPC_SERVER_ENDPOINT")
+ }
+ if grpcServerEndpoint == "" {
+ grpcServerEndpoint = "localhost:9090"
+ }
+
+ grpclog.Infof("Upstream gRPC server endpoint = %s", grpcServerEndpoint)
+
+ err := gw.RegisterCurrencyServiceHandlerFromEndpoint(ctx, mux, grpcServerEndpoint, opts)
+ if err != nil {
+ return err
+ }
+
+ // Start HTTP server (and proxy calls to gRPC server endpoint)
+ port := os.Getenv("PORT")
+ if port == "" {
+ port = "8080"
+ }
+ grpclog.Infof("Serving gRPC-Gateway on http://0.0.0.0:%s", port)
+ return http.ListenAndServe( ":" + port, mux)
+}
+
+func main() {
+ flag.Parse()
+
+ if err := run(); err != nil {
+ grpclog.Fatal(err)
+ }
+}
\ No newline at end of file
diff --git a/tools/grpc-http-gateway-generator/tools/go.mod b/tools/grpc-http-gateway-generator/tools/go.mod
new file mode 100644
index 00000000..f296bf1d
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/tools/go.mod
@@ -0,0 +1,19 @@
+module tools
+
+go 1.22.4
+
+require (
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0
+ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0
+ google.golang.org/protobuf v1.34.2
+)
+
+require (
+ github.com/kr/text v0.2.0 // indirect
+ github.com/rogpeppe/go-internal v1.12.0 // indirect
+ golang.org/x/text v0.15.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 // indirect
+ google.golang.org/grpc v1.64.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/tools/grpc-http-gateway-generator/tools/go.sum b/tools/grpc-http-gateway-generator/tools/go.sum
new file mode 100644
index 00000000..07939fa2
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/tools/go.sum
@@ -0,0 +1,32 @@
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8 h1:W5Xj/70xIA4x60O/IFyXivR5MGqblAb8R3w26pnD6No=
+google.golang.org/genproto/googleapis/api v0.0.0-20240513163218-0867130af1f8/go.mod h1:vPrPUTsDCYxXWjP7clS81mZ6/803D8K4iM9Ma27VKas=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8 h1:mxSlqyb8ZAHsYDCfiXN1EDdNTdvjUJSLY+OnAUtYNYA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240513163218-0867130af1f8/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0 h1:9SxA29VM43MF5Z9dQu694wmY5t8E/Gxr7s+RSxiIDmc=
+google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.4.0/go.mod h1:yZOK5zhQMiALmuweVdIVoQPa6eIJyXn2B9g5dJDhqX4=
+google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
+google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tools/grpc-http-gateway-generator/tools/tools.go b/tools/grpc-http-gateway-generator/tools/tools.go
new file mode 100644
index 00000000..f5f270a6
--- /dev/null
+++ b/tools/grpc-http-gateway-generator/tools/tools.go
@@ -0,0 +1,24 @@
+// Copyright 2024 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.
+
+// +build tools
+
+package tools
+
+import (
+ _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway"
+ _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
+ _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
+ _ "google.golang.org/protobuf/cmd/protoc-gen-go"
+)
\ No newline at end of file
diff --git a/tools/pipeline-runner/Dockerfile b/tools/pipeline-runner/Dockerfile
index d4044cf0..27c1ed8d 100644
--- a/tools/pipeline-runner/Dockerfile
+++ b/tools/pipeline-runner/Dockerfile
@@ -35,7 +35,10 @@ RUN apk add --no-cache \
ca-certificates \
ttf-freefont \
py-pip \
- zip
+ zip \
+ make \
+ go \
+ protobuf-dev
# Reduce nighly log (note: -ntp requires maven 3.6.1+)
RUN mv /usr/bin/mvn /usr/bin/_mvn &&\