-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample-base-class.ts
More file actions
129 lines (117 loc) · 3.7 KB
/
example-base-class.ts
File metadata and controls
129 lines (117 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { randomUUID } from "node:crypto";
import { DatabaseSync } from "node:sqlite";
import {
ProtoObject,
ProtoObjectStaticMethods,
StaticImplements,
} from "protoobject";
export enum RecordState {
ACTIVE,
DELETED,
}
export interface BaseRecordStaticMethods<T extends BaseRecord<T>>
extends ProtoObjectStaticMethods<T> {
table: string;
getById<T extends BaseRecord<T>>(
db: DatabaseSync,
id: string
): Promise<T | undefined>;
}
@StaticImplements<BaseRecordStaticMethods<BaseRecord<any>>>()
export class BaseRecord<T extends BaseRecord<T>> extends ProtoObject<T> {
constructor(data?: Partial<T>) {
super(data);
if (typeof (this as any).record_state === "undefined")
(this as any).record_state = RecordState.ACTIVE;
if (!(this as any).id) (this as any).id = randomUUID();
const dt = new Date();
if (!(this as any).created_at) (this as any).created_at = dt;
if (!(this as any).updated_at) (this as any).updated_at = dt;
return this;
}
public static table = `base`;
public static async getById<T extends BaseRecord<T>>(
db: DatabaseSync,
id: BaseRecord<T>["id"]
): Promise<T | undefined> {
const dbRecord = (await db
.prepare(`SELECT * FROM ${this.table} WHERE id = ?`)
.get(id)) as Partial<T>;
return dbRecord ? this.fromJSON<T>(dbRecord) : undefined;
}
public async reload(db: DatabaseSync): Promise<T> {
const classNode = this.constructor as BaseRecordStaticMethods<T>;
const dbRecord = await classNode.getById<T>(db, this.id);
if (!dbRecord) throw new Error("Not Found");
return this.assign(classNode.fromJSON(dbRecord));
}
public async insert(db: DatabaseSync): Promise<void> {
const classNode = this.constructor as BaseRecordStaticMethods<T>;
this.created_at = new Date();
this.updated_at = new Date();
const jsonData = this.toJSON();
await Promise.resolve(
db
.prepare(
`INSERT INTO ${classNode.table} (${Object.keys(jsonData).join(", ")}) VALUES (${Object.keys(
jsonData
)
.map((e, i) => `?`)
.join(", ")})`
)
.run(...Object.values(jsonData))
);
return;
}
public async update(db: DatabaseSync): Promise<void> {
const classNode = this.constructor as BaseRecordStaticMethods<T>;
this.updated_at = new Date();
const jsonData = this.toJSON();
delete jsonData.id;
await Promise.resolve(
db
.prepare(
`UPDATE ${classNode.table} SET ${Object.keys(jsonData)
.map((e, i) => `${e} = ?`)
.join(", ")} WHERE id = ?`
)
.run(...Object.values(jsonData), this.id)
);
return;
}
public async softDelete(db: DatabaseSync): Promise<void> {
this.record_state = RecordState.DELETED;
this.updated_at = new Date();
await this.update(db);
return;
}
public async delete(db: DatabaseSync): Promise<void> {
const classNode = this.constructor as BaseRecordStaticMethods<T>;
const jsonData = this.toJSON();
delete jsonData.id;
await Promise.resolve(
db.prepare(`DELETE FROM ${classNode.table} WHERE id = ?`).run(this.id)
);
return;
}
public static fromJSON<T>(data: { [key: string]: unknown }) {
return new this({
...super.fromJSON(data),
created_at:
typeof data.created_at === "string"
? new Date(data.created_at)
: undefined,
updated_at:
typeof data.updated_at === "string"
? new Date(data.updated_at)
: undefined,
}) as T;
}
public toJSON(): { [key: string]: any } {
return {
...super.toJSON.call(this),
created_at: this.created_at?.toJSON(),
updated_at: this.updated_at?.toJSON(),
};
}
}