Loose monad condition on test methods

This commit is contained in:
Eugen Wissner 2020-08-18 20:53:47 +02:00
parent c60dd98fc5
commit 1a788a6261
4 changed files with 105 additions and 9 deletions

View File

@ -7,6 +7,8 @@ and this project adheres to
[Haskell Package Versioning Policy](https://pvp.haskell.org/). [Haskell Package Versioning Policy](https://pvp.haskell.org/).
## [Unreleased] ## [Unreleased]
## [0.9.0.0] - 2020-07-24
## Fixed ## Fixed
- Location of a parse error is returned in a singleton array with key - Location of a parse error is returned in a singleton array with key
`locations`. `locations`.
@ -21,7 +23,7 @@ and this project adheres to
- `Error.Error` is an error representation with a message and source location. - `Error.Error` is an error representation with a message and source location.
- `Error.Response` represents a result of running a GraphQL query. - `Error.Response` represents a result of running a GraphQL query.
- `Type.Schema` exports `Type` which lists all types possible in the schema. - `Type.Schema` exports `Type` which lists all types possible in the schema.
- Parsing subscriptions (the execution always fails yet). - Parsing subscriptions.
- `Error.ResponseEventStream`, `Type.Out.Resolve`, `Type.Out.Subscribe` and - `Error.ResponseEventStream`, `Type.Out.Resolve`, `Type.Out.Subscribe` and
`Type.Out.SourceEventStream` define subscription resolvers. `Type.Out.SourceEventStream` define subscription resolvers.
- `Error.ResolverException` is an exception that can be thrown by (field value - `Error.ResolverException` is an exception that can be thrown by (field value
@ -57,8 +59,6 @@ and this project adheres to
## Removed ## Removed
- `Trans.ActionT` is an unneeded layer of complexity. `Type.Out.Resolver` - `Trans.ActionT` is an unneeded layer of complexity. `Type.Out.Resolver`
represents possible resolver configurations. represents possible resolver configurations.
- `Type.Out.Resolver`: It . Resolvers are a
part of the fields and are called `Trans.ResolverT`.
- `Execute.executeWithName`. `Execute.execute` takes the operation name and - `Execute.executeWithName`. `Execute.execute` takes the operation name and
completely replaces `executeWithName`. completely replaces `executeWithName`.
@ -323,7 +323,8 @@ and this project adheres to
### Added ### Added
- Data types for the GraphQL language. - Data types for the GraphQL language.
[Unreleased]: https://github.com/caraus-ecms/graphql/compare/v0.8.0.0...HEAD [Unreleased]: https://github.com/caraus-ecms/graphql/compare/v0.9.0.0...HEAD
[0.9.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.8.0.0...v0.9.0.0
[0.8.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.7.0.0...v0.8.0.0 [0.8.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.7.0.0...v0.8.0.0
[0.7.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.6.1.0...v0.7.0.0 [0.7.0.0]: https://github.com/caraus-ecms/graphql/compare/v0.6.1.0...v0.7.0.0
[0.6.1.0]: https://github.com/caraus-ecms/graphql/compare/v0.6.0.0...v0.6.1.0 [0.6.1.0]: https://github.com/caraus-ecms/graphql/compare/v0.6.0.0...v0.6.1.0

View File

@ -29,6 +29,100 @@ API documentation is available through
You'll also find a small tutorial with some examples under You'll also find a small tutorial with some examples under
[docs/tutorial](https://github.com/caraus-ecms/graphql/tree/master/docs/tutorial). [docs/tutorial](https://github.com/caraus-ecms/graphql/tree/master/docs/tutorial).
### Getting started
We start with a simple GraphQL API that provides us with some famous and less
famous cites.
```graphql
"""
Root Query type.
"""
type Query {
"""
Provides a cite.
"""
cite: String!
}
```
This is called a GraphQL schema, it defines all queries supported by the API.
`Query` is the root query type. Every GraphQL API should define a query type.
`Query` has a single field `cite` that returns a `String`. The `!` after the
type denotes that the returned value cannot be `Null`. GraphQL fields are
nullable by default.
To be able to work with this schema, we are going to implement it in Haskell.
```haskell
{-# LANGUAGE OverloadedStrings #-}
import Control.Exception (SomeException)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy.Char8 as ByteString.Lazy.Char8
import qualified Data.HashMap.Strict as HashMap
import Language.GraphQL
import Language.GraphQL.Type
import qualified Language.GraphQL.Type.Out as Out
-- GraphQL supports 3 kinds of operations: queries, mutations and subscriptions.
-- Our first schema supports only queries.
schema :: Schema IO
schema = Schema
{ query = queryType, mutation = Nothing, subscription = Nothing }
-- GraphQL distinguishes between input and output types. Input types are field
-- argument types and they are defined in Language.GraphQL.Type.In. Output types
-- are result types, they are defined in Language.GraphQL.Type.Out. Root types
-- are always object types.
--
-- Here we define a type "Query". The second argument is an optional
-- description, the third one is the list of interfaces implemented by the
-- object type. The last argument is a field map. Keys are field names, values
-- are field definitions and resolvers. Resolvers are the functions, where the
-- actual logic lives, they return values for the respective fields.
queryType :: Out.ObjectType IO
queryType = Out.ObjectType "Query" (Just "Root Query type.") []
$ HashMap.singleton "cite" citeResolver
where
-- 'ValueResolver' is a 'Resolver' data constructor, it combines a field
-- definition with its resolver function. This function resolves a value for
-- a field (as opposed to the 'EventStreamResolver' used by subscriptions).
-- Our resolver just returns a constant value.
citeResolver = ValueResolver citeField
$ pure "Piscis primum a capite foetat"
-- The first argument is an optional field description. The second one is
-- the field type and the third one is for arguments (we have none in this
-- example).
--
-- GraphQL has named and wrapping types. String is a scalar, named type.
-- Named types are nullable by default. To make our "cite" field
-- non-nullable, we wrap it in the wrapping type, Non-Null.
citeField = Out.Field
(Just "Provides a cite.") (Out.NonNullScalarType string) HashMap.empty
-- Now we can execute a query. Since our schema defines only one field,
-- everything we can do is to ask to resolve it and give back the result.
-- Since subscriptions don't return plain values, the 'graphql' function returns
-- an 'Either'. 'Left' is for subscriptions, 'Right' is for queries and
-- mutations.
main :: IO ()
main = do
Right result <- graphql schema "{ cite }"
ByteString.Lazy.Char8.putStrLn $ Aeson.encode result
```
Executing this query produces the following JSON:
```json
{
"data": {
"cite": "Piscis primum a capite foetat"
}
}
```
## Further information ## Further information
- [Contributing guidelines](CONTRIBUTING.md). - [Contributing guidelines](CONTRIBUTING.md).

View File

@ -11,6 +11,7 @@ module Test.Hspec.GraphQL
, shouldResolveTo , shouldResolveTo
) where ) where
import Control.Monad.Catch (MonadCatch)
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
import Data.Text (Text) import Data.Text (Text)
@ -18,8 +19,8 @@ import Language.GraphQL.Error
import Test.Hspec.Expectations (Expectation, expectationFailure, shouldBe, shouldNotSatisfy) import Test.Hspec.Expectations (Expectation, expectationFailure, shouldBe, shouldNotSatisfy)
-- | Asserts that a query resolves to some value. -- | Asserts that a query resolves to some value.
shouldResolveTo shouldResolveTo :: MonadCatch m
:: Either (ResponseEventStream IO Aeson.Value) Aeson.Object => Either (ResponseEventStream m Aeson.Value) Aeson.Object
-> Aeson.Object -> Aeson.Object
-> Expectation -> Expectation
shouldResolveTo (Right actual) expected = actual `shouldBe` expected shouldResolveTo (Right actual) expected = actual `shouldBe` expected
@ -27,8 +28,8 @@ shouldResolveTo _ _ = expectationFailure
"the query is expected to resolve to a value, but it resolved to an event stream" "the query is expected to resolve to a value, but it resolved to an event stream"
-- | Asserts that the response doesn't contain any errors. -- | Asserts that the response doesn't contain any errors.
shouldResolve shouldResolve :: MonadCatch m
:: (Text -> IO (Either (ResponseEventStream IO Aeson.Value) Aeson.Object)) => (Text -> IO (Either (ResponseEventStream m Aeson.Value) Aeson.Object))
-> Text -> Text
-> Expectation -> Expectation
shouldResolve executor query = do shouldResolve executor query = do

View File

@ -1,4 +1,4 @@
resolver: lts-16.6 resolver: lts-16.10
packages: packages:
- . - .