gRPC/Protobuf Export

Export your FakeDataDSL schemas to Protocol Buffer definitions for use with gRPC microservices.

Quick Start

# Export single schema
fake_data_dsl export User -d schemas/ -f protobuf -o user.proto

# Export all schemas
fake_data_dsl export --all -d schemas/ -f protobuf -o all.proto

Generated Output

For this schema:

User:
  id: uuid
  name: name
  email: email
  age: number(18..65)
  active: boolean
  role: enum(admin, user, moderator)

The exporter generates:

syntax = "proto3";

package generated;

import "google/protobuf/timestamp.proto";

enum UserRole {
  USERROLE_UNSPECIFIED = 0;
  USERROLE_ADMIN = 1;
  USERROLE_USER = 2;
  USERROLE_MODERATOR = 3;
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
  int64 age = 4;
  bool active = 5;
  UserRole role = 6;
}

service UserService {
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
  rpc GenerateUser(GenerateUserRequest) returns (User);
  rpc GenerateManyUsers(GenerateManyUsersRequest) returns (stream User);
}

message GetUserRequest {
  string id = 1;
}

message ListUsersRequest {
  int32 page = 1;
  int32 page_size = 2;
}

message ListUsersResponse {
  repeated User users = 1;
  int32 total_count = 2;
  int32 page = 3;
  int32 page_size = 4;
}

message GenerateUserRequest {
  optional int64 seed = 1;
  optional string mode = 2;
  map<string, string> overrides = 3;
}

message GenerateManyUsersRequest {
  int32 count = 1;
  optional int64 seed = 2;
  optional string mode = 3;
}

Ruby API

Single Schema

schema = FakeDataDSL.load("schemas/user.dsl")

exporter = FakeDataDSL::Export::Protobuf.new(
  schema,
  registry: registry,
  package: "myapp.v1",
  go_package: "github.com/myapp/proto/v1",
  with_service: true
)

proto = exporter.export
File.write("user.proto", proto)

All Schemas

proto = FakeDataDSL::Export::Protobuf.export_all(
  registry,
  package: "myapp.v1",
  go_package: "github.com/myapp/proto/v1",
  with_service: true
)

File.write("all.proto", proto)

Options

Option Description Default
package Proto package name "generated"
go_package Go package path nil
syntax Proto syntax version "proto3"
with_service Generate gRPC service true

Type Mapping

FakeDataDSL Protobuf
text, name, email, etc. string
uuid, ulid, nanoid string
number, integer int64
int, int32 int32
float, double, money double
boolean bool
timestamp, datetime google.protobuf.Timestamp
date, time string
binary, base64 bytes
array(T) repeated T
enum(a, b, c) Custom enum
SchemaRef Message reference

Arrays

Arrays map to repeated fields:

Tags:
  items: array(text, 1..10)

Becomes:

message Tags {
  repeated string items = 1;
}

Nested Schemas

Schema references become message references:

Address:
  street: street
  city: city

Person:
  name: name
  address: Address

Becomes:

message Address {
  string street = 1;
  string city = 2;
}

message Person {
  string name = 1;
  Address address = 2;
}

Enums

Enums are extracted and generated separately:

User:
  role: enum(admin, user, moderator)

Becomes:

enum UserRole {
  USERROLE_UNSPECIFIED = 0;  // Required zero value
  USERROLE_ADMIN = 1;
  USERROLE_USER = 2;
  USERROLE_MODERATOR = 3;
}

message User {
  UserRole role = 1;
}

Using with gRPC

  1. Generate the .proto file:

    fake_data_dsl export --all -d schemas/ -f protobuf -o service.proto
    
  2. Compile to your language:

    # Go
    protoc --go_out=. --go-grpc_out=. service.proto
    

# Ruby grpc_tools_ruby_protoc --ruby_out=. --grpc_out=. service.proto

# Python python -m grpc_tools.protoc --python_out=. --grpc_python_out=. service.proto


3. Implement the service using FakeDataDSL for data generation.

## Example gRPC Server (Ruby)

```ruby
require 'grpc'
require_relative 'service_services_pb'
require 'fake_data_dsl'

class DataGenerationService < Generated::DataGenerationService::Service
  def initialize
    @registry = FakeDataDSL::Registry.new
    @registry.load_dir("schemas/")
  end

  def generate_user(request, _call)
    schema = @registry.schema("User")
    data = schema.generate(
      seed: request.seed.zero? ? nil : request.seed,
      mode: request.mode.empty? ? :random : request.mode.to_sym
    )

    Generated::User.new(data)
  end

  def generate_many_users(request, _call)
    schema = @registry.schema("User")

    return enum_for(:generate_many_users, request, _call) unless block_given?

    schema.generate_stream(count: request.count).each do |data|
      yield Generated::User.new(data)
    end
  end
end

server = GRPC::RpcServer.new
server.add_http2_port('0.0.0.0:50051', :this_port_is_insecure)
server.handle(DataGenerationService.new)
server.run_till_terminated