Skip to content

Commit 5fa7d83

Browse files
rohit-ravikotihuv1k
authored andcommitted
Fixes: Schema Polling (#950)
* Fixes: * Enabling polling by default only for localhost with a new setting: `schema.polling.endpointFilter: "*localhost*"` which is a glob. Move `schema.pollingEnabled` to `schema.polling.enable` * If there’s an active introspection query, wait for it’s result before sending another one * Allow polling interval to be set in the settings: `schema.polling.interval` * tracing.hideTracingResponse default value to true * fixed selector merge conflict * fixed: Query execution must have precedence over polling - the Play button must not be affected by that. * Fix: Pause polling when the tab is not focused * renamed startPolling -> updatePolling
1 parent cf922c8 commit 5fa7d83

File tree

12 files changed

+116
-36
lines changed

12 files changed

+116
-36
lines changed

packages/graphql-playground-react/src/components/Playground.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
getHeaders,
3838
getIsReloadingSchema,
3939
getEndpoint,
40+
getIsPollingSchema,
4041
} from '../state/sessions/selectors'
4142
import { getHistoryOpen } from '../state/general/selectors'
4243
import {
@@ -105,8 +106,13 @@ export interface ReduxProps {
105106
injectHeaders: (headers: string, endpoint: string) => void
106107
setConfigString: (str: string) => void
107108
schemaFetchingError: (endpoint: string, error: string) => void
108-
schemaFetchingSuccess: (endpoint: string, tracingSupported: boolean) => void
109+
schemaFetchingSuccess: (
110+
endpoint: string,
111+
tracingSupported: boolean,
112+
isPollingSchema: boolean,
113+
) => void
109114
isReloadingSchema: boolean
115+
isPollingSchema: boolean
110116
isConfigTab: boolean
111117
isSettingsTab: boolean
112118
isFile: boolean
@@ -143,11 +149,7 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
143149
if (props.schema) {
144150
return
145151
}
146-
if (
147-
this.mounted &&
148-
this.state.schema &&
149-
!props.settings['schema.enablePolling']
150-
) {
152+
if (this.mounted && this.state.schema && !props.isPollingSchema) {
151153
this.setState({ schema: undefined })
152154
}
153155
let first = true
@@ -268,7 +270,11 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
268270
})
269271
if (schema) {
270272
this.updateSchema(currentSchema, schema.schema, props)
271-
this.props.schemaFetchingSuccess(data.endpoint, schema.tracingSupported)
273+
this.props.schemaFetchingSuccess(
274+
data.endpoint,
275+
schema.tracingSupported,
276+
props.isPollingSchema,
277+
)
272278
this.backoff.stop()
273279
}
274280
} catch (e) {
@@ -363,10 +369,7 @@ export class Playground extends React.PureComponent<Props & ReduxProps, State> {
363369
) {
364370
const currentSchemaStr = currentSchema ? printSchema(currentSchema) : null
365371
const newSchemaStr = printSchema(newSchema)
366-
if (
367-
newSchemaStr !== currentSchemaStr ||
368-
!props.settings['schema.enablePolling']
369-
) {
372+
if (newSchemaStr !== currentSchemaStr || !props.isPollingSchema) {
370373
this.setState({ schema: newSchema })
371374
}
372375
}
@@ -386,6 +389,7 @@ const mapStateToProps = createStructuredSelector({
386389
settings: getSettings,
387390
settingsString: getSettingsString,
388391
isReloadingSchema: getIsReloadingSchema,
392+
isPollingSchema: getIsPollingSchema,
389393
sessionEndpoint: getEndpoint,
390394
})
391395

packages/graphql-playground-react/src/components/Playground/SchemaExplorer/SDLEditor.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ISettings } from '../../../types'
77

88
export interface Props {
99
schema?: GraphQLSchema | null
10+
isPollingSchema: boolean
1011
getRef?: (ref: SDLEditor) => void
1112
width?: number
1213
sessionId?: string
@@ -81,7 +82,7 @@ class SDLEditor extends React.PureComponent<Props, { overflowY: boolean }> {
8182
this.props.settings['schema.disableComments'],
8283
),
8384
)
84-
if (this.props.settings['schema.enablePolling']) {
85+
if (this.props.isPollingSchema) {
8586
this.editor.scrollTo(initialScroll.left, initialScroll.top)
8687
}
8788
CodeMirror.signal(this.editor, 'change', this.editor)

packages/graphql-playground-react/src/components/Playground/SchemaExplorer/SDLView.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ import {
99
import Spinner from '../../Spinner'
1010
import { columnWidth } from '../../../constants'
1111
import { SideTabContentProps } from '../ExplorerTabs/SideTabs'
12-
import { getSelectedSessionIdFromRoot } from '../../../state/sessions/selectors'
12+
import {
13+
getSelectedSessionIdFromRoot,
14+
getIsPollingSchema,
15+
} from '../../../state/sessions/selectors'
1316
import { getSessionDocs } from '../../../state/docs/selectors'
1417
import { createStructuredSelector } from 'reselect'
1518
import { ErrorContainer } from '../DocExplorer/ErrorContainer'
1619
import { SchemaExplorerContainer, SDLColumn } from './SDLTypes/SDLStyles'
1720
import SDLHeader from './SDLHeader'
1821
import SDLEditor from './SDLEditor'
1922
import { getSettings } from '../../../state/workspace/reducers'
23+
import { ISettings } from '../../../types'
2024

2125
interface StateFromProps {
2226
docs: {
@@ -25,7 +29,8 @@ interface StateFromProps {
2529
docsWidth: number
2630
keyMove: boolean
2731
}
28-
settings
32+
isPollingSchema: boolean
33+
settings: ISettings
2934
}
3035

3136
interface DispatchFromProps {
@@ -64,7 +69,7 @@ class SDLView extends React.Component<
6469
}
6570

6671
render() {
67-
const { schema, settings } = this.props
72+
const { schema, settings, isPollingSchema } = this.props
6873
let emptySchema
6974
if (schema === undefined) {
7075
// Schema is undefined when it is being loaded via introspection.
@@ -88,6 +93,7 @@ class SDLView extends React.Component<
8893
<SDLEditor
8994
schema={schema}
9095
settings={settings}
96+
isPollingSchema={isPollingSchema}
9197
width={this.props.docs.docsWidth || columnWidth}
9298
/>
9399
</SDLColumn>
@@ -114,6 +120,7 @@ const mapStateToProps = createStructuredSelector({
114120
settings: getSettings,
115121
docs: getSessionDocs,
116122
sessionId: getSelectedSessionIdFromRoot,
123+
isPollingSchema: getIsPollingSchema,
117124
})
118125

119126
export default connect<StateFromProps, DispatchFromProps, SideTabContentProps>(

packages/graphql-playground-react/src/components/Playground/TopBar/Polling.tsx

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,64 @@ import * as React from 'react'
22
import PollingIcon from './PollingIcon'
33

44
export interface Props {
5-
isPollingSchema: boolean
5+
interval: number
6+
isReloadingSchema: boolean
67
onReloadSchema: () => void
78
}
89

9-
class SchemaPolling extends React.Component<Props> {
10+
interface State {
11+
windowVisible: boolean
12+
}
13+
14+
class SchemaPolling extends React.Component<Props, State> {
1015
timer: any
16+
constructor(props) {
17+
super(props)
1118

19+
this.state = {
20+
windowVisible: true,
21+
}
22+
}
1223
componentDidMount() {
13-
this.startPolling()
24+
this.updatePolling()
25+
document.addEventListener('visibilitychange', this.setWindowVisibility)
1426
}
1527
componentWillUnmount() {
1628
this.clearTimer()
29+
document.removeEventListener('visibilitychange', this.setWindowVisibility)
30+
}
31+
setWindowVisibility = () => {
32+
if (document.visibilityState === 'visible') {
33+
this.setState(
34+
{
35+
windowVisible: true,
36+
},
37+
this.updatePolling,
38+
)
39+
}
40+
if (document.visibilityState === 'hidden') {
41+
this.setState(
42+
{
43+
windowVisible: false,
44+
},
45+
this.updatePolling,
46+
)
47+
}
1748
}
1849
componentWillReceiveProps(nextProps: Props) {
19-
if (nextProps.isPollingSchema !== this.props.isPollingSchema) {
20-
this.startPolling(nextProps)
50+
if (nextProps.isReloadingSchema !== this.props.isReloadingSchema) {
51+
this.updatePolling(nextProps)
2152
}
2253
}
2354

2455
render() {
25-
return <PollingIcon animate={true} />
56+
return <PollingIcon animate={this.state.windowVisible} />
2657
}
27-
private startPolling(props: Props = this.props) {
58+
private updatePolling = (props: Props = this.props) => {
2859
this.clearTimer()
29-
if (props.isPollingSchema) {
30-
this.timer = setInterval(() => props.onReloadSchema(), 2000)
60+
if (!props.isReloadingSchema && this.state.windowVisible) {
61+
// timer starts only when introspection not in flight
62+
this.timer = setInterval(() => props.onReloadSchema(), props.interval)
3163
}
3264
}
3365

packages/graphql-playground-react/src/components/Playground/TopBar/SchemaReload.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import * as React from 'react'
22
import ReloadIcon from './Reload'
3-
import PollingIcon from './Polling'
3+
import Polling from './Polling'
4+
import { ISettings } from '../../../types'
45

56
export interface Props {
67
isPollingSchema: boolean
78
isReloadingSchema: boolean
89
onReloadSchema: () => any
10+
settings: ISettings
911
}
1012

1113
export default (props: Props) => {
1214
if (props.isPollingSchema) {
1315
return (
14-
<PollingIcon
15-
isPollingSchema={props.isPollingSchema}
16+
<Polling
17+
interval={props.settings['schema.polling.interval']}
18+
isReloadingSchema={props.isReloadingSchema}
1619
onReloadSchema={props.onReloadSchema}
1720
/>
1821
)

packages/graphql-playground-react/src/components/Playground/TopBar/TopBar.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getSelectedSession,
1111
getIsReloadingSchema,
1212
getEndpointUnreachable,
13+
getIsPollingSchema,
1314
} from '../../../state/sessions/selectors'
1415
import { connect } from 'react-redux'
1516
import { getFixedEndpoint } from '../../../state/general/selectors'
@@ -22,12 +23,14 @@ import {
2223
import { share } from '../../../state/sharing/actions'
2324
import { openHistory } from '../../../state/general/actions'
2425
import { getSettings } from '../../../state/workspace/reducers'
26+
import { ISettings } from '../../../types'
2527

2628
export interface Props {
2729
endpoint: string
2830
shareEnabled?: boolean
2931
fixedEndpoint?: boolean
3032
isReloadingSchema: boolean
33+
isPollingSchema: boolean
3134
endpointUnreachable: boolean
3235

3336
editEndpoint: (value: string) => void
@@ -36,7 +39,7 @@ export interface Props {
3639
share: () => void
3740
refetchSchema: () => void
3841

39-
settings
42+
settings: ISettings
4043
}
4144

4245
class TopBar extends React.Component<Props, {}> {
@@ -78,7 +81,8 @@ class TopBar extends React.Component<Props, {}> {
7881
}}
7982
>
8083
<SchemaReload
81-
isPollingSchema={settings['schema.enablePolling']}
84+
settings={settings}
85+
isPollingSchema={this.props.isPollingSchema}
8286
isReloadingSchema={this.props.isReloadingSchema}
8387
onReloadSchema={this.props.refetchSchema}
8488
/>
@@ -154,6 +158,7 @@ const mapStateToProps = createStructuredSelector({
154158
endpoint: getEndpoint,
155159
fixedEndpoint: getFixedEndpoint,
156160
isReloadingSchema: getIsReloadingSchema,
161+
isPollingSchema: getIsPollingSchema,
157162
endpointUnreachable: getEndpointUnreachable,
158163
settings: getSettings,
159164
})

packages/graphql-playground-react/src/state/sessions/actions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,10 @@ export const {
131131
REFETCH_SCHEMA: simpleAction(),
132132
SET_ENDPOINT_UNREACHABLE: simpleAction('endpoint'),
133133
SET_SCROLL_TOP: (sessionId, scrollTop) => ({ sessionId, scrollTop }),
134-
SCHEMA_FETCHING_SUCCESS: (endpoint, tracingSupported) => ({
134+
SCHEMA_FETCHING_SUCCESS: (endpoint, tracingSupported, isPollingSchema) => ({
135135
endpoint,
136136
tracingSupported,
137+
isPollingSchema,
137138
}),
138139
/*
139140
this.setState({

packages/graphql-playground-react/src/state/sessions/reducers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ const reducer = handleActions(
317317
if (
318318
response &&
319319
session.responses!.size === 1 &&
320-
(response.date.includes('error') ||
320+
((response.date.includes('error') && !payload.isPollingSchema) ||
321321
response.date.includes('Failed to fetch'))
322322
) {
323323
data.responses = List([])

packages/graphql-playground-react/src/state/sessions/sagas.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
put,
88
} from 'redux-saga/effects'
99
import { delay } from 'redux-saga'
10-
import { getSelectedSession } from './selectors'
10+
import { getSelectedSession, getIsPollingSchema } from './selectors'
1111
import getSelectedOperationName from '../../components/Playground/util/getSelectedOperationName'
1212
import { getQueryFacts } from '../../components/Playground/util/getQueryFacts'
1313
import { fromJS, is } from 'immutable'
@@ -139,7 +139,13 @@ function* fetchSchemaSaga() {
139139
const session: Session = yield getSessionWithCredentials()
140140
yield schemaFetcher.fetch(session)
141141
try {
142-
yield put(schemaFetchingSuccess(session.endpoint))
142+
yield put(
143+
schemaFetchingSuccess(
144+
session.endpoint,
145+
null,
146+
yield select(getIsPollingSchema),
147+
),
148+
)
143149
} catch (e) {
144150
yield put(schemaFetchingError(session.endpoint))
145151
yield call(delay, 5000)
@@ -151,7 +157,13 @@ function* refetchSchemaSaga() {
151157
const session: Session = yield getSessionWithCredentials()
152158
yield schemaFetcher.refetch(session)
153159
try {
154-
yield put(schemaFetchingSuccess(session.endpoint))
160+
yield put(
161+
schemaFetchingSuccess(
162+
session.endpoint,
163+
null,
164+
yield select(getIsPollingSchema),
165+
),
166+
)
155167
} catch (e) {
156168
yield put(schemaFetchingError(session.endpoint))
157169
yield call(delay, 5000)

packages/graphql-playground-react/src/state/sessions/selectors.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,17 @@ export const getCurrentQueryStartTime = makeSessionSelector(
6767
)
6868
export const getCurrentQueryEndTime = makeSessionSelector('currentQueryEndTime')
6969
export const getIsReloadingSchema = makeSessionSelector('isReloadingSchema')
70+
export const getIsPollingSchema = createSelector(
71+
[getEndpoint, getSettings],
72+
(endpoint, settings) => {
73+
const json = JSON.parse(settings)
74+
return (
75+
json['schema.polling.enable'] &&
76+
endpoint.match(`/${json['schema.polling.endpointFilter']}`) &&
77+
true
78+
)
79+
},
80+
)
7081

7182
export const getResponseExtensions = makeSessionSelector('responseExtensions')
7283
export const getQueryVariablesActive = makeSessionSelector(

0 commit comments

Comments
 (0)