Skip to content

Commit

Permalink
Release 0.8.0 (#333)
Browse files Browse the repository at this point in the history
* syntax

* update readme

* update

* update

* update index.md

* update version number

* update realese date

* 2019
  • Loading branch information
nalchevanidze authored Dec 15, 2019
1 parent 7e117e6 commit 3034ac7
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 32 deletions.
97 changes: 85 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ _stack.yml_
resolver: lts-14.8

extra-deps:
- morpheus-graphql-0.7.1
- morpheus-graphql-0.8.0
```
As Morpheus is quite new, make sure stack can find morpheus-graphql by running `stack upgrade` and `stack update`
Expand Down Expand Up @@ -118,10 +118,7 @@ data Query m = Query
data Deity = Deity
{ fullName :: Text -- Non-Nullable Field
, power :: Maybe Text -- Nullable Field
} deriving (Generic)
instance GQLType Deity where
type KIND Deity = OBJECT
} deriving (Generic,GQLType)
data DeityArgs = DeityArgs
{ name :: Text -- Required Argument
Expand Down Expand Up @@ -152,8 +149,6 @@ askDB :: Text -> Maybe Text -> IO (Either String Deity)
askDB = ...
```

Note that the type `a -> IORes b` is just Synonym for `a -> ExceptT String IO b`

To make this `Query` type available as an API, we define a `GQLRootResolver` and feed it to the Morpheus `interpreter`. A `GQLRootResolver` consists of `query`, `mutation` and `subscription` definitions, while we omit the latter for this example:

```haskell
Expand Down Expand Up @@ -236,14 +231,70 @@ To use union type, all you have to do is derive the `GQLType` class. Using Graph

```haskell
data Character
= DEITY Deity
| HUMAN Human
deriving (Generic)
= data Character =
CharacterDeity Deity -- Only <tyconName><conName> should generate direct link
-- RECORDS
| Creature { creatureName :: Text, creatureAge :: Int }
--- Types
| SomeDeity Deity
| CharacterInt Int
| SomeMutli Int Text
--- ENUMS
| Zeus
| Cronus
deriving (Generic, GQLType)
```

where deity is and object

as you see there ar different kinds of unions. `morpheus` handles them all.

this type will be represented as

```gql
union Character =
Deity # unwrapped union: becouse Character + Deity = CharacterDeity
| Creature
| SomeDeity # wrapped union: becouse Character + Deity != SomeDeity
| CharacterInt
| SomeMutli
| CharacterEnumObject # object wrapped for enums
type Creature {
creatureName: String!
creatureAge: Int!
}
type SomeDeity {
_0: Deity!
}
type CharacterInt {
_0: Int!
}
type SomeMutli {
_0: Int!
_1: String!
}
instance GQLType Character where
type KIND Character = UNION
# enum
type CharacterEnumObject {
enum: CharacterEnum!
}
enum CharacterEnum {
Zeus
Cronus
}
```

- namespaced Unions: `CharacterDeity` where `Character` is TypeConstructor and `Deity` referenced object (not scalar) type: will be generate regular graphql Union

- for for all other unions will be generated new object type. for types without record syntaxt, fields will be automatally indexed.

- all empty constructors in union will be summed in type `<tyConName>Enum` (e.g `CharacterEnum`), this enum will be wrapped in `CharacterEnumObject` and added to union members.

### Scalar types

To use custom scalar types, you need to provide implementations for `parseValue` and `serialize` respectively.
Expand Down Expand Up @@ -286,6 +337,28 @@ screenshots from `Insomnia`
![alt text](https://morpheusgraphql.com/assets/img/introspection/autocomplete.png "autocomplete")
![alt text](https://morpheusgraphql.com/assets/img/introspection/type.png "type")

## Handling Errors

for errors you can use use either `liftEither` or `failRes`:
at the and they have same result.

with `liftEither`

```haskell
resolveDeity :: DeityArgs -> IORes e Deity
resolveDeity DeityArgs {} = liftEither $ dbDeity
dbDeity :: IO Either Deity
dbDeity = pure $ Left "db error"
```

with `failRes`

```haskell
resolveDeity :: DeityArgs -> IORes e Deity
resolveDeity DeityArgs { } = failRes "db error"
```

### Mutations

In addition to queries, Morpheus also supports mutations. The behave just like regular queries and are defined similarly:
Expand Down
2 changes: 1 addition & 1 deletion changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## [0.8.0] -
## [0.8.0] - 15.12.2019

### Changed

Expand Down
119 changes: 105 additions & 14 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
---
layout: home
---

# Morpheus GraphQL [![Hackage](https://img.shields.io/hackage/v/morpheus-graphql.svg)](https://hackage.haskell.org/package/morpheus-graphql) [![CircleCI](https://circleci.com/gh/morpheusgraphql/morpheus-graphql.svg?style=svg)](https://circleci.com/gh/morpheusgraphql/morpheus-graphql)

Build GraphQL APIs with your favourite functional language!
Expand Down Expand Up @@ -33,7 +32,7 @@ _stack.yml_
resolver: lts-14.8

extra-deps:
- morpheus-graphql-0.7.1
- morpheus-graphql-0.8.0
```
As Morpheus is quite new, make sure stack can find morpheus-graphql by running `stack upgrade` and `stack update`
Expand Down Expand Up @@ -122,10 +121,7 @@ data Query m = Query
data Deity = Deity
{ fullName :: Text -- Non-Nullable Field
, power :: Maybe Text -- Nullable Field
} deriving (Generic)
instance GQLType Deity where
type KIND Deity = OBJECT
} deriving (Generic,GQLType)
data DeityArgs = DeityArgs
{ name :: Text -- Required Argument
Expand Down Expand Up @@ -156,8 +152,6 @@ askDB :: Text -> Maybe Text -> IO (Either String Deity)
askDB = ...
```

Note that the type `a -> IORes b` is just Synonym for `a -> ExceptT String IO b`

To make this `Query` type available as an API, we define a `GQLRootResolver` and feed it to the Morpheus `interpreter`. A `GQLRootResolver` consists of `query`, `mutation` and `subscription` definitions, while we omit the latter for this example:

```haskell
Expand Down Expand Up @@ -240,14 +234,70 @@ To use union type, all you have to do is derive the `GQLType` class. Using Graph

```haskell
data Character
= DEITY Deity
| HUMAN Human
deriving (Generic)
= data Character =
CharacterDeity Deity -- Only <tyconName><conName> should generate direct link
-- RECORDS
| Creature { creatureName :: Text, creatureAge :: Int }
--- Types
| SomeDeity Deity
| CharacterInt Int
| SomeMutli Int Text
--- ENUMS
| Zeus
| Cronus
deriving (Generic, GQLType)
```

where deity is and object

as you see there ar different kinds of unions. `morpheus` handles them all.

instance GQLType Character where
type KIND City = UNION
this type will be represented as

```gql
union Character =
Deity # unwrapped union: becouse Character + Deity = CharacterDeity
| Creature
| SomeDeity # wrapped union: becouse Character + Deity != SomeDeity
| CharacterInt
| SomeMutli
| CharacterEnumObject # object wrapped for enums
type Creature {
creatureName: String!
creatureAge: Int!
}
type SomeDeity {
_0: Deity!
}
type CharacterInt {
_0: Int!
}
type SomeMutli {
_0: Int!
_1: String!
}
# enum
type CharacterEnumObject {
enum: CharacterEnum!
}
enum CharacterEnum {
Zeus
Cronus
}
```

- namespaced Unions: `CharacterDeity` where `Character` is TypeConstructor and `Deity` referenced object (not scalar) type: will be generate regular graphql Union

- for for all other unions will be generated new object type. for types without record syntaxt, fields will be automatally indexed.

- all empty constructors in union will be summed in type `<tyConName>Enum` (e.g `CharacterEnum`), this enum will be wrapped in `CharacterEnumObject` and added to union members.

### Scalar types

To use custom scalar types, you need to provide implementations for `parseValue` and `serialize` respectively.
Expand Down Expand Up @@ -290,6 +340,28 @@ screenshots from `Insomnia`
![alt text](https://morpheusgraphql.com/assets/img/introspection/autocomplete.png "autocomplete")
![alt text](https://morpheusgraphql.com/assets/img/introspection/type.png "type")

## Handling Errors

for errors you can use use either `liftEither` or `failRes`:
at the and they have same result.

with `liftEither`

```haskell
resolveDeity :: DeityArgs -> IORes e Deity
resolveDeity DeityArgs {} = liftEither $ dbDeity
dbDeity :: IO Either Deity
dbDeity = pure $ Left "db error"
```

with `failRes`

```haskell
resolveDeity :: DeityArgs -> IORes e Deity
resolveDeity DeityArgs { } = failRes "db error"
```

### Mutations

In addition to queries, Morpheus also supports mutations. The behave just like regular queries and are defined similarly:
Expand Down Expand Up @@ -445,7 +517,26 @@ with `fetch` you can fetch well typed response `GetHero`.
jsonRes = <GraphQL APi>
```

types can be generatet from `introspection` too:
in this case, `jsonRes` is resolves a request into a response in some monad `m`.

A `fetch` resolver implementation against [a real API](https://swapi.graph.cool) may look like the following:

```haskell
{-# LANGUAGE OverloadedStrings #-}
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Char8 as C8
import Network.HTTP.Req
resolver :: String -> ByteString -> IO ByteString
resolver tok b = runReq defaultHttpConfig $ do
let headers = header "Content-Type" "application/json"
responseBody <$> req POST (https "swapi.graph.cool") (ReqBodyLbs b) lbsResponse headers
```

this is demonstrated in examples/src/Client/StarWarsClient.hs

types can be generated from `introspection` too:

```haskell
defineByIntrospectionFile "./introspection.json"
Expand Down
2 changes: 1 addition & 1 deletion examples/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ dependencies:
- containers >= 0.4.2.1 && < 0.7
- mtl >= 2.0 && <= 2.3
- websockets >= 0.11.0 && <= 0.13
- morpheus-graphql >= 0.7.1
- morpheus-graphql >= 0.8.0
- scotty
- wai
- warp
Expand Down
2 changes: 1 addition & 1 deletion package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: morpheus-graphql
version: 0.7.1
version: 0.8.0
github: "nalchevanidze/morpheus-graphql"
license: MIT
author: "Daviti Nalchevanidze"
Expand Down
6 changes: 3 additions & 3 deletions specifications.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,6 @@ because there will be no unused fragment we will validate all sub fields.

### InputValue Errors:

- **expectedAFoundB**: input value does matches to schema type ✅
- **undefinedField**: required field not found on input value ✅
- **unknownField**: field does not exists on inputObject ✅
- **expectedAFoundB**: input value does matches to schema type ✅ + 🧪
- **undefinedField**: required field not found on input value ✅ + 🧪
- **unknownField**: field does not exists on inputObject ✅ + 🧪

0 comments on commit 3034ac7

Please sign in to comment.