UNPKG

envio

Version:

A latency and sync speed optimized, developer friendly blockchain data indexer.

353 lines (336 loc) • 11 kB
// Generated by ReScript, PLEASE EDIT WITH CARE import * as Rest from "./vendored/Rest.res.mjs"; import * as Time from "./Time.res.mjs"; import * as Table from "./db/Table.res.mjs"; import * as Utils from "./Utils.res.mjs"; import * as Schema from "./db/Schema.res.mjs"; import * as Logging from "./Logging.res.mjs"; import * as InternalTable from "./db/InternalTable.res.mjs"; import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs"; import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js"; function auth(s) { return { role: s.header("X-Hasura-Role", S$RescriptSchema.string), secret: s.header("X-Hasura-Admin-Secret", S$RescriptSchema.string) }; } let responses = [ s => { s.status(200); s.data(S$RescriptSchema.unknown); return "QuerySucceeded"; }, s => { s.field("code", S$RescriptSchema.$$enum([ "already-exists", "already-tracked" ])); return "AlreadyDone"; } ]; function clearMetadataRoute() { return { method: "POST", path: "", input: s => { s.field("type", S$RescriptSchema.literal("clear_metadata")); s.field("args", S$RescriptSchema.literal({})); return auth(s); }, responses: responses }; } function reloadMetadataRoute() { return { method: "POST", path: "", input: s => { s.field("type", S$RescriptSchema.literal("reload_metadata")); return { args: s.field("args", S$RescriptSchema.json(false)), auth: auth(s) }; }, responses: responses }; } function trackTablesRoute() { return { method: "POST", path: "", input: s => { s.field("type", S$RescriptSchema.literal("pg_track_tables")); return { args: s.field("args", S$RescriptSchema.json(false)), auth: auth(s) }; }, responses: responses }; } function rawBodyRoute() { return { method: "POST", path: "", input: s => ({ bodyString: s.rawBody(S$RescriptSchema.string), auth: auth(s) }), responses: responses }; } async function sendOperation(endpoint, auth, operation) { let retry = async attempt => { try { await Rest.fetch(rawBodyRoute, { bodyString: JSON.stringify(operation), auth: auth }, Rest.client(endpoint, undefined)); return; } catch (raw_exn) { let exn = Primitive_exceptions.internalToException(raw_exn); if (attempt >= 3) { return Logging.warn({ msg: "Hasura configuration request failed. Indexing will still work - but you may have issues querying data via GraphQL.", err: Utils.prettifyExn(exn) }); } let backoffMs = (Math.pow(2.0, attempt) | 0) * 1000 | 0; await Time.resolvePromiseAfterDelay(backoffMs); return await retry(attempt + 1 | 0); } }; return await retry(0); } async function clearHasuraMetadata(endpoint, auth) { try { let result = await Rest.fetch(clearMetadataRoute, auth, Rest.client(endpoint, undefined)); let tmp; tmp = result === "QuerySucceeded" ? "Hasura metadata cleared" : "Hasura metadata already cleared"; return Logging.trace(tmp); } catch (raw_exn) { let exn = Primitive_exceptions.internalToException(raw_exn); return Logging.error({ msg: `There was an issue clearing metadata in hasura - indexing may still work - but you may have issues querying the data in hasura.`, err: Utils.prettifyExn(exn) }); } } async function reloadHasuraMetadata(endpoint, auth) { try { let result = await Rest.fetch(reloadMetadataRoute, { auth: auth, args: { reload_sources: ["default"] } }, Rest.client(endpoint, undefined)); let tmp; tmp = result === "QuerySucceeded" ? "Hasura metadata reloaded" : "Hasura metadata reload acknowledged"; return Logging.trace(tmp); } catch (raw_exn) { let exn = Primitive_exceptions.internalToException(raw_exn); return Logging.error({ msg: `There was an issue reloading hasura metadata - table tracking may race with schema creation.`, err: Utils.prettifyExn(exn) }); } } function makeColumnConfigs(table) { let columnConfigs = {}; table.fields.forEach(fieldOrDerived => { if (fieldOrDerived.TAG !== "Field") { return; } let field = fieldOrDerived._0; let apiFieldName = Table.getApiFieldName(field); let dbFieldName = Table.getPgDbFieldName(field); let customName = apiFieldName === dbFieldName ? undefined : apiFieldName; let match = field.description; if (customName === undefined && match === undefined) { return; } columnConfigs[dbFieldName] = { customName: customName, comment: match }; }); return columnConfigs; } async function trackTables(endpoint, auth, pgSchema, tableConfigs) { try { let result = await Rest.fetch(trackTablesRoute, { auth: auth, args: { allow_warnings: false, tables: tableConfigs.map(param => { let description = param.description; let tableName = param.tableName; let configuration = { custom_name: tableName }; if (description !== undefined) { configuration["comment"] = description; } let columnConfigEntries = Object.entries(param.columnConfigs); if (columnConfigEntries.length !== 0) { let columnConfigJson = {}; columnConfigEntries.forEach(param => { let config = param[1]; let entry = {}; let customName = config.customName; if (customName !== undefined) { entry["custom_name"] = customName; } let comment = config.comment; if (comment !== undefined) { entry["comment"] = comment; } columnConfigJson[param[0]] = entry; }); configuration["column_config"] = columnConfigJson; } return { table: { name: tableName, schema: pgSchema }, configuration: configuration }; }) } }, Rest.client(endpoint, undefined)); let msg; msg = result === "QuerySucceeded" ? "Hasura finished tracking tables" : "Hasura tables already tracked"; return Logging.trace({ msg: msg, tableNames: tableConfigs.map(c => c.tableName) }); } catch (raw_exn) { let exn = Primitive_exceptions.internalToException(raw_exn); return Logging.error({ msg: `There was an issue tracking tables in hasura - indexing may still work - but you may have issues querying the data in hasura.`, tableNames: tableConfigs.map(c => c.tableName), err: Utils.prettifyExn(exn) }); } } async function createSelectPermission(endpoint, auth, tableName, pgSchema, responseLimit, aggregateEntities) { return await sendOperation(endpoint, auth, { type: "pg_create_select_permission", args: { table: { schema: pgSchema, name: tableName }, role: "public", source: "default", permission: { columns: "*", filter: {}, limit: responseLimit, allow_aggregations: aggregateEntities.includes(tableName) } } }); } async function createEntityRelationship(endpoint, auth, pgSchema, tableName, relationshipType, relationalKey, objectName, mappedEntity, isDerivedFrom, comment) { let derivedFromTo = isDerivedFrom ? `"id": "` + relationalKey + `"` : `"` + relationalKey + `" : "id"`; let tableJson = { schema: pgSchema, name: tableName }; let usingJson = { manual_configuration: { remote_table: { schema: pgSchema, name: mappedEntity }, column_mapping: JSON.parse(`{` + derivedFromTo + `}`) } }; let args = { table: tableJson, name: objectName, source: "default", using: usingJson }; if (comment !== undefined) { args["comment"] = comment; } return await sendOperation(endpoint, auth, { type: `pg_create_` + relationshipType + `_relationship`, args: args }); } async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) { let exposedInternalTableConfigs = [ { tableName: InternalTable.RawEvents.table.tableName, description: undefined, columnConfigs: {} }, { tableName: InternalTable.Views.metaViewName, description: undefined, columnConfigs: {} }, { tableName: InternalTable.Views.chainMetadataViewName, description: undefined, columnConfigs: {} } ]; let userTableConfigs = userEntities.map(entity => ({ tableName: entity.table.tableName, description: entity.table.description, columnConfigs: makeColumnConfigs(entity.table) })); let tableConfigs = [ exposedInternalTableConfigs, userTableConfigs ].flat(); let tableNames = tableConfigs.map(c => c.tableName); Logging.info("Tracking tables in Hasura"); await clearHasuraMetadata(endpoint, auth); await reloadHasuraMetadata(endpoint, auth); await trackTables(endpoint, auth, pgSchema, tableConfigs); for (let i = 0, i_finish = tableNames.length; i < i_finish; ++i) { let tableName = tableNames[i]; await createSelectPermission(endpoint, auth, tableName, pgSchema, responseLimit, aggregateEntities); } for (let i$1 = 0, i_finish$1 = userEntities.length; i$1 < i_finish$1; ++i$1) { let entityConfig = userEntities[i$1]; let match = entityConfig.table; let tableName$1 = match.tableName; let derivedFromFields = Table.getDerivedFromFields(entityConfig.table); for (let j = 0, j_finish = derivedFromFields.length; j < j_finish; ++j) { let derivedFromField = derivedFromFields[j]; let relationalFieldName = Utils.unwrapResultExn(Schema.getDerivedFromPgFieldName(schema, derivedFromField)); await createEntityRelationship(endpoint, auth, pgSchema, tableName$1, "array", relationalFieldName, derivedFromField.fieldName, derivedFromField.derivedFromEntity, true, derivedFromField.description); } let linkedEntityFields = Table.getLinkedEntityFields(entityConfig.table); for (let j$1 = 0, j_finish$1 = linkedEntityFields.length; j$1 < j_finish$1; ++j$1) { let match$1 = linkedEntityFields[j$1]; let field = match$1[0]; await createEntityRelationship(endpoint, auth, pgSchema, tableName$1, "object", Table.getPgDbFieldName(field), field.fieldName, match$1[1], false, field.description); } } return Logging.info("Hasura configuration completed"); } export { auth, responses, clearMetadataRoute, reloadMetadataRoute, trackTablesRoute, rawBodyRoute, sendOperation, clearHasuraMetadata, reloadHasuraMetadata, makeColumnConfigs, trackTables, createSelectPermission, createEntityRelationship, trackDatabase, } /* Rest Not a pure module */