From df2bbb9a24024656feb7ae8eff8b7347bae02360 Mon Sep 17 00:00:00 2001 From: Kevin McDonald Date: Sun, 13 Oct 2024 17:08:09 +0200 Subject: [PATCH] Add command to generate fake data --- cmd/fauxrpc/cmd_generate.go | 84 ++++++++++++++++++++ cmd/fauxrpc/main.go | 1 + private/{server/grpc.go => grpc/encoding.go} | 4 +- private/registry/loaders.go | 1 + private/server/handler.go | 3 +- 5 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 cmd/fauxrpc/cmd_generate.go rename private/{server/grpc.go => grpc/encoding.go} (72%) diff --git a/cmd/fauxrpc/cmd_generate.go b/cmd/fauxrpc/cmd_generate.go new file mode 100644 index 0000000..07f51f8 --- /dev/null +++ b/cmd/fauxrpc/cmd_generate.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/brianvoe/gofakeit/v7" + "github.com/brianvoe/gofakeit/v7/source" + "github.com/sudorandom/fauxrpc" + "github.com/sudorandom/fauxrpc/private/grpc" + "github.com/sudorandom/fauxrpc/private/registry" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" +) + +type GenerateCmd struct { + Schema []string `required:"" help:"The modules to use for the RPC schema. It can be protobuf descriptors (binpb, json, yaml), a URL for reflection or a directory of descriptors."` + Target string `required:"" help:"Protobuf type" example:"'connectrpc.eliza.v1.IntroduceResponse'"` + Format string `default:"json" enum:"json,proto,grpc" help:"Format to output"` + Seed *uint64 `help:"Seed for random number generator"` +} + +func (c *GenerateCmd) Run(globals *Globals) error { + theRegistry, err := registry.NewServiceRegistry() + if err != nil { + return err + } + for _, schema := range c.Schema { + if err := registry.AddServicesFromPath(theRegistry, schema); err != nil { + if strings.Contains(err.Error(), "name conflict") { + continue + } + return err + } + } + + desc, err := theRegistry.Files().FindDescriptorByName(protoreflect.FullName(c.Target)) + if err != nil { + return err + } + md, ok := desc.(protoreflect.MessageDescriptor) + if !ok { + return fmt.Errorf("unexpected type: %T", desc) + } + + seed := uint64(0) + if c.Seed == nil { + seed = uint64(time.Now().UnixNano()) + } else { + seed = *c.Seed + } + fakeSrc := source.NewJSF(seed) + msg := fauxrpc.NewMessage(md, fauxrpc.GenOptions{ + Faker: gofakeit.NewFaker(fakeSrc, true), + }) + + switch c.Format { + case "json": + jsonBytes, err := protojson.Marshal(msg) + if err != nil { + return err + } + os.Stdout.Write(jsonBytes) + case "proto": + protoBytes, err := proto.Marshal(msg) + if err != nil { + return err + } + os.Stdout.Write(protoBytes) + case "grpc": + protoBytes, err := proto.Marshal(msg) + if err != nil { + return err + } + grpc.WriteGRPCMessage(os.Stdout, protoBytes) + default: + return fmt.Errorf("unexpected format: %s", c.Format) + } + + return nil +} diff --git a/cmd/fauxrpc/main.go b/cmd/fauxrpc/main.go index 6864830..a04f5ad 100644 --- a/cmd/fauxrpc/main.go +++ b/cmd/fauxrpc/main.go @@ -34,6 +34,7 @@ type CLI struct { Run RunCmd `cmd:"" help:"Run the FauxRPC server"` Stub StubCmd `cmd:"" help:"Contains stub commands"` + Generate GenerateCmd `cmd:"generate" help:"Generate fake data"` Registry RegistryCmd `cmd:"" help:"Contains registry commands"` } diff --git a/private/server/grpc.go b/private/grpc/encoding.go similarity index 72% rename from private/server/grpc.go rename to private/grpc/encoding.go index 4563aae..9aa4f65 100644 --- a/private/server/grpc.go +++ b/private/grpc/encoding.go @@ -1,11 +1,11 @@ -package server +package grpc import ( "encoding/binary" "io" ) -func writeGRPCMessage(w io.Writer, msg []byte) { +func WriteGRPCMessage(w io.Writer, msg []byte) { prefix := make([]byte, 5) binary.BigEndian.PutUint32(prefix[1:], uint32(len(msg))) _, _ = w.Write(prefix) diff --git a/private/registry/loaders.go b/private/registry/loaders.go index 9993991..3ca3bd3 100644 --- a/private/registry/loaders.go +++ b/private/registry/loaders.go @@ -265,6 +265,7 @@ func AddServicesFromBSR(registry ServiceRegistry, module string) error { // AddServicesFromGlobal adds the 'well known' types to the registry. This is typically implicitly called. func AddServicesFromGlobal(registry ServiceRegistry) error { for _, fd := range []protoreflect.FileDescriptor{ + descriptorpb.File_google_protobuf_descriptor_proto, anypb.File_google_protobuf_any_proto, apipb.File_google_protobuf_api_proto, durationpb.File_google_protobuf_duration_proto, diff --git a/private/server/handler.go b/private/server/handler.go index e2c85d5..5144b95 100644 --- a/private/server/handler.go +++ b/private/server/handler.go @@ -9,6 +9,7 @@ import ( "connectrpc.com/connect" "github.com/sudorandom/fauxrpc" + "github.com/sudorandom/fauxrpc/private/grpc" "github.com/sudorandom/fauxrpc/private/stubs" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -57,7 +58,7 @@ func NewHandler(service protoreflect.ServiceDescriptor, db stubs.StubDatabase) h w.Header().Set("Grpc-Message", err.Error()) return } - writeGRPCMessage(w, b) + grpc.WriteGRPCMessage(w, b) w.Header().Set("Grpc-Status", "0") w.Header().Set("Grpc-Message", "") })