Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot map custom scalars to third party pointer types #1259

Closed
albrow opened this issue Jul 22, 2020 · 2 comments · Fixed by #1277
Closed

Cannot map custom scalars to third party pointer types #1259

albrow opened this issue Jul 22, 2020 · 2 comments · Fixed by #1277

Comments

@albrow
Copy link

albrow commented Jul 22, 2020

What happened?

Following the guide here I am trying to implement a custom scalar type BigNumber which maps to *big.Int in the standard library. This results in compiler errors in the generated code.

validation failed: packages.Load: /Users/alex/programming/gomod/gqlgen-todos/graph/generated/generated.go:1909:9: cannot use scalars.UnmarshalBigNumber(v) (value of type *big.Int) as big.Int value in return statement
/Users/alex/programming/gomod/gqlgen-todos/graph/generated/generated.go:1913:34: cannot use v (variable of type big.Int) as *big.Int value in argument to scalars.MarshalBigNumber

Here is the problematic code:

func (ec *executionContext) unmarshalOBigNumber2mathᚋbigᚐInt(ctx context.Context, v interface{}) (big.Int, error) {
	return scalars.UnmarshalBigNumber(v)
}

func (ec *executionContext) marshalOBigNumber2mathᚋbigᚐInt(ctx context.Context, sel ast.SelectionSet, v big.Int) graphql.Marshaler {
	return scalars.MarshalBigNumber(v)
}

Screen Shot 2020-07-22 at 1 53 07 PM

Screen Shot 2020-07-22 at 1 52 57 PM

In my real application, we need to use *big.Int and cannot use big.Int. All functions and methods in the math/big package expect *big.Int so if my models use big.Int it makes it extremely hard to work with.

What did you expect?

Since I have defined custom marshaler and unmarshaler functions, I should be able to map BigNumber to *big.Int types in my models.

The models are actually being generated correctly. Here's what model/models_gen.go looks like:

type Todo struct {
	Number *big.Int `json:"number"`
}

The problem just lies in the generated code in graph/generated/generated.go. It looks like this can be resolved by changing the generated code so that it uses the correct types when calling scalars.UnmarshalBigNumber and scalars.MarshalBigNumber. I would suggest something like this:

func (ec *executionContext) unmarshalOBigNumber2mathᚋbigᚐInt(ctx context.Context, v interface{}) (big.Int, error) {
	res, err := scalars.UnmarshalBigNumber(v)
	if err != nil || res == nil {
		return big.Int{}, err
	}
	return *res, err
}

func (ec *executionContext) marshalOBigNumber2mathᚋbigᚐInt(ctx context.Context, sel ast.SelectionSet, v big.Int) graphql.Marshaler {
	return scalars.MarshalBigNumber(&v)
}

func (ec *executionContext) unmarshalOBigNumber2ᚖmathᚋbigᚐInt(ctx context.Context, v interface{}) (*big.Int, error) {
	return scalars.UnmarshalBigNumber(v)
}

func (ec *executionContext) marshalOBigNumber2ᚖmathᚋbigᚐInt(ctx context.Context, sel ast.SelectionSet, v *big.Int) graphql.Marshaler {
	return scalars.MarshalBigNumber(v)
}

Minimal graphql.schema and models to reproduce

See https://github.com/albrow/gqlgen-todos.

Schema:

scalar BigNumber

type Todo {
  number: BigNumber
}

Marshal and unmarshal functions:

package scalars

import (
	"fmt"
	"io"
	"math/big"
	"strconv"

	"github.com/99designs/gqlgen/graphql"
)

// UnmarshalBigNumber converts scalar value of type BigNumber into a *big.Int.
func UnmarshalBigNumber(v interface{}) (*big.Int, error) {
	s, ok := v.(string)
	if !ok {
		return nil, fmt.Errorf("BigNumber must be a numerical string")
	}
	bigInt := new(big.Int)
	_, ok = bigInt.SetString(s, 10)
	if !ok {
		return nil, fmt.Errorf("invalid BigNumber value: %q", s)
	}

	return bigInt, nil
}

// MarshalBigNumber converts a *big.Int into a scalar value of type BigNumber.
func MarshalBigNumber(number *big.Int) graphql.Marshaler {
	return graphql.WriterFunc(func(w io.Writer) {
		quotedString := strconv.Quote(number.String())
		_, _ = w.Write([]byte(quotedString))
	})
}

versions

  • gqlgen version? v0.11.3
  • go version? go version go1.14.3 darwin/amd64
  • dep or go modules? go modules
@albrow albrow changed the title Cannot use custom scalars for third party pointer types Cannot map custom scalars to third party pointer types Jul 22, 2020
@austincollinpena
Copy link

Did you find a solution to working with marshalling pointers?

@albrow
Copy link
Author

albrow commented Aug 5, 2020

Depends on what you mean by "find a solution". IMO gqlgen should not produce code that doesn't compile, and what I shared should be considered a bug.

For my specific application I was able to work around this bug by defining my own BigNumber type and writing the MarshalGQL and UnmarshalGQL methods. This creates unnecessary complication because we have to convert between the BigNumber type and *big.Int in order to do any actual math or business logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants