summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorwhinis <whinis@whinis.com>2026-02-17 10:31:03 -0500
committerRory& <root@rory.gay>2026-02-17 18:20:31 +0100
commit9bd8fc2c092fde5f30471e2f345270323d4739e1 (patch)
treeb2ed5c57674f5428d05a10c3b7329caea69ac8ba /src
parentAPI: request embeds in US english preferred (diff)
downloadserver-ts-9bd8fc2c092fde5f30471e2f345270323d4739e1.tar.xz
Add Decorators to clean classes before json serialization and remove invalid states.
Diffstat (limited to 'src')
-rw-r--r--src/api/util/handlers/Message.ts2
-rw-r--r--src/util/entities/BaseClass.ts28
-rw-r--r--src/util/entities/Message.ts9
-rw-r--r--src/util/util/Decorators.ts21
4 files changed, 59 insertions, 1 deletions
diff --git a/src/api/util/handlers/Message.ts b/src/api/util/handlers/Message.ts

index 2ff56a7a..97040fd3 100644 --- a/src/api/util/handlers/Message.ts +++ b/src/api/util/handlers/Message.ts
@@ -486,7 +486,7 @@ export async function postHandleMessage(message: Message) { const content = message.content?.replace(/ *`[^)]*` */g, ""); // remove markdown const linkMatches = content?.match(LINK_REGEX) || []; - + message.clean_data(); const data = { ...message }; const currentNormalizedUrls = new Set<string>(); diff --git a/src/util/entities/BaseClass.ts b/src/util/entities/BaseClass.ts
index 1aaf1479..a8844630 100644 --- a/src/util/entities/BaseClass.ts +++ b/src/util/entities/BaseClass.ts
@@ -20,12 +20,40 @@ import { BaseEntity, BeforeInsert, BeforeUpdate, Column, ColumnOptions, FindOpti import { Snowflake } from "../util/Snowflake"; import { getDatabase } from "../util/Database"; import { OrmUtils } from "../imports/OrmUtils"; +import { annotationsKey } from "../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(); + 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 + } + } + return this; + } + private get metadata() { return getDatabase()?.getMetadata(this.construct); } diff --git a/src/util/entities/Message.ts b/src/util/entities/Message.ts
index 71364673..a54ab7d5 100644 --- a/src/util/entities/Message.ts +++ b/src/util/entities/Message.ts
@@ -31,6 +31,7 @@ import { Attachment } from "./Attachment"; import { NewUrlUserSignatureData } from "../Signing"; import { ActionRowComponent, ApplicationCommandType, Embed, MessageSnapshot, MessageType, PartialMessage, Poll, Reaction } from "@spacebar/schemas"; import { MessageFlags } from "@spacebar/util"; +import { JsonRemoveEmpty } from "../util/Decorators"; @Entity({ name: "messages", @@ -122,14 +123,17 @@ export class Message extends BaseClass { mention_everyone?: boolean; @JoinTable({ name: "message_user_mentions" }) + @JsonRemoveEmpty @ManyToMany(() => User) mentions: User[]; @JoinTable({ name: "message_role_mentions" }) + @JsonRemoveEmpty @ManyToMany(() => Role) mention_roles: Role[]; @JoinTable({ name: "message_channel_mentions" }) + @JsonRemoveEmpty @ManyToMany(() => Channel) mention_channels: Channel[]; @@ -141,12 +145,15 @@ export class Message extends BaseClass { cascade: true, orphanedRowAction: "delete", }) + @JsonRemoveEmpty attachments?: Attachment[]; @Column({ type: "simple-json" }) + @JsonRemoveEmpty embeds: Embed[]; @Column({ type: "simple-json" }) + @JsonRemoveEmpty reactions: Reaction[]; @Column({ type: "text", nullable: true }) @@ -201,9 +208,11 @@ export class Message extends BaseClass { }; @Column({ type: "simple-json", nullable: true }) + @JsonRemoveEmpty components?: ActionRowComponent[]; @Column({ type: "simple-json", nullable: true }) + @JsonRemoveEmpty poll?: Poll; @Column({ nullable: true }) diff --git a/src/util/util/Decorators.ts b/src/util/util/Decorators.ts new file mode 100644
index 00000000..9de9c1c9 --- /dev/null +++ b/src/util/util/Decorators.ts
@@ -0,0 +1,21 @@ +import { BaseClassWithoutId } from "@spacebar/util*"; + +export const annotationsKey = Symbol("Annotations"); + +// Generates an array using the annotationsKey as a property on a class when annotated with the below decorators +export function initAnnotationMetadata(target: BaseClassWithoutId, propertyKey: string) { + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + target[annotationsKey] || (target[annotationsKey] = {}); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions + target[annotationsKey][propertyKey] || (target[annotationsKey][propertyKey] = []); +} + +// Adds to the generated array on the class with the annotation added. +export function addAnnotationMetadata(target: BaseClassWithoutId, propertyKey: string, annotation: string) { + target[annotationsKey] = { ...target[annotationsKey], [propertyKey]: [...target[annotationsKey][propertyKey], annotation] }; +} + +export function JsonRemoveEmpty(target: BaseClassWithoutId, propertyKey: string) { + initAnnotationMetadata(target, propertyKey); + addAnnotationMetadata(target, propertyKey, "JsonRemoveEmpty"); +}