Validate required arguments
This commit is contained in:
parent
6daae8a521
commit
d5f518fe82
@ -52,6 +52,8 @@ and this project adheres to
|
|||||||
- `scalarLeafsRule`
|
- `scalarLeafsRule`
|
||||||
- `knownArgumentNamesRule`
|
- `knownArgumentNamesRule`
|
||||||
- `knownDirectiveNamesRule`
|
- `knownDirectiveNamesRule`
|
||||||
|
- `directivesInValidLocationsRule`
|
||||||
|
- `providedRequiredArgumentsRule`
|
||||||
- `AST.Document.Field`.
|
- `AST.Document.Field`.
|
||||||
- `AST.Document.FragmentSpread`.
|
- `AST.Document.FragmentSpread`.
|
||||||
- `AST.Document.InlineFragment`.
|
- `AST.Document.InlineFragment`.
|
||||||
|
@ -76,7 +76,7 @@ instance forall a. Eq (UnionType a) where
|
|||||||
data Field m = Field
|
data Field m = Field
|
||||||
(Maybe Text) -- ^ Description.
|
(Maybe Text) -- ^ Description.
|
||||||
(Type m) -- ^ Field type.
|
(Type m) -- ^ Field type.
|
||||||
(HashMap Name In.Argument) -- ^ Arguments.
|
In.Arguments -- ^ Arguments.
|
||||||
|
|
||||||
-- | These types may be used as output types as the result of fields.
|
-- | These types may be used as output types as the result of fields.
|
||||||
--
|
--
|
||||||
|
@ -24,6 +24,7 @@ module Language.GraphQL.Validate.Rules
|
|||||||
, noUndefinedVariablesRule
|
, noUndefinedVariablesRule
|
||||||
, noUnusedFragmentsRule
|
, noUnusedFragmentsRule
|
||||||
, noUnusedVariablesRule
|
, noUnusedVariablesRule
|
||||||
|
, providedRequiredArgumentsRule
|
||||||
, scalarLeafsRule
|
, scalarLeafsRule
|
||||||
, singleFieldSubscriptionsRule
|
, singleFieldSubscriptionsRule
|
||||||
, specifiedRules
|
, specifiedRules
|
||||||
@ -47,7 +48,7 @@ import Data.HashMap.Strict (HashMap)
|
|||||||
import Data.HashSet (HashSet)
|
import Data.HashSet (HashSet)
|
||||||
import qualified Data.HashSet as HashSet
|
import qualified Data.HashSet as HashSet
|
||||||
import Data.List (groupBy, sortBy, sortOn)
|
import Data.List (groupBy, sortBy, sortOn)
|
||||||
import Data.Maybe (mapMaybe)
|
import Data.Maybe (isNothing, mapMaybe)
|
||||||
import Data.Ord (comparing)
|
import Data.Ord (comparing)
|
||||||
import Data.Sequence (Seq(..), (|>))
|
import Data.Sequence (Seq(..), (|>))
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
@ -79,6 +80,7 @@ specifiedRules =
|
|||||||
-- Arguments.
|
-- Arguments.
|
||||||
, knownArgumentNamesRule
|
, knownArgumentNamesRule
|
||||||
, uniqueArgumentNamesRule
|
, uniqueArgumentNamesRule
|
||||||
|
, providedRequiredArgumentsRule
|
||||||
-- Fragments.
|
-- Fragments.
|
||||||
, uniqueFragmentNamesRule
|
, uniqueFragmentNamesRule
|
||||||
, fragmentSpreadTypeExistenceRule
|
, fragmentSpreadTypeExistenceRule
|
||||||
@ -893,3 +895,64 @@ directivesInValidLocationsRule = DirectivesRule directivesRule
|
|||||||
, show directiveLocation
|
, show directiveLocation
|
||||||
, "."
|
, "."
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | Arguments can be required. An argument is required if the argument type is
|
||||||
|
-- non‐null and does not have a default value. Otherwise, the argument is
|
||||||
|
-- optional.
|
||||||
|
providedRequiredArgumentsRule :: Rule m
|
||||||
|
providedRequiredArgumentsRule = ArgumentsRule fieldRule directiveRule
|
||||||
|
where
|
||||||
|
fieldRule (Just objectType) (Field _ fieldName arguments _ _ location')
|
||||||
|
| Just typeField <- lookupTypeField fieldName objectType
|
||||||
|
, Out.Field _ _ definitions <- typeField =
|
||||||
|
let forEach = go (fieldMessage fieldName) arguments location'
|
||||||
|
in lift $ HashMap.foldrWithKey forEach Seq.empty definitions
|
||||||
|
fieldRule _ _ = lift mempty
|
||||||
|
directiveRule (Directive directiveName arguments location') = do
|
||||||
|
available <- asks $ HashMap.lookup directiveName . directives
|
||||||
|
case available of
|
||||||
|
Just (Schema.Directive _ _ definitions) ->
|
||||||
|
let forEach = go (directiveMessage directiveName) arguments location'
|
||||||
|
in lift $ HashMap.foldrWithKey forEach Seq.empty definitions
|
||||||
|
_ -> lift mempty
|
||||||
|
inputTypeName (In.ScalarBaseType (Definition.ScalarType typeName _)) =
|
||||||
|
typeName
|
||||||
|
inputTypeName (In.EnumBaseType (Definition.EnumType typeName _ _)) =
|
||||||
|
typeName
|
||||||
|
inputTypeName (In.InputObjectBaseType (In.InputObjectType typeName _ _)) =
|
||||||
|
typeName
|
||||||
|
inputTypeName (In.ListBaseType listType) = inputTypeName listType
|
||||||
|
go makeMessage arguments location' argumentName argumentType errors
|
||||||
|
| In.Argument _ type' optionalValue <- argumentType
|
||||||
|
, In.isNonNullType type'
|
||||||
|
, typeName <- inputTypeName type'
|
||||||
|
, isNothing optionalValue
|
||||||
|
, isNothingOrNull $ find (lookupArgument argumentName) arguments
|
||||||
|
= errors
|
||||||
|
|> makeError (makeMessage argumentName typeName) location'
|
||||||
|
| otherwise = errors
|
||||||
|
makeError errorMessage location' = Error
|
||||||
|
{ message = errorMessage
|
||||||
|
, locations = [location']
|
||||||
|
}
|
||||||
|
isNothingOrNull (Just (Argument _ (Node Null _) _)) = True
|
||||||
|
isNothingOrNull x = isNothing x
|
||||||
|
lookupArgument needle (Argument argumentName _ _) = needle == argumentName
|
||||||
|
fieldMessage fieldName argumentName typeName = concat
|
||||||
|
[ "Field \""
|
||||||
|
, Text.unpack fieldName
|
||||||
|
, "\" argument \""
|
||||||
|
, Text.unpack argumentName
|
||||||
|
, "\" of type \""
|
||||||
|
, Text.unpack typeName
|
||||||
|
, "\" is required, but it was not provided."
|
||||||
|
]
|
||||||
|
directiveMessage directiveName argumentName typeName = concat
|
||||||
|
[ "Directive \"@"
|
||||||
|
, Text.unpack directiveName
|
||||||
|
, "\" argument \""
|
||||||
|
, Text.unpack argumentName
|
||||||
|
, "\" of type \""
|
||||||
|
, Text.unpack typeName
|
||||||
|
, "\" is required, but it was not provided."
|
||||||
|
]
|
||||||
|
@ -550,7 +550,7 @@ spec =
|
|||||||
let queryString = [r|
|
let queryString = [r|
|
||||||
{
|
{
|
||||||
dog {
|
dog {
|
||||||
doesKnowCommand(command: CLEAN_UP_HOUSE)
|
doesKnowCommand(command: CLEAN_UP_HOUSE, dogCommand: SIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|]
|
|]
|
||||||
@ -566,7 +566,7 @@ spec =
|
|||||||
let queryString = [r|
|
let queryString = [r|
|
||||||
{
|
{
|
||||||
dog {
|
dog {
|
||||||
isHousetrained(atOtherHomes: true) @include(unless: false)
|
isHousetrained(atOtherHomes: true) @include(unless: false, if: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|]
|
|]
|
||||||
|
Loading…
Reference in New Issue
Block a user