Commit c02fe2cd authored by Igor Stuev's avatar Igor Stuev Committed by GitHub

Merge pull request #659 from blockscout/graphql-1

Graphql
parents 1bd8a059 2e4b7b31
...@@ -40,7 +40,8 @@ NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_PLATE ...@@ -40,7 +40,8 @@ NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_PLATE
NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER__ NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_SHOW_GAS_TRACKER__
NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME__ NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME=__PLACEHOLDER_FOR_NEXT_PUBLIC_HOMEPAGE_SHOW_AVG_BLOCK_TIME__
NEXT_PUBLIC_AD_DOMAIN_WITH_AD=__PLACEHOLDER_FOR_NEXT_PUBLIC_DOMAIN_WITH_AD__ NEXT_PUBLIC_AD_DOMAIN_WITH_AD=__PLACEHOLDER_FOR_NEXT_PUBLIC_DOMAIN_WITH_AD__
NEXT_PUBLIC_AD_ADBUTLER_ON=__PLACEHOLDER_FORNEXT_PUBLIC_AD_ADBUTLER_ON__ NEXT_PUBLIC_AD_ADBUTLER_ON=__PLACEHOLDER_FOR_NEXT_PUBLIC_AD_ADBUTLER_ON__
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=__PLACEHOLDER_FOR_NEXT_PUBLIC_GRAPHIQL_TRANSACTION__
# api config # api config
NEXT_PUBLIC_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_HOST__ NEXT_PUBLIC_API_HOST=__PLACEHOLDER_FOR_NEXT_PUBLIC_API_HOST__
......
...@@ -81,6 +81,8 @@ The app instance could be customized by passing following variables to NodeJS en ...@@ -81,6 +81,8 @@ The app instance could be customized by passing following variables to NodeJS en
| NEXT_PUBLIC_DOMAIN_WITH_AD | `string` *(optional)* | The domain on which we display ads | `blockscout.com` | | NEXT_PUBLIC_DOMAIN_WITH_AD | `string` *(optional)* | The domain on which we display ads | `blockscout.com` |
| NEXT_PUBLIC_AD_ADBUTLER_ON | `boolean` *(optional)* | Set to true to show Adbutler banner instead of Coinzilla banner | `false` | | NEXT_PUBLIC_AD_ADBUTLER_ON | `boolean` *(optional)* | Set to true to show Adbutler banner instead of Coinzilla banner | `false` |
| NEXT_PUBLIC_API_SPEC_URL | `string` *(optional)* | Spec to be displayed on api-docs page | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` | | NEXT_PUBLIC_API_SPEC_URL | `string` *(optional)* | Spec to be displayed on api-docs page | `https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml` |
| NEXT_PUBLIC_GRAPHIQL_TRANSACTION | `string` *(optional)* | Txn hash for default query at GraphQl playground page | `0x69e3923eef50eada197c3336d546936d0c994211492c9f947a24c02827568f9f` |
### App configuration ### App configuration
| Variable | Type | Description | Default value | Variable | Type | Description | Default value
......
...@@ -136,6 +136,9 @@ const config = Object.freeze({ ...@@ -136,6 +136,9 @@ const config = Object.freeze({
googleAnalytics: { googleAnalytics: {
propertyId: getEnvValue(process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID), propertyId: getEnvValue(process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID),
}, },
graphQL: {
defaultTxnHash: getEnvValue(process.env.NEXT_PUBLIC_GRAPHIQL_TRANSACTION) || '',
},
}); });
export default config; export default config;
# ui config # ui config
NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Ethereum','url':'https://blockscout.com/eth/mainnet','group':'mainnets','type':'eth_mainnet'},{'title':'Ethereum Classic','url':'https://blockscout.com/etx/mainnet','group':'mainnets','type':'etc_mainnet'},{'title':'Gnosis Chain','url':'https://blockscout.com/xdai/mainnet','group':'mainnets','type':'xdai_mainnet'},{'title':'Astar (EVM)','url':'https://blockscout.com/astar','group':'mainnets','type':'astar'},{'title':'Shiden (EVM)','url':'https://blockscout.com/shiden','group':'mainnets','type':'astar'},{'title':'Klaytn Mainnet (Cypress)','url':'https://klaytn-mainnet.aws-k8s.blockscout.com/','group':'mainnets','type':'klaytn'},{'title':'Goerli','url':'https://blockscout.com/eth/goerli/','group':'testnets','type':'goerli'},{'title':'Optimism Goerli','url':'https://blockscout.com/optimism/goerli/','group':'testnets','type':'optimism_goerli'},{'title':'Optimism Bedrock Alpha','url':'https://blockscout.com/optimism/bedrock-alpha','group':'testnets','type':'optimism_bedrock_alpha'},{'title':'Gnosis Chiado','url':'https://blockscout.com/gnosis/chiado/','group':'testnets','type':'gnosis_chiado'},{'title':'Shibuya (EVM)','url':'https://blockscout.com/shibuya','group':'testnets','type':'shibuya'},{'title':'Optimism Opcraft','url':'https://blockscout.com/optimism/opcraft','group':'other','type':'optimism_opcraft'},{'title':'Optimism on Gnosis Chain','url':'https://blockscout.com/xdai/optimism','group':'other','type':'optimism_gnosis'},{'title':'ARTIS-Σ1','url':'https://blockscout.com/artis/sigma1','group':'other','type':'artis_sigma1'},{'title':'LUKSO L14','url':'https://blockscout.com/lukso/l14','group':'other','type':'lukso_l14'},{'title':'POA','url':'https://blockscout.com/poa/core','group':'other','type':'poa_core'},{'title':'POA Sokol','url':'https://blockscout.com/poa/sokol','group':'other','type':'poa_sokol'}] NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Ethereum','url':'https://blockscout.com/eth/mainnet','group':'mainnets','type':'eth_mainnet'},{'title':'Ethereum Classic','url':'https://blockscout.com/etx/mainnet','group':'mainnets','type':'etc_mainnet'},{'title':'Gnosis Chain','url':'https://blockscout.com/xdai/mainnet','group':'mainnets','type':'xdai_mainnet'},{'title':'Astar (EVM)','url':'https://blockscout.com/astar','group':'mainnets','type':'astar'},{'title':'Shiden (EVM)','url':'https://blockscout.com/shiden','group':'mainnets','type':'astar'},{'title':'Klaytn Mainnet (Cypress)','url':'https://klaytn-mainnet.aws-k8s.blockscout.com/','group':'mainnets','type':'klaytn'},{'title':'Goerli','url':'https://blockscout.com/eth/goerli/','group':'testnets','type':'goerli'},{'title':'Optimism Goerli','url':'https://blockscout.com/optimism/goerli/','group':'testnets','type':'optimism_goerli'},{'title':'Optimism Bedrock Alpha','url':'https://blockscout.com/optimism/bedrock-alpha','group':'testnets','type':'optimism_bedrock_alpha'},{'title':'Gnosis Chiado','url':'https://blockscout.com/gnosis/chiado/','group':'testnets','type':'gnosis_chiado'},{'title':'Shibuya (EVM)','url':'https://blockscout.com/shibuya','group':'testnets','type':'shibuya'},{'title':'Optimism Opcraft','url':'https://blockscout.com/optimism/opcraft','group':'other','type':'optimism_opcraft'},{'title':'Optimism on Gnosis Chain','url':'https://blockscout.com/xdai/optimism','group':'other','type':'optimism_gnosis'},{'title':'ARTIS-Σ1','url':'https://blockscout.com/artis/sigma1','group':'other','type':'artis_sigma1'},{'title':'LUKSO L14','url':'https://blockscout.com/lukso/l14','group':'other','type':'lukso_l14'},{'title':'POA','url':'https://blockscout.com/poa/core','group':'other','type':'poa_core'},{'title':'POA Sokol','url':'https://blockscout.com/poa/sokol','group':'other','type':'poa_sokol'}]
NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/ethereum/goerli/transaction','address':'/ethereum/ethereum/goerli/address'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address'}}] NEXT_PUBLIC_NETWORK_EXPLORERS=[{'title':'Anyblock','baseUrl':'https://explorer.anyblock.tools','paths':{'tx':'/ethereum/ethereum/goerli/transaction','address':'/ethereum/ethereum/goerli/address'}},{'title':'Etherscan','baseUrl':'https://goerli.etherscan.io/','paths':{'tx':'/tx','address':'/address'}}]
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
# network config # network config
NEXT_PUBLIC_NETWORK_NAME=Goerli NEXT_PUBLIC_NETWORK_NAME=Goerli
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Göerli','url':'https://eth-goerli.blockscout.com/','group':'testnets','type':'goerli'},{'title':'Base Göerli','url':'https://l2-goerli.blockscout.com/','group':'testnets','type':'base_goerli'}] NEXT_PUBLIC_FEATURED_NETWORKS=[{'title':'Göerli','url':'https://eth-goerli.blockscout.com/','group':'testnets','type':'goerli'},{'title':'Base Göerli','url':'https://l2-goerli.blockscout.com/','group':'testnets','type':'base_goerli'}]
NEXT_PUBLIC_NETWORK_EXPLORERS= NEXT_PUBLIC_NETWORK_EXPLORERS=
NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT=linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%) NEXT_PUBLIC_HOMEPAGE_PLATE_GRADIENT=linear-gradient(136.9deg,rgb(107 94 236) 1.5%,rgb(0 82 255) 56.84%,rgb(82 62 231) 98.54%)
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
# network config # network config
NEXT_PUBLIC_NETWORK_NAME=Base Göerli NEXT_PUBLIC_NETWORK_NAME=Base Göerli
......
...@@ -316,6 +316,7 @@ frontend: ...@@ -316,6 +316,7 @@ frontend:
- "/api-docs" - "/api-docs"
- "/csv-export" - "/csv-export"
- "/verified-contracts" - "/verified-contracts"
- "/graphiql"
resources: resources:
limits: limits:
...@@ -399,6 +400,8 @@ frontend: ...@@ -399,6 +400,8 @@ frontend:
_default: https://blockscout-main.test.aws-k8s.blockscout.com _default: https://blockscout-main.test.aws-k8s.blockscout.com
NEXT_PUBLIC_L2_WITHDRAWAL_URL: NEXT_PUBLIC_L2_WITHDRAWAL_URL:
_default: https://app.optimism.io/bridge/withdraw _default: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
# enable blockscout-allowance # enable blockscout-allowance
allowance: allowance:
enabled: false enabled: false
......
...@@ -364,6 +364,7 @@ frontend: ...@@ -364,6 +364,7 @@ frontend:
- "/api-docs" - "/api-docs"
- "/csv-export" - "/csv-export"
- "/verified-contracts" - "/verified-contracts"
- "/graphiql"
resources: resources:
limits: limits:
...@@ -442,3 +443,5 @@ frontend: ...@@ -442,3 +443,5 @@ frontend:
_default: https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml _default: https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_IS_TESTNET: NEXT_PUBLIC_IS_TESTNET:
_default: true _default: true
NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
...@@ -39,6 +39,7 @@ frontend: ...@@ -39,6 +39,7 @@ frontend:
- "/api-docs" - "/api-docs"
- "/csv-export" - "/csv-export"
- "/verified-contracts" - "/verified-contracts"
- "/graphiql"
resources: resources:
limits: limits:
...@@ -124,3 +125,5 @@ frontend: ...@@ -124,3 +125,5 @@ frontend:
_default: https://blockscout-main.test.aws-k8s.blockscout.com _default: https://blockscout-main.test.aws-k8s.blockscout.com
NEXT_PUBLIC_L2_WITHDRAWAL_URL: NEXT_PUBLIC_L2_WITHDRAWAL_URL:
_default: https://app.optimism.io/bridge/withdraw _default: https://app.optimism.io/bridge/withdraw
NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0x4a0ed8ddf751a7cb5297f827699117b0f6d21a0b2907594d300dc9fed75c7e62
...@@ -39,6 +39,7 @@ frontend: ...@@ -39,6 +39,7 @@ frontend:
- "/api-docs" - "/api-docs"
- "/csv-export" - "/csv-export"
- "/verified-contracts" - "/verified-contracts"
- "/graphiql"
resources: resources:
limits: limits:
...@@ -120,3 +121,5 @@ frontend: ...@@ -120,3 +121,5 @@ frontend:
_default: https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml _default: https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml
NEXT_PUBLIC_IS_TESTNET: NEXT_PUBLIC_IS_TESTNET:
_default: true _default: true
NEXT_PUBLIC_GRAPHIQL_TRANSACTION:
_default: 0xf7d4972356e6ae44ae948d0cf19ef2beaf0e574c180997e969a2837da15e349d
...@@ -354,6 +354,11 @@ export const RESOURCES = { ...@@ -354,6 +354,11 @@ export const RESOURCES = {
path: '/api/v2/search/check-redirect', path: '/api/v2/search/check-redirect',
}, },
// GraphQL
graphql: {
path: '/graphql',
},
// DEPRECATED // DEPRECATED
old_api: { old_api: {
path: '/api', path: '/api',
......
...@@ -140,7 +140,7 @@ export default function useNavItems(): ReturnType { ...@@ -140,7 +140,7 @@ export default function useNavItems(): ReturnType {
nextRoute: { pathname: '/graphiql' as const }, nextRoute: { pathname: '/graphiql' as const },
icon: graphQLIcon, icon: graphQLIcon,
isActive: pathname === '/graphiql', isActive: pathname === '/graphiql',
isNewUi: false, isNewUi: true,
}, },
{ {
text: 'RPC API', text: 'RPC API',
......
...@@ -51,6 +51,9 @@ ...@@ -51,6 +51,9 @@
"dom-to-image": "^2.6.0", "dom-to-image": "^2.6.0",
"ethers": "^5.7.2", "ethers": "^5.7.2",
"framer-motion": "^6.5.1", "framer-motion": "^6.5.1",
"graphiql": "^2.2.0",
"graphql": "^16.6.0",
"graphql-ws": "^5.11.3",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"lodash": "^4.0.0", "lodash": "^4.0.0",
"next": "12.2.5", "next": "12.2.5",
......
...@@ -10,7 +10,7 @@ import SwaggerUI from 'ui/apiDocs/SwaggerUI'; ...@@ -10,7 +10,7 @@ import SwaggerUI from 'ui/apiDocs/SwaggerUI';
import Page from 'ui/shared/Page/Page'; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle'; import PageTitle from 'ui/shared/Page/PageTitle';
const AppsPage: NextPage = () => { const APIDocsPage: NextPage = () => {
const networkTitle = getNetworkTitle(); const networkTitle = getNetworkTitle();
return ( return (
...@@ -22,7 +22,7 @@ const AppsPage: NextPage = () => { ...@@ -22,7 +22,7 @@ const AppsPage: NextPage = () => {
); );
}; };
export default AppsPage; export default APIDocsPage;
export const getServerSideProps: GetServerSideProps<Props> = async(args) => { export const getServerSideProps: GetServerSideProps<Props> = async(args) => {
if (!appConfig.apiDoc.specUrl) { if (!appConfig.apiDoc.specUrl) {
......
import type { NextPage } from 'next'; import type { NextPage } from 'next';
import dynamic from 'next/dynamic';
const GraphQL = dynamic(() => import('ui/graphQL/GraphQL'), {
loading: () => <ContentLoader/>,
ssr: false,
});
import Head from 'next/head';
import React from 'react';
const GraphQLPage: NextPage = () => { import ContentLoader from 'ui/shared/ContentLoader';
return null; import Page from 'ui/shared/Page/Page';
import PageTitle from 'ui/shared/Page/PageTitle';
const GraphiqlPage: NextPage = () => {
return (
<Page>
<Head><title>Graph Page</title></Head>
<PageTitle text="GraphQL playground"/>
<GraphQL/>
</Page>
);
}; };
export default GraphQLPage; export default GraphiqlPage;
export async function getServerSideProps() { export { getServerSideProps } from 'lib/next/getServerSideProps';
return {
notFound: true,
};
}
/* eslint-disable @typescript-eslint/naming-convention */ /* eslint-disable @typescript-eslint/naming-convention */
const SwaggerUIReact = dynamic(() => import('swagger-ui-react'), { const SwaggerUIReact = dynamic(() => import('swagger-ui-react'), {
loading: () => <Spinner/>, loading: () => <ContentLoader/>,
ssr: false, ssr: false,
}); });
import { Box, Spinner, useColorModeValue } from '@chakra-ui/react'; import { Box, useColorModeValue } from '@chakra-ui/react';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import React from 'react'; import React from 'react';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import ContentLoader from 'ui/shared/ContentLoader';
import 'swagger-ui-react/swagger-ui.css'; import 'swagger-ui-react/swagger-ui.css';
......
import { Box, useColorMode } from '@chakra-ui/react';
import { createGraphiQLFetcher } from '@graphiql/toolkit';
import { GraphiQL } from 'graphiql';
import React from 'react';
import appConfig from 'configs/app/config';
import buildUrl from 'lib/api/buildUrl';
import 'graphiql/graphiql.css';
import isBrowser from 'lib/isBrowser';
const graphQLStyle = {
'.graphiql-container': {
backgroundColor: 'unset',
},
};
const GraphQL = () => {
const { colorMode } = useColorMode();
// colorModeState used as a key to re-render GraphiQL conponent after color mode change
const [ colorModeState, setColorModeState ] = React.useState(colorMode);
React.useEffect(() => {
if (isBrowser()) {
const graphqlTheme = window.localStorage.getItem('graphiql:theme');
if (graphqlTheme !== colorMode) {
window.localStorage.setItem('graphiql:theme', colorMode);
setColorModeState(colorMode);
}
}
}, [ colorMode ]);
const initialQuery = `{
transaction(
hash: "${ appConfig.graphQL.defaultTxnHash }"
) {
hash
blockNumber
value
gasUsed
}
}`;
const graphqlUrl = buildUrl('graphql');
const fetcher = createGraphiQLFetcher({
url: graphqlUrl,
// graphql ws implementation with absinthe plugin is incompatible with graphiql-ws protocol
// or the older one subscriptions-transport-ws
// so we (isstuev & vbaranov) decided to configure playground without subscriptions
// in case of any complaint consider reconfigure the graphql ws server with absinthe_graphql_ws package
// subscriptionUrl: `wss://${appConfig.host}/socket/`,
});
return (
<Box h="100vh" overflowX="scroll" sx={ graphQLStyle }>
<Box h="100vh" minW="900px" sx={ graphQLStyle }>
<GraphiQL fetcher={ fetcher } defaultQuery={ initialQuery } key={ colorModeState }/>
</Box>
</Box>
);
};
export default GraphQL;
This diff is collapsed.
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