From d82c1b6a05cedd6de80ed77aadb0af257238c301 Mon Sep 17 00:00:00 2001 From: Simon Wenmouth Date: Mon, 15 Jul 2024 22:57:02 -0500 Subject: [PATCH 1/2] protoc-gen-ent: support `entgql.Annotation` Added support for `entgql.Annotation` to the `protoc-gen-ent` plugin. It should now be possible to generate an Ent schema and its GraphQL configuration from a Protocol Buffer file. The `opts.proto` was updated adding `GQL` child MessageTypes to the existing MessageTypes (`Schema`, `Field`, `Edge`). The associated generated code (`opts.pb.go`) was regenerated using `protoc-gen-go`. The AST generation code in `schemaast/annotation.go` was updated to know about the `entgql` annotations. The new method attempts to generate code similar to that in the documentation. The main package was updated to map the configuration settings in the updated `opts.proto` to the corresponding annotations for the schema, fields, and edges. --- entproto/cmd/protoc-gen-ent/main.go | 119 +++- .../cmd/protoc-gen-ent/options/ent/opts.pb.go | 567 +++++++++++++++--- .../cmd/protoc-gen-ent/options/ent/opts.proto | 32 + schemast/annotation.go | 65 ++ 4 files changed, 698 insertions(+), 85 deletions(-) diff --git a/entproto/cmd/protoc-gen-ent/main.go b/entproto/cmd/protoc-gen-ent/main.go index 0a0f7c9e0..3c4112fe4 100644 --- a/entproto/cmd/protoc-gen-ent/main.go +++ b/entproto/cmd/protoc-gen-ent/main.go @@ -17,10 +17,13 @@ package main import ( "flag" "fmt" + "strings" + "entgo.io/contrib/entgql" entopts "entgo.io/contrib/entproto/cmd/protoc-gen-ent/options/ent" "entgo.io/contrib/schemast" "entgo.io/ent" + "entgo.io/ent/schema" "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "google.golang.org/protobuf/compiler/protogen" @@ -108,8 +111,37 @@ func toSchema(m *protogen.Message, opts *entopts.Schema) (*schemast.UpsertSchema if opts.Name != nil { name = opts.GetName() } + var annotations []schema.Annotation + gql := opts.GetGql() + if gql != nil { + if gql.GetQueryField() { + if gql.GetQueryFieldName() != "" { + annotations = append(annotations, entgql.QueryField(gql.GetQueryFieldName()).Annotation) + } else { + annotations = append(annotations, entgql.QueryField().Annotation) + } + } + if gql.GetType() != "" { + annotations = append(annotations, entgql.Type(gql.GetType())) + } + if gql.GetRelayConnection() { + annotations = append(annotations, entgql.RelayConnection()) + } + var create, update = gql.GetMutationCreate(), gql.GetMutationUpdate() + if create || update { + var options []entgql.MutationOption + if create { + options = append(options, entgql.MutationCreate()) + } + if update { + options = append(options, entgql.MutationUpdate()) + } + annotations = append(annotations, entgql.Mutations(options...)) + } + } out := &schemast.UpsertSchema{ - Name: name, + Name: name, + Annotations: annotations, } for _, f := range m.Fields { if isEdge(f) { @@ -130,7 +162,16 @@ func toSchema(m *protogen.Message, opts *entopts.Schema) (*schemast.UpsertSchema } func isEdge(f *protogen.Field) bool { - return f.Desc.Kind() == protoreflect.MessageKind + isMessageKind := f.Desc.Kind() == protoreflect.MessageKind + if isMessageKind { + switch f.Desc.Message().FullName() { + case "google.protobuf.Timestamp": + return false + case "google.type.Date": + return false + } + } + return isMessageKind } func toEdge(f *protogen.Field) (ent.Edge, error) { @@ -194,6 +235,15 @@ func toField(f *protogen.Field) (ent.Field, error) { values = append(values, string(pbEnum.Get(i).Name())) } fld = field.Enum(name).Values(values...) + case protoreflect.MessageKind: + switch f.Desc.Message().FullName() { + case "google.protobuf.Timestamp": + fld = field.Time(name) + case "google.type.Date": + fld = field.Time(name) + default: + return nil, fmt.Errorf("protoc-gen-ent: unsupported kind %q", f.Desc.Kind()) + } default: return nil, fmt.Errorf("protoc-gen-ent: unsupported kind %q", f.Desc.Kind()) } @@ -214,6 +264,43 @@ func applyFieldOpts(fld ent.Field, opts *entopts.Field) { d.Tag = opts.GetStructTag() d.StorageKey = opts.GetStorageKey() d.SchemaType = opts.GetSchemaType() + + gql := opts.GetGql() + if gql != nil { + var annotations []schema.Annotation + if gql.GetOrderField() { + if gql.GetOrderFieldName() != "" { + annotations = append(annotations, entgql.OrderField(gql.GetOrderFieldName())) + } else { + annotations = append(annotations, entgql.OrderField(strings.ToUpper(fld.Descriptor().Name))) + } + } + if gql.GetType() != "" { + annotations = append(annotations, entgql.Type(gql.GetType())) + } + skipType, skipEnum, skipOrder, skipWhere, skipCreate, skipUpdate := gql.GetSkipType(), gql.GetSkipEnumField(), gql.GetSkipOrderField(), gql.GetSkipWhereInput(), gql.GetSkipMutationCreateInput(), gql.GetSkipMutationUpdateInput() + if skipType || skipEnum || skipOrder || skipWhere || skipCreate || skipUpdate { + var skipModeVals = []bool{ + skipType, skipEnum, skipOrder, skipWhere, skipCreate, skipUpdate, + } + var skipModeList = []entgql.SkipMode{ + entgql.SkipType, + entgql.SkipEnumField, + entgql.SkipOrderField, + entgql.SkipWhereInput, + entgql.SkipMutationCreateInput, + entgql.SkipMutationUpdateInput, + } + var skipMode entgql.SkipMode + for i, mode := range skipModeList { + if skipModeVals[i] { + skipMode |= mode + } + } + annotations = append(annotations, entgql.Skip(skipMode)) + } + d.Annotations = annotations + } } func applyEdgeOpts(edg ent.Edge, opts *entopts.Edge) { @@ -229,6 +316,34 @@ func applyEdgeOpts(edg ent.Edge, opts *entopts.Edge) { Columns: sk.GetColumns(), } } + + gql := opts.GetGql() + if gql != nil { + var annotations []schema.Annotation + skipType, skipWhere, skipCreate, skipUpdate := gql.GetSkipType(), gql.GetSkipWhereInput(), gql.GetSkipMutationCreateInput(), gql.GetSkipMutationUpdateInput() + if skipType || skipWhere || skipCreate || skipUpdate { + var skipModeVals = []bool{ + skipType, skipWhere, skipCreate, skipUpdate, + } + var skipModeList = []entgql.SkipMode{ + entgql.SkipType, + entgql.SkipWhereInput, + entgql.SkipMutationCreateInput, + entgql.SkipMutationUpdateInput, + } + var skipMode entgql.SkipMode + for i, mode := range skipModeList { + if skipModeVals[i] { + skipMode |= mode + } + } + annotations = append(annotations, entgql.Skip(skipMode)) + } + if gql.GetRelayConnection() { + annotations = append(annotations, entgql.RelayConnection()) + } + d.Annotations = annotations + } } type placeholder struct { diff --git a/entproto/cmd/protoc-gen-ent/options/ent/opts.pb.go b/entproto/cmd/protoc-gen-ent/options/ent/opts.pb.go index 04c73c316..753baedf9 100644 --- a/entproto/cmd/protoc-gen-ent/options/ent/opts.pb.go +++ b/entproto/cmd/protoc-gen-ent/options/ent/opts.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 +// protoc-gen-go v1.28.1 // protoc v3.19.4 // source: opts.proto @@ -26,8 +26,9 @@ type Schema struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Gen *bool `protobuf:"varint,1,opt,name=gen" json:"gen,omitempty"` - Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + Gen *bool `protobuf:"varint,1,opt,name=gen" json:"gen,omitempty"` + Name *string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` + Gql *Schema_GQL `protobuf:"bytes,3,opt,name=gql" json:"gql,omitempty"` } func (x *Schema) Reset() { @@ -76,6 +77,13 @@ func (x *Schema) GetName() string { return "" } +func (x *Schema) GetGql() *Schema_GQL { + if x != nil { + return x.Gql + } + return nil +} + type Field struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -90,6 +98,7 @@ type Field struct { StructTag *string `protobuf:"bytes,7,opt,name=struct_tag,json=structTag" json:"struct_tag,omitempty"` StorageKey *string `protobuf:"bytes,8,opt,name=storage_key,json=storageKey" json:"storage_key,omitempty"` SchemaType map[string]string `protobuf:"bytes,9,rep,name=schema_type,json=schemaType" json:"schema_type,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Gql *Field_GQL `protobuf:"bytes,10,opt,name=gql" json:"gql,omitempty"` } func (x *Field) Reset() { @@ -187,6 +196,13 @@ func (x *Field) GetSchemaType() map[string]string { return nil } +func (x *Field) GetGql() *Field_GQL { + if x != nil { + return x.Gql + } + return nil +} + type Edge struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -198,6 +214,7 @@ type Edge struct { Field *string `protobuf:"bytes,4,opt,name=field" json:"field,omitempty"` StorageKey *Edge_StorageKey `protobuf:"bytes,5,opt,name=storage_key,json=storageKey" json:"storage_key,omitempty"` StructTag *string `protobuf:"bytes,6,opt,name=struct_tag,json=structTag" json:"struct_tag,omitempty"` + Gql *Edge_GQL `protobuf:"bytes,7,opt,name=gql" json:"gql,omitempty"` } func (x *Edge) Reset() { @@ -274,6 +291,211 @@ func (x *Edge) GetStructTag() string { return "" } +func (x *Edge) GetGql() *Edge_GQL { + if x != nil { + return x.Gql + } + return nil +} + +type Schema_GQL struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Type *string `protobuf:"bytes,1,opt,name=type" json:"type,omitempty"` + MutationCreate *bool `protobuf:"varint,2,opt,name=mutationCreate" json:"mutationCreate,omitempty"` + MutationUpdate *bool `protobuf:"varint,3,opt,name=mutationUpdate" json:"mutationUpdate,omitempty"` + QueryField *bool `protobuf:"varint,4,opt,name=queryField" json:"queryField,omitempty"` + QueryFieldName *string `protobuf:"bytes,5,opt,name=queryFieldName" json:"queryFieldName,omitempty"` + RelayConnection *bool `protobuf:"varint,6,opt,name=relayConnection" json:"relayConnection,omitempty"` +} + +func (x *Schema_GQL) Reset() { + *x = Schema_GQL{} + if protoimpl.UnsafeEnabled { + mi := &file_opts_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Schema_GQL) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Schema_GQL) ProtoMessage() {} + +func (x *Schema_GQL) ProtoReflect() protoreflect.Message { + mi := &file_opts_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Schema_GQL.ProtoReflect.Descriptor instead. +func (*Schema_GQL) Descriptor() ([]byte, []int) { + return file_opts_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Schema_GQL) GetType() string { + if x != nil && x.Type != nil { + return *x.Type + } + return "" +} + +func (x *Schema_GQL) GetMutationCreate() bool { + if x != nil && x.MutationCreate != nil { + return *x.MutationCreate + } + return false +} + +func (x *Schema_GQL) GetMutationUpdate() bool { + if x != nil && x.MutationUpdate != nil { + return *x.MutationUpdate + } + return false +} + +func (x *Schema_GQL) GetQueryField() bool { + if x != nil && x.QueryField != nil { + return *x.QueryField + } + return false +} + +func (x *Schema_GQL) GetQueryFieldName() string { + if x != nil && x.QueryFieldName != nil { + return *x.QueryFieldName + } + return "" +} + +func (x *Schema_GQL) GetRelayConnection() bool { + if x != nil && x.RelayConnection != nil { + return *x.RelayConnection + } + return false +} + +type Field_GQL struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrderField *bool `protobuf:"varint,1,opt,name=orderField" json:"orderField,omitempty"` + OrderFieldName *string `protobuf:"bytes,2,opt,name=orderFieldName" json:"orderFieldName,omitempty"` + Type *string `protobuf:"bytes,3,opt,name=type" json:"type,omitempty"` + SkipType *bool `protobuf:"varint,4,opt,name=skipType" json:"skipType,omitempty"` + SkipEnumField *bool `protobuf:"varint,5,opt,name=skipEnumField" json:"skipEnumField,omitempty"` + SkipOrderField *bool `protobuf:"varint,6,opt,name=skipOrderField" json:"skipOrderField,omitempty"` + SkipWhereInput *bool `protobuf:"varint,7,opt,name=skipWhereInput" json:"skipWhereInput,omitempty"` + SkipMutationCreateInput *bool `protobuf:"varint,8,opt,name=skipMutationCreateInput" json:"skipMutationCreateInput,omitempty"` + SkipMutationUpdateInput *bool `protobuf:"varint,9,opt,name=skipMutationUpdateInput" json:"skipMutationUpdateInput,omitempty"` +} + +func (x *Field_GQL) Reset() { + *x = Field_GQL{} + if protoimpl.UnsafeEnabled { + mi := &file_opts_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Field_GQL) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Field_GQL) ProtoMessage() {} + +func (x *Field_GQL) ProtoReflect() protoreflect.Message { + mi := &file_opts_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Field_GQL.ProtoReflect.Descriptor instead. +func (*Field_GQL) Descriptor() ([]byte, []int) { + return file_opts_proto_rawDescGZIP(), []int{1, 1} +} + +func (x *Field_GQL) GetOrderField() bool { + if x != nil && x.OrderField != nil { + return *x.OrderField + } + return false +} + +func (x *Field_GQL) GetOrderFieldName() string { + if x != nil && x.OrderFieldName != nil { + return *x.OrderFieldName + } + return "" +} + +func (x *Field_GQL) GetType() string { + if x != nil && x.Type != nil { + return *x.Type + } + return "" +} + +func (x *Field_GQL) GetSkipType() bool { + if x != nil && x.SkipType != nil { + return *x.SkipType + } + return false +} + +func (x *Field_GQL) GetSkipEnumField() bool { + if x != nil && x.SkipEnumField != nil { + return *x.SkipEnumField + } + return false +} + +func (x *Field_GQL) GetSkipOrderField() bool { + if x != nil && x.SkipOrderField != nil { + return *x.SkipOrderField + } + return false +} + +func (x *Field_GQL) GetSkipWhereInput() bool { + if x != nil && x.SkipWhereInput != nil { + return *x.SkipWhereInput + } + return false +} + +func (x *Field_GQL) GetSkipMutationCreateInput() bool { + if x != nil && x.SkipMutationCreateInput != nil { + return *x.SkipMutationCreateInput + } + return false +} + +func (x *Field_GQL) GetSkipMutationUpdateInput() bool { + if x != nil && x.SkipMutationUpdateInput != nil { + return *x.SkipMutationUpdateInput + } + return false +} + type Edge_StorageKey struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -286,7 +508,7 @@ type Edge_StorageKey struct { func (x *Edge_StorageKey) Reset() { *x = Edge_StorageKey{} if protoimpl.UnsafeEnabled { - mi := &file_opts_proto_msgTypes[4] + mi := &file_opts_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -299,7 +521,7 @@ func (x *Edge_StorageKey) String() string { func (*Edge_StorageKey) ProtoMessage() {} func (x *Edge_StorageKey) ProtoReflect() protoreflect.Message { - mi := &file_opts_proto_msgTypes[4] + mi := &file_opts_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -329,6 +551,85 @@ func (x *Edge_StorageKey) GetColumns() []string { return nil } +type Edge_GQL struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RelayConnection *bool `protobuf:"varint,1,opt,name=relayConnection" json:"relayConnection,omitempty"` + SkipType *bool `protobuf:"varint,2,opt,name=skipType" json:"skipType,omitempty"` + SkipWhereInput *bool `protobuf:"varint,3,opt,name=skipWhereInput" json:"skipWhereInput,omitempty"` + SkipMutationCreateInput *bool `protobuf:"varint,4,opt,name=skipMutationCreateInput" json:"skipMutationCreateInput,omitempty"` + SkipMutationUpdateInput *bool `protobuf:"varint,5,opt,name=skipMutationUpdateInput" json:"skipMutationUpdateInput,omitempty"` +} + +func (x *Edge_GQL) Reset() { + *x = Edge_GQL{} + if protoimpl.UnsafeEnabled { + mi := &file_opts_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Edge_GQL) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Edge_GQL) ProtoMessage() {} + +func (x *Edge_GQL) ProtoReflect() protoreflect.Message { + mi := &file_opts_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Edge_GQL.ProtoReflect.Descriptor instead. +func (*Edge_GQL) Descriptor() ([]byte, []int) { + return file_opts_proto_rawDescGZIP(), []int{2, 1} +} + +func (x *Edge_GQL) GetRelayConnection() bool { + if x != nil && x.RelayConnection != nil { + return *x.RelayConnection + } + return false +} + +func (x *Edge_GQL) GetSkipType() bool { + if x != nil && x.SkipType != nil { + return *x.SkipType + } + return false +} + +func (x *Edge_GQL) GetSkipWhereInput() bool { + if x != nil && x.SkipWhereInput != nil { + return *x.SkipWhereInput + } + return false +} + +func (x *Edge_GQL) GetSkipMutationCreateInput() bool { + if x != nil && x.SkipMutationCreateInput != nil { + return *x.SkipMutationCreateInput + } + return false +} + +func (x *Edge_GQL) GetSkipMutationUpdateInput() bool { + if x != nil && x.SkipMutationUpdateInput != nil { + return *x.SkipMutationUpdateInput + } + return false +} + var file_opts_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*descriptorpb.MessageOptions)(nil), @@ -376,64 +677,122 @@ var file_opts_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x6f, 0x70, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x65, 0x6e, 0x74, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x2e, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a, - 0x03, 0x67, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x67, 0x65, 0x6e, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x22, 0xe9, 0x02, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x6c, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x69, 0x6c, - 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, 0x1c, 0x0a, - 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x69, - 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, - 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, - 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, - 0x61, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x4b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x46, - 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x79, 0x70, 0x65, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x79, 0x70, 0x65, - 0x1a, 0x3d, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x79, 0x70, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0xf6, 0x01, 0x0a, 0x04, 0x45, 0x64, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, - 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, - 0x65, 0x66, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x6e, 0x74, 0x2e, - 0x45, 0x64, 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x52, - 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, - 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, 0x61, 0x67, 0x1a, 0x3c, 0x0a, 0x0a, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x3a, 0x46, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, - 0x6d, 0x61, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0xe7, 0x94, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x65, 0x6e, - 0x74, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, - 0x3a, 0x41, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe7, 0x94, 0x09, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0a, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x3a, 0x3e, 0x0a, 0x04, 0x65, 0x64, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, - 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe8, 0x94, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x52, 0x04, 0x65, - 0x64, 0x67, 0x65, 0x42, 0x3a, 0x5a, 0x38, 0x65, 0x6e, 0x74, 0x67, 0x6f, 0x2e, 0x69, 0x6f, 0x2f, - 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x2f, 0x65, 0x6e, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, - 0x65, 0x6e, 0x74, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x74, + 0x6f, 0x74, 0x6f, 0x22, 0xaf, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, + 0x0a, 0x03, 0x67, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x67, 0x65, 0x6e, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x03, 0x67, 0x71, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x47, + 0x51, 0x4c, 0x52, 0x03, 0x67, 0x71, 0x6c, 0x1a, 0xdb, 0x01, 0x0a, 0x03, 0x47, 0x51, 0x4c, 0x12, + 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x6d, + 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x71, 0x75, 0x65, + 0x72, 0x79, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x72, + 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xf5, 0x05, 0x0a, 0x05, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x1a, 0x0a, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x08, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x6e, + 0x69, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, + 0x69, 0x6c, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x1c, 0x0a, + 0x09, 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x09, 0x69, 0x6d, 0x6d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, + 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x5f, + 0x74, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x72, 0x75, 0x63, + 0x74, 0x54, 0x61, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x0b, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x65, 0x6e, 0x74, + 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x79, 0x70, + 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x20, 0x0a, 0x03, 0x67, 0x71, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0e, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x2e, 0x47, 0x51, 0x4c, 0x52, + 0x03, 0x67, 0x71, 0x6c, 0x1a, 0x3d, 0x0a, 0x0f, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x54, 0x79, + 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0xe7, 0x02, 0x0a, 0x03, 0x47, 0x51, 0x4c, 0x12, 0x1e, 0x0a, 0x0a, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x6f, + 0x72, 0x64, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x6b, 0x69, 0x70, 0x54, + 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x6b, 0x69, 0x70, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x6b, 0x69, 0x70, 0x45, 0x6e, 0x75, 0x6d, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x6b, 0x69, 0x70, + 0x45, 0x6e, 0x75, 0x6d, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x6b, 0x69, + 0x70, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x57, 0x68, 0x65, 0x72, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x57, + 0x68, 0x65, 0x72, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x38, 0x0a, 0x17, 0x73, 0x6b, 0x69, + 0x70, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x73, 0x6b, 0x69, 0x70, + 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x12, 0x38, 0x0a, 0x17, 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x81, 0x04, + 0x0a, 0x04, 0x45, 0x64, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, + 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x12, 0x35, 0x0a, 0x0b, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x64, + 0x67, 0x65, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x0a, 0x73, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x72, + 0x75, 0x63, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, + 0x74, 0x72, 0x75, 0x63, 0x74, 0x54, 0x61, 0x67, 0x12, 0x1f, 0x0a, 0x03, 0x67, 0x71, 0x6c, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x64, 0x67, 0x65, + 0x2e, 0x47, 0x51, 0x4c, 0x52, 0x03, 0x67, 0x71, 0x6c, 0x1a, 0x3c, 0x0a, 0x0a, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, + 0x07, 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, + 0x63, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x73, 0x1a, 0xe7, 0x01, 0x0a, 0x03, 0x47, 0x51, 0x4c, 0x12, + 0x28, 0x0a, 0x0f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x43, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x6b, 0x69, + 0x70, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x73, 0x6b, 0x69, + 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x6b, 0x69, 0x70, 0x57, 0x68, 0x65, + 0x72, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x73, + 0x6b, 0x69, 0x70, 0x57, 0x68, 0x65, 0x72, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x38, 0x0a, + 0x17, 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, + 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x38, 0x0a, 0x17, 0x73, 0x6b, 0x69, 0x70, 0x4d, + 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x17, 0x73, 0x6b, 0x69, 0x70, 0x4d, 0x75, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x3a, 0x46, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1f, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0xe7, 0x94, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, + 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x3a, 0x41, 0x0a, 0x05, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0xe7, 0x94, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x65, 0x6e, 0x74, 0x2e, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x3e, 0x0a, 0x04, + 0x65, 0x64, 0x67, 0x65, 0x12, 0x1d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0xe8, 0x94, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x65, 0x6e, + 0x74, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x52, 0x04, 0x65, 0x64, 0x67, 0x65, 0x42, 0x3a, 0x5a, 0x38, + 0x65, 0x6e, 0x74, 0x67, 0x6f, 0x2e, 0x69, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, + 0x2f, 0x65, 0x6e, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x65, 0x6e, 0x74, 0x2f, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x65, 0x6e, 0x74, } var ( @@ -448,30 +807,36 @@ func file_opts_proto_rawDescGZIP() []byte { return file_opts_proto_rawDescData } -var file_opts_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_opts_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_opts_proto_goTypes = []interface{}{ (*Schema)(nil), // 0: ent.Schema (*Field)(nil), // 1: ent.Field (*Edge)(nil), // 2: ent.Edge - nil, // 3: ent.Field.SchemaTypeEntry - (*Edge_StorageKey)(nil), // 4: ent.Edge.StorageKey - (*descriptorpb.MessageOptions)(nil), // 5: google.protobuf.MessageOptions - (*descriptorpb.FieldOptions)(nil), // 6: google.protobuf.FieldOptions + (*Schema_GQL)(nil), // 3: ent.Schema.GQL + nil, // 4: ent.Field.SchemaTypeEntry + (*Field_GQL)(nil), // 5: ent.Field.GQL + (*Edge_StorageKey)(nil), // 6: ent.Edge.StorageKey + (*Edge_GQL)(nil), // 7: ent.Edge.GQL + (*descriptorpb.MessageOptions)(nil), // 8: google.protobuf.MessageOptions + (*descriptorpb.FieldOptions)(nil), // 9: google.protobuf.FieldOptions } var file_opts_proto_depIdxs = []int32{ - 3, // 0: ent.Field.schema_type:type_name -> ent.Field.SchemaTypeEntry - 4, // 1: ent.Edge.storage_key:type_name -> ent.Edge.StorageKey - 5, // 2: ent.schema:extendee -> google.protobuf.MessageOptions - 6, // 3: ent.field:extendee -> google.protobuf.FieldOptions - 6, // 4: ent.edge:extendee -> google.protobuf.FieldOptions - 0, // 5: ent.schema:type_name -> ent.Schema - 1, // 6: ent.field:type_name -> ent.Field - 2, // 7: ent.edge:type_name -> ent.Edge - 8, // [8:8] is the sub-list for method output_type - 8, // [8:8] is the sub-list for method input_type - 5, // [5:8] is the sub-list for extension type_name - 2, // [2:5] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 3, // 0: ent.Schema.gql:type_name -> ent.Schema.GQL + 4, // 1: ent.Field.schema_type:type_name -> ent.Field.SchemaTypeEntry + 5, // 2: ent.Field.gql:type_name -> ent.Field.GQL + 6, // 3: ent.Edge.storage_key:type_name -> ent.Edge.StorageKey + 7, // 4: ent.Edge.gql:type_name -> ent.Edge.GQL + 8, // 5: ent.schema:extendee -> google.protobuf.MessageOptions + 9, // 6: ent.field:extendee -> google.protobuf.FieldOptions + 9, // 7: ent.edge:extendee -> google.protobuf.FieldOptions + 0, // 8: ent.schema:type_name -> ent.Schema + 1, // 9: ent.field:type_name -> ent.Field + 2, // 10: ent.edge:type_name -> ent.Edge + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 8, // [8:11] is the sub-list for extension type_name + 5, // [5:8] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_opts_proto_init() } @@ -516,7 +881,31 @@ func file_opts_proto_init() { return nil } } - file_opts_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_opts_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Schema_GQL); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opts_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Field_GQL); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_opts_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Edge_StorageKey); i { case 0: return &v.state @@ -528,6 +917,18 @@ func file_opts_proto_init() { return nil } } + file_opts_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Edge_GQL); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -535,7 +936,7 @@ func file_opts_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_opts_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 8, NumExtensions: 3, NumServices: 0, }, diff --git a/entproto/cmd/protoc-gen-ent/options/ent/opts.proto b/entproto/cmd/protoc-gen-ent/options/ent/opts.proto index 9b0bc53a1..478beb71e 100644 --- a/entproto/cmd/protoc-gen-ent/options/ent/opts.proto +++ b/entproto/cmd/protoc-gen-ent/options/ent/opts.proto @@ -7,6 +7,16 @@ option go_package = "entgo.io/contrib/entproto/cmd/protoc-gen-ent/options/ent"; message Schema { optional bool gen = 1; optional string name = 2; + optional GQL gql = 3; + + message GQL { + optional string type = 1; + optional bool mutationCreate = 2; + optional bool mutationUpdate = 3; + optional bool queryField = 4; + optional string queryFieldName = 5; + optional bool relayConnection = 6; + } } extend google.protobuf.MessageOptions { @@ -23,6 +33,19 @@ message Field { optional string struct_tag = 7; optional string storage_key = 8; map schema_type = 9; + optional GQL gql = 10; + + message GQL { + optional bool orderField = 1; + optional string orderFieldName = 2; + optional string type = 3; + optional bool skipType = 4; + optional bool skipEnumField = 5; + optional bool skipOrderField = 6; + optional bool skipWhereInput = 7; + optional bool skipMutationCreateInput = 8; + optional bool skipMutationUpdateInput = 9; + } } message Edge { @@ -32,11 +55,20 @@ message Edge { optional string field = 4; optional StorageKey storage_key = 5; optional string struct_tag = 6; + optional GQL gql = 7; message StorageKey { optional string table = 1; repeated string columns = 2; } + + message GQL { + optional bool relayConnection = 1; + optional bool skipType = 2; + optional bool skipWhereInput = 3; + optional bool skipMutationCreateInput = 4; + optional bool skipMutationUpdateInput = 5; + } } extend google.protobuf.FieldOptions { diff --git a/schemast/annotation.go b/schemast/annotation.go index efecc6250..ebf77ae25 100644 --- a/schemast/annotation.go +++ b/schemast/annotation.go @@ -17,8 +17,10 @@ package schemast import ( "fmt" "go/ast" + "go/token" "sort" + "entgo.io/contrib/entgql" "entgo.io/contrib/entproto" "entgo.io/ent/dialect/entsql" "entgo.io/ent/schema" @@ -48,6 +50,7 @@ func Annotation(annot schema.Annotation) (ast.Expr, bool, error) { entproto.FieldAnnotation: protoField, entproto.EnumAnnotation: protoEnum, "EntSQL": entSQL, + "EntGQL": entGQL, } fn, ok := annotators[annot.Name()] if !ok { @@ -186,6 +189,68 @@ func entSQL(annot schema.Annotation) (ast.Expr, bool, error) { return c, true, nil } +func entGQL(annot schema.Annotation) (ast.Expr, bool, error) { + m := &entgql.Annotation{} + if err := mapstructure.Decode(annot, m); err != nil { + return nil, false, err + } + if m.OrderField != "" { + return fnCall(selectorLit("entgql", "OrderField"), strLit(m.OrderField)), true, nil + } + if m.MultiOrder { + return fnCall(selectorLit("entgql", "MultiOrder")), true, nil + } + if m.Type != "" { + return fnCall(selectorLit("entgql", "Type"), strLit(m.Type)), true, nil + } + if m.Skip != 0 { + var skipModeList = []*ast.SelectorExpr{ + selectorLit("entgql", "SkipType"), + selectorLit("entgql", "SkipEnumField"), + selectorLit("entgql", "SkipOrderField"), + selectorLit("entgql", "SkipWhereInput"), + selectorLit("entgql", "SkipMutationCreateInput"), + selectorLit("entgql", "SkipMutationUpdateInput")} + var skip []*ast.SelectorExpr + for i, expr := range skipModeList { + if m.Skip&(1< 0 { + var options []ast.Expr + for _, mutation := range m.MutationInputs { + if mutation.IsCreate { + options = append(options, fnCall(selectorLit("entgql", "MutationCreate"))) + } else { + options = append(options, fnCall(selectorLit("entgql", "MutationUpdate"))) + } + } + return fnCall(selectorLit("entgql", "Mutations"), options...), true, nil + } + return &ast.CompositeLit{Type: selectorLit("entgql", "Annotation")}, true, nil +} + func toAnnotASTs(annots []schema.Annotation) ([]ast.Expr, error) { out := make([]ast.Expr, 0, len(annots)) for _, annot := range annots { From 7b3f10027f76e0eee4171bac221d913adb77b829 Mon Sep 17 00:00:00 2001 From: Simon Wenmouth Date: Fri, 10 Jan 2025 13:04:49 -0600 Subject: [PATCH 2/2] entgql: pagination name overrides When a GraphQL type extends a definition in another sub-graph the names of the pagination types clash, in particular the where-inputs. Rather than taking the union of the properties when creating the supergraph, it takes the common properties. This is true whether your input types are declared with `type` or `extend type` (or so it seems). It doesn't make sense to copy-paste the where-input types between projects, since Ent cannot use them (would mean joining across databases). This commit allows the rename of the pagination types, avoiding the clash described above. The feature is used as follows: entgql.PaginationNameOverrides = map[string]string{ "Domain1_EntityA": "DomainN_Domain1_EntityA", "Domain2_EntityA": "DomainN_Domain2_EntityB", } In this example, our sub-graph (DomainN) is providing type extensions for entities in two different sub-graphs (Domain1, Domain2). In this manner, we will not get collisions when multiple sub-graphs extend a particular type. This is combined with a `entgql.WithOutputWriter` that uses a fork of `github.com/vektah/gqlparser/v2@v2.5.20/formatter/formatter.go` that overrides `FormatSchema` to accept a set of extension types. --- entgql/template.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/entgql/template.go b/entgql/template.go index 69f20dc3d..6223883be 100644 --- a/entgql/template.go +++ b/entgql/template.go @@ -96,6 +96,8 @@ var ( "skipMode": skipModeFromString, } + PaginationNameOverrides = map[string]string{} + //go:embed template/* _templates embed.FS @@ -703,6 +705,17 @@ func nodePaginationNames(t *gen.Type) (*PaginationNames, error) { } func paginationNames(node string) *PaginationNames { + override, exists := PaginationNameOverrides[node] + if exists { + return &PaginationNames{ + Connection: fmt.Sprintf("%sConnection", override), + Edge: fmt.Sprintf("%sEdge", override), + Node: node, + Order: fmt.Sprintf("%sOrder", override), + OrderField: fmt.Sprintf("%sOrderField", override), + WhereInput: fmt.Sprintf("%sWhereInput", override), + } + } return &PaginationNames{ Connection: fmt.Sprintf("%sConnection", node), Edge: fmt.Sprintf("%sEdge", node),