forked from OSS/graphql
		
	Initial support for variable substitution
The correspondent end-to-end test has been ported. The variable definition still needs to be checked.
This commit is contained in:
		@@ -12,5 +12,9 @@ import Data.GraphQL.Parser
 | 
				
			|||||||
import Data.GraphQL.Schema
 | 
					import Data.GraphQL.Schema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
graphql :: (Alternative m, Monad m) => Schema m -> Text -> m Aeson.Value
 | 
					graphql :: (Alternative m, Monad m) => Schema m -> Text -> m Aeson.Value
 | 
				
			||||||
graphql schema = either (const empty) (execute schema)
 | 
					graphql = flip graphqlSubs $ const Nothing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					graphqlSubs :: (Alternative m, Monad m) => Schema m -> Subs -> Text -> m Aeson.Value
 | 
				
			||||||
 | 
					graphqlSubs schema f =
 | 
				
			||||||
 | 
					    either (const empty) (execute schema f)
 | 
				
			||||||
  . Attoparsec.parseOnly document
 | 
					  . Attoparsec.parseOnly document
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,4 @@
 | 
				
			|||||||
{-# LANGUAGE CPP #-}
 | 
					{-# LANGUAGE CPP #-}
 | 
				
			||||||
{-# LANGUAGE LambdaCase #-}
 | 
					 | 
				
			||||||
module Data.GraphQL.Execute where
 | 
					module Data.GraphQL.Execute where
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if !MIN_VERSION_base(4,8,0)
 | 
					#if !MIN_VERSION_base(4,8,0)
 | 
				
			||||||
@@ -8,6 +7,7 @@ import Data.Traversable (traverse)
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
import Control.Applicative (Alternative, empty)
 | 
					import Control.Applicative (Alternative, empty)
 | 
				
			||||||
import Data.Foldable (fold)
 | 
					import Data.Foldable (fold)
 | 
				
			||||||
 | 
					import Data.Maybe (catMaybes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import qualified Data.Aeson as Aeson
 | 
					import qualified Data.Aeson as Aeson
 | 
				
			||||||
import qualified Data.HashMap.Strict as HashMap
 | 
					import qualified Data.HashMap.Strict as HashMap
 | 
				
			||||||
@@ -16,31 +16,37 @@ import Data.GraphQL.AST
 | 
				
			|||||||
import Data.GraphQL.Schema (Resolver, Schema(..))
 | 
					import Data.GraphQL.Schema (Resolver, Schema(..))
 | 
				
			||||||
import qualified Data.GraphQL.Schema as Schema
 | 
					import qualified Data.GraphQL.Schema as Schema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
execute :: (Alternative m, Monad m) => Schema m -> Document -> m Aeson.Value
 | 
					execute :: (Alternative m, Monad m) => Schema m -> Schema.Subs -> Document -> m Aeson.Value
 | 
				
			||||||
execute (Schema resolv) doc = selectionSet resolv =<< query doc
 | 
					execute (Schema resolv) f doc = selectionSet f resolv =<< query doc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
query :: Alternative f => Document -> f SelectionSet
 | 
					query :: Alternative f => Document -> f SelectionSet
 | 
				
			||||||
query (Document [DefinitionOperation (Query (Node _ _ _ sels))]) = pure sels
 | 
					query (Document [DefinitionOperation (Query (Node _varDefs _ _ sels))]) =
 | 
				
			||||||
 | 
					  pure sels
 | 
				
			||||||
query  _ = empty
 | 
					query  _ = empty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selectionSet :: Alternative f => Resolver f -> SelectionSet -> f Aeson.Value
 | 
					selectionSet :: Alternative f => Schema.Subs -> Resolver f -> SelectionSet -> f Aeson.Value
 | 
				
			||||||
selectionSet resolv = fmap (Aeson.Object . fold) . traverse (selection resolv)
 | 
					selectionSet f resolv = fmap (Aeson.Object . fold) . traverse (selection f resolv)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selection :: Alternative f => Resolver f -> Selection -> f Aeson.Object
 | 
					selection :: Alternative f => Schema.Subs -> Resolver f -> Selection -> f Aeson.Object
 | 
				
			||||||
selection resolv (SelectionField field@(Field _ name _ _ _)) =
 | 
					selection f resolv (SelectionField field@(Field _ name _ _ _)) =
 | 
				
			||||||
    fmap (HashMap.singleton name) $ Aeson.toJSON <$> resolv (fieldToInput field)
 | 
					    fmap (HashMap.singleton name) $ Aeson.toJSON <$> resolv (fieldToInput f field)
 | 
				
			||||||
selection _ _ = empty
 | 
					selection _ _ _ = empty
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- * AST/Schema conversions
 | 
					-- * AST/Schema conversions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
argument :: Argument -> Schema.Argument
 | 
					argument :: Schema.Subs -> Argument -> Maybe Schema.Argument
 | 
				
			||||||
argument (Argument n (ValueInt v)) = (n, Schema.ScalarInt $ fromIntegral v)
 | 
					argument f (Argument n (ValueVariable (Variable v))) =
 | 
				
			||||||
argument (Argument n (ValueString (StringValue v))) = (n, Schema.ScalarString v)
 | 
					    maybe Nothing (\v' -> Just (n, v')) $ f v
 | 
				
			||||||
argument _ = error "argument: not implemented yet"
 | 
					argument _ (Argument n (ValueInt v)) =
 | 
				
			||||||
 | 
					    Just (n, Schema.ScalarInt $ fromIntegral v)
 | 
				
			||||||
 | 
					argument _ (Argument n (ValueString (StringValue v))) =
 | 
				
			||||||
 | 
					    Just (n, Schema.ScalarString v)
 | 
				
			||||||
 | 
					argument _ _ = error "argument: not implemented yet"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fieldToInput :: Field -> Schema.Input
 | 
					fieldToInput :: Schema.Subs -> Field -> Schema.Input
 | 
				
			||||||
fieldToInput (Field _ n as _ sels) =
 | 
					fieldToInput f (Field _ n as _ sels) =
 | 
				
			||||||
  Schema.InputField n (argument <$> as) (fieldToInput . selectionToField <$> sels)
 | 
					    Schema.InputField n (catMaybes $ argument f <$> as)
 | 
				
			||||||
 | 
					                        (fieldToInput f . selectionToField <$> sels)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selectionToField :: Selection -> Field
 | 
					selectionToField :: Selection -> Field
 | 
				
			||||||
selectionToField (SelectionField x) = x
 | 
					selectionToField (SelectionField x) = x
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,11 @@ module Data.GraphQL.Schema where
 | 
				
			|||||||
#if !MIN_VERSION_base(4,8,0)
 | 
					#if !MIN_VERSION_base(4,8,0)
 | 
				
			||||||
import Control.Applicative ((<$>))
 | 
					import Control.Applicative ((<$>))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					import Data.String (IsString(fromString))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Data.Aeson (ToJSON(toJSON))
 | 
					import Data.Aeson (ToJSON(toJSON))
 | 
				
			||||||
import Data.HashMap.Strict (HashMap)
 | 
					import Data.HashMap.Strict (HashMap)
 | 
				
			||||||
import Data.Text (Text)
 | 
					import Data.Text (Text, pack)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data Schema f = Schema (QueryRoot f) -- (Maybe  MutationRoot)
 | 
					data Schema f = Schema (QueryRoot f) -- (Maybe  MutationRoot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,6 +26,8 @@ data Output = OutputObject (HashMap Text Output)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type Argument = (Text, Scalar)
 | 
					type Argument = (Text, Scalar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Subs = Text -> Maybe Scalar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data Input = InputField Text [Argument] [Input]
 | 
					data Input = InputField Text [Argument] [Input]
 | 
				
			||||||
             deriving (Show)
 | 
					             deriving (Show)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,6 +39,9 @@ data Scalar = ScalarInt     Int
 | 
				
			|||||||
            | ScalarID      Text
 | 
					            | ScalarID      Text
 | 
				
			||||||
              deriving (Show)
 | 
					              deriving (Show)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					instance IsString Scalar where
 | 
				
			||||||
 | 
					  fromString = ScalarString . pack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
instance ToJSON Scalar where
 | 
					instance ToJSON Scalar where
 | 
				
			||||||
    toJSON (ScalarInt     x) = toJSON x
 | 
					    toJSON (ScalarInt     x) = toJSON x
 | 
				
			||||||
    toJSON (ScalarFloat   x) = toJSON x
 | 
					    toJSON (ScalarFloat   x) = toJSON x
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ import Test.Tasty (TestTree, testGroup)
 | 
				
			|||||||
import Test.Tasty.HUnit (Assertion, testCase, (@?=))
 | 
					import Test.Tasty.HUnit (Assertion, testCase, (@?=))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Data.GraphQL
 | 
					import Data.GraphQL
 | 
				
			||||||
 | 
					import Data.GraphQL.Schema (Subs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import Test.StarWars.Schema
 | 
					import Test.StarWars.Schema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -20,6 +21,9 @@ import Test.StarWars.Schema
 | 
				
			|||||||
testQuery :: Text -> Aeson.Value -> Assertion
 | 
					testQuery :: Text -> Aeson.Value -> Assertion
 | 
				
			||||||
testQuery q expected = graphql schema q @?= Just expected
 | 
					testQuery q expected = graphql schema q @?= Just expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					testQueryParams :: Subs -> Text -> Aeson.Value -> Assertion
 | 
				
			||||||
 | 
					testQueryParams f q expected = graphqlSubs schema f q @?= Just expected
 | 
				
			||||||
 | 
					
 | 
				
			||||||
test :: TestTree
 | 
					test :: TestTree
 | 
				
			||||||
test = testGroup "Star Wars Query Tests"
 | 
					test = testGroup "Star Wars Query Tests"
 | 
				
			||||||
  [ testGroup "Basic Queries"
 | 
					  [ testGroup "Basic Queries"
 | 
				
			||||||
@@ -118,4 +122,17 @@ test = testGroup "Star Wars Query Tests"
 | 
				
			|||||||
          ]
 | 
					          ]
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					    , testCase "Luke ID with variable" . testQueryParams
 | 
				
			||||||
 | 
					         (\v -> if v == "someId"
 | 
				
			||||||
 | 
					                   then Just "1000"
 | 
				
			||||||
 | 
					                   else Nothing)
 | 
				
			||||||
 | 
					         [r| query FetchSomeIDQuery($someId: String!) {
 | 
				
			||||||
 | 
					               human(id: $someId) {
 | 
				
			||||||
 | 
					                 name
 | 
				
			||||||
 | 
					               }
 | 
				
			||||||
 | 
					             }
 | 
				
			||||||
 | 
					         |]
 | 
				
			||||||
 | 
					      $ object [
 | 
				
			||||||
 | 
					          "human" .= object ["name" .= ("Luke Skywalker" :: Text)]
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user