跳到主要内容

使用 entproto 生成 Protobuf

由于 Ent 与 Protobuf 的 schema 并非完全一致,我们需要在 schema 上添加一些注解来帮助 entproto 准确生成 Protobuf 定义(在 protobuf 术语中称为 "Messages")。

首先需要添加 entproto.Message() 注解。这是我们选择加入 Protobuf schema 生成的方式——我们不一定希望从所有 schema 实体生成 proto 消息或 gRPC 服务定义,此注解提供了这种控制。将其添加到 ent/schema/user.go

ent/schema/user.go
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
entproto.Message(),
}
}

接下来需要为每个字段添加注解并分配字段编号。请记住,在定义 protobuf 消息类型时,每个字段都必须分配一个唯一编号。为此,我们在每个字段上添加 entproto.Field 注解。更新 ent/schema/user.go 中的 Fields

ent/schema/user.go
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.String("name").
Unique().
Annotations(
entproto.Field(2),
),
field.String("email_address").
Unique().
Annotations(
entproto.Field(3),
),
}
}

注意我们没有从 1 开始编号字段,这是因为 ent 隐式创建了实体的 ID 字段,该字段会自动分配编号 1。现在我们可以生成 protobuf 消息类型定义了。为此,我们将在 ent/generate.go 中添加一个 go:generate 指令来调用 entproto 命令行工具。现在它应该看起来像这样:

ent/generate.go
package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
//go:generate go run -mod=mod entgo.io/contrib/entproto/cmd/entproto -path ./schema

重新生成代码:

go generate ./...

注意创建了一个新目录,其中将包含所有与 protobuf 相关的生成代码:ent/proto。现在包含:

ent/proto
└── entpb
├── entpb.proto
└── generate.go

创建了两个文件。查看它们的内容:

ent/proto/entpb/entpb.proto
// Code generated by entproto. DO NOT EDIT.
syntax = "proto3";

package entpb;

option go_package = "ent-grpc-example/ent/proto/entpb";

message User {
int32 id = 1;

string user_name = 2;

string email_address = 3;
}

很好!创建了一个新的 .proto 文件,其中包含映射到我们 User schema 的消息类型定义!

ent/proto/entpb/generate.go
package entpb
//go:generate protoc -I=.. --go_out=.. --go-grpc_out=.. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative --entgrpc_out=.. --entgrpc_opt=paths=source_relative,schema_path=../../schema entpb/entpb.proto

创建了一个新的 generate.go 文件,其中包含对 protoc(protobuf 代码生成器)的调用指令,指导它如何从我们的 .proto 文件生成 Go 代码。要使此命令工作,必须首先安装 protoc 以及 3 个 protobuf 插件:protoc-gen-go(生成 Go Protobuf 结构体)、protoc-gen-go-grpc(生成 Go gRPC 服务接口和客户端)和 protoc-gen-entgrpc(生成服务接口的实现)。如果尚未安装,请按照以下说明操作:

安装这些依赖项后,重新运行代码生成:

go generate ./...

注意创建了一个名为 ent/proto/entpb/entpb.pb.go 的新文件,其中包含为我们实体生成的 Go 结构体。

编写一个测试来验证一切是否正确连接。创建名为 pb_test.go 的新文件并编写:

package main

import (
"testing"

"ent-grpc-example/ent/proto/entpb"
)

func TestUserProto(t *testing.T) {
user := entpb.User{
Name: "rotemtam",
EmailAddress: "rotemtam@example.com",
}
if user.GetName() != "rotemtam" {
t.Fatal("expected user name to be rotemtam")
}
if user.GetEmailAddress() != "rotemtam@example.com" {
t.Fatal("expected email address to be rotemtam@example.com")
}
}

运行测试:

go get -u ./... # 安装生成包的依赖
go test ./...

太好了!测试通过。我们已经成功从 Ent schema 生成了可工作的 Go Protobuf 结构体。接下来,看看如何从 schema 自动生成可工作的 CRUD gRPC 服务器