Skip to content

Commit 21f7aef

Browse files
authored
Merge pull request #450 from esome/master
expose packer.Unmarshaler as decode.Unmarshaler to the public
2 parents 1a8cd9c + 60ab3c8 commit 21f7aef

File tree

6 files changed

+184
-9
lines changed

6 files changed

+184
-9
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/.idea
2+
/.vscode
13
/internal/validation/testdata/graphql-js
24
/internal/validation/testdata/node_modules
35
/vendor

decode/decode.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package decode
2+
3+
// Unmarshaler defines the api of Go types mapped to custom GraphQL scalar types
4+
type Unmarshaler interface {
5+
// ImplementsGraphQLType maps the implementing custom Go type
6+
// to the GraphQL scalar type in the schema.
7+
ImplementsGraphQLType(name string) bool
8+
// UnmarshalGraphQL is the custom unmarshaler for the implementing type
9+
//
10+
// This function will be called whenever you use the
11+
// custom GraphQL scalar type as an input
12+
UnmarshalGraphQL(input interface{}) error
13+
}

internal/exec/packer/packer.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"reflect"
77
"strings"
88

9+
"github.com/graph-gophers/graphql-go/decode"
910
"github.com/graph-gophers/graphql-go/errors"
1011
"github.com/graph-gophers/graphql-go/internal/common"
1112
"github.com/graph-gophers/graphql-go/internal/schema"
@@ -115,7 +116,7 @@ func (b *Builder) makePacker(schemaType common.Type, reflectType reflect.Type) (
115116
}
116117

