From 8306543ddd00851252f5e9de43d39f19c9ab0fd0 Mon Sep 17 00:00:00 2001 From: grimhilt Date: Wed, 29 Mar 2023 21:00:43 +0200 Subject: [PATCH] improve saveMessage (switch to class) and started to test --- back/controllers/messages.js | 3 +- back/controllers/rooms.js | 3 +- back/db/db.js | 2 +- back/mails/saveMessage.js | 209 ++++++++++++++++------------- back/mails/utils/statusUtils.js | 5 - back/test/mail/saveMessage-test.js | 80 +++++------ back/test/test-utils/names.js | 206 ++++++++++++++++++++++++++++ front/src/services/imapAPI.js | 5 +- front/src/views/room/Message.vue | 2 +- 9 files changed, 366 insertions(+), 149 deletions(-) create mode 100644 back/test/test-utils/names.js diff --git a/back/controllers/messages.js b/back/controllers/messages.js index 913e673..9e697a0 100644 --- a/back/controllers/messages.js +++ b/back/controllers/messages.js @@ -1,12 +1,13 @@ const statusCode = require("../utils/statusCodes").statusCodes; const { getMessages } = require("../db/api.js"); +const { logger } = require("../system/Logger"); async function messages(body, res) { const { roomId } = body; getMessages(roomId).then((messages) => { res.status(statusCode.OK).json(messages); }).catch((err) => { - console.log(err) + logger.err(err) res.status(statusCode.INTERNAL_SERVER_ERROR); }); } diff --git a/back/controllers/rooms.js b/back/controllers/rooms.js index 36b793d..14ffb1c 100644 --- a/back/controllers/rooms.js +++ b/back/controllers/rooms.js @@ -1,12 +1,13 @@ const statusCode = require("../utils/statusCodes").statusCodes; const { getRooms } = require("../db/api.js"); +const { logger } = require("../system/Logger"); async function rooms(body, res) { const { mailboxId, offset, limit } = body; getRooms(mailboxId).then((rooms) => { res.status(statusCode.OK).json(rooms); }).catch((err) => { - console.log(err) + logger.error(err) res.status(statusCode.INTERNAL_SERVER_ERROR); }); } diff --git a/back/db/db.js b/back/db/db.js index a245b7c..78c9a70 100644 --- a/back/db/db.js +++ b/back/db/db.js @@ -44,7 +44,7 @@ function execQueryAsyncWithId(query, values) { function execQuery(query, values) { db.query(query, values, (err, results, fields) => { if (err) { - logger.err(err); + logger.error(err); throw (err); } return results; diff --git a/back/mails/saveMessage.js b/back/mails/saveMessage.js index 5cac78b..c1351bc 100644 --- a/back/mails/saveMessage.js +++ b/back/mails/saveMessage.js @@ -9,7 +9,7 @@ const { } = require("../db/saveMessageApp"); const { findRoomByOwner, getAddresseId, getUserIdOfMailbox } = require("../db/mail"); -const { isDmOnEnvelope, nbMembers } = require("./utils/statusUtils"); +const { nbMembers } = require("./utils/statusUtils"); /** * take object address and join mailbox and host to return mailbox@host @@ -18,105 +18,128 @@ function createAddress(elt) { return `${elt.mailbox}@${elt.host}`; } -async function registerMessageInApp(messageId, attrs, boxId) { - const isSeen = attrs.flags.includes("\\Seen") ? 1 : 0; // todo verify - const envelope = attrs.envelope; - - await getAddresseId(createAddress(envelope.sender[0])).then(async (ownerId) => { - if (envelope.inReplyTo) { - await registerReplyMessage(envelope, messageId, isSeen, ownerId); - } else { - const userId = (await getUserIdOfMailbox(boxId))[0]?.user_id; - if (ownerId == userId) { - // send by the user - if (nbMembers(envelope) == 2) { - // this is a dm - console.log(envelope) - const userTo = (await getAddresseId(createAddress(envelope.to[0]))); - await findRoomByOwner(userTo).then(async (res) => { - if (res.length == 0) { - // first message of this conv with this sender - await createRoom(envelope.subject, userTo, messageId).then(async (roomId) => { - await registerMessageInRoom(messageId, roomId, isSeen, envelope.date); - }); - } else { - // not a reply, add to the list of message if this sender - await registerMessageInRoom(messageId, res[0].room_id, isSeen, envelope.date); - } - }); - } else { - // message coming from user with multiple member is a group - await createRoom(envelope.subject, ownerId, messageId).then(async (roomId) => { - await registerMessageInRoom(messageId, roomId, isSeen, envelope.date); - }); - } - } else { - await findRoomByOwner(ownerId).then(async (res) => { - if (res.length == 0) { - // first message of this sender - if (!envelope.subject) console.error(envelope) - await createRoom(envelope.subject, ownerId, messageId).then(async (roomId) => { - await registerMessageInRoom(messageId, roomId, isSeen, envelope.date); - }); - } else { - // not a reply, add to the list of message if this sender - await registerMessageInRoom(messageId, res[0].room_id, isSeen, envelope.date); - } - }); - } - } +async function initiateRoom(envelope, ownerId, messageId, isSeen) { + await createRoom(envelope.subject, ownerId, messageId).then(async (roomId) => { + // todo register members + await registerMessageInRoom(messageId, roomId, isSeen, envelope.date); }); } -async function registerReplyMessage(envelope, messageId, isSeen, ownerId) { - const messageID = envelope.messageId; - await findRoomsFromMessage(messageId).then(async (rooms) => { - if (rooms.length == 0) { - // no rooms, so is a transfer - // todo test if members of transferred message are included - } else if (rooms.length === 1) { - // only one room so message is only in a room and not in a thread - // as a thread is associated to a room to begin - await isRoomGroup(rooms[0].room_id).then(async (isGroup) => { - if (isGroup) { - const hasSameMembers = await hasSameMembersAsParent(messageID, envelope.inReplyTo); - if (hasSameMembers) { - await registerMessageInRoom(messageId, rooms[0].room_id, isSeen, envelope.date); - } else { - // is a group and has not the same member as the previous message - // some recipient has been removed create a thread - const isDm = isDmOnEnvelope(envelope); - await createThread(envelope.subject, ownerId, messageId, rooms[0].room_id, isDm).then(async (threadId) => { - await registerMessageInThread(messageId, threadId, isSeen); - }); - } - } else { - // reply from channel - // todo - // if (sender == owner) { // correction from the original sender - // // leave in the same channel - // } - } - }); - } else if (rooms.length > 1) { - // get the lowest thread (order by room_id) - const room = rooms[rooms.length-1]; - const hasSameMembers = await hasSameMembersAsParent(messageID, envelope.inReplyTo); - if (hasSameMembers) { - await registerMessageInThread(messageId, room.room_id, isSeen); +const roomType = { + ROOM: 'room', + CHANNEL: 'channel', + GROUP: 'group', + DM: 'dm', + THREAD: 'thread' +} + +class registerMessageInApp { + constructor(_messageId, _attrs, _boxId) { + this.messageId = _messageId; + this.attrs = _attrs; + this.envelope = this.attrs.envelope; + this.messageID = this.envelope.messageId; + this.boxId = _boxId; + this.isSeen = this.attrs.flags.includes("\\Seen") ? 1 : 0; + this.ownerId; + this.userId; + } + + async init() { + this.ownerId = await getAddresseId(createAddress(this.envelope.sender[0])); // todo use sender or from ? + } + + isDm = () => nbMembers(this.envelope) == 2; + + async isFromUs() { + if (!this.userId) this.userId = (await getUserIdOfMailbox(boxId))[0]?.user_id; + return this.ownerId = this.userId; + } + + async initiateRoom(owner, roomType) { + // todo roomType + await createRoom(this.envelope.subject, owner, this.messageId).then(async (roomId) => { + // todo register members + await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); + }); + } + + async createOrRegisterOnExistence(owner, roomType) { + await findRoomByOwner(owner).then(async (res) => { + if (res.length == 0) { + // first message with this sender + await initiateRoom(owner, roomType); } else { - // has not the same members so it is a derivation of this thread - // todo put this in a function and add default message in the reply chain - const isDm = isDmOnEnvelope(envelope); - await createThread(envelope.subject, ownerId, messageId, room.room_id, isDm).then(async (threadId) => { - await registerMessageInThread(messageId, threadId, isSeen); - }); + // 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); + } + }); + } + + async createOrRegisterOnMembers(roomId) { + const hasSameMembers = await hasSameMembersAsParent(this.messageID, this.envelope.inReplyTo); + if (hasSameMembers) { + await registerMessageInRoom(this.messageId, roomId, this.isSeen, this.envelope.date); + } else { + await createThread(this.envelope.subject, this.ownerId, this.messageId, roomId, this.isDm()).then( + async (threadId) => { + await registerMessageInThread(this.messageId, threadId, this.isSeen); + }, + ); + } + } + + async save() { + await this.init(); + if (this.envelope.inReplyTo) { + this.saveReply(); + } else { + if (await this.isFromUs()) { + if (this.isDm()) { + // create or add new message to DM + const userTo = await getAddresseId(createAddress(this.envelope.to[0])); + await this.createOrRegisterOnExistence(userTo, roomType.DM); + } else { + // it is not a reply and not a dm + // so it is a channel, which can be possibly a group + initiateRoom(this.envelope, this.ownerId, this.messageId, this.isSeen); + } + } else { + await this.createOrRegisterOnExistence(this.ownerId, roomType.ROOM); } } - }); + } + + async saveReply() { + const messageID = envelope.messageId; + await findRoomsFromMessage(messageId).then(async (rooms) => { + if (rooms.length == 0) { + // no rooms, so is a transfer + // todo test if members of transferred message are included + } else if (rooms.length === 1) { + // only one room so message is only in a room and not in a thread + // as a thread is associated to a room + await isRoomGroup(rooms[0].room_id).then(async (isGroup) => { + if (isGroup) { + this.createOrRegisterOnMembers(rooms[0].room_id); + } else { + // reply from channel + // todo + // if (sender == owner) { // correction from the original sender + // // leave in the same channel + // } + } + }); + } else if (rooms.length > 1) { + // get the lowest thread (order by room_id) + const roomId = rooms[rooms.length - 1].room_id; + this.createOrRegisterOnMembers(roomId); + } + }); + } } module.exports = { - registerMessageInApp + registerMessageInApp, + roomType }; - diff --git a/back/mails/utils/statusUtils.js b/back/mails/utils/statusUtils.js index d5da778..08b7c13 100644 --- a/back/mails/utils/statusUtils.js +++ b/back/mails/utils/statusUtils.js @@ -1,7 +1,3 @@ -function isDmOnEnvelope(envelope) { - return nbMembers(envelope) === 2; -} - function nbMembers(envelope) { let nbMembers = (envelope.bcc?.length ?? 0) + @@ -18,6 +14,5 @@ function nbMembers(envelope) { } module.exports = { - isDmOnEnvelope, nbMembers, }; diff --git a/back/test/mail/saveMessage-test.js b/back/test/mail/saveMessage-test.js index 808f0ed..32d92f0 100644 --- a/back/test/mail/saveMessage-test.js +++ b/back/test/mail/saveMessage-test.js @@ -1,52 +1,40 @@ -process.env["NODE_DEV"] = "TEST"; -const { saveFromParsedData } = require("../../mails/storeMessage"); -const { TestDb } = require("../sql/test-utilsDb"); -const MYSQL = require("../../db/config.json").MYSQL; +const { getAddresseId } = require("../../db/mail"); +const { generateAttrs, generateUsers } = require("../test-utils/test-attrsUtils"); +const { registerMessageInApp, roomType } = require("../../mails/saveMessage"); -let db; -beforeAll(async () => { - const options = { - databaseOptions: { - host: "localhost", - port: 3306, - user: MYSQL.user, - password: MYSQL.pwd, - database: "mail_test", - }, - dbSchema: "../../sql/structureV2.sql", - }; - db = new TestDb(options); - await db.init(); -}); +jest.mock("../../db/mail"); -afterAll(async () => { - db.cleanTables(); -}); +// todo mock db -describe("saveMessage", async () => { - describe("rooms", async () => { - it("messages not related and from same sender should be in the same room", async () => { - await saveFromParsedData( - { - to: { value: [{ address: "address1@email.com" }] }, - from: { value: [{ address: "address2@email.com" }] }, - messageId: "", - }, - 1, - ); - await saveFromParsedData( - { - to: { value: [{ address: "address1@email.com" }] }, - from: { value: [{ address: "address2@email.com" }] }, - messageId: "", - }, - 2, - ); - // todo call parser - const query = "" - db.execQueryAsync().then((res) => { - expect(res.length).toBe(2); - }) +describe("saveMessage", () => { + describe("is not a reply", () => { + it("first DM from user should create a DM room", async () => { + const users = generateUsers(2); + const attrs = generateAttrs({ from: [users[0].user], to: [users[1].user] }); + + getAddresseId.mockReturnValue(users[1].id); + + const register = new registerMessageInApp(1, attrs, 1); + register.ownerId = users[0].id; + + jest.spyOn(register, "isFromUs").mockReturnValue(true); + jest.spyOn(register, "init").mockReturnValue(""); + + const createOrRegisterOnExistence = jest + .spyOn(register, "createOrRegisterOnExistence") + .mockImplementation(() => undefined); + + await register.save(); + + expect(createOrRegisterOnExistence).toHaveBeenCalledWith(users[1].id, roomType.DM); }); + // it("DM message from user should be added to DM room", async () => { + + // }); + // it("first GROUP message should create a group", () => {}); }); + describe("replies", () => { + it("", () => {}); + }); + describe("", () => {}); }); diff --git a/back/test/test-utils/names.js b/back/test/test-utils/names.js new file mode 100644 index 0000000..8c9905d --- /dev/null +++ b/back/test/test-utils/names.js @@ -0,0 +1,206 @@ +const names = [ +"James", +"Mary", +"Robert", +"Patricia", +"John", +"Jennifer", +"Michael", +"Linda", +"David", +"Elizabeth", +"William", +"Barbara", +"Richard", +"Susan", +"Joseph", +"Jessica", +"Thomas", +"Sarah", +"Charles", +"Karen", +"Christopher", +"Lisa", +"Daniel", +"Nancy", +"Matthew", +"Betty", +"Anthony", +"Margaret", +"Mark", +"Sandra", +"Donald", +"Ashley", +"Steven", +"Kimberly", +"Paul", +"Emily", +"Andrew", +"Donna", +"Joshua", +"Michelle", +"Kenneth", +"Carol", +"Kevin", +"Amanda", +"Brian", +"Dorothy", +"George", +"Melissa", +"Timothy", +"Deborah", +"Ronald", +"Stephanie", +"Edward", +"Rebecca", +"Jason", +"Sharon", +"Jeffrey", +"Laura", +"Ryan", +"Cynthia", +"Jacob", +"Kathleen", +"Gary", +"Amy", +"Nicholas", +"Angela", +"Eric", +"Shirley", +"Jonathan", +"Anna", +"Stephen", +"Brenda", +"Larry", +"Pamela", +"Justin", +"Emma", +"Scott", +"Nicole", +"Brandon", +"Helen", +"Benjamin", +"Samantha", +"Samuel", +"Katherine", +"Gregory", +"Christine", +"Alexander", +"Debra", +"Frank", +"Rachel", +"Patrick", +"Carolyn", +"Raymond", +"Janet", +"Jack", +"Catherine", +"Dennis", +"Maria", +"Jerry", +"Heather", +"Tyler", +"Diane", +"Aaron", +"Ruth", +"Jose", +"Julie", +"Adam", +"Olivia", +"Nathan", +"Joyce", +"Henry", +"Virginia", +"Douglas", +"Victoria", +"Zachary", +"Kelly", +"Peter", +"Lauren", +"Kyle", +"Christina", +"Ethan", +"Joan", +"Walter", +"Evelyn", +"Noah", +"Judith", +"Jeremy", +"Megan", +"Christian", +"Andrea", +"Keith", +"Cheryl", +"Roger", +"Hannah", +"Terry", +"Jacqueline", +"Gerald", +"Martha", +"Harold", +"Gloria", +"Sean", +"Teresa", +"Austin", +"Ann", +"Carl", +"Sara", +"Arthur", +"Madison", +"Lawrence", +"Frances", +"Dylan", +"Kathryn", +"Jesse", +"Janice", +"Jordan", +"Jean", +"Bryan", +"Abigail", +"Billy", +"Alice", +"Joe", +"Julia", +"Bruce", +"Judy", +"Gabriel", +"Sophia", +"Logan", +"Grace", +"Albert", +"Denise", +"Willie", +"Amber", +"Alan", +"Doris", +"Juan", +"Marilyn", +"Wayne", +"Danielle", +"Elijah", +"Beverly", +"Randy", +"Isabella", +"Roy", +"Theresa", +"Vincent", +"Diana", +"Ralph", +"Natalie", +"Eugene", +"Brittany", +"Russell", +"Charlotte", +"Bobby", +"Marie", +"Mason", +"Kayla", +"Philip", +"Alexis", +"Louis", +"Lori" +]; + +module.exports = { + names +} \ No newline at end of file diff --git a/front/src/services/imapAPI.js b/front/src/services/imapAPI.js index 476faec..af10e2e 100644 --- a/front/src/services/imapAPI.js +++ b/front/src/services/imapAPI.js @@ -12,5 +12,8 @@ export default { }, getMessages(roomId) { return API().get(`/mail/${roomId}/messages`); - } + }, + getMembers(roomId) { + return API().get(`/mail/${roomId}/members`); + }, } \ No newline at end of file diff --git a/front/src/views/room/Message.vue b/front/src/views/room/Message.vue index 6433e1c..2a2a380 100644 --- a/front/src/views/room/Message.vue +++ b/front/src/views/room/Message.vue @@ -39,7 +39,7 @@ onMounted(() => {