Bot releases are hidden (Show)
Published by nalchevanidze over 2 years ago
Published by nalchevanidze over 2 years ago
key
, value
to _0
, _1
NonEmpty
, Vector
, Seq
Published by nalchevanidze almost 3 years ago
NamedResoplvers
(experimental featuure): typed Haskell approach of providing appollo
like named resolvers, apps with NamedResolvers theoretically can be safelly merged.
TypeGuards
: as an alternative for interfaces
TaggedArguments
: support of type level tagged argument definition. for example:
Haskell field definition
myField :: Arg "a" Int -> Arg "b" (Maybe Text) -> m Text
will generate GraphQL field
myField(a:Int!, b:String): String!
deriving will merge function arguments. for example:
for following data type definitions:
data A = A { a1 :: Text, a2 :: Int} deriving (Show, Generic, GQLType)
data B = B {b :: Text} deriving (Show, Generic, GQLType)
Haskell field definition
myField :: A -> B -> m Text
will generate GraphQL field
myField(a1:String!, a2:Int!, b:String!): String!
implements
field from GQLType
interface
from Morpheus.Types
INTERFACE
extensions :: Maybe Value
Add defineBy*File' variants that take a Q FilePath
#584
fixed: Generation of data constructors for non-capitalized enums
fixed invalid scalar type generation and added tests to ensure their validity for each upcoming version #583
return all response errors gracefully as a Left when fetching #577 - Thanks @AlistairB
Published by nalchevanidze over 3 years ago
(issue #543 & #558): GQLTypeOptions
supports new option typeNameModifier
.
Before the schema failed if you wanted to use the same type for input and output, and the user had no control over the eventual GraphQL type name of the generated schema. Now with this option you can
provide a function of type Bool -> String -> String
that generates a custom GraphQL type name. The first argument is a Bool
that is True
if the type is an input, and False
otherwise. The second
argument is a String
representing the initial, auto-generated GraphQL type name. The function returns the desired type name. thanks @nalchevanidze & @bradsherman
e.g this schema will not fail. morpheus will generate types: Deity
and InputDeity
data Deity = Deity
{ name :: Text,
age :: Int
}
deriving (Show, Generic)
deityTypeNameModifier isInput original
| isInput = "Input" ++ original
| otherwise = original
instance GQLType Deity where
typeOptions _ opt = opt {typeNameModifier = deityTypeNameModifier}
newtype DeityArgs = DeityArgs
{ input :: Deity
}
deriving (Show, Generic, GQLType)
newtype Query (m :: * -> *) = Query
{ deity :: DeityArgs -> m Deity
}
deriving (Generic, GQLType)
exposed EncodeWrapper
and DecodeWrapper
type-classes.
Map k v
is now represented as just [Pair k v]
GQLScalar
was replaced with EncodeScalar
and DecodeScalar
type-classes.
Exclusive input objects: Sum types used as input types are represented as input objects, where only one field must have a value. Namespaced constructors (i.e., where referenced type name concatenated with union type name is equal to constructor name) are unpacked. Furthermore, empty constructors are represented as fields with the unit type.
for example:
data Device
| DevicePC PC
| Laptop { macAdress :: ID }
| Smartphone
this type will generate the following SDL:
enum Unit {
Unit
}
input Laptop {
macAdress: ID
}
input Device {
PC: PC
Laptops: Laptop
Smartphone: Unit
}
For each nullary constructor will be defined GQL object type with a single field _: Unit
(since GraphQL does not allow empty objects).
for example:
data Person = Client { name :: Text } | Accountant | Developer
this type will generate the following SDL:
enum Unit {
Unit
}
type Student {
name: String!
}
type Accountant {
_: Unit!
}
type Developer {
_: Unit!
}
union Person = Client | Accountant | Developer
changed signature of GQLType.typeOptions
from f a -> GQLTypeOptions
to f a -> GQLTypeOptions -> GQLTypeOptions
.
now you can write:
typeOptions _ options = options { fieldLabelModifier = <my function> }
whre argument options is default gql options.
deexposed constructor of GQLTypeOptions
.
Type name for parametrized types like One (Two Three)
will be generated directly, concatenating them OneTwoThree
instead of One_Two_Three.
Haskell Float
was renamed to custom scalar type Float32.
Haskell Double
now represents GraphQL Float
.
INPUT
, ENUM
and OUTPUT
in favor of more generalized kind TYPE
.deriving (Generic, GQLType)
.importGQLDocument
orimportGQLDocumentWithNamespace
is changedGQLScalar
was replaced with EncodeScalar
and DecodeScalar
type-classes.defineByDocumentFile
ordefineByIntrospectionFile
is changedData.Morpheus.Core
provides default GrapHQL type definitions with internalSchema
Data.Morpheus.Internal.Ext
parseTypeSystemDefinition
and parseGQLDocument
is replaced with parseSchema
parseFullGQLDocument
replaced with parseFullSchema
removed parseDSL
from Data.Morpheus.Core
following Types and modules are migrated to the new package morpheus-graphql-app
:
Data.Morpheus.Core
are moved in to Data.Morpheus.App
:App
, AppData
, runApp
, withDebugger
, mkApp
, runAppStream
MapAPI
migrated from Data.Morpheus.Types.IO
is moved into Data.Morpheus.App
Data.Morpheus.Types.Internal.Resolving
moved asData.Morpheus.App.Internal.Resolving
RootResModel
was renamed to RootResolverValue
ResModel
was replaced with more general ResolverValue
GQLScalar
was replaced with EncodeScalar
and DecodeScalar
type-classes.
Value.Float
is now Double
instead of Float
.
Published by nalchevanidze almost 4 years ago
morpheus-graphql-subscriptions
.Event
, httpPubApp
and webSocketsApp
moved Data.Morpheus.Subscriptions
Data.Morpheus.Subscriptions
provides:
httpPubApp
webSocketsApp
New encode and decode instances for Set
, NonEmpty
, Seq
and Vector
Set
and NonEmpty
throw a graphql error when a duplicate is found (Set)
or when an empty list is sent (NonEmpty).
Beware: Right now, all these types are advertised as lists in the introspection query.
This is something we are trying to change by submitting a proposal to the graphql spec.
signature changes:
render
:
a -> Text
to a -> ByteString
parseTypeSystemDefinition :
Text -> Eventless (Schema VALID)
to ByteString -> Eventless (Schema VALID)
parseTypeDefinitions:
Text -> Eventless [TypeDefinition ANY CONST]
to ByteString -> Eventless [TypeDefinition ANY CONST]
defineByIntrospectionFile
support custom (Query,Mutation,Subscription)Published by nalchevanidze about 4 years ago
relaxed upper boundary of megaparsec
up to 10.0.0
Published by nalchevanidze about 4 years ago
RootResolver IO () MyQuery MyMutation Undefined
App event m
and deriveApp
App
supports semigroup(schema Stitching
): if whe have two graphql appsGQLType
exposes typeOptions
to modify labels: typeOptions :: f a -> GQLTypeOptions
GQLType.getDescriptions
to document field or enum ValuesimportGQLDocumentWithNamespace
they will be namespaced enum ValuesGQLType
instancesData.Morpheus.Server
:
subscriptionApp
webSocketsApp
type to App e m -> m (ServerApp, e -> m ())
httpPubApp
type to [e -> m ()] -> App e m -> a -> m b
Stream
from Data.Morpheus.Types
Interpreter
, interpreter
is now just regular function.render
renders SchemaDefinition e.g__typename
to interface typesApp
App
supports semigroup(schema Stitching
):runApp
changed signature to:runApi
.__typename
#509Published by nalchevanidze about 4 years ago
Published by nalchevanidze about 4 years ago
debugInterpreter
: displays internal context on grahql errorscompileTimeSchemaValidation
import Morpheus.Graphql.Server(compileTimeSchemaValidation)
_validateSchema :: ()
_validateSchema = $(compileTimeSchemaValidation (Identity gqlRoot))
directive Validation for Document (TypeSystem).
supports of block string values. e.g:
query {
createDeity(
name: """
powerqwe
bla \n sd
blu \\ dete
"""
) {
name
}
}
Data.Morpheus.Document
exposes RootResolverConstraint
Data.Morpheus.Server
exposes httpPlayground
httpPubApp
supports GQLRequest -> GQLResponse
morpheus-graphql-core
support of schema
. issue #412
schema {
query: Query
}
note that this does not affect morpheus-graphql-server
at all. since it has its own schema derivation. you still need to provide:
rootResolver :: RootResolver () IO Query Undefined Undefined
rootResolver = RootResolver <resolvers ...>
Subscription Resolver supports Monad
.
nested Subscription Resolvers.
Context' renamed to
ResolverContext'SubscriptionField
. e.g:data Subscription (m :: * -> *) = Subscription
{ newDeity :: SubscriptionField (m Deity),
newHuman :: HumanArgs -> SubscriptionField (m Human)
}
deriving (Generic)
subscribe
is changed. now you can use it as followed:resolveNewAdress :: SubscriptionField (ResolverS EVENT IO Address)
resolveNewAdress = subscribe ADDRESS $ do
-- executed only once
-- immediate response on failures
requireAuthorized
pure $ \(Event _ content) -> do
-- exectues on every event
lift (getDBAddress content)
Data.Morpheus.Types
SubField
ComposedSubField
supports interfaces.
supports of block string values.
support of schema
. issue #412
schema {
query: MyQuery
}
generated types have instance of class Eq
Eq
query validation supports interfaces
exposed: Data.Morpheus.Types.SelectionTree
configurable api: Data.Morpheus.Core
exports
Config
defaultConfig
debugConfig
for better debuging, internal errors messages will display resolving state:
current TypeName
current Selection
OperationDefinition
SchemaDefinition
rendering graphql "AST". e.g render ( slection :: Selection VALID)
will render
{
user(arg1: 1) {
name
}
}
[dsl| <type definitions> |]
generates Schema VALID
.directive @MyDirective on FIELD_DEFINITION | OBJECT
directive Validation for Document (TypeSystem).
supports of block string values. e.g:
query {
createDeity(
name: """
powerqwe
bla \n sd
blu \\ dete
"""
) {
name
}
}
support of schema
. issue #412
schema {
query: MyQuery
}
Context' renamed to
ResolverContext'EventCon
from Data.Morpheus.Core
stage = RAW | CONST | VALID
.
Schema VALID
TypeDefinition VALID
FieldDefinition IN VALID
runApi ::
Schema s ->
RootResModel event m ->
Config ->
GQLRequest ->
ResponseStream event m (Value VALID)
Published by nalchevanidze over 4 years ago
GQLRootResolver
-> RootResolver
importGQLDocument
automatically defines GQLType
instances for scalar definitionsData.Morpheus.Types.GQLScalar
Data.Morpheus.Types.ID
mtl
from now you should provide for every custom graphql scalar definition coresponoding haskell type definition and GQLScalar
implementation fot it. for details see examples-client
input fields and query arguments are imported without namespacing
Published by nalchevanidze over 4 years ago
Package was extracted as:
morpheus-graphql-core
: core components like: parser, validator, executor, utils.
morpheus-graphql-client
: lightweight version of morpheus client without server implementation
morpheus-graphql
: morpheus graphql server
deprecated:
Res
, IORes
, ResolveQ
: use ResolverQ
MutRes
, IOMutRes
, ResolveM
: use ResolverM
SubRes
, IOSubRes
, ResolveS
: use ResolverS
failRes
: use MonadFail
Semigroup
support for Resolver
MonadFail
Support for Resolver
flexible resolvers: ResolverO
, ResolverQ
, RwsolverM
, ResolverS
they can handle object and scalar types:
-- if we have record and regular Int
data Object m = Object { field :: m Int }
-- we canwrite
-- handes kind : (* -> *) -> *
resolveObject :: ResolverO o EVENT IO Object
-- is alias to: Resolver o () IO (Object (Resolver o () IO))
-- or
-- handes kind : *
resolveInt :: ResolverO o EVENT IO Int
-- is alias to: Resolver o () IO Int
the resolvers : ResolverQ
, RwsolverM
, ResolverS
, are like
ResolverO
but with QUERY
, MUTATION
and SUBSCRIPTION
as argument.
flexible compsed Resolver Type alias: ComposedResolver
. extends ResolverO
with
parameter (f :: * -> *)
. so that you can compose Resolvers e.g:
resolveList :: ComposedResolver o EVENT IO [] Object
-- is alias to: Resolver o () IO [Object (Resolver o () IO))]
resolveList :: ComposedResolver o EVENT IO Maybe Int
-- is alias to: Resolver o () IO (Maybe Int)
server supports interfaces (see Readme):
importGQLDocument
and DSL
(compile time validation):support default directives: @skip
and @include
SelectionTree interface
parser supports implemnets interfaces seperated with empty spaces
type T implements A , B C & D {
introspection can render interfaces
Published by nalchevanidze over 4 years ago
Client generated enum data constructors are now prefixed with with the type name to avoid name conflicts.
for Variant selection inputUnion uses inputname
insead of __typename
in Data.Morpheus.Server
gqlSocketApp
and gqlSocketMonadIOApp
are replaced with webSocketsApp
initGQLState
, GQLState
for better control of subscriptions
interpreter gqlRoot state
withinterpreter gqlRoot
.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 httpApp
where 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.
subscription can select only one top level field (based on the GraphQL specification).
FromJSON
ToJSON
instances for ID
Published by nalchevanidze almost 5 years ago
all constructors of Resolver
: QueryResolver
,MutResolver
,SubResolver
are unexposed. use lift
, publish
or subscribe
instead.
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)
exposed publish
for mutation resolvers, now you can write
resolveCreateUser :: ResolveM EVENT IO User
resolveCreateUser = do
requireAuthorized
publish [userUpdate]
liftEither setDBUser
exposed subscribe
for subscription resolvers, now you can write
resolveNewUser :: ResolveS EVENT IO User
resolveNewUser = subscribe [USER] $ do
requireAuthorized
pure userByEvent
where userByEvent (Event _ content) = liftEither (getDBUser content)
type SubField
will convert your subscription monad to query monad.
SubField (Resolver Subscription Event IO) User
will generate same as
Resolver Subscription Event IO (User ((Resolver QUERY Event IO)))
now if you can define subscription as follows
data Subscription m = Subscription {
newUser :: SubField m User
}
unsafeInternalContext
to 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)
mtl
Published by nalchevanidze almost 5 years ago
mtl
Published by nalchevanidze almost 5 years ago
WithOperation
constraint for Generic Resolvers (#347) thanks @dandohliftEither support in MutResolver (#351)
selection of __typename
on 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 argument
e.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`
support of resolver fields m type
for the fields without arguments
data Diety m = Deity {
name :: m Text
}
-- is equal to
data Diety m = Deity {
name :: () -> m Text
}
template haskell generates m type
insead of () -> m type
for 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)
}
Published by nalchevanidze almost 5 years ago
deprecated: INPUT_OBJECT
, OBJECT
, UNION
,
INPUT
instead of INPUT_OBJECT
deriving(GQLType)
insead of OBJECT
or UNION
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 Deity
is Object.
will generate
union CHaracter = Deity | SomeDeity
type SomeDeity {
_0: Deity
}
failRes
for resolver failures
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 graphql enum
haskell record without union (e.g Deity
), will generate graphql object
namespaced Unions: CharacterDeity
where Character
is TypeConstructor and Deity
referenced object (not scalar) type: will be generate regular graphql Union
union Character =
Deity
| ...
for union recrods (Creature { creatureName :: Text, creatureAge :: Int }
) will be referenced in union type, plus type Creature
will 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.g CharacterEnum
), this enum will be wrapped in CharacterEnumObject
and this type will be added to union Character
. as in example above
there is only types left with form TypeName Type1 2Type ..
(e.g SomeDeity Deity
,CharacterInt Int
, SomeMutli Int Text
),
morpheus will generate objet type from it:
type TypeName {
_0: Type1!
_1: Type2!
...
}
-4
)1.000
)Published by nalchevanidze almost 5 years ago
Published by nalchevanidze almost 5 years ago
toMorpheusHaskellAPi
from Data.Morpheus.Document
functionality will be migrated in morpheus-graphql-cli
liftM
to MonadTrans
instance method lift
liftEitherM
to liftEither
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 parseDSL
to Data.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!
}
type User {
name: String! @deprecated(reason: "some reason")
}
will displayed in introspection
{
"data": {
"__type": {
"fields": [
{
"name": "city",
"isDeprecated": true,
"deprecationReason": "test deprecation field with reason"
}
]
}
}
}
basic support of directive @deprecated
on enumValue
and object field
, 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:
ResolveM EVENT IO Address
is same as MutRes EVENT IO (Address (MutRes EVENT IO))
is helpfull wenn you want to resolve GraphQL object
defineByIntrospectionFile
does not breaks if schema contains interfacesSubscription
and Mutation
operationsPublished by nalchevanidze almost 5 years ago
Published by nalchevanidze almost 5 years ago
removed morpheus
cli for code generating, if you need cli you should use
morpheus-graphql-cli
example API
executable is removed from Production build
helper functions: liftEitherM
, liftM
liftM :: m a -> Resolver o m e a
liftEitherM :: m (Either String a) -> Resolver o m e a