Rework API urls (#5840)

* Rework API urls

* Adjust

* Fix build
This commit is contained in:
Jaco 2021-07-16 12:33:03 +03:00 committed by GitHub
parent 27b82e7ac2
commit aa6ea3a486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 52 additions and 114 deletions

View File

@ -30,7 +30,7 @@ export function checkEndpoints (issueFile: string, failures: string[]): void {
value &&
isString(value) &&
!value.includes('127.0.0.1') &&
!value.includes('substrate-connect')
!value.startsWith('light://')
)
.map(({ text, value }): Partial<Endpoint> => ({
name: text as string,

View File

@ -7,12 +7,7 @@ import { createWsEndpoints } from '.';
interface Endpoint {
name: string;
ws: string;
}
interface LightClientEndpoint {
name: string;
param: string;
value: string;
}
const allEndpoints = createWsEndpoints((k: string, v?: string) => v || k);
@ -22,34 +17,15 @@ describe('WS urls are all valid', (): void => {
.filter(({ value }) =>
value &&
isString(value) &&
!value.includes('127.0.0.1') &&
!value.includes('substrate-connect')
!value.includes('127.0.0.1')
)
.map(({ text, value }): Endpoint => ({
name: text as string,
ws: value
value
}))
.forEach(({ name, ws }) =>
it(`${name} @ ${ws}`, (): void => {
assert(ws.startsWith('wss://'), `${name} @ ${ws} should start with wss://`);
})
);
});
describe('light client urls are all valid', (): void => {
allEndpoints
.filter(({ value }) =>
value &&
isString(value) &&
value.includes('substrate-connect')
)
.map(({ text, value }): LightClientEndpoint => ({
name: text as string,
param: value
}))
.forEach(({ name, param }) =>
it(`${name} @ ${param}`, (): void => {
assert(param.substr(param.indexOf('-')) === '-substrate-connect', `${name} @ ${param} should end with '-substrate-connect'`);
.forEach(({ name, value }) =>
it(`${name} @ ${value}`, (): void => {
assert(value.startsWith('wss://') || value.startsWith('light://substrate-connect/'), `${name} @ ${value} should start with wss:// or light://`);
})
);
});

View File

@ -5,7 +5,6 @@ import type { TFunction } from 'i18next';
import type { EndpointOption } from './types';
import { KUSAMA_GENESIS } from '../api/constants';
import { createProviderUrl } from './util';
/* eslint-disable sort-keys */
@ -24,7 +23,7 @@ export function createKusama (t: TFunction): EndpointOption {
Parity: 'wss://kusama-rpc.polkadot.io',
OnFinality: 'wss://kusama.api.onfinality.io/public-ws',
'Patract Elara': 'wss://kusama.elara.patract.io',
'light client': createProviderUrl('kusama-substrate-connect', 'substrate-connect')
'light client': 'light://substrate-connect/kusama'
// Pinknode: 'wss://rpc.pinknode.io/kusama/explorer' // https://github.com/polkadot-js/apps/issues/5721
},
teleport: [1000],

View File

@ -5,7 +5,6 @@ import type { TFunction } from 'i18next';
import type { EndpointOption } from './types';
import { POLKADOT_GENESIS } from '../api/constants';
import { createProviderUrl } from './util';
/* eslint-disable sort-keys */
@ -24,7 +23,7 @@ export function createPolkadot (t: TFunction): EndpointOption {
Parity: 'wss://rpc.polkadot.io',
OnFinality: 'wss://polkadot.api.onfinality.io/public-ws',
'Patract Elara': 'wss://polkadot.elara.patract.io',
'light client': createProviderUrl('polkadot-substrate-connect', 'substrate-connect')
'light client': 'light://substrate-connect/polkadot'
// Pinknode: 'wss://rpc.pinknode.io/polkadot/explorer' // https://github.com/polkadot-js/apps/issues/5721
},
linked: [

View File

@ -5,7 +5,6 @@ import type { TFunction } from 'i18next';
import type { EndpointOption } from './types';
import { WESTEND_GENESIS } from '../api/constants';
import { createProviderUrl } from './util';
/* eslint-disable sort-keys */
@ -26,7 +25,7 @@ export function createWestend (t: TFunction): EndpointOption {
Parity: 'wss://westend-rpc.polkadot.io',
'Patract Elara': 'wss://westend.elara.patract.io',
OnFinality: 'wss://westend.api.onfinality.io/public-ws',
'light client': createProviderUrl('westend-substrate-connect', 'substrate-connect')
'light client': 'light://substrate-connect/westend'
// 'NodeFactory(Vedran)': 'wss://westend.vedran.nodefactory.io/ws', // https://github.com/polkadot-js/apps/issues/5580
// Pinknode: 'wss://rpc.pinknode.io/westend/explorer' // https://github.com/polkadot-js/apps/issues/5721
},

View File

@ -3,8 +3,6 @@
import type { Option } from '../settings/types';
import { Endpoint } from '@polkadot/ui-settings/types';
export interface EndpointOption {
dnslink?: string;
genesisHash?: string;
@ -16,7 +14,7 @@ export interface EndpointOption {
linked?: EndpointOption[];
info?: string;
paraId?: number;
providers: Record<string, string | Endpoint>;
providers: Record<string, string>;
summary?: string;
teleport?: number[];
text: React.ReactNode;

View File

@ -4,9 +4,6 @@
import type { TFunction } from 'i18next';
import type { EndpointOption, LinkOption } from './types';
import { Endpoint, EndpointType } from '@polkadot/ui-settings/types';
import { isString } from '@polkadot/util';
interface SortOption {
isUnreachable?: boolean;
}
@ -55,12 +52,12 @@ export function expandEndpoint (t: TFunction, { dnslink, genesisHash, homepage,
.map(([host, value], index): LinkOption => ({
...base,
dnslink: index === 0 ? dnslink : undefined,
isLightClient: isString(value) ? false : value.type === 'substrate-connect',
isLightClient: value.startsWith('light://'),
isRelay: false,
textBy: isString(value)
? t('rpc.hosted.by', 'hosted by {{host}}', { ns: 'apps-config', replace: { host } })
: t('lightclient.experimental', 'light client (experimental)', { ns: 'apps-config' }),
value: isString(value) ? value : value.param
textBy: value.startsWith('light://')
? t('lightclient.experimental', 'light client (experimental)', { ns: 'apps-config' })
: t('rpc.hosted.by', 'hosted by {{host}}', { ns: 'apps-config', replace: { host } }),
value
}));
if (linked) {
@ -84,7 +81,3 @@ export function expandEndpoint (t: TFunction, { dnslink, genesisHash, homepage,
export function expandEndpoints (t: TFunction, input: EndpointOption[], firstOnly?: boolean): LinkOption[] {
return input.sort(sortLinks).reduce((result: LinkOption[], input) => result.concat(expandEndpoint(t, input, firstOnly)), []);
}
export function createProviderUrl (param: string, type: EndpointType): Endpoint {
return { param, type };
}

View File

@ -26,7 +26,7 @@ function NetworkDisplay ({ apiUrl, className = '', setApiUrl, value: { icon, isC
const _selectUrl = useCallback(
() => {
const filteredProviders = providers.filter((p) => !p.name.startsWith('light client'));
const filteredProviders = providers.filter(({ url }) => !url.startsWith('light://'));
return setApiUrl(name, filteredProviders[Math.floor(Math.random() * filteredProviders.length)].url);
},

View File

@ -14,7 +14,6 @@ import styled from 'styled-components';
import { createWsEndpoints, CUSTOM_ENDPOINT_KEY } from '@polkadot/apps-config';
import { Button, Input, Sidebar } from '@polkadot/react-components';
import { settings } from '@polkadot/ui-settings';
import { Endpoint, EndpointType } from '@polkadot/ui-settings/types';
import { isAscii } from '@polkadot/util';
import { useTranslation } from '../translate';
@ -40,18 +39,10 @@ function isValidUrl (url: string): boolean {
// some random length... we probably want to parse via some lib
(url.length >= 7) &&
// check that it starts with a valid ws identifier
(url.startsWith('ws://') || url.startsWith('wss://'))
(url.startsWith('ws://') || url.startsWith('wss://') || url.startsWith('light://'))
);
}
function getApiType (param: string): Endpoint {
if (param.includes('-substrate-connect')) {
return { param, type: 'substrate-connect' };
}
return { param, type: 'json-rpc' };
}
function combineEndpoints (endpoints: LinkOption[]): Group[] {
return endpoints.reduce((result: Group[], e): Group[] => {
if (e.isHeader) {
@ -127,10 +118,10 @@ function loadAffinities (groups: Group[]): Record<string, string> {
}), {});
}
function isSwitchDisabled (hasUrlChanged: boolean, apiType: EndpointType, isUrlValid: boolean): boolean {
function isSwitchDisabled (hasUrlChanged: boolean, apiUrl: string, isUrlValid: boolean): boolean {
if (!hasUrlChanged) {
return true;
} else if (apiType === 'substrate-connect') {
} else if (apiUrl.startsWith('light://')) {
return false;
} else if (isUrlValid) {
return false;
@ -237,7 +228,7 @@ function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElem
const _onApply = useCallback(
(): void => {
settings.set({ ...(settings.get()), apiUrl });
window.location.assign(`${window.location.origin}${window.location.pathname}${getApiType(apiUrl).type === 'substrate-connect' ? '?sc=' : '?rpc='}${encodeURIComponent(apiUrl)}${window.location.hash}`);
window.location.assign(`${window.location.origin}${window.location.pathname}?rpc=${encodeURIComponent(apiUrl)}${window.location.hash}`);
// window.location.reload();
onClose();
},
@ -245,7 +236,7 @@ function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElem
);
const canSwitch = useMemo(
() => isSwitchDisabled(hasUrlChanged, getApiType(apiUrl).type, isUrlValid),
() => isSwitchDisabled(hasUrlChanged, apiUrl, isUrlValid),
[hasUrlChanged, apiUrl, isUrlValid]
);
@ -275,7 +266,7 @@ function Endpoints ({ className = '', offset, onClose }: Props): React.ReactElem
setGroup={_changeGroup}
value={group}
>
{group.isDevelopment && getApiType(apiUrl).type === 'json-rpc' && (
{group.isDevelopment && (
<div className='endpointCustomWrapper'>
<Input
className='endpointCustom'

View File

@ -44,7 +44,7 @@ function Root ({ store }: Props): React.ReactElement<Props> {
<ThemeProvider theme={theme}>
<Queue>
<Api
apiType={settings.apiType}
apiUrl={settings.apiUrl}
store={store}
>
<BlockAuthors>

View File

@ -7,18 +7,17 @@ import store from 'store';
import { createWsEndpoints } from '@polkadot/apps-config';
import { extractIpfsDetails } from '@polkadot/react-hooks/useIpfs';
import { settings } from '@polkadot/ui-settings';
import { Endpoint } from '@polkadot/ui-settings/types';
import { assert } from '@polkadot/util';
function networkOrUrl (apiType: Endpoint): void {
if (apiType.type === 'json-rpc') {
console.log('WS endpoint=', apiType.param);
} else if (apiType.type === 'substrate-connect') {
console.log('Chain of light client is =', apiType.param);
function networkOrUrl (apiUrl: string): void {
if (apiUrl.startsWith('light://')) {
console.log('Light endpoint=', apiUrl.replace('light://', ''));
} else {
console.log('WS endpoint=', apiUrl);
}
}
function getApiType (): Endpoint {
function getApiUrl (): string {
// we split here so that both these forms are allowed
// - http://localhost:3000/?rpc=wss://substrate-rpc.parity.io/#/explorer
// - http://localhost:3000/#/explorer?rpc=wss://substrate-rpc.parity.io
@ -31,17 +30,9 @@ function getApiType (): Endpoint {
// https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:9944#/explorer;
const url = decodeURIComponent(urlOptions.rpc.split('#')[0]);
assert(url.startsWith('ws://') || url.startsWith('wss://'), 'Non-prefixed ws/wss url');
assert(url.startsWith('ws://') || url.startsWith('wss://') || url.startsWith('light://'), 'Non-prefixed ws/wss/light url');
return { param: url, type: 'json-rpc' };
} else if (urlOptions.sc) {
assert(!Array.isArray(urlOptions.sc), 'Invalid network specified');
// https://polkadot.js.org/apps/?sc=kusama#/explorer;
const network = decodeURIComponent(urlOptions.sc.split('#')[0]);
const chain = network.split('-')[0];
return { param: chain, type: 'substrate-connect' };
return url;
}
const endpoints = createWsEndpoints(<T = string>(): T => ('' as unknown as T));
@ -52,7 +43,7 @@ function getApiType (): Endpoint {
const option = endpoints.find(({ dnslink }) => dnslink === ipnsChain);
if (option) {
return { param: option.value, type: 'json-rpc' };
return option.value;
}
}
@ -60,17 +51,17 @@ function getApiType (): Endpoint {
const fallbackUrl = endpoints.find(({ value }) => !!value);
// via settings, or the default chain
return [stored.apiType, process.env.WS_URL].includes(settings.apiType)
? settings.apiType // keep as-is
return [stored.apiUrl, process.env.WS_URL].includes(settings.apiUrl)
? settings.apiUrl // keep as-is
: fallbackUrl
? { param: fallbackUrl.value, type: 'json-rpc' } // grab the fallback
: { param: 'ws://127.0.0.1:9944', type: 'json-rpc' }; // nothing found, go local
? fallbackUrl.value // grab the fallback
: 'ws://127.0.0.1:9944'; // nothing found, go local
}
// There cannot be a Substrate Connect light client default (expect only jrpc EndpointType)
const apiType = getApiType();
const apiUrl = getApiUrl();
// set the default as retrieved here
settings.set({ apiType });
settings.set({ apiUrl });
networkOrUrl(apiType);
networkOrUrl(apiUrl);

View File

@ -14,17 +14,15 @@ import { Api } from '@polkadot/react-api';
import { MemoryStore } from '@polkadot/test-support/keyring';
import { WaitForApi } from '@polkadot/test-support/react';
import { SUBSTRATE_PORT } from '@polkadot/test-support/substrate';
import { Endpoint } from '@polkadot/ui-settings/types';
const renderAccounts = () => {
const memoryStore = new MemoryStore();
const apiType: Endpoint = { param: `ws://127.0.0.1:${SUBSTRATE_PORT}`, type: 'json-rpc' };
return render(
<MemoryRouter>
<ThemeProvider theme={lightTheme}>
<Api
apiType={apiType}
apiUrl={`ws://127.0.0.1:${SUBSTRATE_PORT}`}
store={memoryStore}
>
<WaitForApi>

View File

@ -17,21 +17,18 @@ import { MemoryStore } from '@polkadot/test-support/keyring';
import { aliceSigner } from '@polkadot/test-support/keyring/signers';
import { WaitForApi } from '@polkadot/test-support/react';
import { execute } from '@polkadot/test-support/transaction/execute';
import { Endpoint } from '@polkadot/ui-settings/types';
const SUBSTRATE_PORT = Number.parseInt(process.env.TEST_SUBSTRATE_PORT || '30333');
const renderBounties = () => {
const memoryStore = new MemoryStore();
const apiType: Endpoint = { param: `ws://127.0.0.1:${SUBSTRATE_PORT}`, type: 'json-rpc' };
return render(
<Suspense fallback='...'>
<MemoryRouter>
<ThemeProvider theme={lightTheme}>
<Api
apiType={apiType}
apiUrl={`ws://127.0.0.1:${SUBSTRATE_PORT}`}
store={memoryStore}
>
<WaitForApi>

View File

@ -19,10 +19,8 @@ import { web3Accounts, web3Enable } from '@polkadot/extension-dapp';
import { TokenUnit } from '@polkadot/react-components/InputNumber';
import { StatusContext } from '@polkadot/react-components/Status';
import ApiSigner from '@polkadot/react-signer/signers/ApiSigner';
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { keyring } from '@polkadot/ui-keyring';
import { settings } from '@polkadot/ui-settings';
import { Endpoint } from '@polkadot/ui-settings/types';
import { formatBalance, isTestChain } from '@polkadot/util';
import { defaults as addressDefaults } from '@polkadot/util-crypto/address/defaults';
@ -32,7 +30,7 @@ import { decodeUrlTypes } from './urlTypes';
interface Props {
children: React.ReactNode;
apiType: Endpoint;
apiUrl: string;
store?: KeyringStore;
}
@ -180,7 +178,7 @@ async function loadOnReady (api: ApiPromise, injectedPromise: Promise<InjectedEx
};
}
function Api ({ apiType, children, store }: Props): React.ReactElement<Props> | null {
function Api ({ apiUrl, children, store }: Props): React.ReactElement<Props> | null {
const { queuePayload, queueSetTxStatus } = useContext(StatusContext);
const [state, setState] = useState<ApiState>({ hasInjectedAccounts: false, isApiReady: false } as unknown as ApiState);
const [isApiConnected, setIsApiConnected] = useState(false);
@ -189,22 +187,21 @@ function Api ({ apiType, children, store }: Props): React.ReactElement<Props> |
const [extensions, setExtensions] = useState<InjectedExtension[] | undefined>();
const value = useMemo<ApiProps>(
() => ({ ...state, api, apiError, apiUrl: apiType.param, extensions, isApiConnected, isApiInitialized, isWaitingInjected: !extensions }),
[apiError, extensions, isApiConnected, isApiInitialized, state, apiType]
() => ({ ...state, api, apiError, apiUrl, extensions, isApiConnected, isApiInitialized, isWaitingInjected: !extensions }),
[apiError, extensions, isApiConnected, isApiInitialized, state, apiUrl]
);
// initial initialization
useEffect((): void => {
let provider;
if (apiType.type === 'substrate-connect') {
const detect: Detector = new Detector('PolkadotJS apps');
if (apiUrl.startsWith('light://')) {
const detect = new Detector('polkadot-js/apps');
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
provider = detect.provider(apiType.param) as ProviderInterface;
provider = detect.provider(apiUrl.replace('light://substrate-connect/', ''));
provider.connect().catch(console.error);
} else if (apiType.type === 'json-rpc') {
provider = new WsProvider(apiType.param);
} else {
provider = new WsProvider(apiUrl);
}
const signer = new ApiSigner(registry, queuePayload, queueSetTxStatus);
@ -232,7 +229,7 @@ function Api ({ apiType, children, store }: Props): React.ReactElement<Props> |
});
setIsApiInitialized(true);
}, [apiType, queuePayload, queueSetTxStatus, store]);
}, [apiUrl, queuePayload, queueSetTxStatus, store]);
if (!value.isApiInitialized) {
return null;