From f3683e5694b78b3e999a56bab60e1fed4193c092 Mon Sep 17 00:00:00 2001 From: Akshay Shah Date: Wed, 25 Oct 2023 10:40:06 -0700 Subject: [PATCH] Add more comments to examples (#100) --- README.md | 3 +- transcoder.go | 2 + vanguard_examples_test.go | 115 +++++++++++++++++++++++--------------- 3 files changed, 74 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index f9a4bac..2805aae 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # ⚔️ Vanguard -[![License](https://img.shields.io/github/license/connectrpc/vanguard-go?color=blue)][badges_license] -[![Slack](https://img.shields.io/badge/slack-buf-%23e01563)][badges_slack] [![Build](https://github.com/connectrpc/vanguard-go/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/connectrpc/vanguard-go/actions/workflows/ci.yaml) [![Report Card](https://goreportcard.com/badge/connectrpc.com/vanguard)](https://goreportcard.com/report/github.com/connectrpc/vanguard-go) [![GoDoc](https://pkg.go.dev/badge/connectrpc.com/vanguard.svg)](https://pkg.go.dev/github.com/connectrpc/vanguard-go) +[![Slack](https://img.shields.io/badge/slack-buf-%23e01563)][badges_slack] Vanguard is a powerful library for Go `net/http` servers that enables seamless transcoding between REST and RPC protocols. Whether you need to bridge the gap diff --git a/transcoder.go b/transcoder.go index 2dc20ea..fb7a74e 100644 --- a/transcoder.go +++ b/transcoder.go @@ -38,6 +38,8 @@ import ( // service handlers support. It can do simple routing based on RPC method name, for simple // protocols like Connect, gRPC, and gRPC-Web; but it can also route based on REST-ful URI // paths configured with HTTP transcoding annotations. +// +// See the package-level examples for sample usage. type Transcoder struct { bufferPool bufferPool codecs codecMap diff --git a/vanguard_examples_test.go b/vanguard_examples_test.go index 3e1c37e..3db4f5b 100644 --- a/vanguard_examples_test.go +++ b/vanguard_examples_test.go @@ -38,26 +38,35 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -func ExampleNewTranscoder_restToConnect() { - log := log.New(os.Stdout, "" /* prefix */, 0 /* flags */) - - // RPC service implementing testv1connect.LibraryService annotations. +func Example_restClientToRpcServer() { + // This example shows Vanguard adding REST support to an RPC server built + // with Connect. (To add REST, gRPC-Web, and Connect support to servers built + // with grpc-go, use the connectrpc.com/vanguard/vanguardgrpc sub-package.) + logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */) + + // libraryRPC is an implementation of the testv1connect.LibraryService RPC + // server. It's a pure RPC server, without any hand-written translation to or + // from RESTful semantics. svc := &libraryRPC{} + rpcRoute, rpcHandler := testv1connect.NewLibraryServiceHandler(svc) - handler, err := vanguard.NewTranscoder([]*vanguard.Service{ - vanguard.NewService(testv1connect.NewLibraryServiceHandler(svc)), - }) + // Using Vanguard, the server can also accept RESTful requests. The Vanguard + // Transcoder handles both REST and RPC traffic, so there's no need to mount + // the RPC-only handler. + services := []*vanguard.Service{vanguard.NewService(rpcRoute, rpcHandler)} + transcoder, err := vanguard.NewTranscoder(services) if err != nil { - log.Println("error:", err) + logger.Println(err) return } - // Create the server. - // (This is a httptest.Server, but it could be any http.Server) - server := httptest.NewServer(handler) + // We can use any server that works with http.Handlers. Since this is a + // testable example, we're using httptest. + server := httptest.NewServer(transcoder) defer server.Close() - client := server.Client() + // With the server running, we can make a RESTful call. + client := server.Client() book := &testv1.Book{ Title: "2001: A Space Odyssey", Author: "Arthur C. Clarke", @@ -66,61 +75,79 @@ func ExampleNewTranscoder_restToConnect() { "genre": "science fiction", }, } - body, _ := protojson.Marshal(book) + body, err := protojson.Marshal(book) + if err != nil { + logger.Println(err) + return + } - // Create the POST request. - req, _ := http.NewRequestWithContext( + req, err := http.NewRequestWithContext( context.Background(), http.MethodPost, server.URL+"/v1/shelves/top/books", bytes.NewReader(body), ) + if err != nil { + logger.Println(err) + return + } req.Header.Set("Content-Type", "application/json") req.URL.RawQuery = "book_id=2&request_id=123" rsp, err := client.Do(req) if err != nil { - log.Println("error:", err) + logger.Println(err) return } defer rsp.Body.Close() - log.Println(rsp.Status) - log.Println(rsp.Header.Get("Content-Type")) + logger.Println(rsp.Status) + logger.Println(rsp.Header.Get("Content-Type")) - // Decode the response. - body, _ = io.ReadAll(rsp.Body) - - _ = protojson.Unmarshal(body, book) - log.Println(book.Author) + body, err = io.ReadAll(rsp.Body) + if err != nil { + logger.Println(err) + return + } + if err := protojson.Unmarshal(body, book); err != nil { + logger.Println(err) + return + } + logger.Println(book.Author) // Output: 200 OK // application/json // Arthur C. Clarke } -func ExampleNewTranscoder_connectToREST() { - log := log.New(os.Stdout, "" /* prefix */, 0 /* flags */) - - // REST service implementing testv1connect.LibraryService annotations. - svc := &libraryREST{} - - handler, err := vanguard.NewTranscoder( - []*vanguard.Service{vanguard.NewService( - testv1connect.LibraryServiceName, - svc, - // This tells vanguard that it must transform requests to REST. - vanguard.WithTargetProtocols(vanguard.ProtocolREST), - )}, - ) +func Example_rpcClientToRestServer() { + // This example shows Vanguard adding RPC support to an REST server. This + // lets organizations use RPC clients in new codebases without rewriting + // existing REST services. + logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */) + + // libraryREST is an http.Handler that implements a RESTful server. The + // implementation doesn't use Protobuf or RPC directly. + restHandler := &libraryREST{} + + // Using Vanguard, the server can also accept RPC traffic. The Vanguard + // Transcoder handles both REST and RPC traffic, so there's no need to mount + // the REST-only handler. + services := []*vanguard.Service{vanguard.NewService( + testv1connect.LibraryServiceName, + restHandler, + // This tells vanguard that the service implementation only supports REST. + vanguard.WithTargetProtocols(vanguard.ProtocolREST), + )} + transcoder, err := vanguard.NewTranscoder(services) if err != nil { - log.Println("error:", err) + logger.Println(err) return } - // Create the server. - // (This is a httptest.Server, but it could be any http.Server) - server := httptest.NewServer(handler) + // We can serve RPC and REST traffic using any server that works with + // http.Handlers. Since this is a testable example, we're using httptest. + server := httptest.NewServer(transcoder) defer server.Close() - // Create a connect client and call the service. + // With the server running, we can make an RPC call using a generated client. client := testv1connect.NewLibraryServiceClient(server.Client(), server.URL) rsp, err := client.GetBook( context.Background(), @@ -129,10 +156,10 @@ func ExampleNewTranscoder_connectToREST() { }), ) if err != nil { - log.Println("error:", err) + logger.Println(err) return } - log.Println(rsp.Msg.Description) + logger.Println(rsp.Msg.Description) // Output: Have you seen Blade Runner? }