fauxrpc

package module
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2024 License: MIT Imports: 15 Imported by: 2

README

FauxRPC

Go Go Report Card Go Reference

FauxRPC is a powerful tool that empowers you to accelerate development and testing by effortlessly generating fake implementations of gRPC, gRPC-Web, Connect, and REST services. If you have a protobuf-based workflow, this tool could help.

Why FauxRPC?

  • Faster Development & Testing: Work independently without relying on fully functional backend services.
  • Isolation & Control: Test frontend components in isolation with controlled fake data.
  • Multi-Protocol Support: Supports multiple protocols (gRPC, gRPC-Web, Connect, and REST).
  • Prototyping & Demos: Create prototypes and demos quickly without building the full backend. Fake it till you make it.
  • Improved Collaboration: Bridge the gap between frontend and backend teams.
  • Plays well with others: Test data from FauxRPC will try to automatically follow any protovalidate constraints that are defined.

How it Works

FauxRPC leverages your Protobuf definitions to generate fake services that mimic the behavior of real ones. You can easily configure the fake data returned, allowing you to simulate various scenarios and edge cases. It takes in *.proto files or protobuf descriptors (in binpb, json, txtpb, yaml formats), then it automatically starts up a server that can speak gRPC/gRPC-Web/Connect and REST (as long as there are google.api.http annotations defined). Descriptors contain all of the information found in a set of .proto files. You can generate them with protoc or the buf build command.

Get Started

Install via source
go install github.com/sudorandom/fauxrpc/cmd/fauxrpc@latest
Pre-built binaries

Binaries are built for several platforms for each release. See the latest ones on the releases page.

Using Descriptors

Make an example.proto file (or use a file that already exists):

syntax = "proto3";

package greet.v1;

message GreetRequest {
  string name = 1;
}

message GreetResponse {
  string greeting = 1;
}

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {}
}

Create a descriptors file and use it to start the FauxRPC server:

$ buf build ./example.proto -o ./example.binpb
$ fauxrpc run --schema=./example.binpb
2024/08/17 08:01:19 INFO Listening on http://127.0.0.1:6660
2024/08/17 08:01:19 INFO See available methods: buf curl --http2-prior-knowledge http://127.0.0.1:6660 --list-methods

Done! It's that easy. Now you can call the service with any tooling that supports gRPC, gRPC-Web, or connect. So buf curl, grpcurl, Postman, Insomnia all work fine!

$ buf curl --http2-prior-knowledge http://127.0.0.1:6660/greet.v1.GreetService/Greet
{
  "greeting":  "3 wolf moon fashion axe."
}
Using Server Reflection

If there's an existing gRPC service running that you want to emulate, you can use server reflection to start the FauxRPC service:

$ fauxrpc run --schema=https://demo.connectrpc.com
From BSR (Buf Schema Registry)

Buf has a schema registry where many schemas are hosted. Here's how to use FauxRPC using images from the registry.

$ buf build buf.build/bufbuild/registry -o bufbuild.registry.json
$ fauxrpc run --schema=./bufbuild.registry.json

This will start a fake version of the BSR API by downloading descriptors for bufbuild/registry from the BSR and using them with FauxRPC. Very meta.

Multiple Sources

You can define this --schema option as many times as you want. That means you can add services from multiple descriptors and even mix and match from descriptors and from server reflection:

$ fauxrpc run --schema=https://demo.connectrpc.com --schema=./example.binpb

Multi-protocol Support

The multi-protocol support is based on ConnectRPC. So with FauxRPC, you get gRPC, gRPC-Web and Connect out of the box. However, FauxRPC does one thing more. It allows you to use google.api.http annotations to present a JSON/HTTP API, so you can gRPC and REST together! This is normally done with an additional service that runs in-between the outside world and your actual gRPC service but with FauxRPC you get the so-called transcoding from HTTP/JSON to gRPC all in the same package. Here's a concrete example:

syntax = "proto3";

package http.service;

import "google/api/annotations.proto";

service HTTPService {
  rpc GetMessage(GetMessageRequest) returns (Message) {
    option (google.api.http) = {get: "/v1/{name=messages/*}"};
  }
}
message GetMessageRequest {
  string name = 1; // Mapped to URL path.
}
message Message {
  string text = 1; // The resource content.
}

Again, we start the service by building the descriptors and using

$ buf build ./httpservice.proto -o ./httpservice.binpb
$ fauxrpc run --schema=httpservice.binpb

Now that we have the server running we can test this with the "normal" curl:

$ curl http://127.0.0.1:6660/v1/messages/123456
{"text":"Retro."}⏎

Sweet. You can now easily support REST alongside gRPC. If you are wondering how to do this with "real" services, look into vanguard-go. This library is doing the real heavy lifting.

What does the fake data look like?

You might be wondering what actual responses look like. FauxRPC's fake data generation is continually improving so these details might change as time goes on. It uses a library called fakeit to generate fake data. Because protobufs have pretty well-defined types, we can easily generate data that technically matches the types. This works well for most use cases, but FauxRPC tries to be a little bit better. If you annotate your protobuf files with protovalidate constraints, FauxRPC will try its best to generate data that matches these constraints. Let's look at some examples!

syntax = "proto3";

package greet.v1;

