Commit 7f197552 authored by tom's avatar tom

better resource types

parent 7f3c8b3a
...@@ -4,14 +4,14 @@ import appConfig from 'configs/app/config'; ...@@ -4,14 +4,14 @@ import appConfig from 'configs/app/config';
import isNeedProxy from './isNeedProxy'; import isNeedProxy from './isNeedProxy';
import { RESOURCES } from './resources'; import { RESOURCES } from './resources';
import type { ApiResource, ResourceName } from './resources'; import type { ApiResource, ResourceName, ResourcePathParams } from './resources';
export default function buildUrl( export default function buildUrl<R extends ResourceName>(
_resource: ApiResource | ResourceName, resourceName: R,
pathParams?: Record<string, string | undefined>, pathParams?: ResourcePathParams<R>,
queryParams?: Record<string, string | Array<string> | number | undefined>, queryParams?: Record<string, string | Array<string> | number | undefined>,
) { ): string {
const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource; const resource: ApiResource = RESOURCES[resourceName];
const baseUrl = isNeedProxy() ? appConfig.baseUrl : (resource.endpoint || appConfig.api.endpoint); const baseUrl = isNeedProxy() ? appConfig.baseUrl : (resource.endpoint || appConfig.api.endpoint);
const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath; const basePath = resource.basePath !== undefined ? resource.basePath : appConfig.api.basePath;
const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
......
...@@ -37,6 +37,7 @@ export interface ApiResource { ...@@ -37,6 +37,7 @@ export interface ApiResource {
path: string; path: string;
endpoint?: string; endpoint?: string;
basePath?: string; basePath?: string;
pathParams?: Array<string>;
} }
export const RESOURCES = { export const RESOURCES = {
...@@ -49,21 +50,27 @@ export const RESOURCES = { ...@@ -49,21 +50,27 @@ export const RESOURCES = {
}, },
custom_abi: { custom_abi: {
path: '/api/account/v1/user/custom_abis/:id?', path: '/api/account/v1/user/custom_abis/:id?',
pathParams: [ 'id' as const ],
}, },
watchlist: { watchlist: {
path: '/api/account/v1/user/watchlist/:id?', path: '/api/account/v1/user/watchlist/:id?',
pathParams: [ 'id' as const ],
}, },
public_tags: { public_tags: {
path: '/api/account/v1/user/public_tags/:id?', path: '/api/account/v1/user/public_tags/:id?',
pathParams: [ 'id' as const ],
}, },
private_tags_address: { private_tags_address: {
path: '/api/account/v1/user/tags/address/:id?', path: '/api/account/v1/user/tags/address/:id?',
pathParams: [ 'id' as const ],
}, },
private_tags_tx: { private_tags_tx: {
path: '/api/account/v1/user/tags/transaction/:id?', path: '/api/account/v1/user/tags/transaction/:id?',
pathParams: [ 'id' as const ],
}, },
api_keys: { api_keys: {
path: '/api/account/v1/user/api_keys/:id?', path: '/api/account/v1/user/api_keys/:id?',
pathParams: [ 'id' as const ],
}, },
// STATS // STATS
...@@ -79,6 +86,7 @@ export const RESOURCES = { ...@@ -79,6 +86,7 @@ export const RESOURCES = {
}, },
stats_line: { stats_line: {
path: '/api/v1/lines/:id', path: '/api/v1/lines/:id',
pathParams: [ 'id' as const ],
endpoint: appConfig.statsApi.endpoint, endpoint: appConfig.statsApi.endpoint,
basePath: appConfig.statsApi.basePath, basePath: appConfig.statsApi.basePath,
}, },
...@@ -98,9 +106,11 @@ export const RESOURCES = { ...@@ -98,9 +106,11 @@ export const RESOURCES = {
}, },
block: { block: {
path: '/api/v2/blocks/:height', path: '/api/v2/blocks/:height',
pathParams: [ 'height' as const ],
}, },
block_txs: { block_txs: {
path: '/api/v2/blocks/:height/transactions', path: '/api/v2/blocks/:height/transactions',
pathParams: [ 'height' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ],
filterFields: [], filterFields: [],
}, },
...@@ -116,24 +126,29 @@ export const RESOURCES = { ...@@ -116,24 +126,29 @@ export const RESOURCES = {
}, },
tx: { tx: {
path: '/api/v2/transactions/:hash', path: '/api/v2/transactions/:hash',
pathParams: [ 'hash' as const ],
}, },
tx_internal_txs: { tx_internal_txs: {
path: '/api/v2/transactions/:hash/internal-transactions', path: '/api/v2/transactions/:hash/internal-transactions',
pathParams: [ 'hash' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'transaction_hash' as const, 'index' as const, 'transaction_index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'transaction_hash' as const, 'index' as const, 'transaction_index' as const ],
filterFields: [ ], filterFields: [ ],
}, },
tx_logs: { tx_logs: {
path: '/api/v2/transactions/:hash/logs', path: '/api/v2/transactions/:hash/logs',
pathParams: [ 'hash' as const ],
paginationFields: [ 'items_count' as const, 'transaction_hash' as const, 'index' as const ], paginationFields: [ 'items_count' as const, 'transaction_hash' as const, 'index' as const ],
filterFields: [ ], filterFields: [ ],
}, },
tx_token_transfers: { tx_token_transfers: {
path: '/api/v2/transactions/:hash/token-transfers', path: '/api/v2/transactions/:hash/token-transfers',
pathParams: [ 'hash' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'transaction_hash' as const, 'index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'transaction_hash' as const, 'index' as const ],
filterFields: [ 'type' as const ], filterFields: [ 'type' as const ],
}, },
tx_raw_trace: { tx_raw_trace: {
path: '/api/v2/transactions/:hash/raw-trace', path: '/api/v2/transactions/:hash/raw-trace',
pathParams: [ 'hash' as const ],
}, },
// ADDRESSES // ADDRESSES
...@@ -146,9 +161,11 @@ export const RESOURCES = { ...@@ -146,9 +161,11 @@ export const RESOURCES = {
// ADDRESS // ADDRESS
address: { address: {
path: '/api/v2/addresses/:hash', path: '/api/v2/addresses/:hash',
pathParams: [ 'hash' as const ],
}, },
address_counters: { address_counters: {
path: '/api/v2/addresses/:hash/counters', path: '/api/v2/addresses/:hash/counters',
pathParams: [ 'hash' as const ],
}, },
// this resource doesn't have pagination, so causing huge problems on some addresses page // this resource doesn't have pagination, so causing huge problems on some addresses page
// address_token_balances: { // address_token_balances: {
...@@ -156,39 +173,47 @@ export const RESOURCES = { ...@@ -156,39 +173,47 @@ export const RESOURCES = {
// }, // },
address_txs: { address_txs: {
path: '/api/v2/addresses/:hash/transactions', path: '/api/v2/addresses/:hash/transactions',
pathParams: [ 'hash' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ],
filterFields: [ 'filter' as const ], filterFields: [ 'filter' as const ],
}, },
address_internal_txs: { address_internal_txs: {
path: '/api/v2/addresses/:hash/internal-transactions', path: '/api/v2/addresses/:hash/internal-transactions',
pathParams: [ 'hash' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const, 'transaction_index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const, 'transaction_index' as const ],
filterFields: [ 'filter' as const ], filterFields: [ 'filter' as const ],
}, },
address_token_transfers: { address_token_transfers: {
path: '/api/v2/addresses/:hash/token-transfers', path: '/api/v2/addresses/:hash/token-transfers',
pathParams: [ 'hash' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const, 'transaction_index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const, 'transaction_index' as const ],
filterFields: [ 'filter' as const, 'type' as const, 'token_hash' as const ], filterFields: [ 'filter' as const, 'type' as const, 'token_hash' as const ],
}, },
address_blocks_validated: { address_blocks_validated: {
path: '/api/v2/addresses/:hash/blocks-validated', path: '/api/v2/addresses/:hash/blocks-validated',
pathParams: [ 'hash' as const ],
paginationFields: [ 'items_count' as const, 'block_number' as const ], paginationFields: [ 'items_count' as const, 'block_number' as const ],
filterFields: [ ], filterFields: [ ],
}, },
address_coin_balance: { address_coin_balance: {
path: '/api/v2/addresses/:hash/coin-balance-history', path: '/api/v2/addresses/:hash/coin-balance-history',
pathParams: [ 'hash' as const ],
paginationFields: [ 'items_count' as const, 'block_number' as const ], paginationFields: [ 'items_count' as const, 'block_number' as const ],
filterFields: [ ], filterFields: [ ],
}, },
address_coin_balance_chart: { address_coin_balance_chart: {
path: '/api/v2/addresses/:hash/coin-balance-history-by-day', path: '/api/v2/addresses/:hash/coin-balance-history-by-day',
pathParams: [ 'hash' as const ],
}, },
address_logs: { address_logs: {
path: '/api/v2/addresses/:hash/logs', path: '/api/v2/addresses/:hash/logs',
pathParams: [ 'hash' as const ],
paginationFields: [ 'items_count' as const, 'transaction_index' as const, 'index' as const, 'block_number' as const ], paginationFields: [ 'items_count' as const, 'transaction_index' as const, 'index' as const, 'block_number' as const ],
filterFields: [ ], filterFields: [ ],
}, },
address_tokens: { address_tokens: {
path: '/api/v2/addresses/:hash/tokens', path: '/api/v2/addresses/:hash/tokens',
pathParams: [ 'hash' as const ],
paginationFields: [ 'items_count' as const, 'token_name' as const, 'token_type' as const, 'value' as const ], paginationFields: [ 'items_count' as const, 'token_name' as const, 'token_type' as const, 'value' as const ],
filterFields: [ 'type' as const ], filterFields: [ 'type' as const ],
}, },
...@@ -196,48 +221,60 @@ export const RESOURCES = { ...@@ -196,48 +221,60 @@ export const RESOURCES = {
// CONTRACT // CONTRACT
contract: { contract: {
path: '/api/v2/smart-contracts/:hash', path: '/api/v2/smart-contracts/:hash',
pathParams: [ 'hash' as const ],
}, },
contract_methods_read: { contract_methods_read: {
path: '/api/v2/smart-contracts/:hash/methods-read', path: '/api/v2/smart-contracts/:hash/methods-read',
pathParams: [ 'hash' as const ],
}, },
contract_methods_read_proxy: { contract_methods_read_proxy: {
path: '/api/v2/smart-contracts/:hash/methods-read-proxy', path: '/api/v2/smart-contracts/:hash/methods-read-proxy',
pathParams: [ 'hash' as const ],
}, },
contract_method_query: { contract_method_query: {
path: '/api/v2/smart-contracts/:hash/query-read-method', path: '/api/v2/smart-contracts/:hash/query-read-method',
pathParams: [ 'hash' as const ],
}, },
contract_methods_write: { contract_methods_write: {
path: '/api/v2/smart-contracts/:hash/methods-write', path: '/api/v2/smart-contracts/:hash/methods-write',
pathParams: [ 'hash' as const ],
}, },
contract_methods_write_proxy: { contract_methods_write_proxy: {
path: '/api/v2/smart-contracts/:hash/methods-write-proxy', path: '/api/v2/smart-contracts/:hash/methods-write-proxy',
pathParams: [ 'hash' as const ],
}, },
contract_verification_config: { contract_verification_config: {
path: '/api/v2/smart-contracts/verification/config', path: '/api/v2/smart-contracts/verification/config',
}, },
contract_verification_via: { contract_verification_via: {
path: '/api/v2/smart-contracts/:hash/verification/via/:method', path: '/api/v2/smart-contracts/:hash/verification/via/:method',
pathParams: [ 'hash' as const, 'method' as const ],
}, },
// TOKEN // TOKEN
token: { token: {
path: '/api/v2/tokens/:hash', path: '/api/v2/tokens/:hash',
pathParams: [ 'hash' as const ],
}, },
token_counters: { token_counters: {
path: '/api/v2/tokens/:hash/counters', path: '/api/v2/tokens/:hash/counters',
pathParams: [ 'hash' as const ],
}, },
token_holders: { token_holders: {
path: '/api/v2/tokens/:hash/holders', path: '/api/v2/tokens/:hash/holders',
pathParams: [ 'hash' as const ],
paginationFields: [ 'items_count' as const, 'value' as const ], paginationFields: [ 'items_count' as const, 'value' as const ],
filterFields: [], filterFields: [],
}, },
token_transfers: { token_transfers: {
path: '/api/v2/tokens/:hash/transfers', path: '/api/v2/tokens/:hash/transfers',
pathParams: [ 'hash' as const ],
paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ], paginationFields: [ 'block_number' as const, 'items_count' as const, 'index' as const ],
filterFields: [], filterFields: [],
}, },
token_inventory: { token_inventory: {
path: '/api/v2/tokens/:hash/instances', path: '/api/v2/tokens/:hash/instances',
pathParams: [ 'hash' as const ],
paginationFields: [ 'unique_token' as const ], paginationFields: [ 'unique_token' as const ],
filterFields: [], filterFields: [],
}, },
...@@ -250,9 +287,11 @@ export const RESOURCES = { ...@@ -250,9 +287,11 @@ export const RESOURCES = {
// TOKEN INSTANCE // TOKEN INSTANCE
token_instance: { token_instance: {
path: '/api/v2/tokens/:hash/instances/:id', path: '/api/v2/tokens/:hash/instances/:id',
pathParams: [ 'hash' as const, 'id' as const ],
}, },
token_instance_transfers_count: { token_instance_transfers_count: {
path: '/api/v2/tokens/:hash/instances/:id/transfers-count', path: '/api/v2/tokens/:hash/instances/:id/transfers-count',
pathParams: [ 'hash' as const, 'id' as const ],
}, },
// HOMEPAGE // HOMEPAGE
...@@ -313,6 +352,15 @@ export type ResourcePaginationKey<R extends ResourceName> = typeof RESOURCES[R] ...@@ -313,6 +352,15 @@ export type ResourcePaginationKey<R extends ResourceName> = typeof RESOURCES[R]
export const resourceKey = (x: keyof typeof RESOURCES) => x; export const resourceKey = (x: keyof typeof RESOURCES) => x;
type ResourcePathParamName<Resource extends ResourceName> =
typeof RESOURCES[Resource] extends { pathParams: Array<string> } ?
ArrayElement<typeof RESOURCES[Resource]['pathParams']> :
string;
export type ResourcePathParams<Resource extends ResourceName> = typeof RESOURCES[Resource] extends { pathParams: Array<string> } ?
Record<ResourcePathParamName<Resource>, string | undefined> :
never;
export interface ResourceError<T = unknown> { export interface ResourceError<T = unknown> {
payload?: T; payload?: T;
status: Response['status']; status: Response['status'];
......
...@@ -6,10 +6,10 @@ import useFetch from 'lib/hooks/useFetch'; ...@@ -6,10 +6,10 @@ import useFetch from 'lib/hooks/useFetch';
import buildUrl from './buildUrl'; import buildUrl from './buildUrl';
import { RESOURCES } from './resources'; import { RESOURCES } from './resources';
import type { ApiResource } from './resources'; import type { ApiResource, ResourceName, ResourcePathParams } from './resources';
export interface Params { export interface Params<R extends ResourceName> {
pathParams?: Record<string, string | undefined>; pathParams?: ResourcePathParams<R>;
queryParams?: Record<string, string | Array<string> | number | undefined>; queryParams?: Record<string, string | Array<string> | number | undefined>;
fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal'>; fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal'>;
} }
...@@ -17,12 +17,12 @@ export interface Params { ...@@ -17,12 +17,12 @@ export interface Params {
export default function useApiFetch() { export default function useApiFetch() {
const fetch = useFetch(); const fetch = useFetch();
return React.useCallback(<R extends keyof typeof RESOURCES, SuccessType = unknown, ErrorType = unknown>( return React.useCallback(<R extends ResourceName, SuccessType = unknown, ErrorType = unknown>(
resourceName: R, resourceName: R,
{ pathParams, queryParams, fetchParams }: Params = {}, { pathParams, queryParams, fetchParams }: Params<R> = {},
) => { ) => {
const resource: ApiResource = RESOURCES[resourceName]; const resource: ApiResource = RESOURCES[resourceName];
const url = buildUrl(resource, pathParams, queryParams); const url = buildUrl(resourceName, pathParams, queryParams);
return fetch<SuccessType, ErrorType>(url, { return fetch<SuccessType, ErrorType>(url, {
credentials: 'include', credentials: 'include',
...(resource.endpoint && appConfig.host === 'localhost' ? { ...(resource.endpoint && appConfig.host === 'localhost' ? {
......
...@@ -5,7 +5,7 @@ import type { ResourceError, ResourceName, ResourcePayload } from './resources'; ...@@ -5,7 +5,7 @@ import type { ResourceError, ResourceName, ResourcePayload } from './resources';
import type { Params as ApiFetchParams } from './useApiFetch'; import type { Params as ApiFetchParams } from './useApiFetch';
import useApiFetch from './useApiFetch'; import useApiFetch from './useApiFetch';
export interface Params<R extends ResourceName, E = unknown> extends ApiFetchParams { export interface Params<R extends ResourceName, E = unknown> extends ApiFetchParams<R> {
queryOptions?: Omit<UseQueryOptions<unknown, ResourceError<E>, ResourcePayload<R>>, 'queryKey' | 'queryFn'>; queryOptions?: Omit<UseQueryOptions<unknown, ResourceError<E>, ResourcePayload<R>>, 'queryKey' | 'queryFn'>;
} }
......
import { compile } from 'path-to-regexp'; import { compile } from 'path-to-regexp';
import type { ResourceName } from 'lib/api/resources'; import type { ResourceName, ResourcePathParams } from 'lib/api/resources';
import { RESOURCES } from 'lib/api/resources'; import { RESOURCES } from 'lib/api/resources';
export default function buildApiUrl(resourceName: ResourceName, pathParams?: Record<string, string>) { export default function buildApiUrl<R extends ResourceName>(resourceName: R, pathParams?: ResourcePathParams<R>) {
const resource = RESOURCES[resourceName]; const resource = RESOURCES[resourceName];
return compile('/node-api/proxy/poa/core' + resource.path)(pathParams); return compile('/node-api/proxy/poa/core' + resource.path)(pathParams);
} }
...@@ -15,7 +15,7 @@ const ASSET_URL = 'https://raw.githubusercontent.com/trustwallet/assets/master/b ...@@ -15,7 +15,7 @@ const ASSET_URL = 'https://raw.githubusercontent.com/trustwallet/assets/master/b
const TOKENS_ERC20_API_URL = buildApiUrl('address_tokens', { hash: '1' }) + '?type=ERC-20'; const TOKENS_ERC20_API_URL = buildApiUrl('address_tokens', { hash: '1' }) + '?type=ERC-20';
const TOKENS_ERC721_API_URL = buildApiUrl('address_tokens', { hash: '1' }) + '?type=ERC-721'; const TOKENS_ERC721_API_URL = buildApiUrl('address_tokens', { hash: '1' }) + '?type=ERC-721';
const TOKENS_ER1155_API_URL = buildApiUrl('address_tokens', { hash: '1' }) + '?type=ERC-1155'; const TOKENS_ER1155_API_URL = buildApiUrl('address_tokens', { hash: '1' }) + '?type=ERC-1155';
const ADDRESS_API_URL = buildApiUrl('address', { id: '1' }); const ADDRESS_API_URL = buildApiUrl('address', { hash: '1' });
const hooksConfig = { const hooksConfig = {
router: { router: {
query: { hash: '1' }, query: { hash: '1' },
......
...@@ -8,7 +8,7 @@ import buildApiUrl from 'playwright/utils/buildApiUrl'; ...@@ -8,7 +8,7 @@ import buildApiUrl from 'playwright/utils/buildApiUrl';
import TokenInstanceDetails from './TokenInstanceDetails'; import TokenInstanceDetails from './TokenInstanceDetails';
const API_URL_ADDRESS = buildApiUrl('address', { id: tokenInstanceMock.base.token.address }); const API_URL_ADDRESS = buildApiUrl('address', { hash: tokenInstanceMock.base.token.address });
const API_URL_TOKEN_TRANSFERS_COUNT = buildApiUrl('token_instance_transfers_count', { const API_URL_TOKEN_TRANSFERS_COUNT = buildApiUrl('token_instance_transfers_count', {
id: tokenInstanceMock.base.id, id: tokenInstanceMock.base.id,
hash: tokenInstanceMock.base.token.address, hash: tokenInstanceMock.base.token.address,
......
...@@ -14,7 +14,7 @@ interface Props { ...@@ -14,7 +14,7 @@ interface Props {
const TokenInstanceCreatorAddress = ({ hash }: Props) => { const TokenInstanceCreatorAddress = ({ hash }: Props) => {
const addressQuery = useApiQuery('address', { const addressQuery = useApiQuery('address', {
pathParams: { id: hash }, pathParams: { hash },
}); });
if (addressQuery.isError) { if (addressQuery.isError) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment