Changelog for morpheus-graphql-0.11.0
Changelog
0.11.0 - 01.05.2020
Breaking Changes
-
Client generated enum data constructors are now prefixed with with the type name to avoid name conflicts.
-
for Variant selection inputUnion uses
inputnameinsead of__typename -
in
Data.Morpheus.ServergqlSocketAppandgqlSocketMonadIOAppare replaced withwebSocketsApp- removed
initGQLState,GQLState
-
for better control of subscriptions
- replaced instance
interpreter gqlRoot statewithinterpreter gqlRoot. - added:
Input,Stream,httpPubApp
from now on you can define API that can be used in websockets as well as in http servers
api :: Input api -> Stream api EVENT IO api = interpreter gqlRoot server :: IO () server = do (wsApp, publish) <- webSocketsApp api let httpApp = httpPubApp api publish ... runBoth wsApp httpAppwhere
publish :: e -> m ()websockets and http app do not have to be on the same server. e.g. you can pass events between servers with webhooks.
- replaced instance
-
subscription can select only one top level field (based on the GraphQL specification).
New features
- Instead of rejecting conflicting selections, they are merged (based on the GraphQL specification).
- Support for input lists separated by newlines. thanks @charlescrain
- conflicting variable , fragment ... validation
- issue #411: Aeson
FromJSONToJSONinstances forID
minor
- changes to internal types
- fixed validation of apollo websockets requests
0.10.0 - 07.01.2020
Breaking Changes
-
all constructors of
Resolver:QueryResolver,MutResolver,SubResolverare unexposed. uselift,publishorsubscribe. e.g-- Query Resolver resolveUser :: ResolveQ EVENT IO User resolveUser = lift getDBUser -- Mutation Resolver resolveCreateUser :: ResolveM EVENT IO User resolveCreateUser = do publish [userUpdate] -- publishes event inside mutation lift setDBUser -- Subscription Resolver resolveNewUser :: ResolveS EVENT IO User resolveNewUser = subscribe [USER] $ do pure $ \(Event _ content) -> lift (getDBUserByContent content)
New features
-
exposed
publishfor mutation resolvers, now you can writeresolveCreateUser :: ResolveM EVENT IO User resolveCreateUser = do requireAuthorized publish [userUpdate] liftEither setDBUser -
exposed
subscribefor subscription resolvers, now you can writeresolveNewUser :: ResolveS EVENT IO User resolveNewUser = subscribe [USER] $ do requireAuthorized pure userByEvent where userByEvent (Event _ content) = liftEither (getDBUser content) -
type SubFieldwill convert your subscription monad to query monad.SubField (Resolver Subscription Event IO) Userwill generate same asResolver Subscription Event IO (User ((Resolver QUERY Event IO)))now if you want define subscription as follows
data Subscription m = Subscription { newUser :: SubField m User } -
unsafeInternalContextto get resolver context, use only if it really necessary. the code depending on it may break even on minor version changes.resolveUser :: ResolveQ EVENT IO User resolveUser = do Context { currentSelection, schema, operation } <- unsafeInternalContext lift (getDBUser currentSelection)
minor
- monadio instance for resolvers. thanks @dandoh
- example using stm, authentication, monad transformers. thanks @dandoh
- added dependency
mtl
[0.9.1] - 02.01.2020
- removed dependency
mtl
[0.9.0] - 02.01.2020
Added
WithOperationconstraint for Generic Resolvers (#347) thanks @dandoh
Fixed
-
liftEither support in MutResolver (#351)
-
selection of
__typenameon object und union objects (#337) -
auto inferece of external types in gql document (#343)
th will generate field
m (Type m)if type has an argumente.g for this types and DSL
data Type1 = Type1 { ... } type Type2 m = SomeType m data Type3 m = Type2 { bla :: m Text } deriving ...type Query { field1: Type1! field2: Type2! field3: Type3! }morpheus generates
data Query m = Query { field1 :: m Type1 field2 :: m (Type2 m) field3 :: m (Type3 m) } deriving ...now you can combine multiple gql documents:
importDocumentWithNamespace `coreTypes.gql` importDocumentWithNamespace `operations.gql`
Changed
-
support of resolver fields
m typefor the fields without argumentsdata Diety m = Deity { name :: m Text } -- is equal to data Diety m = Deity { name :: () -> m Text } -
template haskell generates
m typeinsead of() -> m typefor fields without argument (#334)data Diety m = Deity { name :: (Arrow () (m Text)), power :: (Arrow () (m (Maybe Text))) } -- changed to data Diety m = Deity { name :: m Text, power :: m (Maybe Text) }
[0.8.0] - 15.12.2019
Changed
-
deprecated:
INPUT_OBJECT,OBJECT,UNION,- use
INPUTinstead ofINPUT_OBJECT - use
deriving(GQLType)insead ofOBJECTorUNION
- use
-
only namespaced Unions generate regular graphql Union, other attempts will be wrapped inside an object with constructor name :
e.g:
data Character = CharacterDeity Deity SomeDeity Deity deriving (GQLType)where
Deityis Object. will generateunion CHaracter = Deity | SomeDeity type SomeDeity { _0: Deity }
Added
failResfor resolver failures- added kind: INPUT , OUTPUT
- Automatic Type Inference (only for Object, Union and Enum)
- More general stateful resolvers which accept instances of MonadIO (Authored by Sebastian Pulido [sebashack])
- Utility to create web-socket applications with custom MonadIO instances (Authored by Sebastian Pulido [sebashack])
data Realm =
Sky
| Sea
| Underworld
deriving (Generic, GQLType)
data Deity = Deity{
fullName:: Text,
realm:: Realm
} deriving (Generic, GQLType)
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)
will generate schema:
enum Realm {
Sky
Sea
Underworld
}
type Deity {
fullName: String!
realm: Realm!
}
union Character =
Deity
| Creature
| SomeDeity
| CharacterInt
| SomeMutli
| CharacterEnumObject
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
}
rules:
-
haskell union type with only empty constructors (e.g
Realm), will generate graphqlenum -
haskell record without union (e.g
Deity), will generate graphqlobject -
namespaced Unions:
CharacterDeitywhereCharacteris TypeConstructor andDeityreferenced object (not scalar) type: will be generate regular graphql Unionunion Character = Deity | ... -
for union recrods (
Creature { creatureName :: Text, creatureAge :: Int }) will be referenced in union type, plus typeCreaturewill be added in schema.e.g
union Character = ... | Creature | ... type Creature { creatureName : String! creatureAge: Int! }-
all empty constructors in union will be summed in type
<tyConName>Enum(e.gCharacterEnum), this enum will be wrapped inCharacterEnumObjectand this type will be added to unionCharacter. as in example above -
there is only types left with form
TypeName Type1 2Type ..(e.gSomeDeity Deity,CharacterInt Int,SomeMutli Int Text),morpheus will generate objet type from it:
type TypeName { _0: Type1! _1: Type2! ... }
-
Removed
- removed kind: INPUT_UNION
Fixed
- on filed resolver was displayed. unexhausted case exception of graphql error
- support of signed numbers (e.g
-4) - support of round floats (e.g
1.000) - validation checks undefined fields on inputObject
- variables are supported inside input values
[0.7.1] - 26.11.2019
- max bound icludes: support-megaparsec-8.0
[0.7.0] - 24.11.2019
Removed
toMorpheusHaskellAPifromData.Morpheus.Documentfunctionality will be migrated inmorpheus-graphql-cli
Changed
-
liftMtoMonadTransinstance methodlift -
liftEitherMtoliftEither -
Resolver operation m event value->Resolver operation event m value, monad trans needs that last 2 type arguments are monad and value that why it was necessary -
exposed
Data.Morpheus.Types.Internal.AST -
Mutation Resolver was changed from
resolver :: () -> ResolveM EVENT IO Address
resolver = MutResolver {
mutEvents = [someEventForSubscription],
mutResolver = lift setDBAddress
}
-- Mutation Wit Event Triggering : sends events to subscription
resolver :: () -> ResolveM EVENT IO Address
resolver = MutResolver \$ do
value <- lift setDBAddress
pure ([someEventForSubscription], value)
-- or
-- Mutation Without Event Triggering
resolver :: () -> ResolveM EVENT IO Address
resolver _args = lift setDBAddress
Added
-
added
parseDSLtoData.Morpheus.Document -
GraphQL SDL support fully supports descriptions: onTypes, fields , args ... with (enums, inputObjects , union, object) for example :
""" Description for Type Address """ type Address { """ Description for Field city """ city: String! street( """ Description argument id """ id: ID! ): Int! }GraphQL SDL
type User { name: String! @deprecated(reason: "some reason") }will displayed in introspection
introspection.json
{ "data": { "__type": { "fields": [ { "name": "city", "isDeprecated": true, "deprecationReason": "test deprecation field with reason" } ] } } } -
basic support of directive
@deprecatedonenumValueand objectfield, only on introspection -
GraphQL Client deprecation warnings
on type
type Human { humanName: String! lifetime: Lifetime! @deprecated(reason: "some reason") profession: Profession }compiler output:
warning: Morpheus Client Warning: { "message":"the field \"Human.lifetime\" is deprecated. some reason", "locations":[{"line":24,"column":15}] } -
new helper resolver types aliases:
- ResolveQ : for Query
- ResolveM : for Mutation
- ResolveS : for Subscription
ResolveM EVENT IO Addressis same asMutRes EVENT IO (Address (MutRes EVENT IO))is helpfull wenn you want to resolve GraphQL object
Fixed
- added missing Monad instance for Mutation resolver
defineByIntrospectionFiledoes not breaks if schema contains interfaces- Morpheus Client supports
SubscriptionandMutationoperations
[0.6.2] - 2.11.2019
Added
- support of ghc 8.8.1
[0.6.0] - 1.11.2019
Removed
-
removed
morpheuscli for code generating, if you need cli you should use morpheus-graphql-cli -
example
APIexecutable is removed from Production build
Added
-
helper functions:
liftEitherM,liftMliftM :: m a -> Resolver o m e a liftEitherM :: m (Either String a) -> Resolver o m e a
[0.5.0] - 31.10.2019
Added
- dummy support of
directives, only parsing not actual implementation
Fixed
-
can be parsed
implementswith multiple interfaces separated by& -
can be parsed default value on
inputobject -
Parser supports anonymous Operation:
query,mutation,subscriptionfor example:mutation { name } -
Morpheus client does not breaks on
Booleantype, converts every GraphQL typeBooleanto haskellBooland GQLStringtoText
Changed
-
Reduced
GQLRootResolversignature :GQLRootResolver IO () () Query () ()->GQLRootResolver IO () Query () ()GQLRootResolver IO Channel Content Query Mutation Subscription->GQLRootResolver IO APIEvent Query Mutation Subscriptionwhere
APIEvent = Event Channel Content -
GQLRootResolverautomatically assigns corresponding monad to GraphQL Types.you can write just:
GQLRootResolver IO APIEvent Query Mutation Subscriptioninstead of:
GQLRootResolver IO APIEvent (Query (Resolver IO)) (Mutation (MutResolver IO ApiEvent) (Subscription (SubResolver IO ApiEvent))where operations are generated by
importGQLDocumentor have form :data Query m = Query { field1 :: Args -> m Field1, .... } -
()was replaced withUndefinedinGQLRootResolverfor empty operationsmutation,subscriptionrootResolver :: GQLRootResolver IO () Query Undefined Undefined -
Root Operations
Query,Mutation,Subscriptionare passed to root resolvers without boxing inside a monad. -
there are only 3 kind of resolvers
MutResolver,SubResolver,QueryResolverdefined by GADTResolver
[0.4.0] - 09.10.2019
Changed
-
support of Default Value:
- on query: Parsing Validating and resolving
- on Document: only Parsing
-
'lens' is removed from Library, client field collision can be handled with GraphQL
alias:{ user { name friend { friendName: name } } }
Fixed:
-
Data.Morpheus.Document.toGraphQLDocumentgenerates only my user defined types. #259 -
Morpheus Client Namespaces Input Type Fields, they don't collide anymore: example: schema:
input Person { name: String! }query:
query GetUser (parent: Person!) { .... }wil generate:
data GetUserArgs = GetUserArgs { getUserArgsParent: Person } deriving ... data Person = Person { personName: Person } deriving ... -
Morpheus Client Generated Output Object And Union Types don't collide:
type Person { name: String! parent: Person! friend: Person! }And we select
{ user { name friend { name } parent { name } bestFriend: friend { name parent { name } } } }client will Generate:
UserPersonfrom{userUserFriendPerson: from{user{freindUserParentPerson: from{user{parentUserBestFriendPerson: from{user{bestFrendUserBestFriendParentPerson: from{user{bestFrend{parent
-
GraphQL Client Defines enums and Input Types only once per query and they don't collide
[0.3.1] - 05.10.2019
Changed
- removed dependencies: attoparsec , utf8-string
- updated aeson lower bound up to: 1.4.4.0
[0.3.0] - 04.10.2019
Added
-
user can import GraphQL Document and generate types with it.
importGQLDocument "API.gql"this will generate types defined in
API.gql
Fixed
-
Stringdefined in GQLDcoument will be converted toTextby template haskell -
importGQLDocumentandgqlDocumentsupports Mutation, Subscription and Resolvers with custom Monadfor example. if we have:
type Deity { name: String! power: Power! }where
Poweris another object defined by gql schema. template haskell will represent this type as:data Deity m = Deity { name :: () -> m Text, power :: () -> m (Power m) }where
mis resolver Monad. -
importGQLDocumentWithNamespacegenerates namespaced haskell records. so that you have no more problem with name collision. from this gql type:type Deity { name: (id:Int)String! power: Power! }will be generated.
data Deity m = Deity { deityName :: DeityNameArgs -> m Text, deityPower :: () -> m (Power m) } data DeityNameArgs = DeityNameArgs { deityNameArgsId :: Int }
Changed
-
GQLTypeis mandatory for every GQL Type (including Query, Mutation and Subscription) -
subscription Resolver changed
from:
Subscription {newDeity = \args -> Event {channels = [ChannelA], content = newDeityResolver } }to:
Subscription {newDeity = \args -> SubResolver {subChannels = [ChannelA], subResolver = newDeityResolver } }
[0.2.2] - 30.08.2019
Fixed
-
Parser Supports GraphQL multiline comments
-
Morpheus GraphQL Client: Support GraphQL Alias
-
Support of GraphQL Interfaces on GraphQL Document:
# simple.gql interface Node { nodeId: ID! } type SimpleType implements Node { nodeId: ID! name: String! }morpheus compiler will read interfaces and validate implements. template haskell will generate haskell types only for types not for interfaces.
haskell type from
simple.gql:data SimpleType = SimpleType { nodeId :: ID! name :: Text! } deriving (Generic)at the time compiler does not validates field Arguments by interface
[0.2.1] - 23.08.2019
- assets are added to cabal source files
[0.2.0] - 23.08.2019
Added
-
Parser Supports GraphQL comments
-
Enhanced Subscription: mutation can trigger subscription with arguments
-
Experimental Support of Input Unions
-
GraphQL schema generating with:
Data.Morpheus.Document.toGraphQLDocument -
Generating dummy Morpheus Api from
schema.gql:morpheus build schema/mythology.gql src/MythologyApi.hs -
convertToJSONName&convertToHaskellNamehas been extended to support all Haskell 2010 reserved identities. details -
GraphQL Clientwith Template haskell QuasiQuotes (Experimental, Not fully Implemented)defineQuery [gql| query GetHero ($byRealm: Realm) { deity (realm:$byRealm) { power fullName } } |]will Generate:
- response type
GetHero,DeitywithLensInstances - input types:
GetHeroArgs,Realm - instance for
FetchtypeClass
so that
fetchHero :: Args GetHero -> m (Either String GetHero) fetchHero = fetch jsonRes args where args = GetHeroArgs {byRealm = Just Realm {owner = "Zeus", surface = Just 10}} jsonRes :: ByteString -> m ByteString jsonRes = <fetch query from server>resolves well typed response
GetHero. - response type
-
Ability to define
GQLSchemawith GraphQL syntax , so that with this schema[gqlDocument| type Query { deity (uid: Text! ) : Deity! } type Deity { name : Text! power : Text } |] rootResolver :: GQLRootResolver IO () () Query () () rootResolver = GQLRootResolver {queryResolver = return Query {deity}, mutationResolver = pure (), subscriptionResolver = pure ()} where deity DeityArgs {uid} = pure Deity {name, power} where name _ = pure "Morpheus" power _ = pure (Just "Shapeshifting")Template Haskell Generates types:
Query,Deity,DeityArgs, that can be used byrootResolvergenerated types are not compatible with
Mutation,Subscription, they can be used only inQuery, but this issue will be fixed in next release
Fixed:
- Parser supports enums inside input Object
- fulfilled fragment Validation (added: unusedFragment,nameConflict)
- correct decoding of Enums with more than 3 constructor #201
Changed
-
WebSocket subProtocol changed from
graphql-subscriptionstographql-ws -
type familiy
KINDis moved into typeClassesGQLType, so you should replacetype instance KIND Deity = OBJECT instance GQLType Deity where description = const "Custom Description for Client Defined User Type" data Deity = Deity { fullName :: Text } deriving (Generic)with
instance GQLType Deity where type KIND Deity = OBJECT description = const "Custom Description for Client Defined User Type" data Deity = Deity { fullName :: Text } deriving (Generic) -
Duplicated variable names in Http requests are validated using
Aeson'sjsonNoDupfunction. So the following request will result in a parsing error{"query":"...", "variables":{"email":"foo@mail.net", "email":"bar@mail.net",...}}
[0.1.1] - 1.07.2019
Fixed:
- () as Subscription or Mutation does not defines Operator without fields
[0.1.0] - 30.06.2019
thanks for contributing to: @krisajenkins, @hovind, @vmchale, @msvbg
Added
-
support for Union Types:
type instance KIND <type> = UNION -
support of haskell Types:
Map,Set, and Pair(a,b) -
GraphQL Resolver supports custom Monad
-
add
Interpreterclass with instances:ByteString -> m ByteStringand LazyByteString, wheremis resolver monadText -> m Textand LazyText, wheremis resolver monadGQLRequest -> m GQLResponse, When you using it inside another Component that have ManualToJSONderiving, you have to ensure thatGQLResponsewill be encoded withtoEncoding, and not withtoJSON.
-
Schema Validation:
- Name Collision
-
support of Parsing input values:
Objects,Arrays -
support scalar type:
ID -
scalar Types are validated by
GQLScalarinstance functionparseValue -
TypeFamily
KINDwith:SCALAROBJECT,ENUMINPUT_OBJECTUNION
-
inline Fragments
-
GraphQL Aliases
-
Subscriptions:
GQLSubscriptiona -> EffectM boperation: is resolver that contains side effect inEffectM. is used for Mutation and Subscribe communicationgqlEffectResolver ["CHANNEL_ID"]: packs as effect Resolver. if mutation and subscription resolver have same channel then every call of mutation will trigger subscription resolverGQLState: shared state betweenhttpandwebsocketservergqlSocketApp:convertsinterpretertowebsocketapplicationgraphql-subscriptions:Apollo GraphQLsubProtocol
-
language:
- Query supports :
__type(name:"type") - On every Object can be selected :
__typename
- Query supports :
Changed
GQLRootResolver,GQLType(..),GQLScalar(..)are moved inData.Morpheus.TypesGQLRoot { query, mutation, subscription }toGQLRootResolver {queryResolver, mutationResolver, subscriptionResolver}interpreter: can be used inhttpandwebsocketserverGQLKindrenamed asGQLType- types can be derived just with
(Generic,GQLType) - haskell record field
type'will generate GQL Object fieldtype - public API (all other modules are hidden):
- Data.Morpheus
- Data.Morpheus.Kind
- Data.Morpheus.Types
- Data.Morpheus.Execution.Subscription
Fixed:
- parser can read fields with digits like: a1 , _1
- you can use Wrapped type and Wrapped Primitive Types issue #136:
- wrapped TypesNames will be separated with "_" : typeName(Either A B) -> "Either_A_B"
- introspection:
- argument supports
Non-NullandList - every field has correct kind
- argument supports
Removed
GQLArgs: you can derive arguments just withGenericwithoutGQLArgsGQLObject: replaced with instancetype instance KIND <Type> = OBJECTGQLEnum: replaced with instancetype instance KIND <Type> = ENUMGQLInput: replaced with instancetype instance KIND <Type> = INPUT_OBJECTTypeable: with new deriving it is not required anymoreWrapper: with TypeFamilies there is no need forWrappera ::-> bis Replaced bya -> ResM bwhereResMis alias forResolver IO aGQLMutation,GQLQuery: with new deriving it is not required anymoreResolverconstructor replaced by functions:gqlResolver: packsm Either String atoResolver m agqlEffectResolver: resolver constructor for effectedResolverliftEffectResolver: lifts normal resolver to Effect Resolver.