diff --git a/.gitignore b/.gitignore index 8b2580f..2952864 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ pkg/ .vscode/ .vs/slnx.sqlite .vs/microservices-demo/v15/.suo +.idea +.skaffold-*.yaml +.kubernetes-manifests-*/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c79b0c2 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +sudo: required + +services: +- docker + +install: +- curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/v0.12.0/skaffold-linux-amd64 +- chmod +x skaffold +- sudo mv skaffold /usr/local/bin + +script: +- skaffold build --profile travis-ci diff --git a/README.md b/README.md index a4b975c..aa6709b 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,72 @@ -# Microservices demo +# Hipster Shop: Cloud-Native Microservices Demo Application This project contains a 10-tier microservices application. The application is a -web-based e-commerce app called “Hipster Shop” where users can browse items, +web-based e-commerce app called **“Hipster Shop”** where users can browse items, add them to the cart, and purchase them. -Google has used this application to demonstrate Kubernetes, GKE, Istio, -Stackdriver, gRPC and similar cloud-native technologies. +**Google uses this application to demonstrate Kubernetes, GKE, Istio, +Stackdriver, gRPC** and similar cloud-native technologies nowadays. -### Running locally +## Screenshots + +| Home Page | Checkout Screen | +|-----------|-----------------| +| [![Screenshot of store homepage](./img/hipster-shop-frontend-1.png)](./img/hipster-shop-frontend-1.png) | [![Screenshot of checkout screen](./img/hipster-shop-frontend-2.png)](./img/hipster-shop-frontend-2.png) | + +## Service Architecture + +**Hipster Shop** is composed of many microservices written in different +languages that talk to each other over gRPC. + +[![Architecture of +microservices](./img/architecture-diagram.png)](./img/architecture-diagram.png) + +Find **Protocol Buffers Descriptions** at the [`./pb` directory](./pb). + +| 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 shipping 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 (hypothetically😇) 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 (hypothetically😇) | +| [emailservice](./src/emailservice) | Python | Sends users an order confirmation email (hypothetically😇). | +| [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. | + + +## Features + +- **[Kubernetes](https://kubernetes.io)/[GKE](https://cloud.google.com/kubernetes-engine/):** + The app is designed to run on Kubernetes (both locally on "Docker for + Desktop", as well as on the cloud with GKE). +- **[gRPC](https://grpc.io):** Microservices use a high volume of gRPC calls to + communicate to each other. +- **[Istio](https://istio.io):** Application works on Istio service mesh. +- **[OpenCensus](https://opencensus.io/) Tracing:** Most services are + instrumented using OpenCensus trace interceptors for gRPC/HTTP. +- **[Stackdriver APM](https://cloud.google.com/stackdriver/):** Many services + are instrumented with **Profiling**, **Tracing** and **Debugging**. In + addition to these, using Istio enables features like Request/Response + **Metrics** and **Context Graph** out of the box. When it is running out of + Google Cloud, this code path remains inactive. +- **[Skaffold](https://github.com/GoogleContainerTools/skaffold):** Application + is deployed to Kubernetes with a single command using Skaffold. +- **Synthetic Load Generation:** The application demo comes with a background + job that creates realistic usage patterns on the website using + [Locust](https://locust.io/) load generator. + +## Installation + +> **Note:** that the first build can take up to 20-30 minutes. Consequent builds +> will be faster. + +### Option 1: Running locally with “Docker for Desktop” + +> 💡 Recommended if you're planning to develop the application. 1. Install tools to run a Kubernetes cluster locally: @@ -28,38 +87,54 @@ Stackdriver, gRPC and similar cloud-native technologies. application frontend should be available at http://localhost:80 on your machine. -### Setup on GKE +### Option 2: Running on Google Kubernetes Engine (GKE) + +> 💡 Recommended for demos and making it available publicly. 1. Install tools specified in the previous section (Docker, kubectl, skaffold) 1. Create a Google Kubernetes Engine cluster and make sure `kubectl` is pointing to the cluster. -1. Enable Google Container Registry (GCR) on your GCP project: + gcloud services enable container.googleapis.com + + gcloud container clusters create demo --enable-autoupgrade \ + --enable-autoscaling --min-nodes=3 --max-nodes=10 --num-nodes=5 + + kubectl get nodes + +2. Enable Google Container Registry (GCR) on your GCP project and configure the + `docker` CLI to authenticate to GCR: gcloud services enable containerregistry.googleapis.com - -1. Configure docker to authenticate to GCR: gcloud auth configure-docker -q -1. Edit `skaffold.yaml`, prepend your GCR registry host (`gcr.io/YOUR_PROJECT/`) - to all `imageName:` fields (or update the existing project name). +3. Set your project ID on image names: -1. Edit the Deployment manifests at `kubernetes-manifests` directory and update - the `image` fields to match the changes you made in the previous step. + - Edit `skaffold.yaml`, update the `imageName:` fields that look like + `gcr.io/[PROJECT_ID]` with your own GCP project ID. -1. Run `skaffold run`. This builds the container - images, pushes them to GCR, and deploys the application to Kubernetes. + - Similarly, edit all Kubernetes Deployment manifests in the + [`./kubernetes-manifests`](./kubernetes-manifests) directory. Find the + `image:` fields with `gcr.io/[...]` and change them to your own GCP project + ID. -1. Find the IP address of your application: +5. Run `skaffold run` from the root of this repository. This command: + - builds the container images + - pushes them to GCR + - applies the `./kubernetes-manifests` deploying the application to + Kubernetes. + +6. Find the IP address of your application, then visit the application on your + browser to confirm installation. kubectl get service frontend-external - then visit the application on your browser to confirm - installation. +### (Optional) Deploying on a Istio-installed cluster -### Istio Deployment +> **Note:** you followed GKE deployment steps above, run `skaffold delete` first +> to delete what's deployed. 1. Create a GKE cluster. @@ -71,14 +146,29 @@ Stackdriver, gRPC and similar cloud-native technologies. kubectl label namespace default istio-injection=enabled -4. Deploy the application with. - -5. Apply the manifests in [`./istio-manifests`](./istio-manifests) directory. +4. Apply the manifests in [`./istio-manifests`](./istio-manifests) directory. kubectl apply -f ./istio-manifests + This is required only once. + +5. Deploy the application with `skaffold run`. + 6. Run `kubectl get pods` to see pods are in a healthy and ready state. +7. Find the IP address of your istio gateway Ingress or Service, and visit the + application. + + INGRESS_HOST="$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')" + + echo "$INGRESS_HOST" + + curl -v "http://$INGRESS_HOST" + --- +**Note to fellow Googlers:** Please fill out the form at +[go/microservices-demo](http://go/microservices-demo) if you are using this +application. + This is not an official Google project. diff --git a/img/architecture-diagram.png b/img/architecture-diagram.png new file mode 100644 index 0000000..91a8a18 Binary files /dev/null and b/img/architecture-diagram.png differ diff --git a/img/hipster-shop-frontend-1.png b/img/hipster-shop-frontend-1.png new file mode 100644 index 0000000..8084e23 Binary files /dev/null and b/img/hipster-shop-frontend-1.png differ diff --git a/img/hipster-shop-frontend-2.png b/img/hipster-shop-frontend-2.png new file mode 100644 index 0000000..c5a686f Binary files /dev/null and b/img/hipster-shop-frontend-2.png differ diff --git a/kubernetes-manifests/adservice.yaml b/kubernetes-manifests/adservice.yaml new file mode 100644 index 0000000..37ee35a --- /dev/null +++ b/kubernetes-manifests/adservice.yaml @@ -0,0 +1,63 @@ +# Copyright 2018 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: extensions/v1beta1 +kind: Deployment +metadata: + name: adservice +spec: + template: + metadata: + labels: + app: adservice + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: gcr.io/microservices-demo-app/adservice + ports: + - containerPort: 9555 + env: + - name: PORT + value: "9555" + resources: + requests: + cpu: 200m + memory: 180Mi + limits: + cpu: 300m + memory: 300Mi + readinessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + exec: + command: ["/bin/grpc_health_probe", "-addr=:9555"] + livenessProbe: + initialDelaySeconds: 20 + periodSeconds: 5 + exec: + command: ["/bin/grpc_health_probe", "-addr=:9555"] +--- +apiVersion: v1 +kind: Service +metadata: + name: adservice +spec: + type: ClusterIP + selector: + app: adservice + ports: + - name: grpc + port: 9555 + targetPort: 9555 diff --git a/kubernetes-manifests/checkoutservice.yaml b/kubernetes-manifests/checkoutservice.yaml index b78048a..23668a6 100644 --- a/kubernetes-manifests/checkoutservice.yaml +++ b/kubernetes-manifests/checkoutservice.yaml @@ -28,13 +28,11 @@ spec: ports: - containerPort: 5050 readinessProbe: - periodSeconds: 5 - tcpSocket: - port: 5050 + exec: + command: ["/bin/grpc_health_probe", "-addr=:5050"] livenessProbe: - periodSeconds: 5 - tcpSocket: - port: 5050 + exec: + command: ["/bin/grpc_health_probe", "-addr=:5050"] env: - name: PRODUCT_CATALOG_SERVICE_ADDR value: "productcatalogservice:3550" diff --git a/kubernetes-manifests/currencyservice.yaml b/kubernetes-manifests/currencyservice.yaml index b08121c..b344b1a 100644 --- a/kubernetes-manifests/currencyservice.yaml +++ b/kubernetes-manifests/currencyservice.yaml @@ -30,13 +30,11 @@ spec: - name: grpc containerPort: 7000 readinessProbe: - periodSeconds: 5 - tcpSocket: - port: 7000 + exec: + command: ["/bin/grpc_health_probe", "-addr=:7000"] livenessProbe: - periodSeconds: 5 - tcpSocket: - port: 7000 + exec: + command: ["/bin/grpc_health_probe", "-addr=:7000"] resources: requests: cpu: 100m diff --git a/kubernetes-manifests/emailservice.yaml b/kubernetes-manifests/emailservice.yaml index 83e9c77..7479c2e 100644 --- a/kubernetes-manifests/emailservice.yaml +++ b/kubernetes-manifests/emailservice.yaml @@ -30,12 +30,12 @@ spec: - containerPort: 8080 readinessProbe: periodSeconds: 5 - tcpSocket: - port: 8080 + exec: + command: ["/bin/grpc_health_probe", "-addr=:8080"] livenessProbe: periodSeconds: 5 - tcpSocket: - port: 8080 + exec: + command: ["/bin/grpc_health_probe", "-addr=:8080"] resources: requests: cpu: 100m diff --git a/kubernetes-manifests/frontend.yaml b/kubernetes-manifests/frontend.yaml index 45cdfee..d921a8f 100644 --- a/kubernetes-manifests/frontend.yaml +++ b/kubernetes-manifests/frontend.yaml @@ -30,7 +30,7 @@ spec: readinessProbe: initialDelaySeconds: 10 httpGet: - path: "/" + path: "/_healthz" port: 8080 httpHeaders: - name: "Cookie" @@ -56,6 +56,8 @@ spec: value: "shippingservice:50051" - name: CHECKOUT_SERVICE_ADDR value: "checkoutservice:5050" + - name: AD_SERVICE_ADDR + value: "adservice:9555" resources: requests: cpu: 100m diff --git a/kubernetes-manifests/paymentservice.yaml b/kubernetes-manifests/paymentservice.yaml index 8664d3c..8f28410 100644 --- a/kubernetes-manifests/paymentservice.yaml +++ b/kubernetes-manifests/paymentservice.yaml @@ -29,13 +29,11 @@ spec: ports: - containerPort: 50051 readinessProbe: - periodSeconds: 5 - tcpSocket: - port: 50051 + exec: + command: ["/bin/grpc_health_probe", "-addr=:50051"] livenessProbe: - periodSeconds: 5 - tcpSocket: - port: 50051 + exec: + command: ["/bin/grpc_health_probe", "-addr=:50051"] resources: requests: cpu: 100m diff --git a/kubernetes-manifests/productcatalogservice.yaml b/kubernetes-manifests/productcatalogservice.yaml index 45b5f88..1196356 100644 --- a/kubernetes-manifests/productcatalogservice.yaml +++ b/kubernetes-manifests/productcatalogservice.yaml @@ -29,13 +29,11 @@ spec: ports: - containerPort: 3550 readinessProbe: - periodSeconds: 5 - tcpSocket: - port: 3550 + exec: + command: ["/bin/grpc_health_probe", "-addr=:3550"] livenessProbe: - periodSeconds: 5 - tcpSocket: - port: 3550 + exec: + command: ["/bin/grpc_health_probe", "-addr=:3550"] resources: requests: cpu: 100m diff --git a/kubernetes-manifests/recommendationservice.yaml b/kubernetes-manifests/recommendationservice.yaml index 18a7103..15be40f 100644 --- a/kubernetes-manifests/recommendationservice.yaml +++ b/kubernetes-manifests/recommendationservice.yaml @@ -30,12 +30,12 @@ spec: - containerPort: 8080 readinessProbe: periodSeconds: 5 - tcpSocket: - port: 8080 + exec: + command: ["/bin/grpc_health_probe", "-addr=:8080"] livenessProbe: periodSeconds: 5 - tcpSocket: - port: 8080 + exec: + command: ["/bin/grpc_health_probe", "-addr=:8080"] env: - name: PRODUCT_CATALOG_SERVICE_ADDR value: "productcatalogservice:3550" diff --git a/kubernetes-manifests/shippingservice.yaml b/kubernetes-manifests/shippingservice.yaml index dd78165..107b574 100644 --- a/kubernetes-manifests/shippingservice.yaml +++ b/kubernetes-manifests/shippingservice.yaml @@ -29,12 +29,11 @@ spec: - containerPort: 50051 readinessProbe: periodSeconds: 5 - tcpSocket: - port: 50051 + exec: + command: ["/bin/grpc_health_probe", "-addr=:50051"] livenessProbe: - periodSeconds: 5 - tcpSocket: - port: 50051 + exec: + command: ["/bin/grpc_health_probe", "-addr=:50051"] resources: requests: cpu: 100m diff --git a/pb/demo.proto b/pb/demo.proto index 0862730..09e5e4d 100644 --- a/pb/demo.proto +++ b/pb/demo.proto @@ -217,3 +217,26 @@ message PlaceOrderRequest { message PlaceOrderResponse { OrderResult order = 1; } + +// ------------Ad service------------------ + +service AdService { + rpc GetAds(AdRequest) returns (AdResponse) {} +} + +message AdRequest { + // List of important key words from the current page describing the context. + repeated string context_keys = 1; +} + +message AdResponse { + repeated Ad ads = 1; +} + +message Ad { + // url to redirect to when an ad is clicked. + string redirect_url = 1; + + // short advertisement text to display. + string text = 2; +} diff --git a/pb/grpc/health/v1/health.proto b/pb/grpc/health/v1/health.proto new file mode 100644 index 0000000..4b4677b --- /dev/null +++ b/pb/grpc/health/v1/health.proto @@ -0,0 +1,43 @@ +// Copyright 2015 The gRPC Authors +// +// 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. + +// The canonical version of this proto can be found at +// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto + +syntax = "proto3"; + +package grpc.health.v1; + +option csharp_namespace = "Grpc.Health.V1"; +option go_package = "google.golang.org/grpc/health/grpc_health_v1"; +option java_multiple_files = true; +option java_outer_classname = "HealthProto"; +option java_package = "io.grpc.health.v1"; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} diff --git a/skaffold.yaml b/skaffold.yaml index a05d6eb..bd04f86 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -38,7 +38,31 @@ build: workspace: src/frontend - imageName: gcr.io/microservices-demo-app/loadgenerator workspace: src/loadgenerator + - imageName: gcr.io/microservices-demo-app/adservice + workspace: src/adservice deploy: kubectl: manifests: - ./kubernetes-manifests/**.yaml + + +profiles: + # "travis-ci" profile is used to build the images without + # pushing them. + - name: travis-ci + build: + local: + skipPush: true + # "gcb" profile allows building and pushing the images + # on Google Container Builder without requiring docker + # installed on the developer machine. However, note that + # since GCB does not cache the builds, each build will + # start from scratch and therefore take a long time. + # + # This is not used by default. To use it, run: + # skaffold run -p gcb + - name: gcb + build: + googleCloudBuild: + diskSizeGb: 300 + machineType: "N1_HIGHCPU_32" diff --git a/src/adservice/.gitignore b/src/adservice/.gitignore new file mode 100644 index 0000000..1809715 --- /dev/null +++ b/src/adservice/.gitignore @@ -0,0 +1,8 @@ +*.iml +*.ipr +*.iws +.gradle/** +.idea/** +build/** + + diff --git a/src/adservice/Dockerfile b/src/adservice/Dockerfile new file mode 100644 index 0000000..1095220 --- /dev/null +++ b/src/adservice/Dockerfile @@ -0,0 +1,16 @@ +FROM openjdk:8 +RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe +WORKDIR /app + +# Next three steps are for caching dependency downloads +# to improve subsequent docker build. +COPY ["build.gradle", "gradlew", "./"] +COPY gradle gradle +RUN ./gradlew downloadRepos + +COPY . . +RUN ./gradlew installDist +EXPOSE 9555 +ENTRYPOINT ["/app/build/install/hipstershop/bin/AdService"] diff --git a/src/adservice/README.md b/src/adservice/README.md new file mode 100644 index 0000000..6d41ec3 --- /dev/null +++ b/src/adservice/README.md @@ -0,0 +1,26 @@ +# Ad Service + +The Ad service provides advertisement based on context keys. If no context keys are provided then it returns random ads. + +## Local Build + +The Ad service uses gradlew to compile/install/distribute. Gradle wrapper is already part of the source code. To build Ad Service, run +``` + cd src/adservice; ./gradlew installDist +``` +It will create executable script src/adservice/build/install/hipstershop/bin/AdService + +### Upgrade gradle version +If you need to upgrade the version of gradle then run +``` + cd src/adservice ; ./gradlew wrapper --gradle-version +``` + +## Docker Build + +From repository root, run: + +``` +docker build --file src/adservice/Dockerfile . +``` + diff --git a/src/adservice/build.gradle b/src/adservice/build.gradle new file mode 100644 index 0000000..47653ac --- /dev/null +++ b/src/adservice/build.gradle @@ -0,0 +1,122 @@ +description = 'Ad Service' + +buildscript { + repositories { + mavenCentral() + mavenLocal() + maven { + url "https://plugins.gradle.org/m2/" + } + } + dependencies { + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.3' + } +} + +apply plugin: 'idea' +apply plugin: 'java' +apply plugin: 'com.google.protobuf' + +repositories { + mavenCentral() + mavenLocal() +} + +group = "adservice" +version = "0.1.0-SNAPSHOT" // CURRENT_OPENCENSUS_VERSION + +def opencensusVersion = "0.15.0" // LATEST_OPENCENSUS_RELEASE_VERSION +def grpcVersion = "1.10.1" // CURRENT_GRPC_VERSION +def prometheusVersion = "0.3.0" + +tasks.withType(JavaCompile) { + sourceCompatibility = '1.8' + targetCompatibility = '1.8' +} + +ext { + speed = project.hasProperty('speed') ? project.getProperty('speed') : false + offlineCompile = new File("$buildDir/output/lib") +} + +dependencies { + if (speed) { + compile fileTree(dir: offlineCompile, include: '*.jar') + } else { + compile "com.google.api.grpc:proto-google-common-protos:1.11.0", + "io.opencensus:opencensus-exporter-stats-prometheus:${opencensusVersion}", + "io.opencensus:opencensus-exporter-stats-stackdriver:${opencensusVersion}", + "io.opencensus:opencensus-exporter-trace-stackdriver:${opencensusVersion}", + "io.opencensus:opencensus-exporter-trace-logging:${opencensusVersion}", + "io.grpc:grpc-protobuf:${grpcVersion}", + "io.grpc:grpc-stub:${grpcVersion}", + "io.grpc:grpc-netty:${grpcVersion}", + "io.grpc:grpc-services:${grpcVersion}", + "io.prometheus:simpleclient_httpserver:${prometheusVersion}" + + runtime "io.opencensus:opencensus-impl:${opencensusVersion}", + "io.netty:netty-tcnative-boringssl-static:2.0.8.Final" + } +} + +protobuf { + protoc { + artifact = 'com.google.protobuf:protoc:3.5.1-1' + } + plugins { + grpc { + artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" + } + } + generateProtoTasks { + all()*.plugins { + grpc {} + } + ofSourceSet('main') + } +} + +// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code. +sourceSets { + main { + java { + srcDirs 'hipstershop' + srcDirs 'build/generated/source/proto/main/java/hipstershop' + srcDirs 'build/generated/source/proto/main/grpc/hipstershop' + } + } +} + +// Provide convenience executables for trying out the examples. +apply plugin: 'application' + +startScripts.enabled = false + +// This to cache dependencies during Docker image building. First build will take time. +// Subsequent build will be incremental. +task downloadRepos(type: Copy) { + from configurations.compile + into offlineCompile + from configurations.runtime + into offlineCompile +} + +task adService(type: CreateStartScripts) { + mainClassName = 'hipstershop.AdService' + applicationName = 'AdService' + outputDir = new File(project.buildDir, 'tmp') + classpath = jar.outputs.files + project.configurations.runtime +} + +task adServiceClient(type: CreateStartScripts) { + mainClassName = 'hipstershop.AdServiceClient' + applicationName = 'AdServiceClient' + outputDir = new File(project.buildDir, 'tmp') + classpath = jar.outputs.files + project.configurations.runtime +} + +applicationDistribution.into('bin') { + from(adService) + from(adServiceClient) + fileMode = 0755 +} diff --git a/src/adservice/genproto.sh b/src/adservice/genproto.sh new file mode 100755 index 0000000..36525e0 --- /dev/null +++ b/src/adservice/genproto.sh @@ -0,0 +1,22 @@ +#!/bin/bash -eu +# +# Copyright 2018 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. + +#!/bin/bash -e + +# protos are needed in adservice folder for compiling during Docker build. + +mkdir -p proto && \ +cp ../../pb/demo.proto src/main/proto diff --git a/src/adservice/gradle/wrapper/gradle-wrapper.jar b/src/adservice/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..01b8bf6 Binary files /dev/null and b/src/adservice/gradle/wrapper/gradle-wrapper.jar differ diff --git a/src/adservice/gradle/wrapper/gradle-wrapper.properties b/src/adservice/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..933b647 --- /dev/null +++ b/src/adservice/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip diff --git a/src/adservice/gradlew b/src/adservice/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/src/adservice/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/src/adservice/gradlew.bat b/src/adservice/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/src/adservice/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/adservice/settings.gradle b/src/adservice/settings.gradle new file mode 100644 index 0000000..0bbe011 --- /dev/null +++ b/src/adservice/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'hipstershop' diff --git a/src/adservice/src/main/java/hipstershop/AdService.java b/src/adservice/src/main/java/hipstershop/AdService.java new file mode 100644 index 0000000..75ce76f --- /dev/null +++ b/src/adservice/src/main/java/hipstershop/AdService.java @@ -0,0 +1,242 @@ +/* + * Copyright 2018, Google LLC. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package hipstershop; + +import com.google.common.collect.ImmutableMap; +import hipstershop.Demo.Ad; +import hipstershop.Demo.AdRequest; +import hipstershop.Demo.AdResponse; +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.StatusRuntimeException; +import io.grpc.health.v1.HealthCheckResponse.ServingStatus; +import io.grpc.stub.StreamObserver; +import io.grpc.services.*; +import io.opencensus.common.Duration; +import io.opencensus.common.Scope; +import io.opencensus.contrib.grpc.metrics.RpcViews; +import io.opencensus.exporter.stats.prometheus.PrometheusStatsCollector; +import io.opencensus.exporter.trace.logging.LoggingTraceExporter; +import io.opencensus.exporter.stats.stackdriver.StackdriverStatsConfiguration; +import io.opencensus.exporter.stats.stackdriver.StackdriverStatsExporter; +import io.opencensus.exporter.trace.stackdriver.StackdriverTraceConfiguration; +import io.opencensus.exporter.trace.stackdriver.StackdriverTraceExporter; +import io.opencensus.trace.AttributeValue; +import io.opencensus.trace.Span; +import io.opencensus.trace.SpanBuilder; +import io.opencensus.trace.Tracer; +import io.opencensus.trace.Tracing; +import io.opencensus.trace.samplers.Samplers; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class AdService { + private static final Logger logger = Logger.getLogger(AdService.class.getName()); + + private static final Tracer tracer = Tracing.getTracer(); + + private int MAX_ADS_TO_SERVE = 2; + private Server server; + private HealthStatusManager healthMgr; + + static final AdService service = new AdService(); + private void start() throws IOException { + int port = Integer.parseInt(System.getenv("PORT")); + healthMgr = new HealthStatusManager(); + + server = ServerBuilder.forPort(port).addService(new AdServiceImpl()) + .addService(healthMgr.getHealthService()).build().start(); + logger.info("Ad Service started, listening on " + port); + Runtime.getRuntime() + .addShutdownHook( + new Thread() { + @Override + public void run() { + // Use stderr here since the logger may have been reset by its JVM shutdown hook. + System.err.println("*** shutting down gRPC ads server since JVM is shutting down"); + AdService.this.stop(); + System.err.println("*** server shut down"); + } + }); + healthMgr.setStatus("", ServingStatus.SERVING); + } + + private void stop() { + if (server != null) { + healthMgr.clearStatus(""); + server.shutdown(); + } + } + + static class AdServiceImpl extends hipstershop.AdServiceGrpc.AdServiceImplBase { + + /** + * Retrieves ads based on context provided in the request {@code AdRequest}. + * + * @param req the request containing context. + * @param responseObserver the stream observer which gets notified with the value of + * {@code AdResponse} + */ + @Override + public void getAds(AdRequest req, StreamObserver responseObserver) { + AdService service = AdService.getInstance(); + Span parentSpan = tracer.getCurrentSpan(); + SpanBuilder spanBuilder = + tracer + .spanBuilderWithExplicitParent("Retrieve Ads", parentSpan) + .setRecordEvents(true) + .setSampler(Samplers.alwaysSample()); + try (Scope scope = spanBuilder.startScopedSpan()) { + Span span = tracer.getCurrentSpan(); + span.putAttribute("method", AttributeValue.stringAttributeValue("getAds")); + List ads = new ArrayList<>(); + logger.info("received ad request (context_words=" + req.getContextKeysCount() + ")"); + if (req.getContextKeysCount() > 0) { + span.addAnnotation( + "Constructing Ads using context", + ImmutableMap.of( + "Context Keys", + AttributeValue.stringAttributeValue(req.getContextKeysList().toString()), + "Context Keys length", + AttributeValue.longAttributeValue(req.getContextKeysCount()))); + for (int i = 0; i < req.getContextKeysCount(); i++) { + Ad ad = service.getAdsByKey(req.getContextKeys(i)); + if (ad != null) { + ads.add(ad); + } + } + } else { + span.addAnnotation("No Context provided. Constructing random Ads."); + ads = service.getDefaultAds(); + } + if (ads.isEmpty()) { + // Serve default ads. + span.addAnnotation("No Ads found based on context. Constructing random Ads."); + ads = service.getDefaultAds(); + } + AdResponse reply = AdResponse.newBuilder().addAllAds(ads).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (StatusRuntimeException e) { + logger.log(Level.WARNING, "GetAds Failed", e.getStatus()); + return; + } + } + } + + static final HashMap cacheMap = new HashMap(); + + Ad getAdsByKey(String key) { + return cacheMap.get(key); + } + + + public List getDefaultAds() { + List ads = new ArrayList<>(MAX_ADS_TO_SERVE); + Object[] keys = cacheMap.keySet().toArray(); + for (int i=0; i { } }); -client.convert(request, function (err, response) { +client.convert(request, (err, response) => { if (err) { console.error(`Error in convert: ${err}`); } else { diff --git a/src/currencyservice/genproto.sh b/src/currencyservice/genproto.sh index ba89804..a9609fd 100755 --- a/src/currencyservice/genproto.sh +++ b/src/currencyservice/genproto.sh @@ -14,7 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash -e # protos are loaded dynamically for node, simply copies over the proto. -mkdir -p proto && \ -cp ../../pb/demo.proto proto \ No newline at end of file +mkdir -p proto +cp -r ../../pb/* ./proto diff --git a/src/currencyservice/package.json b/src/currencyservice/package.json index 5b2f859..1d89148 100644 --- a/src/currencyservice/package.json +++ b/src/currencyservice/package.json @@ -2,17 +2,25 @@ "name": "grpc-currency-service", "version": "0.1.0", "description": "A gRPC currency conversion microservice", - "repository": "TODO", + "repository": "https://github.com/GoogleCloudPlatform/microservices-demo", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "lint": "semistandard *.js" + }, "license": "Apache-2.0", "dependencies": { "@google-cloud/debug-agent": "^2.6.0", "@google-cloud/profiler": "^0.1.14", "@google-cloud/trace-agent": "^2.11.0", + "@grpc/proto-loader": "^0.3.0", "async": "^1.5.2", "google-protobuf": "^3.0.0", "grpc": "^1.0.0", "left-pad": "^1.3.0", "request": "^2.87.0", "xml2js": "^0.4.19" + }, + "devDependencies": { + "semistandard": "^12.0.1" } } diff --git a/src/currencyservice/proto/demo.proto b/src/currencyservice/proto/demo.proto index 0c3fdf2..11b8c29 100644 --- a/src/currencyservice/proto/demo.proto +++ b/src/currencyservice/proto/demo.proto @@ -108,9 +108,9 @@ message ShipOrderResponse { } message Address { - string street_address_1 = 1; - string street_address_2 = 2; - string city= 3; + string street_address = 1; + string city = 2; + string state = 3; string country = 4; int32 zip_code = 5; } @@ -202,21 +202,9 @@ message SendOrderConfirmationRequest { // -------------Checkout service----------------- service CheckoutService { - rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) {} rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {} } -message CreateOrderRequest { - string user_id = 1; - string user_currency = 2; - Address address = 3; -} - -message CreateOrderResponse { - repeated OrderItem items = 1; - Money shipping_cost = 2; -} - message PlaceOrderRequest { string user_id = 1; string user_currency = 2; @@ -229,3 +217,26 @@ message PlaceOrderRequest { message PlaceOrderResponse { OrderResult order = 1; } + +// ------------Ads service------------------ + +service AdsService { + rpc GetAds(AdsRequest) returns (AdsResponse) {} +} + +message AdsRequest { + // List of important key words from the current page describing the context. + repeated string context_keys = 1; +} + +message AdsResponse { + repeated Ad ads = 1; +} + +message Ad { + // url to redirect to when an ad is clicked. + string redirect_url = 1; + + // short advertisement text to display. + string text = 2; +} diff --git a/src/currencyservice/proto/grpc/health/v1/health.proto b/src/currencyservice/proto/grpc/health/v1/health.proto new file mode 100644 index 0000000..4b4677b --- /dev/null +++ b/src/currencyservice/proto/grpc/health/v1/health.proto @@ -0,0 +1,43 @@ +// Copyright 2015 The gRPC Authors +// +// 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. + +// The canonical version of this proto can be found at +// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto + +syntax = "proto3"; + +package grpc.health.v1; + +option csharp_namespace = "Grpc.Health.V1"; +option go_package = "google.golang.org/grpc/health/grpc_health_v1"; +option java_multiple_files = true; +option java_outer_classname = "HealthProto"; +option java_package = "io.grpc.health.v1"; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} diff --git a/src/currencyservice/server.js b/src/currencyservice/server.js index e6b07c8..69f7c3c 100644 --- a/src/currencyservice/server.js +++ b/src/currencyservice/server.js @@ -16,27 +16,49 @@ require('@google-cloud/profiler').start({ serviceContext: { - service: 'currencyservice', - version: '1.0.0' + service: 'currencyservice', + version: '1.0.0' } }); - require('@google-cloud/trace-agent').start(); - require('@google-cloud/debug-agent').start({ +require('@google-cloud/trace-agent').start(); +require('@google-cloud/debug-agent').start({ serviceContext: { service: 'currencyservice', version: 'VERSION' } -}) +}); const path = require('path'); const grpc = require('grpc'); const request = require('request'); const xml2js = require('xml2js'); +const protoLoader = require('@grpc/proto-loader'); + +const MAIN_PROTO_PATH = path.join(__dirname, './proto/demo.proto'); +const HEALTH_PROTO_PATH = path.join(__dirname, './proto/grpc/health/v1/health.proto'); -const PROTO_PATH = path.join(__dirname, './proto/demo.proto'); const PORT = 7000; const DATA_URL = 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'; -const shopProto = grpc.load(PROTO_PATH).hipstershop; + +const shopProto = _loadProto(MAIN_PROTO_PATH).hipstershop; +const healthProto = _loadProto(HEALTH_PROTO_PATH).grpc.health.v1; + +/** + * Helper function that loads a protobuf file. + */ +function _loadProto (path) { + const packageDefinition = protoLoader.loadSync( + path, + { + keepCase: true, + longs: String, + enums: String, + defaults: true, + oneofs: true + } + ); + return grpc.loadPackageDefinition(packageDefinition); +} /** * Helper function that gets currency data from an XML webpage @@ -116,8 +138,8 @@ function convert (call, callback) { nanos: euros.nanos * data[request.to_code] }); - result.units = Math.floor(result.units) - result.nanos = Math.floor(result.nanos) + result.units = Math.floor(result.units); + result.nanos = Math.floor(result.nanos); result.currency_code = request.to_code; console.log(`conversion request successful`); @@ -130,6 +152,13 @@ function convert (call, callback) { } } +/** + * Endpoint for health checks + */ +function check (call, callback) { + callback(null, { status: 'SERVING' }); +} + /** * Starts an RPC server that receives requests for the * CurrencyConverter service at the sample server port @@ -138,6 +167,7 @@ function main () { console.log(`Starting gRPC server on port ${PORT}...`); const server = new grpc.Server(); server.addService(shopProto.CurrencyService.service, {getSupportedCurrencies, convert}); + server.addService(healthProto.Health.service, {check}); server.bind(`0.0.0.0:${PORT}`, grpc.ServerCredentials.createInsecure()); server.start(); } diff --git a/src/emailservice/Dockerfile b/src/emailservice/Dockerfile index e5c0bf3..8395949 100644 --- a/src/emailservice/Dockerfile +++ b/src/emailservice/Dockerfile @@ -1,6 +1,13 @@ + + # Use the grpc.io provided Python image as the base image FROM grpc/python:1.0 +# download the grpc health probe +RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe + # show python logs as they occur ENV PYTHONUNBUFFERED=0 diff --git a/src/emailservice/email_server.py b/src/emailservice/email_server.py index 680591c..a2cb073 100644 --- a/src/emailservice/email_server.py +++ b/src/emailservice/email_server.py @@ -19,13 +19,14 @@ import argparse import os import sys import time - +import grpc from jinja2 import Environment, FileSystemLoader, select_autoescape, TemplateError from google.api_core.exceptions import GoogleAPICallError -import grpc import demo_pb2 import demo_pb2_grpc +from grpc_health.v1 import health_pb2 +from grpc_health.v1 import health_pb2_grpc # from opencensus.trace.ext.grpc import server_interceptor # from opencensus.trace.samplers import always_on @@ -56,7 +57,12 @@ env = Environment( ) template = env.get_template('confirmation.html') -class EmailService(demo_pb2_grpc.EmailServiceServicer): +class BaseEmailService(demo_pb2_grpc.EmailServiceServicer): + def Check(self, request, context): + return health_pb2.HealthCheckResponse( + status=health_pb2.HealthCheckResponse.SERVING) + +class EmailService(BaseEmailService): def __init__(self): raise Exception('cloud mail client not implemented') super().__init__() @@ -79,7 +85,6 @@ class EmailService(demo_pb2_grpc.EmailServiceServicer): "html_body": content } ) - print("Message sent: {}".format(response.rfc822_message_id)) def SendOrderConfirmation(self, request, context): @@ -104,18 +109,30 @@ class EmailService(demo_pb2_grpc.EmailServiceServicer): return demo_pb2.Empty() -class DummyEmailService(demo_pb2_grpc.EmailServiceServicer): +class DummyEmailService(BaseEmailService): def SendOrderConfirmation(self, request, context): print('A request to send order confirmation email to {} has been received.'.format(request.email)) return demo_pb2.Empty() +class HealthCheck(): + def Check(self, request, context): + return health_pb2.HealthCheckResponse( + status=health_pb2.HealthCheckResponse.SERVING) + def start(dummy_mode): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))#, interceptors=(tracer_interceptor,)) + service = None if dummy_mode: - demo_pb2_grpc.add_EmailServiceServicer_to_server(DummyEmailService(), server) + service = DummyEmailService() else: - raise Exception('non-dummy mode not implemented') - server.add_insecure_port('[::]:8080') + raise Exception('non-dummy mode not implemented yet') + + demo_pb2_grpc.add_EmailServiceServicer_to_server(service, server) + health_pb2_grpc.add_HealthServicer_to_server(service, server) + + port = os.environ.get('PORT', "8080") + print("listening on port: "+port) + server.add_insecure_port('[::]:'+port) server.start() try: while True: @@ -125,5 +142,5 @@ def start(dummy_mode): if __name__ == '__main__': - print('Starting the email service in dummy mode.') + print('starting the email service in dummy mode.') start(dummy_mode = True) diff --git a/src/emailservice/requirements.txt b/src/emailservice/requirements.txt index 5c65c2b..de1567e 100644 --- a/src/emailservice/requirements.txt +++ b/src/emailservice/requirements.txt @@ -9,6 +9,7 @@ google-cloud-trace==0.19.0 googleapis-common-protos==1.5.3 grpc-google-iam-v1==0.11.4 grpcio==1.12.1 +grpcio-health-checking==1.14.1 grpcio-tools==1.12.1 idna==2.7 Jinja2==2.10 diff --git a/src/frontend/.dockerignore b/src/frontend/.dockerignore new file mode 100644 index 0000000..48b8bf9 --- /dev/null +++ b/src/frontend/.dockerignore @@ -0,0 +1 @@ +vendor/ diff --git a/src/frontend/Gopkg.lock b/src/frontend/Gopkg.lock index 3a9ed24..52f77b9 100644 --- a/src/frontend/Gopkg.lock +++ b/src/frontend/Gopkg.lock @@ -2,27 +2,43 @@ [[projects]] + digest = "1:467af0aad47996b25b838d6f14c8371123a8a76ec239020a6c5894e1f8f60272" name = "cloud.google.com/go" packages = [ "compute/metadata", "internal/version", "monitoring/apiv3", "profiler", - "trace/apiv2" + "trace/apiv2", ] - revision = "aad3f485ee528456e0768f20397b4d9dd941e755" - version = "v0.25.0" + pruneopts = "UT" + revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad" + version = "v0.27.0" [[projects]] + digest = "1:9fe70def8f0ceb3d455a0acad9dadd6632287cdbf9c8c2ea50e8dabe2ade40c4" name = "contrib.go.opencensus.io/exporter/stackdriver" packages = [ ".", - "propagation" + "propagation", ] + pruneopts = "UT" revision = "37aa2801fbf0205003e15636096ebf0373510288" version = "v0.5.0" [[projects]] + branch = "master" + digest = "1:3ef905a7059a17712b7b27315692992f84f356e828d38f6ff0624e04103ec675" + name = "github.com/GoogleCloudPlatform/microservices-demo" + packages = [ + "src/frontend/genproto", + "src/frontend/money", + ] + pruneopts = "UT" + revision = "6d969441585ade8c91c235115c7cdb12ac61354f" + +[[projects]] + digest = "1:72856926f8208767b837bf51e3373f49139f61889b67dc7fd3c2a0fd711e3f7a" name = "github.com/golang/protobuf" packages = [ "proto", @@ -33,54 +49,70 @@ "ptypes/empty", "ptypes/struct", "ptypes/timestamp", - "ptypes/wrappers" + "ptypes/wrappers", ] - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" + pruneopts = "UT" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" [[projects]] branch = "master" + digest = "1:089d56c0adb79140365b5c86815ce97233986da6f3a525c6b706773e4b83876f" name = "github.com/google/pprof" packages = ["profile"] - revision = "ef437552946f69f7e3bdf1fd81c385c29530944d" + pruneopts = "UT" + revision = "84b7d314e22c8d12334e52726f68517973b6027b" [[projects]] + digest = "1:3a26588bc48b96825977c1b3df964f8fd842cd6860cc26370588d3563433cf11" name = "github.com/google/uuid" packages = ["."] - revision = "064e2069ce9c359c118179501254f67d7d37ba24" - version = "0.2" + pruneopts = "UT" + revision = "d460ce9f8df2e77fb1ba55ca87fafed96c607494" + version = "v1.0.0" [[projects]] + digest = "1:e145e9710a10bc114a6d3e2738aadf8de146adaa031854ffdf7bbfe15da85e63" name = "github.com/googleapis/gax-go" packages = ["."] + pruneopts = "UT" revision = "317e0006254c44a0ac427cc52a0e083ff0b9622f" version = "v2.0.0" [[projects]] + digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "UT" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "UT" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" version = "v1.6.2" [[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "UT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:d867dfa6751c8d7a435821ad3b736310c2ed68945d05b50fb9d23aee0540c8cc" name = "github.com/sirupsen/logrus" packages = ["."] + pruneopts = "UT" revision = "3e01752db0189b9157070a0e1668a620f9a85da2" version = "v1.0.6" [[projects]] + digest = "1:1bb914cfb78f68f488a91cd7872d3d06a5f83c5bbacf0296dbef44e120b00a91" name = "go.opencensus.io" packages = [ ".", @@ -95,19 +127,24 @@ "tag", "trace", "trace/internal", - "trace/propagation" + "trace/propagation", + "trace/tracestate", ] - revision = "e262766cd0d230a1bb7c37281e345e465f19b41b" - version = "v0.14.0" + pruneopts = "UT" + revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89" + version = "v0.16.0" [[projects]] branch = "master" + digest = "1:3f3a05ae0b95893d90b9b3b5afdb79a9b3d96e4e36e099d841ae602e4aca0da8" name = "golang.org/x/crypto" packages = ["ssh/terminal"] - revision = "56440b844dfe139a8ac053f4ecac0b20b79058f4" + pruneopts = "UT" + revision = "0e37d006457bf46f9e6692014ba72ef82c33022c" [[projects]] branch = "master" + digest = "1:1c14517b2f106c61d75006199b46a46576058661d469658cb0f90739919641d2" name = "golang.org/x/net" packages = [ "context", @@ -117,38 +154,46 @@ "http2/hpack", "idna", "internal/timeseries", - "trace" + "trace", ] - revision = "f4c29de78a2a91c00474a2e689954305c350adf9" + pruneopts = "UT" + revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" [[projects]] branch = "master" + digest = "1:f645667d687fc8bf228865a2c5455824ef05bad08841e673673ef2bb89ac5b90" name = "golang.org/x/oauth2" packages = [ ".", "google", "internal", "jws", - "jwt" + "jwt", ] - revision = "3d292e4d0cdc3a0113e6d207bb137145ef1de42f" + pruneopts = "UT" + revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9" [[projects]] branch = "master" + digest = "1:e0140c0c868c6e0f01c0380865194592c011fe521d6e12d78bfd33e756fe018a" name = "golang.org/x/sync" packages = ["semaphore"] + pruneopts = "UT" revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" [[projects]] branch = "master" + digest = "1:374fc90fcb026e9a367e3fad29e988e5dd944b68ca3f24a184d77abc5307dda4" name = "golang.org/x/sys" packages = [ "unix", - "windows" + "windows", ] - revision = "0ffbfd41fbef8ffcf9b62b0b0aa3a5873ed7a4fe" + pruneopts = "UT" + revision = "d0be0721c37eeb5299f245a996a483160fc36940" [[projects]] + digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" name = "golang.org/x/text" packages = [ "collate", @@ -164,13 +209,15 @@ "unicode/bidi", "unicode/cldr", "unicode/norm", - "unicode/rangetable" + "unicode/rangetable", ] + pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] branch = "master" + digest = "1:e9e388241f9f0f02000dddaeeb91153d53f0cd09dcec33879cc7e043a2e65d75" name = "google.golang.org/api" packages = [ "googleapi/transport", @@ -180,11 +227,13 @@ "support/bundler", "transport", "transport/grpc", - "transport/http" + "transport/http", ] - revision = "f6d94689cbd71030af1108ddac733886fcae1d75" + pruneopts = "UT" + revision = "7ca32eb868bf53ea2fc406698eb98583a8073d19" [[projects]] + digest = "1:26619fcd2452b4044174d26acd8b09c09dffee9a1c3a22d2383b873aa9a0131f" name = "google.golang.org/appengine" packages = [ ".", @@ -198,13 +247,15 @@ "internal/socket", "internal/urlfetch", "socket", - "urlfetch" + "urlfetch", ] + pruneopts = "UT" revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" version = "v1.1.0" [[projects]] branch = "master" + digest = "1:7a3da01a8f840fbbef1f027dc091ae52a29c6ab9374e126b6bdc5bf3b0ff2687" name = "google.golang.org/genproto" packages = [ "googleapis/api/annotations", @@ -217,11 +268,13 @@ "googleapis/monitoring/v3", "googleapis/rpc/errdetails", "googleapis/rpc/status", - "protobuf/field_mask" + "protobuf/field_mask", ] - revision = "daca94659cb50e9f37c1b834680f2e46358f10b0" + pruneopts = "UT" + revision = "36d5787dc5356b711fe8f3040271aaf51c35955b" [[projects]] + digest = "1:4ad047d772a7d4841687df9534a767a9e243885ed6d7d2ced6af5995a5dc44b3" name = "google.golang.org/grpc" packages = [ ".", @@ -250,14 +303,32 @@ "resolver/passthrough", "stats", "status", - "tap" + "tap", ] - revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455" - version = "v1.14.0" + pruneopts = "UT" + revision = "8dea3dc473e90c8179e519d91302d0597c0ca1d1" + version = "v1.15.0" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "806889b149b944cab2216a1769d221df9ca18b3b62fb5040e40737fd0550084c" + input-imports = [ + "cloud.google.com/go/profiler", + "contrib.go.opencensus.io/exporter/stackdriver", + "github.com/GoogleCloudPlatform/microservices-demo/src/frontend/genproto", + "github.com/GoogleCloudPlatform/microservices-demo/src/frontend/money", + "github.com/golang/protobuf/proto", + "github.com/google/uuid", + "github.com/gorilla/mux", + "github.com/pkg/errors", + "github.com/sirupsen/logrus", + "go.opencensus.io/plugin/ocgrpc", + "go.opencensus.io/plugin/ochttp", + "go.opencensus.io/plugin/ochttp/propagation/b3", + "go.opencensus.io/stats/view", + "go.opencensus.io/trace", + "golang.org/x/net/context", + "google.golang.org/grpc", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/src/frontend/Gopkg.toml b/src/frontend/Gopkg.toml index b328524..b90cfbb 100644 --- a/src/frontend/Gopkg.toml +++ b/src/frontend/Gopkg.toml @@ -27,19 +27,23 @@ [[constraint]] name = "cloud.google.com/go" - version = "0.25.0" + version = "0.27.0" [[constraint]] name = "contrib.go.opencensus.io/exporter/stackdriver" version = "0.5.0" +[[constraint]] + branch = "master" + name = "github.com/GoogleCloudPlatform/microservices-demo" + [[constraint]] name = "github.com/golang/protobuf" - version = "1.1.0" + version = "1.2.0" [[constraint]] name = "github.com/google/uuid" - version = "0.2.0" + version = "1.0.0" [[constraint]] name = "github.com/gorilla/mux" @@ -55,7 +59,7 @@ [[constraint]] name = "go.opencensus.io" - version = "0.14.0" + version = "0.16.0" [[constraint]] branch = "master" @@ -63,7 +67,7 @@ [[constraint]] name = "google.golang.org/grpc" - version = "1.14.0" + version = "1.15.0" [prune] go-tests = true diff --git a/src/frontend/genproto/demo.pb.go b/src/frontend/genproto/demo.pb.go index 4fdb7f0..e63e268 100644 --- a/src/frontend/genproto/demo.pb.go +++ b/src/frontend/genproto/demo.pb.go @@ -24,8 +24,8 @@ var _ = math.Inf const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package type CartItem struct { - ProductId string `protobuf:"bytes,1,opt,name=product_id,json=productId" json:"product_id,omitempty"` - Quantity int32 `protobuf:"varint,2,opt,name=quantity" json:"quantity,omitempty"` + ProductId string `protobuf:"bytes,1,opt,name=product_id,json=productId,proto3" json:"product_id,omitempty"` + Quantity int32 `protobuf:"varint,2,opt,name=quantity,proto3" json:"quantity,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -35,7 +35,7 @@ func (m *CartItem) Reset() { *m = CartItem{} } func (m *CartItem) String() string { return proto.CompactTextString(m) } func (*CartItem) ProtoMessage() {} func (*CartItem) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{0} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{0} } func (m *CartItem) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CartItem.Unmarshal(m, b) @@ -70,8 +70,8 @@ func (m *CartItem) GetQuantity() int32 { } type AddItemRequest struct { - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId" json:"user_id,omitempty"` - Item *CartItem `protobuf:"bytes,2,opt,name=item" json:"item,omitempty"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Item *CartItem `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -81,7 +81,7 @@ func (m *AddItemRequest) Reset() { *m = AddItemRequest{} } func (m *AddItemRequest) String() string { return proto.CompactTextString(m) } func (*AddItemRequest) ProtoMessage() {} func (*AddItemRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{1} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{1} } func (m *AddItemRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_AddItemRequest.Unmarshal(m, b) @@ -116,7 +116,7 @@ func (m *AddItemRequest) GetItem() *CartItem { } type EmptyCartRequest struct { - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId" json:"user_id,omitempty"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -126,7 +126,7 @@ func (m *EmptyCartRequest) Reset() { *m = EmptyCartRequest{} } func (m *EmptyCartRequest) String() string { return proto.CompactTextString(m) } func (*EmptyCartRequest) ProtoMessage() {} func (*EmptyCartRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{2} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{2} } func (m *EmptyCartRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_EmptyCartRequest.Unmarshal(m, b) @@ -154,7 +154,7 @@ func (m *EmptyCartRequest) GetUserId() string { } type GetCartRequest struct { - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId" json:"user_id,omitempty"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -164,7 +164,7 @@ func (m *GetCartRequest) Reset() { *m = GetCartRequest{} } func (m *GetCartRequest) String() string { return proto.CompactTextString(m) } func (*GetCartRequest) ProtoMessage() {} func (*GetCartRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{3} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{3} } func (m *GetCartRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetCartRequest.Unmarshal(m, b) @@ -192,8 +192,8 @@ func (m *GetCartRequest) GetUserId() string { } type Cart struct { - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId" json:"user_id,omitempty"` - Items []*CartItem `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + Items []*CartItem `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -203,7 +203,7 @@ func (m *Cart) Reset() { *m = Cart{} } func (m *Cart) String() string { return proto.CompactTextString(m) } func (*Cart) ProtoMessage() {} func (*Cart) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{4} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{4} } func (m *Cart) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Cart.Unmarshal(m, b) @@ -247,7 +247,7 @@ func (m *Empty) Reset() { *m = Empty{} } func (m *Empty) String() string { return proto.CompactTextString(m) } func (*Empty) ProtoMessage() {} func (*Empty) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{5} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{5} } func (m *Empty) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Empty.Unmarshal(m, b) @@ -268,8 +268,8 @@ func (m *Empty) XXX_DiscardUnknown() { var xxx_messageInfo_Empty proto.InternalMessageInfo type ListRecommendationsRequest struct { - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId" json:"user_id,omitempty"` - ProductIds []string `protobuf:"bytes,2,rep,name=product_ids,json=productIds" json:"product_ids,omitempty"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + ProductIds []string `protobuf:"bytes,2,rep,name=product_ids,json=productIds,proto3" json:"product_ids,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -279,7 +279,7 @@ func (m *ListRecommendationsRequest) Reset() { *m = ListRecommendationsR func (m *ListRecommendationsRequest) String() string { return proto.CompactTextString(m) } func (*ListRecommendationsRequest) ProtoMessage() {} func (*ListRecommendationsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{6} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{6} } func (m *ListRecommendationsRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ListRecommendationsRequest.Unmarshal(m, b) @@ -314,7 +314,7 @@ func (m *ListRecommendationsRequest) GetProductIds() []string { } type ListRecommendationsResponse struct { - ProductIds []string `protobuf:"bytes,1,rep,name=product_ids,json=productIds" json:"product_ids,omitempty"` + ProductIds []string `protobuf:"bytes,1,rep,name=product_ids,json=productIds,proto3" json:"product_ids,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -324,7 +324,7 @@ func (m *ListRecommendationsResponse) Reset() { *m = ListRecommendations func (m *ListRecommendationsResponse) String() string { return proto.CompactTextString(m) } func (*ListRecommendationsResponse) ProtoMessage() {} func (*ListRecommendationsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{7} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{7} } func (m *ListRecommendationsResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ListRecommendationsResponse.Unmarshal(m, b) @@ -352,11 +352,11 @@ func (m *ListRecommendationsResponse) GetProductIds() []string { } type Product struct { - Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description" json:"description,omitempty"` - Picture string `protobuf:"bytes,4,opt,name=picture" json:"picture,omitempty"` - PriceUsd *Money `protobuf:"bytes,5,opt,name=price_usd,json=priceUsd" json:"price_usd,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + Picture string `protobuf:"bytes,4,opt,name=picture,proto3" json:"picture,omitempty"` + PriceUsd *Money `protobuf:"bytes,5,opt,name=price_usd,json=priceUsd,proto3" json:"price_usd,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -366,7 +366,7 @@ func (m *Product) Reset() { *m = Product{} } func (m *Product) String() string { return proto.CompactTextString(m) } func (*Product) ProtoMessage() {} func (*Product) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{8} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{8} } func (m *Product) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Product.Unmarshal(m, b) @@ -422,7 +422,7 @@ func (m *Product) GetPriceUsd() *Money { } type ListProductsResponse struct { - Products []*Product `protobuf:"bytes,1,rep,name=products" json:"products,omitempty"` + Products []*Product `protobuf:"bytes,1,rep,name=products,proto3" json:"products,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -432,7 +432,7 @@ func (m *ListProductsResponse) Reset() { *m = ListProductsResponse{} } func (m *ListProductsResponse) String() string { return proto.CompactTextString(m) } func (*ListProductsResponse) ProtoMessage() {} func (*ListProductsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{9} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{9} } func (m *ListProductsResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ListProductsResponse.Unmarshal(m, b) @@ -460,7 +460,7 @@ func (m *ListProductsResponse) GetProducts() []*Product { } type GetProductRequest struct { - Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -470,7 +470,7 @@ func (m *GetProductRequest) Reset() { *m = GetProductRequest{} } func (m *GetProductRequest) String() string { return proto.CompactTextString(m) } func (*GetProductRequest) ProtoMessage() {} func (*GetProductRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{10} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{10} } func (m *GetProductRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetProductRequest.Unmarshal(m, b) @@ -498,7 +498,7 @@ func (m *GetProductRequest) GetId() string { } type SearchProductsRequest struct { - Query string `protobuf:"bytes,1,opt,name=query" json:"query,omitempty"` + Query string `protobuf:"bytes,1,opt,name=query,proto3" json:"query,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -508,7 +508,7 @@ func (m *SearchProductsRequest) Reset() { *m = SearchProductsRequest{} } func (m *SearchProductsRequest) String() string { return proto.CompactTextString(m) } func (*SearchProductsRequest) ProtoMessage() {} func (*SearchProductsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{11} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{11} } func (m *SearchProductsRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SearchProductsRequest.Unmarshal(m, b) @@ -536,7 +536,7 @@ func (m *SearchProductsRequest) GetQuery() string { } type SearchProductsResponse struct { - Results []*Product `protobuf:"bytes,1,rep,name=results" json:"results,omitempty"` + Results []*Product `protobuf:"bytes,1,rep,name=results,proto3" json:"results,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -546,7 +546,7 @@ func (m *SearchProductsResponse) Reset() { *m = SearchProductsResponse{} func (m *SearchProductsResponse) String() string { return proto.CompactTextString(m) } func (*SearchProductsResponse) ProtoMessage() {} func (*SearchProductsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{12} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{12} } func (m *SearchProductsResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SearchProductsResponse.Unmarshal(m, b) @@ -574,8 +574,8 @@ func (m *SearchProductsResponse) GetResults() []*Product { } type GetQuoteRequest struct { - Address *Address `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` - Items []*CartItem `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"` + Address *Address `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Items []*CartItem `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -585,7 +585,7 @@ func (m *GetQuoteRequest) Reset() { *m = GetQuoteRequest{} } func (m *GetQuoteRequest) String() string { return proto.CompactTextString(m) } func (*GetQuoteRequest) ProtoMessage() {} func (*GetQuoteRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{13} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{13} } func (m *GetQuoteRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetQuoteRequest.Unmarshal(m, b) @@ -620,7 +620,7 @@ func (m *GetQuoteRequest) GetItems() []*CartItem { } type GetQuoteResponse struct { - CostUsd *Money `protobuf:"bytes,1,opt,name=cost_usd,json=costUsd" json:"cost_usd,omitempty"` + CostUsd *Money `protobuf:"bytes,1,opt,name=cost_usd,json=costUsd,proto3" json:"cost_usd,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -630,7 +630,7 @@ func (m *GetQuoteResponse) Reset() { *m = GetQuoteResponse{} } func (m *GetQuoteResponse) String() string { return proto.CompactTextString(m) } func (*GetQuoteResponse) ProtoMessage() {} func (*GetQuoteResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{14} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{14} } func (m *GetQuoteResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetQuoteResponse.Unmarshal(m, b) @@ -658,8 +658,8 @@ func (m *GetQuoteResponse) GetCostUsd() *Money { } type ShipOrderRequest struct { - Address *Address `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` - Items []*CartItem `protobuf:"bytes,2,rep,name=items" json:"items,omitempty"` + Address *Address `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Items []*CartItem `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -669,7 +669,7 @@ func (m *ShipOrderRequest) Reset() { *m = ShipOrderRequest{} } func (m *ShipOrderRequest) String() string { return proto.CompactTextString(m) } func (*ShipOrderRequest) ProtoMessage() {} func (*ShipOrderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{15} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{15} } func (m *ShipOrderRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ShipOrderRequest.Unmarshal(m, b) @@ -704,7 +704,7 @@ func (m *ShipOrderRequest) GetItems() []*CartItem { } type ShipOrderResponse struct { - TrackingId string `protobuf:"bytes,1,opt,name=tracking_id,json=trackingId" json:"tracking_id,omitempty"` + TrackingId string `protobuf:"bytes,1,opt,name=tracking_id,json=trackingId,proto3" json:"tracking_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -714,7 +714,7 @@ func (m *ShipOrderResponse) Reset() { *m = ShipOrderResponse{} } func (m *ShipOrderResponse) String() string { return proto.CompactTextString(m) } func (*ShipOrderResponse) ProtoMessage() {} func (*ShipOrderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{16} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{16} } func (m *ShipOrderResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ShipOrderResponse.Unmarshal(m, b) @@ -742,11 +742,11 @@ func (m *ShipOrderResponse) GetTrackingId() string { } type Address struct { - StreetAddress string `protobuf:"bytes,1,opt,name=street_address,json=streetAddress" json:"street_address,omitempty"` - City string `protobuf:"bytes,2,opt,name=city" json:"city,omitempty"` - State string `protobuf:"bytes,3,opt,name=state" json:"state,omitempty"` - Country string `protobuf:"bytes,4,opt,name=country" json:"country,omitempty"` - ZipCode int32 `protobuf:"varint,5,opt,name=zip_code,json=zipCode" json:"zip_code,omitempty"` + StreetAddress string `protobuf:"bytes,1,opt,name=street_address,json=streetAddress,proto3" json:"street_address,omitempty"` + City string `protobuf:"bytes,2,opt,name=city,proto3" json:"city,omitempty"` + State string `protobuf:"bytes,3,opt,name=state,proto3" json:"state,omitempty"` + Country string `protobuf:"bytes,4,opt,name=country,proto3" json:"country,omitempty"` + ZipCode int32 `protobuf:"varint,5,opt,name=zip_code,json=zipCode,proto3" json:"zip_code,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -756,7 +756,7 @@ func (m *Address) Reset() { *m = Address{} } func (m *Address) String() string { return proto.CompactTextString(m) } func (*Address) ProtoMessage() {} func (*Address) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{17} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{17} } func (m *Address) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Address.Unmarshal(m, b) @@ -814,17 +814,17 @@ func (m *Address) GetZipCode() int32 { // Represents an amount of money with its currency type. type Money struct { // The 3-letter currency code defined in ISO 4217. - CurrencyCode string `protobuf:"bytes,1,opt,name=currency_code,json=currencyCode" json:"currency_code,omitempty"` + CurrencyCode string `protobuf:"bytes,1,opt,name=currency_code,json=currencyCode,proto3" json:"currency_code,omitempty"` // The whole units of the amount. // For example if `currencyCode` is `"USD"`, then 1 unit is one US dollar. - Units int64 `protobuf:"varint,2,opt,name=units" json:"units,omitempty"` + Units int64 `protobuf:"varint,2,opt,name=units,proto3" json:"units,omitempty"` // Number of nano (10^-9) units of the amount. // The value must be between -999,999,999 and +999,999,999 inclusive. // If `units` is positive, `nanos` must be positive or zero. // If `units` is zero, `nanos` can be positive, zero, or negative. // If `units` is negative, `nanos` must be negative or zero. // For example $-1.75 is represented as `units`=-1 and `nanos`=-750,000,000. - Nanos int32 `protobuf:"varint,3,opt,name=nanos" json:"nanos,omitempty"` + Nanos int32 `protobuf:"varint,3,opt,name=nanos,proto3" json:"nanos,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -834,7 +834,7 @@ func (m *Money) Reset() { *m = Money{} } func (m *Money) String() string { return proto.CompactTextString(m) } func (*Money) ProtoMessage() {} func (*Money) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{18} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{18} } func (m *Money) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Money.Unmarshal(m, b) @@ -877,7 +877,7 @@ func (m *Money) GetNanos() int32 { type GetSupportedCurrenciesResponse struct { // The 3-letter currency code defined in ISO 4217. - CurrencyCodes []string `protobuf:"bytes,1,rep,name=currency_codes,json=currencyCodes" json:"currency_codes,omitempty"` + CurrencyCodes []string `protobuf:"bytes,1,rep,name=currency_codes,json=currencyCodes,proto3" json:"currency_codes,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -887,7 +887,7 @@ func (m *GetSupportedCurrenciesResponse) Reset() { *m = GetSupportedCurr func (m *GetSupportedCurrenciesResponse) String() string { return proto.CompactTextString(m) } func (*GetSupportedCurrenciesResponse) ProtoMessage() {} func (*GetSupportedCurrenciesResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{19} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{19} } func (m *GetSupportedCurrenciesResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_GetSupportedCurrenciesResponse.Unmarshal(m, b) @@ -915,9 +915,9 @@ func (m *GetSupportedCurrenciesResponse) GetCurrencyCodes() []string { } type CurrencyConversionRequest struct { - From *Money `protobuf:"bytes,1,opt,name=from" json:"from,omitempty"` + From *Money `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // The 3-letter currency code defined in ISO 4217. - ToCode string `protobuf:"bytes,2,opt,name=to_code,json=toCode" json:"to_code,omitempty"` + ToCode string `protobuf:"bytes,2,opt,name=to_code,json=toCode,proto3" json:"to_code,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -927,7 +927,7 @@ func (m *CurrencyConversionRequest) Reset() { *m = CurrencyConversionReq func (m *CurrencyConversionRequest) String() string { return proto.CompactTextString(m) } func (*CurrencyConversionRequest) ProtoMessage() {} func (*CurrencyConversionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{20} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{20} } func (m *CurrencyConversionRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CurrencyConversionRequest.Unmarshal(m, b) @@ -962,10 +962,10 @@ func (m *CurrencyConversionRequest) GetToCode() string { } type CreditCardInfo struct { - CreditCardNumber string `protobuf:"bytes,1,opt,name=credit_card_number,json=creditCardNumber" json:"credit_card_number,omitempty"` - CreditCardCvv int32 `protobuf:"varint,2,opt,name=credit_card_cvv,json=creditCardCvv" json:"credit_card_cvv,omitempty"` - CreditCardExpirationYear int32 `protobuf:"varint,3,opt,name=credit_card_expiration_year,json=creditCardExpirationYear" json:"credit_card_expiration_year,omitempty"` - CreditCardExpirationMonth int32 `protobuf:"varint,4,opt,name=credit_card_expiration_month,json=creditCardExpirationMonth" json:"credit_card_expiration_month,omitempty"` + CreditCardNumber string `protobuf:"bytes,1,opt,name=credit_card_number,json=creditCardNumber,proto3" json:"credit_card_number,omitempty"` + CreditCardCvv int32 `protobuf:"varint,2,opt,name=credit_card_cvv,json=creditCardCvv,proto3" json:"credit_card_cvv,omitempty"` + CreditCardExpirationYear int32 `protobuf:"varint,3,opt,name=credit_card_expiration_year,json=creditCardExpirationYear,proto3" json:"credit_card_expiration_year,omitempty"` + CreditCardExpirationMonth int32 `protobuf:"varint,4,opt,name=credit_card_expiration_month,json=creditCardExpirationMonth,proto3" json:"credit_card_expiration_month,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -975,7 +975,7 @@ func (m *CreditCardInfo) Reset() { *m = CreditCardInfo{} } func (m *CreditCardInfo) String() string { return proto.CompactTextString(m) } func (*CreditCardInfo) ProtoMessage() {} func (*CreditCardInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{21} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{21} } func (m *CreditCardInfo) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_CreditCardInfo.Unmarshal(m, b) @@ -1024,8 +1024,8 @@ func (m *CreditCardInfo) GetCreditCardExpirationMonth() int32 { } type ChargeRequest struct { - Amount *Money `protobuf:"bytes,1,opt,name=amount" json:"amount,omitempty"` - CreditCard *CreditCardInfo `protobuf:"bytes,2,opt,name=credit_card,json=creditCard" json:"credit_card,omitempty"` + Amount *Money `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + CreditCard *CreditCardInfo `protobuf:"bytes,2,opt,name=credit_card,json=creditCard,proto3" json:"credit_card,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1035,7 +1035,7 @@ func (m *ChargeRequest) Reset() { *m = ChargeRequest{} } func (m *ChargeRequest) String() string { return proto.CompactTextString(m) } func (*ChargeRequest) ProtoMessage() {} func (*ChargeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{22} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{22} } func (m *ChargeRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ChargeRequest.Unmarshal(m, b) @@ -1070,7 +1070,7 @@ func (m *ChargeRequest) GetCreditCard() *CreditCardInfo { } type ChargeResponse struct { - TransactionId string `protobuf:"bytes,1,opt,name=transaction_id,json=transactionId" json:"transaction_id,omitempty"` + TransactionId string `protobuf:"bytes,1,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1080,7 +1080,7 @@ func (m *ChargeResponse) Reset() { *m = ChargeResponse{} } func (m *ChargeResponse) String() string { return proto.CompactTextString(m) } func (*ChargeResponse) ProtoMessage() {} func (*ChargeResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{23} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{23} } func (m *ChargeResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_ChargeResponse.Unmarshal(m, b) @@ -1108,8 +1108,8 @@ func (m *ChargeResponse) GetTransactionId() string { } type OrderItem struct { - Item *CartItem `protobuf:"bytes,1,opt,name=item" json:"item,omitempty"` - Cost *Money `protobuf:"bytes,2,opt,name=cost" json:"cost,omitempty"` + Item *CartItem `protobuf:"bytes,1,opt,name=item,proto3" json:"item,omitempty"` + Cost *Money `protobuf:"bytes,2,opt,name=cost,proto3" json:"cost,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1119,7 +1119,7 @@ func (m *OrderItem) Reset() { *m = OrderItem{} } func (m *OrderItem) String() string { return proto.CompactTextString(m) } func (*OrderItem) ProtoMessage() {} func (*OrderItem) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{24} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{24} } func (m *OrderItem) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_OrderItem.Unmarshal(m, b) @@ -1154,11 +1154,11 @@ func (m *OrderItem) GetCost() *Money { } type OrderResult struct { - OrderId string `protobuf:"bytes,1,opt,name=order_id,json=orderId" json:"order_id,omitempty"` - ShippingTrackingId string `protobuf:"bytes,2,opt,name=shipping_tracking_id,json=shippingTrackingId" json:"shipping_tracking_id,omitempty"` - ShippingCost *Money `protobuf:"bytes,3,opt,name=shipping_cost,json=shippingCost" json:"shipping_cost,omitempty"` - ShippingAddress *Address `protobuf:"bytes,4,opt,name=shipping_address,json=shippingAddress" json:"shipping_address,omitempty"` - Items []*OrderItem `protobuf:"bytes,5,rep,name=items" json:"items,omitempty"` + OrderId string `protobuf:"bytes,1,opt,name=order_id,json=orderId,proto3" json:"order_id,omitempty"` + ShippingTrackingId string `protobuf:"bytes,2,opt,name=shipping_tracking_id,json=shippingTrackingId,proto3" json:"shipping_tracking_id,omitempty"` + ShippingCost *Money `protobuf:"bytes,3,opt,name=shipping_cost,json=shippingCost,proto3" json:"shipping_cost,omitempty"` + ShippingAddress *Address `protobuf:"bytes,4,opt,name=shipping_address,json=shippingAddress,proto3" json:"shipping_address,omitempty"` + Items []*OrderItem `protobuf:"bytes,5,rep,name=items,proto3" json:"items,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1168,7 +1168,7 @@ func (m *OrderResult) Reset() { *m = OrderResult{} } func (m *OrderResult) String() string { return proto.CompactTextString(m) } func (*OrderResult) ProtoMessage() {} func (*OrderResult) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{25} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{25} } func (m *OrderResult) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_OrderResult.Unmarshal(m, b) @@ -1224,8 +1224,8 @@ func (m *OrderResult) GetItems() []*OrderItem { } type SendOrderConfirmationRequest struct { - Email string `protobuf:"bytes,1,opt,name=email" json:"email,omitempty"` - Order *OrderResult `protobuf:"bytes,2,opt,name=order" json:"order,omitempty"` + Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` + Order *OrderResult `protobuf:"bytes,2,opt,name=order,proto3" json:"order,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1235,7 +1235,7 @@ func (m *SendOrderConfirmationRequest) Reset() { *m = SendOrderConfirmat func (m *SendOrderConfirmationRequest) String() string { return proto.CompactTextString(m) } func (*SendOrderConfirmationRequest) ProtoMessage() {} func (*SendOrderConfirmationRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{26} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{26} } func (m *SendOrderConfirmationRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_SendOrderConfirmationRequest.Unmarshal(m, b) @@ -1270,11 +1270,11 @@ func (m *SendOrderConfirmationRequest) GetOrder() *OrderResult { } type PlaceOrderRequest struct { - UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId" json:"user_id,omitempty"` - UserCurrency string `protobuf:"bytes,2,opt,name=user_currency,json=userCurrency" json:"user_currency,omitempty"` - Address *Address `protobuf:"bytes,3,opt,name=address" json:"address,omitempty"` - Email string `protobuf:"bytes,5,opt,name=email" json:"email,omitempty"` - CreditCard *CreditCardInfo `protobuf:"bytes,6,opt,name=credit_card,json=creditCard" json:"credit_card,omitempty"` + UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` + UserCurrency string `protobuf:"bytes,2,opt,name=user_currency,json=userCurrency,proto3" json:"user_currency,omitempty"` + Address *Address `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` + Email string `protobuf:"bytes,5,opt,name=email,proto3" json:"email,omitempty"` + CreditCard *CreditCardInfo `protobuf:"bytes,6,opt,name=credit_card,json=creditCard,proto3" json:"credit_card,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1284,7 +1284,7 @@ func (m *PlaceOrderRequest) Reset() { *m = PlaceOrderRequest{} } func (m *PlaceOrderRequest) String() string { return proto.CompactTextString(m) } func (*PlaceOrderRequest) ProtoMessage() {} func (*PlaceOrderRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{27} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{27} } func (m *PlaceOrderRequest) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PlaceOrderRequest.Unmarshal(m, b) @@ -1340,7 +1340,7 @@ func (m *PlaceOrderRequest) GetCreditCard() *CreditCardInfo { } type PlaceOrderResponse struct { - Order *OrderResult `protobuf:"bytes,1,opt,name=order" json:"order,omitempty"` + Order *OrderResult `protobuf:"bytes,1,opt,name=order,proto3" json:"order,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1350,7 +1350,7 @@ func (m *PlaceOrderResponse) Reset() { *m = PlaceOrderResponse{} } func (m *PlaceOrderResponse) String() string { return proto.CompactTextString(m) } func (*PlaceOrderResponse) ProtoMessage() {} func (*PlaceOrderResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_demo_e1d03823e14b5fb0, []int{28} + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{28} } func (m *PlaceOrderResponse) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PlaceOrderResponse.Unmarshal(m, b) @@ -1377,6 +1377,131 @@ func (m *PlaceOrderResponse) GetOrder() *OrderResult { return nil } +type AdRequest struct { + // List of important key words from the current page describing the context. + ContextKeys []string `protobuf:"bytes,1,rep,name=context_keys,json=contextKeys,proto3" json:"context_keys,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AdRequest) Reset() { *m = AdRequest{} } +func (m *AdRequest) String() string { return proto.CompactTextString(m) } +func (*AdRequest) ProtoMessage() {} +func (*AdRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{29} +} +func (m *AdRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AdRequest.Unmarshal(m, b) +} +func (m *AdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AdRequest.Marshal(b, m, deterministic) +} +func (dst *AdRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AdRequest.Merge(dst, src) +} +func (m *AdRequest) XXX_Size() int { + return xxx_messageInfo_AdRequest.Size(m) +} +func (m *AdRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AdRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AdRequest proto.InternalMessageInfo + +func (m *AdRequest) GetContextKeys() []string { + if m != nil { + return m.ContextKeys + } + return nil +} + +type AdResponse struct { + Ads []*Ad `protobuf:"bytes,1,rep,name=ads,proto3" json:"ads,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AdResponse) Reset() { *m = AdResponse{} } +func (m *AdResponse) String() string { return proto.CompactTextString(m) } +func (*AdResponse) ProtoMessage() {} +func (*AdResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{30} +} +func (m *AdResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AdResponse.Unmarshal(m, b) +} +func (m *AdResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AdResponse.Marshal(b, m, deterministic) +} +func (dst *AdResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AdResponse.Merge(dst, src) +} +func (m *AdResponse) XXX_Size() int { + return xxx_messageInfo_AdResponse.Size(m) +} +func (m *AdResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AdResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AdResponse proto.InternalMessageInfo + +func (m *AdResponse) GetAds() []*Ad { + if m != nil { + return m.Ads + } + return nil +} + +type Ad struct { + // url to redirect to when an ad is clicked. + RedirectUrl string `protobuf:"bytes,1,opt,name=redirect_url,json=redirectUrl,proto3" json:"redirect_url,omitempty"` + // short advertisement text to display. + Text string `protobuf:"bytes,2,opt,name=text,proto3" json:"text,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Ad) Reset() { *m = Ad{} } +func (m *Ad) String() string { return proto.CompactTextString(m) } +func (*Ad) ProtoMessage() {} +func (*Ad) Descriptor() ([]byte, []int) { + return fileDescriptor_demo_88bb8fdac9cd6be5, []int{31} +} +func (m *Ad) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Ad.Unmarshal(m, b) +} +func (m *Ad) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Ad.Marshal(b, m, deterministic) +} +func (dst *Ad) XXX_Merge(src proto.Message) { + xxx_messageInfo_Ad.Merge(dst, src) +} +func (m *Ad) XXX_Size() int { + return xxx_messageInfo_Ad.Size(m) +} +func (m *Ad) XXX_DiscardUnknown() { + xxx_messageInfo_Ad.DiscardUnknown(m) +} + +var xxx_messageInfo_Ad proto.InternalMessageInfo + +func (m *Ad) GetRedirectUrl() string { + if m != nil { + return m.RedirectUrl + } + return "" +} + +func (m *Ad) GetText() string { + if m != nil { + return m.Text + } + return "" +} + func init() { proto.RegisterType((*CartItem)(nil), "hipstershop.CartItem") proto.RegisterType((*AddItemRequest)(nil), "hipstershop.AddItemRequest") @@ -1407,6 +1532,9 @@ func init() { proto.RegisterType((*SendOrderConfirmationRequest)(nil), "hipstershop.SendOrderConfirmationRequest") proto.RegisterType((*PlaceOrderRequest)(nil), "hipstershop.PlaceOrderRequest") proto.RegisterType((*PlaceOrderResponse)(nil), "hipstershop.PlaceOrderResponse") + proto.RegisterType((*AdRequest)(nil), "hipstershop.AdRequest") + proto.RegisterType((*AdResponse)(nil), "hipstershop.AdResponse") + proto.RegisterType((*Ad)(nil), "hipstershop.Ad") } // Reference imports to suppress errors if they are not otherwise used. @@ -2127,95 +2255,165 @@ var _CheckoutService_serviceDesc = grpc.ServiceDesc{ Metadata: "demo.proto", } -func init() { proto.RegisterFile("demo.proto", fileDescriptor_demo_e1d03823e14b5fb0) } - -var fileDescriptor_demo_e1d03823e14b5fb0 = []byte{ - // 1389 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdd, 0x72, 0xd3, 0xc6, - 0x17, 0x8f, 0x12, 0x3b, 0xb6, 0x8f, 0x63, 0x27, 0xd9, 0x7f, 0xc2, 0xdf, 0x28, 0x7c, 0xa4, 0x9b, - 0x81, 0x42, 0x81, 0x94, 0x49, 0x3b, 0xc3, 0x45, 0x69, 0x29, 0x63, 0x32, 0xc6, 0x33, 0x50, 0xa8, - 0x02, 0x1d, 0x3a, 0x74, 0xea, 0x11, 0xda, 0x05, 0xab, 0x44, 0x5a, 0xb1, 0xbb, 0xca, 0xd4, 0x5c, - 0xb6, 0x0f, 0xd0, 0xfb, 0x3e, 0x42, 0x5f, 0xa0, 0xef, 0xd0, 0xfb, 0xbe, 0x42, 0x9f, 0xa3, 0xb3, - 0x2b, 0xad, 0xbe, 0x62, 0x25, 0xe1, 0xa6, 0x77, 0xda, 0xb3, 0xbf, 0x3d, 0xe7, 0x77, 0xce, 0x9e, - 0x73, 0xf6, 0x08, 0x80, 0xd0, 0x80, 0xed, 0x46, 0x9c, 0x49, 0x86, 0xba, 0x53, 0x3f, 0x12, 0x92, - 0x72, 0x31, 0x65, 0x11, 0xde, 0x87, 0xf6, 0xd0, 0xe5, 0x72, 0x2c, 0x69, 0x80, 0x2e, 0x02, 0x44, - 0x9c, 0x91, 0xd8, 0x93, 0x13, 0x9f, 0x0c, 0xac, 0x6d, 0xeb, 0x5a, 0xc7, 0xe9, 0xa4, 0x92, 0x31, - 0x41, 0x36, 0xb4, 0xdf, 0xc5, 0x6e, 0x28, 0x7d, 0x39, 0x1b, 0x2c, 0x6e, 0x5b, 0xd7, 0x9a, 0x4e, - 0xb6, 0xc6, 0xcf, 0xa0, 0x7f, 0x9f, 0x10, 0xa5, 0xc5, 0xa1, 0xef, 0x62, 0x2a, 0x24, 0xfa, 0x3f, - 0xb4, 0x62, 0x41, 0x79, 0xae, 0x69, 0x59, 0x2d, 0xc7, 0x04, 0x5d, 0x87, 0x86, 0x2f, 0x69, 0xa0, - 0x55, 0x74, 0xf7, 0x36, 0x77, 0x0b, 0x6c, 0x76, 0x0d, 0x15, 0x47, 0x43, 0xf0, 0x0d, 0x58, 0xdb, - 0x0f, 0x22, 0x39, 0x53, 0xe2, 0xd3, 0xf4, 0xe2, 0xeb, 0xd0, 0x1f, 0x51, 0x79, 0x26, 0xe8, 0x23, - 0x68, 0x28, 0x5c, 0x3d, 0xc7, 0x1b, 0xd0, 0x54, 0x04, 0xc4, 0x60, 0x71, 0x7b, 0xa9, 0x9e, 0x64, - 0x82, 0xc1, 0x2d, 0x68, 0x6a, 0x96, 0xf8, 0x3b, 0xb0, 0x1f, 0xf9, 0x42, 0x3a, 0xd4, 0x63, 0x41, - 0x40, 0x43, 0xe2, 0x4a, 0x9f, 0x85, 0xe2, 0xd4, 0x80, 0x5c, 0x86, 0x6e, 0x1e, 0xf6, 0xc4, 0x64, - 0xc7, 0x81, 0x2c, 0xee, 0x02, 0x7f, 0x05, 0x5b, 0x73, 0xf5, 0x8a, 0x88, 0x85, 0x82, 0x56, 0xcf, - 0x5b, 0xc7, 0xce, 0xff, 0x6e, 0x41, 0xeb, 0x69, 0xb2, 0x44, 0x7d, 0x58, 0xcc, 0x08, 0x2c, 0xfa, - 0x04, 0x21, 0x68, 0x84, 0x6e, 0x40, 0xf5, 0x6d, 0x74, 0x1c, 0xfd, 0x8d, 0xb6, 0xa1, 0x4b, 0xa8, - 0xf0, 0xb8, 0x1f, 0x29, 0x43, 0x83, 0x25, 0xbd, 0x55, 0x14, 0xa1, 0x01, 0xb4, 0x22, 0xdf, 0x93, - 0x31, 0xa7, 0x83, 0x86, 0xde, 0x35, 0x4b, 0xf4, 0x29, 0x74, 0x22, 0xee, 0x7b, 0x74, 0x12, 0x0b, - 0x32, 0x68, 0xea, 0x2b, 0x46, 0xa5, 0xe8, 0x3d, 0x66, 0x21, 0x9d, 0x39, 0x6d, 0x0d, 0x7a, 0x2e, - 0x08, 0x7e, 0x08, 0x1b, 0xca, 0xb9, 0x94, 0x5f, 0xee, 0xd5, 0x6d, 0x68, 0xa7, 0x2e, 0x24, 0x2e, - 0x75, 0xf7, 0x36, 0x4a, 0x7a, 0xd2, 0x03, 0x4e, 0x86, 0xc2, 0x3b, 0xb0, 0x3e, 0xa2, 0x46, 0x91, - 0x89, 0x7a, 0xc5, 0x5f, 0x7c, 0x0b, 0x36, 0x0f, 0xa8, 0xcb, 0xbd, 0x69, 0x6e, 0x30, 0x01, 0x6e, - 0x40, 0xf3, 0x5d, 0x4c, 0xf9, 0x2c, 0xc5, 0x26, 0x0b, 0xfc, 0x10, 0xce, 0x55, 0xe1, 0x29, 0xbf, - 0x5d, 0x68, 0x71, 0x2a, 0xe2, 0xc3, 0x53, 0xe8, 0x19, 0x10, 0x0e, 0x61, 0x75, 0x44, 0xe5, 0xb7, - 0x31, 0x93, 0xd4, 0x98, 0xdc, 0x85, 0x96, 0x4b, 0x08, 0xa7, 0x42, 0x68, 0xa3, 0x55, 0x15, 0xf7, - 0x93, 0x3d, 0xc7, 0x80, 0x3e, 0x2c, 0x2b, 0xef, 0xc3, 0x5a, 0x6e, 0x2f, 0xe5, 0x7c, 0x0b, 0xda, - 0x1e, 0x13, 0x52, 0xdf, 0x8d, 0x55, 0x7b, 0x37, 0x2d, 0x85, 0x51, 0x57, 0xc3, 0x60, 0xed, 0x60, - 0xea, 0x47, 0x4f, 0x38, 0xa1, 0xfc, 0x3f, 0xe1, 0xfc, 0x39, 0xac, 0x17, 0x0c, 0xe6, 0xe9, 0x2d, - 0xb9, 0xeb, 0xbd, 0xf5, 0xc3, 0x37, 0x79, 0xed, 0x80, 0x11, 0x8d, 0x09, 0xfe, 0xcd, 0x82, 0x56, - 0x6a, 0x17, 0x5d, 0x81, 0xbe, 0x90, 0x9c, 0x52, 0x39, 0x29, 0xb2, 0xec, 0x38, 0xbd, 0x44, 0x6a, - 0x60, 0x08, 0x1a, 0x9e, 0x69, 0x63, 0x1d, 0x47, 0x7f, 0xab, 0x04, 0x10, 0xd2, 0x95, 0x34, 0xcd, - 0xf7, 0x64, 0xa1, 0x32, 0xdd, 0x63, 0x71, 0x28, 0xf9, 0xcc, 0x64, 0x7a, 0xba, 0x44, 0xe7, 0xa1, - 0xfd, 0xde, 0x8f, 0x26, 0x1e, 0x23, 0x54, 0x27, 0x7a, 0xd3, 0x69, 0xbd, 0xf7, 0xa3, 0x21, 0x23, - 0x14, 0xbf, 0x80, 0xa6, 0x0e, 0x25, 0xda, 0x81, 0x9e, 0x17, 0x73, 0x4e, 0x43, 0x6f, 0x96, 0x00, - 0x13, 0x36, 0x2b, 0x46, 0xa8, 0xd0, 0xca, 0x70, 0x1c, 0xfa, 0x52, 0x68, 0x36, 0x4b, 0x4e, 0xb2, - 0x50, 0xd2, 0xd0, 0x0d, 0x99, 0xd0, 0x74, 0x9a, 0x4e, 0xb2, 0xc0, 0x23, 0xb8, 0x34, 0xa2, 0xf2, - 0x20, 0x8e, 0x22, 0xc6, 0x25, 0x25, 0xc3, 0x44, 0x8f, 0x4f, 0xf3, 0xbc, 0xbc, 0x02, 0xfd, 0x92, - 0x49, 0xd3, 0x10, 0x7a, 0x45, 0x9b, 0x02, 0xff, 0x00, 0xe7, 0x87, 0x99, 0x20, 0x3c, 0xa2, 0x5c, - 0xf8, 0x2c, 0x34, 0x97, 0x7c, 0x15, 0x1a, 0xaf, 0x39, 0x0b, 0x4e, 0xc8, 0x11, 0xbd, 0xaf, 0x5a, - 0x9a, 0x64, 0x89, 0x63, 0x49, 0x24, 0x97, 0x25, 0xd3, 0x01, 0xf8, 0xc7, 0x82, 0xfe, 0x90, 0x53, - 0xe2, 0xab, 0x7e, 0x4c, 0xc6, 0xe1, 0x6b, 0x86, 0x6e, 0x02, 0xf2, 0xb4, 0x64, 0xe2, 0xb9, 0x9c, - 0x4c, 0xc2, 0x38, 0x78, 0x45, 0x79, 0x1a, 0x8f, 0x35, 0x2f, 0xc3, 0x7e, 0xa3, 0xe5, 0xe8, 0x2a, - 0xac, 0x16, 0xd1, 0xde, 0xd1, 0x51, 0xfa, 0xe4, 0xf4, 0x72, 0xe8, 0xf0, 0xe8, 0x08, 0x7d, 0x09, - 0x5b, 0x45, 0x1c, 0xfd, 0x39, 0xf2, 0xb9, 0x6e, 0x8f, 0x93, 0x19, 0x75, 0x79, 0x1a, 0xbb, 0x41, - 0x7e, 0x66, 0x3f, 0x03, 0x7c, 0x4f, 0x5d, 0x8e, 0xee, 0xc1, 0x85, 0x9a, 0xe3, 0x01, 0x0b, 0xe5, - 0x54, 0x5f, 0x79, 0xd3, 0x39, 0x3f, 0xef, 0xfc, 0x63, 0x05, 0xc0, 0x33, 0xe8, 0x0d, 0xa7, 0x2e, - 0x7f, 0x93, 0xd5, 0xf4, 0x27, 0xb0, 0xec, 0x06, 0x2a, 0x43, 0x4e, 0x08, 0x5e, 0x8a, 0x40, 0x77, - 0xa1, 0x5b, 0xb0, 0x9e, 0x3e, 0x88, 0x5b, 0xe5, 0x0a, 0x29, 0x05, 0xd1, 0x81, 0x9c, 0x09, 0xbe, - 0x03, 0x7d, 0x63, 0x3a, 0xbf, 0x7a, 0xc9, 0xdd, 0x50, 0xb8, 0x9e, 0x76, 0x21, 0x2b, 0x96, 0x5e, - 0x41, 0x3a, 0x26, 0xf8, 0x47, 0xe8, 0xe8, 0x0a, 0xd3, 0x6f, 0xbe, 0x79, 0x8d, 0xad, 0x53, 0x5f, - 0x63, 0x95, 0x15, 0xaa, 0x33, 0xa4, 0x3c, 0xe7, 0x66, 0x85, 0xda, 0xc7, 0xbf, 0x2c, 0x42, 0xd7, - 0x94, 0x70, 0x7c, 0x28, 0x55, 0xa1, 0x30, 0xb5, 0xcc, 0x09, 0xb5, 0xf4, 0x7a, 0x4c, 0xd0, 0x6d, - 0xd8, 0x10, 0x53, 0x3f, 0x8a, 0x54, 0x6d, 0x17, 0x8b, 0x3c, 0xc9, 0x26, 0x64, 0xf6, 0x9e, 0x65, - 0xc5, 0x8e, 0xee, 0x40, 0x2f, 0x3b, 0xa1, 0xd9, 0x2c, 0xd5, 0xb2, 0x59, 0x31, 0xc0, 0x21, 0x13, - 0x12, 0xdd, 0x83, 0xb5, 0xec, 0xa0, 0xe9, 0x0d, 0x8d, 0x13, 0x3a, 0xd8, 0xaa, 0x41, 0x9b, 0x9e, - 0x71, 0xd3, 0x74, 0xb2, 0xa6, 0xee, 0x64, 0xe7, 0x4a, 0xa7, 0xb2, 0x80, 0x9a, 0x56, 0x46, 0xe0, - 0xc2, 0x01, 0x0d, 0x89, 0x96, 0x0f, 0x59, 0xf8, 0xda, 0xe7, 0x81, 0x4e, 0x9b, 0xc2, 0x73, 0x43, - 0x03, 0xd7, 0x3f, 0x34, 0xcf, 0x8d, 0x5e, 0xa0, 0x5d, 0x68, 0xea, 0xd0, 0xa4, 0x31, 0x1e, 0x1c, - 0xb7, 0x91, 0xc4, 0xd4, 0x49, 0x60, 0xf8, 0x6f, 0x0b, 0xd6, 0x9f, 0x1e, 0xba, 0x1e, 0x2d, 0xf5, - 0xe8, 0xda, 0x49, 0x63, 0x07, 0x7a, 0x7a, 0xc3, 0xb4, 0x82, 0x34, 0xce, 0x2b, 0x4a, 0x68, 0xba, - 0x41, 0xb1, 0xc3, 0x2f, 0x9d, 0xa5, 0xc3, 0x67, 0x9e, 0x34, 0x8b, 0x9e, 0x54, 0x72, 0x7b, 0xf9, - 0xc3, 0x72, 0xfb, 0x01, 0xa0, 0xa2, 0x5b, 0xd9, 0x93, 0x9b, 0x46, 0xc7, 0x3a, 0x53, 0x74, 0xf6, - 0xfe, 0xb2, 0xa0, 0xab, 0x72, 0xf8, 0x80, 0xf2, 0x23, 0xdf, 0xa3, 0xe8, 0xae, 0x7e, 0x27, 0x74, - 0xda, 0x6f, 0x55, 0x7d, 0x2a, 0x8c, 0xae, 0x76, 0x39, 0x99, 0x92, 0xd9, 0x6e, 0x01, 0x7d, 0x01, - 0xad, 0x74, 0xbe, 0xac, 0x9c, 0x2e, 0x4f, 0x9d, 0xf6, 0xfa, 0xb1, 0x1a, 0xc2, 0x0b, 0xe8, 0x6b, - 0xe8, 0x64, 0x93, 0x2c, 0xba, 0x78, 0x5c, 0x7f, 0x51, 0xc1, 0x5c, 0xf3, 0x7b, 0xbf, 0x5a, 0xb0, - 0x59, 0x9e, 0x00, 0x8d, 0x5b, 0x3f, 0xc1, 0xff, 0xe6, 0x8c, 0x87, 0xe8, 0xe3, 0x92, 0x9a, 0xfa, - 0xc1, 0xd4, 0xbe, 0x76, 0x3a, 0x30, 0xb9, 0x00, 0xc5, 0x62, 0x11, 0x36, 0xd3, 0xd1, 0x66, 0xe8, - 0x4a, 0xf7, 0x90, 0xbd, 0x31, 0x2c, 0x46, 0xb0, 0x52, 0x9c, 0xe3, 0xd0, 0x1c, 0x2f, 0xec, 0x8f, - 0x8e, 0x59, 0xaa, 0x8e, 0x55, 0x78, 0x01, 0x3d, 0x00, 0xc8, 0xc7, 0x38, 0x74, 0xa9, 0x1a, 0xea, - 0xf2, 0x7c, 0x67, 0xcf, 0x9d, 0xba, 0xf0, 0x02, 0x7a, 0x09, 0xfd, 0xf2, 0xe0, 0x86, 0x70, 0x09, - 0x39, 0x77, 0x08, 0xb4, 0x77, 0x4e, 0xc4, 0x64, 0x51, 0xf8, 0xc3, 0x82, 0xd5, 0x83, 0xb4, 0x3d, - 0x18, 0xff, 0xc7, 0xd0, 0x36, 0xf3, 0x16, 0xba, 0x50, 0x25, 0x5d, 0x1c, 0xfb, 0xec, 0x8b, 0x35, - 0xbb, 0x59, 0x04, 0x1e, 0x41, 0x27, 0x1b, 0x83, 0x2a, 0xc9, 0x52, 0x9d, 0xc7, 0xec, 0x4b, 0x75, - 0xdb, 0x19, 0xd9, 0x3f, 0x2d, 0x58, 0x35, 0xc5, 0x6d, 0xc8, 0xbe, 0x84, 0x73, 0xf3, 0xc7, 0x88, - 0xb9, 0xd7, 0x76, 0xa3, 0x4a, 0xf8, 0x84, 0xf9, 0x03, 0x2f, 0xa0, 0x11, 0xb4, 0x92, 0x91, 0x42, - 0xa2, 0xab, 0xe5, 0x5a, 0xa8, 0x1b, 0x38, 0xec, 0x39, 0xed, 0x1b, 0x2f, 0xec, 0x3d, 0x87, 0xfe, - 0x53, 0x77, 0x16, 0xd0, 0x30, 0xab, 0xe0, 0x21, 0x2c, 0x27, 0x6f, 0x1e, 0xb2, 0xcb, 0x9a, 0x8b, - 0x6f, 0xb0, 0xbd, 0x35, 0x77, 0x2f, 0x0b, 0xc8, 0x14, 0x56, 0xf6, 0x55, 0x8f, 0x32, 0x4a, 0x5f, - 0xa8, 0x5f, 0x82, 0x39, 0xad, 0x1a, 0x5d, 0xaf, 0x64, 0x43, 0x7d, 0x3b, 0xaf, 0xa9, 0xd9, 0x57, - 0xb0, 0x3a, 0x9c, 0x52, 0xef, 0x2d, 0x8b, 0x33, 0x0f, 0x9e, 0x00, 0xe4, 0x9d, 0xad, 0x92, 0xdd, - 0xc7, 0x3a, 0xb9, 0x7d, 0xb9, 0x76, 0xdf, 0x78, 0xf3, 0x6a, 0x59, 0xff, 0xd4, 0x7f, 0xf6, 0x6f, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x59, 0xab, 0x01, 0x43, 0xe2, 0x0f, 0x00, 0x00, +// AdServiceClient is the client API for AdService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type AdServiceClient interface { + GetAds(ctx context.Context, in *AdRequest, opts ...grpc.CallOption) (*AdResponse, error) +} + +type adServiceClient struct { + cc *grpc.ClientConn +} + +func NewAdServiceClient(cc *grpc.ClientConn) AdServiceClient { + return &adServiceClient{cc} +} + +func (c *adServiceClient) GetAds(ctx context.Context, in *AdRequest, opts ...grpc.CallOption) (*AdResponse, error) { + out := new(AdResponse) + err := c.cc.Invoke(ctx, "/hipstershop.AdService/GetAds", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdServiceServer is the server API for AdService service. +type AdServiceServer interface { + GetAds(context.Context, *AdRequest) (*AdResponse, error) +} + +func RegisterAdServiceServer(s *grpc.Server, srv AdServiceServer) { + s.RegisterService(&_AdService_serviceDesc, srv) +} + +func _AdService_GetAds_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdServiceServer).GetAds(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hipstershop.AdService/GetAds", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdServiceServer).GetAds(ctx, req.(*AdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _AdService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "hipstershop.AdService", + HandlerType: (*AdServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetAds", + Handler: _AdService_GetAds_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "demo.proto", +} + +func init() { proto.RegisterFile("demo.proto", fileDescriptor_demo_88bb8fdac9cd6be5) } + +var fileDescriptor_demo_88bb8fdac9cd6be5 = []byte{ + // 1483 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdd, 0x72, 0xd3, 0xc6, + 0x17, 0x8f, 0x92, 0x38, 0x8e, 0x8f, 0x63, 0x27, 0xd9, 0x7f, 0x12, 0x8c, 0xc2, 0x47, 0xd8, 0x0c, + 0xfc, 0xa1, 0x80, 0x61, 0xd2, 0xce, 0x70, 0x01, 0x2d, 0xcd, 0x98, 0x8c, 0xf1, 0x14, 0x0a, 0x55, + 0xa0, 0x43, 0x87, 0x4e, 0x3d, 0x42, 0xbb, 0x60, 0x95, 0x48, 0x2b, 0x76, 0x57, 0x19, 0xcc, 0x65, + 0xfb, 0x00, 0xbd, 0xef, 0x23, 0xf4, 0x05, 0xfa, 0x0e, 0xbd, 0xef, 0x2b, 0xf4, 0x39, 0x3a, 0xbb, + 0xd2, 0xea, 0xcb, 0x76, 0x02, 0x37, 0xbd, 0xf3, 0x9e, 0xfd, 0xe9, 0x9c, 0xdf, 0x39, 0x7b, 0xbe, + 0x12, 0x00, 0x42, 0x03, 0xd6, 0x8d, 0x38, 0x93, 0x0c, 0x35, 0x47, 0x7e, 0x24, 0x24, 0xe5, 0x62, + 0xc4, 0x22, 0x7c, 0x00, 0xcb, 0x3d, 0x97, 0xcb, 0x81, 0xa4, 0x01, 0x3a, 0x0f, 0x10, 0x71, 0x46, + 0x62, 0x4f, 0x0e, 0x7d, 0xd2, 0xb1, 0x76, 0xac, 0xab, 0x0d, 0xa7, 0x91, 0x4a, 0x06, 0x04, 0xd9, + 0xb0, 0xfc, 0x2e, 0x76, 0x43, 0xe9, 0xcb, 0x71, 0x67, 0x7e, 0xc7, 0xba, 0x5a, 0x73, 0xb2, 0x33, + 0x7e, 0x06, 0xed, 0x7d, 0x42, 0x94, 0x16, 0x87, 0xbe, 0x8b, 0xa9, 0x90, 0xe8, 0x0c, 0xd4, 0x63, + 0x41, 0x79, 0xae, 0x69, 0x49, 0x1d, 0x07, 0x04, 0x5d, 0x83, 0x45, 0x5f, 0xd2, 0x40, 0xab, 0x68, + 0xee, 0x6d, 0x76, 0x0b, 0x6c, 0xba, 0x86, 0x8a, 0xa3, 0x21, 0xf8, 0x3a, 0xac, 0x1d, 0x04, 0x91, + 0x1c, 0x2b, 0xf1, 0x69, 0x7a, 0xf1, 0x35, 0x68, 0xf7, 0xa9, 0xfc, 0x28, 0xe8, 0x23, 0x58, 0x54, + 0xb8, 0xd9, 0x1c, 0xaf, 0x43, 0x4d, 0x11, 0x10, 0x9d, 0xf9, 0x9d, 0x85, 0xd9, 0x24, 0x13, 0x0c, + 0xae, 0x43, 0x4d, 0xb3, 0xc4, 0xdf, 0x83, 0xfd, 0xc8, 0x17, 0xd2, 0xa1, 0x1e, 0x0b, 0x02, 0x1a, + 0x12, 0x57, 0xfa, 0x2c, 0x14, 0xa7, 0x06, 0xe4, 0x22, 0x34, 0xf3, 0xb0, 0x27, 0x26, 0x1b, 0x0e, + 0x64, 0x71, 0x17, 0xf8, 0x2b, 0xd8, 0x9e, 0xaa, 0x57, 0x44, 0x2c, 0x14, 0xb4, 0xfa, 0xbd, 0x35, + 0xf1, 0xfd, 0xef, 0x16, 0xd4, 0x9f, 0x26, 0x47, 0xd4, 0x86, 0xf9, 0x8c, 0xc0, 0xbc, 0x4f, 0x10, + 0x82, 0xc5, 0xd0, 0x0d, 0xa8, 0x7e, 0x8d, 0x86, 0xa3, 0x7f, 0xa3, 0x1d, 0x68, 0x12, 0x2a, 0x3c, + 0xee, 0x47, 0xca, 0x50, 0x67, 0x41, 0x5f, 0x15, 0x45, 0xa8, 0x03, 0xf5, 0xc8, 0xf7, 0x64, 0xcc, + 0x69, 0x67, 0x51, 0xdf, 0x9a, 0x23, 0xba, 0x05, 0x8d, 0x88, 0xfb, 0x1e, 0x1d, 0xc6, 0x82, 0x74, + 0x6a, 0xfa, 0x89, 0x51, 0x29, 0x7a, 0x8f, 0x59, 0x48, 0xc7, 0xce, 0xb2, 0x06, 0x3d, 0x17, 0x04, + 0x3f, 0x84, 0x0d, 0xe5, 0x5c, 0xca, 0x2f, 0xf7, 0xea, 0x36, 0x2c, 0xa7, 0x2e, 0x24, 0x2e, 0x35, + 0xf7, 0x36, 0x4a, 0x7a, 0xd2, 0x0f, 0x9c, 0x0c, 0x85, 0x77, 0x61, 0xbd, 0x4f, 0x8d, 0x22, 0x13, + 0xf5, 0x8a, 0xbf, 0xf8, 0x26, 0x6c, 0x1e, 0x52, 0x97, 0x7b, 0xa3, 0xdc, 0x60, 0x02, 0xdc, 0x80, + 0xda, 0xbb, 0x98, 0xf2, 0x71, 0x8a, 0x4d, 0x0e, 0xf8, 0x21, 0x6c, 0x55, 0xe1, 0x29, 0xbf, 0x2e, + 0xd4, 0x39, 0x15, 0xf1, 0xd1, 0x29, 0xf4, 0x0c, 0x08, 0x87, 0xb0, 0xda, 0xa7, 0xf2, 0xbb, 0x98, + 0x49, 0x6a, 0x4c, 0x76, 0xa1, 0xee, 0x12, 0xc2, 0xa9, 0x10, 0xda, 0x68, 0x55, 0xc5, 0x7e, 0x72, + 0xe7, 0x18, 0xd0, 0xa7, 0x65, 0xe5, 0x3e, 0xac, 0xe5, 0xf6, 0x52, 0xce, 0x37, 0x61, 0xd9, 0x63, + 0x42, 0xea, 0xb7, 0xb1, 0x66, 0xbe, 0x4d, 0x5d, 0x61, 0xd4, 0xd3, 0x30, 0x58, 0x3b, 0x1c, 0xf9, + 0xd1, 0x13, 0x4e, 0x28, 0xff, 0x4f, 0x38, 0x7f, 0x01, 0xeb, 0x05, 0x83, 0x79, 0x7a, 0x4b, 0xee, + 0x7a, 0x6f, 0xfd, 0xf0, 0x4d, 0x5e, 0x3b, 0x60, 0x44, 0x03, 0x82, 0x7f, 0xb3, 0xa0, 0x9e, 0xda, + 0x45, 0x97, 0xa1, 0x2d, 0x24, 0xa7, 0x54, 0x0e, 0x8b, 0x2c, 0x1b, 0x4e, 0x2b, 0x91, 0x1a, 0x18, + 0x82, 0x45, 0xcf, 0xb4, 0xb1, 0x86, 0xa3, 0x7f, 0xab, 0x04, 0x10, 0xd2, 0x95, 0x34, 0xcd, 0xf7, + 0xe4, 0xa0, 0x32, 0xdd, 0x63, 0x71, 0x28, 0xf9, 0xd8, 0x64, 0x7a, 0x7a, 0x44, 0x67, 0x61, 0xf9, + 0x83, 0x1f, 0x0d, 0x3d, 0x46, 0xa8, 0x4e, 0xf4, 0x9a, 0x53, 0xff, 0xe0, 0x47, 0x3d, 0x46, 0x28, + 0x7e, 0x01, 0x35, 0x1d, 0x4a, 0xb4, 0x0b, 0x2d, 0x2f, 0xe6, 0x9c, 0x86, 0xde, 0x38, 0x01, 0x26, + 0x6c, 0x56, 0x8c, 0x50, 0xa1, 0x95, 0xe1, 0x38, 0xf4, 0xa5, 0xd0, 0x6c, 0x16, 0x9c, 0xe4, 0xa0, + 0xa4, 0xa1, 0x1b, 0x32, 0xa1, 0xe9, 0xd4, 0x9c, 0xe4, 0x80, 0xfb, 0x70, 0xa1, 0x4f, 0xe5, 0x61, + 0x1c, 0x45, 0x8c, 0x4b, 0x4a, 0x7a, 0x89, 0x1e, 0x9f, 0xe6, 0x79, 0x79, 0x19, 0xda, 0x25, 0x93, + 0xa6, 0x21, 0xb4, 0x8a, 0x36, 0x05, 0xfe, 0x11, 0xce, 0xf6, 0x32, 0x41, 0x78, 0x4c, 0xb9, 0xf0, + 0x59, 0x68, 0x1e, 0xf9, 0x0a, 0x2c, 0xbe, 0xe6, 0x2c, 0x38, 0x21, 0x47, 0xf4, 0xbd, 0x6a, 0x69, + 0x92, 0x25, 0x8e, 0x25, 0x91, 0x5c, 0x92, 0x4c, 0x07, 0xe0, 0x1f, 0x0b, 0xda, 0x3d, 0x4e, 0x89, + 0xaf, 0xfa, 0x31, 0x19, 0x84, 0xaf, 0x19, 0xba, 0x01, 0xc8, 0xd3, 0x92, 0xa1, 0xe7, 0x72, 0x32, + 0x0c, 0xe3, 0xe0, 0x15, 0xe5, 0x69, 0x3c, 0xd6, 0xbc, 0x0c, 0xfb, 0xad, 0x96, 0xa3, 0x2b, 0xb0, + 0x5a, 0x44, 0x7b, 0xc7, 0xc7, 0xe9, 0xc8, 0x69, 0xe5, 0xd0, 0xde, 0xf1, 0x31, 0xfa, 0x12, 0xb6, + 0x8b, 0x38, 0xfa, 0x3e, 0xf2, 0xb9, 0x6e, 0x8f, 0xc3, 0x31, 0x75, 0x79, 0x1a, 0xbb, 0x4e, 0xfe, + 0xcd, 0x41, 0x06, 0xf8, 0x81, 0xba, 0x1c, 0xdd, 0x87, 0x73, 0x33, 0x3e, 0x0f, 0x58, 0x28, 0x47, + 0xfa, 0xc9, 0x6b, 0xce, 0xd9, 0x69, 0xdf, 0x3f, 0x56, 0x00, 0x3c, 0x86, 0x56, 0x6f, 0xe4, 0xf2, + 0x37, 0x59, 0x4d, 0x7f, 0x06, 0x4b, 0x6e, 0xa0, 0x32, 0xe4, 0x84, 0xe0, 0xa5, 0x08, 0x74, 0x0f, + 0x9a, 0x05, 0xeb, 0xe9, 0x40, 0xdc, 0x2e, 0x57, 0x48, 0x29, 0x88, 0x0e, 0xe4, 0x4c, 0xf0, 0x1d, + 0x68, 0x1b, 0xd3, 0xf9, 0xd3, 0x4b, 0xee, 0x86, 0xc2, 0xf5, 0xb4, 0x0b, 0x59, 0xb1, 0xb4, 0x0a, + 0xd2, 0x01, 0xc1, 0x3f, 0x41, 0x43, 0x57, 0x98, 0x9e, 0xf9, 0x66, 0x1a, 0x5b, 0xa7, 0x4e, 0x63, + 0x95, 0x15, 0xaa, 0x33, 0xa4, 0x3c, 0xa7, 0x66, 0x85, 0xba, 0xc7, 0xbf, 0xcc, 0x43, 0xd3, 0x94, + 0x70, 0x7c, 0x24, 0x55, 0xa1, 0x30, 0x75, 0xcc, 0x09, 0xd5, 0xf5, 0x79, 0x40, 0xd0, 0x6d, 0xd8, + 0x10, 0x23, 0x3f, 0x8a, 0x54, 0x6d, 0x17, 0x8b, 0x3c, 0xc9, 0x26, 0x64, 0xee, 0x9e, 0x65, 0xc5, + 0x8e, 0xee, 0x40, 0x2b, 0xfb, 0x42, 0xb3, 0x59, 0x98, 0xc9, 0x66, 0xc5, 0x00, 0x7b, 0x4c, 0x48, + 0x74, 0x1f, 0xd6, 0xb2, 0x0f, 0x4d, 0x6f, 0x58, 0x3c, 0xa1, 0x83, 0xad, 0x1a, 0xb4, 0xe9, 0x19, + 0x37, 0x4c, 0x27, 0xab, 0xe9, 0x4e, 0xb6, 0x55, 0xfa, 0x2a, 0x0b, 0xa8, 0x69, 0x65, 0x04, 0xce, + 0x1d, 0xd2, 0x90, 0x68, 0x79, 0x8f, 0x85, 0xaf, 0x7d, 0x1e, 0xe8, 0xb4, 0x29, 0x8c, 0x1b, 0x1a, + 0xb8, 0xfe, 0x91, 0x19, 0x37, 0xfa, 0x80, 0xba, 0x50, 0xd3, 0xa1, 0x49, 0x63, 0xdc, 0x99, 0xb4, + 0x91, 0xc4, 0xd4, 0x49, 0x60, 0xf8, 0x6f, 0x0b, 0xd6, 0x9f, 0x1e, 0xb9, 0x1e, 0x2d, 0xf5, 0xe8, + 0x99, 0x9b, 0xc6, 0x2e, 0xb4, 0xf4, 0x85, 0x69, 0x05, 0x69, 0x9c, 0x57, 0x94, 0xd0, 0x74, 0x83, + 0x62, 0x87, 0x5f, 0xf8, 0x98, 0x0e, 0x9f, 0x79, 0x52, 0x2b, 0x7a, 0x52, 0xc9, 0xed, 0xa5, 0x4f, + 0xcb, 0xed, 0x07, 0x80, 0x8a, 0x6e, 0x65, 0x23, 0x37, 0x8d, 0x8e, 0xf5, 0x71, 0xd1, 0xe9, 0x42, + 0x63, 0x9f, 0x98, 0xa0, 0x5c, 0x82, 0x15, 0x8f, 0x85, 0x92, 0xbe, 0x97, 0xc3, 0xb7, 0x74, 0x6c, + 0xba, 0x62, 0x33, 0x95, 0x7d, 0x43, 0xc7, 0x02, 0xdf, 0x02, 0x50, 0xf8, 0xd4, 0xda, 0x25, 0x58, + 0x70, 0x89, 0x19, 0xee, 0xab, 0x95, 0x18, 0x38, 0xea, 0x0e, 0xdf, 0x85, 0xf9, 0x7d, 0xa2, 0x34, + 0x2b, 0xe6, 0x9c, 0x7a, 0x72, 0x18, 0x73, 0xf3, 0xa2, 0x4d, 0x23, 0x7b, 0xce, 0x8f, 0xd4, 0xbc, + 0x51, 0x56, 0xcc, 0xbc, 0x51, 0xbf, 0xf7, 0xfe, 0xb2, 0xa0, 0xa9, 0x2a, 0xec, 0x90, 0xf2, 0x63, + 0xdf, 0xa3, 0xe8, 0x9e, 0x9e, 0x62, 0xba, 0x28, 0xb7, 0xab, 0x11, 0x2f, 0x2c, 0xd6, 0x76, 0x39, + 0xd5, 0x93, 0xcd, 0x73, 0x0e, 0xdd, 0x85, 0x7a, 0xba, 0xfd, 0x56, 0xbe, 0x2e, 0xef, 0xc4, 0xf6, + 0xfa, 0x44, 0x85, 0xe3, 0x39, 0xf4, 0x35, 0x34, 0xb2, 0x3d, 0x1b, 0x9d, 0x9f, 0xd4, 0x5f, 0x54, + 0x30, 0xd5, 0xfc, 0xde, 0xaf, 0x16, 0x6c, 0x96, 0xf7, 0x53, 0xe3, 0xd6, 0xcf, 0xf0, 0xbf, 0x29, + 0xcb, 0x2b, 0xfa, 0x7f, 0x49, 0xcd, 0xec, 0xb5, 0xd9, 0xbe, 0x7a, 0x3a, 0x30, 0x79, 0x30, 0xc5, + 0x62, 0x1e, 0x36, 0xd3, 0xc5, 0xab, 0xe7, 0x4a, 0xf7, 0x88, 0xbd, 0x31, 0x2c, 0xfa, 0xb0, 0x52, + 0xdc, 0x32, 0xd1, 0x14, 0x2f, 0xec, 0x4b, 0x13, 0x96, 0xaa, 0x4b, 0x1f, 0x9e, 0x43, 0x0f, 0x00, + 0xf2, 0x25, 0x13, 0x5d, 0xa8, 0x86, 0xba, 0xbc, 0x7d, 0xda, 0x53, 0x77, 0x42, 0x3c, 0x87, 0x5e, + 0x42, 0xbb, 0xbc, 0x56, 0x22, 0x5c, 0x42, 0x4e, 0x5d, 0x51, 0xed, 0xdd, 0x13, 0x31, 0x59, 0x14, + 0xfe, 0xb0, 0x60, 0xf5, 0x30, 0x6d, 0x5e, 0xc6, 0xff, 0x01, 0x2c, 0x9b, 0x6d, 0x10, 0x9d, 0xab, + 0x92, 0x2e, 0x2e, 0xa5, 0xf6, 0xf9, 0x19, 0xb7, 0x59, 0x04, 0x1e, 0x41, 0x23, 0x5b, 0xd2, 0x2a, + 0xc9, 0x52, 0xdd, 0x16, 0xed, 0x0b, 0xb3, 0xae, 0x33, 0xb2, 0x7f, 0x5a, 0xb0, 0x6a, 0x5a, 0x8f, + 0x21, 0xfb, 0x12, 0xb6, 0xa6, 0x2f, 0x39, 0x53, 0x9f, 0xed, 0x7a, 0x95, 0xf0, 0x09, 0xdb, 0x11, + 0x9e, 0x43, 0x7d, 0xa8, 0x27, 0x0b, 0x8f, 0x44, 0x57, 0xca, 0xb5, 0x30, 0x6b, 0x1d, 0xb2, 0xa7, + 0x0c, 0x17, 0x3c, 0xb7, 0xf7, 0x1c, 0xda, 0x4f, 0xdd, 0x71, 0x40, 0xc3, 0xac, 0x82, 0x7b, 0xb0, + 0x94, 0x4c, 0x64, 0x64, 0x97, 0x35, 0x17, 0x37, 0x04, 0x7b, 0x7b, 0xea, 0x5d, 0x16, 0x90, 0x11, + 0xac, 0x1c, 0xa8, 0x0e, 0x6a, 0x94, 0xbe, 0x50, 0x7f, 0xb0, 0x4c, 0x19, 0x24, 0xe8, 0x5a, 0x25, + 0x1b, 0x66, 0x0f, 0x9b, 0x19, 0x35, 0xfb, 0x0a, 0x56, 0x7b, 0x23, 0xea, 0xbd, 0x65, 0x71, 0xe6, + 0xc1, 0x13, 0x80, 0xbc, 0xef, 0x56, 0xb2, 0x7b, 0x62, 0xce, 0xd8, 0x17, 0x67, 0xde, 0x67, 0xde, + 0x3c, 0x54, 0x2d, 0xd8, 0x68, 0xbf, 0x0b, 0x4b, 0x7d, 0xb5, 0x83, 0x0b, 0xb4, 0x55, 0x6d, 0xa7, + 0xa9, 0xc6, 0x33, 0x13, 0x72, 0xa3, 0xe9, 0xd5, 0x92, 0xfe, 0xe7, 0xc5, 0xe7, 0xff, 0x06, 0x00, + 0x00, 0xff, 0xff, 0x22, 0xc9, 0xfe, 0x20, 0xca, 0x10, 0x00, 0x00, } diff --git a/src/frontend/handlers.go b/src/frontend/handlers.go index d215f6b..10bc2ff 100644 --- a/src/frontend/handlers.go +++ b/src/frontend/handlers.go @@ -15,8 +15,10 @@ package main import ( + "context" "fmt" "html/template" + "math/rand" "net/http" "os" "strconv" @@ -78,6 +80,7 @@ func (fe *frontendServer) homeHandler(w http.ResponseWriter, r *http.Request) { "products": ps, "cart_size": len(cart), "banner_color": os.Getenv("BANNER_COLOR"), // illustrates canary deployments + "ad": fe.chooseAd(r.Context(), log), }); err != nil { log.Error(err) } @@ -130,6 +133,7 @@ func (fe *frontendServer) productHandler(w http.ResponseWriter, r *http.Request) if err := templates.ExecuteTemplate(w, "product", map[string]interface{}{ "session_id": sessionID(r), "request_id": r.Context().Value(ctxKeyRequestID{}), + "ad": fe.chooseAd(r.Context(), log), "user_currency": currentCurrency(r), "currencies": currencies, "product": product, @@ -340,6 +344,17 @@ func (fe *frontendServer) setCurrencyHandler(w http.ResponseWriter, r *http.Requ w.WriteHeader(http.StatusFound) } +// chooseAd queries for advertisements available and randomly chooses one, if +// available. It ignores the error retrieving the ad since it is not critical. +func (fe *frontendServer) chooseAd(ctx context.Context, log logrus.FieldLogger) *pb.Ad { + ads, err := fe.getAd(ctx) + if err != nil { + log.WithField("error", err).Warn("failed to retrieve ads") + return nil + } + return ads[rand.Intn(len(ads))] +} + func renderHTTPError(log logrus.FieldLogger, r *http.Request, w http.ResponseWriter, err error, code int) { log.WithField("error", err).Error("request error") errMsg := fmt.Sprintf("%+v", err) diff --git a/src/frontend/main.go b/src/frontend/main.go index 4363b3f..e70dac3 100644 --- a/src/frontend/main.go +++ b/src/frontend/main.go @@ -29,6 +29,7 @@ import ( "go.opencensus.io/plugin/ocgrpc" "go.opencensus.io/plugin/ochttp" "go.opencensus.io/plugin/ochttp/propagation/b3" + "go.opencensus.io/stats/view" "go.opencensus.io/trace" "google.golang.org/grpc" ) @@ -73,6 +74,9 @@ type frontendServer struct { shippingSvcAddr string shippingSvcConn *grpc.ClientConn + + adSvcAddr string + adSvcConn *grpc.ClientConn } func main() { @@ -96,6 +100,7 @@ func main() { mustMapEnv(&svc.recommendationSvcAddr, "RECOMMENDATION_SERVICE_ADDR") mustMapEnv(&svc.checkoutSvcAddr, "CHECKOUT_SERVICE_ADDR") mustMapEnv(&svc.shippingSvcAddr, "SHIPPING_SERVICE_ADDR") + mustMapEnv(&svc.adSvcAddr, "AD_SERVICE_ADDR") mustConnGRPC(ctx, &svc.currencySvcConn, svc.currencySvcAddr) mustConnGRPC(ctx, &svc.productCatalogSvcConn, svc.productCatalogSvcAddr) @@ -103,6 +108,7 @@ func main() { mustConnGRPC(ctx, &svc.recommendationSvcConn, svc.recommendationSvcAddr) mustConnGRPC(ctx, &svc.shippingSvcConn, svc.shippingSvcAddr) mustConnGRPC(ctx, &svc.checkoutSvcConn, svc.checkoutSvcAddr) + mustConnGRPC(ctx, &svc.adSvcConn, svc.adSvcAddr) r := mux.NewRouter() r.HandleFunc("/", svc.homeHandler).Methods(http.MethodGet, http.MethodHead) @@ -114,6 +120,8 @@ func main() { r.HandleFunc("/logout", svc.logoutHandler).Methods(http.MethodGet) r.HandleFunc("/cart/checkout", svc.placeOrderHandler).Methods(http.MethodPost) r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("./static/")))) + r.HandleFunc("/robots.txt", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprint(w, "User-agent: *\nDisallow: /") }) + r.HandleFunc("/_healthz", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprint(w, "ok") }) var handler http.Handler = r handler = &logHandler{log: log, next: handler} // add logging @@ -126,6 +134,20 @@ func main() { log.Fatal(http.ListenAndServe(addr+":"+srvPort, handler)) } +func initStats(log logrus.FieldLogger, exporter *stackdriver.Exporter) { + view.RegisterExporter(exporter) + if err := view.Register(ochttp.DefaultServerViews...); err != nil { + log.Warn("Error registering http default server views") + } else { + log.Info("Registered http default server views") + } + if err := view.Register(ocgrpc.DefaultClientViews...); err != nil { + log.Warn("Error registering grpc default client views") + } else { + log.Info("Registered grpc default client views") + } +} + func initTracing(log logrus.FieldLogger) { // TODO(ahmetb) this method is duplicated in other microservices using Go // since they are not sharing packages. @@ -138,6 +160,9 @@ func initTracing(log logrus.FieldLogger) { trace.RegisterExporter(exporter) trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) log.Info("registered stackdriver tracing") + + // Register the views to collect server stats. + initStats(log, exporter) return } d := time.Second * 20 * time.Duration(i) diff --git a/src/frontend/rpc.go b/src/frontend/rpc.go index 4a396c7..05c99b2 100644 --- a/src/frontend/rpc.go +++ b/src/frontend/rpc.go @@ -16,6 +16,7 @@ package main import ( "context" + "time" pb "github.com/GoogleCloudPlatform/microservices-demo/src/frontend/genproto" @@ -114,3 +115,13 @@ func (fe *frontendServer) getRecommendations(ctx context.Context, userID string, } return out, err } + +func (fe *frontendServer) getAd(ctx context.Context) ([]*pb.Ad, error) { + ctx, cancel := context.WithTimeout(ctx, time.Millisecond*100) + defer cancel() + + resp, err := pb.NewAdServiceClient(fe.adSvcConn).GetAds(ctx, &pb.AdRequest{ + ContextKeys: nil, + }) + return resp.GetAds(), errors.Wrap(err, "failed to get ads") +} diff --git a/src/frontend/templates/ad.html b/src/frontend/templates/ad.html new file mode 100644 index 0000000..56fe321 --- /dev/null +++ b/src/frontend/templates/ad.html @@ -0,0 +1,10 @@ +{{ define "text_ad" }} +
+ +
+{{ end }} \ No newline at end of file diff --git a/src/frontend/templates/footer.html b/src/frontend/templates/footer.html index 881b7cd..0ca31e1 100644 --- a/src/frontend/templates/footer.html +++ b/src/frontend/templates/footer.html @@ -1,7 +1,12 @@ {{ define "footer" }}
-

