mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
parent
981960cb3b
commit
95a27fba89
@ -11,6 +11,9 @@ module Hasura.GraphQL.Transport.HTTP.Protocol
|
||||
, encodeGQResp
|
||||
, GQResp(..)
|
||||
, isExecError
|
||||
, RemoteGqlResp(..)
|
||||
, GraphqlResponse(..)
|
||||
, encodeGraphqlResponse
|
||||
) where
|
||||
|
||||
import Hasura.EncJSON
|
||||
@ -96,3 +99,29 @@ encodeGQResp gqResp =
|
||||
GQSuccess r -> [("data", encJFromLBS r)]
|
||||
GQPreExecError e -> [("errors", encJFromJValue e)]
|
||||
GQExecError e -> [("data", "null"), ("errors", encJFromJValue e)]
|
||||
|
||||
-- | Represents GraphQL response from a remote server
|
||||
data RemoteGqlResp
|
||||
= RemoteGqlResp
|
||||
{ _rgqrData :: !(Maybe J.Value)
|
||||
, _rgqrErrors :: !(Maybe [J.Value])
|
||||
, _rgqrExtensions :: !(Maybe J.Value)
|
||||
} deriving (Show, Eq)
|
||||
$(J.deriveFromJSON (J.aesonDrop 5 J.camelCase) ''RemoteGqlResp)
|
||||
|
||||
encodeRemoteGqlResp :: RemoteGqlResp -> EncJSON
|
||||
encodeRemoteGqlResp (RemoteGqlResp d e ex) =
|
||||
encJFromAssocList [ ("data", encJFromJValue d)
|
||||
, ("errors", encJFromJValue e)
|
||||
, ("extensions", encJFromJValue ex)
|
||||
]
|
||||
|
||||
-- | Represents a proper GraphQL response
|
||||
data GraphqlResponse
|
||||
= GRHasura !GQResp
|
||||
| GRRemote !RemoteGqlResp
|
||||
|
||||
encodeGraphqlResponse :: GraphqlResponse -> EncJSON
|
||||
encodeGraphqlResponse = \case
|
||||
GRHasura resp -> encodeGQResp resp
|
||||
GRRemote resp -> encodeRemoteGqlResp resp
|
||||
|
@ -14,6 +14,7 @@ import qualified Data.Aeson.TH as J
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.CaseInsensitive as CI
|
||||
import qualified Data.HashMap.Strict as Map
|
||||
import qualified Data.IORef as IORef
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Encoding as TE
|
||||
import qualified Data.Time.Clock as TC
|
||||
@ -26,7 +27,6 @@ import qualified StmContainers.Map as STMMap
|
||||
|
||||
import Control.Concurrent (threadDelay)
|
||||
import Data.ByteString (ByteString)
|
||||
import qualified Data.IORef as IORef
|
||||
|
||||
import Hasura.EncJSON
|
||||
import qualified Hasura.GraphQL.Execute as E
|
||||
@ -285,9 +285,17 @@ onStart serverEnv wsConn (StartMsg opId q) msgRaw = catchAndIgnore $ do
|
||||
let payload = J.encode $ _wpPayload sockPayload
|
||||
resp <- runExceptT $ E.execRemoteGQ httpMgr userInfo reqHdrs
|
||||
payload rsi opDef
|
||||
either postExecErr sendSuccResp resp
|
||||
either postExecErr sendRemoteResp resp
|
||||
sendCompleted
|
||||
|
||||
sendRemoteResp resp =
|
||||
case J.eitherDecodeStrict (encJToBS resp) of
|
||||
Left e -> postExecErr $ invalidGqlErr $ T.pack e
|
||||
Right res -> sendMsg wsConn $ SMData $ DataMsg opId (GRRemote res)
|
||||
|
||||
invalidGqlErr err = err500 Unexpected $
|
||||
"Failed parsing GraphQL response from remote: " <> err
|
||||
|
||||
WSServerEnv logger pgExecCtx lqMap gCtxMapRef httpMgr _
|
||||
sqlGenCtx planCache _ enableAL = serverEnv
|
||||
|
||||
@ -315,7 +323,7 @@ onStart serverEnv wsConn (StartMsg opId q) msgRaw = catchAndIgnore $ do
|
||||
let errFn = getErrFn errRespTy
|
||||
logOpEv $ ODQueryErr qErr
|
||||
sendMsg wsConn $ SMData $ DataMsg opId $
|
||||
GQExecError $ pure $ errFn False qErr
|
||||
GRHasura $ GQExecError $ pure $ errFn False qErr
|
||||
|
||||
-- why wouldn't pre exec error use graphql response?
|
||||
preExecErr qErr = do
|
||||
@ -327,7 +335,8 @@ onStart serverEnv wsConn (StartMsg opId q) msgRaw = catchAndIgnore $ do
|
||||
sendMsg wsConn $ SMErr $ ErrorMsg opId err
|
||||
|
||||
sendSuccResp encJson =
|
||||
sendMsg wsConn $ SMData $ DataMsg opId $ GQSuccess $ encJToLBS encJson
|
||||
sendMsg wsConn $ SMData $ DataMsg opId $
|
||||
GRHasura $ GQSuccess $ encJToLBS encJson
|
||||
|
||||
withComplete :: ExceptT () IO () -> ExceptT () IO a
|
||||
withComplete action = do
|
||||
@ -337,7 +346,8 @@ onStart serverEnv wsConn (StartMsg opId q) msgRaw = catchAndIgnore $ do
|
||||
|
||||
-- on change, send message on the websocket
|
||||
liveQOnChange resp =
|
||||
WS.sendMsg wsConn $ encodeServerMsg $ SMData $ DataMsg opId resp
|
||||
WS.sendMsg wsConn $ encodeServerMsg $ SMData $
|
||||
DataMsg opId (GRHasura resp)
|
||||
|
||||
catchAndIgnore :: ExceptT () IO () -> IO ()
|
||||
catchAndIgnore m = void $ runExceptT m
|
||||
|
@ -67,7 +67,7 @@ instance J.FromJSON ClientMsg where
|
||||
data DataMsg
|
||||
= DataMsg
|
||||
{ _dmId :: !OperationId
|
||||
, _dmPayload :: !GQResp
|
||||
, _dmPayload :: !GraphqlResponse
|
||||
}
|
||||
|
||||
data ErrorMsg
|
||||
@ -131,7 +131,7 @@ encodeServerMsg msg =
|
||||
SMData (DataMsg opId payload) ->
|
||||
[ encTy SMT_GQL_DATA
|
||||
, ("id", encJFromJValue opId)
|
||||
, ("payload", encodeGQResp payload)
|
||||
, ("payload", encodeGraphqlResponse payload)
|
||||
]
|
||||
|
||||
SMErr (ErrorMsg opId payload) ->
|
||||
|
@ -6,6 +6,7 @@ module Hasura.Server.Version
|
||||
where
|
||||
|
||||
import Control.Lens ((^.), (^?))
|
||||
import Data.Either (isLeft)
|
||||
|
||||
import qualified Data.SemVer as V
|
||||
import qualified Data.Text as T
|
||||
@ -24,9 +25,7 @@ currentVersion :: T.Text
|
||||
currentVersion = version
|
||||
|
||||
isDevVersion :: Bool
|
||||
isDevVersion = case parsedVersion of
|
||||
Left _ -> False
|
||||
Right _ -> True
|
||||
isDevVersion = isLeft parsedVersion
|
||||
|
||||
rawVersion :: T.Text
|
||||
rawVersion = "versioned/" <> version
|
||||
|
@ -301,11 +301,34 @@ class TestRemoteSchemaQueriesOverWebsocket:
|
||||
}
|
||||
"""
|
||||
query_id = ws_client.gen_id()
|
||||
resp = ws_client.send_query({'query': query},query_id = query_id,timeout=5)
|
||||
resp = ws_client.send_query({'query': query}, query_id=query_id,
|
||||
timeout=5)
|
||||
try:
|
||||
ev = next(resp)
|
||||
assert ev['type'] == 'data' and ev['id'] == query_id, ev
|
||||
assert ev['payload']['data']['data']['user']['username'] == 'john'
|
||||
assert ev['payload']['data']['user']['username'] == 'john'
|
||||
finally:
|
||||
ws_client.stop(query_id)
|
||||
|
||||
def test_remote_query_error(self, ws_client):
|
||||
query = """
|
||||
query {
|
||||
user(id: 2) {
|
||||
blah
|
||||
username
|
||||
}
|
||||
}
|
||||
"""
|
||||
query_id = ws_client.gen_id()
|
||||
resp = ws_client.send_query({'query': query}, query_id=query_id,
|
||||
timeout=5)
|
||||
try:
|
||||
ev = next(resp)
|
||||
print(ev)
|
||||
assert ev['type'] == 'data' and ev['id'] == query_id, ev
|
||||
assert 'errors' in ev['payload']
|
||||
assert ev['payload']['errors'][0]['message'] == \
|
||||
'Cannot query field "blah" on type "User".'
|
||||
finally:
|
||||
ws_client.stop(query_id)
|
||||
|
||||
@ -321,12 +344,13 @@ class TestRemoteSchemaQueriesOverWebsocket:
|
||||
}
|
||||
"""
|
||||
query_id = ws_client.gen_id()
|
||||
resp = ws_client.send_query({'query': query},query_id = query_id,timeout=5)
|
||||
resp = ws_client.send_query({'query': query}, query_id=query_id,
|
||||
timeout=5)
|
||||
try:
|
||||
ev = next(resp)
|
||||
assert ev['type'] == 'data' and ev['id'] == query_id, ev
|
||||
assert ev['payload']['data']['data']['createUser']['user']['id'] == 42
|
||||
assert ev['payload']['data']['data']['createUser']['user']['username'] == 'foobar'
|
||||
assert ev['payload']['data']['createUser']['user']['id'] == 42
|
||||
assert ev['payload']['data']['createUser']['user']['username'] == 'foobar'
|
||||
finally:
|
||||
ws_client.stop(query_id)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user