vanguardgrpc

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2023 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package vanguardgrpc provides convenience functions to make it easy to wrap your grpc.Server with a vanguard.Transcoder, to upgrade it to supporting Connect, gRPC-Web, and REST+JSON protocols.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewCodec

func NewCodec(codec vanguard.Codec) encoding.Codec

NewCodec returns a gRPC encoding.Codec that uses the given Vanguard Codec as its backing implementation. In particular, this can be combined with vanguard.JSONCodec to easily create a gRPC Codec to support the "json" message format.

func NewTranscoder

func NewTranscoder(server *grpc.Server, opts ...vanguard.TranscoderOption) (*vanguard.Transcoder, error)

NewTranscoder returns a Vanguard handler that wraps the given gRPC server. All services registered with the server will be supported by the returned handler. The Vanguard handler will be configured to transcode incoming requests to the gRPC protocol.

The returned handler will allow data in the "proto" codec through, but must transcode other codecs to "proto". If a gRPC Codec has been registered with the name "json" (via encoding.RegisterCodec) then the Vanguard handler will pass JSON requests through unchanged as well.

For maximum efficiency, especially if REST and/or Connect clients are expected, a JSON codec should be registered before calling this function. If the server program does not already register such a codec, it may do so via the following:

encoding.RegisterCodec(vanguardgrpc.NewCodec(&vanguard.JSONCodec{
	// These fields can be used to customize the serialization and
	// de-serialization behavior. The options presented below are
	// highly recommended.
	MarshalOptions: protojson.MarshalOptions{
		EmitUnpopulated: true,
	},
	UnmarshalOptions: protojson.UnmarshalOptions{
		DiscardUnknown: true,
	},
})
Example (ConnectToGRPC)
package main

import (
	"context"
	"log"
	"net/http/httptest"
	"os"
	"strings"
	"time"

	"connectrpc.com/connect"
	"connectrpc.com/vanguard"
	testv1 "connectrpc.com/vanguard/internal/gen/vanguard/test/v1"
	"connectrpc.com/vanguard/internal/gen/vanguard/test/v1/testv1connect"
	"connectrpc.com/vanguard/vanguardgrpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/encoding"
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func main() {
	log := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)

	// Configure gRPC servers to support JSON.
	encoding.RegisterCodec(vanguardgrpc.NewCodec(&vanguard.JSONCodec{
		MarshalOptions:   protojson.MarshalOptions{EmitUnpopulated: true},
		UnmarshalOptions: protojson.UnmarshalOptions{DiscardUnknown: true},
	}))

	// Now create a gRPC server that provides the test LibraryService.
	svc := &libraryRPC{}
	grpcServer := grpc.NewServer()
	testv1.RegisterLibraryServiceServer(grpcServer, svc)

	// Create a vanguard handler for all services registered in grpcServer
	handler, err := vanguardgrpc.NewTranscoder(grpcServer)
	if err != nil {
		log.Println("error:", err)
		return
	}

	// Create the server.
	// (This is a httptest.Server, but it could be any http.Server)
	server := httptest.NewUnstartedServer(handler)
	server.EnableHTTP2 = true // HTTP/2 required for gRPC
	server.StartTLS()
	defer server.Close()

	// Create a connect client and call the service.
	client := testv1connect.NewLibraryServiceClient(server.Client(), server.URL, connect.WithProtoJSON())

	// Call the service using Connect, translated by the middleware to gRPC.
	rsp, err := client.GetBook(
		context.Background(),
		connect.NewRequest(&testv1.GetBookRequest{
			Name: "shelves/top/books/1",
		}),
	)
	if err != nil {
		log.Println("error:", err)
		return
	}
	log.Println(rsp.Msg.Title)
}

type libraryRPC struct {
	testv1.UnimplementedLibraryServiceServer
}

func (s *libraryRPC) GetBook(_ context.Context, req *testv1.GetBookRequest) (*testv1.Book, error) {
	return &testv1.Book{
		Name:        req.Name,
		Parent:      strings.Join(strings.Split(req.Name, "/")[:2], "/"),
		CreateTime:  timestamppb.New(time.Date(1968, 1, 1, 0, 0, 0, 0, time.UTC)),
		Title:       "Do Androids Dream of Electric Sheep?",
		Author:      "Philip K. Dick",
		Description: "Have you seen Blade Runner?",
		Labels: map[string]string{
			"genre": "science fiction",
		},
	}, nil
}

func (s *libraryRPC) CreateBook(_ context.Context, req *testv1.CreateBookRequest) (*testv1.Book, error) {
	book := req.Book
	return &testv1.Book{
		Name:        strings.Join([]string{req.Parent, "books", req.BookId}, "/"),
		Parent:      req.Parent,
		CreateTime:  timestamppb.New(time.Date(1968, 1, 1, 0, 0, 0, 0, time.UTC)),
		Title:       book.GetTitle(),
		Author:      book.GetAuthor(),
		Description: book.GetDescription(),
		Labels:      book.GetLabels(),
	}, nil
}
Output:

Do Androids Dream of Electric Sheep?

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL