mirror of
https://github.com/ent/ent.git
synced 2026-04-28 13:40:56 +03:00
doc/md: a detailed tutorial on the gRPC integration (#1663)
This commit is contained in:
97
doc/md/tutorial-grpc-optional-fields.md
Normal file
97
doc/md/tutorial-grpc-optional-fields.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: grpc-optional-fields
|
||||
title: Optional Fields
|
||||
sidebar_label: Optional Fields
|
||||
---
|
||||
A common issue with Protobufs is that the way that nil values are represented: a zero-valued primitive field isn't
|
||||
encoded into the binary representation, this means that applications cannot distinguish between zero and not-set for
|
||||
primitive fields.
|
||||
|
||||
To support this, the Protobuf project supports some [Well-Known types](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) called "wrapper types".
|
||||
For example, the wrapper type for a `bool`, is called `google.protobuf.BoolValue` and is [defined as](https://github.com/protocolbuffers/protobuf/blob/991bcada050d7e9919503adef5b52547ec249d35/src/google/protobuf/wrappers.proto#L103-L107):
|
||||
```protobuf
|
||||
// Wrapper message for `bool`.
|
||||
//
|
||||
// The JSON representation for `BoolValue` is JSON `true` and `false`.
|
||||
message BoolValue {
|
||||
// The bool value.
|
||||
bool value = 1;
|
||||
}
|
||||
```
|
||||
When `entproto` generates a Protobuf message definition, it uses these wrapper types to represent "Optional" ent fields.
|
||||
|
||||
Let's see this in action, modifying our ent schema to include an optional field:
|
||||
|
||||
```go {14-16}
|
||||
// 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),
|
||||
),
|
||||
field.String("alias").
|
||||
Optional().
|
||||
Annotations(entproto.Field(4)),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Re-running `go generate ./...`, observe that our Protobuf definition for `User` now looks like:
|
||||
|
||||
```protobuf {8}
|
||||
message User {
|
||||
int32 id = 1;
|
||||
|
||||
string name = 2;
|
||||
|
||||
string email_address = 3;
|
||||
|
||||
google.protobuf.StringValue alias = 4; // <-- this is new
|
||||
}
|
||||
```
|
||||
|
||||
The generated service implementation also utilize this field. Observe in `entpb_user_service.go`:
|
||||
|
||||
```go {5-7}
|
||||
// Create implements UserServiceServer.Create
|
||||
func (svc *UserService) Create(ctx context.Context, req *CreateUserRequest) (*User, error) {
|
||||
user := req.GetUser()
|
||||
m := svc.client.User.Create()
|
||||
if user.GetAlias() != nil {
|
||||
m.SetAlias(user.GetAlias().GetValue())
|
||||
}
|
||||
m.SetEmailAddress(user.GetEmailAddress())
|
||||
m.SetName(user.GetName())
|
||||
res, err := m.Save(ctx)
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
return toProtoUser(res), nil
|
||||
case sqlgraph.IsUniqueConstraintError(err):
|
||||
return nil, status.Errorf(codes.AlreadyExists, "already exists: %s", err)
|
||||
case ent.IsConstraintError(err):
|
||||
return nil, status.Errorf(codes.InvalidArgument, "invalid argument: %s", err)
|
||||
default:
|
||||
return nil, status.Errorf(codes.Internal, "internal: %s", err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To use the wrapper types in our client code, we can use helper methods supplied by the [wrapperspb](https://github.com/protocolbuffers/protobuf-go/blob/3f51f05e40d61e930a5416f1ed7092cef14cc058/types/known/wrapperspb/wrappers.pb.go#L458-L460)
|
||||
package to easily build instances of these types. For example in `cmd/client/main.go`:
|
||||
```go {5}
|
||||
func randomUser() *entpb.User {
|
||||
return &entpb.User{
|
||||
Name: fmt.Sprintf("user_%d", rand.Int()),
|
||||
EmailAddress: fmt.Sprintf("user_%d@example.com", rand.Int()),
|
||||
Alias: wrapperspb.String("John Doe"),
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user