envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
353 lines (336 loc) • 11 kB
JavaScript
// 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 */