feat: wip refactor config hook to run on init

This commit is contained in:
Botond
2022-07-06 19:15:10 +01:00
parent 9b7c26386b
commit 92315181e1
11 changed files with 266 additions and 178 deletions
@@ -1 +0,0 @@
export * from './network-loader';
@@ -11,7 +11,6 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { useEnvironment } from '../../hooks';
import type { ConfigStatus } from '../../types';
type MessageComponentProps = {
children: ReactNode;
@@ -44,11 +43,10 @@ const Error = ({ children, showTryAgain }: ErrorComponentProps) => (
);
type StatusComponentProps = {
status: ConfigStatus;
children?: ReactNode;
};
const StatusComponent = ({ status, children }: StatusComponentProps) => {
const StatusComponent = ({ children }: StatusComponentProps) => {
switch (status) {
case 'error-loading-config':
return (
@@ -137,7 +135,7 @@ export function NetworkLoader<T>({
}: NetworkLoaderProps<T>) {
// this is to prevent an error rendering callouts on the server side
const [canShowCallout, setShowCallout] = useState(false);
const { configStatus, VEGA_URL } = useEnvironment();
const { VEGA_URL } = useEnvironment();
const client = useMemo(() => {
if (VEGA_URL) {
@@ -9,6 +9,7 @@ type NodeSwitcherDialogProps = ComponentProps<typeof NodeSwitcher> & {
export const NodeSwitcherDialog = ({
config,
initialErrorType,
dialogOpen,
setDialogOpen,
onConnect,
@@ -17,6 +18,7 @@ export const NodeSwitcherDialog = ({
<Dialog open={dialogOpen} contentClassNames="md:w-[856px] w-[856px]">
<NodeSwitcher
config={config}
initialErrorType={initialErrorType}
onConnect={(url) => {
onConnect(url);
setDialogOpen(false);
@@ -13,10 +13,11 @@ import {
getIsNodeLoading,
getIsNodeDisabled,
getIsFormDisabled,
getErrorMessage,
getErrorByData,
getErrorByType,
} from '../../utils/validate-node';
import { CUSTOM_NODE_KEY } from '../../types';
import type { Configuration, NodeData } from '../../types';
import type { Configuration, NodeData, ErrorType } from '../../types';
import { LayoutRow } from './layout-row';
import { LayoutCell } from './layout-cell';
import { NodeError } from './node-error';
@@ -25,6 +26,7 @@ import { NodeStats } from './node-stats';
type NodeSwitcherProps = {
error?: string;
config: Configuration;
initialErrorType?: ErrorType;
onConnect: (url: string) => void;
};
@@ -39,8 +41,9 @@ const getHighestBlock = (state: Record<string, NodeData>) => {
}, 0);
};
export const NodeSwitcher = ({ config, onConnect }: NodeSwitcherProps) => {
export const NodeSwitcher = ({ config, initialErrorType, onConnect }: NodeSwitcherProps) => {
const { VEGA_ENV, VEGA_URL } = useEnvironment();
const [networkError, setNetworkError] = useState(getErrorByType(initialErrorType, VEGA_ENV, VEGA_URL));
const [customNodeText, setCustomNodeText] = useState('');
const [nodeRadio, setNodeRadio] = useState(
getDefaultNode(config.hosts, VEGA_URL)
@@ -61,7 +64,7 @@ export const NodeSwitcher = ({ config, onConnect }: NodeSwitcherProps) => {
VEGA_ENV,
state
);
const currentNodeError = getErrorMessage(
const currentNodeError = getErrorByData(
VEGA_ENV,
nodeRadio && customNode && customNode === customNodeText
? state[nodeRadio]
@@ -70,7 +73,7 @@ export const NodeSwitcher = ({ config, onConnect }: NodeSwitcherProps) => {
return (
<div className="text-black dark:text-white min-w-[800px]">
<NodeError {...currentNodeError} />
<NodeError {...(currentNodeError || networkError)} />
<form onSubmit={() => onSubmit(nodeRadio)}>
<p className="text-body-large font-bold mt-16 mb-32">
{t('Select a GraphQL node to connect to:')}
@@ -85,7 +88,10 @@ export const NodeSwitcher = ({ config, onConnect }: NodeSwitcherProps) => {
<RadioGroup
className="block"
value={nodeRadio}
onChange={(value) => setNodeRadio(value)}
onChange={(value) => {
setNodeRadio(value)
setNetworkError(null);
}}
>
<div className="w-full">
{config.hosts.map((node, index) => (
@@ -137,12 +143,14 @@ export const NodeSwitcher = ({ config, onConnect }: NodeSwitcherProps) => {
value={customNodeText}
hasError={
!!customNodeText &&
(!!currentNodeError.headline ||
!!currentNodeError.message)
!!(currentNodeError?.headline || currentNodeError?.message)
}
onChange={(e) => setCustomNodeText(e.target.value)}
/>
<Link onClick={() => setCustomNode(customNodeText)}>
<Link onClick={() => {
setNetworkError(null);
setCustomNode(customNodeText)
}}>
{getIsNodeLoading(state[CUSTOM_NODE_KEY])
? t('Checking')
: t('Check')}
+15 -13
View File
@@ -57,10 +57,12 @@ const noop = () => {};
global.fetch = jest.fn();
const mockUpdate = jest.fn();
const onConnectionError = jest.fn();
beforeEach(() => {
jest.useFakeTimers();
mockUpdate.mockClear();
onConnectionError.mockClear();
window.localStorage.clear();
// @ts-ignore typescript doesn't recognise the mocked instance
@@ -82,7 +84,7 @@ describe('useConfig hook', () => {
...mockEnvironment,
VEGA_URL: 'https://some.url/query',
};
const { result } = renderHook(() => useConfig(mockEnvWithUrl, mockUpdate));
const { result } = renderHook(() => useConfig(mockEnvWithUrl, mockUpdate, onConnectionError));
expect(fetch).not.toHaveBeenCalled();
expect(mockUpdate).not.toHaveBeenCalled();
@@ -98,7 +100,7 @@ describe('useConfig hook', () => {
];
const { result, waitForNextUpdate } = renderHook(() =>
useConfig(mockEnvironment, mockUpdate)
useConfig(mockEnvironment, mockUpdate, onConnectionError)
);
await waitForNextUpdate();
@@ -128,7 +130,7 @@ describe('useConfig hook', () => {
});
const { result, waitForNextUpdate } = renderHook(() =>
useConfig(mockEnvironment, mockUpdate)
useConfig(mockEnvironment, mockUpdate, onConnectionError)
);
await waitForNextUpdate();
@@ -153,7 +155,7 @@ describe('useConfig hook', () => {
);
const { result, waitForNextUpdate } = renderHook(() =>
useConfig(mockEnvironment, mockUpdate)
useConfig(mockEnvironment, mockUpdate, onConnectionError)
);
await waitForNextUpdate();
@@ -174,7 +176,7 @@ describe('useConfig hook', () => {
});
const { result, waitForNextUpdate } = renderHook(() =>
useConfig(mockEnvironment, mockUpdate)
useConfig(mockEnvironment, mockUpdate, onConnectionError)
);
await waitForNextUpdate();
@@ -196,7 +198,7 @@ describe('useConfig hook', () => {
});
const { result, waitForNextUpdate } = renderHook(() =>
useConfig(mockEnvironment, mockUpdate)
useConfig(mockEnvironment, mockUpdate, onConnectionError)
);
await waitForNextUpdate();
@@ -219,7 +221,7 @@ describe('useConfig hook', () => {
);
const { result, waitForNextUpdate } = renderHook(() =>
useConfig(mockEnvironment, mockUpdate)
useConfig(mockEnvironment, mockUpdate, onConnectionError)
);
await waitForNextUpdate();
@@ -229,7 +231,7 @@ describe('useConfig hook', () => {
});
it('caches the list of networks', async () => {
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate, onConnectionError));
await run1.waitForNextUpdate();
jest.runAllTimers();
@@ -241,7 +243,7 @@ describe('useConfig hook', () => {
// @ts-ignore typescript doesn't recognise the mocked instance
fetch.mockClear();
const run2 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
const run2 = renderHook(() => useConfig(mockEnvironment, mockUpdate, onConnectionError));
jest.runAllTimers();
await run2.waitForNextUpdate();
@@ -251,7 +253,7 @@ describe('useConfig hook', () => {
});
it('caches the list of networks between runs', async () => {
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate, onConnectionError));
await run1.waitForNextUpdate();
jest.runAllTimers();
@@ -263,7 +265,7 @@ describe('useConfig hook', () => {
// @ts-ignore typescript doesn't recognise the mocked instance
fetch.mockClear();
const run2 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
const run2 = renderHook(() => useConfig(mockEnvironment, mockUpdate, onConnectionError));
jest.runAllTimers();
await run2.waitForNextUpdate();
@@ -276,7 +278,7 @@ describe('useConfig hook', () => {
window.localStorage.setItem(LOCAL_STORAGE_NETWORK_KEY, '{not:{valid:{json');
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(noop);
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate, onConnectionError));
await run1.waitForNextUpdate();
jest.runAllTimers();
@@ -296,7 +298,7 @@ describe('useConfig hook', () => {
);
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(noop);
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate));
const run1 = renderHook(() => useConfig(mockEnvironment, mockUpdate, onConnectionError));
await run1.waitForNextUpdate();
jest.runAllTimers();
+86 -47
View File
@@ -1,27 +1,50 @@
import type { Dispatch, SetStateAction } from 'react';
import { useState, useEffect } from 'react';
import { LocalStorage } from '@vegaprotocol/react-helpers';
import { ErrorType } from '../types';
import type {
Environment,
Configuration,
ConfigStatus,
Networks,
} from '../types';
import { validateConfiguration } from '../utils/validate-configuration';
import { promiseRaceToSuccess } from '../utils/promise-race-success';
import { requestNode } from '../utils/request-node';
import { getHasInvalidChain } from '../utils/validate-node';
export const LOCAL_STORAGE_NETWORK_KEY = 'vegaNetworkConfig';
export type EnvironmentWithOptionalUrl = Partial<Environment> &
Omit<Environment, 'VEGA_URL'>;
const requestToNode = async (url: string, index: number): Promise<number> => {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed connecting to node: ${url}.`);
const requestToNode = (env: Networks, setSubscriptionStatus: (url: string, status: boolean) => void) => async (url: string): Promise<string> => new Promise((resolve, reject) => {
requestNode(url, {
onStatsSuccess: (data) => {
if (getHasInvalidChain(env, data.statistics.chainId)) {
reject(ErrorType.INVALID_NETWORK);
return;
}
resolve(url);
return;
},
onStatsFailure: () => {
reject(ErrorType.CONNECTION_ERROR);
},
onSubscriptionSuccess: () => {
setSubscriptionStatus(url, true);
},
onSubscriptionFailure: () => {
setSubscriptionStatus(url, false);
},
});
});
const compileHosts = (hosts: string[], envUrl?: string) => {
if (envUrl && !hosts.includes(envUrl)) {
return [...hosts, envUrl];
}
return index;
};
return hosts;
}
const getCachedConfig = (env: Networks) => {
const value = LocalStorage.getItem(LOCAL_STORAGE_NETWORK_KEY);
@@ -39,7 +62,7 @@ const getCachedConfig = (env: Networks) => {
} catch (err) {
LocalStorage.removeItem(LOCAL_STORAGE_NETWORK_KEY);
console.warn(
'Malformed data found for network configuration. Removed and continuing...'
'Malformed data found for network configuration. Removed cached configuration, continuing...'
);
}
}
@@ -49,84 +72,100 @@ const getCachedConfig = (env: Networks) => {
export const useConfig = (
environment: EnvironmentWithOptionalUrl,
updateEnvironment: Dispatch<SetStateAction<Environment>>
updateEnvironment: Dispatch<SetStateAction<Environment>>,
onNodeError: (errorType: ErrorType) => void,
onConfigLoadError: () => void,
) => {
const [verified, setVerified] = useState(false);
const [subscriptionStatusMap, setSubscriptionStatusMap] = useState<Record<string, boolean>>({});
const [config, setConfig] = useState<Configuration | undefined>(
getCachedConfig(environment.VEGA_ENV)
);
const [status, setStatus] = useState<ConfigStatus>(
!environment.VEGA_URL ? 'idle' : 'success'
);
useEffect(() => {
if (!config && status === 'idle') {
if (!config) {
(async () => {
setStatus('loading-config');
try {
const response = await fetch(environment.VEGA_CONFIG_URL ?? '');
const configData: Configuration = await response.json();
if (validateConfiguration(configData)) {
setStatus('error-validating-config');
onConfigLoadError();
return;
}
setConfig({ hosts: configData.hosts });
const hosts = compileHosts(configData.hosts, environment.VEGA_URL);
setConfig({ hosts });
LocalStorage.setItem(
LOCAL_STORAGE_NETWORK_KEY,
JSON.stringify({
[environment.VEGA_ENV]: {
hosts: configData.hosts,
hosts,
},
})
);
} catch (err) {
setStatus('error-loading-config');
onConfigLoadError();
}
})();
}
// load config only once per runtime
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [environment.VEGA_CONFIG_URL, !!config, status, setStatus, setConfig]);
}, [environment.VEGA_CONFIG_URL, environment.VEGA_URL, !!config, setConfig, onConfigLoadError]);
useEffect(() => {
if (
config &&
!['loading-node', 'success', 'error-loading-node'].includes(status)
) {
(async () => {
setStatus('loading-node');
// if there's only one configured node to choose from, set is as the env url
if (config.hosts.length === 1) {
setStatus('success');
updateEnvironment((prevEnvironment) => ({
...prevEnvironment,
VEGA_URL: config.hosts[0],
}));
return;
}
// when there are multiple possible hosts, set the env url to the node which responds first
(async () => {
if (environment.VEGA_URL && !verified) {
try {
const requests = config.hosts.map(requestToNode);
const index = await promiseRaceToSuccess(requests);
setStatus('success');
await requestToNode(environment.VEGA_ENV, (index, status) => {
setSubscriptionStatusMap(state => ({
...state,
[index]: status,
}))
})(environment.VEGA_URL);
setVerified(true);
} catch (err: any) {
if (err in ErrorType) {
onNodeError(err);
return;
}
onNodeError(ErrorType.CONNECTION_ERROR);
}
}
if (config && !environment.VEGA_URL) {
try {
const requests = config.hosts.map(requestToNode(environment.VEGA_ENV, (url, status) => {
setSubscriptionStatusMap(state => ({
...state,
[url]: status,
}))
}));
const node = await promiseRaceToSuccess(requests);
setVerified(true);
updateEnvironment((prevEnvironment) => ({
...prevEnvironment,
VEGA_URL: config.hosts[index],
VEGA_URL: node,
}));
} catch (err) {
setStatus('error-loading-node');
} catch (err: any) {
onNodeError(ErrorType.CONNECTION_ERROR_ALL);
}
})();
}
}
})();
// load config only once per runtime
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status, !!config, setStatus, updateEnvironment]);
}, [environment.VEGA_URL, verified, !!config, updateEnvironment]);
useEffect(() => {
if (subscriptionStatusMap[environment.VEGA_URL ?? ''] === false) {
onNodeError(ErrorType.SSL_ERROR);
}
}, [onNodeError, subscriptionStatusMap[environment.VEGA_URL ?? '']])
return {
status,
config,
};
};
@@ -5,7 +5,7 @@ import { NodeSwitcherDialog } from '../components/node-switcher-dialog';
import { useConfig } from './use-config';
import { compileEnvironment } from '../utils/compile-environment';
import { validateEnvironment } from '../utils/validate-environment';
import type { Environment, RawEnvironment, ConfigStatus } from '../types';
import type { Environment, RawEnvironment } from '../types';
type EnvironmentProviderProps = {
definitions?: Partial<RawEnvironment>;
@@ -13,7 +13,6 @@ type EnvironmentProviderProps = {
};
export type EnvironmentState = Environment & {
configStatus: ConfigStatus;
setNodeSwitcherOpen: () => void;
};
@@ -27,9 +26,11 @@ export const EnvironmentProvider = ({
const [environment, updateEnvironment] = useState<Environment>(
compileEnvironment(definitions)
);
const { status: configStatus, config } = useConfig(
const { config } = useConfig(
environment,
updateEnvironment
updateEnvironment,
() => setNodeSwitcherOpen(true),
() => setNodeSwitcherOpen(true),
);
const errorMessage = validateEnvironment(environment);
@@ -46,7 +47,6 @@ export const EnvironmentProvider = ({
<EnvironmentContext.Provider
value={{
...environment,
configStatus,
setNodeSwitcherOpen: () => setNodeSwitcherOpen(true),
}}
>
+8 -9
View File
@@ -8,6 +8,14 @@ export { ENV_KEYS, Networks };
export const CUSTOM_NODE_KEY = 'custom';
export enum ErrorType {
INVALID_URL,
INVALID_NETWORK,
SSL_ERROR,
CONNECTION_ERROR,
CONNECTION_ERROR_ALL,
}
export type Environment = z.infer<typeof envSchema> & {
// provide this manually, zod fails to compile the correct type fot VEGA_NETWORKS
VEGA_NETWORKS: Partial<Record<Networks, string>>;
@@ -19,15 +27,6 @@ export type RawEnvironment = Record<EnvKey, string>;
export type Configuration = z.infer<typeof configSchema>;
export type ConfigStatus =
| 'idle'
| 'success'
| 'loading-config'
| 'loading-node'
| 'error-loading-config'
| 'error-validating-config'
| 'error-loading-node';
type NodeCheck<T> = {
isLoading: boolean;
hasError: boolean;
+19 -65
View File
@@ -1,26 +1,7 @@
import type { Dispatch } from 'react';
import { gql } from '@apollo/client';
import createClient from './apollo-client';
import { ACTIONS } from '../hooks/use-nodes';
import type { Action } from '../hooks/use-nodes';
import type { Statistics } from './__generated__/Statistics';
export const STATS_QUERY = gql`
query Statistics {
statistics {
chainId
blockHeight
}
}
`;
export const TIME_UPDATE_SUBSCRIPTION = gql`
subscription BlockTime {
busEvents(types: TimeUpdate, batchSize: 1) {
eventId
}
}
`;
import { requestNode } from './request-node';
const getResponseTime = (url: string) => {
const requests = window.performance.getEntriesByName(url);
@@ -33,69 +14,42 @@ export const initializeNode = (
node: string,
nodeUrl?: string
) => {
let isMounted = true;
const url = nodeUrl ?? node;
dispatch({ type: ACTIONS.GET_STATISTICS, node, payload: { url } });
dispatch({ type: ACTIONS.CHECK_SUBSCRIPTION, node, payload: { url } });
try {
new URL(url);
} catch (err) {
dispatch({ type: ACTIONS.GET_STATISTICS_FAILURE, node });
dispatch({ type: ACTIONS.CHECK_SUBSCRIPTION_FAILURE, node });
return {
client: undefined,
unsubscribe: () => {
isMounted = false;
},
};
}
const client = createClient(url);
let isMounted = true;
client
.query<Statistics>({
query: STATS_QUERY,
})
.then((res) => {
const client = requestNode(url, {
onStatsSuccess: data => {
isMounted &&
dispatch({
type: ACTIONS.GET_STATISTICS_SUCCESS,
node,
payload: {
chain: res.data.statistics.chainId,
block: Number(res.data.statistics.blockHeight),
chain: data.statistics.chainId,
block: Number(data.statistics.blockHeight),
responseTime: getResponseTime(url),
},
});
})
.catch(() => {
},
onStatsFailure: () => {
isMounted && dispatch({ type: ACTIONS.GET_STATISTICS_FAILURE, node });
});
const subscription = client
.subscribe({
query: TIME_UPDATE_SUBSCRIPTION,
errorPolicy: 'all',
})
.subscribe({
next() {
isMounted &&
dispatch({ type: ACTIONS.CHECK_SUBSCRIPTION_SUCCESS, node });
subscription.unsubscribe();
},
error() {
isMounted &&
dispatch({ type: ACTIONS.CHECK_SUBSCRIPTION_FAILURE, node });
subscription.unsubscribe();
},
});
},
onSubscriptionSuccess: () => {
isMounted &&
dispatch({ type: ACTIONS.CHECK_SUBSCRIPTION_SUCCESS, node });
},
onSubscriptionFailure: () => {
isMounted &&
dispatch({ type: ACTIONS.CHECK_SUBSCRIPTION_FAILURE, node });
},
});
return {
client,
unsubscribe: () => {
client.stop();
client?.stop();
isMounted = false;
},
};
@@ -0,0 +1,76 @@
import { gql } from '@apollo/client';
import createClient from './apollo-client';
import type { Statistics } from './__generated__/Statistics';
export const STATS_QUERY = gql`
query Statistics {
statistics {
chainId
blockHeight
}
}
`;
export const TIME_UPDATE_SUBSCRIPTION = gql`
subscription BlockTime {
busEvents(types: TimeUpdate, batchSize: 1) {
eventId
}
}
`;
type Callbacks = {
onStatsSuccess: (data: Statistics) => void,
onStatsFailure: () => void,
onSubscriptionSuccess: () => void,
onSubscriptionFailure: () => void,
}
export const requestNode = (
url: string,
{
onStatsSuccess,
onStatsFailure,
onSubscriptionSuccess,
onSubscriptionFailure,
}: Callbacks
) => {
try {
new URL(url);
} catch (err) {
onStatsFailure();
onSubscriptionFailure();
return;
}
const client = createClient(url);
client
.query<Statistics>({
query: STATS_QUERY,
})
.then((res) => {
onStatsSuccess(res.data);
})
.catch(() => {
onStatsFailure();
});
const subscription = client
.subscribe({
query: TIME_UPDATE_SUBSCRIPTION,
errorPolicy: 'all',
})
.subscribe({
next() {
onSubscriptionSuccess();
subscription.unsubscribe();
},
error() {
onSubscriptionFailure();
subscription.unsubscribe();
},
});
return client;
}
+36 -25
View File
@@ -1,5 +1,5 @@
import { t } from '@vegaprotocol/react-helpers';
import { CUSTOM_NODE_KEY } from '../types';
import { CUSTOM_NODE_KEY, ErrorType } from '../types';
import type { Networks, NodeData } from '../types';
export const getIsNodeLoading = ({
@@ -16,7 +16,7 @@ export const getIsNodeLoading = ({
);
};
const getHasInvalidChain = (env: Networks, chain?: string) => {
export const getHasInvalidChain = (env: Networks, chain?: string) => {
return !(chain?.includes(env.toLowerCase()) ?? false);
};
@@ -62,20 +62,42 @@ export const getIsFormDisabled = (
return getIsNodeDisabled(env, data);
};
export const getErrorMessage = (env: Networks, data?: NodeData) => {
export const getErrorByType = (errorType: ErrorType | undefined, env: Networks, url?: string) => {
switch (errorType) {
case ErrorType.INVALID_URL: return {
headline: t('Error: invalid url'),
message: t(url ? `${url} is not a valid url.` : ''),
}
case ErrorType.INVALID_NETWORK: return {
headline: t(`Error: incorrect network`),
message: t(`This node is not on the ${env} network.`),
}
case ErrorType.SSL_ERROR: return {
headline: t(`Error: the node you are reading from does not have SSL`),
message: t(
'${data.url} does not have SSL. SSL is required to subscribe to data.'
),
}
case ErrorType.CONNECTION_ERROR: return {
headline: t(`Error: can't connect to node`),
message: t(url ? `There was an error connecting to ${url}.` : ''),
}
case ErrorType.CONNECTION_ERROR_ALL: return {
headline: t(`Error: can't connect to any of the nodes on the network`),
message: t(`Please try entering a custom node address, or try again later.`),
}
default: return null;
}
}
export const getErrorByData = (env: Networks, data?: NodeData) => {
if (data && !getIsNodeLoading(data)) {
if (getHasInvalidChain(env, data.chain.value)) {
return {
headline: t(`Error: incorrect network`),
message: t(`This node is not on the ${env} network.`),
};
return getErrorByType(ErrorType.INVALID_NETWORK, env, data.url);
}
if (getHasInvalidUrl(data.url)) {
return {
headline: t('Error: invalid url'),
message: t(`${data.url} is not a valid url.`),
};
return getErrorByType(ErrorType.INVALID_URL, env, data.url);
}
if (
@@ -83,24 +105,13 @@ export const getErrorMessage = (env: Networks, data?: NodeData) => {
data.responseTime.hasError ||
data.block.hasError
) {
return {
headline: t(`Error: can't connect to node`),
message: t(`There was an error connecting to ${data.url}.`),
};
return getErrorByType(ErrorType.CONNECTION_ERROR, env, data.url);
}
if (data.ssl.hasError) {
return {
headline: t(`Error: the node you are reading from does not have SSL`),
message: t(
'${data.url} does not have SSL. SSL is required to subscribe to data.'
),
};
return getErrorByType(ErrorType.SSL_ERROR, env, data.url);
}
}
return {
message: undefined,
headline: undefined,
};
return null;
};