diff --git a/.changeset/violet-chairs-yawn.md b/.changeset/violet-chairs-yawn.md new file mode 100644 index 0000000000..cc6002cd22 --- /dev/null +++ b/.changeset/violet-chairs-yawn.md @@ -0,0 +1,6 @@ +--- +'@kadena/graph-client': patch +'@kadena/graph': patch +--- + +Added proper error logging for the graph and client diff --git a/packages/apps/graph-client/src/components/compact-transactions-table/compact-transactions-table.tsx b/packages/apps/graph-client/src/components/compact-transactions-table/compact-transactions-table.tsx index 8d11ba01ad..f277321b7f 100644 --- a/packages/apps/graph-client/src/components/compact-transactions-table/compact-transactions-table.tsx +++ b/packages/apps/graph-client/src/components/compact-transactions-table/compact-transactions-table.tsx @@ -1,8 +1,8 @@ import type { - GetAccountQuery, - GetBlockFromHashQuery, - GetChainAccountQuery, - GetTransactionsQuery, + BlockTransactionsConnection, + ChainModuleAccountTransactionsConnection, + ModuleAccountTransactionsConnection, + QueryTransactionsConnection, } from '@/__generated__/sdk'; import routes from '@constants/routes'; import { Box, Button, ContentHeader, Link, Table } from '@kadena/react-ui'; @@ -13,10 +13,10 @@ interface ICompactTransactionsTableProps { viewAllHref?: string; description?: string; transactions: - | GetAccountQuery['account']['transactions'] - | GetChainAccountQuery['chainAccount']['transactions'] - | GetBlockFromHashQuery['block']['transactions'] - | GetTransactionsQuery['transactions']; + | ModuleAccountTransactionsConnection + | ChainModuleAccountTransactionsConnection + | BlockTransactionsConnection + | QueryTransactionsConnection; } export const CompactTransactionsTable = ( diff --git a/packages/apps/graph-client/src/components/compact-transfers-table/compact-transfers-table.tsx b/packages/apps/graph-client/src/components/compact-transfers-table/compact-transfers-table.tsx index a3b16f6b9d..48de49461f 100644 --- a/packages/apps/graph-client/src/components/compact-transfers-table/compact-transfers-table.tsx +++ b/packages/apps/graph-client/src/components/compact-transfers-table/compact-transfers-table.tsx @@ -1,6 +1,6 @@ import type { - GetAccountQuery, - GetChainAccountQuery, + ChainModuleAccountTransfersConnection, + ModuleAccountTransfersConnection, } from '@/__generated__/sdk'; import routes from '@constants/routes'; import { Box, Button, ContentHeader, Link, Table } from '@kadena/react-ui'; @@ -12,8 +12,9 @@ interface ICompactTransfersTableProps { accountName: string; chainId?: string; transfers: - | GetAccountQuery['account']['transfers'] - | GetChainAccountQuery['chainAccount']['transfers']; + | ModuleAccountTransfersConnection + | ChainModuleAccountTransfersConnection; + description?: string; } diff --git a/packages/apps/graph-client/src/components/error-box/error-box.tsx b/packages/apps/graph-client/src/components/error-box/error-box.tsx new file mode 100644 index 0000000000..18765a69d3 --- /dev/null +++ b/packages/apps/graph-client/src/components/error-box/error-box.tsx @@ -0,0 +1,43 @@ +import type { ApolloError } from '@apollo/client'; +import { Box, Notification } from '@kadena/react-ui'; +import React from 'react'; + +interface IErrorBoxProps { + error: ApolloError; +} + +export const ErrorBox = (props: IErrorBoxProps): JSX.Element => { + const { error } = props; + + let errorTitle = 'Unknown Error Occured'; + let errorMessage = error.message; + let errorExtra; + + if (error.graphQLErrors.length > 0) { + const mainError = error.graphQLErrors[0]; + + if (mainError.extensions) { + errorTitle = (mainError.extensions.message as string) ?? errorTitle; + errorMessage = + (mainError.extensions.description as string) ?? errorMessage; + + if (!mainError.extensions.description) { + errorExtra = JSON.stringify(mainError.extensions.data); + } + } + } + + return ( + + {errorTitle} + + {errorMessage} + {errorExtra !== undefined && ( + <> + + {errorExtra} + + )} + + ); +}; diff --git a/packages/apps/graph-client/src/pages/account/overview/[module]/[account].tsx b/packages/apps/graph-client/src/pages/account/overview/[module]/[account].tsx index 139f9b8946..fe1ec9a1e8 100644 --- a/packages/apps/graph-client/src/pages/account/overview/[module]/[account].tsx +++ b/packages/apps/graph-client/src/pages/account/overview/[module]/[account].tsx @@ -1,6 +1,11 @@ +import type { + ModuleAccountTransactionsConnection, + ModuleAccountTransfersConnection, +} from '@/__generated__/sdk'; import { useGetAccountQuery } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import { ChainModuleAccountTable } from '@components/chain-module-account-table/chain-module-account-table'; import { CompactTransactionsTable } from '@components/compact-transactions-table/compact-transactions-table'; import { CompactTransfersTable } from '@components/compact-transfers-table/compact-transfers-table'; @@ -39,15 +44,18 @@ const Account: React.FC = () => { Retrieving account information... )} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } + {accountQuery?.account && + accountQuery?.account?.totalBalance === 0 && + accountQuery?.account?.chainAccounts.length === 0 && ( + <> + + We could not find any data on this account. Please check the + module and account name. + + + + )} {accountQuery?.account && (
@@ -85,7 +93,10 @@ const Account: React.FC = () => { description="All transfers from or to this account" moduleName={router.query.module as string} accountName={router.query.account as string} - transfers={accountQuery.account.transfers} + transfers={ + accountQuery.account + .transfers as ModuleAccountTransfersConnection + } /> @@ -93,7 +104,10 @@ const Account: React.FC = () => { viewAllHref={`${routes.ACCOUNT_TRANSACTIONS}/${ router.query.module as string }/${router.query.account as string}`} - transactions={accountQuery.account.transactions} + transactions={ + accountQuery.account + .transactions as ModuleAccountTransactionsConnection + } /> diff --git a/packages/apps/graph-client/src/pages/account/overview/[module]/[account]/[chain].tsx b/packages/apps/graph-client/src/pages/account/overview/[module]/[account]/[chain].tsx index 14ef457f39..6dc1482cab 100644 --- a/packages/apps/graph-client/src/pages/account/overview/[module]/[account]/[chain].tsx +++ b/packages/apps/graph-client/src/pages/account/overview/[module]/[account]/[chain].tsx @@ -1,10 +1,15 @@ +import type { + ChainModuleAccountTransactionsConnection, + ChainModuleAccountTransfersConnection, +} from '@/__generated__/sdk'; import { useGetChainAccountQuery } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import { CompactTransactionsTable } from '@components/compact-transactions-table/compact-transactions-table'; import { CompactTransfersTable } from '@components/compact-transfers-table/compact-transfers-table'; import routes from '@constants/routes'; -import { Box, Breadcrumbs, Grid, Notification, Table } from '@kadena/react-ui'; +import { Box, Breadcrumbs, Grid, Table } from '@kadena/react-ui'; import { useRouter } from 'next/router'; import React from 'react'; @@ -46,15 +51,7 @@ const ChainAccount: React.FC = () => { Retrieving account information...
)} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {chainAccountQuery?.chainAccount && (
@@ -116,7 +113,10 @@ const ChainAccount: React.FC = () => { moduleName={router.query.module as string} accountName={router.query.account as string} chainId={router.query.chain as string} - transfers={chainAccountQuery.chainAccount.transfers} + transfers={ + chainAccountQuery.chainAccount + .transfers as ChainModuleAccountTransfersConnection + } /> @@ -126,7 +126,10 @@ const ChainAccount: React.FC = () => { }/${router.query.account as string}?chain=${ router.query.chain as string }`} - transactions={chainAccountQuery.chainAccount.transactions} + transactions={ + chainAccountQuery.chainAccount + .transactions as ChainModuleAccountTransactionsConnection + } /> diff --git a/packages/apps/graph-client/src/pages/account/transactions/[module]/[account].tsx b/packages/apps/graph-client/src/pages/account/transactions/[module]/[account].tsx index f519facfe4..8b2bb0bff1 100644 --- a/packages/apps/graph-client/src/pages/account/transactions/[module]/[account].tsx +++ b/packages/apps/graph-client/src/pages/account/transactions/[module]/[account].tsx @@ -1,9 +1,10 @@ import { useGetTransactionsQuery } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import { ExtendedTransactionsTable } from '@/components/extended-transactions-table/extended-transactions-table'; import routes from '@/constants/routes'; -import { Box, Breadcrumbs, Notification } from '@kadena/react-ui'; +import { Box, Breadcrumbs } from '@kadena/react-ui'; import { useRouter } from 'next/router'; import React from 'react'; @@ -42,15 +43,7 @@ const AccountTransactions: React.FC = () => { Retrieving transactions...
)} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {data?.transactions && ( { Retrieving transfers... )} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {data?.transfers && ( <> diff --git a/packages/apps/graph-client/src/pages/block/overview/[hash].tsx b/packages/apps/graph-client/src/pages/block/overview/[hash].tsx index b142683977..03cefe6c4b 100644 --- a/packages/apps/graph-client/src/pages/block/overview/[hash].tsx +++ b/packages/apps/graph-client/src/pages/block/overview/[hash].tsx @@ -1,20 +1,15 @@ +import type { BlockTransactionsConnection } from '@/__generated__/sdk'; import { useGetBlockFromHashQuery, useGetGraphConfigurationQuery, } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import { CompactTransactionsTable } from '@components/compact-transactions-table/compact-transactions-table'; import { Text } from '@components/text'; import routes from '@constants/routes'; -import { - Accordion, - Box, - Breadcrumbs, - Link, - Notification, - Table, -} from '@kadena/react-ui'; +import { Accordion, Box, Breadcrumbs, Link, Table } from '@kadena/react-ui'; import { useRouter } from 'next/router'; import React from 'react'; @@ -49,15 +44,7 @@ const Block: React.FC = () => { )} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {data?.block && (
@@ -225,7 +212,9 @@ const Block: React.FC = () => { {data.block.transactions.totalCount > 0 && ( )} diff --git a/packages/apps/graph-client/src/pages/block/transactions/[hash].tsx b/packages/apps/graph-client/src/pages/block/transactions/[hash].tsx index 544208d1da..941474910b 100644 --- a/packages/apps/graph-client/src/pages/block/transactions/[hash].tsx +++ b/packages/apps/graph-client/src/pages/block/transactions/[hash].tsx @@ -1,9 +1,10 @@ import { useGetTransactionsQuery } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import { ExtendedTransactionsTable } from '@/components/extended-transactions-table/extended-transactions-table'; import routes from '@/constants/routes'; -import { Box, Breadcrumbs, Notification } from '@kadena/react-ui'; +import { Box, Breadcrumbs } from '@kadena/react-ui'; import { useRouter } from 'next/router'; import React from 'react'; @@ -34,15 +35,7 @@ const BlockTransactions: React.FC = () => { Retrieving transactions...
)} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {data?.transactions && ( { )} - {error && ( - - Unknown error: -
-
- {error.message} -
-
- Check if the Graph server is running. -
- )} + {error && } {eventSubscription?.event && (
diff --git a/packages/apps/graph-client/src/pages/index.tsx b/packages/apps/graph-client/src/pages/index.tsx index 0a649430a6..e0a7fe7e92 100644 --- a/packages/apps/graph-client/src/pages/index.tsx +++ b/packages/apps/graph-client/src/pages/index.tsx @@ -1,5 +1,6 @@ import { Box } from '@kadena/react-ui'; +import type { QueryTransactionsConnection } from '@/__generated__/sdk'; import { useGetBlocksSubscription, useGetRecentHeightsQuery, @@ -71,7 +72,7 @@ const Home: React.FC = () => {
diff --git a/packages/apps/graph-client/src/pages/transactions/[key].tsx b/packages/apps/graph-client/src/pages/transactions/[key].tsx index f7362d27ad..b634eef7d6 100644 --- a/packages/apps/graph-client/src/pages/transactions/[key].tsx +++ b/packages/apps/graph-client/src/pages/transactions/[key].tsx @@ -1,6 +1,7 @@ import { useGetTransactionByRequestKeySubscription } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import routes from '@/constants/routes'; import { formatCode, formatLisp } from '@/utils/formatter'; import { Box, Breadcrumbs, Link, Notification, Table } from '@kadena/react-ui'; @@ -36,15 +37,7 @@ const RequestKey: React.FC = () => { Waiting for request key...
)} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {transactionSubscription?.transaction && (
{/* center content inside the div */} diff --git a/packages/apps/graph-client/src/pages/transactions/index.tsx b/packages/apps/graph-client/src/pages/transactions/index.tsx index 36f6862043..f5aae3a0c7 100644 --- a/packages/apps/graph-client/src/pages/transactions/index.tsx +++ b/packages/apps/graph-client/src/pages/transactions/index.tsx @@ -1,8 +1,9 @@ -import { Box, Breadcrumbs, Notification } from '@kadena/react-ui'; +import { Box, Breadcrumbs } from '@kadena/react-ui'; import { useGetTransactionsQuery } from '@/__generated__/sdk'; import Loader from '@/components/Common/loader/loader'; import { mainStyle } from '@/components/Common/main/styles.css'; +import { ErrorBox } from '@/components/error-box/error-box'; import { ExtendedTransactionsTable } from '@/components/extended-transactions-table/extended-transactions-table'; import routes from '@/constants/routes'; import React from 'react'; @@ -27,15 +28,7 @@ const Transactions: React.FC = () => { Retrieving transactions...
)} - {error && ( - - Unknown error: - - {error.message} - - Check if the Graph server is running. - - )} + {error && } {data?.transactions && ( { moduleName: t.arg.string({ required: true }), }, type: Account, - resolve: async (parent, args) => { + resolve(__parent, args) { return { id: `Account:${args.accountName}`, accountName: args.accountName, diff --git a/packages/apps/graph/src/graph/Query/block.ts b/packages/apps/graph/src/graph/Query/block.ts index 3bea592ea3..8ab0f83260 100644 --- a/packages/apps/graph/src/graph/Query/block.ts +++ b/packages/apps/graph/src/graph/Query/block.ts @@ -1,6 +1,7 @@ +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import type { Debugger } from 'debug'; import _debug from 'debug'; -import { prismaClient } from '../../db/prismaClient'; import { builder } from '../builder'; import Block from '../objects/Block'; @@ -11,23 +12,24 @@ builder.queryField('block', (t) => { args: { hash: t.arg.string({ required: true }), }, - type: Block, + nullable: true, + async resolve(__query, __parent, args) { + try { + log('searching for block with hash:', args.hash); - resolve: async (__query, __parent, { hash }) => { - log('searching for block with hash:', hash); + const block = await prismaClient.block.findUnique({ + where: { + hash: args.hash, + }, + }); - const block = await prismaClient.block.findUnique({ - where: { - hash, - }, - }); + log(`block with hash '${args.hash}' ${block ? '' : 'not'} found`); - log('found block', block); - if (!block) { - throw new Error(`Block not found for hash: ${hash}`); + return block; + } catch (error) { + throw normalizeError(error); } - return block; }, }); }); diff --git a/packages/apps/graph/src/graph/Query/blocksFromHeight.ts b/packages/apps/graph/src/graph/Query/blocksFromHeight.ts index aac1e2f356..7312ffe0fc 100644 --- a/packages/apps/graph/src/graph/Query/blocksFromHeight.ts +++ b/packages/apps/graph/src/graph/Query/blocksFromHeight.ts @@ -1,7 +1,8 @@ +import { prismaClient } from '@db/prismaClient'; +import { dotenv } from '@utils/dotenv'; +import { normalizeError } from '@utils/errors'; import type { Debugger } from 'debug'; import _debug from 'debug'; -import { prismaClient } from '../../db/prismaClient'; -import { dotenv } from '../../utils/dotenv'; import { builder } from '../builder'; import Block from '../objects/Block'; @@ -13,36 +14,38 @@ builder.queryField('blocksFromHeight', (t) => { startHeight: t.arg.int({ required: true }), chainIds: t.arg.intList({ required: false }), }, - type: [Block], - - resolve: async ( + async resolve( __query, __parent, { startHeight, chainIds = Array.from(new Array(dotenv.CHAIN_COUNT)).map((__, i) => i), }, - ) => { - const blocksFromHeight = await prismaClient.block.findMany({ - where: { - AND: [ - { - height: { - gte: startHeight, + ) { + try { + const blocksFromHeight = await prismaClient.block.findMany({ + where: { + AND: [ + { + height: { + gte: startHeight, + }, }, - }, - { - chainId: { - in: chainIds as number[], + { + chainId: { + in: chainIds as number[], + }, }, - }, - ], - }, - }); + ], + }, + }); - log("found '%s' blocks", blocksFromHeight.length); - return blocksFromHeight; + log("found '%s' blocks", blocksFromHeight.length); + return blocksFromHeight; + } catch (error) { + throw normalizeError(error); + } }, }); }); diff --git a/packages/apps/graph/src/graph/Query/chainAccount.ts b/packages/apps/graph/src/graph/Query/chainAccount.ts index 753cf6bc5f..2ea1f94352 100644 --- a/packages/apps/graph/src/graph/Query/chainAccount.ts +++ b/packages/apps/graph/src/graph/Query/chainAccount.ts @@ -1,4 +1,5 @@ -import { getAccountDetails } from '../../services/node-service'; +import { getAccountDetails } from '@services/node-service'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; import ChainModuleAccount from '../objects/ChainModuleAccount'; @@ -10,25 +11,32 @@ builder.queryField('chainAccount', (t) => { chainId: t.arg.string({ required: true }), }, type: ChainModuleAccount, - resolve: async (parent, args) => { - const accountDetails = await getAccountDetails( - args.moduleName, - args.accountName, - args.chainId, - ); + nullable: true, + async resolve(__parent, args) { + try { + const accountDetails = await getAccountDetails( + args.moduleName, + args.accountName, + args.chainId, + ); - return { - chainId: args.chainId, - accountName: args.accountName, - moduleName: args.moduleName, - guard: { - keys: accountDetails.guard.keys, - predicate: accountDetails.guard.pred, - }, - balance: accountDetails.balance, - transactions: [], - transfers: [], - }; + return accountDetails + ? { + chainId: args.chainId, + accountName: args.accountName, + moduleName: args.moduleName, + guard: { + keys: accountDetails.guard.keys, + predicate: accountDetails.guard.pred, + }, + balance: accountDetails.balance, + transactions: [], + transfers: [], + } + : null; + } catch (error) { + throw normalizeError(error); + } }, }); }); diff --git a/packages/apps/graph/src/graph/Query/completedBlockHeights.ts b/packages/apps/graph/src/graph/Query/completedBlockHeights.ts index 14ec7cd317..7b5418cb6d 100644 --- a/packages/apps/graph/src/graph/Query/completedBlockHeights.ts +++ b/packages/apps/graph/src/graph/Query/completedBlockHeights.ts @@ -1,7 +1,8 @@ +import { prismaClient } from '@db/prismaClient'; +import { dotenv } from '@utils/dotenv'; +import { normalizeError } from '@utils/errors'; import type { Debugger } from 'debug'; import _debug from 'debug'; -import { prismaClient } from '../../db/prismaClient'; -import { dotenv } from '../../utils/dotenv'; import { builder } from '../builder'; import Block from '../objects/Block'; @@ -17,13 +18,14 @@ builder.queryField('completedBlockHeights', (t) => { type: [Block], - resolve: async ( + async resolve( __query, __parent, { completedHeights: onlyCompleted = false, heightCount = 3 }, - ) => { - if (onlyCompleted === true) { - const completedHeights = (await prismaClient.$queryRaw` + ) { + try { + if (onlyCompleted === true) { + const completedHeights = (await prismaClient.$queryRaw` SELECT height FROM blocks b GROUP BY height @@ -33,36 +35,36 @@ builder.queryField('completedBlockHeights', (t) => { LIMIT ${heightCount} `) as { height: number }[]; - log("found '%s' blocks", completedHeights.length); + log("found '%s' blocks", completedHeights.length); - if (completedHeights.length > 0) { - return prismaClient.block.findMany({ - where: { - AND: [ - { - OR: [ - { - height: { - in: completedHeights.map((h) => h.height), + if (completedHeights.length > 0) { + return prismaClient.block.findMany({ + where: { + AND: [ + { + OR: [ + { + height: { + in: completedHeights.map((h) => h.height), + }, }, - }, - { - height: { - gt: completedHeights[0].height, + { + height: { + gt: completedHeights[0].height, + }, }, - }, - ], - }, - ], - }, - }); + ], + }, + ], + }, + }); + } } - } - return prismaClient.block.findMany({ - where: { - height: { - in: await prismaClient.$queryRaw` + return prismaClient.block.findMany({ + where: { + height: { + in: await prismaClient.$queryRaw` SELECT height, COUNT(*) FROM blocks b GROUP BY height @@ -71,9 +73,12 @@ builder.queryField('completedBlockHeights', (t) => { ORDER BY height DESC LIMIT ${heightCount} `, + }, }, - }, - }); + }); + } catch (error) { + throw normalizeError(error); + } }, }); }); diff --git a/packages/apps/graph/src/graph/Query/lastBlockHeight.ts b/packages/apps/graph/src/graph/Query/lastBlockHeight.ts index 4fcb0f8307..488ff3d341 100644 --- a/packages/apps/graph/src/graph/Query/lastBlockHeight.ts +++ b/packages/apps/graph/src/graph/Query/lastBlockHeight.ts @@ -1,63 +1,29 @@ -// schema: createSchema({ -// typeDefs: [ -// BigIntTypeDefinition, -// DateTypeDefinition, -// PositiveFloatTypeDefinition, -// loadFileAsString('./schema.graphql'), -// ], -import { PrismaClient } from '@prisma/client'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import type { Debugger } from 'debug'; import _debug from 'debug'; import { builder } from '../builder'; const log: Debugger = _debug('graph:Query:lastBlockHeight'); -// resolvers: { -// Query: { -// hello: (_, args) => { -// return { -// id: '1', -// name: 'hello', -// }; -// }, -// lastBlockHeight: async (parent, args, context) => { -// const lastBlock = await context.prisma.blocks.findFirst({ -// orderBy: { -// height: 'desc', -// }, -// }); -// return lastBlock?.height; -// }, -// }, - -// Block: { -// chainid: (parent) => BigInt(parent.chainid), -// height: (parent) => BigInt(parent.height), -// }, - -// Subscription: { -// newBlocks: { -// subscribe: () => pubsub.subscribe('NEW_BLOCKS'), -// resolve: (payload) => payload, -// }, -// }, -// }, -// }), - builder.queryField('lastBlockHeight', (t) => { return t.field({ type: 'BigInt', nullable: true, - resolve: async () => { - const lastBlock = await new PrismaClient().block.findFirst({ - orderBy: { - height: 'desc', - }, - }); + async resolve() { + try { + const lastBlock = await prismaClient.block.findFirst({ + orderBy: { + height: 'desc', + }, + }); - log('lastBlock found:', lastBlock?.height); + log('lastBlock found:', lastBlock?.height); - return lastBlock?.height; + return lastBlock?.height; + } catch (error) { + throw normalizeError(error); + } }, }); }); diff --git a/packages/apps/graph/src/graph/Query/pactQuery.ts b/packages/apps/graph/src/graph/Query/pactQuery.ts index 5f1356b5b7..6f3bc88011 100644 --- a/packages/apps/graph/src/graph/Query/pactQuery.ts +++ b/packages/apps/graph/src/graph/Query/pactQuery.ts @@ -1,7 +1,6 @@ -import type { ChainId } from '@kadena/client'; -import { Pact } from '@kadena/client'; -import { devnetConfig } from '../../scripts/devnet/config'; -import { dirtyRead } from '../../scripts/devnet/helper'; +import type { CommandData } from '@services/node-service'; +import { sendRawQuery } from '@services/node-service'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; const PactData = builder.inputType('PactQueryData', { @@ -25,29 +24,19 @@ builder.queryField('pactQueries', (t) => { args: { pactQuery: t.arg({ type: [PactQuery], required: true }), }, - resolve: async (parent, args, context, info) => { - const result = args.pactQuery.map(async (query) => { - const transaction = Pact.builder - .execution(query.code) - .setMeta({ - chainId: query.chainId as ChainId, - }) - .setNetworkId(devnetConfig.NETWORK_ID); - - query.data?.forEach((data) => { - transaction.addData(data.key, data.value); - }); - - const response = await dirtyRead(transaction.createTransaction()); - - if (response.result.status === 'failure') { - return String(response.result.status); - } - - return JSON.stringify(response.result.data); - }); - - return result; + async resolve(__parent, args) { + try { + return args.pactQuery.map( + async (query) => + await sendRawQuery( + query.code, + query.chainId, + query.data as CommandData[], + ), + ); + } catch (error) { + throw normalizeError(error); + } }, }); }); @@ -58,25 +47,16 @@ builder.queryField('pactQuery', (t) => { args: { pactQuery: t.arg({ type: PactQuery, required: true }), }, - resolve: async (parent, args, context, info) => { - const transaction = Pact.builder - .execution(args.pactQuery.code) - .setMeta({ - chainId: args.pactQuery.chainId as ChainId, - }) - .setNetworkId(devnetConfig.NETWORK_ID); - - args.pactQuery.data?.forEach((data) => { - transaction.addData(data.key, data.value); - }); - - const response = await dirtyRead(transaction.createTransaction()); - - if (response.result.status === 'failure') { - return String(response.result.status); + async resolve(__parent, args) { + try { + return await sendRawQuery( + args.pactQuery.code, + args.pactQuery.chainId, + args.pactQuery.data as CommandData[], + ); + } catch (error) { + throw normalizeError(error); } - - return JSON.stringify(response.result.data); }, }); }); diff --git a/packages/apps/graph/src/graph/Query/transactions.ts b/packages/apps/graph/src/graph/Query/transactions.ts index bf8642b245..79017b7a8c 100644 --- a/packages/apps/graph/src/graph/Query/transactions.ts +++ b/packages/apps/graph/src/graph/Query/transactions.ts @@ -1,5 +1,6 @@ +import { prismaClient } from '@db/prismaClient'; import type { Prisma } from '@prisma/client'; -import { prismaClient } from '../../db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; builder.queryField('transactions', (t) => { @@ -12,25 +13,31 @@ builder.queryField('transactions', (t) => { }, type: 'Transaction', cursor: 'blockHash_requestKey', - - totalCount(parent, args, context, info) { - return prismaClient.transaction.count({ - where: generateTransactionFilter(args), - }); + async totalCount(__parent, args) { + try { + return await prismaClient.transaction.count({ + where: generateTransactionFilter(args), + }); + } catch (error) { + throw normalizeError(error); + } }, + async resolve(query, __parent, args) { + try { + const whereFilter = generateTransactionFilter(args); - resolve: (query, parent, args) => { - const whereFilter = generateTransactionFilter(args); - - return prismaClient.transaction.findMany({ - ...query, - where: { - ...whereFilter, - }, - orderBy: { - height: 'desc', - }, - }); + return await prismaClient.transaction.findMany({ + ...query, + where: { + ...whereFilter, + }, + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }); }); diff --git a/packages/apps/graph/src/graph/Query/transfers.ts b/packages/apps/graph/src/graph/Query/transfers.ts index abf0670cf9..e8af1b671a 100644 --- a/packages/apps/graph/src/graph/Query/transfers.ts +++ b/packages/apps/graph/src/graph/Query/transfers.ts @@ -1,4 +1,5 @@ -import { prismaClient } from '../../db/prismaClient'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; builder.queryField('transfers', (t) => { @@ -10,25 +11,29 @@ builder.queryField('transfers', (t) => { }, type: 'Transfer', cursor: 'blockHash_chainId_orderIndex_moduleHash_requestKey', - resolve: async (query, parent, args) => { - return prismaClient.transfer.findMany({ - ...query, - where: { - OR: [ - { - senderAccount: args.accountName, - }, - { - receiverAccount: args.accountName, - }, - ], - moduleName: args.moduleName, - ...(args.chainId && { chainId: parseInt(args.chainId) }), - }, - orderBy: { - height: 'desc', - }, - }); + async resolve(query, __parent, args) { + try { + return await prismaClient.transfer.findMany({ + ...query, + where: { + OR: [ + { + senderAccount: args.accountName, + }, + { + receiverAccount: args.accountName, + }, + ], + moduleName: args.moduleName, + ...(args.chainId && { chainId: parseInt(args.chainId) }), + }, + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }); }); diff --git a/packages/apps/graph/src/graph/data-loaders/account-details.ts b/packages/apps/graph/src/graph/data-loaders/account-details.ts index c26a657613..d6b84866bb 100644 --- a/packages/apps/graph/src/graph/data-loaders/account-details.ts +++ b/packages/apps/graph/src/graph/data-loaders/account-details.ts @@ -1,5 +1,5 @@ +import { getAccountDetails } from '@services/node-service'; import DataLoader from 'dataloader'; -import { getAccountDetails } from '../../services/node-service'; interface AccountDetailsKey { moduleName: string; diff --git a/packages/apps/graph/src/graph/objects/Block.ts b/packages/apps/graph/src/graph/objects/Block.ts index f9f7d62611..89b850be33 100644 --- a/packages/apps/graph/src/graph/objects/Block.ts +++ b/packages/apps/graph/src/graph/objects/Block.ts @@ -1,5 +1,6 @@ -import { prismaClient } from '../../db/prismaClient'; -import { dotenv } from '../../utils/dotenv'; +import { prismaClient } from '@db/prismaClient'; +import { dotenv } from '@utils/dotenv'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; export default builder.prismaNode('Block', { @@ -20,18 +21,21 @@ export default builder.prismaNode('Block', { parent: t.prismaField({ type: 'Block', nullable: true, - resolve(query, parent, args, context, info) { - return prismaClient.block.findUnique({ - where: { - hash: parent.parentBlockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.block.findUnique({ + where: { + hash: parent.parentBlockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), - parentHash: t.string({ nullable: true, - resolve: (parent, args, context, info) => { + resolve(parent) { // Access the parent block's hash from the parent object return parent.parentBlockHash; }, @@ -41,41 +45,57 @@ export default builder.prismaNode('Block', { transactions: t.prismaConnection({ type: 'Transaction', cursor: 'blockHash_requestKey', - async totalCount(parent, args, context, info) { - return prismaClient.transaction.count({ - where: { - blockHash: parent.hash, - }, - }); + async totalCount(parent) { + try { + return await prismaClient.transaction.count({ + where: { + blockHash: parent.hash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, - resolve: (query, parent, args, context, info) => { - return prismaClient.transaction.findMany({ - ...query, - where: { - blockHash: parent.hash, - }, - orderBy: { - height: 'desc', - }, - }); + async resolve(query, parent) { + try { + return await prismaClient.transaction.findMany({ + ...query, + where: { + blockHash: parent.hash, + }, + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), minerKeys: t.prismaField({ type: ['MinerKey'], nullable: true, - resolve(query, parent, args, context, info) { - return prismaClient.minerKey.findMany({ - where: { - blockHash: parent.hash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.minerKey.findMany({ + where: { + blockHash: parent.hash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), confirmationDepth: t.int({ - resolve: async (parent, args, context, info) => { - return getConfirmationDepth(parent.hash); + async resolve(parent) { + try { + return await getConfirmationDepth(parent.hash); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/graph/objects/ChainModuleAccount.ts b/packages/apps/graph/src/graph/objects/ChainModuleAccount.ts index d17488d1d8..b2b8184c56 100644 --- a/packages/apps/graph/src/graph/objects/ChainModuleAccount.ts +++ b/packages/apps/graph/src/graph/objects/ChainModuleAccount.ts @@ -1,4 +1,5 @@ -import { prismaClient } from '../../db/prismaClient'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; import { accountDetailsLoader } from '../data-loaders/account-details'; @@ -9,7 +10,7 @@ export default builder.objectType('ChainModuleAccount', { moduleName: t.exposeString('moduleName'), guard: t.field({ type: 'Guard', - resolve: async (parent, args) => { + async resolve(parent) { const accountDetails = await accountDetailsLoader.load({ moduleName: parent.moduleName, accountName: parent.accountName, @@ -26,46 +27,54 @@ export default builder.objectType('ChainModuleAccount', { transactions: t.prismaConnection({ type: 'Transaction', cursor: 'blockHash_requestKey', - resolve: (query, parent) => { - return prismaClient.transaction.findMany({ - ...query, - where: { - senderAccount: parent.accountName, - events: { - some: { - moduleName: parent.moduleName, + async resolve(query, parent) { + try { + return await prismaClient.transaction.findMany({ + ...query, + where: { + senderAccount: parent.accountName, + events: { + some: { + moduleName: parent.moduleName, + }, }, + chainId: parseInt(parent.chainId), }, - chainId: parseInt(parent.chainId), - }, - orderBy: { - height: 'desc', - }, - }); + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), transfers: t.prismaConnection({ type: 'Transfer', cursor: 'blockHash_chainId_orderIndex_moduleHash_requestKey', - resolve: async (query, parent) => { - return prismaClient.transfer.findMany({ - ...query, - where: { - OR: [ - { - senderAccount: parent.accountName, - }, - { - receiverAccount: parent.accountName, - }, - ], - moduleName: parent.moduleName, - chainId: parseInt(parent.chainId), - }, - orderBy: { - height: 'desc', - }, - }); + async resolve(query, parent) { + try { + return await prismaClient.transfer.findMany({ + ...query, + where: { + OR: [ + { + senderAccount: parent.accountName, + }, + { + receiverAccount: parent.accountName, + }, + ], + moduleName: parent.moduleName, + chainId: parseInt(parent.chainId), + }, + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/graph/objects/Event.ts b/packages/apps/graph/src/graph/objects/Event.ts index 4feb47ef87..2087bd35cc 100644 --- a/packages/apps/graph/src/graph/objects/Event.ts +++ b/packages/apps/graph/src/graph/objects/Event.ts @@ -1,4 +1,5 @@ -import { prismaClient } from '../../db/prismaClient'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; export default builder.prismaNode('Event', { @@ -18,28 +19,35 @@ export default builder.prismaNode('Event', { transaction: t.prismaField({ type: 'Transaction', nullable: true, - resolve(query, parent, args, context, info) { - return prismaClient.transaction.findUnique({ - where: { - blockHash_requestKey: { - blockHash: parent.blockHash, - requestKey: parent.requestKey, + async resolve(__query, parent) { + try { + return await prismaClient.transaction.findUnique({ + where: { + blockHash_requestKey: { + blockHash: parent.blockHash, + requestKey: parent.requestKey, + }, }, - }, - }); + }); + } catch (error) { + throw normalizeError(error); + } }, }), block: t.prismaField({ type: 'Block', nullable: false, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.block.findUniqueOrThrow({ - where: { - hash: parent.blockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.block.findUniqueOrThrow({ + where: { + hash: parent.blockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/graph/objects/MinerKey.ts b/packages/apps/graph/src/graph/objects/MinerKey.ts index 31b44ec27d..0be53d37a0 100644 --- a/packages/apps/graph/src/graph/objects/MinerKey.ts +++ b/packages/apps/graph/src/graph/objects/MinerKey.ts @@ -1,4 +1,5 @@ -import { prismaClient } from '../../db/prismaClient'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; export default builder.prismaNode('MinerKey', { @@ -12,13 +13,16 @@ export default builder.prismaNode('MinerKey', { block: t.prismaField({ type: 'Block', nullable: false, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.block.findUniqueOrThrow({ - where: { - hash: parent.blockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.block.findUniqueOrThrow({ + where: { + hash: parent.blockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/graph/objects/ModuleAccount.ts b/packages/apps/graph/src/graph/objects/ModuleAccount.ts index 97e00284c6..d9b48faa01 100644 --- a/packages/apps/graph/src/graph/objects/ModuleAccount.ts +++ b/packages/apps/graph/src/graph/objects/ModuleAccount.ts @@ -1,4 +1,5 @@ -import { prismaClient } from '../../db/prismaClient'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; import { accountDetailsLoader } from '../data-loaders/account-details'; import type { ChainModuleAccount } from '../types/graphql-types'; @@ -10,7 +11,7 @@ export default builder.objectType('ModuleAccount', { moduleName: t.exposeString('moduleName'), chainAccounts: t.field({ type: ['ChainModuleAccount'], - resolve: async (parent) => { + async resolve(parent) { const chainAccounts: ChainModuleAccount[] = []; for (let i = 0; i < 20; i++) { @@ -21,19 +22,23 @@ export default builder.objectType('ModuleAccount', { chainId: i.toString(), }); - chainAccounts.push({ - chainId: i.toString(), - accountName: parent.accountName, - moduleName: parent.moduleName, - guard: { - keys: accountDetails.guard.keys, - predicate: accountDetails.guard.pred, - }, - balance: accountDetails.balance, - transactions: [], - transfers: [], - }); - } catch (e) {} + if (accountDetails !== null) { + chainAccounts.push({ + chainId: i.toString(), + accountName: parent.accountName, + moduleName: parent.moduleName, + guard: { + keys: accountDetails.guard.keys, + predicate: accountDetails.guard.pred, + }, + balance: accountDetails.balance, + transactions: [], + transfers: [], + }); + } + } catch (error) { + throw normalizeError(error); + } } return chainAccounts; @@ -41,7 +46,7 @@ export default builder.objectType('ModuleAccount', { }), totalBalance: t.field({ type: 'Decimal', - resolve: async (parent) => { + async resolve(parent) { let totalBalance = 0; for (let i = 0; i < 20; i++) { @@ -52,8 +57,12 @@ export default builder.objectType('ModuleAccount', { chainId: i.toString(), }); - totalBalance += accountDetails.balance; - } catch (e) {} + if (accountDetails !== null) { + totalBalance += accountDetails.balance; + } + } catch (error) { + throw normalizeError(error); + } } return totalBalance; @@ -62,43 +71,51 @@ export default builder.objectType('ModuleAccount', { transactions: t.prismaConnection({ type: 'Transaction', cursor: 'blockHash_requestKey', - resolve: (query, parent) => { - return prismaClient.transaction.findMany({ - ...query, - where: { - senderAccount: parent.accountName, - events: { - some: { - moduleName: parent.moduleName, + async resolve(query, parent) { + try { + return await prismaClient.transaction.findMany({ + ...query, + where: { + senderAccount: parent.accountName, + events: { + some: { + moduleName: parent.moduleName, + }, }, }, - }, - orderBy: { - height: 'desc', - }, - }); + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), transfers: t.prismaConnection({ type: 'Transfer', cursor: 'blockHash_chainId_orderIndex_moduleHash_requestKey', - resolve: async (query, parent) => { - return prismaClient.transfer.findMany({ - ...query, - where: { - OR: [ - { - senderAccount: parent.accountName, - }, - { - receiverAccount: parent.accountName, - }, - ], - }, - orderBy: { - height: 'desc', - }, - }); + async resolve(query, parent) { + try { + return await prismaClient.transfer.findMany({ + ...query, + where: { + OR: [ + { + senderAccount: parent.accountName, + }, + { + receiverAccount: parent.accountName, + }, + ], + }, + orderBy: { + height: 'desc', + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/graph/objects/Signer.ts b/packages/apps/graph/src/graph/objects/Signer.ts index a758008f65..1686656231 100644 --- a/packages/apps/graph/src/graph/objects/Signer.ts +++ b/packages/apps/graph/src/graph/objects/Signer.ts @@ -1,4 +1,4 @@ -import { nullishOrEmpty } from '../../utils/nullishOrEmpty'; +import { nullishOrEmpty } from '@utils/nullishOrEmpty'; import { builder } from '../builder'; export default builder.prismaNode('Signer', { diff --git a/packages/apps/graph/src/graph/objects/Transaction.ts b/packages/apps/graph/src/graph/objects/Transaction.ts index 7a4106dea9..3779e46c34 100644 --- a/packages/apps/graph/src/graph/objects/Transaction.ts +++ b/packages/apps/graph/src/graph/objects/Transaction.ts @@ -1,5 +1,6 @@ -import { prismaClient } from '../../db/prismaClient'; -import { nullishOrEmpty } from '../../utils/nullishOrEmpty'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; +import { nullishOrEmpty } from '@utils/nullishOrEmpty'; import { builder } from '../builder'; export default builder.prismaNode('Transaction', { @@ -73,54 +74,66 @@ export default builder.prismaNode('Transaction', { block: t.prismaField({ type: 'Block', nullable: true, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.block.findUnique({ - where: { - hash: parent.blockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.block.findUnique({ + where: { + hash: parent.blockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), events: t.prismaField({ type: ['Event'], nullable: true, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.event.findMany({ - where: { - requestKey: parent.requestKey, - blockHash: parent.blockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.event.findMany({ + where: { + requestKey: parent.requestKey, + blockHash: parent.blockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), transfers: t.prismaField({ type: ['Transfer'], nullable: true, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.transfer.findMany({ - where: { - requestKey: parent.requestKey, - blockHash: parent.blockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.transfer.findMany({ + where: { + requestKey: parent.requestKey, + blockHash: parent.blockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), signers: t.prismaField({ type: ['Signer'], nullable: true, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.signer.findMany({ - where: { - requestKey: parent.requestKey, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.signer.findMany({ + where: { + requestKey: parent.requestKey, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/graph/objects/Transfer.ts b/packages/apps/graph/src/graph/objects/Transfer.ts index 66db0e91de..9a13cf00d8 100644 --- a/packages/apps/graph/src/graph/objects/Transfer.ts +++ b/packages/apps/graph/src/graph/objects/Transfer.ts @@ -1,4 +1,5 @@ -import { prismaClient } from '../../db/prismaClient'; +import { prismaClient } from '@db/prismaClient'; +import { normalizeError } from '@utils/errors'; import { builder } from '../builder'; export default builder.prismaNode('Transfer', { @@ -19,29 +20,35 @@ export default builder.prismaNode('Transfer', { // relations blocks: t.prismaField({ type: ['Block'], - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.block.findMany({ - where: { - hash: parent.blockHash, - }, - }); + async resolve(__query, parent) { + try { + return await prismaClient.block.findMany({ + where: { + hash: parent.blockHash, + }, + }); + } catch (error) { + throw normalizeError(error); + } }, }), transaction: t.prismaField({ type: 'Transaction', nullable: true, - // eslint-disable-next-line @typescript-eslint/typedef - resolve(query, parent, args, context, info) { - return prismaClient.transaction.findUnique({ - where: { - blockHash_requestKey: { - blockHash: parent.blockHash, - requestKey: parent.requestKey, + async resolve(__query, parent) { + try { + return await prismaClient.transaction.findUnique({ + where: { + blockHash_requestKey: { + blockHash: parent.blockHash, + requestKey: parent.requestKey, + }, }, - }, - }); + }); + } catch (error) { + throw normalizeError(error); + } }, }), }), diff --git a/packages/apps/graph/src/index.ts b/packages/apps/graph/src/index.ts index 0f0397c3bd..a25daae205 100644 --- a/packages/apps/graph/src/index.ts +++ b/packages/apps/graph/src/index.ts @@ -1,23 +1,17 @@ import { createYoga } from 'graphql-yoga'; import 'json-bigint-patch'; +import 'module-alias/register'; import { createServer } from 'node:http'; import './graph'; import { builder } from './graph/builder'; import { writeSchema } from './utils/write-schema'; -// eslint-disable-next-line @rushstack/typedef-var -const schema = builder.toSchema(); - -// eslint-disable-next-line @rushstack/typedef-var -const yoga = createYoga({ - schema, -}); - -// eslint-disable-next-line @rushstack/typedef-var -const server = createServer(yoga); - writeSchema(); -server.listen(4000, () => { +createServer( + createYoga({ + schema: builder.toSchema(), + }), +).listen(4000, () => { console.info('Server is running on http://localhost:4000/graphql'); }); diff --git a/packages/apps/graph/src/services/node-service.ts b/packages/apps/graph/src/services/node-service.ts index e6e96ce8b7..0fc6f835c6 100644 --- a/packages/apps/graph/src/services/node-service.ts +++ b/packages/apps/graph/src/services/node-service.ts @@ -1,6 +1,23 @@ -import type { ChainId, IClient } from '@kadena/client'; +import type { ChainId, IClient, ICommandResult } from '@kadena/client'; import { Pact, createClient } from '@kadena/client'; -import { dotenv } from '../utils/dotenv'; +import { dotenv } from '@utils/dotenv'; + +export class PactCommandError extends Error { + public commandResult: ICommandResult; + public pactError: any; + + constructor(message: string, commandResult: ICommandResult, pactError?: any) { + super(message); + this.commandResult = commandResult; + this.pactError = pactError; + } +} + +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type CommandData = { + key: string; + value: string; +}; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ChainModuleAccountDetails = { @@ -12,7 +29,7 @@ export type ChainModuleAccountDetails = { }; }; -function getClient(chainId: string): IClient { +function getClient(chainId: ChainId): IClient { return createClient( `http://${dotenv.NETWORK_HOST}/chainweb/0.0/${dotenv.NETWORK_ID}/chain/${chainId}/pact`, ); @@ -22,26 +39,32 @@ export async function getAccountDetails( module: string, accountName: string, chainId: string, -): Promise { - const commandResult = await getClient(chainId).dirtyRead( +): Promise { + const commandResult = await getClient(chainId as ChainId).dirtyRead( Pact.builder - .execution( - // @ts-ignore - Pact.modules[module].details(accountName), - ) + .execution(Pact.modules[module as 'fungible-v2'].details(accountName)) .setMeta({ chainId: chainId as ChainId, }) - .setNetworkId(dotenv.NETWORK_ID as string) + .setNetworkId(dotenv.NETWORK_ID) .createTransaction(), ); if (commandResult.result.status !== 'success') { - const error = { - message: 'Failed with error', - result: JSON.stringify(commandResult), - }; - throw error; + // If the account does not exist on a chain, we get a row not found error. + if ( + (commandResult.result.error as any).message?.includes( + 'with-read: row not found', + ) + ) { + return null; + } else { + throw new PactCommandError( + 'Pact Command failed with error', + commandResult, + commandResult.result.error, + ); + } } const result = commandResult.result.data as unknown as any; @@ -52,3 +75,36 @@ export async function getAccountDetails( return result as ChainModuleAccountDetails; } + +export async function sendRawQuery( + code: string, + chainId: string, + data?: CommandData[], +): Promise { + const commandBuilder = Pact.builder + .execution(code) + .setMeta({ + chainId: chainId as ChainId, + }) + .setNetworkId(dotenv.NETWORK_ID); + + if (data) { + data.forEach((data) => { + commandBuilder.addData(data.key, data.value); + }); + } + + const commandResult = await getClient(chainId as ChainId).dirtyRead( + commandBuilder.createTransaction(), + ); + + if (commandResult.result.status !== 'success') { + throw new PactCommandError( + 'Pact Command failed with error', + commandResult, + commandResult.result.error, + ); + } + + return JSON.stringify(commandResult.result.data); +} diff --git a/packages/apps/graph/src/utils/errors.ts b/packages/apps/graph/src/utils/errors.ts new file mode 100644 index 0000000000..717c764fbe --- /dev/null +++ b/packages/apps/graph/src/utils/errors.ts @@ -0,0 +1,80 @@ +import { PrismaClientInitializationError } from '@prisma/client/runtime/library'; +import { GraphQLError } from 'graphql'; +import { PactCommandError } from '../services/node-service'; + +/** + * Checks what type of error it is and returns a normalized GraphQLError with the correct type, message and a description that clearly translates to the user what the error means. + */ +export function normalizeError(error: any): GraphQLError { + if (error instanceof PrismaClientInitializationError) { + return new GraphQLError('Prisma Client Initialization Error', { + extensions: { + type: error.name, + message: error.message, + description: + 'Prisma Client failed to initialize. Are you sure the database is running and reachable?', + data: error.stack, + }, + }); + } + + if (error instanceof PactCommandError) { + let description: string | undefined; + + if (error.pactError?.message.includes('with-read: row not found')) { + description = + 'The requested resource (account, e.g.) was most likely not found.'; + } else if (error.pactError?.message.startsWith('Cannot resolve')) { + description = + 'The requested module or function was most likely not found.'; + } else if ( + (error.commandResult as any).message.includes('Failed reading: mzero') + ) { + description = + 'Empty code was most likely sent to the Chainweb Node. Please check your arguments.'; + } + + return new GraphQLError('Chainweb Node Command Failure', { + extensions: { + type: error.pactError?.type || 'UnknownType', + message: + error.pactError?.message || + (error.commandResult as any).message || + error.message, + description, + data: error.commandResult, + }, + }); + } + + if (error.type === 'system' && error.code === 'ECONNREFUSED') { + return new GraphQLError('Chainweb Node Connection Refused', { + extensions: { + type: error.type, + message: error.message, + description: + 'Chainweb Node connection refused. Are you sure the Chainweb Node is running and reachable?', + data: error.stack, + }, + }); + } + + return new GraphQLError('Unknown error occured.', { + extensions: { + type: 'UnknownError', + message: error.message, + description: 'An unknown error occured. Check the logs for more details.', + data: error.stack, + }, + }); +} + +/** + * Checks if the error is a row not found error. An example case is when we loop over all chainIds to get the account details for a module account. If the module account does not exist on a chain, we get a row not found error. We want to ignore this error and continue with the next chainId. + */ +export function isRowNotFoundError(error: any): boolean { + return ( + error instanceof PactCommandError && + error.pactError?.message?.includes('with-read: row not found') + ); +} diff --git a/packages/apps/graph/tsconfig.json b/packages/apps/graph/tsconfig.json index b7be4301aa..1acc14abc7 100644 --- a/packages/apps/graph/tsconfig.json +++ b/packages/apps/graph/tsconfig.json @@ -2,6 +2,11 @@ "extends": "./node_modules/@kadena-dev/shared-config/tsconfig-base.json", "compilerOptions": { "types": [".kadena/pactjs-generated", "node"], - "target": "ES2020" + "target": "ES2020", + "paths": { + "@db/*": ["./src/db/*"], + "@services/*": ["./src/services/*"], + "@utils/*": ["./src/utils/*"] + } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ad3df10ffa..d9f1e34fe4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -330,6 +330,9 @@ importers: json-bigint-patch: specifier: ~0.0.8 version: 0.0.8 + module-alias: + specifier: ^2.2.3 + version: 2.2.3 prisma: specifier: ^5.1.1 version: 5.1.1 @@ -10834,7 +10837,7 @@ packages: chalk: 4.1.2 debug: 4.3.4(supports-color@5.5.0) loader-utils: 2.0.4 - webpack: 5.88.2(webpack-cli@4.9.2) + webpack: 5.88.2(@swc/core@1.3.80)(esbuild@0.18.20) transitivePeerDependencies: - '@types/node' - less @@ -14460,7 +14463,7 @@ packages: eslint: 8.45.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.23.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.45.0) eslint-plugin-react: 7.31.11(eslint@8.45.0) eslint-plugin-react-hooks: 4.6.0(eslint@8.45.0) @@ -14509,6 +14512,7 @@ packages: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color + dev: false /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.45.0): resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} @@ -14532,7 +14536,6 @@ packages: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: true /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.45.0): resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==} @@ -14545,7 +14548,7 @@ packages: enhanced-resolve: 5.15.0 eslint: 8.45.0 eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) - eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.23.0)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) + eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0) get-tsconfig: 4.6.2 globby: 13.2.2 is-core-module: 2.12.1 @@ -14585,6 +14588,7 @@ packages: eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.23.0)(eslint-plugin-import@2.27.5)(eslint@8.45.0) transitivePeerDependencies: - supports-color + dev: false /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} @@ -14614,7 +14618,6 @@ packages: eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.11)(eslint-plugin-import@2.27.5)(eslint@8.45.0) transitivePeerDependencies: - supports-color - dev: true /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} @@ -14676,6 +14679,7 @@ packages: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color + dev: false /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.11)(eslint-import-resolver-typescript@3.5.5)(eslint@8.45.0): resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==} @@ -14708,7 +14712,6 @@ packages: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true /eslint-plugin-jsx-a11y@6.7.1(eslint@8.45.0): resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==} @@ -19746,6 +19749,10 @@ packages: resolution: {integrity: sha512-HdKewQEREEJgsWnErClfbFoVebze6rGazxFLU/XUyrII8dORfVszN1V0BMRnQSzcgsNNtkX8DHj3nC6cdWE9YQ==} dev: false + /module-alias@2.2.3: + resolution: {integrity: sha512-23g5BFj4zdQL/b6tor7Ji+QY4pEfNH784BMslY9Qb0UnJWRAt+lQGLYmRaM0KDBwIG23ffEBELhZDP2rhi9f/Q==} + dev: false + /moo@0.5.1: resolution: {integrity: sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==} dev: false