From 11eea56bd9c4b3e79a39a8ec23caa99ed66c7598 Mon Sep 17 00:00:00 2001 From: Ludwig Bedacht Date: Mon, 7 Jul 2025 09:42:38 +0200 Subject: [PATCH 1/5] feat: allow use of nullable types in the response message --- .../datasource/grpc_datasource/compiler.go | 34 +- .../grpc_datasource/execution_plan.go | 10 +- .../grpc_datasource/execution_plan_test.go | 134 +- .../grpc_datasource/execution_plan_visitor.go | 5 + .../grpc_datasource/grpc_datasource.go | 19 + .../grpc_datasource/grpc_datasource_test.go | 81 + .../grpc_datasource/mapping_test_helper.go | 126 +- v2/pkg/grpctest/mapping/mapping.go | 132 ++ v2/pkg/grpctest/mockservice.go | 282 ++++ v2/pkg/grpctest/product.proto | 85 ++ v2/pkg/grpctest/productv1/product.pb.go | 1317 ++++++++++++++--- v2/pkg/grpctest/productv1/product_grpc.pb.go | 230 +++ v2/pkg/grpctest/testdata/products.graphqls | 38 + 13 files changed, 2252 insertions(+), 241 deletions(-) diff --git a/v2/pkg/engine/datasource/grpc_datasource/compiler.go b/v2/pkg/engine/datasource/grpc_datasource/compiler.go index 6192c2ea0c..ecbe110bf4 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/compiler.go +++ b/v2/pkg/engine/datasource/grpc_datasource/compiler.go @@ -90,6 +90,7 @@ func parseDataType(name string) DataType { // Document represents a compiled protobuf document with all its services, messages, and methods. type Document struct { Package string // The package name of the protobuf document + Imports []string // The imports of the protobuf document Services []Service // All services defined in the document Enums []Enum // All enums defined in the document Messages []Message // All messages defined in the document @@ -271,34 +272,47 @@ func NewProtoCompiler(schema string, mapping *GRPCMapping) (*RPCCompiler, error) return nil, fmt.Errorf("no files compiled") } - f := fd[0] + schemaFile := fd[0] pc := &RPCCompiler{ doc: &Document{ - Package: string(f.Package()), + Package: string(schemaFile.Package()), }, report: operationreport.Report{}, } // Extract information from the compiled file descriptor - pc.doc.Package = string(f.Package()) + pc.doc.Package = string(schemaFile.Package()) + // We potentially have imported other files and need to resolve the types first + // before we can parse the schema. + for i := 0; i < schemaFile.Imports().Len(); i++ { + protoImport := schemaFile.Imports().Get(i) + pc.doc.Imports = append(pc.doc.Imports, string(protoImport.Path())) + pc.processFile(protoImport, mapping) + } + + // Process the schema file + pc.processFile(schemaFile, mapping) + + return pc, nil +} + +func (p *RPCCompiler) processFile(f protoref.FileDescriptor, mapping *GRPCMapping) { // Process all enums in the schema for i := 0; i < f.Enums().Len(); i++ { - pc.doc.Enums = append(pc.doc.Enums, pc.parseEnum(f.Enums().Get(i), mapping)) + p.doc.Enums = append(p.doc.Enums, p.parseEnum(f.Enums().Get(i), mapping)) } // Process all messages in the schema - pc.doc.Messages = pc.parseMessageDefinitions(f.Messages()) - for ref, message := range pc.doc.Messages { - pc.enrichMessageData(ref, message.Desc) + p.doc.Messages = append(p.doc.Messages, p.parseMessageDefinitions(f.Messages())...) + for ref, message := range p.doc.Messages { + p.enrichMessageData(ref, message.Desc) } // Process all services in the schema for i := 0; i < f.Services().Len(); i++ { - pc.doc.Services = append(pc.doc.Services, pc.parseService(f.Services().Get(i))) + p.doc.Services = append(p.doc.Services, p.parseService(f.Services().Get(i))) } - - return pc, nil } // ConstructExecutionPlan constructs an RPCExecutionPlan from a parsed GraphQL operation and schema. diff --git a/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go b/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go index 0d8e417ce5..0b6915fa05 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go +++ b/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go @@ -80,6 +80,9 @@ type RPCMessage struct { OneOfType OneOfType // MemberTypes provides the names of the types that are implemented by the Interface or Union MemberTypes []string + // Message represents the nested message type definition for complex fields. + // This enables recursive construction of nested protobuf message structures. + Message *RPCMessage } // IsOneOf checks if the message is a oneof field. @@ -160,9 +163,10 @@ type RPCField struct { EnumName string // StaticValue is the static value of the field StaticValue string - - // Message is the message type if the field is a nested message type - // This allows for recursive construction of complex message types + // Optional indicates if the field is optional + Optional bool + // Message represents the nested message type definition for complex fields. + // This enables recursive construction of nested protobuf message structures. Message *RPCMessage } diff --git a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go index bbe93fc88d..9a632323c7 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go +++ b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go @@ -14,6 +14,44 @@ import ( "github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor" ) +type testCase struct { + query string + expectedPlan *RPCExecutionPlan + expectedError string +} + +func runTest(t *testing.T, testCase testCase) { + // Parse the GraphQL schema + schemaDoc := grpctest.MustGraphQLSchema(t) + + // Parse the GraphQL query + queryDoc, report := astparser.ParseGraphqlDocumentString(testCase.query) + if report.HasErrors() { + t.Fatalf("failed to parse query: %s", report.Error()) + } + + walker := astvisitor.NewWalker(48) + + rpcPlanVisitor := newRPCPlanVisitor(&walker, rpcPlanVisitorConfig{ + subgraphName: "Products", + mapping: testMapping(), + }) + + walker.Walk(&queryDoc, &schemaDoc, &report) + + if report.HasErrors() { + require.NotEmpty(t, testCase.expectedError) + require.Contains(t, report.Error(), testCase.expectedError) + return + } + + require.Empty(t, testCase.expectedError) + diff := cmp.Diff(testCase.expectedPlan, rpcPlanVisitor.plan) + if diff != "" { + t.Fatalf("execution plan mismatch: %s", diff) + } +} + func TestEntityLookup(t *testing.T) { tests := []struct { name string @@ -2257,7 +2295,6 @@ func TestMutationUnionExecutionPlan(t *testing.T) { } } -// TODO: Define test cases for product execution plans func TestProductExecutionPlan(t *testing.T) { tests := []struct { name string @@ -3118,3 +3155,98 @@ func TestProductExecutionPlanWithAliases(t *testing.T) { } } + +func TestNullableFieldsExecutionPlan(t *testing.T) { + tests := []struct { + name string + query string + expectedPlan *RPCExecutionPlan + expectedError string + }{ + { + name: "Should create an execution plan for a query with nullable fields type", + query: "query NullableFieldsTypeQuery { nullableFieldsType { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }", + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "QueryNullableFieldsType", + Request: RPCMessage{ + Name: "QueryNullableFieldsTypeRequest", + }, + Response: RPCMessage{ + Name: "QueryNullableFieldsTypeResponse", + Fields: []RPCField{ + { + Name: "nullable_fields_type", + TypeName: string(DataTypeMessage), + JSONPath: "nullableFieldsType", + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "optional_float", + TypeName: string(DataTypeDouble), + JSONPath: "optionalFloat", + Optional: true, + }, + { + Name: "optional_boolean", + TypeName: string(DataTypeBool), + JSONPath: "optionalBoolean", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + { + Name: "required_int", + TypeName: string(DataTypeInt32), + JSONPath: "requiredInt", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + runTest(t, testCase{ + query: tt.query, + expectedPlan: tt.expectedPlan, + expectedError: tt.expectedError, + }) + }) + } +} diff --git a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go index 2caf7eb350..29ae3af9bb 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go +++ b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go @@ -334,6 +334,7 @@ func (r *rpcPlanVisitor) EnterField(ref int) { JSONPath: fieldName, Repeated: r.definition.TypeIsList(fdt), Alias: r.operation.FieldAliasString(ref), + Optional: r.isNullableScalar(fdt), } if typeName == DataTypeEnum { @@ -815,6 +816,10 @@ func (r *rpcPlanVisitor) parseGraphQLType(t *ast.Type) DataType { } } +func (r *rpcPlanVisitor) isNullableScalar(fdt int) bool { + return r.definition.TypeIsScalar(fdt, r.definition) && !r.definition.TypeIsNonNull(fdt) +} + // titleSlice capitalizes the first letter of each string in a slice. func titleSlice(s []string) []string { for i, v := range s { diff --git a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource.go b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource.go index 73531ce9cd..74bbb9c33c 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource.go +++ b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource.go @@ -218,6 +218,15 @@ func (d *DataSource) marshalResponseJSON(arena *astjson.Arena, message *RPCMessa continue } + if field.Optional { + err := d.resolveOptionalField(arena, root, field.JSONPath, msg) + if err != nil { + return nil, err + } + + continue + } + value, err := d.marshalResponseJSON(arena, field.Message, msg) if err != nil { return nil, err @@ -241,6 +250,16 @@ func (d *DataSource) marshalResponseJSON(arena *astjson.Arena, message *RPCMessa return root, nil } +func (d *DataSource) resolveOptionalField(arena *astjson.Arena, root *astjson.Value, name string, data protoref.Message) error { + fd := data.Descriptor().Fields().ByName(protoref.Name("value")) + if fd == nil { + return fmt.Errorf("unable to resolve optional field: field value not found in message %s", data.Descriptor().Name()) + } + + d.setJSONValue(arena, root, name, data, fd) + return nil +} + func (d *DataSource) setJSONValue(arena *astjson.Arena, root *astjson.Value, name string, data protoref.Message, fd protoref.FieldDescriptor) { if !data.IsValid() { root.Set(name, arena.NewNull()) diff --git a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go index 01130a4dde..0e3a6efce4 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go +++ b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go @@ -1537,3 +1537,84 @@ func Test_DataSource_Load_WithAliases(t *testing.T) { }) } } + +func Test_DataSource_Load_WithNullableFieldsType(t *testing.T) { + conn, cleanup := setupTestGRPCServer(t) + t.Cleanup(cleanup) + + testCases := []struct { + name string + query string + vars string + validate func(t *testing.T, data map[string]interface{}) + }{ + { + name: "Query nullable fields type", + query: `query { nullableFieldsType { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: "{}", + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType, ok := data["nullableFieldsType"].(map[string]interface{}) + require.True(t, ok, "NullableFieldsType should be an object") + require.NotEmpty(t, nullableFieldsType["id"], "ID should not be empty") + require.NotEmpty(t, nullableFieldsType["name"], "Name should not be empty") + require.NotEmpty(t, nullableFieldsType["optionalString"], "OptionalString should not be empty") + require.NotEmpty(t, nullableFieldsType["optionalInt"], "OptionalInt should not be empty") + require.NotEmpty(t, nullableFieldsType["optionalBoolean"], "OptionalBoolean should not be empty") + require.NotEmpty(t, nullableFieldsType["requiredString"], "RequiredString should not be empty") + require.NotEmpty(t, nullableFieldsType["requiredInt"], "RequiredInt should not be empty") + require.Empty(t, nullableFieldsType["optionalFloat"], "OptionalFloat should be empty") + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + + // Parse the GraphQL schema + schemaDoc := grpctest.MustGraphQLSchema(t) + + // Parse the GraphQL query + queryDoc, report := astparser.ParseGraphqlDocumentString(tc.query) + if report.HasErrors() { + t.Fatalf("failed to parse query: %s", report.Error()) + } + + compiler, err := NewProtoCompiler(grpctest.MustProtoSchema(t), testMapping()) + if err != nil { + t.Fatalf("failed to compile proto: %v", err) + } + + // Create the datasource + ds, err := NewDataSource(conn, DataSourceConfig{ + Operation: &queryDoc, + Definition: &schemaDoc, + SubgraphName: "Products", + Mapping: testMapping(), + Compiler: compiler, + }) + require.NoError(t, err) + + // Execute the query through our datasource + output := new(bytes.Buffer) + input := fmt.Sprintf(`{"query":%q,"body":%s}`, tc.query, tc.vars) + err = ds.Load(context.Background(), []byte(input), output) + require.NoError(t, err) + + // Parse the response + var resp struct { + Data map[string]interface{} `json:"data"` + Errors []struct { + Message string `json:"message"` + } `json:"errors,omitempty"` + } + + err = json.Unmarshal(output.Bytes(), &resp) + require.NoError(t, err, "Failed to unmarshal response") + require.Empty(t, resp.Errors, "Response should not contain errors") + require.NotEmpty(t, resp.Data, "Response should contain data") + + // Run the validation function + tc.validate(t, resp.Data) + }) + } +} diff --git a/v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go b/v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go index ae31951412..8149103b12 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go +++ b/v2/pkg/engine/datasource/grpc_datasource/mapping_test_helper.go @@ -84,6 +84,26 @@ func testMapping() *GRPCMapping { Request: "QuerySearchRequest", Response: "QuerySearchResponse", }, + "nullableFieldsType": { + RPC: "QueryNullableFieldsType", + Request: "QueryNullableFieldsTypeRequest", + Response: "QueryNullableFieldsTypeResponse", + }, + "nullableFieldsTypeById": { + RPC: "QueryNullableFieldsTypeById", + Request: "QueryNullableFieldsTypeByIdRequest", + Response: "QueryNullableFieldsTypeByIdResponse", + }, + "nullableFieldsTypeWithFilter": { + RPC: "QueryNullableFieldsTypeWithFilter", + Request: "QueryNullableFieldsTypeWithFilterRequest", + Response: "QueryNullableFieldsTypeWithFilterResponse", + }, + "allNullableFieldsTypes": { + RPC: "QueryAllNullableFieldsTypes", + Request: "QueryAllNullableFieldsTypesRequest", + Response: "QueryAllNullableFieldsTypesResponse", + }, }, MutationRPCs: RPCConfigMap{ "createUser": { @@ -96,6 +116,16 @@ func testMapping() *GRPCMapping { Request: "MutationPerformActionRequest", Response: "MutationPerformActionResponse", }, + "createNullableFieldsType": { + RPC: "MutationCreateNullableFieldsType", + Request: "MutationCreateNullableFieldsTypeRequest", + Response: "MutationCreateNullableFieldsTypeResponse", + }, + "updateNullableFieldsType": { + RPC: "MutationUpdateNullableFieldsType", + Request: "MutationUpdateNullableFieldsTypeRequest", + Response: "MutationUpdateNullableFieldsTypeResponse", + }, }, SubscriptionRPCs: RPCConfigMap{}, EntityRPCs: map[string]EntityRPCConfig{ @@ -199,6 +229,24 @@ func testMapping() *GRPCMapping { "randomSearchResult": { TargetName: "random_search_result", }, + "nullableFieldsType": { + TargetName: "nullable_fields_type", + }, + "nullableFieldsTypeById": { + TargetName: "nullable_fields_type_by_id", + ArgumentMappings: map[string]string{ + "id": "id", + }, + }, + "nullableFieldsTypeWithFilter": { + TargetName: "nullable_fields_type_with_filter", + ArgumentMappings: map[string]string{ + "filter": "filter", + }, + }, + "allNullableFieldsTypes": { + TargetName: "all_nullable_fields_types", + }, }, "Mutation": { "createUser": { @@ -213,6 +261,19 @@ func testMapping() *GRPCMapping { "input": "input", }, }, + "createNullableFieldsType": { + TargetName: "create_nullable_fields_type", + ArgumentMappings: map[string]string{ + "input": "input", + }, + }, + "updateNullableFieldsType": { + TargetName: "update_nullable_fields_type", + ArgumentMappings: map[string]string{ + "id": "id", + "input": "input", + }, + }, }, "UserInput": { "name": { @@ -473,23 +534,64 @@ func testMapping() *GRPCMapping { TargetName: "payload", }, }, - "SearchResult": { - "product": { - TargetName: "product", + "NullableFieldsType": { + "id": { + TargetName: "id", }, - "user": { - TargetName: "user", + "name": { + TargetName: "name", }, - "category": { - TargetName: "category", + "optionalString": { + TargetName: "optional_string", + }, + "optionalInt": { + TargetName: "optional_int", + }, + "optionalFloat": { + TargetName: "optional_float", + }, + "optionalBoolean": { + TargetName: "optional_boolean", + }, + "requiredString": { + TargetName: "required_string", + }, + "requiredInt": { + TargetName: "required_int", }, }, - "ActionResult": { - "actionSuccess": { - TargetName: "action_success", + "NullableFieldsInput": { + "name": { + TargetName: "name", + }, + "optionalString": { + TargetName: "optional_string", + }, + "optionalInt": { + TargetName: "optional_int", + }, + "optionalFloat": { + TargetName: "optional_float", + }, + "optionalBoolean": { + TargetName: "optional_boolean", + }, + "requiredString": { + TargetName: "required_string", + }, + "requiredInt": { + TargetName: "required_int", + }, + }, + "NullableFieldsFilter": { + "name": { + TargetName: "name", + }, + "optionalString": { + TargetName: "optional_string", }, - "actionError": { - TargetName: "action_error", + "includeNulls": { + TargetName: "include_nulls", }, }, }, diff --git a/v2/pkg/grpctest/mapping/mapping.go b/v2/pkg/grpctest/mapping/mapping.go index e0b254603c..fe22e10538 100644 --- a/v2/pkg/grpctest/mapping/mapping.go +++ b/v2/pkg/grpctest/mapping/mapping.go @@ -71,6 +71,11 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { Request: "QueryCategoriesByKindRequest", Response: "QueryCategoriesByKindResponse", }, + "categoriesByKinds": { + RPC: "QueryCategoriesByKinds", + Request: "QueryCategoriesByKindsRequest", + Response: "QueryCategoriesByKindsResponse", + }, "filterCategories": { RPC: "QueryFilterCategories", Request: "QueryFilterCategoriesRequest", @@ -86,6 +91,26 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { Request: "QuerySearchRequest", Response: "QuerySearchResponse", }, + "nullableFieldsType": { + RPC: "QueryNullableFieldsType", + Request: "QueryNullableFieldsTypeRequest", + Response: "QueryNullableFieldsTypeResponse", + }, + "nullableFieldsTypeById": { + RPC: "QueryNullableFieldsTypeById", + Request: "QueryNullableFieldsTypeByIdRequest", + Response: "QueryNullableFieldsTypeByIdResponse", + }, + "nullableFieldsTypeWithFilter": { + RPC: "QueryNullableFieldsTypeWithFilter", + Request: "QueryNullableFieldsTypeWithFilterRequest", + Response: "QueryNullableFieldsTypeWithFilterResponse", + }, + "allNullableFieldsTypes": { + RPC: "QueryAllNullableFieldsTypes", + Request: "QueryAllNullableFieldsTypesRequest", + Response: "QueryAllNullableFieldsTypesResponse", + }, }, MutationRPCs: grpcdatasource.RPCConfigMap{ "createUser": { @@ -98,6 +123,16 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { Request: "MutationPerformActionRequest", Response: "MutationPerformActionResponse", }, + "createNullableFieldsType": { + RPC: "MutationCreateNullableFieldsType", + Request: "MutationCreateNullableFieldsTypeRequest", + Response: "MutationCreateNullableFieldsTypeResponse", + }, + "updateNullableFieldsType": { + RPC: "MutationUpdateNullableFieldsType", + Request: "MutationUpdateNullableFieldsTypeRequest", + Response: "MutationUpdateNullableFieldsTypeResponse", + }, }, SubscriptionRPCs: grpcdatasource.RPCConfigMap{}, EntityRPCs: map[string]grpcdatasource.EntityRPCConfig{ @@ -155,6 +190,12 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { "kind": "kind", }, }, + "categoriesByKinds": { + TargetName: "categories_by_kinds", + ArgumentMappings: map[string]string{ + "kinds": "kinds", + }, + }, "filterCategories": { TargetName: "filter_categories", ArgumentMappings: map[string]string{ @@ -195,6 +236,24 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { "randomSearchResult": { TargetName: "random_search_result", }, + "nullableFieldsType": { + TargetName: "nullable_fields_type", + }, + "nullableFieldsTypeById": { + TargetName: "nullable_fields_type_by_id", + ArgumentMappings: map[string]string{ + "id": "id", + }, + }, + "nullableFieldsTypeWithFilter": { + TargetName: "nullable_fields_type_with_filter", + ArgumentMappings: map[string]string{ + "filter": "filter", + }, + }, + "allNullableFieldsTypes": { + TargetName: "all_nullable_fields_types", + }, }, "Mutation": { "createUser": { @@ -209,6 +268,19 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { "input": "input", }, }, + "createNullableFieldsType": { + TargetName: "create_nullable_fields_type", + ArgumentMappings: map[string]string{ + "input": "input", + }, + }, + "updateNullableFieldsType": { + TargetName: "update_nullable_fields_type", + ArgumentMappings: map[string]string{ + "id": "id", + "input": "input", + }, + }, }, "UserInput": { "name": { @@ -482,6 +554,66 @@ func DefaultGRPCMapping() *grpcdatasource.GRPCMapping { TargetName: "action_error", }, }, + "NullableFieldsType": { + "id": { + TargetName: "id", + }, + "name": { + TargetName: "name", + }, + "optionalString": { + TargetName: "optional_string", + }, + "optionalInt": { + TargetName: "optional_int", + }, + "optionalFloat": { + TargetName: "optional_float", + }, + "optionalBoolean": { + TargetName: "optional_boolean", + }, + "requiredString": { + TargetName: "required_string", + }, + "requiredInt": { + TargetName: "required_int", + }, + }, + "NullableFieldsInput": { + "name": { + TargetName: "name", + }, + "optionalString": { + TargetName: "optional_string", + }, + "optionalInt": { + TargetName: "optional_int", + }, + "optionalFloat": { + TargetName: "optional_float", + }, + "optionalBoolean": { + TargetName: "optional_boolean", + }, + "requiredString": { + TargetName: "required_string", + }, + "requiredInt": { + TargetName: "required_int", + }, + }, + "NullableFieldsFilter": { + "name": { + TargetName: "name", + }, + "optionalString": { + TargetName: "optional_string", + }, + "includeNulls": { + TargetName: "include_nulls", + }, + }, }, } } diff --git a/v2/pkg/grpctest/mockservice.go b/v2/pkg/grpctest/mockservice.go index 7664cae011..dd5fc40caa 100644 --- a/v2/pkg/grpctest/mockservice.go +++ b/v2/pkg/grpctest/mockservice.go @@ -9,6 +9,7 @@ import ( "github.com/wundergraph/graphql-go-tools/v2/pkg/grpctest/productv1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/wrapperspb" ) var _ productv1.ProductServiceServer = &MockService{} @@ -17,6 +18,287 @@ type MockService struct { productv1.UnimplementedProductServiceServer } +// MutationCreateNullableFieldsType implements productv1.ProductServiceServer. +func (s *MockService) MutationCreateNullableFieldsType(ctx context.Context, in *productv1.MutationCreateNullableFieldsTypeRequest) (*productv1.MutationCreateNullableFieldsTypeResponse, error) { + input := in.GetInput() + + // Create a new NullableFieldsType from the input + result := &productv1.NullableFieldsType{ + Id: fmt.Sprintf("nullable-%d", rand.Intn(1000)), + Name: input.GetName(), + RequiredString: input.GetRequiredString(), + RequiredInt: input.GetRequiredInt(), + } + + // Handle optional fields - copy from input if they exist + if input.OptionalString != nil { + result.OptionalString = &wrapperspb.StringValue{Value: input.OptionalString.GetValue()} + } + if input.OptionalInt != nil { + result.OptionalInt = &wrapperspb.Int32Value{Value: input.OptionalInt.GetValue()} + } + if input.OptionalFloat != nil { + result.OptionalFloat = &wrapperspb.FloatValue{Value: input.OptionalFloat.GetValue()} + } + if input.OptionalBoolean != nil { + result.OptionalBoolean = &wrapperspb.BoolValue{Value: input.OptionalBoolean.GetValue()} + } + + return &productv1.MutationCreateNullableFieldsTypeResponse{ + CreateNullableFieldsType: result, + }, nil +} + +// MutationUpdateNullableFieldsType implements productv1.ProductServiceServer. +func (s *MockService) MutationUpdateNullableFieldsType(ctx context.Context, in *productv1.MutationUpdateNullableFieldsTypeRequest) (*productv1.MutationUpdateNullableFieldsTypeResponse, error) { + id := in.GetId() + input := in.GetInput() + + // Return nil if trying to update a non-existent ID + if id == "non-existent" { + return &productv1.MutationUpdateNullableFieldsTypeResponse{ + UpdateNullableFieldsType: nil, + }, nil + } + + // Create updated NullableFieldsType + result := &productv1.NullableFieldsType{ + Id: id, + Name: input.GetName(), + RequiredString: input.GetRequiredString(), + RequiredInt: input.GetRequiredInt(), + } + + // Handle optional fields - copy from input if they exist + if input.OptionalString != nil { + result.OptionalString = &wrapperspb.StringValue{Value: input.OptionalString.GetValue()} + } + if input.OptionalInt != nil { + result.OptionalInt = &wrapperspb.Int32Value{Value: input.OptionalInt.GetValue()} + } + if input.OptionalFloat != nil { + result.OptionalFloat = &wrapperspb.FloatValue{Value: input.OptionalFloat.GetValue()} + } + if input.OptionalBoolean != nil { + result.OptionalBoolean = &wrapperspb.BoolValue{Value: input.OptionalBoolean.GetValue()} + } + + return &productv1.MutationUpdateNullableFieldsTypeResponse{ + UpdateNullableFieldsType: result, + }, nil +} + +// QueryAllNullableFieldsTypes implements productv1.ProductServiceServer. +func (s *MockService) QueryAllNullableFieldsTypes(ctx context.Context, in *productv1.QueryAllNullableFieldsTypesRequest) (*productv1.QueryAllNullableFieldsTypesResponse, error) { + var results []*productv1.NullableFieldsType + + // Create a variety of test data with different nullable field combinations + + // Entry 1: All fields populated + results = append(results, &productv1.NullableFieldsType{ + Id: "nullable-1", + Name: "Full Data Entry", + OptionalString: &wrapperspb.StringValue{Value: "Optional String Value"}, + OptionalInt: &wrapperspb.Int32Value{Value: 42}, + OptionalFloat: &wrapperspb.FloatValue{Value: 3.14}, + OptionalBoolean: &wrapperspb.BoolValue{Value: true}, + RequiredString: "Required String 1", + RequiredInt: 100, + }) + + // Entry 2: Some nullable fields are null + results = append(results, &productv1.NullableFieldsType{ + Id: "nullable-2", + Name: "Partial Data Entry", + OptionalString: &wrapperspb.StringValue{Value: "Only string is set"}, + OptionalInt: nil, // null + OptionalFloat: nil, // null + OptionalBoolean: &wrapperspb.BoolValue{Value: false}, + RequiredString: "Required String 2", + RequiredInt: 200, + }) + + // Entry 3: All nullable fields are null + results = append(results, &productv1.NullableFieldsType{ + Id: "nullable-3", + Name: "Minimal Data Entry", + OptionalString: nil, // null + OptionalInt: nil, // null + OptionalFloat: nil, // null + OptionalBoolean: nil, // null + RequiredString: "Required String 3", + RequiredInt: 300, + }) + + return &productv1.QueryAllNullableFieldsTypesResponse{ + AllNullableFieldsTypes: results, + }, nil +} + +// QueryNullableFieldsType implements productv1.ProductServiceServer. +func (s *MockService) QueryNullableFieldsType(ctx context.Context, in *productv1.QueryNullableFieldsTypeRequest) (*productv1.QueryNullableFieldsTypeResponse, error) { + // Return a single NullableFieldsType with mixed null/non-null values + result := &productv1.NullableFieldsType{ + Id: "nullable-default", + Name: "Default Nullable Fields Type", + OptionalString: &wrapperspb.StringValue{Value: "Default optional string"}, + OptionalInt: &wrapperspb.Int32Value{Value: 777}, + OptionalFloat: nil, // null + OptionalBoolean: &wrapperspb.BoolValue{Value: true}, + RequiredString: "Default required string", + RequiredInt: 999, + } + + return &productv1.QueryNullableFieldsTypeResponse{ + NullableFieldsType: result, + }, nil +} + +// QueryNullableFieldsTypeById implements productv1.ProductServiceServer. +func (s *MockService) QueryNullableFieldsTypeById(ctx context.Context, in *productv1.QueryNullableFieldsTypeByIdRequest) (*productv1.QueryNullableFieldsTypeByIdResponse, error) { + id := in.GetId() + + // Return null for specific test IDs + if id == "not-found" || id == "null-test" { + return &productv1.QueryNullableFieldsTypeByIdResponse{ + NullableFieldsTypeById: nil, + }, nil + } + + // Create different test data based on ID + var result *productv1.NullableFieldsType + + switch id { + case "full-data": + result = &productv1.NullableFieldsType{ + Id: id, + Name: "Full Data by ID", + OptionalString: &wrapperspb.StringValue{Value: "All fields populated"}, + OptionalInt: &wrapperspb.Int32Value{Value: 123}, + OptionalFloat: &wrapperspb.FloatValue{Value: 12.34}, + OptionalBoolean: &wrapperspb.BoolValue{Value: false}, + RequiredString: "Required by ID", + RequiredInt: 456, + } + case "partial-data": + result = &productv1.NullableFieldsType{ + Id: id, + Name: "Partial Data by ID", + OptionalString: nil, // null + OptionalInt: &wrapperspb.Int32Value{Value: 789}, + OptionalFloat: nil, // null + OptionalBoolean: &wrapperspb.BoolValue{Value: true}, + RequiredString: "Partial required by ID", + RequiredInt: 321, + } + case "minimal-data": + result = &productv1.NullableFieldsType{ + Id: id, + Name: "Minimal Data by ID", + OptionalString: nil, // null + OptionalInt: nil, // null + OptionalFloat: nil, // null + OptionalBoolean: nil, // null + RequiredString: "Only required fields", + RequiredInt: 111, + } + default: + // Generic response for any other ID + result = &productv1.NullableFieldsType{ + Id: id, + Name: fmt.Sprintf("Nullable Type %s", id), + OptionalString: &wrapperspb.StringValue{Value: fmt.Sprintf("Optional for %s", id)}, + OptionalInt: &wrapperspb.Int32Value{Value: int32(len(id) * 10)}, + OptionalFloat: &wrapperspb.FloatValue{Value: float32(len(id)) * 1.5}, + OptionalBoolean: &wrapperspb.BoolValue{Value: len(id)%2 == 0}, + RequiredString: fmt.Sprintf("Required for %s", id), + RequiredInt: int32(len(id) * 100), + } + } + + return &productv1.QueryNullableFieldsTypeByIdResponse{ + NullableFieldsTypeById: result, + }, nil +} + +// QueryNullableFieldsTypeWithFilter implements productv1.ProductServiceServer. +func (s *MockService) QueryNullableFieldsTypeWithFilter(ctx context.Context, in *productv1.QueryNullableFieldsTypeWithFilterRequest) (*productv1.QueryNullableFieldsTypeWithFilterResponse, error) { + filter := in.GetFilter() + var results []*productv1.NullableFieldsType + + // If no filter provided, return empty results + if filter == nil { + return &productv1.QueryNullableFieldsTypeWithFilterResponse{ + NullableFieldsTypeWithFilter: results, + }, nil + } + + // Create test data based on filter criteria + nameFilter := "" + if filter.Name != nil { + nameFilter = filter.Name.GetValue() + } + + optionalStringFilter := "" + if filter.OptionalString != nil { + optionalStringFilter = filter.OptionalString.GetValue() + } + + includeNulls := false + if filter.IncludeNulls != nil { + includeNulls = filter.IncludeNulls.GetValue() + } + + // Generate filtered results + for i := 1; i <= 3; i++ { + var optionalString *wrapperspb.StringValue + var optionalInt *wrapperspb.Int32Value + var optionalFloat *wrapperspb.FloatValue + var optionalBoolean *wrapperspb.BoolValue + + // Vary the nullable fields based on includeNulls and index + if includeNulls || i%2 == 1 { + if optionalStringFilter != "" { + optionalString = &wrapperspb.StringValue{Value: optionalStringFilter} + } else { + optionalString = &wrapperspb.StringValue{Value: fmt.Sprintf("Filtered string %d", i)} + } + } + + if includeNulls || i%3 != 0 { + optionalInt = &wrapperspb.Int32Value{Value: int32(i * 100)} + } + + if includeNulls || i%2 == 0 { + optionalFloat = &wrapperspb.FloatValue{Value: float32(i) * 10.5} + } + + if includeNulls || i%4 != 0 { + optionalBoolean = &wrapperspb.BoolValue{Value: i%2 == 0} + } + + name := fmt.Sprintf("Filtered Item %d", i) + if nameFilter != "" { + name = fmt.Sprintf("%s - %d", nameFilter, i) + } + + results = append(results, &productv1.NullableFieldsType{ + Id: fmt.Sprintf("filtered-%d", i), + Name: name, + OptionalString: optionalString, + OptionalInt: optionalInt, + OptionalFloat: optionalFloat, + OptionalBoolean: optionalBoolean, + RequiredString: fmt.Sprintf("Required filtered %d", i), + RequiredInt: int32(i * 1000), + }) + } + + return &productv1.QueryNullableFieldsTypeWithFilterResponse{ + NullableFieldsTypeWithFilter: results, + }, nil +} + // MutationPerformAction implements productv1.ProductServiceServer. func (s *MockService) MutationPerformAction(ctx context.Context, in *productv1.MutationPerformActionRequest) (*productv1.MutationPerformActionResponse, error) { input := in.GetInput() diff --git a/v2/pkg/grpctest/product.proto b/v2/pkg/grpctest/product.proto index 39ce8fbf55..65876625ce 100644 --- a/v2/pkg/grpctest/product.proto +++ b/v2/pkg/grpctest/product.proto @@ -1,6 +1,8 @@ syntax = "proto3"; package productv1; +import "google/protobuf/wrappers.proto"; + option go_package = "cosmo/pkg/proto/productv1;productv1"; // Service definition for ProductService @@ -27,6 +29,13 @@ service ProductService { rpc QueryTypeWithMultipleFilterFields(QueryTypeWithMultipleFilterFieldsRequest) returns (QueryTypeWithMultipleFilterFieldsResponse) {} rpc QueryUser(QueryUserRequest) returns (QueryUserResponse) {} rpc QueryUsers(QueryUsersRequest) returns (QueryUsersResponse) {} + // Nullable fields RPCs + rpc QueryNullableFieldsType(QueryNullableFieldsTypeRequest) returns (QueryNullableFieldsTypeResponse) {} + rpc QueryNullableFieldsTypeById(QueryNullableFieldsTypeByIdRequest) returns (QueryNullableFieldsTypeByIdResponse) {} + rpc QueryNullableFieldsTypeWithFilter(QueryNullableFieldsTypeWithFilterRequest) returns (QueryNullableFieldsTypeWithFilterResponse) {} + rpc QueryAllNullableFieldsTypes(QueryAllNullableFieldsTypesRequest) returns (QueryAllNullableFieldsTypesResponse) {} + rpc MutationCreateNullableFieldsType(MutationCreateNullableFieldsTypeRequest) returns (MutationCreateNullableFieldsTypeResponse) {} + rpc MutationUpdateNullableFieldsType(MutationUpdateNullableFieldsTypeRequest) returns (MutationUpdateNullableFieldsTypeResponse) {} } // Key message for Product entity lookup @@ -236,6 +245,54 @@ message MutationPerformActionResponse { ActionResult perform_action = 1; } +// Request message for nullableFieldsType operation. +message QueryNullableFieldsTypeRequest { +} +// Response message for nullableFieldsType operation. +message QueryNullableFieldsTypeResponse { + NullableFieldsType nullable_fields_type = 1; +} +// Request message for nullableFieldsTypeById operation. +message QueryNullableFieldsTypeByIdRequest { + string id = 1; +} +// Response message for nullableFieldsTypeById operation. +message QueryNullableFieldsTypeByIdResponse { + NullableFieldsType nullable_fields_type_by_id = 1; +} +// Request message for nullableFieldsTypeWithFilter operation. +message QueryNullableFieldsTypeWithFilterRequest { + NullableFieldsFilter filter = 1; +} +// Response message for nullableFieldsTypeWithFilter operation. +message QueryNullableFieldsTypeWithFilterResponse { + repeated NullableFieldsType nullable_fields_type_with_filter = 1; +} +// Request message for allNullableFieldsTypes operation. +message QueryAllNullableFieldsTypesRequest { +} +// Response message for allNullableFieldsTypes operation. +message QueryAllNullableFieldsTypesResponse { + repeated NullableFieldsType all_nullable_fields_types = 1; +} +// Request message for createNullableFieldsType operation. +message MutationCreateNullableFieldsTypeRequest { + NullableFieldsInput input = 1; +} +// Response message for createNullableFieldsType operation. +message MutationCreateNullableFieldsTypeResponse { + NullableFieldsType create_nullable_fields_type = 1; +} +// Request message for updateNullableFieldsType operation. +message MutationUpdateNullableFieldsTypeRequest { + string id = 1; + NullableFieldsInput input = 2; +} +// Response message for updateNullableFieldsType operation. +message MutationUpdateNullableFieldsTypeResponse { + NullableFieldsType update_nullable_fields_type = 1; +} + message Product { string id = 1; string name = 2; @@ -411,4 +468,32 @@ message ActionSuccess { message ActionError { string message = 1; string code = 2; +} + +// New messages for testing nullable fields +message NullableFieldsType { + string id = 1; + string name = 2; + google.protobuf.StringValue optional_string = 3; + google.protobuf.Int32Value optional_int = 4; + google.protobuf.FloatValue optional_float = 5; + google.protobuf.BoolValue optional_boolean = 6; + string required_string = 7; + int32 required_int = 8; +} + +message NullableFieldsInput { + string name = 1; + google.protobuf.StringValue optional_string = 2; + google.protobuf.Int32Value optional_int = 3; + google.protobuf.FloatValue optional_float = 4; + google.protobuf.BoolValue optional_boolean = 5; + string required_string = 6; + int32 required_int = 7; +} + +message NullableFieldsFilter { + google.protobuf.StringValue name = 1; + google.protobuf.StringValue optional_string = 2; + google.protobuf.BoolValue include_nulls = 3; } \ No newline at end of file diff --git a/v2/pkg/grpctest/productv1/product.pb.go b/v2/pkg/grpctest/productv1/product.pb.go index 7473b17855..dcfee5f0ca 100644 --- a/v2/pkg/grpctest/productv1/product.pb.go +++ b/v2/pkg/grpctest/productv1/product.pb.go @@ -9,6 +9,7 @@ package productv1 import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -1950,6 +1951,538 @@ func (x *MutationPerformActionResponse) GetPerformAction() *ActionResult { return nil } +// Request message for nullableFieldsType operation. +type QueryNullableFieldsTypeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryNullableFieldsTypeRequest) Reset() { + *x = QueryNullableFieldsTypeRequest{} + mi := &file_product_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryNullableFieldsTypeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryNullableFieldsTypeRequest) ProtoMessage() {} + +func (x *QueryNullableFieldsTypeRequest) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[42] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryNullableFieldsTypeRequest.ProtoReflect.Descriptor instead. +func (*QueryNullableFieldsTypeRequest) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{42} +} + +// Response message for nullableFieldsType operation. +type QueryNullableFieldsTypeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + NullableFieldsType *NullableFieldsType `protobuf:"bytes,1,opt,name=nullable_fields_type,json=nullableFieldsType,proto3" json:"nullable_fields_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryNullableFieldsTypeResponse) Reset() { + *x = QueryNullableFieldsTypeResponse{} + mi := &file_product_proto_msgTypes[43] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryNullableFieldsTypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryNullableFieldsTypeResponse) ProtoMessage() {} + +func (x *QueryNullableFieldsTypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[43] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryNullableFieldsTypeResponse.ProtoReflect.Descriptor instead. +func (*QueryNullableFieldsTypeResponse) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{43} +} + +func (x *QueryNullableFieldsTypeResponse) GetNullableFieldsType() *NullableFieldsType { + if x != nil { + return x.NullableFieldsType + } + return nil +} + +// Request message for nullableFieldsTypeById operation. +type QueryNullableFieldsTypeByIdRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryNullableFieldsTypeByIdRequest) Reset() { + *x = QueryNullableFieldsTypeByIdRequest{} + mi := &file_product_proto_msgTypes[44] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryNullableFieldsTypeByIdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryNullableFieldsTypeByIdRequest) ProtoMessage() {} + +func (x *QueryNullableFieldsTypeByIdRequest) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[44] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryNullableFieldsTypeByIdRequest.ProtoReflect.Descriptor instead. +func (*QueryNullableFieldsTypeByIdRequest) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{44} +} + +func (x *QueryNullableFieldsTypeByIdRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +// Response message for nullableFieldsTypeById operation. +type QueryNullableFieldsTypeByIdResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + NullableFieldsTypeById *NullableFieldsType `protobuf:"bytes,1,opt,name=nullable_fields_type_by_id,json=nullableFieldsTypeById,proto3" json:"nullable_fields_type_by_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryNullableFieldsTypeByIdResponse) Reset() { + *x = QueryNullableFieldsTypeByIdResponse{} + mi := &file_product_proto_msgTypes[45] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryNullableFieldsTypeByIdResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryNullableFieldsTypeByIdResponse) ProtoMessage() {} + +func (x *QueryNullableFieldsTypeByIdResponse) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[45] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryNullableFieldsTypeByIdResponse.ProtoReflect.Descriptor instead. +func (*QueryNullableFieldsTypeByIdResponse) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{45} +} + +func (x *QueryNullableFieldsTypeByIdResponse) GetNullableFieldsTypeById() *NullableFieldsType { + if x != nil { + return x.NullableFieldsTypeById + } + return nil +} + +// Request message for nullableFieldsTypeWithFilter operation. +type QueryNullableFieldsTypeWithFilterRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Filter *NullableFieldsFilter `protobuf:"bytes,1,opt,name=filter,proto3" json:"filter,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryNullableFieldsTypeWithFilterRequest) Reset() { + *x = QueryNullableFieldsTypeWithFilterRequest{} + mi := &file_product_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryNullableFieldsTypeWithFilterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryNullableFieldsTypeWithFilterRequest) ProtoMessage() {} + +func (x *QueryNullableFieldsTypeWithFilterRequest) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[46] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryNullableFieldsTypeWithFilterRequest.ProtoReflect.Descriptor instead. +func (*QueryNullableFieldsTypeWithFilterRequest) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{46} +} + +func (x *QueryNullableFieldsTypeWithFilterRequest) GetFilter() *NullableFieldsFilter { + if x != nil { + return x.Filter + } + return nil +} + +// Response message for nullableFieldsTypeWithFilter operation. +type QueryNullableFieldsTypeWithFilterResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + NullableFieldsTypeWithFilter []*NullableFieldsType `protobuf:"bytes,1,rep,name=nullable_fields_type_with_filter,json=nullableFieldsTypeWithFilter,proto3" json:"nullable_fields_type_with_filter,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryNullableFieldsTypeWithFilterResponse) Reset() { + *x = QueryNullableFieldsTypeWithFilterResponse{} + mi := &file_product_proto_msgTypes[47] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryNullableFieldsTypeWithFilterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryNullableFieldsTypeWithFilterResponse) ProtoMessage() {} + +func (x *QueryNullableFieldsTypeWithFilterResponse) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[47] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryNullableFieldsTypeWithFilterResponse.ProtoReflect.Descriptor instead. +func (*QueryNullableFieldsTypeWithFilterResponse) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{47} +} + +func (x *QueryNullableFieldsTypeWithFilterResponse) GetNullableFieldsTypeWithFilter() []*NullableFieldsType { + if x != nil { + return x.NullableFieldsTypeWithFilter + } + return nil +} + +// Request message for allNullableFieldsTypes operation. +type QueryAllNullableFieldsTypesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryAllNullableFieldsTypesRequest) Reset() { + *x = QueryAllNullableFieldsTypesRequest{} + mi := &file_product_proto_msgTypes[48] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryAllNullableFieldsTypesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryAllNullableFieldsTypesRequest) ProtoMessage() {} + +func (x *QueryAllNullableFieldsTypesRequest) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[48] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryAllNullableFieldsTypesRequest.ProtoReflect.Descriptor instead. +func (*QueryAllNullableFieldsTypesRequest) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{48} +} + +// Response message for allNullableFieldsTypes operation. +type QueryAllNullableFieldsTypesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + AllNullableFieldsTypes []*NullableFieldsType `protobuf:"bytes,1,rep,name=all_nullable_fields_types,json=allNullableFieldsTypes,proto3" json:"all_nullable_fields_types,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *QueryAllNullableFieldsTypesResponse) Reset() { + *x = QueryAllNullableFieldsTypesResponse{} + mi := &file_product_proto_msgTypes[49] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *QueryAllNullableFieldsTypesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*QueryAllNullableFieldsTypesResponse) ProtoMessage() {} + +func (x *QueryAllNullableFieldsTypesResponse) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[49] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use QueryAllNullableFieldsTypesResponse.ProtoReflect.Descriptor instead. +func (*QueryAllNullableFieldsTypesResponse) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{49} +} + +func (x *QueryAllNullableFieldsTypesResponse) GetAllNullableFieldsTypes() []*NullableFieldsType { + if x != nil { + return x.AllNullableFieldsTypes + } + return nil +} + +// Request message for createNullableFieldsType operation. +type MutationCreateNullableFieldsTypeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Input *NullableFieldsInput `protobuf:"bytes,1,opt,name=input,proto3" json:"input,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MutationCreateNullableFieldsTypeRequest) Reset() { + *x = MutationCreateNullableFieldsTypeRequest{} + mi := &file_product_proto_msgTypes[50] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MutationCreateNullableFieldsTypeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MutationCreateNullableFieldsTypeRequest) ProtoMessage() {} + +func (x *MutationCreateNullableFieldsTypeRequest) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[50] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MutationCreateNullableFieldsTypeRequest.ProtoReflect.Descriptor instead. +func (*MutationCreateNullableFieldsTypeRequest) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{50} +} + +func (x *MutationCreateNullableFieldsTypeRequest) GetInput() *NullableFieldsInput { + if x != nil { + return x.Input + } + return nil +} + +// Response message for createNullableFieldsType operation. +type MutationCreateNullableFieldsTypeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + CreateNullableFieldsType *NullableFieldsType `protobuf:"bytes,1,opt,name=create_nullable_fields_type,json=createNullableFieldsType,proto3" json:"create_nullable_fields_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MutationCreateNullableFieldsTypeResponse) Reset() { + *x = MutationCreateNullableFieldsTypeResponse{} + mi := &file_product_proto_msgTypes[51] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MutationCreateNullableFieldsTypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MutationCreateNullableFieldsTypeResponse) ProtoMessage() {} + +func (x *MutationCreateNullableFieldsTypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[51] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MutationCreateNullableFieldsTypeResponse.ProtoReflect.Descriptor instead. +func (*MutationCreateNullableFieldsTypeResponse) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{51} +} + +func (x *MutationCreateNullableFieldsTypeResponse) GetCreateNullableFieldsType() *NullableFieldsType { + if x != nil { + return x.CreateNullableFieldsType + } + return nil +} + +// Request message for updateNullableFieldsType operation. +type MutationUpdateNullableFieldsTypeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Input *NullableFieldsInput `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MutationUpdateNullableFieldsTypeRequest) Reset() { + *x = MutationUpdateNullableFieldsTypeRequest{} + mi := &file_product_proto_msgTypes[52] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MutationUpdateNullableFieldsTypeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MutationUpdateNullableFieldsTypeRequest) ProtoMessage() {} + +func (x *MutationUpdateNullableFieldsTypeRequest) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[52] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MutationUpdateNullableFieldsTypeRequest.ProtoReflect.Descriptor instead. +func (*MutationUpdateNullableFieldsTypeRequest) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{52} +} + +func (x *MutationUpdateNullableFieldsTypeRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *MutationUpdateNullableFieldsTypeRequest) GetInput() *NullableFieldsInput { + if x != nil { + return x.Input + } + return nil +} + +// Response message for updateNullableFieldsType operation. +type MutationUpdateNullableFieldsTypeResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + UpdateNullableFieldsType *NullableFieldsType `protobuf:"bytes,1,opt,name=update_nullable_fields_type,json=updateNullableFieldsType,proto3" json:"update_nullable_fields_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MutationUpdateNullableFieldsTypeResponse) Reset() { + *x = MutationUpdateNullableFieldsTypeResponse{} + mi := &file_product_proto_msgTypes[53] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MutationUpdateNullableFieldsTypeResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MutationUpdateNullableFieldsTypeResponse) ProtoMessage() {} + +func (x *MutationUpdateNullableFieldsTypeResponse) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[53] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MutationUpdateNullableFieldsTypeResponse.ProtoReflect.Descriptor instead. +func (*MutationUpdateNullableFieldsTypeResponse) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{53} +} + +func (x *MutationUpdateNullableFieldsTypeResponse) GetUpdateNullableFieldsType() *NullableFieldsType { + if x != nil { + return x.UpdateNullableFieldsType + } + return nil +} + type Product struct { state protoimpl.MessageState `protogen:"open.v1"` Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` @@ -1961,7 +2494,7 @@ type Product struct { func (x *Product) Reset() { *x = Product{} - mi := &file_product_proto_msgTypes[42] + mi := &file_product_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1973,7 +2506,7 @@ func (x *Product) String() string { func (*Product) ProtoMessage() {} func (x *Product) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[42] + mi := &file_product_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1986,7 +2519,7 @@ func (x *Product) ProtoReflect() protoreflect.Message { // Deprecated: Use Product.ProtoReflect.Descriptor instead. func (*Product) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{42} + return file_product_proto_rawDescGZIP(), []int{54} } func (x *Product) GetId() string { @@ -2021,7 +2554,7 @@ type Storage struct { func (x *Storage) Reset() { *x = Storage{} - mi := &file_product_proto_msgTypes[43] + mi := &file_product_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2033,7 +2566,7 @@ func (x *Storage) String() string { func (*Storage) ProtoMessage() {} func (x *Storage) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[43] + mi := &file_product_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2046,7 +2579,7 @@ func (x *Storage) ProtoReflect() protoreflect.Message { // Deprecated: Use Storage.ProtoReflect.Descriptor instead. func (*Storage) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{43} + return file_product_proto_rawDescGZIP(), []int{55} } func (x *Storage) GetId() string { @@ -2080,7 +2613,7 @@ type User struct { func (x *User) Reset() { *x = User{} - mi := &file_product_proto_msgTypes[44] + mi := &file_product_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2092,7 +2625,7 @@ func (x *User) String() string { func (*User) ProtoMessage() {} func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[44] + mi := &file_product_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2105,7 +2638,7 @@ func (x *User) ProtoReflect() protoreflect.Message { // Deprecated: Use User.ProtoReflect.Descriptor instead. func (*User) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{44} + return file_product_proto_rawDescGZIP(), []int{56} } func (x *User) GetId() string { @@ -2133,7 +2666,7 @@ type NestedTypeA struct { func (x *NestedTypeA) Reset() { *x = NestedTypeA{} - mi := &file_product_proto_msgTypes[45] + mi := &file_product_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2145,7 +2678,7 @@ func (x *NestedTypeA) String() string { func (*NestedTypeA) ProtoMessage() {} func (x *NestedTypeA) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[45] + mi := &file_product_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2158,7 +2691,7 @@ func (x *NestedTypeA) ProtoReflect() protoreflect.Message { // Deprecated: Use NestedTypeA.ProtoReflect.Descriptor instead. func (*NestedTypeA) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{45} + return file_product_proto_rawDescGZIP(), []int{57} } func (x *NestedTypeA) GetId() string { @@ -2193,7 +2726,7 @@ type RecursiveType struct { func (x *RecursiveType) Reset() { *x = RecursiveType{} - mi := &file_product_proto_msgTypes[46] + mi := &file_product_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2205,7 +2738,7 @@ func (x *RecursiveType) String() string { func (*RecursiveType) ProtoMessage() {} func (x *RecursiveType) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[46] + mi := &file_product_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2218,7 +2751,7 @@ func (x *RecursiveType) ProtoReflect() protoreflect.Message { // Deprecated: Use RecursiveType.ProtoReflect.Descriptor instead. func (*RecursiveType) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{46} + return file_product_proto_rawDescGZIP(), []int{58} } func (x *RecursiveType) GetId() string { @@ -2254,7 +2787,7 @@ type TypeWithMultipleFilterFields struct { func (x *TypeWithMultipleFilterFields) Reset() { *x = TypeWithMultipleFilterFields{} - mi := &file_product_proto_msgTypes[47] + mi := &file_product_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2266,7 +2799,7 @@ func (x *TypeWithMultipleFilterFields) String() string { func (*TypeWithMultipleFilterFields) ProtoMessage() {} func (x *TypeWithMultipleFilterFields) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[47] + mi := &file_product_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2279,7 +2812,7 @@ func (x *TypeWithMultipleFilterFields) ProtoReflect() protoreflect.Message { // Deprecated: Use TypeWithMultipleFilterFields.ProtoReflect.Descriptor instead. func (*TypeWithMultipleFilterFields) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{47} + return file_product_proto_rawDescGZIP(), []int{59} } func (x *TypeWithMultipleFilterFields) GetId() string { @@ -2320,7 +2853,7 @@ type FilterTypeInput struct { func (x *FilterTypeInput) Reset() { *x = FilterTypeInput{} - mi := &file_product_proto_msgTypes[48] + mi := &file_product_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2332,7 +2865,7 @@ func (x *FilterTypeInput) String() string { func (*FilterTypeInput) ProtoMessage() {} func (x *FilterTypeInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[48] + mi := &file_product_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2345,7 +2878,7 @@ func (x *FilterTypeInput) ProtoReflect() protoreflect.Message { // Deprecated: Use FilterTypeInput.ProtoReflect.Descriptor instead. func (*FilterTypeInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{48} + return file_product_proto_rawDescGZIP(), []int{60} } func (x *FilterTypeInput) GetFilterField_1() string { @@ -2371,7 +2904,7 @@ type ComplexFilterTypeInput struct { func (x *ComplexFilterTypeInput) Reset() { *x = ComplexFilterTypeInput{} - mi := &file_product_proto_msgTypes[49] + mi := &file_product_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2383,7 +2916,7 @@ func (x *ComplexFilterTypeInput) String() string { func (*ComplexFilterTypeInput) ProtoMessage() {} func (x *ComplexFilterTypeInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[49] + mi := &file_product_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2396,7 +2929,7 @@ func (x *ComplexFilterTypeInput) ProtoReflect() protoreflect.Message { // Deprecated: Use ComplexFilterTypeInput.ProtoReflect.Descriptor instead. func (*ComplexFilterTypeInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{49} + return file_product_proto_rawDescGZIP(), []int{61} } func (x *ComplexFilterTypeInput) GetFilter() *FilterType { @@ -2416,7 +2949,7 @@ type TypeWithComplexFilterInput struct { func (x *TypeWithComplexFilterInput) Reset() { *x = TypeWithComplexFilterInput{} - mi := &file_product_proto_msgTypes[50] + mi := &file_product_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2428,7 +2961,7 @@ func (x *TypeWithComplexFilterInput) String() string { func (*TypeWithComplexFilterInput) ProtoMessage() {} func (x *TypeWithComplexFilterInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[50] + mi := &file_product_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2441,7 +2974,7 @@ func (x *TypeWithComplexFilterInput) ProtoReflect() protoreflect.Message { // Deprecated: Use TypeWithComplexFilterInput.ProtoReflect.Descriptor instead. func (*TypeWithComplexFilterInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{50} + return file_product_proto_rawDescGZIP(), []int{62} } func (x *TypeWithComplexFilterInput) GetId() string { @@ -2469,7 +3002,7 @@ type OrderInput struct { func (x *OrderInput) Reset() { *x = OrderInput{} - mi := &file_product_proto_msgTypes[51] + mi := &file_product_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2481,7 +3014,7 @@ func (x *OrderInput) String() string { func (*OrderInput) ProtoMessage() {} func (x *OrderInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[51] + mi := &file_product_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2494,7 +3027,7 @@ func (x *OrderInput) ProtoReflect() protoreflect.Message { // Deprecated: Use OrderInput.ProtoReflect.Descriptor instead. func (*OrderInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{51} + return file_product_proto_rawDescGZIP(), []int{63} } func (x *OrderInput) GetOrderId() string { @@ -2530,7 +3063,7 @@ type Order struct { func (x *Order) Reset() { *x = Order{} - mi := &file_product_proto_msgTypes[52] + mi := &file_product_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2542,7 +3075,7 @@ func (x *Order) String() string { func (*Order) ProtoMessage() {} func (x *Order) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[52] + mi := &file_product_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2555,7 +3088,7 @@ func (x *Order) ProtoReflect() protoreflect.Message { // Deprecated: Use Order.ProtoReflect.Descriptor instead. func (*Order) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{52} + return file_product_proto_rawDescGZIP(), []int{64} } func (x *Order) GetOrderId() string { @@ -2597,7 +3130,7 @@ type Category struct { func (x *Category) Reset() { *x = Category{} - mi := &file_product_proto_msgTypes[53] + mi := &file_product_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2609,7 +3142,7 @@ func (x *Category) String() string { func (*Category) ProtoMessage() {} func (x *Category) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[53] + mi := &file_product_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2622,7 +3155,7 @@ func (x *Category) ProtoReflect() protoreflect.Message { // Deprecated: Use Category.ProtoReflect.Descriptor instead. func (*Category) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{53} + return file_product_proto_rawDescGZIP(), []int{65} } func (x *Category) GetId() string { @@ -2656,7 +3189,7 @@ type CategoryFilter struct { func (x *CategoryFilter) Reset() { *x = CategoryFilter{} - mi := &file_product_proto_msgTypes[54] + mi := &file_product_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2668,7 +3201,7 @@ func (x *CategoryFilter) String() string { func (*CategoryFilter) ProtoMessage() {} func (x *CategoryFilter) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[54] + mi := &file_product_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2681,7 +3214,7 @@ func (x *CategoryFilter) ProtoReflect() protoreflect.Message { // Deprecated: Use CategoryFilter.ProtoReflect.Descriptor instead. func (*CategoryFilter) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{54} + return file_product_proto_rawDescGZIP(), []int{66} } func (x *CategoryFilter) GetCategory() CategoryKind { @@ -2711,7 +3244,7 @@ type Animal struct { func (x *Animal) Reset() { *x = Animal{} - mi := &file_product_proto_msgTypes[55] + mi := &file_product_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2723,7 +3256,7 @@ func (x *Animal) String() string { func (*Animal) ProtoMessage() {} func (x *Animal) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[55] + mi := &file_product_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2736,7 +3269,7 @@ func (x *Animal) ProtoReflect() protoreflect.Message { // Deprecated: Use Animal.ProtoReflect.Descriptor instead. func (*Animal) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{55} + return file_product_proto_rawDescGZIP(), []int{67} } func (x *Animal) GetInstance() isAnimal_Instance { @@ -2790,7 +3323,7 @@ type SearchInput struct { func (x *SearchInput) Reset() { *x = SearchInput{} - mi := &file_product_proto_msgTypes[56] + mi := &file_product_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2802,7 +3335,7 @@ func (x *SearchInput) String() string { func (*SearchInput) ProtoMessage() {} func (x *SearchInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[56] + mi := &file_product_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2815,7 +3348,7 @@ func (x *SearchInput) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchInput.ProtoReflect.Descriptor instead. func (*SearchInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{56} + return file_product_proto_rawDescGZIP(), []int{68} } func (x *SearchInput) GetQuery() string { @@ -2846,7 +3379,7 @@ type SearchResult struct { func (x *SearchResult) Reset() { *x = SearchResult{} - mi := &file_product_proto_msgTypes[57] + mi := &file_product_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2858,7 +3391,7 @@ func (x *SearchResult) String() string { func (*SearchResult) ProtoMessage() {} func (x *SearchResult) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[57] + mi := &file_product_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2871,7 +3404,7 @@ func (x *SearchResult) ProtoReflect() protoreflect.Message { // Deprecated: Use SearchResult.ProtoReflect.Descriptor instead. func (*SearchResult) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{57} + return file_product_proto_rawDescGZIP(), []int{69} } func (x *SearchResult) GetValue() isSearchResult_Value { @@ -2939,7 +3472,7 @@ type UserInput struct { func (x *UserInput) Reset() { *x = UserInput{} - mi := &file_product_proto_msgTypes[58] + mi := &file_product_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2951,7 +3484,7 @@ func (x *UserInput) String() string { func (*UserInput) ProtoMessage() {} func (x *UserInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[58] + mi := &file_product_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2964,7 +3497,7 @@ func (x *UserInput) ProtoReflect() protoreflect.Message { // Deprecated: Use UserInput.ProtoReflect.Descriptor instead. func (*UserInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{58} + return file_product_proto_rawDescGZIP(), []int{70} } func (x *UserInput) GetName() string { @@ -2984,7 +3517,7 @@ type ActionInput struct { func (x *ActionInput) Reset() { *x = ActionInput{} - mi := &file_product_proto_msgTypes[59] + mi := &file_product_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2996,7 +3529,7 @@ func (x *ActionInput) String() string { func (*ActionInput) ProtoMessage() {} func (x *ActionInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[59] + mi := &file_product_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3009,7 +3542,7 @@ func (x *ActionInput) ProtoReflect() protoreflect.Message { // Deprecated: Use ActionInput.ProtoReflect.Descriptor instead. func (*ActionInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{59} + return file_product_proto_rawDescGZIP(), []int{71} } func (x *ActionInput) GetType() string { @@ -3039,7 +3572,7 @@ type ActionResult struct { func (x *ActionResult) Reset() { *x = ActionResult{} - mi := &file_product_proto_msgTypes[60] + mi := &file_product_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3051,7 +3584,7 @@ func (x *ActionResult) String() string { func (*ActionResult) ProtoMessage() {} func (x *ActionResult) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[60] + mi := &file_product_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3064,7 +3597,7 @@ func (x *ActionResult) ProtoReflect() protoreflect.Message { // Deprecated: Use ActionResult.ProtoReflect.Descriptor instead. func (*ActionResult) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{60} + return file_product_proto_rawDescGZIP(), []int{72} } func (x *ActionResult) GetValue() isActionResult_Value { @@ -3119,7 +3652,7 @@ type NestedTypeB struct { func (x *NestedTypeB) Reset() { *x = NestedTypeB{} - mi := &file_product_proto_msgTypes[61] + mi := &file_product_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3131,7 +3664,7 @@ func (x *NestedTypeB) String() string { func (*NestedTypeB) ProtoMessage() {} func (x *NestedTypeB) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[61] + mi := &file_product_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3144,7 +3677,7 @@ func (x *NestedTypeB) ProtoReflect() protoreflect.Message { // Deprecated: Use NestedTypeB.ProtoReflect.Descriptor instead. func (*NestedTypeB) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{61} + return file_product_proto_rawDescGZIP(), []int{73} } func (x *NestedTypeB) GetId() string { @@ -3178,7 +3711,7 @@ type NestedTypeC struct { func (x *NestedTypeC) Reset() { *x = NestedTypeC{} - mi := &file_product_proto_msgTypes[62] + mi := &file_product_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3190,7 +3723,7 @@ func (x *NestedTypeC) String() string { func (*NestedTypeC) ProtoMessage() {} func (x *NestedTypeC) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[62] + mi := &file_product_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3203,7 +3736,7 @@ func (x *NestedTypeC) ProtoReflect() protoreflect.Message { // Deprecated: Use NestedTypeC.ProtoReflect.Descriptor instead. func (*NestedTypeC) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{62} + return file_product_proto_rawDescGZIP(), []int{74} } func (x *NestedTypeC) GetId() string { @@ -3232,7 +3765,7 @@ type FilterType struct { func (x *FilterType) Reset() { *x = FilterType{} - mi := &file_product_proto_msgTypes[63] + mi := &file_product_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3244,7 +3777,7 @@ func (x *FilterType) String() string { func (*FilterType) ProtoMessage() {} func (x *FilterType) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[63] + mi := &file_product_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3257,7 +3790,7 @@ func (x *FilterType) ProtoReflect() protoreflect.Message { // Deprecated: Use FilterType.ProtoReflect.Descriptor instead. func (*FilterType) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{63} + return file_product_proto_rawDescGZIP(), []int{75} } func (x *FilterType) GetName() string { @@ -3298,7 +3831,7 @@ type Pagination struct { func (x *Pagination) Reset() { *x = Pagination{} - mi := &file_product_proto_msgTypes[64] + mi := &file_product_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3310,7 +3843,7 @@ func (x *Pagination) String() string { func (*Pagination) ProtoMessage() {} func (x *Pagination) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[64] + mi := &file_product_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3323,7 +3856,7 @@ func (x *Pagination) ProtoReflect() protoreflect.Message { // Deprecated: Use Pagination.ProtoReflect.Descriptor instead. func (*Pagination) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{64} + return file_product_proto_rawDescGZIP(), []int{76} } func (x *Pagination) GetPage() int32 { @@ -3351,7 +3884,7 @@ type OrderLineInput struct { func (x *OrderLineInput) Reset() { *x = OrderLineInput{} - mi := &file_product_proto_msgTypes[65] + mi := &file_product_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3363,7 +3896,7 @@ func (x *OrderLineInput) String() string { func (*OrderLineInput) ProtoMessage() {} func (x *OrderLineInput) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[65] + mi := &file_product_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3376,7 +3909,7 @@ func (x *OrderLineInput) ProtoReflect() protoreflect.Message { // Deprecated: Use OrderLineInput.ProtoReflect.Descriptor instead. func (*OrderLineInput) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{65} + return file_product_proto_rawDescGZIP(), []int{77} } func (x *OrderLineInput) GetProductId() string { @@ -3411,7 +3944,7 @@ type OrderLine struct { func (x *OrderLine) Reset() { *x = OrderLine{} - mi := &file_product_proto_msgTypes[66] + mi := &file_product_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3423,7 +3956,7 @@ func (x *OrderLine) String() string { func (*OrderLine) ProtoMessage() {} func (x *OrderLine) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[66] + mi := &file_product_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3436,7 +3969,7 @@ func (x *OrderLine) ProtoReflect() protoreflect.Message { // Deprecated: Use OrderLine.ProtoReflect.Descriptor instead. func (*OrderLine) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{66} + return file_product_proto_rawDescGZIP(), []int{78} } func (x *OrderLine) GetProductId() string { @@ -3472,7 +4005,7 @@ type Cat struct { func (x *Cat) Reset() { *x = Cat{} - mi := &file_product_proto_msgTypes[67] + mi := &file_product_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3484,7 +4017,7 @@ func (x *Cat) String() string { func (*Cat) ProtoMessage() {} func (x *Cat) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[67] + mi := &file_product_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3497,7 +4030,7 @@ func (x *Cat) ProtoReflect() protoreflect.Message { // Deprecated: Use Cat.ProtoReflect.Descriptor instead. func (*Cat) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{67} + return file_product_proto_rawDescGZIP(), []int{79} } func (x *Cat) GetId() string { @@ -3540,7 +4073,7 @@ type Dog struct { func (x *Dog) Reset() { *x = Dog{} - mi := &file_product_proto_msgTypes[68] + mi := &file_product_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3552,7 +4085,7 @@ func (x *Dog) String() string { func (*Dog) ProtoMessage() {} func (x *Dog) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[68] + mi := &file_product_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3565,7 +4098,7 @@ func (x *Dog) ProtoReflect() protoreflect.Message { // Deprecated: Use Dog.ProtoReflect.Descriptor instead. func (*Dog) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{68} + return file_product_proto_rawDescGZIP(), []int{80} } func (x *Dog) GetId() string { @@ -3606,7 +4139,7 @@ type ActionSuccess struct { func (x *ActionSuccess) Reset() { *x = ActionSuccess{} - mi := &file_product_proto_msgTypes[69] + mi := &file_product_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3618,7 +4151,7 @@ func (x *ActionSuccess) String() string { func (*ActionSuccess) ProtoMessage() {} func (x *ActionSuccess) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[69] + mi := &file_product_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3631,7 +4164,7 @@ func (x *ActionSuccess) ProtoReflect() protoreflect.Message { // Deprecated: Use ActionSuccess.ProtoReflect.Descriptor instead. func (*ActionSuccess) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{69} + return file_product_proto_rawDescGZIP(), []int{81} } func (x *ActionSuccess) GetMessage() string { @@ -3658,7 +4191,7 @@ type ActionError struct { func (x *ActionError) Reset() { *x = ActionError{} - mi := &file_product_proto_msgTypes[70] + mi := &file_product_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3670,7 +4203,7 @@ func (x *ActionError) String() string { func (*ActionError) ProtoMessage() {} func (x *ActionError) ProtoReflect() protoreflect.Message { - mi := &file_product_proto_msgTypes[70] + mi := &file_product_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3683,7 +4216,7 @@ func (x *ActionError) ProtoReflect() protoreflect.Message { // Deprecated: Use ActionError.ProtoReflect.Descriptor instead. func (*ActionError) Descriptor() ([]byte, []int) { - return file_product_proto_rawDescGZIP(), []int{70} + return file_product_proto_rawDescGZIP(), []int{82} } func (x *ActionError) GetMessage() string { @@ -3700,11 +4233,264 @@ func (x *ActionError) GetCode() string { return "" } +// New messages for testing nullable fields +type NullableFieldsType struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + OptionalString *wrapperspb.StringValue `protobuf:"bytes,3,opt,name=optional_string,json=optionalString,proto3" json:"optional_string,omitempty"` + OptionalInt *wrapperspb.Int32Value `protobuf:"bytes,4,opt,name=optional_int,json=optionalInt,proto3" json:"optional_int,omitempty"` + OptionalFloat *wrapperspb.FloatValue `protobuf:"bytes,5,opt,name=optional_float,json=optionalFloat,proto3" json:"optional_float,omitempty"` + OptionalBoolean *wrapperspb.BoolValue `protobuf:"bytes,6,opt,name=optional_boolean,json=optionalBoolean,proto3" json:"optional_boolean,omitempty"` + RequiredString string `protobuf:"bytes,7,opt,name=required_string,json=requiredString,proto3" json:"required_string,omitempty"` + RequiredInt int32 `protobuf:"varint,8,opt,name=required_int,json=requiredInt,proto3" json:"required_int,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NullableFieldsType) Reset() { + *x = NullableFieldsType{} + mi := &file_product_proto_msgTypes[83] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NullableFieldsType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NullableFieldsType) ProtoMessage() {} + +func (x *NullableFieldsType) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[83] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NullableFieldsType.ProtoReflect.Descriptor instead. +func (*NullableFieldsType) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{83} +} + +func (x *NullableFieldsType) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *NullableFieldsType) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NullableFieldsType) GetOptionalString() *wrapperspb.StringValue { + if x != nil { + return x.OptionalString + } + return nil +} + +func (x *NullableFieldsType) GetOptionalInt() *wrapperspb.Int32Value { + if x != nil { + return x.OptionalInt + } + return nil +} + +func (x *NullableFieldsType) GetOptionalFloat() *wrapperspb.FloatValue { + if x != nil { + return x.OptionalFloat + } + return nil +} + +func (x *NullableFieldsType) GetOptionalBoolean() *wrapperspb.BoolValue { + if x != nil { + return x.OptionalBoolean + } + return nil +} + +func (x *NullableFieldsType) GetRequiredString() string { + if x != nil { + return x.RequiredString + } + return "" +} + +func (x *NullableFieldsType) GetRequiredInt() int32 { + if x != nil { + return x.RequiredInt + } + return 0 +} + +type NullableFieldsInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + OptionalString *wrapperspb.StringValue `protobuf:"bytes,2,opt,name=optional_string,json=optionalString,proto3" json:"optional_string,omitempty"` + OptionalInt *wrapperspb.Int32Value `protobuf:"bytes,3,opt,name=optional_int,json=optionalInt,proto3" json:"optional_int,omitempty"` + OptionalFloat *wrapperspb.FloatValue `protobuf:"bytes,4,opt,name=optional_float,json=optionalFloat,proto3" json:"optional_float,omitempty"` + OptionalBoolean *wrapperspb.BoolValue `protobuf:"bytes,5,opt,name=optional_boolean,json=optionalBoolean,proto3" json:"optional_boolean,omitempty"` + RequiredString string `protobuf:"bytes,6,opt,name=required_string,json=requiredString,proto3" json:"required_string,omitempty"` + RequiredInt int32 `protobuf:"varint,7,opt,name=required_int,json=requiredInt,proto3" json:"required_int,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NullableFieldsInput) Reset() { + *x = NullableFieldsInput{} + mi := &file_product_proto_msgTypes[84] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NullableFieldsInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NullableFieldsInput) ProtoMessage() {} + +func (x *NullableFieldsInput) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[84] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NullableFieldsInput.ProtoReflect.Descriptor instead. +func (*NullableFieldsInput) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{84} +} + +func (x *NullableFieldsInput) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *NullableFieldsInput) GetOptionalString() *wrapperspb.StringValue { + if x != nil { + return x.OptionalString + } + return nil +} + +func (x *NullableFieldsInput) GetOptionalInt() *wrapperspb.Int32Value { + if x != nil { + return x.OptionalInt + } + return nil +} + +func (x *NullableFieldsInput) GetOptionalFloat() *wrapperspb.FloatValue { + if x != nil { + return x.OptionalFloat + } + return nil +} + +func (x *NullableFieldsInput) GetOptionalBoolean() *wrapperspb.BoolValue { + if x != nil { + return x.OptionalBoolean + } + return nil +} + +func (x *NullableFieldsInput) GetRequiredString() string { + if x != nil { + return x.RequiredString + } + return "" +} + +func (x *NullableFieldsInput) GetRequiredInt() int32 { + if x != nil { + return x.RequiredInt + } + return 0 +} + +type NullableFieldsFilter struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name *wrapperspb.StringValue `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + OptionalString *wrapperspb.StringValue `protobuf:"bytes,2,opt,name=optional_string,json=optionalString,proto3" json:"optional_string,omitempty"` + IncludeNulls *wrapperspb.BoolValue `protobuf:"bytes,3,opt,name=include_nulls,json=includeNulls,proto3" json:"include_nulls,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *NullableFieldsFilter) Reset() { + *x = NullableFieldsFilter{} + mi := &file_product_proto_msgTypes[85] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *NullableFieldsFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NullableFieldsFilter) ProtoMessage() {} + +func (x *NullableFieldsFilter) ProtoReflect() protoreflect.Message { + mi := &file_product_proto_msgTypes[85] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NullableFieldsFilter.ProtoReflect.Descriptor instead. +func (*NullableFieldsFilter) Descriptor() ([]byte, []int) { + return file_product_proto_rawDescGZIP(), []int{85} +} + +func (x *NullableFieldsFilter) GetName() *wrapperspb.StringValue { + if x != nil { + return x.Name + } + return nil +} + +func (x *NullableFieldsFilter) GetOptionalString() *wrapperspb.StringValue { + if x != nil { + return x.OptionalString + } + return nil +} + +func (x *NullableFieldsFilter) GetIncludeNulls() *wrapperspb.BoolValue { + if x != nil { + return x.IncludeNulls + } + return nil +} + var File_product_proto protoreflect.FileDescriptor const file_product_proto_rawDesc = "" + "\n" + - "\rproduct.proto\x12\tproductv1\"-\n" + + "\rproduct.proto\x12\tproductv1\x1a\x1egoogle/protobuf/wrappers.proto\"-\n" + "\x1bLookupProductByIdRequestKey\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\"V\n" + "\x18LookupProductByIdRequest\x12:\n" + @@ -3787,7 +4573,30 @@ const file_product_proto_rawDesc = "" + "\x1cMutationPerformActionRequest\x12,\n" + "\x05input\x18\x01 \x01(\v2\x16.productv1.ActionInputR\x05input\"_\n" + "\x1dMutationPerformActionResponse\x12>\n" + - "\x0eperform_action\x18\x01 \x01(\v2\x17.productv1.ActionResultR\rperformAction\"C\n" + + "\x0eperform_action\x18\x01 \x01(\v2\x17.productv1.ActionResultR\rperformAction\" \n" + + "\x1eQueryNullableFieldsTypeRequest\"r\n" + + "\x1fQueryNullableFieldsTypeResponse\x12O\n" + + "\x14nullable_fields_type\x18\x01 \x01(\v2\x1d.productv1.NullableFieldsTypeR\x12nullableFieldsType\"4\n" + + "\"QueryNullableFieldsTypeByIdRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\"\x80\x01\n" + + "#QueryNullableFieldsTypeByIdResponse\x12Y\n" + + "\x1anullable_fields_type_by_id\x18\x01 \x01(\v2\x1d.productv1.NullableFieldsTypeR\x16nullableFieldsTypeById\"c\n" + + "(QueryNullableFieldsTypeWithFilterRequest\x127\n" + + "\x06filter\x18\x01 \x01(\v2\x1f.productv1.NullableFieldsFilterR\x06filter\"\x92\x01\n" + + ")QueryNullableFieldsTypeWithFilterResponse\x12e\n" + + " nullable_fields_type_with_filter\x18\x01 \x03(\v2\x1d.productv1.NullableFieldsTypeR\x1cnullableFieldsTypeWithFilter\"$\n" + + "\"QueryAllNullableFieldsTypesRequest\"\x7f\n" + + "#QueryAllNullableFieldsTypesResponse\x12X\n" + + "\x19all_nullable_fields_types\x18\x01 \x03(\v2\x1d.productv1.NullableFieldsTypeR\x16allNullableFieldsTypes\"_\n" + + "'MutationCreateNullableFieldsTypeRequest\x124\n" + + "\x05input\x18\x01 \x01(\v2\x1e.productv1.NullableFieldsInputR\x05input\"\x88\x01\n" + + "(MutationCreateNullableFieldsTypeResponse\x12\\\n" + + "\x1bcreate_nullable_fields_type\x18\x01 \x01(\v2\x1d.productv1.NullableFieldsTypeR\x18createNullableFieldsType\"o\n" + + "'MutationUpdateNullableFieldsTypeRequest\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x124\n" + + "\x05input\x18\x02 \x01(\v2\x1e.productv1.NullableFieldsInputR\x05input\"\x88\x01\n" + + "(MutationUpdateNullableFieldsTypeResponse\x12\\\n" + + "\x1bupdate_nullable_fields_type\x18\x01 \x01(\v2\x1d.productv1.NullableFieldsTypeR\x18updateNullableFieldsType\"C\n" + "\aProduct\x12\x0e\n" + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + "\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n" + @@ -3909,13 +4718,34 @@ const file_product_proto_rawDesc = "" + "\ttimestamp\x18\x02 \x01(\tR\ttimestamp\";\n" + "\vActionError\x12\x18\n" + "\amessage\x18\x01 \x01(\tR\amessage\x12\x12\n" + - "\x04code\x18\x02 \x01(\tR\x04code*\x9a\x01\n" + + "\x04code\x18\x02 \x01(\tR\x04code\"\x96\x03\n" + + "\x12NullableFieldsType\x12\x0e\n" + + "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + + "\x04name\x18\x02 \x01(\tR\x04name\x12E\n" + + "\x0foptional_string\x18\x03 \x01(\v2\x1c.google.protobuf.StringValueR\x0eoptionalString\x12>\n" + + "\foptional_int\x18\x04 \x01(\v2\x1b.google.protobuf.Int32ValueR\voptionalInt\x12B\n" + + "\x0eoptional_float\x18\x05 \x01(\v2\x1b.google.protobuf.FloatValueR\roptionalFloat\x12E\n" + + "\x10optional_boolean\x18\x06 \x01(\v2\x1a.google.protobuf.BoolValueR\x0foptionalBoolean\x12'\n" + + "\x0frequired_string\x18\a \x01(\tR\x0erequiredString\x12!\n" + + "\frequired_int\x18\b \x01(\x05R\vrequiredInt\"\x87\x03\n" + + "\x13NullableFieldsInput\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12E\n" + + "\x0foptional_string\x18\x02 \x01(\v2\x1c.google.protobuf.StringValueR\x0eoptionalString\x12>\n" + + "\foptional_int\x18\x03 \x01(\v2\x1b.google.protobuf.Int32ValueR\voptionalInt\x12B\n" + + "\x0eoptional_float\x18\x04 \x01(\v2\x1b.google.protobuf.FloatValueR\roptionalFloat\x12E\n" + + "\x10optional_boolean\x18\x05 \x01(\v2\x1a.google.protobuf.BoolValueR\x0foptionalBoolean\x12'\n" + + "\x0frequired_string\x18\x06 \x01(\tR\x0erequiredString\x12!\n" + + "\frequired_int\x18\a \x01(\x05R\vrequiredInt\"\xd0\x01\n" + + "\x14NullableFieldsFilter\x120\n" + + "\x04name\x18\x01 \x01(\v2\x1c.google.protobuf.StringValueR\x04name\x12E\n" + + "\x0foptional_string\x18\x02 \x01(\v2\x1c.google.protobuf.StringValueR\x0eoptionalString\x12?\n" + + "\rinclude_nulls\x18\x03 \x01(\v2\x1a.google.protobuf.BoolValueR\fincludeNulls*\x9a\x01\n" + "\fCategoryKind\x12\x1d\n" + "\x19CATEGORY_KIND_UNSPECIFIED\x10\x00\x12\x16\n" + "\x12CATEGORY_KIND_BOOK\x10\x01\x12\x1d\n" + "\x19CATEGORY_KIND_ELECTRONICS\x10\x02\x12\x1b\n" + "\x17CATEGORY_KIND_FURNITURE\x10\x03\x12\x17\n" + - "\x13CATEGORY_KIND_OTHER\x10\x042\x8b\x10\n" + + "\x13CATEGORY_KIND_OTHER\x10\x042\xb2\x16\n" + "\x0eProductService\x12`\n" + "\x11LookupProductById\x12#.productv1.LookupProductByIdRequest\x1a$.productv1.LookupProductByIdResponse\"\x00\x12`\n" + "\x11LookupStorageById\x12#.productv1.LookupStorageByIdRequest\x1a$.productv1.LookupStorageByIdResponse\"\x00\x12c\n" + @@ -3937,7 +4767,13 @@ const file_product_proto_rawDesc = "" + "!QueryTypeWithMultipleFilterFields\x123.productv1.QueryTypeWithMultipleFilterFieldsRequest\x1a4.productv1.QueryTypeWithMultipleFilterFieldsResponse\"\x00\x12H\n" + "\tQueryUser\x12\x1b.productv1.QueryUserRequest\x1a\x1c.productv1.QueryUserResponse\"\x00\x12K\n" + "\n" + - "QueryUsers\x12\x1c.productv1.QueryUsersRequest\x1a\x1d.productv1.QueryUsersResponse\"\x00B%Z#cosmo/pkg/proto/productv1;productv1b\x06proto3" + "QueryUsers\x12\x1c.productv1.QueryUsersRequest\x1a\x1d.productv1.QueryUsersResponse\"\x00\x12r\n" + + "\x17QueryNullableFieldsType\x12).productv1.QueryNullableFieldsTypeRequest\x1a*.productv1.QueryNullableFieldsTypeResponse\"\x00\x12~\n" + + "\x1bQueryNullableFieldsTypeById\x12-.productv1.QueryNullableFieldsTypeByIdRequest\x1a..productv1.QueryNullableFieldsTypeByIdResponse\"\x00\x12\x90\x01\n" + + "!QueryNullableFieldsTypeWithFilter\x123.productv1.QueryNullableFieldsTypeWithFilterRequest\x1a4.productv1.QueryNullableFieldsTypeWithFilterResponse\"\x00\x12~\n" + + "\x1bQueryAllNullableFieldsTypes\x12-.productv1.QueryAllNullableFieldsTypesRequest\x1a..productv1.QueryAllNullableFieldsTypesResponse\"\x00\x12\x8d\x01\n" + + " MutationCreateNullableFieldsType\x122.productv1.MutationCreateNullableFieldsTypeRequest\x1a3.productv1.MutationCreateNullableFieldsTypeResponse\"\x00\x12\x8d\x01\n" + + " MutationUpdateNullableFieldsType\x122.productv1.MutationUpdateNullableFieldsTypeRequest\x1a3.productv1.MutationUpdateNullableFieldsTypeResponse\"\x00B%Z#cosmo/pkg/proto/productv1;productv1b\x06proto3" var ( file_product_proto_rawDescOnce sync.Once @@ -3952,7 +4788,7 @@ func file_product_proto_rawDescGZIP() []byte { } var file_product_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_product_proto_msgTypes = make([]protoimpl.MessageInfo, 71) +var file_product_proto_msgTypes = make([]protoimpl.MessageInfo, 86) var file_product_proto_goTypes = []any{ (CategoryKind)(0), // 0: productv1.CategoryKind (*LookupProductByIdRequestKey)(nil), // 1: productv1.LookupProductByIdRequestKey @@ -3997,130 +4833,181 @@ var file_product_proto_goTypes = []any{ (*MutationCreateUserResponse)(nil), // 40: productv1.MutationCreateUserResponse (*MutationPerformActionRequest)(nil), // 41: productv1.MutationPerformActionRequest (*MutationPerformActionResponse)(nil), // 42: productv1.MutationPerformActionResponse - (*Product)(nil), // 43: productv1.Product - (*Storage)(nil), // 44: productv1.Storage - (*User)(nil), // 45: productv1.User - (*NestedTypeA)(nil), // 46: productv1.NestedTypeA - (*RecursiveType)(nil), // 47: productv1.RecursiveType - (*TypeWithMultipleFilterFields)(nil), // 48: productv1.TypeWithMultipleFilterFields - (*FilterTypeInput)(nil), // 49: productv1.FilterTypeInput - (*ComplexFilterTypeInput)(nil), // 50: productv1.ComplexFilterTypeInput - (*TypeWithComplexFilterInput)(nil), // 51: productv1.TypeWithComplexFilterInput - (*OrderInput)(nil), // 52: productv1.OrderInput - (*Order)(nil), // 53: productv1.Order - (*Category)(nil), // 54: productv1.Category - (*CategoryFilter)(nil), // 55: productv1.CategoryFilter - (*Animal)(nil), // 56: productv1.Animal - (*SearchInput)(nil), // 57: productv1.SearchInput - (*SearchResult)(nil), // 58: productv1.SearchResult - (*UserInput)(nil), // 59: productv1.UserInput - (*ActionInput)(nil), // 60: productv1.ActionInput - (*ActionResult)(nil), // 61: productv1.ActionResult - (*NestedTypeB)(nil), // 62: productv1.NestedTypeB - (*NestedTypeC)(nil), // 63: productv1.NestedTypeC - (*FilterType)(nil), // 64: productv1.FilterType - (*Pagination)(nil), // 65: productv1.Pagination - (*OrderLineInput)(nil), // 66: productv1.OrderLineInput - (*OrderLine)(nil), // 67: productv1.OrderLine - (*Cat)(nil), // 68: productv1.Cat - (*Dog)(nil), // 69: productv1.Dog - (*ActionSuccess)(nil), // 70: productv1.ActionSuccess - (*ActionError)(nil), // 71: productv1.ActionError + (*QueryNullableFieldsTypeRequest)(nil), // 43: productv1.QueryNullableFieldsTypeRequest + (*QueryNullableFieldsTypeResponse)(nil), // 44: productv1.QueryNullableFieldsTypeResponse + (*QueryNullableFieldsTypeByIdRequest)(nil), // 45: productv1.QueryNullableFieldsTypeByIdRequest + (*QueryNullableFieldsTypeByIdResponse)(nil), // 46: productv1.QueryNullableFieldsTypeByIdResponse + (*QueryNullableFieldsTypeWithFilterRequest)(nil), // 47: productv1.QueryNullableFieldsTypeWithFilterRequest + (*QueryNullableFieldsTypeWithFilterResponse)(nil), // 48: productv1.QueryNullableFieldsTypeWithFilterResponse + (*QueryAllNullableFieldsTypesRequest)(nil), // 49: productv1.QueryAllNullableFieldsTypesRequest + (*QueryAllNullableFieldsTypesResponse)(nil), // 50: productv1.QueryAllNullableFieldsTypesResponse + (*MutationCreateNullableFieldsTypeRequest)(nil), // 51: productv1.MutationCreateNullableFieldsTypeRequest + (*MutationCreateNullableFieldsTypeResponse)(nil), // 52: productv1.MutationCreateNullableFieldsTypeResponse + (*MutationUpdateNullableFieldsTypeRequest)(nil), // 53: productv1.MutationUpdateNullableFieldsTypeRequest + (*MutationUpdateNullableFieldsTypeResponse)(nil), // 54: productv1.MutationUpdateNullableFieldsTypeResponse + (*Product)(nil), // 55: productv1.Product + (*Storage)(nil), // 56: productv1.Storage + (*User)(nil), // 57: productv1.User + (*NestedTypeA)(nil), // 58: productv1.NestedTypeA + (*RecursiveType)(nil), // 59: productv1.RecursiveType + (*TypeWithMultipleFilterFields)(nil), // 60: productv1.TypeWithMultipleFilterFields + (*FilterTypeInput)(nil), // 61: productv1.FilterTypeInput + (*ComplexFilterTypeInput)(nil), // 62: productv1.ComplexFilterTypeInput + (*TypeWithComplexFilterInput)(nil), // 63: productv1.TypeWithComplexFilterInput + (*OrderInput)(nil), // 64: productv1.OrderInput + (*Order)(nil), // 65: productv1.Order + (*Category)(nil), // 66: productv1.Category + (*CategoryFilter)(nil), // 67: productv1.CategoryFilter + (*Animal)(nil), // 68: productv1.Animal + (*SearchInput)(nil), // 69: productv1.SearchInput + (*SearchResult)(nil), // 70: productv1.SearchResult + (*UserInput)(nil), // 71: productv1.UserInput + (*ActionInput)(nil), // 72: productv1.ActionInput + (*ActionResult)(nil), // 73: productv1.ActionResult + (*NestedTypeB)(nil), // 74: productv1.NestedTypeB + (*NestedTypeC)(nil), // 75: productv1.NestedTypeC + (*FilterType)(nil), // 76: productv1.FilterType + (*Pagination)(nil), // 77: productv1.Pagination + (*OrderLineInput)(nil), // 78: productv1.OrderLineInput + (*OrderLine)(nil), // 79: productv1.OrderLine + (*Cat)(nil), // 80: productv1.Cat + (*Dog)(nil), // 81: productv1.Dog + (*ActionSuccess)(nil), // 82: productv1.ActionSuccess + (*ActionError)(nil), // 83: productv1.ActionError + (*NullableFieldsType)(nil), // 84: productv1.NullableFieldsType + (*NullableFieldsInput)(nil), // 85: productv1.NullableFieldsInput + (*NullableFieldsFilter)(nil), // 86: productv1.NullableFieldsFilter + (*wrapperspb.StringValue)(nil), // 87: google.protobuf.StringValue + (*wrapperspb.Int32Value)(nil), // 88: google.protobuf.Int32Value + (*wrapperspb.FloatValue)(nil), // 89: google.protobuf.FloatValue + (*wrapperspb.BoolValue)(nil), // 90: google.protobuf.BoolValue } var file_product_proto_depIdxs = []int32{ 1, // 0: productv1.LookupProductByIdRequest.keys:type_name -> productv1.LookupProductByIdRequestKey - 43, // 1: productv1.LookupProductByIdResponse.result:type_name -> productv1.Product + 55, // 1: productv1.LookupProductByIdResponse.result:type_name -> productv1.Product 4, // 2: productv1.LookupStorageByIdRequest.keys:type_name -> productv1.LookupStorageByIdRequestKey - 44, // 3: productv1.LookupStorageByIdResponse.result:type_name -> productv1.Storage - 45, // 4: productv1.QueryUsersResponse.users:type_name -> productv1.User - 45, // 5: productv1.QueryUserResponse.user:type_name -> productv1.User - 46, // 6: productv1.QueryNestedTypeResponse.nested_type:type_name -> productv1.NestedTypeA - 47, // 7: productv1.QueryRecursiveTypeResponse.recursive_type:type_name -> productv1.RecursiveType - 48, // 8: productv1.QueryTypeFilterWithArgumentsResponse.type_filter_with_arguments:type_name -> productv1.TypeWithMultipleFilterFields - 49, // 9: productv1.QueryTypeWithMultipleFilterFieldsRequest.filter:type_name -> productv1.FilterTypeInput - 48, // 10: productv1.QueryTypeWithMultipleFilterFieldsResponse.type_with_multiple_filter_fields:type_name -> productv1.TypeWithMultipleFilterFields - 50, // 11: productv1.QueryComplexFilterTypeRequest.filter:type_name -> productv1.ComplexFilterTypeInput - 51, // 12: productv1.QueryComplexFilterTypeResponse.complex_filter_type:type_name -> productv1.TypeWithComplexFilterInput - 52, // 13: productv1.QueryCalculateTotalsRequest.orders:type_name -> productv1.OrderInput - 53, // 14: productv1.QueryCalculateTotalsResponse.calculate_totals:type_name -> productv1.Order - 54, // 15: productv1.QueryCategoriesResponse.categories:type_name -> productv1.Category + 56, // 3: productv1.LookupStorageByIdResponse.result:type_name -> productv1.Storage + 57, // 4: productv1.QueryUsersResponse.users:type_name -> productv1.User + 57, // 5: productv1.QueryUserResponse.user:type_name -> productv1.User + 58, // 6: productv1.QueryNestedTypeResponse.nested_type:type_name -> productv1.NestedTypeA + 59, // 7: productv1.QueryRecursiveTypeResponse.recursive_type:type_name -> productv1.RecursiveType + 60, // 8: productv1.QueryTypeFilterWithArgumentsResponse.type_filter_with_arguments:type_name -> productv1.TypeWithMultipleFilterFields + 61, // 9: productv1.QueryTypeWithMultipleFilterFieldsRequest.filter:type_name -> productv1.FilterTypeInput + 60, // 10: productv1.QueryTypeWithMultipleFilterFieldsResponse.type_with_multiple_filter_fields:type_name -> productv1.TypeWithMultipleFilterFields + 62, // 11: productv1.QueryComplexFilterTypeRequest.filter:type_name -> productv1.ComplexFilterTypeInput + 63, // 12: productv1.QueryComplexFilterTypeResponse.complex_filter_type:type_name -> productv1.TypeWithComplexFilterInput + 64, // 13: productv1.QueryCalculateTotalsRequest.orders:type_name -> productv1.OrderInput + 65, // 14: productv1.QueryCalculateTotalsResponse.calculate_totals:type_name -> productv1.Order + 66, // 15: productv1.QueryCategoriesResponse.categories:type_name -> productv1.Category 0, // 16: productv1.QueryCategoriesByKindRequest.kind:type_name -> productv1.CategoryKind - 54, // 17: productv1.QueryCategoriesByKindResponse.categories_by_kind:type_name -> productv1.Category + 66, // 17: productv1.QueryCategoriesByKindResponse.categories_by_kind:type_name -> productv1.Category 0, // 18: productv1.QueryCategoriesByKindsRequest.kinds:type_name -> productv1.CategoryKind - 54, // 19: productv1.QueryCategoriesByKindsResponse.categories_by_kinds:type_name -> productv1.Category - 55, // 20: productv1.QueryFilterCategoriesRequest.filter:type_name -> productv1.CategoryFilter - 54, // 21: productv1.QueryFilterCategoriesResponse.filter_categories:type_name -> productv1.Category - 56, // 22: productv1.QueryRandomPetResponse.random_pet:type_name -> productv1.Animal - 56, // 23: productv1.QueryAllPetsResponse.all_pets:type_name -> productv1.Animal - 57, // 24: productv1.QuerySearchRequest.input:type_name -> productv1.SearchInput - 58, // 25: productv1.QuerySearchResponse.search:type_name -> productv1.SearchResult - 58, // 26: productv1.QueryRandomSearchResultResponse.random_search_result:type_name -> productv1.SearchResult - 59, // 27: productv1.MutationCreateUserRequest.input:type_name -> productv1.UserInput - 45, // 28: productv1.MutationCreateUserResponse.create_user:type_name -> productv1.User - 60, // 29: productv1.MutationPerformActionRequest.input:type_name -> productv1.ActionInput - 61, // 30: productv1.MutationPerformActionResponse.perform_action:type_name -> productv1.ActionResult - 62, // 31: productv1.NestedTypeA.b:type_name -> productv1.NestedTypeB - 47, // 32: productv1.RecursiveType.recursive_type:type_name -> productv1.RecursiveType - 64, // 33: productv1.ComplexFilterTypeInput.filter:type_name -> productv1.FilterType - 66, // 34: productv1.OrderInput.lines:type_name -> productv1.OrderLineInput - 67, // 35: productv1.Order.order_lines:type_name -> productv1.OrderLine - 0, // 36: productv1.Category.kind:type_name -> productv1.CategoryKind - 0, // 37: productv1.CategoryFilter.category:type_name -> productv1.CategoryKind - 65, // 38: productv1.CategoryFilter.pagination:type_name -> productv1.Pagination - 68, // 39: productv1.Animal.cat:type_name -> productv1.Cat - 69, // 40: productv1.Animal.dog:type_name -> productv1.Dog - 43, // 41: productv1.SearchResult.product:type_name -> productv1.Product - 45, // 42: productv1.SearchResult.user:type_name -> productv1.User - 54, // 43: productv1.SearchResult.category:type_name -> productv1.Category - 70, // 44: productv1.ActionResult.action_success:type_name -> productv1.ActionSuccess - 71, // 45: productv1.ActionResult.action_error:type_name -> productv1.ActionError - 63, // 46: productv1.NestedTypeB.c:type_name -> productv1.NestedTypeC - 65, // 47: productv1.FilterType.pagination:type_name -> productv1.Pagination - 2, // 48: productv1.ProductService.LookupProductById:input_type -> productv1.LookupProductByIdRequest - 5, // 49: productv1.ProductService.LookupStorageById:input_type -> productv1.LookupStorageByIdRequest - 39, // 50: productv1.ProductService.MutationCreateUser:input_type -> productv1.MutationCreateUserRequest - 41, // 51: productv1.ProductService.MutationPerformAction:input_type -> productv1.MutationPerformActionRequest - 33, // 52: productv1.ProductService.QueryAllPets:input_type -> productv1.QueryAllPetsRequest - 21, // 53: productv1.ProductService.QueryCalculateTotals:input_type -> productv1.QueryCalculateTotalsRequest - 23, // 54: productv1.ProductService.QueryCategories:input_type -> productv1.QueryCategoriesRequest - 25, // 55: productv1.ProductService.QueryCategoriesByKind:input_type -> productv1.QueryCategoriesByKindRequest - 27, // 56: productv1.ProductService.QueryCategoriesByKinds:input_type -> productv1.QueryCategoriesByKindsRequest - 19, // 57: productv1.ProductService.QueryComplexFilterType:input_type -> productv1.QueryComplexFilterTypeRequest - 29, // 58: productv1.ProductService.QueryFilterCategories:input_type -> productv1.QueryFilterCategoriesRequest - 11, // 59: productv1.ProductService.QueryNestedType:input_type -> productv1.QueryNestedTypeRequest - 31, // 60: productv1.ProductService.QueryRandomPet:input_type -> productv1.QueryRandomPetRequest - 37, // 61: productv1.ProductService.QueryRandomSearchResult:input_type -> productv1.QueryRandomSearchResultRequest - 13, // 62: productv1.ProductService.QueryRecursiveType:input_type -> productv1.QueryRecursiveTypeRequest - 35, // 63: productv1.ProductService.QuerySearch:input_type -> productv1.QuerySearchRequest - 15, // 64: productv1.ProductService.QueryTypeFilterWithArguments:input_type -> productv1.QueryTypeFilterWithArgumentsRequest - 17, // 65: productv1.ProductService.QueryTypeWithMultipleFilterFields:input_type -> productv1.QueryTypeWithMultipleFilterFieldsRequest - 9, // 66: productv1.ProductService.QueryUser:input_type -> productv1.QueryUserRequest - 7, // 67: productv1.ProductService.QueryUsers:input_type -> productv1.QueryUsersRequest - 3, // 68: productv1.ProductService.LookupProductById:output_type -> productv1.LookupProductByIdResponse - 6, // 69: productv1.ProductService.LookupStorageById:output_type -> productv1.LookupStorageByIdResponse - 40, // 70: productv1.ProductService.MutationCreateUser:output_type -> productv1.MutationCreateUserResponse - 42, // 71: productv1.ProductService.MutationPerformAction:output_type -> productv1.MutationPerformActionResponse - 34, // 72: productv1.ProductService.QueryAllPets:output_type -> productv1.QueryAllPetsResponse - 22, // 73: productv1.ProductService.QueryCalculateTotals:output_type -> productv1.QueryCalculateTotalsResponse - 24, // 74: productv1.ProductService.QueryCategories:output_type -> productv1.QueryCategoriesResponse - 26, // 75: productv1.ProductService.QueryCategoriesByKind:output_type -> productv1.QueryCategoriesByKindResponse - 28, // 76: productv1.ProductService.QueryCategoriesByKinds:output_type -> productv1.QueryCategoriesByKindsResponse - 20, // 77: productv1.ProductService.QueryComplexFilterType:output_type -> productv1.QueryComplexFilterTypeResponse - 30, // 78: productv1.ProductService.QueryFilterCategories:output_type -> productv1.QueryFilterCategoriesResponse - 12, // 79: productv1.ProductService.QueryNestedType:output_type -> productv1.QueryNestedTypeResponse - 32, // 80: productv1.ProductService.QueryRandomPet:output_type -> productv1.QueryRandomPetResponse - 38, // 81: productv1.ProductService.QueryRandomSearchResult:output_type -> productv1.QueryRandomSearchResultResponse - 14, // 82: productv1.ProductService.QueryRecursiveType:output_type -> productv1.QueryRecursiveTypeResponse - 36, // 83: productv1.ProductService.QuerySearch:output_type -> productv1.QuerySearchResponse - 16, // 84: productv1.ProductService.QueryTypeFilterWithArguments:output_type -> productv1.QueryTypeFilterWithArgumentsResponse - 18, // 85: productv1.ProductService.QueryTypeWithMultipleFilterFields:output_type -> productv1.QueryTypeWithMultipleFilterFieldsResponse - 10, // 86: productv1.ProductService.QueryUser:output_type -> productv1.QueryUserResponse - 8, // 87: productv1.ProductService.QueryUsers:output_type -> productv1.QueryUsersResponse - 68, // [68:88] is the sub-list for method output_type - 48, // [48:68] is the sub-list for method input_type - 48, // [48:48] is the sub-list for extension type_name - 48, // [48:48] is the sub-list for extension extendee - 0, // [0:48] is the sub-list for field type_name + 66, // 19: productv1.QueryCategoriesByKindsResponse.categories_by_kinds:type_name -> productv1.Category + 67, // 20: productv1.QueryFilterCategoriesRequest.filter:type_name -> productv1.CategoryFilter + 66, // 21: productv1.QueryFilterCategoriesResponse.filter_categories:type_name -> productv1.Category + 68, // 22: productv1.QueryRandomPetResponse.random_pet:type_name -> productv1.Animal + 68, // 23: productv1.QueryAllPetsResponse.all_pets:type_name -> productv1.Animal + 69, // 24: productv1.QuerySearchRequest.input:type_name -> productv1.SearchInput + 70, // 25: productv1.QuerySearchResponse.search:type_name -> productv1.SearchResult + 70, // 26: productv1.QueryRandomSearchResultResponse.random_search_result:type_name -> productv1.SearchResult + 71, // 27: productv1.MutationCreateUserRequest.input:type_name -> productv1.UserInput + 57, // 28: productv1.MutationCreateUserResponse.create_user:type_name -> productv1.User + 72, // 29: productv1.MutationPerformActionRequest.input:type_name -> productv1.ActionInput + 73, // 30: productv1.MutationPerformActionResponse.perform_action:type_name -> productv1.ActionResult + 84, // 31: productv1.QueryNullableFieldsTypeResponse.nullable_fields_type:type_name -> productv1.NullableFieldsType + 84, // 32: productv1.QueryNullableFieldsTypeByIdResponse.nullable_fields_type_by_id:type_name -> productv1.NullableFieldsType + 86, // 33: productv1.QueryNullableFieldsTypeWithFilterRequest.filter:type_name -> productv1.NullableFieldsFilter + 84, // 34: productv1.QueryNullableFieldsTypeWithFilterResponse.nullable_fields_type_with_filter:type_name -> productv1.NullableFieldsType + 84, // 35: productv1.QueryAllNullableFieldsTypesResponse.all_nullable_fields_types:type_name -> productv1.NullableFieldsType + 85, // 36: productv1.MutationCreateNullableFieldsTypeRequest.input:type_name -> productv1.NullableFieldsInput + 84, // 37: productv1.MutationCreateNullableFieldsTypeResponse.create_nullable_fields_type:type_name -> productv1.NullableFieldsType + 85, // 38: productv1.MutationUpdateNullableFieldsTypeRequest.input:type_name -> productv1.NullableFieldsInput + 84, // 39: productv1.MutationUpdateNullableFieldsTypeResponse.update_nullable_fields_type:type_name -> productv1.NullableFieldsType + 74, // 40: productv1.NestedTypeA.b:type_name -> productv1.NestedTypeB + 59, // 41: productv1.RecursiveType.recursive_type:type_name -> productv1.RecursiveType + 76, // 42: productv1.ComplexFilterTypeInput.filter:type_name -> productv1.FilterType + 78, // 43: productv1.OrderInput.lines:type_name -> productv1.OrderLineInput + 79, // 44: productv1.Order.order_lines:type_name -> productv1.OrderLine + 0, // 45: productv1.Category.kind:type_name -> productv1.CategoryKind + 0, // 46: productv1.CategoryFilter.category:type_name -> productv1.CategoryKind + 77, // 47: productv1.CategoryFilter.pagination:type_name -> productv1.Pagination + 80, // 48: productv1.Animal.cat:type_name -> productv1.Cat + 81, // 49: productv1.Animal.dog:type_name -> productv1.Dog + 55, // 50: productv1.SearchResult.product:type_name -> productv1.Product + 57, // 51: productv1.SearchResult.user:type_name -> productv1.User + 66, // 52: productv1.SearchResult.category:type_name -> productv1.Category + 82, // 53: productv1.ActionResult.action_success:type_name -> productv1.ActionSuccess + 83, // 54: productv1.ActionResult.action_error:type_name -> productv1.ActionError + 75, // 55: productv1.NestedTypeB.c:type_name -> productv1.NestedTypeC + 77, // 56: productv1.FilterType.pagination:type_name -> productv1.Pagination + 87, // 57: productv1.NullableFieldsType.optional_string:type_name -> google.protobuf.StringValue + 88, // 58: productv1.NullableFieldsType.optional_int:type_name -> google.protobuf.Int32Value + 89, // 59: productv1.NullableFieldsType.optional_float:type_name -> google.protobuf.FloatValue + 90, // 60: productv1.NullableFieldsType.optional_boolean:type_name -> google.protobuf.BoolValue + 87, // 61: productv1.NullableFieldsInput.optional_string:type_name -> google.protobuf.StringValue + 88, // 62: productv1.NullableFieldsInput.optional_int:type_name -> google.protobuf.Int32Value + 89, // 63: productv1.NullableFieldsInput.optional_float:type_name -> google.protobuf.FloatValue + 90, // 64: productv1.NullableFieldsInput.optional_boolean:type_name -> google.protobuf.BoolValue + 87, // 65: productv1.NullableFieldsFilter.name:type_name -> google.protobuf.StringValue + 87, // 66: productv1.NullableFieldsFilter.optional_string:type_name -> google.protobuf.StringValue + 90, // 67: productv1.NullableFieldsFilter.include_nulls:type_name -> google.protobuf.BoolValue + 2, // 68: productv1.ProductService.LookupProductById:input_type -> productv1.LookupProductByIdRequest + 5, // 69: productv1.ProductService.LookupStorageById:input_type -> productv1.LookupStorageByIdRequest + 39, // 70: productv1.ProductService.MutationCreateUser:input_type -> productv1.MutationCreateUserRequest + 41, // 71: productv1.ProductService.MutationPerformAction:input_type -> productv1.MutationPerformActionRequest + 33, // 72: productv1.ProductService.QueryAllPets:input_type -> productv1.QueryAllPetsRequest + 21, // 73: productv1.ProductService.QueryCalculateTotals:input_type -> productv1.QueryCalculateTotalsRequest + 23, // 74: productv1.ProductService.QueryCategories:input_type -> productv1.QueryCategoriesRequest + 25, // 75: productv1.ProductService.QueryCategoriesByKind:input_type -> productv1.QueryCategoriesByKindRequest + 27, // 76: productv1.ProductService.QueryCategoriesByKinds:input_type -> productv1.QueryCategoriesByKindsRequest + 19, // 77: productv1.ProductService.QueryComplexFilterType:input_type -> productv1.QueryComplexFilterTypeRequest + 29, // 78: productv1.ProductService.QueryFilterCategories:input_type -> productv1.QueryFilterCategoriesRequest + 11, // 79: productv1.ProductService.QueryNestedType:input_type -> productv1.QueryNestedTypeRequest + 31, // 80: productv1.ProductService.QueryRandomPet:input_type -> productv1.QueryRandomPetRequest + 37, // 81: productv1.ProductService.QueryRandomSearchResult:input_type -> productv1.QueryRandomSearchResultRequest + 13, // 82: productv1.ProductService.QueryRecursiveType:input_type -> productv1.QueryRecursiveTypeRequest + 35, // 83: productv1.ProductService.QuerySearch:input_type -> productv1.QuerySearchRequest + 15, // 84: productv1.ProductService.QueryTypeFilterWithArguments:input_type -> productv1.QueryTypeFilterWithArgumentsRequest + 17, // 85: productv1.ProductService.QueryTypeWithMultipleFilterFields:input_type -> productv1.QueryTypeWithMultipleFilterFieldsRequest + 9, // 86: productv1.ProductService.QueryUser:input_type -> productv1.QueryUserRequest + 7, // 87: productv1.ProductService.QueryUsers:input_type -> productv1.QueryUsersRequest + 43, // 88: productv1.ProductService.QueryNullableFieldsType:input_type -> productv1.QueryNullableFieldsTypeRequest + 45, // 89: productv1.ProductService.QueryNullableFieldsTypeById:input_type -> productv1.QueryNullableFieldsTypeByIdRequest + 47, // 90: productv1.ProductService.QueryNullableFieldsTypeWithFilter:input_type -> productv1.QueryNullableFieldsTypeWithFilterRequest + 49, // 91: productv1.ProductService.QueryAllNullableFieldsTypes:input_type -> productv1.QueryAllNullableFieldsTypesRequest + 51, // 92: productv1.ProductService.MutationCreateNullableFieldsType:input_type -> productv1.MutationCreateNullableFieldsTypeRequest + 53, // 93: productv1.ProductService.MutationUpdateNullableFieldsType:input_type -> productv1.MutationUpdateNullableFieldsTypeRequest + 3, // 94: productv1.ProductService.LookupProductById:output_type -> productv1.LookupProductByIdResponse + 6, // 95: productv1.ProductService.LookupStorageById:output_type -> productv1.LookupStorageByIdResponse + 40, // 96: productv1.ProductService.MutationCreateUser:output_type -> productv1.MutationCreateUserResponse + 42, // 97: productv1.ProductService.MutationPerformAction:output_type -> productv1.MutationPerformActionResponse + 34, // 98: productv1.ProductService.QueryAllPets:output_type -> productv1.QueryAllPetsResponse + 22, // 99: productv1.ProductService.QueryCalculateTotals:output_type -> productv1.QueryCalculateTotalsResponse + 24, // 100: productv1.ProductService.QueryCategories:output_type -> productv1.QueryCategoriesResponse + 26, // 101: productv1.ProductService.QueryCategoriesByKind:output_type -> productv1.QueryCategoriesByKindResponse + 28, // 102: productv1.ProductService.QueryCategoriesByKinds:output_type -> productv1.QueryCategoriesByKindsResponse + 20, // 103: productv1.ProductService.QueryComplexFilterType:output_type -> productv1.QueryComplexFilterTypeResponse + 30, // 104: productv1.ProductService.QueryFilterCategories:output_type -> productv1.QueryFilterCategoriesResponse + 12, // 105: productv1.ProductService.QueryNestedType:output_type -> productv1.QueryNestedTypeResponse + 32, // 106: productv1.ProductService.QueryRandomPet:output_type -> productv1.QueryRandomPetResponse + 38, // 107: productv1.ProductService.QueryRandomSearchResult:output_type -> productv1.QueryRandomSearchResultResponse + 14, // 108: productv1.ProductService.QueryRecursiveType:output_type -> productv1.QueryRecursiveTypeResponse + 36, // 109: productv1.ProductService.QuerySearch:output_type -> productv1.QuerySearchResponse + 16, // 110: productv1.ProductService.QueryTypeFilterWithArguments:output_type -> productv1.QueryTypeFilterWithArgumentsResponse + 18, // 111: productv1.ProductService.QueryTypeWithMultipleFilterFields:output_type -> productv1.QueryTypeWithMultipleFilterFieldsResponse + 10, // 112: productv1.ProductService.QueryUser:output_type -> productv1.QueryUserResponse + 8, // 113: productv1.ProductService.QueryUsers:output_type -> productv1.QueryUsersResponse + 44, // 114: productv1.ProductService.QueryNullableFieldsType:output_type -> productv1.QueryNullableFieldsTypeResponse + 46, // 115: productv1.ProductService.QueryNullableFieldsTypeById:output_type -> productv1.QueryNullableFieldsTypeByIdResponse + 48, // 116: productv1.ProductService.QueryNullableFieldsTypeWithFilter:output_type -> productv1.QueryNullableFieldsTypeWithFilterResponse + 50, // 117: productv1.ProductService.QueryAllNullableFieldsTypes:output_type -> productv1.QueryAllNullableFieldsTypesResponse + 52, // 118: productv1.ProductService.MutationCreateNullableFieldsType:output_type -> productv1.MutationCreateNullableFieldsTypeResponse + 54, // 119: productv1.ProductService.MutationUpdateNullableFieldsType:output_type -> productv1.MutationUpdateNullableFieldsTypeResponse + 94, // [94:120] is the sub-list for method output_type + 68, // [68:94] is the sub-list for method input_type + 68, // [68:68] is the sub-list for extension type_name + 68, // [68:68] is the sub-list for extension extendee + 0, // [0:68] is the sub-list for field type_name } func init() { file_product_proto_init() } @@ -4128,16 +5015,16 @@ func file_product_proto_init() { if File_product_proto != nil { return } - file_product_proto_msgTypes[55].OneofWrappers = []any{ + file_product_proto_msgTypes[67].OneofWrappers = []any{ (*Animal_Cat)(nil), (*Animal_Dog)(nil), } - file_product_proto_msgTypes[57].OneofWrappers = []any{ + file_product_proto_msgTypes[69].OneofWrappers = []any{ (*SearchResult_Product)(nil), (*SearchResult_User)(nil), (*SearchResult_Category)(nil), } - file_product_proto_msgTypes[60].OneofWrappers = []any{ + file_product_proto_msgTypes[72].OneofWrappers = []any{ (*ActionResult_ActionSuccess)(nil), (*ActionResult_ActionError)(nil), } @@ -4147,7 +5034,7 @@ func file_product_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_product_proto_rawDesc), len(file_product_proto_rawDesc)), NumEnums: 1, - NumMessages: 71, + NumMessages: 86, NumExtensions: 0, NumServices: 1, }, diff --git a/v2/pkg/grpctest/productv1/product_grpc.pb.go b/v2/pkg/grpctest/productv1/product_grpc.pb.go index 0fe45e5eb3..6ce5c39e95 100644 --- a/v2/pkg/grpctest/productv1/product_grpc.pb.go +++ b/v2/pkg/grpctest/productv1/product_grpc.pb.go @@ -39,6 +39,12 @@ const ( ProductService_QueryTypeWithMultipleFilterFields_FullMethodName = "/productv1.ProductService/QueryTypeWithMultipleFilterFields" ProductService_QueryUser_FullMethodName = "/productv1.ProductService/QueryUser" ProductService_QueryUsers_FullMethodName = "/productv1.ProductService/QueryUsers" + ProductService_QueryNullableFieldsType_FullMethodName = "/productv1.ProductService/QueryNullableFieldsType" + ProductService_QueryNullableFieldsTypeById_FullMethodName = "/productv1.ProductService/QueryNullableFieldsTypeById" + ProductService_QueryNullableFieldsTypeWithFilter_FullMethodName = "/productv1.ProductService/QueryNullableFieldsTypeWithFilter" + ProductService_QueryAllNullableFieldsTypes_FullMethodName = "/productv1.ProductService/QueryAllNullableFieldsTypes" + ProductService_MutationCreateNullableFieldsType_FullMethodName = "/productv1.ProductService/MutationCreateNullableFieldsType" + ProductService_MutationUpdateNullableFieldsType_FullMethodName = "/productv1.ProductService/MutationUpdateNullableFieldsType" ) // ProductServiceClient is the client API for ProductService service. @@ -69,6 +75,13 @@ type ProductServiceClient interface { QueryTypeWithMultipleFilterFields(ctx context.Context, in *QueryTypeWithMultipleFilterFieldsRequest, opts ...grpc.CallOption) (*QueryTypeWithMultipleFilterFieldsResponse, error) QueryUser(ctx context.Context, in *QueryUserRequest, opts ...grpc.CallOption) (*QueryUserResponse, error) QueryUsers(ctx context.Context, in *QueryUsersRequest, opts ...grpc.CallOption) (*QueryUsersResponse, error) + // Nullable fields RPCs + QueryNullableFieldsType(ctx context.Context, in *QueryNullableFieldsTypeRequest, opts ...grpc.CallOption) (*QueryNullableFieldsTypeResponse, error) + QueryNullableFieldsTypeById(ctx context.Context, in *QueryNullableFieldsTypeByIdRequest, opts ...grpc.CallOption) (*QueryNullableFieldsTypeByIdResponse, error) + QueryNullableFieldsTypeWithFilter(ctx context.Context, in *QueryNullableFieldsTypeWithFilterRequest, opts ...grpc.CallOption) (*QueryNullableFieldsTypeWithFilterResponse, error) + QueryAllNullableFieldsTypes(ctx context.Context, in *QueryAllNullableFieldsTypesRequest, opts ...grpc.CallOption) (*QueryAllNullableFieldsTypesResponse, error) + MutationCreateNullableFieldsType(ctx context.Context, in *MutationCreateNullableFieldsTypeRequest, opts ...grpc.CallOption) (*MutationCreateNullableFieldsTypeResponse, error) + MutationUpdateNullableFieldsType(ctx context.Context, in *MutationUpdateNullableFieldsTypeRequest, opts ...grpc.CallOption) (*MutationUpdateNullableFieldsTypeResponse, error) } type productServiceClient struct { @@ -279,6 +292,66 @@ func (c *productServiceClient) QueryUsers(ctx context.Context, in *QueryUsersReq return out, nil } +func (c *productServiceClient) QueryNullableFieldsType(ctx context.Context, in *QueryNullableFieldsTypeRequest, opts ...grpc.CallOption) (*QueryNullableFieldsTypeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(QueryNullableFieldsTypeResponse) + err := c.cc.Invoke(ctx, ProductService_QueryNullableFieldsType_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *productServiceClient) QueryNullableFieldsTypeById(ctx context.Context, in *QueryNullableFieldsTypeByIdRequest, opts ...grpc.CallOption) (*QueryNullableFieldsTypeByIdResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(QueryNullableFieldsTypeByIdResponse) + err := c.cc.Invoke(ctx, ProductService_QueryNullableFieldsTypeById_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *productServiceClient) QueryNullableFieldsTypeWithFilter(ctx context.Context, in *QueryNullableFieldsTypeWithFilterRequest, opts ...grpc.CallOption) (*QueryNullableFieldsTypeWithFilterResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(QueryNullableFieldsTypeWithFilterResponse) + err := c.cc.Invoke(ctx, ProductService_QueryNullableFieldsTypeWithFilter_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *productServiceClient) QueryAllNullableFieldsTypes(ctx context.Context, in *QueryAllNullableFieldsTypesRequest, opts ...grpc.CallOption) (*QueryAllNullableFieldsTypesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(QueryAllNullableFieldsTypesResponse) + err := c.cc.Invoke(ctx, ProductService_QueryAllNullableFieldsTypes_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *productServiceClient) MutationCreateNullableFieldsType(ctx context.Context, in *MutationCreateNullableFieldsTypeRequest, opts ...grpc.CallOption) (*MutationCreateNullableFieldsTypeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MutationCreateNullableFieldsTypeResponse) + err := c.cc.Invoke(ctx, ProductService_MutationCreateNullableFieldsType_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *productServiceClient) MutationUpdateNullableFieldsType(ctx context.Context, in *MutationUpdateNullableFieldsTypeRequest, opts ...grpc.CallOption) (*MutationUpdateNullableFieldsTypeResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(MutationUpdateNullableFieldsTypeResponse) + err := c.cc.Invoke(ctx, ProductService_MutationUpdateNullableFieldsType_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // ProductServiceServer is the server API for ProductService service. // All implementations must embed UnimplementedProductServiceServer // for forward compatibility. @@ -307,6 +380,13 @@ type ProductServiceServer interface { QueryTypeWithMultipleFilterFields(context.Context, *QueryTypeWithMultipleFilterFieldsRequest) (*QueryTypeWithMultipleFilterFieldsResponse, error) QueryUser(context.Context, *QueryUserRequest) (*QueryUserResponse, error) QueryUsers(context.Context, *QueryUsersRequest) (*QueryUsersResponse, error) + // Nullable fields RPCs + QueryNullableFieldsType(context.Context, *QueryNullableFieldsTypeRequest) (*QueryNullableFieldsTypeResponse, error) + QueryNullableFieldsTypeById(context.Context, *QueryNullableFieldsTypeByIdRequest) (*QueryNullableFieldsTypeByIdResponse, error) + QueryNullableFieldsTypeWithFilter(context.Context, *QueryNullableFieldsTypeWithFilterRequest) (*QueryNullableFieldsTypeWithFilterResponse, error) + QueryAllNullableFieldsTypes(context.Context, *QueryAllNullableFieldsTypesRequest) (*QueryAllNullableFieldsTypesResponse, error) + MutationCreateNullableFieldsType(context.Context, *MutationCreateNullableFieldsTypeRequest) (*MutationCreateNullableFieldsTypeResponse, error) + MutationUpdateNullableFieldsType(context.Context, *MutationUpdateNullableFieldsTypeRequest) (*MutationUpdateNullableFieldsTypeResponse, error) mustEmbedUnimplementedProductServiceServer() } @@ -377,6 +457,24 @@ func (UnimplementedProductServiceServer) QueryUser(context.Context, *QueryUserRe func (UnimplementedProductServiceServer) QueryUsers(context.Context, *QueryUsersRequest) (*QueryUsersResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method QueryUsers not implemented") } +func (UnimplementedProductServiceServer) QueryNullableFieldsType(context.Context, *QueryNullableFieldsTypeRequest) (*QueryNullableFieldsTypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryNullableFieldsType not implemented") +} +func (UnimplementedProductServiceServer) QueryNullableFieldsTypeById(context.Context, *QueryNullableFieldsTypeByIdRequest) (*QueryNullableFieldsTypeByIdResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryNullableFieldsTypeById not implemented") +} +func (UnimplementedProductServiceServer) QueryNullableFieldsTypeWithFilter(context.Context, *QueryNullableFieldsTypeWithFilterRequest) (*QueryNullableFieldsTypeWithFilterResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryNullableFieldsTypeWithFilter not implemented") +} +func (UnimplementedProductServiceServer) QueryAllNullableFieldsTypes(context.Context, *QueryAllNullableFieldsTypesRequest) (*QueryAllNullableFieldsTypesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method QueryAllNullableFieldsTypes not implemented") +} +func (UnimplementedProductServiceServer) MutationCreateNullableFieldsType(context.Context, *MutationCreateNullableFieldsTypeRequest) (*MutationCreateNullableFieldsTypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MutationCreateNullableFieldsType not implemented") +} +func (UnimplementedProductServiceServer) MutationUpdateNullableFieldsType(context.Context, *MutationUpdateNullableFieldsTypeRequest) (*MutationUpdateNullableFieldsTypeResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method MutationUpdateNullableFieldsType not implemented") +} func (UnimplementedProductServiceServer) mustEmbedUnimplementedProductServiceServer() {} func (UnimplementedProductServiceServer) testEmbeddedByValue() {} @@ -758,6 +856,114 @@ func _ProductService_QueryUsers_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } +func _ProductService_QueryNullableFieldsType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNullableFieldsTypeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductServiceServer).QueryNullableFieldsType(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductService_QueryNullableFieldsType_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductServiceServer).QueryNullableFieldsType(ctx, req.(*QueryNullableFieldsTypeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProductService_QueryNullableFieldsTypeById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNullableFieldsTypeByIdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductServiceServer).QueryNullableFieldsTypeById(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductService_QueryNullableFieldsTypeById_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductServiceServer).QueryNullableFieldsTypeById(ctx, req.(*QueryNullableFieldsTypeByIdRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProductService_QueryNullableFieldsTypeWithFilter_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryNullableFieldsTypeWithFilterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductServiceServer).QueryNullableFieldsTypeWithFilter(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductService_QueryNullableFieldsTypeWithFilter_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductServiceServer).QueryNullableFieldsTypeWithFilter(ctx, req.(*QueryNullableFieldsTypeWithFilterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProductService_QueryAllNullableFieldsTypes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryAllNullableFieldsTypesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductServiceServer).QueryAllNullableFieldsTypes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductService_QueryAllNullableFieldsTypes_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductServiceServer).QueryAllNullableFieldsTypes(ctx, req.(*QueryAllNullableFieldsTypesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProductService_MutationCreateNullableFieldsType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MutationCreateNullableFieldsTypeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductServiceServer).MutationCreateNullableFieldsType(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductService_MutationCreateNullableFieldsType_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductServiceServer).MutationCreateNullableFieldsType(ctx, req.(*MutationCreateNullableFieldsTypeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ProductService_MutationUpdateNullableFieldsType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MutationUpdateNullableFieldsTypeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ProductServiceServer).MutationUpdateNullableFieldsType(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ProductService_MutationUpdateNullableFieldsType_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ProductServiceServer).MutationUpdateNullableFieldsType(ctx, req.(*MutationUpdateNullableFieldsTypeRequest)) + } + return interceptor(ctx, in, info, handler) +} + // ProductService_ServiceDesc is the grpc.ServiceDesc for ProductService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -845,6 +1051,30 @@ var ProductService_ServiceDesc = grpc.ServiceDesc{ MethodName: "QueryUsers", Handler: _ProductService_QueryUsers_Handler, }, + { + MethodName: "QueryNullableFieldsType", + Handler: _ProductService_QueryNullableFieldsType_Handler, + }, + { + MethodName: "QueryNullableFieldsTypeById", + Handler: _ProductService_QueryNullableFieldsTypeById_Handler, + }, + { + MethodName: "QueryNullableFieldsTypeWithFilter", + Handler: _ProductService_QueryNullableFieldsTypeWithFilter_Handler, + }, + { + MethodName: "QueryAllNullableFieldsTypes", + Handler: _ProductService_QueryAllNullableFieldsTypes_Handler, + }, + { + MethodName: "MutationCreateNullableFieldsType", + Handler: _ProductService_MutationCreateNullableFieldsType_Handler, + }, + { + MethodName: "MutationUpdateNullableFieldsType", + Handler: _ProductService_MutationUpdateNullableFieldsType_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "product.proto", diff --git a/v2/pkg/grpctest/testdata/products.graphqls b/v2/pkg/grpctest/testdata/products.graphqls index 421e376b32..328ae2eddb 100644 --- a/v2/pkg/grpctest/testdata/products.graphqls +++ b/v2/pkg/grpctest/testdata/products.graphqls @@ -164,6 +164,34 @@ input ActionInput { payload: String! } +# New types for testing nullable fields +type NullableFieldsType { + id: ID! + name: String! + optionalString: String + optionalInt: Int + optionalFloat: Float + optionalBoolean: Boolean + requiredString: String! + requiredInt: Int! +} + +input NullableFieldsInput { + name: String! + optionalString: String + optionalInt: Int + optionalFloat: Float + optionalBoolean: Boolean + requiredString: String! + requiredInt: Int! +} + +input NullableFieldsFilter { + name: String + optionalString: String + includeNulls: Boolean +} + type Query { _entities(representations: [_Any!]!): [_Entity!]! users: [User!]! @@ -191,6 +219,12 @@ type Query { # Union queries search(input: SearchInput!): [SearchResult!]! randomSearchResult: SearchResult! + + # Nullable fields queries + nullableFieldsType: NullableFieldsType! + nullableFieldsTypeById(id: ID!): NullableFieldsType + nullableFieldsTypeWithFilter(filter: NullableFieldsFilter!): [NullableFieldsType!]! + allNullableFieldsTypes: [NullableFieldsType!]! } input UserInput { @@ -202,6 +236,10 @@ type Mutation { # Union mutation performAction(input: ActionInput!): ActionResult! + + # Nullable fields mutation + createNullableFieldsType(input: NullableFieldsInput!): NullableFieldsType! + updateNullableFieldsType(id: ID!, input: NullableFieldsInput!): NullableFieldsType } union _Entity = Product | Storage From 189883b12853e869997e3624da84746c3062297f Mon Sep 17 00:00:00 2001 From: Ludwig Bedacht Date: Mon, 7 Jul 2025 11:18:53 +0200 Subject: [PATCH 2/5] feat: add handling for nullable fields in arguments --- .../datasource/grpc_datasource/compiler.go | 1 + .../grpc_datasource/execution_plan_test.go | 545 ++++++++++++++++++ .../grpc_datasource/execution_plan_visitor.go | 2 + 3 files changed, 548 insertions(+) diff --git a/v2/pkg/engine/datasource/grpc_datasource/compiler.go b/v2/pkg/engine/datasource/grpc_datasource/compiler.go index ecbe110bf4..17a88136d8 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/compiler.go +++ b/v2/pkg/engine/datasource/grpc_datasource/compiler.go @@ -471,6 +471,7 @@ func (p *RPCCompiler) buildProtoMessage(inputMessage Message, rpcMessage *RPCMes } // Handle scalar fields + // TODO handle optional fields value := data.Get(rpcField.JSONPath) message.Set(fd.ByName(protoref.Name(field.Name)), p.setValueForKind(field.Type, value)) } diff --git a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go index 9a632323c7..856321b842 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go +++ b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_test.go @@ -1742,6 +1742,7 @@ func TestCompositeTypeExecutionPlan(t *testing.T) { Name: "limit", TypeName: string(DataTypeInt32), JSONPath: "limit", + Optional: true, }, }, }, @@ -1933,6 +1934,7 @@ func TestCompositeTypeExecutionPlan(t *testing.T) { Name: "limit", TypeName: string(DataTypeInt32), JSONPath: "limit", + Optional: true, }, }, }, @@ -3237,6 +3239,549 @@ func TestNullableFieldsExecutionPlan(t *testing.T) { }, }, }, + { + name: "Should create an execution plan for a query with nullable fields in the request", + query: `query NullableFieldsTypeWithFilterQuery($filter: NullableFieldsFilter!) { nullableFieldsTypeWithFilter(filter: $filter) { id name optionalString optionalInt optionalFloat optionalBoolean } }`, + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "QueryNullableFieldsTypeWithFilter", + Request: RPCMessage{ + Name: "QueryNullableFieldsTypeWithFilterRequest", + Fields: []RPCField{ + { + Name: "filter", + TypeName: string(DataTypeMessage), + JSONPath: "filter", + Message: &RPCMessage{ + Name: "NullableFieldsFilter", + Fields: []RPCField{ + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + Optional: true, + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "include_nulls", + TypeName: string(DataTypeBool), + JSONPath: "includeNulls", + Optional: true, + }, + }, + }, + }, + }, + }, + Response: RPCMessage{ + Name: "QueryNullableFieldsTypeWithFilterResponse", + Fields: []RPCField{ + { + Name: "nullable_fields_type_with_filter", + TypeName: string(DataTypeMessage), + JSONPath: "nullableFieldsTypeWithFilter", + Repeated: true, + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "optional_float", + TypeName: string(DataTypeDouble), + JSONPath: "optionalFloat", + Optional: true, + }, + { + Name: "optional_boolean", + TypeName: string(DataTypeBool), + JSONPath: "optionalBoolean", + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should create an execution plan for nullable fields type by ID query", + query: `query NullableFieldsTypeByIdQuery($id: ID!) { nullableFieldsTypeById(id: $id) { id name optionalString requiredString } }`, + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "QueryNullableFieldsTypeById", + Request: RPCMessage{ + Name: "QueryNullableFieldsTypeByIdRequest", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + }, + }, + Response: RPCMessage{ + Name: "QueryNullableFieldsTypeByIdResponse", + Fields: []RPCField{ + { + Name: "nullable_fields_type_by_id", + TypeName: string(DataTypeMessage), + JSONPath: "nullableFieldsTypeById", + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should create an execution plan for all nullable fields types query", + query: "query AllNullableFieldsTypesQuery { allNullableFieldsTypes { id name optionalString optionalInt requiredString requiredInt } }", + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "QueryAllNullableFieldsTypes", + Request: RPCMessage{ + Name: "QueryAllNullableFieldsTypesRequest", + }, + Response: RPCMessage{ + Name: "QueryAllNullableFieldsTypesResponse", + Fields: []RPCField{ + { + Name: "all_nullable_fields_types", + TypeName: string(DataTypeMessage), + JSONPath: "allNullableFieldsTypes", + Repeated: true, + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + { + Name: "required_int", + TypeName: string(DataTypeInt32), + JSONPath: "requiredInt", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should create an execution plan for create nullable fields type mutation", + query: `mutation CreateNullableFieldsType($input: NullableFieldsInput!) { createNullableFieldsType(input: $input) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "MutationCreateNullableFieldsType", + Request: RPCMessage{ + Name: "MutationCreateNullableFieldsTypeRequest", + Fields: []RPCField{ + { + Name: "input", + TypeName: string(DataTypeMessage), + JSONPath: "input", + Message: &RPCMessage{ + Name: "NullableFieldsInput", + Fields: []RPCField{ + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "optional_float", + TypeName: string(DataTypeDouble), + JSONPath: "optionalFloat", + Optional: true, + }, + { + Name: "optional_boolean", + TypeName: string(DataTypeBool), + JSONPath: "optionalBoolean", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + { + Name: "required_int", + TypeName: string(DataTypeInt32), + JSONPath: "requiredInt", + }, + }, + }, + }, + }, + }, + Response: RPCMessage{ + Name: "MutationCreateNullableFieldsTypeResponse", + Fields: []RPCField{ + { + Name: "create_nullable_fields_type", + TypeName: string(DataTypeMessage), + JSONPath: "createNullableFieldsType", + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "optional_float", + TypeName: string(DataTypeDouble), + JSONPath: "optionalFloat", + Optional: true, + }, + { + Name: "optional_boolean", + TypeName: string(DataTypeBool), + JSONPath: "optionalBoolean", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + { + Name: "required_int", + TypeName: string(DataTypeInt32), + JSONPath: "requiredInt", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should create an execution plan for update nullable fields type mutation", + query: `mutation UpdateNullableFieldsType($id: ID!, $input: NullableFieldsInput!) { updateNullableFieldsType(id: $id, input: $input) { id name optionalString requiredString } }`, + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "MutationUpdateNullableFieldsType", + Request: RPCMessage{ + Name: "MutationUpdateNullableFieldsTypeRequest", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "input", + TypeName: string(DataTypeMessage), + JSONPath: "input", + Message: &RPCMessage{ + Name: "NullableFieldsInput", + Fields: []RPCField{ + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "optional_float", + TypeName: string(DataTypeDouble), + JSONPath: "optionalFloat", + Optional: true, + }, + { + Name: "optional_boolean", + TypeName: string(DataTypeBool), + JSONPath: "optionalBoolean", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + { + Name: "required_int", + TypeName: string(DataTypeInt32), + JSONPath: "requiredInt", + }, + }, + }, + }, + }, + }, + Response: RPCMessage{ + Name: "MutationUpdateNullableFieldsTypeResponse", + Fields: []RPCField{ + { + Name: "update_nullable_fields_type", + TypeName: string(DataTypeMessage), + JSONPath: "updateNullableFieldsType", + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "name", + TypeName: string(DataTypeString), + JSONPath: "name", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "required_string", + TypeName: string(DataTypeString), + JSONPath: "requiredString", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should create an execution plan for nullable fields with partial field selection", + query: "query PartialNullableFieldsQuery { nullableFieldsType { id optionalString } }", + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "QueryNullableFieldsType", + Request: RPCMessage{ + Name: "QueryNullableFieldsTypeRequest", + }, + Response: RPCMessage{ + Name: "QueryNullableFieldsTypeResponse", + Fields: []RPCField{ + { + Name: "nullable_fields_type", + TypeName: string(DataTypeMessage), + JSONPath: "nullableFieldsType", + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "id", + TypeName: string(DataTypeString), + JSONPath: "id", + }, + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "Should create an execution plan for nullable fields with only optional fields", + query: "query OptionalFieldsOnlyQuery { nullableFieldsType { optionalString optionalInt optionalFloat optionalBoolean } }", + expectedPlan: &RPCExecutionPlan{ + Calls: []RPCCall{ + { + ServiceName: "Products", + MethodName: "QueryNullableFieldsType", + Request: RPCMessage{ + Name: "QueryNullableFieldsTypeRequest", + }, + Response: RPCMessage{ + Name: "QueryNullableFieldsTypeResponse", + Fields: []RPCField{ + { + Name: "nullable_fields_type", + TypeName: string(DataTypeMessage), + JSONPath: "nullableFieldsType", + Message: &RPCMessage{ + Name: "NullableFieldsType", + Fields: []RPCField{ + { + Name: "optional_string", + TypeName: string(DataTypeString), + JSONPath: "optionalString", + Optional: true, + }, + { + Name: "optional_int", + TypeName: string(DataTypeInt32), + JSONPath: "optionalInt", + Optional: true, + }, + { + Name: "optional_float", + TypeName: string(DataTypeDouble), + JSONPath: "optionalFloat", + Optional: true, + }, + { + Name: "optional_boolean", + TypeName: string(DataTypeBool), + JSONPath: "optionalBoolean", + Optional: true, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go index 29ae3af9bb..cb9c35e9d0 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go +++ b/v2/pkg/engine/datasource/grpc_datasource/execution_plan_visitor.go @@ -456,6 +456,7 @@ func (r *rpcPlanVisitor) enrichRequestMessageFromInputArgument(argRef, typeRef i TypeName: dt.String(), JSONPath: jsonPath, Repeated: r.definition.TypeIsList(typeRef), + Optional: r.isNullableScalar(typeRef), } if dt == DataTypeEnum { @@ -507,6 +508,7 @@ func (r *rpcPlanVisitor) buildMessageField(fieldName string, typeRef, parentType TypeName: dt.String(), JSONPath: fieldName, Repeated: r.definition.TypeIsList(typeRef), + Optional: r.isNullableScalar(typeRef), } if dt == DataTypeEnum { From bf1f7f1f1b6ece013c92df3991eb49ed8a449446 Mon Sep 17 00:00:00 2001 From: Ludwig Bedacht Date: Mon, 7 Jul 2025 12:42:24 +0200 Subject: [PATCH 3/5] chore: improve handling for optional types and add more tests --- .../engine/execution_engine_grpc_test.go | 12 + .../datasource/grpc_datasource/compiler.go | 21 +- .../grpc_datasource/execution_plan.go | 25 ++ .../grpc_datasource/grpc_datasource_test.go | 280 +++++++++++++++++- v2/pkg/grpctest/schema.go | 211 +++++++++++++ 5 files changed, 536 insertions(+), 13 deletions(-) diff --git a/execution/engine/execution_engine_grpc_test.go b/execution/engine/execution_engine_grpc_test.go index 15e0a26f75..244132518f 100644 --- a/execution/engine/execution_engine_grpc_test.go +++ b/execution/engine/execution_engine_grpc_test.go @@ -498,4 +498,16 @@ func TestGRPCSubgraphExecution(t *testing.T) { }) } }) + + t.Run("should handle nullable fields", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "NullableFieldsTypeQuery", + Query: `query NullableFieldsTypeQuery { nullableFieldsType { id optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"nullableFieldsType":{"id":"nullable-default","optionalString":"Default optional string","optionalInt":777,"optionalFloat":null,"optionalBoolean":true,"requiredString":"Default required string","requiredInt":999}}}`, response) + }) } diff --git a/v2/pkg/engine/datasource/grpc_datasource/compiler.go b/v2/pkg/engine/datasource/grpc_datasource/compiler.go index 17a88136d8..21afbf2406 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/compiler.go +++ b/v2/pkg/engine/datasource/grpc_datasource/compiler.go @@ -443,9 +443,26 @@ func (p *RPCCompiler) buildProtoMessage(inputMessage Message, rpcMessage *RPCMes // Handle nested message fields if field.MessageRef >= 0 { - fieldMsg := p.buildProtoMessage(p.doc.Messages[field.MessageRef], rpcField.Message, data.Get(rpcField.JSONPath)) - message.Set(inputMessage.Desc.Fields().ByName(protoref.Name(field.Name)), protoref.ValueOfMessage(fieldMsg)) + var fieldMsg *dynamicpb.Message + + // If the field is optional, we are handling a scalar value that is wrapped in a message + // as protobuf scalar types are not nullable. + if rpcField.Optional { + // If we don't have a value for an optional field, we skip it to provide a null message + if !data.Get(rpcField.JSONPath).Exists() { + continue + } + fieldMsg = p.buildProtoMessage( + p.doc.Messages[field.MessageRef], + rpcField.ToOptionalTypeMessage(p.doc.Messages[field.MessageRef].Name), + data, + ) + } else { + fieldMsg = p.buildProtoMessage(p.doc.Messages[field.MessageRef], rpcField.Message, data.Get(rpcField.JSONPath)) + } + + message.Set(inputMessage.Desc.Fields().ByName(protoref.Name(field.Name)), protoref.ValueOfMessage(fieldMsg)) continue } diff --git a/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go b/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go index 0b6915fa05..32916427b0 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go +++ b/v2/pkg/engine/datasource/grpc_datasource/execution_plan.go @@ -11,6 +11,9 @@ import ( const ( federationKeyDirectiveName = "key" + // knownTypeOptionalFieldValueName is the name of the field that is used to wrap optional scalar values + // in a message as protobuf scalar types are not nullable. + knownTypeOptionalFieldValueName = "value" ) // OneOfType represents the type of a oneof field in a protobuf message. @@ -170,6 +173,28 @@ type RPCField struct { Message *RPCMessage } +// ToOptionalTypeMessage returns a message that wraps the scalar value in a message +// as protobuf scalar types are not nullable. +func (r *RPCField) ToOptionalTypeMessage(protoName string) *RPCMessage { + if r == nil { + return nil + } + + return &RPCMessage{ + Name: protoName, + Fields: RPCFields{ + RPCField{ + Name: knownTypeOptionalFieldValueName, + JSONPath: r.JSONPath, + TypeName: r.TypeName, + Repeated: r.Repeated, + EnumName: r.EnumName, + }, + }, + } + +} + // AliasOrPath returns the alias of the field if it exists, otherwise it returns the JSONPath. func (r *RPCField) AliasOrPath() string { if r.Alias != "" { diff --git a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go index 0e3a6efce4..f1b37bdd12 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go +++ b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "math" "net" "strings" "testing" @@ -1549,27 +1550,284 @@ func Test_DataSource_Load_WithNullableFieldsType(t *testing.T) { validate func(t *testing.T, data map[string]interface{}) }{ { - name: "Query nullable fields type", + name: "Query nullable fields type with all fields", query: `query { nullableFieldsType { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, vars: "{}", validate: func(t *testing.T, data map[string]interface{}) { nullableFieldsType, ok := data["nullableFieldsType"].(map[string]interface{}) - require.True(t, ok, "NullableFieldsType should be an object") - require.NotEmpty(t, nullableFieldsType["id"], "ID should not be empty") - require.NotEmpty(t, nullableFieldsType["name"], "Name should not be empty") - require.NotEmpty(t, nullableFieldsType["optionalString"], "OptionalString should not be empty") - require.NotEmpty(t, nullableFieldsType["optionalInt"], "OptionalInt should not be empty") - require.NotEmpty(t, nullableFieldsType["optionalBoolean"], "OptionalBoolean should not be empty") - require.NotEmpty(t, nullableFieldsType["requiredString"], "RequiredString should not be empty") - require.NotEmpty(t, nullableFieldsType["requiredInt"], "RequiredInt should not be empty") - require.Empty(t, nullableFieldsType["optionalFloat"], "OptionalFloat should be empty") + require.True(t, ok, "nullableFieldsType should be an object") + require.NotEmpty(t, nullableFieldsType, "nullableFieldsType should not be empty") + + // Check required fields are present + require.Contains(t, nullableFieldsType, "id") + require.Contains(t, nullableFieldsType, "name") + require.Contains(t, nullableFieldsType, "requiredString") + require.Contains(t, nullableFieldsType, "requiredInt") + + require.NotEmpty(t, nullableFieldsType["id"], "id should not be empty") + require.NotEmpty(t, nullableFieldsType["name"], "name should not be empty") + require.NotEmpty(t, nullableFieldsType["requiredString"], "requiredString should not be empty") + require.NotEmpty(t, nullableFieldsType["requiredInt"], "requiredInt should not be empty") + + // Check optional fields are present (but may be null) + require.Contains(t, nullableFieldsType, "optionalString") + require.Contains(t, nullableFieldsType, "optionalInt") + require.Contains(t, nullableFieldsType, "optionalFloat") + require.Contains(t, nullableFieldsType, "optionalBoolean") + + // Verify types of non-null optional fields + if nullableFieldsType["optionalString"] != nil { + require.IsType(t, "", nullableFieldsType["optionalString"]) + } + if nullableFieldsType["optionalInt"] != nil { + require.IsType(t, float64(0), nullableFieldsType["optionalInt"]) // JSON numbers are float64 + } + if nullableFieldsType["optionalFloat"] != nil { + require.IsType(t, float64(0), nullableFieldsType["optionalFloat"]) + } + if nullableFieldsType["optionalBoolean"] != nil { + require.IsType(t, false, nullableFieldsType["optionalBoolean"]) + } + }, + }, + { + name: "Query nullable fields type by ID", + query: `query($id: ID!) { nullableFieldsTypeById(id: $id) { id name optionalString requiredString } }`, + vars: `{"variables":{"id":"full-data"}}`, + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType, ok := data["nullableFieldsTypeById"].(map[string]interface{}) + require.True(t, ok, "nullableFieldsTypeById should be an object") + require.NotEmpty(t, nullableFieldsType, "nullableFieldsTypeById should not be empty") + + require.Equal(t, "full-data", nullableFieldsType["id"]) + require.Equal(t, "Full Data by ID", nullableFieldsType["name"]) + require.Equal(t, "All fields populated", nullableFieldsType["optionalString"]) + require.Equal(t, "Required by ID", nullableFieldsType["requiredString"]) + }, + }, + { + name: "Query nullable fields type by ID with partial data", + query: `query($id: ID!) { nullableFieldsTypeById(id: $id) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: `{"variables":{"id":"partial-data"}}`, + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType, ok := data["nullableFieldsTypeById"].(map[string]interface{}) + require.True(t, ok, "nullableFieldsTypeById should be an object") + require.NotEmpty(t, nullableFieldsType, "nullableFieldsTypeById should not be empty") + + require.Equal(t, "partial-data", nullableFieldsType["id"]) + require.Equal(t, "Partial Data by ID", nullableFieldsType["name"]) + require.Nil(t, nullableFieldsType["optionalString"], "optionalString should be null") + require.NotNil(t, nullableFieldsType["optionalInt"], "optionalInt should not be null") + require.Nil(t, nullableFieldsType["optionalFloat"], "optionalFloat should be null") + require.NotNil(t, nullableFieldsType["optionalBoolean"], "optionalBoolean should not be null") + require.Equal(t, "Partial required by ID", nullableFieldsType["requiredString"]) + require.Equal(t, float64(321), nullableFieldsType["requiredInt"]) // JSON numbers are float64 + }, + }, + { + name: "Query nullable fields type by ID with minimal data", + query: `query($id: ID!) { nullableFieldsTypeById(id: $id) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: `{"variables":{"id":"minimal-data"}}`, + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType, ok := data["nullableFieldsTypeById"].(map[string]interface{}) + require.True(t, ok, "nullableFieldsTypeById should be an object") + require.NotEmpty(t, nullableFieldsType, "nullableFieldsTypeById should not be empty") + + require.Equal(t, "minimal-data", nullableFieldsType["id"]) + require.Equal(t, "Minimal Data by ID", nullableFieldsType["name"]) + require.Nil(t, nullableFieldsType["optionalString"], "optionalString should be null") + require.Nil(t, nullableFieldsType["optionalInt"], "optionalInt should be null") + require.Nil(t, nullableFieldsType["optionalFloat"], "optionalFloat should be null") + require.Nil(t, nullableFieldsType["optionalBoolean"], "optionalBoolean should be null") + require.Equal(t, "Only required fields", nullableFieldsType["requiredString"]) + require.Equal(t, float64(111), nullableFieldsType["requiredInt"]) // JSON numbers are float64 + }, + }, + { + name: "Query nullable fields type by ID - not found", + query: `query($id: ID!) { nullableFieldsTypeById(id: $id) { id name optionalString requiredString } }`, + vars: `{"variables":{"id":"not-found"}}`, + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType := data["nullableFieldsTypeById"] + require.Nil(t, nullableFieldsType, "nullableFieldsTypeById should be null for not-found ID") + }, + }, + { + name: "Query all nullable fields types", + query: `query { allNullableFieldsTypes { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: "{}", + validate: func(t *testing.T, data map[string]interface{}) { + allNullableFieldsTypes, ok := data["allNullableFieldsTypes"].([]interface{}) + require.True(t, ok, "allNullableFieldsTypes should be an array") + require.Len(t, allNullableFieldsTypes, 3, "should return 3 nullable field types") + + // Check first entry (full data) + firstEntry := allNullableFieldsTypes[0].(map[string]interface{}) + require.Equal(t, "nullable-1", firstEntry["id"]) + require.Equal(t, "Full Data Entry", firstEntry["name"]) + require.Equal(t, "Optional String Value", firstEntry["optionalString"]) + require.Equal(t, float64(42), firstEntry["optionalInt"]) + require.Equal(t, float64(3.14), math.Round(firstEntry["optionalFloat"].(float64)*100)/100) // round to 2 decimal places + require.Equal(t, true, firstEntry["optionalBoolean"]) + require.Equal(t, "Required String 1", firstEntry["requiredString"]) + require.Equal(t, float64(100), firstEntry["requiredInt"]) + + // Check second entry (partial data) + secondEntry := allNullableFieldsTypes[1].(map[string]interface{}) + require.Equal(t, "nullable-2", secondEntry["id"]) + require.Equal(t, "Partial Data Entry", secondEntry["name"]) + require.Equal(t, "Only string is set", secondEntry["optionalString"]) + require.Nil(t, secondEntry["optionalInt"], "optionalInt should be null") + require.Nil(t, secondEntry["optionalFloat"], "optionalFloat should be null") + require.Equal(t, false, secondEntry["optionalBoolean"]) + require.Equal(t, "Required String 2", secondEntry["requiredString"]) + require.Equal(t, float64(200), secondEntry["requiredInt"]) + + // Check third entry (minimal data) + thirdEntry := allNullableFieldsTypes[2].(map[string]interface{}) + require.Equal(t, "nullable-3", thirdEntry["id"]) + require.Equal(t, "Minimal Data Entry", thirdEntry["name"]) + require.Nil(t, thirdEntry["optionalString"], "optionalString should be null") + require.Nil(t, thirdEntry["optionalInt"], "optionalInt should be null") + require.Nil(t, thirdEntry["optionalFloat"], "optionalFloat should be null") + require.Nil(t, thirdEntry["optionalBoolean"], "optionalBoolean should be null") + require.Equal(t, "Required String 3", thirdEntry["requiredString"]) + require.Equal(t, float64(300), thirdEntry["requiredInt"]) + }, + }, + { + name: "Query nullable fields type with filter", + query: `query($filter: NullableFieldsFilter!) { nullableFieldsTypeWithFilter(filter: $filter) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: `{"variables":{"filter":{"name":"TestFilter","optionalString":"FilteredString","includeNulls":true}}}`, + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsTypes, ok := data["nullableFieldsTypeWithFilter"].([]interface{}) + require.True(t, ok, "nullableFieldsTypeWithFilter should be an array") + require.Len(t, nullableFieldsTypes, 3, "should return 3 filtered nullable field types") + + for i, item := range nullableFieldsTypes { + entry := item.(map[string]interface{}) + require.Equal(t, fmt.Sprintf("filtered-%d", i+1), entry["id"]) + require.Equal(t, fmt.Sprintf("TestFilter - %d", i+1), entry["name"]) + require.Equal(t, "FilteredString", entry["optionalString"]) + require.Equal(t, fmt.Sprintf("Required filtered %d", i+1), entry["requiredString"]) + require.Equal(t, float64((i+1)*1000), entry["requiredInt"]) + } + }, + }, + { + name: "Create nullable fields type mutation", + query: `mutation($input: NullableFieldsInput!) { createNullableFieldsType(input: $input) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: `{"variables":{"input":{"name":"Created Type","optionalString":"Optional Value","optionalInt":42,"optionalFloat":3.14,"optionalBoolean":true,"requiredString":"Required Value","requiredInt":100}}}`, + validate: func(t *testing.T, data map[string]interface{}) { + createdType, ok := data["createNullableFieldsType"].(map[string]interface{}) + require.True(t, ok, "createNullableFieldsType should be an object") + require.NotEmpty(t, createdType, "createNullableFieldsType should not be empty") + + require.Contains(t, createdType["id"], "nullable-") // ID should start with "nullable-" + require.Equal(t, "Created Type", createdType["name"]) + require.Equal(t, "Optional Value", createdType["optionalString"]) + require.Equal(t, float64(42), createdType["optionalInt"]) + require.Equal(t, float64(3.14), math.Round(createdType["optionalFloat"].(float64)*100)/100) // round to 2 decimal places + require.Equal(t, true, createdType["optionalBoolean"]) + require.Equal(t, "Required Value", createdType["requiredString"]) + require.Equal(t, float64(100), createdType["requiredInt"]) + }, + }, + { + name: "Create nullable fields type mutation with minimal input", + query: `mutation($input: NullableFieldsInput!) { createNullableFieldsType(input: $input) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: `{"variables":{"input":{"name":"Minimal Type","requiredString":"Only Required","requiredInt":200}}}`, + validate: func(t *testing.T, data map[string]interface{}) { + createdType, ok := data["createNullableFieldsType"].(map[string]interface{}) + require.True(t, ok, "createNullableFieldsType should be an object") + require.NotEmpty(t, createdType, "createNullableFieldsType should not be empty") + + require.Contains(t, createdType["id"], "nullable-") // ID should start with "nullable-" + require.Equal(t, "Minimal Type", createdType["name"]) + require.Nil(t, createdType["optionalString"], "optionalString should be null") + require.Nil(t, createdType["optionalInt"], "optionalInt should be null") + require.Nil(t, createdType["optionalFloat"], "optionalFloat should be null") + require.Nil(t, createdType["optionalBoolean"], "optionalBoolean should be null") + require.Equal(t, "Only Required", createdType["requiredString"]) + require.Equal(t, float64(200), createdType["requiredInt"]) + }, + }, + { + name: "Update nullable fields type mutation", + query: `mutation($id: ID!, $input: NullableFieldsInput!) { updateNullableFieldsType(id: $id, input: $input) { id name optionalString optionalInt optionalFloat optionalBoolean requiredString requiredInt } }`, + vars: `{"variables":{"id":"test-update","input":{"name":"Updated Type","optionalString":"Updated Optional","optionalInt":999,"requiredString":"Updated Required","requiredInt":500}}}`, + validate: func(t *testing.T, data map[string]interface{}) { + updatedType, ok := data["updateNullableFieldsType"].(map[string]interface{}) + require.True(t, ok, "updateNullableFieldsType should be an object") + require.NotEmpty(t, updatedType, "updateNullableFieldsType should not be empty") + + require.Equal(t, "test-update", updatedType["id"]) + require.Equal(t, "Updated Type", updatedType["name"]) + require.Equal(t, "Updated Optional", updatedType["optionalString"]) + require.Equal(t, float64(999), updatedType["optionalInt"]) + require.Nil(t, updatedType["optionalFloat"], "optionalFloat should be null") + require.Nil(t, updatedType["optionalBoolean"], "optionalBoolean should be null") + require.Equal(t, "Updated Required", updatedType["requiredString"]) + require.Equal(t, float64(500), updatedType["requiredInt"]) + }, + }, + { + name: "Update nullable fields type mutation - non-existent ID", + query: `mutation($id: ID!, $input: NullableFieldsInput!) { updateNullableFieldsType(id: $id, input: $input) { id name optionalString requiredString } }`, + vars: `{"variables":{"id":"non-existent","input":{"name":"Should Not Exist","requiredString":"Not Created","requiredInt":0}}}`, + validate: func(t *testing.T, data map[string]interface{}) { + updatedType := data["updateNullableFieldsType"] + require.Nil(t, updatedType, "updateNullableFieldsType should be null for non-existent ID") + }, + }, + { + name: "Query nullable fields with only optional fields", + query: `query { nullableFieldsType { optionalString optionalInt optionalFloat optionalBoolean } }`, + vars: "{}", + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType, ok := data["nullableFieldsType"].(map[string]interface{}) + require.True(t, ok, "nullableFieldsType should be an object") + require.NotEmpty(t, nullableFieldsType, "nullableFieldsType should not be empty") + + // Should only contain the requested optional fields + require.Contains(t, nullableFieldsType, "optionalString") + require.Contains(t, nullableFieldsType, "optionalInt") + require.Contains(t, nullableFieldsType, "optionalFloat") + require.Contains(t, nullableFieldsType, "optionalBoolean") + + // Should not contain other fields + require.NotContains(t, nullableFieldsType, "id") + require.NotContains(t, nullableFieldsType, "name") + require.NotContains(t, nullableFieldsType, "requiredString") + require.NotContains(t, nullableFieldsType, "requiredInt") + }, + }, + { + name: "Query nullable fields with partial selection", + query: `query { nullableFieldsType { id name optionalString requiredString } }`, + vars: "{}", + validate: func(t *testing.T, data map[string]interface{}) { + nullableFieldsType, ok := data["nullableFieldsType"].(map[string]interface{}) + require.True(t, ok, "nullableFieldsType should be an object") + require.NotEmpty(t, nullableFieldsType, "nullableFieldsType should not be empty") + + // Should contain the requested fields + require.Contains(t, nullableFieldsType, "id") + require.Contains(t, nullableFieldsType, "name") + require.Contains(t, nullableFieldsType, "optionalString") + require.Contains(t, nullableFieldsType, "requiredString") + + // Should not contain other fields + require.NotContains(t, nullableFieldsType, "optionalInt") + require.NotContains(t, nullableFieldsType, "optionalFloat") + require.NotContains(t, nullableFieldsType, "optionalBoolean") + require.NotContains(t, nullableFieldsType, "requiredInt") }, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - // Parse the GraphQL schema schemaDoc := grpctest.MustGraphQLSchema(t) diff --git a/v2/pkg/grpctest/schema.go b/v2/pkg/grpctest/schema.go index 0e2a9bf643..fc0248b645 100644 --- a/v2/pkg/grpctest/schema.go +++ b/v2/pkg/grpctest/schema.go @@ -196,6 +196,70 @@ func GetFieldConfigurations() plan.FieldConfigurations { }, }, }, + { + TypeName: "Query", + FieldName: "search", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "input", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Query", + FieldName: "nullableFieldsTypeById", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "id", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Query", + FieldName: "nullableFieldsTypeWithFilter", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "filter", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Mutation", + FieldName: "performAction", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "input", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Mutation", + FieldName: "createNullableFieldsType", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "input", + SourceType: plan.FieldArgumentSource, + }, + }, + }, + { + TypeName: "Mutation", + FieldName: "updateNullableFieldsType", + Arguments: []plan.ArgumentConfiguration{ + { + Name: "id", + SourceType: plan.FieldArgumentSource, + }, + { + Name: "input", + SourceType: plan.FieldArgumentSource, + }, + }, + }, } } @@ -230,15 +294,26 @@ func GetDataSourceMetadata() *plan.DataSourceMetadata { "complexFilterType", "categories", "categoriesByKind", + "categoriesByKinds", "filterCategories", "randomPet", "allPets", + "search", + "calculateTotals", + "randomSearchResult", + "nullableFieldsType", + "nullableFieldsTypeById", + "nullableFieldsTypeWithFilter", + "allNullableFieldsTypes", }, }, { TypeName: "Mutation", FieldNames: []string{ "createUser", + "performAction", + "createNullableFieldsType", + "updateNullableFieldsType", }, }, }, @@ -370,6 +445,142 @@ func GetDataSourceMetadata() *plan.DataSourceMetadata { "modifiers", }, }, + { + TypeName: "ActionInput", + FieldNames: []string{ + "name", + }, + }, + { + TypeName: "Product", + FieldNames: []string{ + "id", + "name", + "price", + }, + }, + { + TypeName: "Storage", + FieldNames: []string{ + "id", + "name", + "location", + }, + }, + { + TypeName: "FilterTypeInput", + FieldNames: []string{ + "filterField1", + "filterField2", + }, + }, + { + TypeName: "FilterType", + FieldNames: []string{ + "name", + "filterField1", + "filterField2", + "pagination", + }, + }, + { + TypeName: "Pagination", + FieldNames: []string{ + "page", + "perPage", + }, + }, + { + TypeName: "ComplexFilterTypeInput", + FieldNames: []string{ + "filter", + }, + }, + { + TypeName: "OrderLineInput", + FieldNames: []string{ + "productId", + "quantity", + "modifiers", + }, + }, + { + TypeName: "OrderInput", + FieldNames: []string{ + "orderId", + "customerName", + "lines", + }, + }, + { + TypeName: "ActionSuccess", + FieldNames: []string{ + "message", + "timestamp", + }, + }, + { + TypeName: "ActionError", + FieldNames: []string{ + "message", + "code", + }, + }, + { + TypeName: "SearchInput", + FieldNames: []string{ + "query", + "limit", + }, + }, + { + TypeName: "SearchResult", + FieldNames: []string{ + "product", + "user", + "category", + }, + }, + { + TypeName: "ActionResult", + FieldNames: []string{ + "actionSuccess", + "actionError", + }, + }, + { + TypeName: "NullableFieldsType", + FieldNames: []string{ + "id", + "name", + "optionalString", + "optionalInt", + "optionalFloat", + "optionalBoolean", + "requiredString", + "requiredInt", + }, + }, + { + TypeName: "NullableFieldsInput", + FieldNames: []string{ + "name", + "optionalString", + "optionalInt", + "optionalFloat", + "optionalBoolean", + "requiredString", + "requiredInt", + }, + }, + { + TypeName: "NullableFieldsFilter", + FieldNames: []string{ + "name", + "optionalString", + "includeNulls", + }, + }, }, } } From 37321d70c28ff38b4f868b57505f487a7628f8e5 Mon Sep 17 00:00:00 2001 From: Ludwig Bedacht Date: Mon, 7 Jul 2025 12:58:39 +0200 Subject: [PATCH 4/5] chore: add more tests --- .../engine/execution_engine_grpc_test.go | 299 ++++++++++++++++++ .../datasource/grpc_datasource/compiler.go | 3 +- 2 files changed, 301 insertions(+), 1 deletion(-) diff --git a/execution/engine/execution_engine_grpc_test.go b/execution/engine/execution_engine_grpc_test.go index 244132518f..0799edaaf6 100644 --- a/execution/engine/execution_engine_grpc_test.go +++ b/execution/engine/execution_engine_grpc_test.go @@ -510,4 +510,303 @@ func TestGRPCSubgraphExecution(t *testing.T) { require.NoError(t, err) require.Equal(t, `{"data":{"nullableFieldsType":{"id":"nullable-default","optionalString":"Default optional string","optionalInt":777,"optionalFloat":null,"optionalBoolean":true,"requiredString":"Default required string","requiredInt":999}}}`, response) }) + + t.Run("should handle nullable fields query by ID with full data", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "NullableFieldsTypeByIdQuery", + Variables: stringify(map[string]any{ + "id": "full-data", + }), + Query: `query NullableFieldsTypeByIdQuery($id: ID!) { + nullableFieldsTypeById(id: $id) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"nullableFieldsTypeById":{"id":"full-data","name":"Full Data by ID","optionalString":"All fields populated","optionalInt":123,"optionalFloat":12.34000015258789,"optionalBoolean":false,"requiredString":"Required by ID","requiredInt":456}}}`, response) + }) + + t.Run("should handle nullable fields query by ID with partial data", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "NullableFieldsTypeByIdQuery", + Variables: stringify(map[string]any{ + "id": "partial-data", + }), + Query: `query NullableFieldsTypeByIdQuery($id: ID!) { + nullableFieldsTypeById(id: $id) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"nullableFieldsTypeById":{"id":"partial-data","name":"Partial Data by ID","optionalString":null,"optionalInt":789,"optionalFloat":null,"optionalBoolean":true,"requiredString":"Partial required by ID","requiredInt":321}}}`, response) + }) + + t.Run("should handle nullable fields query by ID with minimal data", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "NullableFieldsTypeByIdQuery", + Variables: stringify(map[string]any{ + "id": "minimal-data", + }), + Query: `query NullableFieldsTypeByIdQuery($id: ID!) { + nullableFieldsTypeById(id: $id) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"nullableFieldsTypeById":{"id":"minimal-data","name":"Minimal Data by ID","optionalString":null,"optionalInt":null,"optionalFloat":null,"optionalBoolean":null,"requiredString":"Only required fields","requiredInt":111}}}`, response) + }) + + t.Run("should handle nullable fields query by ID returning null for not found", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "NullableFieldsTypeByIdQuery", + Variables: stringify(map[string]any{ + "id": "not-found", + }), + Query: `query NullableFieldsTypeByIdQuery($id: ID!) { + nullableFieldsTypeById(id: $id) { + id + name + optionalString + requiredString + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"nullableFieldsTypeById":null}}`, response) + }) + + t.Run("should handle query for all nullable fields types", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "AllNullableFieldsTypesQuery", + Query: `query AllNullableFieldsTypesQuery { + allNullableFieldsTypes { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"allNullableFieldsTypes":[{"id":"nullable-1","name":"Full Data Entry","optionalString":"Optional String Value","optionalInt":42,"optionalFloat":3.140000104904175,"optionalBoolean":true,"requiredString":"Required String 1","requiredInt":100},{"id":"nullable-2","name":"Partial Data Entry","optionalString":"Only string is set","optionalInt":null,"optionalFloat":null,"optionalBoolean":false,"requiredString":"Required String 2","requiredInt":200},{"id":"nullable-3","name":"Minimal Data Entry","optionalString":null,"optionalInt":null,"optionalFloat":null,"optionalBoolean":null,"requiredString":"Required String 3","requiredInt":300}]}}`, response) + }) + + t.Run("should handle nullable fields query with filter", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "NullableFieldsTypeWithFilterQuery", + Variables: stringify(map[string]any{ + "filter": map[string]any{ + "name": "TestFilter", + "optionalString": "FilteredString", + "includeNulls": true, + }, + }), + Query: `query NullableFieldsTypeWithFilterQuery($filter: NullableFieldsFilter!) { + nullableFieldsTypeWithFilter(filter: $filter) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Contains(t, response, `"id":"filtered-1"`) + require.Contains(t, response, `"name":"TestFilter - 1"`) + require.Contains(t, response, `"optionalString":"FilteredString"`) + require.Contains(t, response, `"requiredString":"Required filtered 1"`) + require.Contains(t, response, `"requiredInt":1000`) + require.Contains(t, response, `"id":"filtered-2"`) + require.Contains(t, response, `"id":"filtered-3"`) + }) + + t.Run("should handle create nullable fields type mutation", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "CreateNullableFieldsTypeMutation", + Variables: stringify(map[string]any{ + "input": map[string]any{ + "name": "Created Type", + "optionalString": "Optional Value", + "optionalInt": 42, + "optionalFloat": 3.14, + "optionalBoolean": true, + "requiredString": "Required Value", + "requiredInt": 100, + }, + }), + Query: `mutation CreateNullableFieldsTypeMutation($input: NullableFieldsInput!) { + createNullableFieldsType(input: $input) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Contains(t, response, `"name":"Created Type"`) + require.Contains(t, response, `"optionalString":"Optional Value"`) + require.Contains(t, response, `"optionalInt":42`) + require.Contains(t, response, `"optionalFloat":3.14`) + require.Contains(t, response, `"optionalBoolean":true`) + require.Contains(t, response, `"requiredString":"Required Value"`) + require.Contains(t, response, `"requiredInt":100`) + // Verify ID contains "nullable-" prefix + require.Contains(t, response, `"id":"nullable-`) + }) + + t.Run("should handle create nullable fields type mutation with minimal input", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "CreateNullableFieldsTypeMutation", + Variables: stringify(map[string]any{ + "input": map[string]any{ + "name": "Minimal Type", + "requiredString": "Only Required", + "requiredInt": 200, + }, + }), + Query: `mutation CreateNullableFieldsTypeMutation($input: NullableFieldsInput!) { + createNullableFieldsType(input: $input) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Contains(t, response, `"name":"Minimal Type"`) + require.Contains(t, response, `"optionalString":null`) + require.Contains(t, response, `"optionalInt":null`) + require.Contains(t, response, `"optionalFloat":null`) + require.Contains(t, response, `"optionalBoolean":null`) + require.Contains(t, response, `"requiredString":"Only Required"`) + require.Contains(t, response, `"requiredInt":200`) + // Verify ID contains "nullable-" prefix + require.Contains(t, response, `"id":"nullable-`) + }) + + t.Run("should handle update nullable fields type mutation", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "UpdateNullableFieldsTypeMutation", + Variables: stringify(map[string]any{ + "id": "test-update", + "input": map[string]any{ + "name": "Updated Type", + "optionalString": "Updated Optional", + "optionalInt": 999, + "requiredString": "Updated Required", + "requiredInt": 500, + }, + }), + Query: `mutation UpdateNullableFieldsTypeMutation($id: ID!, $input: NullableFieldsInput!) { + updateNullableFieldsType(id: $id, input: $input) { + id + name + optionalString + optionalInt + optionalFloat + optionalBoolean + requiredString + requiredInt + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"updateNullableFieldsType":{"id":"test-update","name":"Updated Type","optionalString":"Updated Optional","optionalInt":999,"optionalFloat":null,"optionalBoolean":null,"requiredString":"Updated Required","requiredInt":500}}}`, response) + }) + + t.Run("should handle update nullable fields type mutation returning null for non-existent ID", func(t *testing.T) { + operation := graphql.Request{ + OperationName: "UpdateNullableFieldsTypeMutation", + Variables: stringify(map[string]any{ + "id": "non-existent", + "input": map[string]any{ + "name": "Should Not Exist", + "requiredString": "Not Created", + "requiredInt": 0, + }, + }), + Query: `mutation UpdateNullableFieldsTypeMutation($id: ID!, $input: NullableFieldsInput!) { + updateNullableFieldsType(id: $id, input: $input) { + id + name + optionalString + requiredString + } + }`, + } + + response, err := executeOperation(t, conn, operation, withGRPCMapping(mapping.DefaultGRPCMapping())) + + require.NoError(t, err) + require.Equal(t, `{"data":{"updateNullableFieldsType":null}}`, response) + }) } diff --git a/v2/pkg/engine/datasource/grpc_datasource/compiler.go b/v2/pkg/engine/datasource/grpc_datasource/compiler.go index 21afbf2406..5726126399 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/compiler.go +++ b/v2/pkg/engine/datasource/grpc_datasource/compiler.go @@ -448,11 +448,12 @@ func (p *RPCCompiler) buildProtoMessage(inputMessage Message, rpcMessage *RPCMes // If the field is optional, we are handling a scalar value that is wrapped in a message // as protobuf scalar types are not nullable. if rpcField.Optional { - // If we don't have a value for an optional field, we skip it to provide a null message + // If we don't have a value for an optional field, we skip it to provide a null message. if !data.Get(rpcField.JSONPath).Exists() { continue } + // As those optional messages are well known wrapper types, we can convert them to the underlying message definition. fieldMsg = p.buildProtoMessage( p.doc.Messages[field.MessageRef], rpcField.ToOptionalTypeMessage(p.doc.Messages[field.MessageRef].Name), From e50b1e4fed3cf74b9c2a1e5c4781d723c94368df Mon Sep 17 00:00:00 2001 From: Ludwig Bedacht Date: Mon, 7 Jul 2025 14:45:18 +0200 Subject: [PATCH 5/5] chore: use InDelta --- .../datasource/grpc_datasource/grpc_datasource_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go index f1b37bdd12..3945d1f49b 100644 --- a/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go +++ b/v2/pkg/engine/datasource/grpc_datasource/grpc_datasource_test.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "fmt" - "math" "net" "strings" "testing" @@ -1667,7 +1666,7 @@ func Test_DataSource_Load_WithNullableFieldsType(t *testing.T) { require.Equal(t, "Full Data Entry", firstEntry["name"]) require.Equal(t, "Optional String Value", firstEntry["optionalString"]) require.Equal(t, float64(42), firstEntry["optionalInt"]) - require.Equal(t, float64(3.14), math.Round(firstEntry["optionalFloat"].(float64)*100)/100) // round to 2 decimal places + require.InDelta(t, float64(3.14), firstEntry["optionalFloat"], 0.01) require.Equal(t, true, firstEntry["optionalBoolean"]) require.Equal(t, "Required String 1", firstEntry["requiredString"]) require.Equal(t, float64(100), firstEntry["requiredInt"]) @@ -1727,7 +1726,7 @@ func Test_DataSource_Load_WithNullableFieldsType(t *testing.T) { require.Equal(t, "Created Type", createdType["name"]) require.Equal(t, "Optional Value", createdType["optionalString"]) require.Equal(t, float64(42), createdType["optionalInt"]) - require.Equal(t, float64(3.14), math.Round(createdType["optionalFloat"].(float64)*100)/100) // round to 2 decimal places + require.InDelta(t, float64(3.14), createdType["optionalFloat"], 0.01) require.Equal(t, true, createdType["optionalBoolean"]) require.Equal(t, "Required Value", createdType["requiredString"]) require.Equal(t, float64(100), createdType["requiredInt"])