Commit e930020e authored by tom goriunov's avatar tom goriunov Committed by GitHub

REST API doc fixes (#1292)

* pass API port and protocol from config to REST playground

* ENV for hidding RPC links in nav bar

* tweak styles of SwaggerUI for dark color scheme
parent 1db67c5b
...@@ -22,6 +22,8 @@ const socketEndpoint = [ ...@@ -22,6 +22,8 @@ const socketEndpoint = [
const api = Object.freeze({ const api = Object.freeze({
host: apiHost, host: apiHost,
protocol: apiSchema,
port: apiPort,
endpoint: apiEndpoint, endpoint: apiEndpoint,
socket: socketEndpoint, socket: socketEndpoint,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_API_BASE_PATH') || ''), basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_API_BASE_PATH') || ''),
......
import type { NavItemExternal } from 'types/client/navigation-items'; import { NAVIGATION_LINK_IDS, type NavItemExternal, type NavigationLinkId } from 'types/client/navigation-items';
import type { ChainIndicatorId } from 'types/homepage'; import type { ChainIndicatorId } from 'types/homepage';
import type { NetworkExplorer } from 'types/networks'; import type { NetworkExplorer } from 'types/networks';
import * as views from './ui/views'; import * as views from './ui/views';
import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils'; import { getEnvValue, getExternalAssetFilePath, parseEnvJson } from './utils';
const hiddenLinks = (() => {
const parsedValue = parseEnvJson<Array<NavigationLinkId>>(getEnvValue('NEXT_PUBLIC_NAVIGATION_HIDDEN_LINKS')) || [];
if (!Array.isArray(parsedValue)) {
return undefined;
}
const result = NAVIGATION_LINK_IDS.reduce((result, item) => {
result[item] = parsedValue.includes(item);
return result;
}, {} as Record<NavigationLinkId, boolean>);
return result;
})();
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)'; const HOMEPAGE_PLATE_BACKGROUND_DEFAULT = 'radial-gradient(103.03% 103.03% at 0% 0%, rgba(183, 148, 244, 0.8) 0%, rgba(0, 163, 196, 0.8) 100%), var(--chakra-colors-blue-400)';
...@@ -18,6 +33,7 @@ const UI = Object.freeze({ ...@@ -18,6 +33,7 @@ const UI = Object.freeze({
'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON'), 'default': getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON'),
dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK'), dark: getExternalAssetFilePath('NEXT_PUBLIC_NETWORK_ICON_DARK'),
}, },
hiddenLinks,
otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue('NEXT_PUBLIC_OTHER_LINKS')) || [], otherLinks: parseEnvJson<Array<NavItemExternal>>(getEnvValue('NEXT_PUBLIC_OTHER_LINKS')) || [],
featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS'), featuredNetworks: getExternalAssetFilePath('NEXT_PUBLIC_FEATURED_NETWORKS'),
}, },
......
...@@ -12,7 +12,8 @@ import type { AdButlerConfig } from '../../../types/client/adButlerConfig'; ...@@ -12,7 +12,8 @@ import type { AdButlerConfig } from '../../../types/client/adButlerConfig';
import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS } from '../../../types/client/adProviders'; import { SUPPORTED_AD_TEXT_PROVIDERS, SUPPORTED_AD_BANNER_PROVIDERS } from '../../../types/client/adProviders';
import type { AdTextProviders, AdBannerProviders } from '../../../types/client/adProviders'; import type { AdTextProviders, AdBannerProviders } from '../../../types/client/adProviders';
import type { MarketplaceAppOverview } from '../../../types/client/marketplace'; import type { MarketplaceAppOverview } from '../../../types/client/marketplace';
import type { NavItemExternal } from '../../../types/client/navigation-items'; import { NAVIGATION_LINK_IDS } from '../../../types/client/navigation-items';
import type { NavItemExternal, NavigationLinkId } from '../../../types/client/navigation-items';
import type { BridgedTokenChain, TokenBridge } from '../../../types/client/token'; import type { BridgedTokenChain, TokenBridge } from '../../../types/client/token';
import type { WalletType } from '../../../types/client/wallets'; import type { WalletType } from '../../../types/client/wallets';
import { SUPPORTED_WALLETS } from '../../../types/client/wallets'; import { SUPPORTED_WALLETS } from '../../../types/client/wallets';
...@@ -354,6 +355,11 @@ const schema = yup ...@@ -354,6 +355,11 @@ const schema = yup
.transform(replaceQuotes) .transform(replaceQuotes)
.json() .json()
.of(navItemExternalSchema), .of(navItemExternalSchema),
NEXT_PUBLIC_NAVIGATION_HIDDEN_LINKS: yup
.array()
.transform(replaceQuotes)
.json()
.of(yup.string<NavigationLinkId>().oneOf(NAVIGATION_LINK_IDS)),
NEXT_PUBLIC_NETWORK_LOGO: yup.string().test(urlTest), NEXT_PUBLIC_NETWORK_LOGO: yup.string().test(urlTest),
NEXT_PUBLIC_NETWORK_LOGO_DARK: yup.string().test(urlTest), NEXT_PUBLIC_NETWORK_LOGO_DARK: yup.string().test(urlTest),
NEXT_PUBLIC_NETWORK_ICON: yup.string().test(urlTest), NEXT_PUBLIC_NETWORK_ICON: yup.string().test(urlTest),
......
...@@ -110,6 +110,7 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -110,6 +110,7 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_NETWORK_ICON_DARK | `string` | Network icon for dark color mode; if not provided, **inverted** regular icon will be used instead | - | - | `https://placekitten.com/60/60` | | NEXT_PUBLIC_NETWORK_ICON_DARK | `string` | Network icon for dark color mode; if not provided, **inverted** regular icon will be used instead | - | - | `https://placekitten.com/60/60` |
| NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) which contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` | | NEXT_PUBLIC_FEATURED_NETWORKS | `string` | URL of configuration file (`.json` format only) which contains list of featured networks that will be shown in the network menu. See [below](#featured-network-configuration-properties) list of available properties for particular network | - | - | `https://example.com/featured_networks_config.json` |
| NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` | | NEXT_PUBLIC_OTHER_LINKS | `Array<{url: string; text: string}>` | List of links for the "Other" navigation menu | - | - | `[{'url':'https://blockscout.com','text':'Blockscout'}]` |
| NEXT_PUBLIC_NAVIGATION_HIDDEN_LINKS | `Array<LinkId>` | List of external links hidden in the navigation. Supported ids are `eth_rpc_api`, `rpc_api` | - | - | `['eth_rpc_api']` |
#### Featured network configuration properties #### Featured network configuration properties
......
...@@ -120,12 +120,12 @@ export default function useNavItems(): ReturnType { ...@@ -120,12 +120,12 @@ export default function useNavItems(): ReturnType {
icon: graphQLIcon, icon: graphQLIcon,
isActive: pathname === '/graphiql', isActive: pathname === '/graphiql',
} : null, } : null,
{ !config.UI.sidebar.hiddenLinks?.rpc_api && {
text: 'RPC API', text: 'RPC API',
icon: rpcIcon, icon: rpcIcon,
url: 'https://docs.blockscout.com/for-users/api/rpc-endpoints', url: 'https://docs.blockscout.com/for-users/api/rpc-endpoints',
}, },
{ !config.UI.sidebar.hiddenLinks?.eth_rpc_api && {
text: 'Eth RPC API', text: 'Eth RPC API',
icon: rpcIcon, icon: rpcIcon,
url: ' https://docs.blockscout.com/for-users/api/eth-rpc', url: ' https://docs.blockscout.com/for-users/api/eth-rpc',
...@@ -157,7 +157,7 @@ export default function useNavItems(): ReturnType { ...@@ -157,7 +157,7 @@ export default function useNavItems(): ReturnType {
icon: statsIcon, icon: statsIcon,
isActive: pathname === '/stats', isActive: pathname === '/stats',
} : null, } : null,
{ apiNavItems.length > 0 && {
text: 'API', text: 'API',
icon: apiDocsIcon, icon: apiDocsIcon,
isActive: apiNavItems.some(item => isInternalItem(item) && item.isActive), isActive: apiNavItems.some(item => isInternalItem(item) && item.isActive),
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
"lint:eslint": "eslint . --ext .js,.jsx,.ts,.tsx", "lint:eslint": "eslint . --ext .js,.jsx,.ts,.tsx",
"lint:eslint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", "lint:eslint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
"lint:tsc": "tsc -p ./tsconfig.json", "lint:tsc": "tsc -p ./tsconfig.json",
"lint:envs-validator:dev": "cd ./deploy/tools/envs-validator && ./dev.sh",
"prepare": "husky install", "prepare": "husky install",
"format-svg": "svgo -r ./icons", "format-svg": "svgo -r ./icons",
"test:pw": "./tools/scripts/pw.sh", "test:pw": "./tools/scripts/pw.sh",
......
...@@ -28,3 +28,8 @@ export type NavGroupItem = NavItemCommon & { ...@@ -28,3 +28,8 @@ export type NavGroupItem = NavItemCommon & {
isActive?: boolean; isActive?: boolean;
subItems: Array<NavItem> | Array<Array<NavItem>>; subItems: Array<NavItem> | Array<Array<NavItem>>;
} }
import type { ArrayElement } from '../utils';
export const NAVIGATION_LINK_IDS = [ 'rpc_api', 'eth_rpc_api' ] as const;
export type NavigationLinkId = ArrayElement<typeof NAVIGATION_LINK_IDS>;
...@@ -4,7 +4,8 @@ const SwaggerUIReact = dynamic(() => import('swagger-ui-react'), { ...@@ -4,7 +4,8 @@ const SwaggerUIReact = dynamic(() => import('swagger-ui-react'), {
ssr: false, ssr: false,
}); });
import { Box, useColorModeValue } from '@chakra-ui/react'; import type { SystemStyleObject } from '@chakra-ui/react';
import { Box, useColorModeValue, useToken } from '@chakra-ui/react';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
...@@ -28,12 +29,16 @@ const NeverShowInfoPlugin = () => { ...@@ -28,12 +29,16 @@ const NeverShowInfoPlugin = () => {
}; };
const SwaggerUI = () => { const SwaggerUI = () => {
const swaggerStyle = { const mainColor = useColorModeValue('blackAlpha.800', 'whiteAlpha.800');
'.scheme-container, .opblock-tag': { const borderColor = useToken('colors', 'divider');
const mainBgColor = useColorModeValue('blackAlpha.100', 'whiteAlpha.200');
const swaggerStyle: SystemStyleObject = {
'.swagger-ui .scheme-container, .opblock-tag': {
display: 'none', display: 'none',
}, },
'.swagger-ui': { '.swagger-ui': {
color: useColorModeValue('blackAlpha.800', 'whiteAlpha.800'), color: mainColor,
}, },
'.swagger-ui .opblock-summary-control:focus': { '.swagger-ui .opblock-summary-control:focus': {
outline: 'none', outline: 'none',
...@@ -54,15 +59,70 @@ const SwaggerUI = () => { ...@@ -54,15 +59,70 @@ const SwaggerUI = () => {
'.swagger-ui .wrapper': { '.swagger-ui .wrapper': {
padding: 0, padding: 0,
}, },
'.swagger-ui .prop-type': {
color: useColorModeValue('blue.600', 'blue.400'),
},
'.swagger-ui .btn.try-out__btn': {
borderColor: useToken('colors', 'link'),
color: useToken('colors', 'link'),
borderRadius: 'sm',
},
'.swagger-ui .btn.try-out__btn:hover': {
boxShadow: 'none',
borderColor: useToken('colors', 'link_hovered'),
color: useToken('colors', 'link_hovered'),
},
'.swagger-ui .btn.try-out__btn.cancel': {
borderColor: useToken('colors', 'error'),
color: useToken('colors', 'error'),
},
'.swagger-ui .btn.try-out__btn.cancel:hover': {
borderColor: useColorModeValue('red.600', 'red.500'),
color: useColorModeValue('red.500', 'red.400'),
},
// MODELS
'.swagger-ui section.models': {
borderColor: borderColor,
},
'.swagger-ui section.models h4': {
color: mainColor,
},
'.swagger-ui section.models .model-container': {
bgColor: mainBgColor,
},
'.swagger-ui .model-title': {
wordBreak: 'break-all',
color: mainColor,
},
'.swagger-ui .model': {
color: mainColor,
},
'.swagger-ui .model-box-control:focus': {
outline: 'none',
},
'.swagger-ui .model-toggle': {
bgColor: useColorModeValue('transparent', 'whiteAlpha.700'),
borderRadius: 'sm',
},
'.swagger-ui .model .property.primitive': {
color: useToken('colors', 'text_secondary'),
wordBreak: 'break-all',
},
}; };
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
const reqInterceptor = React.useCallback((req: any) => { const reqInterceptor = React.useCallback((req: any) => {
if (!req.loadSpec) { if (!req.loadSpec) {
req.url = req.url.replace(DEFAULT_SERVER, config.api.host); const newUrl = new URL(req.url.replace(DEFAULT_SERVER, config.api.host));
const url = new URL(req.url);
url.protocol = 'https:'; newUrl.protocol = config.api.protocol + ':';
req.url = url.toString();
if (config.api.port) {
newUrl.port = config.api.port;
}
req.url = newUrl.toString();
} }
return req; return req;
}, []); }, []);
......
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