summary refs log tree commit diff
path: root/src/database/entities/BaseClass.ts
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2026-06-11 22:16:07 +0200
committerRory& <root@rory.gay>2026-06-12 17:58:18 +0200
commitc77d00e243f7cf104b8b0f8b26f339849dbd040f (patch)
tree457a1aac1da052b3be56812a90c8ca50e6b3df51 /src/database/entities/BaseClass.ts
parentMove database to toplevel (diff)
downloadserver-ts-c77d00e243f7cf104b8b0f8b26f339849dbd040f.tar.xz
Move entities to database
Diffstat (limited to 'src/database/entities/BaseClass.ts')
-rw-r--r--src/database/entities/BaseClass.ts125
1 files changed, 125 insertions, 0 deletions
diff --git a/src/database/entities/BaseClass.ts b/src/database/entities/BaseClass.ts
new file mode 100644

index 00000000..770c96e8 --- /dev/null +++ b/src/database/entities/BaseClass.ts
@@ -0,0 +1,125 @@ +/* + Spacebar: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Spacebar and Spacebar Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +import { BaseEntity, BeforeInsert, BeforeUpdate, FindOptionsWhere, PrimaryColumn } from "typeorm"; +import { getDatabase } from "../Database"; +import { Snowflake } from "../../util/util/Snowflake"; +import { OrmUtils } from "../../util/imports/OrmUtils"; +import { annotationsKey } from "../../util/util/Decorators"; + +export class BaseClassWithoutId extends BaseEntity { + private get construct() { + return this.constructor; + } + + // stores custom annotations we may stick on the properties + [annotationsKey]: { [p: string]: string[] }; + + // retrieves the custom annotations as its not super straight forward + get_annotations() { + return Object.getPrototypeOf(this)[annotationsKey]; + } + + // Loops through all the keys and compares it to annotations. If the RemoveEmpty is there it sets the value to undefined if null + clean_data() { + const annotations = this.get_annotations(); + if (annotations == undefined || annotations.length > 0) + //prevent errors if there are no annotations on an object + return; + for (const key in this) { + if ( + key in this && // This object has this property, should never fail but better to be safe + key in annotations && // If this property has an annotation + annotations[key].indexOf("JsonRemoveEmpty") > -1 && // if one of the annotations is JsonRemoveEmpty + (this[key] == null || // If this property is null + (typeof this[key] == "object" && Object.keys(this[key]).length == 0)) + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this[key] = undefined; // set to undefined to remove + } + if ( + key in this && // This object has this property, should never fail but better to be safe + key in annotations && // If this property has an annotation + annotations[key].indexOf("JsonNumber") > -1 && // if one of the annotations is JsonRemoveEmpty + typeof this[key] == "string" // and its a String + ) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + this[key] = Number(this[key]); // convert string back to number + } + } + return this; + } + + private get metadata() { + return getDatabase()?.getMetadata(this.construct); + } + + assign(props: object) { + OrmUtils.mergeDeep(this, props); + return this; + } + + // TODO: fix eslint + // eslint-disable-next-line @typescript-eslint/no-explicit-any + toJSON(): any { + this.clean_data(); + return Object.fromEntries( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + this.metadata!.columns // @ts-ignore + .map((x) => [x.propertyName, this[x.propertyName]]) + .concat( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.metadata.relations.map((x) => [ + x.propertyName, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this[x.propertyName], + ]), + ), + ); + } + + static increment<T extends BaseClass>(conditions: FindOptionsWhere<T>, propertyPath: string, value: number | string) { + const repository = this.getRepository(); + return repository.increment(conditions, propertyPath, value); + } + + static decrement<T extends BaseClass>(conditions: FindOptionsWhere<T>, propertyPath: string, value: number | string) { + const repository = this.getRepository(); + return repository.decrement(conditions, propertyPath, value); + } + + public async insert(): Promise<this> { + await getDatabase()!.getRepository(this.construct).insert(this); + return this; + } +} + +export class BaseClass extends BaseClassWithoutId { + @PrimaryColumn({ type: "int8" }) + id: string = Snowflake.generate(); + + @BeforeUpdate() + @BeforeInsert() + _do_validate() { + if (!this.id) this.id = Snowflake.generate(); + } +}