diff --git a/libs/apollo-client/src/lib/apollo-client.ts b/libs/apollo-client/src/lib/apollo-client.ts index 77d0d023c..0aac83e8e 100644 --- a/libs/apollo-client/src/lib/apollo-client.ts +++ b/libs/apollo-client/src/lib/apollo-client.ts @@ -115,7 +115,19 @@ export function createClient({ ) : httpLink; - const errorLink = onError(({ graphQLErrors, networkError }) => { + const errorLink = onError(({ graphQLErrors, networkError, operation }) => { + // if any of these queries error don't capture any errors, its + // likely the user is connecting to a dodgy node, NodeGuard gets + // called on startup, NodeCheck and NodeCheckTimeUpdate are called + // by the NodeSwitcher component and the useNodeHealth hook + if ( + ['NodeGuard', 'NodeCheck', 'NodeCheckTimeUpdate'].includes( + operation.operationName + ) + ) { + return; + } + if (graphQLErrors) { graphQLErrors.forEach((e) => { if (e.extensions && e.extensions['type'] !== NOT_FOUND) { diff --git a/libs/environment/src/components/node-switcher/row-data.spec.tsx b/libs/environment/src/components/node-switcher/row-data.spec.tsx index 29f805e30..301178a66 100644 --- a/libs/environment/src/components/node-switcher/row-data.spec.tsx +++ b/libs/environment/src/components/node-switcher/row-data.spec.tsx @@ -3,11 +3,13 @@ import { MockedProvider } from '@apollo/react-testing'; import { act, render, screen, waitFor } from '@testing-library/react'; import { RadioGroup } from '@vegaprotocol/ui-toolkit'; import type { - BlockTimeSubscription, - StatisticsQuery, -} from '../../utils/__generated__/Node'; -import { BlockTimeDocument } from '../../utils/__generated__/Node'; -import { StatisticsDocument } from '../../utils/__generated__/Node'; + NodeCheckTimeUpdateSubscription, + NodeCheckQuery, +} from '../../utils/__generated__/NodeCheck'; +import { + NodeCheckDocument, + NodeCheckTimeUpdateDocument, +} from '../../utils/__generated__/NodeCheck'; import type { RowDataProps } from './row-data'; import { POLL_INTERVAL } from './row-data'; import { BLOCK_THRESHOLD, RowData } from './row-data'; @@ -19,9 +21,9 @@ jest.mock('@vegaprotocol/apollo-client', () => ({ useHeaderStore: jest.fn().mockReturnValue({}), })); -const statsQueryMock: MockedResponse = { +const statsQueryMock: MockedResponse = { request: { - query: StatisticsDocument, + query: NodeCheckDocument, }, result: { data: { @@ -34,9 +36,9 @@ const statsQueryMock: MockedResponse = { }, }; -const subMock: MockedResponse = { +const subMock: MockedResponse = { request: { - query: BlockTimeDocument, + query: NodeCheckTimeUpdateDocument, }, result: { data: { @@ -71,8 +73,8 @@ const mockHeaders = ( const renderComponent = ( props: RowDataProps, - queryMock: MockedResponse, - subMock: MockedResponse + queryMock: MockedResponse, + subMock: MockedResponse ) => { return ( @@ -127,16 +129,16 @@ describe('RowData', () => { it('radio button still enabled if query fails', async () => { mockHeaders(props.url, {}); - const failedQueryMock: MockedResponse = { + const failedQueryMock: MockedResponse = { request: { - query: StatisticsDocument, + query: NodeCheckDocument, }, error: new Error('failed'), }; - const failedSubMock: MockedResponse = { + const failedSubMock: MockedResponse = { request: { - query: BlockTimeDocument, + query: NodeCheckTimeUpdateDocument, }, error: new Error('failed'), }; @@ -244,10 +246,10 @@ describe('RowData', () => { jest.useFakeTimers(); const createStatsQueryMock = ( blockHeight: string - ): MockedResponse => { + ): MockedResponse => { return { request: { - query: StatisticsDocument, + query: NodeCheckDocument, }, result: { data: { @@ -261,10 +263,10 @@ describe('RowData', () => { }; }; - const createFailedStatsQueryMock = (): MockedResponse => { + const createFailedStatsQueryMock = (): MockedResponse => { return { request: { - query: StatisticsDocument, + query: NodeCheckDocument, }, result: { data: undefined, diff --git a/libs/environment/src/components/node-switcher/row-data.tsx b/libs/environment/src/components/node-switcher/row-data.tsx index 9a5f01222..77db447a6 100644 --- a/libs/environment/src/components/node-switcher/row-data.tsx +++ b/libs/environment/src/components/node-switcher/row-data.tsx @@ -6,9 +6,9 @@ import { Radio } from '@vegaprotocol/ui-toolkit'; import { useEffect, useState } from 'react'; import { CUSTOM_NODE_KEY } from '../../types'; import { - useBlockTimeSubscription, - useStatisticsQuery, -} from '../../utils/__generated__/Node'; + useNodeCheckQuery, + useNodeCheckTimeUpdateSubscription, +} from '../../utils/__generated__/NodeCheck'; import { LayoutCell } from './layout-cell'; export const POLL_INTERVAL = 1000; @@ -30,13 +30,14 @@ export const RowData = ({ const [subFailed, setSubFailed] = useState(false); const [time, setTime] = useState(); // no use of data here as we need the data nodes reference to block height - const { data, error, loading, startPolling, stopPolling } = - useStatisticsQuery({ + const { data, error, loading, startPolling, stopPolling } = useNodeCheckQuery( + { pollInterval: POLL_INTERVAL, // fix for pollInterval // https://github.com/apollographql/apollo-client/issues/9819 ssr: false, - }); + } + ); const headerStore = useHeaderStore(); const headers = headerStore[url]; @@ -44,7 +45,7 @@ export const RowData = ({ data: subData, error: subError, loading: subLoading, - } = useBlockTimeSubscription(); + } = useNodeCheckTimeUpdateSubscription(); useEffect(() => { const timeout = setTimeout(() => { diff --git a/libs/environment/src/hooks/mocks/apollo-client.tsx b/libs/environment/src/hooks/mocks/apollo-client.tsx index 20a5ea951..e4a85809a 100644 --- a/libs/environment/src/hooks/mocks/apollo-client.tsx +++ b/libs/environment/src/hooks/mocks/apollo-client.tsx @@ -1,11 +1,11 @@ import { - StatisticsDocument, - BlockTimeDocument, -} from '../../utils/__generated__/Node'; + NodeCheckDocument, + NodeCheckTimeUpdateDocument, +} from '../../utils/__generated__/NodeCheck'; import type { - StatisticsQuery, - BlockTimeSubscription, -} from '../../utils/__generated__/Node'; + NodeCheckQuery, + NodeCheckTimeUpdateSubscription, +} from '../../utils/__generated__/NodeCheck'; import { Networks } from '../../types'; import type { RequestHandlerResponse } from 'mock-apollo-client'; import { createMockClient } from 'mock-apollo-client'; @@ -21,7 +21,7 @@ type MockClientProps = { busEvents?: MockRequestConfig; }; -export const getMockBusEventsResult = (): BlockTimeSubscription => ({ +export const getMockBusEventsResult = (): NodeCheckTimeUpdateSubscription => ({ busEvents: [ { __typename: 'BusEvent', @@ -32,19 +32,21 @@ export const getMockBusEventsResult = (): BlockTimeSubscription => ({ export const getMockStatisticsResult = ( env: Networks = Networks.TESTNET -): StatisticsQuery => ({ +): NodeCheckQuery => ({ statistics: { __typename: 'Statistics', chainId: `${env.toLowerCase()}-0123`, blockHeight: '11', + vegaTime: new Date().toISOString(), }, }); -export const getMockQueryResult = (env: Networks): StatisticsQuery => ({ +export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({ statistics: { __typename: 'Statistics', chainId: `${env.toLowerCase()}-0123`, blockHeight: '11', + vegaTime: new Date().toISOString(), }, }); @@ -72,11 +74,11 @@ export default function ({ const mockClient = createMockClient(); mockClient.setRequestHandler( - StatisticsDocument, + NodeCheckDocument, getHandler(statistics, getMockStatisticsResult(network)) ); mockClient.setRequestHandler( - BlockTimeDocument, + NodeCheckTimeUpdateDocument, getHandler(busEvents, getMockBusEventsResult()) ); diff --git a/libs/environment/src/hooks/use-environment.ts b/libs/environment/src/hooks/use-environment.ts index 69cce83a8..5d0faa8aa 100644 --- a/libs/environment/src/hooks/use-environment.ts +++ b/libs/environment/src/hooks/use-environment.ts @@ -5,11 +5,13 @@ import { useEffect } from 'react'; import { create } from 'zustand'; import { createClient } from '@vegaprotocol/apollo-client'; import type { - BlockTimeSubscription, - StatisticsQuery, -} from '../utils/__generated__/Node'; -import { BlockTimeDocument } from '../utils/__generated__/Node'; -import { StatisticsDocument } from '../utils/__generated__/Node'; + NodeCheckTimeUpdateSubscription, + NodeCheckQuery, +} from '../utils/__generated__/NodeCheck'; +import { + NodeCheckDocument, + NodeCheckTimeUpdateDocument, +} from '../utils/__generated__/NodeCheck'; import type { Environment } from '../types'; import { Networks } from '../types'; import { compileErrors } from '../utils/compile-errors'; @@ -220,8 +222,8 @@ const testNode = async ( */ const testQuery = async (client: Client) => { try { - const result = await client.query({ - query: StatisticsDocument, + const result = await client.query({ + query: NodeCheckDocument, }); if (!result || result.error) { return false; @@ -240,8 +242,8 @@ const testQuery = async (client: Client) => { const testSubscription = (client: Client) => { return new Promise((resolve) => { const sub = client - .subscribe({ - query: BlockTimeDocument, + .subscribe({ + query: NodeCheckTimeUpdateDocument, errorPolicy: 'all', }) .subscribe({ diff --git a/libs/environment/src/hooks/use-node-health.spec.tsx b/libs/environment/src/hooks/use-node-health.spec.tsx index 9a280e1fe..971854111 100644 --- a/libs/environment/src/hooks/use-node-health.spec.tsx +++ b/libs/environment/src/hooks/use-node-health.spec.tsx @@ -2,8 +2,8 @@ import { renderHook, waitFor } from '@testing-library/react'; import { useNodeHealth } from './use-node-health'; import type { MockedResponse } from '@apollo/react-testing'; import { MockedProvider } from '@apollo/react-testing'; -import type { StatisticsQuery } from '../utils/__generated__/Node'; -import { StatisticsDocument } from '../utils/__generated__/Node'; +import type { NodeCheckQuery } from '../utils/__generated__/NodeCheck'; +import { NodeCheckDocument } from '../utils/__generated__/NodeCheck'; import { useHeaderStore } from '@vegaprotocol/apollo-client'; import { Intent } from '@vegaprotocol/ui-toolkit'; @@ -16,10 +16,10 @@ jest.mock('@vegaprotocol/apollo-client'); const createStatsMock = ( blockHeight: number -): MockedResponse => { +): MockedResponse => { return { request: { - query: StatisticsDocument, + query: NodeCheckDocument, }, result: { data: { @@ -34,7 +34,7 @@ const createStatsMock = ( }; function setup( - mock: MockedResponse, + mock: MockedResponse, headers: | { blockHeight: number; @@ -93,9 +93,9 @@ describe('useNodeHealth', () => { ); it('block diff is null if query fails indicating non operational', async () => { - const failedQuery: MockedResponse = { + const failedQuery: MockedResponse = { request: { - query: StatisticsDocument, + query: NodeCheckDocument, }, result: { // @ts-ignore failed query with no result diff --git a/libs/environment/src/hooks/use-node-health.ts b/libs/environment/src/hooks/use-node-health.ts index b10360943..79d937afc 100644 --- a/libs/environment/src/hooks/use-node-health.ts +++ b/libs/environment/src/hooks/use-node-health.ts @@ -1,5 +1,5 @@ import { useEffect, useMemo } from 'react'; -import { useStatisticsQuery } from '../utils/__generated__/Node'; +import { useNodeCheckQuery } from '../utils/__generated__/NodeCheck'; import { useHeaderStore } from '@vegaprotocol/apollo-client'; import { useEnvironment } from './use-environment'; import { useNavigatorOnline } from '@vegaprotocol/react-helpers'; @@ -16,7 +16,7 @@ export const useNodeHealth = () => { const url = useEnvironment((store) => store.VEGA_URL); const headerStore = useHeaderStore(); const headers = url ? headerStore[url] : undefined; - const { data, error, startPolling, stopPolling } = useStatisticsQuery({ + const { data, error, startPolling, stopPolling } = useNodeCheckQuery({ fetchPolicy: 'no-cache', }); diff --git a/libs/environment/src/index.ts b/libs/environment/src/index.ts index 735d74739..fa6986ab0 100644 --- a/libs/environment/src/index.ts +++ b/libs/environment/src/index.ts @@ -8,4 +8,4 @@ export * from './hooks'; export * from './types'; // Utils -export * from './utils/__generated__/Node'; +export * from './utils/__generated__/NodeCheck'; diff --git a/libs/environment/src/utils/Node.graphql b/libs/environment/src/utils/NodeCheck.graphql similarity index 69% rename from libs/environment/src/utils/Node.graphql rename to libs/environment/src/utils/NodeCheck.graphql index 64ab73970..f5a2b4ad3 100644 --- a/libs/environment/src/utils/Node.graphql +++ b/libs/environment/src/utils/NodeCheck.graphql @@ -1,4 +1,4 @@ -query Statistics { +query NodeCheck { statistics { chainId blockHeight @@ -6,7 +6,7 @@ query Statistics { } } -subscription BlockTime { +subscription NodeCheckTimeUpdate { busEvents(types: TimeUpdate, batchSize: 1) { id } diff --git a/libs/environment/src/utils/__generated__/Node.ts b/libs/environment/src/utils/__generated__/Node.ts deleted file mode 100644 index 2f256176c..000000000 --- a/libs/environment/src/utils/__generated__/Node.ts +++ /dev/null @@ -1,81 +0,0 @@ -import * as Types from '@vegaprotocol/types'; - -import { gql } from '@apollo/client'; -import * as Apollo from '@apollo/client'; -const defaultOptions = {} as const; -export type StatisticsQueryVariables = Types.Exact<{ [key: string]: never; }>; - - -export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } }; - -export type BlockTimeSubscriptionVariables = Types.Exact<{ [key: string]: never; }>; - - -export type BlockTimeSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null }; - - -export const StatisticsDocument = gql` - query Statistics { - statistics { - chainId - blockHeight - vegaTime - } -} - `; - -/** - * __useStatisticsQuery__ - * - * To run a query within a React component, call `useStatisticsQuery` and pass it any options that fit your needs. - * When your component renders, `useStatisticsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useStatisticsQuery({ - * variables: { - * }, - * }); - */ -export function useStatisticsQuery(baseOptions?: Apollo.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useQuery(StatisticsDocument, options); - } -export function useStatisticsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useLazyQuery(StatisticsDocument, options); - } -export type StatisticsQueryHookResult = ReturnType; -export type StatisticsLazyQueryHookResult = ReturnType; -export type StatisticsQueryResult = Apollo.QueryResult; -export const BlockTimeDocument = gql` - subscription BlockTime { - busEvents(types: TimeUpdate, batchSize: 1) { - id - } -} - `; - -/** - * __useBlockTimeSubscription__ - * - * To run a query within a React component, call `useBlockTimeSubscription` and pass it any options that fit your needs. - * When your component renders, `useBlockTimeSubscription` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useBlockTimeSubscription({ - * variables: { - * }, - * }); - */ -export function useBlockTimeSubscription(baseOptions?: Apollo.SubscriptionHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return Apollo.useSubscription(BlockTimeDocument, options); - } -export type BlockTimeSubscriptionHookResult = ReturnType; -export type BlockTimeSubscriptionResult = Apollo.SubscriptionResult; \ No newline at end of file diff --git a/libs/environment/src/utils/__generated__/NodeCheck.ts b/libs/environment/src/utils/__generated__/NodeCheck.ts new file mode 100644 index 000000000..21910e5be --- /dev/null +++ b/libs/environment/src/utils/__generated__/NodeCheck.ts @@ -0,0 +1,81 @@ +import * as Types from '@vegaprotocol/types'; + +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +const defaultOptions = {} as const; +export type NodeCheckQueryVariables = Types.Exact<{ [key: string]: never; }>; + + +export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } }; + +export type NodeCheckTimeUpdateSubscriptionVariables = Types.Exact<{ [key: string]: never; }>; + + +export type NodeCheckTimeUpdateSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null }; + + +export const NodeCheckDocument = gql` + query NodeCheck { + statistics { + chainId + blockHeight + vegaTime + } +} + `; + +/** + * __useNodeCheckQuery__ + * + * To run a query within a React component, call `useNodeCheckQuery` and pass it any options that fit your needs. + * When your component renders, `useNodeCheckQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useNodeCheckQuery({ + * variables: { + * }, + * }); + */ +export function useNodeCheckQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(NodeCheckDocument, options); + } +export function useNodeCheckLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(NodeCheckDocument, options); + } +export type NodeCheckQueryHookResult = ReturnType; +export type NodeCheckLazyQueryHookResult = ReturnType; +export type NodeCheckQueryResult = Apollo.QueryResult; +export const NodeCheckTimeUpdateDocument = gql` + subscription NodeCheckTimeUpdate { + busEvents(types: TimeUpdate, batchSize: 1) { + id + } +} + `; + +/** + * __useNodeCheckTimeUpdateSubscription__ + * + * To run a query within a React component, call `useNodeCheckTimeUpdateSubscription` and pass it any options that fit your needs. + * When your component renders, `useNodeCheckTimeUpdateSubscription` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useNodeCheckTimeUpdateSubscription({ + * variables: { + * }, + * }); + */ +export function useNodeCheckTimeUpdateSubscription(baseOptions?: Apollo.SubscriptionHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useSubscription(NodeCheckTimeUpdateDocument, options); + } +export type NodeCheckTimeUpdateSubscriptionHookResult = ReturnType; +export type NodeCheckTimeUpdateSubscriptionResult = Apollo.SubscriptionResult; \ No newline at end of file diff --git a/libs/environment/src/utils/node.mock.ts b/libs/environment/src/utils/node.mock.ts index 4c1e207b3..c1477eb4f 100644 --- a/libs/environment/src/utils/node.mock.ts +++ b/libs/environment/src/utils/node.mock.ts @@ -1,11 +1,11 @@ -import type { StatisticsQuery } from './__generated__/Node'; +import type { NodeCheckQuery } from './__generated__/NodeCheck'; import merge from 'lodash/merge'; import type { PartialDeep } from 'type-fest'; export const statisticsQuery = ( - override?: PartialDeep -): StatisticsQuery => { - const defaultResult: StatisticsQuery = { + override?: PartialDeep +): NodeCheckQuery => { + const defaultResult: NodeCheckQuery = { statistics: { __typename: 'Statistics', chainId: 'chain-id',