message GreetRequest {
  string name = 1;
}

message GreetResponse {
  string greeting = 1;
}

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {}
}

With FauxRPC, you will get any kind of word, so it might look like this:

{
  "greeting": "Poutine."
}

This is fine, but for the RPC, we know a bit more about the type being returned. We know that it sends a greeting back that looks like "Hello, [name]". So here's what the same protobuf file might look like with protovalidate constraints:

Now let's see what this looks like with protovalidate constraints:

syntax = "proto3";

import "buf/validate/validate.proto";

package greet.v1;

message GreetRequest {
  string name = 1 [(buf.validate.field).string = {min_len: 3, max_len: 100}];
}

message GreetResponse {
  string greeting = 1 [(buf.validate.field).string.pattern = "^Hello, [a-zA-Z]+$"];
}

service GreetService {
  rpc Greet(GreetRequest) returns (GreetResponse) {}
}

With this new protobuf file, this is what FauxRPC might output now:

{
  "greeting": "Hello, TWXxF"
}

In essence, protovalidate constraints enable FauxRPC to generate more realistic and contextually relevant fake data, aligning it closer to the expected behavior of your actual services.

Status: Alpha

This project is just starting out. I plan to add a lot of things that make this tool actually usable in more situations.

  • Service for adding/updating/removing stub responses with a CLI to add/remove/replace these stubs
  • Configuration file
  • BSR Support (this is a 'maybe' because using buf build to emit descriptors works well enough IMO)
  • Better streaming support. FauxRPC does work with streaming calls but it only returns a single response

Documentation

Index

Constants

View Source
const MaxNestedDepth = 20

Variables

This section is empty.

Functions

func GenerateBool added in v0.0.5

func GenerateBool(fd protoreflect.FieldDescriptor) bool

GenerateBool returns a fake boolean value given a field descriptor.

func GenerateBytes

func GenerateBytes(fd protoreflect.FieldDescriptor) []byte

GenerateBytes returns a fake []byte value given a field descriptor.

func GenerateEnum added in v0.0.5

GenerateEnum returns a fake enum value given a field descriptor.

func GenerateFixed32

func GenerateFixed32(fd protoreflect.FieldDescriptor) uint32

GenerateFixed32 returns a fake fixed32 value given a field descriptor.

func GenerateFixed64

func GenerateFixed64(fd protoreflect.FieldDescriptor) uint64

GenerateFixed64 returns a fake fixed64 value given a field descriptor.

func GenerateFloat32

func GenerateFloat32(fd protoreflect.FieldDescriptor) float32

GenerateFloat32 returns a fake float32 value given a field descriptor.

func GenerateFloat64

func GenerateFloat64(fd protoreflect.FieldDescriptor) float64

GenerateFloat64 returns a fake float64 value given a field descriptor.

func GenerateGoogleDuration added in v0.0.8

func GenerateGoogleDuration(fd protoreflect.FieldDescriptor) *durationpb.Duration

GenerateGoogleDuration generates a random google.protobuf.Duration value.

func GenerateGoogleTimestamp added in v0.0.8

func GenerateGoogleTimestamp(fd protoreflect.FieldDescriptor) *timestamppb.Timestamp

GenerateGoogleTimestamp generates a random google.protobuf.Timestamp value.

func GenerateGoogleValue added in v0.0.8

func GenerateGoogleValue(fd protoreflect.FieldDescriptor, st state) *structpb.Value

func GenerateInt32

func GenerateInt32(fd protoreflect.FieldDescriptor) int32

GenerateInt32 returns a fake int32 value given a field descriptor.

func GenerateInt64

func GenerateInt64(fd protoreflect.FieldDescriptor) int64

GenerateInt64 returns a fake int64 value given a field descriptor.

func GenerateSFixed32 added in v0.0.5

func GenerateSFixed32(fd protoreflect.FieldDescriptor) int32

GenerateSFixed32 returns a fake sfixedint32 value given a field descriptor.

func GenerateSFixed64

func GenerateSFixed64(fd protoreflect.FieldDescriptor) int64

GenerateSFixed64 returns a fake sfixed64 value given a field descriptor.

func GenerateSInt32

func GenerateSInt32(fd protoreflect.FieldDescriptor) int32

GenerateSInt32 returns a fake sint32 value given a field descriptor.

func GenerateSInt64

func GenerateSInt64(fd protoreflect.FieldDescriptor) int64

GenerateSInt64 returns a fake sint64 value given a field descriptor.

func GenerateString

func GenerateString(fd protoreflect.FieldDescriptor) string

GenerateString returns a fake string value given a field descriptor.

func GenerateUInt32

func GenerateUInt32(fd protoreflect.FieldDescriptor) uint32

GenerateUInt32 returns a fake uint32 value given a field descriptor.

func GenerateUInt64

func GenerateUInt64(fd protoreflect.FieldDescriptor) uint64

GenerateUInt64 returns a fake uint64 value given a field descriptor.

func SetDataOnMessage added in v0.0.4

func SetDataOnMessage(msg *dynamicpb.Message)

SetDataOnMessage generates fake data given a *dynamicpb.Message and sets the field values.

Types

This section is empty.

Directories

Path Synopsis
cmd
fauxrpc command
example
private

Jump to

Keyboard shortcuts

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