© 2018 Google Inc

+

+ © 2018 Google Inc + + (Source Code) + +

This website is hosted for demo purposes only. It is not an @@ -12,7 +17,6 @@ {{ if $.session_id }}session-id: {{ $.session_id }}
{{end}} {{ if $.request_id }}request-id: {{ $.request_id }}
{{end}}
-

diff --git a/src/frontend/templates/home.html b/src/frontend/templates/home.html index bd028fb..1d97c6a 100644 --- a/src/frontend/templates/home.html +++ b/src/frontend/templates/home.html @@ -51,6 +51,9 @@ {{ end }} +
+ {{ with $.ad }}{{ template "text_ad" . }}{{ end}} +
diff --git a/src/frontend/templates/product.html b/src/frontend/templates/product.html index 5936054..e939987 100644 --- a/src/frontend/templates/product.html +++ b/src/frontend/templates/product.html @@ -47,9 +47,10 @@ {{ template "recommendations" $.recommendations }} {{ end }} + {{ with $.ad }}{{ template "text_ad" . }}{{ end}} + - {{ template "footer" . }} {{ end }} diff --git a/src/paymentservice/Dockerfile b/src/paymentservice/Dockerfile index 79ee243..abe0eba 100644 --- a/src/paymentservice/Dockerfile +++ b/src/paymentservice/Dockerfile @@ -1,4 +1,7 @@ FROM node:8 +RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe WORKDIR /usr/src/app diff --git a/src/paymentservice/charge.js b/src/paymentservice/charge.js index e11d0c1..a0151ed 100644 --- a/src/paymentservice/charge.js +++ b/src/paymentservice/charge.js @@ -16,26 +16,26 @@ const cardValidator = require('simple-card-validator'); const uuid = require('uuid/v4'); class CreditCardError extends Error { - constructor(message) { + constructor (message) { super(message); this.code = 400; // Invalid argument error } } class InvalidCreditCard extends CreditCardError { - constructor(cardType) { + constructor (cardType) { super(`Credit card info is invalid`); } } class UnacceptedCreditCard extends CreditCardError { - constructor(cardType) { + constructor (cardType) { super(`Sorry, we cannot process ${cardType} credit cards. Only VISA or MasterCard is accepted.`); } } class ExpiredCreditCard extends CreditCardError { - constructor(number, month, year) { + constructor (number, month, year) { super(`Your credit card (ending ${number.substr(-4)}) expired on ${month}/${year}`); } } @@ -46,34 +46,29 @@ class ExpiredCreditCard extends CreditCardError { * @param {*} request * @return transaction_id - a random uuid v4. */ -module.exports = function charge(request) { +module.exports = function charge (request) { const { amount, credit_card: creditCard } = request; const cardNumber = creditCard.credit_card_number; const cardInfo = cardValidator(cardNumber); const { card_type: cardType, - valid, - cvv_length: cvvLength, + valid } = cardInfo.getCardDetails(); - if (!valid) - throw new InvalidCreditCard(); + if (!valid) { throw new InvalidCreditCard(); } // Only VISA and mastercard is accepted, other card types (AMEX, dinersclub) will // throw UnacceptedCreditCard error. - if (!(cardType === 'visa' || cardType == 'mastercard')) - throw new UnacceptedCreditCard(cardType); + if (!(cardType === 'visa' || cardType === 'mastercard')) { throw new UnacceptedCreditCard(cardType); } // Also validate expiration is > today. const currentMonth = new Date().getMonth() + 1; const currentYear = new Date().getFullYear(); const { credit_card_expiration_year: year, credit_card_expiration_month: month } = creditCard; - if ((currentYear * 12 + currentMonth) > (year * 12 + month)) - throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); + if ((currentYear * 12 + currentMonth) > (year * 12 + month)) { throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); } console.log(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \ - Amount: ${amount.currency_code}${amount.units}.${amount.nanos}`) - - return { transaction_id: uuid() } -} + Amount: ${amount.currency_code}${amount.units}.${amount.nanos}`); + return { transaction_id: uuid() }; +}; diff --git a/src/paymentservice/genproto.sh b/src/paymentservice/genproto.sh index 2529fd9..a9609fd 100755 --- a/src/paymentservice/genproto.sh +++ b/src/paymentservice/genproto.sh @@ -14,9 +14,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -#!/bin/bash -e - # protos are loaded dynamically for node, simply copies over the proto. - -mkdir -p proto && \ -cp ../../pb/demo.proto proto +mkdir -p proto +cp -r ../../pb/* ./proto diff --git a/src/paymentservice/index.js b/src/paymentservice/index.js index 6f32be8..661bc82 100644 --- a/src/paymentservice/index.js +++ b/src/paymentservice/index.js @@ -17,25 +17,25 @@ 'use strict'; require('@google-cloud/profiler').start({ - serviceContext: { - service: 'paymentservice', - version: '1.0.0' - } - }); + serviceContext: { + service: 'paymentservice', + version: '1.0.0' + } +}); require('@google-cloud/trace-agent').start(); require('@google-cloud/debug-agent').start({ - serviceContext: { - service: 'paymentservice', - version: 'VERSION' - } - }) + serviceContext: { + service: 'paymentservice', + version: 'VERSION' + } +}); +const path = require('path'); const HipsterShopServer = require('./server'); const PORT = process.env['PORT']; -const PROTO_PATH = __dirname + '/proto/demo.proto'; +const PROTO_PATH = path.join(__dirname, '/proto/'); const server = new HipsterShopServer(PROTO_PATH, PORT); server.listen(); - diff --git a/src/paymentservice/package.json b/src/paymentservice/package.json index 4aa6c29..c62033b 100644 --- a/src/paymentservice/package.json +++ b/src/paymentservice/package.json @@ -2,9 +2,11 @@ "name": "paymentservice", "version": "0.0.1", "description": "Payment Microservice demo", + "repository": "https://github.com/GoogleCloudPlatform/microservices-demo", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "lint": "semistandard *.js" }, "author": "Jonathan Lui", "license": "ISC", @@ -16,5 +18,8 @@ "grpc": "^1.12.3", "simple-card-validator": "^1.1.0", "uuid": "^3.2.1" + }, + "devDependencies": { + "semistandard": "^12.0.1" } } diff --git a/src/paymentservice/proto/demo.proto b/src/paymentservice/proto/demo.proto index 0c3fdf2..11b8c29 100644 --- a/src/paymentservice/proto/demo.proto +++ b/src/paymentservice/proto/demo.proto @@ -108,9 +108,9 @@ message ShipOrderResponse { } message Address { - string street_address_1 = 1; - string street_address_2 = 2; - string city= 3; + string street_address = 1; + string city = 2; + string state = 3; string country = 4; int32 zip_code = 5; } @@ -202,21 +202,9 @@ message SendOrderConfirmationRequest { // -------------Checkout service----------------- service CheckoutService { - rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse) {} rpc PlaceOrder(PlaceOrderRequest) returns (PlaceOrderResponse) {} } -message CreateOrderRequest { - string user_id = 1; - string user_currency = 2; - Address address = 3; -} - -message CreateOrderResponse { - repeated OrderItem items = 1; - Money shipping_cost = 2; -} - message PlaceOrderRequest { string user_id = 1; string user_currency = 2; @@ -229,3 +217,26 @@ message PlaceOrderRequest { message PlaceOrderResponse { OrderResult order = 1; } + +// ------------Ads service------------------ + +service AdsService { + rpc GetAds(AdsRequest) returns (AdsResponse) {} +} + +message AdsRequest { + // List of important key words from the current page describing the context. + repeated string context_keys = 1; +} + +message AdsResponse { + repeated Ad ads = 1; +} + +message Ad { + // url to redirect to when an ad is clicked. + string redirect_url = 1; + + // short advertisement text to display. + string text = 2; +} diff --git a/src/paymentservice/proto/grpc/health/v1/health.proto b/src/paymentservice/proto/grpc/health/v1/health.proto new file mode 100644 index 0000000..4b4677b --- /dev/null +++ b/src/paymentservice/proto/grpc/health/v1/health.proto @@ -0,0 +1,43 @@ +// Copyright 2015 The gRPC Authors +// +// 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. + +// The canonical version of this proto can be found at +// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto + +syntax = "proto3"; + +package grpc.health.v1; + +option csharp_namespace = "Grpc.Health.V1"; +option go_package = "google.golang.org/grpc/health/grpc_health_v1"; +option java_multiple_files = true; +option java_outer_classname = "HealthProto"; +option java_package = "io.grpc.health.v1"; + +message HealthCheckRequest { + string service = 1; +} + +message HealthCheckResponse { + enum ServingStatus { + UNKNOWN = 0; + SERVING = 1; + NOT_SERVING = 2; + } + ServingStatus status = 1; +} + +service Health { + rpc Check(HealthCheckRequest) returns (HealthCheckResponse); +} diff --git a/src/paymentservice/server.js b/src/paymentservice/server.js index 410757c..ebce323 100644 --- a/src/paymentservice/server.js +++ b/src/paymentservice/server.js @@ -12,17 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +const path = require('path'); const grpc = require('grpc'); const protoLoader = require('@grpc/proto-loader'); const charge = require('./charge'); class HipsterShopServer { - constructor(protoFile, port = HipsterShopServer.DEFAULT_PORT) { + constructor (protoRoot, port = HipsterShopServer.DEFAULT_PORT) { this.port = port; + this.packages = { + hipsterShop: this.loadProto(path.join(protoRoot, 'demo.proto')), + health: this.loadProto(path.join(protoRoot, 'grpc/health/v1/health.proto')) + }; + this.server = new grpc.Server(); - this.loadProto(protoFile); + this.loadAllProtos(protoRoot); } /** @@ -30,10 +36,10 @@ class HipsterShopServer { * @param {*} call { ChargeRequest } * @param {*} callback fn(err, ChargeResponse) */ - static ChargeServiceHandler(call, callback) { + static ChargeServiceHandler (call, callback) { try { - console.log(`PaymentService#Charge invoked with request ${JSON.stringify(call.request)}`) - const response = charge(call.request) + console.log(`PaymentService#Charge invoked with request ${JSON.stringify(call.request)}`); + const response = charge(call.request); callback(null, response); } catch (err) { console.warn(err); @@ -41,13 +47,17 @@ class HipsterShopServer { } } - listen() { + static CheckHandler (call, callback) { + callback(null, { status: 'SERVING' }); + } + + listen () { this.server.bind(`0.0.0.0:${this.port}`, grpc.ServerCredentials.createInsecure()); console.log(`PaymentService grpc server listening on ${this.port}`); this.server.start(); } - loadProto(path) { + loadProto (path) { const packageDefinition = protoLoader.loadSync( path, { @@ -55,21 +65,28 @@ class HipsterShopServer { longs: String, enums: String, defaults: true, - oneofs: true, - }, + oneofs: true + } ); - const protoDescriptor = grpc.loadPackageDefinition(packageDefinition); - const hipsterShopPackage = protoDescriptor.hipstershop; - - this.addProtoService(hipsterShopPackage.PaymentService.service); + return grpc.loadPackageDefinition(packageDefinition); } - addProtoService(service) { + loadAllProtos (protoRoot) { + const hipsterShopPackage = this.packages.hipsterShop.hipstershop; + const healthPackage = this.packages.health.grpc.health.v1; + this.server.addService( - service, + hipsterShopPackage.PaymentService.service, { - charge: HipsterShopServer.ChargeServiceHandler.bind(this), - }, + charge: HipsterShopServer.ChargeServiceHandler.bind(this) + } + ); + + this.server.addService( + healthPackage.Health.service, + { + check: HipsterShopServer.CheckHandler.bind(this) + } ); } } diff --git a/src/productcatalogservice/Dockerfile b/src/productcatalogservice/Dockerfile index b2677d5..f941605 100644 --- a/src/productcatalogservice/Dockerfile +++ b/src/productcatalogservice/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.10-alpine as builder +FROM golang:1.10-alpine AS builder RUN apk add --no-cache ca-certificates git && \ wget -qO/go/bin/dep https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 && \ chmod +x /go/bin/dep @@ -13,8 +13,11 @@ RUN dep ensure --vendor-only -v COPY . . RUN go build -o /productcatalogservice . -FROM alpine as release +FROM alpine AS release RUN apk add --no-cache ca-certificates +RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe WORKDIR /productcatalogservice COPY --from=builder /productcatalogservice ./server COPY products.json . diff --git a/src/productcatalogservice/Gopkg.lock b/src/productcatalogservice/Gopkg.lock index 2e01527..b9a3bbd 100644 --- a/src/productcatalogservice/Gopkg.lock +++ b/src/productcatalogservice/Gopkg.lock @@ -10,8 +10,8 @@ "profiler", "trace/apiv2" ] - revision = "aad3f485ee528456e0768f20397b4d9dd941e755" - version = "v0.25.0" + revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad" + version = "v0.27.0" [[projects]] name = "contrib.go.opencensus.io/exporter/stackdriver" @@ -36,8 +36,8 @@ "ptypes/timestamp", "ptypes/wrappers" ] - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" [[projects]] name = "github.com/google/go-cmp" @@ -54,7 +54,7 @@ branch = "master" name = "github.com/google/pprof" packages = ["profile"] - revision = "ef437552946f69f7e3bdf1fd81c385c29530944d" + revision = "e027b505a088ac3c68c339a1d7ce7724bf34538b" [[projects]] name = "github.com/googleapis/gax-go" @@ -77,10 +77,11 @@ "tag", "trace", "trace/internal", - "trace/propagation" + "trace/propagation", + "trace/tracestate" ] - revision = "e262766cd0d230a1bb7c37281e345e465f19b41b" - version = "v0.14.0" + revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89" + version = "v0.16.0" [[projects]] branch = "master" @@ -95,7 +96,7 @@ "internal/timeseries", "trace" ] - revision = "f4c29de78a2a91c00474a2e689954305c350adf9" + revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" [[projects]] branch = "master" @@ -107,7 +108,7 @@ "jws", "jwt" ] - revision = "3d292e4d0cdc3a0113e6d207bb137145ef1de42f" + revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9" [[projects]] branch = "master" @@ -119,7 +120,7 @@ branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "0ffbfd41fbef8ffcf9b62b0b0aa3a5873ed7a4fe" + revision = "1561086e645b2809fb9f8a1e2a38160bf8d53bf4" [[projects]] name = "golang.org/x/text" @@ -155,7 +156,7 @@ "transport/grpc", "transport/http" ] - revision = "f6d94689cbd71030af1108ddac733886fcae1d75" + revision = "19ff8768a5c0b8e46ea281065664787eefc24121" [[projects]] name = "google.golang.org/appengine" @@ -173,8 +174,8 @@ "socket", "urlfetch" ] - revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" - version = "v1.1.0" + revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06" + version = "v1.2.0" [[projects]] branch = "master" @@ -192,7 +193,7 @@ "googleapis/rpc/status", "protobuf/field_mask" ] - revision = "daca94659cb50e9f37c1b834680f2e46358f10b0" + revision = "c3f76f3b92d1ffa4c58a9ff842a58b8877655e0f" [[projects]] name = "google.golang.org/grpc" @@ -208,6 +209,7 @@ "encoding", "encoding/proto", "grpclog", + "health/grpc_health_v1", "internal", "internal/backoff", "internal/channelz", @@ -231,6 +233,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "c8e8e119ed16d97490ab7d949aa70654738f91a59da70583dfed22ee44661a1b" + inputs-digest = "b68fd9438c2eb711d9fc51c1f23c5ca0d5169bf4022351dfc400cd35ba39dfaa" solver-name = "gps-cdcl" solver-version = 1 diff --git a/src/productcatalogservice/Gopkg.toml b/src/productcatalogservice/Gopkg.toml index 3dc2c8e..a9b34ec 100644 --- a/src/productcatalogservice/Gopkg.toml +++ b/src/productcatalogservice/Gopkg.toml @@ -27,7 +27,7 @@ [[constraint]] name = "cloud.google.com/go" - version = "0.25.0" + version = "0.27.0" [[constraint]] name = "contrib.go.opencensus.io/exporter/stackdriver" @@ -35,7 +35,7 @@ [[constraint]] name = "github.com/golang/protobuf" - version = "1.1.0" + version = "1.2.0" [[constraint]] name = "github.com/google/go-cmp" @@ -43,7 +43,7 @@ [[constraint]] name = "go.opencensus.io" - version = "0.14.0" + version = "0.16.0" [[constraint]] branch = "master" @@ -51,7 +51,7 @@ [[constraint]] name = "google.golang.org/grpc" - version = "1.14.0" + version = "=1.14.0" [prune] go-tests = true diff --git a/src/productcatalogservice/server.go b/src/productcatalogservice/server.go index 6870b02..6599877 100644 --- a/src/productcatalogservice/server.go +++ b/src/productcatalogservice/server.go @@ -26,11 +26,13 @@ import ( "time" pb "github.com/GoogleCloudPlatform/microservices-demo/src/productcatalogservice/genproto" + healthpb "google.golang.org/grpc/health/grpc_health_v1" "cloud.google.com/go/profiler" "contrib.go.opencensus.io/exporter/stackdriver" "github.com/golang/protobuf/jsonpb" "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/stats/view" "go.opencensus.io/trace" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -68,11 +70,22 @@ func run(port int) string { log.Fatal(err) } srv := grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{})) - pb.RegisterProductCatalogServiceServer(srv, &productCatalog{}) + svc := &productCatalog{} + pb.RegisterProductCatalogServiceServer(srv, svc) + healthpb.RegisterHealthServer(srv, svc) go srv.Serve(l) return l.Addr().String() } +func initStats(exporter *stackdriver.Exporter) { + view.RegisterExporter(exporter) + if err := view.Register(ocgrpc.DefaultServerViews...); err != nil { + log.Printf("Error registering default server views") + } else { + log.Printf("Registered default server views") + } +} + func initTracing() { // TODO(ahmetb) this method is duplicated in other microservices using Go // since they are not sharing packages. @@ -84,6 +97,9 @@ func initTracing() { trace.RegisterExporter(exporter) trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) log.Print("registered stackdriver tracing") + + // Register the views to collect server stats. + initStats(exporter) return } d := time.Second * 10 * time.Duration(i) @@ -127,6 +143,10 @@ func parseCatalog() []*pb.Product { return cat.Products } +func (p *productCatalog) Check(ctx context.Context, req *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { + return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil +} + func (p *productCatalog) ListProducts(context.Context, *pb.Empty) (*pb.ListProductsResponse, error) { return &pb.ListProductsResponse{Products: parseCatalog()}, nil } diff --git a/src/recommendationservice/Dockerfile b/src/recommendationservice/Dockerfile index 6616e73..1490261 100644 --- a/src/recommendationservice/Dockerfile +++ b/src/recommendationservice/Dockerfile @@ -3,6 +3,11 @@ FROM grpc/python:1.0 # show python logs as they occur ENV PYTHONUNBUFFERED=0 +# download the grpc health probe +RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe + # get packages WORKDIR /recommendationservice COPY requirements.txt requirements.txt diff --git a/src/recommendationservice/demo_pb2.py b/src/recommendationservice/demo_pb2.py index d72a5e7..e662731 100644 --- a/src/recommendationservice/demo_pb2.py +++ b/src/recommendationservice/demo_pb2.py @@ -1,19 +1,3 @@ -#!/usr/bin/python -# -# Copyright 2018 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. - # Generated by the protocol buffer compiler. DO NOT EDIT! # source: demo.proto @@ -35,7 +19,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( name='demo.proto', package='hipstershop', syntax='proto3', - serialized_pb=_b('\n\ndemo.proto\x12\x0bhipstershop\"0\n\x08\x43\x61rtItem\x12\x12\n\nproduct_id\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\x05\"F\n\x0e\x41\x64\x64ItemRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12#\n\x04item\x18\x02 \x01(\x0b\x32\x15.hipstershop.CartItem\"#\n\x10\x45mptyCartRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\"!\n\x0eGetCartRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\"=\n\x04\x43\x61rt\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12$\n\x05items\x18\x02 \x03(\x0b\x32\x15.hipstershop.CartItem\"\x07\n\x05\x45mpty\"B\n\x1aListRecommendationsRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x13\n\x0bproduct_ids\x18\x02 \x03(\t\"2\n\x1bListRecommendationsResponse\x12\x13\n\x0bproduct_ids\x18\x01 \x03(\t\"p\n\x07Product\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07picture\x18\x04 \x01(\t\x12%\n\tprice_usd\x18\x05 \x01(\x0b\x32\x12.hipstershop.Money\">\n\x14ListProductsResponse\x12&\n\x08products\x18\x01 \x03(\x0b\x32\x14.hipstershop.Product\"\x1f\n\x11GetProductRequest\x12\n\n\x02id\x18\x01 \x01(\t\"&\n\x15SearchProductsRequest\x12\r\n\x05query\x18\x01 \x01(\t\"?\n\x16SearchProductsResponse\x12%\n\x07results\x18\x01 \x03(\x0b\x32\x14.hipstershop.Product\"^\n\x0fGetQuoteRequest\x12%\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x14.hipstershop.Address\x12$\n\x05items\x18\x02 \x03(\x0b\x32\x15.hipstershop.CartItem\"8\n\x10GetQuoteResponse\x12$\n\x08\x63ost_usd\x18\x01 \x01(\x0b\x32\x12.hipstershop.Money\"_\n\x10ShipOrderRequest\x12%\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x14.hipstershop.Address\x12$\n\x05items\x18\x02 \x03(\x0b\x32\x15.hipstershop.CartItem\"(\n\x11ShipOrderResponse\x12\x13\n\x0btracking_id\x18\x01 \x01(\t\"n\n\x07\x41\x64\x64ress\x12\x18\n\x10street_address_1\x18\x01 \x01(\t\x12\x18\n\x10street_address_2\x18\x02 \x01(\t\x12\x0c\n\x04\x63ity\x18\x03 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x04 \x01(\t\x12\x10\n\x08zip_code\x18\x05 \x01(\x05\"<\n\x05Money\x12\x15\n\rcurrency_code\x18\x01 \x01(\t\x12\r\n\x05units\x18\x02 \x01(\x03\x12\r\n\x05nanos\x18\x03 \x01(\x05\"8\n\x1eGetSupportedCurrenciesResponse\x12\x16\n\x0e\x63urrency_codes\x18\x01 \x03(\t\"N\n\x19\x43urrencyConversionRequest\x12 \n\x04\x66rom\x18\x01 \x01(\x0b\x32\x12.hipstershop.Money\x12\x0f\n\x07to_code\x18\x02 \x01(\t\"\x90\x01\n\x0e\x43reditCardInfo\x12\x1a\n\x12\x63redit_card_number\x18\x01 \x01(\t\x12\x17\n\x0f\x63redit_card_cvv\x18\x02 \x01(\x05\x12#\n\x1b\x63redit_card_expiration_year\x18\x03 \x01(\x05\x12$\n\x1c\x63redit_card_expiration_month\x18\x04 \x01(\x05\"e\n\rChargeRequest\x12\"\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x12.hipstershop.Money\x12\x30\n\x0b\x63redit_card\x18\x02 \x01(\x0b\x32\x1b.hipstershop.CreditCardInfo\"(\n\x0e\x43hargeResponse\x12\x16\n\x0etransaction_id\x18\x01 \x01(\t\"R\n\tOrderItem\x12#\n\x04item\x18\x01 \x01(\x0b\x32\x15.hipstershop.CartItem\x12 \n\x04\x63ost\x18\x02 \x01(\x0b\x32\x12.hipstershop.Money\"\xbf\x01\n\x0bOrderResult\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x1c\n\x14shipping_tracking_id\x18\x02 \x01(\t\x12)\n\rshipping_cost\x18\x03 \x01(\x0b\x32\x12.hipstershop.Money\x12.\n\x10shipping_address\x18\x04 \x01(\x0b\x32\x14.hipstershop.Address\x12%\n\x05items\x18\x05 \x03(\x0b\x32\x16.hipstershop.OrderItem\"V\n\x1cSendOrderConfirmationRequest\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\'\n\x05order\x18\x02 \x01(\x0b\x32\x18.hipstershop.OrderResult\"c\n\x12\x43reateOrderRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x15\n\ruser_currency\x18\x02 \x01(\t\x12%\n\x07\x61\x64\x64ress\x18\x03 \x01(\x0b\x32\x14.hipstershop.Address\"g\n\x13\x43reateOrderResponse\x12%\n\x05items\x18\x01 \x03(\x0b\x32\x16.hipstershop.OrderItem\x12)\n\rshipping_cost\x18\x02 \x01(\x0b\x32\x12.hipstershop.Money\"\xa3\x01\n\x11PlaceOrderRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x15\n\ruser_currency\x18\x02 \x01(\t\x12%\n\x07\x61\x64\x64ress\x18\x03 \x01(\x0b\x32\x14.hipstershop.Address\x12\r\n\x05\x65mail\x18\x05 \x01(\t\x12\x30\n\x0b\x63redit_card\x18\x06 \x01(\x0b\x32\x1b.hipstershop.CreditCardInfo\"=\n\x12PlaceOrderResponse\x12\'\n\x05order\x18\x01 \x01(\x0b\x32\x18.hipstershop.OrderResult2\xca\x01\n\x0b\x43\x61rtService\x12<\n\x07\x41\x64\x64Item\x12\x1b.hipstershop.AddItemRequest\x1a\x12.hipstershop.Empty\"\x00\x12;\n\x07GetCart\x12\x1b.hipstershop.GetCartRequest\x1a\x11.hipstershop.Cart\"\x00\x12@\n\tEmptyCart\x12\x1d.hipstershop.EmptyCartRequest\x1a\x12.hipstershop.Empty\"\x00\x32\x83\x01\n\x15RecommendationService\x12j\n\x13ListRecommendations\x12\'.hipstershop.ListRecommendationsRequest\x1a(.hipstershop.ListRecommendationsResponse\"\x00\x32\x83\x02\n\x15ProductCatalogService\x12G\n\x0cListProducts\x12\x12.hipstershop.Empty\x1a!.hipstershop.ListProductsResponse\"\x00\x12\x44\n\nGetProduct\x12\x1e.hipstershop.GetProductRequest\x1a\x14.hipstershop.Product\"\x00\x12[\n\x0eSearchProducts\x12\".hipstershop.SearchProductsRequest\x1a#.hipstershop.SearchProductsResponse\"\x00\x32\xaa\x01\n\x0fShippingService\x12I\n\x08GetQuote\x12\x1c.hipstershop.GetQuoteRequest\x1a\x1d.hipstershop.GetQuoteResponse\"\x00\x12L\n\tShipOrder\x12\x1d.hipstershop.ShipOrderRequest\x1a\x1e.hipstershop.ShipOrderResponse\"\x00\x32\xb7\x01\n\x0f\x43urrencyService\x12[\n\x16GetSupportedCurrencies\x12\x12.hipstershop.Empty\x1a+.hipstershop.GetSupportedCurrenciesResponse\"\x00\x12G\n\x07\x43onvert\x12&.hipstershop.CurrencyConversionRequest\x1a\x12.hipstershop.Money\"\x00\x32U\n\x0ePaymentService\x12\x43\n\x06\x43harge\x12\x1a.hipstershop.ChargeRequest\x1a\x1b.hipstershop.ChargeResponse\"\x00\x32h\n\x0c\x45mailService\x12X\n\x15SendOrderConfirmation\x12).hipstershop.SendOrderConfirmationRequest\x1a\x12.hipstershop.Empty\"\x00\x32\xb6\x01\n\x0f\x43heckoutService\x12R\n\x0b\x43reateOrder\x12\x1f.hipstershop.CreateOrderRequest\x1a .hipstershop.CreateOrderResponse\"\x00\x12O\n\nPlaceOrder\x12\x1e.hipstershop.PlaceOrderRequest\x1a\x1f.hipstershop.PlaceOrderResponse\"\x00\x62\x06proto3') + serialized_pb=_b('\n\ndemo.proto\x12\x0bhipstershop\"0\n\x08\x43\x61rtItem\x12\x12\n\nproduct_id\x18\x01 \x01(\t\x12\x10\n\x08quantity\x18\x02 \x01(\x05\"F\n\x0e\x41\x64\x64ItemRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12#\n\x04item\x18\x02 \x01(\x0b\x32\x15.hipstershop.CartItem\"#\n\x10\x45mptyCartRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\"!\n\x0eGetCartRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\"=\n\x04\x43\x61rt\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12$\n\x05items\x18\x02 \x03(\x0b\x32\x15.hipstershop.CartItem\"\x07\n\x05\x45mpty\"B\n\x1aListRecommendationsRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x13\n\x0bproduct_ids\x18\x02 \x03(\t\"2\n\x1bListRecommendationsResponse\x12\x13\n\x0bproduct_ids\x18\x01 \x03(\t\"p\n\x07Product\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x03 \x01(\t\x12\x0f\n\x07picture\x18\x04 \x01(\t\x12%\n\tprice_usd\x18\x05 \x01(\x0b\x32\x12.hipstershop.Money\">\n\x14ListProductsResponse\x12&\n\x08products\x18\x01 \x03(\x0b\x32\x14.hipstershop.Product\"\x1f\n\x11GetProductRequest\x12\n\n\x02id\x18\x01 \x01(\t\"&\n\x15SearchProductsRequest\x12\r\n\x05query\x18\x01 \x01(\t\"?\n\x16SearchProductsResponse\x12%\n\x07results\x18\x01 \x03(\x0b\x32\x14.hipstershop.Product\"^\n\x0fGetQuoteRequest\x12%\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x14.hipstershop.Address\x12$\n\x05items\x18\x02 \x03(\x0b\x32\x15.hipstershop.CartItem\"8\n\x10GetQuoteResponse\x12$\n\x08\x63ost_usd\x18\x01 \x01(\x0b\x32\x12.hipstershop.Money\"_\n\x10ShipOrderRequest\x12%\n\x07\x61\x64\x64ress\x18\x01 \x01(\x0b\x32\x14.hipstershop.Address\x12$\n\x05items\x18\x02 \x03(\x0b\x32\x15.hipstershop.CartItem\"(\n\x11ShipOrderResponse\x12\x13\n\x0btracking_id\x18\x01 \x01(\t\"a\n\x07\x41\x64\x64ress\x12\x16\n\x0estreet_address\x18\x01 \x01(\t\x12\x0c\n\x04\x63ity\x18\x02 \x01(\t\x12\r\n\x05state\x18\x03 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x04 \x01(\t\x12\x10\n\x08zip_code\x18\x05 \x01(\x05\"<\n\x05Money\x12\x15\n\rcurrency_code\x18\x01 \x01(\t\x12\r\n\x05units\x18\x02 \x01(\x03\x12\r\n\x05nanos\x18\x03 \x01(\x05\"8\n\x1eGetSupportedCurrenciesResponse\x12\x16\n\x0e\x63urrency_codes\x18\x01 \x03(\t\"N\n\x19\x43urrencyConversionRequest\x12 \n\x04\x66rom\x18\x01 \x01(\x0b\x32\x12.hipstershop.Money\x12\x0f\n\x07to_code\x18\x02 \x01(\t\"\x90\x01\n\x0e\x43reditCardInfo\x12\x1a\n\x12\x63redit_card_number\x18\x01 \x01(\t\x12\x17\n\x0f\x63redit_card_cvv\x18\x02 \x01(\x05\x12#\n\x1b\x63redit_card_expiration_year\x18\x03 \x01(\x05\x12$\n\x1c\x63redit_card_expiration_month\x18\x04 \x01(\x05\"e\n\rChargeRequest\x12\"\n\x06\x61mount\x18\x01 \x01(\x0b\x32\x12.hipstershop.Money\x12\x30\n\x0b\x63redit_card\x18\x02 \x01(\x0b\x32\x1b.hipstershop.CreditCardInfo\"(\n\x0e\x43hargeResponse\x12\x16\n\x0etransaction_id\x18\x01 \x01(\t\"R\n\tOrderItem\x12#\n\x04item\x18\x01 \x01(\x0b\x32\x15.hipstershop.CartItem\x12 \n\x04\x63ost\x18\x02 \x01(\x0b\x32\x12.hipstershop.Money\"\xbf\x01\n\x0bOrderResult\x12\x10\n\x08order_id\x18\x01 \x01(\t\x12\x1c\n\x14shipping_tracking_id\x18\x02 \x01(\t\x12)\n\rshipping_cost\x18\x03 \x01(\x0b\x32\x12.hipstershop.Money\x12.\n\x10shipping_address\x18\x04 \x01(\x0b\x32\x14.hipstershop.Address\x12%\n\x05items\x18\x05 \x03(\x0b\x32\x16.hipstershop.OrderItem\"V\n\x1cSendOrderConfirmationRequest\x12\r\n\x05\x65mail\x18\x01 \x01(\t\x12\'\n\x05order\x18\x02 \x01(\x0b\x32\x18.hipstershop.OrderResult\"\xa3\x01\n\x11PlaceOrderRequest\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x15\n\ruser_currency\x18\x02 \x01(\t\x12%\n\x07\x61\x64\x64ress\x18\x03 \x01(\x0b\x32\x14.hipstershop.Address\x12\r\n\x05\x65mail\x18\x05 \x01(\t\x12\x30\n\x0b\x63redit_card\x18\x06 \x01(\x0b\x32\x1b.hipstershop.CreditCardInfo\"=\n\x12PlaceOrderResponse\x12\'\n\x05order\x18\x01 \x01(\x0b\x32\x18.hipstershop.OrderResult\"\"\n\nAdsRequest\x12\x14\n\x0c\x63ontext_keys\x18\x01 \x03(\t\"+\n\x0b\x41\x64sResponse\x12\x1c\n\x03\x61\x64s\x18\x01 \x03(\x0b\x32\x0f.hipstershop.Ad\"(\n\x02\x41\x64\x12\x14\n\x0credirect_url\x18\x01 \x01(\t\x12\x0c\n\x04text\x18\x02 \x01(\t2\xca\x01\n\x0b\x43\x61rtService\x12<\n\x07\x41\x64\x64Item\x12\x1b.hipstershop.AddItemRequest\x1a\x12.hipstershop.Empty\"\x00\x12;\n\x07GetCart\x12\x1b.hipstershop.GetCartRequest\x1a\x11.hipstershop.Cart\"\x00\x12@\n\tEmptyCart\x12\x1d.hipstershop.EmptyCartRequest\x1a\x12.hipstershop.Empty\"\x00\x32\x83\x01\n\x15RecommendationService\x12j\n\x13ListRecommendations\x12\'.hipstershop.ListRecommendationsRequest\x1a(.hipstershop.ListRecommendationsResponse\"\x00\x32\x83\x02\n\x15ProductCatalogService\x12G\n\x0cListProducts\x12\x12.hipstershop.Empty\x1a!.hipstershop.ListProductsResponse\"\x00\x12\x44\n\nGetProduct\x12\x1e.hipstershop.GetProductRequest\x1a\x14.hipstershop.Product\"\x00\x12[\n\x0eSearchProducts\x12\".hipstershop.SearchProductsRequest\x1a#.hipstershop.SearchProductsResponse\"\x00\x32\xaa\x01\n\x0fShippingService\x12I\n\x08GetQuote\x12\x1c.hipstershop.GetQuoteRequest\x1a\x1d.hipstershop.GetQuoteResponse\"\x00\x12L\n\tShipOrder\x12\x1d.hipstershop.ShipOrderRequest\x1a\x1e.hipstershop.ShipOrderResponse\"\x00\x32\xb7\x01\n\x0f\x43urrencyService\x12[\n\x16GetSupportedCurrencies\x12\x12.hipstershop.Empty\x1a+.hipstershop.GetSupportedCurrenciesResponse\"\x00\x12G\n\x07\x43onvert\x12&.hipstershop.CurrencyConversionRequest\x1a\x12.hipstershop.Money\"\x00\x32U\n\x0ePaymentService\x12\x43\n\x06\x43harge\x12\x1a.hipstershop.ChargeRequest\x1a\x1b.hipstershop.ChargeResponse\"\x00\x32h\n\x0c\x45mailService\x12X\n\x15SendOrderConfirmation\x12).hipstershop.SendOrderConfirmationRequest\x1a\x12.hipstershop.Empty\"\x00\x32\x62\n\x0f\x43heckoutService\x12O\n\nPlaceOrder\x12\x1e.hipstershop.PlaceOrderRequest\x1a\x1f.hipstershop.PlaceOrderResponse\"\x00\x32K\n\nAdsService\x12=\n\x06GetAds\x12\x17.hipstershop.AdsRequest\x1a\x18.hipstershop.AdsResponse\"\x00\x62\x06proto3') ) @@ -639,21 +623,21 @@ _ADDRESS = _descriptor.Descriptor( containing_type=None, fields=[ _descriptor.FieldDescriptor( - name='street_address_1', full_name='hipstershop.Address.street_address_1', index=0, + name='street_address', full_name='hipstershop.Address.street_address', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( - name='street_address_2', full_name='hipstershop.Address.street_address_2', index=1, + name='city', full_name='hipstershop.Address.city', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None, file=DESCRIPTOR), _descriptor.FieldDescriptor( - name='city', full_name='hipstershop.Address.city', index=2, + name='state', full_name='hipstershop.Address.state', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, @@ -686,7 +670,7 @@ _ADDRESS = _descriptor.Descriptor( oneofs=[ ], serialized_start=1022, - serialized_end=1132, + serialized_end=1119, ) @@ -730,8 +714,8 @@ _MONEY = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1134, - serialized_end=1194, + serialized_start=1121, + serialized_end=1181, ) @@ -761,8 +745,8 @@ _GETSUPPORTEDCURRENCIESRESPONSE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1196, - serialized_end=1252, + serialized_start=1183, + serialized_end=1239, ) @@ -799,8 +783,8 @@ _CURRENCYCONVERSIONREQUEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1254, - serialized_end=1332, + serialized_start=1241, + serialized_end=1319, ) @@ -851,8 +835,8 @@ _CREDITCARDINFO = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1335, - serialized_end=1479, + serialized_start=1322, + serialized_end=1466, ) @@ -889,8 +873,8 @@ _CHARGEREQUEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1481, - serialized_end=1582, + serialized_start=1468, + serialized_end=1569, ) @@ -920,8 +904,8 @@ _CHARGERESPONSE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1584, - serialized_end=1624, + serialized_start=1571, + serialized_end=1611, ) @@ -958,8 +942,8 @@ _ORDERITEM = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1626, - serialized_end=1708, + serialized_start=1613, + serialized_end=1695, ) @@ -1017,8 +1001,8 @@ _ORDERRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1711, - serialized_end=1902, + serialized_start=1698, + serialized_end=1889, ) @@ -1055,91 +1039,8 @@ _SENDORDERCONFIRMATIONREQUEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1904, - serialized_end=1990, -) - - -_CREATEORDERREQUEST = _descriptor.Descriptor( - name='CreateOrderRequest', - full_name='hipstershop.CreateOrderRequest', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='user_id', full_name='hipstershop.CreateOrderRequest.user_id', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='user_currency', full_name='hipstershop.CreateOrderRequest.user_currency', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=_b("").decode('utf-8'), - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='address', full_name='hipstershop.CreateOrderRequest.address', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=1992, - serialized_end=2091, -) - - -_CREATEORDERRESPONSE = _descriptor.Descriptor( - name='CreateOrderResponse', - full_name='hipstershop.CreateOrderResponse', - filename=None, - file=DESCRIPTOR, - containing_type=None, - fields=[ - _descriptor.FieldDescriptor( - name='items', full_name='hipstershop.CreateOrderResponse.items', index=0, - number=1, type=11, cpp_type=10, label=3, - has_default_value=False, default_value=[], - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - _descriptor.FieldDescriptor( - name='shipping_cost', full_name='hipstershop.CreateOrderResponse.shipping_cost', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, - message_type=None, enum_type=None, containing_type=None, - is_extension=False, extension_scope=None, - options=None, file=DESCRIPTOR), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=2093, - serialized_end=2196, + serialized_start=1891, + serialized_end=1977, ) @@ -1197,8 +1098,8 @@ _PLACEORDERREQUEST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2199, - serialized_end=2362, + serialized_start=1980, + serialized_end=2143, ) @@ -1228,8 +1129,108 @@ _PLACEORDERRESPONSE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2364, - serialized_end=2425, + serialized_start=2145, + serialized_end=2206, +) + + +_ADSREQUEST = _descriptor.Descriptor( + name='AdsRequest', + full_name='hipstershop.AdsRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='context_keys', full_name='hipstershop.AdsRequest.context_keys', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2208, + serialized_end=2242, +) + + +_ADSRESPONSE = _descriptor.Descriptor( + name='AdsResponse', + full_name='hipstershop.AdsResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='ads', full_name='hipstershop.AdsResponse.ads', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2244, + serialized_end=2287, +) + + +_AD = _descriptor.Descriptor( + name='Ad', + full_name='hipstershop.Ad', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='redirect_url', full_name='hipstershop.Ad.redirect_url', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='text', full_name='hipstershop.Ad.text', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2289, + serialized_end=2329, ) _ADDITEMREQUEST.fields_by_name['item'].message_type = _CARTITEM @@ -1251,12 +1252,10 @@ _ORDERRESULT.fields_by_name['shipping_cost'].message_type = _MONEY _ORDERRESULT.fields_by_name['shipping_address'].message_type = _ADDRESS _ORDERRESULT.fields_by_name['items'].message_type = _ORDERITEM _SENDORDERCONFIRMATIONREQUEST.fields_by_name['order'].message_type = _ORDERRESULT -_CREATEORDERREQUEST.fields_by_name['address'].message_type = _ADDRESS -_CREATEORDERRESPONSE.fields_by_name['items'].message_type = _ORDERITEM -_CREATEORDERRESPONSE.fields_by_name['shipping_cost'].message_type = _MONEY _PLACEORDERREQUEST.fields_by_name['address'].message_type = _ADDRESS _PLACEORDERREQUEST.fields_by_name['credit_card'].message_type = _CREDITCARDINFO _PLACEORDERRESPONSE.fields_by_name['order'].message_type = _ORDERRESULT +_ADSRESPONSE.fields_by_name['ads'].message_type = _AD DESCRIPTOR.message_types_by_name['CartItem'] = _CARTITEM DESCRIPTOR.message_types_by_name['AddItemRequest'] = _ADDITEMREQUEST DESCRIPTOR.message_types_by_name['EmptyCartRequest'] = _EMPTYCARTREQUEST @@ -1284,10 +1283,11 @@ DESCRIPTOR.message_types_by_name['ChargeResponse'] = _CHARGERESPONSE DESCRIPTOR.message_types_by_name['OrderItem'] = _ORDERITEM DESCRIPTOR.message_types_by_name['OrderResult'] = _ORDERRESULT DESCRIPTOR.message_types_by_name['SendOrderConfirmationRequest'] = _SENDORDERCONFIRMATIONREQUEST -DESCRIPTOR.message_types_by_name['CreateOrderRequest'] = _CREATEORDERREQUEST -DESCRIPTOR.message_types_by_name['CreateOrderResponse'] = _CREATEORDERRESPONSE DESCRIPTOR.message_types_by_name['PlaceOrderRequest'] = _PLACEORDERREQUEST DESCRIPTOR.message_types_by_name['PlaceOrderResponse'] = _PLACEORDERRESPONSE +DESCRIPTOR.message_types_by_name['AdsRequest'] = _ADSREQUEST +DESCRIPTOR.message_types_by_name['AdsResponse'] = _ADSRESPONSE +DESCRIPTOR.message_types_by_name['Ad'] = _AD _sym_db.RegisterFileDescriptor(DESCRIPTOR) CartItem = _reflection.GeneratedProtocolMessageType('CartItem', (_message.Message,), dict( @@ -1479,20 +1479,6 @@ SendOrderConfirmationRequest = _reflection.GeneratedProtocolMessageType('SendOrd )) _sym_db.RegisterMessage(SendOrderConfirmationRequest) -CreateOrderRequest = _reflection.GeneratedProtocolMessageType('CreateOrderRequest', (_message.Message,), dict( - DESCRIPTOR = _CREATEORDERREQUEST, - __module__ = 'demo_pb2' - # @@protoc_insertion_point(class_scope:hipstershop.CreateOrderRequest) - )) -_sym_db.RegisterMessage(CreateOrderRequest) - -CreateOrderResponse = _reflection.GeneratedProtocolMessageType('CreateOrderResponse', (_message.Message,), dict( - DESCRIPTOR = _CREATEORDERRESPONSE, - __module__ = 'demo_pb2' - # @@protoc_insertion_point(class_scope:hipstershop.CreateOrderResponse) - )) -_sym_db.RegisterMessage(CreateOrderResponse) - PlaceOrderRequest = _reflection.GeneratedProtocolMessageType('PlaceOrderRequest', (_message.Message,), dict( DESCRIPTOR = _PLACEORDERREQUEST, __module__ = 'demo_pb2' @@ -1507,6 +1493,27 @@ PlaceOrderResponse = _reflection.GeneratedProtocolMessageType('PlaceOrderRespons )) _sym_db.RegisterMessage(PlaceOrderResponse) +AdsRequest = _reflection.GeneratedProtocolMessageType('AdsRequest', (_message.Message,), dict( + DESCRIPTOR = _ADSREQUEST, + __module__ = 'demo_pb2' + # @@protoc_insertion_point(class_scope:hipstershop.AdsRequest) + )) +_sym_db.RegisterMessage(AdsRequest) + +AdsResponse = _reflection.GeneratedProtocolMessageType('AdsResponse', (_message.Message,), dict( + DESCRIPTOR = _ADSRESPONSE, + __module__ = 'demo_pb2' + # @@protoc_insertion_point(class_scope:hipstershop.AdsResponse) + )) +_sym_db.RegisterMessage(AdsResponse) + +Ad = _reflection.GeneratedProtocolMessageType('Ad', (_message.Message,), dict( + DESCRIPTOR = _AD, + __module__ = 'demo_pb2' + # @@protoc_insertion_point(class_scope:hipstershop.Ad) + )) +_sym_db.RegisterMessage(Ad) + _CARTSERVICE = _descriptor.ServiceDescriptor( @@ -1515,8 +1522,8 @@ _CARTSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=0, options=None, - serialized_start=2428, - serialized_end=2630, + serialized_start=2332, + serialized_end=2534, methods=[ _descriptor.MethodDescriptor( name='AddItem', @@ -1557,8 +1564,8 @@ _RECOMMENDATIONSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=1, options=None, - serialized_start=2633, - serialized_end=2764, + serialized_start=2537, + serialized_end=2668, methods=[ _descriptor.MethodDescriptor( name='ListRecommendations', @@ -1581,8 +1588,8 @@ _PRODUCTCATALOGSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=2, options=None, - serialized_start=2767, - serialized_end=3026, + serialized_start=2671, + serialized_end=2930, methods=[ _descriptor.MethodDescriptor( name='ListProducts', @@ -1623,8 +1630,8 @@ _SHIPPINGSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=3, options=None, - serialized_start=3029, - serialized_end=3199, + serialized_start=2933, + serialized_end=3103, methods=[ _descriptor.MethodDescriptor( name='GetQuote', @@ -1656,8 +1663,8 @@ _CURRENCYSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=4, options=None, - serialized_start=3202, - serialized_end=3385, + serialized_start=3106, + serialized_end=3289, methods=[ _descriptor.MethodDescriptor( name='GetSupportedCurrencies', @@ -1689,8 +1696,8 @@ _PAYMENTSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=5, options=None, - serialized_start=3387, - serialized_end=3472, + serialized_start=3291, + serialized_end=3376, methods=[ _descriptor.MethodDescriptor( name='Charge', @@ -1713,8 +1720,8 @@ _EMAILSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=6, options=None, - serialized_start=3474, - serialized_end=3578, + serialized_start=3378, + serialized_end=3482, methods=[ _descriptor.MethodDescriptor( name='SendOrderConfirmation', @@ -1737,22 +1744,13 @@ _CHECKOUTSERVICE = _descriptor.ServiceDescriptor( file=DESCRIPTOR, index=7, options=None, - serialized_start=3581, - serialized_end=3763, + serialized_start=3484, + serialized_end=3582, methods=[ - _descriptor.MethodDescriptor( - name='CreateOrder', - full_name='hipstershop.CheckoutService.CreateOrder', - index=0, - containing_service=None, - input_type=_CREATEORDERREQUEST, - output_type=_CREATEORDERRESPONSE, - options=None, - ), _descriptor.MethodDescriptor( name='PlaceOrder', full_name='hipstershop.CheckoutService.PlaceOrder', - index=1, + index=0, containing_service=None, input_type=_PLACEORDERREQUEST, output_type=_PLACEORDERRESPONSE, @@ -1763,4 +1761,28 @@ _sym_db.RegisterServiceDescriptor(_CHECKOUTSERVICE) DESCRIPTOR.services_by_name['CheckoutService'] = _CHECKOUTSERVICE + +_ADSSERVICE = _descriptor.ServiceDescriptor( + name='AdsService', + full_name='hipstershop.AdsService', + file=DESCRIPTOR, + index=8, + options=None, + serialized_start=3584, + serialized_end=3659, + methods=[ + _descriptor.MethodDescriptor( + name='GetAds', + full_name='hipstershop.AdsService.GetAds', + index=0, + containing_service=None, + input_type=_ADSREQUEST, + output_type=_ADSRESPONSE, + options=None, + ), +]) +_sym_db.RegisterServiceDescriptor(_ADSSERVICE) + +DESCRIPTOR.services_by_name['AdsService'] = _ADSSERVICE + # @@protoc_insertion_point(module_scope) diff --git a/src/recommendationservice/demo_pb2_grpc.py b/src/recommendationservice/demo_pb2_grpc.py index 7b567b1..e7842d7 100644 --- a/src/recommendationservice/demo_pb2_grpc.py +++ b/src/recommendationservice/demo_pb2_grpc.py @@ -1,19 +1,3 @@ -#!/usr/bin/python -# -# Copyright 2018 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. - # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! import grpc @@ -441,11 +425,6 @@ class CheckoutServiceStub(object): Args: channel: A grpc.Channel. """ - self.CreateOrder = channel.unary_unary( - '/hipstershop.CheckoutService/CreateOrder', - request_serializer=demo__pb2.CreateOrderRequest.SerializeToString, - response_deserializer=demo__pb2.CreateOrderResponse.FromString, - ) self.PlaceOrder = channel.unary_unary( '/hipstershop.CheckoutService/PlaceOrder', request_serializer=demo__pb2.PlaceOrderRequest.SerializeToString, @@ -458,13 +437,6 @@ class CheckoutServiceServicer(object): """ - def CreateOrder(self, request, context): - # missing associated documentation comment in .proto file - pass - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - def PlaceOrder(self, request, context): # missing associated documentation comment in .proto file pass @@ -475,11 +447,6 @@ class CheckoutServiceServicer(object): def add_CheckoutServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'CreateOrder': grpc.unary_unary_rpc_method_handler( - servicer.CreateOrder, - request_deserializer=demo__pb2.CreateOrderRequest.FromString, - response_serializer=demo__pb2.CreateOrderResponse.SerializeToString, - ), 'PlaceOrder': grpc.unary_unary_rpc_method_handler( servicer.PlaceOrder, request_deserializer=demo__pb2.PlaceOrderRequest.FromString, @@ -489,3 +456,47 @@ def add_CheckoutServiceServicer_to_server(servicer, server): generic_handler = grpc.method_handlers_generic_handler( 'hipstershop.CheckoutService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + + +class AdsServiceStub(object): + """------------Ads service------------------ + + """ + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.GetAds = channel.unary_unary( + '/hipstershop.AdsService/GetAds', + request_serializer=demo__pb2.AdsRequest.SerializeToString, + response_deserializer=demo__pb2.AdsResponse.FromString, + ) + + +class AdsServiceServicer(object): + """------------Ads service------------------ + + """ + + def GetAds(self, request, context): + # missing associated documentation comment in .proto file + pass + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_AdsServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'GetAds': grpc.unary_unary_rpc_method_handler( + servicer.GetAds, + request_deserializer=demo__pb2.AdsRequest.FromString, + response_serializer=demo__pb2.AdsResponse.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'hipstershop.AdsService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) diff --git a/src/recommendationservice/recommendation_server.py b/src/recommendationservice/recommendation_server.py index b1ec87e..b897f73 100644 --- a/src/recommendationservice/recommendation_server.py +++ b/src/recommendationservice/recommendation_server.py @@ -15,16 +15,19 @@ # limitations under the License. import grpc -import demo_pb2 -import demo_pb2_grpc from concurrent import futures import time import traceback import random import os - import googleclouddebugger +import demo_pb2 +import demo_pb2_grpc +from grpc_health.v1 import health_pb2 +from grpc_health.v1 import health_pb2_grpc + + # TODO(morganmclean,ahmetb) tracing currently disabled due to memory leak (see TODO below) # from opencensus.trace.ext.grpc import server_interceptor # from opencensus.trace.samplers import always_on @@ -35,7 +38,7 @@ class RecommendationService(demo_pb2_grpc.RecommendationServiceServicer): def ListRecommendations(self, request, context): max_responses = 5 # fetch list of products from product catalog stub - cat_response = stub.ListProducts(demo_pb2.Empty()) + cat_response = product_catalog_stub.ListProducts(demo_pb2.Empty()) product_ids = [x.id for x in cat_response.products] filtered_products = list(set(product_ids)-set(request.product_ids)) num_products = len(filtered_products) @@ -50,6 +53,11 @@ class RecommendationService(demo_pb2_grpc.RecommendationServiceServicer): response.product_ids.extend(prod_list) return response + def Check(self, request, context): + return health_pb2.HealthCheckResponse( + status=health_pb2.HealthCheckResponse.SERVING) + + if __name__ == "__main__": print("initializing recommendationservice") @@ -78,16 +86,16 @@ if __name__ == "__main__": if catalog_addr == "": raise Exception('PRODUCT_CATALOG_SERVICE_ADDR environment variable not set') print("product catalog address: " + catalog_addr) - - # stub for product catalog service channel = grpc.insecure_channel(catalog_addr) - stub = demo_pb2_grpc.ProductCatalogServiceStub(channel) - + product_catalog_stub = demo_pb2_grpc.ProductCatalogServiceStub(channel) + # create gRPC server server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) # ,interceptors=(tracer_interceptor,)) # add class to gRPC server - demo_pb2_grpc.add_RecommendationServiceServicer_to_server(RecommendationService(), server) + service = RecommendationService() + demo_pb2_grpc.add_RecommendationServiceServicer_to_server(service, server) + health_pb2_grpc.add_HealthServicer_to_server(service, server) # start server print("listening on port: " + port) diff --git a/src/recommendationservice/requirements.txt b/src/recommendationservice/requirements.txt index 0d062af..bbf9bcc 100644 --- a/src/recommendationservice/requirements.txt +++ b/src/recommendationservice/requirements.txt @@ -1,29 +1,30 @@ -cachetools==2.1.0 -certifi==2018.4.16 -chardet==3.0.4 -enum34==1.1.6 -futures==3.2.0 -google-api-core==1.2.1 -google-api-python-client==1.7.4 -google-auth==1.5.0 -google-auth-httplib2==0.0.3 -google-cloud-core==0.28.1 -google-cloud-trace==0.19.0 -google-python-cloud-debugger==2.8 -googleapis-common-protos==1.5.3 -grpcio==1.13.0 -grpcio-tools==1.0.0 -httplib2==0.11.3 -idna==2.7 -opencensus==0.1.5 -protobuf==3.5.2.post1 -pyasn1==0.4.3 -pyasn1-modules==0.2.2 -pytz==2018.5 -PyYAML==3.13 -requests==2.19.1 -rsa==3.4.2 -six==1.11.0 -uritemplate==3.0.0 -urllib3==1.23 -virtualenv==16.0.0 +cachetools==2.1.0 +certifi==2018.4.16 +chardet==3.0.4 +enum34==1.1.6 +futures==3.2.0 +google-api-core==1.2.1 +google-api-python-client==1.7.4 +google-auth==1.5.0 +google-auth-httplib2==0.0.3 +google-cloud-core==0.28.1 +google-cloud-trace==0.19.0 +google-python-cloud-debugger==2.8 +googleapis-common-protos==1.5.3 +grpcio==1.13.0 +grpcio-health-checking==1.14.1 +grpcio-tools==1.0.0 +httplib2==0.11.3 +idna==2.7 +opencensus==0.1.5 +protobuf==3.5.2.post1 +pyasn1==0.4.3 +pyasn1-modules==0.2.2 +pytz==2018.5 +PyYAML==3.13 +requests==2.19.1 +rsa==3.4.2 +six==1.11.0 +uritemplate==3.0.0 +urllib3==1.23 +virtualenv==16.0.0 diff --git a/src/shippingservice/Dockerfile b/src/shippingservice/Dockerfile index a9b3e17..ef44642 100644 --- a/src/shippingservice/Dockerfile +++ b/src/shippingservice/Dockerfile @@ -14,6 +14,9 @@ RUN go install . FROM alpine as release RUN apk add --no-cache ca-certificates +RUN GRPC_HEALTH_PROBE_VERSION=v0.1.0-alpha.1 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe COPY --from=builder /go/bin/shippingservice /shippingservice ENV APP_PORT=50051 EXPOSE 50051 diff --git a/src/shippingservice/Gopkg.lock b/src/shippingservice/Gopkg.lock index d925677..998e4eb 100644 --- a/src/shippingservice/Gopkg.lock +++ b/src/shippingservice/Gopkg.lock @@ -10,8 +10,8 @@ "profiler", "trace/apiv2" ] - revision = "aad3f485ee528456e0768f20397b4d9dd941e755" - version = "v0.25.0" + revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad" + version = "v0.27.0" [[projects]] name = "contrib.go.opencensus.io/exporter/stackdriver" @@ -35,14 +35,14 @@ "ptypes/timestamp", "ptypes/wrappers" ] - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" + revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5" + version = "v1.2.0" [[projects]] branch = "master" name = "github.com/google/pprof" packages = ["profile"] - revision = "ef437552946f69f7e3bdf1fd81c385c29530944d" + revision = "e027b505a088ac3c68c339a1d7ce7724bf34538b" [[projects]] name = "github.com/googleapis/gax-go" @@ -65,10 +65,11 @@ "tag", "trace", "trace/internal", - "trace/propagation" + "trace/propagation", + "trace/tracestate" ] - revision = "e262766cd0d230a1bb7c37281e345e465f19b41b" - version = "v0.14.0" + revision = "b11f239c032624b045c4c2bfd3d1287b4012ce89" + version = "v0.16.0" [[projects]] branch = "master" @@ -83,7 +84,7 @@ "internal/timeseries", "trace" ] - revision = "f4c29de78a2a91c00474a2e689954305c350adf9" + revision = "26e67e76b6c3f6ce91f7c52def5af501b4e0f3a2" [[projects]] branch = "master" @@ -95,7 +96,7 @@ "jws", "jwt" ] - revision = "3d292e4d0cdc3a0113e6d207bb137145ef1de42f" + revision = "d2e6202438beef2727060aa7cabdd924d92ebfd9" [[projects]] branch = "master" @@ -107,7 +108,7 @@ branch = "master" name = "golang.org/x/sys" packages = ["unix"] - revision = "0ffbfd41fbef8ffcf9b62b0b0aa3a5873ed7a4fe" + revision = "1561086e645b2809fb9f8a1e2a38160bf8d53bf4" [[projects]] name = "golang.org/x/text" @@ -143,7 +144,7 @@ "transport/grpc", "transport/http" ] - revision = "f6d94689cbd71030af1108ddac733886fcae1d75" + revision = "19ff8768a5c0b8e46ea281065664787eefc24121" [[projects]] name = "google.golang.org/appengine" @@ -161,8 +162,8 @@ "socket", "urlfetch" ] - revision = "b1f26356af11148e710935ed1ac8a7f5702c7612" - version = "v1.1.0" + revision = "ae0ab99deb4dc413a2b4bd6c8bdd0eb67f1e4d06" + version = "v1.2.0" [[projects]] branch = "master" @@ -180,7 +181,7 @@ "googleapis/rpc/status", "protobuf/field_mask" ] - revision = "daca94659cb50e9f37c1b834680f2e46358f10b0" + revision = "c3f76f3b92d1ffa4c58a9ff842a58b8877655e0f" [[projects]] name = "google.golang.org/grpc" @@ -196,6 +197,7 @@ "encoding", "encoding/proto", "grpclog", + "health/grpc_health_v1", "internal", "internal/backoff", "internal/channelz", @@ -221,6 +223,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "3f4776192cec97ef87c4d20a52c9752d106a298befc139e5bca176bf915ed63b" + inputs-digest = "5dac84cd5efcc89491aa3cda06a774c1b370c8e0cbdb99668cbfd8ba27b3e32c" solver-name = "gps-cdcl" solver-version = 1 diff --git a/src/shippingservice/Gopkg.toml b/src/shippingservice/Gopkg.toml index 3f810ed..eafb618 100644 --- a/src/shippingservice/Gopkg.toml +++ b/src/shippingservice/Gopkg.toml @@ -27,7 +27,7 @@ [[constraint]] name = "cloud.google.com/go" - version = "0.25.0" + version = "0.27.0" [[constraint]] name = "contrib.go.opencensus.io/exporter/stackdriver" @@ -35,11 +35,11 @@ [[constraint]] name = "github.com/golang/protobuf" - version = "1.1.0" + version = "1.2.0" [[constraint]] name = "go.opencensus.io" - version = "0.14.0" + version = "0.16.0" [[constraint]] branch = "master" @@ -47,7 +47,7 @@ [[constraint]] name = "google.golang.org/grpc" - version = "1.14.0" + version = "=1.14.0" [prune] go-tests = true diff --git a/src/shippingservice/main.go b/src/shippingservice/main.go index 18a7a7b..f82787a 100644 --- a/src/shippingservice/main.go +++ b/src/shippingservice/main.go @@ -24,12 +24,14 @@ import ( "cloud.google.com/go/profiler" "contrib.go.opencensus.io/exporter/stackdriver" "go.opencensus.io/plugin/ocgrpc" + "go.opencensus.io/stats/view" "go.opencensus.io/trace" "golang.org/x/net/context" "google.golang.org/grpc" "google.golang.org/grpc/reflection" pb "github.com/GoogleCloudPlatform/microservices-demo/src/shippingservice/genproto" + healthpb "google.golang.org/grpc/health/grpc_health_v1" ) const ( @@ -50,13 +52,15 @@ func main() { if err != nil { log.Fatalf("failed to listen: %v", err) } - s := grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{})) - pb.RegisterShippingServiceServer(s, &server{}) + srv := grpc.NewServer(grpc.StatsHandler(&ocgrpc.ServerHandler{})) + svc := &server{} + pb.RegisterShippingServiceServer(srv, svc) + healthpb.RegisterHealthServer(srv, svc) log.Printf("Shipping Service listening on port %s", port) // Register reflection service on gRPC server. - reflection.Register(s) - if err := s.Serve(lis); err != nil { + reflection.Register(srv) + if err := srv.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } @@ -64,6 +68,11 @@ func main() { // server controls RPC service responses. type server struct{} +// Check is for health checking. +func (s *server) Check(ctx context.Context, req *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { + return &healthpb.HealthCheckResponse{Status: healthpb.HealthCheckResponse_SERVING}, nil +} + // GetQuote produces a shipping quote (cost) in USD. func (s *server) GetQuote(ctx context.Context, in *pb.GetQuoteRequest) (*pb.GetQuoteResponse, error) { log.Printf("[GetQuote] received request") @@ -103,6 +112,15 @@ func (s *server) ShipOrder(ctx context.Context, in *pb.ShipOrderRequest) (*pb.Sh }, nil } +func initStats(exporter *stackdriver.Exporter) { + view.RegisterExporter(exporter) + if err := view.Register(ocgrpc.DefaultServerViews...); err != nil { + log.Printf("Error registering default server views") + } else { + log.Printf("Registered default server views") + } +} + func initTracing() { // TODO(ahmetb) this method is duplicated in other microservices using Go // since they are not sharing packages. @@ -114,6 +132,9 @@ func initTracing() { trace.RegisterExporter(exporter) trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) log.Print("registered stackdriver tracing") + + // Register the views to collect server stats. + initStats(exporter) return } d := time.Second * 10 * time.Duration(i)