Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CreateJunctionTable checks for table and optional foreign key and better error handling #2278

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 73 additions & 31 deletions backend/src/db/utils.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,82 @@
import { Knex } from "knex";

import { TableName } from "./schemas";

export const createJunctionTable = (knex: Knex, tableName: TableName, table1Name: TableName, table2Name: TableName) =>
knex.schema.createTable(tableName, (table) => {
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
table.uuid(`${table1Name}Id`).unsigned().notNullable(); // Foreign key for table1
table.uuid(`${table2Name}Id`).unsigned().notNullable(); // Foreign key for table2
table.foreign(`${table1Name}Id`).references("id").inTable(table1Name).onDelete("CASCADE");
table.foreign(`${table2Name}Id`).references("id").inTable(table2Name).onDelete("CASCADE");
});
interface JunctionTableOptions {
table1PrimaryKey?: string;
table2PrimaryKey?: string;
indexForeignKeys?: boolean;
}

export const createJunctionTable = async (
knex: Knex,
tableName: TableName,
table1Name: TableName,
table2Name: TableName,
options: JunctionTableOptions = {}
): Promise<void> => {
const {
table1PrimaryKey = 'id',
table2PrimaryKey = 'id',
indexForeignKeys = false
} = options;

// Check if the table already exists
const tableExists = await knex.schema.hasTable(tableName);

if (!tableExists) {
await knex.schema.createTable(tableName, (table) => {
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());

// Create foreign key columns
table.uuid(`${table1Name}Id`).unsigned().notNullable();
table.uuid(`${table2Name}Id`).unsigned().notNullable();

// Set up foreign key constraints
table.foreign(`${table1Name}Id`)
.references(table1PrimaryKey)
.inTable(table1Name)
.onDelete("CASCADE");

table.foreign(`${table2Name}Id`)
.references(table2PrimaryKey)
.inTable(table2Name)
.onDelete("CASCADE");

// Add indexes on foreign key columns if specified
if (indexForeignKeys) {
table.index(`${table1Name}Id`);
table.index(`${table2Name}Id`);
}
});
}
};

// one time logic
// this is a postgres function log to set updateAt to present time whenever row gets updated
export const createUpdateAtTriggerFunction = (knex: Knex) =>
knex.raw(`
CREATE OR REPLACE FUNCTION on_update_timestamp() RETURNS TRIGGER AS $$ BEGIN NEW."updatedAt" = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
`);
export const createUpdateAtTriggerFunction = async (knex: Knex): Promise<void> => {
await knex.raw(`
CREATE OR REPLACE FUNCTION on_update_timestamp() RETURNS TRIGGER AS $$ BEGIN NEW."updatedAt" = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
`);
};

export const dropUpdatedAtTriggerFunction = (knex: Knex) =>
knex.raw(`
DROP FUNCTION IF EXISTS on_update_timestamp() CASCADE;
`);
export const dropUpdatedAtTriggerFunction = async (knex: Knex): Promise<void> => {
await knex.raw(`
DROP FUNCTION IF EXISTS on_update_timestamp() CASCADE;
`);
};

// we would be using this to apply updatedAt where ever we wanta
// remember to set `timestamps(true,true,true)` before this on schema
export const createOnUpdateTrigger = (knex: Knex, tableName: string) =>
knex.raw(`
CREATE TRIGGER "${tableName}_updatedAt"
BEFORE UPDATE ON ${tableName}
FOR EACH ROW
EXECUTE PROCEDURE on_update_timestamp();
`);
export const createOnUpdateTrigger = async (knex: Knex, tableName: string): Promise<void> => {
await knex.raw(`
CREATE TRIGGER "${tableName}_updatedAt"
BEFORE UPDATE ON ${tableName}
FOR EACH ROW
EXECUTE PROCEDURE on_update_timestamp();
`);
};

export const dropOnUpdateTrigger = (knex: Knex, tableName: string) =>
knex.raw(`DROP TRIGGER IF EXISTS "${tableName}_updatedAt" ON ${tableName}`);
export const dropOnUpdateTrigger = async (knex: Knex, tableName: string): Promise<void> => {
await knex.raw(`DROP TRIGGER IF EXISTS "${tableName}_updatedAt" ON ${tableName}`);
};