graphql-api
graphql-api copied to clipboard
(List Text) as argument
The following query works:
query {
user {
friends
}
}
against a toApplication @RootQuery rootHandler request respond
on the following:
type RootQuery = Object "Query" '[]
'[ Field "user" User ]
type User = Object "User" '[]
'[ Field "friends" (List Text) ]
rootHandler :: Handler IO RootQuery
rootHandler = pure $ pure myFriends
myFriends :: Handler IO (List Text)
myFriends = pure [pure "Jack", pure "Jim"]
However, I am having trouble doing this with an argument like:
type User = Object "User" '[]
'[ Argument "friendNames" (List Text) :> Field "friends" (List Text) ]
myFriends :: [Text] -> Handler IO (List Text)
myFriends friends = pure $ map (pure :: Text -> IO Text) friends
This results in an error:
Couldn't match type ‘[Text]’ with ‘List Text’
Expected type: Handler IO RootQuery
Actual type: IO
(IO
((Text -> IO Text)
:<> ((UserENUM -> IO UserENUM)
:<> ((DogStuff -> IO Text)
:<> (IO [IO Text] :<> ([Text] -> IO [IO Text]))))))
The issue lies here ...............................................................................^^^^^
The types say that the argument needs to be List Text
, but the handler function takes [Text]
If I set the type signature of the handler `myFriends' to:
myFriends :: List Text -> Handler IO (List Text)
I get the following error:
Couldn't match type ‘List (IO Text)’ with ‘[IO Text]’
Expected type: Handler IO (List Text)
Actual type: IO (List (IO Text))
Which is no surprise, given this from GraphQl/Resolver.hs:
type Handler m (API.List hg) = m [Handler m hg]
Ultimately, what I interpret this to mean is that type User
should be defined as:
type User = Object "User" '[]
'[ Argument "friendNames" [Text] :> Field "friends" (List Text) ]
Which results in the error:
No instance for (GraphQL.API.GenericAnnotatedInputType
(D1
('MetaData "[]" "GHC.Types" "ghc-prim" 'False)
(C1 ('MetaCons "[]" 'PrefixI 'False) U1
:+: C1
('MetaCons ":" ('InfixI 'LeftAssociative 9) 'False)
(S1
('MetaSel
'Nothing
'GHC.Generics.NoSourceUnpackedness
'GHC.Generics.NoSourceStrictness
'GHC.Generics.DecidedLazy)
(Rec0 Text)
:*: S1
('MetaSel
'Nothing
'GHC.Generics.NoSourceUnpackedness
'GHC.Generics.NoSourceStrictness
'GHC.Generics.DecidedLazy)
(Rec0 [Text])))))
arising from a use of ‘GraphQL.API.$dmgetAnnotatedInputType’
In the expression: GraphQL.API.$dmgetAnnotatedInputType @[Text]
I have solved this for myself, but I need feedback before I presume to push it as a PR.
I included a [t]
as an instance of HasAnnotatedInputType
in API.hs
:
instance forall t. (HasAnnotatedInputType t) => HasAnnotatedInputType [t] where
getAnnotatedInputType = TypeList . ListType <$> getAnnotatedInputType @t
the same definition as for List t
^^^.
Also included a Defaultable
instance for [a]
in Resolver.hs
:
instance Defaultable [a]
Which allows the following:
type User = Object "User" '[]
'[ Argument "friendNames" [Text] :> Field "friends" (List Text) ]
myFriends :: [Text] -> Handler IO (List Text)
myFriends friends = pure $ map pure friends
I feel like I should have been able to convince the compiler that List a
and [a] are the same for HasAnnotatedInputType a
, but I am having trouble even grokking how GraphQL.API.List leaves the realm of Types and becomes a usable value.
@teh is probably best placed to help with this issue. Our type gymnastics are a bit much for me.
It heartens me that I'm not the only one ;)
@sunwukonga I had a go at this last weekend and I'm not so sure what I did any more either! I feel you are on the right track (we forgot to implement HasAnnotatedInputType
for lists) but right now I'm not 100% sure yet that your solution is correct (I'm 90% sure though!). I'm sorry it's taking a while.
@teh any update on whether the solution suggested by @sunwukonga can be pushed as a fix? Even if you are not fully sure we can perhaps try it and see if users find any problem with it.
I tried this solution and so far it is working fine for me.
@harendra-kumar if it's working for you and @sunwukonga we should include it. Do you want to open a PR?
With the information provided in this post, I have been using the following patch to enable user input :: [input]
that behaves accordingly in my handlers. I import the Patch
where I would otherwise do so for GraphQL.API
.
module Patch
(
module Patch
, module GraphQL.API
)
where
import GraphQL.API
import Protolude hiding (Enum, TypeError)
import qualified GraphQL.Internal.Schema as Schema
instance Defaultable [a]
instance forall t. (HasAnnotatedInputType t)
=> HasAnnotatedInputType [t] where
getAnnotatedInputType = Schema.TypeList . Schema.ListType <$> getAnnotatedInputType @t