Skip to content

Commit

Permalink
Merge pull request #5391 from LiskHQ/5357-allow-mainchain-to-switch-t…
Browse files Browse the repository at this point in the history
…o-different-service-url

Allow mainchain to switch to different service url
  • Loading branch information
eniolam1000752 authored Oct 31, 2023
2 parents a14cb26 + 00241fc commit 4acfc91
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 9 deletions.
26 changes: 23 additions & 3 deletions setup/react/app/ApplicationBootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { useCurrentAccount } from 'src/modules/account/hooks';
import { Client } from 'src/utils/api/client';
import { useReduxStateModifier } from 'src/utils/useReduxStateModifier';
import { useLedgerDeviceListener } from '@libs/hardwareWallet/ledger/ledgerDeviceListener/useLedgerDeviceListener';
import { useValidServiceUrl } from '@blockchainApplication/manage/hooks/useValidServiceUrl';
import { useRewardsClaimable } from 'src/modules/pos/reward/hooks/queries';

export const ApplicationBootstrapContext = createContext({
Expand Down Expand Up @@ -52,6 +53,9 @@ const ApplicationBootstrap = ({ children }) => {
client: queryClient.current,
});

const serviceUrls = blockchainAppsMeta.data?.data[0]?.serviceURLs;
const { validServiceUrl } = useValidServiceUrl(serviceUrls);

const mainChainApplication = blockchainAppsMeta.data?.data?.find(
({ chainID }) => chainID === networkStatus?.data?.data?.chainID
);
Expand All @@ -61,17 +65,32 @@ const ApplicationBootstrap = ({ children }) => {
!!mainChainNetwork;

useEffect(() => {
if (mainChainApplication) {
if (mainChainApplication && validServiceUrl) {
const refreshedCurrentApplication = blockchainAppsMeta?.data?.data?.find(
({ chainID }) => chainID === currentApplication?.chainID
);
const networkCode = mainChainApplication.chainID.match(/^\d{4}/g)[0];
const currentAppToSet =
const currentAppToSelect =
refreshedCurrentApplication?.chainID?.indexOf(networkCode) === 0
? refreshedCurrentApplication
: mainChainApplication;

setCurrentApplication(currentAppToSet);
const currentApplicationWithValidServiceUrlAtTheTop = {
...currentAppToSelect,
serviceURLs: currentAppToSelect.serviceURLs.sort((a, b) => {
const nameA = a.http === validServiceUrl;
const nameB = b.http === validServiceUrl;
if (nameA && nameB) {
return 0;
}
if (nameA) {
return -1;
}
return 1;
}),
};

setCurrentApplication(currentApplicationWithValidServiceUrlAtTheTop);
setApplications([mainChainApplication]);
}

Expand All @@ -83,6 +102,7 @@ const ApplicationBootstrap = ({ children }) => {
blockchainAppsMeta.isFetched,
blockchainAppsMeta.isError,
blockchainAppsMeta.isLoading,
validServiceUrl,
]);

useLedgerDeviceListener();
Expand Down
4 changes: 4 additions & 0 deletions setup/react/app/ApplicationBootstrap.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useBlockchainApplicationMeta } from '@blockchainApplication/manage/hook
import { mockBlockchainAppMeta } from '@blockchainApplication/manage/__fixtures__/mockBlockchainAppMeta';
import useSettings from '@settings/hooks/useSettings';
import networks from '@network/configuration/networks';
import { useValidServiceUrl } from '@blockchainApplication/manage/hooks/useValidServiceUrl';
import { smartRender } from 'src/utils/testHelpers';
import ApplicationBootstrap from './ApplicationBootstrap';

Expand All @@ -19,6 +20,7 @@ jest.mock('@blockchainApplication/manage/hooks/queries/useBlockchainApplicationM
jest.mock('@blockchainApplication/manage/hooks/useApplicationManagement');
jest.mock('@settings/hooks/useSettings');
jest.mock('@blockchainApplication/manage/hooks/useCurrentApplication');
jest.mock('@blockchainApplication/manage/hooks/useValidServiceUrl');

const props = {
children: <span>testing</span>,
Expand Down Expand Up @@ -54,7 +56,9 @@ describe('ApplicationBootstrap', () => {
isLoading: false,
isError: undefined,
});

useNetworkStatus.mockReturnValue({ data: mockNetworkStatus });
useValidServiceUrl.mockReturnValue({validServiceUrl: mockBlockchainAppMeta.data[0].serviceURLs[0].http})

it('Should set main chain application for the selected network', async () => {
smartRender(ApplicationBootstrap, props, renderConfig);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useEffect, useState } from 'react';
import { isEmpty } from 'src/utils/helpers';
import { Client } from 'src/utils/api/client';

async function isNetworkUrlSuccess(fetchUrl, successBaseUrlToReturn) {
try {
const client = new Client({ http: fetchUrl });
await client.rest();
return successBaseUrlToReturn;
} catch (error) {
return false;
}
}

export async function resolveApiValidity(serviceURLs) {
const promises = [];
for (let i = 0; i < serviceURLs.length; i++) {
const baseServiceUrl = serviceURLs[i]?.http;
promises.push(isNetworkUrlSuccess(`${baseServiceUrl}/api/v3/index/status`, baseServiceUrl));
}
const responses = await Promise.all(promises);
return responses.find((response) => response);
}

/* istanbul ignore next */
export function useValidServiceUrl(serviceURLs) {
const [validServiceUrl, setValidServiceUrl] = useState('');
const [isLoading, setIsLoading] = useState(!!serviceURLs);

useEffect(() => {
(async () => {
if (!isEmpty(serviceURLs)) {
setIsLoading(true);
const serviceUrl = await resolveApiValidity(serviceURLs);
setValidServiceUrl(serviceUrl);
setIsLoading(false);
}
})();
}, [serviceURLs]);

return { validServiceUrl, isLoading };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as client from 'src/utils/api/client';
import { resolveApiValidity } from './useValidServiceUrl';

describe('useValidServiceurl', () => {
it('returns valid service url', async () => {
jest.spyOn(client, 'Client').mockReturnValue({
rest: jest.fn().mockResolvedValue(),
});
const serviceUrls = [{ http: 'http://localhost:9901' }, { http: 'http://localhost-2:9901' }];
const validServiceUrl = await resolveApiValidity(serviceUrls);

expect(validServiceUrl).toBe(serviceUrls[0].http);
});

it('should not return a url if serviceUrls are invalid', async () => {
jest.spyOn(client, 'Client').mockReturnValue({
rest: jest.fn().mockRejectedValue(),
});

const serviceUrls = [{ http: 'http://localhost:9901' }, { http: 'http://localhost-2:9901' }];
const validServiceUrl = await resolveApiValidity(serviceUrls);

expect(validServiceUrl).toBeFalsy();
});

it('should return the second url if the first is invalid', async () => {
const serviceUrls = [{ http: 'http://localhost:9901' }, { http: 'http://localhost-2:9901' }];

jest.spyOn(client, 'Client').mockImplementation(({ http }) => ({
rest:
http === `${serviceUrls[0].http}/api/v3/index/status`
? jest.fn().mockRejectedValue()
: jest.fn().mockReturnValue(),
}));
const validServiceUrl = await resolveApiValidity(serviceUrls);

expect(validServiceUrl).toBe(serviceUrls[1].http);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ const DialogAddNetwork = () => {
});
const formValues = watch();
const networkCheck = useNetworkCheck(formValues.serviceUrl);
const isNetworkUrlOk = networkCheck.isOffchainOK && networkCheck.isOnchainOK;

async function onTryNetworkUrl() {
networkCheck.refetch();
Expand Down Expand Up @@ -141,7 +140,7 @@ const DialogAddNetwork = () => {
})}
/>

{networkCheck.isError && !!formValues.serviceUrl && !networkCheck.isFetching && (
{!networkCheck.isNetworkOK && !!formValues.serviceUrl && !networkCheck.isFetching && (
<span className={styles.connectionFailed}>
<span className={styles.errorText}>
{t(
Expand Down Expand Up @@ -171,7 +170,10 @@ const DialogAddNetwork = () => {
/>
<PrimaryButton
disabled={
!isDirty || !isNetworkUrlOk || networkCheck.isFetching || networkCheck.isError
!isDirty ||
!networkCheck.isNetworkOK ||
networkCheck.isFetching ||
networkCheck.isError
}
type="submit"
className={`${styles.button}`}
Expand Down
12 changes: 9 additions & 3 deletions src/modules/network/components/DialogAddNetwork/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Client } from 'src/utils/api/client';
import { useNetworkStatus } from '@network/hooks/queries';
import { useBlockchainApplicationMeta } from '@blockchainApplication/manage/hooks/queries/useBlockchainApplicationMeta';
import { useDebounce } from 'src/modules/search/hooks/useDebounce';
import { useValidServiceUrl } from '@blockchainApplication/manage/hooks/useValidServiceUrl';

export const DEFAULT_NETWORK_FORM_STATE = {
name: '',
Expand Down Expand Up @@ -72,17 +73,22 @@ export function useNetworkCheck(serviceUrl) {
client,
});

const serviceUrls = blockchainAppsMeta?.data?.data[0]?.serviceURLs;
const { validServiceUrl, isLoading } = useValidServiceUrl(serviceUrls);
const hasValidServiceUrl = !!validServiceUrl;

const handleRefetch = () => {
networkStatus.refetch();
blockchainAppsMeta.refetch();
};

return {
isNetworkOK: !!networkStatus?.data?.data && !!blockchainAppsMeta?.data?.data,
isNetworkOK:
!!networkStatus?.data?.data && !!blockchainAppsMeta?.data?.data && hasValidServiceUrl,
isOnchainOK: !!networkStatus?.data?.data,
isOffchainOK: !!blockchainAppsMeta?.data?.data,
isFetching: networkStatus?.isFetching || blockchainAppsMeta?.isFetching,
isError: networkStatus?.isError || blockchainAppsMeta?.isError,
isFetching: networkStatus?.isFetching || blockchainAppsMeta?.isFetching || isLoading,
isError: networkStatus?.isError || blockchainAppsMeta?.isError || !hasValidServiceUrl,
refetch: handleRefetch,
};
}

0 comments on commit 4acfc91

Please sign in to comment.