Skip to content

Commit

Permalink
Merge pull request #28 from outerbase/bwilmoth/model-attach-connection
Browse files Browse the repository at this point in the history
Attach connections and actions to models
  • Loading branch information
Brayden authored Jun 5, 2024
2 parents c4e34aa + c1ee4ce commit 9421674
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 12 deletions.
30 changes: 23 additions & 7 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { BaseTable } from 'src/models'
import { Connection } from './connections'
import { Query, constructRawQuery } from './query'
import { QueryParams, QueryType } from './query-params'
Expand Down Expand Up @@ -70,8 +71,16 @@ export function Outerbase(connection: Connection): OuterbaseType {
return this
},
where(condition) {
if (this.queryBuilder.whereClauses)
this.queryBuilder.whereClauses.push(condition)
// Check if `condition` is an array of conditions
if (Array.isArray(condition)) {
if (this.queryBuilder.whereClauses) {
this.queryBuilder.whereClauses.push(`(${condition.join(" AND ")})`);
}
} else {
if (this.queryBuilder.whereClauses) {
this.queryBuilder.whereClauses.push(condition);
}
}
return this
},
limit(limit) {
Expand Down Expand Up @@ -212,7 +221,8 @@ export function Outerbase(connection: Connection): OuterbaseType {
const response = await connection.query(query)
let result = mapToClass(
response?.data,
this.queryBuilder.asClass
this.queryBuilder.asClass,
connection
)
return {
data: result,
Expand All @@ -234,7 +244,8 @@ export function Outerbase(connection: Connection): OuterbaseType {
const response = await connection.query({ query, parameters })
let result = mapToClass(
response?.data,
this.queryBuilder.asClass
this.queryBuilder.asClass,
connection
)
return {
data: result,
Expand Down Expand Up @@ -394,11 +405,16 @@ function buildQueryString(
return query
}

function mapToClass<T>(data: any | any[], ctor: new (data: any) => T): T | T[] {
function mapToClass<T>(data: any | any[], ctor: new (data: any) => T, connection?: Connection): T | T[] {
if (Array.isArray(data)) {
let array = data.map((item) => new ctor(item))
return array
return data.map(item => {
const model = new ctor(item) as BaseTable;
model._connection = connection;
return model;
}) as T[];
} else {
const model = new ctor(data) as BaseTable
model._connection = connection
return new ctor(data)
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/generators/model-template.handlebars
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export class {{ capitalize (camelCase name) }} extends BaseTable {
constructor(data: any) {
super({
_name: "{{name}}",
_schema: "{{schema}}"
_schema: "{{schema}}",
_original: data
});

{{#each columns}}
Expand Down
77 changes: 74 additions & 3 deletions src/models/decorators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* 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>();
Expand Down Expand Up @@ -48,16 +49,38 @@ export function Column(options?: {
};
}

/**
* Indicates that the provided property name is a valid column in the database
* for this model class (database table).
*
* @param targetClass
* @param propertyName
* @returns Boolean – whether the property is a column
*/
export function isColumn(targetClass: Function, propertyName: string): boolean {
const metadata = metadataRegistry.get(targetClass);
return metadata && metadata.columns[propertyName];
}

/**
* Indicates whether a column is a unique column in the database.
*
* @param targetClass
* @param propertyName
* @returns Boolean – whether the column is a unique column
*/
export function isPropertyUnique(targetClass: Function, propertyName: string): boolean {
const metadata = metadataRegistry.get(targetClass);
return metadata && metadata[propertyName] && metadata[propertyName].unique
}

/**
* Indicates whether a column can be set as a null value in the database.
*
* @param targetClass
* @param propertyName
* @returns Boolean – whether the column can be null
*/
export function isColumnNullable(targetClass: Function, propertyName: string): boolean {
const metadata = metadataRegistry.get(targetClass);
if (metadata && metadata.columns[propertyName] && metadata.columns[propertyName].hasOwnProperty('nullable')) {
Expand All @@ -66,10 +89,58 @@ export function isColumnNullable(targetClass: Function, propertyName: string): b
return false;
}

export function getPrimaryKey(targetClass: Function): string | symbol | undefined {
/**
* Retrieve the primary key column names for a given model class
* based on the metadata stored by the decorators.
*
* @param targetClass
* @returns Array of strings – the primary key column names
*/
export function getPrimaryKeys(targetClass: Function): string[] {
const metadata = metadataRegistry.get(targetClass);
if (metadata) {
// Return the actual column name as it is stored in the database
const primaryKeyColumnsNames = Object.keys(metadata.columns)
.filter((key) => metadata.columns[key].primary)
.map((key) => metadata.columns[key].name);

return primaryKeyColumnsNames;
}
return [];
}

/**
* Based on the column name value, however it is stored in the database, get the actual
* key name of the property in the class.
*
* @param targetClass
* @param columnName
* @returns String – the property name
*/
export function getColumnValueFromName(targetClass: Function, columnName: string): string | null {
const metadata = metadataRegistry.get(targetClass);
if (metadata) {
for (const key in metadata.columns) {
if (metadata.columns[key].name === columnName) {
return key;
}
}
}
return null;
}

/**
* Based on the actual property name, usually camel cased, get the column name value
* that is stored in the database.
*
* @param targetClass
* @param propertyName
* @returns String – the column name
*/
export function getColumnValueFromProperty(targetClass: Function, propertyName: string): string | null {
const metadata = metadataRegistry.get(targetClass);
if (metadata) {
return metadata.primaryKey;
return metadata.columns[propertyName].name;
}
return undefined;
return null;
}
Loading

0 comments on commit 9421674

Please sign in to comment.