fix response for remote schema queries over ws (fix #2246) (#2248)

This commit is contained in:
Anon Ray 2019-05-29 11:51:09 +00:00 committed by Vamshi Surabhi
parent 981960cb3b
commit 95a27fba89
5 changed files with 77 additions and 15 deletions

View File

@ -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

View File

@ -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

View File

@ -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) ->

View File

@ -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

View File

@ -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)