-
Notifications
You must be signed in to change notification settings - Fork 16
Integrate ENSDb into ENSApi #1753
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
Changes from all commits
e300888
a1656f6
d53db6d
7ec0fb8
8a056f2
38f30d8
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,5 @@ | ||
| --- | ||
| "ensapi": minor | ||
| --- | ||
|
|
||
| Replaced ENSIndexer Public Config source, from ENSIndexer to ENSDb. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,9 +12,12 @@ ENSINDEXER_URL=http://localhost:42069 | |||||||||||||
| # It should be in the format of `postgresql://<username>:<password>@<host>:<port>/<database>` | ||||||||||||||
| # | ||||||||||||||
| # See https://ensnode.io/ensindexer/usage/configuration/ for additional information. | ||||||||||||||
| # NOTE that ENSApi does NOT need to define DATABASE_SCHEMA, as it is inferred from the connected ENSIndexer's Config. | ||||||||||||||
| DATABASE_URL=postgresql://dbuser:abcd1234@localhost:5432/my_database | ||||||||||||||
|
|
||||||||||||||
| # ENSDb: Database Schema name for ENSIndexer Schema | ||||||||||||||
| # Required. Should match the DATABASE_SCHEMA used by the connected ENSIndexer. | ||||||||||||||
| DATABASE_SCHEMA=public | ||||||||||||||
|
Comment on lines
+17
to
+19
Member
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.
Suggested change
Comment on lines
+17
to
+19
Member
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. Why are we promoting the use of Better to name the default something like Please think more about the mental model of devs who are new to ENSNode: they have a big learning curve and we should be doing everything we can to help them. Please apply this idea everywhere across our monorepo. |
||||||||||||||
|
|
||||||||||||||
| # ENSApi: RPC Configuration | ||||||||||||||
| # Required. ENSApi requires an HTTP RPC to the connected ENSIndexer's ENS Root Chain, which depends | ||||||||||||||
| # on ENSIndexer's NAMESPACE (ex: mainnet, sepolia, ens-test-env). This ENS Root Chain RPC | ||||||||||||||
|
|
||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,12 +19,20 @@ vi.mock("@/lib/logger", () => ({ | |||||||||||||||||||||||||||||||||||||
| error: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| info: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| makeLogger: vi.fn(() => ({ | ||||||||||||||||||||||||||||||||||||||
| error: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| info: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| debug: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| warn: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| trace: vi.fn(), | ||||||||||||||||||||||||||||||||||||||
| })), | ||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const VALID_RPC_URL = "https://eth-sepolia.g.alchemy.com/v2/1234"; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| const BASE_ENV = { | ||||||||||||||||||||||||||||||||||||||
| DATABASE_URL: "postgresql://user:password@localhost:5432/mydb", | ||||||||||||||||||||||||||||||||||||||
| DATABASE_SCHEMA: "public", | ||||||||||||||||||||||||||||||||||||||
|
Member
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. Please see related comment, even for test files. |
||||||||||||||||||||||||||||||||||||||
| ENSINDEXER_URL: "http://localhost:42069", | ||||||||||||||||||||||||||||||||||||||
|
Member
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. Can this be removed now?
Contributor
Author
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. Not yet, but will be removed in PR #1716. |
||||||||||||||||||||||||||||||||||||||
| RPC_URL_1: VALID_RPC_URL, | ||||||||||||||||||||||||||||||||||||||
| } satisfies EnsApiEnvironment; | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -50,6 +58,20 @@ const ENSINDEXER_PUBLIC_CONFIG = { | |||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||
| } satisfies ENSIndexerPublicConfig; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Mock EnsDbClient - must be defined after ENSINDEXER_PUBLIC_CONFIG since vi.mock is hoisted | ||||||||||||||||||||||||||||||||||||||
| // We'll use a simple class mock and configure it in beforeEach | ||||||||||||||||||||||||||||||||||||||
| const mockGetVersion = vi.fn().mockResolvedValue("1.0.0"); | ||||||||||||||||||||||||||||||||||||||
| const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG); | ||||||||||||||||||||||||||||||||||||||
| const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(null); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| vi.mock("@/lib/ensdb-client/ensdb-client", () => ({ | ||||||||||||||||||||||||||||||||||||||
| EnsDbClient: class MockEnsDbClient { | ||||||||||||||||||||||||||||||||||||||
| getVersion = mockGetVersion; | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+69
|
||||||||||||||||||||||||||||||||||||||
| const mockGetVersion = vi.fn().mockResolvedValue("1.0.0"); | |
| const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG); | |
| const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(null); | |
| vi.mock("@/lib/ensdb-client/ensdb-client", () => ({ | |
| EnsDbClient: class MockEnsDbClient { | |
| getVersion = mockGetVersion; | |
| const mockGetEnsDbVersion = vi.fn().mockResolvedValue("1.0.0"); | |
| const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG); | |
| const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(undefined); | |
| vi.mock("@/lib/ensdb-client/ensdb-client", () => ({ | |
| EnsDbClient: class MockEnsDbClient { | |
| getEnsDbVersion = mockGetEnsDbVersion; |
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.
Mock method name getVersion doesn't match actual implementation
The mock defines getVersion (line 69) but the real EnsDbClient exposes getEnsDbVersion(). The EnsDbClientQuery interface is the source of truth here, but the discrepancy can be misleading and won't surface as a type error because the mock class is untyped.
| const mockGetVersion = vi.fn().mockResolvedValue("1.0.0"); | |
| const mockGetEnsIndexerPublicConfig = vi.fn().mockResolvedValue(ENSINDEXER_PUBLIC_CONFIG); | |
| const mockGetIndexingStatusSnapshot = vi.fn().mockResolvedValue(null); | |
| vi.mock("@/lib/ensdb-client/ensdb-client", () => ({ | |
| EnsDbClient: class MockEnsDbClient { | |
| getVersion = mockGetVersion; | |
| getEnsIndexerPublicConfig = mockGetEnsIndexerPublicConfig; | |
| getIndexingStatusSnapshot = mockGetIndexingStatusSnapshot; | |
| }, | |
| })); | |
| vi.mock("@/lib/ensdb-client/ensdb-client", () => ({ | |
| EnsDbClient: class MockEnsDbClient { | |
| getEnsDbVersion = mockGetVersion; | |
| getEnsIndexerPublicConfig = mockGetEnsIndexerPublicConfig; | |
| getIndexingStatusSnapshot = mockGetIndexingStatusSnapshot; | |
| }, | |
| })); |
Copilot
AI
Mar 12, 2026
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.
fetch is still stubbed and configured in these tests, but buildConfigFromEnvironment no longer calls fetch after the switch to EnsDbClient. This makes the test setup misleading and reduces confidence that the new ENSDb path is being exercised; consider removing the fetch mocking and instead asserting EnsDbClient construction + getEnsIndexerPublicConfig() calls.
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.
Please create a follow-up issue to rename DATABASE_URL to ENSDB_URL everywhere.
We should be maximally aligning our terminology everywhere.
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.
Issue created here:
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.
Can this be removed 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.
Not yet, but will be removed in PR #1716.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,5 @@ | ||||||||||||||||||||||
| import packageJson from "@/../package.json" with { type: "json" }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import pRetry from "p-retry"; | ||||||||||||||||||||||
| import { parse as parseConnectionString } from "pg-connection-string"; | ||||||||||||||||||||||
| import { prettifyError, ZodError, z } from "zod/v4"; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -21,7 +20,7 @@ import { | |||||||||||||||||||||
| import { ENSApi_DEFAULT_PORT } from "@/config/defaults"; | ||||||||||||||||||||||
| import type { EnsApiEnvironment } from "@/config/environment"; | ||||||||||||||||||||||
| import { invariant_ensIndexerPublicConfigVersionInfo } from "@/config/validations"; | ||||||||||||||||||||||
| import { fetchENSIndexerConfig } from "@/lib/fetch-ensindexer-config"; | ||||||||||||||||||||||
| import { EnsDbClient } from "@/lib/ensdb-client/ensdb-client"; | ||||||||||||||||||||||
| import logger from "@/lib/logger"; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export const DatabaseUrlSchema = z.string().refine( | ||||||||||||||||||||||
|
|
@@ -77,6 +76,19 @@ const EnsApiConfigSchema = z | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| export type EnsApiConfig = z.infer<typeof EnsApiConfigSchema>; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Builds an instance of {@link EnsDbClient} using environment variables. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * @returns instance of {@link EnsDbClient} | ||||||||||||||||||||||
| * @throws Error with formatted validation messages if environment parsing fails | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function buildEnsDbClientFromEnvironment(env: EnsApiEnvironment): EnsDbClient { | ||||||||||||||||||||||
|
Member
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. Why is this function in this file? This seems like bad code organization.
Contributor
Author
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. I can move it to a separate file, but it's also a temporary measure that will be resolved with PR #1716, when there will be no need to call
Contributor
Author
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. Here, I'm just updating this bit, where ENSIndexer client configuration was parsed from env, and then ENSIndexer client object would be created from it. If you like, I can replace ensnode/apps/ensapi/src/config/config.schema.ts Lines 88 to 97 in 9429530
@lightwalker-eth advice appreciated. |
||||||||||||||||||||||
| const databaseUrl = DatabaseUrlSchema.parse(env.DATABASE_URL); | ||||||||||||||||||||||
| const ensIndexerSchemaName = DatabaseSchemaNameSchema.parse(env.DATABASE_SCHEMA); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return new EnsDbClient(databaseUrl, ensIndexerSchemaName); | ||||||||||||||||||||||
|
Comment on lines
+87
to
+89
|
||||||||||||||||||||||
| const ensIndexerSchemaName = DatabaseSchemaNameSchema.parse(env.DATABASE_SCHEMA); | |
| return new EnsDbClient(databaseUrl, ensIndexerSchemaName); | |
| // NOTE: This is the ENSIndexer instance identifier (often the schema name), used as `ensIndexerRef` in EnsDbClient. | |
| const ensIndexerRef = DatabaseSchemaNameSchema.parse(env.DATABASE_SCHEMA); | |
| return new EnsDbClient(databaseUrl, ensIndexerRef); |
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.
This needs refactoring / renaming.
It's not appropriate to call this function buildConfigFromEnvironment.
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.
@lightwalker-eth this week, we discussed how managing configuration objects in ENSApi has to be changed.
I suggest we address the refactoring buildConfigFromEnvironment function in PR #1716. The PR 1716 needs PRs 1752, 1753, and 1754 to be merged first.
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.
In my mind, there should only ever be a single ensDbClient ever created in ENSApi.
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.
PR #1716 wil address required refactoring of how ENSApi configuration is applied. For now, we need to create ensDbClient to fetch EnsIndexerPublicConfig. The target state that PR 1716 will introduce is that there will be no need to call ensDbClient when building configuration from environment.
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.
Copilot
AI
Mar 12, 2026
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.
Previously the ENSIndexer public config load had retry/backoff; the ENSDb read now happens once and any transient DB startup error will immediately exit the process. Consider reintroducing a small retry (or a clearer classification of “missing row” vs “DB connectivity/permission error”) around getEnsIndexerPublicConfig() to avoid flakiness during deploy/startup ordering.
Copilot
AI
Mar 12, 2026
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.
PR description implies ENSApi won’t reference an ENSIndexer instance directly, but this function still builds a config that includes ensIndexerUrl (and ENSApi still has codepaths that use it). If full decoupling isn’t part of this PR, consider tightening the PR description/scope so it matches what’s actually changed here (public config source only).
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.
No retry logic for DB query at startup
The previous implementation used pRetry with 3 retries (and informative log messages) when fetching the ENSIndexer config from the ENSIndexer HTTP endpoint. The new code performs a single database query with no retry: if the database is temporarily unavailable at startup (e.g., during a rolling deploy), ENSApi will immediately log an error and call process.exit(1), requiring an operator to manually restart the service.
Consider wrapping the ensDbClient.getEnsIndexerPublicConfig() call in similar retry logic, for example:
import pRetry from "p-retry";
const ensIndexerPublicConfig = await pRetry(
() => ensDbClient.getEnsIndexerPublicConfig(),
{
retries: 3,
onFailedAttempt: ({ error, attemptNumber, retriesLeft }) => {
logger.info(
`ENSIndexer Public Config DB read attempt ${attemptNumber} failed (${error.message}). ${retriesLeft} retries left.`,
);
},
},
);| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ import type { | |
| * their state in `process.env`. This interface is intended to be the source type which then gets | ||
| * mapped/parsed into a structured configuration object like `EnsApiConfig`. | ||
| */ | ||
| export type EnsApiEnvironment = Omit<DatabaseEnvironment, "DATABASE_SCHEMA"> & | ||
| export type EnsApiEnvironment = DatabaseEnvironment & | ||
| EnsIndexerUrlEnvironment & | ||
|
Member
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. Remove this now?
Contributor
Author
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. Not yet, but will be removed in PR #1716. |
||
| RpcEnvironment & | ||
| PortEnvironment & | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| import type { NodePgDatabase } from "drizzle-orm/node-postgres"; | ||
| import { and, eq } from "drizzle-orm/sql"; | ||
|
|
||
| import * as ensNodeSchema from "@ensnode/ensnode-schema/ensnode"; | ||
| import { | ||
| type CrossChainIndexingStatusSnapshot, | ||
| deserializeCrossChainIndexingStatusSnapshot, | ||
| deserializeEnsIndexerPublicConfig, | ||
| type EnsDbClientQuery, | ||
| type EnsIndexerPublicConfig, | ||
| EnsNodeMetadataKeys, | ||
| type SerializedEnsNodeMetadata, | ||
| type SerializedEnsNodeMetadataEnsDbVersion, | ||
| type SerializedEnsNodeMetadataEnsIndexerIndexingStatus, | ||
| type SerializedEnsNodeMetadataEnsIndexerPublicConfig, | ||
| } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| import { makeReadOnlyDrizzle } from "@/lib/handlers/drizzle"; | ||
|
|
||
| /** | ||
| * Drizzle database | ||
| * | ||
| * Allows interacting with Postgres database for ENSDb, using Drizzle ORM. | ||
| */ | ||
| interface DrizzleDb extends NodePgDatabase<typeof ensNodeSchema> {} | ||
|
|
||
| /** | ||
| * ENSDb Client | ||
| * | ||
| * This client exists to provide an abstraction layer for interacting with ENSDb. | ||
| * It enables ENSIndexer and ENSApi to decouple from each other, and use | ||
| * ENSDb as the integration point between the two (via ENSDb Client). | ||
| * | ||
| * Enables querying ENSDb data, such as: | ||
| * - ENSDb version | ||
| * - ENSIndexer Public Config, | ||
| * - Indexing Status Snapshot. | ||
| */ | ||
| export class EnsDbClient implements EnsDbClientQuery { | ||
|
Member
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. We're getting so many ideas badly mixed up. This is taking way too much of my time to identify all the problems and suggest solutions. It's very inappropriate to push all of these problems on me. Issues here include how you're mixing up the idea of ENSDb with the ENSNode Schema in ENSDb. |
||
| /** | ||
| * Drizzle database instance for ENSDb. | ||
| * | ||
| * This is a read-only Drizzle instance, since ENSApi should not be | ||
| * performing any mutations on the database. | ||
| */ | ||
| private db: DrizzleDb; | ||
|
|
||
| /** | ||
| * ENSIndexer reference string for multi-tenancy in ENSDb. | ||
| */ | ||
| private ensIndexerRef: string; | ||
|
|
||
| /** | ||
| * @param databaseUrl connection string for ENSDb Postgres database | ||
| * @param ensIndexerRef reference string for ENSIndexer instance (used for multi-tenancy in ENSDb) | ||
| */ | ||
| constructor(databaseUrl: string, ensIndexerRef: string) { | ||
| this.db = makeReadOnlyDrizzle({ | ||
|
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.
Contributor
Author
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. Passing the name of target database schema is only required while working with ENSIndexer schema, as the schema name is dynamic in this case. Otherwise, we don't have to pass the schema name, as it's bound to the database object definition. |
||
| databaseUrl, | ||
| schema: ensNodeSchema, | ||
| }); | ||
|
|
||
| this.ensIndexerRef = ensIndexerRef; | ||
| } | ||
|
Comment on lines
+57
to
+64
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. Database connection pool is never closed
Since // in EnsDbClient
async close(): Promise<void> {
await (this.db as any).$client?.end();
}// in buildConfigFromEnvironment
const ensDbClient = buildEnsDbClientFromEnvironment(env);
try {
const ensIndexerPublicConfig = await ensDbClient.getEnsIndexerPublicConfig();
...
} finally {
await ensDbClient.close();
}Alternatively, consider sharing the
Contributor
Author
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. The connection must remain open for the process lifetime. It will be needed for loading current indexing status object while handling HTTP traffic. |
||
|
|
||
| /** | ||
| * @inheritdoc | ||
| */ | ||
| async getEnsDbVersion(): Promise<string | undefined> { | ||
| const record = await this.getEnsNodeMetadata<SerializedEnsNodeMetadataEnsDbVersion>({ | ||
| key: EnsNodeMetadataKeys.EnsDbVersion, | ||
| }); | ||
|
|
||
| return record; | ||
| } | ||
|
|
||
| /** | ||
| * @inheritdoc | ||
| */ | ||
| async getEnsIndexerPublicConfig(): Promise<EnsIndexerPublicConfig | undefined> { | ||
| const record = await this.getEnsNodeMetadata<SerializedEnsNodeMetadataEnsIndexerPublicConfig>({ | ||
| key: EnsNodeMetadataKeys.EnsIndexerPublicConfig, | ||
| }); | ||
|
|
||
| if (!record) { | ||
| return undefined; | ||
| } | ||
|
|
||
| return deserializeEnsIndexerPublicConfig(record); | ||
| } | ||
|
|
||
| /** | ||
| * @inheritdoc | ||
| */ | ||
| async getIndexingStatusSnapshot(): Promise<CrossChainIndexingStatusSnapshot | undefined> { | ||
| const record = await this.getEnsNodeMetadata<SerializedEnsNodeMetadataEnsIndexerIndexingStatus>( | ||
| { | ||
| key: EnsNodeMetadataKeys.EnsIndexerIndexingStatus, | ||
| }, | ||
| ); | ||
|
|
||
| if (!record) { | ||
| return undefined; | ||
| } | ||
|
|
||
| return deserializeCrossChainIndexingStatusSnapshot(record); | ||
| } | ||
|
|
||
| /** | ||
| * Get ENSNode metadata record | ||
| * | ||
| * @returns selected record in ENSDb. | ||
| * @throws when more than one matching metadata record is found | ||
| * (should be impossible given the PK constraint on 'key') | ||
| */ | ||
tk-o marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private async getEnsNodeMetadata<EnsNodeMetadataType extends SerializedEnsNodeMetadata>( | ||
| metadata: Pick<EnsNodeMetadataType, "key">, | ||
| ): Promise<EnsNodeMetadataType["value"] | undefined> { | ||
| const result = await this.db | ||
| .select() | ||
| .from(ensNodeSchema.ensNodeMetadata) | ||
| .where( | ||
| and( | ||
| eq(ensNodeSchema.ensNodeMetadata.ensIndexerRef, this.ensIndexerRef), | ||
| eq(ensNodeSchema.ensNodeMetadata.key, metadata.key), | ||
| ), | ||
| ); | ||
|
|
||
| if (result.length === 0) { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (result.length === 1 && result[0]) { | ||
| return result[0].value as EnsNodeMetadataType["value"]; | ||
| } | ||
|
|
||
| throw new Error(`There must be exactly one ENSNodeMetadata record for '${metadata.key}' key`); | ||
| } | ||
| } | ||
This file was deleted.
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.
Is it fair to also say that this change fully decouples ENSApi and ENSIndexer?