summary refs log tree commit diff
path: root/util/src
diff options
context:
space:
mode:
Diffstat (limited to 'util/src')
-rw-r--r--util/src/entities/Channel.ts11
-rw-r--r--util/src/entities/Guild.ts7
-rw-r--r--util/src/entities/Member.ts9
-rw-r--r--util/src/entities/User.ts5
-rw-r--r--util/src/util/imports/OrmUtils.ts113
-rw-r--r--util/src/util/imports/index.ts1
6 files changed, 132 insertions, 14 deletions
diff --git a/util/src/entities/Channel.ts b/util/src/entities/Channel.ts
index a7ca647b..48469103 100644
--- a/util/src/entities/Channel.ts
+++ b/util/src/entities/Channel.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId } from "typeorm";

+import { Column, Entity, JoinColumn, ManyToOne, OneToMany, RelationId} from "typeorm";

+import { OrmUtils } from "@fosscord/util";

 import { BaseClass } from "./BaseClass";

 import { Guild } from "./Guild";

 import { PublicUserProjection, User } from "./User";

@@ -222,7 +223,7 @@ export class Channel extends BaseClass {
 		};

 

 		await Promise.all([

-			Object.assign(new Channel(),channel).save(),

+			OrmUtils.mergeDeep(new Channel(),channel).save(),

 			!opts?.skipEventEmit

 				? emitEvent({

 					event: "CHANNEL_CREATE",

@@ -263,7 +264,7 @@ export class Channel extends BaseClass {
 				if (containsAll(re, channelRecipients)) {

 					if (channel == null) {

 						channel = ur.channel;

-						ur = Object.assign(ur, { closed: false });

+						ur = OrmUtils.mergeDeep(ur, { closed: false });

 						await ur.save();

 					}

 				}

@@ -273,7 +274,7 @@ export class Channel extends BaseClass {
 		if (channel == null) {

 			name = trimSpecial(name);

 

-			channel = await (Object.assign(new Channel(), {

+			channel = await (OrmUtils.mergeDeep(new Channel(), {

 				name,

 				type,

 				owner_id: type === ChannelType.DM ? undefined : null, // 1:1 DMs are ownerless in fosscord-server

@@ -281,7 +282,7 @@ export class Channel extends BaseClass {
 				last_message_id: null,

 				recipients: channelRecipients.map(

 					(x) =>

-						Object.assign(new Recipient(), { user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })

+						OrmUtils.mergeDeep(new Recipient(), { user_id: x, closed: !(type === ChannelType.GROUP_DM || x === creator_user_id) })

 				),

 			}) as Channel).save();

 		}

diff --git a/util/src/entities/Guild.ts b/util/src/entities/Guild.ts
index 058033e8..da8a2c2f 100644
--- a/util/src/entities/Guild.ts
+++ b/util/src/entities/Guild.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId } from "typeorm";
+import { Column, Entity, JoinColumn, ManyToMany, ManyToOne, OneToMany, OneToOne, RelationId} from "typeorm";
+import { OrmUtils } from "@fosscord/util";
 import { Config, handleFile, Snowflake } from "..";
 import { Ban } from "./Ban";
 import { BaseClass } from "./BaseClass";
@@ -285,7 +286,7 @@ export class Guild extends BaseClass {
 	}) {
 		const guild_id = Snowflake.generate();
 
-		const guild: Guild = Object.assign(new Guild(),{
+		const guild: Guild = OrmUtils.mergeDeep(new Guild(),{
 			name: body.name || "Fosscord",
 			icon: await handleFile(`/icons/${guild_id}`, body.icon as string),
 			region: Config.get().regions.default,
@@ -321,7 +322,7 @@ export class Guild extends BaseClass {
 
 		// we have to create the role _after_ the guild because else we would get a "SQLITE_CONSTRAINT: FOREIGN KEY constraint failed" error
 		// TODO: make the @everyone a pseudorole that is dynamically generated at runtime so we can save storage
-		let role: Role = Object.assign(new Role(), {
+		let role: Role = OrmUtils.mergeDeep(new Role(), {
 			id: guild_id,
 			guild_id: guild_id,
 			color: 0,
diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts
index e4aa8331..f0c361d0 100644
--- a/util/src/entities/Member.ts
+++ b/util/src/entities/Member.ts
@@ -25,6 +25,7 @@ import { Role } from "./Role";
 import { BaseClassWithoutId } from "./BaseClass";
 import { Ban, PublicGuildRelations } from ".";
 import { DiscordApiErrors } from "../util/Constants";
+import { OrmUtils } from "@fosscord/util";
 
 export const MemberPrivateProjection: (keyof Member)[] = [
 	"id",
@@ -161,7 +162,7 @@ export class Member extends BaseClassWithoutId {
 			}),
 			Role.findOneOrFail({ where: { id: role_id, guild_id }, select: ["id"] }),
 		]);
-		member.roles.push(Object.assign(new Role(), { id: role_id }));
+		member.roles.push(OrmUtils.mergeDeep(new Role(), { id: role_id }));
 
 		await Promise.all([
 			member.save(),
@@ -256,7 +257,7 @@ export class Member extends BaseClassWithoutId {
 			nick: undefined,
 			roles: [guild_id], // @everyone role
 			joined_at: new Date(),
-			premium_since: (new Date()).getTime(),
+			premium_since: new Date(),
 			deaf: false,
 			mute: false,
 			pending: false,
@@ -264,9 +265,9 @@ export class Member extends BaseClassWithoutId {
 		//TODO: check for bugs
 		if(guild.member_count) guild.member_count++;
 		await Promise.all([
-			Object.assign(new Member(), {
+			OrmUtils.mergeDeep(new Member(), {
 				...member,
-				roles: [Object.assign(new Role(), { id: guild_id })],
+				roles: [OrmUtils.mergeDeep(new Role(), { id: guild_id })],
 				// read_state: {},
 				settings: {
 					channel_overrides: [],
diff --git a/util/src/entities/User.ts b/util/src/entities/User.ts
index 81017c2d..d023780b 100644
--- a/util/src/entities/User.ts
+++ b/util/src/entities/User.ts
@@ -1,4 +1,5 @@
-import { Column, Entity, FindOneOptions, FindOptionsSelectByString, JoinColumn, ManyToMany, OneToMany, RelationId } from "typeorm";
+import { Column, Entity, FindOneOptions, FindOptionsSelectByString, JoinColumn, ManyToMany, OneToMany, RelationId} from "typeorm";
+import { OrmUtils } from "@fosscord/util";
 import { BaseClass } from "./BaseClass";
 import { BitField } from "../util/BitField";
 import { Relationship } from "./Relationship";
@@ -255,7 +256,7 @@ export class User extends BaseClass {
 		// if nsfw_allowed is null/undefined it'll require date_of_birth to set it to true/false
 		const language = req.language === "en" ? "en-US" : req.language || "en-US";
 
-		const user = Object.assign(new User(), {
+		const user = OrmUtils.mergeDeep(new User(), {
 			created_at: new Date(),
 			username: username,
 			discriminator,
diff --git a/util/src/util/imports/OrmUtils.ts b/util/src/util/imports/OrmUtils.ts
new file mode 100644
index 00000000..91d88172
--- /dev/null
+++ b/util/src/util/imports/OrmUtils.ts
@@ -0,0 +1,113 @@
+//source: https://github.com/typeorm/typeorm/blob/master/src/util/OrmUtils.ts
+export class OrmUtils {
+    // Checks if it's an object made by Object.create(null), {} or new Object()
+    private static isPlainObject(item: any) {
+        if (item === null || item === undefined) {
+            return false
+        }
+
+        return !item.constructor || item.constructor === Object
+    }
+
+    private static mergeArrayKey(
+        target: any,
+        key: number,
+        value: any,
+        memo: Map<any, any>,
+    ) {
+        // Have we seen this before?  Prevent infinite recursion.
+        if (memo.has(value)) {
+            target[key] = memo.get(value)
+            return
+        }
+
+        if (value instanceof Promise) {
+            // Skip promises entirely.
+            // This is a hold-over from the old code & is because we don't want to pull in
+            // the lazy fields.  Ideally we'd remove these promises via another function first
+            // but for now we have to do it here.
+            return
+        }
+
+        if (!this.isPlainObject(value) && !Array.isArray(value)) {
+            target[key] = value
+            return
+        }
+
+        if (!target[key]) {
+            target[key] = Array.isArray(value) ? [] : {}
+        }
+
+        memo.set(value, target[key])
+        this.merge(target[key], value, memo)
+        memo.delete(value)
+    }
+
+    private static mergeObjectKey(
+        target: any,
+        key: string,
+        value: any,
+        memo: Map<any, any>,
+    ) {
+        // Have we seen this before?  Prevent infinite recursion.
+        if (memo.has(value)) {
+            Object.assign(target, { [key]: memo.get(value) })
+            return
+        }
+
+        if (value instanceof Promise) {
+            // Skip promises entirely.
+            // This is a hold-over from the old code & is because we don't want to pull in
+            // the lazy fields.  Ideally we'd remove these promises via another function first
+            // but for now we have to do it here.
+            return
+        }
+
+        if (!this.isPlainObject(value) && !Array.isArray(value)) {
+            Object.assign(target, { [key]: value })
+            return
+        }
+
+        if (!target[key]) {
+            Object.assign(target, { [key]: value })
+        }
+
+        memo.set(value, target[key])
+        this.merge(target[key], value, memo)
+        memo.delete(value)
+    }
+
+    private static merge(
+        target: any,
+        source: any,
+        memo: Map<any, any> = new Map(),
+    ): any {
+        if (Array.isArray(target) && Array.isArray(source)) {
+            for (let key = 0; key < source.length; key++) {
+                this.mergeArrayKey(target, key, source[key], memo)
+            }
+        }
+        else {
+            for (const key of Object.keys(source)) {
+                this.mergeObjectKey(target, key, source[key], memo)
+            }
+        }
+
+        
+    }
+
+    /**
+     * Deep Object.assign.
+     */
+    static mergeDeep(target: any, ...sources: any[]): any {
+        if (!sources.length) {
+            return target
+        }
+
+        for (const source of sources) {
+            OrmUtils.merge(target, source)
+        }
+
+        return target
+    }
+}
\ No newline at end of file
diff --git a/util/src/util/imports/index.ts b/util/src/util/imports/index.ts
index 4a4448ba..18c47a3b 100644
--- a/util/src/util/imports/index.ts
+++ b/util/src/util/imports/index.ts
@@ -1,2 +1,3 @@
 export * from './Checks';
 export * from './HTTPError';
+export * from './OrmUtils';
\ No newline at end of file