refactor(models): Moved models and Repositories to database

This commit is contained in:
Michel Fedde 2025-06-22 22:59:38 +02:00
parent d5f5fe5f1a
commit 9155f630d9
18 changed files with 42 additions and 42 deletions

View file

@ -0,0 +1,65 @@
import {Repository} from "./Repository";
import GroupConfiguration, {DBGroupConfiguration} from "../tables/GroupConfiguration";
import {GroupConfigurationModel} from "../Models/GroupConfigurationModel";
import {GroupModel} from "../Models/GroupModel";
import {Nullable} from "../../types/Nullable";
import {DatabaseConnection} from "../DatabaseConnection";
import {GroupRepository} from "./GroupRepository";
export class GroupConfigurationRepository extends Repository<GroupConfigurationModel, DBGroupConfiguration> {
constructor(
protected readonly database: DatabaseConnection,
private readonly groupRepository: GroupRepository,
) {
super(
database,
GroupConfiguration
);
}
public findGroupConfigurations(group: GroupModel): GroupConfigurationModel[] {
return this.database.fetchAll<number, DBGroupConfiguration>(`
SELECT * FROM groupConfiguration WHERE groupid = ?`,
group.id
).map((config) => {
return this.convertToModelType(config, group);
})
}
public findConfigurationByPath(group: GroupModel, path: string): Nullable<GroupConfigurationModel> {
const result = this.database.fetch<number, DBGroupConfiguration>(`
SELECT * FROM groupConfiguration WHERE groupid = ? AND key = ?`,
group.id,
path
);
if (!result) {
return null;
}
return this.convertToModelType(result, group);
}
protected convertToModelType(intermediateModel: DBGroupConfiguration | undefined, group: Nullable<GroupModel> = null): GroupConfigurationModel {
if (!intermediateModel) {
throw new Error("No intermediate model provided");
}
return {
id: intermediateModel.id,
group: group ?? this.groupRepository.getById(intermediateModel.id),
key: intermediateModel.key,
value: intermediateModel.value,
}
}
protected convertToCreateObject(instance: Partial<GroupConfigurationModel>): object {
return {
groupid: instance.group?.id ?? undefined,
key: instance.key ?? undefined,
value: instance.value ?? undefined,
}
}
}

View file

@ -0,0 +1,114 @@
import {Repository} from "./Repository";
import {GroupModel} from "../Models/GroupModel";
import Groups, {DBGroup} from "../tables/Groups";
import {DatabaseConnection} from "../DatabaseConnection";
import {GuildMember, UserFlagsBitField} from "discord.js";
import {Nullable} from "../../types/Nullable";
import {PlaydateRepository} from "./PlaydateRepository";
import {Container} from "../../Container/Container";
export class GroupRepository extends Repository<GroupModel, DBGroup> {
constructor(
protected readonly database: DatabaseConnection,
) {
super(
database,
Groups
);
}
public findGroupByName(name: string): Nullable<GroupModel> {
const result = this.database.fetch<string, DBGroup>(
`SELECT * FROM groups WHERE name = ? LIMIT 1`,
name
)
if (!result) {
return undefined;
}
return this.convertToModelType(result);
}
public findGroupsByRoles(server: string, roleIds: string[]): GroupModel[] {
const template = roleIds.map(_roleId => '?').join(',');
const dbResult = this.database.fetchAll<number[], DBGroup>(`
SELECT * FROM groups WHERE server = ? AND role IN (${template})
`,
server,
...roleIds)
return dbResult.map((result) => this.convertToModelType(result));
}
public findGroupsByMember(member: GuildMember, onlyLeader: boolean = false) {
if (!member) {
throw new Error("Can't find member for guild: none given");
}
const groups = this.findGroupsByRoles(member.guild.id, [...member.roles.cache.keys()])
if (!onlyLeader) {
return groups;
}
return groups.filter((group: GroupModel) => {
return group.leader.memberid === member.id;
})
}
public isMemberInGroup(member: Nullable<GuildMember>, group: GroupModel): boolean
{
if (!member) {
throw new Error("Can't find member for guild: none given");
}
const groups = this.findGroupsByRoles(member.guild.id, [...member.roles.cache.keys()])
return groups.some((dbGroup) => {
return group.id === dbGroup.id;
})
}
public deleteGroup(group: GroupModel): void {
this.delete(group.id);
const repo = Container.get<PlaydateRepository>(PlaydateRepository.name);
const playdates = repo.findFromGroup(group, true)
playdates.forEach((playdate) => {
repo.delete(playdate.id);
})
}
protected convertToModelType(intermediateModel: DBGroup | undefined): GroupModel {
if (!intermediateModel) {
throw new Error("No intermediate model provided");
}
return {
id: intermediateModel.id,
name: intermediateModel.name,
leader: {
server: intermediateModel.server,
memberid: intermediateModel.leader
},
role: {
server: intermediateModel.server,
roleid: intermediateModel.role
}
}
}
protected convertToCreateObject(instance: Partial<GroupModel>): object {
return {
name: instance.name ?? '',
server: instance.role?.server ?? null,
leader: instance.leader?.memberid ?? null,
role: instance.role?.roleid ?? null,
}
}
}

View file

