_examples/scalars/model/model.go (158 lines of code) (raw):

package model import ( "errors" "fmt" "io" "strconv" "strings" "time" "github.com/99designs/gqlgen/_examples/scalars/external" "github.com/99designs/gqlgen/graphql" ) type Banned bool func (b Banned) MarshalGQL(w io.Writer) { if b { w.Write([]byte("true")) } else { w.Write([]byte("false")) } } func (b *Banned) UnmarshalGQL(v any) error { switch v := v.(type) { case string: *b = Banned(strings.EqualFold(v, "true")) return nil case bool: *b = Banned(v) return nil default: return fmt.Errorf("%T is not a bool", v) } } type User struct { ID external.ObjectID Name string Created time.Time // direct binding to builtin types with external Marshal/Unmarshal methods Modified *time.Time // direct binding to builtin types with external Marshal/Unmarshal methods ValPrefs Prefs // external un/marshal that act on pointers PtrPrefs *Prefs IsBanned Banned Address Address Tier Tier } // Point is serialized as a simple array, eg [1, 2] type Point struct { X int Y int } func (p *Point) UnmarshalGQL(v any) error { pointStr, ok := v.(string) if !ok { return errors.New("points must be strings") } parts := strings.Split(pointStr, ",") if len(parts) != 2 { return errors.New("points must have 2 parts") } var err error if p.X, err = strconv.Atoi(parts[0]); err != nil { return err } if p.Y, err = strconv.Atoi(parts[1]); err != nil { return err } return nil } // MarshalGQL implements the graphql.Marshaler interface func (p Point) MarshalGQL(w io.Writer) { fmt.Fprintf(w, `"%d,%d"`, p.X, p.Y) } // if the type referenced in .gqlgen.yml is a function that returns a marshaller we can use it to encode and decode // onto any existing go type. func MarshalTimestamp(t time.Time) graphql.Marshaler { return graphql.WriterFunc(func(w io.Writer) { io.WriteString(w, strconv.FormatInt(t.Unix(), 10)) }) } // Unmarshal{Typename} is only required if the scalar appears as an input. The raw values have already been decoded // from json into int/float64/bool/nil/map[string]interface/[]interface func UnmarshalTimestamp(v any) (time.Time, error) { if tmpStr, ok := v.(int64); ok { return time.Unix(tmpStr, 0), nil } return time.Time{}, errors.New("time should be a unix timestamp") } // Lets redefine the base ID type to use an id from an external library func MarshalID(id external.ObjectID) graphql.Marshaler { return graphql.WriterFunc(func(w io.Writer) { io.WriteString(w, strconv.Quote(fmt.Sprintf("=%d=", id))) }) } // And the same for the unmarshaler func UnmarshalID(v any) (external.ObjectID, error) { str, ok := v.(string) if !ok { return 0, errors.New("ids must be strings") } i, err := strconv.Atoi(str[1 : len(str)-1]) return external.ObjectID(i), err } type SearchArgs struct { Location *Point CreatedAfter *time.Time IsBanned Banned } // A custom enum that uses integers to represent the values in memory but serialize as string for graphql type Tier uint const ( TierA Tier = iota TierB Tier = iota TierC Tier = iota ) func TierForStr(str string) (Tier, error) { switch str { case "A": return TierA, nil case "B": return TierB, nil case "C": return TierC, nil default: return 0, fmt.Errorf("%s is not a valid Tier", str) } } func (e Tier) IsValid() bool { switch e { case TierA, TierB, TierC: return true } return false } func (e Tier) String() string { switch e { case TierA: return "A" case TierB: return "B" case TierC: return "C" default: panic("invalid enum value") } } func (e *Tier) UnmarshalGQL(v any) error { str, ok := v.(string) if !ok { return errors.New("enums must be strings") } var err error *e, err = TierForStr(str) return err } func (e Tier) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } type Prefs struct { DarkMode bool } func MarshalPreferences(p *Prefs) graphql.Marshaler { return graphql.MarshalBoolean(p.DarkMode) } func UnmarshalPreferences(v any) (*Prefs, error) { tmp, err := graphql.UnmarshalBoolean(v) if err != nil { return nil, err } return &Prefs{DarkMode: tmp}, nil }