Allow "extensions" field in remote schema response

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2818
GitOrigin-RevId: 505e8bae6d3e11199c229bd2b86af09161eb8b66
This commit is contained in:
David Overton 2021-11-10 13:33:58 +11:00 committed by hasura-bot
parent 984955d194
commit ec60386f9c
4 changed files with 58 additions and 9 deletions

View File

@ -3,6 +3,7 @@
## Next release ## Next release
(Add highlights/major features below) (Add highlights/major features below)
- server: log locking DB queries during source catalog migration - server: log locking DB queries during source catalog migration
- server: fix to allow remote schema response to contain an "extensions" field (#7143)
- console: add comments to tracked functions - console: add comments to tracked functions
### Bug fixes and improvements ### Bug fixes and improvements

View File

@ -545,6 +545,21 @@ coalescePostgresMutations plan = do
_ -> Nothing _ -> Nothing
Just (oneSourceConfig, mutations) Just (oneSourceConfig, mutations)
data GraphQLResponse
= GraphQLResponseErrors [J.Value]
| GraphQLResponseData JO.Value
decodeGraphQLResponse :: LBS.ByteString -> Either Text GraphQLResponse
decodeGraphQLResponse bs = do
val <- mapLeft T.pack $ JO.eitherDecode bs
valObj <- JO.asObject val
case JO.lookup "errors" valObj of
Just (JO.Array errs) -> Right $ GraphQLResponseErrors (toList $ JO.fromOrdered <$> errs)
Just _ -> Left "Invalid \"errors\" field in response from remote"
Nothing -> do
dataVal <- JO.lookup "data" valObj `onNothing` Left "Missing \"data\" field in response from remote"
Right $ GraphQLResponseData dataVal
extractFieldFromResponse :: extractFieldFromResponse ::
forall m. forall m.
Monad m => Monad m =>
@ -557,14 +572,13 @@ extractFieldFromResponse fieldName rsi resultCustomizer resp = do
let namespace = fmap G.unName $ _rscNamespaceFieldName $ rsCustomizer rsi let namespace = fmap G.unName $ _rscNamespaceFieldName $ rsCustomizer rsi
fieldName' = G.unName $ _rfaAlias fieldName fieldName' = G.unName $ _rfaAlias fieldName
-- TODO: use RootFieldAlias for remote fields -- TODO: use RootFieldAlias for remote fields
val <- onLeft (JO.eitherDecode resp) $ do400 . T.pack
valObj <- onLeft (JO.asObject val) do400
dataVal <- dataVal <-
applyResultCustomizer resultCustomizer <$> case JO.toList valObj of applyResultCustomizer resultCustomizer
[("data", v)] -> pure v <$> do
_ -> case JO.lookup "errors" valObj of graphQLResponse <- decodeGraphQLResponse resp `onLeft` do400
Just (JO.Array err) -> doGQExecError $ toList $ fmap JO.fromOrdered err case graphQLResponse of
_ -> do400 "Received invalid JSON value from remote" GraphQLResponseErrors errs -> doGQExecError errs
GraphQLResponseData d -> pure d
case namespace of case namespace of
Just _ -> Just _ ->
-- If using a custom namespace field then the response from the remote server -- If using a custom namespace field then the response from the remote server

View File

@ -12,8 +12,8 @@ from graphql import GraphQLError
HGE_URLS=[] HGE_URLS=[]
def mkJSONResp(graphql_result): def mkJSONResp(graphql_result, extensions={}):
return Response(HTTPStatus.OK, graphql_result.to_dict(), return Response(HTTPStatus.OK, {**graphql_result.to_dict(), **extensions},
{'Content-Type': 'application/json'}) {'Content-Type': 'application/json'})
@ -48,6 +48,17 @@ class HelloGraphQL(RequestHandler):
res = hello_schema.execute(request.json['query']) res = hello_schema.execute(request.json['query'])
return mkJSONResp(res) return mkJSONResp(res)
class HelloGraphQLExtensions(RequestHandler):
def get(self, request):
return Response(HTTPStatus.METHOD_NOT_ALLOWED)
def post(self, request):
if not request.json:
return Response(HTTPStatus.BAD_REQUEST)
res = hello_schema.execute(request.json['query'])
extensions = {'extensions': {'message': 'an extra field in response object'}}
return mkJSONResp(res, extensions)
class User(graphene.ObjectType): class User(graphene.ObjectType):
id = graphene.Int() id = graphene.Int()
username = graphene.String() username = graphene.String()
@ -785,6 +796,7 @@ class MessagesGraphQL(RequestHandler):
handlers = MkHandlers({ handlers = MkHandlers({
'/hello': HelloWorldHandler, '/hello': HelloWorldHandler,
'/hello-graphql': HelloGraphQL, '/hello-graphql': HelloGraphQL,
'/hello-graphql-extensions': HelloGraphQLExtensions,
'/user-graphql': UserGraphQL, '/user-graphql': UserGraphQL,
'/country-graphql': CountryGraphQL, '/country-graphql': CountryGraphQL,
'/character-iface-graphql' : CharacterInterfaceGraphQL, '/character-iface-graphql' : CharacterInterfaceGraphQL,

View File

@ -297,6 +297,28 @@ class TestRemoteSchemaBasic:
st_code, resp = hge_ctx.v1q_f(self.dir + '/basic_bulk_remove_add.yaml') st_code, resp = hge_ctx.v1q_f(self.dir + '/basic_bulk_remove_add.yaml')
assert st_code == 200, resp assert st_code == 200, resp
class TestRemoteSchemaBasicExtensions:
""" basic => no hasura tables are tracked """
teardown = {"type": "clear_metadata", "args": {}}
dir = 'queries/remote_schemas'
@pytest.fixture(autouse=True)
def transact(self, request, hge_ctx):
config = request.config
# This is needed for supporting server upgrade tests
# Some marked tests in this class will be run as server upgrade tests
if not config.getoption('--skip-schema-setup'):
q = mk_add_remote_q('simple 1', 'http://localhost:5000/hello-graphql-extensions')
st_code, resp = hge_ctx.v1q(q)
assert st_code == 200, resp
yield
if request.session.testsfailed > 0 or not config.getoption('--skip-schema-teardown'):
hge_ctx.v1q(self.teardown)
def test_remote_query(self, hge_ctx):
check_query_f(hge_ctx, self.dir + '/basic_query.yaml')
class TestAddRemoteSchemaTbls: class TestAddRemoteSchemaTbls:
""" tests with adding a table in hasura """ """ tests with adding a table in hasura """