117118
func (b *Builder) makeNonNullPacker(schemaType common.Type, reflectType reflect.Type) (packer, error) {
118-
if u, ok := reflect.New(reflectType).Interface().(Unmarshaler); ok {
119+
if u, ok := reflect.New(reflectType).Interface().(decode.Unmarshaler); ok {
119120
if !u.ImplementsGraphQLType(schemaType.String()) {
120121
return nil, fmt.Errorf("can not unmarshal %s into %s", schemaType, reflectType)
121122
}
@@ -323,17 +324,12 @@ func (p *unmarshalerPacker) Pack(value interface{}) (reflect.Value, error) {
323324
}
324325

325326
v := reflect.New(p.ValueType)
326-
if err := v.Interface().(Unmarshaler).UnmarshalGraphQL(value); err != nil {
327+
if err := v.Interface().(decode.Unmarshaler).UnmarshalGraphQL(value); err != nil {
327328
return reflect.Value{}, err
328329
}
329330
return v.Elem(), nil
330331
}
331332

332-
type Unmarshaler interface {
333-
ImplementsGraphQLType(name string) bool
334-
UnmarshalGraphQL(input interface{}) error
335-
}
336-
337333
func unmarshalInput(typ reflect.Type, input interface{}) (interface{}, error) {
338334
if reflect.TypeOf(input) == typ {
339335
return input, nil
@@ -385,7 +381,7 @@ func stripUnderscore(s string) string {
385381

386382
// NullUnmarshaller is an unmarshaller that can handle a nil input
387383
type NullUnmarshaller interface {
388-
Unmarshaler
384+
decode.Unmarshaler
389385
Nullable()
390386
}
391387

internal/exec/resolvable/resolvable.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"reflect"
77
"strings"
88

9+
"github.com/graph-gophers/graphql-go/decode"
910
"github.com/graph-gophers/graphql-go/internal/common"
1011
"github.com/graph-gophers/graphql-go/internal/exec/packer"
1112
"github.com/graph-gophers/graphql-go/internal/schema"
@@ -207,7 +208,7 @@ func makeScalarExec(t *schema.Scalar, resolverType reflect.Type) (Resolvable, er
207208
implementsType = t.Name == "String"
208209
case *bool:
209210
implementsType = t.Name == "Boolean"
210-
case packer.Unmarshaler:
211+
case decode.Unmarshaler:
211212
implementsType = r.ImplementsGraphQLType(t.Name)
212213
}
213214
if !implementsType {

time.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ func (t *Time) UnmarshalGraphQL(input interface{}) error {
3131
var err error
3232
t.Time, err = time.Parse(time.RFC3339, input)
3333
return err
34+
case []byte:
35+
var err error
36+
t.Time, err = time.Parse(time.RFC3339, string(input))
37+
return err
3438
case int32:
3539
t.Time = time.Unix(int64(input), 0)
3640
return nil

time_test.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package graphql_test
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"testing"
7+
"time"
8+
9+
. "github.com/graph-gophers/graphql-go"
10+
"github.com/graph-gophers/graphql-go/decode"
11+
)
12+
13+
func TestTime_ImplementsUnmarshaler(t *testing.T) {
14+
defer func() {
15+
if err := recover(); err != nil {
16+
t.Error(err)
17+
}
18+
}()
19+
20+
// assert *Time implements decode.Unmarshaler interface
21+
var _ decode.Unmarshaler = (*Time)(nil)
22+
}
23+
24+
func TestTime_ImplementsGraphQLType(t *testing.T) {
25+
gt := new(Time)
26+
27+
if gt.ImplementsGraphQLType("foobar") {
28+
t.Error("Type *Time must not claim to implement GraphQL type 'foobar'")
29+
}
30+
31+
if !gt.ImplementsGraphQLType("Time") {
32+
t.Error("Failed asserting *Time implements GraphQL type Time")
33+
}
34+
}
35+
36+
func TestTime_MarshalJSON(t *testing.T) {
37+
var err error
38+
var b1, b2 []byte
39+
ref := time.Date(2021, time.April, 20, 12, 3, 23, 0, time.UTC)
40+
41+
if b1, err = json.Marshal(ref); err != nil {
42+
t.Error(err)
43+
return
44+
}
45+
46+
if b2, err = json.Marshal(Time{Time: ref}); err != nil {
47+
t.Errorf("MarshalJSON() error = %v", err)
48+
return
49+
}
50+
51+
if !bytes.Equal(b1, b2) {
52+
t.Errorf("MarshalJSON() got = %s, want = %s", b2, b1)
53+
}
54+
}
55+
56+
func TestTime_UnmarshalGraphQL(t *testing.T) {
57+
type args struct {
58+
input interface{}
59+
}
60+
61+
ref := time.Date(2021, time.April, 20, 12, 3, 23, 0, time.UTC)
62+
63+
t.Run("invalid", func(t *testing.T) {
64+
tests := []struct {
65+
name string
66+
args args
67+
wantErr string
68+
}{
69+
{
70+
name: "boolean",
71+
args: args{input: true},
72+
wantErr: "wrong type for Time: bool",
73+
},
74+
{
75+
name: "invalid format",
76+
args: args{input: ref.Format(time.ANSIC)},
77+
wantErr: `parsing time "Tue Apr 20 12:03:23 2021" as "2006-01-02T15:04:05Z07:00": cannot parse "Tue Apr 20 12:03:23 2021" as "2006"`,
78+
},
79+
}
80+
81+
for _, tt := range tests {
82+
t.Run(tt.name, func(t *testing.T) {
83+
gt := new(Time)
84+
if err := gt.UnmarshalGraphQL(tt.args.input); err != nil {
85+
if err.Error() != tt.wantErr {
86+
t.Errorf("UnmarshalGraphQL() error = %v, want = %s", err, tt.wantErr)
87+
}
88+
89+
return
90+
}
91+
92+
t.Error("UnmarshalGraphQL() expected error not raised")
93+
})
94+
}
95+
})
96+
97+
tests := []struct {
98+
name string
99+
args args
100+
wantEq time.Time
101+
}{
102+
{
103+
name: "time.Time",
104+
args: args{
105+
input: ref,
106+
},
107+
wantEq: ref,
108+
},
109+
{
110+
name: "string",
111+
args: args{
112+
input: ref.Format(time.RFC3339),
113+
},
114+
wantEq: ref,
115+
},
116+
{
117+
name: "bytes",
118+
args: args{
119+
input: []byte(ref.Format(time.RFC3339)),
120+
},
121+
wantEq: ref,
122+
},
123+
{
124+
name: "int32",
125+
args: args{
126+
input: int32(ref.Unix()),
127+
},
128+
wantEq: ref,
129+
},
130+
{
131+
name: "int64",
132+
args: args{
133+
input: ref.Unix(),
134+
},
135+
wantEq: ref,
136+
},
137+
{
138+
name: "float64",
139+
args: args{
140+
input: float64(ref.Unix()),
141+
},
142+
wantEq: ref,
143+
},
144+
}
145+
146+
for _, tt := range tests {
147+
t.Run(tt.name, func(t *testing.T) {
148+
gt := new(Time)
149+
if err := gt.UnmarshalGraphQL(tt.args.input); err != nil {
150+
t.Errorf("UnmarshalGraphQL() error = %v", err)
151+
return
152+
}
153+
154+
if !gt.Equal(tt.wantEq) {
155+
t.Errorf("UnmarshalGraphQL() got = %v, want = %v", gt, tt.wantEq)
156+
}
157+
})
158+
}
159+
}

0 commit comments

Comments
 (0)