Add endpoint fallbacks option and handling of consts to ui-api (#1416)

* Add `fallback` option to ui-api to pass more than one endpoint for backwards compatibility

* set correct endpoint

* remove console.logs

* linting issues

* const -> consts typo

* changed query example to blockPeriod

* turn `fallback`: string[] | string into fallbacks array

* turn while loop into map

* less params & removed let

* Adjust withCall typing, don't allow voting in treasury
This commit is contained in:
Stefanie Doll 2019-07-18 15:11:57 +02:00 committed by Jaco Greeff
parent bfdb6d6cd7
commit 7d451c6f88
10 changed files with 98 additions and 52 deletions

View File

@ -16,7 +16,7 @@ import translate from '../translate';
type Props = ApiProps & I18nProps & {
codeHash?: string | null;
contract_codeStorage?: Option<PrefabWasmModule>;
contracts_codeStorage?: Option<PrefabWasmModule>;
onChange: (isValid: boolean) => void;
};
@ -33,9 +33,9 @@ class ValidateCode extends React.PureComponent<Props> {
isValid: false
};
public static getDerivedStateFromProps ({ codeHash, contract_codeStorage, onChange }: Props): State {
public static getDerivedStateFromProps ({ codeHash, contracts_codeStorage, onChange }: Props): State {
const isValidHex = !!codeHash && isHex(codeHash) && codeHash.length === 66;
const isStored = !!contract_codeStorage && contract_codeStorage.isSome;
const isStored = !!contracts_codeStorage && contracts_codeStorage.isSome;
const isValid = isValidHex && isStored;
// FIXME Really not convinced this is the correct place to do this type of callback?
@ -70,7 +70,6 @@ class ValidateCode extends React.PureComponent<Props> {
export default translate(
withCalls<Props>(
['query.contracts.codeStorage', { paramName: 'codeHash' }], // 2.x
['query.contract.codeStorage', { paramName: 'codeHash' }] // 1.x
['query.contracts.codeStorage', { fallbacks: ['query.contract.codeStorage'], paramName: 'codeHash' }]
)(ValidateCode)
);

View File

@ -16,7 +16,7 @@ import translate from '../translate';
type Props = ApiProps & I18nProps & {
address?: string | null;
contract_contractInfoOf?: Option<ContractInfo>;
contracts_contractInfoOf?: Option<ContractInfo>;
onChange: (isValid: boolean) => void;
};
@ -33,7 +33,7 @@ class ValidateAddr extends React.PureComponent<Props> {
isValid: false
};
public static getDerivedStateFromProps ({ address, contract_contractInfoOf, onChange }: Props): State {
public static getDerivedStateFromProps ({ address, contracts_contractInfoOf, onChange }: Props): State {
let isValidAddr = false;
try {
@ -45,7 +45,7 @@ class ValidateAddr extends React.PureComponent<Props> {
}
const isStored = (
(!!contract_contractInfoOf && contract_contractInfoOf.isSome)
(!!contracts_contractInfoOf && contracts_contractInfoOf.isSome)
// (!!contract_codeHashOf && contract_codeHashOf.isSome)
);
const isValid = isValidAddr && isStored;
@ -82,7 +82,6 @@ class ValidateAddr extends React.PureComponent<Props> {
export default translate(
withCalls<Props>(
['query.contracts.contractInfoOf', { paramName: 'address' }], // 2.x
['query.contract.contractInfoOf', { paramName: 'address' }] // 1.x
['query.contracts.contractInfoOf', { fallbacks: ['query.contract.contractInfoOf'], paramName: 'address' }]
)(ValidateAddr)
);

View File

@ -29,7 +29,7 @@ interface Props extends I18nProps {
idNumber: BN;
chain_bestNumber?: BN;
democracy_referendumVotesFor?: DerivedReferendumVote[];
democracy_publicDelay?: BN;
democracy_enactmentPeriod: BN;
value: ReferendumInfoExtended;
}
@ -111,13 +111,13 @@ class Referendum extends React.PureComponent<Props, State> {
}
private renderInfo (): React.ReactNode {
const { chain_bestNumber, democracy_publicDelay, t, value: { end, threshold } } = this.props;
const { chain_bestNumber, democracy_enactmentPeriod, t, value: { end, threshold } } = this.props;
if (!chain_bestNumber) {
return null;
}
const enactBlock = (democracy_publicDelay || new BN(0)).add(end);
const enactBlock = (democracy_enactmentPeriod || new BN(0)).add(end);
return (
<div>
@ -193,6 +193,6 @@ export default withMulti(
withCalls<Props>(
'derive.chain.bestNumber',
['derive.democracy.referendumVotesFor', { paramName: 'idNumber' }],
'query.democracy.publicDelay'
['consts.democracy.enactmentPeriod', { fallbacks: ['query.democracy.publicDelay'] }]
)
);

View File

@ -17,14 +17,20 @@ interface Props extends I18nProps {
chain_bestNumber?: BN;
democracy_launchPeriod?: BN;
democracy_nextTally?: BN;
democracy_publicDelay?: BN;
democracy_publicPropCount?: BN;
democracy_referendumCount?: BN;
}
class Summary extends React.PureComponent<Props> {
public render (): React.ReactNode {
const { chain_bestNumber = new BN(0), democracy_launchPeriod = new BN(1), democracy_nextTally = new BN(0), democracy_publicPropCount, democracy_referendumCount = new BN(0), t } = this.props;
const {
chain_bestNumber = new BN(0),
democracy_launchPeriod = new BN(1),
democracy_nextTally = new BN(0),
democracy_publicPropCount,
democracy_referendumCount = new BN(0),
t
} = this.props;
return (
<SummaryBox>
@ -55,7 +61,7 @@ class Summary extends React.PureComponent<Props> {
export default translate(
withCalls<Props>(
'query.democracy.launchPeriod',
['consts.democracy.launchPeriod', { fallbacks: ['query.democracy.launchPeriod'] }],
'query.democracy.nextTally',
'query.democracy.publicPropCount',
'query.democracy.referendumCount',

View File

@ -11,13 +11,14 @@ export const storageGetInfo: Snippet = {
code: `// Get chain state information
// Make our basic chain state/storage queries, all in one go
const [minimumPeriod, validators, transferFee] = await Promise.all([
api.query.timestamp.minimumPeriod(),
api.query.session.validators(),
api.query.balances.transferFee()
const [minimumValidatorCount, validators] = await Promise.all([
api.query.staking.minimumValidatorCount(),
api.query.session.validators()
]);
console.log('minimum period between blocks: ' + minimumPeriod);
const transferFee = api.consts.balances.transferFee;
console.log('The minimum validator count: ' + minimumValidatorCount);
console.log('transfer fee: ', transferFee);
if (validators && validators.length > 0) {

View File

@ -50,7 +50,13 @@ class Approve extends TxModal<Props, State> {
}
protected renderTrigger = (): React.ReactNode => {
const { t } = this.props;
const { api, t } = this.props;
// disable voting for 1.x (we only use elections here)
if (!api.query.elections) {
return null;
}
return (
<div className='ui--Row-buttons'>
<Button.Group>
@ -100,13 +106,10 @@ export default withMulti(
translate,
withApi,
withCalls(
[
'query.elections.members',
{
propName: 'threshold',
transform: (value: [AccountId, BlockNumber][]): number =>
1 + (value.length / 2)
}
]
['query.elections.members', {
propName: 'threshold',
transform: (value: [AccountId, BlockNumber][]): number =>
1 + (value.length / 2)
}]
)
);

View File

@ -201,10 +201,10 @@ export default withMulti(
Settings,
translate,
withCalls<Props>(
['query.treasury.proposalBond', { propName: 'proposalBond' }],
['query.treasury.proposalBondMinimum', { propName: 'proposalBondMinimum' }],
['query.treasury.spendPeriod', { propName: 'spendPeriod' }],
['query.treasury.burn', { propName: 'burn' }],
['consts.treasury.proposalBond', { fallbacks: ['query.treasury.proposalBond'], propName: 'proposalBond' }],
['consts.treasury.proposalBondMinimum', { fallbacks: ['query.treasury.proposalBondMinimum'], propName: 'proposalBondMinimum' }],
['consts.treasury.spendPeriod', { fallbacks: ['query.treasury.spendPeriod'], propName: 'spendPeriod' }],
['consts.treasury.burn', { fallbacks: ['query.treasury.burn'], propName: 'burn' }],
['query.treasury.pot', { propName: 'pot' }]
)
);

View File

@ -12,13 +12,15 @@ import { isEqual, triggerChange } from '../util';
import echoTransform from '../transform/echo';
import withApi from './api';
// FIXME This is not correct, we need some junction of derive, query & consts
interface Method {
(...params: any[]): Promise<any>;
at: (hash: Uint8Array | string, ...params: any[]) => Promise<any>;
meta: any;
multi: (params: any[], cb: (value?: any) => void) => Promise<any>;
}
type ApiMethodInfo = [Method, any[], boolean];
type ApiMethodInfo = [Method, any[], string];
type State = CallState;
@ -26,7 +28,21 @@ const NOOP = (): void => {
// ignore
};
export default function withCall<P extends ApiProps> (endpoint: string, { at, atProp, callOnResult, isMulti = false, params = [], paramName, paramPick, paramValid = false, propName, transform = echoTransform }: Options = {}): (Inner: React.ComponentType<ApiProps>) => React.ComponentType<any> {
export default function withCall<P extends ApiProps> (
endpoint: string,
{
at,
atProp,
callOnResult,
fallbacks,
isMulti = false,
params = [],
paramName,
paramPick,
paramValid = false,
propName,
transform = echoTransform
}: Options = {}): (Inner: React.ComponentType<ApiProps>) => React.ComponentType<any> {
return (Inner: React.ComponentType<ApiProps>): React.ComponentType<SubtractProps<P, ApiProps>> => {
class WithPromise extends React.Component<P, State> {
public state: State = {
@ -122,30 +138,43 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
? paramValue
: [paramValue]
);
return [true, values];
}
private getApiMethod (newParams: any[]): ApiMethodInfo {
private constructApiSection = (endpoint: string): [Record<string, Method>, string, string, string] => {
const { api } = this.props;
const [area, section, method, ...others] = endpoint.split('.');
assert(area.length && section.length && method.length && others.length === 0, `Invalid API format, expected <area>.<section>.<method>, found ${endpoint}`);
assert(['consts', 'rpc', 'query', 'derive'].includes(area), `Unknown api.${area}, expected consts, rpc, query or derive`);
assert(!at || area === 'query', `Only able to do an 'at' query on the api.query interface`);
const apiSection = (api as any)[area][section];
return [
apiSection,
area,
section,
method
];
}
private getApiMethod (newParams: any[]): ApiMethodInfo {
if (endpoint === 'subscribe') {
const [fn, ...params] = newParams;
return [
fn,
params,
true
'subscribe'
];
}
const [area, section, method, ...others] = endpoint.split('.');
assert(area.length && section.length && method.length && others.length === 0, `Invalid API format, expected <area>.<section>.<method>, found ${endpoint}`);
assert(['rpc', 'query', 'derive'].includes(area), `Unknown api.${area}, expected rpc, query or derive`);
assert(!at || area === 'query', `Only able to do an 'at' query on the api.query interface`);
const apiSection = (api as any)[area][section];
const endpoints: string[] = [endpoint].concat(fallbacks || []);
const expanded = endpoints.map(this.constructApiSection);
const [apiSection, area, section, method] = expanded.find(([apiSection]): boolean =>
!!apiSection
) || [{}, expanded[0][1], expanded[0][2], expanded[0][3]];
assert(apiSection && apiSection[method], `Unable to find api.${area}.${section}.${method}`);
@ -160,7 +189,7 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
return [
apiSection[method],
newParams,
area === 'derive' || (area === 'query' && (!at && !atProp)) || method.startsWith('subscribe')
method.startsWith('subscribe') ? 'subscribe' : area
];
}
@ -186,17 +215,19 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
return;
}
const [apiMethod, params, isSubscription] = info;
const [apiMethod, params, area] = info;
const updateCb = (value?: any): void =>
this.triggerUpdate(this.props, value);
await this.unsubscribe();
try {
if (isSubscription) {
if (['derive', 'subscribe'].includes(area) || (area === 'query' && (!at && !atProp))) {
this.destroy = isMulti
? await apiMethod.multi(params, updateCb)
: await apiMethod(...params, updateCb);
} else if (area === 'consts') {
updateCb(apiMethod);
} else {
updateCb(
at

View File

@ -17,6 +17,7 @@ export interface Options {
at?: Uint8Array | string;
atProp?: string;
callOnResult?: OnChangeCb;
fallbacks?: string[];
isMulti?: boolean;
params?: any[];
paramName?: string;

View File

@ -35,7 +35,11 @@ export class Proposal extends React.PureComponent<Props, State> {
isBelowMinimum: false
};
public static getDerivedStateFromProps ({ deposit, democracy_minimumDeposit = new BN(0), onChange }: Props): State {
public static getDerivedStateFromProps ({
deposit,
democracy_minimumDeposit = new BN(0),
onChange
}: Props): State {
const extraAmount = deposit instanceof Compact
? deposit.toBn()
: deposit;
@ -87,5 +91,7 @@ export class Proposal extends React.PureComponent<Props, State> {
export default withMulti(
Proposal,
translate,
withCalls<Props>('query.democracy.minimumDeposit')
withCalls<Props>(
['consts.democracy.minimumDeposit', { fallbacks: ['query.democracy.minimumDeposit'] }]
)
);