-
-
Save srobertson/4b38b927202cd44975c4dae15fce4b63 to your computer and use it in GitHub Desktop.
string :: Name -> ResModel o e m | |
string = ResScalar . Data.Morpheus.Types.String | |
prompt :: String -> IO Text | |
prompt msg = | |
putStr (msg ++ ": ") | |
>> hFlush stdout -- Send output to terminal now | |
>> pack <$> getLine -- Get string from User and convert to Text | |
getSchema :: Monad m => ResponseStream e m Data.Morpheus.Types.Internal.AST.Schema | |
getSchema = | |
schemaFromTypeDefinitions | |
[dsl| | |
type Query { | |
deity(name: String): Deity! | |
} | |
type Deity { | |
name: String! | |
power: [String!]! | |
} | |
|] | |
resolver :: MonadIO m => RootResModel e m | |
resolver = | |
RootResModel | |
{ query = | |
pure $ | |
ResObject | |
( ObjectResModel | |
{ __typename = "Query", | |
objectFields = | |
[ ( "deity", | |
pure | |
$ ResObject | |
$ ObjectResModel | |
{ __typename = "Deity", | |
objectFields = | |
[ ( "name", | |
liftIO $ prompt "Name" >>= pure . string | |
), | |
( "power", | |
pure $ | |
ResList | |
[string "Shapeshifting"] | |
) | |
] | |
} | |
) | |
] | |
} | |
), | |
mutation = pure ResNull, | |
subscription = pure ResNull | |
} | |
main :: IO () | |
main = Web.scotty 3000 $ Web.post "/api" $ Web.raw =<< (liftIO . api =<< Web.body) | |
apiD :: MonadIO m => GQLRequest -> m GQLResponse | |
apiD req = | |
renderResponse <$> runResultT app | |
where | |
app = do | |
schema <- getSchema | |
runApi schema resolver req | |
api :: L.ByteString -> IO L.ByteString | |
api = mapAPI apiD | |
- I was expecting a function like interpreter to get from
my derived resolver to a ByteString -> IO ByteString. Something
equivalent to the examples from the main ReadME. Ideally it would be nice if interpreter TypeClass could be refactored to
work with runApi
feel free to open PR
- It felt weird to have to have to declare
resolver :: MonadIO m => RootResModel e m but that's the only way
I could figure out how to use IO in the resolver. Was this the right
thing to do?
yes.
MonadIO m => RootResModel e m
if you want pass IO into monad and still support custom Monad is right way to go.
- It was not immediately clear how to create a Schema by hand.
So I just followed the implementation you had using the quasiquoter
in Spec.hs. Which had the undesired effect of being in a Monad when
I think for the real use case I should be able to programmatically
build up a Schema directly. But I didn't spend much time here
anyway you are generating schema from swager.
but i think you should use schema validator from Morpheus.
- After succeeding in getting Scotty to server these queries and do some IO
I tried to figure out how I would go about using arguments to return a different
result when diety(name="Bob") is called. My assumption is that with this
approach I'd have to interpret the GQLRequest directly and return
a different (RootResModel e m). Am I on the right track or do you imagine
a different way of doing this?
your assumption is right. you provide different ResModel for diffenet arguments.
- The final piece of the puzzle is figure out a way to mix derived
resolvers perhaps with the help of deriveModel with this approach.
Is this doable? Or would I be better of deriving everything using
the method I'm currently working with?
don't understand what do you mean?
@nalchevanidze I think I answered my own question when I went to elaborate. Looks like what I was looking for is objectResolvers ::
and possibly other Helpers ... that being said. Here's a more detailed account of what I'm trying to do:
Ideally It would be nice to blend things derived by Generics/Morpheus with the swagger gateway I'm creating
I'm building a gateway that blends data from microservices (that expose either Swagger or Graphql endpoints) with some custom logic
Imagine the gateway expose the combined schema below:
type Query {
deity(name: String!): Deity!
demigod(name: String!): Demigod
}
"""
Description for Deity
"""
type Deity {
"""
Description for name
"""
name: String!
power: String @deprecated(reason: "some reason for")
}
type Demigod {
name: String!
patron: Diety
}
deity() -> Deity comes the above RootResModel done in the example I posted in this gist. Where as demigod() -> Demigod may come from code that looks like the traditional way of working with Morpheus: Something like:
data Demigod = Demigod
{ name :: Text -- Non-Nullable Field
, patron :: Text -- Nullable Field
} deriving (Generic,GQLType)
data DemiGodArgs = DemiGodArgs
{ name :: Text -- Required Argument
} deriving (Generic)
So the question (or task for me to determine) is can I have the best of both worlds.
Do you mean like: https://www.apollographql.com/docs/apollo-server/federation/introduction/
Where you can derive resolvers and schema from swagger and merge with api of motpheus?
Yeah. I am considering that too.
morpheusDerivedApi <:> swaggerApi.
What I did was take the example from Spec.hs and hooked it up to Scotty. Then to simulate
IO prompted someone at the console to type in a name at the console whenever
name
was requested I.e.Obviously not a scalable solution for the API but fun none the less.
Observations/Findings/Questions:
interpreter
to get frommy derived resolver to a ByteString -> IO ByteString. Something
equivalent to the examples from the main ReadME
The closest I was able to get was following the lead in
statelessResolver
Ideally it would be nice if
interpreter
TypeClass could be refactored towork with
runApi
It felt weird to have to have to declare
resolver :: MonadIO m => RootResModel e m
but that's the only wayI could figure out how to use IO in the resolver. Was this the right
thing to do?
It was not immediately clear how to create a Schema by hand.
So I just followed the implementation you had using the quasiquoter
in Spec.hs. Which had the undesired effect of being in a Monad when
I think for the real use case I should be able to programmatically
build up a Schema directly. But I didn't spend much time here.
After succeeding in getting Scotty to server these queries and do some IO
I tried to figure out how I would go about using arguments to return a different
result when
diety(name="Bob")
is called. My assumption is that with thisapproach I'd have to interpret the GQLRequest directly and return
a different (RootResModel e m). Am I on the right track or do you imagine
a different way of doing this?
The final piece of the puzzle is figure out a way to mix derived
resolvers perhaps with the help of
deriveModel
with this approach.Is this doable? Or would I be better of deriving everything using
the method I'm currently working with?