fix(explorer): place sensible limits on oracles page queries (#2699)

This commit is contained in:
Edd 2023-01-26 14:50:52 +00:00 committed by GitHub
parent 33e528c569
commit 3188f23439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 283 additions and 82 deletions

View File

@ -70,7 +70,7 @@
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.66.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.67.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},

View File

@ -1,4 +1,3 @@
import React from 'react';
import { Routes } from '../../../routes/route-names';
import { useExplorerMarketQuery } from './__generated__/Market';
import { Link } from 'react-router-dom';
@ -8,6 +7,7 @@ import { t } from '@vegaprotocol/react-helpers';
export type MarketLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
showMarketName?: boolean;
};
/**
@ -15,7 +15,11 @@ export type MarketLinkProps = Partial<ComponentProps<typeof Link>> & {
* with a link to the markets list. If the name does not come back
* it will use the ID instead
*/
const MarketLink = ({ id, ...props }: MarketLinkProps) => {
const MarketLink = ({
id,
showMarketName = true,
...props
}: MarketLinkProps) => {
const { data, error, loading } = useExplorerMarketQuery({
variables: { id },
});
@ -37,11 +41,24 @@ const MarketLink = ({ id, ...props }: MarketLinkProps) => {
}
}
return (
<Link className="underline" {...props} to={`/${Routes.MARKETS}#${id}`}>
{label}
</Link>
);
if (showMarketName) {
return (
<Link
className="underline"
{...props}
to={`/${Routes.MARKETS}#${id}`}
title={id}
>
{label}
</Link>
);
} else {
return (
<Link className="underline" {...props} to={`/${Routes.MARKETS}#${id}`}>
{id}
</Link>
);
}
};
export default MarketLink;

View File

@ -0,0 +1,22 @@
import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
export type OracleLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
};
const OracleLink = ({ id, ...props }: OracleLinkProps) => {
return (
<Link
className="underline font-mono"
{...props}
to={`/${Routes.ORACLES}/${id}`}
>
{id}
</Link>
);
};
export default OracleLink;

View File

