mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
This commit is contained in:
parent
669bb40e72
commit
98784212e2
@ -103,4 +103,5 @@ if (globals.consoleMode === SERVER_CONSOLE_MODE) {
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
export default globals;
|
||||
|
@ -21,12 +21,15 @@ const prefixUrl = globals.urlPrefix + appPrefix;
|
||||
const MANUAL_URL_CHANGED = '@addRemoteSchema/MANUAL_URL_CHANGED';
|
||||
const ENV_URL_CHANGED = '@addRemoteSchema/ENV_URL_CHANGED';
|
||||
const NAME_CHANGED = '@addRemoteSchema/NAME_CHANGED';
|
||||
const TIMEOUT_CONF_CHANGED = '@addRemoteSchema/TIMEOUT_CONF_CHANGED';
|
||||
// const HEADER_CHANGED = '@addRemoteSchema/HEADER_CHANGED';
|
||||
const ADDING_REMOTE_SCHEMA = '@addRemoteSchema/ADDING_REMOTE_SCHEMA';
|
||||
const ADD_REMOTE_SCHEMA_FAIL = '@addRemoteSchema/ADD_REMOTE_SCHEMA_FAIL';
|
||||
const RESET = '@addRemoteSchema/RESET';
|
||||
const FETCHING_INDIV_REMOTE_SCHEMA = '@addRemoteSchema/FETCHING_INDIV_REMOTE_SCHEMA';
|
||||
const REMOTE_SCHEMA_FETCH_SUCCESS = '@addRemoteSchema/REMOTE_SCHEMA_FETCH_SUCCESS';
|
||||
const FETCHING_INDIV_REMOTE_SCHEMA =
|
||||
'@addRemoteSchema/FETCHING_INDIV_REMOTE_SCHEMA';
|
||||
const REMOTE_SCHEMA_FETCH_SUCCESS =
|
||||
'@addRemoteSchema/REMOTE_SCHEMA_FETCH_SUCCESS';
|
||||
const REMOTE_SCHEMA_FETCH_FAIL = '@addRemoteSchema/REMOTE_SCHEMA_FETCH_FAIL';
|
||||
|
||||
const DELETING_REMOTE_SCHEMA = '@addRemoteSchema/DELETING_REMOTE_SCHEMA';
|
||||
@ -47,6 +50,7 @@ const inputEventMap = {
|
||||
name: NAME_CHANGED,
|
||||
envName: ENV_URL_CHANGED,
|
||||
manualUrl: MANUAL_URL_CHANGED,
|
||||
timeoutConf: TIMEOUT_CONF_CHANGED,
|
||||
};
|
||||
|
||||
/* Action creators */
|
||||
@ -139,12 +143,17 @@ const addRemoteSchema = () => {
|
||||
return (dispatch, getState) => {
|
||||
const currState = getState().remoteSchemas.addData;
|
||||
// const url = Endpoints.getSchema;
|
||||
|
||||
let timeoutSeconds = parseInt(currState.timeoutConf, 10);
|
||||
if (isNaN(timeoutSeconds)) timeoutSeconds = 60;
|
||||
|
||||
const resolveObj = {
|
||||
name: currState.name.trim().replace(/ +/g, ''),
|
||||
definition: {
|
||||
url: currState.manualUrl,
|
||||
url_from_env: currState.envName,
|
||||
headers: [],
|
||||
timeout_seconds: timeoutSeconds,
|
||||
forward_client_headers: currState.forwardClientHeaders,
|
||||
},
|
||||
};
|
||||
@ -316,11 +325,18 @@ const modifyRemoteSchema = () => {
|
||||
name: currState.editState.originalName,
|
||||
},
|
||||
};
|
||||
|
||||
let newTimeout = parseInt(currState.timeoutConf, 10);
|
||||
let oldTimeout = parseInt(currState.editState.originalTimeoutConf, 10);
|
||||
if (isNaN(newTimeout)) newTimeout = 60;
|
||||
if (isNaN(oldTimeout)) oldTimeout = 60;
|
||||
|
||||
const resolveObj = {
|
||||
name: remoteSchemaName,
|
||||
definition: {
|
||||
url: currState.manualUrl,
|
||||
url_from_env: currState.envName,
|
||||
timeout_seconds: newTimeout,
|
||||
forward_client_headers: currState.forwardClientHeaders,
|
||||
headers: [],
|
||||
},
|
||||
@ -351,11 +367,13 @@ const modifyRemoteSchema = () => {
|
||||
name: remoteSchemaName,
|
||||
},
|
||||
};
|
||||
|
||||
const resolveDownObj = {
|
||||
name: currState.editState.originalName,
|
||||
definition: {
|
||||
url: currState.editState.originalUrl,
|
||||
url_from_env: currState.editState.originalEnvUrl,
|
||||
timeout_seconds: oldTimeout,
|
||||
headers: [],
|
||||
forward_client_headers:
|
||||
currState.editState.originalForwardClientHeaders,
|
||||
@ -377,6 +395,7 @@ const modifyRemoteSchema = () => {
|
||||
...resolveDownObj,
|
||||
},
|
||||
};
|
||||
|
||||
downQueryArgs.push(deleteRemoteSchemaDown);
|
||||
downQueryArgs.push(createRemoteSchemaDown);
|
||||
// End of down
|
||||
@ -389,6 +408,7 @@ const modifyRemoteSchema = () => {
|
||||
type: 'bulk',
|
||||
args: downQueryArgs,
|
||||
};
|
||||
|
||||
const requestMsg = 'Modifying remote schema...';
|
||||
const successMsg = 'Remote schema modified';
|
||||
const errorMsg = 'Modify remote schema failed';
|
||||
@ -441,6 +461,11 @@ const addRemoteSchemaReducer = (state = addState, action) => {
|
||||
envName: action.data,
|
||||
manualUrl: null,
|
||||
};
|
||||
case TIMEOUT_CONF_CHANGED:
|
||||
return {
|
||||
...state,
|
||||
timeoutConf: action.data,
|
||||
};
|
||||
case ADDING_REMOTE_SCHEMA:
|
||||
return {
|
||||
...state,
|
||||
@ -480,6 +505,9 @@ const addRemoteSchemaReducer = (state = addState, action) => {
|
||||
manualUrl: action.data[0].definition.url || null,
|
||||
envName: action.data[0].definition.url_from_env || null,
|
||||
headers: action.data[0].definition.headers || [],
|
||||
timeoutConf: action.data[0].definition.timeout_seconds
|
||||
? action.data[0].definition.timeout_seconds.toString()
|
||||
: '60',
|
||||
forwardClientHeaders: action.data[0].definition.forward_client_headers,
|
||||
editState: {
|
||||
...state,
|
||||
|
@ -1,4 +1,6 @@
|
||||
import React from 'react';
|
||||
import globals from '../../../../Globals';
|
||||
import { REMOTE_SCHEMA_TIMEOUT_CONF_SUPPORT } from '../../../../helpers/versionUtils';
|
||||
import PropTypes from 'prop-types';
|
||||
import OverlayTrigger from 'react-bootstrap/lib/OverlayTrigger';
|
||||
import Tooltip from 'react-bootstrap/lib/Tooltip';
|
||||
@ -36,42 +38,92 @@ class Common extends React.Component {
|
||||
render() {
|
||||
const styles = require('../RemoteSchema.scss');
|
||||
|
||||
const { name, manualUrl, envName, forwardClientHeaders } = this.props;
|
||||
const {
|
||||
name,
|
||||
manualUrl,
|
||||
envName,
|
||||
timeoutConf,
|
||||
forwardClientHeaders,
|
||||
} = this.props;
|
||||
const { isModify, id } = this.props.editState;
|
||||
|
||||
const isDisabled = id >= 0 && !isModify;
|
||||
const urlRequired = !manualUrl && !envName;
|
||||
|
||||
const graphqlurl = (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Remote GraphQL server’s URL. E.g. https://my-domain/v1/graphql
|
||||
</Tooltip>
|
||||
);
|
||||
const tooltips = {
|
||||
graphqlurl: (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Remote GraphQL server’s URL. E.g. https://my-domain/v1/graphql
|
||||
</Tooltip>
|
||||
),
|
||||
clientHeaderForward: (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Toggle forwarding headers sent by the client app in the request to
|
||||
your remote GraphQL server
|
||||
</Tooltip>
|
||||
),
|
||||
additionalHeaders: (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Custom headers to be sent to the remote GraphQL server
|
||||
</Tooltip>
|
||||
),
|
||||
schema: (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Give this GraphQL schema a friendly name.
|
||||
</Tooltip>
|
||||
),
|
||||
timeoutConf: (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Configure timeout for your remote GraphQL server. Defaults to 60
|
||||
seconds.
|
||||
</Tooltip>
|
||||
),
|
||||
};
|
||||
|
||||
const clientHeaderForward = (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Toggle forwarding headers sent by the client app in the request to your
|
||||
remote GraphQL server
|
||||
</Tooltip>
|
||||
);
|
||||
const getTimeoutSection = () => {
|
||||
const supportTimeoutConf =
|
||||
globals.featuresCompatibility &&
|
||||
globals.featuresCompatibility[REMOTE_SCHEMA_TIMEOUT_CONF_SUPPORT];
|
||||
|
||||
const additionalHeaders = (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Custom headers to be sent to the remote GraphQL server
|
||||
</Tooltip>
|
||||
);
|
||||
if (!supportTimeoutConf) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const schema = (
|
||||
<Tooltip id="tooltip-cascade">
|
||||
Give this GraphQL schema a friendly name.
|
||||
</Tooltip>
|
||||
);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className={styles.subheading_text}>
|
||||
GraphQL server timeout
|
||||
<OverlayTrigger placement="right" overlay={tooltips.timeoutConf}>
|
||||
<i className="fa fa-question-circle" aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
<label
|
||||
className={
|
||||
styles.inputLabel + ' radio-inline ' + styles.padd_left_remove
|
||||
}
|
||||
>
|
||||
<input
|
||||
className={'form-control'}
|
||||
type="text"
|
||||
placeholder="Timeout in seconds"
|
||||
value={timeoutConf}
|
||||
data-key="timeoutConf"
|
||||
onChange={this.handleInputChange.bind(this)}
|
||||
disabled={isDisabled}
|
||||
data-test="remote-schema-timeout-conf"
|
||||
pattern="^\d+$"
|
||||
title="Only non negative integers are allowed"
|
||||
/>
|
||||
</label>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.CommonWrapper}>
|
||||
<div className={styles.subheading_text + ' ' + styles.addPaddTop}>
|
||||
Remote Schema name *
|
||||
<OverlayTrigger placement="right" overlay={schema}>
|
||||
<OverlayTrigger placement="right" overlay={tooltips.schema}>
|
||||
<i className="fa fa-question-circle" aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
@ -95,12 +147,12 @@ class Common extends React.Component {
|
||||
/>
|
||||
</label>
|
||||
<hr />
|
||||
<h4 className={styles.subheading_text}>
|
||||
<div className={styles.subheading_text}>
|
||||
GraphQL server URL *
|
||||
<OverlayTrigger placement="right" overlay={graphqlurl}>
|
||||
<OverlayTrigger placement="right" overlay={tooltips.graphqlurl}>
|
||||
<i className="fa fa-question-circle" aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</h4>
|
||||
</div>
|
||||
<div className={styles.wd_300}>
|
||||
<DropdownButton
|
||||
dropdownOptions={[
|
||||
@ -152,13 +204,19 @@ class Common extends React.Component {
|
||||
/>
|
||||
<span>Forward all headers from client</span>
|
||||
</label>
|
||||
<OverlayTrigger placement="right" overlay={clientHeaderForward}>
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
overlay={tooltips.clientHeaderForward}
|
||||
>
|
||||
<i className="fa fa-question-circle" aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
<div className={styles.subheading_text + ' ' + styles.font_normal}>
|
||||
Additional headers:
|
||||
<OverlayTrigger placement="right" overlay={additionalHeaders}>
|
||||
<OverlayTrigger
|
||||
placement="right"
|
||||
overlay={tooltips.additionalHeaders}
|
||||
>
|
||||
<i className="fa fa-question-circle" aria-hidden="true" />
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
@ -174,6 +232,8 @@ class Common extends React.Component {
|
||||
placeHolderText={this.getPlaceHolderText.bind(this)}
|
||||
keyInputPlaceholder="header name"
|
||||
/>
|
||||
<hr />
|
||||
{getTimeoutSection()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ const addState = {
|
||||
manualUrl: '',
|
||||
envName: null,
|
||||
headers: [],
|
||||
timeoutConf: '',
|
||||
name: '',
|
||||
forwardClientHeaders: false,
|
||||
...asyncState,
|
||||
@ -27,6 +28,7 @@ const addState = {
|
||||
originalHeaders: [],
|
||||
originalUrl: '',
|
||||
originalEnvUrl: '',
|
||||
originalTimeoutConf: '',
|
||||
originalForwardClientHeaders: false,
|
||||
},
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ const envObj = `apiHost: '${process.env.API_HOST}',
|
||||
enableTelemetry: ${process.env.ENABLE_TELEMETRY},
|
||||
assetsPath: '${process.env.ASSETS_PATH}',
|
||||
assetsVersion: '${process.env.ASSETS_VERSION}',
|
||||
serverVersion: '${process.env.SERVER_VERSION}',
|
||||
cdnAssets: ${process.env.CDN_ASSETS},
|
||||
`;
|
||||
|
||||
|
@ -2,12 +2,15 @@ const semver = require('semver');
|
||||
|
||||
export const FT_JWT_ANALYZER = 'JWTAnalyzer';
|
||||
export const RELOAD_METADATA_API_CHANGE = 'reloadMetaDataApiChange';
|
||||
export const REMOTE_SCHEMA_TIMEOUT_CONF_SUPPORT =
|
||||
'remoteSchemaTimeoutConfSupport';
|
||||
|
||||
// list of feature launch versions
|
||||
const featureLaunchVersions = {
|
||||
// feature: 'v1.0.0'
|
||||
[RELOAD_METADATA_API_CHANGE]: 'v1.0.0-beta.3',
|
||||
[FT_JWT_ANALYZER]: 'v1.0.0-beta.3',
|
||||
[REMOTE_SCHEMA_TIMEOUT_CONF_SUPPORT]: 'v1.0.0-beta.5',
|
||||
};
|
||||
|
||||
export const getFeaturesCompatibility = serverVersion => {
|
||||
|
@ -30,7 +30,8 @@ An example request as follows:
|
||||
"definition": {
|
||||
"url": "https://remote-server.com/graphql",
|
||||
"headers": [{"name": "X-Server-Request-From", "value": "Hasura"}],
|
||||
"forward_client_headers": false
|
||||
"forward_client_headers": false,
|
||||
"timeout_seconds": 60
|
||||
},
|
||||
"comment": "some optional comment"
|
||||
}
|
||||
|
@ -433,7 +433,8 @@ RemoteSchemaDef
|
||||
"value_from_env": env-var-string
|
||||
}
|
||||
],
|
||||
"forward_client_headers": boolean
|
||||
"forward_client_headers": boolean,
|
||||
"timeout_seconds": integer
|
||||
}
|
||||
|
||||
.. _CollectionName:
|
||||
|
@ -20,10 +20,10 @@ import Data.Int (Int64)
|
||||
import Data.IORef (IORef, readIORef)
|
||||
import Data.Time.Clock
|
||||
import Hasura.Events.HTTP
|
||||
import Hasura.HTTP
|
||||
import Hasura.Prelude
|
||||
import Hasura.RQL.DDL.Headers
|
||||
import Hasura.RQL.Types
|
||||
import Hasura.Server.Version (currentVersion)
|
||||
import Hasura.SQL.Types
|
||||
|
||||
import qualified Control.Concurrent.STM.TQueue as TQ
|
||||
@ -344,12 +344,6 @@ decodeHeader logenv headerInfos (hdrName, hdrVal)
|
||||
where
|
||||
decodeBS = TE.decodeUtf8With TE.lenientDecode
|
||||
|
||||
addDefaultHeaders :: [HTTP.Header] -> [HTTP.Header]
|
||||
addDefaultHeaders hdrs = hdrs ++
|
||||
[ (CI.mk "Content-Type", "application/json")
|
||||
, (CI.mk "User-Agent", "hasura-graphql-engine/" <> T.encodeUtf8 currentVersion)
|
||||
]
|
||||
|
||||
mkInvo
|
||||
:: EventPayload -> Int -> [HeaderConf] -> TBS.TByteString -> [HeaderConf]
|
||||
-> Invocation
|
||||
|
@ -371,19 +371,26 @@ execRemoteGQ reqId userInfo reqHdrs q rsi opDef = do
|
||||
, Map.fromList userInfoToHdrs
|
||||
, Map.fromList clientHdrs
|
||||
]
|
||||
finalHdrs = foldr Map.union Map.empty hdrMaps
|
||||
options = wreqOptions manager (Map.toList finalHdrs)
|
||||
headers = Map.toList $ foldr Map.union Map.empty hdrMaps
|
||||
finalHeaders = addDefaultHeaders headers
|
||||
initReqE <- liftIO $ try $ HTTP.parseRequest (show url)
|
||||
initReq <- either httpThrow pure initReqE
|
||||
let req = initReq
|
||||
{ HTTP.method = "POST"
|
||||
, HTTP.requestHeaders = finalHeaders
|
||||
, HTTP.requestBody = HTTP.RequestBodyLBS (J.encode q)
|
||||
, HTTP.responseTimeout = HTTP.responseTimeoutMicro (timeout * 1000000)
|
||||
}
|
||||
|
||||
-- log the graphql query
|
||||
liftIO $ logGraphqlQuery logger $ QueryLog q Nothing reqId
|
||||
res <- liftIO $ try $ Wreq.postWith options (show url) (J.toJSON q)
|
||||
res <- liftIO $ try $ HTTP.httpLbs req manager
|
||||
resp <- either httpThrow return res
|
||||
let cookieHdrs = getCookieHdr (resp ^.. Wreq.responseHeader "Set-Cookie")
|
||||
respHdrs = Just $ mkRespHeaders cookieHdrs
|
||||
return $ HttpResponse (encJFromLBS $ resp ^. Wreq.responseBody) respHdrs
|
||||
|
||||
where
|
||||
RemoteSchemaInfo url hdrConf fwdClientHdrs = rsi
|
||||
RemoteSchemaInfo url hdrConf fwdClientHdrs timeout = rsi
|
||||
httpThrow :: (MonadError QErr m) => HTTP.HttpException -> m a
|
||||
httpThrow = \case
|
||||
HTTP.HttpExceptionRequest _req content -> throw500 $ T.pack . show $ content
|
||||
|
@ -5,6 +5,7 @@ import Control.Lens ((^.))
|
||||
import Data.Aeson ((.:), (.:?))
|
||||
import Data.FileEmbed (embedStringFile)
|
||||
import Data.Foldable (foldlM)
|
||||
import Hasura.HTTP
|
||||
import Hasura.Prelude
|
||||
|
||||
import qualified Data.Aeson as J
|
||||
@ -19,7 +20,6 @@ import qualified Language.GraphQL.Draft.Syntax as G
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import qualified Network.Wreq as Wreq
|
||||
|
||||
import Hasura.HTTP (wreqOptions)
|
||||
import Hasura.RQL.DDL.Headers (getHeadersFromConf)
|
||||
import Hasura.RQL.Types
|
||||
import Hasura.Server.Utils (httpExceptToJSON)
|
||||
@ -37,12 +37,21 @@ fetchRemoteSchema
|
||||
-> RemoteSchemaName
|
||||
-> RemoteSchemaInfo
|
||||
-> m GC.RemoteGCtx
|
||||
fetchRemoteSchema manager name def@(RemoteSchemaInfo url headerConf _) = do
|
||||
fetchRemoteSchema manager name def@(RemoteSchemaInfo url headerConf _ timeout) = do
|
||||
headers <- getHeadersFromConf headerConf
|
||||
let hdrs = flip map headers $
|
||||
\(hn, hv) -> (CI.mk . T.encodeUtf8 $ hn, T.encodeUtf8 hv)
|
||||
options = wreqOptions manager hdrs
|
||||
res <- liftIO $ try $ Wreq.postWith options (show url) introspectionQuery
|
||||
hdrsWithDefaults = addDefaultHeaders hdrs
|
||||
|
||||
initReqE <- liftIO $ try $ HTTP.parseRequest (show url)
|
||||
initReq <- either throwHttpErr pure initReqE
|
||||
let req = initReq
|
||||
{ HTTP.method = "POST"
|
||||
, HTTP.requestHeaders = hdrsWithDefaults
|
||||
, HTTP.requestBody = HTTP.RequestBodyLBS introspectionQuery
|
||||
, HTTP.responseTimeout = HTTP.responseTimeoutMicro (timeout * 1000000)
|
||||
}
|
||||
res <- liftIO $ try $ HTTP.httpLbs req manager
|
||||
resp <- either throwHttpErr return res
|
||||
|
||||
let respData = resp ^. Wreq.responseBody
|
||||
|
@ -2,6 +2,7 @@ module Hasura.HTTP
|
||||
( wreqOptions
|
||||
, HttpException(..)
|
||||
, hdrsToText
|
||||
, addDefaultHeaders
|
||||
) where
|
||||
|
||||
import Control.Lens hiding ((.=))
|
||||
@ -25,9 +26,21 @@ hdrsToText hdrs =
|
||||
wreqOptions :: HTTP.Manager -> [HTTP.Header] -> Wreq.Options
|
||||
wreqOptions manager hdrs =
|
||||
Wreq.defaults
|
||||
& Wreq.headers .~ contentType : userAgent : hdrs
|
||||
& Wreq.headers .~ addDefaultHeaders hdrs
|
||||
& Wreq.checkResponse ?~ (\_ _ -> return ())
|
||||
& Wreq.manager .~ Right manager
|
||||
|
||||
-- Adds defaults headers overwriting any existing ones
|
||||
addDefaultHeaders :: [HTTP.Header] -> [HTTP.Header]
|
||||
addDefaultHeaders hdrs = defaultHeaders <> rmDefaultHeaders hdrs
|
||||
where
|
||||
rmDefaultHeaders = filter (not . isDefaultHeader)
|
||||
|
||||
isDefaultHeader :: HTTP.Header -> Bool
|
||||
isDefaultHeader (hdrName, _) = hdrName `elem` (map fst defaultHeaders)
|
||||
|
||||
defaultHeaders :: [HTTP.Header]
|
||||
defaultHeaders = [contentType, userAgent]
|
||||
where
|
||||
contentType = ("Content-Type", "application/json")
|
||||
userAgent = ( "User-Agent"
|
||||
|
@ -30,6 +30,7 @@ data RemoteSchemaInfo
|
||||
{ rsUrl :: !N.URI
|
||||
, rsHeaders :: ![HeaderConf]
|
||||
, rsFwdClientHeaders :: !Bool
|
||||
, rsTimeoutSeconds :: !Int
|
||||
} deriving (Show, Eq, Lift, Generic)
|
||||
|
||||
instance Hashable RemoteSchemaInfo
|
||||
@ -42,6 +43,7 @@ data RemoteSchemaDef
|
||||
, _rsdUrlFromEnv :: !(Maybe UrlFromEnv)
|
||||
, _rsdHeaders :: !(Maybe [HeaderConf])
|
||||
, _rsdForwardClientHeaders :: !Bool
|
||||
, _rsdTimeoutSeconds :: !(Maybe Int)
|
||||
} deriving (Show, Eq, Lift)
|
||||
|
||||
$(J.deriveJSON (J.aesonDrop 4 J.snakeCase) ''RemoteSchemaDef)
|
||||
@ -77,15 +79,18 @@ validateRemoteSchemaDef
|
||||
:: (MonadError QErr m, MonadIO m)
|
||||
=> RemoteSchemaDef
|
||||
-> m RemoteSchemaInfo
|
||||
validateRemoteSchemaDef (RemoteSchemaDef mUrl mUrlEnv hdrC fwdHdrs) =
|
||||
validateRemoteSchemaDef (RemoteSchemaDef mUrl mUrlEnv hdrC fwdHdrs mTimeout) =
|
||||
case (mUrl, mUrlEnv) of
|
||||
(Just url, Nothing) ->
|
||||
return $ RemoteSchemaInfo url hdrs fwdHdrs
|
||||
return $ RemoteSchemaInfo url hdrs fwdHdrs timeout
|
||||
(Nothing, Just urlEnv) -> do
|
||||
url <- getUrlFromEnv urlEnv
|
||||
return $ RemoteSchemaInfo url hdrs fwdHdrs
|
||||
return $ RemoteSchemaInfo url hdrs fwdHdrs timeout
|
||||
(Nothing, Nothing) ->
|
||||
throw400 InvalidParams "both `url` and `url_from_env` can't be empty"
|
||||
(Just _, Just _) ->
|
||||
throw400 InvalidParams "both `url` and `url_from_env` can't be present"
|
||||
where hdrs = fromMaybe [] hdrC
|
||||
where
|
||||
hdrs = fromMaybe [] hdrC
|
||||
|
||||
timeout = fromMaybe 60 mTimeout
|
||||
|
@ -10,6 +10,8 @@ from webserver import RequestHandler, WebServer, MkHandlers, Response
|
||||
|
||||
from enum import Enum
|
||||
|
||||
import time
|
||||
|
||||
def mkJSONResp(graphql_result):
|
||||
return Response(HTTPStatus.OK, graphql_result.to_dict(),
|
||||
{'Content-Type': 'application/json'})
|
||||
@ -25,9 +27,15 @@ class HelloWorldHandler(RequestHandler):
|
||||
class Hello(graphene.ObjectType):
|
||||
hello = graphene.String(arg=graphene.String(default_value="world"))
|
||||
|
||||
delayedHello = graphene.String(arg=graphene.String(default_value="world"))
|
||||
|
||||
def resolve_hello(self, info, arg):
|
||||
return "Hello " + arg
|
||||
|
||||
def resolve_delayedHello(self, info, arg):
|
||||
time.sleep(10)
|
||||
return "Hello " + arg
|
||||
|
||||
hello_schema = graphene.Schema(query=Hello, subscription=Hello)
|
||||
|
||||
class HelloGraphQL(RequestHandler):
|
||||
|
@ -0,0 +1,12 @@
|
||||
description: Simple GraphQL query (takes 10s to respond)
|
||||
url: /v1/graphql
|
||||
status: 200
|
||||
response:
|
||||
errors:
|
||||
- message: ResponseTimeout
|
||||
extensions: {'path': '$', 'code': 'unexpected'}
|
||||
query:
|
||||
query: |
|
||||
query {
|
||||
delayedHello(arg: "me")
|
||||
}
|
@ -6,13 +6,14 @@ import yaml
|
||||
import json
|
||||
import queue
|
||||
import requests
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from validate import check_query_f, check_query
|
||||
|
||||
|
||||
def mk_add_remote_q(name, url, headers=None, client_hdrs=False):
|
||||
def mk_add_remote_q(name, url, headers=None, client_hdrs=False, timeout=None):
|
||||
return {
|
||||
"type": "add_remote_schema",
|
||||
"args": {
|
||||
@ -21,7 +22,8 @@ def mk_add_remote_q(name, url, headers=None, client_hdrs=False):
|
||||
"definition": {
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"forward_client_headers": client_hdrs
|
||||
"forward_client_headers": client_hdrs,
|
||||
"timeout_seconds": timeout
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -434,6 +436,22 @@ class TestAddRemoteSchemaCompareRootQueryFields:
|
||||
compare_flds(fldH, fldR)
|
||||
assert has_fld[fldR['name']], 'Field ' + fldR['name'] + ' in the remote shema root query type not found in Hasura schema'
|
||||
|
||||
class TestRemoteSchemaTimeout:
|
||||
dir = 'queries/remote_schemas'
|
||||
teardown = {"type": "clear_metadata", "args": {}}
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def transact(self, hge_ctx):
|
||||
q = mk_add_remote_q('simple 1', 'http://localhost:5000/hello-graphql', timeout = 5)
|
||||
st_code, resp = hge_ctx.v1q(q)
|
||||
assert st_code == 200, resp
|
||||
yield
|
||||
hge_ctx.v1q(self.teardown)
|
||||
|
||||
def test_remote_query_timeout(self, hge_ctx):
|
||||
check_query_f(hge_ctx, self.dir + '/basic_timeout_query.yaml')
|
||||
# wait for graphql server to finish else teardown throws
|
||||
time.sleep(6)
|
||||
|
||||
# def test_remote_query_variables(self, hge_ctx):
|
||||
# pass
|
||||
|
Loading…
Reference in New Issue
Block a user