Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save beccasaurus/a4e372b5bc2dc075bf38ccb1b539858d to your computer and use it in GitHub Desktop.
Save beccasaurus/a4e372b5bc2dc075bf38ccb1b539858d to your computer and use it in GitHub Desktop.
gRPC Dogs API – with GAPIC generated libraries 🐕

gRPC Dogs API :: with GAPIC generated libraries 🐕

🔬 potential source material for a simple gRPC/GAPIC codelab

This sample contains:

File Description
dogs.proto Defines dogs API, e.g. rpc methods to create/list/delete dogs
dogs_server.js Node.js implementation of the defined dogs API, e.g. implements the create/list/delete dogs methods
dogs_gapic.yaml Configuration for client library generation. Configures name of generated package as well as generated code samples
dogs_service.yaml Configures endpoint for the service, allowing us to generate a library to hit the service running on localhost
generate-and-run.sh Shell script which generates Python client library and generated code samples

NOTE GAPIC now supports code sample generation in Node.js, Python, PHP, Go, and Java.

Getting Started

  1. Clone the sample repository

    git clone https://gist.github.com/a4e372b5bc2dc075bf38ccb1b539858d.git grpc-dogs
    cd grpc-dogs
    
  2. Install grpc Node.js library

    npm install
    
  3. Run the local Node.js gRPC server

    node dogs_server.js
    
  4. Generate Python client library with generated code samples and run each sample

    ./generate-and-run.sh
    

If all goes well, the Python client library for the dogs API should be generated, installed locally, and samples run to verify the behavior of the API and library/

Successfully installed codelabs-dogs-0.1.0
[–> –> –> Running Python Samples]
[–> –> –> Sample: List Dogs]
[–> –> –> Sample: Add Dog]
Dog added
[–> –> –> Sample: List Dogs]
Dog name: Rover
Dog name: Spot
Dog name: Rex
[–> –> –> Sample: Remove Dogs]
Dog removed
[–> –> –> Sample: List Dogs]
Dog name: Rover
Dog name: Spot

[Check the console where you ran dogs_server.js to verify this client library hit it]

>> Try it out for yourself! <<

The codelabs.dogs_v1 pip package is now installed locally

Client Library:
- artman-genfiles/python/dogs-v1/

