Skip to content

Commit

Permalink
[WIP] grpc support
Browse files Browse the repository at this point in the history
  • Loading branch information
diegorubin committed Jun 23, 2020
1 parent adce193 commit e4ec417
Show file tree
Hide file tree
Showing 22 changed files with 312 additions and 53 deletions.
1 change: 0 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# frozen_string_literal: true

source 'https://rubygems.org'
gem 'sinatra-cross_origin'
gemspec
16 changes: 12 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ PATH
specs:
tshield (0.11.14.0)
byebug (~> 11.0, >= 11.0.1)
grpc (~> 1.28, >= 1.28.0)
grpc-tools (~> 1.28, >= 1.28.0)
httparty (~> 0.14, >= 0.14.0)
json (~> 2.0, >= 2.0)
puma (~> 4.3, >= 4.3.3)
Expand Down Expand Up @@ -59,6 +61,13 @@ GEM
ffi (1.11.1)
formatador (0.2.5)
gherkin (5.1.0)
google-protobuf (3.12.2)
googleapis-common-protos-types (1.0.5)
google-protobuf (~> 3.11)
grpc (1.28.0)
google-protobuf (~> 3.11)
googleapis-common-protos-types (~> 1.0)
grpc-tools (1.28.0)
guard (2.15.0)
formatador (>= 0.2.4)
listen (>= 2.7, < 4.0)
Expand All @@ -74,7 +83,7 @@ GEM
guard-compat (~> 1.1)
rspec (>= 2.99.0, < 4.0)
hashdiff (0.3.7)
httparty (0.18.0)
httparty (0.18.1)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
ice_nine (0.11.2)
Expand All @@ -89,7 +98,7 @@ GEM
method_source (0.9.2)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.1009)
mime-types-data (3.2020.0512)
multi_json (1.13.1)
multi_test (0.1.2)
multi_xml (0.6.0)
Expand All @@ -106,7 +115,7 @@ GEM
method_source (~> 0.9.0)
psych (3.1.0)
public_suffix (3.0.3)
puma (4.3.3)
puma (4.3.5)
nio4r (~> 2.0)
rack (1.6.12)
rack-protection (1.5.5)
Expand Down Expand Up @@ -192,7 +201,6 @@ DEPENDENCIES
rubocop (~> 0.73.0, >= 0.73.0)
rubocop-rails (~> 2.2.0, >= 2.2.1)
simplecov (~> 0.12, >= 0.12.0)
sinatra-cross_origin
tshield!
webmock (~> 2.1, >= 2.1.0)

Expand Down
25 changes: 20 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ TShield is an open source proxy for mocks API responses.
* Session manager to separate multiple scenarios (success, error, sucess variation, ...)
* Lightweight
* MIT license

## Table of Contents

