Skip to content

Commit

Permalink
More settings
Browse files Browse the repository at this point in the history
  • Loading branch information
caleb-mabry committed Jun 5, 2024
1 parent 43f8b95 commit e5b71b6
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 34 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
83 changes: 50 additions & 33 deletions src/models/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,75 +1,92 @@
/**
* A registry of metadata for classes decorated with the @Entity decorator.
* The metadata is stored as a Map where the key is the class constructor and
* The metadata is stored as a Map where the key is the class constructor and
* the value is an object with the following properties:
* - columns: an object where the keys are property keys and the values are objects with column options
* - primaryKey: the property key of the primary key column
* @type {Map<Function, any>}
*/
export const metadataRegistry = new Map<Function, any>();
export const metadataRegistry = new Map<Function, any>()

export function Column(options?: {
unique?: boolean,
primary?: boolean,
nullable?: boolean,
name?: string,
export function Column(options?: {
unique?: boolean
primary?: boolean
nullable?: boolean
name?: string
relation?: any
}): PropertyDecorator {
return function(target: any, propertyKey: string | symbol): void {
const constructor = target.constructor;
return function (target: any, propertyKey: string | symbol): void {
const constructor = target.constructor
if (!metadataRegistry.has(constructor)) {
metadataRegistry.set(constructor, { columns: {}, primaryKey: undefined });
metadataRegistry.set(constructor, {
columns: {},
primaryKey: undefined,
})
}

const classMetadata = metadataRegistry.get(constructor);
const classMetadata = metadataRegistry.get(constructor)

const columnName = options?.name || propertyKey.toString();
const relationName = options?.relation || propertyKey.toString();
const columnName = options?.name || propertyKey.toString()
const relationName = options?.relation || propertyKey.toString()

// Initialize the column metadata if it doesn't exist
if (!classMetadata.columns[propertyKey]) {
classMetadata.columns[propertyKey] = {};
classMetadata.columns[propertyKey] = {}
}

// Update the column metadata with new options
classMetadata.columns[propertyKey] = {
...classMetadata.columns[propertyKey],
...options,
name: columnName,
relation: relationName
};
relation: relationName,
}

if (options?.primary) {
if (classMetadata.primaryKey) {
throw new Error(`Multiple primary keys are not allowed: ${constructor.name} already has a primary key on property '${String(classMetadata.primaryKey)}'.`);
throw new Error(
`Multiple primary keys are not allowed: ${constructor.name} already has a primary key on property '${String(classMetadata.primaryKey)}'.`
)
}
classMetadata.primaryKey = propertyKey;
classMetadata.primaryKey = propertyKey
}
};
}
}

export function isColumn(targetClass: Function, propertyName: string): boolean {
const metadata = metadataRegistry.get(targetClass);
return metadata && metadata.columns[propertyName];
const metadata = metadataRegistry.get(targetClass)
return metadata && metadata.columns[propertyName]
}

export function isPropertyUnique(targetClass: Function, propertyName: string): boolean {
const metadata = metadataRegistry.get(targetClass);
export function isPropertyUnique(
targetClass: Function,
propertyName: string
): boolean {
const metadata = metadataRegistry.get(targetClass)
return metadata && metadata[propertyName] && metadata[propertyName].unique
}

export function isColumnNullable(targetClass: Function, propertyName: string): boolean {
const metadata = metadataRegistry.get(targetClass);
if (metadata && metadata.columns[propertyName] && metadata.columns[propertyName].hasOwnProperty('nullable')) {
return metadata.columns[propertyName].nullable;
export function isColumnNullable(
targetClass: Function,
propertyName: string
): boolean {
const metadata = metadataRegistry.get(targetClass)
if (
metadata &&
metadata.columns[propertyName] &&
metadata.columns[propertyName].hasOwnProperty('nullable')
) {
return metadata.columns[propertyName].nullable
}
return false;
return false
}

export function getPrimaryKey(targetClass: Function): string | symbol | undefined {
const metadata = metadataRegistry.get(targetClass);
export function getPrimaryKey(
targetClass: Function
): string | symbol | undefined {
const metadata = metadataRegistry.get(targetClass)
if (metadata) {
return metadata.primaryKey;
return metadata.primaryKey
}
return undefined;
}
return undefined
}
5 changes: 4 additions & 1 deletion tests/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,12 @@ describe('Query Builder', () => {
test('Update a table', () => {
const query = outerbaseD1
.update({ crab: 'cake' })
.where('cheese = thing')
.into('testTable')
.toString()
expect(query).toBe("UPDATE testTable SET crab = 'cake'")
expect(query).toBe(
"UPDATE testTable SET crab = 'cake' WHERE cheese = thing"
)
})
test('Update a table with named', () => {
const query = outerbase
Expand Down
63 changes: 63 additions & 0 deletions tests/models/decorators.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { describe, expect, it } from '@jest/globals'
import {
metadataRegistry,
Column,
isColumn,
isPropertyUnique,
isColumnNullable,
getPrimaryKey,
} from '../../src/models/decorators' // Adjust the path as necessary

class TestEntity {
@Column({ primary: true })
id: number

@Column({ unique: true, nullable: false })
uniqueColumn: string

@Column({ nullable: true })
nullableColumn: string

@Column()
regularColumn: string
}

describe('metadataRegistry', () => {
it('should register primary key column', () => {
expect(getPrimaryKey(TestEntity)).toBe('id')
})

it('should register columns with correct metadata', () => {
const metadata = metadataRegistry.get(TestEntity)
expect(metadata).toBeDefined()
expect(metadata.columns).toHaveProperty('id')
expect(metadata.columns.id).toHaveProperty('primary', true)
expect(metadata.columns.id).toHaveProperty('name', 'id')
expect(metadata.columns).toHaveProperty('uniqueColumn')
expect(metadata.columns.uniqueColumn).toHaveProperty('unique', true)
expect(metadata.columns.uniqueColumn).toHaveProperty('nullable', false)
expect(metadata.columns).toHaveProperty('nullableColumn')
expect(metadata.columns.nullableColumn).toHaveProperty('nullable', true)
expect(metadata.columns).toHaveProperty('regularColumn')
})

it('should detect if a property is a column', () => {
expect(isColumn(TestEntity, 'id')).toBe(true)
expect(isColumn(TestEntity, 'uniqueColumn')).toBe(true)
expect(isColumn(TestEntity, 'nullableColumn')).toBe(true)
expect(isColumn(TestEntity, 'regularColumn')).toBe(true)
expect(isColumn(TestEntity, 'nonExistentColumn')).toBe(false)
})

it('should detect if a column is unique', () => {
expect(isPropertyUnique(TestEntity, 'uniqueColumn')).toBe(true)
expect(isPropertyUnique(TestEntity, 'nullableColumn')).toBe(false)
expect(isPropertyUnique(TestEntity, 'regularColumn')).toBe(false)
})

it('should detect if a column is nullable', () => {
expect(isColumnNullable(TestEntity, 'nullableColumn')).toBe(true)
expect(isColumnNullable(TestEntity, 'uniqueColumn')).toBe(false)
expect(isColumnNullable(TestEntity, 'regularColumn')).toBe(false)
})
})
31 changes: 31 additions & 0 deletions tests/models/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { describe, expect, it } from '@jest/globals'
import { BaseTable } from '../../src/models/index' // Adjust the path as necessary

describe('BaseTable class', () => {
it('should create an instance with a name and schema', () => {
const name = 'testTable'
const schema = 'testSchema'
const baseTable = new BaseTable({ _name: name, _schema: schema })

expect(baseTable).toBeInstanceOf(BaseTable)
expect(baseTable._name).toBe(name)
expect(baseTable._schema).toBe(schema)
})

it('should create an instance with only a name', () => {
const name = 'testTable'
const baseTable = new BaseTable({ _name: name })

expect(baseTable).toBeInstanceOf(BaseTable)
expect(baseTable._name).toBe(name)
expect(baseTable._schema).toBeUndefined()
})

it('should handle missing schema parameter', () => {
const name = 'testTable'
const baseTable = new BaseTable({ _name: name })

expect(baseTable._name).toBe(name)
expect(baseTable._schema).toBeUndefined()
})
})
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"outDir": "./dist",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strictPropertyInitialization": false,
"strict": true,
"baseUrl": "./",
"paths": {
Expand Down

0 comments on commit e5b71b6

Please sign in to comment.