-
Notifications
You must be signed in to change notification settings - Fork 15
WIP: Merge ENSApi /api/config endpoint into /api/indexing-status endpoint
#1671
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1f1e3df
e518a43
ac26750
55414e5
2557708
da220f3
098ff6e
f7603b8
1d45940
ba81822
72d7ae7
c3a1579
352be15
f88cc15
709443d
b768b3d
2a2b40f
5edb862
073f642
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import { useQuery } from "@tanstack/react-query"; | ||
|
|
||
| import { useEnsApiProviderOptions } from "@ensnode/ensnode-react"; | ||
|
|
||
| import { useIndexingStatusWithSwr } from "@/components/indexing-status"; | ||
|
|
||
| export function useEnsApiConfig() { | ||
| const ensApiProviderOptions = useEnsApiProviderOptions(); | ||
| const indexingStatus = useIndexingStatusWithSwr(); | ||
|
|
||
| return useQuery({ | ||
| enabled: indexingStatus.isSuccess, | ||
| queryKey: ["swr", ensApiProviderOptions.client.url.href, "config"], | ||
| queryFn: async () => indexingStatus.data?.config, // enabled flag ensures this is only called when indexingStatus.data is available | ||
vercel[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,23 +2,22 @@ | |
|
|
||
| import type { PropsWithChildren } from "react"; | ||
|
|
||
| import { useENSNodeConfig } from "@ensnode/ensnode-react"; | ||
|
|
||
| import { useEnsApiConfig } from "@/components/config/useEnsApiConfig"; | ||
| import { ErrorInfo } from "@/components/error-info"; | ||
| import { LoadingSpinner } from "@/components/loading-spinner"; | ||
|
|
||
| /** | ||
| * Allows consumers to use `useActiveConnection` by blocking rendering until it is available. | ||
| */ | ||
| export function RequireActiveConnection({ children }: PropsWithChildren) { | ||
| const { status, error } = useENSNodeConfig(); | ||
| const ensApiConfig = useEnsApiConfig(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
# First, let's check the useEnsApiConfig hook to see how it's gated
echo "=== Checking useEnsApiConfig implementation ==="
if [ -f "apps/ensadmin/src/components/config/useEnsApiConfig.ts" ]; then
cat -n "apps/ensadmin/src/components/config/useEnsApiConfig.ts"
else
echo "File not found, searching for it..."
find . -name "useEnsApiConfig.ts" -type f
fiRepository: namehash/ensnode Length of output: 837 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Check RequireActiveConnection component
echo "=== Checking RequireActiveConnection implementation ==="
if [ -f "apps/ensadmin/src/components/connections/require-active-connection.tsx" ]; then
cat -n "apps/ensadmin/src/components/connections/require-active-connection.tsx"
else
echo "File not found, searching for it..."
find . -name "require-active-connection.tsx" -type f
fiRepository: namehash/ensnode Length of output: 1284 Error UI is unreachable when indexing status fails during initial load.
🤖 Prompt for AI Agents |
||
|
|
||
| if (status === "pending") return <Loading />; | ||
| if (ensApiConfig.status === "pending") return <Loading />; | ||
|
|
||
| if (status === "error") { | ||
| if (ensApiConfig.status === "error") { | ||
| return ( | ||
| <section className="p-6"> | ||
| <ErrorInfo title="Unable to parse ENSNode Config" description={error.message} /> | ||
| <ErrorInfo title="Failed to connect to ENSApi" description={ensApiConfig.error.message} /> | ||
| </section> | ||
| ); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,75 +7,92 @@ import { useCallback, useMemo } from "react"; | |
| import { | ||
| createIndexingStatusQueryOptions, | ||
| QueryParameter, | ||
| useENSNodeSDKConfig, | ||
| useEnsApiProviderOptions, | ||
| type useIndexingStatus, | ||
| useSwrQuery, | ||
| WithSDKConfigParameter, | ||
| WithEnsApiProviderOptions, | ||
| } from "@ensnode/ensnode-react"; | ||
|
Comment on lines
7
to
14
|
||
| import { | ||
| CrossChainIndexingStatusSnapshotOmnichain, | ||
| createRealtimeIndexingStatusProjection, | ||
| Duration, | ||
| type IndexingStatusRequest, | ||
| IndexingStatusResponseCodes, | ||
| IndexingStatusResponseOk, | ||
| EnsApiIndexingStatusRequest, | ||
| EnsApiIndexingStatusResponseCodes, | ||
| EnsApiIndexingStatusResponseOk, | ||
| EnsApiPublicConfig, | ||
| } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| const DEFAULT_REFETCH_INTERVAL = secondsToMilliseconds(10); | ||
|
|
||
| const REALTIME_PROJECTION_REFRESH_RATE: Duration = 1; | ||
|
|
||
| interface CachableIndexingStatus { | ||
| crossChainIndexingStatusSnapshot: CrossChainIndexingStatusSnapshotOmnichain; | ||
| config: EnsApiPublicConfig; | ||
| } | ||
|
|
||
| interface UseIndexingStatusParameters | ||
| extends IndexingStatusRequest, | ||
| QueryParameter<CrossChainIndexingStatusSnapshotOmnichain> {} | ||
| extends EnsApiIndexingStatusRequest, | ||
| QueryParameter<CachableIndexingStatus> {} | ||
|
|
||
| /** | ||
| * A proxy hook for {@link useIndexingStatus} which applies | ||
| * stale-while-revalidate cache for successful responses. | ||
| */ | ||
| export function useIndexingStatusWithSwr( | ||
| parameters: WithSDKConfigParameter & UseIndexingStatusParameters = {}, | ||
| parameters: WithEnsApiProviderOptions & UseIndexingStatusParameters = {}, | ||
| ) { | ||
| const { config, query = {} } = parameters; | ||
| const _config = useENSNodeSDKConfig(config); | ||
| const { options, query = {} } = parameters; | ||
| const providerOptions = useEnsApiProviderOptions(options); | ||
| const now = useNow({ timeToRefresh: REALTIME_PROJECTION_REFRESH_RATE }); | ||
|
|
||
| const queryOptions = useMemo(() => createIndexingStatusQueryOptions(_config), [_config]); | ||
| const queryOptions = useMemo( | ||
| () => createIndexingStatusQueryOptions(providerOptions), | ||
| [providerOptions], | ||
| ); | ||
| const queryKey = useMemo(() => ["swr", ...queryOptions.queryKey], [queryOptions.queryKey]); | ||
| const queryFn = useCallback( | ||
| async () => | ||
| queryOptions.queryFn().then(async (response) => { | ||
| // An indexing status response was successfully fetched, | ||
| // but the response code contained within the response was not 'ok'. | ||
| // Therefore, throw an error to avoid caching this response. | ||
| if (response.responseCode !== IndexingStatusResponseCodes.Ok) { | ||
| if (response.responseCode !== EnsApiIndexingStatusResponseCodes.Ok) { | ||
| throw new Error( | ||
| "Received Indexing Status response with responseCode other than 'ok' which will not be cached.", | ||
| ); | ||
| } | ||
|
|
||
| // The indexing status snapshot has been fetched and successfully validated for caching. | ||
| // The indexing status snapshot, including ENSApi public config, | ||
| // has been fetched and successfully validated for caching. | ||
| // Therefore, return it so that query cache for `queryOptions.queryKey` will: | ||
| // - Replace the currently cached value (if any) with this new value. | ||
| // - Return this non-null value. | ||
| return response.realtimeProjection.snapshot; | ||
| return { | ||
| crossChainIndexingStatusSnapshot: response.realtimeProjection.snapshot, | ||
| config: response.config, | ||
| } satisfies CachableIndexingStatus; | ||
| }), | ||
| [queryOptions.queryFn], | ||
| ); | ||
|
|
||
| // Call select function to `createRealtimeIndexingStatusProjection` each time | ||
| // `now` is updated. | ||
| const select = useCallback( | ||
| (cachedSnapshot: CrossChainIndexingStatusSnapshotOmnichain): IndexingStatusResponseOk => { | ||
| const realtimeProjection = createRealtimeIndexingStatusProjection(cachedSnapshot, now); | ||
| (cachedResult: CachableIndexingStatus): EnsApiIndexingStatusResponseOk => { | ||
| const realtimeProjection = createRealtimeIndexingStatusProjection( | ||
| cachedResult.crossChainIndexingStatusSnapshot, | ||
| now, | ||
| ); | ||
|
|
||
| // Maintain the original response shape of `IndexingStatusResponse` | ||
| // Maintain the original response shape of `EnsApiIndexingStatusResponseOk` | ||
| // for the consumers. Creating a new projection from the cached snapshot | ||
| // each time `now` is updated should be implementation detail. | ||
| return { | ||
| responseCode: IndexingStatusResponseCodes.Ok, | ||
| responseCode: EnsApiIndexingStatusResponseCodes.Ok, | ||
| realtimeProjection, | ||
| } satisfies IndexingStatusResponseOk; | ||
| config: cachedResult.config, | ||
| } satisfies EnsApiIndexingStatusResponseOk; | ||
| }, | ||
| [now], | ||
| ); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
queryFncan currently returnundefinedbecause of the optional chain (indexingStatus.data?.config). Sinceenabledgates onindexingStatus.isSuccess, you can (and should) returnindexingStatus.data.configdirectly (or throw if unexpectedly missing) to keep the query typed as always returning a config object.