implement save of thread and members

This commit is contained in:
grimhilt 2023-03-31 16:07:02 +02:00
parent e2bd0bafea
commit 7f28823758
10 changed files with 225 additions and 142 deletions

View File

@ -2,13 +2,31 @@ const { transformEmojis } = require("../utils/string.js");
const { db, execQueryAsync, execQueryAsyncWithId, execQuery } = require("./db.js"); const { db, execQueryAsync, execQueryAsyncWithId, execQuery } = require("./db.js");
const { queryFromId, queryToId, queryCcId } = require("./utils/addressQueries.js"); const { queryFromId, queryToId, queryCcId } = require("./utils/addressQueries.js");
async function createRoom(roomName, ownerId, messageId) { async function getAllMembers(messageId) {
const query = `
SELECT GROUP_CONCAT(address.address_id) AS is
FROM address
INNER JOIN address_field ON
address_field.address_id = address.address_id AND
address_field.message_id = ?
GROUP BY address_field.message_id
`;
const values = [messageId];
return await execQueryAsync(query, values);
}
async function registerMember(roomId, memberId) {
const query = `INSERT IGNORE INTO app_room_member (room_id, member_id) VALUES (?, ?)`;
const values = [roomId, memberId];
return await execQueryAsync(query, values);
}
async function createRoom(roomName, ownerId, messageId, roomType) {
if (!roomName) roomName = "No room name"; if (!roomName) roomName = "No room name";
roomName = transformEmojis(roomName); roomName = transformEmojis(roomName);
const query = `INSERT IGNORE INTO app_room (room_name, owner_id, message_id) VALUES (?, ?, ?)`; const query = `INSERT IGNORE INTO app_room (room_name, owner_id, message_id, room_type) VALUES (?, ?, ?, ?)`;
const values = [roomName.substring(0, 255), ownerId, messageId]; const values = [roomName.substring(0, 255), ownerId, messageId, roomType];
return await execQueryAsyncWithId(query, values); return await execQueryAsyncWithId(query, values);
// todo add members
} }
async function registerMessageInRoom(messageId, roomId, isSeen, idate) { async function registerMessageInRoom(messageId, roomId, isSeen, idate) {
@ -17,10 +35,9 @@ async function registerMessageInRoom(messageId, roomId, isSeen, idate) {
await execQueryAsync(query, values); await execQueryAsync(query, values);
updateLastUpdateRoom(roomId, idate); updateLastUpdateRoom(roomId, idate);
// if (!isSeen) {
if (!isSeen) { // incrementNotSeenRoom(roomId);
incrementNotSeenRoom(roomId); // }
}
} }
function updateLastUpdateRoom(roomId, idate) { function updateLastUpdateRoom(roomId, idate) {
@ -33,29 +50,25 @@ function incrementNotSeenRoom(roomId) {
// todo // todo
} }
async function createThread(threadName, ownerId, messageId, parentRoomId, isDm) { async function getRoomInfo(messageID) {
const rootRoomId = -1; // todo const query = `
const threadId = await createRoom(threadName, ownerId, messageId); SELECT
const query = `INSERT INTO app_thread (room_id, parent_room_id, root_room_id, isDm) VALUES (?, ?, ?, ?)`; app_room.room_id
const values = [threadId, parentRoomId, rootRoomId, isDm]; app_thread.root_id
FROM app_room
LEFT JOIN app_thread ON app_thread.room_id = app_room.room_id
INNER JOIN app_room_message ON app_room_message.room_id = app_room.room_id
INNER JOIN message ON message.message_id = app_room_message.message_id
WHERE message.messageID = ?
`;
const values = [messageID];
return await execQueryAsync(query, values); return await execQueryAsync(query, values);
// todo add members
} }
async function registerMessageInThread(messageId, threadId, isSeen) { async function registerThread(roomId, parentId, rootId) {
// todo check if it is still a thread or should be a room const query = `INSERT IGNORE INTO app_thread (room_id, parent_id, root_id) VALUES (?, ?, ?)`;
// todo isdm const values = [roomId, parentId, rootId];
console.log("register message in thread") return await execQueryAsync(query, values);
}
function updateLastUpdateThread(threadId) {
// todo
// check for parent
}
function incrementNotSeenThread(threadId) {
// todo
// also increment root room
} }
async function isRoomGroup(roomId) { async function isRoomGroup(roomId) {
@ -119,11 +132,13 @@ async function hasSameMembersAsParent(messageId, messageID) {
} }
module.exports = { module.exports = {
getAllMembers,
registerMember,
createRoom, createRoom,
registerMessageInRoom, registerMessageInRoom,
createThread, registerThread,
registerMessageInThread,
isRoomGroup, isRoomGroup,
findRoomsFromMessage, findRoomsFromMessage,
hasSameMembersAsParent, hasSameMembersAsParent,
getRoomInfo
}; };

View File

@ -122,11 +122,11 @@ CREATE TABLE app_room (
room_name VARCHAR(255) NOT NULL, room_name VARCHAR(255) NOT NULL,
owner_id INT NOT NULL, owner_id INT NOT NULL,
message_id INT NOT NULL, message_id INT NOT NULL,
isGroup BOOLEAN NOT NULL DEFAULT false, room_type INT NOT NULL DEFAULT 0,
notSeen INT NOT NULL DEFAULT 0, notSeen INT NOT NULL DEFAULT 0,
lastUpdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(), lastUpdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(),
PRIMARY KEY (room_id), PRIMARY KEY (room_id),
UNIQUE KEY (owner_id, message_id, isGroup), UNIQUE KEY (owner_id, message_id, room_type),
FOREIGN KEY (owner_id) REFERENCES address(address_id), FOREIGN KEY (owner_id) REFERENCES address(address_id),
FOREIGN KEY (message_id) REFERENCES message(message_id) FOREIGN KEY (message_id) REFERENCES message(message_id)
); );
@ -134,14 +134,13 @@ CREATE TABLE app_room (
-- 12 -- 12
CREATE TABLE app_thread ( CREATE TABLE app_thread (
room_id INT NOT NULL, room_id INT NOT NULL,
parent_room_id INT, parent_id INT,
root_room_id INT, root_id INT,
isDm BOOLEAN NOT NULL DEFAULT false,
PRIMARY KEY (room_id), PRIMARY KEY (room_id),
UNIQUE KEY (room_id, parent_room_id, root_room_id), UNIQUE KEY (room_id, parent_id, root_id),
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE, FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE,
FOREIGN KEY (parent_room_id) REFERENCES app_room(room_id) ON DELETE SET NULL, FOREIGN KEY (parent_id) REFERENCES app_room(room_id) ON DELETE SET NULL,
FOREIGN KEY (root_room_id) REFERENCES app_room(room_id) ON DELETE SET NULL FOREIGN KEY (root_id) REFERENCES app_room(room_id) ON DELETE SET NULL
); );
-- 13 -- 13
@ -157,6 +156,7 @@ CREATE TABLE app_room_message (
CREATE TABLE app_room_member ( CREATE TABLE app_room_member (
room_id INT NOT NULL, room_id INT NOT NULL,
member_id INT NOT NULL, member_id INT NOT NULL,
UNIQUE KEY (room_id, member_id),
FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE, FOREIGN KEY (room_id) REFERENCES app_room(room_id) ON DELETE CASCADE,
FOREIGN KEY (member_id) REFERENCES address(address_id) FOREIGN KEY (member_id) REFERENCES address(address_id)
); );

View File

@ -1,14 +0,0 @@
const MYSQL = require("./sql/config.json").mysql;
module.exports = {
databaseOptions: {
host: "localhost",
port: 3306,
user: MYSQL.user,
password: MYSQL.pwd,
database: "mail_test",
},
createDatabase: true,
dbSchema: "./sql/structureV2.sql",
truncateDatabase: true,
};

View File

@ -6,10 +6,14 @@ const {
isRoomGroup, isRoomGroup,
findRoomsFromMessage, findRoomsFromMessage,
hasSameMembersAsParent, hasSameMembersAsParent,
registerThread,
registerMember,
getAllMembers,
} = require("../db/saveMessageApp"); } = require("../db/saveMessageApp");
const { findRoomByOwner, getAddresseId, getUserIdOfMailbox } = require("../db/mail"); const { findRoomByOwner, getAddresseId, getUserIdOfMailbox } = require("../db/mail");
const { nbMembers } = require("./utils/statusUtils"); const { nbMembers } = require("./utils/envelopeUtils");
const { logger } = require("../system/Logger");
/** /**
* take object address and join mailbox and host to return mailbox@host * take object address and join mailbox and host to return mailbox@host
@ -26,11 +30,11 @@ async function initiateRoom(envelope, ownerId, messageId, isSeen) {
} }
const roomType = { const roomType = {
ROOM: 'room', ROOM: 0,
CHANNEL: 'channel', CHANNEL: 1,
GROUP: 'group', GROUP: 2,
DM: 'dm', DM: 3,
THREAD: 'thread' THREAD: 4
} }
class registerMessageInApp { class registerMessageInApp {
@ -56,19 +60,30 @@ class registerMessageInApp {
return this.ownerId == this.userId; return this.ownerId == this.userId;
} }
async initiateRoom(owner, roomType) { async registerMembers(roomId) {
// todo roomType getAllMembers(this.messageId).then((res) => {
await createRoom(this.envelope.subject, owner, this.messageId).then(async (roomId) => { res[0].id.split(',').foreach(async (memberId) => {
// todo register members await registerMember(roomId, memberId);
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); });
}); });
} }
async initiateRoom(owner, type) {
try {
const roomId = await createRoom(this.envelope.subject, owner, this.messageId, type);
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
this.registerMembers(roomId);
return roomId;
} catch (err) {
logger.error(err);
}
}
async createOrRegisterOnExistence(owner, roomType) { async createOrRegisterOnExistence(owner, roomType) {
await findRoomByOwner(owner).then(async (res) => { await findRoomByOwner(owner).then(async (res) => {
if (res.length == 0) { if (res.length == 0) {
// first message with this sender // first message with this sender
await initiateRoom(owner, roomType); await this.initiateRoom(owner, roomType);
} else { } else {
// not a reply, add to the list of message if this sender // not a reply, add to the list of message if this sender
await registerMessageInRoom(this.messageId, res[0].room_id, this.isSeen, this.envelope.date); await registerMessageInRoom(this.messageId, res[0].room_id, this.isSeen, this.envelope.date);
@ -76,11 +91,28 @@ class registerMessageInApp {
}); });
} }
async initiateThread() {
await createRoom(this.envelope.subject, owner, this.messageId, roomType.THREAD).then(async (roomId) => {
// find parent room infos
await getRoomInfo(this.envelope.inReplyTo).then(async (room) => {
// todo room not lenght, reply to transfer ?
let root_id = room[0].root_id
if (!root_id) root_id = room[0].room_id
await registerThread(roomId, room[0].room_id, root_id);
});
// impl register previous message ?
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
await this.registerMembers(roomId);
});
}
async createOrRegisterOnMembers(roomId) { async createOrRegisterOnMembers(roomId) {
const hasSameMembers = await hasSameMembersAsParent(this.messageID, this.envelope.inReplyTo); const hasSameMembers = await hasSameMembersAsParent(this.messageID, this.envelope.inReplyTo);
if (hasSameMembers) { if (hasSameMembers) {
await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date);
} else { } else {
await this.initiateThread();
await createThread(this.envelope.subject, this.ownerId, this.messageId, roomId, this.isDm()).then( await createThread(this.envelope.subject, this.ownerId, this.messageId, roomId, this.isDm()).then(
async (threadId) => { async (threadId) => {
await registerMessageInThread(this.messageId, threadId, this.isSeen); await registerMessageInThread(this.messageId, threadId, this.isSeen);
@ -105,7 +137,7 @@ class registerMessageInApp {
initiateRoom(this.envelope, this.ownerId, this.messageId, this.isSeen); initiateRoom(this.envelope, this.ownerId, this.messageId, this.isSeen);
} }
} else { } else {
await this.createOrRegisterOnExistence(this.ownerId, roomType.ROOM); await this.createOrRegisterOnExistence(this.ownerId, roomType.CHANNEL);
} }
} }
} }

View File

@ -0,0 +1,21 @@
function nbMembers(envelope) {
return getMembers(envelope).length;
}
function getMembers(envelope) {
const members = [];
const fields = ["from", "to", "sender", "replyTo", "cc", "bcc"];
fields.forEach((field) => {
if (!envelope[field]) return;
envelope[field].forEach((member) => {
if (members.find((m) => m.mailbox === member.mailbox && m.host === member.host)) return;
members.push(member);
});
});
return members;
}
module.exports = {
nbMembers,
getMembers,
};

View File

@ -1,18 +0,0 @@
function nbMembers(envelope) {
let nbMembers =
(envelope.bcc?.length ?? 0) +
(envelope.cc?.length ?? 0) +
(envelope.to?.length ?? 0) +
(envelope.from?.length ?? 0);
if (
envelope.sender?.length > 0 &&
!(envelope.sender[0].mailbox == envelope.from[0].mailbox && envelope.sender[0].host == envelope.from[0].host)
) {
nbMembers += envelope.sender?.length ?? 0;
}
return nbMembers;
}
module.exports = {
nbMembers,
};

View File

@ -0,0 +1,45 @@
const { nbMembers } = require("../../../mails/utils/envelopeUtils");
const { generateUsers } = require("../../test-utils/test-attrsUtils");
describe("envelopeUtils", () => {
const names = generateUsers(6);
describe("nbMembers", () => {
it("sender and from shouldn't be counted twice if there are the same", () => {
const envelope = {
from: [names[0].user],
sender: [names[0].user],
replyTo: null,
to: null,
cc: null,
bcc: null,
inReplyTo: null,
};
expect(nbMembers(envelope)).toBe(1);
});
it("sender and from shoud be counted twice if there are the same", () => {
const envelope = {
from: [names[0].user],
sender: [names[1].user],
replyTo: null,
to: null,
cc: null,
bcc: null,
inReplyTo: null,
};
expect(nbMembers(envelope)).toBe(2);
});
it("should count every members", () => {
// todo should merge identic members
const envelope = {
from: [names[0].user],
sender: [names[1].user],
replyTo: [names[2].user],
to: [names[3].user],
cc: [names[4].user],
bcc: [names[5].user],
inReplyTo: null,
};
expect(nbMembers(envelope)).toBe(6);
});
});
});

View File

@ -1,40 +0,0 @@
const { nbMembers } = require("../../../mails/utils/statusUtils");
describe("statusUtils", () => {
it("sender and from shouldn't be counted twice if there are the same", () => {
const envelope = {
from: [{ name: "", mailbox: "user_1", host: "provider.com" }],
sender: [{ name: "", mailbox: "user_1", host: "provider.com" }],
replyTo: [{ name: "", mailbox: "user_1", host: "provider.com" }],
to: null,
cc: null,
bcc: null,
inReplyTo: null,
};
expect(nbMembers(envelope)).toBe(1);
});
it("sender and from shoud be counted twice if there are the same", () => {
const envelope = {
from: [{ name: "", mailbox: "user_1", host: "provider.com" }],
sender: [{ name: "", mailbox: "user_2", host: "provider.com" }],
replyTo: [{ name: "", mailbox: "user_1", host: "provider.com" }],
to: null,
cc: null,
bcc: null,
inReplyTo: null,
};
expect(nbMembers(envelope)).toBe(2);
});
it("should count every members", () => {
const envelope = {
from: [{ name: "", mailbox: "user_1", host: "provider.com" }],
sender: [{ name: "", mailbox: "user_2", host: "provider.com" }],
replyTo: [{ name: "", mailbox: "user_1", host: "provider.com" }],
to: [{ name: "", mailbox: "user_1", host: "provider.com" }],
cc: [{ name: "", mailbox: "user_1", host: "provider.com" }],
bcc: [{ name: "", mailbox: "user_1", host: "provider.com" }],
inReplyTo: null,
};
expect(nbMembers(envelope)).toBe(5);
});
});

View File

@ -1,19 +0,0 @@
beforeAll(async () => {
// const schema = fs.readFileSync('../../sql/structureV2.sql', 'utf8');
// await setupDB(mysqlConfig, schema);
});
afterAll(async () => {
global.db.query("DROP database mail_test");
});
describe('saveMessageApp', async () => {
beforeEach(() => {
});
it("", () => {
});
});

View File

@ -0,0 +1,61 @@
const { names } = require("./names");
function generateAttrs(options) {
const attrs = {
"size": 42,
"envelope": {
date: "2023-03-21T15:25:42.000Z",
subject: options.subject ?? "subject" + randomString(10),
from: options.from ?? null,
sender: options.sender ?? null,
replyTo: options.replyTo ?? null,
to: options.to ?? null,
cc: options.cc ?? null,
bcc: options.bcc ?? null,
inReplyTo: options.inReplyTo ?? null,
messageId: options.messageId ?? randomString(10),
},
"date": options.date ?? new Date(),
"flags": options.flags ?? [],
"uid": options.uid ?? randomInt(3),
"modseq": options.modseq ?? randomInt(7),
"x-gm-labels": ["\\Inbox"],
"x-gm-msgid": "1760991478422670209",
"x-gm-thrid": "1760991478422670209",
};
return attrs;
}
function generateUsers(nb) {
const users = [];
for (let i = 0; i < nb; i++) {
users.push({
user: {
name: "",
mailbox: names[i],
host: "provider.com",
},
id: i,
});
}
return users;
}
function randomString(length) {
let result = "";
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
function randomInt(length) {
return (Math.random() * Math.pow(10, length)).toFixed();
}
module.exports = {
generateAttrs,
generateUsers,
};