diff --git a/README.md b/README.md index 72a731424..a500143c5 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,7 @@ The React component `` and all middlewares expose the following op - `props` (Middlewares & React Component) - `endpoint` [`string`](optional) - the GraphQL endpoint url. - `subscriptionEndpoint` [`string`](optional) - the GraphQL subscriptions endpoint url. + - `subscriptionEndpointConnectionTimeout` [`number`](optional) - the GraphQL subscriptions endpoint connection timeout. - `workspaceName` [`string`](optional) - in case you provide a GraphQL Config, you can name your workspace here - `config` [`string`](optional) - the JSON of a GraphQL Config. See an example [here](https://github.com/prismagraphql/graphql-playground/blob/master/packages/graphql-playground-react/src/localDevIndex.tsx#L47) - `settings` [`ISettings`](optional) - Editor settings in json format as [described here](https://github.com/prismagraphql/graphql-playground#settings) diff --git a/packages/graphql-playground-html/src/render-playground-page.ts b/packages/graphql-playground-html/src/render-playground-page.ts index 5d37e98f4..b44c8e0ce 100644 --- a/packages/graphql-playground-html/src/render-playground-page.ts +++ b/packages/graphql-playground-html/src/render-playground-page.ts @@ -5,6 +5,7 @@ import getLoadingMarkup from './get-loading-markup' export interface MiddlewareOptions { endpoint?: string subscriptionEndpoint?: string + subscriptionEndpointConnectionTimeout?: number workspaceName?: string env?: any config?: any diff --git a/packages/graphql-playground-react/src/components/Playground.tsx b/packages/graphql-playground-react/src/components/Playground.tsx index c3fae7b40..8c3bfcf58 100644 --- a/packages/graphql-playground-react/src/components/Playground.tsx +++ b/packages/graphql-playground-react/src/components/Playground.tsx @@ -44,6 +44,7 @@ import { setLinkCreator, schemaFetcher, setSubscriptionEndpoint, + setSubscriptionEndpointConnectionTimeout, } from '../state/sessions/fetchingSagas' import { Session } from '../state/sessions/reducers' import { getWorkspaceId } from './Playground/util/getWorkspaceId' @@ -62,6 +63,7 @@ export interface Props { endpoint: string sessionEndpoint: string subscriptionEndpoint?: string + subscriptionEndpointConnectionTimeout?: number projectId?: string shareEnabled?: boolean fixedEndpoint?: boolean @@ -194,6 +196,11 @@ export class Playground extends React.PureComponent { setLinkCreator(props.createApolloLink) this.getSchema() setSubscriptionEndpoint(props.subscriptionEndpoint) + if (props.subscriptionEndpointConnectionTimeout) { + setSubscriptionEndpointConnectionTimeout( + props.subscriptionEndpointConnectionTimeout, + ) + } } UNSAFE_componentWillMount() { @@ -262,15 +269,15 @@ export class Playground extends React.PureComponent { props.sessionHeaders && props.sessionHeaders.length > 0 ? props.sessionHeaders : props.headers && Object.keys(props.headers).length > 0 - ? JSON.stringify(props.headers) - : undefined, + ? JSON.stringify(props.headers) + : undefined, credentials: props.settings['request.credentials'], useTracingHeader: !this.initialSchemaFetch && props.settings['tracing.tracingSupported'], } const schema = await schemaFetcher.fetch(data) - schemaFetcher.subscribe(data, newSchema => { + schemaFetcher.subscribe(data, (newSchema) => { if ( data.endpoint === this.props.endpoint || data.endpoint === this.props.sessionEndpoint @@ -413,24 +420,21 @@ const mapStateToProps = createStructuredSelector({ sessionEndpoint: getEndpoint, }) -export default connect( - mapStateToProps, - { - selectTabIndex, - selectNextTab, - selectPrevTab, - newSession, - closeSelectedTab, - initState, - saveSettings, - saveConfig, - setTracingSupported, - injectHeaders, - setConfigString, - schemaFetchingError, - schemaFetchingSuccess, - }, -)(Playground) +export default connect(mapStateToProps, { + selectTabIndex, + selectNextTab, + selectPrevTab, + newSession, + closeSelectedTab, + initState, + saveSettings, + saveConfig, + setTracingSupported, + injectHeaders, + setConfigString, + schemaFetchingError, + schemaFetchingSuccess, +})(Playground) const PlaygroundContainer = styled.div` flex: 1; diff --git a/packages/graphql-playground-react/src/components/PlaygroundWrapper.tsx b/packages/graphql-playground-react/src/components/PlaygroundWrapper.tsx index 834549286..4d9c1c7dd 100644 --- a/packages/graphql-playground-react/src/components/PlaygroundWrapper.tsx +++ b/packages/graphql-playground-react/src/components/PlaygroundWrapper.tsx @@ -42,6 +42,7 @@ export interface PlaygroundWrapperProps { endpoint?: string endpointUrl?: string subscriptionEndpoint?: string + subscriptionEndpointConnectionTimeout?: number setTitle?: boolean settings?: ISettings shareEnabled?: boolean @@ -199,7 +200,9 @@ class PlaygroundWrapper extends React.Component< return endpoint.replace(/^http/, 'ws') } - UNSAFE_componentWillReceiveProps(nextProps: PlaygroundWrapperProps & ReduxProps) { + UNSAFE_componentWillReceiveProps( + nextProps: PlaygroundWrapperProps & ReduxProps, + ) { // Reactive props (props that cause a state change upon being changed) if ( nextProps.endpoint !== this.props.endpoint || @@ -361,25 +364,27 @@ class PlaygroundWrapper extends React.Component< }} > - {this.props.config && - this.state.activeEnv && ( - - )} + {this.props.config && this.state.activeEnv && ( + + )} { + getPlaygroundRef = (ref) => { this.playground = ref if (typeof this.props.getRef === 'function') { this.props.getRef(ref) @@ -450,11 +455,11 @@ class PlaygroundWrapper extends React.Component< }) } - private handleChangeEndpoint = endpoint => { + private handleChangeEndpoint = (endpoint) => { this.setState({ endpoint }) } - private handleChangeSubscriptionsEndpoint = subscriptionEndpoint => { + private handleChangeSubscriptionsEndpoint = (subscriptionEndpoint) => { this.setState({ subscriptionEndpoint }) } @@ -475,7 +480,7 @@ class PlaygroundWrapper extends React.Component< private async updateSubscriptionsUrl() { const candidates = this.getSubscriptionsUrlCandidated(this.state.endpoint) - const validCandidate = await find(candidates, candidate => + const validCandidate = await find(candidates, (candidate) => this.wsEndpointValid(candidate), ) if (validCandidate) { @@ -502,18 +507,18 @@ class PlaygroundWrapper extends React.Component< } private wsEndpointValid(url): Promise { - return new Promise(resolve => { + return new Promise((resolve) => { const socket = new WebSocket(url, 'graphql-ws') - socket.addEventListener('open', event => { + socket.addEventListener('open', (event) => { socket.send(JSON.stringify({ type: 'connection_init' })) }) - socket.addEventListener('message', event => { + socket.addEventListener('message', (event) => { const data = JSON.parse(event.data) if (data.type === 'connection_ack') { resolve(true) } }) - socket.addEventListener('error', event => { + socket.addEventListener('error', (event) => { resolve(false) }) setTimeout(() => { @@ -533,10 +538,7 @@ const mapStateToProps = (state, ownProps) => { return { theme, settings } } -export default connect( - mapStateToProps, - { injectTabs }, -)(PlaygroundWrapper) +export default connect(mapStateToProps, { injectTabs })(PlaygroundWrapper) async function find( iterable: any[], diff --git a/packages/graphql-playground-react/src/state/sessions/fetchingSagas.ts b/packages/graphql-playground-react/src/state/sessions/fetchingSagas.ts index b45d1fcbb..f160f443d 100644 --- a/packages/graphql-playground-react/src/state/sessions/fetchingSagas.ts +++ b/packages/graphql-playground-react/src/state/sessions/fetchingSagas.ts @@ -41,11 +41,16 @@ import { set } from 'immutable' // tslint:disable let subscriptionEndpoint +let subscriptionTimeout = 20000 export function setSubscriptionEndpoint(endpoint) { subscriptionEndpoint = endpoint } +export function setSubscriptionEndpointConnectionTimeout(timeout) { + subscriptionTimeout = timeout +} + export interface LinkCreatorProps { endpoint: string headers?: Headers @@ -78,7 +83,8 @@ export const defaultLinkCreator = ( } const subscriptionClient = new SubscriptionClient(subscriptionEndpoint, { - timeout: 20000, + minTimeout: subscriptionTimeout, + timeout: subscriptionTimeout, lazy: true, connectionParams, }) @@ -86,7 +92,7 @@ export const defaultLinkCreator = ( const webSocketLink = new WebSocketLink(subscriptionClient) return { link: ApolloLink.split( - operation => isSubscription(operation), + (operation) => isSubscription(operation), webSocketLink as any, httpLink, ), @@ -137,7 +143,7 @@ function* runQuerySaga(action) { yield put(setCurrentQueryStartTime(new Date())) let firstResponse = false - const channel = eventChannel(emitter => { + const channel = eventChannel((emitter) => { let closed = false if (subscriptionClient && operationIsSubscription) { subscriptionClient.onDisconnected(() => { @@ -151,10 +157,10 @@ function* runQuerySaga(action) { }) } const subscription = execute(link, operation).subscribe({ - next: function(value) { + next: function (value) { emitter({ value }) }, - error: error => { + error: (error) => { emitter({ error }) emitter(END) },