使用 entproto 生成 Protobufs
由于 Ent 和 Protobuf 的模式并不相同,我们必须在模式上提供注解来帮助 entproto 准确识别如何生成 Protobuf 定义(在 protobuf 术语中成为 “消息 Message”)。
我们要做的第一件事情是添加 entproto.Message() 注解。
这是我们对 Protobuf 模式生成的选择性支持,我们无需为 所有 模式实体生成 proto 消息或 gRPC 服务定义,
该注解让我们可以控制这一点。将其追加到 ent/schema/user.go:
func (User) Annotations() []schema.Annotation {
return []schema.Annotation{
entproto.Message(),
}
}
下一步我们需要注解每个字段并为其分配字段编号。
请记住当 定义 protobuf 消息类型 时每个字段都必须分配唯一编号。为做到这一点我们为每个字段添加 entproto.Field 注解。在 ent/schema/user.go 中更新 Fields:
// 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 命令行工具。如下:
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 ./...
可以看到一个名为 ent/proto 的包含所有 protobuf 关联的生成代码的新目录已经被创建了。现在看包括:
ent/proto
└── entpb
├── entpb.proto
└── generate.go
有两个文件被创建了,让我们看一下他们的内容:
// 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;
}
很好!一个包含消息类型定义的映射到 User 模式的新的.proto 文件已经被创建了!
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
一个可以唤起 protoc 的新的 generate.go 文件已经被创建了,protobuf 代码生成器会告诉它如何从 .proto 文件生成 Go 语言代码。
为使这个命令能够运行,我们必须首先安装 protoc 及其三个插件:protoc-gen-go(用来生成 GO 语言 Protobuf 结构体)、
protoc-gen-go-grpc(生成 Go 语言 gRPC 服务接口和客户端)、protoc-gen-entgrpc(生成服务接口的实现)。
如果你没有安装这些,请直接参考如下内容:
-
安装
protoc-gen-entgrpc请运行:go install entgo.io/contrib/entproto/cmd/protoc-gen-entgrpc@master
安装完这些依赖后再次运行代码生成:
go generate ./...
可以看到一个包含为实体而生成 GO 语言结构体的 ent/proto/entpb/entpb.pb.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 ./... # install deps of the generated package
go test ./...
哇!测试通过了。我们成功由 Ent 模式生成了可以工作的 Go 语言 Protobuf 结构体。 下一步我们看一下如何由模式自动生成可以工作的 CRUD gRPC 服务器。