scalars.go

  1package graphql
  2
  3import (
  4	"fmt"
  5	"math"
  6	"strconv"
  7	"time"
  8
  9	"github.com/graphql-go/graphql/language/ast"
 10)
 11
 12// As per the GraphQL Spec, Integers are only treated as valid when a valid
 13// 32-bit signed integer, providing the broadest support across platforms.
 14//
 15// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
 16// they are internally represented as IEEE 754 doubles.
 17func coerceInt(value interface{}) interface{} {
 18	switch value := value.(type) {
 19	case bool:
 20		if value == true {
 21			return 1
 22		}
 23		return 0
 24	case int:
 25		if value < int(math.MinInt32) || value > int(math.MaxInt32) {
 26			return nil
 27		}
 28		return value
 29	case *int:
 30		return coerceInt(*value)
 31	case int8:
 32		return int(value)
 33	case *int8:
 34		return int(*value)
 35	case int16:
 36		return int(value)
 37	case *int16:
 38		return int(*value)
 39	case int32:
 40		return int(value)
 41	case *int32:
 42		return int(*value)
 43	case int64:
 44		if value < int64(math.MinInt32) || value > int64(math.MaxInt32) {
 45			return nil
 46		}
 47		return int(value)
 48	case *int64:
 49		return coerceInt(*value)
 50	case uint:
 51		if value > math.MaxInt32 {
 52			return nil
 53		}
 54		return int(value)
 55	case *uint:
 56		return coerceInt(*value)
 57	case uint8:
 58		return int(value)
 59	case *uint8:
 60		return int(*value)
 61	case uint16:
 62		return int(value)
 63	case *uint16:
 64		return int(*value)
 65	case uint32:
 66		if value > uint32(math.MaxInt32) {
 67			return nil
 68		}
 69		return int(value)
 70	case *uint32:
 71		return coerceInt(*value)
 72	case uint64:
 73		if value > uint64(math.MaxInt32) {
 74			return nil
 75		}
 76		return int(value)
 77	case *uint64:
 78		return coerceInt(*value)
 79	case float32:
 80		if value < float32(math.MinInt32) || value > float32(math.MaxInt32) {
 81			return nil
 82		}
 83		return int(value)
 84	case *float32:
 85		return coerceInt(*value)
 86	case float64:
 87		if value < float64(math.MinInt32) || value > float64(math.MaxInt32) {
 88			return nil
 89		}
 90		return int(value)
 91	case *float64:
 92		return coerceInt(*value)
 93	case string:
 94		val, err := strconv.ParseFloat(value, 0)
 95		if err != nil {
 96			return nil
 97		}
 98		return coerceInt(val)
 99	case *string:
100		return coerceInt(*value)
101	}
102
103	// If the value cannot be transformed into an int, return nil instead of '0'
104	// to denote 'no integer found'
105	return nil
106}
107
108// Int is the GraphQL Integer type definition.
109var Int = NewScalar(ScalarConfig{
110	Name: "Int",
111	Description: "The `Int` scalar type represents non-fractional signed whole numeric " +
112		"values. Int can represent values between -(2^31) and 2^31 - 1. ",
113	Serialize:  coerceInt,
114	ParseValue: coerceInt,
115	ParseLiteral: func(valueAST ast.Value) interface{} {
116		switch valueAST := valueAST.(type) {
117		case *ast.IntValue:
118			if intValue, err := strconv.Atoi(valueAST.Value); err == nil {
119				return intValue
120			}
121		}
122		return nil
123	},
124})
125
126func coerceFloat(value interface{}) interface{} {
127	switch value := value.(type) {
128	case bool:
129		if value == true {
130			return 1.0
131		}
132		return 0.0
133	case *bool:
134		return coerceFloat(*value)
135	case int:
136		return float64(value)
137	case *int32:
138		return coerceFloat(*value)
139	case float32:
140		return value
141	case *float32:
142		return coerceFloat(*value)
143	case float64:
144		return value
145	case *float64:
146		return coerceFloat(*value)
147	case string:
148		val, err := strconv.ParseFloat(value, 0)
149		if err != nil {
150			return nil
151		}
152		return val
153	case *string:
154		return coerceFloat(*value)
155	}
156	return 0.0
157}
158
159// Float is the GraphQL float type definition.
160var Float = NewScalar(ScalarConfig{
161	Name: "Float",
162	Description: "The `Float` scalar type represents signed double-precision fractional " +
163		"values as specified by " +
164		"[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ",
165	Serialize:  coerceFloat,
166	ParseValue: coerceFloat,
167	ParseLiteral: func(valueAST ast.Value) interface{} {
168		switch valueAST := valueAST.(type) {
169		case *ast.FloatValue:
170			if floatValue, err := strconv.ParseFloat(valueAST.Value, 32); err == nil {
171				return floatValue
172			}
173		case *ast.IntValue:
174			if floatValue, err := strconv.ParseFloat(valueAST.Value, 32); err == nil {
175				return floatValue
176			}
177		}
178		return nil
179	},
180})
181
182func coerceString(value interface{}) interface{} {
183	if v, ok := value.(*string); ok {
184		return *v
185	}
186	return fmt.Sprintf("%v", value)
187}
188
189// String is the GraphQL string type definition
190var String = NewScalar(ScalarConfig{
191	Name: "String",
192	Description: "The `String` scalar type represents textual data, represented as UTF-8 " +
193		"character sequences. The String type is most often used by GraphQL to " +
194		"represent free-form human-readable text.",
195	Serialize:  coerceString,
196	ParseValue: coerceString,
197	ParseLiteral: func(valueAST ast.Value) interface{} {
198		switch valueAST := valueAST.(type) {
199		case *ast.StringValue:
200			return valueAST.Value
201		}
202		return nil
203	},
204})
205
206func coerceBool(value interface{}) interface{} {
207	switch value := value.(type) {
208	case bool:
209		return value
210	case *bool:
211		return *value
212	case string:
213		switch value {
214		case "", "false":
215			return false
216		}
217		return true
218	case *string:
219		return coerceBool(*value)
220	case float64:
221		if value != 0 {
222			return true
223		}
224		return false
225	case *float64:
226		return coerceBool(*value)
227	case float32:
228		if value != 0 {
229			return true
230		}
231		return false
232	case *float32:
233		return coerceBool(*value)
234	case int:
235		if value != 0 {
236			return true
237		}
238		return false
239	case *int:
240		return coerceBool(*value)
241	}
242	return false
243}
244
245// Boolean is the GraphQL boolean type definition
246var Boolean = NewScalar(ScalarConfig{
247	Name:        "Boolean",
248	Description: "The `Boolean` scalar type represents `true` or `false`.",
249	Serialize:   coerceBool,
250	ParseValue:  coerceBool,
251	ParseLiteral: func(valueAST ast.Value) interface{} {
252		switch valueAST := valueAST.(type) {
253		case *ast.BooleanValue:
254			return valueAST.Value
255		}
256		return nil
257	},
258})
259
260// ID is the GraphQL id type definition
261var ID = NewScalar(ScalarConfig{
262	Name: "ID",
263	Description: "The `ID` scalar type represents a unique identifier, often used to " +
264		"refetch an object or as key for a cache. The ID type appears in a JSON " +
265		"response as a String; however, it is not intended to be human-readable. " +
266		"When expected as an input type, any string (such as `\"4\"`) or integer " +
267		"(such as `4`) input value will be accepted as an ID.",
268	Serialize:  coerceString,
269	ParseValue: coerceString,
270	ParseLiteral: func(valueAST ast.Value) interface{} {
271		switch valueAST := valueAST.(type) {
272		case *ast.IntValue:
273			return valueAST.Value
274		case *ast.StringValue:
275			return valueAST.Value
276		}
277		return nil
278	},
279})
280
281func serializeDateTime(value interface{}) interface{} {
282	switch value := value.(type) {
283	case time.Time:
284		buff, err := value.MarshalText()
285		if err != nil {
286			return nil
287		}
288
289		return string(buff)
290	case *time.Time:
291		return serializeDateTime(*value)
292	default:
293		return nil
294	}
295}
296
297func unserializeDateTime(value interface{}) interface{} {
298	switch value := value.(type) {
299	case []byte:
300		t := time.Time{}
301		err := t.UnmarshalText(value)
302		if err != nil {
303			return nil
304		}
305
306		return t
307	case string:
308		return unserializeDateTime([]byte(value))
309	case *string:
310		return unserializeDateTime([]byte(*value))
311	default:
312		return nil
313	}
314}
315
316var DateTime = NewScalar(ScalarConfig{
317	Name: "DateTime",
318	Description: "The `DateTime` scalar type represents a DateTime." +
319		" The DateTime is serialized as an RFC 3339 quoted string",
320	Serialize:  serializeDateTime,
321	ParseValue: unserializeDateTime,
322	ParseLiteral: func(valueAST ast.Value) interface{} {
323		switch valueAST := valueAST.(type) {
324		case *ast.StringValue:
325			return valueAST.Value
326		}
327		return nil
328	},
329})