-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathensdb-client.ts
More file actions
139 lines (120 loc) · 3.93 KB
/
ensdb-client.ts
File metadata and controls
139 lines (120 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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 {
/**
* 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({
databaseUrl,
schema: ensNodeSchema,
});
this.ensIndexerRef = ensIndexerRef;
}
/**
* @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')
*/
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`);
}
}