Merge branch 'Xe/feat/twirp'
This commit is contained in:
commit
1d4169edc7
|
@ -206,10 +206,12 @@
|
|||
branch = "master"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"jsonpb",
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/struct",
|
||||
"ptypes/timestamp"
|
||||
]
|
||||
revision = "1e59b77b52bf8e4b449a57e6f79f21226d571845"
|
||||
|
@ -589,6 +591,16 @@
|
|||
revision = "98aa888b79d8de04afe0fccf45ed10594efc858b"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/twitchtv/twirp"
|
||||
packages = [
|
||||
".",
|
||||
"ctxsetters",
|
||||
"internal/contextkeys"
|
||||
]
|
||||
revision = "db96cdf354e8dc053e5ee5fe890bb0a7f18123ab"
|
||||
version = "v5.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ulikunitz/xz"
|
||||
packages = [
|
||||
|
@ -753,6 +765,6 @@
|
|||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "97c8282ef9b3abed71907d17ccf38379134714596610880b02d5ca03be634678"
|
||||
inputs-digest = "51866d1bd0089290b4562a563c65db61b4973e66be0e14297f0680059dbdf138"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
@ -16,14 +15,12 @@ import (
|
|||
"git.xeserv.us/xena/route/internal/database"
|
||||
"git.xeserv.us/xena/route/internal/routecrypto"
|
||||
proto "git.xeserv.us/xena/route/proto"
|
||||
"git.xeserv.us/xena/route/proto/route"
|
||||
"github.com/Xe/ln"
|
||||
"github.com/Xe/uuid"
|
||||
jwtcreds "github.com/Xe/x/tools/svc/credentials/jwt"
|
||||
"github.com/dickeyxxx/netrc"
|
||||
"github.com/kr/pretty"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
|
@ -192,28 +189,11 @@ retry_netrc:
|
|||
|
||||
m := n.Machine(*grpcServer)
|
||||
|
||||
connCreds := credentials.NewTLS(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
creds := jwtcreds.NewFromToken(m.Get("password"))
|
||||
conn, err := grpc.Dial(*grpcServer,
|
||||
grpc.WithTransportCredentials(connCreds),
|
||||
grpc.WithPerRPCCredentials(creds))
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("dialing grpc server"), ln.F{"hostname": *grpcServer})
|
||||
}
|
||||
|
||||
rc := proto.NewRoutesClient(conn)
|
||||
tc := proto.NewTokensClient(conn)
|
||||
bc := proto.NewBackendsClient(conn)
|
||||
|
||||
_ = rc
|
||||
_ = tc
|
||||
_ = bc
|
||||
cl := route.New("https://"+*grpcServer, m.Get("password"), &http.Client{})
|
||||
|
||||
switch cmdline {
|
||||
case "route create":
|
||||
rt, err := rc.Put(ctx, &proto.Route{Host: *routesCreateDomain})
|
||||
rt, err := cl.Routes.Put(ctx, &proto.Route{Host: *routesCreateDomain})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("create new route"))
|
||||
}
|
||||
|
@ -223,7 +203,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "route inspect":
|
||||
r, err := rc.Get(context.Background(), &proto.GetRouteRequest{
|
||||
r, err := cl.Routes.Get(context.Background(), &proto.GetRouteRequest{
|
||||
Id: *routesInspectID,
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -236,7 +216,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "route list":
|
||||
rts, err := rc.GetAll(context.Background(), &proto.Nil{})
|
||||
rts, err := cl.Routes.GetAll(context.Background(), &proto.Nil{})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("get all routes"))
|
||||
}
|
||||
|
@ -253,7 +233,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "route rm":
|
||||
_, err := rc.Delete(context.Background(), &proto.Route{Id: *routesRmID})
|
||||
_, err := cl.Routes.Delete(context.Background(), &proto.Route{Id: *routesRmID})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("remove single route"), ln.F{"id": *routesRmID})
|
||||
}
|
||||
|
@ -261,7 +241,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "backend list":
|
||||
bkds, err := bc.List(context.Background(), &proto.BackendSelector{
|
||||
bkds, err := cl.Backends.List(context.Background(), &proto.BackendSelector{
|
||||
Domain: *backendListDomain,
|
||||
User: *backendListUser,
|
||||
})
|
||||
|
@ -281,7 +261,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "backend kill":
|
||||
_, err := bc.Kill(context.Background(), &proto.BackendID{Id: *backendKillID})
|
||||
_, err := cl.Backends.Kill(context.Background(), &proto.BackendID{Id: *backendKillID})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("attempt to kill backend"), ln.F{"backend_id": *backendKillID})
|
||||
}
|
||||
|
@ -291,7 +271,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "token list":
|
||||
lis, err := tc.GetAll(ctx, &proto.Nil{})
|
||||
lis, err := cl.Tokens.GetAll(ctx, &proto.Nil{})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("get all tokens"))
|
||||
}
|
||||
|
@ -313,7 +293,7 @@ retry_netrc:
|
|||
Scopes: scps,
|
||||
}
|
||||
|
||||
ftkn, err := tc.Put(ctx, tkn)
|
||||
ftkn, err := cl.Tokens.Put(ctx, tkn)
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("put token to server"))
|
||||
}
|
||||
|
@ -324,7 +304,7 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "token inspect":
|
||||
tkn, err := tc.Get(ctx, &proto.GetTokenRequest{Id: *tokenInspectID})
|
||||
tkn, err := cl.Tokens.Get(ctx, &proto.GetTokenRequest{Id: *tokenInspectID})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("fetch token from server"), ln.F{"token_id": *tokenInspectID})
|
||||
}
|
||||
|
@ -334,17 +314,17 @@ retry_netrc:
|
|||
return
|
||||
|
||||
case "token rm":
|
||||
tkn, err := tc.Get(ctx, &proto.GetTokenRequest{Id: *tokenRmID})
|
||||
tkn, err := cl.Tokens.Get(ctx, &proto.GetTokenRequest{Id: *tokenRmID})
|
||||
if err != nil {
|
||||
ln.FatalErr(ctx, err, ln.Action("fetch token from server"), ln.F{"token_id": *tokenRmID})
|
||||
}
|
||||
|
||||
var action ln.Fer
|
||||
if *tokenRmHard {
|
||||
_, err = tc.Delete(ctx, tkn)
|
||||
_, err = cl.Tokens.Delete(ctx, tkn)
|
||||
action = ln.Action("actually delete token")
|
||||
} else {
|
||||
_, err = tc.Deactivate(ctx, tkn)
|
||||
_, err = cl.Tokens.Deactivate(ctx, tkn)
|
||||
action = ln.Action("deactivate token")
|
||||
}
|
||||
|
||||
|
|
|
@ -3,13 +3,14 @@ package main
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.xeserv.us/xena/route/internal/database"
|
||||
"git.xeserv.us/xena/route/internal/middleware"
|
||||
"github.com/Xe/ln"
|
||||
"github.com/twitchtv/twirp"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// errors
|
||||
|
@ -17,23 +18,92 @@ var (
|
|||
ErrNotAuthorized = errors.New("server: not authorized")
|
||||
)
|
||||
|
||||
func (s *Server) getAuth(ctx context.Context, operation, scope string) (database.Token, error) {
|
||||
var err error
|
||||
func (s *Server) makeTwirpHooks() *twirp.ServerHooks {
|
||||
hooks := &twirp.ServerHooks{}
|
||||
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
hooks.RequestRouted = func(ctx context.Context) (context.Context, error) {
|
||||
ctx = withStartTime(ctx)
|
||||
|
||||
method, ok := twirp.MethodName(ctx)
|
||||
if !ok {
|
||||
return database.Token{}, grpc.Errorf(codes.Unauthenticated, "valid token required.")
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
jwtToken, ok := md["authorization"]
|
||||
pkg, ok := twirp.PackageName(ctx)
|
||||
if !ok {
|
||||
return database.Token{}, grpc.Errorf(codes.Unauthenticated, "valid token required.")
|
||||
return ctx, nil
|
||||
}
|
||||
val := jwtToken[0]
|
||||
|
||||
t, err := s.db.GetToken(ctx, val)
|
||||
svc, ok := twirp.ServiceName(ctx)
|
||||
if !ok {
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
ctx = ln.WithF(ctx, ln.F{
|
||||
"twirp_method": method,
|
||||
"twirp_package": pkg,
|
||||
"twirp_service": svc,
|
||||
})
|
||||
|
||||
hdr, ok := middleware.GetHeaders(ctx)
|
||||
if !ok {
|
||||
return ctx, errors.New("can't get request headers")
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
req.Header = hdr
|
||||
|
||||
ck, err := req.Cookie("routed")
|
||||
if err != nil {
|
||||
return database.Token{}, grpc.Errorf(codes.Unauthenticated, "valid token required.")
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
tok := ck.Value
|
||||
|
||||
t, err := s.db.GetToken(ctx, tok)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
|
||||
ctx = withAuthToken(ctx, t)
|
||||
ctx = ln.WithF(ctx, t.F())
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
hooks.ResponseSent = func(ctx context.Context) {
|
||||
f := ln.F{}
|
||||
now := time.Now()
|
||||
t, ok := getStartTime(ctx)
|
||||
if ok {
|
||||
f["response_time"] = now.Sub(t)
|
||||
}
|
||||
|
||||
ln.Log(ctx, f, ln.Action("response sent"))
|
||||
}
|
||||
|
||||
hooks.Error = func(ctx context.Context, e twirp.Error) context.Context {
|
||||
f := ln.F{}
|
||||
|
||||
for k, v := range e.MetaMap() {
|
||||
f["twirp_meta_"+k] = v
|
||||
}
|
||||
|
||||
ln.Error(ctx, e, f, ln.Action("twirp error"), ln.F{
|
||||
"twirp_error_code": e.Code(),
|
||||
"twirp_error_msg": e.Msg(),
|
||||
})
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
return hooks
|
||||
}
|
||||
|
||||
func (s *Server) getAuth(ctx context.Context, operation, scope string) (database.Token, error) {
|
||||
t, ok := getAuthToken(ctx)
|
||||
if !ok {
|
||||
return database.Token{}, errors.New("no auth token in context")
|
||||
}
|
||||
|
||||
ok = false
|
||||
|
@ -42,11 +112,12 @@ func (s *Server) getAuth(ctx context.Context, operation, scope string) (database
|
|||
ok = true
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return database.Token{}, grpc.Errorf(codes.Unauthenticated, "invalid scope.")
|
||||
return database.Token{}, ErrNotAuthorized
|
||||
}
|
||||
|
||||
ln.Log(ctx, t)
|
||||
ln.WithF(ctx, ln.F{"operation": operation})
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.xeserv.us/xena/route/internal/database"
|
||||
)
|
||||
|
||||
type ctxKey int
|
||||
|
||||
const (
|
||||
startTimeKey ctxKey = iota
|
||||
tokenKey
|
||||
)
|
||||
|
||||
func withStartTime(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, startTimeKey, time.Now())
|
||||
}
|
||||
|
||||
func getStartTime(ctx context.Context) (time.Time, bool) {
|
||||
t, ok := ctx.Value(startTimeKey).(time.Time)
|
||||
if !ok {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
return t, true
|
||||
}
|
||||
|
||||
func withAuthToken(ctx context.Context, token database.Token) context.Context {
|
||||
return context.WithValue(ctx, tokenKey, token)
|
||||
}
|
||||
|
||||
func getAuthToken(ctx context.Context) (database.Token, bool) {
|
||||
t, ok := ctx.Value(tokenKey).(database.Token)
|
||||
if !ok {
|
||||
return database.Token{}, false
|
||||
}
|
||||
|
||||
return t, true
|
||||
}
|
|
@ -117,7 +117,7 @@ func setupQuic(ctx context.Context, s *Server, scfg Config) {
|
|||
|
||||
qs := &h2quic.Server{
|
||||
Server: &http.Server{
|
||||
Handler: middleware.Trace(s),
|
||||
Handler: middleware.Trace("http-quic")(s),
|
||||
Addr: scfg.QuicAddr,
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: s.GetCertificate,
|
||||
|
@ -156,7 +156,7 @@ func setupTLS(ctx context.Context, s *Server, scfg Config) {
|
|||
}
|
||||
|
||||
hs := &http.Server{
|
||||
Handler: middleware.Trace(s),
|
||||
Handler: middleware.Trace("https")(s),
|
||||
Addr: scfg.SSLAddr,
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: s.GetCertificate,
|
||||
|
|
|
@ -14,7 +14,7 @@ type Route struct {
|
|||
|
||||
// interface assertions
|
||||
var (
|
||||
_ proto.RoutesServer = &Route{}
|
||||
_ proto.Routes = &Route{}
|
||||
)
|
||||
|
||||
// Get fetches a route from the database.
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"git.xeserv.us/xena/route/internal/database"
|
||||
"git.xeserv.us/xena/route/internal/middleware"
|
||||
"git.xeserv.us/xena/route/internal/tun2"
|
||||
proto "git.xeserv.us/xena/route/proto"
|
||||
"github.com/Xe/ln"
|
||||
|
@ -17,8 +18,6 @@ import (
|
|||
kcp "github.com/xtaci/kcp-go"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
// RPC constants
|
||||
|
@ -149,19 +148,35 @@ func New(cfg Config) (*Server, error) {
|
|||
go s.listenKCP(context.Background(), cfg.BackendKCPAddr, tc)
|
||||
go s.listenTCP(context.Background(), cfg.BackendTCPAddr, tc)
|
||||
|
||||
// gRPC setup
|
||||
gs := grpc.NewServer(grpc.Creds(credentials.NewTLS(tc)))
|
||||
|
||||
proto.RegisterBackendsServer(gs, &Backend{Server: s})
|
||||
proto.RegisterRoutesServer(gs, &Route{Server: s})
|
||||
proto.RegisterTokensServer(gs, &Token{Server: s})
|
||||
|
||||
l, err := net.Listen("tcp", cfg.GRPCAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
withHandler := func(disc string, h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
h.ServeHTTP(w, r.WithContext(ln.WithF(r.Context(), ln.F{"disc": disc})))
|
||||
})
|
||||
}
|
||||
|
||||
go gs.Serve(l)
|
||||
bhdr := proto.NewBackendsServer(&Backend{Server: s}, s.makeTwirpHooks())
|
||||
rhdr := proto.NewRoutesServer(&Route{Server: s}, s.makeTwirpHooks())
|
||||
thdr := proto.NewTokensServer(&Token{Server: s}, s.makeTwirpHooks())
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.Handle(proto.BackendsPathPrefix, withHandler("backends", bhdr))
|
||||
mux.Handle(proto.RoutesPathPrefix, withHandler("routes", rhdr))
|
||||
mux.Handle(proto.TokensPathPrefix, withHandler("tokens", thdr))
|
||||
|
||||
hs := &http.Server{
|
||||
TLSConfig: tc,
|
||||
Addr: cfg.GRPCAddr,
|
||||
Handler: middleware.SaveHeaders(middleware.Trace("twirp-https")(mux)),
|
||||
}
|
||||
|
||||
ln.Log(context.Background(), ln.F{
|
||||
"kind": "api",
|
||||
"backends": proto.BackendsPathPrefix,
|
||||
"routes": proto.RoutesPathPrefix,
|
||||
"tokens": proto.TokensPathPrefix,
|
||||
"addr": cfg.GRPCAddr,
|
||||
})
|
||||
go hs.ListenAndServeTLS("", "")
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ type Token struct {
|
|||
|
||||
// interface assertions
|
||||
var (
|
||||
_ proto.TokensServer = &Token{}
|
||||
_ proto.Tokens = &Token{}
|
||||
)
|
||||
|
||||
func (t *Token) Get(ctx context.Context, req *proto.GetTokenRequest) (*proto.Token, error) {
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
jwtcreds "github.com/Xe/x/tools/svc/credentials/jwt"
|
||||
"git.xeserv.us/xena/route/proto/route"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"github.com/hashicorp/terraform/plugin"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -33,11 +31,6 @@ func provider() terraform.ResourceProvider {
|
|||
Required: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ROUTE_HOST", nil),
|
||||
},
|
||||
"verify_tls": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
DefaultFunc: schema.EnvDefaultFunc("ROUTE_VERIFY_TLS", nil),
|
||||
},
|
||||
},
|
||||
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
|
@ -52,20 +45,10 @@ func provider() terraform.ResourceProvider {
|
|||
func providerConfigure(d *schema.ResourceData) (interface{}, error) {
|
||||
token := d.Get("token").(string)
|
||||
host := d.Get("host").(string)
|
||||
verifyTLS := d.Get("verify_tls").(bool)
|
||||
|
||||
log.Printf("[INFO] Initializing route client connecting to %s", host)
|
||||
|
||||
connCreds := credentials.NewTLS(&tls.Config{
|
||||
InsecureSkipVerify: verifyTLS,
|
||||
})
|
||||
creds := jwtcreds.NewFromToken(token)
|
||||
conn, err := grpc.Dial(host,
|
||||
grpc.WithTransportCredentials(connCreds),
|
||||
grpc.WithPerRPCCredentials(creds))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cl := route.New(host, token, &http.Client{})
|
||||
|
||||
return conn, nil
|
||||
return cl, nil
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"log"
|
||||
|
||||
proto "git.xeserv.us/xena/route/proto"
|
||||
"git.xeserv.us/xena/route/proto/route"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func routeResource() *schema.Resource {
|
||||
|
@ -31,9 +31,9 @@ func routeResource() *schema.Resource {
|
|||
}
|
||||
|
||||
func resourceRouteCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
cli := proto.NewRoutesClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
rt, err := cli.Put(context.Background(), &proto.Route{
|
||||
rt, err := cli.Routes.Put(context.Background(), &proto.Route{
|
||||
Host: d.Get("host").(string),
|
||||
})
|
||||
|
||||
|
@ -47,14 +47,14 @@ func resourceRouteCreate(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
func resourceRouteDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
cli := proto.NewRoutesClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
rt, err := cli.Get(context.Background(), &proto.GetRouteRequest{Id: d.Id()})
|
||||
rt, err := cli.Routes.Get(context.Background(), &proto.GetRouteRequest{Id: d.Id()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cli.Delete(context.Background(), rt)
|
||||
_, err = cli.Routes.Delete(context.Background(), rt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -65,9 +65,9 @@ func resourceRouteDelete(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
func resourceRouteExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||
cli := proto.NewRoutesClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
_, err := cli.Get(context.Background(), &proto.GetRouteRequest{Id: d.Id()})
|
||||
_, err := cli.Routes.Get(context.Background(), &proto.GetRouteRequest{Id: d.Id()})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -76,9 +76,9 @@ func resourceRouteExists(d *schema.ResourceData, meta interface{}) (bool, error)
|
|||
}
|
||||
|
||||
func resourceRouteRead(d *schema.ResourceData, meta interface{}) error {
|
||||
cli := proto.NewRoutesClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
rt, err := cli.Get(context.Background(), &proto.GetRouteRequest{Id: d.Id()})
|
||||
rt, err := cli.Routes.Get(context.Background(), &proto.GetRouteRequest{Id: d.Id()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"log"
|
||||
|
||||
proto "git.xeserv.us/xena/route/proto"
|
||||
"git.xeserv.us/xena/route/proto/route"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func tokenResource() *schema.Resource {
|
||||
|
@ -31,7 +31,7 @@ func tokenResource() *schema.Resource {
|
|||
}
|
||||
|
||||
func resourceTokenCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
cli := proto.NewTokensClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
var scps []string
|
||||
|
||||
|
@ -45,7 +45,7 @@ func resourceTokenCreate(d *schema.ResourceData, meta interface{}) error {
|
|||
scps = append(scps, sc)
|
||||
}
|
||||
|
||||
tok, err := cli.Put(context.Background(), &proto.Token{Scopes: scps})
|
||||
tok, err := cli.Tokens.Put(context.Background(), &proto.Token{Scopes: scps})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -56,14 +56,14 @@ func resourceTokenCreate(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
func resourceTokenDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
cli := proto.NewTokensClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
tok, err := cli.Get(context.Background(), &proto.GetTokenRequest{Id: d.Id()})
|
||||
tok, err := cli.Tokens.Get(context.Background(), &proto.GetTokenRequest{Id: d.Id()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = cli.Deactivate(context.Background(), tok)
|
||||
_, err = cli.Tokens.Deactivate(context.Background(), tok)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -72,9 +72,9 @@ func resourceTokenDelete(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
|
||||
func resourceTokenExists(d *schema.ResourceData, meta interface{}) (bool, error) {
|
||||
cli := proto.NewTokensClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
_, err := cli.Get(context.Background(), &proto.GetTokenRequest{Id: d.Id()})
|
||||
_, err := cli.Tokens.Get(context.Background(), &proto.GetTokenRequest{Id: d.Id()})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -83,9 +83,9 @@ func resourceTokenExists(d *schema.ResourceData, meta interface{}) (bool, error)
|
|||
}
|
||||
|
||||
func resourceTokenRead(d *schema.ResourceData, meta interface{}) error {
|
||||
cli := proto.NewTokensClient(meta.(*grpc.ClientConn))
|
||||
cli := meta.(*route.Client)
|
||||
|
||||
tok, err := cli.Get(context.Background(), &proto.GetTokenRequest{Id: d.Id()})
|
||||
tok, err := cli.Tokens.Get(context.Background(), &proto.GetTokenRequest{Id: d.Id()})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ func (t Token) F() ln.F {
|
|||
"token-id": t.ID,
|
||||
"token-owner": t.Owner,
|
||||
"token-active": t.Active,
|
||||
"token-scopes": t.Scopes,
|
||||
"token-created-at": t.CreatedAt.String(),
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type headerKey int
|
||||
|
||||
const hdrKey headerKey = iota
|
||||
|
||||
// GetHeaders fetches http headers from the request context.
|
||||
func GetHeaders(ctx context.Context) (http.Header, bool) {
|
||||
h, ok := ctx.Value(hdrKey).(http.Header)
|
||||
if !ok {
|
||||
return http.Header{}, false
|
||||
}
|
||||
|
||||
return h, true
|
||||
}
|
||||
|
||||
// SaveHeaders adds the needed values to the request context for twirp services.
|
||||
func SaveHeaders(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if ctx == nil {
|
||||
panic("context is nil")
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, hdrKey, r.Header)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
|
@ -3,7 +3,6 @@ package middleware
|
|||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
|
@ -12,20 +11,23 @@ import (
|
|||
)
|
||||
|
||||
// Trace adds go stdlib tracing to this http handler.
|
||||
func Trace(next http.Handler) http.Handler {
|
||||
func Trace(family string) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
sp := trace.New(filepath.Base(os.Args[0]+"-https"), r.Host+r.RequestURI)
|
||||
sp := trace.New(filepath.Base(family), r.Host+r.RequestURI)
|
||||
defer sp.Finish()
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
ctx = trace.NewContext(ctx, sp)
|
||||
f := ln.F{
|
||||
"family": family,
|
||||
"method": r.Method,
|
||||
"path": r.URL.Path,
|
||||
"remote_addr": r.RemoteAddr,
|
||||
"user_agent": r.UserAgent(),
|
||||
}
|
||||
ctx = ln.WithF(ctx, f)
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
|
||||
|
@ -39,3 +41,4 @@ func Trace(next http.Handler) http.Handler {
|
|||
ln.Log(ctx, f)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
3
mage.go
3
mage.go
|
@ -164,8 +164,7 @@ func Tools(ctx context.Context) {
|
|||
tools := []string{
|
||||
"github.com/golang/dep/cmd/dep",
|
||||
"github.com/golang/protobuf/protoc-gen-go",
|
||||
// "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway",
|
||||
// "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger",
|
||||
"github.com/twitchtv/twirp/protoc-gen-twirp",
|
||||
}
|
||||
|
||||
for _, t := range tools {
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package routeclient
|
|
@ -1,2 +0,0 @@
|
|||
// Package routeclient is a higer level convenience wrapper around the RPC layer for route.
|
||||
package routeclient
|
|
@ -1,7 +1,6 @@
|
|||
#!/bin/bash
|
||||
|
||||
protoc -I/usr/local/include -I. \
|
||||
-I$GOPATH/src \
|
||||
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
|
||||
--go_out=Mgoogle/api/annotations.proto=github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis/google/api,plugins=grpc:. \
|
||||
protoc -I. \
|
||||
--go_out=:. \
|
||||
--twirp_out=. \
|
||||
route.proto
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// source: route.proto
|
||||
|
||||
/*
|
||||
Package route is a generated protocol buffer package.
|
||||
Package proto is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
route.proto
|
||||
|
@ -20,19 +20,14 @@ It has these top-level messages:
|
|||
BackendSelector
|
||||
BackendID
|
||||
*/
|
||||
package route
|
||||
package proto
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import proto1 "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = proto1.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
|
@ -40,14 +35,14 @@ var _ = math.Inf
|
|||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// Nil represents nothing.
|
||||
type Nil struct {
|
||||
}
|
||||
|
||||
func (m *Nil) Reset() { *m = Nil{} }
|
||||
func (m *Nil) String() string { return proto.CompactTextString(m) }
|
||||
func (m *Nil) String() string { return proto1.CompactTextString(m) }
|
||||
func (*Nil) ProtoMessage() {}
|
||||
func (*Nil) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
|
@ -59,7 +54,7 @@ type GetRouteRequest struct {
|
|||
}
|
||||
|
||||
func (m *GetRouteRequest) Reset() { *m = GetRouteRequest{} }
|
||||
func (m *GetRouteRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (m *GetRouteRequest) String() string { return proto1.CompactTextString(m) }
|
||||
func (*GetRouteRequest) ProtoMessage() {}
|
||||
func (*GetRouteRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
|
@ -85,7 +80,7 @@ type Route struct {
|
|||
}
|
||||
|
||||
func (m *Route) Reset() { *m = Route{} }
|
||||
func (m *Route) String() string { return proto.CompactTextString(m) }
|
||||
func (m *Route) String() string { return proto1.CompactTextString(m) }
|
||||
func (*Route) ProtoMessage() {}
|
||||
func (*Route) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
|
@ -116,7 +111,7 @@ type GetAllRoutesResponse struct {
|
|||
}
|
||||
|
||||
func (m *GetAllRoutesResponse) Reset() { *m = GetAllRoutesResponse{} }
|
||||
func (m *GetAllRoutesResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (m *GetAllRoutesResponse) String() string { return proto1.CompactTextString(m) }
|
||||
func (*GetAllRoutesResponse) ProtoMessage() {}
|
||||
func (*GetAllRoutesResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
|
@ -137,7 +132,7 @@ type Token struct {
|
|||
}
|
||||
|
||||
func (m *Token) Reset() { *m = Token{} }
|
||||
func (m *Token) String() string { return proto.CompactTextString(m) }
|
||||
func (m *Token) String() string { return proto1.CompactTextString(m) }
|
||||
func (*Token) ProtoMessage() {}
|
||||
func (*Token) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
|
@ -175,7 +170,7 @@ type TokenSet struct {
|
|||
}
|
||||
|
||||
func (m *TokenSet) Reset() { *m = TokenSet{} }
|
||||
func (m *TokenSet) String() string { return proto.CompactTextString(m) }
|
||||
func (m *TokenSet) String() string { return proto1.CompactTextString(m) }
|
||||
func (*TokenSet) ProtoMessage() {}
|
||||
func (*TokenSet) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
|
@ -192,7 +187,7 @@ type GetTokenRequest struct {
|
|||
}
|
||||
|
||||
func (m *GetTokenRequest) Reset() { *m = GetTokenRequest{} }
|
||||
func (m *GetTokenRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (m *GetTokenRequest) String() string { return proto1.CompactTextString(m) }
|
||||
func (*GetTokenRequest) ProtoMessage() {}
|
||||
func (*GetTokenRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
|
||||
|
@ -221,7 +216,7 @@ type Backend struct {
|
|||
}
|
||||
|
||||
func (m *Backend) Reset() { *m = Backend{} }
|
||||
func (m *Backend) String() string { return proto.CompactTextString(m) }
|
||||
func (m *Backend) String() string { return proto1.CompactTextString(m) }
|
||||
func (*Backend) ProtoMessage() {}
|
||||
func (*Backend) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||
|
||||
|
@ -280,7 +275,7 @@ type BackendList struct {
|
|||
}
|
||||
|
||||
func (m *BackendList) Reset() { *m = BackendList{} }
|
||||
func (m *BackendList) String() string { return proto.CompactTextString(m) }
|
||||
func (m *BackendList) String() string { return proto1.CompactTextString(m) }
|
||||
func (*BackendList) ProtoMessage() {}
|
||||
func (*BackendList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
|
||||
|
||||
|
@ -304,7 +299,7 @@ type BackendSelector struct {
|
|||
}
|
||||
|
||||
func (m *BackendSelector) Reset() { *m = BackendSelector{} }
|
||||
func (m *BackendSelector) String() string { return proto.CompactTextString(m) }
|
||||
func (m *BackendSelector) String() string { return proto1.CompactTextString(m) }
|
||||
func (*BackendSelector) ProtoMessage() {}
|
||||
func (*BackendSelector) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
|
||||
|
||||
|
@ -327,7 +322,7 @@ type BackendID struct {
|
|||
}
|
||||
|
||||
func (m *BackendID) Reset() { *m = BackendID{} }
|
||||
func (m *BackendID) String() string { return proto.CompactTextString(m) }
|
||||
func (m *BackendID) String() string { return proto1.CompactTextString(m) }
|
||||
func (*BackendID) ProtoMessage() {}
|
||||
func (*BackendID) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
|
||||
|
||||
|
@ -339,528 +334,58 @@ func (m *BackendID) GetId() string {
|
|||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Nil)(nil), "route.Nil")
|
||||
proto.RegisterType((*GetRouteRequest)(nil), "route.GetRouteRequest")
|
||||
proto.RegisterType((*Route)(nil), "route.Route")
|
||||
proto.RegisterType((*GetAllRoutesResponse)(nil), "route.GetAllRoutesResponse")
|
||||
proto.RegisterType((*Token)(nil), "route.Token")
|
||||
proto.RegisterType((*TokenSet)(nil), "route.TokenSet")
|
||||
proto.RegisterType((*GetTokenRequest)(nil), "route.GetTokenRequest")
|
||||
proto.RegisterType((*Backend)(nil), "route.Backend")
|
||||
proto.RegisterType((*BackendList)(nil), "route.BackendList")
|
||||
proto.RegisterType((*BackendSelector)(nil), "route.BackendSelector")
|
||||
proto.RegisterType((*BackendID)(nil), "route.BackendID")
|
||||
proto1.RegisterType((*Nil)(nil), "xeserv.us.route.Nil")
|
||||
proto1.RegisterType((*GetRouteRequest)(nil), "xeserv.us.route.GetRouteRequest")
|
||||
proto1.RegisterType((*Route)(nil), "xeserv.us.route.Route")
|
||||
proto1.RegisterType((*GetAllRoutesResponse)(nil), "xeserv.us.route.GetAllRoutesResponse")
|
||||
proto1.RegisterType((*Token)(nil), "xeserv.us.route.Token")
|
||||
proto1.RegisterType((*TokenSet)(nil), "xeserv.us.route.TokenSet")
|
||||
proto1.RegisterType((*GetTokenRequest)(nil), "xeserv.us.route.GetTokenRequest")
|
||||
proto1.RegisterType((*Backend)(nil), "xeserv.us.route.Backend")
|
||||
proto1.RegisterType((*BackendList)(nil), "xeserv.us.route.BackendList")
|
||||
proto1.RegisterType((*BackendSelector)(nil), "xeserv.us.route.BackendSelector")
|
||||
proto1.RegisterType((*BackendID)(nil), "xeserv.us.route.BackendID")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// Client API for Routes service
|
||||
|
||||
type RoutesClient interface {
|
||||
// Get fetches a single route based on the Host or ID.
|
||||
Get(ctx context.Context, in *GetRouteRequest, opts ...grpc.CallOption) (*Route, error)
|
||||
// GetAll fetches all of the routes that the user owns.
|
||||
GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*GetAllRoutesResponse, error)
|
||||
// Put creates a new route based on user-supplied details.
|
||||
Put(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Route, error)
|
||||
// Delete removes a route.
|
||||
Delete(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Nil, error)
|
||||
}
|
||||
|
||||
type routesClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewRoutesClient(cc *grpc.ClientConn) RoutesClient {
|
||||
return &routesClient{cc}
|
||||
}
|
||||
|
||||
func (c *routesClient) Get(ctx context.Context, in *GetRouteRequest, opts ...grpc.CallOption) (*Route, error) {
|
||||
out := new(Route)
|
||||
err := grpc.Invoke(ctx, "/route.Routes/Get", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *routesClient) GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*GetAllRoutesResponse, error) {
|
||||
out := new(GetAllRoutesResponse)
|
||||
err := grpc.Invoke(ctx, "/route.Routes/GetAll", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *routesClient) Put(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Route, error) {
|
||||
out := new(Route)
|
||||
err := grpc.Invoke(ctx, "/route.Routes/Put", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *routesClient) Delete(ctx context.Context, in *Route, opts ...grpc.CallOption) (*Nil, error) {
|
||||
out := new(Nil)
|
||||
err := grpc.Invoke(ctx, "/route.Routes/Delete", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Routes service
|
||||
|
||||
type RoutesServer interface {
|
||||
// Get fetches a single route based on the Host or ID.
|
||||
Get(context.Context, *GetRouteRequest) (*Route, error)
|
||||
// GetAll fetches all of the routes that the user owns.
|
||||
GetAll(context.Context, *Nil) (*GetAllRoutesResponse, error)
|
||||
// Put creates a new route based on user-supplied details.
|
||||
Put(context.Context, *Route) (*Route, error)
|
||||
// Delete removes a route.
|
||||
Delete(context.Context, *Route) (*Nil, error)
|
||||
}
|
||||
|
||||
func RegisterRoutesServer(s *grpc.Server, srv RoutesServer) {
|
||||
s.RegisterService(&_Routes_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Routes_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetRouteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoutesServer).Get(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Routes/Get",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoutesServer).Get(ctx, req.(*GetRouteRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Routes_GetAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Nil)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoutesServer).GetAll(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Routes/GetAll",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoutesServer).GetAll(ctx, req.(*Nil))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Routes_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Route)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoutesServer).Put(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Routes/Put",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoutesServer).Put(ctx, req.(*Route))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Routes_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Route)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(RoutesServer).Delete(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Routes/Delete",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(RoutesServer).Delete(ctx, req.(*Route))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Routes_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "route.Routes",
|
||||
HandlerType: (*RoutesServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Get",
|
||||
Handler: _Routes_Get_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAll",
|
||||
Handler: _Routes_GetAll_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Put",
|
||||
Handler: _Routes_Put_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _Routes_Delete_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "route.proto",
|
||||
}
|
||||
|
||||
// Client API for Tokens service
|
||||
|
||||
type TokensClient interface {
|
||||
Get(ctx context.Context, in *GetTokenRequest, opts ...grpc.CallOption) (*Token, error)
|
||||
GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*TokenSet, error)
|
||||
Put(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Token, error)
|
||||
Delete(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error)
|
||||
Deactivate(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error)
|
||||
}
|
||||
|
||||
type tokensClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewTokensClient(cc *grpc.ClientConn) TokensClient {
|
||||
return &tokensClient{cc}
|
||||
}
|
||||
|
||||
func (c *tokensClient) Get(ctx context.Context, in *GetTokenRequest, opts ...grpc.CallOption) (*Token, error) {
|
||||
out := new(Token)
|
||||
err := grpc.Invoke(ctx, "/route.Tokens/Get", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *tokensClient) GetAll(ctx context.Context, in *Nil, opts ...grpc.CallOption) (*TokenSet, error) {
|
||||
out := new(TokenSet)
|
||||
err := grpc.Invoke(ctx, "/route.Tokens/GetAll", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *tokensClient) Put(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Token, error) {
|
||||
out := new(Token)
|
||||
err := grpc.Invoke(ctx, "/route.Tokens/Put", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *tokensClient) Delete(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error) {
|
||||
out := new(Nil)
|
||||
err := grpc.Invoke(ctx, "/route.Tokens/Delete", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *tokensClient) Deactivate(ctx context.Context, in *Token, opts ...grpc.CallOption) (*Nil, error) {
|
||||
out := new(Nil)
|
||||
err := grpc.Invoke(ctx, "/route.Tokens/Deactivate", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Tokens service
|
||||
|
||||
type TokensServer interface {
|
||||
Get(context.Context, *GetTokenRequest) (*Token, error)
|
||||
GetAll(context.Context, *Nil) (*TokenSet, error)
|
||||
Put(context.Context, *Token) (*Token, error)
|
||||
Delete(context.Context, *Token) (*Nil, error)
|
||||
Deactivate(context.Context, *Token) (*Nil, error)
|
||||
}
|
||||
|
||||
func RegisterTokensServer(s *grpc.Server, srv TokensServer) {
|
||||
s.RegisterService(&_Tokens_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Tokens_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetTokenRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TokensServer).Get(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Tokens/Get",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TokensServer).Get(ctx, req.(*GetTokenRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Tokens_GetAll_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Nil)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TokensServer).GetAll(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Tokens/GetAll",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TokensServer).GetAll(ctx, req.(*Nil))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Tokens_Put_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Token)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TokensServer).Put(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Tokens/Put",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TokensServer).Put(ctx, req.(*Token))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Tokens_Delete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Token)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TokensServer).Delete(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Tokens/Delete",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TokensServer).Delete(ctx, req.(*Token))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Tokens_Deactivate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Token)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(TokensServer).Deactivate(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Tokens/Deactivate",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(TokensServer).Deactivate(ctx, req.(*Token))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Tokens_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "route.Tokens",
|
||||
HandlerType: (*TokensServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Get",
|
||||
Handler: _Tokens_Get_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetAll",
|
||||
Handler: _Tokens_GetAll_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Put",
|
||||
Handler: _Tokens_Put_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Delete",
|
||||
Handler: _Tokens_Delete_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Deactivate",
|
||||
Handler: _Tokens_Deactivate_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "route.proto",
|
||||
}
|
||||
|
||||
// Client API for Backends service
|
||||
|
||||
type BackendsClient interface {
|
||||
List(ctx context.Context, in *BackendSelector, opts ...grpc.CallOption) (*BackendList, error)
|
||||
Kill(ctx context.Context, in *BackendID, opts ...grpc.CallOption) (*Nil, error)
|
||||
}
|
||||
|
||||
type backendsClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewBackendsClient(cc *grpc.ClientConn) BackendsClient {
|
||||
return &backendsClient{cc}
|
||||
}
|
||||
|
||||
func (c *backendsClient) List(ctx context.Context, in *BackendSelector, opts ...grpc.CallOption) (*BackendList, error) {
|
||||
out := new(BackendList)
|
||||
err := grpc.Invoke(ctx, "/route.Backends/List", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *backendsClient) Kill(ctx context.Context, in *BackendID, opts ...grpc.CallOption) (*Nil, error) {
|
||||
out := new(Nil)
|
||||
err := grpc.Invoke(ctx, "/route.Backends/Kill", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Backends service
|
||||
|
||||
type BackendsServer interface {
|
||||
List(context.Context, *BackendSelector) (*BackendList, error)
|
||||
Kill(context.Context, *BackendID) (*Nil, error)
|
||||
}
|
||||
|
||||
func RegisterBackendsServer(s *grpc.Server, srv BackendsServer) {
|
||||
s.RegisterService(&_Backends_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Backends_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BackendSelector)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BackendsServer).List(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Backends/List",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BackendsServer).List(ctx, req.(*BackendSelector))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Backends_Kill_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BackendID)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BackendsServer).Kill(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/route.Backends/Kill",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BackendsServer).Kill(ctx, req.(*BackendID))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Backends_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "route.Backends",
|
||||
HandlerType: (*BackendsServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "List",
|
||||
Handler: _Backends_List_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Kill",
|
||||
Handler: _Backends_Kill_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "route.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("route.proto", fileDescriptor0) }
|
||||
func init() { proto1.RegisterFile("route.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 556 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0xd1, 0x6a, 0xdb, 0x4a,
|
||||
0x10, 0xb5, 0x24, 0x4b, 0xb1, 0xc7, 0x97, 0x38, 0x0c, 0x26, 0x08, 0xe7, 0xc5, 0xec, 0x0d, 0xa9,
|
||||
0x29, 0x34, 0x14, 0xa7, 0x50, 0x0a, 0xed, 0x43, 0x8b, 0x8b, 0x09, 0x2d, 0xa1, 0xc8, 0x7d, 0xeb,
|
||||
0x93, 0x64, 0x0d, 0x58, 0x44, 0xf5, 0xba, 0xda, 0x55, 0xa1, 0x9f, 0xd2, 0x2f, 0xe9, 0x97, 0xf4,
|
||||
0x7f, 0xca, 0xce, 0xae, 0x63, 0xcb, 0x4a, 0xfb, 0x36, 0x33, 0x67, 0x76, 0x74, 0xce, 0xcc, 0x11,
|
||||
0x0c, 0x2a, 0x59, 0x6b, 0xba, 0xde, 0x56, 0x52, 0x4b, 0x0c, 0x39, 0x11, 0x21, 0x04, 0x77, 0x45,
|
||||
0x29, 0x5e, 0xc1, 0x70, 0x41, 0x3a, 0x31, 0xa5, 0x84, 0xbe, 0xd5, 0xa4, 0x34, 0x9e, 0x43, 0x54,
|
||||
0x6f, 0x6a, 0x45, 0x79, 0xec, 0x4d, 0xbc, 0x69, 0x3f, 0x71, 0x19, 0x9e, 0x82, 0x5f, 0xe4, 0xb1,
|
||||
0xcf, 0x35, 0xbf, 0xc8, 0xc5, 0x7b, 0x08, 0xf9, 0x9d, 0x03, 0xbc, 0x1d, 0x80, 0x31, 0x9c, 0xac,
|
||||
0x2a, 0x4a, 0xb5, 0xac, 0x5c, 0xf7, 0x2e, 0x45, 0x84, 0xee, 0x5a, 0x2a, 0x1d, 0x07, 0x5c, 0xe6,
|
||||
0x58, 0xbc, 0x86, 0xd1, 0x82, 0xf4, 0xdb, 0xb2, 0xe4, 0x61, 0x2a, 0x21, 0xb5, 0x95, 0x1b, 0x45,
|
||||
0x78, 0x09, 0x11, 0x33, 0x55, 0xb1, 0x37, 0x09, 0xa6, 0x83, 0xd9, 0x7f, 0xd7, 0x56, 0x85, 0xe5,
|
||||
0xea, 0x30, 0xf1, 0x05, 0xc2, 0xcf, 0xf2, 0x9e, 0x36, 0x2d, 0x12, 0x08, 0xdd, 0x4c, 0xe6, 0x3f,
|
||||
0x1c, 0x03, 0x8e, 0x8d, 0x32, 0xb5, 0x92, 0x5b, 0x52, 0x71, 0x30, 0x09, 0x8c, 0x32, 0x9b, 0x99,
|
||||
0x7a, 0xba, 0xd2, 0xc5, 0x77, 0x8a, 0xbb, 0x13, 0x6f, 0xda, 0x4b, 0x5c, 0x26, 0x9e, 0x43, 0x8f,
|
||||
0x87, 0x2f, 0x49, 0x1b, 0x3a, 0xda, 0xc4, 0xc7, 0x74, 0xb8, 0x21, 0x71, 0x98, 0x78, 0xc9, 0xeb,
|
||||
0xb4, 0x35, 0xb7, 0xce, 0x11, 0x84, 0x0c, 0x3a, 0x6e, 0x36, 0x69, 0x2d, 0xf3, 0xa7, 0x07, 0x27,
|
||||
0xef, 0xd2, 0xd5, 0x3d, 0x6d, 0xf2, 0x96, 0x94, 0x11, 0x84, 0x7c, 0x3a, 0xd7, 0x6e, 0x13, 0x23,
|
||||
0xb0, 0x56, 0x54, 0xed, 0x76, 0x69, 0x62, 0x23, 0x24, 0x97, 0x5f, 0xd3, 0x62, 0xc3, 0x42, 0xfa,
|
||||
0x89, 0xcb, 0xf0, 0x0c, 0x82, 0xed, 0xba, 0x88, 0xc3, 0x89, 0x37, 0xf5, 0x13, 0x13, 0x3e, 0x5c,
|
||||
0x22, 0xda, 0x5f, 0x82, 0x0f, 0xaf, 0xd2, 0xac, 0xa4, 0xf8, 0xc4, 0xae, 0xc1, 0x66, 0x22, 0x85,
|
||||
0x81, 0xa3, 0xf6, 0xb1, 0x50, 0x1a, 0xaf, 0xc0, 0xcf, 0x14, 0xd3, 0x1b, 0xcc, 0xce, 0xdd, 0x16,
|
||||
0x1c, 0xbe, 0xa4, 0x92, 0x56, 0x5a, 0x56, 0x89, 0x9f, 0x29, 0x7c, 0x0a, 0xbd, 0xcc, 0x96, 0x55,
|
||||
0xec, 0xf3, 0xce, 0x4e, 0x9b, 0xdd, 0xc9, 0x03, 0x2e, 0xde, 0xc0, 0xf0, 0x68, 0xc4, 0x81, 0x16,
|
||||
0xaf, 0xa1, 0x65, 0xa7, 0xdb, 0xdf, 0xeb, 0x16, 0x17, 0xd0, 0x77, 0xcf, 0x6f, 0xe7, 0xc7, 0xeb,
|
||||
0x9b, 0xfd, 0xf2, 0x20, 0xb2, 0xde, 0xc2, 0x67, 0x10, 0x2c, 0x48, 0xe3, 0x8e, 0xf5, 0x91, 0xf3,
|
||||
0xc7, 0x0d, 0x8b, 0x89, 0x0e, 0xde, 0x40, 0x64, 0xad, 0x89, 0xe0, 0x90, 0xbb, 0xa2, 0x1c, 0x5f,
|
||||
0xec, 0x5f, 0xb7, 0x5c, 0x2b, 0x3a, 0xf8, 0x3f, 0x04, 0x9f, 0x6a, 0x8d, 0x8d, 0x59, 0xad, 0xc9,
|
||||
0x97, 0x10, 0xcd, 0xa9, 0x24, 0x4d, 0x47, 0x7d, 0x07, 0xdf, 0x11, 0x9d, 0xd9, 0x6f, 0x0f, 0x22,
|
||||
0xf6, 0xd2, 0x63, 0xcc, 0x0f, 0x4d, 0x36, 0x6e, 0xb8, 0x51, 0x74, 0xf0, 0xc9, 0xa3, 0xcc, 0x87,
|
||||
0x87, 0x5d, 0x4b, 0xd2, 0x6d, 0xb6, 0x8c, 0xb4, 0xa6, 0xb5, 0xd9, 0xda, 0xbe, 0x06, 0x5b, 0x9c,
|
||||
0x02, 0xcc, 0x89, 0xff, 0x9c, 0xf4, 0xdf, 0x9d, 0xb3, 0x35, 0xf4, 0xdc, 0xb9, 0x14, 0xbe, 0x80,
|
||||
0x2e, 0xbb, 0xea, 0x2f, 0x4e, 0x1a, 0x63, 0xb3, 0x6e, 0x7a, 0x45, 0x07, 0xaf, 0xa0, 0xfb, 0xa1,
|
||||
0x28, 0x4b, 0x3c, 0x6b, 0xa2, 0xb7, 0xf3, 0xe6, 0x97, 0xb2, 0x88, 0xff, 0x95, 0x9b, 0x3f, 0x01,
|
||||
0x00, 0x00, 0xff, 0xff, 0x48, 0xf0, 0xaf, 0x88, 0x02, 0x05, 0x00, 0x00,
|
||||
// 579 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x6e, 0xd3, 0x40,
|
||||
0x10, 0x8d, 0xed, 0xd8, 0x49, 0x27, 0x12, 0x41, 0xa3, 0xa8, 0x32, 0x81, 0x83, 0xb5, 0x12, 0x52,
|
||||
0x4e, 0x16, 0x0a, 0x48, 0x85, 0x02, 0x87, 0x56, 0x81, 0x50, 0x81, 0x2a, 0xe4, 0x70, 0x82, 0x93,
|
||||
0x1d, 0x8f, 0x54, 0xab, 0x26, 0x0e, 0xde, 0x75, 0x05, 0x5f, 0xc0, 0x95, 0x2b, 0x9f, 0xc2, 0xdf,
|
||||
0x21, 0xcf, 0x6e, 0xda, 0x12, 0xc7, 0xa9, 0x38, 0x65, 0x66, 0xf6, 0xcd, 0xfa, 0xbd, 0xb7, 0x33,
|
||||
0x81, 0x41, 0x59, 0x54, 0x8a, 0xc2, 0x75, 0x59, 0xa8, 0x02, 0x87, 0xdf, 0x49, 0x52, 0x79, 0x15,
|
||||
0x56, 0x32, 0xe4, 0xb2, 0x70, 0xc1, 0x39, 0xcf, 0x72, 0xf1, 0x02, 0x86, 0x73, 0x52, 0x51, 0x5d,
|
||||
0x8a, 0xe8, 0x5b, 0x45, 0x52, 0xe1, 0x21, 0x78, 0xd5, 0xaa, 0x92, 0x94, 0xfa, 0x56, 0x60, 0x4d,
|
||||
0x0e, 0x22, 0x93, 0xe1, 0x3d, 0xb0, 0xb3, 0xd4, 0xb7, 0xb9, 0x66, 0x67, 0xa9, 0x78, 0x03, 0x2e,
|
||||
0xf7, 0x99, 0x03, 0x6b, 0x73, 0x80, 0x3e, 0xf4, 0x96, 0x25, 0xc5, 0xaa, 0x28, 0x0d, 0x7a, 0x93,
|
||||
0x22, 0x42, 0xf7, 0xa2, 0x90, 0xca, 0x77, 0xb8, 0xcc, 0xb1, 0x78, 0x0b, 0xa3, 0x39, 0xa9, 0x93,
|
||||
0x3c, 0xe7, 0xcb, 0x64, 0x44, 0x72, 0x5d, 0xac, 0x24, 0x61, 0x08, 0x1e, 0x33, 0x95, 0xbe, 0x15,
|
||||
0x38, 0x93, 0xc1, 0xf4, 0x30, 0xdc, 0x92, 0x10, 0x6a, 0xd6, 0x06, 0x25, 0xbe, 0x80, 0xfb, 0xa9,
|
||||
0xb8, 0xa4, 0x55, 0x83, 0x0e, 0x42, 0x37, 0x29, 0xd2, 0x1f, 0x86, 0x0b, 0xc7, 0xb5, 0x46, 0xb9,
|
||||
0x2c, 0xd6, 0x24, 0x7d, 0x27, 0x70, 0x6a, 0x8d, 0x3a, 0xab, 0xeb, 0xf1, 0x52, 0x65, 0x57, 0xe4,
|
||||
0x77, 0x03, 0x6b, 0xd2, 0x8f, 0x4c, 0x26, 0x8e, 0xa1, 0xcf, 0x97, 0x2f, 0x48, 0xd5, 0xc4, 0x54,
|
||||
0x1d, 0xb7, 0x13, 0x63, 0x68, 0x64, 0x50, 0xe2, 0x88, 0x2d, 0xd6, 0x35, 0x63, 0xf1, 0x08, 0x5c,
|
||||
0x3e, 0x34, 0x2c, 0x75, 0xd2, 0x30, 0xf8, 0xb7, 0x05, 0xbd, 0xd3, 0x78, 0x79, 0x49, 0xab, 0xb4,
|
||||
0x21, 0x6a, 0x04, 0x2e, 0x3f, 0xac, 0x81, 0xeb, 0xa4, 0x96, 0x5a, 0x49, 0x2a, 0x37, 0xfe, 0xd6,
|
||||
0x71, 0x2d, 0x29, 0x2d, 0xbe, 0xc6, 0xd9, 0x8a, 0x25, 0x1d, 0x44, 0x26, 0xc3, 0xfb, 0xe0, 0xac,
|
||||
0x2f, 0x32, 0xdf, 0x0d, 0xac, 0x89, 0x1d, 0xd5, 0xe1, 0xf5, 0xeb, 0x78, 0x37, 0xaf, 0xc3, 0xc3,
|
||||
0x20, 0xe3, 0x24, 0x27, 0xbf, 0xa7, 0x0d, 0xd1, 0x99, 0xa8, 0x60, 0x60, 0xa8, 0x7d, 0xc8, 0xa4,
|
||||
0xc2, 0x27, 0x60, 0x27, 0x92, 0xe9, 0x0d, 0xa6, 0x41, 0xc3, 0x0f, 0x83, 0x5c, 0x50, 0x4e, 0x4b,
|
||||
0x55, 0x94, 0x91, 0x9d, 0x48, 0x7c, 0x06, 0xfd, 0x44, 0x97, 0xa5, 0x6f, 0xb3, 0x8f, 0x7e, 0x5b,
|
||||
0x5f, 0x74, 0x8d, 0x14, 0xaf, 0x61, 0xb8, 0x75, 0xd9, 0x2d, 0x7d, 0xd6, 0x3f, 0xfa, 0x36, 0x5e,
|
||||
0xd8, 0x37, 0x5e, 0x88, 0x87, 0x70, 0x60, 0xda, 0xcf, 0x66, 0xdb, 0x96, 0x4e, 0x7f, 0xda, 0xe0,
|
||||
0xe9, 0x19, 0xc4, 0x13, 0x70, 0xe6, 0xa4, 0xb0, 0xa9, 0x64, 0x6b, 0x57, 0xc6, 0x2d, 0x43, 0x29,
|
||||
0x3a, 0x38, 0x07, 0x4f, 0x8f, 0x35, 0x8e, 0x1a, 0x98, 0xf3, 0x2c, 0x1f, 0x3f, 0xde, 0x75, 0x77,
|
||||
0x63, 0x0b, 0x44, 0x07, 0x8f, 0xc0, 0xf9, 0x58, 0x29, 0x6c, 0xf9, 0xd2, 0x1e, 0x06, 0xcf, 0xc1,
|
||||
0x9b, 0x51, 0x4e, 0x8a, 0x5a, 0x7b, 0x77, 0x32, 0x13, 0x9d, 0xe9, 0x1f, 0x1b, 0x3c, 0x9e, 0xd7,
|
||||
0xfd, 0x4e, 0xdc, 0x1e, 0xe9, 0x71, 0xcb, 0x16, 0x88, 0x0e, 0xbe, 0xbc, 0xc3, 0x89, 0x07, 0xbb,
|
||||
0x3b, 0x17, 0xa4, 0xf6, 0xa9, 0x67, 0xcc, 0x9e, 0xaf, 0xee, 0x53, 0xaf, 0x7b, 0x5b, 0xd4, 0xe3,
|
||||
0x2b, 0x80, 0x19, 0xf1, 0xde, 0xc7, 0xff, 0xdf, 0x3d, 0xfd, 0x65, 0x41, 0xdf, 0xcc, 0x98, 0xc4,
|
||||
0x77, 0xd0, 0xe5, 0xf5, 0xb8, 0x73, 0x25, 0xc6, 0x8f, 0xda, 0x10, 0x75, 0xbf, 0xe8, 0xe0, 0x31,
|
||||
0x74, 0xdf, 0x67, 0x79, 0x8e, 0xe3, 0x36, 0xdc, 0xd9, 0xac, 0x8d, 0xd2, 0x69, 0xef, 0xb3, 0xfe,
|
||||
0x7b, 0x48, 0x3c, 0xfe, 0x79, 0xfa, 0x37, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x50, 0x5f, 0x13, 0x1a,
|
||||
0x06, 0x00, 0x00,
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
syntax = "proto3";
|
||||
|
||||
// option go_package = "git.xeserv.us/xena/route/routerpc/routegrpc;routegrpc";
|
||||
|
||||
package route;
|
||||
package xeserv.us.route;
|
||||
option go_package = "proto";
|
||||
|
||||
// Nil represents nothing.
|
||||
message Nil {}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,55 @@
|
|||
// Package route is a higher level client for routed suitable to embed into
|
||||
// Go programs.
|
||||
package route
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"git.xeserv.us/xena/route/proto"
|
||||
)
|
||||
|
||||
// New creates a new instance of the routed client with a given token and service
|
||||
// URL.
|
||||
func New(routedURL, token string, underlying *http.Client) *Client {
|
||||
c := &Client{
|
||||
underlying: underlying,
|
||||
authToken: token,
|
||||
serverURL: routedURL,
|
||||
}
|
||||
|
||||
c.Backends = proto.NewBackendsProtobufClient(routedURL, c.hClient())
|
||||
c.Routes = proto.NewRoutesProtobufClient(routedURL, c.hClient())
|
||||
c.Tokens = proto.NewTokensProtobufClient(routedURL, c.hClient())
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Client is a higher level client for routed
|
||||
type Client struct {
|
||||
Backends proto.Backends
|
||||
Routes proto.Routes
|
||||
Tokens proto.Tokens
|
||||
|
||||
underlying *http.Client
|
||||
authToken string
|
||||
serverURL string
|
||||
}
|
||||
|
||||
// RoundTrip executes a HTTP request, adding authentication headers and then
|
||||
// executing it using the underlying http client.
|
||||
func (c *Client) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
ck := &http.Cookie{
|
||||
Name: "routed",
|
||||
Value: c.authToken,
|
||||
}
|
||||
|
||||
r.AddCookie(ck)
|
||||
|
||||
return c.underlying.Do(r)
|
||||
}
|
||||
|
||||
func (c *Client) hClient() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: c,
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,897 @@
|
|||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package jsonpb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
|
||||
pb "github.com/golang/protobuf/jsonpb/jsonpb_test_proto"
|
||||
proto3pb "github.com/golang/protobuf/proto/proto3_proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
anypb "github.com/golang/protobuf/ptypes/any"
|
||||
durpb "github.com/golang/protobuf/ptypes/duration"
|
||||
stpb "github.com/golang/protobuf/ptypes/struct"
|
||||
tspb "github.com/golang/protobuf/ptypes/timestamp"
|
||||
wpb "github.com/golang/protobuf/ptypes/wrappers"
|
||||
)
|
||||
|
||||
var (
|
||||
marshaler = Marshaler{}
|
||||
|
||||
marshalerAllOptions = Marshaler{
|
||||
Indent: " ",
|
||||
}
|
||||
|
||||
simpleObject = &pb.Simple{
|
||||
OInt32: proto.Int32(-32),
|
||||
OInt64: proto.Int64(-6400000000),
|
||||
OUint32: proto.Uint32(32),
|
||||
OUint64: proto.Uint64(6400000000),
|
||||
OSint32: proto.Int32(-13),
|
||||
OSint64: proto.Int64(-2600000000),
|
||||
OFloat: proto.Float32(3.14),
|
||||
ODouble: proto.Float64(6.02214179e23),
|
||||
OBool: proto.Bool(true),
|
||||
OString: proto.String("hello \"there\""),
|
||||
OBytes: []byte("beep boop"),
|
||||
}
|
||||
|
||||
simpleObjectJSON = `{` +
|
||||
`"oBool":true,` +
|
||||
`"oInt32":-32,` +
|
||||
`"oInt64":"-6400000000",` +
|
||||
`"oUint32":32,` +
|
||||
`"oUint64":"6400000000",` +
|
||||
`"oSint32":-13,` +
|
||||
`"oSint64":"-2600000000",` +
|
||||
`"oFloat":3.14,` +
|
||||
`"oDouble":6.02214179e+23,` +
|
||||
`"oString":"hello \"there\"",` +
|
||||
`"oBytes":"YmVlcCBib29w"` +
|
||||
`}`
|
||||
|
||||
simpleObjectPrettyJSON = `{
|
||||
"oBool": true,
|
||||
"oInt32": -32,
|
||||
"oInt64": "-6400000000",
|
||||
"oUint32": 32,
|
||||
"oUint64": "6400000000",
|
||||
"oSint32": -13,
|
||||
"oSint64": "-2600000000",
|
||||
"oFloat": 3.14,
|
||||
"oDouble": 6.02214179e+23,
|
||||
"oString": "hello \"there\"",
|
||||
"oBytes": "YmVlcCBib29w"
|
||||
}`
|
||||
|
||||
repeatsObject = &pb.Repeats{
|
||||
RBool: []bool{true, false, true},
|
||||
RInt32: []int32{-3, -4, -5},
|
||||
RInt64: []int64{-123456789, -987654321},
|
||||
RUint32: []uint32{1, 2, 3},
|
||||
RUint64: []uint64{6789012345, 3456789012},
|
||||
RSint32: []int32{-1, -2, -3},
|
||||
RSint64: []int64{-6789012345, -3456789012},
|
||||
RFloat: []float32{3.14, 6.28},
|
||||
RDouble: []float64{299792458 * 1e20, 6.62606957e-34},
|
||||
RString: []string{"happy", "days"},
|
||||
RBytes: [][]byte{[]byte("skittles"), []byte("m&m's")},
|
||||
}
|
||||
|
||||
repeatsObjectJSON = `{` +
|
||||
`"rBool":[true,false,true],` +
|
||||
`"rInt32":[-3,-4,-5],` +
|
||||
`"rInt64":["-123456789","-987654321"],` +
|
||||
`"rUint32":[1,2,3],` +
|
||||
`"rUint64":["6789012345","3456789012"],` +
|
||||
`"rSint32":[-1,-2,-3],` +
|
||||
`"rSint64":["-6789012345","-3456789012"],` +
|
||||
`"rFloat":[3.14,6.28],` +
|
||||
`"rDouble":[2.99792458e+28,6.62606957e-34],` +
|
||||
`"rString":["happy","days"],` +
|
||||
`"rBytes":["c2tpdHRsZXM=","bSZtJ3M="]` +
|
||||
`}`
|
||||
|
||||
repeatsObjectPrettyJSON = `{
|
||||
"rBool": [
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
"rInt32": [
|
||||
-3,
|
||||
-4,
|
||||
-5
|
||||
],
|
||||
"rInt64": [
|
||||
"-123456789",
|
||||
"-987654321"
|
||||
],
|
||||
"rUint32": [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"rUint64": [
|
||||
"6789012345",
|
||||
"3456789012"
|
||||
],
|
||||
"rSint32": [
|
||||
-1,
|
||||
-2,
|
||||
-3
|
||||
],
|
||||
"rSint64": [
|
||||
"-6789012345",
|
||||
"-3456789012"
|
||||
],
|
||||
"rFloat": [
|
||||
3.14,
|
||||
6.28
|
||||
],
|
||||
"rDouble": [
|
||||
2.99792458e+28,
|
||||
6.62606957e-34
|
||||
],
|
||||
"rString": [
|
||||
"happy",
|
||||
"days"
|
||||
],
|
||||
"rBytes": [
|
||||
"c2tpdHRsZXM=",
|
||||
"bSZtJ3M="
|
||||
]
|
||||
}`
|
||||
|
||||
innerSimple = &pb.Simple{OInt32: proto.Int32(-32)}
|
||||
innerSimple2 = &pb.Simple{OInt64: proto.Int64(25)}
|
||||
innerRepeats = &pb.Repeats{RString: []string{"roses", "red"}}
|
||||
innerRepeats2 = &pb.Repeats{RString: []string{"violets", "blue"}}
|
||||
complexObject = &pb.Widget{
|
||||
Color: pb.Widget_GREEN.Enum(),
|
||||
RColor: []pb.Widget_Color{pb.Widget_RED, pb.Widget_GREEN, pb.Widget_BLUE},
|
||||
Simple: innerSimple,
|
||||
RSimple: []*pb.Simple{innerSimple, innerSimple2},
|
||||
Repeats: innerRepeats,
|
||||
RRepeats: []*pb.Repeats{innerRepeats, innerRepeats2},
|
||||
}
|
||||
|
||||
complexObjectJSON = `{"color":"GREEN",` +
|
||||
`"rColor":["RED","GREEN","BLUE"],` +
|
||||
`"simple":{"oInt32":-32},` +
|
||||
`"rSimple":[{"oInt32":-32},{"oInt64":"25"}],` +
|
||||
`"repeats":{"rString":["roses","red"]},` +
|
||||
`"rRepeats":[{"rString":["roses","red"]},{"rString":["violets","blue"]}]` +
|
||||
`}`
|
||||
|
||||
complexObjectPrettyJSON = `{
|
||||
"color": "GREEN",
|
||||
"rColor": [
|
||||
"RED",
|
||||
"GREEN",
|
||||
"BLUE"
|
||||
],
|
||||
"simple": {
|
||||
"oInt32": -32
|
||||
},
|
||||
"rSimple": [
|
||||
{
|
||||
"oInt32": -32
|
||||
},
|
||||
{
|
||||
"oInt64": "25"
|
||||
}
|
||||
],
|
||||
"repeats": {
|
||||
"rString": [
|
||||
"roses",
|
||||
"red"
|
||||
]
|
||||
},
|
||||
"rRepeats": [
|
||||
{
|
||||
"rString": [
|
||||
"roses",
|
||||
"red"
|
||||
]
|
||||
},
|
||||
{
|
||||
"rString": [
|
||||
"violets",
|
||||
"blue"
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
colorPrettyJSON = `{
|
||||
"color": 2
|
||||
}`
|
||||
|
||||
colorListPrettyJSON = `{
|
||||
"color": 1000,
|
||||
"rColor": [
|
||||
"RED"
|
||||
]
|
||||
}`
|
||||
|
||||
nummyPrettyJSON = `{
|
||||
"nummy": {
|
||||
"1": 2,
|
||||
"3": 4
|
||||
}
|
||||
}`
|
||||
|
||||
objjyPrettyJSON = `{
|
||||
"objjy": {
|
||||
"1": {
|
||||
"dub": 1
|
||||
}
|
||||
}
|
||||
}`
|
||||
realNumber = &pb.Real{Value: proto.Float64(3.14159265359)}
|
||||
realNumberName = "Pi"
|
||||
complexNumber = &pb.Complex{Imaginary: proto.Float64(0.5772156649)}
|
||||
realNumberJSON = `{` +
|
||||
`"value":3.14159265359,` +
|
||||
`"[jsonpb.Complex.real_extension]":{"imaginary":0.5772156649},` +
|
||||
`"[jsonpb.name]":"Pi"` +
|
||||
`}`
|
||||
|
||||
anySimple = &pb.KnownTypes{
|
||||
An: &anypb.Any{
|
||||
TypeUrl: "something.example.com/jsonpb.Simple",
|
||||
Value: []byte{
|
||||
// &pb.Simple{OBool:true}
|
||||
1 << 3, 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
anySimpleJSON = `{"an":{"@type":"something.example.com/jsonpb.Simple","oBool":true}}`
|
||||
anySimplePrettyJSON = `{
|
||||
"an": {
|
||||
"@type": "something.example.com/jsonpb.Simple",
|
||||
"oBool": true
|
||||
}
|
||||
}`
|
||||
|
||||
anyWellKnown = &pb.KnownTypes{
|
||||
An: &anypb.Any{
|
||||
TypeUrl: "type.googleapis.com/google.protobuf.Duration",
|
||||
Value: []byte{
|
||||
// &durpb.Duration{Seconds: 1, Nanos: 212000000 }
|
||||
1 << 3, 1, // seconds
|
||||
2 << 3, 0x80, 0xba, 0x8b, 0x65, // nanos
|
||||
},
|
||||
},
|
||||
}
|
||||
anyWellKnownJSON = `{"an":{"@type":"type.googleapis.com/google.protobuf.Duration","value":"1.212s"}}`
|
||||
anyWellKnownPrettyJSON = `{
|
||||
"an": {
|
||||
"@type": "type.googleapis.com/google.protobuf.Duration",
|
||||
"value": "1.212s"
|
||||
}
|
||||
}`
|
||||
|
||||
nonFinites = &pb.NonFinites{
|
||||
FNan: proto.Float32(float32(math.NaN())),
|
||||
FPinf: proto.Float32(float32(math.Inf(1))),
|
||||
FNinf: proto.Float32(float32(math.Inf(-1))),
|
||||
DNan: proto.Float64(float64(math.NaN())),
|
||||
DPinf: proto.Float64(float64(math.Inf(1))),
|
||||
DNinf: proto.Float64(float64(math.Inf(-1))),
|
||||
}
|
||||
nonFinitesJSON = `{` +
|
||||
`"fNan":"NaN",` +
|
||||
`"fPinf":"Infinity",` +
|
||||
`"fNinf":"-Infinity",` +
|
||||
`"dNan":"NaN",` +
|
||||
`"dPinf":"Infinity",` +
|
||||
`"dNinf":"-Infinity"` +
|
||||
`}`
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := proto.SetExtension(realNumber, pb.E_Name, &realNumberName); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := proto.SetExtension(realNumber, pb.E_Complex_RealExtension, complexNumber); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
var marshalingTests = []struct {
|
||||
desc string
|
||||
marshaler Marshaler
|
||||
pb proto.Message
|
||||
json string
|
||||
}{
|
||||
{"simple flat object", marshaler, simpleObject, simpleObjectJSON},
|
||||
{"simple pretty object", marshalerAllOptions, simpleObject, simpleObjectPrettyJSON},
|
||||
{"non-finite floats fields object", marshaler, nonFinites, nonFinitesJSON},
|
||||
{"repeated fields flat object", marshaler, repeatsObject, repeatsObjectJSON},
|
||||
{"repeated fields pretty object", marshalerAllOptions, repeatsObject, repeatsObjectPrettyJSON},
|
||||
{"nested message/enum flat object", marshaler, complexObject, complexObjectJSON},
|
||||
{"nested message/enum pretty object", marshalerAllOptions, complexObject, complexObjectPrettyJSON},
|
||||
{"enum-string flat object", Marshaler{},
|
||||
&pb.Widget{Color: pb.Widget_BLUE.Enum()}, `{"color":"BLUE"}`},
|
||||
{"enum-value pretty object", Marshaler{EnumsAsInts: true, Indent: " "},
|
||||
&pb.Widget{Color: pb.Widget_BLUE.Enum()}, colorPrettyJSON},
|
||||
{"unknown enum value object", marshalerAllOptions,
|
||||
&pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}, colorListPrettyJSON},
|
||||
{"repeated proto3 enum", Marshaler{},
|
||||
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{
|
||||
proto3pb.Message_PUNS,
|
||||
proto3pb.Message_SLAPSTICK,
|
||||
}},
|
||||
`{"rFunny":["PUNS","SLAPSTICK"]}`},
|
||||
{"repeated proto3 enum as int", Marshaler{EnumsAsInts: true},
|
||||
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{
|
||||
proto3pb.Message_PUNS,
|
||||
proto3pb.Message_SLAPSTICK,
|
||||
}},
|
||||
`{"rFunny":[1,2]}`},
|
||||
{"empty value", marshaler, &pb.Simple3{}, `{}`},
|
||||
{"empty value emitted", Marshaler{EmitDefaults: true}, &pb.Simple3{}, `{"dub":0}`},
|
||||
{"empty repeated emitted", Marshaler{EmitDefaults: true}, &pb.SimpleSlice3{}, `{"slices":[]}`},
|
||||
{"empty map emitted", Marshaler{EmitDefaults: true}, &pb.SimpleMap3{}, `{"stringy":{}}`},
|
||||
{"nested struct null", Marshaler{EmitDefaults: true}, &pb.SimpleNull3{}, `{"simple":null}`},
|
||||
{"map<int64, int32>", marshaler, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, `{"nummy":{"1":2,"3":4}}`},
|
||||
{"map<int64, int32>", marshalerAllOptions, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}, nummyPrettyJSON},
|
||||
{"map<string, string>", marshaler,
|
||||
&pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}},
|
||||
`{"strry":{"\"one\"":"two","three":"four"}}`},
|
||||
{"map<int32, Object>", marshaler,
|
||||
&pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}, `{"objjy":{"1":{"dub":1}}}`},
|
||||
{"map<int32, Object>", marshalerAllOptions,
|
||||
&pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}, objjyPrettyJSON},
|
||||
{"map<int64, string>", marshaler, &pb.Mappy{Buggy: map[int64]string{1234: "yup"}},
|
||||
`{"buggy":{"1234":"yup"}}`},
|
||||
{"map<bool, bool>", marshaler, &pb.Mappy{Booly: map[bool]bool{false: true}}, `{"booly":{"false":true}}`},
|
||||
// TODO: This is broken.
|
||||
//{"map<string, enum>", marshaler, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}, `{"enumy":{"XIV":"ROMAN"}`},
|
||||
{"map<string, enum as int>", Marshaler{EnumsAsInts: true}, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}, `{"enumy":{"XIV":2}}`},
|
||||
{"map<int32, bool>", marshaler, &pb.Mappy{S32Booly: map[int32]bool{1: true, 3: false, 10: true, 12: false}}, `{"s32booly":{"1":true,"3":false,"10":true,"12":false}}`},
|
||||
{"map<int64, bool>", marshaler, &pb.Mappy{S64Booly: map[int64]bool{1: true, 3: false, 10: true, 12: false}}, `{"s64booly":{"1":true,"3":false,"10":true,"12":false}}`},
|
||||
{"map<uint32, bool>", marshaler, &pb.Mappy{U32Booly: map[uint32]bool{1: true, 3: false, 10: true, 12: false}}, `{"u32booly":{"1":true,"3":false,"10":true,"12":false}}`},
|
||||
{"map<uint64, bool>", marshaler, &pb.Mappy{U64Booly: map[uint64]bool{1: true, 3: false, 10: true, 12: false}}, `{"u64booly":{"1":true,"3":false,"10":true,"12":false}}`},
|
||||
{"proto2 map<int64, string>", marshaler, &pb.Maps{MInt64Str: map[int64]string{213: "cat"}},
|
||||
`{"mInt64Str":{"213":"cat"}}`},
|
||||
{"proto2 map<bool, Object>", marshaler,
|
||||
&pb.Maps{MBoolSimple: map[bool]*pb.Simple{true: {OInt32: proto.Int32(1)}}},
|
||||
`{"mBoolSimple":{"true":{"oInt32":1}}}`},
|
||||
{"oneof, not set", marshaler, &pb.MsgWithOneof{}, `{}`},
|
||||
{"oneof, set", marshaler, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Title{"Grand Poobah"}}, `{"title":"Grand Poobah"}`},
|
||||
{"force orig_name", Marshaler{OrigName: true}, &pb.Simple{OInt32: proto.Int32(4)},
|
||||
`{"o_int32":4}`},
|
||||
{"proto2 extension", marshaler, realNumber, realNumberJSON},
|
||||
{"Any with message", marshaler, anySimple, anySimpleJSON},
|
||||
{"Any with message and indent", marshalerAllOptions, anySimple, anySimplePrettyJSON},
|
||||
{"Any with WKT", marshaler, anyWellKnown, anyWellKnownJSON},
|
||||
{"Any with WKT and indent", marshalerAllOptions, anyWellKnown, anyWellKnownPrettyJSON},
|
||||
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}, `{"dur":"3.000s"}`},
|
||||
{"Duration", marshaler, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 100000000, Nanos: 1}}, `{"dur":"100000000.000000001s"}`},
|
||||
{"Struct", marshaler, &pb.KnownTypes{St: &stpb.Struct{
|
||||
Fields: map[string]*stpb.Value{
|
||||
"one": {Kind: &stpb.Value_StringValue{"loneliest number"}},
|
||||
"two": {Kind: &stpb.Value_NullValue{stpb.NullValue_NULL_VALUE}},
|
||||
},
|
||||
}}, `{"st":{"one":"loneliest number","two":null}}`},
|
||||
{"empty ListValue", marshaler, &pb.KnownTypes{Lv: &stpb.ListValue{}}, `{"lv":[]}`},
|
||||
{"basic ListValue", marshaler, &pb.KnownTypes{Lv: &stpb.ListValue{Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_StringValue{"x"}},
|
||||
{Kind: &stpb.Value_NullValue{}},
|
||||
{Kind: &stpb.Value_NumberValue{3}},
|
||||
{Kind: &stpb.Value_BoolValue{true}},
|
||||
}}}, `{"lv":["x",null,3,true]}`},
|
||||
{"Timestamp", marshaler, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}, `{"ts":"2014-05-13T16:53:20.021Z"}`},
|
||||
{"number Value", marshaler, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NumberValue{1}}}, `{"val":1}`},
|
||||
{"null Value", marshaler, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NullValue{stpb.NullValue_NULL_VALUE}}}, `{"val":null}`},
|
||||
{"string number value", marshaler, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_StringValue{"9223372036854775807"}}}, `{"val":"9223372036854775807"}`},
|
||||
{"list of lists Value", marshaler, &pb.KnownTypes{Val: &stpb.Value{
|
||||
Kind: &stpb.Value_ListValue{&stpb.ListValue{
|
||||
Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_StringValue{"x"}},
|
||||
{Kind: &stpb.Value_ListValue{&stpb.ListValue{
|
||||
Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_ListValue{&stpb.ListValue{
|
||||
Values: []*stpb.Value{{Kind: &stpb.Value_StringValue{"y"}}},
|
||||
}}},
|
||||
{Kind: &stpb.Value_StringValue{"z"}},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
}},
|
||||
}}, `{"val":["x",[["y"],"z"]]}`},
|
||||
|
||||
{"DoubleValue", marshaler, &pb.KnownTypes{Dbl: &wpb.DoubleValue{Value: 1.2}}, `{"dbl":1.2}`},
|
||||
{"FloatValue", marshaler, &pb.KnownTypes{Flt: &wpb.FloatValue{Value: 1.2}}, `{"flt":1.2}`},
|
||||
{"Int64Value", marshaler, &pb.KnownTypes{I64: &wpb.Int64Value{Value: -3}}, `{"i64":"-3"}`},
|
||||
{"UInt64Value", marshaler, &pb.KnownTypes{U64: &wpb.UInt64Value{Value: 3}}, `{"u64":"3"}`},
|
||||
{"Int32Value", marshaler, &pb.KnownTypes{I32: &wpb.Int32Value{Value: -4}}, `{"i32":-4}`},
|
||||
{"UInt32Value", marshaler, &pb.KnownTypes{U32: &wpb.UInt32Value{Value: 4}}, `{"u32":4}`},
|
||||
{"BoolValue", marshaler, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}, `{"bool":true}`},
|
||||
{"StringValue", marshaler, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}, `{"str":"plush"}`},
|
||||
{"BytesValue", marshaler, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}, `{"bytes":"d293"}`},
|
||||
}
|
||||
|
||||
func TestMarshaling(t *testing.T) {
|
||||
for _, tt := range marshalingTests {
|
||||
json, err := tt.marshaler.MarshalToString(tt.pb)
|
||||
if err != nil {
|
||||
t.Errorf("%s: marshaling error: %v", tt.desc, err)
|
||||
} else if tt.json != json {
|
||||
t.Errorf("%s: got [%v] want [%v]", tt.desc, json, tt.json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalJSONPBMarshaler(t *testing.T) {
|
||||
rawJson := `{ "foo": "bar", "baz": [0, 1, 2, 3] }`
|
||||
msg := dynamicMessage{rawJson: rawJson}
|
||||
str, err := new(Marshaler).MarshalToString(&msg)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshalling JSONPBMarshaler: %v", err)
|
||||
}
|
||||
if str != rawJson {
|
||||
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, rawJson)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalAnyJSONPBMarshaler(t *testing.T) {
|
||||
msg := dynamicMessage{rawJson: `{ "foo": "bar", "baz": [0, 1, 2, 3] }`}
|
||||
a, err := ptypes.MarshalAny(&msg)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshalling to Any: %v", err)
|
||||
}
|
||||
str, err := new(Marshaler).MarshalToString(a)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshalling Any to JSON: %v", err)
|
||||
}
|
||||
// after custom marshaling, it's round-tripped through JSON decoding/encoding already,
|
||||
// so the keys are sorted, whitespace is compacted, and "@type" key has been added
|
||||
expected := `{"@type":"type.googleapis.com/` + dynamicMessageName + `","baz":[0,1,2,3],"foo":"bar"}`
|
||||
if str != expected {
|
||||
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, expected)
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalingTests = []struct {
|
||||
desc string
|
||||
unmarshaler Unmarshaler
|
||||
json string
|
||||
pb proto.Message
|
||||
}{
|
||||
{"simple flat object", Unmarshaler{}, simpleObjectJSON, simpleObject},
|
||||
{"simple pretty object", Unmarshaler{}, simpleObjectPrettyJSON, simpleObject},
|
||||
{"repeated fields flat object", Unmarshaler{}, repeatsObjectJSON, repeatsObject},
|
||||
{"repeated fields pretty object", Unmarshaler{}, repeatsObjectPrettyJSON, repeatsObject},
|
||||
{"nested message/enum flat object", Unmarshaler{}, complexObjectJSON, complexObject},
|
||||
{"nested message/enum pretty object", Unmarshaler{}, complexObjectPrettyJSON, complexObject},
|
||||
{"enum-string object", Unmarshaler{}, `{"color":"BLUE"}`, &pb.Widget{Color: pb.Widget_BLUE.Enum()}},
|
||||
{"enum-value object", Unmarshaler{}, "{\n \"color\": 2\n}", &pb.Widget{Color: pb.Widget_BLUE.Enum()}},
|
||||
{"unknown field with allowed option", Unmarshaler{AllowUnknownFields: true}, `{"unknown": "foo"}`, new(pb.Simple)},
|
||||
{"proto3 enum string", Unmarshaler{}, `{"hilarity":"PUNS"}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
|
||||
{"proto3 enum value", Unmarshaler{}, `{"hilarity":1}`, &proto3pb.Message{Hilarity: proto3pb.Message_PUNS}},
|
||||
{"unknown enum value object",
|
||||
Unmarshaler{},
|
||||
"{\n \"color\": 1000,\n \"r_color\": [\n \"RED\"\n ]\n}",
|
||||
&pb.Widget{Color: pb.Widget_Color(1000).Enum(), RColor: []pb.Widget_Color{pb.Widget_RED}}},
|
||||
{"repeated proto3 enum", Unmarshaler{}, `{"rFunny":["PUNS","SLAPSTICK"]}`,
|
||||
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{
|
||||
proto3pb.Message_PUNS,
|
||||
proto3pb.Message_SLAPSTICK,
|
||||
}}},
|
||||
{"repeated proto3 enum as int", Unmarshaler{}, `{"rFunny":[1,2]}`,
|
||||
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{
|
||||
proto3pb.Message_PUNS,
|
||||
proto3pb.Message_SLAPSTICK,
|
||||
}}},
|
||||
{"repeated proto3 enum as mix of strings and ints", Unmarshaler{}, `{"rFunny":["PUNS",2]}`,
|
||||
&proto3pb.Message{RFunny: []proto3pb.Message_Humour{
|
||||
proto3pb.Message_PUNS,
|
||||
proto3pb.Message_SLAPSTICK,
|
||||
}}},
|
||||
{"unquoted int64 object", Unmarshaler{}, `{"oInt64":-314}`, &pb.Simple{OInt64: proto.Int64(-314)}},
|
||||
{"unquoted uint64 object", Unmarshaler{}, `{"oUint64":123}`, &pb.Simple{OUint64: proto.Uint64(123)}},
|
||||
{"NaN", Unmarshaler{}, `{"oDouble":"NaN"}`, &pb.Simple{ODouble: proto.Float64(math.NaN())}},
|
||||
{"Inf", Unmarshaler{}, `{"oFloat":"Infinity"}`, &pb.Simple{OFloat: proto.Float32(float32(math.Inf(1)))}},
|
||||
{"-Inf", Unmarshaler{}, `{"oDouble":"-Infinity"}`, &pb.Simple{ODouble: proto.Float64(math.Inf(-1))}},
|
||||
{"map<int64, int32>", Unmarshaler{}, `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}},
|
||||
{"map<string, string>", Unmarshaler{}, `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}},
|
||||
{"map<int32, Object>", Unmarshaler{}, `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}},
|
||||
{"proto2 extension", Unmarshaler{}, realNumberJSON, realNumber},
|
||||
{"Any with message", Unmarshaler{}, anySimpleJSON, anySimple},
|
||||
{"Any with message and indent", Unmarshaler{}, anySimplePrettyJSON, anySimple},
|
||||
{"Any with WKT", Unmarshaler{}, anyWellKnownJSON, anyWellKnown},
|
||||
{"Any with WKT and indent", Unmarshaler{}, anyWellKnownPrettyJSON, anyWellKnown},
|
||||
// TODO: This is broken.
|
||||
//{"map<string, enum>", Unmarshaler{}, `{"enumy":{"XIV":"ROMAN"}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}},
|
||||
{"map<string, enum as int>", Unmarshaler{}, `{"enumy":{"XIV":2}}`, &pb.Mappy{Enumy: map[string]pb.Numeral{"XIV": pb.Numeral_ROMAN}}},
|
||||
{"oneof", Unmarshaler{}, `{"salary":31000}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Salary{31000}}},
|
||||
{"oneof spec name", Unmarshaler{}, `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}},
|
||||
{"oneof orig_name", Unmarshaler{}, `{"Country":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Country{"Australia"}}},
|
||||
{"oneof spec name2", Unmarshaler{}, `{"homeAddress":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_HomeAddress{"Australia"}}},
|
||||
{"oneof orig_name2", Unmarshaler{}, `{"home_address":"Australia"}`, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_HomeAddress{"Australia"}}},
|
||||
{"orig_name input", Unmarshaler{}, `{"o_bool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
|
||||
{"camelName input", Unmarshaler{}, `{"oBool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
|
||||
|
||||
{"Duration", Unmarshaler{}, `{"dur":"3.000s"}`, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}},
|
||||
{"null Duration", Unmarshaler{}, `{"dur":null}`, &pb.KnownTypes{Dur: nil}},
|
||||
{"Timestamp", Unmarshaler{}, `{"ts":"2014-05-13T16:53:20.021Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}},
|
||||
{"PreEpochTimestamp", Unmarshaler{}, `{"ts":"1969-12-31T23:59:58.999999995Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: -2, Nanos: 999999995}}},
|
||||
{"ZeroTimeTimestamp", Unmarshaler{}, `{"ts":"0001-01-01T00:00:00Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: -62135596800, Nanos: 0}}},
|
||||
{"null Timestamp", Unmarshaler{}, `{"ts":null}`, &pb.KnownTypes{Ts: nil}},
|
||||
{"null Struct", Unmarshaler{}, `{"st": null}`, &pb.KnownTypes{St: nil}},
|
||||
{"empty Struct", Unmarshaler{}, `{"st": {}}`, &pb.KnownTypes{St: &stpb.Struct{}}},
|
||||
{"basic Struct", Unmarshaler{}, `{"st": {"a": "x", "b": null, "c": 3, "d": true}}`, &pb.KnownTypes{St: &stpb.Struct{Fields: map[string]*stpb.Value{
|
||||
"a": {Kind: &stpb.Value_StringValue{"x"}},
|
||||
"b": {Kind: &stpb.Value_NullValue{}},
|
||||
"c": {Kind: &stpb.Value_NumberValue{3}},
|
||||
"d": {Kind: &stpb.Value_BoolValue{true}},
|
||||
}}}},
|
||||
{"nested Struct", Unmarshaler{}, `{"st": {"a": {"b": 1, "c": [{"d": true}, "f"]}}}`, &pb.KnownTypes{St: &stpb.Struct{Fields: map[string]*stpb.Value{
|
||||
"a": {Kind: &stpb.Value_StructValue{&stpb.Struct{Fields: map[string]*stpb.Value{
|
||||
"b": {Kind: &stpb.Value_NumberValue{1}},
|
||||
"c": {Kind: &stpb.Value_ListValue{&stpb.ListValue{Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_StructValue{&stpb.Struct{Fields: map[string]*stpb.Value{"d": {Kind: &stpb.Value_BoolValue{true}}}}}},
|
||||
{Kind: &stpb.Value_StringValue{"f"}},
|
||||
}}}},
|
||||
}}}},
|
||||
}}}},
|
||||
{"null ListValue", Unmarshaler{}, `{"lv": null}`, &pb.KnownTypes{Lv: nil}},
|
||||
{"empty ListValue", Unmarshaler{}, `{"lv": []}`, &pb.KnownTypes{Lv: &stpb.ListValue{}}},
|
||||
{"basic ListValue", Unmarshaler{}, `{"lv": ["x", null, 3, true]}`, &pb.KnownTypes{Lv: &stpb.ListValue{Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_StringValue{"x"}},
|
||||
{Kind: &stpb.Value_NullValue{}},
|
||||
{Kind: &stpb.Value_NumberValue{3}},
|
||||
{Kind: &stpb.Value_BoolValue{true}},
|
||||
}}}},
|
||||
{"number Value", Unmarshaler{}, `{"val":1}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NumberValue{1}}}},
|
||||
{"null Value", Unmarshaler{}, `{"val":null}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_NullValue{stpb.NullValue_NULL_VALUE}}}},
|
||||
{"bool Value", Unmarshaler{}, `{"val":true}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_BoolValue{true}}}},
|
||||
{"string Value", Unmarshaler{}, `{"val":"x"}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_StringValue{"x"}}}},
|
||||
{"string number value", Unmarshaler{}, `{"val":"9223372036854775807"}`, &pb.KnownTypes{Val: &stpb.Value{Kind: &stpb.Value_StringValue{"9223372036854775807"}}}},
|
||||
{"list of lists Value", Unmarshaler{}, `{"val":["x", [["y"], "z"]]}`, &pb.KnownTypes{Val: &stpb.Value{
|
||||
Kind: &stpb.Value_ListValue{&stpb.ListValue{
|
||||
Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_StringValue{"x"}},
|
||||
{Kind: &stpb.Value_ListValue{&stpb.ListValue{
|
||||
Values: []*stpb.Value{
|
||||
{Kind: &stpb.Value_ListValue{&stpb.ListValue{
|
||||
Values: []*stpb.Value{{Kind: &stpb.Value_StringValue{"y"}}},
|
||||
}}},
|
||||
{Kind: &stpb.Value_StringValue{"z"}},
|
||||
},
|
||||
}}},
|
||||
},
|
||||
}}}}},
|
||||
|
||||
{"DoubleValue", Unmarshaler{}, `{"dbl":1.2}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{Value: 1.2}}},
|
||||
{"FloatValue", Unmarshaler{}, `{"flt":1.2}`, &pb.KnownTypes{Flt: &wpb.FloatValue{Value: 1.2}}},
|
||||
{"Int64Value", Unmarshaler{}, `{"i64":"-3"}`, &pb.KnownTypes{I64: &wpb.Int64Value{Value: -3}}},
|
||||
{"UInt64Value", Unmarshaler{}, `{"u64":"3"}`, &pb.KnownTypes{U64: &wpb.UInt64Value{Value: 3}}},
|
||||
{"Int32Value", Unmarshaler{}, `{"i32":-4}`, &pb.KnownTypes{I32: &wpb.Int32Value{Value: -4}}},
|
||||
{"UInt32Value", Unmarshaler{}, `{"u32":4}`, &pb.KnownTypes{U32: &wpb.UInt32Value{Value: 4}}},
|
||||
{"BoolValue", Unmarshaler{}, `{"bool":true}`, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}},
|
||||
{"StringValue", Unmarshaler{}, `{"str":"plush"}`, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}},
|
||||
{"BytesValue", Unmarshaler{}, `{"bytes":"d293"}`, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}},
|
||||
|
||||
// Ensure that `null` as a value ends up with a nil pointer instead of a [type]Value struct.
|
||||
{"null DoubleValue", Unmarshaler{}, `{"dbl":null}`, &pb.KnownTypes{Dbl: nil}},
|
||||
{"null FloatValue", Unmarshaler{}, `{"flt":null}`, &pb.KnownTypes{Flt: nil}},
|
||||
{"null Int64Value", Unmarshaler{}, `{"i64":null}`, &pb.KnownTypes{I64: nil}},
|
||||
{"null UInt64Value", Unmarshaler{}, `{"u64":null}`, &pb.KnownTypes{U64: nil}},
|
||||
{"null Int32Value", Unmarshaler{}, `{"i32":null}`, &pb.KnownTypes{I32: nil}},
|
||||
{"null UInt32Value", Unmarshaler{}, `{"u32":null}`, &pb.KnownTypes{U32: nil}},
|
||||
{"null BoolValue", Unmarshaler{}, `{"bool":null}`, &pb.KnownTypes{Bool: nil}},
|
||||
{"null StringValue", Unmarshaler{}, `{"str":null}`, &pb.KnownTypes{Str: nil}},
|
||||
{"null BytesValue", Unmarshaler{}, `{"bytes":null}`, &pb.KnownTypes{Bytes: nil}},
|
||||
}
|
||||
|
||||
func TestUnmarshaling(t *testing.T) {
|
||||
for _, tt := range unmarshalingTests {
|
||||
// Make a new instance of the type of our expected object.
|
||||
p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message)
|
||||
|
||||
err := tt.unmarshaler.Unmarshal(strings.NewReader(tt.json), p)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tt.desc, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// For easier diffs, compare text strings of the protos.
|
||||
exp := proto.MarshalTextString(tt.pb)
|
||||
act := proto.MarshalTextString(p)
|
||||
if string(exp) != string(act) {
|
||||
t.Errorf("%s: got [%s] want [%s]", tt.desc, act, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalNullArray(t *testing.T) {
|
||||
var repeats pb.Repeats
|
||||
if err := UnmarshalString(`{"rBool":null}`, &repeats); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(repeats, pb.Repeats{}) {
|
||||
t.Errorf("got non-nil fields in [%#v]", repeats)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalNullObject(t *testing.T) {
|
||||
var maps pb.Maps
|
||||
if err := UnmarshalString(`{"mInt64Str":null}`, &maps); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(maps, pb.Maps{}) {
|
||||
t.Errorf("got non-nil fields in [%#v]", maps)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalNext(t *testing.T) {
|
||||
// We only need to check against a few, not all of them.
|
||||
tests := unmarshalingTests[:5]
|
||||
|
||||
// Create a buffer with many concatenated JSON objects.
|
||||
var b bytes.Buffer
|
||||
for _, tt := range tests {
|
||||
b.WriteString(tt.json)
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(&b)
|
||||
for _, tt := range tests {
|
||||
// Make a new instance of the type of our expected object.
|
||||
p := reflect.New(reflect.TypeOf(tt.pb).Elem()).Interface().(proto.Message)
|
||||
|
||||
err := tt.unmarshaler.UnmarshalNext(dec, p)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", tt.desc, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// For easier diffs, compare text strings of the protos.
|
||||
exp := proto.MarshalTextString(tt.pb)
|
||||
act := proto.MarshalTextString(p)
|
||||
if string(exp) != string(act) {
|
||||
t.Errorf("%s: got [%s] want [%s]", tt.desc, act, exp)
|
||||
}
|
||||
}
|
||||
|
||||
p := &pb.Simple{}
|
||||
err := new(Unmarshaler).UnmarshalNext(dec, p)
|
||||
if err != io.EOF {
|
||||
t.Errorf("eof: got %v, expected io.EOF", err)
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalingShouldError = []struct {
|
||||
desc string
|
||||
in string
|
||||
pb proto.Message
|
||||
}{
|
||||
{"a value", "666", new(pb.Simple)},
|
||||
{"gibberish", "{adskja123;l23=-=", new(pb.Simple)},
|
||||
{"unknown field", `{"unknown": "foo"}`, new(pb.Simple)},
|
||||
{"unknown enum name", `{"hilarity":"DAVE"}`, new(proto3pb.Message)},
|
||||
}
|
||||
|
||||
func TestUnmarshalingBadInput(t *testing.T) {
|
||||
for _, tt := range unmarshalingShouldError {
|
||||
err := UnmarshalString(tt.in, tt.pb)
|
||||
if err == nil {
|
||||
t.Errorf("an error was expected when parsing %q instead of an object", tt.desc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type funcResolver func(turl string) (proto.Message, error)
|
||||
|
||||
func (fn funcResolver) Resolve(turl string) (proto.Message, error) {
|
||||
return fn(turl)
|
||||
}
|
||||
|
||||
func TestAnyWithCustomResolver(t *testing.T) {
|
||||
var resolvedTypeUrls []string
|
||||
resolver := funcResolver(func(turl string) (proto.Message, error) {
|
||||
resolvedTypeUrls = append(resolvedTypeUrls, turl)
|
||||
return new(pb.Simple), nil
|
||||
})
|
||||
msg := &pb.Simple{
|
||||
OBytes: []byte{1, 2, 3, 4},
|
||||
OBool: proto.Bool(true),
|
||||
OString: proto.String("foobar"),
|
||||
OInt64: proto.Int64(1020304),
|
||||
}
|
||||
msgBytes, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshaling message: %v", err)
|
||||
}
|
||||
// make an Any with a type URL that won't resolve w/out custom resolver
|
||||
any := &anypb.Any{
|
||||
TypeUrl: "https://foobar.com/some.random.MessageKind",
|
||||
Value: msgBytes,
|
||||
}
|
||||
|
||||
m := Marshaler{AnyResolver: resolver}
|
||||
js, err := m.MarshalToString(any)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshaling any to JSON: %v", err)
|
||||
}
|
||||
if len(resolvedTypeUrls) != 1 {
|
||||
t.Errorf("custom resolver was not invoked during marshaling")
|
||||
} else if resolvedTypeUrls[0] != "https://foobar.com/some.random.MessageKind" {
|
||||
t.Errorf("custom resolver was invoked with wrong URL: got %q, wanted %q", resolvedTypeUrls[0], "https://foobar.com/some.random.MessageKind")
|
||||
}
|
||||
wanted := `{"@type":"https://foobar.com/some.random.MessageKind","oBool":true,"oInt64":"1020304","oString":"foobar","oBytes":"AQIDBA=="}`
|
||||
if js != wanted {
|
||||
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", js, wanted)
|
||||
}
|
||||
|
||||
u := Unmarshaler{AnyResolver: resolver}
|
||||
roundTrip := &anypb.Any{}
|
||||
err = u.Unmarshal(bytes.NewReader([]byte(js)), roundTrip)
|
||||
if err != nil {
|
||||
t.Errorf("an unexpected error occurred when unmarshaling any from JSON: %v", err)
|
||||
}
|
||||
if len(resolvedTypeUrls) != 2 {
|
||||
t.Errorf("custom resolver was not invoked during marshaling")
|
||||
} else if resolvedTypeUrls[1] != "https://foobar.com/some.random.MessageKind" {
|
||||
t.Errorf("custom resolver was invoked with wrong URL: got %q, wanted %q", resolvedTypeUrls[1], "https://foobar.com/some.random.MessageKind")
|
||||
}
|
||||
if !proto.Equal(any, roundTrip) {
|
||||
t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", roundTrip, any)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSONPBUnmarshaler(t *testing.T) {
|
||||
rawJson := `{ "foo": "bar", "baz": [0, 1, 2, 3] }`
|
||||
var msg dynamicMessage
|
||||
if err := Unmarshal(strings.NewReader(rawJson), &msg); err != nil {
|
||||
t.Errorf("an unexpected error occurred when parsing into JSONPBUnmarshaler: %v", err)
|
||||
}
|
||||
if msg.rawJson != rawJson {
|
||||
t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", msg.rawJson, rawJson)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalNullWithJSONPBUnmarshaler(t *testing.T) {
|
||||
rawJson := `{"stringField":null}`
|
||||
var ptrFieldMsg ptrFieldMessage
|
||||
if err := Unmarshal(strings.NewReader(rawJson), &ptrFieldMsg); err != nil {
|
||||
t.Errorf("unmarshal error: %v", err)
|
||||
}
|
||||
|
||||
want := ptrFieldMessage{StringField: &stringField{IsSet: true, StringValue: "null"}}
|
||||
if !proto.Equal(&ptrFieldMsg, &want) {
|
||||
t.Errorf("unmarshal result StringField: got %v, want %v", ptrFieldMsg, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalAnyJSONPBUnmarshaler(t *testing.T) {
|
||||
rawJson := `{ "@type": "blah.com/` + dynamicMessageName + `", "foo": "bar", "baz": [0, 1, 2, 3] }`
|
||||
var got anypb.Any
|
||||
if err := Unmarshal(strings.NewReader(rawJson), &got); err != nil {
|
||||
t.Errorf("an unexpected error occurred when parsing into JSONPBUnmarshaler: %v", err)
|
||||
}
|
||||
|
||||
dm := &dynamicMessage{rawJson: `{"baz":[0,1,2,3],"foo":"bar"}`}
|
||||
var want anypb.Any
|
||||
if b, err := proto.Marshal(dm); err != nil {
|
||||
t.Errorf("an unexpected error occurred when marshaling message: %v", err)
|
||||
} else {
|
||||
want.TypeUrl = "blah.com/" + dynamicMessageName
|
||||
want.Value = b
|
||||
}
|
||||
|
||||
if !proto.Equal(&got, &want) {
|
||||
t.Errorf("message contents not set correctly after unmarshalling JSON: got %s, wanted %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
dynamicMessageName = "google.protobuf.jsonpb.testing.dynamicMessage"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// we register the custom type below so that we can use it in Any types
|
||||
proto.RegisterType((*dynamicMessage)(nil), dynamicMessageName)
|
||||
}
|
||||
|
||||
type ptrFieldMessage struct {
|
||||
StringField *stringField `protobuf:"bytes,1,opt,name=stringField"`
|
||||
}
|
||||
|
||||
func (m *ptrFieldMessage) Reset() {
|
||||
}
|
||||
|
||||
func (m *ptrFieldMessage) String() string {
|
||||
return m.StringField.StringValue
|
||||
}
|
||||
|
||||
func (m *ptrFieldMessage) ProtoMessage() {
|
||||
}
|
||||
|
||||
type stringField struct {
|
||||
IsSet bool `protobuf:"varint,1,opt,name=isSet"`
|
||||
StringValue string `protobuf:"bytes,2,opt,name=stringValue"`
|
||||
}
|
||||
|
||||
func (s *stringField) Reset() {
|
||||
}
|
||||
|
||||
func (s *stringField) String() string {
|
||||
return s.StringValue
|
||||
}
|
||||
|
||||
func (s *stringField) ProtoMessage() {
|
||||
}
|
||||
|
||||
func (s *stringField) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error {
|
||||
s.IsSet = true
|
||||
s.StringValue = string(js)
|
||||
return nil
|
||||
}
|
||||
|
||||
// dynamicMessage implements protobuf.Message but is not a normal generated message type.
|
||||
// It provides implementations of JSONPBMarshaler and JSONPBUnmarshaler for JSON support.
|
||||
type dynamicMessage struct {
|
||||
rawJson string `protobuf:"bytes,1,opt,name=rawJson"`
|
||||
}
|
||||
|
||||
func (m *dynamicMessage) Reset() {
|
||||
m.rawJson = "{}"
|
||||
}
|
||||
|
||||
func (m *dynamicMessage) String() string {
|
||||
return m.rawJson
|
||||
}
|
||||
|
||||
func (m *dynamicMessage) ProtoMessage() {
|
||||
}
|
||||
|
||||
func (m *dynamicMessage) MarshalJSONPB(jm *Marshaler) ([]byte, error) {
|
||||
return []byte(m.rawJson), nil
|
||||
}
|
||||
|
||||
func (m *dynamicMessage) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error {
|
||||
m.rawJson = string(js)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,380 @@
|
|||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: google/protobuf/struct.proto
|
||||
|
||||
/*
|
||||
Package structpb is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
google/protobuf/struct.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Struct
|
||||
Value
|
||||
ListValue
|
||||
*/
|
||||
package structpb
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// `NullValue` is a singleton enumeration to represent the null value for the
|
||||
// `Value` type union.
|
||||
//
|
||||
// The JSON representation for `NullValue` is JSON `null`.
|
||||
type NullValue int32
|
||||
|
||||
const (
|
||||
// Null value.
|
||||
NullValue_NULL_VALUE NullValue = 0
|
||||
)
|
||||
|
||||
var NullValue_name = map[int32]string{
|
||||
0: "NULL_VALUE",
|
||||
}
|
||||
var NullValue_value = map[string]int32{
|
||||
"NULL_VALUE": 0,
|
||||
}
|
||||
|
||||
func (x NullValue) String() string {
|
||||
return proto.EnumName(NullValue_name, int32(x))
|
||||
}
|
||||
func (NullValue) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (NullValue) XXX_WellKnownType() string { return "NullValue" }
|
||||
|
||||
// `Struct` represents a structured data value, consisting of fields
|
||||
// which map to dynamically typed values. In some languages, `Struct`
|
||||
// might be supported by a native representation. For example, in
|
||||
// scripting languages like JS a struct is represented as an
|
||||
// object. The details of that representation are described together
|
||||
// with the proto support for the language.
|
||||
//
|
||||
// The JSON representation for `Struct` is JSON object.
|
||||
type Struct struct {
|
||||
// Unordered map of dynamically typed values.
|
||||
Fields map[string]*Value `protobuf:"bytes,1,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||
}
|
||||
|
||||
func (m *Struct) Reset() { *m = Struct{} }
|
||||
func (m *Struct) String() string { return proto.CompactTextString(m) }
|
||||
func (*Struct) ProtoMessage() {}
|
||||
func (*Struct) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
func (*Struct) XXX_WellKnownType() string { return "Struct" }
|
||||
|
||||
func (m *Struct) GetFields() map[string]*Value {
|
||||
if m != nil {
|
||||
return m.Fields
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// `Value` represents a dynamically typed value which can be either
|
||||
// null, a number, a string, a boolean, a recursive struct value, or a
|
||||
// list of values. A producer of value is expected to set one of that
|
||||
// variants, absence of any variant indicates an error.
|
||||
//
|
||||
// The JSON representation for `Value` is JSON value.
|
||||
type Value struct {
|
||||
// The kind of value.
|
||||
//
|
||||
// Types that are valid to be assigned to Kind:
|
||||
// *Value_NullValue
|
||||
// *Value_NumberValue
|
||||
// *Value_StringValue
|
||||
// *Value_BoolValue
|
||||
// *Value_StructValue
|
||||
// *Value_ListValue
|
||||
Kind isValue_Kind `protobuf_oneof:"kind"`
|
||||
}
|
||||
|
||||
func (m *Value) Reset() { *m = Value{} }
|
||||
func (m *Value) String() string { return proto.CompactTextString(m) }
|
||||
func (*Value) ProtoMessage() {}
|
||||
func (*Value) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
func (*Value) XXX_WellKnownType() string { return "Value" }
|
||||
|
||||
type isValue_Kind interface {
|
||||
isValue_Kind()
|
||||
}
|
||||
|
||||
type Value_NullValue struct {
|
||||
NullValue NullValue `protobuf:"varint,1,opt,name=null_value,json=nullValue,enum=google.protobuf.NullValue,oneof"`
|
||||
}
|
||||
type Value_NumberValue struct {
|
||||
NumberValue float64 `protobuf:"fixed64,2,opt,name=number_value,json=numberValue,oneof"`
|
||||
}
|
||||
type Value_StringValue struct {
|
||||
StringValue string `protobuf:"bytes,3,opt,name=string_value,json=stringValue,oneof"`
|
||||
}
|
||||
type Value_BoolValue struct {
|
||||
BoolValue bool `protobuf:"varint,4,opt,name=bool_value,json=boolValue,oneof"`
|
||||
}
|
||||
type Value_StructValue struct {
|
||||
StructValue *Struct `protobuf:"bytes,5,opt,name=struct_value,json=structValue,oneof"`
|
||||
}
|
||||
type Value_ListValue struct {
|
||||
ListValue *ListValue `protobuf:"bytes,6,opt,name=list_value,json=listValue,oneof"`
|
||||
}
|
||||
|
||||
func (*Value_NullValue) isValue_Kind() {}
|
||||
func (*Value_NumberValue) isValue_Kind() {}
|
||||
func (*Value_StringValue) isValue_Kind() {}
|
||||
func (*Value_BoolValue) isValue_Kind() {}
|
||||
func (*Value_StructValue) isValue_Kind() {}
|
||||
func (*Value_ListValue) isValue_Kind() {}
|
||||
|
||||
func (m *Value) GetKind() isValue_Kind {
|
||||
if m != nil {
|
||||
return m.Kind
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Value) GetNullValue() NullValue {
|
||||
if x, ok := m.GetKind().(*Value_NullValue); ok {
|
||||
return x.NullValue
|
||||
}
|
||||
return NullValue_NULL_VALUE
|
||||
}
|
||||
|
||||
func (m *Value) GetNumberValue() float64 {
|
||||
if x, ok := m.GetKind().(*Value_NumberValue); ok {
|
||||
return x.NumberValue
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *Value) GetStringValue() string {
|
||||
if x, ok := m.GetKind().(*Value_StringValue); ok {
|
||||
return x.StringValue
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Value) GetBoolValue() bool {
|
||||
if x, ok := m.GetKind().(*Value_BoolValue); ok {
|
||||
return x.BoolValue
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *Value) GetStructValue() *Struct {
|
||||
if x, ok := m.GetKind().(*Value_StructValue); ok {
|
||||
return x.StructValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Value) GetListValue() *ListValue {
|
||||
if x, ok := m.GetKind().(*Value_ListValue); ok {
|
||||
return x.ListValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||
func (*Value) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||
return _Value_OneofMarshaler, _Value_OneofUnmarshaler, _Value_OneofSizer, []interface{}{
|
||||
(*Value_NullValue)(nil),
|
||||
(*Value_NumberValue)(nil),
|
||||
(*Value_StringValue)(nil),
|
||||
(*Value_BoolValue)(nil),
|
||||
(*Value_StructValue)(nil),
|
||||
(*Value_ListValue)(nil),
|
||||
}
|
||||
}
|
||||
|
||||
func _Value_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||
m := msg.(*Value)
|
||||
// kind
|
||||
switch x := m.Kind.(type) {
|
||||
case *Value_NullValue:
|
||||
b.EncodeVarint(1<<3 | proto.WireVarint)
|
||||
b.EncodeVarint(uint64(x.NullValue))
|
||||
case *Value_NumberValue:
|
||||
b.EncodeVarint(2<<3 | proto.WireFixed64)
|
||||
b.EncodeFixed64(math.Float64bits(x.NumberValue))
|
||||
case *Value_StringValue:
|
||||
b.EncodeVarint(3<<3 | proto.WireBytes)
|
||||
b.EncodeStringBytes(x.StringValue)
|
||||
case *Value_BoolValue:
|
||||
t := uint64(0)
|
||||
if x.BoolValue {
|
||||
t = 1
|
||||
}
|
||||
b.EncodeVarint(4<<3 | proto.WireVarint)
|
||||
b.EncodeVarint(t)
|
||||
case *Value_StructValue:
|
||||
b.EncodeVarint(5<<3 | proto.WireBytes)
|
||||
if err := b.EncodeMessage(x.StructValue); err != nil {
|
||||
return err
|
||||
}
|
||||
case *Value_ListValue:
|
||||
b.EncodeVarint(6<<3 | proto.WireBytes)
|
||||
if err := b.EncodeMessage(x.ListValue); err != nil {
|
||||
return err
|
||||
}
|
||||
case nil:
|
||||
default:
|
||||
return fmt.Errorf("Value.Kind has unexpected type %T", x)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _Value_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||
m := msg.(*Value)
|
||||
switch tag {
|
||||
case 1: // kind.null_value
|
||||
if wire != proto.WireVarint {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeVarint()
|
||||
m.Kind = &Value_NullValue{NullValue(x)}
|
||||
return true, err
|
||||
case 2: // kind.number_value
|
||||
if wire != proto.WireFixed64 {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeFixed64()
|
||||
m.Kind = &Value_NumberValue{math.Float64frombits(x)}
|
||||
return true, err
|
||||
case 3: // kind.string_value
|
||||
if wire != proto.WireBytes {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeStringBytes()
|
||||
m.Kind = &Value_StringValue{x}
|
||||
return true, err
|
||||
case 4: // kind.bool_value
|
||||
if wire != proto.WireVarint {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
x, err := b.DecodeVarint()
|
||||
m.Kind = &Value_BoolValue{x != 0}
|
||||
return true, err
|
||||
case 5: // kind.struct_value
|
||||
if wire != proto.WireBytes {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
msg := new(Struct)
|
||||
err := b.DecodeMessage(msg)
|
||||
m.Kind = &Value_StructValue{msg}
|
||||
return true, err
|
||||
case 6: // kind.list_value
|
||||
if wire != proto.WireBytes {
|
||||
return true, proto.ErrInternalBadWireType
|
||||
}
|
||||
msg := new(ListValue)
|
||||
err := b.DecodeMessage(msg)
|
||||
m.Kind = &Value_ListValue{msg}
|
||||
return true, err
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func _Value_OneofSizer(msg proto.Message) (n int) {
|
||||
m := msg.(*Value)
|
||||
// kind
|
||||
switch x := m.Kind.(type) {
|
||||
case *Value_NullValue:
|
||||
n += proto.SizeVarint(1<<3 | proto.WireVarint)
|
||||
n += proto.SizeVarint(uint64(x.NullValue))
|
||||
case *Value_NumberValue:
|
||||
n += proto.SizeVarint(2<<3 | proto.WireFixed64)
|
||||
n += 8
|
||||
case *Value_StringValue:
|
||||
n += proto.SizeVarint(3<<3 | proto.WireBytes)
|
||||
n += proto.SizeVarint(uint64(len(x.StringValue)))
|
||||
n += len(x.StringValue)
|
||||
case *Value_BoolValue:
|
||||
n += proto.SizeVarint(4<<3 | proto.WireVarint)
|
||||
n += 1
|
||||
case *Value_StructValue:
|
||||
s := proto.Size(x.StructValue)
|
||||
n += proto.SizeVarint(5<<3 | proto.WireBytes)
|
||||
n += proto.SizeVarint(uint64(s))
|
||||
n += s
|
||||
case *Value_ListValue:
|
||||
s := proto.Size(x.ListValue)
|
||||
n += proto.SizeVarint(6<<3 | proto.WireBytes)
|
||||
n += proto.SizeVarint(uint64(s))
|
||||
n += s
|
||||
case nil:
|
||||
default:
|
||||
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// `ListValue` is a wrapper around a repeated field of values.
|
||||
//
|
||||
// The JSON representation for `ListValue` is JSON array.
|
||||
type ListValue struct {
|
||||
// Repeated field of dynamically typed values.
|
||||
Values []*Value `protobuf:"bytes,1,rep,name=values" json:"values,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ListValue) Reset() { *m = ListValue{} }
|
||||
func (m *ListValue) String() string { return proto.CompactTextString(m) }
|
||||
func (*ListValue) ProtoMessage() {}
|
||||
func (*ListValue) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
func (*ListValue) XXX_WellKnownType() string { return "ListValue" }
|
||||
|
||||
func (m *ListValue) GetValues() []*Value {
|
||||
if m != nil {
|
||||
return m.Values
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Struct)(nil), "google.protobuf.Struct")
|
||||
proto.RegisterType((*Value)(nil), "google.protobuf.Value")
|
||||
proto.RegisterType((*ListValue)(nil), "google.protobuf.ListValue")
|
||||
proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value)
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 417 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8b, 0xd3, 0x40,
|
||||
0x14, 0xc7, 0x3b, 0xc9, 0x36, 0x98, 0x17, 0x59, 0x97, 0x11, 0xb4, 0xac, 0xa2, 0xa1, 0x7b, 0x09,
|
||||
0x22, 0x29, 0xd6, 0x8b, 0x18, 0x2f, 0x06, 0xd6, 0x5d, 0x30, 0x2c, 0x31, 0xba, 0x15, 0xbc, 0x94,
|
||||
0x26, 0x4d, 0x63, 0xe8, 0x74, 0x26, 0x24, 0x33, 0x4a, 0x8f, 0x7e, 0x0b, 0xcf, 0x1e, 0x3d, 0xfa,
|
||||
0xe9, 0x3c, 0xca, 0xcc, 0x24, 0xa9, 0xb4, 0xf4, 0x94, 0xbc, 0xf7, 0x7e, 0xef, 0x3f, 0xef, 0xff,
|
||||
0x66, 0xe0, 0x71, 0xc1, 0x58, 0x41, 0xf2, 0x49, 0x55, 0x33, 0xce, 0x52, 0xb1, 0x9a, 0x34, 0xbc,
|
||||
0x16, 0x19, 0xf7, 0x55, 0x8c, 0xef, 0xe9, 0xaa, 0xdf, 0x55, 0xc7, 0x3f, 0x11, 0x58, 0x1f, 0x15,
|
||||
0x81, 0x03, 0xb0, 0x56, 0x65, 0x4e, 0x96, 0xcd, 0x08, 0xb9, 0xa6, 0xe7, 0x4c, 0x2f, 0xfc, 0x3d,
|
||||
0xd8, 0xd7, 0xa0, 0xff, 0x4e, 0x51, 0x97, 0x94, 0xd7, 0xdb, 0xa4, 0x6d, 0x39, 0xff, 0x00, 0xce,
|
||||
0x7f, 0x69, 0x7c, 0x06, 0xe6, 0x3a, 0xdf, 0x8e, 0x90, 0x8b, 0x3c, 0x3b, 0x91, 0xbf, 0xf8, 0x39,
|
||||
0x0c, 0xbf, 0x2d, 0x88, 0xc8, 0x47, 0x86, 0x8b, 0x3c, 0x67, 0xfa, 0xe0, 0x40, 0x7c, 0x26, 0xab,
|
||||
0x89, 0x86, 0x5e, 0x1b, 0xaf, 0xd0, 0xf8, 0x8f, 0x01, 0x43, 0x95, 0xc4, 0x01, 0x00, 0x15, 0x84,
|
||||
0xcc, 0xb5, 0x80, 0x14, 0x3d, 0x9d, 0x9e, 0x1f, 0x08, 0xdc, 0x08, 0x42, 0x14, 0x7f, 0x3d, 0x48,
|
||||
0x6c, 0xda, 0x05, 0xf8, 0x02, 0xee, 0x52, 0xb1, 0x49, 0xf3, 0x7a, 0xbe, 0x3b, 0x1f, 0x5d, 0x0f,
|
||||
0x12, 0x47, 0x67, 0x7b, 0xa8, 0xe1, 0x75, 0x49, 0x8b, 0x16, 0x32, 0xe5, 0xe0, 0x12, 0xd2, 0x59,
|
||||
0x0d, 0x3d, 0x05, 0x48, 0x19, 0xeb, 0xc6, 0x38, 0x71, 0x91, 0x77, 0x47, 0x1e, 0x25, 0x73, 0x1a,
|
||||
0x78, 0xa3, 0x54, 0x44, 0xc6, 0x5b, 0x64, 0xa8, 0xac, 0x3e, 0x3c, 0xb2, 0xc7, 0x56, 0x5e, 0x64,
|
||||
0xbc, 0x77, 0x49, 0xca, 0xa6, 0xeb, 0xb5, 0x54, 0xef, 0xa1, 0xcb, 0xa8, 0x6c, 0x78, 0xef, 0x92,
|
||||
0x74, 0x41, 0x68, 0xc1, 0xc9, 0xba, 0xa4, 0xcb, 0x71, 0x00, 0x76, 0x4f, 0x60, 0x1f, 0x2c, 0x25,
|
||||
0xd6, 0xdd, 0xe8, 0xb1, 0xa5, 0xb7, 0xd4, 0xb3, 0x47, 0x60, 0xf7, 0x4b, 0xc4, 0xa7, 0x00, 0x37,
|
||||
0xb7, 0x51, 0x34, 0x9f, 0xbd, 0x8d, 0x6e, 0x2f, 0xcf, 0x06, 0xe1, 0x0f, 0x04, 0xf7, 0x33, 0xb6,
|
||||
0xd9, 0x97, 0x08, 0x1d, 0xed, 0x26, 0x96, 0x71, 0x8c, 0xbe, 0xbc, 0x28, 0x4a, 0xfe, 0x55, 0xa4,
|
||||
0x7e, 0xc6, 0x36, 0x93, 0x82, 0x91, 0x05, 0x2d, 0x76, 0x4f, 0xb1, 0xe2, 0xdb, 0x2a, 0x6f, 0xda,
|
||||
0x17, 0x19, 0xe8, 0x4f, 0x95, 0xfe, 0x45, 0xe8, 0x97, 0x61, 0x5e, 0xc5, 0xe1, 0x6f, 0xe3, 0xc9,
|
||||
0x95, 0x16, 0x8f, 0xbb, 0xf9, 0x3e, 0xe7, 0x84, 0xbc, 0xa7, 0xec, 0x3b, 0xfd, 0x24, 0x3b, 0x53,
|
||||
0x4b, 0x49, 0xbd, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x1b, 0x59, 0xf8, 0xe5, 0x02, 0x00,
|
||||
0x00,
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package google.protobuf;
|
||||
|
||||
option csharp_namespace = "Google.Protobuf.WellKnownTypes";
|
||||
option cc_enable_arenas = true;
|
||||
option go_package = "github.com/golang/protobuf/ptypes/struct;structpb";
|
||||
option java_package = "com.google.protobuf";
|
||||
option java_outer_classname = "StructProto";
|
||||
option java_multiple_files = true;
|
||||
option objc_class_prefix = "GPB";
|
||||
|
||||
|
||||
// `Struct` represents a structured data value, consisting of fields
|
||||
// which map to dynamically typed values. In some languages, `Struct`
|
||||
// might be supported by a native representation. For example, in
|
||||
// scripting languages like JS a struct is represented as an
|
||||
// object. The details of that representation are described together
|
||||
// with the proto support for the language.
|
||||
//
|
||||
// The JSON representation for `Struct` is JSON object.
|
||||
message Struct {
|
||||
// Unordered map of dynamically typed values.
|
||||
map<string, Value> fields = 1;
|
||||
}
|
||||
|
||||
// `Value` represents a dynamically typed value which can be either
|
||||
// null, a number, a string, a boolean, a recursive struct value, or a
|
||||
// list of values. A producer of value is expected to set one of that
|
||||
// variants, absence of any variant indicates an error.
|
||||
//
|
||||
// The JSON representation for `Value` is JSON value.
|
||||
message Value {
|
||||
// The kind of value.
|
||||
oneof kind {
|
||||
// Represents a null value.
|
||||
NullValue null_value = 1;
|
||||
// Represents a double value.
|
||||
double number_value = 2;
|
||||
// Represents a string value.
|
||||
string string_value = 3;
|
||||
// Represents a boolean value.
|
||||
bool bool_value = 4;
|
||||
// Represents a structured value.
|
||||
Struct struct_value = 5;
|
||||
// Represents a repeated `Value`.
|
||||
ListValue list_value = 6;
|
||||
}
|
||||
}
|
||||
|
||||
// `NullValue` is a singleton enumeration to represent the null value for the
|
||||
// `Value` type union.
|
||||
//
|
||||
// The JSON representation for `NullValue` is JSON `null`.
|
||||
enum NullValue {
|
||||
// Null value.
|
||||
NULL_VALUE = 0;
|
||||
}
|
||||
|
||||
// `ListValue` is a wrapper around a repeated field of values.
|
||||
//
|
||||
// The JSON representation for `ListValue` is JSON array.
|
||||
message ListValue {
|
||||
// Repeated field of dynamically typed values.
|
||||
repeated Value values = 1;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
*.test
|
||||
/bin
|
||||
|
||||
clientcompat/pycompat/ENV
|
||||
*.pyc
|
||||
|
||||
build
|
|
@ -0,0 +1,62 @@
|
|||
# Contributing #
|
||||
|
||||
Thanks for helping make Twirp better! This is great!
|
||||
|
||||
First, if you have run into a bug, please file an issue. We try to get back to
|
||||
issue reporters within a day or two. We might be able to help you right away.
|
||||
|
||||
If you'd rather not publicly discuss the issue, please email spencer@twitch.tv
|
||||
and/or security@twitch.tv.
|
||||
|
||||
Issues are also a good place to present experience reports or requests for new
|
||||
features.
|
||||
|
||||
If you'd like to make changes to Twirp, read on:
|
||||
|
||||
## Setup Requirements ##
|
||||
|
||||
You will need git, Go 1.9+, and Python 2.7 installed and on your system's path.
|
||||
Install them however you feel.
|
||||
|
||||
We work on a branch called `develop`. We periodically release this branch as a
|
||||
new version, then accumulate more changes on the develop branch until the next
|
||||
release. Use `develop` as the base for your branches.
|
||||
|
||||
## Developer Loop ##
|
||||
|
||||
Generally you want to make changes and run `make`, which will install all
|
||||
dependencies we know about, build the core, and run all of the tests that we
|
||||
have against all of the languages we support.
|
||||
|
||||
Most tests of the Go server are in `internal/twirptest/service_test.go`. Tests
|
||||
of cross-language clients are in the [clientcompat](./clientcompat) directory.
|
||||
|
||||
## Contributing Code ##
|
||||
|
||||
Twirp uses github pull requests. Fork a branch from `develop`, hack away at your
|
||||
changes, run the test suite with `make`, and submit a PR.
|
||||
|
||||
## Releasing Versions ##
|
||||
|
||||
Releasing versions is the responsibility of the core maintainers. Most people
|
||||
don't need to know this stuff.
|
||||
|
||||
Twirp uses [Semantic versioning](http://semver.org/): `v<major>.<minor>.<patch>`.
|
||||
|
||||
* Increment major if you're making a backwards-incompatible change.
|
||||
* Increment minor if you're adding a feature that's backwards-compatible.
|
||||
* Increment patch if you're making a bugfix.
|
||||
|
||||
To make a release, remember to update the version number in
|
||||
[internal/gen/version.go](./internal/gen/version.go).
|
||||
|
||||
Twirp uses Github releases. To make a new release:
|
||||
1. Merge all changes that should be included in the release into the master
|
||||
branch.
|
||||
2. Add a new commit to master with a message like "Version vX.X.X release".
|
||||
3. Tag the commit you just made: `git tag <version number>` and `git push
|
||||
origin --tags`
|
||||
3. Go to Github https://github.com/twitchtv/twirp/releases and
|
||||
"Draft a new release".
|
||||
4. Make sure to document changes, specially when upgrade instructions are
|
||||
needed.
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018 Twitch Interactive, Inc.
|
||||
|
||||
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.
|
|
@ -0,0 +1,51 @@
|
|||
RETOOL=$(CURDIR)/_tools/bin/retool
|
||||
PATH := ${PWD}/bin:${PWD}/ENV/bin:${PATH}
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
all: setup test_all
|
||||
|
||||
.PHONY: test test_all test_core test_clients test_go_client test_python_client generate
|
||||
|
||||
# Phony commands:
|
||||
generate:
|
||||
PATH=$(CURDIR)/_tools/bin:$(PATH) GOBIN="${PWD}/bin" go install -v ./protoc-gen-...
|
||||
$(RETOOL) do go generate ./...
|
||||
|
||||
test_all: setup test_core test_clients
|
||||
|
||||
test_core: generate
|
||||
$(RETOOL) do errcheck -blank ./internal/twirptest
|
||||
go test -race $(shell go list ./... | grep -v /vendor/ | grep -v /_tools/)
|
||||
|
||||
test_clients: test_go_client test_python_client
|
||||
|
||||
test_go_client: generate build/clientcompat build/gocompat
|
||||
./build/clientcompat -client ./build/gocompat
|
||||
|
||||
test_python_client: generate build/clientcompat build/pycompat
|
||||
./build/clientcompat -client ./build/pycompat
|
||||
|
||||
setup:
|
||||
./install_proto.bash
|
||||
GOPATH=$(CURDIR)/_tools go install github.com/twitchtv/retool/...
|
||||
$(RETOOL) build
|
||||
|
||||
# Actual files for testing clients:
|
||||
./build:
|
||||
mkdir build
|
||||
|
||||
./build/gocompat: ./build
|
||||
go build -o build/gocompat ./clientcompat/gocompat
|
||||
|
||||
./build/clientcompat: ./build
|
||||
go build -o build/clientcompat ./clientcompat
|
||||
|
||||
./build/venv: ./build
|
||||
virtualenv ./build/venv
|
||||
|
||||
./build/venv/bin/pycompat.py: ./build/venv
|
||||
./build/venv/bin/pip install --upgrade ./clientcompat/pycompat
|
||||
|
||||
./build/pycompat: ./build/venv/bin/pycompat.py
|
||||
cp ./clientcompat/pycompat/pycompat.sh ./build/pycompat
|
||||
chmod +x ./build/pycompat
|
|
@ -0,0 +1,34 @@
|
|||
![Twirp Logo](./logo.png)
|
||||
|
||||
---
|
||||
|
||||
Twirp is a framework for service-to-service communication emphasizing simplicity
|
||||
and minimalism. It generates routing and serialization from API definition files
|
||||
and lets you focus on your application's logic instead of thinking about
|
||||
folderol like HTTP methods and paths and JSON.
|
||||
|
||||
Define your service in a
|
||||
[Protobuf](https://developers.google.com/protocol-buffers/docs/proto3) file and
|
||||
then Twirp autogenerates Go code with a server interface and fully functional
|
||||
clients. It's similar to [gRPC](http://www.grpc.io/), but without the custom
|
||||
HTTP server and transport implementations: it runs on the standard library's
|
||||
extremely-well-tested-and-high-performance `net/http` Server. It can run on HTTP
|
||||
1.1, not just http/2, and supports JSON clients for easy integrations across
|
||||
languages
|
||||
|
||||
Twirp handles routing and serialization for you in a well-tested, standardized,
|
||||
thoughtful way so you don't have to. Serialization and deserialization code is
|
||||
error-prone and tricky, and you shouldn't be wasting your time deciding whether
|
||||
it should be "POST /friends/:id/new" or "POST /:id/friend" or whatever. Just
|
||||
get to the real work of building services!
|
||||
|
||||
Along the way, you get an autogenerated client and a simple, smart framework for
|
||||
passing error messages. Nice!
|
||||
|
||||
### Releases
|
||||
Twirp follows semantic versioning through git tags, and uses Github Releases for
|
||||
release notes and upgrade guides:
|
||||
[Twirp Releases](https://github.com/twitchtv/twirp/releases)
|
||||
|
||||
### Contributing
|
||||
Check out [CONTRIBUTING.md](./CONTRIBUTING.md) for notes on making contributions.
|
|
@ -0,0 +1,109 @@
|
|||
package twirp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/twitchtv/twirp/internal/contextkeys"
|
||||
)
|
||||
|
||||
// MethodName extracts the name of the method being handled in the given
|
||||
// context. If it is not known, it returns ("", false).
|
||||
func MethodName(ctx context.Context) (string, bool) {
|
||||
name, ok := ctx.Value(contextkeys.MethodNameKey).(string)
|
||||
return name, ok
|
||||
}
|
||||
|
||||
// ServiceName extracts the name of the service handling the given context. If
|
||||
// it is not known, it returns ("", false).
|
||||
func ServiceName(ctx context.Context) (string, bool) {
|
||||
name, ok := ctx.Value(contextkeys.ServiceNameKey).(string)
|
||||
return name, ok
|
||||
}
|
||||
|
||||
// PackageName extracts the fully-qualified protobuf package name of the service
|
||||
// handling the given context. If it is not known, it returns ("", false). If
|
||||
// the service comes from a proto file that does not declare a package name, it
|
||||
// returns ("", true).
|
||||
//
|
||||
// Note that the protobuf package name can be very different than the go package
|
||||
// name; the two are unrelated.
|
||||
func PackageName(ctx context.Context) (string, bool) {
|
||||
name, ok := ctx.Value(contextkeys.PackageNameKey).(string)
|
||||
return name, ok
|
||||
}
|
||||
|
||||
// StatusCode retrieves the status code of the response (as string like "200").
|
||||
// If it is known returns (status, true).
|
||||
// If it is not known, it returns ("", false).
|
||||
func StatusCode(ctx context.Context) (string, bool) {
|
||||
code, ok := ctx.Value(contextkeys.StatusCodeKey).(string)
|
||||
return code, ok
|
||||
}
|
||||
|
||||
// WithHTTPRequestHeaders stores an http.Header in a context.Context. When
|
||||
// using a Twirp-generated client, you can pass the returned context
|
||||
// into any of the request methods, and the stored header will be
|
||||
// included in outbound HTTP requests.
|
||||
//
|
||||
// This can be used to set custom HTTP headers like authorization tokens or
|
||||
// client IDs. But note that HTTP headers are a Twirp implementation detail,
|
||||
// only visible by middleware, not by the server implementtion.
|
||||
//
|
||||
// WithHTTPRequestHeaders returns an error if the provided http.Header
|
||||
// would overwrite a header that is needed by Twirp, like "Content-Type".
|
||||
func WithHTTPRequestHeaders(ctx context.Context, h http.Header) (context.Context, error) {
|
||||
if _, ok := h["Content-Type"]; ok {
|
||||
return nil, errors.New("provided header cannot set Content-Type")
|
||||
}
|
||||
if _, ok := h["Twirp-Version"]; ok {
|
||||
return nil, errors.New("provided header cannot set Twirp-Version")
|
||||
}
|
||||
|
||||
copied := make(http.Header, len(h))
|
||||
for k, vv := range h {
|
||||
if vv == nil {
|
||||
copied[k] = nil
|
||||
continue
|
||||
}
|
||||
copied[k] = make([]string, len(vv))
|
||||
copy(copied[k], vv)
|
||||
}
|
||||
|
||||
return context.WithValue(ctx, contextkeys.RequestHeaderKey, copied), nil
|
||||
}
|
||||
|
||||
func HTTPRequestHeaders(ctx context.Context) (http.Header, bool) {
|
||||
h, ok := ctx.Value(contextkeys.RequestHeaderKey).(http.Header)
|
||||
return h, ok
|
||||
}
|
||||
|
||||
// SetHTTPResponseHeader sets an HTTP header key-value pair using a context
|
||||
// provided by a twirp-generated server, or a child of that context.
|
||||
// The server will include the header in its response for that request context.
|
||||
//
|
||||
// This can be used to respond with custom HTTP headers like "Cache-Control".
|
||||
// But note that HTTP headers are a Twirp implementation detail,
|
||||
// only visible by middleware, not by the clients or their responses.
|
||||
//
|
||||
// The header will be ignored (noop) if the context is invalid (i.e. using a new
|
||||
// context.Background() instead of passing the context from the handler).
|
||||
//
|
||||
// If called multiple times with the same key, it replaces any existing values
|
||||
// associated with that key.
|
||||
//
|
||||
// SetHTTPResponseHeader returns an error if the provided header key
|
||||
// would overwrite a header that is needed by Twirp, like "Content-Type".
|
||||
func SetHTTPResponseHeader(ctx context.Context, key, value string) error {
|
||||
if key == "Content-Type" {
|
||||
return errors.New("header key can not be Content-Type")
|
||||
}
|
||||
|
||||
responseWriter, ok := ctx.Value(contextkeys.ResponseWriterKey).(http.ResponseWriter)
|
||||
if ok {
|
||||
responseWriter.Header().Set(key, value)
|
||||
} // invalid context is ignored, not an error, this is to allow easy unit testing with mock servers
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
// Package ctxsetters is an implementation detail for twirp generated code, used
|
||||
// by the generated servers to set values in contexts for later access with the
|
||||
// twirp package's accessors.
|
||||
//
|
||||
// Do not use ctxsetters outside of twirp's generated code.
|
||||
package ctxsetters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/twitchtv/twirp/internal/contextkeys"
|
||||
)
|
||||
|
||||
func WithMethodName(ctx context.Context, name string) context.Context {
|
||||
return context.WithValue(ctx, contextkeys.MethodNameKey, name)
|
||||
}
|
||||
|
||||
func WithServiceName(ctx context.Context, name string) context.Context {
|
||||
return context.WithValue(ctx, contextkeys.ServiceNameKey, name)
|
||||
}
|
||||
|
||||
func WithPackageName(ctx context.Context, name string) context.Context {
|
||||
return context.WithValue(ctx, contextkeys.PackageNameKey, name)
|
||||
}
|
||||
|
||||
func WithStatusCode(ctx context.Context, code int) context.Context {
|
||||
return context.WithValue(ctx, contextkeys.StatusCodeKey, strconv.Itoa(code))
|
||||
}
|
||||
|
||||
func WithResponseWriter(ctx context.Context, w http.ResponseWriter) context.Context {
|
||||
return context.WithValue(ctx, contextkeys.ResponseWriterKey, w)
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
// package twirp provides core types used in generated Twirp servers and client.
|
||||
//
|
||||
// Twirp services handle errors using the `twirp.Error` interface.
|
||||
//
|
||||
// For example, a server method may return an InvalidArgumentError:
|
||||
//
|
||||
// if req.Order != "DESC" && req.Order != "ASC" {
|
||||
// return nil, twirp.InvalidArgumentError("Order", "must be DESC or ASC")
|
||||
// }
|
||||
//
|
||||
// And the same twirp.Error is returned by the client, for example:
|
||||
//
|
||||
// resp, err := twirpClient.RPCMethod(ctx, req)
|
||||
// if err != nil {
|
||||
// if twerr := err.(twirp.Error) {
|
||||
// switch twerr.Code() {
|
||||
// case twirp.InvalidArgument:
|
||||
// log.Error("invalid argument "+twirp.Meta("argument"))
|
||||
// default:
|
||||
// log.Error(twerr.Error())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Clients may also return Internal errors if something failed on the system:
|
||||
// the server, the network, or the client itself (i.e. failure parsing
|
||||
// response).
|
||||
//
|
||||
package twirp
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Error represents an error in a Twirp service call.
|
||||
type Error interface {
|
||||
// Code is of the valid error codes.
|
||||
Code() ErrorCode
|
||||
|
||||
// Msg returns a human-readable, unstructured messages describing the error.
|
||||
Msg() string
|
||||
|
||||
// WithMeta returns a copy of the Error with the given key-value pair attached
|
||||
// as metadata. If the key is already set, it is overwritten.
|
||||
WithMeta(key string, val string) Error
|
||||
|
||||
// Meta returns the stored value for the given key. If the key has no set
|
||||
// value, Meta returns an empty string. There is no way to distinguish between
|
||||
// an unset value and an explicit empty string.
|
||||
Meta(key string) string
|
||||
|
||||
// MetaMap returns the complete key-value metadata map stored on the error.
|
||||
MetaMap() map[string]string
|
||||
|
||||
// Error returns a string of the form "twirp error <Type>: <Msg>"
|
||||
Error() string
|
||||
}
|
||||
|
||||
// NewError is the generic constructor for a twirp.Error. The ErrorCode must be
|
||||
// one of the valid predefined constants, otherwise it will be converted to an
|
||||
// error {type: Internal, msg: "invalid error type {{code}}"}. If you need to
|
||||
// add metadata, use .WithMeta(key, value) method after building the error.
|
||||
func NewError(code ErrorCode, msg string) Error {
|
||||
if IsValidErrorCode(code) {
|
||||
return &twerr{
|
||||
code: code,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
return &twerr{
|
||||
code: Internal,
|
||||
msg: "invalid error type " + string(code),
|
||||
}
|
||||
}
|
||||
|
||||
// NotFoundError constructor for the common NotFound error.
|
||||
func NotFoundError(msg string) Error {
|
||||
return NewError(NotFound, msg)
|
||||
}
|
||||
|
||||
// InvalidArgumentError constructor for the common InvalidArgument error. Can be
|
||||
// used when an argument has invalid format, is a number out of range, is a bad
|
||||
// option, etc).
|
||||
func InvalidArgumentError(argument string, validationMsg string) Error {
|
||||
err := NewError(InvalidArgument, argument+" "+validationMsg)
|
||||
err = err.WithMeta("argument", argument)
|
||||
return err
|
||||
}
|
||||
|
||||
// RequiredArgumentError is a more scpecific constructor for InvalidArgument
|
||||
// error. Should be used when the argument is required (expected to have a
|
||||
// non-zero value).
|
||||
func RequiredArgumentError(argument string) Error {
|
||||
return InvalidArgumentError(argument, "is required")
|
||||
}
|
||||
|
||||
// InternalError constructor for the common Internal error. Should be used to
|
||||
// specify that something bad or unexpected happened.
|
||||
func InternalError(msg string) Error {
|
||||
return NewError(Internal, msg)
|
||||
}
|
||||
|
||||
// InternalErrorWith is an easy way to wrap another error. It adds the
|
||||
// underlying error's type as metadata with a key of "cause", which can be
|
||||
// useful for debugging. Should be used in the common case of an unexpected
|
||||
// error returned from another API, but sometimes it is better to build a more
|
||||
// specific error (like with NewError(Unknown, err.Error()), for example).
|
||||
//
|
||||
// The returned error also has a Cause() method which will return the original
|
||||
// error, if it is known. This can be used with the github.com/pkg/errors
|
||||
// package to extract the root cause of an error. Information about the root
|
||||
// cause of an error is lost when it is serialized, so this doesn't let a client
|
||||
// know the exact root cause of a server's error.
|
||||
func InternalErrorWith(err error) Error {
|
||||
msg := err.Error()
|
||||
twerr := NewError(Internal, msg)
|
||||
twerr = twerr.WithMeta("cause", fmt.Sprintf("%T", err)) // to easily tell apart wrapped internal errors from explicit ones
|
||||
return &wrappedErr{
|
||||
wrapper: twerr,
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorCode represents a Twirp error type.
|
||||
type ErrorCode string
|
||||
|
||||
// Valid Twirp error types. Most error types are equivalent to GRPC status codes
|
||||
// and follow the same semantics.
|
||||
const (
|
||||
// Canceled indicates the operation was cancelled (typically by the caller).
|
||||
Canceled ErrorCode = "canceled"
|
||||
|
||||
// Unknown error. For example when handling errors raised by APIs that do not
|
||||
// return enough error information.
|
||||
Unknown ErrorCode = "unknown"
|
||||
|
||||
// InvalidArgument indicates client specified an invalid argument. It
|
||||
// indicates arguments that are problematic regardless of the state of the
|
||||
// system (i.e. a malformed file name, required argument, number out of range,
|
||||
// etc.).
|
||||
InvalidArgument ErrorCode = "invalid_argument"
|
||||
|
||||
// DeadlineExceeded means operation expired before completion. For operations
|
||||
// that change the state of the system, this error may be returned even if the
|
||||
// operation has completed successfully (timeout).
|
||||
DeadlineExceeded ErrorCode = "deadline_exceeded"
|
||||
|
||||
// NotFound means some requested entity was not found.
|
||||
NotFound ErrorCode = "not_found"
|
||||
|
||||
// BadRoute means that the requested URL path wasn't routable to a Twirp
|
||||
// service and method. This is returned by the generated server, and usually
|
||||
// shouldn't be returned by applications. Instead, applications should use
|
||||
// NotFound or Unimplemented.
|
||||
BadRoute ErrorCode = "bad_route"
|
||||
|
||||
// AlreadyExists means an attempt to create an entity failed because one
|
||||
// already exists.
|
||||
AlreadyExists ErrorCode = "already_exists"
|
||||
|
||||
// PermissionDenied indicates the caller does not have permission to execute
|
||||
// the specified operation. It must not be used if the caller cannot be
|
||||
// identified (Unauthenticated).
|
||||
PermissionDenied ErrorCode = "permission_denied"
|
||||
|
||||
// Unauthenticated indicates the request does not have valid authentication
|
||||
// credentials for the operation.
|
||||
Unauthenticated ErrorCode = "unauthenticated"
|
||||
|
||||
// ResourceExhausted indicates some resource has been exhausted, perhaps a
|
||||
// per-user quota, or perhaps the entire file system is out of space.
|
||||
ResourceExhausted ErrorCode = "resource_exhausted"
|
||||
|
||||
// FailedPrecondition indicates operation was rejected because the system is
|
||||
// not in a state required for the operation's execution. For example, doing
|
||||
// an rmdir operation on a directory that is non-empty, or on a non-directory
|
||||
// object, or when having conflicting read-modify-write on the same resource.
|
||||
FailedPrecondition ErrorCode = "failed_precondition"
|
||||
|
||||
// Aborted indicates the operation was aborted, typically due to a concurrency
|
||||
// issue like sequencer check failures, transaction aborts, etc.
|
||||
Aborted ErrorCode = "aborted"
|
||||
|
||||
// OutOfRange means operation was attempted past the valid range. For example,
|
||||
// seeking or reading past end of a paginated collection.
|
||||
//
|
||||
// Unlike InvalidArgument, this error indicates a problem that may be fixed if
|
||||
// the system state changes (i.e. adding more items to the collection).
|
||||
//
|
||||
// There is a fair bit of overlap between FailedPrecondition and OutOfRange.
|
||||
// We recommend using OutOfRange (the more specific error) when it applies so
|
||||
// that callers who are iterating through a space can easily look for an
|
||||
// OutOfRange error to detect when they are done.
|
||||
OutOfRange ErrorCode = "out_of_range"
|
||||
|
||||
// Unimplemented indicates operation is not implemented or not
|
||||
// supported/enabled in this service.
|
||||
Unimplemented ErrorCode = "unimplemented"
|
||||
|
||||
// Internal errors. When some invariants expected by the underlying system
|
||||
// have been broken. In other words, something bad happened in the library or
|
||||
// backend service. Do not confuse with HTTP Internal Server Error; an
|
||||
// Internal error could also happen on the client code, i.e. when parsing a
|
||||
// server response.
|
||||
Internal ErrorCode = "internal"
|
||||
|
||||
// Unavailable indicates the service is currently unavailable. This is a most
|
||||
// likely a transient condition and may be corrected by retrying with a
|
||||
// backoff.
|
||||
Unavailable ErrorCode = "unavailable"
|
||||
|
||||
// DataLoss indicates unrecoverable data loss or corruption.
|
||||
DataLoss ErrorCode = "data_loss"
|
||||
|
||||
// NoError is the zero-value, is considered an empty error and should not be
|
||||
// used.
|
||||
NoError ErrorCode = ""
|
||||
)
|
||||
|
||||
// ServerHTTPStatusFromErrorCode maps a Twirp error type into a similar HTTP
|
||||
// response status. It is used by the Twirp server handler to set the HTTP
|
||||
// response status code. Returns 0 if the ErrorCode is invalid.
|
||||
func ServerHTTPStatusFromErrorCode(code ErrorCode) int {
|
||||
switch code {
|
||||
case Canceled:
|
||||
return 408 // RequestTimeout
|
||||
case Unknown:
|
||||
return 500 // Internal Server Error
|
||||
case InvalidArgument:
|
||||
return 400 // BadRequest
|
||||
case DeadlineExceeded:
|
||||
return 408 // RequestTimeout
|
||||
case NotFound:
|
||||
return 404 // Not Found
|
||||
case BadRoute:
|
||||
return 404 // Not Found
|
||||
case AlreadyExists:
|
||||
return 409 // Conflict
|
||||
case PermissionDenied:
|
||||
return 403 // Forbidden
|
||||
case Unauthenticated:
|
||||
return 401 // Unauthorized
|
||||
case ResourceExhausted:
|
||||
return 403 // Forbidden
|
||||
case FailedPrecondition:
|
||||
return 412 // Precondition Failed
|
||||
case Aborted:
|
||||
return 409 // Conflict
|
||||
case OutOfRange:
|
||||
return 400 // Bad Request
|
||||
case Unimplemented:
|
||||
return 501 // Not Implemented
|
||||
case Internal:
|
||||
return 500 // Internal Server Error
|
||||
case Unavailable:
|
||||
return 503 // Service Unavailable
|
||||
case DataLoss:
|
||||
return 500 // Internal Server Error
|
||||
case NoError:
|
||||
return 200 // OK
|
||||
default:
|
||||
return 0 // Invalid!
|
||||
}
|
||||
}
|
||||
|
||||
// IsValidErrorCode returns true if is one of the valid predefined constants.
|
||||
func IsValidErrorCode(code ErrorCode) bool {
|
||||
return ServerHTTPStatusFromErrorCode(code) != 0
|
||||
}
|
||||
|
||||
// twirp.Error implementation
|
||||
type twerr struct {
|
||||
code ErrorCode
|
||||
msg string
|
||||
meta map[string]string
|
||||
}
|
||||
|
||||
func (e *twerr) Code() ErrorCode { return e.code }
|
||||
func (e *twerr) Msg() string { return e.msg }
|
||||
|
||||
func (e *twerr) Meta(key string) string {
|
||||
if e.meta != nil {
|
||||
return e.meta[key] // also returns "" if key is not in meta map
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (e *twerr) WithMeta(key string, value string) Error {
|
||||
newErr := &twerr{
|
||||
code: e.code,
|
||||
msg: e.msg,
|
||||
meta: make(map[string]string, len(e.meta)),
|
||||
}
|
||||
for k, v := range e.meta {
|
||||
newErr.meta[k] = v
|
||||
}
|
||||
newErr.meta[key] = value
|
||||
return newErr
|
||||
}
|
||||
|
||||
func (e *twerr) MetaMap() map[string]string {
|
||||
return e.meta
|
||||
}
|
||||
|
||||
func (e *twerr) Error() string {
|
||||
return fmt.Sprintf("twirp error %s: %s", e.code, e.msg)
|
||||
}
|
||||
|
||||
// wrappedErr fulfills the twirp.Error interface and the
|
||||
// github.com/pkg/errors.Causer interface. It exposes all the twirp error
|
||||
// methods, but root cause of an error can be retrieved with
|
||||
// (*wrappedErr).Cause. This is expected to be used with the InternalErrorWith
|
||||
// function.
|
||||
type wrappedErr struct {
|
||||
wrapper Error
|
||||
cause error
|
||||
}
|
||||
|
||||
func (e *wrappedErr) Code() ErrorCode { return e.wrapper.Code() }
|
||||
func (e *wrappedErr) Msg() string { return e.wrapper.Msg() }
|
||||
func (e *wrappedErr) Meta(key string) string { return e.wrapper.Meta(key) }
|
||||
func (e *wrappedErr) MetaMap() map[string]string { return e.wrapper.MetaMap() }
|
||||
func (e *wrappedErr) Error() string { return e.wrapper.Error() }
|
||||
func (e *wrappedErr) WithMeta(key string, val string) Error {
|
||||
return &wrappedErr{
|
||||
wrapper: e.wrapper.WithMeta(key, val),
|
||||
cause: e.cause,
|
||||
}
|
||||
}
|
||||
func (e *wrappedErr) Cause() error { return e.cause }
|
|
@ -0,0 +1,38 @@
|
|||
package twirp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func TestWithMetaRaces(t *testing.T) {
|
||||
err := NewError(Internal, "msg")
|
||||
err = err.WithMeta("k1", "v1")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 1000; i++ {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
_ = err.WithMeta(fmt.Sprintf("k-%d", i), "v")
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if len(err.MetaMap()) != 1 {
|
||||
t.Errorf("err was mutated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorCause(t *testing.T) {
|
||||
rootCause := fmt.Errorf("this is only a test")
|
||||
twerr := InternalErrorWith(rootCause)
|
||||
cause := errors.Cause(twerr)
|
||||
if cause != rootCause {
|
||||
t.Errorf("got wrong cause for err. have=%q, want=%q", cause, rootCause)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package twirp
|
||||
|
||||
import "context"
|
||||
|
||||
// ServerHooks is a container for callbacks that can instrument a
|
||||
// Twirp-generated server. These callbacks all accept a context and return a
|
||||
// context. They can use this to add to the request context as it threads
|
||||
// through the system, appending values or deadlines to it.
|
||||
//
|
||||
// The RequestReceived and RequestRouted hooks are special: they can return
|
||||
// errors. If they return a non-nil error, handling for that request will be
|
||||
// stopped at that point. The Error hook will be triggered, and the error will
|
||||
// be sent to the client. This can be used for stuff like auth checks before
|
||||
// deserializing a request.
|
||||
//
|
||||
// The RequestReceived hook is always called first, and it is called for every
|
||||
// request that the Twirp server handles. The last hook to be called in a
|
||||
// request's lifecycle is always ResponseSent, even in the case of an error.
|
||||
//
|
||||
// Details on the timing of each hook are documented as comments on the fields
|
||||
// of the ServerHooks type.
|
||||
type ServerHooks struct {
|
||||
// RequestReceived is called as soon as a request enters the Twirp
|
||||
// server at the earliest available moment.
|
||||
RequestReceived func(context.Context) (context.Context, error)
|
||||
|
||||
// RequestRouted is called when a request has been routed to a
|
||||
// particular method of the Twirp server.
|
||||
RequestRouted func(context.Context) (context.Context, error)
|
||||
|
||||
// ResponsePrepared is called when a request has been handled and a
|
||||
// response is ready to be sent to the client.
|
||||
ResponsePrepared func(context.Context) context.Context
|
||||
|
||||
// ResponseSent is called when all bytes of a response (including an error
|
||||
// response) have been written. Because the ResponseSent hook is terminal, it
|
||||
// does not return a context.
|
||||
ResponseSent func(context.Context)
|
||||
|
||||
// Error hook is called when a request responds with an Error,
|
||||
// either by the service implementation or by Twirp itself.
|
||||
// The Error is passed as argument to the hook.
|
||||
Error func(context.Context, Error) context.Context
|
||||
}
|
||||
|
||||
// ChainHooks creates a new *ServerHooks which chains the callbacks in
|
||||
// each of the constituent hooks passed in. Each hook function will be
|
||||
// called in the order of the ServerHooks values passed in.
|
||||
//
|
||||
// For the erroring hooks, RequestReceived and RequestRouted, any returned
|
||||
// errors prevent processing by later hooks.
|
||||
func ChainHooks(hooks ...*ServerHooks) *ServerHooks {
|
||||
if len(hooks) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(hooks) == 1 {
|
||||
return hooks[0]
|
||||
}
|
||||
return &ServerHooks{
|
||||
RequestReceived: func(ctx context.Context) (context.Context, error) {
|
||||
var err error
|
||||
for _, h := range hooks {
|
||||
if h != nil && h.RequestReceived != nil {
|
||||
ctx, err = h.RequestReceived(ctx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx, nil
|
||||
},
|
||||
RequestRouted: func(ctx context.Context) (context.Context, error) {
|
||||
var err error
|
||||
for _, h := range hooks {
|
||||
if h != nil && h.RequestRouted != nil {
|
||||
ctx, err = h.RequestRouted(ctx)
|
||||
if err != nil {
|
||||
return ctx, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return ctx, nil
|
||||
},
|
||||
ResponsePrepared: func(ctx context.Context) context.Context {
|
||||
for _, h := range hooks {
|
||||
if h != nil && h.ResponsePrepared != nil {
|
||||
ctx = h.ResponsePrepared(ctx)
|
||||
}
|
||||
}
|
||||
return ctx
|
||||
},
|
||||
ResponseSent: func(ctx context.Context) {
|
||||
for _, h := range hooks {
|
||||
if h != nil && h.ResponseSent != nil {
|
||||
h.ResponseSent(ctx)
|
||||
}
|
||||
}
|
||||
},
|
||||
Error: func(ctx context.Context, twerr Error) context.Context {
|
||||
for _, h := range hooks {
|
||||
if h != nil && h.Error != nil {
|
||||
ctx = h.Error(ctx, twerr)
|
||||
}
|
||||
}
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package twirp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChainHooks(t *testing.T) {
|
||||
var (
|
||||
hook1 = new(ServerHooks)
|
||||
hook2 = new(ServerHooks)
|
||||
hook3 = new(ServerHooks)
|
||||
)
|
||||
|
||||
const key = "key"
|
||||
|
||||
hook1.RequestReceived = func(ctx context.Context) (context.Context, error) {
|
||||
return context.WithValue(ctx, key, []string{"hook1"}), nil
|
||||
}
|
||||
hook2.RequestReceived = func(ctx context.Context) (context.Context, error) {
|
||||
v := ctx.Value(key).([]string)
|
||||
return context.WithValue(ctx, key, append(v, "hook2")), nil
|
||||
}
|
||||
hook3.RequestReceived = func(ctx context.Context) (context.Context, error) {
|
||||
v := ctx.Value(key).([]string)
|
||||
return context.WithValue(ctx, key, append(v, "hook3")), nil
|
||||
}
|
||||
|
||||
hook1.RequestRouted = func(ctx context.Context) (context.Context, error) {
|
||||
return context.WithValue(ctx, key, []string{"hook1"}), nil
|
||||
}
|
||||
|
||||
hook2.ResponsePrepared = func(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, key, []string{"hook2"})
|
||||
}
|
||||
|
||||
chain := ChainHooks(hook1, hook2, hook3)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// When all three chained hooks have a handler, all should be called in order.
|
||||
want := []string{"hook1", "hook2", "hook3"}
|
||||
haveCtx, err := chain.RequestReceived(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("RequestReceived chain has unexpected err %v", err)
|
||||
}
|
||||
have := haveCtx.Value(key)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("RequestReceived chain has unexpected ctx, have=%v, want=%v", have, want)
|
||||
}
|
||||
|
||||
// When only the first chained hook has a handler, it should be called, and
|
||||
// there should be no panic.
|
||||
want = []string{"hook1"}
|
||||
haveCtx, err = chain.RequestRouted(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("RequestRouted chain has unexpected err %v", err)
|
||||
}
|
||||
have = haveCtx.Value(key)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("RequestRouted chain has unexpected ctx, have=%v, want=%v", have, want)
|
||||
}
|
||||
|
||||
// When only the second chained hook has a handler, it should be called, and
|
||||
// there should be no panic.
|
||||
want = []string{"hook2"}
|
||||
have = chain.ResponsePrepared(ctx).Value(key)
|
||||
if !reflect.DeepEqual(want, have) {
|
||||
t.Errorf("RequestRouted chain has unexpected ctx, have=%v, want=%v", have, want)
|
||||
}
|
||||
|
||||
// When none of the chained hooks has a handler there should be no panic.
|
||||
chain.ResponseSent(ctx)
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
which protoc
|
||||
PROTOC_EXISTS=$?
|
||||
if [ $PROTOC_EXISTS -eq 0 ]; then
|
||||
echo "Protoc already installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
brew install protobuf
|
||||
elif [ `whoami` == "root" ]; then
|
||||
mkdir -p /usr/local/src/protoc
|
||||
pushd /usr/local/src/protoc
|
||||
wget https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip -O /usr/local/src/protoc-3.1.0-linux-x86_64.zip
|
||||
unzip -x ../protoc-3.1.0-linux-x86_64.zip
|
||||
if [ ! -e /usr/local/bin/protoc ]; then
|
||||
ln -s `pwd`/bin/protoc /usr/local/bin/protoc
|
||||
fi
|
||||
popd
|
||||
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
|
||||
echo "Project setup needs sudo to put protoc in /usr/local/src, so it will ask a few times"
|
||||
sudo chmod a+w /usr/local/src
|
||||
mkdir -p /usr/local/src/protoc
|
||||
pushd /usr/local/src/protoc
|
||||
wget https://github.com/google/protobuf/releases/download/v3.1.0/protoc-3.1.0-linux-x86_64.zip -O /usr/local/src/protoc-3.1.0-linux-x86_64.zip
|
||||
unzip -x ../protoc-3.1.0-linux-x86_64.zip
|
||||
if [ ! -e /usr/local/bin/protoc ]; then
|
||||
sudo ln -s `pwd`/bin/protoc /usr/local/bin/protoc
|
||||
fi
|
||||
popd
|
||||
fi
|
||||
exit 0
|
|
@ -0,0 +1,15 @@
|
|||
// Package contextkeys stores the keys to the context accessor
|
||||
// functions, letting generated code safely set values in contexts
|
||||
// without exposing the setters to the outside world.
|
||||
package contextkeys
|
||||
|
||||
type contextKey int
|
||||
|
||||
const (
|
||||
MethodNameKey contextKey = 1 + iota
|
||||
ServiceNameKey
|
||||
PackageNameKey
|
||||
StatusCodeKey
|
||||
RequestHeaderKey
|
||||
ResponseWriterKey
|
||||
)
|
|
@ -0,0 +1,13 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# gogo_protoc_gen.sh foo.proto will compile foo.proto using
|
||||
# github.com/gogo/protobuf/protoc-gen-gofast, an alternative generator used
|
||||
# sometimes at Twitch.. Should be run in the same directory as its input.
|
||||
# Handles multi-element GOPATHs so it works with retool.
|
||||
|
||||
# Append '/src' to every element in GOPATH.
|
||||
PROTOPATH=${GOPATH/://src:}/src
|
||||
|
||||
protoc --proto_path="${PROTOPATH}:." --twirp_out=. --gofast_out=. "$@"
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# protoc_gen.sh foo.proto will compile foo.proto. Should be run in the same
|
||||
# directory as its input. Handles multi-element GOPATHs so it works with retool.
|
||||
|
||||
# Append '/src' to every element in GOPATH.
|
||||
PROTOPATH=${GOPATH/://src:}/src
|
||||
|
||||
protoc --proto_path="${PROTOPATH}:." --twirp_out=. --go_out=. "$@"
|
||||
protoc --proto_path="${PROTOPATH}:." --python_out=. --twirp_python_out=. "$@"
|
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
|
@ -0,0 +1,7 @@
|
|||
certifi==2017.4.17
|
||||
chardet==3.0.4
|
||||
idna==2.5
|
||||
protobuf==3.3.0
|
||||
requests==2.17.3
|
||||
six==1.10.0
|
||||
urllib3==1.21.1
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"Tools": [
|
||||
{
|
||||
"Repository": "github.com/golang/protobuf/protoc-gen-go",
|
||||
"Commit": "c9c7427a2a70d2eb3bafa0ab2dc163e45f143317"
|
||||
},
|
||||
{
|
||||
"Repository": "github.com/kisielk/errcheck",
|
||||
"Commit": "db0ca22445717d1b2c51ac1034440e0a2a2de645"
|
||||
},
|
||||
{
|
||||
"Repository": "github.com/gogo/protobuf/protoc-gen-gofast",
|
||||
"Commit": "30433562cfbf487fe1df7cd26c7bab168d2f14d0"
|
||||
},
|
||||
{
|
||||
"Repository": "github.com/twitchtv/retool",
|
||||
"Commit": "6f6d4930d88c40e23d2b54d12e64f0444e1fb4ef"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue