summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md9
-rw-r--r--README.md94
-rw-r--r--src/Test/Hspec/GraphQL.hs9
-rw-r--r--stack.yaml2
4 files changed, 105 insertions, 9 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae76a2c..ecc03af 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,8 @@ and this project adheres to
[Haskell Package Versioning Policy](https://pvp.haskell.org/).
## [Unreleased]
+
+## [0.9.0.0] - 2020-07-24
## Fixed
- Location of a parse error is returned in a singleton array with key
`locations`.
@@ -21,7 +23,7 @@ and this project adheres to
- `Error.Error` is an error representation with a message and source location.
- `Error.Response` represents a result of running a GraphQL query.
- `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
`Type.Out.SourceEventStream` define subscription resolvers.
- `Error.ResolverException` is an exception that can be thrown by (field value
@@ -57,8 +59,6 @@ and this project adheres to
## Removed
- `Trans.ActionT` is an unneeded layer of complexity. `Type.Out.Resolver`
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
completely replaces `executeWithName`.
@@ -323,7 +323,8 @@ and this project adheres to
### Added
- 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.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
diff --git a/README.md b/README.md
index 3b33ad7..917da40 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,100 @@ API documentation is available through
You'll also find a small tutorial with some examples under
[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
- [Contributing guidelines](CONTRIBUTING.md).
diff --git a/src/Test/Hspec/GraphQL.hs b/src/Test/Hspec/GraphQL.hs
index 093685b..253b366 100644
--- a/src/Test/Hspec/GraphQL.hs
+++ b/src/Test/Hspec/GraphQL.hs
@@ -11,6 +11,7 @@ module Test.Hspec.GraphQL
, shouldResolveTo
) where
+import Control.Monad.Catch (MonadCatch)
import qualified Data.Aeson as Aeson
import qualified Data.HashMap.Strict as HashMap
import Data.Text (Text)
@@ -18,8 +19,8 @@ import Language.GraphQL.Error
import Test.Hspec.Expectations (Expectation, expectationFailure, shouldBe, shouldNotSatisfy)
-- | Asserts that a query resolves to some value.
-shouldResolveTo
- :: Either (ResponseEventStream IO Aeson.Value) Aeson.Object
+shouldResolveTo :: MonadCatch m
+ => Either (ResponseEventStream m Aeson.Value) Aeson.Object
-> Aeson.Object
-> Expectation
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"
-- | Asserts that the response doesn't contain any errors.
-shouldResolve
- :: (Text -> IO (Either (ResponseEventStream IO Aeson.Value) Aeson.Object))
+shouldResolve :: MonadCatch m
+ => (Text -> IO (Either (ResponseEventStream m Aeson.Value) Aeson.Object))
-> Text
-> Expectation
shouldResolve executor query = do
diff --git a/stack.yaml b/stack.yaml
index a494b98..042ce94 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,4 +1,4 @@
-resolver: lts-16.6
+resolver: lts-16.10
packages:
- .