这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions server/src-lib/Hasura/GraphQL/Transport/HTTP/Protocol.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ module Hasura.GraphQL.Transport.HTTP.Protocol
, encodeGQResp
, GQResp(..)
, isExecError
, RemoteGqlResp(..)
, GraphqlResponse(..)
, encodeGraphqlResponse
) where

import Hasura.EncJSON
Expand Down Expand Up @@ -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
20 changes: 15 additions & 5 deletions server/src-lib/Hasura/GraphQL/Transport/WebSocket.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ instance J.FromJSON ClientMsg where
data DataMsg
= DataMsg
{ _dmId :: !OperationId
, _dmPayload :: !GQResp
, _dmPayload :: !GraphqlResponse
}

data ErrorMsg
Expand Down Expand Up @@ -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) ->
Expand Down
5 changes: 2 additions & 3 deletions server/src-lib/Hasura/Server/Version.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
34 changes: 29 additions & 5 deletions server/tests-py/test_schema_stitching.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,34 @@ def test_remote_query(self, ws_client):
}
"""
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)

Expand All @@ -321,12 +344,13 @@ def test_remote_mutation(self, ws_client):
}
"""
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)

Expand Down