envio 2.27.6 → 2.28.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/rescript.json +3 -0
- package/src/Hasura.res +135 -12
- package/src/Hasura.res.js +95 -13
- package/src/Internal.res +7 -4
- package/src/InternalConfig.res +20 -0
- package/src/InternalConfig.res.js +2 -0
- package/src/Js.shim.ts +11 -0
- package/src/LoadManager.res +12 -6
- package/src/LoadManager.res.js +13 -6
- package/src/Persistence.res +25 -33
- package/src/Persistence.res.js +18 -20
- package/src/PgStorage.res +155 -101
- package/src/PgStorage.res.js +141 -100
- package/src/Prometheus.res +2 -2
- package/src/Prometheus.res.js +2 -3
- package/src/bindings/Pino.res +1 -1
- package/src/bindings/Pino.res.js +2 -1
- package/src/db/EntityHistory.res +18 -17
- package/src/db/EntityHistory.res.js +28 -26
- package/src/db/InternalTable.gen.ts +43 -0
- package/src/db/InternalTable.res +392 -0
- package/src/db/InternalTable.res.js +295 -0
- package/src/vendored/Rest.res +11 -2
- package/src/vendored/Rest.res.js +44 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "envio",
|
|
3
|
-
"version": "v2.
|
|
3
|
+
"version": "v2.28.0-alpha.1",
|
|
4
4
|
"description": "A latency and sync speed optimized, developer friendly blockchain data indexer.",
|
|
5
5
|
"bin": "./bin.js",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://envio.dev",
|
|
27
27
|
"optionalDependencies": {
|
|
28
|
-
"envio-linux-x64": "v2.
|
|
29
|
-
"envio-linux-arm64": "v2.
|
|
30
|
-
"envio-darwin-x64": "v2.
|
|
31
|
-
"envio-darwin-arm64": "v2.
|
|
28
|
+
"envio-linux-x64": "v2.28.0-alpha.1",
|
|
29
|
+
"envio-linux-arm64": "v2.28.0-alpha.1",
|
|
30
|
+
"envio-darwin-x64": "v2.28.0-alpha.1",
|
|
31
|
+
"envio-darwin-arm64": "v2.28.0-alpha.1"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@envio-dev/hypersync-client": "0.6.5",
|
package/rescript.json
CHANGED
package/src/Hasura.res
CHANGED
|
@@ -218,28 +218,149 @@ let createEntityRelationship = async (
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
let trackMeta = async (~auth, ~endpoint, ~pgSchema) => {
|
|
222
|
+
try {
|
|
223
|
+
// Track EnvioMeta logical model with scalar fields
|
|
224
|
+
let result = await rawBodyRoute->Rest.fetch(
|
|
225
|
+
{
|
|
226
|
+
"auth": auth,
|
|
227
|
+
"bodyString": `{"type": "pg_track_logical_model","args": {"source": "default","name":"EnvioMeta","fields":[{"name":"chainId","type":"int"},{"name":"startBlock","type":"int"},{"name":"endBlock","type":"int","nullable":true},{"name":"bufferBlock","type":"int"},{"name":"readyAt","type":"timestamptz","nullable":true},{"name":"firstEventBlock","type":"int","nullable":true},{"name":"eventsProcessed","type":"int"},{"name":"isReady","type":"bool"}]}}`,
|
|
228
|
+
},
|
|
229
|
+
~client=Rest.client(endpoint),
|
|
230
|
+
)
|
|
231
|
+
let msg = switch result {
|
|
232
|
+
| QuerySucceeded => `Hasura EnvioMeta logical model created`
|
|
233
|
+
| AlreadyDone => `Hasura EnvioMeta logical model already created`
|
|
234
|
+
}
|
|
235
|
+
Logging.trace({
|
|
236
|
+
"msg": msg,
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// Update _meta native query to return hardcoded block object
|
|
240
|
+
let result = await rawBodyRoute->Rest.fetch(
|
|
241
|
+
{
|
|
242
|
+
"auth": auth,
|
|
243
|
+
"bodyString": `{"type":"pg_track_native_query","args":{"type":"query","source":"default","root_field_name":"_meta","arguments":{},"returns":"EnvioMeta","code":"SELECT \\\"${(#id: InternalTable.Chains.field :> string)}\\\" AS \\\"chainId\\\", \\\"${(#start_block: InternalTable.Chains.field :> string)}\\\" AS \\\"startBlock\\\", \\\"${(#end_block: InternalTable.Chains.field :> string)}\\\" AS \\\"endBlock\\\", \\\"${(#buffer_block: InternalTable.Chains.field :> string)}\\\" AS \\\"bufferBlock\\\", \\\"${(#ready_at: InternalTable.Chains.field :> string)}\\\" AS \\\"readyAt\\\", \\\"${(#first_event_block: InternalTable.Chains.field :> string)}\\\" AS \\\"firstEventBlock\\\", \\\"${(#events_processed: InternalTable.Chains.field :> string)}\\\" AS \\\"eventsProcessed\\\", (\\\"${(#ready_at: InternalTable.Chains.field :> string)}\\\" IS NOT NULL) AS \\\"isReady\\\" FROM \\\"${pgSchema}\\\".\\\"${InternalTable.Chains.table.tableName}\\\" ORDER BY \\\"id\\\""}}`,
|
|
244
|
+
},
|
|
245
|
+
~client=Rest.client(endpoint),
|
|
246
|
+
)
|
|
247
|
+
let msg = switch result {
|
|
248
|
+
| QuerySucceeded => `Hasura _meta native query created`
|
|
249
|
+
| AlreadyDone => `Hasura _meta native query already created`
|
|
250
|
+
}
|
|
251
|
+
Logging.trace({
|
|
252
|
+
"msg": msg,
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
// Add public select permissions for EnvioMeta logical model
|
|
256
|
+
let result = await rawBodyRoute->Rest.fetch(
|
|
257
|
+
{
|
|
258
|
+
"auth": auth,
|
|
259
|
+
"bodyString": `{"type": "pg_create_logical_model_select_permission", "args": {"source": "default", "name": "EnvioMeta", "role": "public", "permission": {"columns": "*", "filter": {}}}}`,
|
|
260
|
+
},
|
|
261
|
+
~client=Rest.client(endpoint),
|
|
262
|
+
)
|
|
263
|
+
let msg = switch result {
|
|
264
|
+
| QuerySucceeded => `Hasura _meta public select permission created`
|
|
265
|
+
| AlreadyDone => `Hasura _meta public select permission already exists`
|
|
266
|
+
}
|
|
267
|
+
Logging.trace({
|
|
268
|
+
"msg": msg,
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
let result = await rawBodyRoute->Rest.fetch(
|
|
272
|
+
{
|
|
273
|
+
"auth": auth,
|
|
274
|
+
"bodyString": `{"type": "pg_track_logical_model","args": {"source": "default","name":"chain_metadata","fields":[{"name":"block_height","type":"int"},{"name":"chain_id","type":"int"},{"name":"end_block","type":"int"},{"name":"first_event_block_number","type":"int"},{"name":"is_hyper_sync","type":"boolean"},{"name":"latest_fetched_block_number","type":"int"},{"name":"latest_processed_block","type":"int"},{"name":"num_batches_fetched","type":"int"},{"name":"num_events_processed","type":"int"},{"name":"start_block","type":"int"},{"name":"timestamp_caught_up_to_head_or_endblock","type":"timestamptz"}]}}`,
|
|
275
|
+
},
|
|
276
|
+
~client=Rest.client(endpoint),
|
|
277
|
+
)
|
|
278
|
+
let msg = switch result {
|
|
279
|
+
| QuerySucceeded => `Hasura chain_metadata logical model created`
|
|
280
|
+
| AlreadyDone => `Hasura chain_metadata logical model already created`
|
|
281
|
+
}
|
|
282
|
+
Logging.trace({
|
|
283
|
+
"msg": msg,
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
// Need this to keep backwards compatibility,
|
|
287
|
+
// since it's used on Hosted Service
|
|
288
|
+
let result = await rawBodyRoute->Rest.fetch(
|
|
289
|
+
{
|
|
290
|
+
"auth": auth,
|
|
291
|
+
"bodyString": `{"type":"pg_track_native_query","args":{"type":"query","source":"default","root_field_name":"chain_metadata","arguments":{},"returns":"chain_metadata","code":"SELECT \\\"${(#source_block: InternalTable.Chains.field :> string)}\\\" AS \\\"block_height\\\", \\\"${(#id: InternalTable.Chains.field :> string)}\\\" AS \\\"chain_id\\\", \\\"${(#end_block: InternalTable.Chains.field :> string)}\\\", \\\"${(#first_event_block: InternalTable.Chains.field :> string)}\\\" AS \\\"first_event_block_number\\\", \\\"${(#_is_hyper_sync: InternalTable.Chains.field :> string)}\\\" AS \\\"is_hyper_sync\\\", \\\"${(#buffer_block: InternalTable.Chains.field :> string)}\\\" AS \\\"latest_fetched_block_number\\\", \\\"${(#_latest_processed_block: InternalTable.Chains.field :> string)}\\\" AS \\\"latest_processed_block\\\", \\\"${(#_num_batches_fetched: InternalTable.Chains.field :> string)}\\\" AS \\\"num_batches_fetched\\\", \\\"${(#events_processed: InternalTable.Chains.field :> string)}\\\" AS \\\"num_events_processed\\\", \\\"${(#start_block: InternalTable.Chains.field :> string)}\\\", \\\"${(#ready_at: InternalTable.Chains.field :> string)}\\\" AS \\\"timestamp_caught_up_to_head_or_endblock\\\" FROM \\\"${pgSchema}\\\".\\\"${InternalTable.Chains.table.tableName}\\\""}}`,
|
|
292
|
+
},
|
|
293
|
+
~client=Rest.client(endpoint),
|
|
294
|
+
)
|
|
295
|
+
let msg = switch result {
|
|
296
|
+
| QuerySucceeded => `Hasura chain_metadata native query created`
|
|
297
|
+
| AlreadyDone => `Hasura chain_metadata native query already created`
|
|
298
|
+
}
|
|
299
|
+
Logging.trace({
|
|
300
|
+
"msg": msg,
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
// Add public select permissions for chain_metadata logical model
|
|
304
|
+
let result = await rawBodyRoute->Rest.fetch(
|
|
305
|
+
{
|
|
306
|
+
"auth": auth,
|
|
307
|
+
"bodyString": `{"type": "pg_create_logical_model_select_permission", "args": {"source": "default", "name": "chain_metadata", "role": "public", "permission": {"columns": "*", "filter": {}}}}`,
|
|
308
|
+
},
|
|
309
|
+
~client=Rest.client(endpoint),
|
|
310
|
+
)
|
|
311
|
+
let msg = switch result {
|
|
312
|
+
| QuerySucceeded => `Hasura chain_metadata public select permission created`
|
|
313
|
+
| AlreadyDone => `Hasura chain_metadata public select permission already exists`
|
|
314
|
+
}
|
|
315
|
+
Logging.trace({
|
|
316
|
+
"msg": msg,
|
|
317
|
+
})
|
|
318
|
+
} catch {
|
|
319
|
+
| exn =>
|
|
320
|
+
Logging.error({
|
|
321
|
+
"msg": `EE808: There was an issue setting up _meta field in hasura - indexing may still work - but you may have issues querying the data in hasura.`,
|
|
322
|
+
"err": exn->Utils.prettifyExn,
|
|
323
|
+
})
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
221
327
|
let trackDatabase = async (
|
|
222
328
|
~endpoint,
|
|
223
329
|
~auth,
|
|
224
330
|
~pgSchema,
|
|
225
|
-
~
|
|
226
|
-
~allEntityTables,
|
|
331
|
+
~userEntities: array<Internal.entityConfig>,
|
|
227
332
|
~aggregateEntities,
|
|
228
333
|
~responseLimit,
|
|
229
334
|
~schema,
|
|
230
335
|
) => {
|
|
336
|
+
let trackOnlyInternalTableNames = [
|
|
337
|
+
InternalTable.Chains.table.tableName,
|
|
338
|
+
InternalTable.EventSyncState.table.tableName,
|
|
339
|
+
InternalTable.PersistedState.table.tableName,
|
|
340
|
+
InternalTable.EndOfBlockRangeScannedData.table.tableName,
|
|
341
|
+
InternalTable.DynamicContractRegistry.table.tableName,
|
|
342
|
+
]
|
|
343
|
+
let exposedInternalTableNames = [InternalTable.RawEvents.table.tableName]
|
|
344
|
+
let userTableNames = userEntities->Js.Array2.map(entity => entity.table.tableName)
|
|
345
|
+
|
|
231
346
|
Logging.info("Tracking tables in Hasura")
|
|
232
347
|
|
|
233
348
|
let _ = await clearHasuraMetadata(~endpoint, ~auth)
|
|
234
|
-
let tableNames =
|
|
235
|
-
[allStaticTables, allEntityTables]
|
|
236
|
-
->Belt.Array.concatMany
|
|
237
|
-
->Js.Array2.map(({tableName}: Table.table) => tableName)
|
|
238
349
|
|
|
239
|
-
await trackTables(
|
|
350
|
+
await trackTables(
|
|
351
|
+
~endpoint,
|
|
352
|
+
~auth,
|
|
353
|
+
~pgSchema,
|
|
354
|
+
~tableNames=[
|
|
355
|
+
exposedInternalTableNames,
|
|
356
|
+
trackOnlyInternalTableNames,
|
|
357
|
+
userTableNames,
|
|
358
|
+
]->Belt.Array.concatMany,
|
|
359
|
+
)
|
|
240
360
|
|
|
241
361
|
let _ =
|
|
242
|
-
await
|
|
362
|
+
await [exposedInternalTableNames, userTableNames]
|
|
363
|
+
->Belt.Array.concatMany
|
|
243
364
|
->Js.Array2.map(tableName =>
|
|
244
365
|
createSelectPermissions(
|
|
245
366
|
~endpoint,
|
|
@@ -251,11 +372,11 @@ let trackDatabase = async (
|
|
|
251
372
|
)
|
|
252
373
|
)
|
|
253
374
|
->Js.Array2.concatMany(
|
|
254
|
-
|
|
255
|
-
let {tableName} = table
|
|
375
|
+
userEntities->Js.Array2.map(entityConfig => {
|
|
376
|
+
let {tableName} = entityConfig.table
|
|
256
377
|
[
|
|
257
378
|
//Set array relationships
|
|
258
|
-
table
|
|
379
|
+
entityConfig.table
|
|
259
380
|
->Table.getDerivedFromFields
|
|
260
381
|
->Js.Array2.map(derivedFromField => {
|
|
261
382
|
//determines the actual name of the underlying relational field (if it's an entity mapping then suffixes _id for eg.)
|
|
@@ -275,7 +396,7 @@ let trackDatabase = async (
|
|
|
275
396
|
)
|
|
276
397
|
}),
|
|
277
398
|
//Set object relationships
|
|
278
|
-
table
|
|
399
|
+
entityConfig.table
|
|
279
400
|
->Table.getLinkedEntityFields
|
|
280
401
|
->Js.Array2.map(((field, linkedEntityName)) => {
|
|
281
402
|
createEntityRelationship(
|
|
@@ -294,4 +415,6 @@ let trackDatabase = async (
|
|
|
294
415
|
}),
|
|
295
416
|
)
|
|
296
417
|
->Promise.all
|
|
418
|
+
|
|
419
|
+
await trackMeta(~auth, ~endpoint, ~pgSchema)
|
|
297
420
|
}
|
package/src/Hasura.res.js
CHANGED
|
@@ -7,6 +7,7 @@ var Utils = require("./Utils.res.js");
|
|
|
7
7
|
var Schema = require("./db/Schema.res.js");
|
|
8
8
|
var Logging = require("./Logging.res.js");
|
|
9
9
|
var Belt_Array = require("rescript/lib/js/belt_Array.js");
|
|
10
|
+
var InternalTable = require("./db/InternalTable.res.js");
|
|
10
11
|
var Caml_splice_call = require("rescript/lib/js/caml_splice_call.js");
|
|
11
12
|
var S$RescriptSchema = require("rescript-schema/src/S.res.js");
|
|
12
13
|
var Caml_js_exceptions = require("rescript/lib/js/caml_js_exceptions.js");
|
|
@@ -203,31 +204,111 @@ async function createEntityRelationship(pgSchema, endpoint, auth, tableName, rel
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
async function
|
|
207
|
+
async function trackMeta(auth, endpoint, pgSchema) {
|
|
208
|
+
try {
|
|
209
|
+
var result = await Rest.$$fetch(rawBodyRoute, {
|
|
210
|
+
auth: auth,
|
|
211
|
+
bodyString: "{\"type\": \"pg_track_logical_model\",\"args\": {\"source\": \"default\",\"name\":\"EnvioMeta\",\"fields\":[{\"name\":\"chainId\",\"type\":\"int\"},{\"name\":\"startBlock\",\"type\":\"int\"},{\"name\":\"endBlock\",\"type\":\"int\",\"nullable\":true},{\"name\":\"bufferBlock\",\"type\":\"int\"},{\"name\":\"readyAt\",\"type\":\"timestamptz\",\"nullable\":true},{\"name\":\"firstEventBlock\",\"type\":\"int\",\"nullable\":true},{\"name\":\"eventsProcessed\",\"type\":\"int\"},{\"name\":\"isReady\",\"type\":\"bool\"}]}}"
|
|
212
|
+
}, Rest.client(endpoint, undefined));
|
|
213
|
+
var msg;
|
|
214
|
+
msg = result === "QuerySucceeded" ? "Hasura EnvioMeta logical model created" : "Hasura EnvioMeta logical model already created";
|
|
215
|
+
Logging.trace({
|
|
216
|
+
msg: msg
|
|
217
|
+
});
|
|
218
|
+
var result$1 = await Rest.$$fetch(rawBodyRoute, {
|
|
219
|
+
auth: auth,
|
|
220
|
+
bodyString: "{\"type\":\"pg_track_native_query\",\"args\":{\"type\":\"query\",\"source\":\"default\",\"root_field_name\":\"_meta\",\"arguments\":{},\"returns\":\"EnvioMeta\",\"code\":\"SELECT \\\"" + "id" + "\\\" AS \\\"chainId\\\", \\\"" + "start_block" + "\\\" AS \\\"startBlock\\\", \\\"" + "end_block" + "\\\" AS \\\"endBlock\\\", \\\"" + "buffer_block" + "\\\" AS \\\"bufferBlock\\\", \\\"" + "ready_at" + "\\\" AS \\\"readyAt\\\", \\\"" + "first_event_block" + "\\\" AS \\\"firstEventBlock\\\", \\\"" + "events_processed" + "\\\" AS \\\"eventsProcessed\\\", (\\\"" + "ready_at" + "\\\" IS NOT NULL) AS \\\"isReady\\\" FROM \\\"" + pgSchema + "\\\".\\\"" + InternalTable.Chains.table.tableName + "\\\" ORDER BY \\\"id\\\"\"}}"
|
|
221
|
+
}, Rest.client(endpoint, undefined));
|
|
222
|
+
var msg$1;
|
|
223
|
+
msg$1 = result$1 === "QuerySucceeded" ? "Hasura _meta native query created" : "Hasura _meta native query already created";
|
|
224
|
+
Logging.trace({
|
|
225
|
+
msg: msg$1
|
|
226
|
+
});
|
|
227
|
+
var result$2 = await Rest.$$fetch(rawBodyRoute, {
|
|
228
|
+
auth: auth,
|
|
229
|
+
bodyString: "{\"type\": \"pg_create_logical_model_select_permission\", \"args\": {\"source\": \"default\", \"name\": \"EnvioMeta\", \"role\": \"public\", \"permission\": {\"columns\": \"*\", \"filter\": {}}}}"
|
|
230
|
+
}, Rest.client(endpoint, undefined));
|
|
231
|
+
var msg$2;
|
|
232
|
+
msg$2 = result$2 === "QuerySucceeded" ? "Hasura _meta public select permission created" : "Hasura _meta public select permission already exists";
|
|
233
|
+
Logging.trace({
|
|
234
|
+
msg: msg$2
|
|
235
|
+
});
|
|
236
|
+
var result$3 = await Rest.$$fetch(rawBodyRoute, {
|
|
237
|
+
auth: auth,
|
|
238
|
+
bodyString: "{\"type\": \"pg_track_logical_model\",\"args\": {\"source\": \"default\",\"name\":\"chain_metadata\",\"fields\":[{\"name\":\"block_height\",\"type\":\"int\"},{\"name\":\"chain_id\",\"type\":\"int\"},{\"name\":\"end_block\",\"type\":\"int\"},{\"name\":\"first_event_block_number\",\"type\":\"int\"},{\"name\":\"is_hyper_sync\",\"type\":\"boolean\"},{\"name\":\"latest_fetched_block_number\",\"type\":\"int\"},{\"name\":\"latest_processed_block\",\"type\":\"int\"},{\"name\":\"num_batches_fetched\",\"type\":\"int\"},{\"name\":\"num_events_processed\",\"type\":\"int\"},{\"name\":\"start_block\",\"type\":\"int\"},{\"name\":\"timestamp_caught_up_to_head_or_endblock\",\"type\":\"timestamptz\"}]}}"
|
|
239
|
+
}, Rest.client(endpoint, undefined));
|
|
240
|
+
var msg$3;
|
|
241
|
+
msg$3 = result$3 === "QuerySucceeded" ? "Hasura chain_metadata logical model created" : "Hasura chain_metadata logical model already created";
|
|
242
|
+
Logging.trace({
|
|
243
|
+
msg: msg$3
|
|
244
|
+
});
|
|
245
|
+
var result$4 = await Rest.$$fetch(rawBodyRoute, {
|
|
246
|
+
auth: auth,
|
|
247
|
+
bodyString: "{\"type\":\"pg_track_native_query\",\"args\":{\"type\":\"query\",\"source\":\"default\",\"root_field_name\":\"chain_metadata\",\"arguments\":{},\"returns\":\"chain_metadata\",\"code\":\"SELECT \\\"" + "source_block" + "\\\" AS \\\"block_height\\\", \\\"" + "id" + "\\\" AS \\\"chain_id\\\", \\\"" + "end_block" + "\\\", \\\"" + "first_event_block" + "\\\" AS \\\"first_event_block_number\\\", \\\"" + "_is_hyper_sync" + "\\\" AS \\\"is_hyper_sync\\\", \\\"" + "buffer_block" + "\\\" AS \\\"latest_fetched_block_number\\\", \\\"" + "_latest_processed_block" + "\\\" AS \\\"latest_processed_block\\\", \\\"" + "_num_batches_fetched" + "\\\" AS \\\"num_batches_fetched\\\", \\\"" + "events_processed" + "\\\" AS \\\"num_events_processed\\\", \\\"" + "start_block" + "\\\", \\\"" + "ready_at" + "\\\" AS \\\"timestamp_caught_up_to_head_or_endblock\\\" FROM \\\"" + pgSchema + "\\\".\\\"" + InternalTable.Chains.table.tableName + "\\\"\"}}"
|
|
248
|
+
}, Rest.client(endpoint, undefined));
|
|
249
|
+
var msg$4;
|
|
250
|
+
msg$4 = result$4 === "QuerySucceeded" ? "Hasura chain_metadata native query created" : "Hasura chain_metadata native query already created";
|
|
251
|
+
Logging.trace({
|
|
252
|
+
msg: msg$4
|
|
253
|
+
});
|
|
254
|
+
var result$5 = await Rest.$$fetch(rawBodyRoute, {
|
|
255
|
+
auth: auth,
|
|
256
|
+
bodyString: "{\"type\": \"pg_create_logical_model_select_permission\", \"args\": {\"source\": \"default\", \"name\": \"chain_metadata\", \"role\": \"public\", \"permission\": {\"columns\": \"*\", \"filter\": {}}}}"
|
|
257
|
+
}, Rest.client(endpoint, undefined));
|
|
258
|
+
var msg$5;
|
|
259
|
+
msg$5 = result$5 === "QuerySucceeded" ? "Hasura chain_metadata public select permission created" : "Hasura chain_metadata public select permission already exists";
|
|
260
|
+
return Logging.trace({
|
|
261
|
+
msg: msg$5
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
catch (raw_exn){
|
|
265
|
+
var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
|
|
266
|
+
return Logging.error({
|
|
267
|
+
msg: "EE808: There was an issue setting up _meta field in hasura - indexing may still work - but you may have issues querying the data in hasura.",
|
|
268
|
+
err: Utils.prettifyExn(exn)
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) {
|
|
274
|
+
var trackOnlyInternalTableNames = [
|
|
275
|
+
InternalTable.Chains.table.tableName,
|
|
276
|
+
InternalTable.EventSyncState.table.tableName,
|
|
277
|
+
InternalTable.PersistedState.table.tableName,
|
|
278
|
+
InternalTable.EndOfBlockRangeScannedData.table.tableName,
|
|
279
|
+
InternalTable.DynamicContractRegistry.table.tableName
|
|
280
|
+
];
|
|
281
|
+
var exposedInternalTableNames = [InternalTable.RawEvents.table.tableName];
|
|
282
|
+
var userTableNames = userEntities.map(function (entity) {
|
|
283
|
+
return entity.table.tableName;
|
|
284
|
+
});
|
|
207
285
|
Logging.info("Tracking tables in Hasura");
|
|
208
286
|
await clearHasuraMetadata(endpoint, auth);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
287
|
+
await trackTables(endpoint, auth, pgSchema, Belt_Array.concatMany([
|
|
288
|
+
exposedInternalTableNames,
|
|
289
|
+
trackOnlyInternalTableNames,
|
|
290
|
+
userTableNames
|
|
291
|
+
]));
|
|
292
|
+
await Promise.all(Caml_splice_call.spliceObjApply(Belt_Array.concatMany([
|
|
293
|
+
exposedInternalTableNames,
|
|
294
|
+
userTableNames
|
|
295
|
+
]).map(function (tableName) {
|
|
217
296
|
return createSelectPermissions(auth, endpoint, tableName, pgSchema, responseLimit, aggregateEntities);
|
|
218
|
-
}), "concat", [
|
|
219
|
-
var
|
|
297
|
+
}), "concat", [userEntities.map(function (entityConfig) {
|
|
298
|
+
var match = entityConfig.table;
|
|
299
|
+
var tableName = match.tableName;
|
|
220
300
|
return [
|
|
221
|
-
Table.getDerivedFromFields(table).map(function (derivedFromField) {
|
|
301
|
+
Table.getDerivedFromFields(entityConfig.table).map(function (derivedFromField) {
|
|
222
302
|
var relationalFieldName = Utils.unwrapResultExn(Schema.getDerivedFromFieldName(schema, derivedFromField));
|
|
223
303
|
return createEntityRelationship(pgSchema, endpoint, auth, tableName, "array", relationalFieldName, derivedFromField.fieldName, derivedFromField.derivedFromEntity, true);
|
|
224
304
|
}),
|
|
225
|
-
Table.getLinkedEntityFields(table).map(function (param) {
|
|
305
|
+
Table.getLinkedEntityFields(entityConfig.table).map(function (param) {
|
|
226
306
|
var field = param[0];
|
|
227
307
|
return createEntityRelationship(pgSchema, endpoint, auth, tableName, "object", field.fieldName, field.fieldName, param[1], false);
|
|
228
308
|
})
|
|
229
309
|
].flat(1);
|
|
230
310
|
})]));
|
|
311
|
+
return await trackMeta(auth, endpoint, pgSchema);
|
|
231
312
|
}
|
|
232
313
|
|
|
233
314
|
exports.auth = auth;
|
|
@@ -240,5 +321,6 @@ exports.clearHasuraMetadata = clearHasuraMetadata;
|
|
|
240
321
|
exports.trackTables = trackTables;
|
|
241
322
|
exports.createSelectPermissions = createSelectPermissions;
|
|
242
323
|
exports.createEntityRelationship = createEntityRelationship;
|
|
324
|
+
exports.trackMeta = trackMeta;
|
|
243
325
|
exports.trackDatabase = trackDatabase;
|
|
244
326
|
/* Rest Not a pure module */
|
package/src/Internal.res
CHANGED
|
@@ -176,13 +176,16 @@ let fuelTransferParamsSchema = S.schema(s => {
|
|
|
176
176
|
})
|
|
177
177
|
|
|
178
178
|
type entity = private {id: string}
|
|
179
|
-
type
|
|
179
|
+
type genericEntityConfig<'entity> = {
|
|
180
180
|
name: string,
|
|
181
|
-
schema: S.t<entity>,
|
|
182
|
-
rowsSchema: S.t<array<entity>>,
|
|
181
|
+
schema: S.t<'entity>,
|
|
182
|
+
rowsSchema: S.t<array<'entity>>,
|
|
183
183
|
table: Table.table,
|
|
184
|
-
entityHistory: EntityHistory.t<entity>,
|
|
184
|
+
entityHistory: EntityHistory.t<'entity>,
|
|
185
185
|
}
|
|
186
|
+
type entityConfig = genericEntityConfig<entity>
|
|
187
|
+
external fromGenericEntityConfig: genericEntityConfig<'entity> => entityConfig = "%identity"
|
|
188
|
+
|
|
186
189
|
type enum
|
|
187
190
|
type enumConfig<'enum> = {
|
|
188
191
|
name: string,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// TODO: rename the file to Config.res after finishing the migration from codegen
|
|
2
|
+
// And turn it into PublicConfig instead
|
|
3
|
+
// For internal use we should create Indexer.res with a stateful type
|
|
4
|
+
|
|
5
|
+
type contract = {
|
|
6
|
+
name: string,
|
|
7
|
+
abi: EvmTypes.Abi.t,
|
|
8
|
+
addresses: array<Address.t>,
|
|
9
|
+
events: array<Internal.eventConfig>,
|
|
10
|
+
startBlock: option<int>,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type chain = {
|
|
14
|
+
id: int,
|
|
15
|
+
startBlock: int,
|
|
16
|
+
endBlock?: int,
|
|
17
|
+
confirmedBlockThreshold: int,
|
|
18
|
+
contracts: array<contract>,
|
|
19
|
+
sources: array<Source.t>,
|
|
20
|
+
}
|
package/src/Js.shim.ts
ADDED
package/src/LoadManager.res
CHANGED
|
@@ -66,9 +66,10 @@ let schedule = async loadManager => {
|
|
|
66
66
|
}
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
if inputsToLoad->Utils.Array.isEmpty->not {
|
|
69
|
+
let isSuccess = if inputsToLoad->Utils.Array.isEmpty->not {
|
|
70
70
|
try {
|
|
71
71
|
await group.load(inputsToLoad)
|
|
72
|
+
true
|
|
72
73
|
} catch {
|
|
73
74
|
| exn => {
|
|
74
75
|
let exn = exn->Utils.prettifyExn
|
|
@@ -76,16 +77,21 @@ let schedule = async loadManager => {
|
|
|
76
77
|
let call = calls->Js.Dict.unsafeGet(inputKey)
|
|
77
78
|
call.reject(exn)
|
|
78
79
|
})
|
|
80
|
+
false
|
|
79
81
|
}
|
|
80
82
|
}
|
|
83
|
+
} else {
|
|
84
|
+
true
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
if currentInputKeys->Utils.Array.isEmpty->not {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
if isSuccess {
|
|
89
|
+
currentInputKeys->Js.Array2.forEach(inputKey => {
|
|
90
|
+
let call = calls->Js.Dict.unsafeGet(inputKey)
|
|
91
|
+
calls->Utils.Dict.deleteInPlace(inputKey)
|
|
92
|
+
call.resolve(group.getUnsafeInMemory(inputKey))
|
|
93
|
+
})
|
|
94
|
+
}
|
|
89
95
|
|
|
90
96
|
// Clean up executed batch to reset
|
|
91
97
|
// provided load function which
|
package/src/LoadManager.res.js
CHANGED
|
@@ -40,9 +40,13 @@ async function schedule(loadManager) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
});
|
|
43
|
-
|
|
43
|
+
var isSuccess;
|
|
44
|
+
if (Utils.$$Array.isEmpty(inputsToLoad)) {
|
|
45
|
+
isSuccess = true;
|
|
46
|
+
} else {
|
|
44
47
|
try {
|
|
45
48
|
await group.load(inputsToLoad);
|
|
49
|
+
isSuccess = true;
|
|
46
50
|
}
|
|
47
51
|
catch (raw_exn){
|
|
48
52
|
var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
|
|
@@ -51,16 +55,19 @@ async function schedule(loadManager) {
|
|
|
51
55
|
var call = calls[inputKey];
|
|
52
56
|
call.reject(exn$1);
|
|
53
57
|
}));
|
|
58
|
+
isSuccess = false;
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
if (Utils.$$Array.isEmpty(currentInputKeys)) {
|
|
57
62
|
return ;
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
if (isSuccess) {
|
|
65
|
+
currentInputKeys.forEach(function (inputKey) {
|
|
66
|
+
var call = calls[inputKey];
|
|
67
|
+
Utils.Dict.deleteInPlace(calls, inputKey);
|
|
68
|
+
call.resolve(group.getUnsafeInMemory(inputKey));
|
|
69
|
+
});
|
|
70
|
+
}
|
|
64
71
|
var latestGroup = groups[key];
|
|
65
72
|
if (Utils.$$Array.isEmpty(Object.keys(latestGroup.calls))) {
|
|
66
73
|
return Utils.Dict.deleteInPlace(groups, key);
|
package/src/Persistence.res
CHANGED
|
@@ -13,6 +13,12 @@ type effectCacheRecord = {
|
|
|
13
13
|
mutable count: int,
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
type initialState = {
|
|
17
|
+
cleanRun: bool,
|
|
18
|
+
cache: dict<effectCacheRecord>,
|
|
19
|
+
chains: array<InternalTable.Chains.t>,
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
type operator = [#">" | #"="]
|
|
17
23
|
|
|
18
24
|
type storage = {
|
|
@@ -22,10 +28,11 @@ type storage = {
|
|
|
22
28
|
// Should initialize the storage so we can start interacting with it
|
|
23
29
|
// Eg create connection, schema, tables, etc.
|
|
24
30
|
initialize: (
|
|
31
|
+
~chainConfigs: array<InternalConfig.chain>=?,
|
|
25
32
|
~entities: array<Internal.entityConfig>=?,
|
|
26
|
-
~generalTables: array<Table.table>=?,
|
|
27
33
|
~enums: array<Internal.enumConfig<Internal.enum>>=?,
|
|
28
|
-
) => promise<
|
|
34
|
+
) => promise<initialState>,
|
|
35
|
+
loadInitialState: unit => promise<initialState>,
|
|
29
36
|
@raises("StorageError")
|
|
30
37
|
loadByIdsOrThrow: 'item. (
|
|
31
38
|
~ids: array<string>,
|
|
@@ -55,10 +62,6 @@ type storage = {
|
|
|
55
62
|
) => promise<unit>,
|
|
56
63
|
// This is to download cache from the database to .envio/cache
|
|
57
64
|
dumpEffectCache: unit => promise<unit>,
|
|
58
|
-
// This is not good, but the function does two things:
|
|
59
|
-
// - Gets info about existing cache tables
|
|
60
|
-
// - if withUpload is true, it also populates the cache from .envio/cache to the database
|
|
61
|
-
restoreEffectCache: (~withUpload: bool) => promise<array<effectCacheRecord>>,
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
exception StorageError({message: string, reason: exn})
|
|
@@ -66,11 +69,10 @@ exception StorageError({message: string, reason: exn})
|
|
|
66
69
|
type storageStatus =
|
|
67
70
|
| Unknown
|
|
68
71
|
| Initializing(promise<unit>)
|
|
69
|
-
| Ready(
|
|
72
|
+
| Ready(initialState)
|
|
70
73
|
|
|
71
74
|
type t = {
|
|
72
75
|
userEntities: array<Internal.entityConfig>,
|
|
73
|
-
staticTables: array<Table.table>,
|
|
74
76
|
allEntities: array<Internal.entityConfig>,
|
|
75
77
|
allEnums: array<Internal.enumConfig<Internal.enum>>,
|
|
76
78
|
mutable storageStatus: storageStatus,
|
|
@@ -86,18 +88,15 @@ let entityHistoryActionEnumConfig: Internal.enumConfig<EntityHistory.RowAction.t
|
|
|
86
88
|
|
|
87
89
|
let make = (
|
|
88
90
|
~userEntities,
|
|
89
|
-
~dcRegistryEntityConfig,
|
|
90
91
|
// TODO: Should only pass userEnums and create internal config in runtime
|
|
91
92
|
~allEnums,
|
|
92
|
-
~staticTables,
|
|
93
93
|
~storage,
|
|
94
94
|
) => {
|
|
95
|
-
let allEntities = userEntities->Js.Array2.concat([
|
|
95
|
+
let allEntities = userEntities->Js.Array2.concat([InternalTable.DynamicContractRegistry.config])
|
|
96
96
|
let allEnums =
|
|
97
97
|
allEnums->Js.Array2.concat([entityHistoryActionEnumConfig->Internal.fromGenericEnumConfig])
|
|
98
98
|
{
|
|
99
99
|
userEntities,
|
|
100
|
-
staticTables,
|
|
101
100
|
allEntities,
|
|
102
101
|
allEnums,
|
|
103
102
|
storageStatus: Unknown,
|
|
@@ -106,17 +105,7 @@ let make = (
|
|
|
106
105
|
}
|
|
107
106
|
|
|
108
107
|
let init = {
|
|
109
|
-
|
|
110
|
-
let effectCacheRecords = await persistence.storage.restoreEffectCache(~withUpload)
|
|
111
|
-
let cache = Js.Dict.empty()
|
|
112
|
-
effectCacheRecords->Js.Array2.forEach(record => {
|
|
113
|
-
Prometheus.EffectCacheCount.set(~count=record.count, ~effectName=record.effectName)
|
|
114
|
-
cache->Js.Dict.set(record.effectName, record)
|
|
115
|
-
})
|
|
116
|
-
cache
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async (persistence, ~reset=false) => {
|
|
108
|
+
async (persistence, ~chainConfigs, ~reset=false) => {
|
|
120
109
|
try {
|
|
121
110
|
let shouldRun = switch persistence.storageStatus {
|
|
122
111
|
| Unknown => true
|
|
@@ -135,17 +124,14 @@ let init = {
|
|
|
135
124
|
if reset || !(await persistence.storage.isInitialized()) {
|
|
136
125
|
Logging.info(`Initializing the indexer storage...`)
|
|
137
126
|
|
|
138
|
-
await persistence.storage.initialize(
|
|
127
|
+
let initialState = await persistence.storage.initialize(
|
|
139
128
|
~entities=persistence.allEntities,
|
|
140
|
-
~generalTables=persistence.staticTables,
|
|
141
129
|
~enums=persistence.allEnums,
|
|
130
|
+
~chainConfigs,
|
|
142
131
|
)
|
|
143
132
|
|
|
144
133
|
Logging.info(`The indexer storage is ready. Uploading cache...`)
|
|
145
|
-
persistence.storageStatus = Ready(
|
|
146
|
-
cleanRun: true,
|
|
147
|
-
cache: await loadInitialCache(persistence, ~withUpload=true),
|
|
148
|
-
})
|
|
134
|
+
persistence.storageStatus = Ready(initialState)
|
|
149
135
|
} else if (
|
|
150
136
|
// In case of a race condition,
|
|
151
137
|
// we want to set the initial status to Ready only once.
|
|
@@ -155,10 +141,7 @@ let init = {
|
|
|
155
141
|
}
|
|
156
142
|
) {
|
|
157
143
|
Logging.info(`The indexer storage is ready.`)
|
|
158
|
-
persistence.storageStatus = Ready(
|
|
159
|
-
cleanRun: false,
|
|
160
|
-
cache: await loadInitialCache(persistence, ~withUpload=false),
|
|
161
|
-
})
|
|
144
|
+
persistence.storageStatus = Ready(await persistence.storage.loadInitialState())
|
|
162
145
|
}
|
|
163
146
|
resolveRef.contents()
|
|
164
147
|
}
|
|
@@ -178,6 +161,15 @@ let getInitializedStorageOrThrow = persistence => {
|
|
|
178
161
|
}
|
|
179
162
|
}
|
|
180
163
|
|
|
164
|
+
let getInitializedState = persistence => {
|
|
165
|
+
switch persistence.storageStatus {
|
|
166
|
+
| Unknown
|
|
167
|
+
| Initializing(_) =>
|
|
168
|
+
Js.Exn.raiseError(`Failed to access the initial state. The Persistence layer is not initialized.`)
|
|
169
|
+
| Ready(initialState) => initialState
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
181
173
|
let setEffectCacheOrThrow = async (persistence, ~effect: Internal.effect, ~items) => {
|
|
182
174
|
switch persistence.storageStatus {
|
|
183
175
|
| Unknown
|