Skip unknown fields

This commit is contained in:
Eugen Wissner 2020-06-10 11:42:00 +02:00
parent fdb1268213
commit c37b9c88b1
4 changed files with 51 additions and 10 deletions

View File

@ -13,10 +13,11 @@ and this project adheres to
constants cannot be variables. `AST.Document.ConstValue` was added, constants cannot be variables. `AST.Document.ConstValue` was added,
`AST.Document.ObjectField` was modified. `AST.Document.ObjectField` was modified.
- AST transformation should never fail. - AST transformation should never fail.
* Missing variable are assumed to be null. * Arguments and fields with a missing variable as value should be left out.
* Invalid (recusrive or non-existing) fragments should be skipped. * Invalid (recusrive or non-existing) fragments should be skipped.
- Argument value coercion. - Argument value coercion.
- Variable value coercion. - Variable value coercion.
- The executor should skip the fields missing in the object type and not fail.
### Changed ### Changed
- `Schema.Resolver` was moved to `Type.Out`, it is a field and resolver function - `Schema.Resolver` was moved to `Type.Out`, it is a field and resolver function

View File

@ -17,7 +17,6 @@ import qualified Data.Map.Strict as Map
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Sequence (Seq(..)) import Data.Sequence (Seq(..))
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
import Language.GraphQL.AST (Name) import Language.GraphQL.AST (Name)
import Language.GraphQL.AST.Core import Language.GraphQL.AST.Core
@ -100,10 +99,10 @@ instanceOf objectType (AbstractUnionType unionType) =
executeField :: Monad m executeField :: Monad m
=> Definition.Value => Definition.Value
-> Out.Resolver m
-> Field m -> Field m
-> Out.Resolver m
-> CollectErrsT m Aeson.Value -> CollectErrsT m Aeson.Value
executeField prev (Out.Resolver fieldDefinition resolver) field = do executeField prev field (Out.Resolver fieldDefinition resolver) = do
let Out.Field _ fieldType argumentDefinitions = fieldDefinition let Out.Field _ fieldType argumentDefinitions = fieldDefinition
let Field _ _ arguments' _ = field let Field _ _ arguments' _ = field
case coerceArgumentValues argumentDefinitions arguments' of case coerceArgumentValues argumentDefinitions arguments' of
@ -160,13 +159,12 @@ executeSelectionSet result objectType@(Out.ObjectType _ _ _ resolvers) selection
pure $ Aeson.toJSON resolvedValues pure $ Aeson.toJSON resolvedValues
where where
forEach _responseKey (field :<| _) = forEach _responseKey (field :<| _) =
tryResolvers field >>= lift . pure . pure let Field _ name _ _ = field
in traverse (tryResolver field) $ lookupResolver name
forEach _ _ = pure Nothing forEach _ _ = pure Nothing
lookupResolver = flip HashMap.lookup resolvers lookupResolver = flip HashMap.lookup resolvers
tryResolvers fld@(Field _ name _ _) tryResolver typeField field =
| Just typeField <- lookupResolver name = executeField result typeField field >>= lift . pure
executeField result typeField fld
| otherwise = errmsg $ Text.unwords ["field", name, "not resolved."]
coerceArgumentValues coerceArgumentValues
:: HashMap Name In.Argument :: HashMap Name In.Argument

View File

@ -1,4 +1,4 @@
resolver: lts-15.16 resolver: lts-16.0
packages: packages:
- . - .

View File

@ -0,0 +1,42 @@
{-# LANGUAGE OverloadedStrings #-}
module Language.GraphQL.ExecuteSpec
( spec
) where
import Data.Aeson ((.=))
import qualified Data.Aeson as Aeson
import Data.Functor.Identity (Identity(..))
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Language.GraphQL.AST (Name)
import Language.GraphQL.AST.Parser (document)
import Language.GraphQL.Error
import Language.GraphQL.Execute
import Language.GraphQL.Type
import Language.GraphQL.Type.Out as Out
import Test.Hspec (Spec, describe, it, shouldBe)
import Text.Megaparsec (parse)
schema :: Schema Identity
schema = Schema {query = queryType, mutation = Nothing}
queryType :: Out.ObjectType Identity
queryType = Out.ObjectType "Query" Nothing []
$ HashMap.singleton "count"
$ Out.Resolver countField
$ pure
$ Int 8
where
countField = Out.Field Nothing (Out.NonNullScalarType int) HashMap.empty
spec :: Spec
spec =
describe "execute" $
it "skips unknown fields" $
let expected = Aeson.object
["data" .= Aeson.object ["count" .= (8 :: Int)]]
execute' = execute schema (mempty :: HashMap Name Aeson.Value)
actual = runIdentity
$ either parseError execute'
$ parse document "" "{ count number }"
in actual `shouldBe` expected