@ -50,6 +50,12 @@ export const TxDetailsOrder = ({
<code>{deterministicId}</code>
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Market ID')}</TableCell>
<TableCell>
<MarketLink id={marketId} showMarketName={false} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>

View File

@ -51,6 +51,9 @@ fragment ExplorerOracleDataSource on OracleSpec {
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
pageInfo {
hasNextPage
}
edges {
node {
externalData {
@ -79,12 +82,21 @@ fragment ExplorerOracleDataConnection on OracleSpec {
}
query ExplorerOracleSpecs {
oracleSpecsConnection {
oracleSpecsConnection(pagination: { first: 50 }) {
pageInfo {
hasNextPage
}
edges {
node {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
}
}
query ExplorerOracleSpecById($id: ID!) {
oracleSpec(oracleSpecId: $id) {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}

View File

@ -5,12 +5,19 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
export const ExplorerOracleDataSourceFragmentDoc = gql`
fragment ExplorerOracleDataSource on OracleSpec {
@ -66,7 +73,10 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
`;
export const ExplorerOracleDataConnectionFragmentDoc = gql`
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
dataConnection(pagination: {first: 1}) {
pageInfo {
hasNextPage
}
edges {
node {
externalData {
@ -96,7 +106,10 @@ export const ExplorerOracleDataConnectionFragmentDoc = gql`
`;
export const ExplorerOracleSpecsDocument = gql`
query ExplorerOracleSpecs {
oracleSpecsConnection {
oracleSpecsConnection(pagination: {first: 50}) {
pageInfo {
hasNextPage
}
edges {
node {
...ExplorerOracleDataSource
@ -133,4 +146,41 @@ export function useExplorerOracleSpecsLazyQuery(baseOptions?: Apollo.LazyQueryHo
}
export type ExplorerOracleSpecsQueryHookResult = ReturnType<typeof useExplorerOracleSpecsQuery>;
export type ExplorerOracleSpecsLazyQueryHookResult = ReturnType<typeof useExplorerOracleSpecsLazyQuery>;
export type ExplorerOracleSpecsQueryResult = Apollo.QueryResult<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>;
export type ExplorerOracleSpecsQueryResult = Apollo.QueryResult<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>;
export const ExplorerOracleSpecByIdDocument = gql`
query ExplorerOracleSpecById($id: ID!) {
oracleSpec(oracleSpecId: $id) {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
${ExplorerOracleDataSourceFragmentDoc}
${ExplorerOracleDataConnectionFragmentDoc}`;
/**
* __useExplorerOracleSpecByIdQuery__
*
* To run a query within a React component, call `useExplorerOracleSpecByIdQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerOracleSpecByIdQuery` 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 } = useExplorerOracleSpecByIdQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerOracleSpecByIdQuery(baseOptions: Apollo.QueryHookOptions<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>(ExplorerOracleSpecByIdDocument, options);
}
export function useExplorerOracleSpecByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>(ExplorerOracleSpecByIdDocument, options);
}
export type ExplorerOracleSpecByIdQueryHookResult = ReturnType<typeof useExplorerOracleSpecByIdQuery>;
export type ExplorerOracleSpecByIdLazyQueryHookResult = ReturnType<typeof useExplorerOracleSpecByIdLazyQuery>;
export type ExplorerOracleSpecByIdQueryResult = Apollo.QueryResult<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>;

View File

@ -37,7 +37,7 @@ describe('Oracle Data view', () => {
dataConnection: {
edges: [],
},
} as ExplorerOracleDataConnectionFragment)
} as unknown as ExplorerOracleDataConnectionFragment)
);
expect(res.container).toBeEmptyDOMElement();
});

View File

@ -67,7 +67,7 @@ describe('Oracle Signers component', () => {
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '123',
key: '1234567891234567789123456789123456778912345678912345677891234567',
},
},
],

View File

@ -4,6 +4,7 @@ import {
EthExplorerLinkTypes,
} from '../../../components/links/eth-explorer-link/eth-explorer-link';
import { TableRow, TableCell, TableHeader } from '../../../components/table';
import { remove0x } from '@vegaprotocol/react-helpers';
import type { SourceType } from './oracle';
@ -14,7 +15,15 @@ export type Signer = {
};
export function getAddressTypeLabel(signer: Signer) {
return signer.__typename === 'ETHAddress' ? 'ETH' : 'Vega';
const res = signer.__typename === 'ETHAddress' ? 'ETH' : 'Vega';
// This is a hack: some older oracles were submitted before proper checks stopped
// ETH addresses being returned as Vega addresses
if (res === 'Vega' && signer?.key?.length !== 64) {
return 'ETH';
} else {
return res;
}
}
export function getAddress(signer: Signer) {
@ -29,6 +38,16 @@ export function getAddressLink(signer: Signer) {
if (signer.__typename === 'ETHAddress') {
return <EthExplorerLink id={address} type={EthExplorerLinkTypes.address} />;
} else if (signer.__typename === 'PubKey' && address.length !== 64) {
// This is a hack: some older oracles were submitted before proper checks stopped
// ETH addresses being returned as Vega addresses
// Hacky 0x prefixing as a bonus
return (
<EthExplorerLink
id={`0x${remove0x(address)}`}
type={EthExplorerLinkTypes.address}
/>
);
} else if (signer.__typename === 'PubKey') {
return <PartyLink id={address} />;
}

View File

@ -13,6 +13,8 @@ import { OracleData } from './oracle-data';
import { OracleFilter } from './oracle-filter';
import { OracleDetailsType } from './oracle-details-type';
import { OracleMarkets } from './oracle-markets';
import { OracleSigners } from './oracle-signers';
import OracleLink from '../../../components/links/oracle-link/oracle-link';
export type SourceType =
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
@ -21,36 +23,47 @@ interface OracleDetailsProps {
id: string;
dataSource: ExplorerOracleDataSourceFragment;
dataConnection: ExplorerOracleDataConnectionFragment;
// Defaults to false. Hides the count of 'broadcasts' this oracle has seen
showBroadcasts?: boolean;
}
/**
* Notes:
* - Matched data is really 'Data that matched this oracle' and given oracles are unique
* to each market, and each serves either as trading termination or settlement, really
* they will only ever see 1 match (most likely). So it should be more like 'Has seen
* data' vs 'Has not yet seen data'
*/
export const OracleDetails = ({
id,
dataSource,
dataConnection,
showBroadcasts = false,
}: OracleDetailsProps) => {
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
const reportsCount: number = dataConnection.dataConnection.edges?.length || 0;
return (
<div>
<TableWithTbody>
<TableWithTbody className="mb-2">
<TableRow modifier="bordered">
<TableHeader scope="row">{t('ID')}</TableHeader>
<TableCell modifier="bordered">{id}</TableCell>
<TableCell modifier="bordered">
<OracleLink id={id} />
</TableCell>
</TableRow>
<OracleDetailsType type={sourceType.__typename} />
{
// Disabled until https://github.com/vegaprotocol/vega/issues/7286 is released
/*<OracleSigners sourceType={sourceType} />*/
}
<OracleSigners sourceType={sourceType} />
<OracleMarkets id={id} />
<TableRow modifier="bordered">
<TableHeader scope="row">{t('Broadcasts')}</TableHeader>
<TableCell modifier="bordered">{reportsCount}</TableCell>
<TableHeader scope="row">{t('Matched data')}</TableHeader>
<TableCell modifier="bordered">
{showBroadcasts ? reportsCount : reportsCount > 0 ? '✅' : '❌'}
</TableCell>
</TableRow>
</TableWithTbody>
<OracleFilter data={dataSource} />
<OracleData data={dataConnection} />
{showBroadcasts ? <OracleData data={dataConnection} /> : null}
</div>
);
};

View File

@ -0,0 +1,45 @@
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../../components/route-title';
import { t } from '@vegaprotocol/react-helpers';
import { useExplorerOracleSpecsQuery } from '../__generated__/Oracles';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { OracleDetails } from '../components/oracle';
import { useScrollToLocation } from '../../../hooks/scroll-to-location';
import filter from 'recursive-key-filter';
const Oracles = () => {
const { data, loading } = useExplorerOracleSpecsQuery();
useDocumentTitle(['Oracles']);
useScrollToLocation();
return (
<section>
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
{loading ? <Loader /> : null}
{data?.oracleSpecsConnection?.edges
? data.oracleSpecsConnection.edges.map((o) => {
const id = o?.node.dataSourceSpec.spec.id;
if (!id) {
return null;
}
return (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id}
dataSource={o?.node}
dataConnection={o?.node}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(o, ['__typename'])} />
</details>
</div>
);
})
: null}
</section>
);
};
export default Oracles;

View File

@ -0,0 +1,49 @@
import { RouteTitle } from '../../../components/route-title';
import { RenderFetched } from '../../../components/render-fetched';
import { t, truncateByChars } from '@vegaprotocol/react-helpers';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { useParams } from 'react-router-dom';
import { useExplorerOracleSpecByIdQuery } from '../__generated__/Oracles';
import { OracleDetails } from '../components/oracle';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import filter from 'recursive-key-filter';
import { TruncateInline } from '../../../components/truncate/truncate';
export const Oracle = () => {
const { id } = useParams<{ id: string }>();
useDocumentTitle(['Oracle', `Oracle #${truncateByChars(id || '1', 5, 5)}`]);
const { data, error, loading } = useExplorerOracleSpecByIdQuery({
variables: {
id: id || '1',
},
});
return (
<section>
<RouteTitle data-testid="block-header">
{t(`Oracle `)}
<TruncateInline startChars={5} endChars={5} text={id || '1'} />
</RouteTitle>
<RenderFetched error={error} loading={loading}>
{data?.oracleSpec ? (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id || ''}
dataSource={data?.oracleSpec}
dataConnection={data?.oracleSpec}
showBroadcasts={true}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(data, ['__typename'])} />
</details>
</div>
) : (
<span></span>
)}
</RenderFetched>
</section>
);
};

View File

@ -1,45 +1,7 @@
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../components/route-title';
import { t } from '@vegaprotocol/react-helpers';
import { useExplorerOracleSpecsQuery } from './__generated__/Oracles';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { OracleDetails } from './components/oracle';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import filter from 'recursive-key-filter';
import { Outlet } from 'react-router-dom';
const Oracles = () => {
const { data, loading } = useExplorerOracleSpecsQuery();
useDocumentTitle(['Oracles']);
useScrollToLocation();
return (
<section>
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
{loading ? <Loader /> : null}
{data?.oracleSpecsConnection?.edges
? data.oracleSpecsConnection.edges.map((o) => {
const id = o?.node.dataSourceSpec.spec.id;
if (!id) {
return null;
}
return (
<div id={id} key={id} className="mb-10 cursor-pointer">
<OracleDetails
id={id}
dataSource={o?.node}
dataConnection={o?.node}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(o, ['__typename'])} />
</details>
</div>
);
})
: null}
</section>
);
const OraclePage = () => {
return <Outlet />;
};
export default Oracles;
export default OraclePage;

View File

@ -3,7 +3,9 @@ import BlockPage from './blocks';
import Governance from './governance';
import Home from './home';
import Markets from './markets';
import Oracles from './oracles';
import OraclePage from './oracles';
import Oracles from './oracles/home';
import { Oracle } from './oracles/id';
import Party from './parties';
import { Parties } from './parties/home';
import { Party as PartySingle } from './parties/id';
@ -85,17 +87,6 @@ const marketsRoutes = flags.markets
]
: [];
const oraclesRoutes = flags.oracles
? [
{
path: Routes.ORACLES,
name: 'Oracles',
text: t('Oracles'),
element: <Oracles />,
},
]
: [];
const networkParametersRoutes = flags.networkParameters
? [
{
@ -161,12 +152,27 @@ const routerConfig = [
},
],
},
{
path: Routes.ORACLES,
name: 'Oracles',
text: t('Oracles'),
element: <OraclePage />,
children: [
{
index: true,
element: <Oracles />,
},
{
path: ':id',
element: <Oracle />,
},
],
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...oraclesRoutes,
...networkParametersRoutes,
...validators,
];