@ -0,0 +1,102 @@
import {Repository} from "./Repository";
import {PlaydateModel} from "../Models/PlaydateModel";
import Playdate, {DBPlaydate} from "../tables/Playdate";
import {DatabaseConnection} from "../DatabaseConnection";
import {GroupRepository} from "./GroupRepository";
import {GroupModel} from "../Models/GroupModel";
import {Nullable} from "../../types/Nullable";
export class PlaydateRepository extends Repository<PlaydateModel, DBPlaydate> {
constructor(
protected readonly database: DatabaseConnection,
private readonly groupRepository: GroupRepository,
) {
super(
database,
Playdate
);
}
findFromGroup(group: GroupModel, all = false) {
let sql = `SELECT * FROM ${this.schema.name} WHERE groupid = ?`;
const params = [group.id];
if (!all) {
sql += " AND time_from > ?"
params.push(new Date().getTime())
}
const finds = this.database.fetchAll<number, DBPlaydate>(
sql,
...params
);
return finds.map((playdate) => this.convertToModelType(playdate, group));
}
findPlaydatesInRange(fromDate: Date | number, toDate: Date | number | undefined = undefined, group: GroupModel | undefined = undefined) {
if (fromDate instanceof Date) {
fromDate = fromDate.getTime();
}
if (toDate instanceof Date) {
toDate = toDate.getTime();
}
let sql = `SELECT * FROM ${this.schema.name} WHERE time_from > ?`;
const params = [fromDate];
if (toDate) {
sql = `${sql} AND time_from < ?`
params.push(toDate);
}
if (group) {
sql = `${sql} AND groupid = ?`
params.push(<number>group.id)
}
const finds = this.database.fetchAll<number, DBPlaydate>(
sql,
...params
);
return finds.map((playdate) => this.convertToModelType(playdate, group));
}
getNextPlaydateForGroup(group: GroupModel): PlaydateModel | null {
const sql = `SELECT * FROM ${this.schema.name} WHERE groupid = ? AND time_from > ? ORDER BY time_from LIMIT 1`;
const find = this.database.fetch<number, DBPlaydate>(
sql,
group.id,
Date.now()
)
if (!find) {
return null;
}
return this.convertToModelType(find, group)
}
protected convertToModelType(intermediateModel: DBPlaydate | undefined, fixedGroup: Nullable<GroupModel> = null): PlaydateModel {
if (!intermediateModel) {
throw new Error("Unable to convert the playdate model");
}
return {
id: intermediateModel.id,
group: fixedGroup ?? this.groupRepository.getById(intermediateModel.groupid),
from_time: new Date(intermediateModel.time_from),
to_time: new Date(intermediateModel.time_to),
};
}
protected convertToCreateObject(instance: Partial<PlaydateModel>): object {
return {
groupid: instance.group?.id ?? null,
time_from: instance.from_time?.getTime() ?? 0,
time_to: instance.to_time?.getTime() ?? 0,
}
}
}

View file

@ -0,0 +1,85 @@
import {DatabaseConnection} from "../DatabaseConnection";
import {Model} from "../Models/Model";
import {Nullable} from "../../types/Nullable";
import {DatabaseDefinition} from "../DatabaseDefinition";
import {Container} from "../../Container/Container";
import {EventHandler} from "../../Events/EventHandler";
import {ElementCreatedEvent} from "../../Events/EventClasses/ElementCreatedEvent";
export class Repository<ModelType extends Model, IntermediateModelType = unknown> {
constructor(
protected readonly database: DatabaseConnection,
public readonly schema: DatabaseDefinition,
) {
}
public create(instance: Partial<ModelType>): number | bigint {
const columnNames = this.schema.columns.filter((column) => {
return !column.primaryKey
}).map((column) => {
return column.name;
});
const createObject = this.convertToCreateObject(instance);
const keys = Object.keys(createObject);
const missingColumns = columnNames.filter((columnName) => {
return !keys.includes(columnName);
})
if (missingColumns.length > 0) {
throw new Error("Can't create instance, due to missing column values: " + missingColumns);
}
const sql = `INSERT INTO ${this.schema.name}(${Object.keys(createObject).join(',')})
VALUES (${Object.keys(createObject).map(() => "?").join(',')})`;
const result = this.database.execute(sql, ...Object.values(createObject));
const id = result.lastInsertRowid;
Container.get<EventHandler>(EventHandler.name).dispatch(new ElementCreatedEvent<ModelType>(this.schema.name, instance, id));
return id;
}
public update(instance: Partial<ModelType> & { id: number }): boolean {
const columnNames = this.schema.columns.filter((column) => {
return !column.primaryKey
}).map((column) => {
return column.name;
});
const createObject = this.convertToCreateObject(instance);
const keys = Object.keys(createObject);
const missingColumns = columnNames.filter((columnName) => {
return !keys.includes(columnName);
})
if (missingColumns.length > 0) {
throw new Error("Can't create instance, due to missing column values: " + missingColumns);
}
const sql = `UPDATE ${this.schema.name}
SET ${Object.keys(createObject).map((key) => `${key} = ?`).join(',')}
WHERE id = ?`;
const result = this.database.execute(sql, ...Object.values(createObject), instance.id);
return result.changes > 0;
}
public getById(id: number): Nullable<ModelType> {
const sql = `SELECT * FROM ${this.schema.name} WHERE id = ? LIMIT 1`;
return this.convertToModelType(this.database.fetch<number, IntermediateModelType>(sql, id));
}
public delete(id: number) {
const sql = `DELETE FROM ${this.schema.name} WHERE id = ?`;
return this.database.execute(sql, id);
}
protected convertToModelType(intermediateModel: IntermediateModelType | undefined): ModelType {
return intermediateModel as unknown as ModelType;
}
protected convertToCreateObject(instance: Partial<ModelType>): object {
return instance;
}
}