Commit dfaf4417 authored by William Naslund's avatar William Naslund

Added table constraints

parent e10d5919
......@@ -5,18 +5,18 @@ services:
postgres:
image: postgres:11-alpine
environment:
POSTGRES_PASSWORD: admin
POSTGRES_USER: admin
POSTGRES_DB: test
POSTGRES_PASSWORD: swirl
POSTGRES_USER: swirl
POSTGRES_DB: swirl
volumes:
- "./volumes/postgresql:/var/lib/postgresql/data"
ports:
- "5432:5432"
pgadmin:
image: dpage/pgadmin4:4
environment:
PGADMIN_DEFAULT_EMAIL: admin@starlightconsultants.com
PGADMIN_DEFAULT_PASSWORD: test
PGADMIN_DEFAULT_EMAIL: swirl
PGADMIN_DEFAULT_PASSWORD: swirl
ports:
- "8080:80"
This diff is collapsed.
......@@ -5,6 +5,12 @@ export interface DBConstraint {
/** Returns the SQL necessary to create the constraint */
create(): Promise<string>;
/** If this is a column constraint, this is the SQL to create and drop the constraint */
columnModifySQL?(): {
create: string;
drop: string;
};
/** Returns the type identifer of this constraint (used in the constraint name) */
getTypeId(): string;
......
import { DBAdapter } from "./adapter/database-adapter";
import { DBModelConstructor, DBModel } from "./model";
import { getTableInformation, getTableFields, getParentLookups } from "./internal/meta-keys";
import { DBConstraintConstructor, DBForeignKeyConstraint, DBPrimaryKeyConstraint } from "./constraints";
import { DBTableInformation } from "./decorators";
import { getTableInformation, getTableFields, getParentLookups, getTableConstraints } from "./internal/meta-keys";
import { DBConstraintConstructor, DBForeignKeyConstraint, DBPrimaryKeyConstraint, DBConstraint } from "./constraints";
import { DBTableInformation, DBFieldInformation } from "./decorators";
import { DBQuery } from "./query";
import { MODEL_DATABASE } from "./internal/model-registry";
......@@ -26,6 +26,7 @@ export class Database {
/** Migrates the database to its currently registered components */
async migrate() {
const constraints: ConstraintIdentifier[] = [];
// Setup Tables
for(const modelType of this.tables) {
......@@ -60,17 +61,12 @@ export class Database {
// Id Unique Constraint
if(tableInfo.options && tableInfo.options.id) {
const idConstraint = new DBPrimaryKeyConstraint([ String(tableInfo.options.id) ]);
const idConstraintName = `${tableInfo.name}__id__${idConstraint.getTypeId()}`;
const existingType = await this.getConstraintType(tableInfo, idConstraintName);
if(existingType == null) {
await this.adapter.constraint.create(tableInfo, idConstraintName, idConstraint);
} else {
if(existingType !== DBPrimaryKeyConstraint) {
await this.adapter.constraint.drop(tableInfo, idConstraintName);
await this.adapter.constraint.create(tableInfo, idConstraintName, idConstraint);
}
}
constraints.push({
table: tableInfo,
constraint: idConstraint,
name: `${tableInfo.name}__id__${idConstraint.getTypeId()}`
});
}
}
......@@ -82,21 +78,42 @@ export class Database {
// Parent lookup constraints
for(const lookup of getParentLookups(modelType)) {
const parentInfo = getTableInformation(lookup.model);
const lookupConstraint = new DBForeignKeyConstraint([ lookup.name ], parentInfo, [ String(parentInfo.options.id) ]);
const constraintName = `${tableInfo.name}__lookup__${lookup.name}`;
const existingType = await this.getConstraintType(tableInfo, constraintName);
if(existingType == null) {
await this.adapter.constraint.create(tableInfo, constraintName, lookupConstraint);
} else {
if(existingType !== DBForeignKeyConstraint) {
await this.adapter.constraint.drop(tableInfo, constraintName);
await this.adapter.constraint.create(tableInfo, constraintName, lookupConstraint);
}
}
constraints.push({
table: tableInfo,
constraint: new DBForeignKeyConstraint([ lookup.name ], parentInfo, [ String(parentInfo.options.id) ]),
name: `${tableInfo.name}__lookup__${lookup.name}`
});
}
}
// Setup User Table Constraints
for(const modelType of this.tables) {
for(const tableConstraint of getTableConstraints(modelType)) {
const tableInfo = getTableInformation(tableConstraint.table);
constraints.push({
table: tableInfo,
constraint: tableConstraint.constraint,
name: `${tableInfo.name}__table_constraint__${tableConstraint.name}`
});
}
}
// Setup Table Constraints
for(const constraintInfo of constraints) {
const existingConstraintType = await this.getConstraintType(constraintInfo.table, constraintInfo.name);
if(existingConstraintType == null) {
await this.adapter.constraint.create(constraintInfo.table, constraintInfo.name, constraintInfo.constraint);
} else {
if(existingConstraintType !== constraintInfo.constraint.constructor) {
await this.adapter.constraint.drop(constraintInfo.table, constraintInfo.name);
await this.adapter.constraint.create(constraintInfo.table, constraintInfo.name, constraintInfo.constraint);
}
}
}
}
......@@ -138,3 +155,12 @@ export class Database {
}
}
/** Identifies a constraint for a table and optionally a column */
interface ConstraintIdentifier {
table: DBTableInformation;
column?: DBFieldInformation;
constraint: DBConstraint;
name: string;
}
import "reflect-metadata";
import { DBConstraint } from "../constraints/constraint";
import { DBModelConstructor, DBModel } from "../model";
import { TABLE_CONSTRAINTS, DBRegisteredTableConstraint, COLUMN_CONSTRAINTS, DBRegisteredColumnConstraint } from "../internal/meta-keys";
/** Adds a constraint to a table */
export function DBTableConstraint(name: string, constraint: DBConstraint) {
return function(constructor: DBModelConstructor<any>) {
let constraintList = Reflect.getMetadata(TABLE_CONSTRAINTS, constructor) as DBRegisteredTableConstraint[];
if(constraintList == null) {
constraintList = [];
}
constraintList.push({
table: constructor,
name: name,
constraint: constraint
});
Reflect.defineMetadata(TABLE_CONSTRAINTS, constraintList, constructor);
return constructor;
}
}
/** Adds a constraint to a column */
export function DBColumnConstraint(name: string, constraint: DBConstraint) {
return function<T extends DBModel>(proto: T, propertyKey: string | symbol) {
let constraintList = Reflect.getMetadata(COLUMN_CONSTRAINTS, proto.constructor, propertyKey) as DBRegisteredColumnConstraint[];
if(constraintList == null) {
constraintList = [];
}
constraintList.push({
table: proto.constructor,
name: name,
propertyKey: propertyKey,
constraint: constraint
});
Reflect.defineMetadata(COLUMN_CONSTRAINTS, constraintList, proto.constructor, propertyKey);
}
}
export * from './db-table';
export * from './db-field';
export * from './db-lookup';
\ No newline at end of file
export * from './db-lookup';
export * from './db-constraint';
\ No newline at end of file
import { DBModelConstructor, DBModel } from "../model";
import { DBTableInformation, DBFieldInformation, DBLookupInformation } from "../decorators";
import { DBConstraint } from "../constraints";
/** Identifier for table information */
export const TABLE_INFO = Symbol();
......@@ -19,6 +20,13 @@ export const FIELD_INFO = Symbol();
/** Identifier for a foreign key (AKA a lookup) */
export const LOOKUP_INFO = Symbol();
/** Identifies registered table constraints */
export const TABLE_CONSTRAINTS = Symbol();
/** Identifies registered column constraints */
export const COLUMN_CONSTRAINTS = Symbol();
/** Loads the table information for a constructor */
export function getTableInformation(modelType: DBModelConstructor<any>): DBTableInformation {
return Reflect.getMetadata(TABLE_INFO, modelType);
......@@ -72,3 +80,24 @@ export function getFieldInformation<T extends DBModel>(modelType: DBModelConstru
export function getParentLookups(modelType: DBModelConstructor<any>): DBLookupInformation[] {
return Reflect.getMetadata(TABLE_PARENT_LOOKUPS, modelType) || [];
}
/** Loads the table constraints for a given table */
export function getTableConstraints(modelType: DBModelConstructor<any>): DBRegisteredTableConstraint[] {
return Reflect.getMetadata(TABLE_CONSTRAINTS, modelType) || [];
}
/** Loads the column constraints for a given column */
export function getColumnConstraints<T extends DBModel>(modelType: DBModelConstructor<T>, propertyKey: keyof T): DBRegisteredColumnConstraint[] {
return Reflect.getMetadata(COLUMN_CONSTRAINTS, modelType, propertyKey as any) || [];
}
export interface DBRegisteredTableConstraint {
table: DBModelConstructor<any>;
name: string;
constraint: DBConstraint;
}
export interface DBRegisteredColumnConstraint extends DBRegisteredTableConstraint {
propertyKey: any;
}
......@@ -3,8 +3,8 @@ import { PGAdapter } from "@swirl/db";
export function getTestAdapter() {
return new PGAdapter({
host: 'localhost',
user: 'admin',
password: 'admin',
database: 'test'
user: 'swirl',
password: 'swirl',
database: 'swirl'
});
}
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment