From 1d0d9ea810541203ca3fb0e13053e4bb5688a88c Mon Sep 17 00:00:00 2001 From: utahta Date: Wed, 21 Aug 2024 19:41:27 +0900 Subject: [PATCH 1/3] show warning about file import --- .../federation/federation.pb.go | 25 +- .../federation_grpc_federation.pb.go | 4 - .../federation/other_grpc_federation.pb.go | 2 - .../proto/federation/federation.proto | 4 +- generator/generator.go | 8 +- grpc/federation/cel/bind.go | 16 - grpc/federation/cel/conv.go | 63 ++ internal/testutil/cmpopt.go | 6 +- lsp/server/handler_test.go | 2 +- lsp/server/testdata/service.proto | 5 +- resolver/context.go | 1 - resolver/error.go | 8 + resolver/resolver.go | 293 +++++++- resolver/types.go | 4 + .../testdata/duplicated_variable_name.proto | 4 +- validator/testdata/empty_response_field.proto | 4 +- .../testdata/invalid_call_error_handler.proto | 4 +- .../testdata/invalid_condition_type.proto | 4 +- .../testdata/invalid_enum_alias_target.proto | 4 +- .../testdata/invalid_enum_selector.proto | 4 +- validator/testdata/invalid_field_option.proto | 5 +- validator/testdata/invalid_file_import.proto | 6 +- .../testdata/invalid_message_alias.proto | 4 +- .../invalid_message_alias_target.proto | 4 +- .../testdata/invalid_message_argument.proto | 5 +- .../invalid_message_field_alias.proto | 4 +- validator/testdata/invalid_message_map.proto | 4 +- .../testdata/invalid_message_map_alias.proto | 4 +- validator/testdata/invalid_message_name.proto | 4 +- validator/testdata/invalid_method.proto | 5 +- validator/testdata/invalid_method_name.proto | 5 +- .../testdata/invalid_method_request.proto | 3 + .../testdata/invalid_method_response.proto | 5 +- .../invalid_method_service_name.proto | 5 +- validator/testdata/invalid_multi_alias.proto | 5 +- validator/testdata/invalid_oneof.proto | 3 + validator/testdata/invalid_retry.proto | 5 +- .../invalid_validation_bad_request.proto | 4 +- ...valid_validation_details_return_type.proto | 4 +- ...invalid_validation_localized_message.proto | 4 +- .../invalid_validation_message_argument.proto | 4 +- ...alid_validation_precondition_failure.proto | 4 +- .../invalid_validation_return_type.proto | 4 +- .../testdata/invalid_variable_name.proto | 4 +- validator/testdata/missing_enum_alias.proto | 4 +- validator/testdata/missing_enum_value.proto | 4 +- .../testdata/missing_enum_value_alias.proto | 4 +- validator/testdata/missing_field_option.proto | 5 +- .../testdata/missing_message_alias.proto | 4 +- .../missing_message_field_alias.proto | 4 +- .../testdata/missing_message_option.proto | 5 +- .../missing_method_request_value.proto | 5 +- .../testdata/recursive_message_name.proto | 4 +- .../testdata/valid_enum_value_reference.proto | 4 +- validator/validator_test.go | 648 +++++++++--------- 55 files changed, 816 insertions(+), 444 deletions(-) create mode 100644 grpc/federation/cel/conv.go diff --git a/_examples/11_multi_service/federation/federation.pb.go b/_examples/11_multi_service/federation/federation.pb.go index 361d4a70..4d62a51d 100644 --- a/_examples/11_multi_service/federation/federation.pb.go +++ b/_examples/11_multi_service/federation/federation.pb.go @@ -588,19 +588,18 @@ var file_federation_federation_proto_rawDesc = []byte{ 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x1a, 0x03, 0x9a, 0x4a, 0x00, 0x42, 0xbb, 0x01, 0x9a, 0x4a, 0x30, 0x12, 0x15, 0x63, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x2f, 0x66, 0x61, - 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x0a, 0x0e, 0x63, 0x6f, - 0x6d, 0x2e, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0f, 0x46, 0x65, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x1d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x3b, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xa2, 0x02, - 0x03, 0x46, 0x58, 0x58, 0xaa, 0x02, 0x0a, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0xca, 0x02, 0x0a, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xe2, 0x02, - 0x16, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0a, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x00, 0x1a, 0x03, 0x9a, 0x4a, 0x00, 0x42, 0xa4, 0x01, 0x9a, 0x4a, 0x19, 0x12, 0x17, 0x66, 0x61, + 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x2f, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e, 0x66, 0x65, 0x64, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0f, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x1d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x2f, 0x66, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x66, 0x65, 0x64, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xa2, 0x02, 0x03, 0x46, 0x58, 0x58, 0xaa, 0x02, 0x0a, + 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xca, 0x02, 0x0a, 0x46, 0x65, 0x64, + 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0xe2, 0x02, 0x16, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0xea, 0x02, 0x0a, 0x46, 0x65, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/_examples/11_multi_service/federation/federation_grpc_federation.pb.go b/_examples/11_multi_service/federation/federation_grpc_federation.pb.go index 5df23db4..3851cdbb 100644 --- a/_examples/11_multi_service/federation/federation_grpc_federation.pb.go +++ b/_examples/11_multi_service/federation/federation_grpc_federation.pb.go @@ -17,7 +17,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" - comment "example/comment" favorite "example/favorite" ) @@ -164,7 +163,6 @@ func NewFederationService(cfg FederationServiceConfig) (*FederationService, erro celTypeHelper := grpcfed.NewCELTypeHelper("federation", celTypeHelperFieldMap) var celEnvOpts []grpcfed.CELEnvOption celEnvOpts = append(celEnvOpts, grpcfed.NewDefaultEnvOptions(celTypeHelper)...) - celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("comment.CommentType", comment.CommentType_value, comment.CommentType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("favorite.FavoriteType", favorite.FavoriteType_value, favorite.FavoriteType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("federation.MyFavoriteType", MyFavoriteType_value, MyFavoriteType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.NewCELVariable("grpc.federation.env", grpcfed.CELObjectType("grpc.federation.private.Env"))) @@ -1066,7 +1064,6 @@ func NewPrivateService(cfg PrivateServiceConfig) (*PrivateService, error) { celTypeHelper := grpcfed.NewCELTypeHelper("federation", celTypeHelperFieldMap) var celEnvOpts []grpcfed.CELEnvOption celEnvOpts = append(celEnvOpts, grpcfed.NewDefaultEnvOptions(celTypeHelper)...) - celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("comment.CommentType", comment.CommentType_value, comment.CommentType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("favorite.FavoriteType", favorite.FavoriteType_value, favorite.FavoriteType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("federation.MyFavoriteType", MyFavoriteType_value, MyFavoriteType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.NewCELVariable("grpc.federation.env", grpcfed.CELObjectType("grpc.federation.private.Env"))) @@ -1916,7 +1913,6 @@ func NewDebugService(cfg DebugServiceConfig) (*DebugService, error) { celTypeHelper := grpcfed.NewCELTypeHelper("federation", celTypeHelperFieldMap) var celEnvOpts []grpcfed.CELEnvOption celEnvOpts = append(celEnvOpts, grpcfed.NewDefaultEnvOptions(celTypeHelper)...) - celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("comment.CommentType", comment.CommentType_value, comment.CommentType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("favorite.FavoriteType", favorite.FavoriteType_value, favorite.FavoriteType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("federation.MyFavoriteType", MyFavoriteType_value, MyFavoriteType_name)...) return &DebugService{ diff --git a/_examples/11_multi_service/federation/other_grpc_federation.pb.go b/_examples/11_multi_service/federation/other_grpc_federation.pb.go index 524bc63c..41fd5fe7 100644 --- a/_examples/11_multi_service/federation/other_grpc_federation.pb.go +++ b/_examples/11_multi_service/federation/other_grpc_federation.pb.go @@ -17,7 +17,6 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" - comment "example/comment" favorite "example/favorite" ) @@ -154,7 +153,6 @@ func NewOtherService(cfg OtherServiceConfig) (*OtherService, error) { celTypeHelper := grpcfed.NewCELTypeHelper("federation", celTypeHelperFieldMap) var celEnvOpts []grpcfed.CELEnvOption celEnvOpts = append(celEnvOpts, grpcfed.NewDefaultEnvOptions(celTypeHelper)...) - celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("comment.CommentType", comment.CommentType_value, comment.CommentType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("favorite.FavoriteType", favorite.FavoriteType_value, favorite.FavoriteType_name)...) celEnvOpts = append(celEnvOpts, grpcfed.EnumAccessorOptions("federation.MyFavoriteType", MyFavoriteType_value, MyFavoriteType_name)...) return &OtherService{ diff --git a/_examples/11_multi_service/proto/federation/federation.proto b/_examples/11_multi_service/proto/federation/federation.proto index 9b120352..881d607b 100644 --- a/_examples/11_multi_service/proto/federation/federation.proto +++ b/_examples/11_multi_service/proto/federation/federation.proto @@ -7,8 +7,8 @@ import "federation/reaction.proto"; option go_package = "example/federation;federation"; -option (grpc.federation.file)= { - import: ["comment/comment.proto", "favorite/favorite.proto"] +option (grpc.federation.file) = { + import: ["favorite/favorite.proto"] }; service FederationService { diff --git a/generator/generator.go b/generator/generator.go index 166ab28d..a887ecb1 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -639,9 +639,11 @@ func CreateCodeGeneratorResponse(ctx context.Context, req *pluginpb.CodeGenerato } outputPathResolver := resolver.NewOutputFilePathResolver(opt.Path) result, err := resolver.New(req.GetProtoFile(), resolver.ImportPathOption(opt.Path.ImportPaths...)).Resolve() - outs := validator.New().ToValidationOutputByResolverResult(result, err, validator.ImportPathOption(opt.Path.ImportPaths...)) - if validator.ExistsError(outs) { - return nil, errors.New(validator.Format(outs)) + if outs := validator.New().ToValidationOutputByResolverResult(result, err, validator.ImportPathOption(opt.Path.ImportPaths...)); len(outs) > 0 { + if validator.ExistsError(outs) { + return nil, errors.New(validator.Format(outs)) + } + fmt.Fprint(os.Stderr, validator.Format(outs)) } var outDir string diff --git a/grpc/federation/cel/bind.go b/grpc/federation/cel/bind.go index 43a42459..12e65d7a 100644 --- a/grpc/federation/cel/bind.go +++ b/grpc/federation/cel/bind.go @@ -78,19 +78,3 @@ func MemberOverloadFunc(name string, self *cel.Type, args []*cel.Type, result *c ), } } - -func toSelectorName(v ast.Expr) string { - switch v.Kind() { - case ast.SelectKind: - sel := v.AsSelect() - parent := toSelectorName(sel.Operand()) - if parent != "" { - return parent + "." + sel.FieldName() - } - return sel.FieldName() - case ast.IdentKind: - return v.AsIdent() - default: - return "" - } -} diff --git a/grpc/federation/cel/conv.go b/grpc/federation/cel/conv.go new file mode 100644 index 00000000..26ad8011 --- /dev/null +++ b/grpc/federation/cel/conv.go @@ -0,0 +1,63 @@ +package cel + +import "github.com/google/cel-go/common/ast" + +func ToIdentifiers(expr ast.Expr) []string { + var idents []string + switch expr.Kind() { + case ast.CallKind: + call := expr.AsCall() + idents = append(idents, ToIdentifiers(call.Target())...) + for _, arg := range call.Args() { + idents = append(idents, ToIdentifiers(arg)...) + } + case ast.IdentKind: + idents = append(idents, expr.AsIdent()) + case ast.ListKind: + l := expr.AsList() + for _, e := range l.Elements() { + idents = append(idents, ToIdentifiers(e)...) + } + case ast.MapKind: + m := expr.AsMap() + for _, entry := range m.Entries() { + idents = append(idents, toEntryNames(entry)...) + } + case ast.SelectKind: + idents = append(idents, toSelectorName(expr)) + case ast.StructKind: + idents = append(idents, expr.AsStruct().TypeName()) + for _, field := range expr.AsStruct().Fields() { + idents = append(idents, toEntryNames(field)...) + } + } + return idents +} + +func toEntryNames(entry ast.EntryExpr) []string { + var ident []string + switch entry.Kind() { + case ast.MapEntryKind: + ident = append(ident, ToIdentifiers(entry.AsMapEntry().Key())...) + ident = append(ident, ToIdentifiers(entry.AsMapEntry().Value())...) + case ast.StructFieldKind: + ident = append(ident, ToIdentifiers(entry.AsStructField().Value())...) + } + return ident +} + +func toSelectorName(v ast.Expr) string { + switch v.Kind() { + case ast.SelectKind: + sel := v.AsSelect() + parent := toSelectorName(sel.Operand()) + if parent != "" { + return parent + "." + sel.FieldName() + } + return sel.FieldName() + case ast.IdentKind: + return v.AsIdent() + default: + return "" + } +} diff --git a/internal/testutil/cmpopt.go b/internal/testutil/cmpopt.go index 852de6b0..171e36dc 100644 --- a/internal/testutil/cmpopt.go +++ b/internal/testutil/cmpopt.go @@ -11,10 +11,10 @@ func ResolverCmpOpts() []cmp.Option { return []cmp.Option{ cmpopts.IgnoreUnexported(resolver.VariableDefinition{}), cmpopts.IgnoreFields(resolver.File{}, "Messages", "Services", "Enums", "Desc", "CELPlugins", "ImportFiles"), - cmpopts.IgnoreFields(resolver.Service{}, "CELPlugins"), + cmpopts.IgnoreFields(resolver.Service{}, "CELPlugins", "Desc"), cmpopts.IgnoreFields(resolver.Package{}, "Files"), cmpopts.IgnoreFields(resolver.Method{}, "Service"), - cmpopts.IgnoreFields(resolver.Message{}, "File", "ParentMessage"), + cmpopts.IgnoreFields(resolver.Message{}, "File", "ParentMessage", "Desc"), cmpopts.IgnoreFields(resolver.Enum{}, "File", "Message.Rule"), cmpopts.IgnoreFields(resolver.EnumValue{}, "Enum"), cmpopts.IgnoreFields(resolver.EnumValueAlias{}, "EnumAlias"), @@ -31,7 +31,7 @@ func ResolverCmpOpts() []cmp.Option { cmpopts.IgnoreFields(resolver.AutoBindField{}, "VariableDefinition"), cmpopts.IgnoreFields(resolver.Type{}, "Message.Rule", "Enum.Rule", "OneofField"), cmpopts.IgnoreFields(resolver.Oneof{}, "Message"), - cmpopts.IgnoreFields(resolver.Field{}, "Message", "Oneof.Message", "Oneof.Fields"), + cmpopts.IgnoreFields(resolver.Field{}, "Message", "Oneof.Message", "Oneof.Fields", "Desc"), cmpopts.IgnoreFields(resolver.Value{}, "CEL"), cmpopts.IgnoreFields(resolver.CELValue{}, "CheckedExpr"), cmpopts.IgnoreFields(resolver.MessageExpr{}, "Message.Rule", "Message.Fields"), diff --git a/lsp/server/handler_test.go b/lsp/server/handler_test.go index 1bdb7efd..4d031fc8 100644 --- a/lsp/server/handler_test.go +++ b/lsp/server/handler_test.go @@ -110,7 +110,7 @@ func TestHandler_DidChange(t *testing.T) { ) client := protocol.ClientDispatcher(conn, zap.NewNop()) - handler := server.NewHandler(client, &bytes.Buffer{}, nil) + handler := server.NewHandler(client, &bytes.Buffer{}, []string{"testdata"}) err := handler.DidChange(context.Background(), tc.params) if err != nil { diff --git a/lsp/server/testdata/service.proto b/lsp/server/testdata/service.proto index ec473a61..fce23ea4 100644 --- a/lsp/server/testdata/service.proto +++ b/lsp/server/testdata/service.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/resolver/context.go b/resolver/context.go index ca961837..83a04b34 100644 --- a/resolver/context.go +++ b/resolver/context.go @@ -129,7 +129,6 @@ func (c *context) error() error { return c.errorBuilder.build() } -//nolint:unused func (c *context) addWarning(w *Warning) { c.allWarnings.warnings = append(c.allWarnings.warnings, w) } diff --git a/resolver/error.go b/resolver/error.go index 0aae4197..49f47c30 100644 --- a/resolver/error.go +++ b/resolver/error.go @@ -61,6 +61,14 @@ func ErrWithLocation(msg string, loc *source.Location) *LocationError { } } +// WarnWithLocation creates Warnings instance from message and location. +func WarnWithLocation(msg string, loc *source.Location) *Warning { + return &Warning{ + Location: loc, + Message: msg, + } +} + type errorBuilder struct { errs []error } diff --git a/resolver/resolver.go b/resolver/resolver.go index 030b7a3c..12a15932 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "log/slog" + "maps" "os" "path/filepath" "regexp" @@ -22,6 +23,7 @@ import ( "golang.org/x/text/language" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/descriptorpb" "github.com/mercari/grpc-federation/compiler" @@ -168,7 +170,7 @@ func (r *Resolver) Resolve() (*Result, error) { r.resolveAutoBind(ctx, files) r.resolveMessageDependencies(ctx, files) - r.validateServiceFromFiles(ctx, files) + r.validateFiles(ctx, files) resultFiles := r.resultFiles(files) return &Result{ @@ -374,15 +376,255 @@ func (r *Resolver) allMessages(files []*File) []*Message { return msgs } -func (r *Resolver) validateServiceFromFiles(ctx *context, files []*File) { +func (r *Resolver) validateFiles(ctx *context, files []*File) { for _, file := range files { - ctx := ctx.withFile(file) + ctx = ctx.withFile(file) + r.validateFileImport(ctx, file) + for _, svc := range file.Services { r.validateService(ctx, svc) } } } +func (r *Resolver) validateFileImport(ctx *context, file *File) { + pkgNameUsedInProtoMap := r.lookupPackageNameMapUsedInProtoDefinitionFromFile(file) + pkgNameUsedInGrpcFedMap := r.lookupPackageNameMapUsedInGRPCFederationDefinitionFromFile(file) + ruleDef, err := getExtensionRule[*federation.FileRule](file.Desc.GetOptions(), federation.E_File) + if err != nil { + ctx.addError( + ErrWithLocation( + err.Error(), + source.NewLocationBuilder(file.Desc.GetName()).Location(), + ), + ) + return + } + grpcFedFileImports := map[string]struct{}{} + if ruleDef != nil { + for _, path := range ruleDef.GetImport() { + grpcFedFileImports[path] = struct{}{} + } + } + + for _, importFile := range file.ImportFiles { + if _, imported := grpcFedFileImports[importFile.Name]; imported { + if _, used := pkgNameUsedInGrpcFedMap[importFile.PackageName()]; !used { + ctx.addWarning(WarnWithLocation( + fmt.Sprintf("Import %s is unused for the definition of grpc federation.", importFile.Name), + source.NewLocationBuilder(file.Desc.GetName()).WithImportName(importFile.Name).Location(), + )) + } + } else { + if _, used := pkgNameUsedInProtoMap[importFile.PackageName()]; used { + continue + } + if _, used := pkgNameUsedInGrpcFedMap[importFile.PackageName()]; used { + ctx.addWarning(WarnWithLocation( + fmt.Sprintf("Import %s is used only for the definition of grpc federation. You can use grpc.federation.file.import instead.", importFile.Name), + source.NewLocationBuilder(file.Desc.GetName()).WithImportName(importFile.Name).Location(), + )) + } + } + } +} + +func (r *Resolver) lookupPackageNameMapUsedInProtoDefinitionFromFile(file *File) map[string]struct{} { + pkgNameMap := map[string]struct{}{} + for _, s := range file.Services { + if opt := s.Desc.GetOptions(); opt != nil { + opt.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool { + pkgNameMap[string(fd.ParentFile().Package())] = struct{}{} + return true + }) + } + + for _, m := range s.Methods { + if opt := m.Desc.GetOptions(); opt != nil { + opt.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool { + pkgNameMap[string(fd.ParentFile().Package())] = struct{}{} + return true + }) + } + pkgNameMap[m.Request.PackageName()] = struct{}{} + pkgNameMap[m.Response.PackageName()] = struct{}{} + } + } + + for _, msg := range file.Messages { + maps.Copy(pkgNameMap, r.lookupPackageNameMapUsedInProtoDefinitionFromMessage(msg)) + } + return pkgNameMap +} + +func (r *Resolver) lookupPackageNameMapUsedInProtoDefinitionFromMessage(msg *Message) map[string]struct{} { + pkgNameMap := map[string]struct{}{} + for _, field := range msg.Fields { + if opt := field.Desc.GetOptions(); opt != nil { + opt.ProtoReflect().Range(func(fd protoreflect.FieldDescriptor, val protoreflect.Value) bool { + pkgNameMap[string(fd.ParentFile().Package())] = struct{}{} + return true + }) + } + maps.Copy(pkgNameMap, r.lookupPackageNameMapRecursiveFromType(field.Type)) + } + + for _, nestedMsg := range msg.NestedMessages { + maps.Copy(pkgNameMap, r.lookupPackageNameMapUsedInProtoDefinitionFromMessage(nestedMsg)) + } + return pkgNameMap +} + +func (r *Resolver) lookupPackageNameMapRecursiveFromType(typ *Type) map[string]struct{} { + pkgNameMap := map[string]struct{}{} + if typ == nil { + return pkgNameMap + } + switch typ.Kind { + case types.Message: + if typ.Message == nil { + return pkgNameMap + } + if typ.Message.IsMapEntry { + for _, field := range typ.Message.Fields { + maps.Copy(pkgNameMap, r.lookupPackageNameMapRecursiveFromType(field.Type)) + } + } else { + pkgNameMap[typ.Message.PackageName()] = struct{}{} + } + case types.Enum: + if typ.Enum == nil { + return pkgNameMap + } + pkgNameMap[typ.Enum.PackageName()] = struct{}{} + } + return pkgNameMap +} + +func (r *Resolver) lookupPackageNameMapUsedInGRPCFederationDefinitionFromFile(file *File) map[string]struct{} { + pkgNameMap := map[string]struct{}{} + for _, s := range file.Services { + if s.Rule != nil && s.Rule.Env != nil { + for _, v := range s.Rule.Env.Vars { + maps.Copy(pkgNameMap, r.lookupPackageNameMapRecursiveFromType(v.Type)) + } + } + } + + for _, msg := range file.Messages { + maps.Copy(pkgNameMap, r.lookupPackageNameMapUsedInGRPCFederationDefinitionFromMessage(msg)) + } + return pkgNameMap +} + +func (r *Resolver) lookupPackageNameMapUsedInGRPCFederationDefinitionFromMessage(msg *Message) map[string]struct{} { + pkgNameMap := map[string]struct{}{} + if msg.Rule != nil { + for _, a := range msg.Rule.Aliases { + pkgNameMap[a.PackageName()] = struct{}{} + } + if msg.Rule.DefSet != nil { + for _, v := range msg.Rule.DefSet.Defs { + if v.Expr == nil { + continue + } + switch { + case v.Expr.By != nil: + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromCELValue(v.Expr.By)) + case v.Expr.Call != nil: + if v.Expr.Call.Method != nil && v.Expr.Call.Method.Service != nil { + pkgNameMap[v.Expr.Call.Method.Service.PackageName()] = struct{}{} + } + if v.Expr.Call.Request != nil { + if v.Expr.Call.Request.Type != nil { + pkgNameMap[v.Expr.Call.Request.Type.PackageName()] = struct{}{} + } + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromMessageArguments(v.Expr.Call.Request.Args)) + } + case v.Expr.Message != nil: + if v.Expr.Message.Message != nil { + pkgNameMap[v.Expr.Message.Message.PackageName()] = struct{}{} + } + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromMessageArguments(v.Expr.Message.Args)) + case v.Expr.Map != nil: + if v.Expr.Map.Expr != nil { + if v.Expr.Map.Expr.By != nil { + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromCELValue(v.Expr.Map.Expr.By)) + } + if v.Expr.Map.Expr.Message != nil { + pkgNameMap[v.Expr.Map.Expr.Message.Message.PackageName()] = struct{}{} + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromMessageArguments(v.Expr.Map.Expr.Message.Args)) + } + } + } + } + } + } + + for _, field := range msg.Fields { + rule := field.Rule + if rule == nil { + continue + } + for _, a := range rule.Aliases { + if a.Message == nil { + continue + } + pkgNameMap[a.Message.PackageName()] = struct{}{} + } + if rule.Value != nil { + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromCELValue(rule.Value.CEL)) + } + if rule.Oneof != nil { + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromCELValue(rule.Oneof.By)) + } + } + + for _, nestedMsg := range msg.NestedMessages { + maps.Copy(pkgNameMap, r.lookupPackageNameMapUsedInGRPCFederationDefinitionFromMessage(nestedMsg)) + } + return pkgNameMap +} + +func (r *Resolver) lookupPackageNameMapFromCELValue(val *CELValue) map[string]struct{} { + if val == nil { + return nil + } + env, err := r.createCELEnv() + if err != nil { + // skip reporting error + return nil + } + expr := strings.Replace(val.Expr, "$", federation.MessageArgumentVariableName, -1) + ast, issues := env.Parse(expr) + if issues.Err() != nil { + // skip reporting error + return nil + } + idents := grpcfedcel.ToIdentifiers(ast.NativeRep().Expr()) + + pkgNameMap := map[string]struct{}{} + for _, ident := range idents { + if pkg, err := r.lookupPackage(ident); err == nil { + pkgNameMap[pkg.Name] = struct{}{} + } else if _, exists := r.protoPackageNameToPackage[ident]; exists { + pkgNameMap[ident] = struct{}{} + } + } + return pkgNameMap +} + +func (r *Resolver) lookupPackageNameMapFromMessageArguments(args []*Argument) map[string]struct{} { + pkgNameMap := map[string]struct{}{} + for _, arg := range args { + if arg.Value != nil { + maps.Copy(pkgNameMap, r.lookupPackageNameMapFromCELValue(arg.Value.CEL)) + } + maps.Copy(pkgNameMap, r.lookupPackageNameMapRecursiveFromType(arg.Type)) + } + return pkgNameMap +} + func (r *Resolver) resultFiles(allFiles []*File) []*File { fileMap := make(map[*File]struct{}) ret := make([]*File, 0, len(allFiles)) @@ -734,6 +976,7 @@ func (r *Resolver) resolveService(ctx *context, pkg *Package, name string, build service := &Service{ File: file, Name: name, + Desc: serviceDef, Methods: make([]*Method, 0, len(serviceDef.GetMethod())), CELPlugins: plugins, } @@ -798,6 +1041,7 @@ func (r *Resolver) resolveMethod(ctx *context, service *Service, methodDef *desc method := &Method{ Service: service, Name: methodDef.GetName(), + Desc: methodDef, Request: req, Response: res, } @@ -838,7 +1082,11 @@ func (r *Resolver) resolveMessage(ctx *context, pkg *Package, name string, build ) return nil } - msg := &Message{File: file, Name: msgDef.GetName()} + msg := &Message{ + File: file, + Name: msgDef.GetName(), + Desc: msgDef, + } for _, nestedMsgDef := range msgDef.GetNestedType() { nestedMsg := r.resolveMessage(ctx, pkg, fmt.Sprintf("%s.%s", name, nestedMsgDef.GetName()), builder.WithMessage(name)) if nestedMsg == nil { @@ -2610,7 +2858,12 @@ func (r *Resolver) resolveField(ctx *context, fieldDef *descriptorpb.FieldDescri ) return nil } - field := &Field{Name: fieldDef.GetName(), Type: typ, Message: ctx.msg} + field := &Field{ + Name: fieldDef.GetName(), + Desc: fieldDef, + Type: typ, + Message: ctx.msg, + } if fieldDef.OneofIndex != nil { oneof := oneofs[fieldDef.GetOneofIndex()] oneof.Fields = append(oneof.Fields, field) @@ -2973,7 +3226,7 @@ func (r *Resolver) resolveMessageArgumentRecursive( ) return nil } - env, err := r.createCELEnv(ctx, msg, svcMsgSet, builder) + env, err := r.createMessageCELEnv(ctx, msg, svcMsgSet, builder) if err != nil { ctx.addError( ErrWithLocation( @@ -3725,15 +3978,8 @@ func (r *Resolver) removeContextArgumentFromErrorText(err *cel.Error) { err.Message = strings.Replace(err.Message, federation.ContextTypeName, "", -1) } -func (r *Resolver) createCELEnv(ctx *context, msg *Message, svcMsgSet map[*Service]map[*Message]struct{}, builder *source.MessageBuilder) (*cel.Env, error) { +func (r *Resolver) createMessageCELEnv(ctx *context, msg *Message, svcMsgSet map[*Service]map[*Message]struct{}, builder *source.MessageBuilder) (*cel.Env, error) { envOpts := []cel.EnvOption{ - cel.StdLib(), - cel.Lib(grpcfedcel.NewLibrary(r.celRegistry)), - cel.CrossTypeNumericComparisons(true), - cel.CustomTypeAdapter(r.celRegistry), - cel.CustomTypeProvider(r.celRegistry), - cel.ASTValidators(grpcfedcel.NewASTValidators()...), - cel.Variable(federation.ContextVariableName, cel.ObjectType(federation.ContextTypeName)), cel.Container(msg.Package().Name), } envMsg := r.buildEnvMessage(ctx, msg, svcMsgSet, builder) @@ -3744,14 +3990,27 @@ func (r *Resolver) createCELEnv(ctx *context, msg *Message, svcMsgSet map[*Servi } envOpts = append(envOpts, cel.Variable("grpc.federation.env", cel.ObjectType(envMsg.FQDN()))) } + if msg.Rule != nil && msg.Rule.MessageArgument != nil { + envOpts = append(envOpts, cel.Variable(federation.MessageArgumentVariableName, cel.ObjectType(msg.Rule.MessageArgument.FQDN()))) + } + return r.createCELEnv(envOpts...) +} + +func (r *Resolver) createCELEnv(envOpts ...cel.EnvOption) (*cel.Env, error) { + envOpts = append(envOpts, []cel.EnvOption{ + cel.StdLib(), + cel.Lib(grpcfedcel.NewLibrary(r.celRegistry)), + cel.CrossTypeNumericComparisons(true), + cel.CustomTypeAdapter(r.celRegistry), + cel.CustomTypeProvider(r.celRegistry), + cel.ASTValidators(grpcfedcel.NewASTValidators()...), + cel.Variable(federation.ContextVariableName, cel.ObjectType(federation.ContextTypeName)), + }...) envOpts = append(envOpts, r.enumAccessors()...) envOpts = append(envOpts, r.enumOperators()...) for _, plugin := range r.celPluginMap { envOpts = append(envOpts, cel.Lib(plugin)) } - if msg.Rule != nil && msg.Rule.MessageArgument != nil { - envOpts = append(envOpts, cel.Variable(federation.MessageArgumentVariableName, cel.ObjectType(msg.Rule.MessageArgument.FQDN()))) - } env, err := cel.NewCustomEnv(envOpts...) if err != nil { return nil, err diff --git a/resolver/types.go b/resolver/types.go index ec3cf43f..675ded3c 100644 --- a/resolver/types.go +++ b/resolver/types.go @@ -54,6 +54,7 @@ type GoPackage struct { type Service struct { File *File Name string + Desc *descriptorpb.ServiceDescriptorProto Methods []*Method Rule *ServiceRule Messages []*Message @@ -64,6 +65,7 @@ type Service struct { type Method struct { Service *Service Name string + Desc *descriptorpb.MethodDescriptorProto Request *Message Response *Message Rule *MethodRule @@ -102,6 +104,7 @@ type MethodRule struct { type Message struct { File *File Name string + Desc *descriptorpb.DescriptorProto IsMapEntry bool ParentMessage *Message NestedMessages []*Message @@ -306,6 +309,7 @@ type Oneof struct { type Field struct { Name string + Desc *descriptorpb.FieldDescriptorProto Type *Type Oneof *Oneof Rule *FieldRule diff --git a/validator/testdata/duplicated_variable_name.proto b/validator/testdata/duplicated_variable_name.proto index 0477b2c1..a1f55c6c 100644 --- a/validator/testdata/duplicated_variable_name.proto +++ b/validator/testdata/duplicated_variable_name.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "echo.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["echo.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/empty_response_field.proto b/validator/testdata/empty_response_field.proto index 806ec92c..3acb6f03 100644 --- a/validator/testdata/empty_response_field.proto +++ b/validator/testdata/empty_response_field.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "echo.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["echo.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_call_error_handler.proto b/validator/testdata/invalid_call_error_handler.proto index a5a73731..45552979 100644 --- a/validator/testdata/invalid_call_error_handler.proto +++ b/validator/testdata/invalid_call_error_handler.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "grpc/federation/federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_condition_type.proto b/validator/testdata/invalid_condition_type.proto index 7b61058b..ba329ef9 100644 --- a/validator/testdata/invalid_condition_type.proto +++ b/validator/testdata/invalid_condition_type.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "grpc/federation/federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_enum_alias_target.proto b/validator/testdata/invalid_enum_alias_target.proto index f9b76973..124d4a6c 100644 --- a/validator/testdata/invalid_enum_alias_target.proto +++ b/validator/testdata/invalid_enum_alias_target.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_enum_selector.proto b/validator/testdata/invalid_enum_selector.proto index 60d5f249..29dd768e 100644 --- a/validator/testdata/invalid_enum_selector.proto +++ b/validator/testdata/invalid_enum_selector.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "grpc/federation/federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_field_option.proto b/validator/testdata/invalid_field_option.proto index e17c605f..9bf0481f 100644 --- a/validator/testdata/invalid_field_option.proto +++ b/validator/testdata/invalid_field_option.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_file_import.proto b/validator/testdata/invalid_file_import.proto index ddc81d08..ad85fa28 100644 --- a/validator/testdata/invalid_file_import.proto +++ b/validator/testdata/invalid_file_import.proto @@ -3,11 +3,15 @@ syntax = "proto3"; package federation; import "grpc/federation/federation.proto"; +import "post.proto"; option go_package = "example/federation;federation"; option (grpc.federation.file)= { - import: ["unknown.proto"] + import: [ + "user.proto", + "unknown.proto" + ] }; service FederationService { diff --git a/validator/testdata/invalid_message_alias.proto b/validator/testdata/invalid_message_alias.proto index 6ccb98bc..a052bccf 100644 --- a/validator/testdata/invalid_message_alias.proto +++ b/validator/testdata/invalid_message_alias.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_message_alias_target.proto b/validator/testdata/invalid_message_alias_target.proto index 7b6bcc94..096d1ee4 100644 --- a/validator/testdata/invalid_message_alias_target.proto +++ b/validator/testdata/invalid_message_alias_target.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_message_argument.proto b/validator/testdata/invalid_message_argument.proto index ea3b0d8d..3a4341ff 100644 --- a/validator/testdata/invalid_message_argument.proto +++ b/validator/testdata/invalid_message_argument.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_message_field_alias.proto b/validator/testdata/invalid_message_field_alias.proto index 9143514c..3118198d 100644 --- a/validator/testdata/invalid_message_field_alias.proto +++ b/validator/testdata/invalid_message_field_alias.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_message_map.proto b/validator/testdata/invalid_message_map.proto index c3ec4758..0e980aa0 100644 --- a/validator/testdata/invalid_message_map.proto +++ b/validator/testdata/invalid_message_map.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_message_map_alias.proto b/validator/testdata/invalid_message_map_alias.proto index adacebe4..29427868 100644 --- a/validator/testdata/invalid_message_map_alias.proto +++ b/validator/testdata/invalid_message_map_alias.proto @@ -4,9 +4,11 @@ package federation; import "federation.proto"; import "post.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_message_name.proto b/validator/testdata/invalid_message_name.proto index b8c687ca..c6c15df4 100644 --- a/validator/testdata/invalid_message_name.proto +++ b/validator/testdata/invalid_message_name.proto @@ -3,10 +3,12 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_method.proto b/validator/testdata/invalid_method.proto index 45dfe29f..7c029e82 100644 --- a/validator/testdata/invalid_method.proto +++ b/validator/testdata/invalid_method.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_method_name.proto b/validator/testdata/invalid_method_name.proto index c398cd7d..c716d638 100644 --- a/validator/testdata/invalid_method_name.proto +++ b/validator/testdata/invalid_method_name.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_method_request.proto b/validator/testdata/invalid_method_request.proto index ec57a439..e988d052 100644 --- a/validator/testdata/invalid_method_request.proto +++ b/validator/testdata/invalid_method_request.proto @@ -7,6 +7,9 @@ import "post.proto"; import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_method_response.proto b/validator/testdata/invalid_method_response.proto index 320338aa..ef3b4f4f 100644 --- a/validator/testdata/invalid_method_response.proto +++ b/validator/testdata/invalid_method_response.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_method_service_name.proto b/validator/testdata/invalid_method_service_name.proto index 7bca5845..292f01b1 100644 --- a/validator/testdata/invalid_method_service_name.proto +++ b/validator/testdata/invalid_method_service_name.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_multi_alias.proto b/validator/testdata/invalid_multi_alias.proto index 3f601cf9..e4e7ec3f 100644 --- a/validator/testdata/invalid_multi_alias.proto +++ b/validator/testdata/invalid_multi_alias.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package org.federation; import "grpc/federation/federation.proto"; -import "nested_post.proto"; -import "nested_post2.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto", "nested_post2.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_oneof.proto b/validator/testdata/invalid_oneof.proto index 07371818..f2442ef6 100644 --- a/validator/testdata/invalid_oneof.proto +++ b/validator/testdata/invalid_oneof.proto @@ -6,6 +6,9 @@ import "federation.proto"; import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_retry.proto b/validator/testdata/invalid_retry.proto index c05bb7a7..da250ffa 100644 --- a/validator/testdata/invalid_retry.proto +++ b/validator/testdata/invalid_retry.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_validation_bad_request.proto b/validator/testdata/invalid_validation_bad_request.proto index f7cb9753..77f59758 100644 --- a/validator/testdata/invalid_validation_bad_request.proto +++ b/validator/testdata/invalid_validation_bad_request.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_validation_details_return_type.proto b/validator/testdata/invalid_validation_details_return_type.proto index 2e8ab8d0..deb1a7ee 100644 --- a/validator/testdata/invalid_validation_details_return_type.proto +++ b/validator/testdata/invalid_validation_details_return_type.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_validation_localized_message.proto b/validator/testdata/invalid_validation_localized_message.proto index 40837ff6..a0fe4570 100644 --- a/validator/testdata/invalid_validation_localized_message.proto +++ b/validator/testdata/invalid_validation_localized_message.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_validation_message_argument.proto b/validator/testdata/invalid_validation_message_argument.proto index d59f5fbf..d8830c12 100644 --- a/validator/testdata/invalid_validation_message_argument.proto +++ b/validator/testdata/invalid_validation_message_argument.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_validation_precondition_failure.proto b/validator/testdata/invalid_validation_precondition_failure.proto index 7aa409a7..87a25d37 100644 --- a/validator/testdata/invalid_validation_precondition_failure.proto +++ b/validator/testdata/invalid_validation_precondition_failure.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_validation_return_type.proto b/validator/testdata/invalid_validation_return_type.proto index 7c9bf4f8..39a4da8b 100644 --- a/validator/testdata/invalid_validation_return_type.proto +++ b/validator/testdata/invalid_validation_return_type.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/invalid_variable_name.proto b/validator/testdata/invalid_variable_name.proto index 5d156753..a3e1dc3e 100644 --- a/validator/testdata/invalid_variable_name.proto +++ b/validator/testdata/invalid_variable_name.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "echo.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["echo.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_enum_alias.proto b/validator/testdata/missing_enum_alias.proto index a6caf96d..d0d237fe 100644 --- a/validator/testdata/missing_enum_alias.proto +++ b/validator/testdata/missing_enum_alias.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_enum_value.proto b/validator/testdata/missing_enum_value.proto index e643923c..cd227eff 100644 --- a/validator/testdata/missing_enum_value.proto +++ b/validator/testdata/missing_enum_value.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_enum_value_alias.proto b/validator/testdata/missing_enum_value_alias.proto index 23053dc9..12dbdc1d 100644 --- a/validator/testdata/missing_enum_value_alias.proto +++ b/validator/testdata/missing_enum_value_alias.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_field_option.proto b/validator/testdata/missing_field_option.proto index 4ad07060..ac17178f 100644 --- a/validator/testdata/missing_field_option.proto +++ b/validator/testdata/missing_field_option.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_message_alias.proto b/validator/testdata/missing_message_alias.proto index 9c0b4434..0541cee6 100644 --- a/validator/testdata/missing_message_alias.proto +++ b/validator/testdata/missing_message_alias.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_message_field_alias.proto b/validator/testdata/missing_message_field_alias.proto index 04488fdc..fcb1401d 100644 --- a/validator/testdata/missing_message_field_alias.proto +++ b/validator/testdata/missing_message_field_alias.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_message_option.proto b/validator/testdata/missing_message_option.proto index ce8e5a0f..8666ff65 100644 --- a/validator/testdata/missing_message_option.proto +++ b/validator/testdata/missing_message_option.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/missing_method_request_value.proto b/validator/testdata/missing_method_request_value.proto index 336c1f00..c6803930 100644 --- a/validator/testdata/missing_method_request_value.proto +++ b/validator/testdata/missing_method_request_value.proto @@ -3,10 +3,11 @@ syntax = "proto3"; package federation; import "federation.proto"; -import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto", "user.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/recursive_message_name.proto b/validator/testdata/recursive_message_name.proto index f2b57b0f..8baec4b3 100644 --- a/validator/testdata/recursive_message_name.proto +++ b/validator/testdata/recursive_message_name.proto @@ -4,9 +4,11 @@ package federation; import "federation.proto"; import "post.proto"; -import "user.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/testdata/valid_enum_value_reference.proto b/validator/testdata/valid_enum_value_reference.proto index 285294f4..699e83df 100644 --- a/validator/testdata/valid_enum_value_reference.proto +++ b/validator/testdata/valid_enum_value_reference.proto @@ -3,9 +3,11 @@ syntax = "proto3"; package org.federation; import "federation.proto"; -import "nested_post.proto"; option go_package = "example/federation;federation"; +option (grpc.federation.file) = { + import: ["nested_post.proto"] +}; service FederationService { option (grpc.federation.service) = {}; diff --git a/validator/validator_test.go b/validator/validator_test.go index 1b552545..f59af5a9 100644 --- a/validator/validator_test.go +++ b/validator/validator_test.go @@ -21,241 +21,241 @@ func TestValidator(t *testing.T) { {file: "empty_response_field.proto", expected: ` `}, {file: "different_message_argument_type.proto", expected: ` -testdata/different_message_argument_type.proto:28:14: "id" argument name is declared with a different type kind. found "string" and "int64" type +different_message_argument_type.proto:28:14: "id" argument name is declared with a different type kind. found "string" and "int64" type 28: args { name: "id" by: "1" } ^ `}, {file: "duplicated_variable_name.proto", expected: ` -testdata/duplicated_variable_name.proto:25:25: found duplicated variable name "a" -25: def { name: "a" by: "1" } +duplicated_variable_name.proto:27:25: found duplicated variable name "a" +27: def { name: "a" by: "1" } ^ -testdata/duplicated_variable_name.proto:27:27: found duplicated variable name "a" -27: def { name: "a" by: "2" } +duplicated_variable_name.proto:29:27: found duplicated variable name "a" +29: def { name: "a" by: "2" } ^ -testdata/duplicated_variable_name.proto:36:25: found duplicated variable name "a" -36: def { name: "a" by: "3" } +duplicated_variable_name.proto:38:25: found duplicated variable name "a" +38: def { name: "a" by: "3" } ^ -testdata/duplicated_variable_name.proto:38:27: found duplicated variable name "a" -38: def { name: "a" by: "4" } +duplicated_variable_name.proto:40:27: found duplicated variable name "a" +40: def { name: "a" by: "4" } ^ -testdata/duplicated_variable_name.proto:48:19: found duplicated variable name "a" -48: def { name: "a" by: "5" } +duplicated_variable_name.proto:50:19: found duplicated variable name "a" +50: def { name: "a" by: "5" } ^ `}, {file: "invalid_autobind.proto", expected: ` -testdata/invalid_autobind.proto:23:3: "id" field found multiple times in the message specified by autobind. since it is not possible to determine one, please use "grpc.federation.field" to explicitly bind it. found message names are "a" name at def and "b" name at def +invalid_autobind.proto:23:3: "id" field found multiple times in the message specified by autobind. since it is not possible to determine one, please use "grpc.federation.field" to explicitly bind it. found message names are "a" name at def and "b" name at def 23: string id = 1; ^ -testdata/invalid_autobind.proto:23:3: "id" field in "org.federation.GetResponse" message needs to specify "grpc.federation.field" option +invalid_autobind.proto:23:3: "id" field in "org.federation.GetResponse" message needs to specify "grpc.federation.field" option 23: string id = 1; ^ `}, {file: "invalid_call_error_handler.proto", expected: ` -testdata/invalid_call_error_handler.proto:42:21: cannot set both "ignore" and "ignore_and_response" -42: ignore: true +invalid_call_error_handler.proto:44:21: cannot set both "ignore" and "ignore_and_response" +44: ignore: true ^ -testdata/invalid_call_error_handler.proto:43:34: cannot set both "ignore" and "ignore_and_response" -43: ignore_and_response: "post.GetPostResponse{}" +invalid_call_error_handler.proto:45:34: cannot set both "ignore" and "ignore_and_response" +45: ignore_and_response: "post.GetPostResponse{}" ^ -testdata/invalid_call_error_handler.proto:47:19: "by" must always return a message value -47: by: "1" +invalid_call_error_handler.proto:49:19: "by" must always return a message value +49: by: "1" ^ -testdata/invalid_call_error_handler.proto:51:34: value must be "post.GetPostResponse" type -51: ignore_and_response: "10" +invalid_call_error_handler.proto:53:34: value must be "post.GetPostResponse" type +53: ignore_and_response: "10" ^ `}, {file: "invalid_condition_type.proto", expected: ` -testdata/invalid_condition_type.proto:36:13: return value of "if" must be bool type but got string type -36: if: "$.id" +invalid_condition_type.proto:38:13: return value of "if" must be bool type but got string type +38: if: "$.id" ^ `}, {file: "invalid_field_option.proto", expected: ` -testdata/invalid_field_option.proto:30:50: ERROR: :1:5: undefined field 'invalid' +invalid_field_option.proto:31:50: ERROR: :1:5: undefined field 'invalid' | post.invalid | ....^ -30: Post post = 1 [(grpc.federation.field) = { by: "post.invalid" }]; +31: Post post = 1 [(grpc.federation.field) = { by: "post.invalid" }]; ^ `}, {file: "invalid_field_type.proto", expected: ` -testdata/invalid_field_type.proto:18:3: cannot convert type automatically: field type is "string" but specified value type is "int64" +invalid_field_type.proto:18:3: cannot convert type automatically: field type is "string" but specified value type is "int64" 18: string a = 1 [(grpc.federation.field).by = "1"]; ^ `}, {file: "invalid_go_package.proto", expected: ` -testdata/invalid_go_package.proto:9:21: go_package option "a;b;c;d" is invalid +invalid_go_package.proto:9:21: go_package option "a;b;c;d" is invalid 9: option go_package = "a;b;c;d"; ^ `}, {file: "invalid_enum_alias_target.proto", expected: ` -testdata/invalid_enum_alias_target.proto:47:1: required specify alias = "org.post.PostDataType" in grpc.federation.enum option for the "org.federation.PostType" type to automatically assign a value to the "PostData.type" field via autobind -47: enum PostType { +invalid_enum_alias_target.proto:49:1: required specify alias = "org.post.PostDataType" in grpc.federation.enum option for the "org.federation.PostType" type to automatically assign a value to the "PostData.type" field via autobind +49: enum PostType { ^ -testdata/invalid_enum_alias_target.proto:66:3: required specify alias = "org.post.PostContent.Category" in grpc.federation.enum option for the "org.federation.PostContent.Category" type to automatically assign a value to the "PostContent.category" field via autobind -66: enum Category { +invalid_enum_alias_target.proto:68:3: required specify alias = "org.post.PostContent.Category" in grpc.federation.enum option for the "org.federation.PostContent.Category" type to automatically assign a value to the "PostContent.category" field via autobind +68: enum Category { ^ `}, {file: "invalid_enum_selector.proto", expected: ` -testdata/invalid_enum_selector.proto:20:15: ERROR: :1:56: cannot specify an int type. if you are directly specifying an enum value, you need to explicitly use "pkg.EnumName.value('ENUM_VALUE')" function to use the enum type +invalid_enum_selector.proto:22:15: ERROR: :1:56: cannot specify an int type. if you are directly specifying an enum value, you need to explicitly use "pkg.EnumName.value('ENUM_VALUE')" function to use the enum type | grpc.federation.enum.select(true, org.post.PostDataType.POST_TYPE_B, 'foo') | .......................................................^ -20: def { by: "grpc.federation.enum.select(true, org.post.PostDataType.POST_TYPE_B, 'foo')" } +22: def { by: "grpc.federation.enum.select(true, org.post.PostDataType.POST_TYPE_B, 'foo')" } ^ `}, {file: "invalid_env.proto", expected: ` -testdata/invalid_env.proto:11:9: "message" and "var" cannot be used simultaneously +invalid_env.proto:11:9: "message" and "var" cannot be used simultaneously 11: env { ^ -testdata/invalid_env.proto:24:16: "org.federation.Invalid" message does not exist +invalid_env.proto:24:16: "org.federation.Invalid" message does not exist 24: message: "Invalid" ^ `}, {file: "invalid_multiple_env.proto", expected: ` -testdata/invalid_multiple_env.proto:56:1: environment variable "ccc" has different types across services: InlineEnvService, RefEnvService +invalid_multiple_env.proto:56:1: environment variable "ccc" has different types across services: InlineEnvService, RefEnvService 56: message GetNameResponse { ^ -testdata/invalid_multiple_env.proto:56:1: environment variable "eee" has different types across services: InlineEnvService, RefEnvService +invalid_multiple_env.proto:56:1: environment variable "eee" has different types across services: InlineEnvService, RefEnvService 56: message GetNameResponse { ^ `}, {file: "invalid_error_variable.proto", expected: ` -testdata/invalid_error_variable.proto:20:17: "error" is the reserved keyword. this name is not available +invalid_error_variable.proto:20:17: "error" is the reserved keyword. this name is not available 20: def { name: "error" by: "'foo'" } ^ -testdata/invalid_error_variable.proto:21:25: ERROR: :1:1: undeclared reference to 'error' (in container 'org.federation') +invalid_error_variable.proto:21:25: ERROR: :1:1: undeclared reference to 'error' (in container 'org.federation') | error | ^ 21: def { name: "e" by: "error" } ^ -testdata/invalid_error_variable.proto:25:15: ERROR: :1:1: undeclared reference to 'error' (in container 'org.federation') +invalid_error_variable.proto:25:15: ERROR: :1:1: undeclared reference to 'error' (in container 'org.federation') | error.code == 0 | ^ 25: if: "error.code == 0" ^ `}, {file: "invalid_map_iterator_src_type.proto", expected: ` -testdata/invalid_map_iterator_src_type.proto:40:13: map iterator's src value type must be repeated type +invalid_map_iterator_src_type.proto:40:13: map iterator's src value type must be repeated type 40: map { ^ -testdata/invalid_map_iterator_src_type.proto:54:57: ERROR: :1:1: undeclared reference to 'users' (in container 'org.federation') +invalid_map_iterator_src_type.proto:54:57: ERROR: :1:1: undeclared reference to 'users' (in container 'org.federation') | users | ^ 54: repeated User users = 4 [(grpc.federation.field).by = "users"]; ^ -testdata/invalid_map_iterator_src_type.proto:58:47: ERROR: :1:8: undefined field 'user_id' +invalid_map_iterator_src_type.proto:58:47: ERROR: :1:8: undefined field 'user_id' | __ARG__.user_id | .......^ 58: string id = 1 [(grpc.federation.field).by = "$.user_id"]; ^ `}, {file: "invalid_map_iterator_src.proto", expected: ` -testdata/invalid_map_iterator_src.proto:36:13: "posts" variable is not defined +invalid_map_iterator_src.proto:36:13: "posts" variable is not defined 36: map { ^ -testdata/invalid_map_iterator_src.proto:36:13: ERROR: :1:1: undeclared reference to 'iter' (in container 'org.federation') +invalid_map_iterator_src.proto:36:13: ERROR: :1:1: undeclared reference to 'iter' (in container 'org.federation') | iter.id | ^ 36: map { ^ -testdata/invalid_map_iterator_src.proto:54:47: ERROR: :1:8: undefined field 'user_id' +invalid_map_iterator_src.proto:54:47: ERROR: :1:8: undefined field 'user_id' | __ARG__.user_id | .......^ 54: string id = 1 [(grpc.federation.field).by = "$.user_id"]; ^ `}, {file: "invalid_message_alias_target.proto", expected: ` -testdata/invalid_message_alias_target.proto:44:3: required specify alias = "org.post.PostData" in grpc.federation.message option for the "org.federation.PostData" type to automatically assign a value to the "Post.data" field via autobind -44: PostData data = 4; +invalid_message_alias_target.proto:46:3: required specify alias = "org.post.PostData" in grpc.federation.message option for the "org.federation.PostData" type to automatically assign a value to the "Post.data" field via autobind +46: PostData data = 4; ^ `}, {file: "invalid_message_alias.proto", expected: ` -testdata/invalid_message_alias.proto:44:3: required specify alias = "org.post.PostData" in grpc.federation.message option for the "org.federation.PostData" type to automatically assign a value to the "Post.data" field via autobind -44: PostData data = 4; +invalid_message_alias.proto:46:3: required specify alias = "org.post.PostData" in grpc.federation.message option for the "org.federation.PostData" type to automatically assign a value to the "Post.data" field via autobind +46: PostData data = 4; ^ -testdata/invalid_message_alias.proto:54:44: cannot find package from "invalid.Invalid" -54: option (grpc.federation.message).alias = "invalid.Invalid"; +invalid_message_alias.proto:56:44: cannot find package from "invalid.Invalid" +56: option (grpc.federation.message).alias = "invalid.Invalid"; ^ -testdata/invalid_message_alias.proto:56:3: "type" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -56: PostType type = 1; +invalid_message_alias.proto:58:3: "type" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +58: PostType type = 1; ^ -testdata/invalid_message_alias.proto:57:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -57: string title = 2; +invalid_message_alias.proto:59:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +59: string title = 2; ^ -testdata/invalid_message_alias.proto:58:3: "content" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -58: PostContent content = 3; +invalid_message_alias.proto:60:3: "content" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +60: PostContent content = 3; ^ -testdata/invalid_message_alias.proto:73:3: "org.federation.SomeUser" message does not exist -73: option (grpc.federation.message).alias = "SomeUser"; +invalid_message_alias.proto:75:3: "org.federation.SomeUser" message does not exist +75: option (grpc.federation.message).alias = "SomeUser"; ^ -testdata/invalid_message_alias.proto:75:3: "name" field in "org.federation.User" message needs to specify "grpc.federation.field" option -75: string name = 1; +invalid_message_alias.proto:77:3: "name" field in "org.federation.User" message needs to specify "grpc.federation.field" option +77: string name = 1; ^ -testdata/invalid_message_alias.proto:79:3: "google.protobuf.Comment" message does not exist -79: option (grpc.federation.message).alias = "google.protobuf.Comment"; +invalid_message_alias.proto:81:3: "google.protobuf.Comment" message does not exist +81: option (grpc.federation.message).alias = "google.protobuf.Comment"; ^ -testdata/invalid_message_alias.proto:81:3: "body" field in "org.federation.Comment" message needs to specify "grpc.federation.field" option -81: string body = 1; +invalid_message_alias.proto:83:3: "body" field in "org.federation.Comment" message needs to specify "grpc.federation.field" option +83: string body = 1; ^ `}, {file: "invalid_nested_message_field.proto", expected: ` -testdata/invalid_nested_message_field.proto:52:7: "body" field in "federation.A.B.C" message needs to specify "grpc.federation.field" option +invalid_nested_message_field.proto:52:7: "body" field in "federation.A.B.C" message needs to specify "grpc.federation.field" option 52: string body = 1; ^ `}, {file: "invalid_method.proto", expected: ` -testdata/invalid_method.proto:36:24: invalid method format. required format is "./" but specified "" -36: { call { method: "" } }, +invalid_method.proto:37:24: invalid method format. required format is "./" but specified "" +37: { call { method: "" } }, ^ -testdata/invalid_method.proto:41:26: ERROR: :1:1: undeclared reference to 'invalid' (in container 'federation') +invalid_method.proto:42:26: ERROR: :1:1: undeclared reference to 'invalid' (in container 'federation') | invalid | ^ -41: args { inline: "invalid" } +42: args { inline: "invalid" } ^ -testdata/invalid_method.proto:46:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option -46: string id = 1; +invalid_method.proto:47:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option +47: string id = 1; ^ -testdata/invalid_method.proto:47:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option -47: string title = 2; +invalid_method.proto:48:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option +48: string title = 2; ^ -testdata/invalid_method.proto:48:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option -48: string content = 3; +invalid_method.proto:49:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option +49: string content = 3; ^ -testdata/invalid_method.proto:58:36: ERROR: :1:8: undefined field 'user_id' +invalid_method.proto:59:36: ERROR: :1:8: undefined field 'user_id' | __ARG__.user_id | .......^ -58: request { field: "id", by: "$.user_id" } +59: request { field: "id", by: "$.user_id" } ^ `}, {file: "invalid_multi_alias.proto", expected: ` -testdata/invalid_multi_alias.proto:55:3: if multiple aliases are specified, you must use grpc.federation.enum.select function to bind -55: PostType post_type = 4 [(grpc.federation.field).by = "org.post.PostDataType.POST_TYPE_A"]; +invalid_multi_alias.proto:56:3: if multiple aliases are specified, you must use grpc.federation.enum.select function to bind +56: PostType post_type = 4 [(grpc.federation.field).by = "org.post.PostDataType.POST_TYPE_A"]; ^ -testdata/invalid_multi_alias.proto:64:3: "POST_TYPE_A" value must be present in all enums, but it is missing in "org.post.PostDataType", "org.post.v2.PostDataType" enum -64: POST_TYPE_FOO = 1 [(grpc.federation.enum_value) = { alias: ["POST_TYPE_A"] }]; +invalid_multi_alias.proto:65:3: "POST_TYPE_A" value must be present in all enums, but it is missing in "org.post.PostDataType", "org.post.v2.PostDataType" enum +65: POST_TYPE_FOO = 1 [(grpc.federation.enum_value) = { alias: ["POST_TYPE_A"] }]; ^ -testdata/invalid_multi_alias.proto:65:3: "org.post.v2.PostDataType.POST_TYPE_B" value does not exist in "org.post.PostDataType", "org.post.v2.PostDataType" enum -65: POST_TYPE_BAR = 2 [(grpc.federation.enum_value) = { alias: ["org.post.v2.PostDataType.POST_TYPE_B", "POST_TYPE_C"] }]; +invalid_multi_alias.proto:66:3: "org.post.v2.PostDataType.POST_TYPE_B" value does not exist in "org.post.PostDataType", "org.post.v2.PostDataType" enum +66: POST_TYPE_BAR = 2 [(grpc.federation.enum_value) = { alias: ["org.post.v2.PostDataType.POST_TYPE_B", "POST_TYPE_C"] }]; ^ -testdata/invalid_multi_alias.proto:65:3: "POST_TYPE_C" value must be present in all enums, but it is missing in "org.post.PostDataType", "org.post.v2.PostDataType" enum -65: POST_TYPE_BAR = 2 [(grpc.federation.enum_value) = { alias: ["org.post.v2.PostDataType.POST_TYPE_B", "POST_TYPE_C"] }]; +invalid_multi_alias.proto:66:3: "POST_TYPE_C" value must be present in all enums, but it is missing in "org.post.PostDataType", "org.post.v2.PostDataType" enum +66: POST_TYPE_BAR = 2 [(grpc.federation.enum_value) = { alias: ["org.post.v2.PostDataType.POST_TYPE_B", "POST_TYPE_C"] }]; ^ -testdata/invalid_multi_alias.proto:74:3: The types of "org.federation.PostData"'s "title" field ("string") and "org.post.v2.PostData"'s field ("int64") are different. This field cannot be resolved automatically, so you must use the "grpc.federation.field" option to bind it yourself -74: string title = 2; +invalid_multi_alias.proto:75:3: The types of "org.federation.PostData"'s "title" field ("string") and "org.post.v2.PostData"'s field ("int64") are different. This field cannot be resolved automatically, so you must use the "grpc.federation.field" option to bind it yourself +75: string title = 2; ^ -testdata/invalid_multi_alias.proto:74:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -74: string title = 2; +invalid_multi_alias.proto:75:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +75: string title = 2; ^ -testdata/invalid_multi_alias.proto:75:3: required specify alias = "org.post.v2.PostContent" in grpc.federation.message option for the "org.federation.PostContent" type to automatically assign a value to the "PostData.content" field via autobind -75: PostContent content = 3; +invalid_multi_alias.proto:76:3: required specify alias = "org.post.v2.PostContent" in grpc.federation.message option for the "org.federation.PostContent" type to automatically assign a value to the "PostData.content" field via autobind +76: PostContent content = 3; ^ -testdata/invalid_multi_alias.proto:76:3: specified "alias" in grpc.federation.message option, but "dummy" field does not exist in "org.post.PostData" message -76: int64 dummy = 4; +invalid_multi_alias.proto:77:3: specified "alias" in grpc.federation.message option, but "dummy" field does not exist in "org.post.PostData" message +77: int64 dummy = 4; ^ -testdata/invalid_multi_alias.proto:76:3: "dummy" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -76: int64 dummy = 4; +invalid_multi_alias.proto:77:3: "dummy" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +77: int64 dummy = 4; ^ `}, {file: "invalid_oneof_selection.proto", expected: ` -testdata/invalid_oneof_selection.proto:26:47: "org.federation.UserSelection" type has "user" as oneof name, but "user" has a difference type and cannot be accessed directly, so "user" becomes an undefined field +invalid_oneof_selection.proto:26:47: "org.federation.UserSelection" type has "user" as oneof name, but "user" has a difference type and cannot be accessed directly, so "user" becomes an undefined field ERROR: :1:4: undefined field 'user' | sel.user | ...^ @@ -263,433 +263,433 @@ ERROR: :1:4: undefined field 'user' ^ `}, {file: "invalid_oneof.proto", expected: ` -testdata/invalid_oneof.proto:40:13: return value of "if" must be bool type but got int64 type -40: if: "1" +invalid_oneof.proto:43:13: return value of "if" must be bool type but got int64 type +43: if: "1" ^ -testdata/invalid_oneof.proto:53:39: "if" or "default" must be specified in "grpc.federation.field.oneof" -53: (grpc.federation.field).oneof = { +invalid_oneof.proto:56:39: "if" or "default" must be specified in "grpc.federation.field.oneof" +56: (grpc.federation.field).oneof = { ^ -testdata/invalid_oneof.proto:66:39: "by" must be specified in "grpc.federation.field.oneof" -66: (grpc.federation.field).oneof = { +invalid_oneof.proto:69:39: "by" must be specified in "grpc.federation.field.oneof" +69: (grpc.federation.field).oneof = { ^ -testdata/invalid_oneof.proto:80:18: "default" found multiple times in the "grpc.federation.field.oneof". "default" can only be specified once per oneof -80: default: true +invalid_oneof.proto:83:18: "default" found multiple times in the "grpc.federation.field.oneof". "default" can only be specified once per oneof +83: default: true ^ -testdata/invalid_oneof.proto:92:3: "oneof" feature can only be used for fields within oneof -92: bool foo = 5 [(grpc.federation.field).oneof = { +invalid_oneof.proto:95:3: "oneof" feature can only be used for fields within oneof +95: bool foo = 5 [(grpc.federation.field).oneof = { ^ -testdata/invalid_oneof.proto:92:3: value must be specified -92: bool foo = 5 [(grpc.federation.field).oneof = { +invalid_oneof.proto:95:3: value must be specified +95: bool foo = 5 [(grpc.federation.field).oneof = { ^ -testdata/invalid_oneof.proto:92:3: "foo" field in "org.federation.UserSelection" message needs to specify "grpc.federation.field" option -92: bool foo = 5 [(grpc.federation.field).oneof = { +invalid_oneof.proto:95:3: "foo" field in "org.federation.UserSelection" message needs to specify "grpc.federation.field" option +95: bool foo = 5 [(grpc.federation.field).oneof = { ^ -testdata/invalid_oneof.proto:109:20: "foo" field is a oneof field, so you need to specify an "if" expression -109: { field: "foo" by: "1" }, +invalid_oneof.proto:112:20: "foo" field is a oneof field, so you need to specify an "if" expression +112: { field: "foo" by: "1" }, ^ -testdata/invalid_oneof.proto:110:20: "bar" field is a oneof field, so you need to specify an "if" expression -110: { field: "bar" by: "'hello'" } +invalid_oneof.proto:113:20: "bar" field is a oneof field, so you need to specify an "if" expression +113: { field: "bar" by: "'hello'" } ^ `}, {file: "invalid_retry.proto", expected: ` -testdata/invalid_retry.proto:37:15: ERROR: :1:1: undeclared reference to 'foo' (in container 'org.federation') +invalid_retry.proto:38:15: ERROR: :1:1: undeclared reference to 'foo' (in container 'org.federation') | foo | ^ -37: if: "foo" +38: if: "foo" ^ -testdata/invalid_retry.proto:39:23: time: missing unit in duration "1" -39: interval: "1" +invalid_retry.proto:40:23: time: missing unit in duration "1" +40: interval: "1" ^ -testdata/invalid_retry.proto:54:15: if must always return a boolean value -54: if: "1" +invalid_retry.proto:55:15: if must always return a boolean value +55: if: "1" ^ -testdata/invalid_retry.proto:56:31: time: missing unit in duration "2" -56: initial_interval: "2" +invalid_retry.proto:57:31: time: missing unit in duration "2" +57: initial_interval: "2" ^ -testdata/invalid_retry.proto:72:27: time: missing unit in duration "3" -72: max_interval: "3" +invalid_retry.proto:73:27: time: missing unit in duration "3" +73: max_interval: "3" ^ `}, {file: "invalid_method_service_name.proto", expected: ` -testdata/invalid_method_service_name.proto: "post.InvalidService" service does not exist -testdata/invalid_method_service_name.proto:36:24: cannot find "method" method because the service to which the method belongs does not exist -36: { call { method: "post.InvalidService/method" } }, +invalid_method_service_name.proto: "post.InvalidService" service does not exist +invalid_method_service_name.proto:37:24: cannot find "method" method because the service to which the method belongs does not exist +37: { call { method: "post.InvalidService/method" } }, ^ -testdata/invalid_method_service_name.proto:46:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option -46: string id = 1; +invalid_method_service_name.proto:47:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option +47: string id = 1; ^ -testdata/invalid_method_service_name.proto:47:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option -47: string title = 2; +invalid_method_service_name.proto:48:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option +48: string title = 2; ^ -testdata/invalid_method_service_name.proto:48:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option -48: string content = 3; +invalid_method_service_name.proto:49:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option +49: string content = 3; ^ `}, {file: "invalid_method_name.proto", expected: ` -testdata/invalid_method_name.proto:36:24: "invalid" method does not exist in PostService service -36: { call { method: "post.PostService/invalid" } }, +invalid_method_name.proto:37:24: "invalid" method does not exist in PostService service +37: { call { method: "post.PostService/invalid" } }, ^ -testdata/invalid_method_name.proto:46:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option -46: string id = 1; +invalid_method_name.proto:47:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option +47: string id = 1; ^ -testdata/invalid_method_name.proto:47:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option -47: string title = 2; +invalid_method_name.proto:48:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option +48: string title = 2; ^ -testdata/invalid_method_name.proto:48:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option -48: string content = 3; +invalid_method_name.proto:49:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option +49: string content = 3; ^ `}, {file: "invalid_method_timeout_format.proto", expected: ` -testdata/invalid_method_timeout_format.proto:12:47: time: unknown unit "p" in duration "1p" +invalid_method_timeout_format.proto:12:47: time: unknown unit "p" in duration "1p" 12: option (grpc.federation.method).timeout = "1p"; ^ `}, {file: "invalid_method_request.proto", expected: ` -testdata/invalid_method_request.proto:40:28: "invalid" field does not exist in "post.GetPostRequest" message for method request -40: request { field: "invalid", by: "$.invalid" } +invalid_method_request.proto:43:28: "invalid" field does not exist in "post.GetPostRequest" message for method request +43: request { field: "invalid", by: "$.invalid" } ^ -testdata/invalid_method_request.proto:40:43: ERROR: :1:8: undefined field 'invalid' +invalid_method_request.proto:43:43: ERROR: :1:8: undefined field 'invalid' | __ARG__.invalid | .......^ -40: request { field: "invalid", by: "$.invalid" } +43: request { field: "invalid", by: "$.invalid" } ^ `}, {file: "missing_field_option.proto", expected: ` -testdata/missing_field_option.proto:56:3: "user" field in "federation.Post" message needs to specify "grpc.federation.field" option -56: User user = 4; +missing_field_option.proto:57:3: "user" field in "federation.Post" message needs to specify "grpc.federation.field" option +57: User user = 4; ^ `}, {file: "missing_map_iterator.proto", expected: ` -testdata/missing_map_iterator.proto:36:13: map iterator name must be specified +missing_map_iterator.proto:36:13: map iterator name must be specified 36: map { ^ -testdata/missing_map_iterator.proto:36:13: map iterator src must be specified +missing_map_iterator.proto:36:13: map iterator src must be specified 36: map { ^ `}, {file: "missing_message_alias.proto", expected: ` -testdata/missing_message_alias.proto:44:3: required specify alias = "org.post.PostData" in grpc.federation.message option for the "org.federation.PostData" type to automatically assign a value to the "Post.data" field via autobind -44: PostData data = 4; +missing_message_alias.proto:46:3: required specify alias = "org.post.PostData" in grpc.federation.message option for the "org.federation.PostData" type to automatically assign a value to the "Post.data" field via autobind +46: PostData data = 4; ^ -testdata/missing_message_alias.proto:54:3: "type" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -54: PostType type = 1; +missing_message_alias.proto:56:3: "type" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +56: PostType type = 1; ^ -testdata/missing_message_alias.proto:55:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -55: string title = 2; +missing_message_alias.proto:57:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +57: string title = 2; ^ -testdata/missing_message_alias.proto:56:3: use "alias" in "grpc.federation.field" option, but "alias" is not defined in "grpc.federation.message" option -56: PostContent content = 3 [(grpc.federation.field).alias = "content"]; +missing_message_alias.proto:58:3: use "alias" in "grpc.federation.field" option, but "alias" is not defined in "grpc.federation.message" option +58: PostContent content = 3 [(grpc.federation.field).alias = "content"]; ^ `}, {file: "missing_enum_alias.proto", expected: ` -testdata/missing_enum_alias.proto:47:1: required specify alias = "org.post.PostDataType" in grpc.federation.enum option for the "org.federation.PostType" type to automatically assign a value to the "PostData.type" field via autobind -47: enum PostType { +missing_enum_alias.proto:49:1: required specify alias = "org.post.PostDataType" in grpc.federation.enum option for the "org.federation.PostType" type to automatically assign a value to the "PostData.type" field via autobind +49: enum PostType { ^ -testdata/missing_enum_alias.proto:49:62: use "alias" in "grpc.federation.enum_value" option, but "alias" is not defined in "grpc.federation.enum" option -49: POST_TYPE_FOO = 1 [(grpc.federation.enum_value) = { alias: ["POST_TYPE_A"] }]; +missing_enum_alias.proto:51:62: use "alias" in "grpc.federation.enum_value" option, but "alias" is not defined in "grpc.federation.enum" option +51: POST_TYPE_FOO = 1 [(grpc.federation.enum_value) = { alias: ["POST_TYPE_A"] }]; ^ -testdata/missing_enum_alias.proto:50:62: use "alias" in "grpc.federation.enum_value" option, but "alias" is not defined in "grpc.federation.enum" option -50: POST_TYPE_BAR = 2 [(grpc.federation.enum_value) = { alias: ["POST_TYPE_B", "POST_TYPE_C"] }]; +missing_enum_alias.proto:52:62: use "alias" in "grpc.federation.enum_value" option, but "alias" is not defined in "grpc.federation.enum" option +52: POST_TYPE_BAR = 2 [(grpc.federation.enum_value) = { alias: ["POST_TYPE_B", "POST_TYPE_C"] }]; ^ -testdata/missing_enum_alias.proto:64:3: required specify alias = "org.post.PostContent.Category" in grpc.federation.enum option for the "org.federation.PostContent.Category" type to automatically assign a value to the "PostContent.category" field via autobind -64: enum Category { +missing_enum_alias.proto:66:3: required specify alias = "org.post.PostContent.Category" in grpc.federation.enum option for the "org.federation.PostContent.Category" type to automatically assign a value to the "PostContent.category" field via autobind +66: enum Category { ^ `}, {file: "missing_enum_value.proto", expected: ` -testdata/missing_enum_value.proto:50:3: specified "alias" in grpc.federation.enum option, but "FOO" value does not exist in "org.post.PostDataType" enum -50: FOO = 0; +missing_enum_value.proto:52:3: specified "alias" in grpc.federation.enum option, but "FOO" value does not exist in "org.post.PostDataType" enum +52: FOO = 0; ^ -testdata/missing_enum_value.proto:67:5: specified "alias" in grpc.federation.enum option, but "CATEGORY_C" value does not exist in "org.post.PostContent.Category" enum -67: CATEGORY_C = 2; +missing_enum_value.proto:69:5: specified "alias" in grpc.federation.enum option, but "CATEGORY_C" value does not exist in "org.post.PostContent.Category" enum +69: CATEGORY_C = 2; ^ `}, {file: "missing_enum_value_alias.proto", expected: ` -testdata/missing_enum_value_alias.proto:50:3: specified "alias" in grpc.federation.enum option, but "POST_TYPE_UNKNOWN" value does not exist in "org.post.PostDataType" enum -50: POST_TYPE_UNKNOWN = 0; +missing_enum_value_alias.proto:52:3: specified "alias" in grpc.federation.enum option, but "POST_TYPE_UNKNOWN" value does not exist in "org.post.PostDataType" enum +52: POST_TYPE_UNKNOWN = 0; ^ -testdata/missing_enum_value_alias.proto:51:3: specified "alias" in grpc.federation.enum option, but "POST_TYPE_FOO" value does not exist in "org.post.PostDataType" enum -51: POST_TYPE_FOO = 1; +missing_enum_value_alias.proto:53:3: specified "alias" in grpc.federation.enum option, but "POST_TYPE_FOO" value does not exist in "org.post.PostDataType" enum +53: POST_TYPE_FOO = 1; ^ -testdata/missing_enum_value_alias.proto:52:3: specified "alias" in grpc.federation.enum option, but "POST_TYPE_BAR" value does not exist in "org.post.PostDataType" enum -52: POST_TYPE_BAR = 2; +missing_enum_value_alias.proto:54:3: specified "alias" in grpc.federation.enum option, but "POST_TYPE_BAR" value does not exist in "org.post.PostDataType" enum +54: POST_TYPE_BAR = 2; ^ `}, {file: "valid_enum_value_reference.proto", expected: ""}, {file: "missing_message_field_alias.proto", expected: ` -testdata/missing_message_field_alias.proto:76:3: specified "alias" in grpc.federation.message option, but "dup_body" field does not exist in "org.post.PostContent" message -76: string dup_body = 4; +missing_message_field_alias.proto:78:3: specified "alias" in grpc.federation.message option, but "dup_body" field does not exist in "org.post.PostContent" message +78: string dup_body = 4; ^ -testdata/missing_message_field_alias.proto:76:3: "dup_body" field in "org.federation.PostContent" message needs to specify "grpc.federation.field" option -76: string dup_body = 4; +missing_message_field_alias.proto:78:3: "dup_body" field in "org.federation.PostContent" message needs to specify "grpc.federation.field" option +78: string dup_body = 4; ^ `}, {file: "missing_message_option.proto", expected: ` -testdata/missing_message_option.proto:47:17: "federation.User" message does not specify "grpc.federation.message" option -47: name: "User" +missing_message_option.proto:48:17: "federation.User" message does not specify "grpc.federation.message" option +48: name: "User" ^ `}, {file: "missing_method_request_value.proto", expected: ` -testdata/missing_method_request_value.proto:40:19: value must be specified -40: request { field: "id" } +missing_method_request_value.proto:41:19: value must be specified +41: request { field: "id" } ^ `}, {file: "missing_response_message_option.proto", expected: ` -testdata/missing_response_message_option.proto:18:1: "federation.GetPostResponse" message needs to specify "grpc.federation.message" option +missing_response_message_option.proto:18:1: "federation.GetPostResponse" message needs to specify "grpc.federation.message" option 18: message GetPostResponse { ^ `}, {file: "invalid_method_response.proto", expected: ` -testdata/invalid_method_response.proto:42:27: ERROR: :1:1: undeclared reference to 'invalid' (in container 'federation') +invalid_method_response.proto:43:27: ERROR: :1:1: undeclared reference to 'invalid' (in container 'federation') | invalid | ^ -42: { name: "post", by: "invalid", autobind: true }, +43: { name: "post", by: "invalid", autobind: true }, ^ -testdata/invalid_method_response.proto:47:26: ERROR: :1:1: undeclared reference to 'post' (in container 'federation') +invalid_method_response.proto:48:26: ERROR: :1:1: undeclared reference to 'post' (in container 'federation') | post | ^ -47: args { inline: "post" } +48: args { inline: "post" } ^ -testdata/invalid_method_response.proto:52:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option -52: string id = 1; +invalid_method_response.proto:53:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option +53: string id = 1; ^ -testdata/invalid_method_response.proto:53:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option -53: string title = 2; +invalid_method_response.proto:54:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option +54: string title = 2; ^ -testdata/invalid_method_response.proto:54:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option -54: string content = 3; +invalid_method_response.proto:55:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option +55: string content = 3; ^ -testdata/invalid_method_response.proto:64:36: ERROR: :1:8: undefined field 'user_id' +invalid_method_response.proto:65:36: ERROR: :1:8: undefined field 'user_id' | __ARG__.user_id | .......^ -64: request { field: "id", by: "$.user_id" } +65: request { field: "id", by: "$.user_id" } ^ `}, {file: "invalid_message_name.proto", expected: ` -testdata/invalid_message_name.proto:46:17: "federation.Invalid" message does not exist -46: message { +invalid_message_name.proto:48:17: "federation.Invalid" message does not exist +48: message { ^ -testdata/invalid_message_name.proto:47:17: undefined message specified -47: name: "Invalid" +invalid_message_name.proto:49:17: undefined message specified +49: name: "Invalid" ^ -testdata/invalid_message_name.proto:53:17: "post.Invalid" message does not exist -53: message { +invalid_message_name.proto:55:17: "post.Invalid" message does not exist +55: message { ^ -testdata/invalid_message_name.proto:54:17: undefined message specified -54: name: "post.Invalid" +invalid_message_name.proto:56:17: undefined message specified +56: name: "post.Invalid" ^ `}, {file: "invalid_nested_message_name.proto", expected: ` -testdata/invalid_nested_message_name.proto:36:31: "federation.Invalid1" message does not exist +invalid_nested_message_name.proto:36:31: "federation.Invalid1" message does not exist 36: { name: "b1" message: { name: "Invalid1" } } ^ -testdata/invalid_nested_message_name.proto:36:39: undefined message specified +invalid_nested_message_name.proto:36:39: undefined message specified 36: { name: "b1" message: { name: "Invalid1" } } ^ -testdata/invalid_nested_message_name.proto:42:33: "federation.Invalid2" message does not exist +invalid_nested_message_name.proto:42:33: "federation.Invalid2" message does not exist 42: { name: "c1" message: { name: "Invalid2" } } ^ -testdata/invalid_nested_message_name.proto:42:41: undefined message specified +invalid_nested_message_name.proto:42:41: undefined message specified 42: { name: "c1" message: { name: "Invalid2" } } ^ -testdata/invalid_nested_message_name.proto:45:7: cannot convert type automatically: field type is "string" but specified value type is "null" +invalid_nested_message_name.proto:45:7: cannot convert type automatically: field type is "string" but specified value type is "null" 45: string c1 = 1 [(grpc.federation.field).by = "c1"]; ^ -testdata/invalid_nested_message_name.proto:47:5: cannot convert type automatically: field type is "string" but specified value type is "null" +invalid_nested_message_name.proto:47:5: cannot convert type automatically: field type is "string" but specified value type is "null" 47: string b1 = 1 [(grpc.federation.field).by = "b1"]; ^ `}, {file: "invalid_message_argument.proto", expected: ` -testdata/invalid_message_argument.proto:51:19: ERROR: :1:11: type 'string' does not support field selection +invalid_message_argument.proto:52:19: ERROR: :1:11: type 'string' does not support field selection | __ARG__.id.invalid | ..........^ -51: { by: "$.id.invalid" }, +52: { by: "$.id.invalid" }, ^ -testdata/invalid_message_argument.proto:52:23: inline value is not message type -52: { inline: "post.id" }, +invalid_message_argument.proto:53:23: inline value is not message type +53: { inline: "post.id" }, ^ -testdata/invalid_message_argument.proto:53:19: ERROR: :1:2: Syntax error: no viable alternative at input '..' +invalid_message_argument.proto:54:19: ERROR: :1:2: Syntax error: no viable alternative at input '..' | .... | .^ -53: { by: "...." }, +54: { by: "...." }, ^ -testdata/invalid_message_argument.proto:54:23: ERROR: :1:2: Syntax error: no viable alternative at input '..' +invalid_message_argument.proto:55:23: ERROR: :1:2: Syntax error: no viable alternative at input '..' | .... | .^ -54: { inline: "...." } +55: { inline: "...." } ^ -testdata/invalid_message_argument.proto:72:36: ERROR: :1:8: undefined field 'user_id' +invalid_message_argument.proto:73:36: ERROR: :1:8: undefined field 'user_id' | __ARG__.user_id | .......^ -72: request { field: "id", by: "$.user_id" } +73: request { field: "id", by: "$.user_id" } ^ -testdata/invalid_message_argument.proto:87:14: "x" argument name is declared with a different type kind. found "string" and "bool" type -87: args { name: "x" by: "true" } +invalid_message_argument.proto:88:14: "x" argument name is declared with a different type kind. found "string" and "bool" type +88: args { name: "x" by: "true" } ^ `}, {file: "invalid_message_field_alias.proto", expected: ` -testdata/invalid_message_field_alias.proto:59:3: The types of "org.federation.PostData"'s "title" field ("int64") and "org.post.PostData"'s field ("string") are different. This field cannot be resolved automatically, so you must use the "grpc.federation.field" option to bind it yourself -59: int64 title = 2; +invalid_message_field_alias.proto:61:3: The types of "org.federation.PostData"'s "title" field ("int64") and "org.post.PostData"'s field ("string") are different. This field cannot be resolved automatically, so you must use the "grpc.federation.field" option to bind it yourself +61: int64 title = 2; ^ -testdata/invalid_message_field_alias.proto:59:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option -59: int64 title = 2; +invalid_message_field_alias.proto:61:3: "title" field in "org.federation.PostData" message needs to specify "grpc.federation.field" option +61: int64 title = 2; ^ -testdata/invalid_message_field_alias.proto:86:3: The types of "org.federation.PostContent"'s "body" field ("int64") and "org.post.PostContent"'s field ("string") are different. This field cannot be resolved automatically, so you must use the "grpc.federation.field" option to bind it yourself -86: int64 body = 3; +invalid_message_field_alias.proto:88:3: The types of "org.federation.PostContent"'s "body" field ("int64") and "org.post.PostContent"'s field ("string") are different. This field cannot be resolved automatically, so you must use the "grpc.federation.field" option to bind it yourself +88: int64 body = 3; ^ -testdata/invalid_message_field_alias.proto:86:3: "body" field in "org.federation.PostContent" message needs to specify "grpc.federation.field" option -86: int64 body = 3; +invalid_message_field_alias.proto:88:3: "body" field in "org.federation.PostContent" message needs to specify "grpc.federation.field" option +88: int64 body = 3; ^ `}, {file: "recursive_message_name.proto", expected: ` -testdata/recursive_message_name.proto:33:1: found cyclic dependency in "federation.Post" message. dependency path: GetPostResponse => Post => Post -33: message Post { +recursive_message_name.proto:35:1: found cyclic dependency in "federation.Post" message. dependency path: GetPostResponse => Post => Post +35: message Post { ^ -testdata/recursive_message_name.proto:44:39: recursive definition: "Post" is own message name -44: { name: "self", message { name: "Post" } } +recursive_message_name.proto:46:39: recursive definition: "Post" is own message name +46: { name: "self", message { name: "Post" } } ^ -testdata/recursive_message_name.proto:47:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option -47: string id = 1; +recursive_message_name.proto:49:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option +49: string id = 1; ^ -testdata/recursive_message_name.proto:48:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option -48: string title = 2; +recursive_message_name.proto:50:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option +50: string title = 2; ^ -testdata/recursive_message_name.proto:49:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option -49: string content = 3; +recursive_message_name.proto:51:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option +51: string content = 3; ^ `}, {file: "message_cyclic_dependency.proto", expected: ` -testdata/message_cyclic_dependency.proto:27:1: found cyclic dependency in "org.federation.A" message. dependency path: GetResponse => A => AA => AAA => A +message_cyclic_dependency.proto:27:1: found cyclic dependency in "org.federation.A" message. dependency path: GetResponse => A => AA => AAA => A 27: message A { ^ `}, {file: "nested_message_cyclic_dependency.proto", expected: ` -testdata/nested_message_cyclic_dependency.proto:50:5: found cyclic dependency in "federation.C" message. dependency path: GetAResponse => A => B => C => C +nested_message_cyclic_dependency.proto:50:5: found cyclic dependency in "federation.C" message. dependency path: GetAResponse => A => B => C => C 50: message C { ^ -testdata/nested_message_cyclic_dependency.proto:55:19: recursive definition: "C" is own message name +nested_message_cyclic_dependency.proto:55:19: recursive definition: "C" is own message name 55: name: "A.B.C" ^ `}, {file: "invalid_variable_name.proto", expected: ` -testdata/invalid_variable_name.proto:20:15: "_def0" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ -20: { name: "_def0" by: "0" }, +invalid_variable_name.proto:22:15: "_def0" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ +22: { name: "_def0" by: "0" }, ^ -testdata/invalid_variable_name.proto:24:25: "_def1" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ -24: def { name: "_def1" by: "1" } +invalid_variable_name.proto:26:25: "_def1" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ +26: def { name: "_def1" by: "1" } ^ -testdata/invalid_variable_name.proto:26:27: "_def2" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ -26: def { name: "_def2" by: "2" } +invalid_variable_name.proto:28:27: "_def2" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ +28: def { name: "_def2" by: "2" } ^ -testdata/invalid_variable_name.proto:35:25: "_def3" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ -35: def { name: "_def3" by: "3" } +invalid_variable_name.proto:37:25: "_def3" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ +37: def { name: "_def3" by: "3" } ^ -testdata/invalid_variable_name.proto:37:27: "_def4" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ -37: def { name: "_def4" by: "4" } +invalid_variable_name.proto:39:27: "_def4" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ +39: def { name: "_def4" by: "4" } ^ -testdata/invalid_variable_name.proto:47:19: "_def5" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ -47: def { name: "_def5" by: "5" } +invalid_variable_name.proto:49:19: "_def5" is invalid name. name should be in the following pattern: ^[a-zA-Z][a-zA-Z0-9_]*$ +49: def { name: "_def5" by: "5" } ^ `}, {file: "invalid_wrapper_type_conversion.proto", expected: ` -testdata/invalid_wrapper_type_conversion.proto:20:3: cannot convert message to "double" +invalid_wrapper_type_conversion.proto:20:3: cannot convert message to "double" 20: double double_value = 1 [(grpc.federation.field).by = "google.protobuf.DoubleValue{value: 1.23}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:21:3: cannot convert message to "float" +invalid_wrapper_type_conversion.proto:21:3: cannot convert message to "float" 21: float float_value = 2 [(grpc.federation.field).by = "google.protobuf.FloatValue{value: 3.45}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:22:3: cannot convert message to "int64" +invalid_wrapper_type_conversion.proto:22:3: cannot convert message to "int64" 22: int64 i64_value = 3 [(grpc.federation.field).by = "google.protobuf.Int64Value{value: 1}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:23:3: cannot convert message to "uint64" +invalid_wrapper_type_conversion.proto:23:3: cannot convert message to "uint64" 23: uint64 u64_value = 4 [(grpc.federation.field).by = "google.protobuf.UInt64Value{value: uint(2)}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:24:3: cannot convert message to "int32" +invalid_wrapper_type_conversion.proto:24:3: cannot convert message to "int32" 24: int32 i32_value = 5 [(grpc.federation.field).by = "google.protobuf.Int32Value{value: 3}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:25:3: cannot convert message to "uint32" +invalid_wrapper_type_conversion.proto:25:3: cannot convert message to "uint32" 25: uint32 u32_value = 6 [(grpc.federation.field).by = "google.protobuf.UInt32Value{value: uint(4)}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:26:3: cannot convert message to "bool" +invalid_wrapper_type_conversion.proto:26:3: cannot convert message to "bool" 26: bool bool_value = 7 [(grpc.federation.field).by = "google.protobuf.BoolValue{value: true}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:27:3: cannot convert message to "string" +invalid_wrapper_type_conversion.proto:27:3: cannot convert message to "string" 27: string string_value = 8 [(grpc.federation.field).by = "google.protobuf.StringValue{value: 'hello'}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:28:3: cannot convert message to "bytes" +invalid_wrapper_type_conversion.proto:28:3: cannot convert message to "bytes" 28: bytes bytes_value = 9 [(grpc.federation.field).by = "google.protobuf.BytesValue{value: bytes('world')}"]; ^ -testdata/invalid_wrapper_type_conversion.proto:40:3: cannot convert type automatically: field type is "message" but specified value type is "double" +invalid_wrapper_type_conversion.proto:40:3: cannot convert type automatically: field type is "message" but specified value type is "double" 40: google.protobuf.DoubleValue double_wrapper_value2 = 19 [(grpc.federation.field).by = "1.23"]; ^ -testdata/invalid_wrapper_type_conversion.proto:41:3: cannot convert type automatically: field type is "message" but specified value type is "double" +invalid_wrapper_type_conversion.proto:41:3: cannot convert type automatically: field type is "message" but specified value type is "double" 41: google.protobuf.FloatValue float_wrapper_value2 = 20 [(grpc.federation.field).by = "3.45"]; ^ -testdata/invalid_wrapper_type_conversion.proto:42:3: cannot convert type automatically: field type is "message" but specified value type is "int64" +invalid_wrapper_type_conversion.proto:42:3: cannot convert type automatically: field type is "message" but specified value type is "int64" 42: google.protobuf.Int64Value i64_wrapper_value2 = 21 [(grpc.federation.field).by = "1"]; ^ -testdata/invalid_wrapper_type_conversion.proto:43:3: cannot convert type automatically: field type is "message" but specified value type is "uint64" +invalid_wrapper_type_conversion.proto:43:3: cannot convert type automatically: field type is "message" but specified value type is "uint64" 43: google.protobuf.UInt64Value u64_wrapper_value2 = 22 [(grpc.federation.field).by = "uint(2)"]; ^ -testdata/invalid_wrapper_type_conversion.proto:44:3: cannot convert type automatically: field type is "message" but specified value type is "int64" +invalid_wrapper_type_conversion.proto:44:3: cannot convert type automatically: field type is "message" but specified value type is "int64" 44: google.protobuf.Int32Value i32_wrapper_value2 = 23 [(grpc.federation.field).by = "3"]; ^ -testdata/invalid_wrapper_type_conversion.proto:45:3: cannot convert type automatically: field type is "message" but specified value type is "uint64" +invalid_wrapper_type_conversion.proto:45:3: cannot convert type automatically: field type is "message" but specified value type is "uint64" 45: google.protobuf.UInt32Value u32_wrapper_value2 = 24 [(grpc.federation.field).by = "uint(4)"]; ^ -testdata/invalid_wrapper_type_conversion.proto:46:3: cannot convert type automatically: field type is "message" but specified value type is "bool" +invalid_wrapper_type_conversion.proto:46:3: cannot convert type automatically: field type is "message" but specified value type is "bool" 46: google.protobuf.BoolValue bool_wrapper_value2 = 25 [(grpc.federation.field).by = "true"]; ^ -testdata/invalid_wrapper_type_conversion.proto:47:3: cannot convert type automatically: field type is "message" but specified value type is "string" +invalid_wrapper_type_conversion.proto:47:3: cannot convert type automatically: field type is "message" but specified value type is "string" 47: google.protobuf.StringValue string_wrapper_value2 = 26 [(grpc.federation.field).by = "'hello'"]; ^ -testdata/invalid_wrapper_type_conversion.proto:48:3: cannot convert type automatically: field type is "message" but specified value type is "bytes" +invalid_wrapper_type_conversion.proto:48:3: cannot convert type automatically: field type is "message" but specified value type is "bytes" 48: google.protobuf.BytesValue bytes_wrapper_value2 = 27 [(grpc.federation.field).by = "bytes('world')"]; ^ `}, {file: "invalid_validation_return_type.proto", expected: ` -testdata/invalid_validation_return_type.proto:48:17: if must always return a boolean value -48: if: "post.id" +invalid_validation_return_type.proto:50:17: if must always return a boolean value +50: if: "post.id" ^ `}, {file: "invalid_validation_details_return_type.proto", expected: ` -testdata/invalid_validation_details_return_type.proto:49:19: if must always return a boolean value -49: if: "'string'" +invalid_validation_details_return_type.proto:51:19: if must always return a boolean value +51: if: "'string'" ^ `}, {file: "invalid_validation_message_argument.proto", expected: ` -testdata/invalid_validation_message_argument.proto:69:52: ERROR: :1:8: undefined field 'message' +invalid_validation_message_argument.proto:71:52: ERROR: :1:8: undefined field 'message' | __ARG__.message | .......^ -69: string message = 1 [(grpc.federation.field).by = "$.message"]; +71: string message = 1 [(grpc.federation.field).by = "$.message"]; ^ `}, {file: "invalid_validation_precondition_failure.proto", expected: ` -testdata/invalid_validation_precondition_failure.proto:52:25: type must always return a string value -52: type: "1", +invalid_validation_precondition_failure.proto:54:25: type must always return a string value +54: type: "1", ^ -testdata/invalid_validation_precondition_failure.proto:53:28: subject must always return a string value -53: subject: "2", +invalid_validation_precondition_failure.proto:55:28: subject must always return a string value +55: subject: "2", ^ -testdata/invalid_validation_precondition_failure.proto:54:32: description must always return a string value -54: description: "3", +invalid_validation_precondition_failure.proto:56:32: description must always return a string value +56: description: "3", ^ `}, {file: "invalid_validation_bad_request.proto", expected: ` -testdata/invalid_validation_bad_request.proto:52:26: field must always return a string value -52: field: "1", +invalid_validation_bad_request.proto:54:26: field must always return a string value +54: field: "1", ^ -testdata/invalid_validation_bad_request.proto:53:32: description must always return a string value -53: description: "2", +invalid_validation_bad_request.proto:55:32: description must always return a string value +55: description: "2", ^ `}, {file: "invalid_validation_localized_message.proto", expected: ` -testdata/invalid_validation_localized_message.proto:52:26: message must always return a string value -52: message: "1" +invalid_validation_localized_message.proto:54:26: message must always return a string value +54: message: "1" ^ `}, {file: "invalid_list_sort.proto", expected: ` -testdata/invalid_list_sort.proto:55:59: ERROR: :1:14: list(org.federation.User) is not comparable +invalid_list_sort.proto:55:59: ERROR: :1:14: list(org.federation.User) is not comparable | users.sortAsc(v, v).sortDesc(v, v).sortStableAsc(v, v).sortStableDesc(v, v) | .............^ ERROR: :1:29: list(org.federation.User) is not comparable @@ -705,51 +705,37 @@ ERROR: :1:70: list(org.federation.User) is not comparable ^ `}, {file: "invalid_message_map.proto", expected: ` -testdata/invalid_message_map.proto:30:3: cannot convert type automatically: map key type is "int32" but specified map key type is "string" -30: map map_value = 1 [(grpc.federation.field).by = "map_value"]; +invalid_message_map.proto:32:3: cannot convert type automatically: map key type is "int32" but specified map key type is "string" +32: map map_value = 1 [(grpc.federation.field).by = "map_value"]; ^ -testdata/invalid_message_map.proto:30:3: cannot convert type automatically: map value type is "int32" but specified map value type is "string" -30: map map_value = 1 [(grpc.federation.field).by = "map_value"]; +invalid_message_map.proto:32:3: cannot convert type automatically: map value type is "int32" but specified map value type is "string" +32: map map_value = 1 [(grpc.federation.field).by = "map_value"]; ^ -testdata/invalid_message_map.proto:40:19: cannot convert type automatically: map key type is "string" but specified map key type is "int64" -40: request { field: "ids", by: "$.ids" } +invalid_message_map.proto:42:19: cannot convert type automatically: map key type is "string" but specified map key type is "int64" +42: request { field: "ids", by: "$.ids" } ^ -testdata/invalid_message_map.proto:40:19: cannot convert type automatically: map value type is "string" but specified map value type is "int64" -40: request { field: "ids", by: "$.ids" } +invalid_message_map.proto:42:19: cannot convert type automatically: map value type is "string" but specified map value type is "int64" +42: request { field: "ids", by: "$.ids" } ^ `}, {file: "invalid_message_map_alias.proto", expected: ` -testdata/invalid_message_map_alias.proto:37:3: cannot convert type automatically: map key type is "string" but specified map key type is "int32" -37: map counts = 4; +invalid_message_map_alias.proto:39:3: cannot convert type automatically: map key type is "string" but specified map key type is "int32" +39: map counts = 4; ^ -testdata/invalid_message_map_alias.proto:37:3: cannot convert type automatically: map value type is "string" but specified map value type is "int32" -37: map counts = 4; +invalid_message_map_alias.proto:39:3: cannot convert type automatically: map value type is "string" but specified map value type is "int32" +39: map counts = 4; ^ `}, {file: "invalid_file_import.proto", expected: ` -testdata/invalid_file_import.proto:10:12: unknown.proto: no such file or directory -10: import: ["unknown.proto"] - ^ -testdata/invalid_file_import.proto:40:17: "post" package does not exist -40: method: "post.PostService/GetPost" - ^ -testdata/invalid_file_import.proto:44:29: ERROR: :1:1: undeclared reference to 'res' (in container 'federation') - | res.post - | ^ -44: def { name: "post", by: "res.post", autobind: true } - ^ -testdata/invalid_file_import.proto:46:3: "id" field in "federation.Post" message needs to specify "grpc.federation.field" option -46: string id = 1; - ^ -testdata/invalid_file_import.proto:47:3: "title" field in "federation.Post" message needs to specify "grpc.federation.field" option -47: string title = 2; - ^ -testdata/invalid_file_import.proto:48:3: "content" field in "federation.Post" message needs to specify "grpc.federation.field" option -48: string content = 3; - ^ -testdata/invalid_file_import.proto:49:3: "user_id" field in "federation.Post" message needs to specify "grpc.federation.field" option -49: string user_id = 4; - ^ +invalid_file_import.proto:6:8: [WARN] Import post.proto is used only for the definition of grpc federation. You can use grpc.federation.file.import instead. +6: import "post.proto"; + ^ +invalid_file_import.proto:12:5: [WARN] Import user.proto is unused for the definition of grpc federation. +12: "user.proto", + ^ +invalid_file_import.proto:13:5: unknown.proto: no such file or directory +13: "unknown.proto" + ^ `}, } for _, test := range tests { @@ -767,7 +753,7 @@ testdata/invalid_file_import.proto:49:3: "user_id" field in "federation.Post" me if err != nil { t.Fatal(err) } - got := validator.Format(v.Validate(ctx, file)) + got := validator.Format(v.Validate(ctx, file, validator.ImportPathOption("testdata"))) if test.expected == "" { if got != "" { t.Errorf("expected to receive no validation error but got: %s", got) From a2acfef6216ab61d3bd86f53755b558536641930 Mon Sep 17 00:00:00 2001 From: utahta Date: Fri, 23 Aug 2024 16:00:44 +0900 Subject: [PATCH 2/3] fix test for lsp --- compiler/compiler.go | 11 +++++++++++ lsp/server/completion_test.go | 20 ++++++++++---------- lsp/server/definition.go | 2 +- lsp/server/handler_test.go | 10 +++++----- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index e2f823fd..3dc80918 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -28,6 +28,7 @@ import ( type Compiler struct { importPaths []string manualImport bool + importRule bool } // Option represents compiler option. @@ -50,6 +51,13 @@ func ManualImportOption() Option { } } +// ImportRuleOption used to reference proto files imported by grpc.federation.file.import rule. +func ImportRuleOption() Option { + return func(c *Compiler) { + c.importRule = true + } +} + // New creates compiler instance. func New() *Compiler { return &Compiler{} @@ -183,6 +191,9 @@ func (c *Compiler) Compile(ctx context.Context, file *source.File, opts ...Optio } files := []string{relPath} files = append(files, file.Imports()...) + if c.importRule { + files = append(files, file.ImportsByImportRule()...) + } linkedFiles, err := compiler.Compile(ctx, files...) if err != nil { return nil, &CompilerError{Err: err, ErrWithPos: r.errs} diff --git a/lsp/server/completion_test.go b/lsp/server/completion_test.go index 49c6d06f..bf70dc7f 100644 --- a/lsp/server/completion_test.go +++ b/lsp/server/completion_test.go @@ -24,8 +24,8 @@ func TestCompletion(t *testing.T) { completer := server.NewCompleter(compiler.New(), slog.New(slog.NewJSONHandler(os.Stdout, nil))) t.Run("method", func(t *testing.T) { // resolver.method value position of Post in service.proto file - _, candidates, err := completer.Completion(ctx, nil, path, file, source.Position{ - Line: 39, + _, candidates, err := completer.Completion(ctx, []string{"testdata"}, path, file, source.Position{ + Line: 40, Col: 19, }) if err != nil { @@ -44,8 +44,8 @@ func TestCompletion(t *testing.T) { t.Run("request.field", func(t *testing.T) { // resolver.request.field value position of Post in service.proto file - _, candidates, err := completer.Completion(ctx, nil, path, file, source.Position{ - Line: 40, + _, candidates, err := completer.Completion(ctx, []string{"testdata"}, path, file, source.Position{ + Line: 41, Col: 28, }) if err != nil { @@ -61,8 +61,8 @@ func TestCompletion(t *testing.T) { t.Run("request.by", func(t *testing.T) { // resolver.request.by value position os Post in service.proto file - _, candidates, err := completer.Completion(ctx, nil, path, file, source.Position{ - Line: 40, + _, candidates, err := completer.Completion(ctx, []string{"testdata"}, path, file, source.Position{ + Line: 41, Col: 38, }) if err != nil { @@ -78,8 +78,8 @@ func TestCompletion(t *testing.T) { t.Run("filter response", func(t *testing.T) { // resolver.response.field value position of Post in service.proto file - _, candidates, err := completer.Completion(ctx, nil, path, file, source.Position{ - Line: 43, + _, candidates, err := completer.Completion(ctx, []string{"testdata"}, path, file, source.Position{ + Line: 44, Col: 28, }) if err != nil { @@ -96,8 +96,8 @@ func TestCompletion(t *testing.T) { t.Run("message", func(t *testing.T) { // def[2].message value position of Post in service.proto file - _, candidates, err := completer.Completion(ctx, nil, path, file, source.Position{ - Line: 47, + _, candidates, err := completer.Completion(ctx, []string{"testdata"}, path, file, source.Position{ + Line: 48, Col: 17, }) if err != nil { diff --git a/lsp/server/definition.go b/lsp/server/definition.go index 4fde7f8c..fad4b62d 100644 --- a/lsp/server/definition.go +++ b/lsp/server/definition.go @@ -52,7 +52,7 @@ func (h *Handler) definitionWithLink(ctx context.Context, params *protocol.Defin return nil, nil } h.logger.Info("node", slog.String("text", nodeInfo.RawText())) - protoFiles, err := h.compiler.Compile(ctx, file, compiler.ImportPathOption(h.importPaths...)) + protoFiles, err := h.compiler.Compile(ctx, file, compiler.ImportPathOption(h.importPaths...), compiler.ImportRuleOption()) if err != nil { return nil, err } diff --git a/lsp/server/handler_test.go b/lsp/server/handler_test.go index 4d031fc8..b3daaba6 100644 --- a/lsp/server/handler_test.go +++ b/lsp/server/handler_test.go @@ -175,7 +175,7 @@ func TestHandler_Definition(t *testing.T) { URI: mustTestdataAbs(t, "testdata/service.proto"), }, Position: protocol.Position{ - Line: 24, + Line: 25, Character: 15, }, }, @@ -184,8 +184,8 @@ func TestHandler_Definition(t *testing.T) { { URI: mustTestdataAbs(t, "testdata/service.proto"), Range: protocol.Range{ - Start: protocol.Position{Line: 32, Character: 8}, - End: protocol.Position{Line: 32, Character: 12}, + Start: protocol.Position{Line: 33, Character: 8}, + End: protocol.Position{Line: 33, Character: 12}, }, }, }, @@ -198,7 +198,7 @@ func TestHandler_Definition(t *testing.T) { URI: mustTestdataAbs(t, "testdata/service.proto"), }, Position: protocol.Position{ - Line: 38, + Line: 39, Character: 19, }, }, @@ -221,7 +221,7 @@ func TestHandler_Definition(t *testing.T) { URI: mustTestdataAbs(t, "testdata/service.proto"), }, Position: protocol.Position{ - Line: 19, + Line: 20, Character: 0, }, }, From 2b5250120770bfe8bd0299f6572e8728ad3dd6ad Mon Sep 17 00:00:00 2001 From: utahta Date: Fri, 23 Aug 2024 16:23:18 +0900 Subject: [PATCH 3/3] fix test for resolver --- internal/testutil/cmpopt.go | 2 +- resolver/resolver.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/testutil/cmpopt.go b/internal/testutil/cmpopt.go index 171e36dc..7835b77b 100644 --- a/internal/testutil/cmpopt.go +++ b/internal/testutil/cmpopt.go @@ -13,7 +13,7 @@ func ResolverCmpOpts() []cmp.Option { cmpopts.IgnoreFields(resolver.File{}, "Messages", "Services", "Enums", "Desc", "CELPlugins", "ImportFiles"), cmpopts.IgnoreFields(resolver.Service{}, "CELPlugins", "Desc"), cmpopts.IgnoreFields(resolver.Package{}, "Files"), - cmpopts.IgnoreFields(resolver.Method{}, "Service"), + cmpopts.IgnoreFields(resolver.Method{}, "Service", "Desc"), cmpopts.IgnoreFields(resolver.Message{}, "File", "ParentMessage", "Desc"), cmpopts.IgnoreFields(resolver.Enum{}, "File", "Message.Rule"), cmpopts.IgnoreFields(resolver.EnumValue{}, "Enum"), diff --git a/resolver/resolver.go b/resolver/resolver.go index 12a15932..4665c5be 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -378,7 +378,7 @@ func (r *Resolver) allMessages(files []*File) []*Message { func (r *Resolver) validateFiles(ctx *context, files []*File) { for _, file := range files { - ctx = ctx.withFile(file) + ctx := ctx.withFile(file) r.validateFileImport(ctx, file) for _, svc := range file.Services {