packages feed

morpheus-graphql 0.15.1 → 0.16.0

raw patch · 39 files changed

+424/−1218 lines, 39 filesdep +morpheus-graphql-subscriptionsdep +reludedep −megaparsecdep −scientificdep −unliftio-coredep ~morpheus-graphql-corePVP ok

version bump matches the API change (PVP)

Dependencies added: morpheus-graphql-subscriptions, relude

Dependencies removed: megaparsec, scientific, unliftio-core, uuid, websockets

Dependency ranges changed: morpheus-graphql-core

API changes (from Hackage documentation)

- Data.Morpheus.Server: httpPubApp :: (MonadIO m, MapAPI a b) => [e -> m ()] -> App e m -> a -> m b
- Data.Morpheus.Server: type ServerConstraint e m = (MonadIO m, MonadUnliftIO m)
- Data.Morpheus.Server: webSocketsApp :: (MonadUnliftIO m, Eq channel) => App (Event channel cont) m -> m (ServerApp, Event channel cont -> m ())
- Data.Morpheus.Types: Event :: [e] -> c -> Event e c
- Data.Morpheus.Types: [channels] :: Event e c -> [e]
- Data.Morpheus.Types: [content] :: Event e c -> c
- Data.Morpheus.Types: data Event e c
- Data.Morpheus.Types: data Input (api :: API)
- Data.Morpheus.Types: type HTTP = 'HTTP
- Data.Morpheus.Types: type WS = 'WS
- Data.Morpheus.Types.Internal.Subscription: Store :: m (ClientConnectionStore e m) -> ((ClientConnectionStore e m -> ClientConnectionStore e m) -> m ()) -> Store e m
- Data.Morpheus.Types.Internal.Subscription: [Init] :: ID -> Input WS
- Data.Morpheus.Types.Internal.Subscription: [Request] :: GQLRequest -> Input HTTP
- Data.Morpheus.Types.Internal.Subscription: [ScopeHTTP] :: {httpCallback :: event -> m ()} -> Scope HTTP event m
- Data.Morpheus.Types.Internal.Subscription: [ScopeWS] :: {listener :: m ByteString, callback :: ByteString -> m (), update :: (ClientConnectionStore event m -> ClientConnectionStore event m) -> m ()} -> Scope WS event m
- Data.Morpheus.Types.Internal.Subscription: [readStore] :: Store e m -> m (ClientConnectionStore e m)
- Data.Morpheus.Types.Internal.Subscription: [writeStore] :: Store e m -> (ClientConnectionStore e m -> ClientConnectionStore e m) -> m ()
- Data.Morpheus.Types.Internal.Subscription: acceptApolloRequest :: MonadIO m => PendingConnection -> m Connection
- Data.Morpheus.Types.Internal.Subscription: connect :: MonadIO m => m (Input WS)
- Data.Morpheus.Types.Internal.Subscription: connectionSessionIds :: ClientConnection e m -> [SessionID]
- Data.Morpheus.Types.Internal.Subscription: connectionThread :: MonadUnliftIO m => App e m -> Scope WS e m -> m ()
- Data.Morpheus.Types.Internal.Subscription: data ClientConnectionStore e (m :: * -> *)
- Data.Morpheus.Types.Internal.Subscription: data Input (api :: API)
- Data.Morpheus.Types.Internal.Subscription: data Scope (api :: API) event (m :: * -> *)
- Data.Morpheus.Types.Internal.Subscription: data Store e m
- Data.Morpheus.Types.Internal.Subscription: disconnect :: Scope WS e m -> Input WS -> m ()
- Data.Morpheus.Types.Internal.Subscription: empty :: Collection a coll => coll
- Data.Morpheus.Types.Internal.Subscription: initDefaultStore :: (MonadIO m, MonadIO m2) => m2 (Store event m)
- Data.Morpheus.Types.Internal.Subscription: publish :: (Monad m, Eq channel) => Event channel content -> ClientConnectionStore (Event channel content) m -> m ()
- Data.Morpheus.Types.Internal.Subscription: publishEventWith :: (MonadIO m, Eq channel) => Store (Event channel cont) m -> Event channel cont -> m ()
- Data.Morpheus.Types.Internal.Subscription: runStreamHTTP :: Monad m => Scope HTTP e m -> Stream HTTP e m -> m GQLResponse
- Data.Morpheus.Types.Internal.Subscription: runStreamWS :: Monad m => Scope WS e m -> Stream WS e m -> m ()
- Data.Morpheus.Types.Internal.Subscription: streamApp :: Monad m => App e m -> Input api -> Stream api e m
- Data.Morpheus.Types.Internal.Subscription: toList :: ClientConnectionStore e m -> [(UUID, ClientConnection e m)]
- Data.Morpheus.Types.Internal.Subscription: type HTTP = 'HTTP
- Data.Morpheus.Types.Internal.Subscription: type SessionID = Text
- Data.Morpheus.Types.Internal.Subscription: type WS = 'WS
+ Data.Morpheus.Types: getDescriptions :: GQLType a => f a -> Map Text Description
+ Data.Morpheus.Types: getDirectives :: GQLType a => f a -> Map Text (Directives CONST)
+ Data.Morpheus.Types: typeOptions :: GQLType a => f a -> GQLTypeOptions
- Data.Morpheus: withDebugger :: () => App e m -> App e m
+ Data.Morpheus: withDebugger :: forall e (m :: Type -> Type). App e m -> App e m
- Data.Morpheus.Kind: type ENUM = 'ENUM
+ Data.Morpheus.Kind: type ENUM = 'ENUM
- Data.Morpheus.Kind: type INPUT = 'INPUT
+ Data.Morpheus.Kind: type INPUT = 'INPUT
- Data.Morpheus.Kind: type INPUT_OBJECT = 'INPUT
+ Data.Morpheus.Kind: type INPUT_OBJECT = 'INPUT
- Data.Morpheus.Kind: type INTERFACE = 'INTERFACE
+ Data.Morpheus.Kind: type INTERFACE = 'INTERFACE
- Data.Morpheus.Kind: type OBJECT = 'OUTPUT
+ Data.Morpheus.Kind: type OBJECT = 'OUTPUT
- Data.Morpheus.Kind: type OUTPUT = 'OUTPUT
+ Data.Morpheus.Kind: type OUTPUT = 'OUTPUT
- Data.Morpheus.Kind: type SCALAR = 'SCALAR
+ Data.Morpheus.Kind: type SCALAR = 'SCALAR
- Data.Morpheus.Kind: type UNION = 'OUTPUT
+ Data.Morpheus.Kind: type UNION = 'OUTPUT
- Data.Morpheus.Kind: type WRAPPER = 'WRAPPER
+ Data.Morpheus.Kind: type WRAPPER = 'WRAPPER
- Data.Morpheus.Types: GQLRequest :: Token -> Maybe FieldName -> Maybe Value -> GQLRequest
+ Data.Morpheus.Types: GQLRequest :: Maybe FieldName -> Text -> Maybe Value -> GQLRequest
- Data.Morpheus.Types: RootResolver :: query (Resolver QUERY event m) -> mut (Resolver MUTATION event m) -> sub (Resolver SUBSCRIPTION event m) -> RootResolver event
+ Data.Morpheus.Types: RootResolver :: query (Resolver QUERY event m) -> mut (Resolver MUTATION event m) -> sub (Resolver SUBSCRIPTION event m) -> RootResolver (m :: * -> *) event (query :: (* -> *) -> *) (mut :: (* -> *) -> *) (sub :: (* -> *) -> *)
- Data.Morpheus.Types: Undefined :: Undefined
+ Data.Morpheus.Types: Undefined :: Undefined (m :: * -> *)
- Data.Morpheus.Types: [mutationResolver] :: RootResolver event -> mut (Resolver MUTATION event m)
+ Data.Morpheus.Types: [mutationResolver] :: RootResolver (m :: * -> *) event (query :: (* -> *) -> *) (mut :: (* -> *) -> *) (sub :: (* -> *) -> *) -> mut (Resolver MUTATION event m)
- Data.Morpheus.Types: [queryResolver] :: RootResolver event -> query (Resolver QUERY event m)
+ Data.Morpheus.Types: [queryResolver] :: RootResolver (m :: * -> *) event (query :: (* -> *) -> *) (mut :: (* -> *) -> *) (sub :: (* -> *) -> *) -> query (Resolver QUERY event m)
- Data.Morpheus.Types: [query] :: GQLRequest -> Token
+ Data.Morpheus.Types: [query] :: GQLRequest -> Text
- Data.Morpheus.Types: [subscriptionResolver] :: RootResolver event -> sub (Resolver SUBSCRIPTION event m)
+ Data.Morpheus.Types: [subscriptionResolver] :: RootResolver (m :: * -> *) event (query :: (* -> *) -> *) (mut :: (* -> *) -> *) (sub :: (* -> *) -> *) -> sub (Resolver SUBSCRIPTION event m)
- Data.Morpheus.Types: render :: RenderGQL a => a -> Rendering
+ Data.Morpheus.Types: render :: RenderGQL a => a -> ByteString
- Data.Morpheus.Types: subscribe :: (Monad m, Event channel cont ~ e) => channel -> Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) -> SubscriptionField (Resolver SUBSCRIPTION e m a)
+ Data.Morpheus.Types: subscribe :: forall (m :: Type -> Type) e a. Monad m => Channel e -> Resolver QUERY e m (e -> Resolver SUBSCRIPTION e m a) -> SubscriptionField (Resolver SUBSCRIPTION e m a)
- Data.Morpheus.Types: type MUTATION = Mutation
+ Data.Morpheus.Types: type MUTATION = 'Mutation
- Data.Morpheus.Types: type QUERY = Query
+ Data.Morpheus.Types: type QUERY = 'Query
- Data.Morpheus.Types: type SUBSCRIPTION = Subscription
+ Data.Morpheus.Types: type SUBSCRIPTION = 'Subscription
- Data.Morpheus.Types: unsafeInternalContext :: (Monad m, LiftOperation o) => Resolver o e m ResolverContext
+ Data.Morpheus.Types: unsafeInternalContext :: forall (m :: Type -> Type) (o :: OperationType) e. (Monad m, LiftOperation o) => Resolver o e m ResolverContext

Files

