main 1725a8044b95 cached
316 files
1.4 MB
478.0k tokens
2102 symbols
1 requests
Download .txt
Showing preview only (1,575K chars total). Download the full file or copy to clipboard to get everything.
Repository: GoogleCloudPlatform/microservices-demo
Branch: main
Commit: 1725a8044b95
Files: 316
Total size: 1.4 MB

Directory structure:
gitextract_n0fmcr6r/

├── .deploystack/
│   ├── deploystack.yaml
│   ├── messages/
│   │   ├── description.txt
│   │   └── success.txt
│   ├── scripts/
│   │   └── preinit.sh
│   ├── test
│   └── test.yaml
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.md
│   │   ├── feature-request.md
│   │   └── other.md
│   ├── SECURITY.md
│   ├── auto-approve.yml
│   ├── header-checker-lint.yml
│   ├── pull_request_template.md
│   ├── release-cluster/
│   │   ├── README.md
│   │   ├── backend-config.yaml
│   │   ├── frontend-config.yaml
│   │   ├── frontend-ingress.yaml
│   │   ├── frontend-service.yaml
│   │   └── managed-cert.yaml
│   ├── renovate.json5
│   ├── snippet-bot.yml
│   ├── terraform/
│   │   ├── README.md
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   └── workflows/
│       ├── README.md
│       ├── ci-main.yaml
│       ├── ci-pr.yaml
│       ├── cleanup.yaml
│       ├── helm-chart-ci.yaml
│       ├── install-dependencies.sh
│       ├── kubevious-manifests-ci.yaml
│       ├── kustomize-build-ci.yaml
│       └── terraform-validate-ci.yaml
├── .gitignore
├── LICENSE
├── README.md
├── cloudbuild.yaml
├── docs/
│   ├── adding-new-microservice.md
│   ├── cloudshell-tutorial.md
│   ├── deploystack.md
│   ├── development-guide.md
│   ├── product-requirements.md
│   ├── purpose.md
│   └── releasing/
│       ├── README.md
│       ├── license_header.txt
│       ├── make-docker-images.sh
│       ├── make-helm-chart.sh
│       ├── make-release-artifacts.sh
│       └── make-release.sh
├── helm-chart/
│   ├── Chart.yaml
│   ├── README.md
│   ├── templates/
│   │   ├── NOTES.txt
│   │   ├── adservice.yaml
│   │   ├── cartservice.yaml
│   │   ├── checkoutservice.yaml
│   │   ├── common.yaml
│   │   ├── currencyservice.yaml
│   │   ├── emailservice.yaml
│   │   ├── frontend.yaml
│   │   ├── loadgenerator.yaml
│   │   ├── opentelemetry-collector.yaml
│   │   ├── paymentservice.yaml
│   │   ├── productcatalogservice.yaml
│   │   ├── recommendationservice.yaml
│   │   └── shippingservice.yaml
│   └── values.yaml
├── istio-manifests/
│   ├── allow-egress-googleapis.yaml
│   ├── frontend-gateway.yaml
│   └── frontend.yaml
├── kubernetes-manifests/
│   ├── README.md
│   ├── adservice.yaml
│   ├── cartservice.yaml
│   ├── checkoutservice.yaml
│   ├── currencyservice.yaml
│   ├── emailservice.yaml
│   ├── frontend.yaml
│   ├── kustomization.yaml
│   ├── loadgenerator.yaml
│   ├── paymentservice.yaml
│   ├── productcatalogservice.yaml
│   ├── recommendationservice.yaml
│   └── shippingservice.yaml
├── kustomize/
│   ├── README.md
│   ├── base/
│   │   ├── adservice.yaml
│   │   ├── cartservice.yaml
│   │   ├── checkoutservice.yaml
│   │   ├── currencyservice.yaml
│   │   ├── emailservice.yaml
│   │   ├── frontend.yaml
│   │   ├── kustomization.yaml
│   │   ├── loadgenerator.yaml
│   │   ├── paymentservice.yaml
│   │   ├── productcatalogservice.yaml
│   │   ├── recommendationservice.yaml
│   │   └── shippingservice.yaml
│   ├── components/
│   │   ├── alloydb/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── container-images-registry/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── container-images-tag/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── container-images-tag-suffix/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── custom-base-url/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── cymbal-branding/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── google-cloud-operations/
│   │   │   ├── README.md
│   │   │   ├── kustomization.yaml
│   │   │   └── otel-collector.yaml
│   │   ├── memorystore/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── network-policies/
│   │   │   ├── README.md
│   │   │   ├── kustomization.yaml
│   │   │   ├── network-policy-adservice.yaml
│   │   │   ├── network-policy-cartservice.yaml
│   │   │   ├── network-policy-checkoutservice.yaml
│   │   │   ├── network-policy-currencyservice.yaml
│   │   │   ├── network-policy-deny-all.yaml
│   │   │   ├── network-policy-emailservice.yaml
│   │   │   ├── network-policy-frontend.yaml
│   │   │   ├── network-policy-loadgenerator.yaml
│   │   │   ├── network-policy-paymentservice.yaml
│   │   │   ├── network-policy-productcatalogservice.yaml
│   │   │   ├── network-policy-recommendationservice.yaml
│   │   │   ├── network-policy-redis.yaml
│   │   │   └── network-policy-shippingservice.yaml
│   │   ├── non-public-frontend/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── service-mesh-istio/
│   │   │   ├── README.md
│   │   │   ├── allow-egress-googleapis.yaml
│   │   │   ├── frontend-gateway.yaml
│   │   │   ├── frontend.yaml
│   │   │   └── kustomization.yaml
│   │   ├── shopping-assistant/
│   │   │   ├── README.md
│   │   │   ├── kustomization.yaml
│   │   │   ├── scripts/
│   │   │   │   ├── 1_deploy_alloydb_infra.sh
│   │   │   │   ├── 2_create_populate_alloydb_tables.sh
│   │   │   │   └── generate_sql_from_products.py
│   │   │   └── shoppingassistantservice.yaml
│   │   ├── single-shared-session/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── spanner/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   └── without-loadgenerator/
│   │       ├── README.md
│   │       ├── delete-loadgenerator.patch.yaml
│   │       └── kustomization.yaml
│   ├── kustomization.yaml
│   └── tests/
│       ├── README.md
│       ├── memorystore-with-all-components/
│       │   └── kustomization.yaml
│       ├── service-mesh-istio-with-all-components/
│       │   └── kustomization.yaml
│       └── spanner-with-all-components/
│           └── kustomization.yaml
├── protos/
│   ├── demo.proto
│   └── grpc/
│       └── health/
│           └── v1/
│               └── health.proto
├── release/
│   ├── istio-manifests.yaml
│   └── kubernetes-manifests.yaml
├── skaffold.yaml
├── src/
│   ├── adservice/
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── build.gradle
│   │   ├── genproto.sh
│   │   ├── gradle/
│   │   │   └── wrapper/
│   │   │       ├── gradle-wrapper.jar
│   │   │       └── gradle-wrapper.properties
│   │   ├── gradlew
│   │   ├── gradlew.bat
│   │   ├── settings.gradle
│   │   └── src/
│   │       └── main/
│   │           ├── java/
│   │           │   └── hipstershop/
│   │           │       ├── AdService.java
│   │           │       └── AdServiceClient.java
│   │           ├── proto/
│   │           │   └── demo.proto
│   │           └── resources/
│   │               └── log4j2.xml
│   ├── cartservice/
│   │   ├── cartservice.sln
│   │   ├── src/
│   │   │   ├── .dockerignore
│   │   │   ├── Dockerfile
│   │   │   ├── Dockerfile.debug
│   │   │   ├── Program.cs
│   │   │   ├── Startup.cs
│   │   │   ├── appsettings.json
│   │   │   ├── cartservice.csproj
│   │   │   ├── cartstore/
│   │   │   │   ├── AlloyDBCartStore.cs
│   │   │   │   ├── ICartStore.cs
│   │   │   │   ├── RedisCartStore.cs
│   │   │   │   └── SpannerCartStore.cs
│   │   │   ├── protos/
│   │   │   │   └── Cart.proto
│   │   │   └── services/
│   │   │       ├── CartService.cs
│   │   │       └── HealthCheckService.cs
│   │   └── tests/
│   │       ├── CartServiceTests.cs
│   │       └── cartservice.tests.csproj
│   ├── checkoutservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── main.go
│   │   └── money/
│   │       ├── money.go
│   │       └── money_test.go
│   ├── currencyservice/
│   │   ├── .dockerignore
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── client.js
│   │   ├── data/
│   │   │   └── currency_conversion.json
│   │   ├── genproto.sh
│   │   ├── package.json
│   │   ├── proto/
│   │   │   ├── demo.proto
│   │   │   └── grpc/
│   │   │       └── health/
│   │   │           └── v1/
│   │   │               └── health.proto
│   │   └── server.js
│   ├── emailservice/
│   │   ├── Dockerfile
│   │   ├── demo_pb2.py
│   │   ├── demo_pb2_grpc.py
│   │   ├── email_client.py
│   │   ├── email_server.py
│   │   ├── genproto.sh
│   │   ├── logger.py
│   │   ├── requirements.in
│   │   ├── requirements.txt
│   │   └── templates/
│   │       └── confirmation.html
│   ├── frontend/
│   │   ├── .dockerignore
│   │   ├── .gitkeep
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── deployment_details.go
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── handlers.go
│   │   ├── main.go
│   │   ├── middleware.go
│   │   ├── money/
│   │   │   ├── money.go
│   │   │   └── money_test.go
│   │   ├── packaging_info.go
│   │   ├── rpc.go
│   │   ├── static/
│   │   │   ├── images/
│   │   │   │   └── credits.txt
│   │   │   └── styles/
│   │   │       ├── bot.css
│   │   │       ├── cart.css
│   │   │       ├── order.css
│   │   │       └── styles.css
│   │   ├── templates/
│   │   │   ├── ad.html
│   │   │   ├── assistant.html
│   │   │   ├── cart.html
│   │   │   ├── error.html
│   │   │   ├── footer.html
│   │   │   ├── header.html
│   │   │   ├── home.html
│   │   │   ├── order.html
│   │   │   ├── product.html
│   │   │   └── recommendations.html
│   │   └── validator/
│   │       ├── validator.go
│   │       └── validator_test.go
│   ├── loadgenerator/
│   │   ├── Dockerfile
│   │   ├── locustfile.py
│   │   ├── requirements.in
│   │   └── requirements.txt
│   ├── paymentservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── charge.js
│   │   ├── genproto.sh
│   │   ├── index.js
│   │   ├── logger.js
│   │   ├── package.json
│   │   ├── proto/
│   │   │   ├── demo.proto
│   │   │   └── grpc/
│   │   │       └── health/
│   │   │           └── v1/
│   │   │               └── health.proto
│   │   └── server.js
│   ├── productcatalogservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── catalog_loader.go
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── product_catalog.go
│   │   ├── product_catalog_test.go
│   │   ├── products.json
│   │   └── server.go
│   ├── recommendationservice/
│   │   ├── Dockerfile
│   │   ├── client.py
│   │   ├── demo_pb2.py
│   │   ├── demo_pb2_grpc.py
│   │   ├── genproto.sh
│   │   ├── logger.py
│   │   ├── recommendation_server.py
│   │   ├── requirements.in
│   │   └── requirements.txt
│   ├── shippingservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── main.go
│   │   ├── quote.go
│   │   ├── shippingservice_test.go
│   │   └── tracker.go
│   └── shoppingassistantservice/
│       ├── Dockerfile
│       ├── requirements.in
│       ├── requirements.txt
│       └── shoppingassistantservice.py
└── terraform/
    ├── README.md
    ├── main.tf
    ├── memorystore.tf
    ├── output.tf
    ├── providers.tf
    └── variables.tf

================================================
FILE CONTENTS
================================================

================================================
FILE: .deploystack/deploystack.yaml
================================================
# The fields inside this deploystack.yaml file are documented in https://github.com/GoogleCloudPlatform/deploystack.

title: Microservices Demo (Online Boutique)
name: microservices-demo
duration: 5
collect_project: true
collect_region: true
region_type: compute
region_default: us-central1
hard_settings:
  filepath_manifest: ../kustomize/
  memorystore: "false"
  name: online-boutique
  namespace: default
documentation_link: https://cloud.google.com/shell/docs/cloud-shell-tutorials/deploystack/microservices-demo


================================================
FILE: .deploystack/messages/description.txt
================================================
Online Boutique is a cloud-first microservices demo application. Online Boutique
consists of an 11-tier microservices application. The application is a web-based
e-commerce app where users can browse items, add them to the cart, and purchase
them.

================================================
FILE: .deploystack/messages/success.txt
================================================
Congrats!
You have successfully provisioned a GKE (Google Kubernetes Engine) cluster and
deployed Online Boutique's 11 microservices, which includes a load generator.



================================================
FILE: .deploystack/scripts/preinit.sh
================================================
# Copyright 2022 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.

ROOT=$(pwd)
sed -i.tmp  "s/project_id/gcp_project_id/" $ROOT/terraform/terraform.tfvars


================================================
FILE: .deploystack/test
================================================
#! /bin/bash
# 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.

# DEPLOYSTACK - this file is a test script that is used by DeployStack's
# testing rig to make sure that the Terraform script installs and uninstalls
# cleanly

# DON'T REMOVE FROM test script.

CYAN='\033[0;36m'
BCYAN='\033[1;36m'
NC='\033[0m' # No Color
DIVIDER=$(printf %"$(tput cols)"s | tr " " "*")
DIVIDER+="\n"

function get_project_id() {
    local __resultvar=$1
    VALUE=$(gcloud config get-value project | xargs)
    eval $__resultvar="'$VALUE'"
}

function get_project_number() {
    local __resultvar=$1
    local PRO=$2
    VALUE=$(gcloud projects list --filter="project_id=$PRO" --format="value(PROJECT_NUMBER)" | xargs)
    eval $__resultvar="'$VALUE'"
}

# DISPLAY HELPERS

function section_open() {
    section_description=$1
    printf "$DIVIDER"
    printf "${CYAN}$section_description${NC} \n"
    printf "$DIVIDER"
}

function section_close() {
    printf "$DIVIDER"
    printf "${CYAN}$section_description ${BCYAN}- done${NC}\n"
    printf "\n\n"
}

function evalTest() {
    local command=$1
    local expected=$2

    local ERR=""
    got=$(eval $command 2>errFile)
    ERR=$(<errFile)

    if [ ${#ERR} -gt 0 ]; then
        if [ "$expected" = "EXPECTERROR" ]; then
            printf "ok \n"
            return
        fi

        printf "expecting no error, got error='$ERR'   \n"
        exit 1
    fi

    if [ "$got" != "$expected" ]; then
        printf "expecting: '$expected' got: '$got'  \n"
        exit 1
    fi

    printf "$expected is ok\n"
}

function generateProject(){
    local __resultvar
    local __STACKSUFFIX
    local __BA
    local __RANDOMSUFFIX
    local __DATELABEL
    local PROJECTID
    local current


    __resultvar=$1
    __STACKSUFFIX=$2
    __BA=$3
    __RANDOMSUFFIX=$(
        LC_ALL=C tr -dc 'a-z0-9' </dev/urandom | head -c 8
        echo
    )
    __DATELABEL=$(date +%F)
    PROJECTID=ds-test-$__STACKSUFFIX-$__RANDOMSUFFIX
    current=$(gcloud config list account --format "value(core.account)")
    # shellcheck disable=SC2034
    GOOGLE_APPLICATION_CREDENTIALS=.deploystack/creds.json

    gcloud projects create "$PROJECTID" --labels="deploystack-disposable-test-project=$__DATELABEL" --folder="155265971980"
    
    gcloud auth activate-service-account --project="$PROJECTID" --key-file=.deploystack/creds.json -q
    gcloud config set account test-runner@ds-tester-helper.iam.gserviceaccount.com -q

    gcloud beta billing projects link "$PROJECTID" --billing-account="$__BA" -q
    gcloud config set account "$current" -q
    eval "$__resultvar"="'$PROJECTID'"
    
}

# END DON'T REMOVE FROM test script.

suffix=ms

# This is only needed if you tests fail alot because of overlapping runs of the
# same set of tests.
section_open "Generate random test project"
    generateProject PROJECT "$suffix" "$BA"
section_close

# We need to force the use of gke-gcloud-auth-plugin (for GKE authentication)
# if we're using kubectl 1.25 or lower.
export USE_GKE_GCLOUD_AUTH_PLUGIN=True

DIR="terraform"
gcloud services enable cloudresourcemanager.googleapis.com --project=$PROJECT
REGION="us-central1"
NAME="online-boutique"
NAMESPACE="default"
FILEPATH_MANIFEST="../kustomize/"
MEMORYSTORE="false"

gcloud config set project ${PROJECT}

terraform -chdir="$DIR" init
terraform -chdir="$DIR" apply -auto-approve \
    -var gcp_project_id="${PROJECT}" \
    -var name="${NAME}" \
    -var region="${REGION}" \
    -var namespace="${NAMESPACE}"  \
    -var filepath_manifest="${FILEPATH_MANIFEST}" \
    -var memorystore="${MEMORYSTORE}"

section_open "Testing Google Kubernetes Engine cluster exists"
    evalTest 'gcloud container clusters describe online-boutique --format="value(name)" --region $REGION' "online-boutique"
section_close

section_open "Testing Online Boutique's services are running"
    evalTest "kubectl get deployment adservice --no-headers -o=name 2> /dev/null"  "deployment.apps/adservice"
    evalTest "kubectl get deployment cartservice --no-headers -o=name 2> /dev/null"  "deployment.apps/cartservice"
    evalTest "kubectl get deployment checkoutservice --no-headers -o=name 2> /dev/null"  "deployment.apps/checkoutservice"
    evalTest "kubectl get deployment currencyservice --no-headers -o=name 2> /dev/null"  "deployment.apps/currencyservice"
    evalTest "kubectl get deployment emailservice --no-headers -o=name 2> /dev/null"  "deployment.apps/emailservice"
    evalTest "kubectl get deployment loadgenerator --no-headers -o=name 2> /dev/null"  "deployment.apps/loadgenerator"
    evalTest "kubectl get deployment paymentservice --no-headers -o=name 2> /dev/null"  "deployment.apps/paymentservice"
    evalTest "kubectl get deployment productcatalogservice --no-headers -o=name 2> /dev/null"  "deployment.apps/productcatalogservice"
    evalTest "kubectl get deployment recommendationservice --no-headers -o=name 2> /dev/null"  "deployment.apps/recommendationservice"
    evalTest "kubectl get deployment redis-cart --no-headers -o=name 2> /dev/null"  "deployment.apps/redis-cart"
    evalTest "kubectl get deployment shippingservice --no-headers -o=name 2> /dev/null"  "deployment.apps/shippingservice"
section_close

sleep 120

ENDPOINT=$( kubectl get service frontend-external --no-headers 2> /dev/null | awk '{print $4}')

section_open "Testing Online Boutique's front-end is working"
    evalTest 'curl -s -o /dev/null -w "%{http_code}" $ENDPOINT' "200"
section_close

# Uncomment the line: "deletion_protection = false"
sed -i "s/# deletion_protection/deletion_protection/g" ${DIR}/main.tf
terraform -chdir="$DIR" apply -auto-approve \
    -var gcp_project_id="${PROJECT}" \
    -var name="${NAME}" \
    -var region="${REGION}" \
    -var namespace="${NAMESPACE}"  \
    -var filepath_manifest="${FILEPATH_MANIFEST}" \
    -var memorystore="${MEMORYSTORE}"

terraform -chdir="$DIR" destroy -auto-approve \
    -var gcp_project_id="${PROJECT}" \
    -var name="${NAME}" \
    -var region="${REGION}" \
    -var namespace="${NAMESPACE}"  \
    -var filepath_manifest="${FILEPATH_MANIFEST}" \
    -var memorystore="${MEMORYSTORE}"

section_open "Testing Google Kubernetes Engine cluster does NOT exist"
    evalTest 'gcloud container clusters describe online-boutique --format="value(name)" --region $REGION' "EXPECTERROR"
section_close

# This is only needed if you tests fail alot because of overlapping runs of the
# same set of tests. Really don't do this if you don't want to severely irritate
# @tpryan
section_open "Delete Test Project"
    gcloud projects delete $PROJECT -q
section_close

printf "$DIVIDER"
printf "CONGRATS!!!!!!! \n"
printf "You got the end the of your test with everything working. \n"
printf "$DIVIDER"


================================================
FILE: .deploystack/test.yaml
================================================
# 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.

# DEPLOYSTACK - this file is the cloudbuild for running testing automatically
# in the testing rig

steps:
   - name: 'bash'
     id: "creds"     
     args: ['-c','echo $$CREDS > .deploystack/creds.json']
     secretEnv: ['CREDS']
   - name: 'gcr.io/cloudshell-images/cloudshell:latest'
     entrypoint: bash
     args: [ '.deploystack/test' ]
     secretEnv: ['BA']
timeout: 4200s
options:
  machineType: 'E2_HIGHCPU_8'
availableSecrets:
  secretManager:
  - versionName: projects/$PROJECT_ID/secrets/creds/versions/latest
    env: 'CREDS'
  - versionName: projects/$PROJECT_ID/secrets/billing_account/versions/latest
    env: 'BA'  

================================================
FILE: .editorconfig
================================================
# The .editorconfig is used to maintain consistent code style.
# The .editorconfig file is supported by most text editors.
# See https://editorconfig.org

root = true

[*]
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 2

[*.cs]
indent_size = 4

[Dockerfile*]
indent_size = 4

[*.go]
indent_style = tab

[*.java]
indent_size = 4

[*.py]
indent_size = 4


================================================
FILE: .gitattributes
================================================
# This file configures the git settings for this repository.

# Converts "CR + LF" to "LF", for all "text" files — for local files on all OSes and files pushed to the remote repo.
* text=auto eol=lf


================================================
FILE: .github/CODEOWNERS
================================================
# See https://help.github.com/en/articles/about-code-owners
# for more info about CODEOWNERS file.

# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence.
* @GoogleCloudPlatform/devrel-flagship-app-maintainers @yoshi-approver


================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
# Contributor Code of Conduct

As contributors and maintainers of this project,
and in the interest of fostering an open and welcoming community,
we pledge to respect all people who contribute through reporting issues,
posting feature requests, updating documentation,
submitting pull requests or patches, and other activities.

We are committed to making participation in this project
a harassment-free experience for everyone,
regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance,
body size, race, ethnicity, age, religion, or nationality.

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information,
such as physical or electronic
addresses, without explicit permission
* Other unethical or unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct.
By adopting this Code of Conduct,
project maintainers commit themselves to fairly and consistently
applying these principles to every aspect of managing this project.
Project maintainers who do not follow or enforce the Code of Conduct
may be permanently removed from the project team.

This code of conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community.

Instances of abusive, harassing, or otherwise unacceptable behavior
may be reported by opening an issue
or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)


================================================
FILE: .github/CONTRIBUTING.md
================================================
# How to Contribute

Thank you so much for your interest in contributing to Online Boutique.
Before contributing, you must:
* Sign the [Contributor License Agreement (CLA)](#contributor-license-agreement).
* Follow the [Google Open Source Community Guidelines](https://opensource.google.com/conduct/).
* Follow the [Contribution Process](#contribution-process).

## Contributor License Agreement

Contributions to Online Boutique must be accompanied by a Contributor License
Agreement (CLA). You (or your employer) retain the copyright to your contribution.
The CLA gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.

You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.

## Contribution Process

Here's the process for making a change to this repository:

1. Review Online Boutique's [purpose](/docs/purpose.md) and [product requirements](/docs/product-requirements.md).
1. If your proposed changes **do not align** with the purpose and product requirements of Online Boutique, you may be asked to instead maintain your own fork of this repository.
1. For **small changes** (such as a bug fixes or spelling corrections):
    1. Fork this repository and submit a [pull request](https://help.github.com/articles/about-pull-requests/).
    1. Wait for a maintainer of this repository to review your change.
1. For **bigger changes**:
    1. Create a [GitHub issue](https://github.com/GoogleCloudPlatform/microservices-demo/issues/new/choose) describing the change **before** working on the implementation. This is important to avoid potentially having to discard your development efforts.
    1. Wait for a maintainer of this repository to review your GitHub issue. For significantly complex proposals, you may be asked to start a Google Doc to discuss design decisions.

If you have any questions, please [create a GitHub issue](https://github.com/GoogleCloudPlatform/microservices-demo/issues/new/choose).


================================================
FILE: .github/ISSUE_TEMPLATE/bug-report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

### Describe the bug 
<!-- A clear and concise description of what the bug is. -->

### To Reproduce 
<!-- Steps to reproduce the behavior: -->
<!-- 1. Built image '...' -->
<!-- 2. Ran command '....' -->
<!-- 3. See error -->

### Logs  
<!-- Add logs to help explain your problem -->

### Screenshots 
<!-- If applicable, add screenshots to help explain your problem -->

### Environment 
<!--  - OS: [e.g. MacOS Big Sur v11.6] -->
<!--  - Kubernetes distribution, version: [e.g. minikube, GKE (Standard or Autopilot), EKS, AWS ... ] -->
<!--  - Any relevant tool version: [e.g. Docker v20.10.8] -->

### Additional context 
<!-- Add any other context about the problem here -->

### Exposure 
<!-- Is the bug intermittent, persistent? Is it widespread, local? -->


================================================
FILE: .github/ISSUE_TEMPLATE/feature-request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

### Describe request or inquiry 
<!-- Add any other context about the problem or helpful links here! -->

### What purpose/environment will this feature serve? 
<!-- Add reasoning -->


================================================
FILE: .github/ISSUE_TEMPLATE/other.md
================================================
---
name: Other
about: Have a question or need clarification?
title: ''
labels: ''
assignees: ''

---
### Write down your inquiry 
<!-- Write your question/inquiry here and any addition context -->


================================================
FILE: .github/SECURITY.md
================================================
# Security Policy

To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).

The Google Security Team will respond within 5 working days of your report on g.co/vulnz.

We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue.


================================================
FILE: .github/auto-approve.yml
================================================
# Copyright 2023 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.

# https://github.com/googleapis/repo-automation-bots/tree/main/packages/auto-approve
processes:
  - "PythonDependency"
  - "PythonSampleAppDependency"
  - "JavaDependency"
  - "JavaSampleAppDependency"
  - "GoDependency"
  - "NodeDependency"
  - "DockerDependency"


================================================
FILE: .github/header-checker-lint.yml
================================================
# Copyright 2023 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.

# This file configures a GitHub Bot called "License Header Lint GCF": https://github.com/apps/license-header-lint-gcf
# The bot runs a GitHub check called "header-check" (inside pull-requests) that warns us about invalid/missing license headers.
# The schema for this configutation file is documented at https://github.com/googleapis/repo-automation-bots/tree/main/packages/header-checker-lint#header-checker-lint.

allowedCopyrightHolders:
  - 'Google LLC'

allowedLicenses:
  - 'Apache-2.0'

# If you want to ignore certain files/folders, use ignoreFiles.
# ignoreFiles:
#  - '**/requirements.txt'

# If you want to ignore checking the license year, use ignoreLicenseYear.
# ignoreLicenseYear: true # Useful when migrating in code licensed at previous years.

sourceFileExtensions:
  - 'cs'
  - 'css'
  - 'Dockerfile'
  - 'dockerignore'
  - 'gitignore'
  - 'go'
  - 'html'
  - 'java'
  - 'js'
  - 'proto'
  - 'py'
  - 'sh'
  - 'tf'
  - 'yaml'
  - 'yml'


================================================
FILE: .github/pull_request_template.md
================================================
### Background 
<!-- What was happening before this PR, and the problem(s) it solves -->

### Fixes 
<!-- Link the issue(s) this PR fixes-->
### Change Summary
<!-- Short summary of the changes submitted -->

### Additional Notes
<!-- Any remaining concerns -->

### Testing Procedure
<!-- If applicable, write how to test for reviewers-->

### Related PRs or Issues 
<!-- Dependent PRs, or any relevant linked issues -->


================================================
FILE: .github/release-cluster/README.md
================================================
# cymbal-shops.retail.cymbal.dev manifests

This directory contains extra deploy manifests for configuring Online Boutique solution on GKE for cymbal-shops.retail.cymbal.dev.

_Note: before moving forward, the Online Boutique apps should already be deployed [on the online-boutique-release GKE cluster](/docs/releasing#10-deploy-releasekubernetes-manifestsyaml-to-our-online-boutique-release-gke-cluster)._

## Public static IP address

Create the static public IP address:
```
STATIC_IP_NAME=online-boutique-ip # name hard-coded in: frontend-ingress.yaml
gcloud compute addresses create $STATIC_IP_NAME --global
```

When ready to do so, you could grab this public IP address and update your DNS:
```
gcloud compute addresses describe $STATIC_IP_NAME \
    --global \
    --format "value(address)"
```

## Cloud Armor

Set up Cloud Armor:
```
SECURITY_POLICY_NAME=online-boutique-security-policy # Name hard-coded in: backendconfig.yaml
gcloud compute security-policies create $SECURITY_POLICY_NAME \
    --description "Block various attacks"
gcloud compute security-policies rules create 1000 \
    --security-policy $SECURITY_POLICY_NAME \
    --expression "evaluatePreconfiguredExpr('xss-stable')" \
    --action "deny-403" \
    --description "XSS attack filtering"
gcloud compute security-policies rules create 12345 \
    --security-policy $SECURITY_POLICY_NAME \
    --expression "evaluatePreconfiguredExpr('cve-canary')" \
    --action "deny-403" \
    --description "CVE-2021-44228 and CVE-2021-45046"
gcloud compute security-policies update $SECURITY_POLICY_NAME \
    --enable-layer7-ddos-defense
gcloud compute security-policies update $SECURITY_POLICY_NAME \
    --log-level=VERBOSE
```

## SSL Policy

Set up an SSL policy in order to later set up a redirect from HTTP to HTTPs:
```
SSL_POLICY_NAME=online-boutique-ssl-policy # Name hard-coded in: frontendconfig.yaml
gcloud compute ssl-policies create $SSL_POLICY_NAME \
    --profile COMPATIBLE  \
    --min-tls-version 1.0
```

## Deploy Kubernetes manifests

Deploy the Kubernetes manifests in this current folder:
```
kubectl apply -f .
```

Wait for the `ManagedCertificate` to be provisioned. This usually takes about 30 minutes.
```
kubectl get managedcertificates
```

Remove the default `LoadBalancer` `Service` not used at this point:
```
kubectl delete service frontend-external
```

Remove the `loadgenerator` `Deployment` not used at this point:
```
kubectl delete deployment loadgenerator
```

================================================
FILE: .github/release-cluster/backend-config.yaml
================================================
# 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
#
#      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.

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: frontend-backend-config
spec:
  securityPolicy:
    name: online-boutique-security-policy

================================================
FILE: .github/release-cluster/frontend-config.yaml
================================================
# 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
#
#      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.

apiVersion: networking.gke.io/v1beta1
kind: FrontendConfig
metadata:
  name: frontend-frontend-config
spec:
  sslPolicy: online-boutique-ssl-policy
  redirectToHttps:
    enabled: true
    responseCodeName: MOVED_PERMANENTLY_DEFAULT

================================================
FILE: .github/release-cluster/frontend-ingress.yaml
================================================
# 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
#
#      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.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: frontend-ingress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: online-boutique-ip
    networking.gke.io/managed-certificates: online-boutique-certificate
    networking.gke.io/v1beta1.FrontendConfig: frontend-frontend-config
spec:
  defaultBackend:
    service:
      name: frontend
      port:
        number: 80
  rules:
  - http:
      paths:
      - path: /*
        pathType: ImplementationSpecific
        backend:
          service:
            name: frontend
            port:
              number: 80


================================================
FILE: .github/release-cluster/frontend-service.yaml
================================================
# 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
#
#      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.

apiVersion: v1
kind: Service
metadata:
  name: frontend
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
    cloud.google.com/backend-config: '{"default": "frontend-backend-config"}'
spec:
  type: ClusterIP
  selector:
    app: frontend
  ports:
  - name: http
    port: 80
    targetPort: 8080

================================================
FILE: .github/release-cluster/managed-cert.yaml
================================================
# 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
#
#      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.

apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: online-boutique-certificate
spec:
  domains:
    - cymbal-shops.retail.cymbal.dev


================================================
FILE: .github/renovate.json5
================================================
{
  extends: [
    'github>GoogleCloudPlatform/kubernetes-engine-samples//.github/renovate-configs/dee-platform-ops.json5',
    'schedule:earlyMondays',
  ],
  'pip-compile': {
    enabled: true,
    managerFilePatterns: [
      '/(^|/)requirements\\.txt$/',
    ],
  },
  pip_requirements: {
    enabled: false,
  },
  constraints: {
    python: '~=3.11.0',
  },
  kubernetes: {
    managerFilePatterns: [
      '/\\.yaml$/',
    ],
    ignorePaths: [
      'release/**',
      'kustomize/base/**',
    ],
  },
}


================================================
FILE: .github/snippet-bot.yml
================================================

# 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.


================================================
FILE: .github/terraform/README.md
================================================
This folder contains the Terraform for some of the infrastructure used by the CICD (continuous integration and continuous delivery/continuous deployment) of this repository.

## Update this Terraform

To make changes to this Terraform, follow these steps:

1. Make sure you have access to the `online-boutique-ci` Google Cloud project.
1. Move into this folder: `cd .github/terraform`
1. Set the PROJECT_ID environment variable: `export PROJECT_ID=online-boutique-ci`
1. Prepare Terraform and download the necessary Terraform dependencies (such as the "hashicorp/google" Terraform provider): `terraform init`
1. Apply the Terraform: `terraform apply -var project_id=${PROJECT_ID}`
    * Ideally, you would see `Apply complete! Resources: 0 added, 0 changed, 0 destroyed.` in the output.
1. Make your desired changes to the Terraform code.
1. Apply the Terraform: `terraform apply -var project_id=${PROJECT_ID}`
    * This time, Terraform will prompt you confirm your changes before applying them.


================================================
FILE: .github/terraform/main.tf
================================================
/**
 * 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 defaults for the google Terraform provider.
provider "google" {
  project = var.project_id
  region  = "us-central1"
  zone    = "us-central1-a"
}

terraform {
  # Store the state inside a Google Cloud Storage bucket.
  backend "gcs" {
    bucket = "cicd-terraform-state"
    prefix = "terraform-state"
  }
}

# Enable Google Cloud APIs.
module "enable_google_apis" {
  source                      = "terraform-google-modules/project-factory/google//modules/project_services"
  version                     = "~> 18.0"
  disable_services_on_destroy = false
  activate_apis = [
    "cloudresourcemanager.googleapis.com",
    "container.googleapis.com",
    "iam.googleapis.com",
    "storage.googleapis.com",
  ]
  project_id = var.project_id
}

# Google Cloud Storage for storing Terraform state (.tfstate).
resource "google_storage_bucket" "terraform_state_storage_bucket" {
  name                        = "cicd-terraform-state"
  location                    = "us"
  storage_class               = "STANDARD"
  force_destroy               = false
  public_access_prevention    = "enforced"
  uniform_bucket_level_access = true
  versioning {
    enabled = true
  }
}

# Google Cloud IAM service account for GKE clusters.
# We avoid using the Compute Engine default service account because it's too permissive.
resource "google_service_account" "gke_clusters_service_account" {
  account_id   = "gke-clusters-service-account"
  display_name = "My Service Account"
  depends_on = [
    module.enable_google_apis
  ]
}

# See https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster#use_least_privilege_sa
resource "google_project_iam_member" "gke_clusters_service_account_role_metric_writer" {
  project = var.project_id
  role    = "roles/monitoring.metricWriter"
  member  = "serviceAccount:${google_service_account.gke_clusters_service_account.email}"
}

# See https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster#use_least_privilege_sa
resource "google_project_iam_member" "gke_clusters_service_account_role_logging_writer" {
  project = var.project_id
  role    = "roles/logging.logWriter"
  member  = "serviceAccount:${google_service_account.gke_clusters_service_account.email}"
}

# See https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster#use_least_privilege_sa
resource "google_project_iam_member" "gke_clusters_service_account_role_monitoring_viewer" {
  project = var.project_id
  role    = "roles/monitoring.viewer"
  member  = "serviceAccount:${google_service_account.gke_clusters_service_account.email}"
}

# See https://cloud.google.com/kubernetes-engine/docs/how-to/hardening-your-cluster#use_least_privilege_sa
resource "google_project_iam_member" "gke_clusters_service_account_role_stackdriver_writer" {
  project = var.project_id
  role    = "roles/stackdriver.resourceMetadata.writer"
  member  = "serviceAccount:${google_service_account.gke_clusters_service_account.email}"
}

# The GKE cluster used for pull-request (PR) staging deployments.
resource "google_container_cluster" "prs_gke_cluster" {
  name                = "prs-gke-cluster"
  location            = "us-central1"
  enable_autopilot    = true
  project             = var.project_id
  deletion_protection = true
  depends_on = [
    module.enable_google_apis
  ]
  cluster_autoscaling {
    auto_provisioning_defaults {
      service_account = google_service_account.gke_clusters_service_account.email
    }
  }
  # Need an empty ip_allocation_policy to overcome an error related to autopilot node pool constraints.
  # Workaround from https://github.com/hashicorp/terraform-provider-google/issues/10782#issuecomment-1024488630
  ip_allocation_policy {
  }
}


================================================
FILE: .github/terraform/variables.tf
================================================
/**
 * 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.
 */

# This file lists variables that you can set using the -var flag during "terraform apply".
# Example: terraform apply -var project_id="${PROJECT_ID}"

variable "project_id" {
  type        = string
  description = "The Google Cloud project ID."
}


================================================
FILE: .github/terraform/versions.tf
================================================
/**
 * 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.
 */

terraform {
  required_version = ">= 0.13"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 7.0"
    }
  }
}


================================================
FILE: .github/workflows/README.md
================================================
# GitHub Actions Workflows

This page describes the CI/CD workflows for the Online Boutique app, which run in [Github Actions](https://github.com/GoogleCloudPlatform/microservices-demo/actions).

## Infrastructure

The CI/CD pipelines for Online Boutique run in Github Actions, using a pool of two [self-hosted runners]((https://help.github.com/en/actions/automating-your-workflow-with-github-actions/about-self-hosted-runners)). These runners are GCE instances (virtual machines) that, for every open Pull Request in the repo, run the code test pipeline, deploy test pipeline, and (on main) deploy the latest version of the app to [cymbal-shops.retail.cymbal.dev](https://cymbal-shops.retail.cymbal.dev)

We also host a test GKE cluster, which is where the deploy tests run. Every PR has its own namespace in the cluster.

## Workflows

**Note**: In order for the current CI/CD setup to work on your pull request, you must branch directly off the repo (no forks). This is because the Github secrets necessary for these tests aren't copied over when you fork.

### Code Tests - [ci-pr.yaml](ci-pr.yaml)

These tests run on every commit for every open PR, as well as any commit to main / any release branch. Currently, this workflow runs only Go unit tests.


### Deploy Tests- [ci-pr.yaml](ci-pr.yaml)

These tests run on every commit for every open PR, as well as any commit to main / any release branch. This workflow:

1. Creates a dedicated GKE namespace for that PR, if it doesn't already exist, in the PR GKE cluster.
2. Uses `skaffold run` to build and push the images specific to that PR commit. Then skaffold deploys those images, via `kubernetes-manifests`, to the PR namespace in the test cluster.
3. Tests to make sure all the pods start up and become ready.
4. Gets the LoadBalancer IP for the frontend service.
5. Comments that IP in the pull request, for staging.

### Push and Deploy Latest - [push-deploy](push-deploy.yml)

This is the Continuous Deployment workflow, and it runs on every commit to the main branch. This workflow:

1. Builds the container images for every service, tagging as `latest`.
2. Pushes those images to Google Container Registry.

Note that this workflow does not update the image tags used in `release/kubernetes-manifests.yaml` - these release manifests are tied to a stable `v0.x.x` release.

### Cleanup - [cleanup.yaml](cleanup.yaml)

This workflow runs when a PR closes, regardless of whether it was merged into main. This workflow deletes the PR-specific GKE namespace in the test cluster.

## Appendix - Creating a new Actions runner

Should one of the two self-hosted Github Actions runners (GCE instances) fail, or you want to add more runner capacity, this is how to provision a new runner. Note that you need IAM access to the admin Online Boutique GCP project in order to do this.

1. Create a GCE instance.
    - VM should be at least n1-standard-4 with 50GB persistent disk
    - VM should use custom service account with permissions to: access a GKE cluster, create GCS storage buckets, and push to GCR.
2. SSH into new VM through the Google Cloud Console.
3. Install project-specific dependencies, including go, docker, skaffold, and kubectl:

```
wget -O - https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/main/.github/workflows/install-dependencies.sh | bash
```

The instance will restart when the script completes in order to finish the Docker install.

4. SSH back into the VM.

5. Follow the instructions to add a new runner on the [Actions Settings page](https://github.com/GoogleCloudPlatform/microservices-demo/settings/actions) to authenticate the new runner
6. Start GitHub Actions as a background service:
```
sudo ~/actions-runner/svc.sh install ; sudo ~/actions-runner/svc.sh start
```


================================================
FILE: .github/workflows/ci-main.yaml
================================================
# 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.

name: "Continuous Integration - Main/Release"
on:
  push:
    # run on pushes to main or release/*
    branches:
      - main
      - release/*
    paths-ignore:
      - '**/README.md'
      - 'kustomize/**'
      - '.github/workflows/kustomize-build-ci.yaml'
      - 'terraform/**'
      - '.github/workflows/terraform-validate-ci.yaml'
      - 'helm-chart/**'
      - '.github/workflows/helm-chart-ci.yaml'
jobs:
  code-tests:
    runs-on: [self-hosted, is-enabled]
    steps:
    - uses: actions/checkout@v6
    - uses: actions/setup-dotnet@v5
      env:
        DOTNET_INSTALL_DIR: "./.dotnet"
      with:
        dotnet-version: '10.0'
    - uses: actions/setup-go@v6
      with:
        go-version: '1.26'
    - name: Go Unit Tests
      timeout-minutes: 10
      run: |
        for SERVICE in "shippingservice" "productcatalogservice"; do
          echo "testing $SERVICE..."
          pushd src/$SERVICE
          go test
          popd
        done
    - name: C# Unit Tests
      timeout-minutes: 10
      run: |
        dotnet test src/cartservice/
  deployment-tests:
    runs-on: [self-hosted, is-enabled]
    needs: code-tests
    strategy:
      matrix:
        profile: ["local-code"]
      fail-fast: true
    steps:
    - uses: actions/checkout@v6
    - name: Build + Deploy PR images to GKE
      timeout-minutes: 20
      run: |
        PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
        NAMESPACE="pr${PR_NUMBER}"
        echo "::set-env name=NAMESPACE::$NAMESPACE"
        echo "::set-env name=PR_NUMBER::$PR_NUMBER"

        yes | gcloud auth configure-docker us-docker.pkg.dev
        gcloud container clusters get-credentials $PR_CLUSTER --region $REGION --project $PROJECT_ID
        cat <<EOF | kubectl apply -f -
        apiVersion: v1
        kind: Namespace
        metadata:
          name: $NAMESPACE
        EOF
        echo Deploying application
        skaffold config set --global local-cluster false
        skaffold run --default-repo=us-docker.pkg.dev/$PROJECT_ID/$GITHUB_REF --tag=$GITHUB_SHA --namespace=$NAMESPACE -p network-policies
      env:
        ACTIONS_ALLOW_UNSECURE_COMMANDS: true
        PROJECT_ID: "online-boutique-ci"
        PR_CLUSTER: "prs-gke-cluster"
        REGION: "us-central1"
    - name: Wait For Pods
      timeout-minutes: 20
      run: |
        set -x
        kubectl config set-context --current --namespace=$NAMESPACE
        kubectl wait --for=condition=available --timeout=1000s deployment/redis-cart
        kubectl wait --for=condition=available --timeout=1000s deployment/adservice
        kubectl wait --for=condition=available --timeout=1000s deployment/cartservice
        kubectl wait --for=condition=available --timeout=1000s deployment/checkoutservice
        kubectl wait --for=condition=available --timeout=1000s deployment/currencyservice
        kubectl wait --for=condition=available --timeout=1000s deployment/emailservice
        kubectl wait --for=condition=available --timeout=1000s deployment/frontend
        kubectl wait --for=condition=available --timeout=1000s deployment/loadgenerator
        kubectl wait --for=condition=available --timeout=1000s deployment/paymentservice
        kubectl wait --for=condition=available --timeout=1000s deployment/productcatalogservice
        kubectl wait --for=condition=available --timeout=1000s deployment/recommendationservice
        kubectl wait --for=condition=available --timeout=1000s deployment/shippingservice
    - name: Smoke Test
      timeout-minutes: 5
      run: |
        set -x
        # start fresh loadgenerator pod
        kubectl delete pod -l app=loadgenerator
        # wait for requests to come in
        REQUEST_COUNT="0"
        while [[ "$REQUEST_COUNT"  -lt "50"  ]]; do
            sleep 5
            REQUEST_COUNT=$(kubectl logs -l app=loadgenerator | grep Aggregated | awk '{print $2}')
        done
        # ensure there are no errors hitting endpoints
        ERROR_COUNT=$(kubectl logs -l app=loadgenerator | grep Aggregated | awk '{print $3}' | sed "s/[(][^)]*[)]//g")
        if [[ "$ERROR_COUNT" -gt "0" ]]; then
          exit 1
        fi


================================================
FILE: .github/workflows/ci-pr.yaml
================================================
# 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.

name: "Continuous Integration - Pull Request"
on:
  pull_request:
    branches:
      - main
    paths-ignore:
      - '**/README.md'
      - 'kustomize/**'
      - '.github/workflows/kustomize-build-ci.yaml'
      - 'terraform/**'
      - '.github/workflows/terraform-validate-ci.yaml'
      - 'helm-chart/**'
      - '.github/workflows/helm-chart-ci.yaml'

# Ensure this workflow only runs for the most recent commit of a pull-request
concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs:
  code-tests:
    runs-on: [self-hosted, is-enabled]
    steps:
    - uses: actions/checkout@v6
    - uses: actions/setup-dotnet@v5
      env:
        DOTNET_INSTALL_DIR: "./.dotnet"
      with:
        dotnet-version: '10.0'
    - uses: actions/setup-go@v6
      with:
        go-version: '1.26'
    - name: Go Unit Tests
      timeout-minutes: 10
      run: |
        for GO_PACKAGE in "shippingservice" "productcatalogservice" "frontend/validator"; do
          echo "Testing $GO_PACKAGE..."
          pushd src/$GO_PACKAGE
          go test
          popd
        done
    - name: C# Unit Tests
      timeout-minutes: 10
      run: |
        dotnet test src/cartservice/

  deployment-tests:
    runs-on: [self-hosted, is-enabled]
    needs: code-tests
    strategy:
      matrix:
        profile: ["local-code"]
      fail-fast: true
    steps:
    - uses: actions/checkout@v6
      with:
        ref: ${{github.event.pull_request.head.sha}}
    - name: Build + Deploy PR images to GKE
      timeout-minutes: 20
      run: |
        NAMESPACE="pr${PR_NUMBER}"
        echo "::set-env name=NAMESPACE::$NAMESPACE"

        yes | gcloud auth configure-docker us-docker.pkg.dev
        gcloud container clusters get-credentials $PR_CLUSTER --region $REGION --project $PROJECT_ID
        cat <<EOF | kubectl apply -f -
        apiVersion: v1
        kind: Namespace
        metadata:
          name: $NAMESPACE
        EOF
        echo Deploying application
        skaffold config set --global local-cluster false
        skaffold run --default-repo=us-docker.pkg.dev/$PROJECT_ID/refs/pull/$PR_NUMBER --tag=$PR_NUMBER --namespace=$NAMESPACE -p network-policies
      env:
        ACTIONS_ALLOW_UNSECURE_COMMANDS: true
        PR_NUMBER: ${{ github.event.pull_request.number }}
        PROJECT_ID: "online-boutique-ci"
        PR_CLUSTER: "prs-gke-cluster"
        REGION: "us-central1"
    - name: Wait For Pods
      timeout-minutes: 20
      run: |
        set -x
        kubectl config set-context --current --namespace=$NAMESPACE
        kubectl wait --for=condition=available --timeout=1000s deployment/redis-cart
        kubectl wait --for=condition=available --timeout=1000s deployment/adservice
        kubectl wait --for=condition=available --timeout=1000s deployment/cartservice
        kubectl wait --for=condition=available --timeout=1000s deployment/checkoutservice
        kubectl wait --for=condition=available --timeout=1000s deployment/currencyservice
        kubectl wait --for=condition=available --timeout=1000s deployment/emailservice
        kubectl wait --for=condition=available --timeout=1000s deployment/frontend
        kubectl wait --for=condition=available --timeout=1000s deployment/loadgenerator
        kubectl wait --for=condition=available --timeout=1000s deployment/paymentservice
        kubectl wait --for=condition=available --timeout=1000s deployment/productcatalogservice
        kubectl wait --for=condition=available --timeout=1000s deployment/recommendationservice
        kubectl wait --for=condition=available --timeout=1000s deployment/shippingservice
    - name: Query EXTERNAL_IP for staging
      timeout-minutes: 5
      run: |
        set -x
        NAMESPACE="pr${PR_NUMBER}"
        get_externalIP() {
          kubectl get service frontend-external --namespace $NAMESPACE -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
        }
        until [[ -n "$(get_externalIP)" ]]; do
          echo "Querying for external IP for frontend-external on namespace: $NAMESPACE{}"
        sleep 3
        done
        EXTERNAL_IP=$(get_externalIP)
        echo "::set-env name=EXTERNAL_IP::$EXTERNAL_IP"
      env:
        ACTIONS_ALLOW_UNSECURE_COMMANDS: true
        PR_NUMBER: ${{ github.event.pull_request.number }}
    - name: Smoke Test
      timeout-minutes: 5
      run: |
        set -x
        # start fresh loadgenerator pod
        kubectl delete pod -l app=loadgenerator
        # wait for requests to come in
        REQUEST_COUNT="0"
        while [[ "$REQUEST_COUNT"  -lt "50"  ]]; do
            sleep 5
            REQUEST_COUNT=$(kubectl logs -l app=loadgenerator | grep Aggregated | awk '{print $2}')
        done
        # ensure there are no errors hitting endpoints
        ERROR_COUNT=$(kubectl logs -l app=loadgenerator | grep Aggregated | awk '{print $3}' | sed "s/[(][^)]*[)]//g")
        if [[ "$ERROR_COUNT" -gt "0" ]]; then
          exit 1
        fi
    - name: Comment EXTERNAL_IP
      timeout-minutes: 5
      env:
          COMMENTS_URL: ${{ github.event.pull_request.comments_url }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
          curl \
            -X POST \
            $COMMENTS_URL \
            -H "Content-Type: application/json" \
            -H "Authorization: token $GITHUB_TOKEN" \
            --data '{ "body": "🚲 PR staged at '"http://${EXTERNAL_IP}"'"}'
          sleep 60


================================================
FILE: .github/workflows/cleanup.yaml
================================================
# 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.

name: "Clean up deployment"
on:
  pull_request:
    # run on pull requests targeting main
    branches:
      - main
    types: closed
    paths-ignore:
      - '**/README.md'
      - 'kustomize/**'
      - '.github/workflows/kustomize-build-ci.yaml'
      - 'terraform/**'
      - '.github/workflows/terraform-validate-ci.yaml'
jobs:
  cleanup-namespace:
    runs-on: [self-hosted, is-enabled]
    steps:
      - name: Delete PR namespace in staging cluster
        if: ${{ always() }}
        timeout-minutes: 20
        run: |
          gcloud container clusters get-credentials $PR_CLUSTER \
              --region $REGION --project $PROJECT_ID
          NAMESPACE="pr${PR_NUMBER}"
          kubectl delete namespace $NAMESPACE
        env:
          PROJECT_ID: "online-boutique-ci"
          PR_CLUSTER: "prs-gke-cluster"
          REGION: "us-central1"
          PR_NUMBER: ${{ github.event.number }}


================================================
FILE: .github/workflows/helm-chart-ci.yaml
================================================
# Copyright 2022 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.

name: helm-chart-ci
on:
  push:
    branches:
      - main
    paths:
      - 'helm-chart/**'
      - '.github/workflows/helm-chart-ci.yaml'
  pull_request:
    paths:
      - 'helm-chart/**'
      - '.github/workflows/helm-chart-ci.yaml'
jobs:
  helm-chart-ci:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v6
      - name: helm lint
        run: |
          cd helm-chart/
          helm lint --strict
      - name: helm template default
        run: |
          cd helm-chart/
          helm template . > helm-template.yaml
          cat helm-template.yaml 
          kustomize create --resources helm-template.yaml
          kustomize build .
      - name: helm template grpc health probes
        run: |
          # Test related to https://medium.com/google-cloud/b5bd26253a4c
          cd helm-chart/
          SPANNER_CONNECTION_STRING=projects/PROJECT_ID/instances/SPANNER_INSTANCE_NAME/databases/SPANNER_DATABASE_NAME
          helm template . \
            --set nativeGrpcHealthCheck=true \
            -n onlineboutique \
            > helm-template.yaml
          cat helm-template.yaml
          kustomize build .
      - name: helm template spanner
        run: |
          # Test related to https://medium.com/google-cloud/f7248e077339
          cd helm-chart/
          SPANNER_CONNECTION_STRING=projects/PROJECT_ID/instances/SPANNER_INSTANCE_NAME/databases/SPANNER_DATABASE_NAME
          SPANNER_DB_USER_GSA_ID=spanner-db-user@my-project.iam.gserviceaccount.com
          helm template . \
            --set cartDatabase.inClusterRedis.create=false \
            --set cartDatabase.type=spanner \
            --set cartDatabase.connectionString=${SPANNER_CONNECTION_STRING} \
            --set serviceAccounts.create=true \
            --set serviceAccounts.annotationsOnlyForCartservice=true \
            --set "serviceAccounts.annotations.iam\.gke\.io/gcp-service-account=${SPANNER_DB_USER_GSA_ID}" \
            -n onlineboutique \
            > helm-template.yaml
          cat helm-template.yaml
          kustomize build .
      - name: helm template asm
        run: |
          # Test related to https://medium.com/google-cloud/246119e46d53
          cd helm-chart/
          helm template . \
            --set networkPolicies.create=true \
            --set sidecars.create=true \
            --set serviceAccounts.create=true \
            --set authorizationPolicies.create=true \
            --set frontend.externalService=false \
            --set frontend.virtualService.create=true \
            --set frontend.virtualService.gateway.name=asm-ingressgateway \
            --set frontend.virtualService.gateway.namespace=asm-ingress \
            --set frontend.virtualService.gateway.labelKey=asm \
            --set frontend.virtualService.gateway.labelValue=ingressgateway \
            -n onlineboutique \
            > helm-template.yaml
          cat helm-template.yaml
          kustomize build .
      - name: helm template memorystore istio tls origination
        run: |
          # Test related to https://medium.com/google-cloud/64b71969318d
          cd helm-chart/
          REDIS_IP=0.0.0.0
          REDIS_PORT=7378
          REDIS_CERT=dsjfgkldsjflkdsjflksdajfkldsjkfljsdaklfjaskjfakdsjfaklsdjflskadjfklasjfkls
          helm template . \
            --set cartDatabase.inClusterRedis.create=false \
            --set cartDatabase.connectionString=${REDIS_IP}:${REDIS_PORT} \
            --set cartDatabase.externalRedisTlsOrigination.enable=true \
            --set cartDatabase.externalRedisTlsOrigination.certificate="${REDIS_CERT}" \
            --set cartDatabase.externalRedisTlsOrigination.endpointAddress=${REDIS_IP} \
            --set cartDatabase.externalRedisTlsOrigination.endpointPort=${REDIS_PORT} \
            -n onlineboutique \
            > helm-template.yaml
          cat helm-template.yaml
          kustomize build .


================================================
FILE: .github/workflows/install-dependencies.sh
================================================
#!/bin/bash
# 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.

set -euo pipefail

# install wget
sudo apt install -y wget

# install dotnet CLI
sudo apt-get update
sudo apt-get install wget
wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
wget https://packages.microsoft.com/config/debian/9/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list

sudo apt-get install -y apt-transport-https && \
sudo apt-get update && \
sudo apt-get install -y dotnet-sdk-10.0
echo "✅ dotnet installed"

# install kubectl
sudo apt-get install -yqq kubectl git
echo "✅ kubectl installed"

# install go
wget https://golang.org/dl/go1.25.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.25.linux-amd64.tar.gz
echo 'export GOPATH=$HOME/go' >> ~/.profile
echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> ~/.profile
source ~/.profile
echo "✅ golang installed"

# install build-essential (gcc, used for go test)
sudo apt install -y build-essential

# install addlicense
go install github.com/google/addlicense@latest
sudo ln -s $HOME/go/bin/addlicense /bin

# install build-essential (gcc, used for go test)
sudo apt install -y build-essential

# install skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \
chmod +x skaffold && \
sudo mv skaffold /usr/local/bin
echo "✅ skaffold installed"

# install docker
sudo apt install -yqq apt-transport-https ca-certificates curl gnupg2 software-properties-common && \
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - && \
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
sudo apt-get update && \
sudo apt-get install -yqq docker-ce && \
sudo usermod -aG docker ${USER}
echo "✅ docker installed, rebooting..."

# reboot for docker setup
sudo reboot


================================================
FILE: .github/workflows/kubevious-manifests-ci.yaml
================================================
# Copyright 2023 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.

name: kubevious-manifests-ci
on:
  push:
    branches:
      - main
    paths:
      - 'helm-chart/**'
      - 'kustomize/**'
      - '.github/workflows/kubevious-manifests-ci.yaml'
  pull_request:
    paths:
      - 'helm-chart/**'
      - 'kustomize/**'
      - '.github/workflows/kubevious-manifests-ci.yaml'
permissions:
  contents: read
jobs:
  kubevious-manifests-ci:
    runs-on: ubuntu-24.04
    timeout-minutes: 1
    steps:
      - uses: actions/checkout@v6

      - name: Validate kubernetes-manifests
        id: kubernetes-manifests-validation
        uses: kubevious/cli@v1.0.64
        with:
          manifests: kubernetes-manifests
          skip_rules: container-latest-image

      - name: Validate helm-chart
        id: helm-chart-validation
        uses: kubevious/cli@v1.0.64
        with:
          manifests: helm-chart

      - name: Validate kustomize
        id: kustomize-validation
        uses: kubevious/cli@v1.0.64
        with:
          manifests: kustomize
          skip_rules: container-latest-image


================================================
FILE: .github/workflows/kustomize-build-ci.yaml
================================================
# 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.

name: kustomize-build-ci
on:
  push:
    branches:
      - main
    paths:
      - 'kustomize/**'
      - '.github/workflows/kustomize-build-ci.yaml'
  pull_request:
    paths:
      - 'kustomize/**'
      - '.github/workflows/kustomize-build-ci.yaml'
jobs:
  kustomize-build-ci:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v6
      - name: kustomize build base
        run: |
          cd kustomize/
          kubectl kustomize .
      # Build the different combinations of Kustomize components found in kustomize/tests.
      - name: kustomize build tests
        run: |
          cd kustomize/tests
          KUSTOMIZE_TESTS_SUBFOLDERS=$(ls -d */)
          for test in $KUSTOMIZE_TESTS_SUBFOLDERS;
          do
              echo "## kustomize build for " + $test
              kustomize build $test
          done


================================================
FILE: .github/workflows/terraform-validate-ci.yaml
================================================
# 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.

name: terraform-validate-ci
on:
  push:
    branches:
      - main
    paths:
      - 'terraform/**'
      - '.github/workflows/terraform-validate-ci.yaml'
  pull_request:
    paths:
      - 'terraform/**'
      - '.github/workflows/terraform-validate-ci.yaml'
jobs:
  terraform-validate-ci:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v6
      - uses: hashicorp/setup-terraform@v4
      - name: terraform init & validate
        run: |
          cd terraform/
          terraform init -backend=false
          terraform validate


================================================
FILE: .gitignore
================================================
.DS_Store
.eclipse.buildship.core.prefs
.gradle/
.idea/
.kubernetes-manifests-*/
.project
.skaffold-*.yaml
.terraform.lock.hcl
.terraform/*
.venv/
.vs/
.vscode/
*.iml
*.ipr
*.iws
*.pyc
*.swp
*.tfstate*
*.tfvars
*~
bin/
build/
Dockerfile.pip
node_modules/
obj/
pkg/
release/wi-kubernetes-manifests.yaml
vendor/


================================================
FILE: LICENSE
================================================

                                 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.


================================================
FILE: README.md
================================================
<!-- <p align="center">
<img src="/src/frontend/static/icons/Hipster_HeroLogoMaroon.svg" width="300" alt="Online Boutique" />
</p> -->
![Continuous Integration](https://github.com/GoogleCloudPlatform/microservices-demo/workflows/Continuous%20Integration%20-%20Main/Release/badge.svg)

**Online Boutique** is a cloud-first microservices demo application.  The application is a
web-based e-commerce app where users can browse items, add them to the cart, and purchase them.

Google uses this application to demonstrate how developers can modernize enterprise applications using Google Cloud products, including: [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine), [Cloud Service Mesh (CSM)](https://cloud.google.com/service-mesh), [gRPC](https://grpc.io/), [Cloud Operations](https://cloud.google.com/products/operations), [Spanner](https://cloud.google.com/spanner), [Memorystore](https://cloud.google.com/memorystore), [AlloyDB](https://cloud.google.com/alloydb), and [Gemini](https://ai.google.dev/). This application works on any Kubernetes cluster.

If you’re using this demo, please **★Star** this repository to show your interest!

**Note to Googlers:** Please fill out the form at [go/microservices-demo](http://go/microservices-demo).

## Architecture

**Online Boutique** is composed of 11 microservices written in different
languages that talk to each other over gRPC.

[![Architecture of
microservices](/docs/img/architecture-diagram.png)](/docs/img/architecture-diagram.png)

Find **Protocol Buffers Descriptions** at the [`./protos` directory](/protos).

| Service                                              | Language      | Description                                                                                                                       |
| ---------------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| [frontend](/src/frontend)                           | Go            | Exposes an HTTP server to serve the website. Does not require signup/login and generates session IDs for all users automatically. |
| [cartservice](/src/cartservice)                     | C#            | Stores the items in the user's shopping cart in Redis and retrieves it.                                                           |
| [productcatalogservice](/src/productcatalogservice) | Go            | Provides the list of products from a JSON file and ability to search products and get individual products.                        |
| [currencyservice](/src/currencyservice)             | Node.js       | Converts one money amount to another currency. Uses real values fetched from European Central Bank. It's the highest QPS service. |
| [paymentservice](/src/paymentservice)               | Node.js       | Charges the given credit card info (mock) with the given amount and returns a transaction ID.                                     |
| [shippingservice](/src/shippingservice)             | Go            | Gives shipping cost estimates based on the shopping cart. Ships items to the given address (mock)                                 |
| [emailservice](/src/emailservice)                   | Python        | Sends users an order confirmation email (mock).                                                                                   |
| [checkoutservice](/src/checkoutservice)             | Go            | Retrieves user cart, prepares order and orchestrates the payment, shipping and the email notification.                            |
| [recommendationservice](/src/recommendationservice) | Python        | Recommends other products based on what's given in the cart.                                                                      |
| [adservice](/src/adservice)                         | Java          | Provides text ads based on given context words.                                                                                   |
| [loadgenerator](/src/loadgenerator)                 | Python/Locust | Continuously sends requests imitating realistic user shopping flows to the frontend.                                              |

## Screenshots

| Home Page                                                                                                         | Checkout Screen                                                                                                    |
| ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| [![Screenshot of store homepage](/docs/img/online-boutique-frontend-1.png)](/docs/img/online-boutique-frontend-1.png) | [![Screenshot of checkout screen](/docs/img/online-boutique-frontend-2.png)](/docs/img/online-boutique-frontend-2.png) |

## Quickstart (GKE)

1. Ensure you have the following requirements:
   - [Google Cloud project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project).
   - Shell environment with `gcloud`, `git`, and `kubectl`.

2. Clone the latest major version.

   ```sh
   git clone --depth 1 --branch v0 https://github.com/GoogleCloudPlatform/microservices-demo.git
   cd microservices-demo/
   ```

   The `--depth 1` argument skips downloading git history.

3. Set the Google Cloud project and region and ensure the Google Kubernetes Engine API is enabled.

   ```sh
   export PROJECT_ID=<PROJECT_ID>
   export REGION=us-central1
   gcloud services enable container.googleapis.com \
     --project=${PROJECT_ID}
   ```

   Substitute `<PROJECT_ID>` with the ID of your Google Cloud project.

4. Create a GKE cluster and get the credentials for it.

   ```sh
   gcloud container clusters create-auto online-boutique \
     --project=${PROJECT_ID} --region=${REGION}
   ```

   Creating the cluster may take a few minutes.

5. Deploy Online Boutique to the cluster.

   ```sh
   kubectl apply -f ./release/kubernetes-manifests.yaml
   ```

6. Wait for the pods to be ready.

   ```sh
   kubectl get pods
   ```

   After a few minutes, you should see the Pods in a `Running` state:

   ```
   NAME                                     READY   STATUS    RESTARTS   AGE
   adservice-76bdd69666-ckc5j               1/1     Running   0          2m58s
   cartservice-66d497c6b7-dp5jr             1/1     Running   0          2m59s
   checkoutservice-666c784bd6-4jd22         1/1     Running   0          3m1s
   currencyservice-5d5d496984-4jmd7         1/1     Running   0          2m59s
   emailservice-667457d9d6-75jcq            1/1     Running   0          3m2s
   frontend-6b8d69b9fb-wjqdg                1/1     Running   0          3m1s
   loadgenerator-665b5cd444-gwqdq           1/1     Running   0          3m
   paymentservice-68596d6dd6-bf6bv          1/1     Running   0          3m
   productcatalogservice-557d474574-888kr   1/1     Running   0          3m
   recommendationservice-69c56b74d4-7z8r5   1/1     Running   0          3m1s
   redis-cart-5f59546cdd-5jnqf              1/1     Running   0          2m58s
   shippingservice-6ccc89f8fd-v686r         1/1     Running   0          2m58s
   ```

7. Access the web frontend in a browser using the frontend's external IP.

   ```sh
   kubectl get service frontend-external | awk '{print $4}'
   ```

   Visit `http://EXTERNAL_IP` in a web browser to access your instance of Online Boutique.

8. Congrats! You've deployed the default Online Boutique. To deploy a different variation of Online Boutique (e.g., with Google Cloud Operations tracing, Istio, etc.), see [Deploy Online Boutique variations with Kustomize](#deploy-online-boutique-variations-with-kustomize).

9. Once you are done with it, delete the GKE cluster.

   ```sh
   gcloud container clusters delete online-boutique \
     --project=${PROJECT_ID} --region=${REGION}
   ```

   Deleting the cluster may take a few minutes.

## Additional deployment options

- **Terraform**: [See these instructions](/terraform) to learn how to deploy Online Boutique using [Terraform](https://www.terraform.io/intro).
- **Istio / Cloud Service Mesh**: [See these instructions](/kustomize/components/service-mesh-istio/README.md) to deploy Online Boutique alongside an Istio-backed service mesh.
- **Non-GKE clusters (Minikube, Kind, etc)**: See the [Development guide](/docs/development-guide.md) to learn how you can deploy Online Boutique on non-GKE clusters.
- **AI assistant using Gemini**: [See these instructions](/kustomize/components/shopping-assistant/README.md) to deploy a Gemini-powered AI assistant that suggests products to purchase based on an image.
- **And more**: The [`/kustomize` directory](/kustomize) contains instructions for customizing the deployment of Online Boutique with other variations.

## Documentation

- [Development](/docs/development-guide.md) to learn how to run and develop this app locally.

## Demos featuring Online Boutique

- [Security hardening of the OnlineBoutique sample apps with the Docker Hardened Images (DHI)](https://medium.com/google-cloud/security-hardening-of-the-onlineboutique-sample-apps-with-docker-hardened-images-dhi-ca1fad348343)
- [alpine, distroless or scratch?](https://medium.com/google-cloud/alpine-distroless-or-scratch-caac35250e0b)
- [Platform Engineering in action: Deploy the Online Boutique sample apps with Score and Humanitec](https://medium.com/p/d99101001e69)
- [The new Kubernetes Gateway API with Istio and Anthos Service Mesh (ASM)](https://medium.com/p/9d64c7009cd)
- [Use Azure Redis Cache with the Online Boutique sample on AKS](https://medium.com/p/981bd98b53f8)
- [Sail Sharp, 8 tips to optimize and secure your .NET containers for Kubernetes](https://medium.com/p/c68ba253844a)
- [Deploy multi-region application with Anthos and Google cloud Spanner](https://medium.com/google-cloud/a2ea3493ed0)
- [Use Google Cloud Memorystore (Redis) with the Online Boutique sample on GKE](https://medium.com/p/82f7879a900d)
- [Use Helm to simplify the deployment of Online Boutique, with a Service Mesh, GitOps, and more!](https://medium.com/p/246119e46d53)
- [How to reduce microservices complexity with Apigee and Anthos Service Mesh](https://cloud.google.com/blog/products/application-modernization/api-management-and-service-mesh-go-together)
- [gRPC health probes with Kubernetes 1.24+](https://medium.com/p/b5bd26253a4c)
- [Use Google Cloud Spanner with the Online Boutique sample](https://medium.com/p/f7248e077339)
- [Seamlessly encrypt traffic from any apps in your Mesh to Memorystore (redis)](https://medium.com/google-cloud/64b71969318d)
- [Strengthen your app's security with Cloud Service Mesh and Anthos Config Management](https://cloud.google.com/service-mesh/docs/strengthen-app-security)
- [From edge to mesh: Exposing service mesh applications through GKE Ingress](https://cloud.google.com/architecture/exposing-service-mesh-apps-through-gke-ingress)
- [Take the first step toward SRE with Cloud Operations Sandbox](https://cloud.google.com/blog/products/operations/on-the-road-to-sre-with-cloud-operations-sandbox)
- [Deploying the Online Boutique sample application on Cloud Service Mesh](https://cloud.google.com/service-mesh/docs/onlineboutique-install-kpt)
- [Anthos Service Mesh Workshop: Lab Guide](https://codelabs.developers.google.com/codelabs/anthos-service-mesh-workshop)
- [KubeCon EU 2019 - Reinventing Networking: A Deep Dive into Istio's Multicluster Gateways - Steve Dake, Independent](https://youtu.be/-t2BfT59zJA?t=982)
- Google Cloud Next'18 SF
  - [Day 1 Keynote](https://youtu.be/vJ9OaAqfxo4?t=2416) showing GKE On-Prem
  - [Day 3 Keynote](https://youtu.be/JQPOPV_VH5w?t=815) showing Stackdriver
    APM (Tracing, Code Search, Profiler, Google Cloud Build)
  - [Introduction to Service Management with Istio](https://www.youtube.com/watch?v=wCJrdKdD6UM&feature=youtu.be&t=586)
- [Google Cloud Next'18 London – Keynote](https://youtu.be/nIq2pkNcfEI?t=3071)
  showing Stackdriver Incident Response Management
- [Microservices demo showcasing Go Micro](https://github.com/go-micro/demo)


================================================
FILE: cloudbuild.yaml
================================================
# 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.

# [START cloudbuild_microservice_demo_cloudbuild]

# This configuration file is used to build and deploy the app into a
# GKE cluster using Google Cloud Build.
#
# PREREQUISITES:
# - Cloud Build service account must have role: "Kubernetes Engine Developer"

# USAGE:
# GCP zone and GKE target cluster must be specified as substitutions
# Example invocation:
# `gcloud builds submit --config=cloudbuild.yaml --substitutions=_ZONE=us-central1-b,_CLUSTER=demo-app-staging .`

steps:
- id: 'Deploy application to cluster'
  name: 'gcr.io/k8s-skaffold/skaffold:v2.16.1'
  entrypoint: 'bash'
  args:
  - '-c'
  - >
    gcloud container clusters get-credentials --zone=$_ZONE $_CLUSTER;
    skaffold run -f=skaffold.yaml --default-repo=gcr.io/$PROJECT_ID;

# Add more power, and more time, for heavy Skaffold build
timeout: '3600s'
options:
  machineType: 'N1_HIGHCPU_8'
  
# [END cloudbuild_microservice_demo_cloudbuild]

================================================
FILE: docs/adding-new-microservice.md
================================================
# Adding a new microservice

This document outlines the steps required to add a new microservice to the Online Boutique application.

## 1. Create a new directory

Create a new directory for your microservice within the `src/` directory. The directory name should be the name of your microservice.

## 2. Add source code

Place your microservice's source code inside the newly created directory. The structure of this directory should follow the conventions of the existing microservices. For example, a Python-based service would include at minimum the following files:

- `README.md`: The service's description and documentation.
- `main.py`: The application's entry point.
- `requirements.in`: A list of Python dependencies.
- `Dockerfile`: To containerize the application.

Take a look at existing microservices for inspiration.

## 3. Create a Dockerfile

Create a `Dockerfile` in your microservice's directory. This file will define the steps to build a container image for your service.

Refer to this example and tweak based on your new service's needs: https://github.com/GoogleCloudPlatform/microservices-demo/blob/main/src/frontend/Dockerfile

## 4. Create Kubernetes manifests

Create a new directory under `kustomize/components/` in the root of the repository for your microservice. Inside this directory, add the necessary Kubernetes YAML files for your new microservice. This typically includes:

- A **Deployment** to manage your service's pods.
- A **Service** to expose your microservice to other services within the cluster.

Ensure you follow the existing naming conventions and that the container image specified in the Deployment matches the one built by your `cloudbuild.yaml` and `skaffold.yaml` files.

Refer to this example and tweak based on your new service's needs: https://github.com/GoogleCloudPlatform/microservices-demo/tree/main/kustomize/components/shopping-assistant

## 5. Update the root `kustomization.yaml` file

Add your newly created component to the root kustomization file so it gets picked up by the deployment cycle.

The file is available here: https://github.com/GoogleCloudPlatform/microservices-demo/blob/main/kustomize/kustomization.yaml

## 6. Update the root `skaffold.yaml`

Add your newly created service to the root skaffold file so the images build correctly.

The file is available here: https://github.com/GoogleCloudPlatform/microservices-demo/blob/main/skaffold.yaml

## 7. Update the Helm chart

Add your newly created service to the Helm chart templates and default values.

The chart is available here: https://github.com/GoogleCloudPlatform/microservices-demo/tree/main/helm-chart

## 8. Update the documentation

Finally, update the project's documentation to reflect the addition of your new microservice. This may include:

- Adding a section to the main `README.md` if the service introduces significant new functionality.
- Updating the architecture diagrams in the `docs/img` directory.
- Adding a new document in the `docs` directory if the service requires detailed explanation.


================================================
FILE: docs/cloudshell-tutorial.md
================================================
# Online Boutique quickstart

This tutorial shows you how to deploy **[Online Boutique](https://github.com/GoogleCloudPlatform/microservices-demo)** to a Kubernetes cluster.

You'll be able to run Online Boutique on:
- a local **[minikube](https://minikube.sigs.k8s.io/docs/)** cluster, which comes built in to the Cloud Shell instance
- a **[Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine)** cluster using a new or existing [Google Cloud project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project)

Let's get started!


## Kubernetes cluster setup

Set up a Kubernetes cluster using the instructions below for either **minikube** or **GKE**.

### Minikube instructions

Minikube creates a local Kubernetes cluster on Cloud Shell.

1. Click <walkthrough-editor-spotlight spotlightId="minikube-status-bar">minikube</walkthrough-editor-spotlight> on the status bar located at the bottom of the editor window.

2. The command palette will prompt you to choose which minikube cluster to control. Select **minikube** and, in the next prompt, click **Start** if the cluster has not already been started. 

3. If prompted, authorize Cloud Shell to make a GCP API call with your credentials.

*It may take a few minutes for minikube to finish starting.*

Once minikube has started, you're ready to move on to the next step. 

### GKE instructions

In order to create a GKE cluster, you'll need to **[create a Google Cloud project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#creating_a_project)** or use an existing project.

1. Access the command palette by going to **View > Find Command**.

2. Run the command **"Cloud Code: Create GKE cluster"**.

3. Select your GCP project. 

4. Apply the following configurations in the GKE wizard:  
> - Zone: us-central1-b
> - Cluster name: onlineboutique
> - Node count: 4
> - Machine type: e2-standard-2

5. Click **Create Cluster**. Once your cluster has been created successfully, you can move on to the next step.


## Run on Kubernetes

Now you can run Online Boutique on your Kubernetes cluster!

1. Launch the <walkthrough-editor-spotlight spotlightId="cloud-code-status-bar">Cloud Code menu</walkthrough-editor-spotlight> from the status bar and select <walkthrough-editor-spotlight spotlightId="cloud-code-run-on-k8s">Run on Kubernetes</walkthrough-editor-spotlight>.

2. If prompted to select a Skaffold Profile, select **[default]**.

3. Select **Yes** to confirm your current context.

4. If you're using a GKE cluster, you'll need to confirm your container image registry.

5. If prompted, authorize Cloud Shell to make a GCP API call with your credentials.

Cloud Code uses configurations defined in <walkthrough-editor-open-file filePath="skaffold.yaml">skaffold.yaml</walkthrough-editor-open-file> to build and deploy the app. *It may take a few minutes for the deploy to complete.*

6. Once the app is running, the local URLs will be displayed in the <walkthrough-editor-spotlight spotlightId="output">Output</walkthrough-editor-spotlight> terminal. 

7. To access your Online Boutique frontend service, click on the <walkthrough-spotlight-pointer spotlightId="devshell-web-preview-button" target="cloudshell">Web Preview button</walkthrough-spotlight-pointer> in the upper right of the editor window.

8. Select **Change Port** and enter '4503' as the port, then click **Change and Preview**. Your app will open in a new window. 


## Stop the app

To stop running the app: 

1. Go to the <walkthrough-editor-spotlight spotlightId="activity-bar-debug">Debug view</walkthrough-editor-spotlight> 

2. Click the **Stop** icon.

3. Select **Yes** to clean up deployed resources. 

You can start, stop, and debug apps from the Debug view.

### Clean up

If you've deployed your app to a GKE cluster in your Google Cloud project, you'll want to delete the cluster to avoid incurring charges.

1. Navigate to the <walkthrough-editor-spotlight spotlightId="activity-bar-cloud-k8s">Cloud Code - Kubernetes view</walkthrough-editor-spotlight> in the Activity bar.

2. Under the <walkthrough-editor-spotlight spotlightId="cloud-code-gke-explorer">Google Kubernetes Engine Explorer tab</walkthrough-editor-spotlight>, right-click on your cluster and select **Delete Cluster**.


## Conclusion

<walkthrough-conclusion-trophy></walkthrough-conclusion-trophy>

Congratulations! You've successfully deployed Online Boutique using Cloud Shell.

<walkthrough-inline-feedback></walkthrough-inline-feedback>

##### What's next?

Try other deployment options for Online Boutique:
- **Istio/Cloud Service Mesh**: <walkthrough-editor-open-file filePath="./kustomize/components/service-mesh-istio/README.md">See these instructions</walkthrough-editor-open-file>.

Learn more about the [Cloud Shell](https://cloud.google.com/shell) IDE environment and the [Cloud Code](https://cloud.google.com/code) extension.


================================================
FILE: docs/deploystack.md
================================================
## Deploy Online Boutique with DeployStack

The "Open in Google Cloud Shell" button below will use [DeployStack](https://cloud.google.com/shell/docs/cloud-shell-tutorials/deploystack/overview) to deploy Online Boutique to a new Google Kubernetes Engine (GKE) cluster.

<!-- TODO: remove reference to the deploystack-enable branch when it pushes to main -->
<a href="https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2FGoogleCloudPlatform%2Fmicroservices-demo&shellonly=true&cloudshell_image=gcr.io/ds-artifacts-cloudshell/deploystack_custom_image" target="_new">
    <img alt="Open in Cloud Shell" src="https://gstatic.com/cloudssh/images/open-btn.svg">
</a>

The button will open up a [Cloud Shell](https://cloud.google.com/shell) session where you will select your Google Cloud project. After project selection, the following will happen automatically:
1. a GKE cluster will be created inside the select project
2. Online Boutique (and its load generator) will be deployed to that cluster


================================================
FILE: docs/development-guide.md
================================================
# Development Guide 

This doc explains how to build and run the Online Boutique source code locally using the `skaffold` command-line tool.  

## Prerequisites

- [Docker for Desktop](https://www.docker.com/products/docker-desktop)
- [kubectl](https://kubernetes.io/docs/tasks/tools/) (can be installed via `gcloud components install kubectl` for Option 1 - GKE)
- [skaffold **2.0.2+**](https://skaffold.dev/docs/install/) (latest version recommended), a tool that builds and deploys Docker images in bulk. 
- Clone the repository.
    ```sh
    git clone https://github.com/GoogleCloudPlatform/microservices-demo
    cd microservices-demo/
    ```
- A Google Cloud project with Google Container Registry enabled. (for Option 1 - GKE)
- [Minikube](https://minikube.sigs.k8s.io/docs/start/) (optional for Option 2 - Local Cluster)
- [Kind](https://kind.sigs.k8s.io/) (optional for Option 2 - Local Cluster)

## Option 1: Google Kubernetes Engine (GKE)

> 💡 Recommended if you're using Google Cloud and want to try it on
> a realistic cluster. **Note**: If your cluster has Workload Identity enabled, 
> [see these instructions](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity#enable)

1.  Create a Google Kubernetes Engine cluster and make sure `kubectl` is pointing
    to the cluster.

    ```sh
    gcloud services enable container.googleapis.com
    ```

    ```sh
    gcloud container clusters create-auto demo --region=us-central1
    ```

    ```
    kubectl get nodes
    ```

2.  Enable Artifact Registry (AR) on your GCP project and configure the
    `docker` CLI to authenticate to AR:

    ```sh
    gcloud services enable artifactregistry.googleapis.com
    ```

    ```sh
    gcloud artifacts repositories create microservices-demo \
      --repository-format=docker \
      --location=us \
    ```

    ```sh
    gcloud auth configure-docker -q 
    ```

3.  In the root of this repository, run:

    ```
    skaffold run --default-repo=us-docker.pkg.dev/PROJECT_ID/microservices-demo
    ```
    
    Where `PROJECT_ID` is replaced by your Google Cloud project ID.

    This command:

    - Builds the container images.
    - Pushes them to AR.
    - Applies the `./kubernetes-manifests` deploying the application to
      Kubernetes.

    **Troubleshooting:** If you get "No space left on device" error on Google
    Cloud Shell, you can build the images on Google Cloud Build: [Enable the
    Cloud Build
    API](https://console.cloud.google.com/flows/enableapi?apiid=cloudbuild.googleapis.com),
    then run `skaffold run -p gcb --default-repo=us-docker.pkg.dev/[PROJECT_ID]/microservices-demo` instead.

4.  Find the IP address of your application, then visit the application on your
    browser to confirm installation.

        kubectl get service frontend-external

5.  Navigate to `http://EXTERNAL-IP` to access the web frontend.

## Option 2 - Local Cluster 

1. Launch a local Kubernetes cluster with one of the following tools:

    - To launch **Minikube** (tested with Ubuntu Linux). Please, ensure that the
       local Kubernetes cluster has at least:
        - 4 CPUs
        - 4.0 GiB memory
        - 32 GB disk space

      ```shell
      minikube start --cpus=4 --memory 4096 --disk-size 32g
      ```

    - To launch **Docker for Desktop** (tested with Mac/Windows). Go to Preferences:
        - choose “Enable Kubernetes”,
        - set CPUs to at least 3, and Memory to at least 6.0 GiB
        - on the "Disk" tab, set at least 32 GB disk space

    - To launch a **Kind** cluster:

      ```shell
      kind create cluster
      ```

2. Run `kubectl get nodes` to verify you're connected to the respective control plane.

3. Run `skaffold run` (first time will be slow, it can take ~20 minutes).
   This will build and deploy the application. If you need to rebuild the images
   automatically as you refactor the code, run `skaffold dev` command.

4. Run `kubectl get pods` to verify the Pods are ready and running.

5. Run `kubectl port-forward deployment/frontend 8080:8080` to forward a port to the frontend service.

6. Navigate to `localhost:8080` to access the web frontend.

## Adding a new microservice

In general, the set of core microservices for Online Boutique is fairly complete and unlikely to change in the future, but it can be useful to add an additional optional microservice that can be deployed to complement the core services.

See the [Adding a new microservice](adding-new-microservice.md) guide for instructions on how to add a new microservice.

## Cleanup

If you've deployed the application with `skaffold run` command, you can run
`skaffold delete` to clean up the deployed resources.


================================================
FILE: docs/product-requirements.md
================================================
## Product Requirements

This document contains a list of requirements that every change made to this repository should meet.
Every change must:
1. Preserve the golden user journey taken by Kubernetes beginners.
1. Preserve the simplicity of demos.
1. Preserve the simplicity of the GKE quickstart.

These requirements are about the default deployment (default configuration) of Online Boutique.
Changes that will violate any of these rules should not be built into the default configuration of Online Boutique.
Such changes should be opt-in only — ideally, as a [Kustomize Component](https://github.com/GoogleCloudPlatform/microservices-demo/tree/main/kustomize) if they align with the [purpose of Online Boutique](/docs/purpose.md).

### 1. Preserve the golden user journey taken by Kubernetes beginners

The following statement about Online Boutique should always be true:

> A user outside of Google can deploy Online Boutique's default configuration on a [_kind_ Kubernetes cluster](https://kind.sigs.k8s.io/).

This statement describes the golden user journey that we expect new Kubernetes users to take while onboarding to Online Boutique.

Being able to run Online Boutique on a _kind_ cluster ensures that Online Boutique is free and cloud-agnostic. This is aligned with [Google's mission](https://about.google/) of making information universally accessible and useful. To be specific, Online Boutique should be useful and accessible to developers that are new to Kubernetes.

### 2. Preserve the simplicity of demos

New changes should not complicate the primary user journey showcased in live demos and tutorials.

Today, the primary user journey is as follows:
1. Visit Online Boutique on a web browser.
2. Select an item from the homepage and add the item to the cart.
3. The checkout form is pre-populated with placeholder data (e.g. the shipping address).
4. The user checks out and completes the order.

### 3. Preserve the simplicity of the GKE quickstart

New changes should not add additional complexity in the [main Online Boutique quickstart](https://github.com/GoogleCloudPlatform/microservices-demo#quickstart-gke).

In particular, new changes should not add extra required steps or additional required tools in that quickstart.

Ideally, extensions to Online Boutique's default functionality (such as a new microservice or a new cloud service integration) should be added as a [Kustomize Component](https://github.com/GoogleCloudPlatform/microservices-demo/tree/main/kustomize/components) which users can optionally opt into.


================================================
FILE: docs/purpose.md
================================================
## Purpose

Today, the primary purpose of Online Boutique is to demonstrate:

* [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine)
* [Anthos](https://cloud.google.com/anthos)
* [Google Cloud Operations](https://cloud.google.com/products/operations)
* tools and technologies commonly used alongside the above products

while being accessible and useful to all new Kubernetes users.

### Why does the purpose matter?

We filter and prioritize the work to be done in this repository based on the purpose defined above.
If you wish to make changes to this repository that do not align with the above purpose, we encourage you to maintain your own fork of Online Boutique.


================================================
FILE: docs/releasing/README.md
================================================
# Releasing Online Boutique

This document walks through the process of creating a new release of Online Boutique.

## Prerequisites for tagging a release

1. Choose the logical [next release tag](https://github.com/GoogleCloudPlatform/bank-of-anthos/releases), using [semantic versioning](https://semver.org/): `vX.Y.Z`.

   If this release includes significant feature changes, update the minor version (`Y`). Otherwise, for bug-fix releases or standard quarterly release, update the patch version `Z`).

2. Ensure that the following commands are in your `PATH`:
   - `gsed` (found in the `gnu-sed` Brew package for macOS, or by symlinking `sed` for Linux)
   - `gcloud`
   - `helm`

3. Make sure that your `gcloud` is authenticated:

   ```sh
   gcloud auth login
   gcloud auth configure-docker us-central1-docker.pkg.dev
   ```

## Create and tag the new release

Run the `make-release.sh` script found inside the `docs/releasing/` directory:

```sh
# assuming you are inside the root path of the bank-of-anthos repository
export TAG=vX.Y.Z # This is the new version (e.g. `v0.3.5`)
export REPO_PREFIX=us-central1-docker.pkg.dev/google-samples/microservices-demo # This is the Docker repository for tagged images
export PROJECT_ID=google-samples # This is the Google Cloud project for the release CI
./docs/releasing/make-release.sh
```

This script does the following:
1. Uses `make-docker-images.sh` to build and push a Docker image for each microservice to the previously specified repository.
2. Uses `make-release-artifacts.sh` to regenerates (and update the image $TAGS) YAML file at `./release/kubernetes-manifests.yaml` and `./kustomize/base/`.
3. Runs `git tag` and pushes a new branch (e.g., `release/v0.3.5`) with the changes to `./release/kubernetes-manifests.yaml`.

You can then browse the [Container Registry repository](https://pantheon.corp.google.com/gcr/images/google-samples/global/microservices-demo?project=google-samples) to make sure a Docker image was created for each microservice (with the new version tag).

## Create the PR

Now that the release branch has been created, you can find it in the [list of branches](https://github.com/GoogleCloudPlatform/microservices-demo/branches) and create a pull request targeting `main` (the default branch).

This process is going to trigger multiple CI checks as well as stage the release onto a temporary cluster. Once the PR has been approved and all checks are successfully passing, you can then merge the branch. Make sure to include the release draft (see next section) in the pull-request description for reviewers to see.

Once reviewed and you're ready to merge, make sure to not delete the release branch or the tags during that process.

## Add notes to the release

Once the PR has been fully merged, you are ready to create a new release for the newly created [tag](https://github.com/GoogleCloudPlatform/microservices-demo/tags).
- Click the breadcrumbs on the row of the latest tag that was created in the [tags](https://github.com/GoogleCloudPlatform/microservices-demo/tags) page
- Select the `Create release` option

The release notes should contain a brief description of the changes since the previous release (like bug fixed and new features). For inspiration, you can look at the list of [releases](https://github.com/GoogleCloudPlatform/microservices-demo/releases).

> ***Note:*** No assets need to be uploaded. They are picked up automatically from the tagged revision

## Deploy on the production environment

Once the release notes are published, you should then replace the version of the production environment to the newly published version.

1. Connect to the [online-boutique-release GKE cluster](https://pantheon.corp.google.com/kubernetes/clusters/details/us-central1-c/online-boutique-release/details?project=online-boutique-ci):

   ```sh
   gcloud container clusters get-credentials online-boutique-release \
     --zone us-central1-c --project online-boutique-ci
   ```

2. Deploy `release/kubernetes-manifests.yaml` to it:

   ```sh
   kubectl apply -f ./release/kubernetes-manifests.yaml
   ```

3. Remove unnecessary objects:

   ```sh
   kubectl delete service frontend-external
   kubectl delete deployment loadgenerator
   ```

3. Make sure [cymbal-shops.retail.cymbal.dev](https://cymbal-shops.retail.cymbal.dev) works.

## Update major tags

1. Update the relevant major tag (for example, `v1`):

  ```sh
  export MAJOR_TAG=v0 # Edit this as needed (to v1/v2/v3/etc)
  git checkout release/${TAG}
  git pull
  git push --delete origin ${MAJOR_TAG} # Delete the remote tag (if it exists)
  git tag --delete ${MAJOR_TAG} # Delete the local tag (if it exists)
  git tag -a ${MAJOR_TAG} -m "Updating ${MAJOR_TAG} to its most recent release: ${TAG}"
  git push origin ${MAJOR_TAG} # Push the new tag to origin
  ```

## Announce the new release internally

Once the new release is out, you can now announce it via [g/online-boutique-announce](https://groups.google.com/a/google.com/g/online-boutique-announce).


================================================
FILE: docs/releasing/license_header.txt
================================================
# Copyright 2025 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.


================================================
FILE: docs/releasing/make-docker-images.sh
================================================
#!/usr/bin/env bash

# Copyright 2019 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.

# Builds and pushes docker image for each demo microservice.

set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT=$SCRIPT_DIR/../..

log() { echo "$1" >&2; }

TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified}"
PROJECT_ID="${PROJECT_ID:?PROJECT_ID env variable must be specified e.g. google-samples}"

while IFS= read -d $'\0' -r dir; do
    # build image
    svcname="$(basename "${dir}")"
    builddir="${dir}"
    #PR 516 moved cartservice build artifacts one level down to src
    if [ $svcname == "cartservice" ]
    then
        builddir="${dir}/src"
    fi
    image="${REPO_PREFIX}/$svcname:$TAG"
    image_with_sample_public_image_tag="${REPO_PREFIX}/$svcname:sample-public-image-$TAG"
    (
        cd "${builddir}"
        log "Building (and pushing) image on Google Cloud Build: ${image}"
        gcloud builds submit --project=${PROJECT_ID} --tag=${image}
        gcloud artifacts docker tags add ${image} ${image_with_sample_public_image_tag}
    )
done < <(find "${REPO_ROOT}/src" -mindepth 1 -maxdepth 1 -type d -print0)

log "Successfully built and pushed all images."


================================================
FILE: docs/releasing/make-helm-chart.sh
================================================
#!/usr/bin/env bash

# Copyright 2019 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.

# Packages and pushes Online Boutique's Helm chart in public Artifact Registry.

set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT=$SCRIPT_DIR/../..

log() { echo "$1" >&2; }

TAG="${TAG:?TAG env variable must be specified}"
HELM_CHART_REPO="us-docker.pkg.dev/online-boutique-ci/charts"

cd ${REPO_ROOT}/helm-chart
gsed -i "s/^appVersion:.*/appVersion: \"${TAG}\"/" Chart.yaml
gsed -i "s/^version:.*/version: ${TAG:1}/" Chart.yaml
helm package .
helm push onlineboutique-${TAG:1}.tgz oci://$HELM_CHART_REPO

rm ./onlineboutique-${TAG:1}.tgz

log "Successfully built and pushed the Helm chart."


================================================
FILE: docs/releasing/make-release-artifacts.sh
================================================
#!/usr/bin/env bash

# Copyright 2019 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.

# This script compiles manifest files with the image tags and places them in
# /release/...

set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT=$SCRIPT_DIR/../..
[[ -n "${DEBUG:-}" ]] && set -x

log() { echo "$1" >&2; }

TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified}"
OUT_DIR="${OUT_DIR:-${REPO_ROOT}/release}"

print_license_header() {
    cat "${SCRIPT_DIR}/license_header.txt"
    echo
}

print_autogenerated_warning() {
    cat<<EOF
# ----------------------------------------------------------
# WARNING: This file is autogenerated. Do not manually edit.
# ----------------------------------------------------------

EOF
}

# define gsed as a function on Linux for compatibility
[ "$(uname -s)" == "Linux" ] && gsed() {
    sed "$@"
}

read_manifests_except_kustomization() {
    local dir
    dir="$1"

    while IFS= read -d $'\0' -r file; do
        echo "---"

        # strip license headers (pattern "^# ")
        awk '
        /^[^# ]/ { found = 1 }
        found { print }' "${file}"
    done < <(find "${dir}" -name '*.yaml' ! -name 'kustomization.yaml' -type f -print0)
}

mk_kubernetes_manifests() {
    out_manifest="$(read_manifests_except_kustomization "${REPO_ROOT}/kubernetes-manifests")"

    # replace "image" repo, tag for each service
    for dir in ./src/*/
    do
        svcname="$(basename "${dir}")"
        image="$REPO_PREFIX/$svcname:$TAG"

        pattern="^(\s*)image:\s.*$svcname(.*)(\s*)"
        replace="\1image: $image\3"
        out_manifest="$(gsed -r "s|$pattern|$replace|g" <(echo "${out_manifest}") )"
    done

    print_license_header
    print_autogenerated_warning
    echo '# [START gke_release_kubernetes_manifests_microservices_demo]'
    echo "${out_manifest}"
    echo "# [END gke_release_kubernetes_manifests_microservices_demo]"
}

mk_istio_manifests() {
    print_license_header
    print_autogenerated_warning
    echo '# [START servicemesh_release_istio_manifests_microservices_demo]'

    # This just copies the yaml from the component (excluding kustomization.yaml)
    # since there is no easy way to render individual kustomize component resources
    read_manifests_except_kustomization "${REPO_ROOT}/kustomize/components/service-mesh-istio/"
    echo '# [END servicemesh_release_istio_manifests_microservices_demo]'
}

mk_kustomize_base() {
  for file_to_copy in ./kubernetes-manifests/*.yaml
  do
    # Don't copy kustomization.yaml.
    if [[ $file_to_copy == "./kubernetes-manifests/kustomization.yaml" ]]; then
      continue
    fi

    cp ${file_to_copy} ./kustomize/base/

    service_name="$(basename "${file_to_copy}" .yaml)"
    image="$REPO_PREFIX/$service_name:$TAG"

    # Inside redis.yaml, we use the official `redis:alpine` Docker image.
    # We don't use an image from `us-central1-docker.pkg.dev/google-samples/microservices-demo`.
    if [[ $service_name == "redis" ]]; then
      continue
    fi

    pattern="^(\s*)image:\s.*${service_name}(.*)(\s*)"
    replace="\1image: ${image}\3"
    gsed --in-place --regexp-extended "s|${pattern}|${replace}|g" ./kustomize/base/${service_name}.yaml
  done
}

main() {
    mkdir -p "${OUT_DIR}"
    local k8s_manifests_file istio_manifests_file

    k8s_manifests_file="${OUT_DIR}/kubernetes-manifests.yaml"
    mk_kubernetes_manifests > "${k8s_manifests_file}"
    log "Written ${k8s_manifests_file}"

    istio_manifests_file="${OUT_DIR}/istio-manifests.yaml"
    mk_istio_manifests > "${istio_manifests_file}"
    log "Written ${istio_manifests_file}"

    mk_kustomize_base
    log "Written Kustomize base"
}

main


================================================
FILE: docs/releasing/make-release.sh
================================================
#!/usr/bin/env bash

# Copyright 2019 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.

# This script creates a new release by:
# - 1. building/pushing images
# - 2. injecting tags into YAML manifests
# - 3. creating a new git tag
# - 4. pushing the tag/commit to main.

set -euo pipefail
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REPO_ROOT=$SCRIPT_DIR/../..
[[ -n "${DEBUG:-}" ]] && set -x

log() { echo "$1" >&2; }
fail() { log "$1"; exit 1; }

TAG="${TAG:?TAG env variable must be specified}"
REPO_PREFIX="${REPO_PREFIX:?REPO_PREFIX env variable must be specified e.g. us-central1-docker.pkg.dev\/google-samples\/microservices-demo}"
PROJECT_ID="${PROJECT_ID:?PROJECT_ID env variable must be specified e.g. google-samples}"

if [[ "$TAG" != v* ]]; then
    fail "\$TAG must start with 'v', e.g. v0.1.0 (got: $TAG)"
fi

# ensure there are no uncommitted changes
if [[ $(git status -s | wc -l) -gt 0 ]]; then
    echo "error: can't have uncommitted changes"
    exit 1
fi

# make sure local source is up to date
git checkout main
git pull

# build and push images
"${SCRIPT_DIR}"/make-docker-images.sh

# update yaml
"${SCRIPT_DIR}"/make-release-artifacts.sh

# build and push images
"${SCRIPT_DIR}"/make-helm-chart.sh

# create git release / push to new branch
git checkout -b "release/${TAG}"
git add "${REPO_ROOT}/release/"
git add "${REPO_ROOT}/kustomize/base/"
git add "${REPO_ROOT}/helm-chart/"
git commit --allow-empty -m "Release $TAG"
log "Pushing k8s manifests to release/${TAG}..."
git tag "$TAG"
git push --set-upstream origin "release/${TAG}"
git push --tags

log "Successfully tagged release $TAG."


================================================
FILE: helm-chart/Chart.yaml
================================================
# Copyright 2023 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: v2
name: onlineboutique
description: A Helm chart for Kubernetes for Online Boutique

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.10.5

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "v0.10.5"


================================================
FILE: helm-chart/README.md
================================================
# Helm chart for Online Boutique

If you'd like to deploy Online Boutique via its Helm chart, you could leverage the following instructions.

**Warning:** Online Boutique's Helm chart is currently experimental. If you have feedback or run into issues, let us know inside [GitHub Issue #1319](https://github.com/GoogleCloudPlatform/microservices-demo/issues/1319) or by creating a [new GitHub Issue](https://github.com/GoogleCloudPlatform/microservices-demo/issues/new/choose).

Deploy the default setup of Online Boutique:
```sh
helm upgrade onlineboutique oci://us-docker.pkg.dev/online-boutique-ci/charts/onlineboutique \
    --install
```

Deploy advanced scenario of Online Boutique:
```sh
helm upgrade onlineboutique oci://us-docker.pkg.dev/online-boutique-ci/charts/onlineboutique \
    --install \
    --create-namespace \
    --set images.repository=us-docker.pkg.dev/my-project/microservices-demo \
    --set frontend.externalService=false \
    --set redis.create=false \
    --set cartservice.database.type=spanner \
    --set cartservice.database.connectionString=projects/my-project/instances/onlineboutique/databases/carts \
    --set serviceAccounts.create=true \
    --set authorizationPolicies.create=true \
    --set networkPolicies.create=true \
    --set sidecars.create=true \
    --set frontend.virtualService.create=true \
    --set 'serviceAccounts.annotations.iam\.gke\.io/gcp-service-account=spanner-db-user@my-project.iam.gserviceaccount.com' \
    --set serviceAccounts.annotationsOnlyForCartservice=true \
    -n onlineboutique
```

For the full list of configurations, see [values.yaml](./values.yaml).

You could also find advanced scenarios with these blogs below:
- [Online Boutique sample’s Helm chart, to simplify the setup of advanced and secured scenarios with Service Mesh and GitOps](https://medium.com/google-cloud/246119e46d53)
- [gRPC health probes with Kubernetes 1.24+](https://medium.com/google-cloud/b5bd26253a4c)
- [Use Google Cloud Spanner with the Online Boutique sample](https://medium.com/google-cloud/f7248e077339)

================================================
FILE: helm-chart/templates/NOTES.txt
================================================
{{- if and .Values.frontend.create .Values.frontend.externalService }}
Note: It may take a few minutes for the LoadBalancer IP to be available.

Watch the status of the frontend IP address with:
    kubectl get --namespace {{ .Release.Namespace }} svc -w {{ .Values.frontend.name }}-external

Get the external IP address of the frontend:
    export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ .Values.frontend.name }}-external --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
    echo http://$SERVICE_IP
{{- end }}
{{- if .Values.frontend.virtualService.create }}
Get the external IP address of the ingress gateway:
    export SERVICE_IP=$(kubectl get svc --namespace {{ .Values.frontend.virtualService.gateway.namespace }} {{ .Values.frontend.virtualService.gateway.name }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
    echo http://$SERVICE_IP
{{- end }}


================================================
FILE: helm-chart/templates/adservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.adService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.adService.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.adService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.adService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.adService.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.adService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.adService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.adService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - containerPort: 9555
        env:
        - name: PORT
          value: "9555"
        resources:
          {{- toYaml .Values.adService.resources | nindent 10 }}
        readinessProbe:
          initialDelaySeconds: 20
          periodSeconds: 15
          grpc:
            port: 9555
        livenessProbe:
          initialDelaySeconds: 20
          periodSeconds: 15
          grpc:
            port: 9555
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.adService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.adService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.adService.name }}
  ports:
  - name: grpc
    port: 9555
    targetPort: 9555
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.adService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.adService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.frontend.name }}
    ports:
     - port: 9555
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.adService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.adService.name }}
  egress:
  - hosts:
    - istio-system/*
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.adService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.adService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.frontend.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        paths:
        - /hipstershop.AdService/GetAds
        methods:
        - POST
        ports:
        - "9555"
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/cartservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.cartService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.cartService.name }}
  namespace: {{.Release.Namespace}}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.cartService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.cartService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.cartService.name }}
  template:
    metadata:
      {{- if .Values.cartDatabase.externalRedisTlsOrigination.enable }}
      annotations:
        sidecar.istio.io/userVolumeMount: '[{"name": "{{ .Values.cartDatabase.externalRedisTlsOrigination.name }}", "mountPath": "/etc/certs", "readonly": true}]'
        sidecar.istio.io/userVolume: '[{"name": "{{ .Values.cartDatabase.externalRedisTlsOrigination.name }}", "secret": {"secretName": "{{ .Values.cartDatabase.externalRedisTlsOrigination.name }}"}}]'
        proxy.istio.io/config: '{"holdApplicationUntilProxyStarts": true}'
      {{- end }}
      labels:
        app: {{ .Values.cartService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.cartService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
      {{- end }}
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.cartService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - containerPort: 7070
        env:
        {{- if eq .Values.cartDatabase.type "spanner" }}
        - name: SPANNER_CONNECTION_STRING
        {{- else }}
        - name: REDIS_ADDR
        {{- end }}
          value: {{ .Values.cartDatabase.connectionString | quote }}
        resources:
          {{- toYaml .Values.cartService.resources | nindent 10 }}
        readinessProbe:
          initialDelaySeconds: 15
          grpc:
            port: 7070
        livenessProbe:
          initialDelaySeconds: 15
          periodSeconds: 10
          grpc:
            port: 7070
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.cartService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.cartService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.cartService.name }}
  ports:
  - name: grpc
    port: 7070
    targetPort: 7070
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.cartService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.cartService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.frontend.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.checkoutService.name }}
    ports:
     - port: 7070
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.cartService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.cartService.name }}
  egress:
  - hosts:
    - istio-system/*
    {{- if eq .Values.cartDatabase.type "redis" }}
    {{- if .Values.cartDatabase.externalRedisTlsOrigination.enable }}
    - ./{{ .Values.cartDatabase.externalRedisTlsOrigination.name }}.{{ .Release.Namespace }}
    {{- else }}
    - ./{{ .Values.cartDatabase.inClusterRedis.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
    {{- end }}
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.cartService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.cartService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.frontend.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.checkoutService.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        paths:
        - /hipstershop.CartService/AddItem
        - /hipstershop.CartService/GetCart
        - /hipstershop.CartService/EmptyCart
        methods:
        - POST
        ports:
        - "7070"
{{- end }}

{{- if .Values.cartDatabase.inClusterRedis.create }}
---
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.cartDatabase.inClusterRedis.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.cartDatabase.inClusterRedis.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.cartDatabase.inClusterRedis.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.cartDatabase.inClusterRedis.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.cartDatabase.inClusterRedis.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: redis
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        {{- if .Values.cartDatabase.inClusterRedis.publicRepository }}
        image: redis:alpine@sha256:2afba59292f25f5d1af200496db41bea2c6c816b059f57ae74703a50a03a27d0
        {{- else }}
        image: {{ .Values.images.repository }}/redis:alpine
        {{- end }}
        ports:
        - containerPort: 6379
        readinessProbe:
          periodSeconds: 5
          tcpSocket:
            port: 6379
        livenessProbe:
          periodSeconds: 5
          tcpSocket:
            port: 6379
        volumeMounts:
        - mountPath: /data
          name: redis-data
        resources:
          limits:
            memory: 256Mi
            cpu: 125m
          requests:
            cpu: 70m
            memory: 200Mi
      volumes:
      - name: redis-data
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.cartDatabase.inClusterRedis.name }}
  namespace: {{ .Release.Namespace }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.cartDatabase.inClusterRedis.name }}
  ports:
  - name: tcp-redis
    port: 6379
    targetPort: 6379
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.cartDatabase.inClusterRedis.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.cartDatabase.inClusterRedis.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.cartService.name }}
    ports:
     - port: 6379
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.cartDatabase.inClusterRedis.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.cartDatabase.inClusterRedis.name }}
  egress:
  - hosts:
    - istio-system/*
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.cartDatabase.inClusterRedis.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.cartDatabase.inClusterRedis.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.cartService.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        ports:
        - "6379"
{{- end }}
{{- end }}
{{- if .Values.cartDatabase.externalRedisTlsOrigination.enable }}
---
apiVersion: v1
data:
  {{ .Values.cartDatabase.externalRedisTlsOrigination.name }}.pem: {{ .Values.cartDatabase.externalRedisTlsOrigination.certificate | b64enc | quote }}
kind: Secret
metadata:
  name: {{ .Values.cartDatabase.externalRedisTlsOrigination.name }}
  namespace: {{ .Release.Namespace }}
---
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: {{ .Values.cartDatabase.externalRedisTlsOrigination.name }}
  namespace: {{ .Release.Namespace }}
spec:
  exportTo:
  - '.'
  host: {{ .Values.cartDatabase.externalRedisTlsOrigination.name }}.{{ .Release.Namespace }}
  trafficPolicy:
    tls:
      mode: SIMPLE
      caCertificates: /etc/certs/{{ .Values.cartDatabase.externalRedisTlsOrigination.name }}.pem
---
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: {{ .Values.cartDatabase.externalRedisTlsOrigination.name }}
  namespace: {{ .Release.Namespace }}
spec:
  hosts:
  - {{ .Values.cartDatabase.externalRedisTlsOrigination.name }}.{{ .Release.Namespace }}
  addresses:
  - {{ .Values.cartDatabase.externalRedisTlsOrigination.endpointAddress }}/32
  endpoints:
  - address: {{ .Values.cartDatabase.externalRedisTlsOrigination.endpointAddress }}
  location: MESH_EXTERNAL
  resolution: STATIC
  ports:
  - number: {{ .Values.cartDatabase.externalRedisTlsOrigination.endpointPort }}
    name: tcp-redis
    protocol: TCP
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/checkoutservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.checkoutService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.checkoutService.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.checkoutService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.checkoutService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.checkoutService.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.checkoutService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.checkoutService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.checkoutService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - containerPort: 5050
        readinessProbe:
          grpc:
            port: 5050
        livenessProbe:
          grpc:
            port: 5050
        env:
        - name: PORT
          value: "5050"
        - name: PRODUCT_CATALOG_SERVICE_ADDR
          value: "{{ .Values.productCatalogService.name }}:3550"
        - name: SHIPPING_SERVICE_ADDR
          value: "{{ .Values.shippingService.name }}:50051"
        - name: PAYMENT_SERVICE_ADDR
          value: "{{ .Values.paymentService.name }}:50051"
        - name: EMAIL_SERVICE_ADDR
          value: "{{ .Values.emailService.name }}:5000"
        - name: CURRENCY_SERVICE_ADDR
          value: "{{ .Values.currencyService.name }}:7000"
        - name: CART_SERVICE_ADDR
          value: "{{ .Values.cartService.name }}:7070"
        {{- if .Values.opentelemetryCollector.create }}
        - name: COLLECTOR_SERVICE_ADDR
          value: "{{ .Values.opentelemetryCollector.name }}:4317"
        - name: OTEL_SERVICE_NAME
          value: "{{ .Values.checkoutService.name }}"
        {{- end }}
        {{- if .Values.googleCloudOperations.tracing }}
        - name: ENABLE_TRACING
          value: "1"
        {{- end }}
        {{- if .Values.googleCloudOperations.profiler }}
        - name: ENABLE_PROFILER
          value: "1"
        {{- end }}
        resources:
          {{- toYaml .Values.checkoutService.resources | nindent 10 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.checkoutService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.checkoutService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.checkoutService.name }}
  ports:
  - name: grpc
    port: 5050
    targetPort: 5050
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.checkoutService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.checkoutService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.frontend.name }}
    ports:
     - port: 5050
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.checkoutService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.checkoutService.name }}
  egress:
  - hosts:
    - istio-system/*
    - ./{{ .Values.cartService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.currencyService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.emailService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.paymentService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.productCatalogService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.shippingService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.checkoutService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.checkoutService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.frontend.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        paths:
        - /hipstershop.CheckoutService/PlaceOrder
        methods:
        - POST
        ports:
        - "5050"
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/common.yaml
================================================
# 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
#
#      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.

{{- if .Values.networkPolicies.create }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: {{ .Release.Namespace }}
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: deny-all
  namespace: {{ .Release.Namespace }}
spec: {}
{{- end }}


================================================
FILE: helm-chart/templates/currencyservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.currencyService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.currencyService.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.currencyService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.currencyService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.currencyService.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.currencyService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.currencyService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.currencyService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - name: grpc
          containerPort: 7000
        env:
        - name: PORT
          value: "7000"
        {{- if .Values.opentelemetryCollector.create }}
        - name: COLLECTOR_SERVICE_ADDR
          value: "{{ .Values.opentelemetryCollector.name }}:4317"
        - name: OTEL_SERVICE_NAME
          value: "{{ .Values.currencyService.name }}"
        {{- end }}
        {{- if .Values.googleCloudOperations.tracing }}
        - name: ENABLE_TRACING
          value: "1"
        {{- end }}
        {{- if not .Values.googleCloudOperations.profiler }}
        - name: DISABLE_PROFILER
          value: "1"
        {{- end }}
        readinessProbe:
          grpc:
            port: 7000
        livenessProbe:
          grpc:
            port: 7000
        resources:
          {{- toYaml .Values.currencyService.resources | nindent 10 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.currencyService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.currencyService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.currencyService.name }}
  ports:
  - name: grpc
    port: 7000
    targetPort: 7000
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.currencyService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.currencyService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.frontend.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.checkoutService.name }}
    ports:
     - port: 7000
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.currencyService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.currencyService.name }}
  egress:
  - hosts:
    - istio-system/*
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.currencyService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.currencyService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.frontend.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.checkoutService.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        paths:
        - /hipstershop.CurrencyService/Convert
        - /hipstershop.CurrencyService/GetSupportedCurrencies
        methods:
        - POST
        ports:
        - "7000"
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/emailservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.emailService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.emailService.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.emailService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.emailService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.emailService.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.emailService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.emailService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.emailService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - containerPort: 8080
        env:
        - name: PORT
          value: "8080"
        {{- if .Values.opentelemetryCollector.create }}
        - name: COLLECTOR_SERVICE_ADDR
          value: "{{ .Values.opentelemetryCollector.name }}:4317"
        - name: OTEL_SERVICE_NAME
          value: "{{ .Values.emailService.name }}"
        {{- end }}
        {{- if .Values.googleCloudOperations.tracing }}
        - name: ENABLE_TRACING
          value: "1"
        {{- end }}
        {{- if not .Values.googleCloudOperations.profiler }}
        - name: DISABLE_PROFILER
          value: "1"
        {{- end }}
        readinessProbe:
          periodSeconds: 5
          grpc:
            port: 8080
        livenessProbe:
          periodSeconds: 5
          grpc:
            port: 8080
        resources:
          {{- toYaml .Values.emailService.resources | nindent 10 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.emailService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.emailService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.emailService.name }}
  ports:
  - name: grpc
    port: 5000
    targetPort: 8080
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.emailService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.emailService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.checkoutService.name }}
    ports:
     - port: 8080
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.emailService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.emailService.name }}
  egress:
  - hosts:
    - istio-system/*
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.emailService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.emailService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.checkoutService.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        paths:
        - /hipstershop.EmailService/SendOrderConfirmation
        methods:
        - POST
        ports:
        - "8080"
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/frontend.yaml
================================================
# 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
#
#      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.

{{- if .Values.frontend.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.frontend.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.frontend.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.frontend.name }}
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "true"
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.frontend.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
        - name: server
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
            privileged: false
            readOnlyRootFilesystem: true
          image: {{ .Values.images.repository }}/{{ .Values.frontend.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
          ports:
          - containerPort: 8080
          readinessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "shop_session-id=x-readiness-probe"
          livenessProbe:
            initialDelaySeconds: 10
            httpGet:
              path: "/_healthz"
              port: 8080
              httpHeaders:
              - name: "Cookie"
                value: "shop_session-id=x-liveness-probe"
          env:
          - name: PORT
            value: "8080"
          - name: PRODUCT_CATALOG_SERVICE_ADDR
            value: "{{ .Values.productCatalogService.name }}:3550"
          - name: CURRENCY_SERVICE_ADDR
            value: "{{ .Values.currencyService.name }}:7000"
          - name: CART_SERVICE_ADDR
            value: "{{ .Values.cartService.name }}:7070"
          - name: RECOMMENDATION_SERVICE_ADDR
            value: "{{ .Values.recommendationService.name }}:8080"
          - name: SHIPPING_SERVICE_ADDR
            value: "{{ .Values.shippingService.name }}:50051"
          - name: CHECKOUT_SERVICE_ADDR
            value: "{{ .Values.checkoutService.name }}:5050"
          - name: AD_SERVICE_ADDR
            value: "{{ .Values.adService.name }}:9555"
          - name: SHOPPING_ASSISTANT_SERVICE_ADDR
            value: "{{ .Values.shoppingAssistantService.name }}:80"
          - name: ENV_PLATFORM
            value: {{ .Values.frontend.platform | quote }}
          {{- if .Values.opentelemetryCollector.create }}
          - name: COLLECTOR_SERVICE_ADDR
            value: "{{ .Values.opentelemetryCollector.name }}:4317"
          - name: OTEL_SERVICE_NAME
            value: "{{ .Values.frontend.name }}"
          {{- end }}
          {{- if .Values.googleCloudOperations.tracing }}
          - name: ENABLE_TRACING
            value: "1"
          {{- end }}
          {{- if .Values.googleCloudOperations.profiler }}
          - name: ENABLE_PROFILER
            value: "1"
          {{- end }}
          - name: CYMBAL_BRANDING
            value: {{ .Values.frontend.cymbalBranding | quote }}
          - name: ENABLE_ASSISTANT
            value: {{ .Values.shoppingAssistantService.create | quote }}
          - name: ENABLE_SINGLE_SHARED_SESSION
            value: {{ .Values.frontend.singleSharedSession | quote }}
          resources:
            {{- toYaml .Values.frontend.resources | nindent 12 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.frontend.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.frontend.name }}
  ports:
  - name: http
    port: 80
    targetPort: 8080
{{- if .Values.frontend.externalService }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.frontend.name }}-external
  namespace: {{ .Release.Namespace }}
spec:
  type: LoadBalancer
  selector:
    app: {{ .Values.frontend.name }}
  ports:
  - name: http
    port: 80
    targetPort: 8080
{{- end }}
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.frontend.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  {{- if .Values.frontend.externalService }}
  - {}
  {{- else }}
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.loadGenerator.name }}
    {{- if .Values.frontend.virtualService.create }}
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: {{ .Values.frontend.virtualService.gateway.namespace }}
      podSelector:
        matchLabels:
          {{ .Values.frontend.virtualService.gateway.labelKey }}: {{ .Values.frontend.virtualService.gateway.labelValue }}
    {{- end }}
    ports:
     - port: 8080
       protocol: TCP
  {{- end }}
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.frontend.name }}
  egress:
  - hosts:
    - istio-system/*
    - ./{{ .Values.adService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.cartService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.checkoutService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.currencyService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.productCatalogService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.recommendationService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    - ./{{ .Values.shippingService.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.frontend.name }}
  rules:
  {{- if .Values.frontend.externalService }}
  - to:
  {{- else }}
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.loadGenerator.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
        {{- if .Values.frontend.virtualService.create }}
        - cluster.local/ns/{{ .Values.frontend.virtualService.gateway.namespace }}/sa/{{ .Values.frontend.virtualService.gateway.name }}
        {{- end }}
    to:
  {{- end }}
    - operation:
        methods:
        - GET
        - POST
        ports:
        - "8080"
{{- end }}
{{- if .Values.frontend.virtualService.create }}
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: {{ .Values.frontend.name }}
  namespace: {{ .Release.Namespace }}
spec:
  {{- with .Values.frontend.virtualService.hosts }}
  hosts:
  {{- toYaml . | nindent 2 }}
  {{- end }}
  gateways:
  - {{ .Values.frontend.virtualService.gateway.namespace }}/{{ .Values.frontend.virtualService.gateway.name }}
  http:
  - route:
    - destination:
        host: {{ .Values.frontend.name }}
        port:
          number: 80
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/loadgenerator.yaml
================================================
# Copyright 2022 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.

{{- if .Values.loadGenerator.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.loadGenerator.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.loadGenerator.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.loadGenerator.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.loadGenerator.name }}
  replicas: 1
  template:
    metadata:
      labels:
        app: {{ .Values.loadGenerator.name }}
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "true"
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.loadGenerator.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      restartPolicy: Always
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      {{- if .Values.loadGenerator.checkFrontendInitContainer }}
      initContainers:
      - command:
        - /bin/sh
        - -exc
        - |
          MAX_RETRIES=12
          RETRY_INTERVAL=10
          for i in $(seq 1 $MAX_RETRIES); do
            echo "Attempt $i: Pinging frontend: ${FRONTEND_ADDR}..."
            STATUSCODE=$(wget --server-response http://${FRONTEND_ADDR} 2>&1 | awk '/^  HTTP/{print $2}')
            if [ $STATUSCODE -eq 200 ]; then
                echo "Frontend is reachable."
                exit 0
            fi
            echo "Error: Could not reach frontend - Status code: ${STATUSCODE}"
            sleep $RETRY_INTERVAL
          done
          echo "Failed to reach frontend after $MAX_RETRIES attempts."
          exit 1
        name: frontend-check
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f
        env:
        - name: FRONTEND_ADDR
          value: "{{ .Values.frontend.name }}:80"
      {{- end }}
      containers:
      - name: main
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.loadGenerator.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        env:
        - name: FRONTEND_ADDR
          value: "{{ .Values.frontend.name }}:80"
        - name: USERS
          value: "10"
        - name: RATE
          value: "1"
        resources:
          {{- toYaml .Values.loadGenerator.resources | nindent 10 }}
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.loadGenerator.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.loadGenerator.name }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.loadGenerator.name }}
  policyTypes:
  - Egress
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.loadGenerator.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.loadGenerator.name }}
  egress:
  - hosts:
    - istio-system/*
    - ./{{ .Values.frontend.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/opentelemetry-collector.yaml
================================================
# Copyright 2022 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.

{{- if .Values.opentelemetryCollector.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.opentelemetryCollector.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.opentelemetryCollector.name }}
  namespace: {{ .Release.Namespace }}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: {{ .Values.opentelemetryCollector.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.opentelemetryCollector.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.opentelemetryCollector.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      {{- if eq .Values.opentelemetryCollector.projectId "PROJECT_ID" }}
      initContainers:
      # Init container retrieves the current cloud project id from the metadata server
      # and inserts it into the collector config template
      # https://cloud.google.com/compute/docs/storing-retrieving-metadata
      - name: otel-gateway-init
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: busybox:latest@sha256:b3255e7dfbcd10cb367af0d409747d511aeb66dfac98cf30e97e87e4207dd76f
        command:
        - '/bin/sh'
        - '-c'
        - |
           sed "s/PROJECT_ID/$(curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/project/project-id)/" /template/collector-gateway-config-template.yaml >> /conf/collector-gateway-config.yaml
        volumeMounts:
        - name: collector-gateway-config-template
          mountPath: /template
        - name: collector-gateway-config
          mountPath: /conf
      {{- end }}
      containers:
      # This gateway container will receive traces and metrics from each microservice
      # and forward it to GCP
      - name: otel-gateway
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        args:
        - --config=/conf/collector-gateway-config.yaml
        image: otel/opentelemetry-collector-contrib:0.147.0@sha256:e7c92c715f28ff142f3bcaccd4fc5603cf4c71276ef09954a38eb4038500a5a5
        volumeMounts:
        - name: collector-gateway-config
          mountPath: /conf
      volumes:
      # Simple ConfigMap volume with template file
      - name: collector-gateway-config-template
        configMap:
          items:
          - key: collector-gateway-config-template.yaml
            path: collector-gateway-config-template.yaml
          name: collector-gateway-config-template
      # Create a volume to store the expanded template (with correct cloud project ID)
      - name: collector-gateway-config
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.opentelemetryCollector.name }}
  namespace: {{ .Release.Namespace }}
spec:
  ports:
  - name: grpc-otlp
    port: 4317
    protocol: TCP
    targetPort: 4317
  selector:
    app: {{ .Values.opentelemetryCollector.name }}
  type: ClusterIP
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: collector-gateway-config-template
  namespace: {{ .Release.Namespace }}
# Open Telemetry Collector config
# https://opentelemetry.io/docs/collector/configuration/
data:
  collector-gateway-config-template.yaml: |
    receivers:
      otlp:
        protocols: 
          grpc:
    processors:
    exporters:
      googlecloud:
        project: {{ .Values.opentelemetryCollector.projectId | quote }}
    service:
      pipelines:
        traces:
          receivers: [otlp] # Receive otlp-formatted data from other collector instances
          processors: []
          exporters: [googlecloud] # Export traces directly to Google Cloud
        metrics:
          receivers: [otlp]
          processors: []
          exporters: [googlecloud] # Export metrics to Google Cloud
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.opentelemetryCollector.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.opentelemetryCollector.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.adService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.cartService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.checkoutService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.currencyService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.emailService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.frontend.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.loadGenerator.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.paymentService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.productCatalogService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.recommendationService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.shippingService.name }}
    ports:
     - port: 4317
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.opentelemetryCollector.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.opentelemetryCollector.name }}
  egress:
  - hosts:
    - istio-system/*
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.opentelemetryCollector.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.opentelemetryCollector.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.adService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.cartService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.checkoutService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.currencyService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.emailService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.frontend.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.loadGenerator.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.paymentService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.productCatalogService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.recommendationService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.shippingService.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        ports:
        - "4317"
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/paymentservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.paymentService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.paymentService.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.paymentService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.paymentService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.paymentService.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.paymentService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.paymentService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.paymentService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - containerPort: 50051
        env:
        - name: PORT
          value: "50051"
        {{- if .Values.opentelemetryCollector.create }}
        - name: COLLECTOR_SERVICE_ADDR
          value: "{{ .Values.opentelemetryCollector.name }}:4317"
        - name: OTEL_SERVICE_NAME
          value: "{{ .Values.paymentService.name }}"
        {{- end }}
        {{- if .Values.googleCloudOperations.tracing }}
        - name: ENABLE_TRACING
          value: "1"
        {{- end }}
        {{- if not .Values.googleCloudOperations.profiler }}
        - name: DISABLE_PROFILER
          value: "1"
        {{- end }}
        readinessProbe:
          grpc:
            port: 50051
        livenessProbe:
          grpc:
            port: 50051
        resources:
          {{- toYaml .Values.paymentService.resources | nindent 10 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.paymentService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.paymentService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.paymentService.name }}
  ports:
  - name: grpc
    port: 50051
    targetPort: 50051
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.paymentService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.paymentService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.checkoutService.name }}
    ports:
     - port: 50051
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.paymentService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.paymentService.name }}
  egress:
  - hosts:
    - istio-system/*
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.paymentService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.paymentService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.checkoutService.name }}
        {{- else }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/default
        {{- end }}
    to:
    - operation:
        paths:
        - /hipstershop.PaymentService/Charge
        methods:
        - POST
        ports:
        - "50051"
{{- end }}
{{- end }}


================================================
FILE: helm-chart/templates/productcatalogservice.yaml
================================================
# 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
#
#      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.

{{- if .Values.productCatalogService.create }}
{{- if .Values.serviceAccounts.create }}
apiVersion: v1
kind: ServiceAccount
metadata:
  name: {{ .Values.productCatalogService.name }}
  namespace: {{.Release.Namespace}}
  {{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}
  {{- with .Values.serviceAccounts.annotations }}
  annotations:
    {{- toYaml . | nindent 4 }}
  {{- end }}
  {{- end }}
---
{{- end }}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Values.productCatalogService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.productCatalogService.name }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.productCatalogService.name }}
  template:
    metadata:
      labels:
        app: {{ .Values.productCatalogService.name }}
    spec:
      {{- if .Values.serviceAccounts.create }}
      serviceAccountName: {{ .Values.productCatalogService.name }}
      {{- else }}
      serviceAccountName: default
      {{- end }}
      terminationGracePeriodSeconds: 5
      {{- if .Values.securityContext.enable }}
      securityContext:
        fsGroup: 1000
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        {{- if .Values.seccompProfile.enable }}
        seccompProfile:
          type: {{ .Values.seccompProfile.type }}
        {{- end }}
      {{- end }}
      containers:
      - name: server
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
              - ALL
          privileged: false
          readOnlyRootFilesystem: true
        image: {{ .Values.images.repository }}/{{ .Values.productCatalogService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}
        ports:
        - containerPort: 3550
        env:
        - name: PORT
          value: "3550"
        {{- if .Values.opentelemetryCollector.create }}
        - name: COLLECTOR_SERVICE_ADDR
          value: "{{ .Values.opentelemetryCollector.name }}:4317"
        - name: OTEL_SERVICE_NAME
          value: "{{ .Values.productCatalogService.name }}"
        {{- end }}
        {{- if .Values.googleCloudOperations.tracing }}
        - name: ENABLE_TRACING
          value: "1"
        {{- end }}
        {{- if not .Values.googleCloudOperations.profiler }}
        - name: DISABLE_PROFILER
          value: "1"
        {{- end }}
        - name: EXTRA_LATENCY
          value: {{ .Values.productCatalogService.extraLatency }}
        readinessProbe:
          grpc:
            port: 3550
        livenessProbe:
          grpc:
            port: 3550
        resources:
          {{- toYaml .Values.productCatalogService.resources | nindent 10 }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .Values.productCatalogService.name }}
  namespace: {{ .Release.Namespace }}
  labels:
    app: {{ .Values.productCatalogService.name }}
spec:
  type: ClusterIP
  selector:
    app: {{ .Values.productCatalogService.name }}
  ports:
  - name: grpc
    port: 3550
    targetPort: 3550
{{- if .Values.networkPolicies.create }}
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: {{ .Values.productCatalogService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  podSelector:
    matchLabels:
      app: {{ .Values.productCatalogService.name }}
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: {{ .Values.frontend.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.checkoutService.name }}
    - podSelector:
        matchLabels:
          app: {{ .Values.recommendationService.name }}
    ports:
     - port: 3550
       protocol: TCP
  egress:
  - {}
{{- end }}
{{- if .Values.sidecars.create }}
---
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
  name: {{ .Values.productCatalogService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  workloadSelector:
    labels:
      app: {{ .Values.productCatalogService.name }}
  egress:
  - hosts:
    - istio-system/*
    {{- if .Values.opentelemetryCollector.create }}
    - ./{{ .Values.opentelemetryCollector.name }}.{{ .Release.Namespace }}.svc.cluster.local
    {{- end }}
{{- end }}
{{- if .Values.authorizationPolicies.create }}
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: {{ .Values.productCatalogService.name }}
  namespace: {{ .Release.Namespace }}
spec:
  selector:
    matchLabels:
      app: {{ .Values.productCatalogService.name }}
  rules:
  - from:
    - source:
        principals:
        {{- if .Values.serviceAccounts.create }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.frontend.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.checkoutService.name }}
        - cluster.local/ns/{{ .Release.Namespace }}/sa/{{ .Values.recommenda
Download .txt
gitextract_n0fmcr6r/

├── .deploystack/
│   ├── deploystack.yaml
│   ├── messages/
│   │   ├── description.txt
│   │   └── success.txt
│   ├── scripts/
│   │   └── preinit.sh
│   ├── test
│   └── test.yaml
├── .editorconfig
├── .gitattributes
├── .github/
│   ├── CODEOWNERS
│   ├── CODE_OF_CONDUCT.md
│   ├── CONTRIBUTING.md
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug-report.md
│   │   ├── feature-request.md
│   │   └── other.md
│   ├── SECURITY.md
│   ├── auto-approve.yml
│   ├── header-checker-lint.yml
│   ├── pull_request_template.md
│   ├── release-cluster/
│   │   ├── README.md
│   │   ├── backend-config.yaml
│   │   ├── frontend-config.yaml
│   │   ├── frontend-ingress.yaml
│   │   ├── frontend-service.yaml
│   │   └── managed-cert.yaml
│   ├── renovate.json5
│   ├── snippet-bot.yml
│   ├── terraform/
│   │   ├── README.md
│   │   ├── main.tf
│   │   ├── variables.tf
│   │   └── versions.tf
│   └── workflows/
│       ├── README.md
│       ├── ci-main.yaml
│       ├── ci-pr.yaml
│       ├── cleanup.yaml
│       ├── helm-chart-ci.yaml
│       ├── install-dependencies.sh
│       ├── kubevious-manifests-ci.yaml
│       ├── kustomize-build-ci.yaml
│       └── terraform-validate-ci.yaml
├── .gitignore
├── LICENSE
├── README.md
├── cloudbuild.yaml
├── docs/
│   ├── adding-new-microservice.md
│   ├── cloudshell-tutorial.md
│   ├── deploystack.md
│   ├── development-guide.md
│   ├── product-requirements.md
│   ├── purpose.md
│   └── releasing/
│       ├── README.md
│       ├── license_header.txt
│       ├── make-docker-images.sh
│       ├── make-helm-chart.sh
│       ├── make-release-artifacts.sh
│       └── make-release.sh
├── helm-chart/
│   ├── Chart.yaml
│   ├── README.md
│   ├── templates/
│   │   ├── NOTES.txt
│   │   ├── adservice.yaml
│   │   ├── cartservice.yaml
│   │   ├── checkoutservice.yaml
│   │   ├── common.yaml
│   │   ├── currencyservice.yaml
│   │   ├── emailservice.yaml
│   │   ├── frontend.yaml
│   │   ├── loadgenerator.yaml
│   │   ├── opentelemetry-collector.yaml
│   │   ├── paymentservice.yaml
│   │   ├── productcatalogservice.yaml
│   │   ├── recommendationservice.yaml
│   │   └── shippingservice.yaml
│   └── values.yaml
├── istio-manifests/
│   ├── allow-egress-googleapis.yaml
│   ├── frontend-gateway.yaml
│   └── frontend.yaml
├── kubernetes-manifests/
│   ├── README.md
│   ├── adservice.yaml
│   ├── cartservice.yaml
│   ├── checkoutservice.yaml
│   ├── currencyservice.yaml
│   ├── emailservice.yaml
│   ├── frontend.yaml
│   ├── kustomization.yaml
│   ├── loadgenerator.yaml
│   ├── paymentservice.yaml
│   ├── productcatalogservice.yaml
│   ├── recommendationservice.yaml
│   └── shippingservice.yaml
├── kustomize/
│   ├── README.md
│   ├── base/
│   │   ├── adservice.yaml
│   │   ├── cartservice.yaml
│   │   ├── checkoutservice.yaml
│   │   ├── currencyservice.yaml
│   │   ├── emailservice.yaml
│   │   ├── frontend.yaml
│   │   ├── kustomization.yaml
│   │   ├── loadgenerator.yaml
│   │   ├── paymentservice.yaml
│   │   ├── productcatalogservice.yaml
│   │   ├── recommendationservice.yaml
│   │   └── shippingservice.yaml
│   ├── components/
│   │   ├── alloydb/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── container-images-registry/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── container-images-tag/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── container-images-tag-suffix/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── custom-base-url/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── cymbal-branding/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── google-cloud-operations/
│   │   │   ├── README.md
│   │   │   ├── kustomization.yaml
│   │   │   └── otel-collector.yaml
│   │   ├── memorystore/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── network-policies/
│   │   │   ├── README.md
│   │   │   ├── kustomization.yaml
│   │   │   ├── network-policy-adservice.yaml
│   │   │   ├── network-policy-cartservice.yaml
│   │   │   ├── network-policy-checkoutservice.yaml
│   │   │   ├── network-policy-currencyservice.yaml
│   │   │   ├── network-policy-deny-all.yaml
│   │   │   ├── network-policy-emailservice.yaml
│   │   │   ├── network-policy-frontend.yaml
│   │   │   ├── network-policy-loadgenerator.yaml
│   │   │   ├── network-policy-paymentservice.yaml
│   │   │   ├── network-policy-productcatalogservice.yaml
│   │   │   ├── network-policy-recommendationservice.yaml
│   │   │   ├── network-policy-redis.yaml
│   │   │   └── network-policy-shippingservice.yaml
│   │   ├── non-public-frontend/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── service-mesh-istio/
│   │   │   ├── README.md
│   │   │   ├── allow-egress-googleapis.yaml
│   │   │   ├── frontend-gateway.yaml
│   │   │   ├── frontend.yaml
│   │   │   └── kustomization.yaml
│   │   ├── shopping-assistant/
│   │   │   ├── README.md
│   │   │   ├── kustomization.yaml
│   │   │   ├── scripts/
│   │   │   │   ├── 1_deploy_alloydb_infra.sh
│   │   │   │   ├── 2_create_populate_alloydb_tables.sh
│   │   │   │   └── generate_sql_from_products.py
│   │   │   └── shoppingassistantservice.yaml
│   │   ├── single-shared-session/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   ├── spanner/
│   │   │   ├── README.md
│   │   │   └── kustomization.yaml
│   │   └── without-loadgenerator/
│   │       ├── README.md
│   │       ├── delete-loadgenerator.patch.yaml
│   │       └── kustomization.yaml
│   ├── kustomization.yaml
│   └── tests/
│       ├── README.md
│       ├── memorystore-with-all-components/
│       │   └── kustomization.yaml
│       ├── service-mesh-istio-with-all-components/
│       │   └── kustomization.yaml
│       └── spanner-with-all-components/
│           └── kustomization.yaml
├── protos/
│   ├── demo.proto
│   └── grpc/
│       └── health/
│           └── v1/
│               └── health.proto
├── release/
│   ├── istio-manifests.yaml
│   └── kubernetes-manifests.yaml
├── skaffold.yaml
├── src/
│   ├── adservice/
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── build.gradle
│   │   ├── genproto.sh
│   │   ├── gradle/
│   │   │   └── wrapper/
│   │   │       ├── gradle-wrapper.jar
│   │   │       └── gradle-wrapper.properties
│   │   ├── gradlew
│   │   ├── gradlew.bat
│   │   ├── settings.gradle
│   │   └── src/
│   │       └── main/
│   │           ├── java/
│   │           │   └── hipstershop/
│   │           │       ├── AdService.java
│   │           │       └── AdServiceClient.java
│   │           ├── proto/
│   │           │   └── demo.proto
│   │           └── resources/
│   │               └── log4j2.xml
│   ├── cartservice/
│   │   ├── cartservice.sln
│   │   ├── src/
│   │   │   ├── .dockerignore
│   │   │   ├── Dockerfile
│   │   │   ├── Dockerfile.debug
│   │   │   ├── Program.cs
│   │   │   ├── Startup.cs
│   │   │   ├── appsettings.json
│   │   │   ├── cartservice.csproj
│   │   │   ├── cartstore/
│   │   │   │   ├── AlloyDBCartStore.cs
│   │   │   │   ├── ICartStore.cs
│   │   │   │   ├── RedisCartStore.cs
│   │   │   │   └── SpannerCartStore.cs
│   │   │   ├── protos/
│   │   │   │   └── Cart.proto
│   │   │   └── services/
│   │   │       ├── CartService.cs
│   │   │       └── HealthCheckService.cs
│   │   └── tests/
│   │       ├── CartServiceTests.cs
│   │       └── cartservice.tests.csproj
│   ├── checkoutservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── main.go
│   │   └── money/
│   │       ├── money.go
│   │       └── money_test.go
│   ├── currencyservice/
│   │   ├── .dockerignore
│   │   ├── .gitignore
│   │   ├── Dockerfile
│   │   ├── client.js
│   │   ├── data/
│   │   │   └── currency_conversion.json
│   │   ├── genproto.sh
│   │   ├── package.json
│   │   ├── proto/
│   │   │   ├── demo.proto
│   │   │   └── grpc/
│   │   │       └── health/
│   │   │           └── v1/
│   │   │               └── health.proto
│   │   └── server.js
│   ├── emailservice/
│   │   ├── Dockerfile
│   │   ├── demo_pb2.py
│   │   ├── demo_pb2_grpc.py
│   │   ├── email_client.py
│   │   ├── email_server.py
│   │   ├── genproto.sh
│   │   ├── logger.py
│   │   ├── requirements.in
│   │   ├── requirements.txt
│   │   └── templates/
│   │       └── confirmation.html
│   ├── frontend/
│   │   ├── .dockerignore
│   │   ├── .gitkeep
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── deployment_details.go
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── handlers.go
│   │   ├── main.go
│   │   ├── middleware.go
│   │   ├── money/
│   │   │   ├── money.go
│   │   │   └── money_test.go
│   │   ├── packaging_info.go
│   │   ├── rpc.go
│   │   ├── static/
│   │   │   ├── images/
│   │   │   │   └── credits.txt
│   │   │   └── styles/
│   │   │       ├── bot.css
│   │   │       ├── cart.css
│   │   │       ├── order.css
│   │   │       └── styles.css
│   │   ├── templates/
│   │   │   ├── ad.html
│   │   │   ├── assistant.html
│   │   │   ├── cart.html
│   │   │   ├── error.html
│   │   │   ├── footer.html
│   │   │   ├── header.html
│   │   │   ├── home.html
│   │   │   ├── order.html
│   │   │   ├── product.html
│   │   │   └── recommendations.html
│   │   └── validator/
│   │       ├── validator.go
│   │       └── validator_test.go
│   ├── loadgenerator/
│   │   ├── Dockerfile
│   │   ├── locustfile.py
│   │   ├── requirements.in
│   │   └── requirements.txt
│   ├── paymentservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── charge.js
│   │   ├── genproto.sh
│   │   ├── index.js
│   │   ├── logger.js
│   │   ├── package.json
│   │   ├── proto/
│   │   │   ├── demo.proto
│   │   │   └── grpc/
│   │   │       └── health/
│   │   │           └── v1/
│   │   │               └── health.proto
│   │   └── server.js
│   ├── productcatalogservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── catalog_loader.go
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── product_catalog.go
│   │   ├── product_catalog_test.go
│   │   ├── products.json
│   │   └── server.go
│   ├── recommendationservice/
│   │   ├── Dockerfile
│   │   ├── client.py
│   │   ├── demo_pb2.py
│   │   ├── demo_pb2_grpc.py
│   │   ├── genproto.sh
│   │   ├── logger.py
│   │   ├── recommendation_server.py
│   │   ├── requirements.in
│   │   └── requirements.txt
│   ├── shippingservice/
│   │   ├── .dockerignore
│   │   ├── Dockerfile
│   │   ├── README.md
│   │   ├── genproto/
│   │   │   ├── demo.pb.go
│   │   │   └── demo_grpc.pb.go
│   │   ├── genproto.sh
│   │   ├── go.mod
│   │   ├── go.sum
│   │   ├── main.go
│   │   ├── quote.go
│   │   ├── shippingservice_test.go
│   │   └── tracker.go
│   └── shoppingassistantservice/
│       ├── Dockerfile
│       ├── requirements.in
│       ├── requirements.txt
│       └── shoppingassistantservice.py
└── terraform/
    ├── README.md
    ├── main.tf
    ├── memorystore.tf
    ├── output.tf
    ├── providers.tf
    └── variables.tf
Download .txt
SYMBOL INDEX (2102 symbols across 54 files)

FILE: src/adservice/src/main/java/hipstershop/AdService.java
  class AdService (line 41) | public final class AdService {
    method start (line 53) | private void start() throws IOException {
    method stop (line 77) | private void stop() {
    class AdServiceImpl (line 84) | private static class AdServiceImpl extends hipstershop.AdServiceGrpc.A...
      method getAds (line 93) | @Override
    method getAdsByCategory (line 123) | private Collection<Ad> getAdsByCategory(String category) {
    method getRandomAds (line 129) | private List<Ad> getRandomAds() {
    method getInstance (line 138) | private static AdService getInstance() {
    method blockUntilShutdown (line 143) | private void blockUntilShutdown() throws InterruptedException {
    method createAdsMap (line 149) | private static ImmutableListMultimap<String, Ad> createAdsMap() {
    method initStats (line 195) | private static void initStats() {
    method initTracing (line 209) | private static void initTracing() {
    method main (line 223) | public static void main(String[] args) throws IOException, Interrupted...

FILE: src/adservice/src/main/java/hipstershop/AdServiceClient.java
  class AdServiceClient (line 32) | public class AdServiceClient {
    method AdServiceClient (line 40) | private AdServiceClient(String host, int port) {
    method AdServiceClient (line 50) | private AdServiceClient(ManagedChannel channel) {
    method shutdown (line 55) | private void shutdown() throws InterruptedException {
    method getAds (line 60) | public void getAds(String contextKey) {
    method getPortOrDefaultFromArgs (line 76) | private static int getPortOrDefaultFromArgs(String[] args) {
    method getStringOrDefaultFromArgs (line 88) | private static String getStringOrDefaultFromArgs(
    method main (line 101) | public static void main(String[] args) throws InterruptedException {

FILE: src/cartservice/src/Startup.cs
  class Startup (line 16) | public class Startup
    method Startup (line 18) | public Startup(IConfiguration configuration)
    method ConfigureServices (line 27) | public void ConfigureServices(IServiceCollection services)
    method Configure (line 63) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

FILE: src/cartservice/src/cartstore/AlloyDBCartStore.cs
  class AlloyDBCartStore (line 25) | public class AlloyDBCartStore : ICartStore
    method AlloyDBCartStore (line 30) | public AlloyDBCartStore(IConfiguration configuration)
    method AddItemAsync (line 62) | public async Task AddItemAsync(string userId, string productId, int qu...
    method GetCartAsync (line 104) | public async Task<Hipstershop.Cart> GetCartAsync(string userId)
    method EmptyCartAsync (line 141) | public async Task EmptyCartAsync(string userId)
    method Ping (line 164) | public bool Ping()

FILE: src/cartservice/src/cartstore/ICartStore.cs
  type ICartStore (line 19) | public interface ICartStore
    method AddItemAsync (line 21) | Task AddItemAsync(string userId, string productId, int quantity);
    method EmptyCartAsync (line 22) | Task EmptyCartAsync(string userId);
    method GetCartAsync (line 23) | Task<Hipstershop.Cart> GetCartAsync(string userId);
    method Ping (line 24) | bool Ping();

FILE: src/cartservice/src/cartstore/RedisCartStore.cs
  class RedisCartStore (line 24) | public class RedisCartStore : ICartStore
    method RedisCartStore (line 28) | public RedisCartStore(IDistributedCache cache)
    method AddItemAsync (line 33) | public async Task AddItemAsync(string userId, string productId, int qu...
    method EmptyCartAsync (line 68) | public async Task EmptyCartAsync(string userId)
    method GetCartAsync (line 83) | public async Task<Hipstershop.Cart> GetCartAsync(string userId)
    method Ping (line 106) | public bool Ping()

FILE: src/cartservice/src/cartstore/SpannerCartStore.cs
  class SpannerCartStore (line 23) | public class SpannerCartStore : ICartStore
    method SpannerCartStore (line 30) | public SpannerCartStore(IConfiguration configuration)
    method AddItemAsync (line 54) | public async Task AddItemAsync(string userId, string productId, int qu...
    method GetCartAsync (line 105) | public async Task<Hipstershop.Cart> GetCartAsync(string userId)
    method EmptyCartAsync (line 145) | public async Task EmptyCartAsync(string userId)
    method Ping (line 172) | public bool Ping()

FILE: src/cartservice/src/services/CartService.cs
  class CartService (line 24) | public class CartService : Hipstershop.CartService.CartServiceBase
    method CartService (line 29) | public CartService(ICartStore cartStore)
    method AddItem (line 34) | public async override Task<Empty> AddItem(AddItemRequest request, Serv...
    method GetCart (line 40) | public override Task<Cart> GetCart(GetCartRequest request, ServerCallC...
    method EmptyCart (line 45) | public async override Task<Empty> EmptyCart(EmptyCartRequest request, ...

FILE: src/cartservice/src/services/HealthCheckService.cs
  class HealthCheckService (line 24) | internal class HealthCheckService : HealthBase
    method HealthCheckService (line 28) | public HealthCheckService (ICartStore cartStore)
    method Check (line 33) | public override Task<HealthCheckResponse> Check(HealthCheckRequest req...

FILE: src/cartservice/tests/CartServiceTests.cs
  class CartServiceTests (line 27) | public class CartServiceTests
    method CartServiceTests (line 31) | public CartServiceTests()
    method GetItem_NoAddItemBefore_EmptyCartReturned (line 41) | [Fact]
    method AddItem_ItemExists_Updated (line 70) | [Fact]
    method AddItem_New_Inserted (line 116) | [Fact]

FILE: src/checkoutservice/genproto/demo.pb.go
  constant _ (line 32) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 34) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type CartItem (line 37) | type CartItem struct
    method Reset (line 46) | func (x *CartItem) Reset() {
    method String (line 55) | func (x *CartItem) String() string {
    method ProtoMessage (line 59) | func (*CartItem) ProtoMessage() {}
    method ProtoReflect (line 61) | func (x *CartItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 74) | func (*CartItem) Descriptor() ([]byte, []int) {
    method GetProductId (line 78) | func (x *CartItem) GetProductId() string {
    method GetQuantity (line 85) | func (x *CartItem) GetQuantity() int32 {
  type AddItemRequest (line 92) | type AddItemRequest struct
    method Reset (line 101) | func (x *AddItemRequest) Reset() {
    method String (line 110) | func (x *AddItemRequest) String() string {
    method ProtoMessage (line 114) | func (*AddItemRequest) ProtoMessage() {}
    method ProtoReflect (line 116) | func (x *AddItemRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 129) | func (*AddItemRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 133) | func (x *AddItemRequest) GetUserId() string {
    method GetItem (line 140) | func (x *AddItemRequest) GetItem() *CartItem {
  type EmptyCartRequest (line 147) | type EmptyCartRequest struct
    method Reset (line 155) | func (x *EmptyCartRequest) Reset() {
    method String (line 164) | func (x *EmptyCartRequest) String() string {
    method ProtoMessage (line 168) | func (*EmptyCartRequest) ProtoMessage() {}
    method ProtoReflect (line 170) | func (x *EmptyCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 183) | func (*EmptyCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 187) | func (x *EmptyCartRequest) GetUserId() string {
  type GetCartRequest (line 194) | type GetCartRequest struct
    method Reset (line 202) | func (x *GetCartRequest) Reset() {
    method String (line 211) | func (x *GetCartRequest) String() string {
    method ProtoMessage (line 215) | func (*GetCartRequest) ProtoMessage() {}
    method ProtoReflect (line 217) | func (x *GetCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 230) | func (*GetCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 234) | func (x *GetCartRequest) GetUserId() string {
  type Cart (line 241) | type Cart struct
    method Reset (line 250) | func (x *Cart) Reset() {
    method String (line 259) | func (x *Cart) String() string {
    method ProtoMessage (line 263) | func (*Cart) ProtoMessage() {}
    method ProtoReflect (line 265) | func (x *Cart) ProtoReflect() protoreflect.Message {
    method Descriptor (line 278) | func (*Cart) Descriptor() ([]byte, []int) {
    method GetUserId (line 282) | func (x *Cart) GetUserId() string {
    method GetItems (line 289) | func (x *Cart) GetItems() []*CartItem {
  type Empty (line 296) | type Empty struct
    method Reset (line 302) | func (x *Empty) Reset() {
    method String (line 311) | func (x *Empty) String() string {
    method ProtoMessage (line 315) | func (*Empty) ProtoMessage() {}
    method ProtoReflect (line 317) | func (x *Empty) ProtoReflect() protoreflect.Message {
    method Descriptor (line 330) | func (*Empty) Descriptor() ([]byte, []int) {
  type ListRecommendationsRequest (line 334) | type ListRecommendationsRequest struct
    method Reset (line 343) | func (x *ListRecommendationsRequest) Reset() {
    method String (line 352) | func (x *ListRecommendationsRequest) String() string {
    method ProtoMessage (line 356) | func (*ListRecommendationsRequest) ProtoMessage() {}
    method ProtoReflect (line 358) | func (x *ListRecommendationsRequest) ProtoReflect() protoreflect.Messa...
    method Descriptor (line 371) | func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 375) | func (x *ListRecommendationsRequest) GetUserId() string {
    method GetProductIds (line 382) | func (x *ListRecommendationsRequest) GetProductIds() []string {
  type ListRecommendationsResponse (line 389) | type ListRecommendationsResponse struct
    method Reset (line 397) | func (x *ListRecommendationsResponse) Reset() {
    method String (line 406) | func (x *ListRecommendationsResponse) String() string {
    method ProtoMessage (line 410) | func (*ListRecommendationsResponse) ProtoMessage() {}
    method ProtoReflect (line 412) | func (x *ListRecommendationsResponse) ProtoReflect() protoreflect.Mess...
    method Descriptor (line 425) | func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) {
    method GetProductIds (line 429) | func (x *ListRecommendationsResponse) GetProductIds() []string {
  type Product (line 436) | type Product struct
    method Reset (line 451) | func (x *Product) Reset() {
    method String (line 460) | func (x *Product) String() string {
    method ProtoMessage (line 464) | func (*Product) ProtoMessage() {}
    method ProtoReflect (line 466) | func (x *Product) ProtoReflect() protoreflect.Message {
    method Descriptor (line 479) | func (*Product) Descriptor() ([]byte, []int) {
    method GetId (line 483) | func (x *Product) GetId() string {
    method GetName (line 490) | func (x *Product) GetName() string {
    method GetDescription (line 497) | func (x *Product) GetDescription() string {
    method GetPicture (line 504) | func (x *Product) GetPicture() string {
    method GetPriceUsd (line 511) | func (x *Product) GetPriceUsd() *Money {
    method GetCategories (line 518) | func (x *Product) GetCategories() []string {
  type ListProductsResponse (line 525) | type ListProductsResponse struct
    method Reset (line 533) | func (x *ListProductsResponse) Reset() {
    method String (line 542) | func (x *ListProductsResponse) String() string {
    method ProtoMessage (line 546) | func (*ListProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 548) | func (x *ListProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 561) | func (*ListProductsResponse) Descriptor() ([]byte, []int) {
    method GetProducts (line 565) | func (x *ListProductsResponse) GetProducts() []*Product {
  type GetProductRequest (line 572) | type GetProductRequest struct
    method Reset (line 580) | func (x *GetProductRequest) Reset() {
    method String (line 589) | func (x *GetProductRequest) String() string {
    method ProtoMessage (line 593) | func (*GetProductRequest) ProtoMessage() {}
    method ProtoReflect (line 595) | func (x *GetProductRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 608) | func (*GetProductRequest) Descriptor() ([]byte, []int) {
    method GetId (line 612) | func (x *GetProductRequest) GetId() string {
  type SearchProductsRequest (line 619) | type SearchProductsRequest struct
    method Reset (line 627) | func (x *SearchProductsRequest) Reset() {
    method String (line 636) | func (x *SearchProductsRequest) String() string {
    method ProtoMessage (line 640) | func (*SearchProductsRequest) ProtoMessage() {}
    method ProtoReflect (line 642) | func (x *SearchProductsRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 655) | func (*SearchProductsRequest) Descriptor() ([]byte, []int) {
    method GetQuery (line 659) | func (x *SearchProductsRequest) GetQuery() string {
  type SearchProductsResponse (line 666) | type SearchProductsResponse struct
    method Reset (line 674) | func (x *SearchProductsResponse) Reset() {
    method String (line 683) | func (x *SearchProductsResponse) String() string {
    method ProtoMessage (line 687) | func (*SearchProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 689) | func (x *SearchProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 702) | func (*SearchProductsResponse) Descriptor() ([]byte, []int) {
    method GetResults (line 706) | func (x *SearchProductsResponse) GetResults() []*Product {
  type GetQuoteRequest (line 713) | type GetQuoteRequest struct
    method Reset (line 722) | func (x *GetQuoteRequest) Reset() {
    method String (line 731) | func (x *GetQuoteRequest) String() string {
    method ProtoMessage (line 735) | func (*GetQuoteRequest) ProtoMessage() {}
    method ProtoReflect (line 737) | func (x *GetQuoteRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 750) | func (*GetQuoteRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 754) | func (x *GetQuoteRequest) GetAddress() *Address {
    method GetItems (line 761) | func (x *GetQuoteRequest) GetItems() []*CartItem {
  type GetQuoteResponse (line 768) | type GetQuoteResponse struct
    method Reset (line 776) | func (x *GetQuoteResponse) Reset() {
    method String (line 785) | func (x *GetQuoteResponse) String() string {
    method ProtoMessage (line 789) | func (*GetQuoteResponse) ProtoMessage() {}
    method ProtoReflect (line 791) | func (x *GetQuoteResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 804) | func (*GetQuoteResponse) Descriptor() ([]byte, []int) {
    method GetCostUsd (line 808) | func (x *GetQuoteResponse) GetCostUsd() *Money {
  type ShipOrderRequest (line 815) | type ShipOrderRequest struct
    method Reset (line 824) | func (x *ShipOrderRequest) Reset() {
    method String (line 833) | func (x *ShipOrderRequest) String() string {
    method ProtoMessage (line 837) | func (*ShipOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 839) | func (x *ShipOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 852) | func (*ShipOrderRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 856) | func (x *ShipOrderRequest) GetAddress() *Address {
    method GetItems (line 863) | func (x *ShipOrderRequest) GetItems() []*CartItem {
  type ShipOrderResponse (line 870) | type ShipOrderResponse struct
    method Reset (line 878) | func (x *ShipOrderResponse) Reset() {
    method String (line 887) | func (x *ShipOrderResponse) String() string {
    method ProtoMessage (line 891) | func (*ShipOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 893) | func (x *ShipOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 906) | func (*ShipOrderResponse) Descriptor() ([]byte, []int) {
    method GetTrackingId (line 910) | func (x *ShipOrderResponse) GetTrackingId() string {
  type Address (line 917) | type Address struct
    method Reset (line 929) | func (x *Address) Reset() {
    method String (line 938) | func (x *Address) String() string {
    method ProtoMessage (line 942) | func (*Address) ProtoMessage() {}
    method ProtoReflect (line 944) | func (x *Address) ProtoReflect() protoreflect.Message {
    method Descriptor (line 957) | func (*Address) Descriptor() ([]byte, []int) {
    method GetStreetAddress (line 961) | func (x *Address) GetStreetAddress() string {
    method GetCity (line 968) | func (x *Address) GetCity() string {
    method GetState (line 975) | func (x *Address) GetState() string {
    method GetCountry (line 982) | func (x *Address) GetCountry() string {
    method GetZipCode (line 989) | func (x *Address) GetZipCode() int32 {
  type Money (line 997) | type Money struct
    method Reset (line 1016) | func (x *Money) Reset() {
    method String (line 1025) | func (x *Money) String() string {
    method ProtoMessage (line 1029) | func (*Money) ProtoMessage() {}
    method ProtoReflect (line 1031) | func (x *Money) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1044) | func (*Money) Descriptor() ([]byte, []int) {
    method GetCurrencyCode (line 1048) | func (x *Money) GetCurrencyCode() string {
    method GetUnits (line 1055) | func (x *Money) GetUnits() int64 {
    method GetNanos (line 1062) | func (x *Money) GetNanos() int32 {
  type GetSupportedCurrenciesResponse (line 1069) | type GetSupportedCurrenciesResponse struct
    method Reset (line 1078) | func (x *GetSupportedCurrenciesResponse) Reset() {
    method String (line 1087) | func (x *GetSupportedCurrenciesResponse) String() string {
    method ProtoMessage (line 1091) | func (*GetSupportedCurrenciesResponse) ProtoMessage() {}
    method ProtoReflect (line 1093) | func (x *GetSupportedCurrenciesResponse) ProtoReflect() protoreflect.M...
    method Descriptor (line 1106) | func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) {
    method GetCurrencyCodes (line 1110) | func (x *GetSupportedCurrenciesResponse) GetCurrencyCodes() []string {
  type CurrencyConversionRequest (line 1117) | type CurrencyConversionRequest struct
    method Reset (line 1127) | func (x *CurrencyConversionRequest) Reset() {
    method String (line 1136) | func (x *CurrencyConversionRequest) String() string {
    method ProtoMessage (line 1140) | func (*CurrencyConversionRequest) ProtoMessage() {}
    method ProtoReflect (line 1142) | func (x *CurrencyConversionRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1155) | func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) {
    method GetFrom (line 1159) | func (x *CurrencyConversionRequest) GetFrom() *Money {
    method GetToCode (line 1166) | func (x *CurrencyConversionRequest) GetToCode() string {
  type CreditCardInfo (line 1173) | type CreditCardInfo struct
    method Reset (line 1184) | func (x *CreditCardInfo) Reset() {
    method String (line 1193) | func (x *CreditCardInfo) String() string {
    method ProtoMessage (line 1197) | func (*CreditCardInfo) ProtoMessage() {}
    method ProtoReflect (line 1199) | func (x *CreditCardInfo) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1212) | func (*CreditCardInfo) Descriptor() ([]byte, []int) {
    method GetCreditCardNumber (line 1216) | func (x *CreditCardInfo) GetCreditCardNumber() string {
    method GetCreditCardCvv (line 1223) | func (x *CreditCardInfo) GetCreditCardCvv() int32 {
    method GetCreditCardExpirationYear (line 1230) | func (x *CreditCardInfo) GetCreditCardExpirationYear() int32 {
    method GetCreditCardExpirationMonth (line 1237) | func (x *CreditCardInfo) GetCreditCardExpirationMonth() int32 {
  type ChargeRequest (line 1244) | type ChargeRequest struct
    method Reset (line 1253) | func (x *ChargeRequest) Reset() {
    method String (line 1262) | func (x *ChargeRequest) String() string {
    method ProtoMessage (line 1266) | func (*ChargeRequest) ProtoMessage() {}
    method ProtoReflect (line 1268) | func (x *ChargeRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1281) | func (*ChargeRequest) Descriptor() ([]byte, []int) {
    method GetAmount (line 1285) | func (x *ChargeRequest) GetAmount() *Money {
    method GetCreditCard (line 1292) | func (x *ChargeRequest) GetCreditCard() *CreditCardInfo {
  type ChargeResponse (line 1299) | type ChargeResponse struct
    method Reset (line 1307) | func (x *ChargeResponse) Reset() {
    method String (line 1316) | func (x *ChargeResponse) String() string {
    method ProtoMessage (line 1320) | func (*ChargeResponse) ProtoMessage() {}
    method ProtoReflect (line 1322) | func (x *ChargeResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1335) | func (*ChargeResponse) Descriptor() ([]byte, []int) {
    method GetTransactionId (line 1339) | func (x *ChargeResponse) GetTransactionId() string {
  type OrderItem (line 1346) | type OrderItem struct
    method Reset (line 1355) | func (x *OrderItem) Reset() {
    method String (line 1364) | func (x *OrderItem) String() string {
    method ProtoMessage (line 1368) | func (*OrderItem) ProtoMessage() {}
    method ProtoReflect (line 1370) | func (x *OrderItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1383) | func (*OrderItem) Descriptor() ([]byte, []int) {
    method GetItem (line 1387) | func (x *OrderItem) GetItem() *CartItem {
    method GetCost (line 1394) | func (x *OrderItem) GetCost() *Money {
  type OrderResult (line 1401) | type OrderResult struct
    method Reset (line 1413) | func (x *OrderResult) Reset() {
    method String (line 1422) | func (x *OrderResult) String() string {
    method ProtoMessage (line 1426) | func (*OrderResult) ProtoMessage() {}
    method ProtoReflect (line 1428) | func (x *OrderResult) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1441) | func (*OrderResult) Descriptor() ([]byte, []int) {
    method GetOrderId (line 1445) | func (x *OrderResult) GetOrderId() string {
    method GetShippingTrackingId (line 1452) | func (x *OrderResult) GetShippingTrackingId() string {
    method GetShippingCost (line 1459) | func (x *OrderResult) GetShippingCost() *Money {
    method GetShippingAddress (line 1466) | func (x *OrderResult) GetShippingAddress() *Address {
    method GetItems (line 1473) | func (x *OrderResult) GetItems() []*OrderItem {
  type SendOrderConfirmationRequest (line 1480) | type SendOrderConfirmationRequest struct
    method Reset (line 1489) | func (x *SendOrderConfirmationRequest) Reset() {
    method String (line 1498) | func (x *SendOrderConfirmationRequest) String() string {
    method ProtoMessage (line 1502) | func (*SendOrderConfirmationRequest) ProtoMessage() {}
    method ProtoReflect (line 1504) | func (x *SendOrderConfirmationRequest) ProtoReflect() protoreflect.Mes...
    method Descriptor (line 1517) | func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) {
    method GetEmail (line 1521) | func (x *SendOrderConfirmationRequest) GetEmail() string {
    method GetOrder (line 1528) | func (x *SendOrderConfirmationRequest) GetOrder() *OrderResult {
  type PlaceOrderRequest (line 1535) | type PlaceOrderRequest struct
    method Reset (line 1547) | func (x *PlaceOrderRequest) Reset() {
    method String (line 1556) | func (x *PlaceOrderRequest) String() string {
    method ProtoMessage (line 1560) | func (*PlaceOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 1562) | func (x *PlaceOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1575) | func (*PlaceOrderRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 1579) | func (x *PlaceOrderRequest) GetUserId() string {
    method GetUserCurrency (line 1586) | func (x *PlaceOrderRequest) GetUserCurrency() string {
    method GetAddress (line 1593) | func (x *PlaceOrderRequest) GetAddress() *Address {
    method GetEmail (line 1600) | func (x *PlaceOrderRequest) GetEmail() string {
    method GetCreditCard (line 1607) | func (x *PlaceOrderRequest) GetCreditCard() *CreditCardInfo {
  type PlaceOrderResponse (line 1614) | type PlaceOrderResponse struct
    method Reset (line 1622) | func (x *PlaceOrderResponse) Reset() {
    method String (line 1631) | func (x *PlaceOrderResponse) String() string {
    method ProtoMessage (line 1635) | func (*PlaceOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 1637) | func (x *PlaceOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1650) | func (*PlaceOrderResponse) Descriptor() ([]byte, []int) {
    method GetOrder (line 1654) | func (x *PlaceOrderResponse) GetOrder() *OrderResult {
  type AdRequest (line 1661) | type AdRequest struct
    method Reset (line 1670) | func (x *AdRequest) Reset() {
    method String (line 1679) | func (x *AdRequest) String() string {
    method ProtoMessage (line 1683) | func (*AdRequest) ProtoMessage() {}
    method ProtoReflect (line 1685) | func (x *AdRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1698) | func (*AdRequest) Descriptor() ([]byte, []int) {
    method GetContextKeys (line 1702) | func (x *AdRequest) GetContextKeys() []string {
  type AdResponse (line 1709) | type AdResponse struct
    method Reset (line 1717) | func (x *AdResponse) Reset() {
    method String (line 1726) | func (x *AdResponse) String() string {
    method ProtoMessage (line 1730) | func (*AdResponse) ProtoMessage() {}
    method ProtoReflect (line 1732) | func (x *AdResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1745) | func (*AdResponse) Descriptor() ([]byte, []int) {
    method GetAds (line 1749) | func (x *AdResponse) GetAds() []*Ad {
  type Ad (line 1756) | type Ad struct
    method Reset (line 1767) | func (x *Ad) Reset() {
    method String (line 1776) | func (x *Ad) String() string {
    method ProtoMessage (line 1780) | func (*Ad) ProtoMessage() {}
    method ProtoReflect (line 1782) | func (x *Ad) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1795) | func (*Ad) Descriptor() ([]byte, []int) {
    method GetRedirectUrl (line 1799) | func (x *Ad) GetRedirectUrl() string {
    method GetText (line 1806) | func (x *Ad) GetText() string {
  function file_demo_proto_rawDescGZIP (line 2098) | func file_demo_proto_rawDescGZIP() []byte {
  function init (line 2201) | func init() { file_demo_proto_init() }
  function file_demo_proto_init (line 2202) | func file_demo_proto_init() {

FILE: src/checkoutservice/genproto/demo_grpc.pb.go
  constant _ (line 33) | _ = grpc.SupportPackageIsVersion9
  constant CartService_AddItem_FullMethodName (line 36) | CartService_AddItem_FullMethodName   = "/hipstershop.CartService/AddItem"
  constant CartService_GetCart_FullMethodName (line 37) | CartService_GetCart_FullMethodName   = "/hipstershop.CartService/GetCart"
  constant CartService_EmptyCart_FullMethodName (line 38) | CartService_EmptyCart_FullMethodName = "/hipstershop.CartService/EmptyCart"
  type CartServiceClient (line 44) | type CartServiceClient interface
  type cartServiceClient (line 50) | type cartServiceClient struct
    method AddItem (line 58) | func (c *cartServiceClient) AddItem(ctx context.Context, in *AddItemRe...
    method GetCart (line 68) | func (c *cartServiceClient) GetCart(ctx context.Context, in *GetCartRe...
    method EmptyCart (line 78) | func (c *cartServiceClient) EmptyCart(ctx context.Context, in *EmptyCa...
  function NewCartServiceClient (line 54) | func NewCartServiceClient(cc grpc.ClientConnInterface) CartServiceClient {
  type CartServiceServer (line 91) | type CartServiceServer interface
  type UnimplementedCartServiceServer (line 103) | type UnimplementedCartServiceServer struct
    method AddItem (line 105) | func (UnimplementedCartServiceServer) AddItem(context.Context, *AddIte...
    method GetCart (line 108) | func (UnimplementedCartServiceServer) GetCart(context.Context, *GetCar...
    method EmptyCart (line 111) | func (UnimplementedCartServiceServer) EmptyCart(context.Context, *Empt...
    method mustEmbedUnimplementedCartServiceServer (line 114) | func (UnimplementedCartServiceServer) mustEmbedUnimplementedCartServic...
    method testEmbeddedByValue (line 115) | func (UnimplementedCartServiceServer) testEmbeddedByValue()           ...
  type UnsafeCartServiceServer (line 120) | type UnsafeCartServiceServer interface
  function RegisterCartServiceServer (line 124) | func RegisterCartServiceServer(s grpc.ServiceRegistrar, srv CartServiceS...
  function _CartService_AddItem_Handler (line 135) | func _CartService_AddItem_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_GetCart_Handler (line 153) | func _CartService_GetCart_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_EmptyCart_Handler (line 171) | func _CartService_EmptyCart_Handler(srv interface{}, ctx context.Context...
  constant RecommendationService_ListRecommendations_FullMethodName (line 214) | RecommendationService_ListRecommendations_FullMethodName = "/hipstershop...
  type RecommendationServiceClient (line 220) | type RecommendationServiceClient interface
  type recommendationServiceClient (line 224) | type recommendationServiceClient struct
    method ListRecommendations (line 232) | func (c *recommendationServiceClient) ListRecommendations(ctx context....
  function NewRecommendationServiceClient (line 228) | func NewRecommendationServiceClient(cc grpc.ClientConnInterface) Recomme...
  type RecommendationServiceServer (line 245) | type RecommendationServiceServer interface
  type UnimplementedRecommendationServiceServer (line 255) | type UnimplementedRecommendationServiceServer struct
    method ListRecommendations (line 257) | func (UnimplementedRecommendationServiceServer) ListRecommendations(co...
    method mustEmbedUnimplementedRecommendationServiceServer (line 260) | func (UnimplementedRecommendationServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 261) | func (UnimplementedRecommendationServiceServer) testEmbeddedByValue() ...
  type UnsafeRecommendationServiceServer (line 266) | type UnsafeRecommendationServiceServer interface
  function RegisterRecommendationServiceServer (line 270) | func RegisterRecommendationServiceServer(s grpc.ServiceRegistrar, srv Re...
  function _RecommendationService_ListRecommendations_Handler (line 281) | func _RecommendationService_ListRecommendations_Handler(srv interface{},...
  constant ProductCatalogService_ListProducts_FullMethodName (line 316) | ProductCatalogService_ListProducts_FullMethodName   = "/hipstershop.Prod...
  constant ProductCatalogService_GetProduct_FullMethodName (line 317) | ProductCatalogService_GetProduct_FullMethodName     = "/hipstershop.Prod...
  constant ProductCatalogService_SearchProducts_FullMethodName (line 318) | ProductCatalogService_SearchProducts_FullMethodName = "/hipstershop.Prod...
  type ProductCatalogServiceClient (line 324) | type ProductCatalogServiceClient interface
  type productCatalogServiceClient (line 330) | type productCatalogServiceClient struct
    method ListProducts (line 338) | func (c *productCatalogServiceClient) ListProducts(ctx context.Context...
    method GetProduct (line 348) | func (c *productCatalogServiceClient) GetProduct(ctx context.Context, ...
    method SearchProducts (line 358) | func (c *productCatalogServiceClient) SearchProducts(ctx context.Conte...
  function NewProductCatalogServiceClient (line 334) | func NewProductCatalogServiceClient(cc grpc.ClientConnInterface) Product...
  type ProductCatalogServiceServer (line 371) | type ProductCatalogServiceServer interface
  type UnimplementedProductCatalogServiceServer (line 383) | type UnimplementedProductCatalogServiceServer struct
    method ListProducts (line 385) | func (UnimplementedProductCatalogServiceServer) ListProducts(context.C...
    method GetProduct (line 388) | func (UnimplementedProductCatalogServiceServer) GetProduct(context.Con...
    method SearchProducts (line 391) | func (UnimplementedProductCatalogServiceServer) SearchProducts(context...
    method mustEmbedUnimplementedProductCatalogServiceServer (line 394) | func (UnimplementedProductCatalogServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 395) | func (UnimplementedProductCatalogServiceServer) testEmbeddedByValue() ...
  type UnsafeProductCatalogServiceServer (line 400) | type UnsafeProductCatalogServiceServer interface
  function RegisterProductCatalogServiceServer (line 404) | func RegisterProductCatalogServiceServer(s grpc.ServiceRegistrar, srv Pr...
  function _ProductCatalogService_ListProducts_Handler (line 415) | func _ProductCatalogService_ListProducts_Handler(srv interface{}, ctx co...
  function _ProductCatalogService_GetProduct_Handler (line 433) | func _ProductCatalogService_GetProduct_Handler(srv interface{}, ctx cont...
  function _ProductCatalogService_SearchProducts_Handler (line 451) | func _ProductCatalogService_SearchProducts_Handler(srv interface{}, ctx ...
  constant ShippingService_GetQuote_FullMethodName (line 494) | ShippingService_GetQuote_FullMethodName  = "/hipstershop.ShippingService...
  constant ShippingService_ShipOrder_FullMethodName (line 495) | ShippingService_ShipOrder_FullMethodName = "/hipstershop.ShippingService...
  type ShippingServiceClient (line 501) | type ShippingServiceClient interface
  type shippingServiceClient (line 506) | type shippingServiceClient struct
    method GetQuote (line 514) | func (c *shippingServiceClient) GetQuote(ctx context.Context, in *GetQ...
    method ShipOrder (line 524) | func (c *shippingServiceClient) ShipOrder(ctx context.Context, in *Shi...
  function NewShippingServiceClient (line 510) | func NewShippingServiceClient(cc grpc.ClientConnInterface) ShippingServi...
  type ShippingServiceServer (line 537) | type ShippingServiceServer interface
  type UnimplementedShippingServiceServer (line 548) | type UnimplementedShippingServiceServer struct
    method GetQuote (line 550) | func (UnimplementedShippingServiceServer) GetQuote(context.Context, *G...
    method ShipOrder (line 553) | func (UnimplementedShippingServiceServer) ShipOrder(context.Context, *...
    method mustEmbedUnimplementedShippingServiceServer (line 556) | func (UnimplementedShippingServiceServer) mustEmbedUnimplementedShippi...
    method testEmbeddedByValue (line 557) | func (UnimplementedShippingServiceServer) testEmbeddedByValue()       ...
  type UnsafeShippingServiceServer (line 562) | type UnsafeShippingServiceServer interface
  function RegisterShippingServiceServer (line 566) | func RegisterShippingServiceServer(s grpc.ServiceRegistrar, srv Shipping...
  function _ShippingService_GetQuote_Handler (line 577) | func _ShippingService_GetQuote_Handler(srv interface{}, ctx context.Cont...
  function _ShippingService_ShipOrder_Handler (line 595) | func _ShippingService_ShipOrder_Handler(srv interface{}, ctx context.Con...
  constant CurrencyService_GetSupportedCurrencies_FullMethodName (line 634) | CurrencyService_GetSupportedCurrencies_FullMethodName = "/hipstershop.Cu...
  constant CurrencyService_Convert_FullMethodName (line 635) | CurrencyService_Convert_FullMethodName                = "/hipstershop.Cu...
  type CurrencyServiceClient (line 641) | type CurrencyServiceClient interface
  type currencyServiceClient (line 646) | type currencyServiceClient struct
    method GetSupportedCurrencies (line 654) | func (c *currencyServiceClient) GetSupportedCurrencies(ctx context.Con...
    method Convert (line 664) | func (c *currencyServiceClient) Convert(ctx context.Context, in *Curre...
  function NewCurrencyServiceClient (line 650) | func NewCurrencyServiceClient(cc grpc.ClientConnInterface) CurrencyServi...
  type CurrencyServiceServer (line 677) | type CurrencyServiceServer interface
  type UnimplementedCurrencyServiceServer (line 688) | type UnimplementedCurrencyServiceServer struct
    method GetSupportedCurrencies (line 690) | func (UnimplementedCurrencyServiceServer) GetSupportedCurrencies(conte...
    method Convert (line 693) | func (UnimplementedCurrencyServiceServer) Convert(context.Context, *Cu...
    method mustEmbedUnimplementedCurrencyServiceServer (line 696) | func (UnimplementedCurrencyServiceServer) mustEmbedUnimplementedCurren...
    method testEmbeddedByValue (line 697) | func (UnimplementedCurrencyServiceServer) testEmbeddedByValue()       ...
  type UnsafeCurrencyServiceServer (line 702) | type UnsafeCurrencyServiceServer interface
  function RegisterCurrencyServiceServer (line 706) | func RegisterCurrencyServiceServer(s grpc.ServiceRegistrar, srv Currency...
  function _CurrencyService_GetSupportedCurrencies_Handler (line 717) | func _CurrencyService_GetSupportedCurrencies_Handler(srv interface{}, ct...
  function _CurrencyService_Convert_Handler (line 735) | func _CurrencyService_Convert_Handler(srv interface{}, ctx context.Conte...
  constant PaymentService_Charge_FullMethodName (line 774) | PaymentService_Charge_FullMethodName = "/hipstershop.PaymentService/Charge"
  type PaymentServiceClient (line 780) | type PaymentServiceClient interface
  type paymentServiceClient (line 784) | type paymentServiceClient struct
    method Charge (line 792) | func (c *paymentServiceClient) Charge(ctx context.Context, in *ChargeR...
  function NewPaymentServiceClient (line 788) | func NewPaymentServiceClient(cc grpc.ClientConnInterface) PaymentService...
  type PaymentServiceServer (line 805) | type PaymentServiceServer interface
  type UnimplementedPaymentServiceServer (line 815) | type UnimplementedPaymentServiceServer struct
    method Charge (line 817) | func (UnimplementedPaymentServiceServer) Charge(context.Context, *Char...
    method mustEmbedUnimplementedPaymentServiceServer (line 820) | func (UnimplementedPaymentServiceServer) mustEmbedUnimplementedPayment...
    method testEmbeddedByValue (line 821) | func (UnimplementedPaymentServiceServer) testEmbeddedByValue()        ...
  type UnsafePaymentServiceServer (line 826) | type UnsafePaymentServiceServer interface
  function RegisterPaymentServiceServer (line 830) | func RegisterPaymentServiceServer(s grpc.ServiceRegistrar, srv PaymentSe...
  function _PaymentService_Charge_Handler (line 841) | func _PaymentService_Charge_Handler(srv interface{}, ctx context.Context...
  constant EmailService_SendOrderConfirmation_FullMethodName (line 876) | EmailService_SendOrderConfirmation_FullMethodName = "/hipstershop.EmailS...
  type EmailServiceClient (line 882) | type EmailServiceClient interface
  type emailServiceClient (line 886) | type emailServiceClient struct
    method SendOrderConfirmation (line 894) | func (c *emailServiceClient) SendOrderConfirmation(ctx context.Context...
  function NewEmailServiceClient (line 890) | func NewEmailServiceClient(cc grpc.ClientConnInterface) EmailServiceClie...
  type EmailServiceServer (line 907) | type EmailServiceServer interface
  type UnimplementedEmailServiceServer (line 917) | type UnimplementedEmailServiceServer struct
    method SendOrderConfirmation (line 919) | func (UnimplementedEmailServiceServer) SendOrderConfirmation(context.C...
    method mustEmbedUnimplementedEmailServiceServer (line 922) | func (UnimplementedEmailServiceServer) mustEmbedUnimplementedEmailServ...
    method testEmbeddedByValue (line 923) | func (UnimplementedEmailServiceServer) testEmbeddedByValue()          ...
  type UnsafeEmailServiceServer (line 928) | type UnsafeEmailServiceServer interface
  function RegisterEmailServiceServer (line 932) | func RegisterEmailServiceServer(s grpc.ServiceRegistrar, srv EmailServic...
  function _EmailService_SendOrderConfirmation_Handler (line 943) | func _EmailService_SendOrderConfirmation_Handler(srv interface{}, ctx co...
  constant CheckoutService_PlaceOrder_FullMethodName (line 978) | CheckoutService_PlaceOrder_FullMethodName = "/hipstershop.CheckoutServic...
  type CheckoutServiceClient (line 984) | type CheckoutServiceClient interface
  type checkoutServiceClient (line 988) | type checkoutServiceClient struct
    method PlaceOrder (line 996) | func (c *checkoutServiceClient) PlaceOrder(ctx context.Context, in *Pl...
  function NewCheckoutServiceClient (line 992) | func NewCheckoutServiceClient(cc grpc.ClientConnInterface) CheckoutServi...
  type CheckoutServiceServer (line 1009) | type CheckoutServiceServer interface
  type UnimplementedCheckoutServiceServer (line 1019) | type UnimplementedCheckoutServiceServer struct
    method PlaceOrder (line 1021) | func (UnimplementedCheckoutServiceServer) PlaceOrder(context.Context, ...
    method mustEmbedUnimplementedCheckoutServiceServer (line 1024) | func (UnimplementedCheckoutServiceServer) mustEmbedUnimplementedChecko...
    method testEmbeddedByValue (line 1025) | func (UnimplementedCheckoutServiceServer) testEmbeddedByValue()       ...
  type UnsafeCheckoutServiceServer (line 1030) | type UnsafeCheckoutServiceServer interface
  function RegisterCheckoutServiceServer (line 1034) | func RegisterCheckoutServiceServer(s grpc.ServiceRegistrar, srv Checkout...
  function _CheckoutService_PlaceOrder_Handler (line 1045) | func _CheckoutService_PlaceOrder_Handler(srv interface{}, ctx context.Co...
  constant AdService_GetAds_FullMethodName (line 1080) | AdService_GetAds_FullMethodName = "/hipstershop.AdService/GetAds"
  type AdServiceClient (line 1086) | type AdServiceClient interface
  type adServiceClient (line 1090) | type adServiceClient struct
    method GetAds (line 1098) | func (c *adServiceClient) GetAds(ctx context.Context, in *AdRequest, o...
  function NewAdServiceClient (line 1094) | func NewAdServiceClient(cc grpc.ClientConnInterface) AdServiceClient {
  type AdServiceServer (line 1111) | type AdServiceServer interface
  type UnimplementedAdServiceServer (line 1121) | type UnimplementedAdServiceServer struct
    method GetAds (line 1123) | func (UnimplementedAdServiceServer) GetAds(context.Context, *AdRequest...
    method mustEmbedUnimplementedAdServiceServer (line 1126) | func (UnimplementedAdServiceServer) mustEmbedUnimplementedAdServiceSer...
    method testEmbeddedByValue (line 1127) | func (UnimplementedAdServiceServer) testEmbeddedByValue()             ...
  type UnsafeAdServiceServer (line 1132) | type UnsafeAdServiceServer interface
  function RegisterAdServiceServer (line 1136) | func RegisterAdServiceServer(s grpc.ServiceRegistrar, srv AdServiceServe...
  function _AdService_GetAds_Handler (line 1147) | func _AdService_GetAds_Handler(srv interface{}, ctx context.Context, dec...

FILE: src/checkoutservice/main.go
  constant listenPort (line 46) | listenPort  = "5050"
  constant usdCurrency (line 47) | usdCurrency = "USD"
  function init (line 52) | func init() {
  type checkoutService (line 66) | type checkoutService struct
    method Check (line 222) | func (cs *checkoutService) Check(ctx context.Context, req *healthpb.He...
    method Watch (line 226) | func (cs *checkoutService) Watch(req *healthpb.HealthCheckRequest, ws ...
    method PlaceOrder (line 230) | func (cs *checkoutService) PlaceOrder(ctx context.Context, req *pb.Pla...
    method prepareOrderItemsAndShippingQuoteFromCart (line 288) | func (cs *checkoutService) prepareOrderItemsAndShippingQuoteFromCart(c...
    method quoteShipping (line 313) | func (cs *checkoutService) quoteShipping(ctx context.Context, address ...
    method getUserCart (line 324) | func (cs *checkoutService) getUserCart(ctx context.Context, userID str...
    method emptyUserCart (line 332) | func (cs *checkoutService) emptyUserCart(ctx context.Context, userID s...
    method prepOrderItems (line 339) | func (cs *checkoutService) prepOrderItems(ctx context.Context, items [...
    method convertCurrency (line 359) | func (cs *checkoutService) convertCurrency(ctx context.Context, from *...
    method chargeCard (line 369) | func (cs *checkoutService) chargeCard(ctx context.Context, amount *pb....
    method sendOrderConfirmation (line 379) | func (cs *checkoutService) sendOrderConfirmation(ctx context.Context, ...
    method shipOrder (line 386) | func (cs *checkoutService) shipOrder(ctx context.Context, address *pb....
  function main (line 88) | func main() {
  function initStats (line 150) | func initStats() {
  function initTracing (line 154) | func initTracing() {
  function initProfiling (line 180) | func initProfiling(service, version string) {
  function mustMapEnv (line 202) | func mustMapEnv(target *string, envKey string) {
  function mustConnGRPC (line 210) | func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr stri...
  type orderPrep (line 282) | type orderPrep struct

FILE: src/checkoutservice/money/money.go
  constant nanosMin (line 24) | nanosMin = -999999999
  constant nanosMax (line 25) | nanosMax = +999999999
  constant nanosMod (line 26) | nanosMod = 1000000000
  function IsValid (line 35) | func IsValid(m pb.Money) bool {
  function signMatches (line 39) | func signMatches(m pb.Money) bool {
  function validNanos (line 43) | func validNanos(nanos int32) bool { return nanosMin <= nanos && nanos <=...
  function IsZero (line 46) | func IsZero(m pb.Money) bool { return m.GetUnits() == 0 && m.GetNanos() ...
  function IsPositive (line 50) | func IsPositive(m pb.Money) bool {
  function IsNegative (line 56) | func IsNegative(m pb.Money) bool {
  function AreSameCurrency (line 62) | func AreSameCurrency(l, r pb.Money) bool {
  function AreEquals (line 68) | func AreEquals(l, r pb.Money) bool {
  function Negate (line 74) | func Negate(m pb.Money) pb.Money {
  function Must (line 83) | func Must(v pb.Money, err error) pb.Money {
  function Sum (line 93) | func Sum(l, r pb.Money) (pb.Money, error) {
  function MultiplySlow (line 125) | func MultiplySlow(m pb.Money, n uint32) pb.Money {

FILE: src/checkoutservice/money/money_test.go
  function mmc (line 25) | func mmc(u int64, n int32, c string) pb.Money { return pb.Money{Units: u...
  function mm (line 26) | func mm(u int64, n int32) pb.Money            { return mmc(u, n, "") }
  function TestIsValid (line 28) | func TestIsValid(t *testing.T) {
  function TestIsZero (line 52) | func TestIsZero(t *testing.T) {
  function TestIsPositive (line 73) | func TestIsPositive(t *testing.T) {
  function TestIsNegative (line 94) | func TestIsNegative(t *testing.T) {
  function TestAreSameCurrency (line 115) | func TestAreSameCurrency(t *testing.T) {
  function TestAreEquals (line 140) | func TestAreEquals(t *testing.T) {
  function TestNegate (line 165) | func TestNegate(t *testing.T) {
  function TestMust_pass (line 185) | func TestMust_pass(t *testing.T) {
  function TestMust_panic (line 192) | func TestMust_panic(t *testing.T) {
  function TestSum (line 202) | func TestSum(t *testing.T) {

FILE: src/currencyservice/client.js
  constant PROTO_PATH (line 24) | const PROTO_PATH = path.join(__dirname, './proto/demo.proto');
  constant PORT (line 25) | const PORT = 7000;
  method level (line 35) | level (logLevelString, logLevelNum) {
  function _moneyToString (line 50) | function _moneyToString (m) {

FILE: src/currencyservice/server.js
  method level (line 22) | level (logLevelString, logLevelNum) {
  constant MAIN_PROTO_PATH (line 80) | const MAIN_PROTO_PATH = path.join(__dirname, './proto/demo.proto');
  constant HEALTH_PROTO_PATH (line 81) | const HEALTH_PROTO_PATH = path.join(__dirname, './proto/grpc/health/v1/h...
  constant PORT (line 83) | const PORT = process.env.PORT;
  function _loadProto (line 91) | function _loadProto (path) {
  function _getCurrencyData (line 109) | function _getCurrencyData (callback) {
  function _carry (line 117) | function _carry (amount) {
  function getSupportedCurrencies (line 128) | function getSupportedCurrencies (call, callback) {
  function convert (line 138) | function convert (call, callback) {
  function check (line 174) | function check (call, callback) {
  function main (line 182) | function main () {

FILE: src/emailservice/demo_pb2_grpc.py
  class CartServiceStub (line 24) | class CartServiceStub(object):
    method __init__ (line 29) | def __init__(self, channel):
  class CartServiceServicer (line 52) | class CartServiceServicer(object):
    method AddItem (line 57) | def AddItem(self, request, context):
    method GetCart (line 63) | def GetCart(self, request, context):
    method EmptyCart (line 69) | def EmptyCart(self, request, context):
  function add_CartServiceServicer_to_server (line 76) | def add_CartServiceServicer_to_server(servicer, server):
  class CartService (line 100) | class CartService(object):
    method AddItem (line 106) | def AddItem(request,
    method GetCart (line 123) | def GetCart(request,
    method EmptyCart (line 140) | def EmptyCart(request,
  class RecommendationServiceStub (line 157) | class RecommendationServiceStub(object):
    method __init__ (line 162) | def __init__(self, channel):
  class RecommendationServiceServicer (line 175) | class RecommendationServiceServicer(object):
    method ListRecommendations (line 180) | def ListRecommendations(self, request, context):
  function add_RecommendationServiceServicer_to_server (line 187) | def add_RecommendationServiceServicer_to_server(servicer, server):
  class RecommendationService (line 201) | class RecommendationService(object):
    method ListRecommendations (line 207) | def ListRecommendations(request,
  class ProductCatalogServiceStub (line 224) | class ProductCatalogServiceStub(object):
    method __init__ (line 229) | def __init__(self, channel):
  class ProductCatalogServiceServicer (line 252) | class ProductCatalogServiceServicer(object):
    method ListProducts (line 257) | def ListProducts(self, request, context):
    method GetProduct (line 263) | def GetProduct(self, request, context):
    method SearchProducts (line 269) | def SearchProducts(self, request, context):
  function add_ProductCatalogServiceServicer_to_server (line 276) | def add_ProductCatalogServiceServicer_to_server(servicer, server):
  class ProductCatalogService (line 300) | class ProductCatalogService(object):
    method ListProducts (line 306) | def ListProducts(request,
    method GetProduct (line 323) | def GetProduct(request,
    method SearchProducts (line 340) | def SearchProducts(request,
  class ShippingServiceStub (line 357) | class ShippingServiceStub(object):
    method __init__ (line 362) | def __init__(self, channel):
  class ShippingServiceServicer (line 380) | class ShippingServiceServicer(object):
    method GetQuote (line 385) | def GetQuote(self, request, context):
    method ShipOrder (line 391) | def ShipOrder(self, request, context):
  function add_ShippingServiceServicer_to_server (line 398) | def add_ShippingServiceServicer_to_server(servicer, server):
  class ShippingService (line 417) | class ShippingService(object):
    method GetQuote (line 423) | def GetQuote(request,
    method ShipOrder (line 440) | def ShipOrder(request,
  class CurrencyServiceStub (line 457) | class CurrencyServiceStub(object):
    method __init__ (line 462) | def __init__(self, channel):
  class CurrencyServiceServicer (line 480) | class CurrencyServiceServicer(object):
    method GetSupportedCurrencies (line 485) | def GetSupportedCurrencies(self, request, context):
    method Convert (line 491) | def Convert(self, request, context):
  function add_CurrencyServiceServicer_to_server (line 498) | def add_CurrencyServiceServicer_to_server(servicer, server):
  class CurrencyService (line 517) | class CurrencyService(object):
    method GetSupportedCurrencies (line 523) | def GetSupportedCurrencies(request,
    method Convert (line 540) | def Convert(request,
  class PaymentServiceStub (line 557) | class PaymentServiceStub(object):
    method __init__ (line 562) | def __init__(self, channel):
  class PaymentServiceServicer (line 575) | class PaymentServiceServicer(object):
    method Charge (line 580) | def Charge(self, request, context):
  function add_PaymentServiceServicer_to_server (line 587) | def add_PaymentServiceServicer_to_server(servicer, server):
  class PaymentService (line 601) | class PaymentService(object):
    method Charge (line 607) | def Charge(request,
  class EmailServiceStub (line 624) | class EmailServiceStub(object):
    method __init__ (line 629) | def __init__(self, channel):
  class EmailServiceServicer (line 642) | class EmailServiceServicer(object):
    method SendOrderConfirmation (line 647) | def SendOrderConfirmation(self, request, context):
  function add_EmailServiceServicer_to_server (line 654) | def add_EmailServiceServicer_to_server(servicer, server):
  class EmailService (line 668) | class EmailService(object):
    method SendOrderConfirmation (line 674) | def SendOrderConfirmation(request,
  class CheckoutServiceStub (line 691) | class CheckoutServiceStub(object):
    method __init__ (line 696) | def __init__(self, channel):
  class CheckoutServiceServicer (line 709) | class CheckoutServiceServicer(object):
    method PlaceOrder (line 714) | def PlaceOrder(self, request, context):
  function add_CheckoutServiceServicer_to_server (line 721) | def add_CheckoutServiceServicer_to_server(servicer, server):
  class CheckoutService (line 735) | class CheckoutService(object):
    method PlaceOrder (line 741) | def PlaceOrder(request,
  class AdServiceStub (line 758) | class AdServiceStub(object):
    method __init__ (line 763) | def __init__(self, channel):
  class AdServiceServicer (line 776) | class AdServiceServicer(object):
    method GetAds (line 781) | def GetAds(self, request, context):
  function add_AdServiceServicer_to_server (line 788) | def add_AdServiceServicer_to_server(servicer, server):
  class AdService (line 802) | class AdService(object):
    method GetAds (line 808) | def GetAds(request,

FILE: src/emailservice/email_client.py
  function send_confirmation_email (line 25) | def send_confirmation_email(email, order):

FILE: src/emailservice/email_server.py
  class BaseEmailService (line 52) | class BaseEmailService(demo_pb2_grpc.EmailServiceServicer):
    method Check (line 53) | def Check(self, request, context):
    method Watch (line 57) | def Watch(self, request, context):
  class EmailService (line 61) | class EmailService(BaseEmailService):
    method __init__ (line 62) | def __init__(self):
    method send_email (line 67) | def send_email(client, email_address, content):
    method SendOrderConfirmation (line 86) | def SendOrderConfirmation(self, request, context):
  class DummyEmailService (line 108) | class DummyEmailService(BaseEmailService):
    method SendOrderConfirmation (line 109) | def SendOrderConfirmation(self, request, context):
  class HealthCheck (line 113) | class HealthCheck():
    method Check (line 114) | def Check(self, request, context):
  function start (line 118) | def start(dummy_mode):
  function initStackdriverProfiling (line 139) | def initStackdriverProfiling():

FILE: src/emailservice/logger.py
  class CustomJsonFormatter (line 23) | class CustomJsonFormatter(jsonlogger.JsonFormatter):
    method add_fields (line 24) | def add_fields(self, log_record, record, message_dict):
  function getJSONLogger (line 33) | def getJSONLogger(name):

FILE: src/frontend/deployment_details.go
  function init (line 15) | func init() {
  function initializeLogger (line 22) | func initializeLogger() {
  function loadDeploymentDetails (line 36) | func loadDeploymentDetails() {

FILE: src/frontend/genproto/demo.pb.go
  constant _ (line 32) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 34) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type CartItem (line 37) | type CartItem struct
    method Reset (line 46) | func (x *CartItem) Reset() {
    method String (line 55) | func (x *CartItem) String() string {
    method ProtoMessage (line 59) | func (*CartItem) ProtoMessage() {}
    method ProtoReflect (line 61) | func (x *CartItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 74) | func (*CartItem) Descriptor() ([]byte, []int) {
    method GetProductId (line 78) | func (x *CartItem) GetProductId() string {
    method GetQuantity (line 85) | func (x *CartItem) GetQuantity() int32 {
  type AddItemRequest (line 92) | type AddItemRequest struct
    method Reset (line 101) | func (x *AddItemRequest) Reset() {
    method String (line 110) | func (x *AddItemRequest) String() string {
    method ProtoMessage (line 114) | func (*AddItemRequest) ProtoMessage() {}
    method ProtoReflect (line 116) | func (x *AddItemRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 129) | func (*AddItemRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 133) | func (x *AddItemRequest) GetUserId() string {
    method GetItem (line 140) | func (x *AddItemRequest) GetItem() *CartItem {
  type EmptyCartRequest (line 147) | type EmptyCartRequest struct
    method Reset (line 155) | func (x *EmptyCartRequest) Reset() {
    method String (line 164) | func (x *EmptyCartRequest) String() string {
    method ProtoMessage (line 168) | func (*EmptyCartRequest) ProtoMessage() {}
    method ProtoReflect (line 170) | func (x *EmptyCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 183) | func (*EmptyCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 187) | func (x *EmptyCartRequest) GetUserId() string {
  type GetCartRequest (line 194) | type GetCartRequest struct
    method Reset (line 202) | func (x *GetCartRequest) Reset() {
    method String (line 211) | func (x *GetCartRequest) String() string {
    method ProtoMessage (line 215) | func (*GetCartRequest) ProtoMessage() {}
    method ProtoReflect (line 217) | func (x *GetCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 230) | func (*GetCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 234) | func (x *GetCartRequest) GetUserId() string {
  type Cart (line 241) | type Cart struct
    method Reset (line 250) | func (x *Cart) Reset() {
    method String (line 259) | func (x *Cart) String() string {
    method ProtoMessage (line 263) | func (*Cart) ProtoMessage() {}
    method ProtoReflect (line 265) | func (x *Cart) ProtoReflect() protoreflect.Message {
    method Descriptor (line 278) | func (*Cart) Descriptor() ([]byte, []int) {
    method GetUserId (line 282) | func (x *Cart) GetUserId() string {
    method GetItems (line 289) | func (x *Cart) GetItems() []*CartItem {
  type Empty (line 296) | type Empty struct
    method Reset (line 302) | func (x *Empty) Reset() {
    method String (line 311) | func (x *Empty) String() string {
    method ProtoMessage (line 315) | func (*Empty) ProtoMessage() {}
    method ProtoReflect (line 317) | func (x *Empty) ProtoReflect() protoreflect.Message {
    method Descriptor (line 330) | func (*Empty) Descriptor() ([]byte, []int) {
  type ListRecommendationsRequest (line 334) | type ListRecommendationsRequest struct
    method Reset (line 343) | func (x *ListRecommendationsRequest) Reset() {
    method String (line 352) | func (x *ListRecommendationsRequest) String() string {
    method ProtoMessage (line 356) | func (*ListRecommendationsRequest) ProtoMessage() {}
    method ProtoReflect (line 358) | func (x *ListRecommendationsRequest) ProtoReflect() protoreflect.Messa...
    method Descriptor (line 371) | func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 375) | func (x *ListRecommendationsRequest) GetUserId() string {
    method GetProductIds (line 382) | func (x *ListRecommendationsRequest) GetProductIds() []string {
  type ListRecommendationsResponse (line 389) | type ListRecommendationsResponse struct
    method Reset (line 397) | func (x *ListRecommendationsResponse) Reset() {
    method String (line 406) | func (x *ListRecommendationsResponse) String() string {
    method ProtoMessage (line 410) | func (*ListRecommendationsResponse) ProtoMessage() {}
    method ProtoReflect (line 412) | func (x *ListRecommendationsResponse) ProtoReflect() protoreflect.Mess...
    method Descriptor (line 425) | func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) {
    method GetProductIds (line 429) | func (x *ListRecommendationsResponse) GetProductIds() []string {
  type Product (line 436) | type Product struct
    method Reset (line 451) | func (x *Product) Reset() {
    method String (line 460) | func (x *Product) String() string {
    method ProtoMessage (line 464) | func (*Product) ProtoMessage() {}
    method ProtoReflect (line 466) | func (x *Product) ProtoReflect() protoreflect.Message {
    method Descriptor (line 479) | func (*Product) Descriptor() ([]byte, []int) {
    method GetId (line 483) | func (x *Product) GetId() string {
    method GetName (line 490) | func (x *Product) GetName() string {
    method GetDescription (line 497) | func (x *Product) GetDescription() string {
    method GetPicture (line 504) | func (x *Product) GetPicture() string {
    method GetPriceUsd (line 511) | func (x *Product) GetPriceUsd() *Money {
    method GetCategories (line 518) | func (x *Product) GetCategories() []string {
  type ListProductsResponse (line 525) | type ListProductsResponse struct
    method Reset (line 533) | func (x *ListProductsResponse) Reset() {
    method String (line 542) | func (x *ListProductsResponse) String() string {
    method ProtoMessage (line 546) | func (*ListProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 548) | func (x *ListProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 561) | func (*ListProductsResponse) Descriptor() ([]byte, []int) {
    method GetProducts (line 565) | func (x *ListProductsResponse) GetProducts() []*Product {
  type GetProductRequest (line 572) | type GetProductRequest struct
    method Reset (line 580) | func (x *GetProductRequest) Reset() {
    method String (line 589) | func (x *GetProductRequest) String() string {
    method ProtoMessage (line 593) | func (*GetProductRequest) ProtoMessage() {}
    method ProtoReflect (line 595) | func (x *GetProductRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 608) | func (*GetProductRequest) Descriptor() ([]byte, []int) {
    method GetId (line 612) | func (x *GetProductRequest) GetId() string {
  type SearchProductsRequest (line 619) | type SearchProductsRequest struct
    method Reset (line 627) | func (x *SearchProductsRequest) Reset() {
    method String (line 636) | func (x *SearchProductsRequest) String() string {
    method ProtoMessage (line 640) | func (*SearchProductsRequest) ProtoMessage() {}
    method ProtoReflect (line 642) | func (x *SearchProductsRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 655) | func (*SearchProductsRequest) Descriptor() ([]byte, []int) {
    method GetQuery (line 659) | func (x *SearchProductsRequest) GetQuery() string {
  type SearchProductsResponse (line 666) | type SearchProductsResponse struct
    method Reset (line 674) | func (x *SearchProductsResponse) Reset() {
    method String (line 683) | func (x *SearchProductsResponse) String() string {
    method ProtoMessage (line 687) | func (*SearchProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 689) | func (x *SearchProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 702) | func (*SearchProductsResponse) Descriptor() ([]byte, []int) {
    method GetResults (line 706) | func (x *SearchProductsResponse) GetResults() []*Product {
  type GetQuoteRequest (line 713) | type GetQuoteRequest struct
    method Reset (line 722) | func (x *GetQuoteRequest) Reset() {
    method String (line 731) | func (x *GetQuoteRequest) String() string {
    method ProtoMessage (line 735) | func (*GetQuoteRequest) ProtoMessage() {}
    method ProtoReflect (line 737) | func (x *GetQuoteRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 750) | func (*GetQuoteRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 754) | func (x *GetQuoteRequest) GetAddress() *Address {
    method GetItems (line 761) | func (x *GetQuoteRequest) GetItems() []*CartItem {
  type GetQuoteResponse (line 768) | type GetQuoteResponse struct
    method Reset (line 776) | func (x *GetQuoteResponse) Reset() {
    method String (line 785) | func (x *GetQuoteResponse) String() string {
    method ProtoMessage (line 789) | func (*GetQuoteResponse) ProtoMessage() {}
    method ProtoReflect (line 791) | func (x *GetQuoteResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 804) | func (*GetQuoteResponse) Descriptor() ([]byte, []int) {
    method GetCostUsd (line 808) | func (x *GetQuoteResponse) GetCostUsd() *Money {
  type ShipOrderRequest (line 815) | type ShipOrderRequest struct
    method Reset (line 824) | func (x *ShipOrderRequest) Reset() {
    method String (line 833) | func (x *ShipOrderRequest) String() string {
    method ProtoMessage (line 837) | func (*ShipOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 839) | func (x *ShipOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 852) | func (*ShipOrderRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 856) | func (x *ShipOrderRequest) GetAddress() *Address {
    method GetItems (line 863) | func (x *ShipOrderRequest) GetItems() []*CartItem {
  type ShipOrderResponse (line 870) | type ShipOrderResponse struct
    method Reset (line 878) | func (x *ShipOrderResponse) Reset() {
    method String (line 887) | func (x *ShipOrderResponse) String() string {
    method ProtoMessage (line 891) | func (*ShipOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 893) | func (x *ShipOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 906) | func (*ShipOrderResponse) Descriptor() ([]byte, []int) {
    method GetTrackingId (line 910) | func (x *ShipOrderResponse) GetTrackingId() string {
  type Address (line 917) | type Address struct
    method Reset (line 929) | func (x *Address) Reset() {
    method String (line 938) | func (x *Address) String() string {
    method ProtoMessage (line 942) | func (*Address) ProtoMessage() {}
    method ProtoReflect (line 944) | func (x *Address) ProtoReflect() protoreflect.Message {
    method Descriptor (line 957) | func (*Address) Descriptor() ([]byte, []int) {
    method GetStreetAddress (line 961) | func (x *Address) GetStreetAddress() string {
    method GetCity (line 968) | func (x *Address) GetCity() string {
    method GetState (line 975) | func (x *Address) GetState() string {
    method GetCountry (line 982) | func (x *Address) GetCountry() string {
    method GetZipCode (line 989) | func (x *Address) GetZipCode() int32 {
  type Money (line 997) | type Money struct
    method Reset (line 1016) | func (x *Money) Reset() {
    method String (line 1025) | func (x *Money) String() string {
    method ProtoMessage (line 1029) | func (*Money) ProtoMessage() {}
    method ProtoReflect (line 1031) | func (x *Money) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1044) | func (*Money) Descriptor() ([]byte, []int) {
    method GetCurrencyCode (line 1048) | func (x *Money) GetCurrencyCode() string {
    method GetUnits (line 1055) | func (x *Money) GetUnits() int64 {
    method GetNanos (line 1062) | func (x *Money) GetNanos() int32 {
  type GetSupportedCurrenciesResponse (line 1069) | type GetSupportedCurrenciesResponse struct
    method Reset (line 1078) | func (x *GetSupportedCurrenciesResponse) Reset() {
    method String (line 1087) | func (x *GetSupportedCurrenciesResponse) String() string {
    method ProtoMessage (line 1091) | func (*GetSupportedCurrenciesResponse) ProtoMessage() {}
    method ProtoReflect (line 1093) | func (x *GetSupportedCurrenciesResponse) ProtoReflect() protoreflect.M...
    method Descriptor (line 1106) | func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) {
    method GetCurrencyCodes (line 1110) | func (x *GetSupportedCurrenciesResponse) GetCurrencyCodes() []string {
  type CurrencyConversionRequest (line 1117) | type CurrencyConversionRequest struct
    method Reset (line 1127) | func (x *CurrencyConversionRequest) Reset() {
    method String (line 1136) | func (x *CurrencyConversionRequest) String() string {
    method ProtoMessage (line 1140) | func (*CurrencyConversionRequest) ProtoMessage() {}
    method ProtoReflect (line 1142) | func (x *CurrencyConversionRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1155) | func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) {
    method GetFrom (line 1159) | func (x *CurrencyConversionRequest) GetFrom() *Money {
    method GetToCode (line 1166) | func (x *CurrencyConversionRequest) GetToCode() string {
  type CreditCardInfo (line 1173) | type CreditCardInfo struct
    method Reset (line 1184) | func (x *CreditCardInfo) Reset() {
    method String (line 1193) | func (x *CreditCardInfo) String() string {
    method ProtoMessage (line 1197) | func (*CreditCardInfo) ProtoMessage() {}
    method ProtoReflect (line 1199) | func (x *CreditCardInfo) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1212) | func (*CreditCardInfo) Descriptor() ([]byte, []int) {
    method GetCreditCardNumber (line 1216) | func (x *CreditCardInfo) GetCreditCardNumber() string {
    method GetCreditCardCvv (line 1223) | func (x *CreditCardInfo) GetCreditCardCvv() int32 {
    method GetCreditCardExpirationYear (line 1230) | func (x *CreditCardInfo) GetCreditCardExpirationYear() int32 {
    method GetCreditCardExpirationMonth (line 1237) | func (x *CreditCardInfo) GetCreditCardExpirationMonth() int32 {
  type ChargeRequest (line 1244) | type ChargeRequest struct
    method Reset (line 1253) | func (x *ChargeRequest) Reset() {
    method String (line 1262) | func (x *ChargeRequest) String() string {
    method ProtoMessage (line 1266) | func (*ChargeRequest) ProtoMessage() {}
    method ProtoReflect (line 1268) | func (x *ChargeRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1281) | func (*ChargeRequest) Descriptor() ([]byte, []int) {
    method GetAmount (line 1285) | func (x *ChargeRequest) GetAmount() *Money {
    method GetCreditCard (line 1292) | func (x *ChargeRequest) GetCreditCard() *CreditCardInfo {
  type ChargeResponse (line 1299) | type ChargeResponse struct
    method Reset (line 1307) | func (x *ChargeResponse) Reset() {
    method String (line 1316) | func (x *ChargeResponse) String() string {
    method ProtoMessage (line 1320) | func (*ChargeResponse) ProtoMessage() {}
    method ProtoReflect (line 1322) | func (x *ChargeResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1335) | func (*ChargeResponse) Descriptor() ([]byte, []int) {
    method GetTransactionId (line 1339) | func (x *ChargeResponse) GetTransactionId() string {
  type OrderItem (line 1346) | type OrderItem struct
    method Reset (line 1355) | func (x *OrderItem) Reset() {
    method String (line 1364) | func (x *OrderItem) String() string {
    method ProtoMessage (line 1368) | func (*OrderItem) ProtoMessage() {}
    method ProtoReflect (line 1370) | func (x *OrderItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1383) | func (*OrderItem) Descriptor() ([]byte, []int) {
    method GetItem (line 1387) | func (x *OrderItem) GetItem() *CartItem {
    method GetCost (line 1394) | func (x *OrderItem) GetCost() *Money {
  type OrderResult (line 1401) | type OrderResult struct
    method Reset (line 1413) | func (x *OrderResult) Reset() {
    method String (line 1422) | func (x *OrderResult) String() string {
    method ProtoMessage (line 1426) | func (*OrderResult) ProtoMessage() {}
    method ProtoReflect (line 1428) | func (x *OrderResult) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1441) | func (*OrderResult) Descriptor() ([]byte, []int) {
    method GetOrderId (line 1445) | func (x *OrderResult) GetOrderId() string {
    method GetShippingTrackingId (line 1452) | func (x *OrderResult) GetShippingTrackingId() string {
    method GetShippingCost (line 1459) | func (x *OrderResult) GetShippingCost() *Money {
    method GetShippingAddress (line 1466) | func (x *OrderResult) GetShippingAddress() *Address {
    method GetItems (line 1473) | func (x *OrderResult) GetItems() []*OrderItem {
  type SendOrderConfirmationRequest (line 1480) | type SendOrderConfirmationRequest struct
    method Reset (line 1489) | func (x *SendOrderConfirmationRequest) Reset() {
    method String (line 1498) | func (x *SendOrderConfirmationRequest) String() string {
    method ProtoMessage (line 1502) | func (*SendOrderConfirmationRequest) ProtoMessage() {}
    method ProtoReflect (line 1504) | func (x *SendOrderConfirmationRequest) ProtoReflect() protoreflect.Mes...
    method Descriptor (line 1517) | func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) {
    method GetEmail (line 1521) | func (x *SendOrderConfirmationRequest) GetEmail() string {
    method GetOrder (line 1528) | func (x *SendOrderConfirmationRequest) GetOrder() *OrderResult {
  type PlaceOrderRequest (line 1535) | type PlaceOrderRequest struct
    method Reset (line 1547) | func (x *PlaceOrderRequest) Reset() {
    method String (line 1556) | func (x *PlaceOrderRequest) String() string {
    method ProtoMessage (line 1560) | func (*PlaceOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 1562) | func (x *PlaceOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1575) | func (*PlaceOrderRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 1579) | func (x *PlaceOrderRequest) GetUserId() string {
    method GetUserCurrency (line 1586) | func (x *PlaceOrderRequest) GetUserCurrency() string {
    method GetAddress (line 1593) | func (x *PlaceOrderRequest) GetAddress() *Address {
    method GetEmail (line 1600) | func (x *PlaceOrderRequest) GetEmail() string {
    method GetCreditCard (line 1607) | func (x *PlaceOrderRequest) GetCreditCard() *CreditCardInfo {
  type PlaceOrderResponse (line 1614) | type PlaceOrderResponse struct
    method Reset (line 1622) | func (x *PlaceOrderResponse) Reset() {
    method String (line 1631) | func (x *PlaceOrderResponse) String() string {
    method ProtoMessage (line 1635) | func (*PlaceOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 1637) | func (x *PlaceOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1650) | func (*PlaceOrderResponse) Descriptor() ([]byte, []int) {
    method GetOrder (line 1654) | func (x *PlaceOrderResponse) GetOrder() *OrderResult {
  type AdRequest (line 1661) | type AdRequest struct
    method Reset (line 1670) | func (x *AdRequest) Reset() {
    method String (line 1679) | func (x *AdRequest) String() string {
    method ProtoMessage (line 1683) | func (*AdRequest) ProtoMessage() {}
    method ProtoReflect (line 1685) | func (x *AdRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1698) | func (*AdRequest) Descriptor() ([]byte, []int) {
    method GetContextKeys (line 1702) | func (x *AdRequest) GetContextKeys() []string {
  type AdResponse (line 1709) | type AdResponse struct
    method Reset (line 1717) | func (x *AdResponse) Reset() {
    method String (line 1726) | func (x *AdResponse) String() string {
    method ProtoMessage (line 1730) | func (*AdResponse) ProtoMessage() {}
    method ProtoReflect (line 1732) | func (x *AdResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1745) | func (*AdResponse) Descriptor() ([]byte, []int) {
    method GetAds (line 1749) | func (x *AdResponse) GetAds() []*Ad {
  type Ad (line 1756) | type Ad struct
    method Reset (line 1767) | func (x *Ad) Reset() {
    method String (line 1776) | func (x *Ad) String() string {
    method ProtoMessage (line 1780) | func (*Ad) ProtoMessage() {}
    method ProtoReflect (line 1782) | func (x *Ad) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1795) | func (*Ad) Descriptor() ([]byte, []int) {
    method GetRedirectUrl (line 1799) | func (x *Ad) GetRedirectUrl() string {
    method GetText (line 1806) | func (x *Ad) GetText() string {
  function file_demo_proto_rawDescGZIP (line 2098) | func file_demo_proto_rawDescGZIP() []byte {
  function init (line 2201) | func init() { file_demo_proto_init() }
  function file_demo_proto_init (line 2202) | func file_demo_proto_init() {

FILE: src/frontend/genproto/demo_grpc.pb.go
  constant _ (line 33) | _ = grpc.SupportPackageIsVersion9
  constant CartService_AddItem_FullMethodName (line 36) | CartService_AddItem_FullMethodName   = "/hipstershop.CartService/AddItem"
  constant CartService_GetCart_FullMethodName (line 37) | CartService_GetCart_FullMethodName   = "/hipstershop.CartService/GetCart"
  constant CartService_EmptyCart_FullMethodName (line 38) | CartService_EmptyCart_FullMethodName = "/hipstershop.CartService/EmptyCart"
  type CartServiceClient (line 44) | type CartServiceClient interface
  type cartServiceClient (line 50) | type cartServiceClient struct
    method AddItem (line 58) | func (c *cartServiceClient) AddItem(ctx context.Context, in *AddItemRe...
    method GetCart (line 68) | func (c *cartServiceClient) GetCart(ctx context.Context, in *GetCartRe...
    method EmptyCart (line 78) | func (c *cartServiceClient) EmptyCart(ctx context.Context, in *EmptyCa...
  function NewCartServiceClient (line 54) | func NewCartServiceClient(cc grpc.ClientConnInterface) CartServiceClient {
  type CartServiceServer (line 91) | type CartServiceServer interface
  type UnimplementedCartServiceServer (line 103) | type UnimplementedCartServiceServer struct
    method AddItem (line 105) | func (UnimplementedCartServiceServer) AddItem(context.Context, *AddIte...
    method GetCart (line 108) | func (UnimplementedCartServiceServer) GetCart(context.Context, *GetCar...
    method EmptyCart (line 111) | func (UnimplementedCartServiceServer) EmptyCart(context.Context, *Empt...
    method mustEmbedUnimplementedCartServiceServer (line 114) | func (UnimplementedCartServiceServer) mustEmbedUnimplementedCartServic...
    method testEmbeddedByValue (line 115) | func (UnimplementedCartServiceServer) testEmbeddedByValue()           ...
  type UnsafeCartServiceServer (line 120) | type UnsafeCartServiceServer interface
  function RegisterCartServiceServer (line 124) | func RegisterCartServiceServer(s grpc.ServiceRegistrar, srv CartServiceS...
  function _CartService_AddItem_Handler (line 135) | func _CartService_AddItem_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_GetCart_Handler (line 153) | func _CartService_GetCart_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_EmptyCart_Handler (line 171) | func _CartService_EmptyCart_Handler(srv interface{}, ctx context.Context...
  constant RecommendationService_ListRecommendations_FullMethodName (line 214) | RecommendationService_ListRecommendations_FullMethodName = "/hipstershop...
  type RecommendationServiceClient (line 220) | type RecommendationServiceClient interface
  type recommendationServiceClient (line 224) | type recommendationServiceClient struct
    method ListRecommendations (line 232) | func (c *recommendationServiceClient) ListRecommendations(ctx context....
  function NewRecommendationServiceClient (line 228) | func NewRecommendationServiceClient(cc grpc.ClientConnInterface) Recomme...
  type RecommendationServiceServer (line 245) | type RecommendationServiceServer interface
  type UnimplementedRecommendationServiceServer (line 255) | type UnimplementedRecommendationServiceServer struct
    method ListRecommendations (line 257) | func (UnimplementedRecommendationServiceServer) ListRecommendations(co...
    method mustEmbedUnimplementedRecommendationServiceServer (line 260) | func (UnimplementedRecommendationServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 261) | func (UnimplementedRecommendationServiceServer) testEmbeddedByValue() ...
  type UnsafeRecommendationServiceServer (line 266) | type UnsafeRecommendationServiceServer interface
  function RegisterRecommendationServiceServer (line 270) | func RegisterRecommendationServiceServer(s grpc.ServiceRegistrar, srv Re...
  function _RecommendationService_ListRecommendations_Handler (line 281) | func _RecommendationService_ListRecommendations_Handler(srv interface{},...
  constant ProductCatalogService_ListProducts_FullMethodName (line 316) | ProductCatalogService_ListProducts_FullMethodName   = "/hipstershop.Prod...
  constant ProductCatalogService_GetProduct_FullMethodName (line 317) | ProductCatalogService_GetProduct_FullMethodName     = "/hipstershop.Prod...
  constant ProductCatalogService_SearchProducts_FullMethodName (line 318) | ProductCatalogService_SearchProducts_FullMethodName = "/hipstershop.Prod...
  type ProductCatalogServiceClient (line 324) | type ProductCatalogServiceClient interface
  type productCatalogServiceClient (line 330) | type productCatalogServiceClient struct
    method ListProducts (line 338) | func (c *productCatalogServiceClient) ListProducts(ctx context.Context...
    method GetProduct (line 348) | func (c *productCatalogServiceClient) GetProduct(ctx context.Context, ...
    method SearchProducts (line 358) | func (c *productCatalogServiceClient) SearchProducts(ctx context.Conte...
  function NewProductCatalogServiceClient (line 334) | func NewProductCatalogServiceClient(cc grpc.ClientConnInterface) Product...
  type ProductCatalogServiceServer (line 371) | type ProductCatalogServiceServer interface
  type UnimplementedProductCatalogServiceServer (line 383) | type UnimplementedProductCatalogServiceServer struct
    method ListProducts (line 385) | func (UnimplementedProductCatalogServiceServer) ListProducts(context.C...
    method GetProduct (line 388) | func (UnimplementedProductCatalogServiceServer) GetProduct(context.Con...
    method SearchProducts (line 391) | func (UnimplementedProductCatalogServiceServer) SearchProducts(context...
    method mustEmbedUnimplementedProductCatalogServiceServer (line 394) | func (UnimplementedProductCatalogServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 395) | func (UnimplementedProductCatalogServiceServer) testEmbeddedByValue() ...
  type UnsafeProductCatalogServiceServer (line 400) | type UnsafeProductCatalogServiceServer interface
  function RegisterProductCatalogServiceServer (line 404) | func RegisterProductCatalogServiceServer(s grpc.ServiceRegistrar, srv Pr...
  function _ProductCatalogService_ListProducts_Handler (line 415) | func _ProductCatalogService_ListProducts_Handler(srv interface{}, ctx co...
  function _ProductCatalogService_GetProduct_Handler (line 433) | func _ProductCatalogService_GetProduct_Handler(srv interface{}, ctx cont...
  function _ProductCatalogService_SearchProducts_Handler (line 451) | func _ProductCatalogService_SearchProducts_Handler(srv interface{}, ctx ...
  constant ShippingService_GetQuote_FullMethodName (line 494) | ShippingService_GetQuote_FullMethodName  = "/hipstershop.ShippingService...
  constant ShippingService_ShipOrder_FullMethodName (line 495) | ShippingService_ShipOrder_FullMethodName = "/hipstershop.ShippingService...
  type ShippingServiceClient (line 501) | type ShippingServiceClient interface
  type shippingServiceClient (line 506) | type shippingServiceClient struct
    method GetQuote (line 514) | func (c *shippingServiceClient) GetQuote(ctx context.Context, in *GetQ...
    method ShipOrder (line 524) | func (c *shippingServiceClient) ShipOrder(ctx context.Context, in *Shi...
  function NewShippingServiceClient (line 510) | func NewShippingServiceClient(cc grpc.ClientConnInterface) ShippingServi...
  type ShippingServiceServer (line 537) | type ShippingServiceServer interface
  type UnimplementedShippingServiceServer (line 548) | type UnimplementedShippingServiceServer struct
    method GetQuote (line 550) | func (UnimplementedShippingServiceServer) GetQuote(context.Context, *G...
    method ShipOrder (line 553) | func (UnimplementedShippingServiceServer) ShipOrder(context.Context, *...
    method mustEmbedUnimplementedShippingServiceServer (line 556) | func (UnimplementedShippingServiceServer) mustEmbedUnimplementedShippi...
    method testEmbeddedByValue (line 557) | func (UnimplementedShippingServiceServer) testEmbeddedByValue()       ...
  type UnsafeShippingServiceServer (line 562) | type UnsafeShippingServiceServer interface
  function RegisterShippingServiceServer (line 566) | func RegisterShippingServiceServer(s grpc.ServiceRegistrar, srv Shipping...
  function _ShippingService_GetQuote_Handler (line 577) | func _ShippingService_GetQuote_Handler(srv interface{}, ctx context.Cont...
  function _ShippingService_ShipOrder_Handler (line 595) | func _ShippingService_ShipOrder_Handler(srv interface{}, ctx context.Con...
  constant CurrencyService_GetSupportedCurrencies_FullMethodName (line 634) | CurrencyService_GetSupportedCurrencies_FullMethodName = "/hipstershop.Cu...
  constant CurrencyService_Convert_FullMethodName (line 635) | CurrencyService_Convert_FullMethodName                = "/hipstershop.Cu...
  type CurrencyServiceClient (line 641) | type CurrencyServiceClient interface
  type currencyServiceClient (line 646) | type currencyServiceClient struct
    method GetSupportedCurrencies (line 654) | func (c *currencyServiceClient) GetSupportedCurrencies(ctx context.Con...
    method Convert (line 664) | func (c *currencyServiceClient) Convert(ctx context.Context, in *Curre...
  function NewCurrencyServiceClient (line 650) | func NewCurrencyServiceClient(cc grpc.ClientConnInterface) CurrencyServi...
  type CurrencyServiceServer (line 677) | type CurrencyServiceServer interface
  type UnimplementedCurrencyServiceServer (line 688) | type UnimplementedCurrencyServiceServer struct
    method GetSupportedCurrencies (line 690) | func (UnimplementedCurrencyServiceServer) GetSupportedCurrencies(conte...
    method Convert (line 693) | func (UnimplementedCurrencyServiceServer) Convert(context.Context, *Cu...
    method mustEmbedUnimplementedCurrencyServiceServer (line 696) | func (UnimplementedCurrencyServiceServer) mustEmbedUnimplementedCurren...
    method testEmbeddedByValue (line 697) | func (UnimplementedCurrencyServiceServer) testEmbeddedByValue()       ...
  type UnsafeCurrencyServiceServer (line 702) | type UnsafeCurrencyServiceServer interface
  function RegisterCurrencyServiceServer (line 706) | func RegisterCurrencyServiceServer(s grpc.ServiceRegistrar, srv Currency...
  function _CurrencyService_GetSupportedCurrencies_Handler (line 717) | func _CurrencyService_GetSupportedCurrencies_Handler(srv interface{}, ct...
  function _CurrencyService_Convert_Handler (line 735) | func _CurrencyService_Convert_Handler(srv interface{}, ctx context.Conte...
  constant PaymentService_Charge_FullMethodName (line 774) | PaymentService_Charge_FullMethodName = "/hipstershop.PaymentService/Charge"
  type PaymentServiceClient (line 780) | type PaymentServiceClient interface
  type paymentServiceClient (line 784) | type paymentServiceClient struct
    method Charge (line 792) | func (c *paymentServiceClient) Charge(ctx context.Context, in *ChargeR...
  function NewPaymentServiceClient (line 788) | func NewPaymentServiceClient(cc grpc.ClientConnInterface) PaymentService...
  type PaymentServiceServer (line 805) | type PaymentServiceServer interface
  type UnimplementedPaymentServiceServer (line 815) | type UnimplementedPaymentServiceServer struct
    method Charge (line 817) | func (UnimplementedPaymentServiceServer) Charge(context.Context, *Char...
    method mustEmbedUnimplementedPaymentServiceServer (line 820) | func (UnimplementedPaymentServiceServer) mustEmbedUnimplementedPayment...
    method testEmbeddedByValue (line 821) | func (UnimplementedPaymentServiceServer) testEmbeddedByValue()        ...
  type UnsafePaymentServiceServer (line 826) | type UnsafePaymentServiceServer interface
  function RegisterPaymentServiceServer (line 830) | func RegisterPaymentServiceServer(s grpc.ServiceRegistrar, srv PaymentSe...
  function _PaymentService_Charge_Handler (line 841) | func _PaymentService_Charge_Handler(srv interface{}, ctx context.Context...
  constant EmailService_SendOrderConfirmation_FullMethodName (line 876) | EmailService_SendOrderConfirmation_FullMethodName = "/hipstershop.EmailS...
  type EmailServiceClient (line 882) | type EmailServiceClient interface
  type emailServiceClient (line 886) | type emailServiceClient struct
    method SendOrderConfirmation (line 894) | func (c *emailServiceClient) SendOrderConfirmation(ctx context.Context...
  function NewEmailServiceClient (line 890) | func NewEmailServiceClient(cc grpc.ClientConnInterface) EmailServiceClie...
  type EmailServiceServer (line 907) | type EmailServiceServer interface
  type UnimplementedEmailServiceServer (line 917) | type UnimplementedEmailServiceServer struct
    method SendOrderConfirmation (line 919) | func (UnimplementedEmailServiceServer) SendOrderConfirmation(context.C...
    method mustEmbedUnimplementedEmailServiceServer (line 922) | func (UnimplementedEmailServiceServer) mustEmbedUnimplementedEmailServ...
    method testEmbeddedByValue (line 923) | func (UnimplementedEmailServiceServer) testEmbeddedByValue()          ...
  type UnsafeEmailServiceServer (line 928) | type UnsafeEmailServiceServer interface
  function RegisterEmailServiceServer (line 932) | func RegisterEmailServiceServer(s grpc.ServiceRegistrar, srv EmailServic...
  function _EmailService_SendOrderConfirmation_Handler (line 943) | func _EmailService_SendOrderConfirmation_Handler(srv interface{}, ctx co...
  constant CheckoutService_PlaceOrder_FullMethodName (line 978) | CheckoutService_PlaceOrder_FullMethodName = "/hipstershop.CheckoutServic...
  type CheckoutServiceClient (line 984) | type CheckoutServiceClient interface
  type checkoutServiceClient (line 988) | type checkoutServiceClient struct
    method PlaceOrder (line 996) | func (c *checkoutServiceClient) PlaceOrder(ctx context.Context, in *Pl...
  function NewCheckoutServiceClient (line 992) | func NewCheckoutServiceClient(cc grpc.ClientConnInterface) CheckoutServi...
  type CheckoutServiceServer (line 1009) | type CheckoutServiceServer interface
  type UnimplementedCheckoutServiceServer (line 1019) | type UnimplementedCheckoutServiceServer struct
    method PlaceOrder (line 1021) | func (UnimplementedCheckoutServiceServer) PlaceOrder(context.Context, ...
    method mustEmbedUnimplementedCheckoutServiceServer (line 1024) | func (UnimplementedCheckoutServiceServer) mustEmbedUnimplementedChecko...
    method testEmbeddedByValue (line 1025) | func (UnimplementedCheckoutServiceServer) testEmbeddedByValue()       ...
  type UnsafeCheckoutServiceServer (line 1030) | type UnsafeCheckoutServiceServer interface
  function RegisterCheckoutServiceServer (line 1034) | func RegisterCheckoutServiceServer(s grpc.ServiceRegistrar, srv Checkout...
  function _CheckoutService_PlaceOrder_Handler (line 1045) | func _CheckoutService_PlaceOrder_Handler(srv interface{}, ctx context.Co...
  constant AdService_GetAds_FullMethodName (line 1080) | AdService_GetAds_FullMethodName = "/hipstershop.AdService/GetAds"
  type AdServiceClient (line 1086) | type AdServiceClient interface
  type adServiceClient (line 1090) | type adServiceClient struct
    method GetAds (line 1098) | func (c *adServiceClient) GetAds(ctx context.Context, in *AdRequest, o...
  function NewAdServiceClient (line 1094) | func NewAdServiceClient(cc grpc.ClientConnInterface) AdServiceClient {
  type AdServiceServer (line 1111) | type AdServiceServer interface
  type UnimplementedAdServiceServer (line 1121) | type UnimplementedAdServiceServer struct
    method GetAds (line 1123) | func (UnimplementedAdServiceServer) GetAds(context.Context, *AdRequest...
    method mustEmbedUnimplementedAdServiceServer (line 1126) | func (UnimplementedAdServiceServer) mustEmbedUnimplementedAdServiceSer...
    method testEmbeddedByValue (line 1127) | func (UnimplementedAdServiceServer) testEmbeddedByValue()             ...
  type UnsafeAdServiceServer (line 1132) | type UnsafeAdServiceServer interface
  function RegisterAdServiceServer (line 1136) | func RegisterAdServiceServer(s grpc.ServiceRegistrar, srv AdServiceServe...
  function _AdService_GetAds_Handler (line 1147) | func _AdService_GetAds_Handler(srv interface{}, ctx context.Context, dec...

FILE: src/frontend/handlers.go
  type platformDetails (line 40) | type platformDetails struct
    method setPlatformDetails (line 122) | func (plat *platformDetails) setPlatformDetails(env string) {
  method homeHandler (line 59) | func (fe *frontendServer) homeHandler(w http.ResponseWriter, r *http.Req...
  method productHandler (line 144) | func (fe *frontendServer) productHandler(w http.ResponseWriter, r *http....
  method addToCartHandler (line 211) | func (fe *frontendServer) addToCartHandler(w http.ResponseWriter, r *htt...
  method emptyCartHandler (line 239) | func (fe *frontendServer) emptyCartHandler(w http.ResponseWriter, r *htt...
  method viewCartHandler (line 251) | func (fe *frontendServer) viewCartHandler(w http.ResponseWriter, r *http...
  method placeOrderHandler (line 320) | func (fe *frontendServer) placeOrderHandler(w http.ResponseWriter, r *ht...
  method assistantHandler (line 403) | func (fe *frontendServer) assistantHandler(w http.ResponseWriter, r *htt...
  method logoutHandler (line 418) | func (fe *frontendServer) logoutHandler(w http.ResponseWriter, r *http.R...
  method getProductByID (line 430) | func (fe *frontendServer) getProductByID(w http.ResponseWriter, r *http....
  method chatBotHandler (line 451) | func (fe *frontendServer) chatBotHandler(w http.ResponseWriter, r *http....
  method setCurrencyHandler (line 499) | func (fe *frontendServer) setCurrencyHandler(w http.ResponseWriter, r *h...
  method chooseAd (line 527) | func (fe *frontendServer) chooseAd(ctx context.Context, ctxKeys []string...
  function renderHTTPError (line 536) | func renderHTTPError(log logrus.FieldLogger, r *http.Request, w http.Res...
  function injectCommonTemplateData (line 551) | func injectCommonTemplateData(r *http.Request, payload map[string]interf...
  function currentCurrency (line 573) | func currentCurrency(r *http.Request) string {
  function sessionID (line 581) | func sessionID(r *http.Request) string {
  function cartIDs (line 589) | func cartIDs(c []*pb.CartItem) []string {
  function cartSize (line 598) | func cartSize(c []*pb.CartItem) int {
  function renderMoney (line 606) | func renderMoney(money pb.Money) string {
  function renderCurrencyLogo (line 611) | func renderCurrencyLogo(currencyCode string) string {
  function stringinSlice (line 628) | func stringinSlice(slice []string, val string) bool {

FILE: src/frontend/main.go
  constant port (line 39) | port            = "8080"
  constant defaultCurrency (line 40) | defaultCurrency = "USD"
  constant cookieMaxAge (line 41) | cookieMaxAge    = 60 * 60 * 48
  constant cookiePrefix (line 43) | cookiePrefix    = "shop_"
  constant cookieSessionID (line 44) | cookieSessionID = cookiePrefix + "session-id"
  constant cookieCurrency (line 45) | cookieCurrency  = cookiePrefix + "currency"
  type ctxKeySessionID (line 61) | type ctxKeySessionID struct
  type frontendServer (line 63) | type frontendServer struct
  function main (line 91) | func main() {
  function initStats (line 173) | func initStats(log logrus.FieldLogger) {
  function initTracing (line 177) | func initTracing(log logrus.FieldLogger, ctx context.Context, svc *front...
  function initProfiling (line 194) | func initProfiling(log logrus.FieldLogger, service, version string) {
  function mustMapEnv (line 217) | func mustMapEnv(target *string, envKey string) {
  function mustConnGRPC (line 225) | func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr stri...

FILE: src/frontend/middleware.go
  type ctxKeyLog (line 27) | type ctxKeyLog struct
  type ctxKeyRequestID (line 28) | type ctxKeyRequestID struct
  type logHandler (line 30) | type logHandler struct
    method ServeHTTP (line 57) | func (lh *logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  type responseRecorder (line 35) | type responseRecorder struct
    method Header (line 41) | func (r *responseRecorder) Header() http.Header { return r.w.Header() }
    method Write (line 43) | func (r *responseRecorder) Write(p []byte) (int, error) {
    method WriteHeader (line 52) | func (r *responseRecorder) WriteHeader(statusCode int) {
  function ensureSessionID (line 85) | func ensureSessionID(next http.Handler) http.HandlerFunc {

FILE: src/frontend/money/money.go
  constant nanosMin (line 24) | nanosMin = -999999999
  constant nanosMax (line 25) | nanosMax = +999999999
  constant nanosMod (line 26) | nanosMod = 1000000000
  function IsValid (line 35) | func IsValid(m pb.Money) bool {
  function signMatches (line 39) | func signMatches(m pb.Money) bool {
  function validNanos (line 43) | func validNanos(nanos int32) bool { return nanosMin <= nanos && nanos <=...
  function IsZero (line 46) | func IsZero(m pb.Money) bool { return m.GetUnits() == 0 && m.GetNanos() ...
  function IsPositive (line 50) | func IsPositive(m pb.Money) bool {
  function IsNegative (line 56) | func IsNegative(m pb.Money) bool {
  function AreSameCurrency (line 62) | func AreSameCurrency(l, r pb.Money) bool {
  function AreEquals (line 68) | func AreEquals(l, r pb.Money) bool {
  function Negate (line 74) | func Negate(m pb.Money) pb.Money {
  function Must (line 83) | func Must(v pb.Money, err error) pb.Money {
  function Sum (line 93) | func Sum(l, r pb.Money) (pb.Money, error) {
  function MultiplySlow (line 125) | func MultiplySlow(m pb.Money, n uint32) pb.Money {

FILE: src/frontend/money/money_test.go
  function mmc (line 25) | func mmc(u int64, n int32, c string) pb.Money { return pb.Money{Units: u...
  function mm (line 26) | func mm(u int64, n int32) pb.Money            { return mmc(u, n, "") }
  function TestIsValid (line 28) | func TestIsValid(t *testing.T) {
  function TestIsZero (line 52) | func TestIsZero(t *testing.T) {
  function TestIsPositive (line 73) | func TestIsPositive(t *testing.T) {
  function TestIsNegative (line 94) | func TestIsNegative(t *testing.T) {
  function TestAreSameCurrency (line 115) | func TestAreSameCurrency(t *testing.T) {
  function TestAreEquals (line 140) | func TestAreEquals(t *testing.T) {
  function TestNegate (line 165) | func TestNegate(t *testing.T) {
  function TestMust_pass (line 185) | func TestMust_pass(t *testing.T) {
  function TestMust_panic (line 192) | func TestMust_panic(t *testing.T) {
  function TestSum (line 202) | func TestSum(t *testing.T) {

FILE: src/frontend/packaging_info.go
  type PackagingInfo (line 34) | type PackagingInfo struct
  function init (line 42) | func init() {
  function isPackagingServiceConfigured (line 46) | func isPackagingServiceConfigured() bool {
  function httpGetPackagingInfo (line 50) | func httpGetPackagingInfo(productId string) (*PackagingInfo, error) {

FILE: src/frontend/rpc.go
  constant avoidNoopCurrencyConversionRPC (line 27) | avoidNoopCurrencyConversionRPC = false
  method getCurrencies (line 30) | func (fe *frontendServer) getCurrencies(ctx context.Context) ([]string, ...
  method getProducts (line 45) | func (fe *frontendServer) getProducts(ctx context.Context) ([]*pb.Produc...
  method getProduct (line 51) | func (fe *frontendServer) getProduct(ctx context.Context, id string) (*p...
  method getCart (line 57) | func (fe *frontendServer) getCart(ctx context.Context, userID string) ([...
  method emptyCart (line 62) | func (fe *frontendServer) emptyCart(ctx context.Context, userID string) ...
  method insertCart (line 67) | func (fe *frontendServer) insertCart(ctx context.Context, userID, produc...
  method convertCurrency (line 77) | func (fe *frontendServer) convertCurrency(ctx context.Context, money *pb...
  method getShippingQuote (line 87) | func (fe *frontendServer) getShippingQuote(ctx context.Context, items []...
  method getRecommendations (line 99) | func (fe *frontendServer) getRecommendations(ctx context.Context, userID...
  method getAd (line 119) | func (fe *frontendServer) getAd(ctx context.Context, ctxKeys []string) (...

FILE: src/frontend/validator/validator.go
  function init (line 29) | func init() {
  type Payload (line 33) | type Payload interface
  type AddToCartPayload (line 37) | type AddToCartPayload struct
    method Validate (line 60) | func (ad *AddToCartPayload) Validate() error {
  type PlaceOrderPayload (line 42) | type PlaceOrderPayload struct
    method Validate (line 64) | func (po *PlaceOrderPayload) Validate() error {
  type SetCurrencyPayload (line 55) | type SetCurrencyPayload struct
    method Validate (line 68) | func (sc *SetCurrencyPayload) Validate() error {
  function ValidationErrorResponse (line 73) | func ValidationErrorResponse(err error) error {

FILE: src/frontend/validator/validator_test.go
  function TestPlaceOrderPassesValidation (line 22) | func TestPlaceOrderPassesValidation(t *testing.T) {
  function TestPlaceOrderFailsValidation (line 59) | func TestPlaceOrderFailsValidation(t *testing.T) {
  function TestAddToCartPassesValidation (line 106) | func TestAddToCartPassesValidation(t *testing.T) {
  function TestAddToCartFailsValidation (line 125) | func TestAddToCartFailsValidation(t *testing.T) {
  function TestSetCurrencyPassesValidation (line 146) | func TestSetCurrencyPassesValidation(t *testing.T) {
  function TestSetCurrencyFailsValidation (line 168) | func TestSetCurrencyFailsValidation(t *testing.T) {

FILE: src/loadgenerator/locustfile.py
  function index (line 34) | def index(l):
  function setCurrency (line 37) | def setCurrency(l):
  function browseProduct (line 42) | def browseProduct(l):
  function viewCart (line 45) | def viewCart(l):
  function addToCart (line 48) | def addToCart(l):
  function empty_cart (line 55) | def empty_cart(l):
  function checkout (line 58) | def checkout(l):
  function logout (line 74) | def logout(l):
  class UserBehavior (line 78) | class UserBehavior(TaskSet):
    method on_start (line 80) | def on_start(self):
  class WebsiteUser (line 90) | class WebsiteUser(FastHttpUser):

FILE: src/paymentservice/charge.js
  method level (line 23) | level (logLevelString, logLevelNum) {
  class CreditCardError (line 30) | class CreditCardError extends Error {
    method constructor (line 31) | constructor (message) {
  class InvalidCreditCard (line 37) | class InvalidCreditCard extends CreditCardError {
    method constructor (line 38) | constructor (cardType) {
  class UnacceptedCreditCard (line 43) | class UnacceptedCreditCard extends CreditCardError {
    method constructor (line 44) | constructor (cardType) {
  class ExpiredCreditCard (line 49) | class ExpiredCreditCard extends CreditCardError {
    method constructor (line 50) | constructor (number, month, year) {

FILE: src/paymentservice/index.js
  constant PORT (line 70) | const PORT = process.env['PORT'];
  constant PROTO_PATH (line 71) | const PROTO_PATH = path.join(__dirname, '/proto/');

FILE: src/paymentservice/logger.js
  method level (line 23) | level (logLevelString, logLevelNum) {

FILE: src/paymentservice/server.js
  class HipsterShopServer (line 23) | class HipsterShopServer {
    method constructor (line 24) | constructor(protoRoot, port = HipsterShopServer.PORT) {
    method ChargeServiceHandler (line 41) | static ChargeServiceHandler(call, callback) {
    method CheckHandler (line 52) | static CheckHandler(call, callback) {
    method listen (line 57) | listen() {
    method loadProto (line 70) | loadProto(path) {
    method loadAllProtos (line 84) | loadAllProtos(protoRoot) {

FILE: src/productcatalogservice/catalog_loader.go
  function loadCatalog (line 33) | func loadCatalog(catalog *pb.ListProductsResponse) error {
  function loadCatalogFromLocalFile (line 44) | func loadCatalogFromLocalFile(catalog *pb.ListProductsResponse) error {
  function getSecretPayload (line 62) | func getSecretPayload(project, secret, version string) (string, error) {
  function loadCatalogFromAlloyDB (line 85) | func loadCatalogFromAlloyDB(catalog *pb.ListProductsResponse) error {

FILE: src/productcatalogservice/genproto/demo.pb.go
  constant _ (line 32) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 34) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type CartItem (line 37) | type CartItem struct
    method Reset (line 46) | func (x *CartItem) Reset() {
    method String (line 55) | func (x *CartItem) String() string {
    method ProtoMessage (line 59) | func (*CartItem) ProtoMessage() {}
    method ProtoReflect (line 61) | func (x *CartItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 74) | func (*CartItem) Descriptor() ([]byte, []int) {
    method GetProductId (line 78) | func (x *CartItem) GetProductId() string {
    method GetQuantity (line 85) | func (x *CartItem) GetQuantity() int32 {
  type AddItemRequest (line 92) | type AddItemRequest struct
    method Reset (line 101) | func (x *AddItemRequest) Reset() {
    method String (line 110) | func (x *AddItemRequest) String() string {
    method ProtoMessage (line 114) | func (*AddItemRequest) ProtoMessage() {}
    method ProtoReflect (line 116) | func (x *AddItemRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 129) | func (*AddItemRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 133) | func (x *AddItemRequest) GetUserId() string {
    method GetItem (line 140) | func (x *AddItemRequest) GetItem() *CartItem {
  type EmptyCartRequest (line 147) | type EmptyCartRequest struct
    method Reset (line 155) | func (x *EmptyCartRequest) Reset() {
    method String (line 164) | func (x *EmptyCartRequest) String() string {
    method ProtoMessage (line 168) | func (*EmptyCartRequest) ProtoMessage() {}
    method ProtoReflect (line 170) | func (x *EmptyCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 183) | func (*EmptyCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 187) | func (x *EmptyCartRequest) GetUserId() string {
  type GetCartRequest (line 194) | type GetCartRequest struct
    method Reset (line 202) | func (x *GetCartRequest) Reset() {
    method String (line 211) | func (x *GetCartRequest) String() string {
    method ProtoMessage (line 215) | func (*GetCartRequest) ProtoMessage() {}
    method ProtoReflect (line 217) | func (x *GetCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 230) | func (*GetCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 234) | func (x *GetCartRequest) GetUserId() string {
  type Cart (line 241) | type Cart struct
    method Reset (line 250) | func (x *Cart) Reset() {
    method String (line 259) | func (x *Cart) String() string {
    method ProtoMessage (line 263) | func (*Cart) ProtoMessage() {}
    method ProtoReflect (line 265) | func (x *Cart) ProtoReflect() protoreflect.Message {
    method Descriptor (line 278) | func (*Cart) Descriptor() ([]byte, []int) {
    method GetUserId (line 282) | func (x *Cart) GetUserId() string {
    method GetItems (line 289) | func (x *Cart) GetItems() []*CartItem {
  type Empty (line 296) | type Empty struct
    method Reset (line 302) | func (x *Empty) Reset() {
    method String (line 311) | func (x *Empty) String() string {
    method ProtoMessage (line 315) | func (*Empty) ProtoMessage() {}
    method ProtoReflect (line 317) | func (x *Empty) ProtoReflect() protoreflect.Message {
    method Descriptor (line 330) | func (*Empty) Descriptor() ([]byte, []int) {
  type ListRecommendationsRequest (line 334) | type ListRecommendationsRequest struct
    method Reset (line 343) | func (x *ListRecommendationsRequest) Reset() {
    method String (line 352) | func (x *ListRecommendationsRequest) String() string {
    method ProtoMessage (line 356) | func (*ListRecommendationsRequest) ProtoMessage() {}
    method ProtoReflect (line 358) | func (x *ListRecommendationsRequest) ProtoReflect() protoreflect.Messa...
    method Descriptor (line 371) | func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 375) | func (x *ListRecommendationsRequest) GetUserId() string {
    method GetProductIds (line 382) | func (x *ListRecommendationsRequest) GetProductIds() []string {
  type ListRecommendationsResponse (line 389) | type ListRecommendationsResponse struct
    method Reset (line 397) | func (x *ListRecommendationsResponse) Reset() {
    method String (line 406) | func (x *ListRecommendationsResponse) String() string {
    method ProtoMessage (line 410) | func (*ListRecommendationsResponse) ProtoMessage() {}
    method ProtoReflect (line 412) | func (x *ListRecommendationsResponse) ProtoReflect() protoreflect.Mess...
    method Descriptor (line 425) | func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) {
    method GetProductIds (line 429) | func (x *ListRecommendationsResponse) GetProductIds() []string {
  type Product (line 436) | type Product struct
    method Reset (line 451) | func (x *Product) Reset() {
    method String (line 460) | func (x *Product) String() string {
    method ProtoMessage (line 464) | func (*Product) ProtoMessage() {}
    method ProtoReflect (line 466) | func (x *Product) ProtoReflect() protoreflect.Message {
    method Descriptor (line 479) | func (*Product) Descriptor() ([]byte, []int) {
    method GetId (line 483) | func (x *Product) GetId() string {
    method GetName (line 490) | func (x *Product) GetName() string {
    method GetDescription (line 497) | func (x *Product) GetDescription() string {
    method GetPicture (line 504) | func (x *Product) GetPicture() string {
    method GetPriceUsd (line 511) | func (x *Product) GetPriceUsd() *Money {
    method GetCategories (line 518) | func (x *Product) GetCategories() []string {
  type ListProductsResponse (line 525) | type ListProductsResponse struct
    method Reset (line 533) | func (x *ListProductsResponse) Reset() {
    method String (line 542) | func (x *ListProductsResponse) String() string {
    method ProtoMessage (line 546) | func (*ListProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 548) | func (x *ListProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 561) | func (*ListProductsResponse) Descriptor() ([]byte, []int) {
    method GetProducts (line 565) | func (x *ListProductsResponse) GetProducts() []*Product {
  type GetProductRequest (line 572) | type GetProductRequest struct
    method Reset (line 580) | func (x *GetProductRequest) Reset() {
    method String (line 589) | func (x *GetProductRequest) String() string {
    method ProtoMessage (line 593) | func (*GetProductRequest) ProtoMessage() {}
    method ProtoReflect (line 595) | func (x *GetProductRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 608) | func (*GetProductRequest) Descriptor() ([]byte, []int) {
    method GetId (line 612) | func (x *GetProductRequest) GetId() string {
  type SearchProductsRequest (line 619) | type SearchProductsRequest struct
    method Reset (line 627) | func (x *SearchProductsRequest) Reset() {
    method String (line 636) | func (x *SearchProductsRequest) String() string {
    method ProtoMessage (line 640) | func (*SearchProductsRequest) ProtoMessage() {}
    method ProtoReflect (line 642) | func (x *SearchProductsRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 655) | func (*SearchProductsRequest) Descriptor() ([]byte, []int) {
    method GetQuery (line 659) | func (x *SearchProductsRequest) GetQuery() string {
  type SearchProductsResponse (line 666) | type SearchProductsResponse struct
    method Reset (line 674) | func (x *SearchProductsResponse) Reset() {
    method String (line 683) | func (x *SearchProductsResponse) String() string {
    method ProtoMessage (line 687) | func (*SearchProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 689) | func (x *SearchProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 702) | func (*SearchProductsResponse) Descriptor() ([]byte, []int) {
    method GetResults (line 706) | func (x *SearchProductsResponse) GetResults() []*Product {
  type GetQuoteRequest (line 713) | type GetQuoteRequest struct
    method Reset (line 722) | func (x *GetQuoteRequest) Reset() {
    method String (line 731) | func (x *GetQuoteRequest) String() string {
    method ProtoMessage (line 735) | func (*GetQuoteRequest) ProtoMessage() {}
    method ProtoReflect (line 737) | func (x *GetQuoteRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 750) | func (*GetQuoteRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 754) | func (x *GetQuoteRequest) GetAddress() *Address {
    method GetItems (line 761) | func (x *GetQuoteRequest) GetItems() []*CartItem {
  type GetQuoteResponse (line 768) | type GetQuoteResponse struct
    method Reset (line 776) | func (x *GetQuoteResponse) Reset() {
    method String (line 785) | func (x *GetQuoteResponse) String() string {
    method ProtoMessage (line 789) | func (*GetQuoteResponse) ProtoMessage() {}
    method ProtoReflect (line 791) | func (x *GetQuoteResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 804) | func (*GetQuoteResponse) Descriptor() ([]byte, []int) {
    method GetCostUsd (line 808) | func (x *GetQuoteResponse) GetCostUsd() *Money {
  type ShipOrderRequest (line 815) | type ShipOrderRequest struct
    method Reset (line 824) | func (x *ShipOrderRequest) Reset() {
    method String (line 833) | func (x *ShipOrderRequest) String() string {
    method ProtoMessage (line 837) | func (*ShipOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 839) | func (x *ShipOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 852) | func (*ShipOrderRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 856) | func (x *ShipOrderRequest) GetAddress() *Address {
    method GetItems (line 863) | func (x *ShipOrderRequest) GetItems() []*CartItem {
  type ShipOrderResponse (line 870) | type ShipOrderResponse struct
    method Reset (line 878) | func (x *ShipOrderResponse) Reset() {
    method String (line 887) | func (x *ShipOrderResponse) String() string {
    method ProtoMessage (line 891) | func (*ShipOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 893) | func (x *ShipOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 906) | func (*ShipOrderResponse) Descriptor() ([]byte, []int) {
    method GetTrackingId (line 910) | func (x *ShipOrderResponse) GetTrackingId() string {
  type Address (line 917) | type Address struct
    method Reset (line 929) | func (x *Address) Reset() {
    method String (line 938) | func (x *Address) String() string {
    method ProtoMessage (line 942) | func (*Address) ProtoMessage() {}
    method ProtoReflect (line 944) | func (x *Address) ProtoReflect() protoreflect.Message {
    method Descriptor (line 957) | func (*Address) Descriptor() ([]byte, []int) {
    method GetStreetAddress (line 961) | func (x *Address) GetStreetAddress() string {
    method GetCity (line 968) | func (x *Address) GetCity() string {
    method GetState (line 975) | func (x *Address) GetState() string {
    method GetCountry (line 982) | func (x *Address) GetCountry() string {
    method GetZipCode (line 989) | func (x *Address) GetZipCode() int32 {
  type Money (line 997) | type Money struct
    method Reset (line 1016) | func (x *Money) Reset() {
    method String (line 1025) | func (x *Money) String() string {
    method ProtoMessage (line 1029) | func (*Money) ProtoMessage() {}
    method ProtoReflect (line 1031) | func (x *Money) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1044) | func (*Money) Descriptor() ([]byte, []int) {
    method GetCurrencyCode (line 1048) | func (x *Money) GetCurrencyCode() string {
    method GetUnits (line 1055) | func (x *Money) GetUnits() int64 {
    method GetNanos (line 1062) | func (x *Money) GetNanos() int32 {
  type GetSupportedCurrenciesResponse (line 1069) | type GetSupportedCurrenciesResponse struct
    method Reset (line 1078) | func (x *GetSupportedCurrenciesResponse) Reset() {
    method String (line 1087) | func (x *GetSupportedCurrenciesResponse) String() string {
    method ProtoMessage (line 1091) | func (*GetSupportedCurrenciesResponse) ProtoMessage() {}
    method ProtoReflect (line 1093) | func (x *GetSupportedCurrenciesResponse) ProtoReflect() protoreflect.M...
    method Descriptor (line 1106) | func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) {
    method GetCurrencyCodes (line 1110) | func (x *GetSupportedCurrenciesResponse) GetCurrencyCodes() []string {
  type CurrencyConversionRequest (line 1117) | type CurrencyConversionRequest struct
    method Reset (line 1127) | func (x *CurrencyConversionRequest) Reset() {
    method String (line 1136) | func (x *CurrencyConversionRequest) String() string {
    method ProtoMessage (line 1140) | func (*CurrencyConversionRequest) ProtoMessage() {}
    method ProtoReflect (line 1142) | func (x *CurrencyConversionRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1155) | func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) {
    method GetFrom (line 1159) | func (x *CurrencyConversionRequest) GetFrom() *Money {
    method GetToCode (line 1166) | func (x *CurrencyConversionRequest) GetToCode() string {
  type CreditCardInfo (line 1173) | type CreditCardInfo struct
    method Reset (line 1184) | func (x *CreditCardInfo) Reset() {
    method String (line 1193) | func (x *CreditCardInfo) String() string {
    method ProtoMessage (line 1197) | func (*CreditCardInfo) ProtoMessage() {}
    method ProtoReflect (line 1199) | func (x *CreditCardInfo) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1212) | func (*CreditCardInfo) Descriptor() ([]byte, []int) {
    method GetCreditCardNumber (line 1216) | func (x *CreditCardInfo) GetCreditCardNumber() string {
    method GetCreditCardCvv (line 1223) | func (x *CreditCardInfo) GetCreditCardCvv() int32 {
    method GetCreditCardExpirationYear (line 1230) | func (x *CreditCardInfo) GetCreditCardExpirationYear() int32 {
    method GetCreditCardExpirationMonth (line 1237) | func (x *CreditCardInfo) GetCreditCardExpirationMonth() int32 {
  type ChargeRequest (line 1244) | type ChargeRequest struct
    method Reset (line 1253) | func (x *ChargeRequest) Reset() {
    method String (line 1262) | func (x *ChargeRequest) String() string {
    method ProtoMessage (line 1266) | func (*ChargeRequest) ProtoMessage() {}
    method ProtoReflect (line 1268) | func (x *ChargeRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1281) | func (*ChargeRequest) Descriptor() ([]byte, []int) {
    method GetAmount (line 1285) | func (x *ChargeRequest) GetAmount() *Money {
    method GetCreditCard (line 1292) | func (x *ChargeRequest) GetCreditCard() *CreditCardInfo {
  type ChargeResponse (line 1299) | type ChargeResponse struct
    method Reset (line 1307) | func (x *ChargeResponse) Reset() {
    method String (line 1316) | func (x *ChargeResponse) String() string {
    method ProtoMessage (line 1320) | func (*ChargeResponse) ProtoMessage() {}
    method ProtoReflect (line 1322) | func (x *ChargeResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1335) | func (*ChargeResponse) Descriptor() ([]byte, []int) {
    method GetTransactionId (line 1339) | func (x *ChargeResponse) GetTransactionId() string {
  type OrderItem (line 1346) | type OrderItem struct
    method Reset (line 1355) | func (x *OrderItem) Reset() {
    method String (line 1364) | func (x *OrderItem) String() string {
    method ProtoMessage (line 1368) | func (*OrderItem) ProtoMessage() {}
    method ProtoReflect (line 1370) | func (x *OrderItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1383) | func (*OrderItem) Descriptor() ([]byte, []int) {
    method GetItem (line 1387) | func (x *OrderItem) GetItem() *CartItem {
    method GetCost (line 1394) | func (x *OrderItem) GetCost() *Money {
  type OrderResult (line 1401) | type OrderResult struct
    method Reset (line 1413) | func (x *OrderResult) Reset() {
    method String (line 1422) | func (x *OrderResult) String() string {
    method ProtoMessage (line 1426) | func (*OrderResult) ProtoMessage() {}
    method ProtoReflect (line 1428) | func (x *OrderResult) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1441) | func (*OrderResult) Descriptor() ([]byte, []int) {
    method GetOrderId (line 1445) | func (x *OrderResult) GetOrderId() string {
    method GetShippingTrackingId (line 1452) | func (x *OrderResult) GetShippingTrackingId() string {
    method GetShippingCost (line 1459) | func (x *OrderResult) GetShippingCost() *Money {
    method GetShippingAddress (line 1466) | func (x *OrderResult) GetShippingAddress() *Address {
    method GetItems (line 1473) | func (x *OrderResult) GetItems() []*OrderItem {
  type SendOrderConfirmationRequest (line 1480) | type SendOrderConfirmationRequest struct
    method Reset (line 1489) | func (x *SendOrderConfirmationRequest) Reset() {
    method String (line 1498) | func (x *SendOrderConfirmationRequest) String() string {
    method ProtoMessage (line 1502) | func (*SendOrderConfirmationRequest) ProtoMessage() {}
    method ProtoReflect (line 1504) | func (x *SendOrderConfirmationRequest) ProtoReflect() protoreflect.Mes...
    method Descriptor (line 1517) | func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) {
    method GetEmail (line 1521) | func (x *SendOrderConfirmationRequest) GetEmail() string {
    method GetOrder (line 1528) | func (x *SendOrderConfirmationRequest) GetOrder() *OrderResult {
  type PlaceOrderRequest (line 1535) | type PlaceOrderRequest struct
    method Reset (line 1547) | func (x *PlaceOrderRequest) Reset() {
    method String (line 1556) | func (x *PlaceOrderRequest) String() string {
    method ProtoMessage (line 1560) | func (*PlaceOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 1562) | func (x *PlaceOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1575) | func (*PlaceOrderRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 1579) | func (x *PlaceOrderRequest) GetUserId() string {
    method GetUserCurrency (line 1586) | func (x *PlaceOrderRequest) GetUserCurrency() string {
    method GetAddress (line 1593) | func (x *PlaceOrderRequest) GetAddress() *Address {
    method GetEmail (line 1600) | func (x *PlaceOrderRequest) GetEmail() string {
    method GetCreditCard (line 1607) | func (x *PlaceOrderRequest) GetCreditCard() *CreditCardInfo {
  type PlaceOrderResponse (line 1614) | type PlaceOrderResponse struct
    method Reset (line 1622) | func (x *PlaceOrderResponse) Reset() {
    method String (line 1631) | func (x *PlaceOrderResponse) String() string {
    method ProtoMessage (line 1635) | func (*PlaceOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 1637) | func (x *PlaceOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1650) | func (*PlaceOrderResponse) Descriptor() ([]byte, []int) {
    method GetOrder (line 1654) | func (x *PlaceOrderResponse) GetOrder() *OrderResult {
  type AdRequest (line 1661) | type AdRequest struct
    method Reset (line 1670) | func (x *AdRequest) Reset() {
    method String (line 1679) | func (x *AdRequest) String() string {
    method ProtoMessage (line 1683) | func (*AdRequest) ProtoMessage() {}
    method ProtoReflect (line 1685) | func (x *AdRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1698) | func (*AdRequest) Descriptor() ([]byte, []int) {
    method GetContextKeys (line 1702) | func (x *AdRequest) GetContextKeys() []string {
  type AdResponse (line 1709) | type AdResponse struct
    method Reset (line 1717) | func (x *AdResponse) Reset() {
    method String (line 1726) | func (x *AdResponse) String() string {
    method ProtoMessage (line 1730) | func (*AdResponse) ProtoMessage() {}
    method ProtoReflect (line 1732) | func (x *AdResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1745) | func (*AdResponse) Descriptor() ([]byte, []int) {
    method GetAds (line 1749) | func (x *AdResponse) GetAds() []*Ad {
  type Ad (line 1756) | type Ad struct
    method Reset (line 1767) | func (x *Ad) Reset() {
    method String (line 1776) | func (x *Ad) String() string {
    method ProtoMessage (line 1780) | func (*Ad) ProtoMessage() {}
    method ProtoReflect (line 1782) | func (x *Ad) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1795) | func (*Ad) Descriptor() ([]byte, []int) {
    method GetRedirectUrl (line 1799) | func (x *Ad) GetRedirectUrl() string {
    method GetText (line 1806) | func (x *Ad) GetText() string {
  function file_demo_proto_rawDescGZIP (line 2098) | func file_demo_proto_rawDescGZIP() []byte {
  function init (line 2201) | func init() { file_demo_proto_init() }
  function file_demo_proto_init (line 2202) | func file_demo_proto_init() {

FILE: src/productcatalogservice/genproto/demo_grpc.pb.go
  constant _ (line 33) | _ = grpc.SupportPackageIsVersion9
  constant CartService_AddItem_FullMethodName (line 36) | CartService_AddItem_FullMethodName   = "/hipstershop.CartService/AddItem"
  constant CartService_GetCart_FullMethodName (line 37) | CartService_GetCart_FullMethodName   = "/hipstershop.CartService/GetCart"
  constant CartService_EmptyCart_FullMethodName (line 38) | CartService_EmptyCart_FullMethodName = "/hipstershop.CartService/EmptyCart"
  type CartServiceClient (line 44) | type CartServiceClient interface
  type cartServiceClient (line 50) | type cartServiceClient struct
    method AddItem (line 58) | func (c *cartServiceClient) AddItem(ctx context.Context, in *AddItemRe...
    method GetCart (line 68) | func (c *cartServiceClient) GetCart(ctx context.Context, in *GetCartRe...
    method EmptyCart (line 78) | func (c *cartServiceClient) EmptyCart(ctx context.Context, in *EmptyCa...
  function NewCartServiceClient (line 54) | func NewCartServiceClient(cc grpc.ClientConnInterface) CartServiceClient {
  type CartServiceServer (line 91) | type CartServiceServer interface
  type UnimplementedCartServiceServer (line 103) | type UnimplementedCartServiceServer struct
    method AddItem (line 105) | func (UnimplementedCartServiceServer) AddItem(context.Context, *AddIte...
    method GetCart (line 108) | func (UnimplementedCartServiceServer) GetCart(context.Context, *GetCar...
    method EmptyCart (line 111) | func (UnimplementedCartServiceServer) EmptyCart(context.Context, *Empt...
    method mustEmbedUnimplementedCartServiceServer (line 114) | func (UnimplementedCartServiceServer) mustEmbedUnimplementedCartServic...
    method testEmbeddedByValue (line 115) | func (UnimplementedCartServiceServer) testEmbeddedByValue()           ...
  type UnsafeCartServiceServer (line 120) | type UnsafeCartServiceServer interface
  function RegisterCartServiceServer (line 124) | func RegisterCartServiceServer(s grpc.ServiceRegistrar, srv CartServiceS...
  function _CartService_AddItem_Handler (line 135) | func _CartService_AddItem_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_GetCart_Handler (line 153) | func _CartService_GetCart_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_EmptyCart_Handler (line 171) | func _CartService_EmptyCart_Handler(srv interface{}, ctx context.Context...
  constant RecommendationService_ListRecommendations_FullMethodName (line 214) | RecommendationService_ListRecommendations_FullMethodName = "/hipstershop...
  type RecommendationServiceClient (line 220) | type RecommendationServiceClient interface
  type recommendationServiceClient (line 224) | type recommendationServiceClient struct
    method ListRecommendations (line 232) | func (c *recommendationServiceClient) ListRecommendations(ctx context....
  function NewRecommendationServiceClient (line 228) | func NewRecommendationServiceClient(cc grpc.ClientConnInterface) Recomme...
  type RecommendationServiceServer (line 245) | type RecommendationServiceServer interface
  type UnimplementedRecommendationServiceServer (line 255) | type UnimplementedRecommendationServiceServer struct
    method ListRecommendations (line 257) | func (UnimplementedRecommendationServiceServer) ListRecommendations(co...
    method mustEmbedUnimplementedRecommendationServiceServer (line 260) | func (UnimplementedRecommendationServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 261) | func (UnimplementedRecommendationServiceServer) testEmbeddedByValue() ...
  type UnsafeRecommendationServiceServer (line 266) | type UnsafeRecommendationServiceServer interface
  function RegisterRecommendationServiceServer (line 270) | func RegisterRecommendationServiceServer(s grpc.ServiceRegistrar, srv Re...
  function _RecommendationService_ListRecommendations_Handler (line 281) | func _RecommendationService_ListRecommendations_Handler(srv interface{},...
  constant ProductCatalogService_ListProducts_FullMethodName (line 316) | ProductCatalogService_ListProducts_FullMethodName   = "/hipstershop.Prod...
  constant ProductCatalogService_GetProduct_FullMethodName (line 317) | ProductCatalogService_GetProduct_FullMethodName     = "/hipstershop.Prod...
  constant ProductCatalogService_SearchProducts_FullMethodName (line 318) | ProductCatalogService_SearchProducts_FullMethodName = "/hipstershop.Prod...
  type ProductCatalogServiceClient (line 324) | type ProductCatalogServiceClient interface
  type productCatalogServiceClient (line 330) | type productCatalogServiceClient struct
    method ListProducts (line 338) | func (c *productCatalogServiceClient) ListProducts(ctx context.Context...
    method GetProduct (line 348) | func (c *productCatalogServiceClient) GetProduct(ctx context.Context, ...
    method SearchProducts (line 358) | func (c *productCatalogServiceClient) SearchProducts(ctx context.Conte...
  function NewProductCatalogServiceClient (line 334) | func NewProductCatalogServiceClient(cc grpc.ClientConnInterface) Product...
  type ProductCatalogServiceServer (line 371) | type ProductCatalogServiceServer interface
  type UnimplementedProductCatalogServiceServer (line 383) | type UnimplementedProductCatalogServiceServer struct
    method ListProducts (line 385) | func (UnimplementedProductCatalogServiceServer) ListProducts(context.C...
    method GetProduct (line 388) | func (UnimplementedProductCatalogServiceServer) GetProduct(context.Con...
    method SearchProducts (line 391) | func (UnimplementedProductCatalogServiceServer) SearchProducts(context...
    method mustEmbedUnimplementedProductCatalogServiceServer (line 394) | func (UnimplementedProductCatalogServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 395) | func (UnimplementedProductCatalogServiceServer) testEmbeddedByValue() ...
  type UnsafeProductCatalogServiceServer (line 400) | type UnsafeProductCatalogServiceServer interface
  function RegisterProductCatalogServiceServer (line 404) | func RegisterProductCatalogServiceServer(s grpc.ServiceRegistrar, srv Pr...
  function _ProductCatalogService_ListProducts_Handler (line 415) | func _ProductCatalogService_ListProducts_Handler(srv interface{}, ctx co...
  function _ProductCatalogService_GetProduct_Handler (line 433) | func _ProductCatalogService_GetProduct_Handler(srv interface{}, ctx cont...
  function _ProductCatalogService_SearchProducts_Handler (line 451) | func _ProductCatalogService_SearchProducts_Handler(srv interface{}, ctx ...
  constant ShippingService_GetQuote_FullMethodName (line 494) | ShippingService_GetQuote_FullMethodName  = "/hipstershop.ShippingService...
  constant ShippingService_ShipOrder_FullMethodName (line 495) | ShippingService_ShipOrder_FullMethodName = "/hipstershop.ShippingService...
  type ShippingServiceClient (line 501) | type ShippingServiceClient interface
  type shippingServiceClient (line 506) | type shippingServiceClient struct
    method GetQuote (line 514) | func (c *shippingServiceClient) GetQuote(ctx context.Context, in *GetQ...
    method ShipOrder (line 524) | func (c *shippingServiceClient) ShipOrder(ctx context.Context, in *Shi...
  function NewShippingServiceClient (line 510) | func NewShippingServiceClient(cc grpc.ClientConnInterface) ShippingServi...
  type ShippingServiceServer (line 537) | type ShippingServiceServer interface
  type UnimplementedShippingServiceServer (line 548) | type UnimplementedShippingServiceServer struct
    method GetQuote (line 550) | func (UnimplementedShippingServiceServer) GetQuote(context.Context, *G...
    method ShipOrder (line 553) | func (UnimplementedShippingServiceServer) ShipOrder(context.Context, *...
    method mustEmbedUnimplementedShippingServiceServer (line 556) | func (UnimplementedShippingServiceServer) mustEmbedUnimplementedShippi...
    method testEmbeddedByValue (line 557) | func (UnimplementedShippingServiceServer) testEmbeddedByValue()       ...
  type UnsafeShippingServiceServer (line 562) | type UnsafeShippingServiceServer interface
  function RegisterShippingServiceServer (line 566) | func RegisterShippingServiceServer(s grpc.ServiceRegistrar, srv Shipping...
  function _ShippingService_GetQuote_Handler (line 577) | func _ShippingService_GetQuote_Handler(srv interface{}, ctx context.Cont...
  function _ShippingService_ShipOrder_Handler (line 595) | func _ShippingService_ShipOrder_Handler(srv interface{}, ctx context.Con...
  constant CurrencyService_GetSupportedCurrencies_FullMethodName (line 634) | CurrencyService_GetSupportedCurrencies_FullMethodName = "/hipstershop.Cu...
  constant CurrencyService_Convert_FullMethodName (line 635) | CurrencyService_Convert_FullMethodName                = "/hipstershop.Cu...
  type CurrencyServiceClient (line 641) | type CurrencyServiceClient interface
  type currencyServiceClient (line 646) | type currencyServiceClient struct
    method GetSupportedCurrencies (line 654) | func (c *currencyServiceClient) GetSupportedCurrencies(ctx context.Con...
    method Convert (line 664) | func (c *currencyServiceClient) Convert(ctx context.Context, in *Curre...
  function NewCurrencyServiceClient (line 650) | func NewCurrencyServiceClient(cc grpc.ClientConnInterface) CurrencyServi...
  type CurrencyServiceServer (line 677) | type CurrencyServiceServer interface
  type UnimplementedCurrencyServiceServer (line 688) | type UnimplementedCurrencyServiceServer struct
    method GetSupportedCurrencies (line 690) | func (UnimplementedCurrencyServiceServer) GetSupportedCurrencies(conte...
    method Convert (line 693) | func (UnimplementedCurrencyServiceServer) Convert(context.Context, *Cu...
    method mustEmbedUnimplementedCurrencyServiceServer (line 696) | func (UnimplementedCurrencyServiceServer) mustEmbedUnimplementedCurren...
    method testEmbeddedByValue (line 697) | func (UnimplementedCurrencyServiceServer) testEmbeddedByValue()       ...
  type UnsafeCurrencyServiceServer (line 702) | type UnsafeCurrencyServiceServer interface
  function RegisterCurrencyServiceServer (line 706) | func RegisterCurrencyServiceServer(s grpc.ServiceRegistrar, srv Currency...
  function _CurrencyService_GetSupportedCurrencies_Handler (line 717) | func _CurrencyService_GetSupportedCurrencies_Handler(srv interface{}, ct...
  function _CurrencyService_Convert_Handler (line 735) | func _CurrencyService_Convert_Handler(srv interface{}, ctx context.Conte...
  constant PaymentService_Charge_FullMethodName (line 774) | PaymentService_Charge_FullMethodName = "/hipstershop.PaymentService/Charge"
  type PaymentServiceClient (line 780) | type PaymentServiceClient interface
  type paymentServiceClient (line 784) | type paymentServiceClient struct
    method Charge (line 792) | func (c *paymentServiceClient) Charge(ctx context.Context, in *ChargeR...
  function NewPaymentServiceClient (line 788) | func NewPaymentServiceClient(cc grpc.ClientConnInterface) PaymentService...
  type PaymentServiceServer (line 805) | type PaymentServiceServer interface
  type UnimplementedPaymentServiceServer (line 815) | type UnimplementedPaymentServiceServer struct
    method Charge (line 817) | func (UnimplementedPaymentServiceServer) Charge(context.Context, *Char...
    method mustEmbedUnimplementedPaymentServiceServer (line 820) | func (UnimplementedPaymentServiceServer) mustEmbedUnimplementedPayment...
    method testEmbeddedByValue (line 821) | func (UnimplementedPaymentServiceServer) testEmbeddedByValue()        ...
  type UnsafePaymentServiceServer (line 826) | type UnsafePaymentServiceServer interface
  function RegisterPaymentServiceServer (line 830) | func RegisterPaymentServiceServer(s grpc.ServiceRegistrar, srv PaymentSe...
  function _PaymentService_Charge_Handler (line 841) | func _PaymentService_Charge_Handler(srv interface{}, ctx context.Context...
  constant EmailService_SendOrderConfirmation_FullMethodName (line 876) | EmailService_SendOrderConfirmation_FullMethodName = "/hipstershop.EmailS...
  type EmailServiceClient (line 882) | type EmailServiceClient interface
  type emailServiceClient (line 886) | type emailServiceClient struct
    method SendOrderConfirmation (line 894) | func (c *emailServiceClient) SendOrderConfirmation(ctx context.Context...
  function NewEmailServiceClient (line 890) | func NewEmailServiceClient(cc grpc.ClientConnInterface) EmailServiceClie...
  type EmailServiceServer (line 907) | type EmailServiceServer interface
  type UnimplementedEmailServiceServer (line 917) | type UnimplementedEmailServiceServer struct
    method SendOrderConfirmation (line 919) | func (UnimplementedEmailServiceServer) SendOrderConfirmation(context.C...
    method mustEmbedUnimplementedEmailServiceServer (line 922) | func (UnimplementedEmailServiceServer) mustEmbedUnimplementedEmailServ...
    method testEmbeddedByValue (line 923) | func (UnimplementedEmailServiceServer) testEmbeddedByValue()          ...
  type UnsafeEmailServiceServer (line 928) | type UnsafeEmailServiceServer interface
  function RegisterEmailServiceServer (line 932) | func RegisterEmailServiceServer(s grpc.ServiceRegistrar, srv EmailServic...
  function _EmailService_SendOrderConfirmation_Handler (line 943) | func _EmailService_SendOrderConfirmation_Handler(srv interface{}, ctx co...
  constant CheckoutService_PlaceOrder_FullMethodName (line 978) | CheckoutService_PlaceOrder_FullMethodName = "/hipstershop.CheckoutServic...
  type CheckoutServiceClient (line 984) | type CheckoutServiceClient interface
  type checkoutServiceClient (line 988) | type checkoutServiceClient struct
    method PlaceOrder (line 996) | func (c *checkoutServiceClient) PlaceOrder(ctx context.Context, in *Pl...
  function NewCheckoutServiceClient (line 992) | func NewCheckoutServiceClient(cc grpc.ClientConnInterface) CheckoutServi...
  type CheckoutServiceServer (line 1009) | type CheckoutServiceServer interface
  type UnimplementedCheckoutServiceServer (line 1019) | type UnimplementedCheckoutServiceServer struct
    method PlaceOrder (line 1021) | func (UnimplementedCheckoutServiceServer) PlaceOrder(context.Context, ...
    method mustEmbedUnimplementedCheckoutServiceServer (line 1024) | func (UnimplementedCheckoutServiceServer) mustEmbedUnimplementedChecko...
    method testEmbeddedByValue (line 1025) | func (UnimplementedCheckoutServiceServer) testEmbeddedByValue()       ...
  type UnsafeCheckoutServiceServer (line 1030) | type UnsafeCheckoutServiceServer interface
  function RegisterCheckoutServiceServer (line 1034) | func RegisterCheckoutServiceServer(s grpc.ServiceRegistrar, srv Checkout...
  function _CheckoutService_PlaceOrder_Handler (line 1045) | func _CheckoutService_PlaceOrder_Handler(srv interface{}, ctx context.Co...
  constant AdService_GetAds_FullMethodName (line 1080) | AdService_GetAds_FullMethodName = "/hipstershop.AdService/GetAds"
  type AdServiceClient (line 1086) | type AdServiceClient interface
  type adServiceClient (line 1090) | type adServiceClient struct
    method GetAds (line 1098) | func (c *adServiceClient) GetAds(ctx context.Context, in *AdRequest, o...
  function NewAdServiceClient (line 1094) | func NewAdServiceClient(cc grpc.ClientConnInterface) AdServiceClient {
  type AdServiceServer (line 1111) | type AdServiceServer interface
  type UnimplementedAdServiceServer (line 1121) | type UnimplementedAdServiceServer struct
    method GetAds (line 1123) | func (UnimplementedAdServiceServer) GetAds(context.Context, *AdRequest...
    method mustEmbedUnimplementedAdServiceServer (line 1126) | func (UnimplementedAdServiceServer) mustEmbedUnimplementedAdServiceSer...
    method testEmbeddedByValue (line 1127) | func (UnimplementedAdServiceServer) testEmbeddedByValue()             ...
  type UnsafeAdServiceServer (line 1132) | type UnsafeAdServiceServer interface
  function RegisterAdServiceServer (line 1136) | func RegisterAdServiceServer(s grpc.ServiceRegistrar, srv AdServiceServe...
  function _AdService_GetAds_Handler (line 1147) | func _AdService_GetAds_Handler(srv interface{}, ctx context.Context, dec...

FILE: src/productcatalogservice/product_catalog.go
  type productCatalog (line 28) | type productCatalog struct
    method Check (line 33) | func (p *productCatalog) Check(ctx context.Context, req *healthpb.Heal...
    method Watch (line 37) | func (p *productCatalog) Watch(req *healthpb.HealthCheckRequest, ws he...
    method ListProducts (line 41) | func (p *productCatalog) ListProducts(context.Context, *pb.Empty) (*pb...
    method GetProduct (line 47) | func (p *productCatalog) GetProduct(ctx context.Context, req *pb.GetPr...
    method SearchProducts (line 63) | func (p *productCatalog) SearchProducts(ctx context.Context, req *pb.S...
    method parseCatalog (line 77) | func (p *productCatalog) parseCatalog() []*pb.Product {

FILE: src/productcatalogservice/product_catalog_test.go
  function TestMain (line 31) | func TestMain(m *testing.M) {
  function TestGetProductExists (line 58) | func TestGetProductExists(t *testing.T) {
  function TestGetProductNotFound (line 70) | func TestGetProductNotFound(t *testing.T) {
  function TestListProducts (line 79) | func TestListProducts(t *testing.T) {
  function TestSearchProducts (line 91) | func TestSearchProducts(t *testing.T) {

FILE: src/productcatalogservice/server.go
  function init (line 54) | func init() {
  function main (line 68) | func main() {
  function run (line 123) | func run(port string) string {
  function initStats (line 151) | func initStats() {
  function initTracing (line 155) | func initTracing() error {
  function initProfiling (line 179) | func initProfiling(service, version string) {
  function mustMapEnv (line 199) | func mustMapEnv(target *string, envKey string) {
  function mustConnGRPC (line 207) | func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr stri...

FILE: src/recommendationservice/demo_pb2_grpc.py
  class CartServiceStub (line 24) | class CartServiceStub(object):
    method __init__ (line 29) | def __init__(self, channel):
  class CartServiceServicer (line 52) | class CartServiceServicer(object):
    method AddItem (line 57) | def AddItem(self, request, context):
    method GetCart (line 63) | def GetCart(self, request, context):
    method EmptyCart (line 69) | def EmptyCart(self, request, context):
  function add_CartServiceServicer_to_server (line 76) | def add_CartServiceServicer_to_server(servicer, server):
  class CartService (line 100) | class CartService(object):
    method AddItem (line 106) | def AddItem(request,
    method GetCart (line 123) | def GetCart(request,
    method EmptyCart (line 140) | def EmptyCart(request,
  class RecommendationServiceStub (line 157) | class RecommendationServiceStub(object):
    method __init__ (line 162) | def __init__(self, channel):
  class RecommendationServiceServicer (line 175) | class RecommendationServiceServicer(object):
    method ListRecommendations (line 180) | def ListRecommendations(self, request, context):
  function add_RecommendationServiceServicer_to_server (line 187) | def add_RecommendationServiceServicer_to_server(servicer, server):
  class RecommendationService (line 201) | class RecommendationService(object):
    method ListRecommendations (line 207) | def ListRecommendations(request,
  class ProductCatalogServiceStub (line 224) | class ProductCatalogServiceStub(object):
    method __init__ (line 229) | def __init__(self, channel):
  class ProductCatalogServiceServicer (line 252) | class ProductCatalogServiceServicer(object):
    method ListProducts (line 257) | def ListProducts(self, request, context):
    method GetProduct (line 263) | def GetProduct(self, request, context):
    method SearchProducts (line 269) | def SearchProducts(self, request, context):
  function add_ProductCatalogServiceServicer_to_server (line 276) | def add_ProductCatalogServiceServicer_to_server(servicer, server):
  class ProductCatalogService (line 300) | class ProductCatalogService(object):
    method ListProducts (line 306) | def ListProducts(request,
    method GetProduct (line 323) | def GetProduct(request,
    method SearchProducts (line 340) | def SearchProducts(request,
  class ShippingServiceStub (line 357) | class ShippingServiceStub(object):
    method __init__ (line 362) | def __init__(self, channel):
  class ShippingServiceServicer (line 380) | class ShippingServiceServicer(object):
    method GetQuote (line 385) | def GetQuote(self, request, context):
    method ShipOrder (line 391) | def ShipOrder(self, request, context):
  function add_ShippingServiceServicer_to_server (line 398) | def add_ShippingServiceServicer_to_server(servicer, server):
  class ShippingService (line 417) | class ShippingService(object):
    method GetQuote (line 423) | def GetQuote(request,
    method ShipOrder (line 440) | def ShipOrder(request,
  class CurrencyServiceStub (line 457) | class CurrencyServiceStub(object):
    method __init__ (line 462) | def __init__(self, channel):
  class CurrencyServiceServicer (line 480) | class CurrencyServiceServicer(object):
    method GetSupportedCurrencies (line 485) | def GetSupportedCurrencies(self, request, context):
    method Convert (line 491) | def Convert(self, request, context):
  function add_CurrencyServiceServicer_to_server (line 498) | def add_CurrencyServiceServicer_to_server(servicer, server):
  class CurrencyService (line 517) | class CurrencyService(object):
    method GetSupportedCurrencies (line 523) | def GetSupportedCurrencies(request,
    method Convert (line 540) | def Convert(request,
  class PaymentServiceStub (line 557) | class PaymentServiceStub(object):
    method __init__ (line 562) | def __init__(self, channel):
  class PaymentServiceServicer (line 575) | class PaymentServiceServicer(object):
    method Charge (line 580) | def Charge(self, request, context):
  function add_PaymentServiceServicer_to_server (line 587) | def add_PaymentServiceServicer_to_server(servicer, server):
  class PaymentService (line 601) | class PaymentService(object):
    method Charge (line 607) | def Charge(request,
  class EmailServiceStub (line 624) | class EmailServiceStub(object):
    method __init__ (line 629) | def __init__(self, channel):
  class EmailServiceServicer (line 642) | class EmailServiceServicer(object):
    method SendOrderConfirmation (line 647) | def SendOrderConfirmation(self, request, context):
  function add_EmailServiceServicer_to_server (line 654) | def add_EmailServiceServicer_to_server(servicer, server):
  class EmailService (line 668) | class EmailService(object):
    method SendOrderConfirmation (line 674) | def SendOrderConfirmation(request,
  class CheckoutServiceStub (line 691) | class CheckoutServiceStub(object):
    method __init__ (line 696) | def __init__(self, channel):
  class CheckoutServiceServicer (line 709) | class CheckoutServiceServicer(object):
    method PlaceOrder (line 714) | def PlaceOrder(self, request, context):
  function add_CheckoutServiceServicer_to_server (line 721) | def add_CheckoutServiceServicer_to_server(servicer, server):
  class CheckoutService (line 735) | class CheckoutService(object):
    method PlaceOrder (line 741) | def PlaceOrder(request,
  class AdServiceStub (line 758) | class AdServiceStub(object):
    method __init__ (line 763) | def __init__(self, channel):
  class AdServiceServicer (line 776) | class AdServiceServicer(object):
    method GetAds (line 781) | def GetAds(self, request, context):
  function add_AdServiceServicer_to_server (line 788) | def add_AdServiceServicer_to_server(servicer, server):
  class AdService (line 802) | class AdService(object):
    method GetAds (line 808) | def GetAds(request,

FILE: src/recommendationservice/logger.py
  class CustomJsonFormatter (line 23) | class CustomJsonFormatter(jsonlogger.JsonFormatter):
    method add_fields (line 24) | def add_fields(self, log_record, record, message_dict):
  function getJSONLogger (line 33) | def getJSONLogger(name):

FILE: src/recommendationservice/recommendation_server.py
  function initStackdriverProfiling (line 43) | def initStackdriverProfiling():
  class RecommendationService (line 69) | class RecommendationService(demo_pb2_grpc.RecommendationServiceServicer):
    method ListRecommendations (line 70) | def ListRecommendations(self, request, context):
    method Check (line 88) | def Check(self, request, context):
    method Watch (line 92) | def Watch(self, request, context):

FILE: src/shippingservice/genproto/demo.pb.go
  constant _ (line 32) | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
  constant _ (line 34) | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
  type CartItem (line 37) | type CartItem struct
    method Reset (line 46) | func (x *CartItem) Reset() {
    method String (line 55) | func (x *CartItem) String() string {
    method ProtoMessage (line 59) | func (*CartItem) ProtoMessage() {}
    method ProtoReflect (line 61) | func (x *CartItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 74) | func (*CartItem) Descriptor() ([]byte, []int) {
    method GetProductId (line 78) | func (x *CartItem) GetProductId() string {
    method GetQuantity (line 85) | func (x *CartItem) GetQuantity() int32 {
  type AddItemRequest (line 92) | type AddItemRequest struct
    method Reset (line 101) | func (x *AddItemRequest) Reset() {
    method String (line 110) | func (x *AddItemRequest) String() string {
    method ProtoMessage (line 114) | func (*AddItemRequest) ProtoMessage() {}
    method ProtoReflect (line 116) | func (x *AddItemRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 129) | func (*AddItemRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 133) | func (x *AddItemRequest) GetUserId() string {
    method GetItem (line 140) | func (x *AddItemRequest) GetItem() *CartItem {
  type EmptyCartRequest (line 147) | type EmptyCartRequest struct
    method Reset (line 155) | func (x *EmptyCartRequest) Reset() {
    method String (line 164) | func (x *EmptyCartRequest) String() string {
    method ProtoMessage (line 168) | func (*EmptyCartRequest) ProtoMessage() {}
    method ProtoReflect (line 170) | func (x *EmptyCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 183) | func (*EmptyCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 187) | func (x *EmptyCartRequest) GetUserId() string {
  type GetCartRequest (line 194) | type GetCartRequest struct
    method Reset (line 202) | func (x *GetCartRequest) Reset() {
    method String (line 211) | func (x *GetCartRequest) String() string {
    method ProtoMessage (line 215) | func (*GetCartRequest) ProtoMessage() {}
    method ProtoReflect (line 217) | func (x *GetCartRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 230) | func (*GetCartRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 234) | func (x *GetCartRequest) GetUserId() string {
  type Cart (line 241) | type Cart struct
    method Reset (line 250) | func (x *Cart) Reset() {
    method String (line 259) | func (x *Cart) String() string {
    method ProtoMessage (line 263) | func (*Cart) ProtoMessage() {}
    method ProtoReflect (line 265) | func (x *Cart) ProtoReflect() protoreflect.Message {
    method Descriptor (line 278) | func (*Cart) Descriptor() ([]byte, []int) {
    method GetUserId (line 282) | func (x *Cart) GetUserId() string {
    method GetItems (line 289) | func (x *Cart) GetItems() []*CartItem {
  type Empty (line 296) | type Empty struct
    method Reset (line 302) | func (x *Empty) Reset() {
    method String (line 311) | func (x *Empty) String() string {
    method ProtoMessage (line 315) | func (*Empty) ProtoMessage() {}
    method ProtoReflect (line 317) | func (x *Empty) ProtoReflect() protoreflect.Message {
    method Descriptor (line 330) | func (*Empty) Descriptor() ([]byte, []int) {
  type ListRecommendationsRequest (line 334) | type ListRecommendationsRequest struct
    method Reset (line 343) | func (x *ListRecommendationsRequest) Reset() {
    method String (line 352) | func (x *ListRecommendationsRequest) String() string {
    method ProtoMessage (line 356) | func (*ListRecommendationsRequest) ProtoMessage() {}
    method ProtoReflect (line 358) | func (x *ListRecommendationsRequest) ProtoReflect() protoreflect.Messa...
    method Descriptor (line 371) | func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 375) | func (x *ListRecommendationsRequest) GetUserId() string {
    method GetProductIds (line 382) | func (x *ListRecommendationsRequest) GetProductIds() []string {
  type ListRecommendationsResponse (line 389) | type ListRecommendationsResponse struct
    method Reset (line 397) | func (x *ListRecommendationsResponse) Reset() {
    method String (line 406) | func (x *ListRecommendationsResponse) String() string {
    method ProtoMessage (line 410) | func (*ListRecommendationsResponse) ProtoMessage() {}
    method ProtoReflect (line 412) | func (x *ListRecommendationsResponse) ProtoReflect() protoreflect.Mess...
    method Descriptor (line 425) | func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) {
    method GetProductIds (line 429) | func (x *ListRecommendationsResponse) GetProductIds() []string {
  type Product (line 436) | type Product struct
    method Reset (line 451) | func (x *Product) Reset() {
    method String (line 460) | func (x *Product) String() string {
    method ProtoMessage (line 464) | func (*Product) ProtoMessage() {}
    method ProtoReflect (line 466) | func (x *Product) ProtoReflect() protoreflect.Message {
    method Descriptor (line 479) | func (*Product) Descriptor() ([]byte, []int) {
    method GetId (line 483) | func (x *Product) GetId() string {
    method GetName (line 490) | func (x *Product) GetName() string {
    method GetDescription (line 497) | func (x *Product) GetDescription() string {
    method GetPicture (line 504) | func (x *Product) GetPicture() string {
    method GetPriceUsd (line 511) | func (x *Product) GetPriceUsd() *Money {
    method GetCategories (line 518) | func (x *Product) GetCategories() []string {
  type ListProductsResponse (line 525) | type ListProductsResponse struct
    method Reset (line 533) | func (x *ListProductsResponse) Reset() {
    method String (line 542) | func (x *ListProductsResponse) String() string {
    method ProtoMessage (line 546) | func (*ListProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 548) | func (x *ListProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 561) | func (*ListProductsResponse) Descriptor() ([]byte, []int) {
    method GetProducts (line 565) | func (x *ListProductsResponse) GetProducts() []*Product {
  type GetProductRequest (line 572) | type GetProductRequest struct
    method Reset (line 580) | func (x *GetProductRequest) Reset() {
    method String (line 589) | func (x *GetProductRequest) String() string {
    method ProtoMessage (line 593) | func (*GetProductRequest) ProtoMessage() {}
    method ProtoReflect (line 595) | func (x *GetProductRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 608) | func (*GetProductRequest) Descriptor() ([]byte, []int) {
    method GetId (line 612) | func (x *GetProductRequest) GetId() string {
  type SearchProductsRequest (line 619) | type SearchProductsRequest struct
    method Reset (line 627) | func (x *SearchProductsRequest) Reset() {
    method String (line 636) | func (x *SearchProductsRequest) String() string {
    method ProtoMessage (line 640) | func (*SearchProductsRequest) ProtoMessage() {}
    method ProtoReflect (line 642) | func (x *SearchProductsRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 655) | func (*SearchProductsRequest) Descriptor() ([]byte, []int) {
    method GetQuery (line 659) | func (x *SearchProductsRequest) GetQuery() string {
  type SearchProductsResponse (line 666) | type SearchProductsResponse struct
    method Reset (line 674) | func (x *SearchProductsResponse) Reset() {
    method String (line 683) | func (x *SearchProductsResponse) String() string {
    method ProtoMessage (line 687) | func (*SearchProductsResponse) ProtoMessage() {}
    method ProtoReflect (line 689) | func (x *SearchProductsResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 702) | func (*SearchProductsResponse) Descriptor() ([]byte, []int) {
    method GetResults (line 706) | func (x *SearchProductsResponse) GetResults() []*Product {
  type GetQuoteRequest (line 713) | type GetQuoteRequest struct
    method Reset (line 722) | func (x *GetQuoteRequest) Reset() {
    method String (line 731) | func (x *GetQuoteRequest) String() string {
    method ProtoMessage (line 735) | func (*GetQuoteRequest) ProtoMessage() {}
    method ProtoReflect (line 737) | func (x *GetQuoteRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 750) | func (*GetQuoteRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 754) | func (x *GetQuoteRequest) GetAddress() *Address {
    method GetItems (line 761) | func (x *GetQuoteRequest) GetItems() []*CartItem {
  type GetQuoteResponse (line 768) | type GetQuoteResponse struct
    method Reset (line 776) | func (x *GetQuoteResponse) Reset() {
    method String (line 785) | func (x *GetQuoteResponse) String() string {
    method ProtoMessage (line 789) | func (*GetQuoteResponse) ProtoMessage() {}
    method ProtoReflect (line 791) | func (x *GetQuoteResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 804) | func (*GetQuoteResponse) Descriptor() ([]byte, []int) {
    method GetCostUsd (line 808) | func (x *GetQuoteResponse) GetCostUsd() *Money {
  type ShipOrderRequest (line 815) | type ShipOrderRequest struct
    method Reset (line 824) | func (x *ShipOrderRequest) Reset() {
    method String (line 833) | func (x *ShipOrderRequest) String() string {
    method ProtoMessage (line 837) | func (*ShipOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 839) | func (x *ShipOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 852) | func (*ShipOrderRequest) Descriptor() ([]byte, []int) {
    method GetAddress (line 856) | func (x *ShipOrderRequest) GetAddress() *Address {
    method GetItems (line 863) | func (x *ShipOrderRequest) GetItems() []*CartItem {
  type ShipOrderResponse (line 870) | type ShipOrderResponse struct
    method Reset (line 878) | func (x *ShipOrderResponse) Reset() {
    method String (line 887) | func (x *ShipOrderResponse) String() string {
    method ProtoMessage (line 891) | func (*ShipOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 893) | func (x *ShipOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 906) | func (*ShipOrderResponse) Descriptor() ([]byte, []int) {
    method GetTrackingId (line 910) | func (x *ShipOrderResponse) GetTrackingId() string {
  type Address (line 917) | type Address struct
    method Reset (line 929) | func (x *Address) Reset() {
    method String (line 938) | func (x *Address) String() string {
    method ProtoMessage (line 942) | func (*Address) ProtoMessage() {}
    method ProtoReflect (line 944) | func (x *Address) ProtoReflect() protoreflect.Message {
    method Descriptor (line 957) | func (*Address) Descriptor() ([]byte, []int) {
    method GetStreetAddress (line 961) | func (x *Address) GetStreetAddress() string {
    method GetCity (line 968) | func (x *Address) GetCity() string {
    method GetState (line 975) | func (x *Address) GetState() string {
    method GetCountry (line 982) | func (x *Address) GetCountry() string {
    method GetZipCode (line 989) | func (x *Address) GetZipCode() int32 {
  type Money (line 997) | type Money struct
    method Reset (line 1016) | func (x *Money) Reset() {
    method String (line 1025) | func (x *Money) String() string {
    method ProtoMessage (line 1029) | func (*Money) ProtoMessage() {}
    method ProtoReflect (line 1031) | func (x *Money) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1044) | func (*Money) Descriptor() ([]byte, []int) {
    method GetCurrencyCode (line 1048) | func (x *Money) GetCurrencyCode() string {
    method GetUnits (line 1055) | func (x *Money) GetUnits() int64 {
    method GetNanos (line 1062) | func (x *Money) GetNanos() int32 {
  type GetSupportedCurrenciesResponse (line 1069) | type GetSupportedCurrenciesResponse struct
    method Reset (line 1078) | func (x *GetSupportedCurrenciesResponse) Reset() {
    method String (line 1087) | func (x *GetSupportedCurrenciesResponse) String() string {
    method ProtoMessage (line 1091) | func (*GetSupportedCurrenciesResponse) ProtoMessage() {}
    method ProtoReflect (line 1093) | func (x *GetSupportedCurrenciesResponse) ProtoReflect() protoreflect.M...
    method Descriptor (line 1106) | func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) {
    method GetCurrencyCodes (line 1110) | func (x *GetSupportedCurrenciesResponse) GetCurrencyCodes() []string {
  type CurrencyConversionRequest (line 1117) | type CurrencyConversionRequest struct
    method Reset (line 1127) | func (x *CurrencyConversionRequest) Reset() {
    method String (line 1136) | func (x *CurrencyConversionRequest) String() string {
    method ProtoMessage (line 1140) | func (*CurrencyConversionRequest) ProtoMessage() {}
    method ProtoReflect (line 1142) | func (x *CurrencyConversionRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1155) | func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) {
    method GetFrom (line 1159) | func (x *CurrencyConversionRequest) GetFrom() *Money {
    method GetToCode (line 1166) | func (x *CurrencyConversionRequest) GetToCode() string {
  type CreditCardInfo (line 1173) | type CreditCardInfo struct
    method Reset (line 1184) | func (x *CreditCardInfo) Reset() {
    method String (line 1193) | func (x *CreditCardInfo) String() string {
    method ProtoMessage (line 1197) | func (*CreditCardInfo) ProtoMessage() {}
    method ProtoReflect (line 1199) | func (x *CreditCardInfo) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1212) | func (*CreditCardInfo) Descriptor() ([]byte, []int) {
    method GetCreditCardNumber (line 1216) | func (x *CreditCardInfo) GetCreditCardNumber() string {
    method GetCreditCardCvv (line 1223) | func (x *CreditCardInfo) GetCreditCardCvv() int32 {
    method GetCreditCardExpirationYear (line 1230) | func (x *CreditCardInfo) GetCreditCardExpirationYear() int32 {
    method GetCreditCardExpirationMonth (line 1237) | func (x *CreditCardInfo) GetCreditCardExpirationMonth() int32 {
  type ChargeRequest (line 1244) | type ChargeRequest struct
    method Reset (line 1253) | func (x *ChargeRequest) Reset() {
    method String (line 1262) | func (x *ChargeRequest) String() string {
    method ProtoMessage (line 1266) | func (*ChargeRequest) ProtoMessage() {}
    method ProtoReflect (line 1268) | func (x *ChargeRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1281) | func (*ChargeRequest) Descriptor() ([]byte, []int) {
    method GetAmount (line 1285) | func (x *ChargeRequest) GetAmount() *Money {
    method GetCreditCard (line 1292) | func (x *ChargeRequest) GetCreditCard() *CreditCardInfo {
  type ChargeResponse (line 1299) | type ChargeResponse struct
    method Reset (line 1307) | func (x *ChargeResponse) Reset() {
    method String (line 1316) | func (x *ChargeResponse) String() string {
    method ProtoMessage (line 1320) | func (*ChargeResponse) ProtoMessage() {}
    method ProtoReflect (line 1322) | func (x *ChargeResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1335) | func (*ChargeResponse) Descriptor() ([]byte, []int) {
    method GetTransactionId (line 1339) | func (x *ChargeResponse) GetTransactionId() string {
  type OrderItem (line 1346) | type OrderItem struct
    method Reset (line 1355) | func (x *OrderItem) Reset() {
    method String (line 1364) | func (x *OrderItem) String() string {
    method ProtoMessage (line 1368) | func (*OrderItem) ProtoMessage() {}
    method ProtoReflect (line 1370) | func (x *OrderItem) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1383) | func (*OrderItem) Descriptor() ([]byte, []int) {
    method GetItem (line 1387) | func (x *OrderItem) GetItem() *CartItem {
    method GetCost (line 1394) | func (x *OrderItem) GetCost() *Money {
  type OrderResult (line 1401) | type OrderResult struct
    method Reset (line 1413) | func (x *OrderResult) Reset() {
    method String (line 1422) | func (x *OrderResult) String() string {
    method ProtoMessage (line 1426) | func (*OrderResult) ProtoMessage() {}
    method ProtoReflect (line 1428) | func (x *OrderResult) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1441) | func (*OrderResult) Descriptor() ([]byte, []int) {
    method GetOrderId (line 1445) | func (x *OrderResult) GetOrderId() string {
    method GetShippingTrackingId (line 1452) | func (x *OrderResult) GetShippingTrackingId() string {
    method GetShippingCost (line 1459) | func (x *OrderResult) GetShippingCost() *Money {
    method GetShippingAddress (line 1466) | func (x *OrderResult) GetShippingAddress() *Address {
    method GetItems (line 1473) | func (x *OrderResult) GetItems() []*OrderItem {
  type SendOrderConfirmationRequest (line 1480) | type SendOrderConfirmationRequest struct
    method Reset (line 1489) | func (x *SendOrderConfirmationRequest) Reset() {
    method String (line 1498) | func (x *SendOrderConfirmationRequest) String() string {
    method ProtoMessage (line 1502) | func (*SendOrderConfirmationRequest) ProtoMessage() {}
    method ProtoReflect (line 1504) | func (x *SendOrderConfirmationRequest) ProtoReflect() protoreflect.Mes...
    method Descriptor (line 1517) | func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) {
    method GetEmail (line 1521) | func (x *SendOrderConfirmationRequest) GetEmail() string {
    method GetOrder (line 1528) | func (x *SendOrderConfirmationRequest) GetOrder() *OrderResult {
  type PlaceOrderRequest (line 1535) | type PlaceOrderRequest struct
    method Reset (line 1547) | func (x *PlaceOrderRequest) Reset() {
    method String (line 1556) | func (x *PlaceOrderRequest) String() string {
    method ProtoMessage (line 1560) | func (*PlaceOrderRequest) ProtoMessage() {}
    method ProtoReflect (line 1562) | func (x *PlaceOrderRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1575) | func (*PlaceOrderRequest) Descriptor() ([]byte, []int) {
    method GetUserId (line 1579) | func (x *PlaceOrderRequest) GetUserId() string {
    method GetUserCurrency (line 1586) | func (x *PlaceOrderRequest) GetUserCurrency() string {
    method GetAddress (line 1593) | func (x *PlaceOrderRequest) GetAddress() *Address {
    method GetEmail (line 1600) | func (x *PlaceOrderRequest) GetEmail() string {
    method GetCreditCard (line 1607) | func (x *PlaceOrderRequest) GetCreditCard() *CreditCardInfo {
  type PlaceOrderResponse (line 1614) | type PlaceOrderResponse struct
    method Reset (line 1622) | func (x *PlaceOrderResponse) Reset() {
    method String (line 1631) | func (x *PlaceOrderResponse) String() string {
    method ProtoMessage (line 1635) | func (*PlaceOrderResponse) ProtoMessage() {}
    method ProtoReflect (line 1637) | func (x *PlaceOrderResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1650) | func (*PlaceOrderResponse) Descriptor() ([]byte, []int) {
    method GetOrder (line 1654) | func (x *PlaceOrderResponse) GetOrder() *OrderResult {
  type AdRequest (line 1661) | type AdRequest struct
    method Reset (line 1670) | func (x *AdRequest) Reset() {
    method String (line 1679) | func (x *AdRequest) String() string {
    method ProtoMessage (line 1683) | func (*AdRequest) ProtoMessage() {}
    method ProtoReflect (line 1685) | func (x *AdRequest) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1698) | func (*AdRequest) Descriptor() ([]byte, []int) {
    method GetContextKeys (line 1702) | func (x *AdRequest) GetContextKeys() []string {
  type AdResponse (line 1709) | type AdResponse struct
    method Reset (line 1717) | func (x *AdResponse) Reset() {
    method String (line 1726) | func (x *AdResponse) String() string {
    method ProtoMessage (line 1730) | func (*AdResponse) ProtoMessage() {}
    method ProtoReflect (line 1732) | func (x *AdResponse) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1745) | func (*AdResponse) Descriptor() ([]byte, []int) {
    method GetAds (line 1749) | func (x *AdResponse) GetAds() []*Ad {
  type Ad (line 1756) | type Ad struct
    method Reset (line 1767) | func (x *Ad) Reset() {
    method String (line 1776) | func (x *Ad) String() string {
    method ProtoMessage (line 1780) | func (*Ad) ProtoMessage() {}
    method ProtoReflect (line 1782) | func (x *Ad) ProtoReflect() protoreflect.Message {
    method Descriptor (line 1795) | func (*Ad) Descriptor() ([]byte, []int) {
    method GetRedirectUrl (line 1799) | func (x *Ad) GetRedirectUrl() string {
    method GetText (line 1806) | func (x *Ad) GetText() string {
  function file_demo_proto_rawDescGZIP (line 2098) | func file_demo_proto_rawDescGZIP() []byte {
  function init (line 2201) | func init() { file_demo_proto_init() }
  function file_demo_proto_init (line 2202) | func file_demo_proto_init() {

FILE: src/shippingservice/genproto/demo_grpc.pb.go
  constant _ (line 33) | _ = grpc.SupportPackageIsVersion9
  constant CartService_AddItem_FullMethodName (line 36) | CartService_AddItem_FullMethodName   = "/hipstershop.CartService/AddItem"
  constant CartService_GetCart_FullMethodName (line 37) | CartService_GetCart_FullMethodName   = "/hipstershop.CartService/GetCart"
  constant CartService_EmptyCart_FullMethodName (line 38) | CartService_EmptyCart_FullMethodName = "/hipstershop.CartService/EmptyCart"
  type CartServiceClient (line 44) | type CartServiceClient interface
  type cartServiceClient (line 50) | type cartServiceClient struct
    method AddItem (line 58) | func (c *cartServiceClient) AddItem(ctx context.Context, in *AddItemRe...
    method GetCart (line 68) | func (c *cartServiceClient) GetCart(ctx context.Context, in *GetCartRe...
    method EmptyCart (line 78) | func (c *cartServiceClient) EmptyCart(ctx context.Context, in *EmptyCa...
  function NewCartServiceClient (line 54) | func NewCartServiceClient(cc grpc.ClientConnInterface) CartServiceClient {
  type CartServiceServer (line 91) | type CartServiceServer interface
  type UnimplementedCartServiceServer (line 103) | type UnimplementedCartServiceServer struct
    method AddItem (line 105) | func (UnimplementedCartServiceServer) AddItem(context.Context, *AddIte...
    method GetCart (line 108) | func (UnimplementedCartServiceServer) GetCart(context.Context, *GetCar...
    method EmptyCart (line 111) | func (UnimplementedCartServiceServer) EmptyCart(context.Context, *Empt...
    method mustEmbedUnimplementedCartServiceServer (line 114) | func (UnimplementedCartServiceServer) mustEmbedUnimplementedCartServic...
    method testEmbeddedByValue (line 115) | func (UnimplementedCartServiceServer) testEmbeddedByValue()           ...
  type UnsafeCartServiceServer (line 120) | type UnsafeCartServiceServer interface
  function RegisterCartServiceServer (line 124) | func RegisterCartServiceServer(s grpc.ServiceRegistrar, srv CartServiceS...
  function _CartService_AddItem_Handler (line 135) | func _CartService_AddItem_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_GetCart_Handler (line 153) | func _CartService_GetCart_Handler(srv interface{}, ctx context.Context, ...
  function _CartService_EmptyCart_Handler (line 171) | func _CartService_EmptyCart_Handler(srv interface{}, ctx context.Context...
  constant RecommendationService_ListRecommendations_FullMethodName (line 214) | RecommendationService_ListRecommendations_FullMethodName = "/hipstershop...
  type RecommendationServiceClient (line 220) | type RecommendationServiceClient interface
  type recommendationServiceClient (line 224) | type recommendationServiceClient struct
    method ListRecommendations (line 232) | func (c *recommendationServiceClient) ListRecommendations(ctx context....
  function NewRecommendationServiceClient (line 228) | func NewRecommendationServiceClient(cc grpc.ClientConnInterface) Recomme...
  type RecommendationServiceServer (line 245) | type RecommendationServiceServer interface
  type UnimplementedRecommendationServiceServer (line 255) | type UnimplementedRecommendationServiceServer struct
    method ListRecommendations (line 257) | func (UnimplementedRecommendationServiceServer) ListRecommendations(co...
    method mustEmbedUnimplementedRecommendationServiceServer (line 260) | func (UnimplementedRecommendationServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 261) | func (UnimplementedRecommendationServiceServer) testEmbeddedByValue() ...
  type UnsafeRecommendationServiceServer (line 266) | type UnsafeRecommendationServiceServer interface
  function RegisterRecommendationServiceServer (line 270) | func RegisterRecommendationServiceServer(s grpc.ServiceRegistrar, srv Re...
  function _RecommendationService_ListRecommendations_Handler (line 281) | func _RecommendationService_ListRecommendations_Handler(srv interface{},...
  constant ProductCatalogService_ListProducts_FullMethodName (line 316) | ProductCatalogService_ListProducts_FullMethodName   = "/hipstershop.Prod...
  constant ProductCatalogService_GetProduct_FullMethodName (line 317) | ProductCatalogService_GetProduct_FullMethodName     = "/hipstershop.Prod...
  constant ProductCatalogService_SearchProducts_FullMethodName (line 318) | ProductCatalogService_SearchProducts_FullMethodName = "/hipstershop.Prod...
  type ProductCatalogServiceClient (line 324) | type ProductCatalogServiceClient interface
  type productCatalogServiceClient (line 330) | type productCatalogServiceClient struct
    method ListProducts (line 338) | func (c *productCatalogServiceClient) ListProducts(ctx context.Context...
    method GetProduct (line 348) | func (c *productCatalogServiceClient) GetProduct(ctx context.Context, ...
    method SearchProducts (line 358) | func (c *productCatalogServiceClient) SearchProducts(ctx context.Conte...
  function NewProductCatalogServiceClient (line 334) | func NewProductCatalogServiceClient(cc grpc.ClientConnInterface) Product...
  type ProductCatalogServiceServer (line 371) | type ProductCatalogServiceServer interface
  type UnimplementedProductCatalogServiceServer (line 383) | type UnimplementedProductCatalogServiceServer struct
    method ListProducts (line 385) | func (UnimplementedProductCatalogServiceServer) ListProducts(context.C...
    method GetProduct (line 388) | func (UnimplementedProductCatalogServiceServer) GetProduct(context.Con...
    method SearchProducts (line 391) | func (UnimplementedProductCatalogServiceServer) SearchProducts(context...
    method mustEmbedUnimplementedProductCatalogServiceServer (line 394) | func (UnimplementedProductCatalogServiceServer) mustEmbedUnimplemented...
    method testEmbeddedByValue (line 395) | func (UnimplementedProductCatalogServiceServer) testEmbeddedByValue() ...
  type UnsafeProductCatalogServiceServer (line 400) | type UnsafeProductCatalogServiceServer interface
  function RegisterProductCatalogServiceServer (line 404) | func RegisterProductCatalogServiceServer(s grpc.ServiceRegistrar, srv Pr...
  function _ProductCatalogService_ListProducts_Handler (line 415) | func _ProductCatalogService_ListProducts_Handler(srv interface{}, ctx co...
  function _ProductCatalogService_GetProduct_Handler (line 433) | func _ProductCatalogService_GetProduct_Handler(srv interface{}, ctx cont...
  function _ProductCatalogService_SearchProducts_Handler (line 451) | func _ProductCatalogService_SearchProducts_Handler(srv interface{}, ctx ...
  constant ShippingService_GetQuote_FullMethodName (line 494) | ShippingService_GetQuote_FullMethodName  = "/hipstershop.ShippingService...
  constant ShippingService_ShipOrder_FullMethodName (line 495) | ShippingService_ShipOrder_FullMethodName = "/hipstershop.ShippingService...
  type ShippingServiceClient (line 501) | type ShippingServiceClient interface
  type shippingServiceClient (line 506) | type shippingServiceClient struct
    method GetQuote (line 514) | func (c *shippingServiceClient) GetQuote(ctx context.Context, in *GetQ...
    method ShipOrder (line 524) | func (c *shippingServiceClient) ShipOrder(ctx context.Context, in *Shi...
  function NewShippingServiceClient (line 510) | func NewShippingServiceClient(cc grpc.ClientConnInterface) ShippingServi...
  type ShippingServiceServer (line 537) | type ShippingServiceServer interface
  type UnimplementedShippingServiceServer (line 548) | type UnimplementedShippingServiceServer struct
    method GetQuote (line 550) | func (UnimplementedShippingServiceServer) GetQuote(context.Context, *G...
    method ShipOrder (line 553) | func (UnimplementedShippingServiceServer) ShipOrder(context.Context, *...
    method mustEmbedUnimplementedShippingServiceServer (line 556) | func (UnimplementedShippingServiceServer) mustEmbedUnimplementedShippi...
    method testEmbeddedByValue (line 557) | func (UnimplementedShippingServiceServer) testEmbeddedByValue()       ...
  type UnsafeShippingServiceServer (line 562) | type UnsafeShippingServiceServer interface
  function RegisterShippingServiceServer (line 566) | func RegisterShippingServiceServer(s grpc.ServiceRegistrar, srv Shipping...
  function _ShippingService_GetQuote_Handler (line 577) | func _ShippingService_GetQuote_Handler(srv interface{}, ctx context.Cont...
  function _ShippingService_ShipOrder_Handler (line 595) | func _ShippingService_ShipOrder_Handler(srv interface{}, ctx context.Con...
  constant CurrencyService_GetSupportedCurrencies_FullMethodName (line 634) | CurrencyService_GetSupportedCurrencies_FullMethodName = "/hipstershop.Cu...
  constant CurrencyService_Convert_FullMethodName (line 635) | CurrencyService_Convert_FullMethodName                = "/hipstershop.Cu...
  type CurrencyServiceClient (line 641) | type CurrencyServiceClient interface
  type currencyServiceClient (line 646) | type currencyServiceClient struct
    method GetSupportedCurrencies (line 654) | func (c *currencyServiceClient) GetSupportedCurrencies(ctx context.Con...
    method Convert (line 664) | func (c *currencyServiceClient) Convert(ctx context.Context, in *Curre...
  function NewCurrencyServiceClient (line 650) | func NewCurrencyServiceClient(cc grpc.ClientConnInterface) CurrencyServi...
  type CurrencyServiceServer (line 677) | type CurrencyServiceServer interface
  type UnimplementedCurrencyServiceServer (line 688) | type UnimplementedCurrencyServiceServer struct
    method GetSupportedCurrencies (line 690) | func (UnimplementedCurrencyServiceServer) GetSupportedCurrencies(conte...
    method Convert (line 693) | func (UnimplementedCurrencyServiceServer) Convert(context.Context, *Cu...
    method mustEmbedUnimplementedCurrencyServiceServer (line 696) | func (UnimplementedCurrencyServiceServer) mustEmbedUnimplementedCurren...
    method testEmbeddedByValue (line 697) | func (UnimplementedCurrencyServiceServer) testEmbeddedByValue()       ...
  type UnsafeCurrencyServiceServer (line 702) | type UnsafeCurrencyServiceServer interface
  function RegisterCurrencyServiceServer (line 706) | func RegisterCurrencyServiceServer(s grpc.ServiceRegistrar, srv Currency...
  function _CurrencyService_GetSupportedCurrencies_Handler (line 717) | func _CurrencyService_GetSupportedCurrencies_Handler(srv interface{}, ct...
  function _CurrencyService_Convert_Handler (line 735) | func _CurrencyService_Convert_Handler(srv interface{}, ctx context.Conte...
  constant PaymentService_Charge_FullMethodName (line 774) | PaymentService_Charge_FullMethodName = "/hipstershop.PaymentService/Charge"
  type PaymentServiceClient (line 780) | type PaymentServiceClient interface
  type paymentServiceClient (line 784) | type paymentServiceClient struct
    method Charge (line 792) | func (c *paymentServiceClient) Charge(ctx context.Context, in *ChargeR...
  function NewPaymentServiceClient (line 788) | func NewPaymentServiceClient(cc grpc.ClientConnInterface) PaymentService...
  type PaymentServiceServer (line 805) | type PaymentServiceServer interface
  type UnimplementedPaymentServiceServer (line 815) | type UnimplementedPaymentServiceServer struct
    method Charge (line 817) | func (UnimplementedPaymentServiceServer) Charge(context.Context, *Char...
    method mustEmbedUnimplementedPaymentServiceServer (line 820) | func (UnimplementedPaymentServiceServer) mustEmbedUnimplementedPayment...
    method testEmbeddedByValue (line 821) | func (UnimplementedPaymentServiceServer) testEmbeddedByValue()        ...
  type UnsafePaymentServiceServer (line 826) | type UnsafePaymentServiceServer interface
  function RegisterPaymentServiceServer (line 830) | func RegisterPaymentServiceServer(s grpc.ServiceRegistrar, srv PaymentSe...
  function _PaymentService_Charge_Handler (line 841) | func _PaymentService_Charge_Handler(srv interface{}, ctx context.Context...
  constant EmailService_SendOrderConfirmation_FullMethodName (line 876) | EmailService_SendOrderConfirmation_FullMethodName = "/hipstershop.EmailS...
  type EmailServiceClient (line 882) | type EmailServiceClient interface
  type emailServiceClient (line 886) | type emailServiceClient struct
    method SendOrderConfirmation (line 894) | func (c *emailServiceClient) SendOrderConfirmation(ctx context.Context...
  function NewEmailServiceClient (line 890) | func NewEmailServiceClient(cc grpc.ClientConnInterface) EmailServiceClie...
  type EmailServiceServer (line 907) | type EmailServiceServer interface
  type UnimplementedEmailServiceServer (line 917) | type UnimplementedEmailServiceServer struct
    method SendOrderConfirmation (line 919) | func (UnimplementedEmailServiceServer) SendOrderConfirmation(context.C...
    method mustEmbedUnimplementedEmailServiceServer (line 922) | func (UnimplementedEmailServiceServer) mustEmbedUnimplementedEmailServ...
    method testEmbeddedByValue (line 923) | func (UnimplementedEmailServiceServer) testEmbeddedByValue()          ...
  type UnsafeEmailServiceServer (line 928) | type UnsafeEmailServiceServer interface
  function RegisterEmailServiceServer (line 932) | func RegisterEmailServiceServer(s grpc.ServiceRegistrar, srv EmailServic...
  function _EmailService_SendOrderConfirmation_Handler (line 943) | func _EmailService_SendOrderConfirmation_Handler(srv interface{}, ctx co...
  constant CheckoutService_PlaceOrder_FullMethodName (line 978) | CheckoutService_PlaceOrder_FullMethodName = "/hipstershop.CheckoutServic...
  type CheckoutServiceClient (line 984) | type CheckoutServiceClient interface
  type checkoutServiceClient (line 988) | type checkoutServiceClient struct
    method PlaceOrder (line 996) | func (c *checkoutServiceClient) PlaceOrder(ctx context.Context, in *Pl...
  function NewCheckoutServiceClient (line 992) | func NewCheckoutServiceClient(cc grpc.ClientConnInterface) CheckoutServi...
  type CheckoutServiceServer (line 1009) | type CheckoutServiceServer interface
  type UnimplementedCheckoutServiceServer (line 1019) | type UnimplementedCheckoutServiceServer struct
    method PlaceOrder (line 1021) | func (UnimplementedCheckoutServiceServer) PlaceOrder(context.Context, ...
    method mustEmbedUnimplementedCheckoutServiceServer (line 1024) | func (UnimplementedCheckoutServiceServer) mustEmbedUnimplementedChecko...
    method testEmbeddedByValue (line 1025) | func (UnimplementedCheckoutServiceServer) testEmbeddedByValue()       ...
  type UnsafeCheckoutServiceServer (line 1030) | type UnsafeCheckoutServiceServer interface
  function RegisterCheckoutServiceServer (line 1034) | func RegisterCheckoutServiceServer(s grpc.ServiceRegistrar, srv Checkout...
  function _CheckoutService_PlaceOrder_Handler (line 1045) | func _CheckoutService_PlaceOrder_Handler(srv interface{}, ctx context.Co...
  constant AdService_GetAds_FullMethodName (line 1080) | AdService_GetAds_FullMethodName = "/hipstershop.AdService/GetAds"
  type AdServiceClient (line 1086) | type AdServiceClient interface
  type adServiceClient (line 1090) | type adServiceClient struct
    method GetAds (line 1098) | func (c *adServiceClient) GetAds(ctx context.Context, in *AdRequest, o...
  function NewAdServiceClient (line 1094) | func NewAdServiceClient(cc grpc.ClientConnInterface) AdServiceClient {
  type AdServiceServer (line 1111) | type AdServiceServer interface
  type UnimplementedAdServiceServer (line 1121) | type UnimplementedAdServiceServer struct
    method GetAds (line 1123) | func (UnimplementedAdServiceServer) GetAds(context.Context, *AdRequest...
    method mustEmbedUnimplementedAdServiceServer (line 1126) | func (UnimplementedAdServiceServer) mustEmbedUnimplementedAdServiceSer...
    method testEmbeddedByValue (line 1127) | func (UnimplementedAdServiceServer) testEmbeddedByValue()             ...
  type UnsafeAdServiceServer (line 1132) | type UnsafeAdServiceServer interface
  function RegisterAdServiceServer (line 1136) | func RegisterAdServiceServer(s grpc.ServiceRegistrar, srv AdServiceServe...
  function _AdService_GetAds_Handler (line 1147) | func _AdService_GetAds_Handler(srv interface{}, ctx context.Context, dec...

FILE: src/shippingservice/main.go
  constant defaultPort (line 37) | defaultPort = "50051"
  function init (line 42) | func init() {
  function main (line 56) | func main() {
  type server (line 105) | type server struct
    method Check (line 110) | func (s *server) Check(ctx context.Context, req *healthpb.HealthCheckR...
    method Watch (line 114) | func (s *server) Watch(req *healthpb.HealthCheckRequest, ws healthpb.H...
    method GetQuote (line 119) | func (s *server) GetQuote(ctx context.Context, in *pb.GetQuoteRequest)...
    method ShipOrder (line 142) | func (s *server) ShipOrder(ctx context.Context, in *pb.ShipOrderReques...
  function initStats (line 155) | func initStats() {
  function initTracing (line 159) | func initTracing() {
  function initProfiling (line 163) | func initProfiling(service, version string) {

FILE: src/shippingservice/quote.go
  type Quote (line 23) | type Quote struct
    method String (line 29) | func (q Quote) String() string {
  function CreateQuoteFromCount (line 34) | func CreateQuoteFromCount(count int) Quote {
  function CreateQuoteFromFloat (line 42) | func CreateQuoteFromFloat(value float64) Quote {

FILE: src/shippingservice/shippingservice_test.go
  function TestGetQuote (line 27) | func TestGetQuote(t *testing.T) {
  function TestGetQuoteEmptyCart (line 60) | func TestGetQuoteEmptyCart(t *testing.T) {
  function TestShipOrder (line 83) | func TestShipOrder(t *testing.T) {
  function TestTrackingIdFormat (line 116) | func TestTrackingIdFormat(t *testing.T) {
  function TestTrackingIdUniqueness (line 128) | func TestTrackingIdUniqueness(t *testing.T) {
  function TestCreateQuoteFromFloat (line 140) | func TestCreateQuoteFromFloat(t *testing.T) {
  function TestCreateQuoteFromCount (line 166) | func TestCreateQuoteFromCount(t *testing.T) {
  function TestGetRandomLetterCode (line 179) | func TestGetRandomLetterCode(t *testing.T) {
  function TestGetRandomNumber (line 189) | func TestGetRandomNumber(t *testing.T) {
  function TestQuoteString (line 200) | func TestQuoteString(t *testing.T) {

FILE: src/shippingservice/tracker.go
  function CreateTrackingId (line 23) | func CreateTrackingId(salt string) string {
  function getRandomLetterCode (line 35) | func getRandomLetterCode() uint32 {
  function getRandomNumber (line 40) | func getRandomNumber(digits int) string {

FILE: src/shoppingassistantservice/shoppingassistantservice.py
  function create_app (line 62) | def create_app():
Condensed preview — 316 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,613K chars).
[
  {
    "path": ".deploystack/deploystack.yaml",
    "chars": 518,
    "preview": "# The fields inside this deploystack.yaml file are documented in https://github.com/GoogleCloudPlatform/deploystack.\n\nti"
  },
  {
    "path": ".deploystack/messages/description.txt",
    "chars": 247,
    "preview": "Online Boutique is a cloud-first microservices demo application. Online Boutique\nconsists of an 11-tier microservices ap"
  },
  {
    "path": ".deploystack/messages/success.txt",
    "chars": 168,
    "preview": "Congrats!\nYou have successfully provisioned a GKE (Google Kubernetes Engine) cluster and\ndeployed Online Boutique's 11 m"
  },
  {
    "path": ".deploystack/scripts/preinit.sh",
    "chars": 664,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".deploystack/test",
    "chars": 7228,
    "preview": "#! /bin/bash\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may n"
  },
  {
    "path": ".deploystack/test.yaml",
    "chars": 1211,
    "preview": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".editorconfig",
    "chars": 401,
    "preview": "# The .editorconfig is used to maintain consistent code style.\n# The .editorconfig file is supported by most text editor"
  },
  {
    "path": ".gitattributes",
    "chars": 199,
    "preview": "# This file configures the git settings for this repository.\n\n# Converts \"CR + LF\" to \"LF\", for all \"text\" files — for l"
  },
  {
    "path": ".github/CODEOWNERS",
    "chars": 282,
    "preview": "# See https://help.github.com/en/articles/about-code-owners\n# for more info about CODEOWNERS file.\n\n# These owners will "
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "chars": 1981,
    "preview": "# Contributor Code of Conduct\n\nAs contributors and maintainers of this project,\nand in the interest of fostering an open"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "chars": 2163,
    "preview": "# How to Contribute\n\nThank you so much for your interest in contributing to Online Boutique.\nBefore contributing, you mu"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug-report.md",
    "chars": 871,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n### Describe the"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "chars": 291,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n### Describe "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other.md",
    "chars": 198,
    "preview": "---\nname: Other\nabout: Have a question or need clarification?\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n### Write down you"
  },
  {
    "path": ".github/SECURITY.md",
    "chars": 329,
    "preview": "# Security Policy\n\nTo report a security issue, please use [g.co/vulnz](https://g.co/vulnz).\n\nThe Google Security Team wi"
  },
  {
    "path": ".github/auto-approve.yml",
    "chars": 841,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/header-checker-lint.yml",
    "chars": 1530,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 422,
    "preview": "### Background \n<!-- What was happening before this PR, and the problem(s) it solves -->\n\n### Fixes \n<!-- Link the issue"
  },
  {
    "path": ".github/release-cluster/README.md",
    "chars": 2472,
    "preview": "# cymbal-shops.retail.cymbal.dev manifests\n\nThis directory contains extra deploy manifests for configuring Online Boutiq"
  },
  {
    "path": ".github/release-cluster/backend-config.yaml",
    "chars": 736,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/release-cluster/frontend-config.yaml",
    "chars": 809,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/release-cluster/frontend-ingress.yaml",
    "chars": 1170,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/release-cluster/frontend-service.yaml",
    "chars": 883,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/release-cluster/managed-cert.yaml",
    "chars": 735,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/renovate.json5",
    "chars": 514,
    "preview": "{\n  extends: [\n    'github>GoogleCloudPlatform/kubernetes-engine-samples//.github/renovate-configs/dee-platform-ops.json"
  },
  {
    "path": ".github/snippet-bot.yml",
    "chars": 576,
    "preview": "\n# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this "
  },
  {
    "path": ".github/terraform/README.md",
    "chars": 997,
    "preview": "This folder contains the Terraform for some of the infrastructure used by the CICD (continuous integration and continuou"
  },
  {
    "path": ".github/terraform/main.tf",
    "chars": 4317,
    "preview": "/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": ".github/terraform/variables.tf",
    "chars": 844,
    "preview": "/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": ".github/terraform/versions.tf",
    "chars": 750,
    "preview": "/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
  },
  {
    "path": ".github/workflows/README.md",
    "chars": 3784,
    "preview": "# GitHub Actions Workflows\n\nThis page describes the CI/CD workflows for the Online Boutique app, which run in [Github Ac"
  },
  {
    "path": ".github/workflows/ci-main.yaml",
    "chars": 4721,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/ci-pr.yaml",
    "chars": 6041,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/cleanup.yaml",
    "chars": 1484,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/helm-chart-ci.yaml",
    "chars": 4491,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/install-dependencies.sh",
    "chars": 2595,
    "preview": "#!/bin/bash\n# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": ".github/workflows/kubevious-manifests-ci.yaml",
    "chars": 1614,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/kustomize-build-ci.yaml",
    "chars": 1419,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".github/workflows/terraform-validate-ci.yaml",
    "chars": 1130,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": ".gitignore",
    "chars": 310,
    "preview": ".DS_Store\n.eclipse.buildship.core.prefs\n.gradle/\n.idea/\n.kubernetes-manifests-*/\n.project\n.skaffold-*.yaml\n.terraform.lo"
  },
  {
    "path": "LICENSE",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "README.md",
    "chars": 12219,
    "preview": "<!-- <p align=\"center\">\n<img src=\"/src/frontend/static/icons/Hipster_HeroLogoMaroon.svg\" width=\"300\" alt=\"Online Boutiqu"
  },
  {
    "path": "cloudbuild.yaml",
    "chars": 1490,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "docs/adding-new-microservice.md",
    "chars": 3052,
    "preview": "# Adding a new microservice\n\nThis document outlines the steps required to add a new microservice to the Online Boutique "
  },
  {
    "path": "docs/cloudshell-tutorial.md",
    "chars": 4935,
    "preview": "# Online Boutique quickstart\n\nThis tutorial shows you how to deploy **[Online Boutique](https://github.com/GoogleCloudPl"
  },
  {
    "path": "docs/deploystack.md",
    "chars": 1033,
    "preview": "## Deploy Online Boutique with DeployStack\n\nThe \"Open in Google Cloud Shell\" button below will use [DeployStack](https:/"
  },
  {
    "path": "docs/development-guide.md",
    "chars": 4683,
    "preview": "# Development Guide \n\nThis doc explains how to build and run the Online Boutique source code locally using the `skaffold"
  },
  {
    "path": "docs/product-requirements.md",
    "chars": 2550,
    "preview": "## Product Requirements\n\nThis document contains a list of requirements that every change made to this repository should "
  },
  {
    "path": "docs/purpose.md",
    "chars": 694,
    "preview": "## Purpose\n\nToday, the primary purpose of Online Boutique is to demonstrate:\n\n* [Google Kubernetes Engine (GKE)](https:/"
  },
  {
    "path": "docs/releasing/README.md",
    "chars": 5024,
    "preview": "# Releasing Online Boutique\n\nThis document walks through the process of creating a new release of Online Boutique.\n\n## P"
  },
  {
    "path": "docs/releasing/license_header.txt",
    "chars": 575,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "docs/releasing/make-docker-images.sh",
    "chars": 1792,
    "preview": "#!/usr/bin/env bash\n\n# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# y"
  },
  {
    "path": "docs/releasing/make-helm-chart.sh",
    "chars": 1232,
    "preview": "#!/usr/bin/env bash\n\n# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# y"
  },
  {
    "path": "docs/releasing/make-release-artifacts.sh",
    "chars": 4257,
    "preview": "#!/usr/bin/env bash\n\n# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# y"
  },
  {
    "path": "docs/releasing/make-release.sh",
    "chars": 2144,
    "preview": "#!/usr/bin/env bash\n\n# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# y"
  },
  {
    "path": "helm-chart/Chart.yaml",
    "chars": 1747,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/README.md",
    "chars": 2066,
    "preview": "# Helm chart for Online Boutique\n\nIf you'd like to deploy Online Boutique via its Helm chart, you could leverage the fol"
  },
  {
    "path": "helm-chart/templates/NOTES.txt",
    "chars": 960,
    "preview": "{{- if and .Values.frontend.create .Values.frontend.externalService }}\nNote: It may take a few minutes for the LoadBalan"
  },
  {
    "path": "helm-chart/templates/adservice.yaml",
    "chars": 4630,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/cartservice.yaml",
    "chars": 11557,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/checkoutservice.yaml",
    "chars": 6204,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/common.yaml",
    "chars": 1009,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/currencyservice.yaml",
    "chars": 5413,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/emailservice.yaml",
    "chars": 5172,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/frontend.yaml",
    "chars": 8919,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/loadgenerator.yaml",
    "chars": 4775,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/opentelemetry-collector.yaml",
    "chars": 8513,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/paymentservice.yaml",
    "chars": 5151,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/productcatalogservice.yaml",
    "chars": 5799,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/recommendationservice.yaml",
    "chars": 5538,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/templates/shippingservice.yaml",
    "chars": 4981,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "helm-chart/values.yaml",
    "chars": 5282,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "istio-manifests/allow-egress-googleapis.yaml",
    "chars": 1192,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "istio-manifests/frontend-gateway.yaml",
    "chars": 1103,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "istio-manifests/frontend.yaml",
    "chars": 817,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/README.md",
    "chars": 317,
    "preview": "# ./kubernetes-manifests\n\n:warning: Kubernetes manifests provided in this directory are not directly\ndeployable to a clu"
  },
  {
    "path": "kubernetes-manifests/adservice.yaml",
    "chars": 2033,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/cartservice.yaml",
    "chars": 3391,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/checkoutservice.yaml",
    "chars": 2474,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/currencyservice.yaml",
    "chars": 2043,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/emailservice.yaml",
    "chars": 2046,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/frontend.yaml",
    "chars": 4032,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/kustomization.yaml",
    "chars": 1460,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/loadgenerator.yaml",
    "chars": 2809,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/paymentservice.yaml",
    "chars": 2018,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/productcatalogservice.yaml",
    "chars": 2082,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/recommendationservice.yaml",
    "chars": 2228,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kubernetes-manifests/shippingservice.yaml",
    "chars": 2016,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/README.md",
    "chars": 7575,
    "preview": "# Use Online Boutique with Kustomize\n\nThis page contains instructions on deploying variations of the [Online Boutique](h"
  },
  {
    "path": "kustomize/base/adservice.yaml",
    "chars": 2102,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/cartservice.yaml",
    "chars": 3460,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/checkoutservice.yaml",
    "chars": 2543,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/currencyservice.yaml",
    "chars": 2112,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/emailservice.yaml",
    "chars": 2115,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/frontend.yaml",
    "chars": 4101,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/kustomization.yaml",
    "chars": 893,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/loadgenerator.yaml",
    "chars": 2878,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/paymentservice.yaml",
    "chars": 2087,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/productcatalogservice.yaml",
    "chars": 2151,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/recommendationservice.yaml",
    "chars": 2297,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/base/shippingservice.yaml",
    "chars": 2085,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/alloydb/README.md",
    "chars": 7496,
    "preview": "# Integrate Online Boutique with AlloyDB\n\nBy default the `cartservice` stores its data in an in-cluster Redis database. "
  },
  {
    "path": "kustomize/components/alloydb/kustomization.yaml",
    "chars": 3245,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/container-images-registry/README.md",
    "chars": 1473,
    "preview": "# Update the container registry of the Online Boutique apps\n\nBy default, Online Boutique's services' container images ar"
  },
  {
    "path": "kustomize/components/container-images-registry/kustomization.yaml",
    "chars": 2198,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/container-images-tag/README.md",
    "chars": 1702,
    "preview": "# Update the container image tag of the Online Boutique apps\n\nBy default, the Online Boutique apps are targeting the lat"
  },
  {
    "path": "kustomize/components/container-images-tag/kustomization.yaml",
    "chars": 1910,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/container-images-tag-suffix/README.md",
    "chars": 2245,
    "preview": "# Add a suffix to the image tag of the Online Boutique container images\n\nYou may want to add a suffix to the Online Bout"
  },
  {
    "path": "kustomize/components/container-images-tag-suffix/kustomization.yaml",
    "chars": 2019,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/custom-base-url/README.md",
    "chars": 1808,
    "preview": "# Customize the Base URL for Online Boutique\n\nThis component allows you to change the base URL for the Online Boutique a"
  },
  {
    "path": "kustomize/components/custom-base-url/kustomization.yaml",
    "chars": 1100,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/cymbal-branding/README.md",
    "chars": 1402,
    "preview": "# Change the Online Boutique theme to the Cymbal Shops Branding\n\nBy default, when you deploy this sample app, the \"Onlin"
  },
  {
    "path": "kustomize/components/cymbal-branding/kustomization.yaml",
    "chars": 906,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/google-cloud-operations/README.md",
    "chars": 5688,
    "preview": "# Integrate Online Boutique with Google Cloud Operations\n\nBy default, [Google Cloud Operations](https://cloud.google.com"
  },
  {
    "path": "kustomize/components/google-cloud-operations/kustomization.yaml",
    "chars": 4932,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/google-cloud-operations/otel-collector.yaml",
    "chars": 4063,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/memorystore/README.md",
    "chars": 2710,
    "preview": "# Integrate Online Boutique with Memorystore (Redis)\n\nBy default the `cartservice` app is serializing the data in an in-"
  },
  {
    "path": "kustomize/components/memorystore/kustomization.yaml",
    "chars": 1310,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/README.md",
    "chars": 3436,
    "preview": "# Secure Online Boutique with Network Policies\n\nYou can use [Network Policies](https://kubernetes.io/docs/concepts/servi"
  },
  {
    "path": "kustomize/components/network-policies/kustomization.yaml",
    "chars": 1114,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-adservice.yaml",
    "chars": 905,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-cartservice.yaml",
    "chars": 980,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-checkoutservice.yaml",
    "chars": 917,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-currencyservice.yaml",
    "chars": 988,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-deny-all.yaml",
    "chars": 718,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-emailservice.yaml",
    "chars": 918,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-frontend.yaml",
    "chars": 786,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-loadgenerator.yaml",
    "chars": 766,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-paymentservice.yaml",
    "chars": 923,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-productcatalogservice.yaml",
    "chars": 1077,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-recommendationservice.yaml",
    "chars": 929,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-redis.yaml",
    "chars": 911,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/network-policies/network-policy-shippingservice.yaml",
    "chars": 989,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/non-public-frontend/README.md",
    "chars": 1008,
    "preview": "# Remove the public exposure of Online Boutique's frontend\n\nBy default, when you deploy Online Boutique, a `Service` (na"
  },
  {
    "path": "kustomize/components/non-public-frontend/kustomization.yaml",
    "chars": 804,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/service-mesh-istio/README.md",
    "chars": 12742,
    "preview": "# Service mesh with Istio\n\nYou can use [Istio](https://istio.io) to enable [service mesh features](https://cloud.google."
  },
  {
    "path": "kustomize/components/service-mesh-istio/allow-egress-googleapis.yaml",
    "chars": 1192,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/service-mesh-istio/frontend-gateway.yaml",
    "chars": 1063,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/service-mesh-istio/frontend.yaml",
    "chars": 817,
    "preview": "# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/service-mesh-istio/kustomization.yaml",
    "chars": 932,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/shopping-assistant/README.md",
    "chars": 5338,
    "preview": "# Shopping Assistant with RAG & AlloyDB\n\nThis demo adds a new service to Online Boutique called `shoppingassistantservic"
  },
  {
    "path": "kustomize/components/shopping-assistant/kustomization.yaml",
    "chars": 951,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/shopping-assistant/scripts/1_deploy_alloydb_infra.sh",
    "chars": 7176,
    "preview": "#!/bin/sh\n#\n# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "kustomize/components/shopping-assistant/scripts/2_create_populate_alloydb_tables.sh",
    "chars": 2912,
    "preview": "#!/bin/sh\n#\n# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may no"
  },
  {
    "path": "kustomize/components/shopping-assistant/scripts/generate_sql_from_products.py",
    "chars": 1666,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/shopping-assistant/shoppingassistantservice.yaml",
    "chars": 2550,
    "preview": "# Copyright 2024 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/single-shared-session/README.md",
    "chars": 1071,
    "preview": "# Manage a single shared session for the Online Boutique apps\n\nBy default, when you deploy this sample app, the Online B"
  },
  {
    "path": "kustomize/components/single-shared-session/kustomization.yaml",
    "chars": 969,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/spanner/README.md",
    "chars": 4328,
    "preview": "# Integrate Online Boutique with Spanner\n\nBy default the `cartservice` stores its data in an in-cluster Redis database.\n"
  },
  {
    "path": "kustomize/components/spanner/kustomization.yaml",
    "chars": 1698,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/components/without-loadgenerator/README.md",
    "chars": 1065,
    "preview": "# Exclude the loadgenerator\n\nBy default, when you deploy Online Boutique, its [loadgenerator](/src/loadgenerator/) will "
  },
  {
    "path": "kustomize/components/without-loadgenerator/delete-loadgenerator.patch.yaml",
    "chars": 84,
    "preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: loadgenerator\n$patch: delete\n"
  },
  {
    "path": "kustomize/components/without-loadgenerator/kustomization.yaml",
    "chars": 686,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/kustomization.yaml",
    "chars": 1183,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/tests/README.md",
    "chars": 210,
    "preview": "This directory contains a list of scenarios (different combinations of Kustomize Components) used for testing.\nSee [/.gi"
  },
  {
    "path": "kustomize/tests/memorystore-with-all-components/kustomization.yaml",
    "chars": 821,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/tests/service-mesh-istio-with-all-components/kustomization.yaml",
    "chars": 828,
    "preview": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "kustomize/tests/spanner-with-all-components/kustomization.yaml",
    "chars": 817,
    "preview": "# Copyright 2022 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "protos/demo.proto",
    "chars": 6155,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "protos/grpc/health/v1/health.proto",
    "chars": 1288,
    "preview": "// Copyright 2015 The gRPC Authors\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not "
  },
  {
    "path": "release/istio-manifests.yaml",
    "chars": 2244,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "release/kubernetes-manifests.yaml",
    "chars": 22522,
    "preview": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "skaffold.yaml",
    "chars": 3412,
    "preview": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "src/adservice/Dockerfile",
    "chars": 1584,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "src/adservice/README.md",
    "chars": 634,
    "preview": "# Ad Service\n\nThe Ad service provides advertisement based on context keys. If no context keys are provided then it retur"
  },
  {
    "path": "src/adservice/build.gradle",
    "chars": 3628,
    "preview": "plugins {\n    id 'com.google.protobuf' version '0.9.6'\n    id 'com.github.sherter.google-java-format' version '0.9'\n    "
  },
  {
    "path": "src/adservice/genproto.sh",
    "chars": 796,
    "preview": "#!/bin/bash -eu\n#\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you "
  },
  {
    "path": "src/adservice/gradle/wrapper/gradle-wrapper.properties",
    "chars": 253,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "src/adservice/gradlew",
    "chars": 8710,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "src/adservice/gradlew.bat",
    "chars": 2843,
    "preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
  },
  {
    "path": "src/adservice/settings.gradle",
    "chars": 33,
    "preview": "rootProject.name = 'hipstershop'\n"
  },
  {
    "path": "src/adservice/src/main/java/hipstershop/AdService.java",
    "chars": 7891,
    "preview": "/*\n * Copyright 2018, Google LLC.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not u"
  },
  {
    "path": "src/adservice/src/main/java/hipstershop/AdServiceClient.java",
    "chars": 3864,
    "preview": "/*\n * Copyright 2018, Google LLC.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not u"
  },
  {
    "path": "src/adservice/src/main/proto/demo.proto",
    "chars": 6069,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/adservice/src/main/resources/log4j2.xml",
    "chars": 1850,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n Copyright 2020 Google LLC\n\n Licensed under the Apache License, Version 2.0 "
  },
  {
    "path": "src/cartservice/cartservice.sln",
    "chars": 2807,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26124.0\nMini"
  },
  {
    "path": "src/cartservice/src/.dockerignore",
    "chars": 52,
    "preview": "**/*.sh\n**/*.bat\n**/bin/\n**/obj/\n**/out/\nDockerfile*"
  },
  {
    "path": "src/cartservice/src/Dockerfile",
    "chars": 1589,
    "preview": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "src/cartservice/src/Dockerfile.debug",
    "chars": 1312,
    "preview": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "src/cartservice/src/Program.cs",
    "chars": 928,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/Startup.cs",
    "chars": 3293,
    "preview": "using System;\nusing Microsoft.AspNetCore.Builder;\nusing Microsoft.AspNetCore.Diagnostics.HealthChecks;\nusing Microsoft.A"
  },
  {
    "path": "src/cartservice/src/appsettings.json",
    "chars": 288,
    "preview": "{\n    \"Logging\": {\n      \"LogLevel\": {\n        \"Default\": \"Information\",\n        \"Microsoft\": \"Warning\",\n        \"Micros"
  },
  {
    "path": "src/cartservice/src/cartservice.csproj",
    "chars": 714,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n  <PropertyGroup>\n    <TargetFramework>net10.0</TargetFramework>\n  </PropertyGrou"
  },
  {
    "path": "src/cartservice/src/cartstore/AlloyDBCartStore.cs",
    "chars": 6653,
    "preview": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/cartstore/ICartStore.cs",
    "chars": 898,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/cartstore/RedisCartStore.cs",
    "chars": 3930,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/cartstore/SpannerCartStore.cs",
    "chars": 7609,
    "preview": "// Copyright 2021 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/protos/Cart.proto",
    "chars": 1210,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/services/CartService.cs",
    "chars": 1707,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/src/services/HealthCheckService.cs",
    "chars": 1415,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/tests/CartServiceTests.cs",
    "chars": 5196,
    "preview": "// Copyright 2018 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/cartservice/tests/cartservice.tests.csproj",
    "chars": 646,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net10.0</TargetFramework>\n\n    <IsPackable>fal"
  },
  {
    "path": "src/checkoutservice/.dockerignore",
    "chars": 8,
    "preview": "vendor/\n"
  },
  {
    "path": "src/checkoutservice/Dockerfile",
    "chars": 1543,
    "preview": "# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
  },
  {
    "path": "src/checkoutservice/README.md",
    "chars": 123,
    "preview": "# checkoutservice\n\nRun the following command to restore dependencies to `vendor/` directory:\n\n    dep ensure --vendor-on"
  },
  {
    "path": "src/checkoutservice/genproto/demo.pb.go",
    "chars": 89615,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/checkoutservice/genproto/demo_grpc.pb.go",
    "chars": 47595,
    "preview": "// Copyright 2020 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use th"
  },
  {
    "path": "src/checkoutservice/genproto.sh",
    "chars": 905,
    "preview": "#!/bin/bash -eu\n#\n# Copyright 2018 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you "
  },
  {
    "path": "src/checkoutservice/go.mod",
    "chars": 2110,
    "preview": "module github.com/GoogleCloudPlatform/microservices-demo/src/checkoutservice\n\ngo 1.25.0\n\ntoolchain go1.26.1\n\nrequire (\n\t"
  },
  {
    "path": "src/checkoutservice/go.sum",
    "chars": 18908,
    "preview": "cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=\ncel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX"
  }
]

// ... and 116 more files (download for full content)

About this extraction

This page contains the full source code of the GoogleCloudPlatform/microservices-demo GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 316 files (1.4 MB), approximately 478.0k tokens, and a symbol index with 2102 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!