Samples:
- artman-genfiles/python/dogs-v1/samples/codelabs/dogs_v1/gapic/remove_dog/remove_dog_request_remove_dog.py]
- artman-genfiles/python/dogs-v1/samples/codelabs/dogs_v1/gapic/list_dogs/list_dogs_request_list_dogs.py]
- artman-genfiles/python/dogs-v1/samples/codelabs/dogs_v1/gapic/add_dog/add_dog_request_add_dog.py]
common:
api_name: dogs
api_version: v1
organization_name: codelabs
service_yaml: dogs_service.yaml
gapic_yaml: dogs_gapic.yaml
src_proto_paths:
- dogs.proto
proto_deps:
- name: google-common-protos
artifacts:
- name: python_gapic
type: GAPIC
language: PYTHON
- name: php_gapic
type: GAPIC
language: PHP
syntax = "proto3";
package codelabs.dogs.v1;
service DogsService {
rpc ListDogs(Empty) returns (DogList) {}
rpc AddDog(Dog) returns (Empty) {}
rpc RemoveDog(Dog) returns (Empty) {}
}
message Empty {}
message Dog { string name = 1; }
message DogList { repeated Dog dogs = 1; }
type: com.google.api.codegen.ConfigProto
config_schema_version: 1.0.0
language_settings:
python:
package_name: codelabs.dogs_v1.gapic
php:
package_name: Codelabs\Dogs\V1
interfaces:
- name: codelabs.dogs.v1.DogsService
methods:
- name: ListDogs
samples:
standalone:
- calling_forms: ".*"
value_sets: list_dogs
region_tag: list_dogs
sample_value_sets:
- id: list_dogs
description: "List Dogs"
on_success:
- loop:
variable: dog
collection: $resp.dogs
body:
- print:
- "Dog name: %s"
- dog.name
- name: AddDog
samples:
standalone:
- calling_forms: ".*"
value_sets: add_dog
region_tag: add_dog
sample_value_sets:
- id: add_dog
description: "Add Dog"
parameters:
defaults:
- name="Your dog’s name, e.g. Rover"
attributes:
- parameter: name
sample_argument_name: name
on_success:
- print:
- "Dog added"
- name: RemoveDog
samples:
standalone:
- calling_forms: ".*"
value_sets: remove_dog
region_tag: remove_dog
sample_value_sets:
- id: remove_dog
description: "Remove Dog"
parameters:
defaults:
- name="Your dog’s name, e.g. Rover"
attributes:
- parameter: name
sample_argument_name: name
on_success:
- print:
- "Dog removed"
// To run: npm install grpc && node dogs_server.js
const grpc = require('grpc')
const proto = grpc.load('dogs.proto')
const service = proto.codelabs.dogs.v1.DogsService.service
const server = new grpc.Server()
const dogs = [{ name: "Rover" }, { name: "Spot" }]
server.addService(service, {
listDogs: function(call, callback) {
console.log(`List dogs (${dogs.length} dogs)`)
callback(null, dogs)
},
addDog: function(call, callback) {
const dog = call.request
dogs.push(dog)
console.log(`Added dog: ${dog.name}`)
callback(null, {})
},
removeDog: function(call, callback) {
const dog = call.request
const dogIndex = dogs.findIndex(d => d.name == dog.name)
if (dogIndex > -1) {
dogs.splice(dogIndex, 1)
console.log(`Deleted dog: ${dog.name}`)
callback(null, {})
} else {
callback({ code: grpc.status.NOT_FOUND, details: 'Not found' })
}
}
})
console.log('gRPC listening on localhost:2222')
server.bind('localhost:2222', grpc.ServerCredentials.createInsecure())
server.start()
type: google.api.Service
config_version: 3
name: localhost:2222
apis:
- name: codelabs.dogs.v1.DogsService
#! /bin/bash
language=python
artman_yaml=artman_dogs.yaml
if [ -z "$GOOGLEAPIS" ]; then
echo "export GOOGLEAPIS – must be set to path to local googleapis/googleapis clone"
echo ">> note: this script makes modifications (additions) to the googleapis directory"
exit 1
else
echo "googleapis directory: $GOOGLEAPIS"
fi
log() {
echo -e "[\033[0;92m–> –> –> $1\033[0m]"
}
log "Deleting previous artman-genfiles artifacts"
rm_genfiles_output="`rm -rfv ./artman-genfiles`"
log "Deleted `echo -e "$rm_genfiles_output" | wc -l` files/dirs"
root="`pwd`"
# Required dependencies (or it blows up)
# tl;dr make the generator think it has all of the Googley files it needs
mkdir -pv ./google/{api,rpc,type,longrunning,iam/v1}
for filepath in google/api/{http,annotations}.proto; do
cp -v "$GOOGLEAPIS/$filepath" "./$filepath"
done
log "Generating Python Library"
output=`docker run --rm -e RUNNING_IN_ARTMAN_DOCKER=True -v "$(pwd):/googleapis" \
-w /googleapis googleapis/artman /bin/bash -c \
"artman --local --config $artman_yaml generate ${language}_gapic" 2>&1`
docker_exit_code=$?
echo -e "$output"
if [ $docker_exit_code -ne 0 ]; then
if [[ "$output" =~ "Path gapic not found in" ]]; then
log "Generation may have failed ($docker_exit_code)"
log "'dogs' related directories created:"
find $root/artman-genfiles -type d -name "*dog*"
else
log "Generating $language failed ($docker_exit_code)"
exit $docker_exit_code
fi
fi
python_library_root="artman-genfiles/python/dogs-v1"
python_samples_path="samples/codelabs/dogs_v1/gapic/list_dogs"
cd "$root/$python_library_root"
log "FIX standardize import namespaces"
grep -rl "from codelabs.dogs\.v1" codelabs | xargs -n1 sed -i.bak -e "s/from codelabs.dogs.v1/from codelabs.dogs_v1/"
find codelabs/ -name "*.py.bak" -exec rm {} \;
log "Copy pb2 proto files into package"
mv ../grpc-codelabs-dogs-v1/codelabs/dogs_v1/proto codelabs/dogs_v1/
touch codelabs/dogs_v1/proto/__init__.py
log "FIX pb2 proto imports"
grep -rl "from codelabs.dogs_v1 import dogs_pb2" . | xargs -n1 sed -i.bak -e "s/dogs_v1 import dogs_pb2/dogs_v1.proto import dogs_pb2/"
find codelabs/ -name "*.py.bak" -exec rm {} \;
log "FIX samples to use localhost server (without auth)"
for sample in `find samples/codelabs/dogs_v1/gapic -name "*.py"`; do
sed -i.bak -e $'s/import sys/import sys\\\nfrom grpc import insecure_channel/' "$sample"
find samples/ -name "*.py.bak" -exec rm {} \;
sed -i.bak -e 's/\.DogsServiceClient()/.DogsServiceClient(channel=insecure_channel("localhost:2222"))/' "$sample"
find samples/ -name "*.py.bak" -exec rm {} \;
done
log "FIX setup.py"
for replacement in "s/^.*namespace.*$//" \
"s/if 'google.cloud'.*$//" \
"s/startswith('google')/startswith('codelabs')/"
do
sed -i.bak -e "$replacement" setup.py
done
echo
log "Install Generated Library"
virtualenv --python python3 env
source env/bin/activate
pip install .
log "Running Python Samples"
log "Sample: List Dogs"
output="$( python samples/codelabs/dogs_v1/gapic/list_dogs/list_dogs_request_list_dogs.py 2>&1 )"
if [[ "$output" =~ "ServiceUnavailable" ]]; then
echo "Client library cannot hit your local gRPC API (ServiceUnavailable)"
echo "Did you run \`node dogs_server.js\`?"
echo "Run \`npm install\` first to install dependencies"
exit 1
fi
log "Sample: Add Dog"
python samples/codelabs/dogs_v1/gapic/add_dog/add_dog_request_add_dog.py --name "Rex"
log "Sample: List Dogs"
python samples/codelabs/dogs_v1/gapic/list_dogs/list_dogs_request_list_dogs.py
log "Sample: Remove Dogs"
python samples/codelabs/dogs_v1/gapic/remove_dog/remove_dog_request_remove_dog.py --name "Rex"
log "Sample: List Dogs"
python samples/codelabs/dogs_v1/gapic/list_dogs/list_dogs_request_list_dogs.py
echo -e "
[\033[0;37mCheck the console where you ran dogs_server.js to verify this client library hit it\033[0m]
>> \033[0;92mTry it out for yourself!\033[0m <<
The \033[0;34mcodelabs.dogs_v1\033[0m pip package is now installed locally
Client Library:
- artman-genfiles/python/dogs-v1/
Samples:
- artman-genfiles/python/dogs-v1/samples/codelabs/dogs_v1/gapic/remove_dog/\033[0;34mremove_dog_request_remove_dog.py\033[0m]
- artman-genfiles/python/dogs-v1/samples/codelabs/dogs_v1/gapic/list_dogs/\033[0;34mlist_dogs_request_list_dogs.py\033[0m]
- artman-genfiles/python/dogs-v1/samples/codelabs/dogs_v1/gapic/add_dog/\033[0;34madd_dog_request_add_dog.py\033[0m]
Example sample:
\033[0;34m
import sys
from codelabs import dogs_v1
def list_dogs():
client = dogs_v1.DogsServiceClient(channel=insecure_channel(localhost:2222))
response = client.list_dogs()
for dog in response.dogs:
print('Dog name: {}'.format(dog.name))
def main():
list_dogs()
if __name__ == '__main__':
main()
\033[0m"
{
"dependencies": {
"grpc": "*"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment