diff --git a/_examples/scalars/.gqlgen.yml b/_examples/scalars/.gqlgen.yml index d649467226..5dd5adfe53 100644 --- a/_examples/scalars/.gqlgen.yml +++ b/_examples/scalars/.gqlgen.yml @@ -22,3 +22,7 @@ models: model: "github.com/99designs/gqlgen/codegen/testserver/singlefile.Bytes" Runes: model: "github.com/99designs/gqlgen/_examples/scalars/model.Runes" + DefinedTypeBytes: + model: "github.com/99designs/gqlgen/_examples/scalars/external.Bytes" + DefinedTypeRunes: + model: "github.com/99designs/gqlgen/_examples/scalars/external.Runes" diff --git a/_examples/scalars/external/model.go b/_examples/scalars/external/model.go index 71e2a5c868..db108a569e 100644 --- a/_examples/scalars/external/model.go +++ b/_examples/scalars/external/model.go @@ -1,9 +1,18 @@ package external +import ( + "fmt" + "io" + + "github.com/99designs/gqlgen/graphql" +) + type ( - ObjectID int - Manufacturer string // remote named string - Count uint8 // remote named uint8 + ObjectID int + Manufacturer string // remote named string + Count uint8 // remote named uint8 + ExternalBytes []byte + ExternalRunes []rune ) const ( @@ -11,3 +20,41 @@ const ( ManufacturerHonda Manufacturer = "HONDA" ManufacturerToyota Manufacturer = "TOYOTA" ) + +func MarshalBytes(b ExternalBytes) graphql.Marshaler { + return graphql.WriterFunc(func(w io.Writer) { + _, _ = fmt.Fprintf(w, "%q", string(b)) + }) +} + +func UnmarshalBytes(v interface{}) (ExternalBytes, error) { + switch v := v.(type) { + case string: + return ExternalBytes(v), nil + case *string: + return ExternalBytes(*v), nil + case ExternalBytes: + return v, nil + default: + return nil, fmt.Errorf("%T is not ExternalBytes", v) + } +} + +func MarshalRunes(r ExternalRunes) graphql.Marshaler { + return graphql.WriterFunc(func(w io.Writer) { + _, _ = fmt.Fprintf(w, "%q", string(r)) + }) +} + +func UnmarshalRunes(v interface{}) (ExternalRunes, error) { + switch v := v.(type) { + case string: + return ExternalRunes(v), nil + case *string: + return ExternalRunes(*v), nil + case ExternalRunes: + return v, nil + default: + return nil, fmt.Errorf("%T is not ExternalRunes", v) + } +} diff --git a/_examples/scalars/generated.go b/_examples/scalars/generated.go index a9238fd4c4..ff09154930 100644 --- a/_examples/scalars/generated.go +++ b/_examples/scalars/generated.go @@ -74,6 +74,8 @@ type ComplexityRoot struct { Name func(childComplexity int) int PrimitiveResolver func(childComplexity int) int PtrPrefs func(childComplexity int) int + RemoteBytes func(childComplexity int) int + RemoteRunes func(childComplexity int) int SomeBytes func(childComplexity int) int SomeOtherBytes func(childComplexity int) int SomeRunes func(childComplexity int) int @@ -256,6 +258,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.PtrPrefs(childComplexity), true + case "User.remoteBytes": + if e.complexity.User.RemoteBytes == nil { + break + } + + return e.complexity.User.RemoteBytes(childComplexity), true + + case "User.remoteRunes": + if e.complexity.User.RemoteRunes == nil { + break + } + + return e.complexity.User.RemoteRunes(childComplexity), true + case "User.someBytes": if e.complexity.User.SomeBytes == nil { break @@ -638,6 +654,10 @@ func (ec *executionContext) fieldContext_Query_user(ctx context.Context, field g return ec.fieldContext_User_someOtherBytes(ctx, field) case "someRunes": return ec.fieldContext_User_someRunes(ctx, field) + case "remoteBytes": + return ec.fieldContext_User_remoteBytes(ctx, field) + case "remoteRunes": + return ec.fieldContext_User_remoteRunes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -734,6 +754,10 @@ func (ec *executionContext) fieldContext_Query_search(ctx context.Context, field return ec.fieldContext_User_someOtherBytes(ctx, field) case "someRunes": return ec.fieldContext_User_someRunes(ctx, field) + case "remoteBytes": + return ec.fieldContext_User_remoteBytes(ctx, field) + case "remoteRunes": + return ec.fieldContext_User_remoteRunes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -830,6 +854,10 @@ func (ec *executionContext) fieldContext_Query_userByTier(ctx context.Context, f return ec.fieldContext_User_someOtherBytes(ctx, field) case "someRunes": return ec.fieldContext_User_someRunes(ctx, field) + case "remoteBytes": + return ec.fieldContext_User_remoteBytes(ctx, field) + case "remoteRunes": + return ec.fieldContext_User_remoteRunes(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1843,6 +1871,94 @@ func (ec *executionContext) fieldContext_User_someRunes(ctx context.Context, fie return fc, nil } +func (ec *executionContext) _User_remoteBytes(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_remoteBytes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.RemoteBytes, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(external.ExternalBytes) + fc.Result = res + return ec.marshalNDefinedTypeBytes2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐExternalBytes(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_remoteBytes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type DefinedTypeBytes does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _User_remoteRunes(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_remoteRunes(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.RemoteRunes, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(external.ExternalRunes) + fc.Result = res + return ec.marshalNDefinedTypeRunes2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐExternalRunes(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_remoteRunes(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type DefinedTypeRunes does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Directive_name(ctx, field) if err != nil { @@ -3953,6 +4069,20 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj out.Values[i] = ec._User_someRunes(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "remoteBytes": + + out.Values[i] = ec._User_remoteBytes(ctx, field, obj) + + if out.Values[i] == graphql.Null { + atomic.AddUint32(&invalids, 1) + } + case "remoteRunes": + + out.Values[i] = ec._User_remoteRunes(ctx, field, obj) + if out.Values[i] == graphql.Null { atomic.AddUint32(&invalids, 1) } @@ -4367,6 +4497,48 @@ func (ec *executionContext) marshalNDarkMode2ᚖgithubᚗcomᚋ99designsᚋgqlge return res } +func (ec *executionContext) unmarshalNDefinedTypeBytes2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐExternalBytes(ctx context.Context, v interface{}) (external.ExternalBytes, error) { + res, err := external.UnmarshalBytes(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDefinedTypeBytes2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐExternalBytes(ctx context.Context, sel ast.SelectionSet, v external.ExternalBytes) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + res := external.MarshalBytes(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + +func (ec *executionContext) unmarshalNDefinedTypeRunes2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐExternalRunes(ctx context.Context, v interface{}) (external.ExternalRunes, error) { + res, err := external.UnmarshalRunes(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNDefinedTypeRunes2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐExternalRunes(ctx context.Context, sel ast.SelectionSet, v external.ExternalRunes) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + res := external.MarshalRunes(v) + if res == graphql.Null { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + } + return res +} + func (ec *executionContext) unmarshalNID2githubᚗcomᚋ99designsᚋgqlgenᚋ_examplesᚋscalarsᚋexternalᚐObjectID(ctx context.Context, v interface{}) (external.ObjectID, error) { res, err := model.UnmarshalID(v) return res, graphql.ErrorOnPath(ctx, err) diff --git a/_examples/scalars/model/model.go b/_examples/scalars/model/model.go index b34235f13a..245f9358bf 100644 --- a/_examples/scalars/model/model.go +++ b/_examples/scalars/model/model.go @@ -59,6 +59,8 @@ type User struct { SomeBytes []byte SomeOtherBytes []byte SomeRunes []rune + RemoteBytes external.ExternalBytes + RemoteRunes external.ExternalRunes } // Point is serialized as a simple array, eg [1, 2] diff --git a/_examples/scalars/resolvers.go b/_examples/scalars/resolvers.go index 11ebade0e6..52d1e21a19 100644 --- a/_examples/scalars/resolvers.go +++ b/_examples/scalars/resolvers.go @@ -43,6 +43,8 @@ func (r *queryResolver) User(ctx context.Context, id external.ObjectID) (*model. SomeBytes: []byte("abcdef"), SomeOtherBytes: []byte{97, 98, 99, 100, 101, 102}, SomeRunes: []rune{'H', 'e', 'l', 'l', 'o', ' ', '世', '界'}, + RemoteBytes: external.ExternalBytes("fedcba"), + RemoteRunes: external.ExternalRunes{'界', '世', ' ', 'H', 'e', 'l', 'l', 'o'}, }, nil } diff --git a/_examples/scalars/scalar_test.go b/_examples/scalars/scalar_test.go index fc37e98cad..de98e9fdb7 100644 --- a/_examples/scalars/scalar_test.go +++ b/_examples/scalars/scalar_test.go @@ -28,6 +28,8 @@ type RawUser struct { SomeBytes string SomeOtherBytes string SomeRunes string + RemoteBytes string + RemoteRunes string } func TestScalars(t *testing.T) { @@ -93,14 +95,16 @@ func TestScalars(t *testing.T) { require.Equal(t, 2, resp.User.Weddings) }) - t.Run("basic alias byte and rune", func(t *testing.T) { + t.Run("basic aliases byte and rune", func(t *testing.T) { var resp struct{ User RawUser } - err := c.Post(`{ user(id:"=1=") { someBytes someOtherBytes someRunes } }`, &resp) + err := c.Post(`{ user(id:"=1=") { someBytes someOtherBytes someRunes remoteBytes remoteRunes } }`, &resp) require.NoError(t, err) require.Equal(t, "abcdef", resp.User.SomeBytes) require.Equal(t, "abcdef", resp.User.SomeOtherBytes) require.Equal(t, "Hello 世界", resp.User.SomeRunes) + require.Equal(t, "fedcba", resp.User.RemoteBytes) + require.Equal(t, "界世 Hello", resp.User.RemoteRunes) }) t.Run("custom error messages", func(t *testing.T) { diff --git a/_examples/scalars/schema.graphql b/_examples/scalars/schema.graphql index 9750eb3adf..b7d875c4cb 100644 --- a/_examples/scalars/schema.graphql +++ b/_examples/scalars/schema.graphql @@ -25,6 +25,8 @@ type User { someBytes: Bytes! someOtherBytes: Bytes! someRunes: Runes! + remoteBytes: DefinedTypeBytes! + remoteRunes: DefinedTypeRunes! } type Address { @@ -50,3 +52,5 @@ scalar Banned scalar DarkMode scalar Bytes scalar Runes +scalar DefinedTypeBytes +scalar DefinedTypeRunes diff --git a/integration/testomitempty.graphql b/integration/testomitempty.graphql index e943c8e091..1b73851f4e 100644 --- a/integration/testomitempty.graphql +++ b/integration/testomitempty.graphql @@ -22,7 +22,7 @@ type DefinedTypeFromBasics { # for example, value of 5.76 comes out as 5.760000228881836, anyone knows how to fix this? newFloat32: Float32! - # uint64 need a scalar because it is bigger than int64 + # uint64 needs a scalar because it is bigger than int64 newUint64: Uint64! } diff --git a/internal/code/compare.go b/internal/code/compare.go index 16afc256d7..8ff95ddce0 100644 --- a/internal/code/compare.go +++ b/internal/code/compare.go @@ -168,7 +168,7 @@ func similarBasicKind(kind types.BasicKind) types.BasicKind { switch kind { case types.Int8, types.Int16: return types.Int64 - case types.Uint, types.Uint8, types.Uint16, types.Uint32: // exclude Uint64: it still needs scalar with custom marshalling/unmarshalling because it is bigger then int64 + case types.Uint, types.Uint8, types.Uint16, types.Uint32: // exclude Uint64: it still needs scalar with custom marshalling/unmarshalling because it is bigger than int64 return types.Int64 default: return kind