git.iko.soy/rowtype-yoga/purescript-graphql
on master help sign in
Fix introspection results

Some things in the introspection were not quite working correctly. First non-nullable and nullable fields had mixed up introspections. Second the types array in the schema introspection object should only contain the named types, not their wrapped versions. For this they have to be unwrapped in the collectTypes function. Also object types need to return an empty array for interfaces, even if they don't implement any.

commit7b518fe1bceaa6621fb833649b566ddc88860abe authorhendrikniemann <h.neverman@web.de> dateTue 28 Apr 2020, 01:11 (6 years ago) parentf7ca5f3
3 files changed+60−33
src/GraphQL/Type.purs+25−23 src/GraphQL/Type/Introspection.purs+17−3 src/GraphQL/Type/Introspection/Util.purs+18−7
src/GraphQL/Type.purs+25−23
@@ -255,11 +255,21 @@ field name t =
255255 , args: {}
256256 , serialize
257257 , argumentIntrospections: []
258− , typeIntrospection: \_ -> introspect t }
258+ , typeIntrospection }
259259 where
260− serialize (AST.FieldNode node) variables _ val = output t node.selectionSet variables val
260+ serialize (AST.FieldNode node) execCtx _ val = output t node.selectionSet execCtx val
261261 serialize _ _ _ _ = pure $ ResultError "Obtained non FieldNode for field serialisation."
262262
263+ typeIntrospection _ =
264+ IntrospectionTypes.TypeIntrospection
265+ { kind: IntrospectionTypes.NonNull
266+ , name: Nothing
267+ , description: Nothing
268+ , fields: Nothing
269+ , enumValues: Nothing
270+ , ofType: Just $ \_ -> introspect t
271+ }
272+
263273-- | Create a new field for an object type that is a list. You can return any `Foldable` in the
264274-- | resolver.
265275listField :: forall m f t a.
@@ -287,12 +297,20 @@ listField name t =
287297
288298 typeIntrospection _ =
289299 IntrospectionTypes.TypeIntrospection
290− { kind: IntrospectionTypes.List
300+ { kind: IntrospectionTypes.NonNull
291301 , name: Nothing
292302 , description: Nothing
293303 , fields: Nothing
294304 , enumValues: Nothing
295− , ofType: Just $ \_ -> introspect t
305+ , ofType: Just $ \_ ->
306+ IntrospectionTypes.TypeIntrospection
307+ { kind: IntrospectionTypes.List
308+ , name: Nothing
309+ , description: Nothing
310+ , fields: Nothing
311+ , enumValues: Nothing
312+ , ofType: Just $ \_ -> introspect t
313+ }
296314 }
297315
298316-- | Create a new field for an object type that is optional (i.e. it can be null). The resolver
@@ -324,15 +342,7 @@ nullableField name t =
324342 serialize _ _ _ _ =
325343 pure $ ResultError "Obtained non FieldNode for field serialisation."
326344
327− typeIntrospection _ =
328− IntrospectionTypes.TypeIntrospection
329− { kind: IntrospectionTypes.NonNull
330− , name: Nothing
331− , description: Nothing
332− , fields: Nothing
333− , enumValues: Nothing
334− , ofType: Just $ \_ -> introspect t
335− }
345+ typeIntrospection _ = introspect t
336346
337347-- | Create a new field for an object type that is optional (i.e. it can be null) and returns a
338348-- | list. The resolver must now return a `Maybe`.
@@ -369,20 +379,12 @@ nullableListField name t =
369379
370380 typeIntrospection _ =
371381 IntrospectionTypes.TypeIntrospection
372− { kind: IntrospectionTypes.NonNull
382+ { kind: IntrospectionTypes.List
373383 , name: Nothing
374384 , description: Nothing
375385 , fields: Nothing
376386 , enumValues: Nothing
377− , ofType: Just $ \_ ->
378− IntrospectionTypes.TypeIntrospection
379− { kind: IntrospectionTypes.List
380− , name: Nothing
381− , description: Nothing
382− , fields: Nothing
383− , enumValues: Nothing
384− , ofType: Just $ \_ -> introspect t
385− }
387+ , ofType: Just $ \_ -> introspect t
386388 }
387389
388390-- | Create a tuple with a given name and a plain argument of the given type.
src/GraphQL/Type/Introspection.purs+17−3
@@ -11,7 +11,7 @@ import Data.Newtype (unwrap)
1111import Effect.Exception (Error)
1212import GraphQL.Type ((!#>), (.>), (:>))
1313import GraphQL.Type as GQL
14−import GraphQL.Type.Introspection.Datatypes (EnumValueIntrospection, FieldIntrospection, InputValueIntrospection, SchemaIntrospection, TypeIntrospection, TypeKind)
14+import GraphQL.Type.Introspection.Datatypes (EnumValueIntrospection, FieldIntrospection, InputValueIntrospection, SchemaIntrospection, TypeIntrospection(..), TypeKind(..))
1515import GraphQL.Type.Introspection.Util (collectTypes)
1616import GraphQL.Type.Scalar as Scalar
1717
@@ -37,6 +37,14 @@ schemaType = schemaType'
3737 If this server supports mutation, the type that mutation operations will be rooted at.
3838 """
3939 !#> unwrap >>> _.mutationType
40+ :> GQL.nullableField "subscriptionType" (typeType :: GQL.ObjectType m TypeIntrospection)
41+ .> """
42+ If this server supports subscriptons, the type that subscription operations will be rooted
43+ at.
44+ """
45+ !#> const Nothing
46+ :> GQL.listField "directives" Scalar.string
47+ !#> const []
4048
4149 typeType :: GQL.ObjectType m TypeIntrospection
4250 typeType = GQL.objectType "__Type"
@@ -58,11 +66,17 @@ schemaType = schemaType'
5866 !#> unwrap >>> _.fields
5967 -- For spec compliance; this implementation does not support interfaces or unions yet
6068 :> GQL.nullableListField "interfaces" (defer \_ -> typeType)
61− !#> const (Nothing :: Maybe (Array TypeIntrospection))
69+ !#> (\(TypeIntrospection t) ->
70+ if t.kind == Object
71+ then Just []
72+ else (Nothing :: Maybe (Array TypeIntrospection))
73+ )
6274 :> GQL.nullableListField "possibleTypes" (defer \_ -> typeType)
6375 !#> const (Nothing :: Maybe (Array TypeIntrospection))
64− :> GQL.nullableListField "enumValue" enumValueType
76+ :> GQL.nullableListField "enumValues" enumValueType
6577 !#> unwrap >>> _.enumValues
78+ :> GQL.nullableListField "inputFields" (defer \_ -> inputValueType)
79+ !#> const (Nothing :: Maybe (Array InputValueIntrospection))
6680 :> GQL.nullableField "ofType" (defer \_ -> typeType)
6781 !#> unwrap >>> _.ofType >>> map applyUnit
6882
src/GraphQL/Type/Introspection/Util.purs+18−7
@@ -4,26 +4,37 @@ import Prelude
44
55import Data.Foldable (foldl)
66import Data.Maybe (Maybe(..), maybe)
7−import Data.Set (Set, empty, member, singleton)
7+import Data.Set (Set, empty, insert, member)
88import GraphQL.Type.Introspection.Datatypes (FieldIntrospection(..), SchemaIntrospection(..), TypeIntrospection(..))
99
1010
1111collectTypes :: SchemaIntrospection -> Set TypeIntrospection
1212collectTypes (SchemaIntrospection { queryType, mutationType }) =
13− collectTypes' queryType <> maybe empty collectTypes' mutationType
13+ collectTypesRec queryType empty <> maybe empty (flip collectTypesRec empty) mutationType
1414 where
15− collectTypes' :: TypeIntrospection -> Set TypeIntrospection
15+ collectTypesRec :: TypeIntrospection -> Set TypeIntrospection -> Set TypeIntrospection
16+ -- If the type is a wrapper type, we try to insert the inner type
17+ collectTypesRec introspection@(TypeIntrospection
18+ { fields: Nothing, ofType: Just t }
19+ ) set = collectTypesRec (t unit) set
20+
1621 -- If the type has no fields we can simply return a set with one element
17− collectTypes' introspection@(TypeIntrospection { fields: Nothing }) = singleton introspection
22+ collectTypesRec introspection@(TypeIntrospection { fields: Nothing }) set =
23+ insert introspection set
1824
1925 -- If the type has fields we need to recursively go over these fields and add the type of the
2026 -- fields as well
21− collectTypes' introspection@(TypeIntrospection { fields: Just fields }) =
22− foldl collectTypesFromField (singleton introspection) fields
27+ collectTypesRec introspection@(TypeIntrospection { fields: Just fields }) set =
28+ if member introspection set
29+ then set
30+ else foldl collectTypesFromField (insert introspection set) fields
2331
2432 collectTypesFromField :: Set TypeIntrospection -> FieldIntrospection -> Set TypeIntrospection
2533 collectTypesFromField set (FieldIntrospection config) =
2634 let
2735 resultType = config.type unit
2836 in
29− if member resultType set then set else set <> collectTypes' resultType
37+ if member resultType set then set else collectTypesRec resultType set
38+
39+
40+