elixir-grpc / grpc

An Elixir implementation of gRPC
https://hex.pm/packages/grpc
Apache License 2.0
1.38k stars 212 forks source link

Support gRPC http/json transcoding #273

Closed drowzy closed 5 months ago

drowzy commented 1 year ago

Support for gRPC transcoding.

This feature is opt-in using the http_transcode option in the server definition:

defmodule Helloworld.Greeter.Server do
  use GRPC.Server,
    service: Helloworld.Greeter.Service,
    http_transcode: true

Request/response mapping can be controlled by adding grpc-gateway annotations to your proto definition:

import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }

  rpc SayHelloFrom (HelloRequestFrom) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/greeter"
      body: "*"
    };
  }
}

This PR does not require any changes to user code.

Changes

Routing

The path template syntax that can be described using Google.Api.HttpRule can not be expressed using the default :cowboy_router middleware. GRPC.Server.Adapters.Cowboy.Router implements the :cowboy_middleware behaviour and lifts most of its functionality from :cowboy_router with the difference being that it's using GRPC.Service.Router which can build and match Google.Api.HttpRule path templates.

The new routing should be able to match all rules described in the path template syntax:

pattern: /v1/{name=*}
match: /v1/anything
bindings: name="anything"` 

pattern: /v1/{name=messages} 
match: /v1/messages 
bindings: name="messages"

pattern: /v1/{name=messages/*} 
match: /v1/messages/anything 
bindings: name="messages/anything" 

pattern: /v1/{name=messages/**} 
match: /v1/messages/catch/all 
bindings: name="messages/catch/all" 

pattern: /v1/{name=messages/**}/suffix 
match: /v1/messages/catch/all/suffix 
bindings: name="messages/catch/all" 

pattern: /v1/{message.name=messages}/{message.id}
match: /v1/messages/1
bindings: message.name="messages" message.id=1

Of course regular gRPC request paths are also matched i.e POST helloworld.Greeter/SayHello.

sleipnir commented 1 year ago

Hi @drowzy any news here ? @polvalente Can this already be merged?

sleipnir commented 1 year ago

ping @drowzy @polvalente