* [Basic Usage](#basic-usage)
Expand All @@ -26,7 +26,7 @@ TShield is an open source proxy for mocks API responses.
* [Features](#features)
* [Examples](#examples)
* [Contributing](#contributing)

## Basic Usage
### Install

Expand All @@ -37,7 +37,7 @@ TShield is an open source proxy for mocks API responses.
To run server execute this command

tshield

Default port is `4567`

#### Command Line Options
Expand Down Expand Up @@ -102,7 +102,7 @@ To register stub into a session create an object with following attributes:
* **session**: name of session.
* **stubs**: an array with objects described above.

### Example of matching configuration
### Example of HTTP matching configuration

```json
[
Expand Down Expand Up @@ -163,7 +163,7 @@ To register stub into a session create an object with following attributes:
]
```

## Config options for VCR
## Config options for HTTP VCR
```yaml
request:
timeout: 8
Expand Down Expand Up @@ -249,6 +249,21 @@ _DELETE_ to http://localhost:4567/sessions
curl -X DELETE \
http://localhost:4567/sessions
```
## [Experimental] Config options for gRPC

### TODO

- Sessions
- Matching

### Configuration

First, generate ruby files from proto files. Use `grpc_tools_ruby_protoc`
present in the gem `grpc-tools`. Example:

`grpc_tools_ruby_protoc -I proto --ruby_out=proto --grpc_out=proto proto/<INPUT>.proto`

### Using in VCR mode

## Custom controllers

Expand Down
9 changes: 1 addition & 8 deletions bin/tshield
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ TShield::Options.init

require 'tshield'
tshield = Thread.new { TShield::Server.run! }

configuration = TShield::Configuration.load_configuration
(configuration.tcp_servers || []).each do |tcp_server|
puts "initializing #{tcp_server['name']}"
require "./servers/#{tcp_server['file']}"
klass = Object.const_get(tcp_server['name'])
Thread.new { klass.new.listen(tcp_server['port']) }
end
TShield::Grpc.run!

tshield.join
48 changes: 48 additions & 0 deletions component_tests/grpc_server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env ruby
# frozen_string_literal: false

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

# Sample gRPC server that implements the Greeter::Helloworld service.
#
# Usage: $ path/to/greeter_server.rb

lib_dir = File.join(__dir__, '..', 'proto')
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

require 'grpc'
require 'helloworld_services_pb'

# GreeterServer is simple server that implements the Helloworld Greeter server.
class GreeterServer < Helloworld::Greeter::Service
# say_hello implements the SayHello rpc method.
def say_hello(hello_req, _unused_call)
Helloworld::HelloReply.new(message: "Hello with message #{hello_req.name}")
end
end

# main starts an RpcServer that receives requests to GreeterServer at the sample
# server port.
def main
s = GRPC::RpcServer.new
s.add_http2_port('0.0.0.0:50051', :this_port_is_insecure)
s.handle(GreeterServer)
# Runs the server with SIGHUP, SIGINT and SIGQUIT signal handlers to
# gracefully shutdown.
# User could also choose to run server via call to run_till_terminated
s.run_till_terminated_or_interrupted([1, 'int', 'SIGQUIT'])
end

main
18 changes: 18 additions & 0 deletions config/tshield.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
grpc:
port: 5678
proto_dir: 'proto'
services:
'helloworld_services_pb':
module: 'Helloworld::Greeter'
hostname: '0.0.0.0:50051'
request:
timeout: 10
domains:
'http://localhost:9090':
name: 'components'
skip_query_params:
- b
paths:
- /users
- /resources
6 changes: 6 additions & 0 deletions features/grpc-vcr.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Feature: Save response on call real grpc and return on second call

Scenario: Save response body
Given a valid gRPC method "helloworld.Greeter"
When this method called throught tshield
Then response should be saved in "grpc/helloworld/Greeter/hashrequest/response"
2 changes: 1 addition & 1 deletion lib/tshield.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

require 'tshield/extensions/string_extensions'
require 'tshield/options'
require 'tshield/simple_tcp_server'
require 'tshield/server'
require 'tshield/grpc'

# TShield: API mocks for development and testing
module TShield
Expand Down
1 change: 1 addition & 0 deletions lib/tshield/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Configuration
#
attr_reader :request
attr_reader :domains
attr_reader :grpc
attr_reader :tcp_servers
attr_reader :session_path

Expand Down
8 changes: 8 additions & 0 deletions lib/tshield/extensions/string_extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ module StringExtensions
def to_rack_name
"HTTP_#{upcase.tr('-', '_')}"
end

def underscore
gsub(/::/, '/')
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
.tr('-', '_')
.downcase
end
end

String.include StringExtensions
57 changes: 57 additions & 0 deletions lib/tshield/grpc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: false

require 'grpc'
require 'byebug'

require 'tshield/configuration'

module TShield
class Grpc
def self.run!
@configuration = TShield::Configuration.singleton.grpc

lib_dir = File.join(Dir.pwd, @configuration['proto_dir'])
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)

TShield.logger.info("loading proto files from #{lib_dir}")

bind = "0.0.0.0:#{@configuration['port']}"

server = GRPC::RpcServer.new
server.add_http2_port(bind, :this_port_is_insecure)

services = load_services(@configuration['services'])
services.each do |class_service|
server.handle(class_service)
end

server.run unless services.empty?
end

def self.load_services(services)
implementations = []
number_of_implementations = 0
services.each do |file, options|
require file

base = Object.const_get("#{options['module']}::Service")
number_of_implementations += 1

implementation = Class.new(base) do
base.rpc_descs.each do |method_name, _description|
method_name = method_name.to_s.underscore.to_sym
define_method(method_name) do |request, _unused_call|
client_class = Object.const_get("#{options['module']}::Stub")
client_instance = client_class.new(options['hostname'], :this_channel_is_insecure)
client_instance.send(method_name, request)
end
end
end
Object.const_set "GrpcService#{number_of_implementations}", implementation

implementations << implementation
end
implementations
end
end
end
1 change: 0 additions & 1 deletion lib/tshield/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ def self.load_controllers

def self.run!
register_resources
require 'byebug'
super
end
end
Expand Down
28 changes: 0 additions & 28 deletions lib/tshield/simple_tcp_server.rb

This file was deleted.

34 changes: 34 additions & 0 deletions proto/helloworld.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2015 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.

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello(HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest { string name = 1; }

// The response message containing the greetings
message HelloReply { string message = 1; }
Loading

0 comments on commit e4ec417

Please sign in to comment.