README.md view
@@ -26,10 +26,10 @@ _stack.yml_  ```yaml-resolver: lts-15.13+resolver: lts-16.2  extra-deps:-  - morpheus-graphql-0.14.1+  - morpheus-graphql-0.16.0 ```  As Morpheus is quite new, make sure stack can find morpheus-graphql by running `stack upgrade` and `stack update`
changelog.md view
@@ -1,5 +1,29 @@ # Changelog +## 0.16.0 - 05.11.2020++## Breaking changes++- subscriptions are extracted in `morpheus-graphql-subscriptions`.+- `Event`, `httpPubApp` and `webSocketsApp` moved `Data.Morpheus.Subscriptions`++## New Features++- `Data.Morpheus.Subscriptions` provides:++  - runPubApp: generalized version of `httpPubApp`+  - runSubApp: generalized version of `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.++### Minor Changes++- parser performance optimization+ ## 0.15.1 - 12.09.2020  ## 0.15.0 - 12.09.2020
morpheus-graphql.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 5ddd838022cbad8d2916bd481dda961537851981070c4556c00e3b9e61d2307c+-- hash: 4f4683e0c6a040813b49314a575621595a6cc515377d78372f2e6ac0c718be74  name:           morpheus-graphql-version:        0.15.1+version:        0.16.0 synopsis:       Morpheus GraphQL description:    Build GraphQL APIs with your favourite functional language! category:       web, graphql@@ -397,7 +397,6 @@       Data.Morpheus.Types       Data.Morpheus.Server       Data.Morpheus.Document-      Data.Morpheus.Types.Internal.Subscription   other-modules:       Data.Morpheus.Server.Deriving.App       Data.Morpheus.Server.Deriving.Channels@@ -418,9 +417,6 @@       Data.Morpheus.Server.Types.GQLType       Data.Morpheus.Server.Types.SchemaT       Data.Morpheus.Server.Types.Types-      Data.Morpheus.Types.Internal.Subscription.Apollo-      Data.Morpheus.Types.Internal.Subscription.ClientConnectionStore-      Data.Morpheus.Types.Internal.Subscription.Stream       Paths_morpheus_graphql   hs-source-dirs:       src@@ -430,18 +426,14 @@     , base >=4.7 && <5     , bytestring >=0.10.4 && <0.11     , containers >=0.4.2.1 && <0.7-    , megaparsec >=7.0.0 && <9.0.0-    , morpheus-graphql-core >=0.15.0 && <0.16.0+    , morpheus-graphql-core >=0.16.0 && <0.17.0     , mtl >=2.0 && <=3.0-    , scientific >=0.3.6.2 && <0.4+    , relude >=0.3.0     , template-haskell >=2.0 && <=3.0     , text >=1.2.3.0 && <1.3     , transformers >=0.3.0.0 && <0.6-    , unliftio-core >=0.0.1 && <=0.4     , unordered-containers >=0.2.8.0 && <0.3-    , uuid >=1.0 && <=1.4     , vector >=0.12.0.1 && <0.13-    , websockets >=0.11.0 && <=1.0   default-language: Haskell2010  test-suite morpheus-test@@ -478,19 +470,16 @@     , base >=4.7 && <5     , bytestring >=0.10.4 && <0.11     , containers >=0.4.2.1 && <0.7-    , megaparsec >=7.0.0 && <9.0.0     , morpheus-graphql-    , morpheus-graphql-core >=0.15.0 && <0.16.0+    , morpheus-graphql-core >=0.16.0 && <0.17.0+    , morpheus-graphql-subscriptions >=0.16.0 && <0.17.0     , mtl >=2.0 && <=3.0-    , scientific >=0.3.6.2 && <0.4+    , relude >=0.3.0     , tasty     , tasty-hunit     , template-haskell >=2.0 && <=3.0     , text >=1.2.3.0 && <1.3     , transformers >=0.3.0.0 && <0.6-    , unliftio-core >=0.0.1 && <=0.4     , unordered-containers >=0.2.8.0 && <0.3-    , uuid >=1.0 && <=1.4     , vector >=0.12.0.1 && <0.13-    , websockets >=0.11.0 && <=1.0   default-language: Haskell2010
src/Data/Morpheus.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-}  -- | Build GraphQL APIs with your favorite functional language! module Data.Morpheus@@ -25,6 +26,7 @@   ( RootResolver (..),   ) import Data.Morpheus.Types.IO (MapAPI)+import Relude  -- | main query processor and resolver interpreter ::
src/Data/Morpheus/Document.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.Document   ( toGraphQLDocument,@@ -31,11 +32,8 @@ import Data.Morpheus.Types.Internal.Resolving   ( resultOr,   )-import qualified Data.Text.Lazy as LT-  ( fromStrict,-  )-import Data.Text.Lazy.Encoding (encodeUtf8) import Language.Haskell.TH+import Relude hiding (ByteString)  importGQLDocument :: FilePath -> Q [Dec] importGQLDocument src =@@ -59,5 +57,5 @@   proxy (RootResolver m event query mut sub) ->   ByteString toGraphQLDocument =-  resultOr (pack . show) (encodeUtf8 . LT.fromStrict . render)+  resultOr (pack . show) render     . deriveSchema
src/Data/Morpheus/Kind.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}  -- | associating types to GraphQL Kinds module Data.Morpheus.Kind@@ -18,6 +19,8 @@     isObject,   ) where++import Relude  data GQL_KIND   = SCALAR
src/Data/Morpheus/Server.hs view
@@ -5,129 +5,20 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE NoImplicitPrelude #-}  -- |  GraphQL Wai Server Applications module Data.Morpheus.Server-  ( webSocketsApp,-    httpPubApp,-    ServerConstraint,-    httpPlayground,+  ( httpPlayground,     compileTimeSchemaValidation,   ) where -import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.IO.Unlift-  ( MonadUnliftIO,-    withRunInIO,-  )--- MORPHEUS--import Data.Foldable (traverse_)-import Data.Function ((&))-import Data.Morpheus.Core-  ( App,-    runApp,-  ) import Data.Morpheus.Server.Deriving.Schema   ( compileTimeSchemaValidation,   ) import Data.Morpheus.Server.Playground   ( httpPlayground,   )-import Data.Morpheus.Types.IO (MapAPI (..))-import Data.Morpheus.Types.Internal.Resolving-  ( Event,-  )-import Data.Morpheus.Types.Internal.Subscription-  ( Input (..),-    Scope (..),-    Store (..),-    WS,-    acceptApolloRequest,-    connectionThread,-    initDefaultStore,-    publishEventWith,-    runStreamHTTP,-    streamApp,-  )-import Network.WebSockets-  ( Connection,-    ServerApp,-    receiveData,-    sendTextData,-  )-import qualified Network.WebSockets as WS--type ServerConstraint e m =-  ( MonadIO m,-    MonadUnliftIO m-  )---- support old version of Websockets-pingThread :: Connection -> IO () -> IO ()--#if MIN_VERSION_websockets(0,12,6)-pingThread connection = WS.withPingThread connection 30 (return ())-#else-pingThread connection = (WS.forkPingThread connection 30 >>)-#endif--defaultWSScope :: MonadIO m => Store e m -> Connection -> Scope WS e m-defaultWSScope Store {writeStore} connection =-  ScopeWS-    { listener = liftIO (receiveData connection),-      callback = liftIO . sendTextData connection,-      update = writeStore-    }--httpPubApp ::-  ( MonadIO m,-    MapAPI a b-  ) =>-  [e -> m ()] ->-  App e m ->-  a ->-  m b-httpPubApp [] app = runApp app-httpPubApp callbacks app =-  mapAPI $-    runStreamHTTP ScopeHTTP {httpCallback}-      . streamApp app-      . Request-  where-    httpCallback e = traverse_ (e &) callbacks---- | Wai WebSocket Server App for GraphQL subscriptions-webSocketsApp ::-  ( MonadUnliftIO m,-    Eq channel-  ) =>-  App (Event channel cont) m ->-  m (ServerApp, Event channel cont -> m ())-webSocketsApp app =-  do-    store <- initDefaultStore-    wsApp <- webSocketsWrapper store (connectionThread app)-    pure-      ( wsApp,-        publishEventWith store-      )--webSocketsWrapper ::-  (MonadUnliftIO m, MonadIO m) =>-  Store e m ->-  (Scope WS e m -> m ()) ->-  m ServerApp-webSocketsWrapper store handler =-  withRunInIO $-    \runIO ->-      pure $-        \pending -> do-          conn <- acceptApolloRequest pending-          pingThread-            conn-            $ runIO (handler (defaultWSScope store conn))
src/Data/Morpheus/Server/Deriving/Channels.hs view
@@ -10,18 +10,15 @@ {-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.Server.Deriving.Channels-  ( getChannels,+  ( channelResolver,     ChannelsConstraint,   ) where -import Control.Applicative (pure)-import Control.Monad ((>>=))-import Data.Functor.Identity (Identity (..))-import Data.Maybe (Maybe (..)) import Data.Morpheus.Internal.Utils   ( Failure (..),     elems,+    selectBy,   ) import Data.Morpheus.Server.Deriving.Decode   ( DecodeConstraint,@@ -51,68 +48,84 @@     SubscriptionField (..),   ) import GHC.Generics-import Prelude-  ( (.),-    const,-    lookup,-    map,-  )+import Relude +newtype DerivedChannel e = DerivedChannel+  { _unpackChannel :: Channel e+  }++type ChannelRes (e :: *) = Selection VALID -> ResolverState (DerivedChannel e)+ type ChannelsConstraint e m (subs :: (* -> *) -> *) =   ExploreConstraint e (subs (Resolver SUBSCRIPTION e m)) -getChannels ::+channelResolver ::+  forall e m subs.   ChannelsConstraint e m subs =>   subs (Resolver SUBSCRIPTION e m) ->   Selection VALID ->   ResolverState (Channel e)-getChannels value sel = selectBy sel (exploreChannels value)+channelResolver value = fmap _unpackChannel . channelSelector+  where+    channelSelector ::+      Selection VALID ->+      ResolverState (DerivedChannel e)+    channelSelector = selectBySelection (exploreChannels value) -selectBy ::-  Failure InternalError m =>+selectBySelection ::+  [(FieldName, ChannelRes e)] ->   Selection VALID ->-  [ ( FieldName,-      Selection VALID -> m (Channel e)-    )-  ] ->-  m (Channel e)-selectBy Selection {selectionContent = SelectionSet selSet} ch =+  ResolverState (DerivedChannel e)+selectBySelection channels = withSubscriptionSelection >=> selectSubscription channels++selectSubscription ::+  [(FieldName, ChannelRes e)] ->+  Selection VALID ->+  ResolverState (DerivedChannel e)+selectSubscription channels sel@Selection {selectionName} =+  selectBy+    onFail+    selectionName+    channels+    >>= onSucc+  where+    onFail = "invalid subscription: no channel is selected." :: InternalError+    onSucc (_, f) = f sel++withSubscriptionSelection :: Selection VALID -> ResolverState (Selection VALID)+withSubscriptionSelection Selection {selectionContent = SelectionSet selSet} =   case elems selSet of-    [sel@Selection {selectionName}] -> case lookup selectionName ch of-      Nothing -> failure ("invalid subscription: no channel is selected." :: InternalError)-      Just f -> f sel+    [sel] -> pure sel     _ -> failure ("invalid subscription: there can be only one top level selection" :: InternalError)-selectBy _ _ = failure ("invalid subscription: expected selectionSet" :: InternalError)+withSubscriptionSelection _ = failure ("invalid subscription: expected selectionSet" :: InternalError)  class GetChannel e a | a -> e where-  getChannel :: a -> Selection VALID -> ResolverState (Channel e)+  getChannel :: a -> ChannelRes e  instance GetChannel e (SubscriptionField (Resolver SUBSCRIPTION e m a)) where-  getChannel SubscriptionField {channel} = const (pure channel)+  getChannel = const . pure . DerivedChannel . channel  instance   DecodeConstraint arg =>   GetChannel e (arg -> SubscriptionField (Resolver SUBSCRIPTION e m a))   where   getChannel f sel@Selection {selectionArguments} =-    decodeArguments selectionArguments >>= (`getChannel` sel) . f+    decodeArguments selectionArguments >>= (`getChannel` sel)+      . f  ------------------------------------------------------ -type ChannelRes e = Selection VALID -> ResolverState (Channel e)- type ExploreConstraint e a =   ( GQLType a,     Generic a,-    TypeRep (GetChannel e) (Selection VALID -> ResolverState (Channel e)) (Rep a)+    TypeRep (GetChannel e) (ChannelRes e) (Rep a)   )  exploreChannels :: forall e a. ExploreConstraint e a => a -> [(FieldName, ChannelRes e)] exploreChannels =   convertNode     . toValue-      ( TypeConstraint (getChannel . runIdentity) ::-          TypeConstraint (GetChannel e) (Selection VALID -> ResolverState (Channel e)) Identity+      ( TypeConstraint (getChannel . runIdentity) :: TypeConstraint (GetChannel e) (ChannelRes e) Identity       )  convertNode :: DataType (ChannelRes e) -> [(FieldName, ChannelRes e)]
src/Data/Morpheus/Server/Deriving/Decode.hs view
@@ -21,10 +21,12 @@   ) where -import Control.Applicative ((<*>), pure)+import Control.Applicative (pure, (<*>)) import Control.Monad ((>>=))-import Data.Functor ((<$>), Functor (..))+import Data.Functor (Functor (..), (<$>)) import Data.List (elem)+import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NonEmpty import Data.Maybe (Maybe (..)) import Data.Morpheus.Internal.Utils   ( elems,@@ -41,19 +43,12 @@     datatypeNameProxy,     selNameProxy,   )-import Data.Morpheus.Server.Internal.TH.Decode-  ( decodeFieldWith,-    withInputObject,-    withInputUnion,-    withList,-    withMaybe,-    withScalar,-  )+import Data.Morpheus.Server.Internal.TH.Decode (decodeFieldWith, withInputObject, withInputUnion, withList, withMaybe, withRefinedList, withScalar) import Data.Morpheus.Server.Types.GQLType   ( GQLType       ( KIND,-        __type,-        typeOptions+        typeOptions,+        __type       ),     GQLTypeOptions (..),     TypeData (..),@@ -79,14 +74,15 @@   ) import Data.Proxy (Proxy (..)) import Data.Semigroup (Semigroup (..))+import Data.Sequence (Seq)+import qualified Data.Sequence as Seq+import Data.Set (Set)+import qualified Data.Set as Set+import Data.String (IsString (fromString))+import Data.Vector (Vector)+import qualified Data.Vector as Vector import GHC.Generics-import Prelude-  ( ($),-    (.),-    Eq (..),-    Ord,-    otherwise,-  )+import Prelude (Either (Left, Right), Eq (..), Foldable (length), Ord, maybe, otherwise, show, ($), (-), (.))  type DecodeConstraint a =   ( Generic a,@@ -112,6 +108,26 @@  instance Decode a => Decode [a] where   decode = withList decode++instance Decode a => Decode (NonEmpty a) where+  decode = withRefinedList (maybe (Left "Expected a NonEmpty list") Right . NonEmpty.nonEmpty) decode++-- | Should this instance dedupe silently or fail on dupes ?+instance (Ord a, Decode a) => Decode (Set a) where+  decode val = do+    listVal <- withList (decode @a) val+    let setVal = Set.fromList listVal+    let setLength = length setVal+    let listLength = length setVal+    if listLength == setLength+      then pure setVal+      else failure (fromString ("Expected a List without duplicates, found " <> show (setLength - listLength) <> " duplicates") :: InternalError)++instance (Decode a) => Decode (Seq a) where+  decode = fmap Seq.fromList . withList decode++instance (Decode a) => Decode (Vector a) where+  decode = fmap Vector.fromList . withList decode  -- | Decode GraphQL type with Specific Kind class DecodeKind (kind :: GQL_KIND) a where
src/Data/Morpheus/Server/Deriving/Encode.hs view
@@ -23,6 +23,8 @@ import Control.Monad (Monad ((>>=))) import Data.Functor (fmap) import Data.Functor.Identity (Identity (..))+import Data.List.NonEmpty (NonEmpty)+import qualified Data.List.NonEmpty as NonEmpty import Data.Map (Map) import qualified Data.Map as M   ( toList,@@ -40,7 +42,7 @@   ) import Data.Morpheus.Server.Deriving.Channels   ( ChannelsConstraint,-    getChannels,+    channelResolver,   ) import Data.Morpheus.Server.Deriving.Decode   ( DecodeConstraint,@@ -92,6 +94,8 @@   ( toList,   ) import Data.Traversable (traverse)+import Data.Vector (Vector)+import qualified Data.Vector as Vector import GHC.Generics   ( Generic (..),   )@@ -123,6 +127,14 @@ instance Encode o e m (Pair k v) => Encode o e m (k, v) where   encode (key, value) = encode (Pair key value) +--  NonEmpty+instance Encode o e m [a] => Encode o e m (NonEmpty a) where+  encode = encode . NonEmpty.toList++--  Vector+instance Encode o e m [a] => Encode o e m (Vector a) where+  encode = encode . Vector.toList+ --  Set instance Encode o e m [a] => Encode o e m (Set a) where   encode = encode . S.toList@@ -269,4 +281,4 @@     where       channelMap         | isEmptyType (Proxy :: Proxy (sub (Resolver SUBSCRIPTION e m))) = Nothing-        | otherwise = Just (getChannels subscriptionResolver)+        | otherwise = Just (channelResolver subscriptionResolver)
src/Data/Morpheus/Server/Deriving/Schema.hs view
@@ -67,9 +67,7 @@   ) import Data.Morpheus.Server.Types.SchemaT   ( SchemaT,-    closeWith,-    setMutation,-    setSubscription,+    toSchema,   ) import Data.Morpheus.Server.Types.Types   ( MapKind,@@ -97,7 +95,6 @@     TypeDefinition (..),     TypeName,     fieldsToArguments,-    initTypeLib,   ) import Data.Morpheus.Types.Internal.Resolving   ( Resolver,@@ -147,10 +144,18 @@   f (Schema CONST) deriveSchema _ = resultOr failure pure schema   where-    schema = closeWith (initTypeLib <$> queryDef <* mutationDef <* subscriptionDef)-    queryDef = deriveObjectType (Proxy @(query (Resolver QUERY e m)))-    mutationDef = deriveObjectType (Proxy @(mut (Resolver MUTATION e m))) >>= setMutation-    subscriptionDef = deriveObjectType (Proxy @(subs (Resolver SUBSCRIPTION e m))) >>= setSubscription+    schema = toSchema schemaT+    schemaT ::+      SchemaT+        ( TypeDefinition OBJECT CONST,+          TypeDefinition OBJECT CONST,+          TypeDefinition OBJECT CONST+        )+    schemaT =+      (,,)+        <$> deriveObjectType (Proxy @(query (Resolver QUERY e m)))+        <*> deriveObjectType (Proxy @(mut (Resolver MUTATION e m)))+        <*> deriveObjectType (Proxy @(subs (Resolver SUBSCRIPTION e m)))  instance {-# OVERLAPPABLE #-} (GQLType a, DeriveKindedType (KIND a) a) => DeriveType cat a where   deriveType _ = deriveKindedType (KindedProxy :: KindedProxy (KIND a) a)
src/Data/Morpheus/Server/Deriving/Schema/Internal.hs view
@@ -10,7 +10,6 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-}@@ -48,7 +47,6 @@ import Data.Morpheus.Error (globalErrorMessage) import Data.Morpheus.Internal.Utils   ( Failure (..),-    empty,     singleton,   ) import Data.Morpheus.Server.Deriving.Utils@@ -71,11 +69,8 @@ import Data.Morpheus.Types.Internal.AST   ( CONST,     DataEnumValue (..),-    DataFingerprint (..),-    DataUnion,     Description,     Directives,-    ELEM,     FieldContent (..),     FieldDefinition (..),     FieldName,@@ -106,7 +101,6 @@   ( Eventless,     Result (..),   )-import Data.Proxy (Proxy (..)) import Data.Semigroup ((<>)) import Data.Traversable (traverse) import Language.Haskell.TH (Exp, Q)@@ -166,10 +160,10 @@ mkObjectType fields typeName = mkType typeName (DataObject [] fields)  failureOnlyObject :: forall c a b. (GQLType a) => KindedType c a -> SchemaT b-failureOnlyObject _ =+failureOnlyObject proxy =   failure     $ globalErrorMessage-    $ msg (gqlTypeName $ __type (Proxy @a)) <> " should have only one nonempty constructor"+    $ msg (gqlTypeName $ __type proxy) <> " should have only one nonempty constructor"  type TyContentM kind = (SchemaT (Maybe (FieldContent TRUE kind CONST))) @@ -187,20 +181,19 @@ unpackMs = traverse unpackCons  builder ::-  forall kind (a :: *).   GQLType a =>   KindedType kind a ->   [ConsRep (TyContent kind)] ->   SchemaT (TypeContent TRUE kind CONST)-builder scope [ConsRep {consFields}] = buildObj <$> sequence (implements (Proxy @a))+builder scope [ConsRep {consFields}] = buildObj <$> sequence (implements scope)   where     buildObj interfaces = wrapFields interfaces scope (mkFieldsDefinition consFields)-builder scope cons = genericUnion scope cons+builder scope cons = genericUnion cons   where-    proxy = Proxy @a-    typeData = __type proxy-    genericUnion InputType = buildInputUnion typeData-    genericUnion OutputType = buildUnionType typeData DataUnion (DataObject [])+    typeData = __type scope+    genericUnion =+      mkUnionType scope typeData+        . analyseRep (gqlTypeName typeData)  class UpdateDef value where   updateDef :: GQLType a => f a -> value -> value@@ -261,12 +254,18 @@   SchemaT () updateByContent f proxy =   updateSchema-    (gqlTypeName $ __type proxy)     (gqlFingerprint $ __type proxy)     deriveD     proxy   where-    deriveD _ = buildType proxy <$> f proxy+    deriveD =+      fmap+        ( TypeDefinition+            (description proxy)+            (gqlTypeName (__type proxy))+            []+        )+        . f  analyseRep :: TypeName -> [ConsRep (Maybe (FieldContent TRUE kind CONST))] -> ResRep (Maybe (FieldContent TRUE kind CONST)) analyseRep baseName cons =@@ -279,47 +278,24 @@     (enumRep, left1) = partition isEmptyConstraint cons     (unionRefRep, unionRecordRep) = partition (isUnionRef baseName) left1 -buildInputUnion ::-  TypeData ->-  [ConsRep (Maybe (FieldContent TRUE IN CONST))] ->-  SchemaT (TypeContent TRUE IN CONST)-buildInputUnion TypeData {gqlTypeName, gqlFingerprint} =-  mkInputUnionType gqlFingerprint . analyseRep gqlTypeName--buildUnionType ::-  (ELEM LEAF kind ~ TRUE) =>+mkUnionType ::+  KindedType kind a ->   TypeData ->-  (DataUnion CONST -> TypeContent TRUE kind CONST) ->-  (FieldsDefinition kind CONST -> TypeContent TRUE kind CONST) ->-  [ConsRep (Maybe (FieldContent TRUE kind CONST))] ->+  ResRep (Maybe (FieldContent TRUE kind CONST)) ->   SchemaT (TypeContent TRUE kind CONST)-buildUnionType typeData wrapUnion wrapObject =-  mkUnionType typeData wrapUnion wrapObject . analyseRep (gqlTypeName typeData)--mkInputUnionType :: DataFingerprint -> ResRep (Maybe (FieldContent TRUE IN CONST)) -> SchemaT (TypeContent TRUE IN CONST)-mkInputUnionType _ ResRep {unionRef = [], unionRecordRep = [], enumCons} = pure $ mkEnumContent enumCons-mkInputUnionType baseFingerprint ResRep {unionRef, unionRecordRep, enumCons} = DataInputUnion <$> typeMembers+mkUnionType InputType _ ResRep {unionRef = [], unionRecordRep = [], enumCons} = pure $ mkEnumContent enumCons+mkUnionType OutputType _ ResRep {unionRef = [], unionRecordRep = [], enumCons} = pure $ mkEnumContent enumCons+mkUnionType InputType _ ResRep {unionRef, unionRecordRep, enumCons} = DataInputUnion <$> typeMembers   where     typeMembers :: SchemaT [UnionMember IN CONST]-    typeMembers = withMembers <$> buildUnions wrapInputObject baseFingerprint unionRecordRep+    typeMembers = withMembers <$> buildUnions unionRecordRep       where         withMembers unionMembers = fmap mkUnionMember (unionRef <> unionMembers) <> fmap (`UnionMember` False) enumCons-    wrapInputObject :: (FieldsDefinition IN CONST -> TypeContent TRUE IN CONST)-    wrapInputObject = DataInputObject--mkUnionType ::-  (ELEM LEAF kind ~ TRUE) =>-  TypeData ->-  (DataUnion CONST -> TypeContent TRUE kind CONST) ->-  (FieldsDefinition kind CONST -> TypeContent TRUE kind CONST) ->-  ResRep (Maybe (FieldContent TRUE kind CONST)) ->-  SchemaT (TypeContent TRUE kind CONST)-mkUnionType _ _ _ ResRep {unionRef = [], unionRecordRep = [], enumCons} = pure $ mkEnumContent enumCons-mkUnionType typeData@TypeData {gqlFingerprint} wrapUnion wrapObject ResRep {unionRef, unionRecordRep, enumCons} = wrapUnion . map mkUnionMember <$> typeMembers+mkUnionType OutputType typeData ResRep {unionRef, unionRecordRep, enumCons} = DataUnion . map mkUnionMember <$> typeMembers   where     typeMembers = do-      enums <- buildUnionEnum wrapObject typeData enumCons-      unions <- buildUnions wrapObject gqlFingerprint unionRecordRep+      enums <- buildUnionEnum typeData enumCons+      unions <- buildUnions unionRecordRep       pure (unionRef <> enums <> unions)  wrapFields :: [TypeName] -> KindedType kind a -> FieldsDefinition kind CONST -> TypeContent TRUE kind CONST@@ -334,21 +310,35 @@   mkField fieldValue fieldSelector fieldTypeRef  buildUnions ::-  (FieldsDefinition kind CONST -> TypeContent TRUE kind CONST) ->-  DataFingerprint ->+  PackObject kind =>   [ConsRep (Maybe (FieldContent TRUE kind CONST))] ->   SchemaT [TypeName]-buildUnions wrapObject baseFingerprint cons =+buildUnions cons =   traverse_ buildURecType cons $> fmap consName cons   where-    buildURecType = insertType . buildUnionRecord wrapObject baseFingerprint+    buildURecType = insertType . buildUnionRecord +buildUnionRecord ::+  PackObject kind =>+  ConsRep (Maybe (FieldContent TRUE kind CONST)) ->+  TypeDefinition kind CONST+buildUnionRecord ConsRep {consName, consFields} =+  mkType consName (packObject $ mkFieldsDefinition consFields)++class PackObject kind where+  packObject :: FieldsDefinition kind CONST -> TypeContent TRUE kind CONST++instance PackObject OUT where+  packObject = DataObject []++instance PackObject IN where+  packObject = DataInputObject+ buildUnionEnum ::-  (FieldsDefinition cat CONST -> TypeContent TRUE cat CONST) ->   TypeData ->   [TypeName] ->   SchemaT [TypeName]-buildUnionEnum wrapObject TypeData {gqlTypeName, gqlFingerprint} enums = updates $> members+buildUnionEnum TypeData {gqlTypeName} enums = updates $> members   where     members       | null enums = []@@ -360,58 +350,24 @@     updates       | null enums = pure ()       | otherwise =-        buildEnumObject wrapObject enumTypeWrapperName gqlFingerprint enumTypeName-          *> buildEnum enumTypeName gqlFingerprint enums--buildType :: GQLType a => f a -> TypeContent TRUE cat CONST -> TypeDefinition cat CONST-buildType proxy typeContent =-  TypeDefinition-    { typeName = gqlTypeName typeData,-      typeFingerprint = gqlFingerprint typeData,-      typeDescription = description proxy,-      typeDirectives = [],-      typeContent-    }-  where-    typeData = __type proxy--buildUnionRecord ::-  (FieldsDefinition kind CONST -> TypeContent TRUE kind CONST) ->-  DataFingerprint ->-  ConsRep (Maybe (FieldContent TRUE kind CONST)) ->-  TypeDefinition kind CONST-buildUnionRecord wrapObject typeFingerprint ConsRep {consName, consFields} =-  mkSubType consName typeFingerprint (wrapObject $ mkFieldsDefinition consFields)+        buildEnumObject enumTypeWrapperName enumTypeName+          *> buildEnum enumTypeName enums -buildEnum :: TypeName -> DataFingerprint -> [TypeName] -> SchemaT ()-buildEnum typeName typeFingerprint tags =+buildEnum :: TypeName -> [TypeName] -> SchemaT ()+buildEnum typeName tags =   insertType-    ( mkSubType typeName typeFingerprint (mkEnumContent tags) ::+    ( mkType typeName (mkEnumContent tags) ::         TypeDefinition LEAF CONST     ) -buildEnumObject ::-  (FieldsDefinition cat CONST -> TypeContent TRUE cat CONST) ->-  TypeName ->-  DataFingerprint ->-  TypeName ->-  SchemaT ()-buildEnumObject wrapObject typeName typeFingerprint enumTypeName =-  insertType $-    mkSubType-      typeName-      typeFingerprint-      ( wrapObject-          $ singleton-          $ mkInputValue "enum" [] enumTypeName-      )--mkSubType :: TypeName -> DataFingerprint -> TypeContent TRUE k CONST -> TypeDefinition k CONST-mkSubType typeName typeFingerprint typeContent =-  TypeDefinition-    { typeName,-      typeFingerprint,-      typeDescription = Nothing,-      typeDirectives = empty,-      typeContent-    }+buildEnumObject :: TypeName -> TypeName -> SchemaT ()+buildEnumObject typeName enumTypeName =+  insertType+    ( mkType+        typeName+        ( DataObject []+            $ singleton+            $ mkInputValue "enum" [] enumTypeName+        ) ::+        TypeDefinition OBJECT CONST+    )
src/Data/Morpheus/Server/Internal/TH/Decode.hs view
@@ -8,6 +8,7 @@   ( withInputObject,     withMaybe,     withList,+    withRefinedList,     withEnum,     withInputUnion,     decodeFieldWith,@@ -72,6 +73,20 @@   m [a] withList decode (List li) = traverse decode li withList _ isType = failure (typeMismatch "List" isType)++-- | Useful for more restrictive instances of lists (non empty, size indexed etc)+withRefinedList ::+  (Failure InternalError m, Monad m) =>+  ([a] -> Either Message (rList a)) ->+  (ValidValue -> m a) ->+  ValidValue ->+  m (rList a)+withRefinedList refiner decode (List li) = do+  listRes <- traverse decode li+  case refiner listRes of+    Left err -> failure (typeMismatch err (List li))+    Right value -> pure value+withRefinedList _ _ isType = failure (typeMismatch "List" isType)  withEnum :: Failure InternalError m => (TypeName -> m a) -> Value VALID -> m a withEnum decode (Enum value) = decode value
src/Data/Morpheus/Server/TH/Compile.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.Server.TH.Compile   ( compileDocument,@@ -10,6 +12,9 @@ -- --  Morpheus +import qualified Data.ByteString.Lazy.Char8 as LB+  ( pack,+  ) import Data.Morpheus.Core   ( parseTypeDefinitions,   )@@ -29,11 +34,9 @@ import Data.Morpheus.Types.Internal.Resolving   ( Result (..),   )-import qualified Data.Text as T-  ( pack,-  ) import Language.Haskell.TH (Dec, Q) import Language.Haskell.TH.Quote (QuasiQuoter (..))+import Relude  gqlDocumentNamespace :: QuasiQuoter gqlDocumentNamespace =@@ -45,7 +48,7 @@     }   where     notHandled things =-      error $ things ++ " are not supported by the GraphQL QuasiQuoter"+      error $ things <> " are not supported by the GraphQL QuasiQuoter"  gqlDocument :: QuasiQuoter gqlDocument =@@ -57,11 +60,11 @@     }   where     notHandled things =-      error $ things ++ " are not supported by the GraphQL QuasiQuoter"+      error $ things <> " are not supported by the GraphQL QuasiQuoter"  compileDocument :: ServerDecContext -> String -> Q [Dec] compileDocument ctx documentTXT =-  case parseTypeDefinitions (T.pack documentTXT) of+  case parseTypeDefinitions (LB.pack documentTXT) of     Failure errors -> fail (renderGQLErrors errors)     Success {result = schema, warnings} ->       gqlWarnings warnings >> toTHDefinitions (namespace ctx) schema >>= declare ctx
src/Data/Morpheus/Server/TH/Declare/Type.hs view
@@ -2,13 +2,13 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Data.Morpheus.Server.TH.Declare.Type   ( declareType,   ) where -import Control.Monad.Reader (asks) import Data.Morpheus.Internal.TH   ( declareTypeRef,     m',@@ -38,8 +38,8 @@ import Data.Morpheus.Types.Internal.Resolving   ( SubscriptionField,   )-import GHC.Generics (Generic) import Language.Haskell.TH+import Relude hiding (Type)  declareType :: ServerTypeDefinition cat s -> ServerDec [Dec] declareType ServerTypeDefinition {tKind = KindScalar} = pure []
src/Data/Morpheus/Server/TH/Transform.hs view
@@ -36,7 +36,6 @@     FieldContent (..),     FieldDefinition (..),     FieldName,-    Fields (..),     FieldsDefinition,     IN,     OUT,@@ -226,7 +225,7 @@     pure       [ ServerTypeDefinition           { tName,-            tCons = [mkCons tName (Fields arguments)],+            tCons = [mkCons tName arguments],             tKind = KindInputObject,             typeArgD = [],             typeOriginal = Nothing
src/Data/Morpheus/Server/Types/GQLType.hs view
@@ -21,7 +21,10 @@ -- MORPHEUS import Data.Map (Map) import Data.Morpheus.Kind-import Data.Morpheus.Server.Types.SchemaT (SchemaT)+import Data.Morpheus.Server.Types.SchemaT+  ( SchemaT,+    TypeFingerprint (..),+  ) import Data.Morpheus.Server.Types.Types   ( MapKind,     Pair,@@ -31,7 +34,6 @@ import Data.Morpheus.Types.Internal.AST   ( ArgumentsDefinition,     CONST,-    DataFingerprint (..),     Description,     Directives,     FieldName,@@ -39,7 +41,6 @@     TypeName (..),     TypeWrapper (..),     Value,-    internalFingerprint,     toNullable,   ) import Data.Morpheus.Types.Internal.Resolving@@ -76,13 +77,12 @@     fmap,     id,     mempty,-    show,   )  data TypeData = TypeData   { gqlTypeName :: TypeName,     gqlWrappers :: [TypeWrapper],-    gqlFingerprint :: DataFingerprint+    gqlFingerprint :: TypeFingerprint   }  data GQLTypeOptions = GQLTypeOptions@@ -102,8 +102,8 @@   where     getName = fmap (fmap (pack . tyConName)) (fmap replacePairCon . ignoreResolver . splitTyConApp . typeRep) -getFingerprint :: Typeable a => f a -> DataFingerprint-getFingerprint = DataFingerprint "Typeable" . fmap show . conFingerprints+getFingerprint :: Typeable a => f a -> TypeFingerprint+getFingerprint = TypeableFingerprint . conFingerprints   where     conFingerprints = fmap (fmap tyConFingerprint) (ignoreResolver . splitTyConApp . typeRep) @@ -119,7 +119,7 @@ mkTypeData name =   TypeData     { gqlTypeName = name,-      gqlFingerprint = internalFingerprint name [],+      gqlFingerprint = InternalFingerprint name,       gqlWrappers = []     } 
src/Data/Morpheus/Server/Types/SchemaT.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-}@@ -10,55 +11,76 @@  module Data.Morpheus.Server.Types.SchemaT   ( SchemaT,-    closeWith,     updateSchema,     insertType,-    setMutation,-    setSubscription,+    TypeFingerprint (..),+    toSchema,   ) where  import Control.Applicative (Applicative (..)) import Control.Monad (Monad (..), foldM) import Data.Function ((&))-import Data.Functor (Functor (..))-import Data.Morpheus.Error (nameCollisionError)+import Data.Functor ((<$>), Functor (..))+import Data.Map+  ( Map,+    elems,+    empty,+    insert,+    member,+  ) import Data.Morpheus.Internal.Utils   ( Failure (..),   ) import Data.Morpheus.Types.Internal.AST-  ( CONST,-    DataFingerprint,+  ( ANY,+    CONST,+    CONST,     OBJECT,     Schema (..),     TypeContent (..),     TypeDefinition (..),-    TypeName (..),-    isNotSystemTypeName,-    isTypeDefined,-    safeDefineType,+    TypeName,+    defineSchemaWith,+    toAny,   ) import Data.Morpheus.Types.Internal.Resolving   ( Eventless,   ) import Data.Semigroup (Semigroup (..))+import GHC.Fingerprint.Type (Fingerprint)+import GHC.Generics (Generic) import Prelude   ( ($),     (.),     Eq (..),     Maybe (..),+    Ord,+    Show,     const,     null,     otherwise,-    uncurry,   ) +data TypeFingerprint+  = TypeableFingerprint [Fingerprint]+  | InternalFingerprint TypeName+  | CustomFingerprint TypeName+  deriving+    ( Generic,+      Show,+      Eq,+      Ord+    )++type MyMap = Map TypeFingerprint (TypeDefinition ANY CONST)+ -- Helper Functions newtype SchemaT a = SchemaT   { runSchemaT ::       Eventless         ( a,-          [Schema CONST -> Eventless (Schema CONST)]+          [MyMap -> Eventless MyMap]         )   }   deriving (Functor)@@ -71,11 +93,10 @@  instance Applicative SchemaT where   pure = SchemaT . pure . (,[])-  (SchemaT v1) <*> (SchemaT v2) =-    SchemaT $ do-      (f, u1) <- v1-      (a, u2) <- v2-      pure (f a, u1 <> u2)+  (SchemaT v1) <*> (SchemaT v2) = SchemaT $ do+    (f, u1) <- v1+    (a, u2) <- v2+    pure (f a, u1 <> u2)  instance Monad SchemaT where   return = pure@@ -85,17 +106,17 @@       (y, up2) <- runSchemaT (f x)       pure (y, up1 <> up2) -closeWith :: SchemaT (Schema CONST) -> Eventless (Schema CONST)-closeWith (SchemaT v) = v >>= uncurry execUpdates--init :: (Schema CONST -> Eventless (Schema CONST)) -> SchemaT ()-init f = SchemaT $ pure ((), [f])--setMutation :: TypeDefinition OBJECT CONST -> SchemaT ()-setMutation mut = init (\schema -> pure $ schema {mutation = optionalType mut})--setSubscription :: TypeDefinition OBJECT CONST -> SchemaT ()-setSubscription x = init (\schema -> pure $ schema {subscription = optionalType x})+toSchema ::+  SchemaT+    ( TypeDefinition OBJECT CONST,+      TypeDefinition OBJECT CONST,+      TypeDefinition OBJECT CONST+    ) ->+  Eventless (Schema CONST)+toSchema (SchemaT v) = do+  ((q, m, s), typeDefs) <- v+  types <- elems <$> execUpdates empty typeDefs+  defineSchemaWith types (optionalType q, optionalType m, optionalType s)  optionalType :: TypeDefinition OBJECT CONST -> Maybe (TypeDefinition OBJECT CONST) optionalType td@TypeDefinition {typeContent = DataObject {objectFields}}@@ -105,28 +126,21 @@ execUpdates :: Monad m => a -> [a -> m a] -> m a execUpdates = foldM (&) -insertType ::-  TypeDefinition cat CONST ->-  SchemaT ()-insertType dt@TypeDefinition {typeName, typeFingerprint} =-  updateSchema typeName typeFingerprint (const $ pure dt) ()+insertType :: TypeDefinition cat CONST -> SchemaT ()+insertType dt = updateSchema (CustomFingerprint (typeName dt)) (const $ pure dt) ()  updateSchema ::-  TypeName ->-  DataFingerprint ->+  TypeFingerprint ->   (a -> SchemaT (TypeDefinition cat CONST)) ->   a ->   SchemaT ()-updateSchema typeName typeFingerprint f x-  | isNotSystemTypeName typeName = SchemaT (pure ((), [upLib]))-  | otherwise = SchemaT (pure ((), []))+updateSchema InternalFingerprint {} _ _ = SchemaT $ pure ((), [])+updateSchema fingerprint f x =+  SchemaT $ pure ((), [upLib])   where-    upLib :: Schema CONST -> Eventless (Schema CONST)-    upLib lib = case isTypeDefined typeName lib of-      Nothing -> do-        (tyDef, updater) <- runSchemaT (f x)-        execUpdates lib (safeDefineType tyDef : updater)-      Just fingerprint'-        | fingerprint' == typeFingerprint -> pure lib-        -- throw error if 2 different types has same name-        | otherwise -> failure [nameCollisionError typeName]+    upLib :: MyMap -> Eventless MyMap+    upLib lib+      | member fingerprint lib = pure lib+      | otherwise = do+        (type', updates) <- runSchemaT (f x)+        execUpdates lib ((pure . insert fingerprint (toAny type')) : updates)
src/Data/Morpheus/Types.hs view
@@ -10,8 +10,7 @@  -- | GQL Types module Data.Morpheus.Types-  ( Event (..),-    GQLType (KIND, description, implements),+  ( GQLType (KIND, description, implements, getDescriptions, typeOptions, getDirectives),     GQLScalar (parseValue, serialize),     GQLRequest (..),     GQLResponse (..),@@ -33,9 +32,6 @@     subscribe,     unsafeInternalContext,     ResolverContext (..),-    Input,-    WS,-    HTTP,     -- Resolvers     ResolverO,     ComposedResolver,@@ -55,7 +51,8 @@     interface,     SubscriptionField,     App,-    RenderGQL (..),+    RenderGQL,+    render,     GQLTypeOptions (..),   ) where@@ -70,7 +67,8 @@   ) import Data.Morpheus.Core   ( App,-    RenderGQL (..),+    RenderGQL,+    render,   ) import Data.Morpheus.Server.Deriving.Schema   ( DeriveType,@@ -104,8 +102,7 @@     msg,   ) import Data.Morpheus.Types.Internal.Resolving-  ( Event (..),-    Failure,+  ( Failure,     PushEvents (..),     Resolver,     ResolverContext (..),@@ -115,11 +112,6 @@     pushEvents,     subscribe,     unsafeInternalContext,-  )-import Data.Morpheus.Types.Internal.Subscription-  ( HTTP,-    Input,-    WS,   ) import Data.Proxy   ( Proxy (..),
− src/Data/Morpheus/Types/Internal/Subscription.hs
@@ -1,147 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}--module Data.Morpheus.Types.Internal.Subscription-  ( connect,-    disconnect,-    connectionThread,-    runStreamWS,-    runStreamHTTP,-    Scope (..),-    Input (..),-    WS,-    HTTP,-    acceptApolloRequest,-    publish,-    Store (..),-    initDefaultStore,-    publishEventWith,-    ClientConnectionStore,-    empty,-    toList,-    connectionSessionIds,-    SessionID,-    streamApp,-  )-where--import Control.Concurrent-  ( modifyMVar_,-    newMVar,-    readMVar,-  )-import Control.Exception (finally)-import Control.Monad (forever)-import Control.Monad.IO.Class (MonadIO (..))-import Control.Monad.IO.Unlift-  ( MonadUnliftIO,-    withRunInIO,-  )--- MORPHEUS--import Data.Morpheus.Core-  ( App,-    runAppStream,-  )-import Data.Morpheus.Internal.Utils-  ( empty,-  )-import Data.Morpheus.Types.Internal.Resolving-  ( Event,-  )-import Data.Morpheus.Types.Internal.Subscription.Apollo-  ( acceptApolloRequest,-  )-import Data.Morpheus.Types.Internal.Subscription.ClientConnectionStore-  ( ClientConnectionStore,-    SessionID,-    connectionSessionIds,-    delete,-    publish,-    toList,-  )-import Data.Morpheus.Types.Internal.Subscription.Stream-  ( HTTP,-    Input (..),-    Scope (..),-    Stream,-    WS,-    runStreamHTTP,-    runStreamWS,-    toOutStream,-  )-import Data.UUID.V4 (nextRandom)--streamApp :: Monad m => App e m -> Input api -> Stream api e m-streamApp app = toOutStream (runAppStream app)--connect :: MonadIO m => m (Input WS)-connect = Init <$> liftIO nextRandom--disconnect :: Scope WS e m -> Input WS -> m ()-disconnect ScopeWS {update} (Init clientID) = update (delete clientID)---- | PubSubStore interface--- shared GraphQL state between __websocket__ and __http__ server,--- you can define your own store if you provide write and read methods--- to work properly Morpheus needs all entries of ClientConnectionStore (+ client Callbacks)--- that why it is recomended that you use many local ClientStores on evenry server node--- rathen then single centralized Store.-data Store e m = Store-  { readStore :: m (ClientConnectionStore e m),-    writeStore :: (ClientConnectionStore e m -> ClientConnectionStore e m) -> m ()-  }--publishEventWith ::-  (MonadIO m, Eq channel) =>-  Store (Event channel cont) m ->-  Event channel cont ->-  m ()-publishEventWith store event = readStore store >>= publish event---- | initializes empty GraphQL state-initDefaultStore ::-  ( MonadIO m,-    MonadIO m2-  ) =>-  m2 (Store event m)-initDefaultStore = do-  store <- liftIO $ newMVar empty-  pure-    Store-      { readStore = liftIO $ readMVar store,-        writeStore = \changes -> liftIO $ modifyMVar_ store (return . changes)-      }--finallyM :: MonadUnliftIO m => m () -> m () -> m ()-finallyM loop end = withRunInIO $ \runIO -> finally (runIO loop) (runIO end)--connectionThread ::-  ( MonadUnliftIO m-  ) =>-  App e m ->-  Scope WS e m ->-  m ()-connectionThread api scope = do-  input <- connect-  finallyM-    (connectionLoop api scope input)-    (disconnect scope input)--connectionLoop ::-  Monad m =>-  App e m ->-  Scope WS e m ->-  Input WS ->-  m ()-connectionLoop app scope input =-  forever-    $ runStreamWS scope-    $ streamApp app input
− src/Data/Morpheus/Types/Internal/Subscription/Apollo.hs
@@ -1,167 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.Types.Internal.Subscription.Apollo-  ( ApolloAction (..),-    apolloFormat,-    acceptApolloRequest,-    toApolloResponse,-    Validation,-  )-where--import Control.Applicative (Applicative (..))-import Control.Monad.IO.Class (MonadIO (..))-import Data.Aeson-  ( (.:),-    (.:?),-    (.=),-    FromJSON (..),-    ToJSON (..),-    Value (..),-    eitherDecode,-    encode,-    pairs,-    withObject,-  )-import Data.ByteString.Lazy.Char8-  ( ByteString,-    pack,-  )-import Data.Either-  ( Either (..),-    either,-  )-import Data.Functor ((<$>))-import Data.Maybe-  ( Maybe (..),-    maybe,-  )-import Data.Morpheus.Types.IO-  ( GQLRequest (..),-    GQLResponse,-  )-import Data.Morpheus.Types.Internal.AST-  ( FieldName,-    Token,-  )-import Data.Semigroup ((<>))-import Data.Text-  ( Text,-    unpack,-  )-import GHC.Generics (Generic)-import Network.WebSockets-  ( AcceptRequest (..),-    Connection,-    PendingConnection,-    RequestHead,-    acceptRequestWith,-    getRequestSubprotocols,-    pendingRequest,-  )-import Prelude-  ( ($),-    (.),-    Show,-    String,-  )--type ID = Text--data ApolloSubscription payload = ApolloSubscription-  { apolloId :: Maybe ID,-    apolloType :: Text,-    apolloPayload :: Maybe payload-  }-  deriving (Show, Generic)--instance FromJSON a => FromJSON (ApolloSubscription a) where-  parseJSON = withObject "ApolloSubscription" objectParser-    where-      objectParser o =-        ApolloSubscription <$> o .:? "id" <*> o .: "type" <*> o .:? "payload"--data RequestPayload = RequestPayload-  { payloadOperationName :: Maybe FieldName,-    payloadQuery :: Maybe Token,-    payloadVariables :: Maybe Value-  }-  deriving (Show, Generic)--instance FromJSON RequestPayload where-  parseJSON = withObject "ApolloPayload" objectParser-    where-      objectParser o =-        RequestPayload <$> o .:? "operationName" <*> o .:? "query"-          <*> o .:? "variables"--instance ToJSON a => ToJSON (ApolloSubscription a) where-  toEncoding (ApolloSubscription id' type' payload') =-    pairs $ "id" .= id' <> "type" .= type' <> "payload" .= payload'--acceptApolloRequest ::-  MonadIO m =>-  PendingConnection ->-  m Connection-acceptApolloRequest pending =-  liftIO $-    acceptRequestWith-      pending-      (acceptApolloSubProtocol (pendingRequest pending))--acceptApolloSubProtocol :: RequestHead -> AcceptRequest-acceptApolloSubProtocol reqHead =-  apolloProtocol (getRequestSubprotocols reqHead)-  where-    apolloProtocol ["graphql-subscriptions"] =-      AcceptRequest (Just "graphql-subscriptions") []-    apolloProtocol ["graphql-ws"] = AcceptRequest (Just "graphql-ws") []-    apolloProtocol _ = AcceptRequest Nothing []--toApolloResponse :: ID -> GQLResponse -> ByteString-toApolloResponse sid val =-  encode $ ApolloSubscription (Just sid) "data" (Just val)--data ApolloAction-  = SessionStop ID-  | SessionStart ID GQLRequest-  | ConnectionInit--type Validation = Either ByteString--apolloFormat :: ByteString -> Validation ApolloAction-apolloFormat = validateReq . eitherDecode-  where-    validateReq :: Either String (ApolloSubscription RequestPayload) -> Validation ApolloAction-    validateReq = either (Left . pack) validateSub-    --------------------------------------    validateSub :: ApolloSubscription RequestPayload -> Validation ApolloAction-    validateSub ApolloSubscription {apolloType = "connection_init"} =-      pure ConnectionInit-    validateSub ApolloSubscription {apolloType = "start", apolloId, apolloPayload} =-      do-        sessionId <- validateSession apolloId-        payload <- validatePayload apolloPayload-        pure $ SessionStart sessionId payload-    validateSub ApolloSubscription {apolloType = "stop", apolloId} =-      SessionStop <$> validateSession apolloId-    validateSub ApolloSubscription {apolloType} =-      Left $ "Unknown Request type \"" <> pack (unpack apolloType) <> "\"."-    ---------------------------------------------    validateSession :: Maybe ID -> Validation ID-    validateSession = maybe (Left "\"id\" was not provided") Right-    --------------------------------------    validatePayload = maybe (Left "\"payload\" was not provided") validatePayloadContent-    --------------------------------------    validatePayloadContent-      RequestPayload-        { payloadQuery,-          payloadOperationName = operationName,-          payloadVariables = variables-        } = do-        query <- maybe (Left "\"payload.query\" was not provided") Right payloadQuery-        pure $ GQLRequest {query, operationName, variables}
− src/Data/Morpheus/Types/Internal/Subscription/ClientConnectionStore.hs
@@ -1,187 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.Types.Internal.Subscription.ClientConnectionStore-  ( ID,-    Session,-    SessionID,-    ClientConnectionStore,-    ClientConnection,-    Updates (..),-    startSession,-    endSession,-    empty,-    insert,-    delete,-    publish,-    toList,-    connectionSessionIds,-  )-where--import Control.Applicative (pure)-import Control.Monad (Monad ((>>=)))-import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Foldable (traverse_)-import Data.HashMap.Lazy (HashMap, keys)-import qualified Data.HashMap.Lazy as HM-  ( adjust,-    delete,-    elems,-    empty,-    insert,-    insertWith,-    keys,-    toList,-  )-import Data.List (filter, intersect)-import Data.Morpheus.Internal.Utils-  ( Collection (..),-    KeyOf (..),-  )-import Data.Morpheus.Types.Internal.Resolving-  ( Event (..),-    SubEvent,-    eventChannels,-  )-import Data.Morpheus.Types.Internal.Subscription.Apollo-  ( toApolloResponse,-  )-import Data.Semigroup ((<>))-import Data.Text (Text)-import Data.UUID (UUID)-import Prelude-  ( (.),-    Eq (..),-    Show (..),-    curry,-    not,-    null,-    otherwise,-    snd,-  )--type ID = UUID--type SessionID = Text--type Session = (ID, SessionID)--data ClientConnection e (m :: * -> *) = ClientConnection-  { connectionId :: ID,-    connectionCallback :: ByteString -> m (),-    -- one connection can have multiple subscription session-    connectionSessions :: HashMap SessionID (SubEvent e m)-  }--connectionSessionIds :: ClientConnection e m -> [SessionID]-connectionSessionIds = HM.keys . connectionSessions--instance Show (ClientConnection e m) where-  show ClientConnection {connectionId, connectionSessions} =-    "Connection { id: "-      <> show connectionId-      <> ", sessions: "-      <> show (keys connectionSessions)-      <> " }"--publish ::-  ( Monad m,-    Eq channel-  ) =>-  Event channel content ->-  ClientConnectionStore (Event channel content) m ->-  m ()-publish event = traverse_ sendMessage . elems-  where-    sendMessage ClientConnection {connectionSessions, connectionCallback}-      | null connectionSessions = pure ()-      | otherwise = traverse_ send (filterByChannels connectionSessions)-      where-        send (sid, Event {content = subscriptionRes}) =-          subscriptionRes event >>= connectionCallback . toApolloResponse sid-        ----------------------------        filterByChannels =-          filter-            ( not-                . null-                . intersect (eventChannels event)-                . channels-                . snd-            )-            . HM.toList--newtype Updates e (m :: * -> *) = Updates-  { _runUpdate :: ClientConnectionStore e m -> ClientConnectionStore e m-  }--updateClient ::-  (ClientConnection e m -> ClientConnection e m) ->-  ID ->-  Updates e m-updateClient f cid = Updates (adjust f cid)--endSession :: Session -> Updates e m-endSession (clientId, sessionId) = updateClient endSub clientId-  where-    endSub client = client {connectionSessions = HM.delete sessionId (connectionSessions client)}--startSession :: SubEvent e m -> Session -> Updates e m-startSession subscriptions (clientId, sessionId) = updateClient startSub clientId-  where-    startSub client = client {connectionSessions = HM.insert sessionId subscriptions (connectionSessions client)}---- stores active client connections--- every registered client has ID--- when client connection is closed client(including all its subscriptions) can By removed By its ID-newtype ClientConnectionStore e (m :: * -> *) = ClientConnectionStore-  { unpackStore :: HashMap ID (ClientConnection e m)-  }-  deriving (Show)--type StoreMap e m =-  ClientConnectionStore e m ->-  ClientConnectionStore e m--mapStore ::-  ( HashMap ID (ClientConnection e m) ->-    HashMap ID (ClientConnection e m)-  ) ->-  StoreMap e m-mapStore f = ClientConnectionStore . f . unpackStore--elems :: ClientConnectionStore e m -> [ClientConnection e m]-elems = HM.elems . unpackStore--toList :: ClientConnectionStore e m -> [(UUID, ClientConnection e m)]-toList = HM.toList . unpackStore--instance KeyOf UUID (ClientConnection e m) where-  keyOf = connectionId--instance Collection (ClientConnection e m) (ClientConnectionStore e m) where-  empty = ClientConnectionStore empty-  singleton = ClientConnectionStore . singleton---- returns original store, if connection with same id already exist-insert ::-  ID ->-  (ByteString -> m ()) ->-  StoreMap e m-insert connectionId connectionCallback = mapStore (HM.insertWith (curry snd) connectionId c)-  where-    c = ClientConnection {connectionId, connectionCallback, connectionSessions = HM.empty}--adjust ::-  (ClientConnection e m -> ClientConnection e m) ->-  ID ->-  StoreMap e m-adjust f key = mapStore (HM.adjust f key)--delete ::-  ID ->-  StoreMap e m-delete key = mapStore (HM.delete key)
− src/Data/Morpheus/Types/Internal/Subscription/Stream.hs
@@ -1,227 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}--module Data.Morpheus.Types.Internal.Subscription.Stream-  ( toOutStream,-    runStreamWS,-    runStreamHTTP,-    Stream,-    Scope (..),-    Input (..),-    API (..),-    HTTP,-    WS,-  )-where--import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Foldable (traverse_)--- MORPHEUS-import Data.Morpheus.Error-  ( globalErrorMessage,-  )-import Data.Morpheus.Internal.Utils-  ( failure,-  )-import Data.Morpheus.Types.IO-  ( GQLRequest (..),-    GQLResponse (..),-  )-import Data.Morpheus.Types.Internal.AST-  ( GQLErrors,-    VALID,-    Value (..),-  )-import Data.Morpheus.Types.Internal.Resolving-  ( ResponseEvent (..),-    ResponseStream,-    Result (..),-    ResultT (..),-    runResultT,-  )-import Data.Morpheus.Types.Internal.Subscription.Apollo-  ( ApolloAction (..),-    apolloFormat,-    toApolloResponse,-  )-import Data.Morpheus.Types.Internal.Subscription.ClientConnectionStore-  ( ClientConnectionStore,-    ID,-    Session,-    Updates (..),-    endSession,-    insert,-    startSession,-  )--data API = HTTP | WS--type WS = 'WS--type HTTP = 'HTTP--data-  Input-    (api :: API)-  where-  Init :: ID -> Input WS-  Request :: GQLRequest -> Input HTTP--run :: Scope WS e m -> Updates e m -> m ()-run ScopeWS {update} (Updates changes) = update changes--data Scope (api :: API) event (m :: * -> *) where-  ScopeHTTP ::-    { httpCallback :: event -> m ()-    } ->-    Scope HTTP event m-  ScopeWS ::-    { listener :: m ByteString,-      callback :: ByteString -> m (),-      update :: (ClientConnectionStore event m -> ClientConnectionStore event m) -> m ()-    } ->-    Scope WS event m--data-  Stream-    (api :: API)-    e-    (m :: * -> *)-  where-  StreamWS ::-    { streamWS :: Scope WS e m -> m (Either ByteString [Updates e m])-    } ->-    Stream WS e m-  StreamHTTP ::-    { streamHTTP :: Scope HTTP e m -> m GQLResponse-    } ->-    Stream HTTP e m--handleResponseStream ::-  ( Monad m-  ) =>-  Session ->-  ResponseStream e m (Value VALID) ->-  Stream WS e m-handleResponseStream session (ResultT res) =-  StreamWS $ const $ unfoldR <$> res-  where-    execute Publish {} = apolloError $ globalErrorMessage "websocket can only handle subscriptions, not mutations"-    execute (Subscribe sub) = Right $ startSession sub session-    ---------------------------    unfoldR Success {events} = traverse execute events-    unfoldR Failure {errors} = apolloError errors-    ---------------------------    apolloError :: GQLErrors -> Either ByteString a-    apolloError = Left . toApolloResponse (snd session) . Errors--handleWSRequest ::-  ( Monad m,-    Functor m-  ) =>-  ( GQLRequest ->-    ResponseStream e m (Value VALID)-  ) ->-  ID ->-  ByteString ->-  Stream WS e m-handleWSRequest gqlApp clientId = handle . apolloFormat-  where-    --handle :: Applicative m => Validation ApolloAction -> Stream WS e m-    handle = either (liftWS . Left) handleAction-    ---------------------------------------------------    -- handleAction :: ApolloAction -> Stream WS e m-    handleAction ConnectionInit = liftWS $ Right []-    handleAction (SessionStart sessionId request) =-      handleResponseStream (clientId, sessionId) (gqlApp request)-    handleAction (SessionStop sessionId) =-      liftWS $-        Right [endSession (clientId, sessionId)]--liftWS ::-  Applicative m =>-  Either ByteString [Updates e m] ->-  Stream WS e m-liftWS = StreamWS . const . pure--runStreamWS ::-  (Monad m) =>-  Scope WS e m ->-  Stream WS e m ->-  m ()-runStreamWS scope@ScopeWS {callback} StreamWS {streamWS} =-  streamWS scope-    >>= either callback (traverse_ (run scope))--runStreamHTTP ::-  (Monad m) =>-  Scope HTTP e m ->-  Stream HTTP e m ->-  m GQLResponse-runStreamHTTP scope StreamHTTP {streamHTTP} =-  streamHTTP scope--toOutStream ::-  (Monad m) =>-  ( GQLRequest ->-    ResponseStream e m (Value VALID)-  ) ->-  Input api ->-  Stream api e m-toOutStream app (Init clientId) =-  StreamWS handle-  where-    handle ws@ScopeWS {listener, callback} = do-      let runS (StreamWS x) = x ws-      bla <- listener >>= runS . handleWSRequest app clientId-      pure $ (Updates (insert clientId callback) :) <$> bla-toOutStream app (Request req) = StreamHTTP $ handleResponseHTTP (app req)--handleResponseHTTP ::-  ( Monad m-  ) =>-  ResponseStream e m (Value VALID) ->-  Scope HTTP e m ->-  m GQLResponse-handleResponseHTTP-  res-  ScopeHTTP {httpCallback} = do-    x <- runResultT (handleRes res execute)-    case x of-      Success r _ events -> do-        traverse_ httpCallback events-        pure $ Data r-      Failure err -> pure (Errors err)-    where-      execute (Publish event) = pure event-      execute Subscribe {} = failure (globalErrorMessage "http server can't handle subscription")--handleRes ::-  (Monad m) =>-  ResponseStream e m a ->-  (ResponseEvent e m -> ResultT e' m e') ->-  ResultT e' m a-handleRes res execute = ResultT $ runResultT res >>= runResultT . unfoldRes execute--unfoldRes ::-  (Monad m) =>-  (e -> ResultT e' m e') ->-  Result e a ->-  ResultT e' m a-unfoldRes execute Success {events, result, warnings} = do-  events' <- traverse execute events-  ResultT $ pure $-    Success-      { result,-        warnings,-        events = events'-      }-unfoldRes _ Failure {errors} = ResultT $ pure $ Failure {errors}
test/Feature/Holistic/API.hs view
@@ -23,9 +23,9 @@     importGQLDocumentWithNamespace,   ) import Data.Morpheus.Kind (SCALAR)+import Data.Morpheus.Subscriptions (Event) import Data.Morpheus.Types-  ( Event,-    GQLRequest,+  ( GQLRequest,     GQLResponse,     GQLScalar (..),     GQLType (..),
test/Feature/Holistic/fragment/loopingFragment/response.json view
@@ -1,12 +1,8 @@ {   "errors": [     {-      "message": "Cannot spread fragment \"A\" within itself via \"A\", \"A\", \"C\".",-      "locations": [-        { "line": 17, "column": 3 },-        { "line": 7, "column": 10 },-        { "line": 9, "column": 3 }-      ]+      "message": "Fragment \"A\" is never used.",+      "locations": [{ "line": 7, "column": 10 }]     },     {       "message": "Cannot spread fragment \"C\" within itself via \"C\", \"B\", \"C\", \"A\".",@@ -26,16 +22,20 @@       ]     },     {-      "message": "Fragment \"A\" is never used.",-      "locations": [{ "line": 7, "column": 10 }]-    },-    {       "message": "Fragment \"B\" is never used.",       "locations": [{ "line": 12, "column": 10 }]     },     {       "message": "Fragment \"C\" is never used.",       "locations": [{ "line": 16, "column": 10 }]+    },+    {+      "message": "Cannot spread fragment \"A\" within itself via \"A\", \"A\", \"C\".",+      "locations": [+        { "line": 17, "column": 3 },+        { "line": 7, "column": 10 },+        { "line": 9, "column": 3 }+      ]     },     {       "message": "Fragment \"D\" is never used.",
test/Feature/Holistic/parsing/complex/response.json view
@@ -1,12 +1,12 @@ {   "errors": [     {-      "message": "Field \"street\" argument \"argInputObject\" is required but not provided.",-      "locations": [{ "line": 54, "column": 3 }]-    },-    {       "message": "Cannot query field \"myUnion\" on type \"User\".",       "locations": [{ "line": 43, "column": 3 }]+    },+    {+      "message": "Field \"street\" argument \"argInputObject\" is required but not provided.",+      "locations": [{ "line": 54, "column": 3 }]     }   ] }
test/Feature/Holistic/parsing/directive/notOnVariable/response.json view
@@ -1,7 +1,7 @@ {   "errors": [     {-      "message": "offset=22:\nunexpected '@'\nexpecting '!', ')', '=', Ignored, IgnoredTokens, VariableDefinition, or white space\n",+      "message": "offset=22:\nunexpected '@'\nexpecting !, ')', =, Ignored, IgnoredTokens, VariableDefinition, or white space\n",       "locations": [{ "line": 1, "column": 23 }]     }   ]
test/Feature/Holistic/parsing/invalidNotNullOperator/response.json view
@@ -1,7 +1,7 @@ {   "errors": [     {-      "message": "offset=20:\nunexpected '@'\nexpecting '!', ']', '_', Ignored, IgnoredTokens, digit, letter, or white space\n",+      "message": "offset=20:\nunexpected '@'\nexpecting !, ']', '_', Ignored, IgnoredTokens, digit, letter, or white space\n",       "locations": [{ "line": 1, "column": 21 }]     }   ]
test/Feature/Input/DefaultValue/intorspection/arguments/response.json view
@@ -18,7 +18,7 @@                   "ofType": null                 }               },-              "defaultValue": "{inputField2: \"value from argument inputCompound\", inputField3: [{fieldWithDefault: \"value2 from inputField3\", simpleField: EnumA}, {fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumA}], inputField4: [{fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumB}]}"+              "defaultValue": "{ inputField2: \"value from argument inputCompound\", inputField3: [{ fieldWithDefault: \"value2 from inputField3\", simpleField: EnumA }, { fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumA }], inputField4: [{ fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumB }] }"             },             {               "name": "input",
test/Feature/Input/DefaultValue/intorspection/input-compound/response.json view
@@ -33,7 +33,7 @@               }             }           },-          "defaultValue": "[{fieldWithDefault: \"value2 from inputField3\", simpleField: EnumA}, {fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumA}]"+          "defaultValue": "[{ fieldWithDefault: \"value2 from inputField3\", simpleField: EnumA }, { fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumA }]"         },         {           "name": "inputField4",@@ -50,7 +50,7 @@               }             }           },-          "defaultValue": "[{fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumB}]"+          "defaultValue": "[{ fieldWithDefault: \"value from fieldWithDefault\", simpleField: EnumB }]"         }       ],       "interfaces": null,
test/Feature/InputType/variables/invalidValue/invalidDefaultValue/response.json view
@@ -1,7 +1,7 @@ {   "errors": [     {-      "message": "Variable \"$v1\" got invalid value. Expected type \"[[[Int!]!]]!\" found {id: \"12\"}.",+      "message": "Variable \"$v1\" got invalid value. Expected type \"[[[Int!]!]]!\" found { id: \"12\" }.",       "locations": [         {           "line": 1,
test/Feature/Schema/nameCollision/response.json view
@@ -1,9 +1,12 @@ {   "errors": [     {-      "message": "Schema Validation Error, Name collision: \"A\" is used for different dataTypes in two separate modules",-      "locations": [-      ]+      "message": "There can Be only One TypeDefinition Named \"A\".",+      "locations": []+    },+    {+      "message": "There can Be only One TypeDefinition Named \"A\".",+      "locations": []     }   ] }
test/Feature/WrappedTypeName/API.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Feature.WrappedTypeName.API   ( api,@@ -9,9 +10,9 @@ where  import Data.Morpheus (interpreter)+import Data.Morpheus.Subscriptions (Event) import Data.Morpheus.Types-  ( Event,-    GQLRequest,+  ( GQLRequest,     GQLResponse,     GQLType (..),     RootResolver (..),@@ -19,8 +20,7 @@     constRes,     subscribe,   )-import Data.Text (Text)-import GHC.Generics (Generic)+import Relude  data Wrapped a b = Wrapped   { fieldA :: a,
test/Lib.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Lib   ( getGQLBody,@@ -8,12 +9,11 @@   ) where -import Control.Applicative ((<|>)) import Data.Aeson (FromJSON, Value (..), decode) import qualified Data.ByteString.Lazy as L (readFile) import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Maybe (fromMaybe)-import Data.Text (Text, unpack)+import Data.Text (unpack)+import Relude hiding (ByteString)  path :: Text -> String path name = "test/" ++ unpack name
test/Subscription/API.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DuplicateRecordFields #-}@@ -9,6 +10,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Subscription.API   ( app,@@ -20,18 +22,25 @@  import Data.Morpheus (App, deriveApp) import Data.Morpheus.Document (importGQLDocument)-import Data.Morpheus.Types+import Data.Morpheus.Subscriptions   ( Event (..),-    RootResolver (..),+  )+import Data.Morpheus.Types+  ( RootResolver (..),     subscribe,   )-import Data.Text (Text)+import Relude import Subscription.Utils (SubM)  data Channel   = DEITY   | HUMAN-  deriving (Show, Eq)+  deriving+    ( Show,+      Eq,+      Generic,+      Hashable+    )  importGQLDocument "test/Subscription/schema.gql" 
test/Subscription/Case/ApolloRequest.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Subscription.Case.ApolloRequest   ( testApolloRequest,@@ -9,11 +10,11 @@ where  import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Function ((&)) import Data.Morpheus (App)-import Data.Morpheus.Types+import Data.Morpheus.Subscriptions   ( Event,   )+import Relude hiding (ByteString) import Subscription.Utils   ( SimulationState (..),     SubM,@@ -36,7 +37,7 @@ type WSApp ch a = App (Event ch a) (SubM (Event ch a))  testUnknownType ::-  (Eq ch) =>+  (Eq ch, Show ch, Hashable ch) =>   App (Event ch a) (SubM (Event ch a)) ->   IO TestTree testUnknownType =@@ -55,7 +56,7 @@         ]  testConnectionInit ::-  (Eq ch) =>+  (Eq ch, Show ch, Hashable ch) =>   App (Event ch a) (SubM (Event ch a)) ->   IO TestTree testConnectionInit = testSimulation test [apolloInit]@@ -74,7 +75,10 @@ startSub :: ByteString -> ByteString startSub = apolloStart "subscription MySubscription { newDeity { name }}" -testSubscriptionStart :: (Eq ch) => WSApp ch a -> IO TestTree+testSubscriptionStart ::+  (Eq ch, Show ch, Hashable ch) =>+  WSApp ch a ->+  IO TestTree testSubscriptionStart =   testSimulation     test@@ -96,7 +100,10 @@             store         ] -testSubscriptionStop :: (Eq ch) => WSApp ch a -> IO TestTree+testSubscriptionStop ::+  (Eq ch, Show ch, Hashable ch) =>+  WSApp ch a ->+  IO TestTree testSubscriptionStop =   testSimulation     test@@ -120,7 +127,7 @@         ]  testApolloRequest ::-  (Eq ch) =>+  (Eq ch, Show ch, Hashable ch) =>   App (Event ch a) (SubM (Event ch a)) ->   IO TestTree testApolloRequest app =
test/Subscription/Case/Publishing.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Subscription.Case.Publishing   ( testPublishing,@@ -10,12 +11,12 @@ import Data.ByteString.Lazy.Char8   ( ByteString,   )-import Data.Morpheus.Types+import Data.Morpheus.Subscriptions   ( Event (..),-    Input,   )-import Data.Morpheus.Types.Internal.Subscription-  ( WS,+import Data.Morpheus.Subscriptions.Internal+  ( Input,+    SUB,     connect,     empty,   )@@ -41,6 +42,7 @@   ( TestTree,     testGroup,   )+import Prelude  startNewDeity :: ByteString -> ByteString startNewDeity = apolloStart "subscription MySubscription { newDeity { name , age }}"@@ -48,7 +50,7 @@ startNewHuman :: ByteString -> ByteString startNewHuman = apolloStart "subscription MySubscription { newHuman { name , age }}" -simulateSubscriptions :: IO (Input WS, SimulationState EVENT)+simulateSubscriptions :: IO (Input SUB, SimulationState EVENT) simulateSubscriptions = do   input <- connect   state <-
test/Subscription/Utils.hs view
@@ -23,33 +23,21 @@   ) where -import Control.Monad ((>>=))-import Control.Monad.State.Lazy-  ( StateT,-    runStateT,-    state,-  ) import Data.ByteString.Lazy.Char8   ( ByteString,   )-import Data.List-  ( sort,-  )-import Data.Maybe-  ( isJust,-  )+import Data.List (lookup) import Data.Morpheus   ( App,   )-import Data.Morpheus.Types+import Data.Morpheus.Subscriptions   ( Event,   )-import Data.Morpheus.Types.Internal.Subscription-  ( ClientConnectionStore,+import Data.Morpheus.Subscriptions.Internal+  ( ApiContext (..),+    ClientConnectionStore,     Input (..),-    Scope (..),-    SessionID,-    WS,+    SUB,     connect,     connectionSessionIds,     empty,@@ -58,8 +46,10 @@     streamApp,     toList,   )-import Data.Semigroup-  ( (<>),+import Relude hiding+  ( ByteString,+    empty,+    toList,   ) import Test.Tasty   ( TestTree,@@ -69,21 +59,6 @@     assertFailure,     testCase,   )-import Prelude-  ( ($),-    (.),-    (<$>),-    Eq (..),-    IO,-    Maybe (..),-    Show (..),-    length,-    lookup,-    null,-    otherwise,-    pure,-    snd,-  )  data SimulationState e = SimulationState   { inputs :: [ByteString],@@ -98,45 +73,48 @@ addOutput :: ByteString -> SimulationState e -> ((), SimulationState e) addOutput x (SimulationState i xs st) = ((), SimulationState i (xs <> [x]) st) -updateStore :: (Store e -> Store e) -> SimulationState e -> ((), SimulationState e)-updateStore up (SimulationState i o st) = ((), SimulationState i o (up st))+mockUpdateStore :: (Store e -> Store e) -> SimulationState e -> ((), SimulationState e)+mockUpdateStore up (SimulationState i o st) = ((), SimulationState i o (up st))  readInput :: SimulationState e -> (ByteString, SimulationState e) readInput (SimulationState (i : ins) o s) = (i, SimulationState ins o s) readInput (SimulationState [] o s) = ("<Error>", SimulationState [] o s)  wsApp ::-  App e (SubM e) ->-  Input WS ->-  SubM e ()+  (Eq ch, Hashable ch) =>+  App (Event ch e) (SubM (Event ch e)) ->+  Input SUB ->+  SubM (Event ch e) () wsApp app =   runStreamWS-    ScopeWS-      { update = state . updateStore,+    SubContext+      { updateStore = state . mockUpdateStore,         listener = state readInput,         callback = state . addOutput       }     . streamApp app  simulatePublish ::-  Eq ch =>+  (Eq ch, Show ch, Hashable ch) =>   Event ch con ->   SimulationState (Event ch con) ->   IO (SimulationState (Event ch con)) simulatePublish event s = snd <$> runStateT (publish event (store s)) s  simulate ::-  App e (SubM e) ->-  Input WS ->-  SimulationState e ->-  IO (SimulationState e)+  (Eq ch, Hashable ch) =>+  App (Event ch con) (SubM (Event ch con)) ->+  Input SUB ->+  SimulationState (Event ch con) ->+  IO (SimulationState (Event ch con)) simulate _ _ s@SimulationState {inputs = []} = pure s simulate api input s = runStateT (wsApp api input) s >>= simulate api input . snd  testSimulation ::-  (Input WS -> SimulationState e -> TestTree) ->+  (Eq ch, Hashable ch) =>+  (Input SUB -> SimulationState (Event ch con) -> TestTree) ->   [ByteString] ->-  App e (SubM e) ->+  App (Event ch con) (SubM (Event ch con)) ->   IO TestTree testSimulation test simInputs api = do   input <- connect@@ -163,7 +141,7 @@       "input stream should be consumed"       [] -storeIsEmpty :: Store e -> TestTree+storeIsEmpty :: (Show ch) => Store (Event ch con) -> TestTree storeIsEmpty cStore   | null (toList cStore) =     testCase "connectionStore: is empty" $ pure ()@@ -174,7 +152,7 @@         <> show           cStore -storedSingle :: Store e -> TestTree+storedSingle :: (Show ch) => Store (Event ch con) -> TestTree storedSingle cStore   | length (toList cStore) == 1 =     testCase "stored single connection" $ pure ()@@ -185,8 +163,8 @@         <> show           cStore -stored :: Input WS -> Store e -> TestTree-stored (Init uuid) cStore+stored :: (Show ch) => Input SUB -> Store (Event ch con) -> TestTree+stored (InitConnection uuid) cStore   | isJust (lookup uuid (toList cStore)) =     testCase "stored connection" $ pure ()   | otherwise =@@ -197,12 +175,13 @@           cStore  storeSubscriptions ::-  Input WS ->-  [SessionID] ->-  Store e ->+  (Show ch) =>+  Input SUB ->+  [Text] ->+  Store (Event ch con) ->   TestTree storeSubscriptions-  (Init uuid)+  (InitConnection uuid)   sids   cStore =     checkSession (lookup uuid (toList cStore))
test/Types.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NoImplicitPrelude #-}  module Types   ( Case (..),@@ -10,10 +11,11 @@ where  import Data.Aeson (FromJSON)-import Data.Text (Text, unpack)+import Data.Text (unpack) import qualified Data.Text as T (concat) import GHC.Generics import Lib (getCases)+import Relude import Test.Tasty (TestTree, testGroup)  type Name = Text