Skip to content
Snippets Groups Projects
Commit 1cd1d0d9 authored by poka's avatar poka
Browse files

enh: refactore generateHasuraMetadata script

parent a5c9aa70
No related branches found
No related tags found
No related merge requests found
import { loadModel } from "@subsquid/openreader/lib/tools";
import fs from 'fs';
import yaml from 'js-yaml';
import { camelToSnakeCase } from "./utils";
import { camelToSnakeCase, ensureDirectoryExists } from "./utils";
import { ArrayRelationship, EntityProperties, HasuraTable, LookupRelationship, ObjectRelationship } from "./types_custom";
const schemaFile = "schema.graphql";
const metadataPath = "./hasura/metadata";
const metadataDbPath = `${metadataPath}/databases/default`;
/**
* Main function to generate Hasura metadata
* Main function to generate Hasura metadata from a GraphQL schema.
*/
function generateHasuraMetadata(schemaPath: string) {
// Parse schema.graphql file
const schema = loadModel(schemaPath);
function generateMetadata() {
const schema = loadModel(schemaFile);
const tablesDirPath = `${metadataDbPath}/tables`;
ensureDirectoryExists(tablesDirPath);
const tableIncludes: string[] = [];
if (!fs.existsSync(tablesDirPath)) {
fs.mkdirSync(tablesDirPath, { recursive: true });
}
for (let [tableNameCamel, type] of Object.entries(schema)) {
let tableNameSnake = camelToSnakeCase(tableNameCamel);
let kind = Object.entries(type)[0][1]
if (kind == "entity") {
let kind = Object.entries(type)[0][1];
if (kind === "entity") {
const properties = Object.entries(type)[1][1] as EntityProperties;
const metadata = generateMetadataForTable(tableNameSnake, properties);
writeMetadataToFile(tableNameSnake, metadata);
tableIncludes.push(`- "!include public_${tableNameSnake}.yaml"`)
tableIncludes.push(`- "!include public_${tableNameSnake}.yaml"`);
}
}
// Add migrations table
// Include migrations table
tableIncludes.push(`- "!include public_migrations.yaml"`);
// Generate the tables.yaml file
fs.writeFileSync(`${tablesDirPath}/tables.yaml`, tableIncludes.join("\n"));
// Write tables.yaml
generateTablesIncludes(tablesDirPath, tableIncludes);
// Generate metadata for custom functions
generateFunctions(['get_ud_history']);
// Geneate static metadata files
generateStaticConfig();
}
/**
* Function to get relationships from the properties of an entity
* Generates relationships from the properties of an entity.
*/
function getRelationships(properties: EntityProperties) {
const objectRelationships: (ObjectRelationship | LookupRelationship)[] = [];
const arrayRelationships: ArrayRelationship[] = [];
// Iterate over the properties of the entity
for (const [relationName, relation] of Object.entries(properties)) {
const relation_name = camelToSnakeCase(relationName);
if (relation.type.kind === 'fk') {
......@@ -57,50 +57,39 @@ function getRelationships(properties: EntityProperties) {
name: relationName,
using: {
foreign_key_constraint_on: relation_name + "_id",
}
});
} else if (relation.type.kind === 'list-lookup') {
const entity = camelToSnakeCase(relation.type.entity);
const snake_field = camelToSnakeCase(relation.type.field);
arrayRelationships.push({
name: relationName,
using: {
foreign_key_constraint_on: {
column: snake_field + "_id",
table: {
name: entity,
schema: "public"
}
}
}
},
});
} else if (relation.type.kind === 'lookup') {
} else if (relation.type.kind === 'list-lookup' || relation.type.kind === 'lookup') {
const entity = camelToSnakeCase(relation.type.entity);
const snake_field = camelToSnakeCase(relation.type.field);
objectRelationships.push({
const relationship = {
name: relationName,
using: {
foreign_key_constraint_on: {
column: snake_field + "_id",
table: {
name: entity,
schema: "public"
}
}
schema: "public",
},
},
},
};
if (relation.type.kind === 'list-lookup') {
arrayRelationships.push(relationship as ArrayRelationship);
} else {
objectRelationships.push(relationship);
}
});
}
}
return { objectRelationships, arrayRelationships };
}
/**
* Function to generate metadata for a table
* Generates metadata for a table based on its properties and relationships.
*/
function generateMetadataForTable(tableName: string, properties: EntityProperties): HasuraTable {
const { objectRelationships, arrayRelationships } = getRelationships(properties)
const tableMetadata: HasuraTable = {
const { objectRelationships, arrayRelationships } = getRelationships(properties);
let tableMetadata: HasuraTable = {
table: {
name: tableName,
schema: "public",
......@@ -117,9 +106,17 @@ function generateMetadataForTable(tableName: string, properties: EntityPropertie
}],
};
// Add the computed fields if provided
if (tableName === "identity") {
// We remove ud_history field from arrayRelationships
tableMetadata = addUdHistoryComputedFields(tableMetadata);
}
return tableMetadata;
}
/**
* Adds computed fields for the 'identity' table, if applicable.
*/
function addUdHistoryComputedFields(tableMetadata: HasuraTable) {
const udHistoryIndex = tableMetadata.array_relationships.findIndex((relationship) => relationship.name === "ud_history");
tableMetadata.array_relationships.splice(udHistoryIndex, 1);
......@@ -133,55 +130,52 @@ function generateMetadataForTable(tableName: string, properties: EntityPropertie
},
comment: '"Get UD History by Identity"',
}];
}
return tableMetadata;
}
/**
* Function to write metadata to a YAML file
* Generates metadata for custom functions and writes them to YAML files.
*/
function writeMetadataToFile(typeName: string, metadata: Object) {
const yamlContent = yaml.dump(metadata);
const dirPath = `${metadataDbPath}/tables`;
fs.writeFileSync(`${dirPath}/public_${typeName}.yaml`, yamlContent);
}
/**
* Function to generate metadata for function
*/
function generateMetadataForFunction(names: string[]) {
function generateFunctions(functionNames: string[]) {
const dirPath = `${metadataDbPath}/functions`;
const functionsYAMLPath = `${dirPath}/functions.yaml`;
const includeFunction: string[] = [];
ensureDirectoryExists(dirPath);
for (const name of names) {
// Add function declaration yaml
const functionYAMLContent = yaml.dump({
const functionsYAMLContent: string[] = functionNames.map(name => {
const functionMetadata = {
function: {
name,
schema: 'public'
}
schema: 'public',
},
};
const yamlContent = yaml.dump(functionMetadata);
fs.writeFileSync(`${dirPath}/public_${name}.yaml`, yamlContent);
return `- "!include public_${name}.yaml"`;
});
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
fs.writeFileSync(`${dirPath}/functions.yaml`, functionsYAMLContent.join("\n"));
}
fs.writeFileSync(`${dirPath}/public_${name}.yaml`, functionYAMLContent);
// Add function to functions.yaml
includeFunction.push(`- "!include public_${name}.yaml"`);
}
fs.writeFileSync(functionsYAMLPath, includeFunction.join("\n"));
/**
* Writes metadata to a YAML file for a given table or function.
*/
function writeMetadataToFile(typeName: string, metadata: Object) {
const yamlContent = yaml.dump(metadata);
const dirPath = `${metadataDbPath}/tables`;
fs.writeFileSync(`${dirPath}/public_${typeName}.yaml`, yamlContent);
}
/**
* Function to create databases.yaml file
* Writes the tables.yaml file that includes all table metadata.
*/
function writeStaticYaml() {
function generateTablesIncludes(tablesDirPath: string, tableIncludes: string[]) {
fs.writeFileSync(`${tablesDirPath}/tables.yaml`, tableIncludes.join("\n"));
}
/**
* Writes the static metadata files, including databases.yaml and version.yaml.
*/
function generateStaticConfig() {
const databasesYAMLContent = yaml.dump([{
name: "default",
kind: "postgres",
......@@ -214,16 +208,9 @@ function writeStaticYaml() {
}
});
if (!fs.existsSync(metadataDbPath)) {
fs.mkdirSync(metadataDbPath, { recursive: true });
}
fs.writeFileSync(`${metadataPath}/databases/databases.yaml`, databasesYAMLContent);
fs.writeFileSync(`${metadataDbPath}/tables/public_migrations.yaml`, migrationsYAMLContent);
fs.writeFileSync(`${metadataPath}/version.yaml`, "version: 3\n");
}
generateHasuraMetadata(schemaFile);
generateMetadataForFunction(["get_ud_history"]);
writeStaticYaml();
generateMetadata();
......@@ -32,3 +32,12 @@ export function bytesToString(bytes: Array<number>): string {
export function camelToSnakeCase(str: string): string {
return str.replace(/[A-Z]/g, letter => `_${letter.toLowerCase()}`).replace(/^_/, '');
}
/**
* Ensures the existence of a directory and creates it if it doesn't exist.
*/
export function ensureDirectoryExists(dirPath: string) {
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment