morpheusgraphql / morpheus-graphql

Haskell GraphQL Api, Client and Tools
https://morpheusgraphql.com
MIT License
412 stars 62 forks source link

GQL Subscription event publishing seems locked to mutations? #874

Closed JulianLeviston closed 4 months ago

JulianLeviston commented 4 months ago

I have a Map in a TVar in application state within a Snap application I'm working on that is set up to track updates of data. I'm also using Morpheus in this application to interface with a React frontend; I'd like to have the code that triggers updates to this TVar also publish an event to trigger an update to a GQL Subscription with Morpheus when the relevant portion of the data in the Map in the TVar changes.

The publish function appears to be locked to only work in MUTATION operations. Is that correct? Does it assume all changes to tracked data will only occur within its controlled GQL interface? Is there a way to trigger an event outside of using the publish function?

I'm using the Pipes library, so ideally I'd like to be able to use a Producer from it to trigger changes, if possible. I could do this pretty easily if publish worked from within a Subscription because I could just pass the input from a Mailbox from Pipes.Concurrent into the Morpheus code from the handler where I have the GQL API set up, and create a pipe that calls publish from the Subscription setup code (ie before the event callback).

Hopefully this is intelligible, and my question makes sense.

JulianLeviston commented 4 months ago

Ok, so after I created this issue, I noticed in the examples in the source code that Data.Morpheus.Subscriptions has webSocketsApp and this is basically what I'm after. Sorry for the noise, I'll experiment with using that :)

JulianLeviston commented 4 months ago

This might help anyone else that gets similarly stuck trying to resolve building subscriptions for Snap. To get this to work I had to switch my base monad from a Snap monad backed transformer stack MonadSnap m => ErrorT e m to normal IO, and embrace the built in failure type rather than encode it into the return type (ie I had a little while of using (Either CustomError a) as my return types, but using the built in error type proved wiser because building a subscription using webSocketsApp wasn't easy enough for me to figure out otherwise — I kept ending up with a failing MonadIOUnlift constraint). Used the Network.WebSockets.Snap library to integrate into the Snap framework.

Thanks so much for making this library!