3 Commits

Author SHA1 Message Date
d2ea9fb467 Release 1.2.0.2
All checks were successful
Build / audit (push) Successful in 15m2s
Build / test (push) Successful in 8m0s
Build / doc (push) Successful in 6m51s
2024-01-08 22:29:58 +01:00
809f446ff1 Fix variable location in objects and lists
All checks were successful
Build / audit (push) Successful in 15m35s
Build / test (push) Successful in 8m6s
Build / doc (push) Successful in 6m59s
2024-01-05 20:46:02 +01:00
b1b6bfcdb9 Add a test for the wrong variable location
All checks were successful
Build / audit (push) Successful in 16m30s
Build / test (push) Successful in 8m26s
Build / doc (push) Successful in 7m6s
inside an input object for the role checking for unused and undefined
variables.
2023-12-28 09:45:39 +01:00
5 changed files with 52 additions and 23 deletions

View File

@ -6,11 +6,12 @@ The format is based on
and this project adheres to and this project adheres to
[Haskell Package Versioning Policy](https://pvp.haskell.org/). [Haskell Package Versioning Policy](https://pvp.haskell.org/).
## [Unreleased] ## [1.2.0.2] - 2024-01-09
### Fixed ### Fixed
- `gql` removes not only leading `\n` but also `\r`. - `gql` removes not only leading `\n` but also `\r`.
- Fix non nullable type string representation in executor error messages. - Fix non nullable type string representation in executor error messages.
- Fix input objects not being coerced to lists. - Fix input objects not being coerced to lists.
- Fix used variables are not found in the properties of input objects.
## [1.2.0.1] - 2023-04-25 ## [1.2.0.1] - 2023-04-25
### Fixed ### Fixed
@ -514,7 +515,7 @@ and this project adheres to
### Added ### Added
- Data types for the GraphQL language. - Data types for the GraphQL language.
[Unreleased]: https://git.caraus.tech/OSS/graphql/compare/v1.2.0.1...master [1.2.0.2]: https://git.caraus.tech/OSS/graphql/compare/v1.2.0.1...v1.2.0.2
[1.2.0.1]: https://git.caraus.tech/OSS/graphql/compare/v1.2.0.0...v1.2.0.1 [1.2.0.1]: https://git.caraus.tech/OSS/graphql/compare/v1.2.0.0...v1.2.0.1
[1.2.0.0]: https://git.caraus.tech/OSS/graphql/compare/v1.1.0.0...v1.2.0.0 [1.2.0.0]: https://git.caraus.tech/OSS/graphql/compare/v1.1.0.0...v1.2.0.0
[1.1.0.0]: https://git.caraus.tech/OSS/graphql/compare/v1.0.3.0...v1.1.0.0 [1.1.0.0]: https://git.caraus.tech/OSS/graphql/compare/v1.0.3.0...v1.1.0.0

View File

@ -1,7 +1,7 @@
cabal-version: 2.4 cabal-version: 2.4
name: graphql name: graphql
version: 1.2.0.1 version: 1.2.0.2
synopsis: Haskell GraphQL implementation synopsis: Haskell GraphQL implementation
description: Haskell <https://spec.graphql.org/June2018/ GraphQL> implementation. description: Haskell <https://spec.graphql.org/June2018/ GraphQL> implementation.
category: Language category: Language
@ -11,7 +11,7 @@ author: Danny Navarro <j@dannynavarro.net>,
Matthías Páll Gissurarson <mpg@mpg.is>, Matthías Páll Gissurarson <mpg@mpg.is>,
Sólrún Halla Einarsdóttir <she@mpg.is> Sólrún Halla Einarsdóttir <she@mpg.is>
maintainer: belka@caraus.de maintainer: belka@caraus.de
copyright: (c) 2019-2023 Eugen Wissner, copyright: (c) 2019-2024 Eugen Wissner,
(c) 2015-2017 J. Daniel Navarro (c) 2015-2017 J. Daniel Navarro
license: MPL-2.0 AND BSD-3-Clause license: MPL-2.0 AND BSD-3-Clause
license-files: LICENSE, license-files: LICENSE,

View File

@ -2,11 +2,13 @@
v. 2.0. If a copy of the MPL was not distributed with this file, You can v. 2.0. If a copy of the MPL was not distributed with this file, You can
obtain one at https://mozilla.org/MPL/2.0/. -} obtain one at https://mozilla.org/MPL/2.0/. -}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
-- | This module contains default rules defined in the GraphQL specification. -- | This module contains default rules defined in the GraphQL specification.
@ -61,6 +63,7 @@ import Data.Sequence (Seq(..), (|>))
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as Text import qualified Data.Text as Text
import GHC.Records (HasField(..))
import qualified Language.GraphQL.AST.Document as Full import qualified Language.GraphQL.AST.Document as Full
import qualified Language.GraphQL.Type.Definition as Definition import qualified Language.GraphQL.Type.Definition as Definition
import qualified Language.GraphQL.Type.Internal as Type import qualified Language.GraphQL.Type.Internal as Type
@ -668,25 +671,16 @@ variableUsageDifference difference errorMessage = OperationDefinitionRule $ \cas
= filterSelections' selections = filterSelections' selections
>>= lift . mapReaderT (<> mapDirectives directives') . pure >>= lift . mapReaderT (<> mapDirectives directives') . pure
findDirectiveVariables (Full.Directive _ arguments _) = mapArguments arguments findDirectiveVariables (Full.Directive _ arguments _) = mapArguments arguments
mapArguments = Seq.fromList . (>>= findArgumentVariables) mapArguments = Seq.fromList . (>>= findArgumentVariables)
mapDirectives = foldMap findDirectiveVariables mapDirectives = foldMap findDirectiveVariables
findArgumentVariables (Full.Argument _ Full.Node{node = value, ..} _) = findArgumentVariables (Full.Argument _ value _) = findNodeVariables value
findValueVariables location value findNodeVariables Full.Node{ node = value, ..} = findValueVariables location value
findValueVariables location (Full.Variable value') = [(value', [location])] findValueVariables location (Full.Variable value') = [(value', [location])]
findValueVariables location (Full.List values) = findValueVariables _ (Full.List values) = values >>= findNodeVariables
values findValueVariables _ (Full.Object fields) = fields
>>= (\(Full.Node{node = value}) -> findValueVariables location value) >>= findNodeVariables . getField @"value"
findValueVariables _ (Full.Object fields) =
fields
>>= ( \( Full.ObjectField
{ location = location
, value = Full.Node{node = value}
}
) -> findValueVariables location value
)
findValueVariables _ _ = [] findValueVariables _ _ = []
makeError operationName (variableName, locations') = Error makeError operationName (variableName, locations') = Error
{ message = errorMessage operationName variableName { message = errorMessage operationName variableName

View File

@ -18,7 +18,7 @@ import Language.GraphQL.Type
import qualified Language.GraphQL.Type.In as In import qualified Language.GraphQL.Type.In as In
import qualified Language.GraphQL.Type.Out as Out import qualified Language.GraphQL.Type.Out as Out
import Language.GraphQL.Validate import Language.GraphQL.Validate
import Test.Hspec (Spec, context, describe, it, shouldBe, shouldContain, xit) import Test.Hspec (Spec, context, describe, it, shouldBe, shouldContain)
import Text.Megaparsec (parse, errorBundlePretty) import Text.Megaparsec (parse, errorBundlePretty)
petSchema :: Schema IO petSchema :: Schema IO
@ -29,6 +29,7 @@ queryType = ObjectType "Query" Nothing [] $ HashMap.fromList
[ ("dog", dogResolver) [ ("dog", dogResolver)
, ("cat", catResolver) , ("cat", catResolver)
, ("findDog", findDogResolver) , ("findDog", findDogResolver)
, ("findCats", findCatsResolver)
] ]
where where
dogField = Field Nothing (Out.NamedObjectType dogType) mempty dogField = Field Nothing (Out.NamedObjectType dogType) mempty
@ -39,6 +40,11 @@ queryType = ObjectType "Query" Nothing [] $ HashMap.fromList
findDogResolver = ValueResolver findDogField $ pure Null findDogResolver = ValueResolver findDogField $ pure Null
catField = Field Nothing (Out.NamedObjectType catType) mempty catField = Field Nothing (Out.NamedObjectType catType) mempty
catResolver = ValueResolver catField $ pure Null catResolver = ValueResolver catField $ pure Null
findCatsArguments = HashMap.singleton "commands"
$ In.Argument Nothing (In.NonNullListType $ In.NonNullEnumType catCommandType)
$ Just $ List []
findCatsField = Field Nothing (Out.NonNullListType $ Out.NonNullObjectType catType) findCatsArguments
findCatsResolver = ValueResolver findCatsField $ pure $ List []
catCommandType :: EnumType catCommandType :: EnumType
catCommandType = EnumType "CatCommand" Nothing $ HashMap.fromList catCommandType = EnumType "CatCommand" Nothing $ HashMap.fromList
@ -538,7 +544,7 @@ spec =
} }
in validate queryString `shouldContain` [expected] in validate queryString `shouldContain` [expected]
context "noUndefinedVariablesRule" $ context "noUndefinedVariablesRule" $ do
it "rejects undefined variables" $ it "rejects undefined variables" $
let queryString = [gql| let queryString = [gql|
query variableIsNotDefinedUsedInSingleFragment { query variableIsNotDefinedUsedInSingleFragment {
@ -560,6 +566,34 @@ spec =
} }
in validate queryString `shouldBe` [expected] in validate queryString `shouldBe` [expected]
it "gets variable location inside an input object" $
let queryString = [gql|
query {
findDog (complex: { name: $name }) {
name
}
}
|]
expected = Error
{ message = "Variable \"$name\" is not defined."
, locations = [AST.Location 2 29]
}
in validate queryString `shouldBe` [expected]
it "gets variable location inside an array" $
let queryString = [gql|
query {
findCats (commands: [JUMP, $command]) {
name
}
}
|]
expected = Error
{ message = "Variable \"$command\" is not defined."
, locations = [AST.Location 2 30]
}
in validate queryString `shouldBe` [expected]
context "noUnusedVariablesRule" $ do context "noUnusedVariablesRule" $ do
it "rejects unused variables" $ it "rejects unused variables" $
let queryString = [gql| let queryString = [gql|
@ -577,7 +611,7 @@ spec =
} }
in validate queryString `shouldBe` [expected] in validate queryString `shouldBe` [expected]
xit "detects variables in properties of input objects" $ it "detects variables in properties of input objects" $
let queryString = [gql| let queryString = [gql|
query withVar ($name: String!) { query withVar ($name: String!) {
findDog (complex: { name: $name }) { findDog (complex: { name: $name }) {