Skip to content

Commit 0dcfb76

Browse files
Fix chat creation
1 parent 9811e06 commit 0dcfb76

File tree

2 files changed

+56
-42
lines changed

2 files changed

+56
-42
lines changed

src/api/v1/chats/chats.service.ts

Lines changed: 35 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import db from '@/lib/db';
1010

1111
const createPaginationArgs = (
1212
filters: Types.BasePaginationFilters & { orderBy: 'createdAt' | 'updatedAt' },
13-
limit = 10
13+
limit = 10,
1414
) => {
1515
return {
1616
...(filters.cursor ? { cursor: { id: filters.cursor }, skip: 1 } : {}),
@@ -36,7 +36,7 @@ const createChatAggregation = () => {
3636

3737
const createCurrentProfileChatArgs = (
3838
currentProfile: Prisma.ProfileGetPayload<{ include: { user: true } }>,
39-
now: Date
39+
now: Date,
4040
) => {
4141
return {
4242
profileName: currentProfile.user.username,
@@ -48,7 +48,7 @@ const createCurrentProfileChatArgs = (
4848

4949
const prepareChat = (
5050
chat: Prisma.ChatGetPayload<{ include: ReturnType<typeof createChatAggregation> }>,
51-
currentProfile: Profile
51+
currentProfile: Profile,
5252
) => {
5353
if (currentProfile.tangible) {
5454
chat.profiles = chat.profiles.map((p) => {
@@ -59,7 +59,7 @@ const prepareChat = (
5959
});
6060
} else {
6161
chat.profiles = chat.profiles.map((p) =>
62-
p.profileId === currentProfile.id ? p : { ...p, lastSeenAt: null }
62+
p.profileId === currentProfile.id ? p : { ...p, lastSeenAt: null },
6363
);
6464
}
6565
return chat;
@@ -69,23 +69,22 @@ export const createChat = async (
6969
user: Types.PublicUser,
7070
data: Schema.ValidChat,
7171
imageData?: Types.ImageFullData,
72-
uploadedImage?: Storage.UploadedImageData
72+
uploadedImage?: Storage.UploadedImageData,
7373
) => {
7474
return await Utils.handleDBKnownErrors(
7575
db.$transaction(async (tx) => {
76-
// Get current user's profile
77-
const currentProfile = await tx.profile.findUnique({
78-
where: { userId: user.id },
76+
const allProfiles = await tx.profile.findMany({
77+
where: { OR: [{ id: { in: data.profiles } }, { userId: user.id }] },
7978
include: { user: true },
79+
distinct: 'id',
8080
});
81+
const otherProfiles: typeof allProfiles = [];
82+
const currentProfile = allProfiles.reduce<(typeof allProfiles)[number] | null>(
83+
(acc, curr) => (curr.user.id === user.id ? curr : (otherProfiles.push(curr), acc)),
84+
null,
85+
);
8186
if (!currentProfile) throw new AppNotFoundError('Profile not found');
8287
const currentProfileChatArgs = createCurrentProfileChatArgs(currentProfile, new Date());
83-
// Get all the other profiles
84-
const otherProfiles = await tx.profile.findMany({
85-
where: { id: { in: data.profiles } },
86-
include: { user: true },
87-
distinct: 'id',
88-
});
8988
const chatAggregation = createChatAggregation();
9089
const messageArgs = {
9190
profileName: currentProfile.user.username,
@@ -101,19 +100,15 @@ export const createChat = async (
101100
profileId: currentProfile.id,
102101
};
103102
// Find chats with the same owner and same group of profiles (there should be at most one)
104-
const existentChats = await tx.chat.findMany({
105-
where: {
106-
managers: { some: { profileId: currentProfile.id, role: 'OWNER' } },
107-
profiles: {
108-
every: {
109-
profileName: {
110-
in: [currentProfile.user.username, ...otherProfiles.map((p) => p.user.username)],
111-
},
112-
},
103+
const existentChats = (
104+
await tx.chat.findMany({
105+
where: {
106+
managers: { some: { profileId: currentProfile.id, role: 'OWNER' } },
107+
profiles: { every: { profileName: { in: allProfiles.map((p) => p.user.username) } } },
113108
},
114-
},
115-
include: chatAggregation,
116-
});
109+
include: { ...chatAggregation, _count: { select: { profiles: true } } },
110+
})
111+
).filter((c) => c._count.profiles === allProfiles.length); // Filter out the profiles with a mismatched count.
117112
// Upsert a chat
118113
let chat: Prisma.ChatGetPayload<{ include: typeof chatAggregation }>;
119114
if (existentChats.length > 0) {
@@ -163,7 +158,7 @@ export const createChat = async (
163158
});
164159
}
165160
return prepareChat(chat, currentProfile);
166-
})
161+
}),
167162
);
168163
};
169164

@@ -187,13 +182,13 @@ export const deleteChat = async (userId: User['id'], chatId: Chat['id']) => {
187182
}
188183
}
189184
}
190-
})
185+
}),
191186
);
192187
};
193188

194189
export const getUserChats = async (
195190
userId: User['id'],
196-
filters: Types.BasePaginationFilters = {}
191+
filters: Types.BasePaginationFilters = {},
197192
) => {
198193
return await Utils.handleDBKnownErrors(
199194
db.$transaction(async (tx) => {
@@ -214,7 +209,7 @@ export const getUserChats = async (
214209
include: createChatAggregation(),
215210
});
216211
return chats.map((chat) => prepareChat(chat, currentProfile));
217-
})
212+
}),
218213
);
219214
};
220215

@@ -242,13 +237,13 @@ export const getUserChatById = async (userId: User['id'], chatId: Chat['id']) =>
242237
throw new AppNotFoundError('Chat not found');
243238
}
244239
return prepareChat(chat, currentProfile);
245-
})
240+
}),
246241
);
247242
};
248243

249244
export const getUserChatsByMemberProfileId = async (
250245
userId: User['id'],
251-
profileId: Profile['id']
246+
profileId: Profile['id'],
252247
) => {
253248
return await Utils.handleDBKnownErrors(
254249
db.$transaction(async (tx) => {
@@ -281,14 +276,14 @@ export const getUserChatsByMemberProfileId = async (
281276
});
282277
if (!currentProfileWithChats) throw new AppNotFoundError('Profile not found');
283278
return currentProfileWithChats.chats.map((c) => prepareChat(c.chat, currentProfileWithChats));
284-
})
279+
}),
285280
);
286281
};
287282

288283
export const getUserChatMessages = async (
289284
userId: User['id'],
290285
chatId: Chat['id'],
291-
filters: Types.BasePaginationFilters = {}
286+
filters: Types.BasePaginationFilters = {},
292287
) => {
293288
return await Utils.handleDBKnownErrors(
294289
db.$transaction(async (tx) => {
@@ -315,14 +310,14 @@ export const getUserChatMessages = async (
315310
where: { chat: { id: chatId, profiles: { some: { profileId } } } },
316311
include: createMessageAggregation(),
317312
});
318-
})
313+
}),
319314
);
320315
};
321316

322317
export const getUserChatMessageById = async (
323318
userId: User['id'],
324319
chatId: Chat['id'],
325-
msgId: Message['id']
320+
msgId: Message['id'],
326321
) => {
327322
return await Utils.handleDBKnownErrors(
328323
db.$transaction(async (tx) => {
@@ -343,7 +338,7 @@ export const getUserChatMessageById = async (
343338
});
344339
if (!message) throw new AppNotFoundError('Message not found');
345340
return message;
346-
})
341+
}),
347342
);
348343
};
349344

@@ -352,7 +347,7 @@ export const createUserChatMessage = async (
352347
chatId: Chat['id'],
353348
{ body }: Schema.ValidMessage,
354349
imageData?: Types.ImageFullData,
355-
uploadedImage?: Storage.UploadedImageData
350+
uploadedImage?: Storage.UploadedImageData,
356351
) => {
357352
return await Utils.handleDBKnownErrors(
358353
db.$transaction(async (tx) => {
@@ -393,7 +388,7 @@ export const createUserChatMessage = async (
393388
},
394389
include: createMessageAggregation(),
395390
});
396-
})
391+
}),
397392
);
398393
};
399394

@@ -411,7 +406,7 @@ export const updateProfileChatLastSeenDate = async (userId: User['id'], chatId:
411406
where: { profileName_chatId: { profileName, chatId } },
412407
data: { lastSeenAt: now },
413408
});
414-
})
409+
}),
415410
);
416411
return now;
417412
};

src/tests/api/v1/chats.int.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,8 @@ describe('Chats endpoints', async () => {
799799
const oldChat = await createChat('', chatMembers);
800800
await createMessage(oldChat.id, dbUserOne);
801801
const chatData = {
802-
profiles: chatMembers.map((u) => u.profile!.id),
802+
// Remove current user from profile ids
803+
profiles: chatMembers.filter((cm) => cm.id !== dbUserOne.id).map((u) => u.profile!.id),
803804
message: { body: 'Whats up?' },
804805
};
805806
const { authorizedApi } = await prepForAuthorizedTest(userOneData);
@@ -824,7 +825,8 @@ describe('Chats endpoints', async () => {
824825
const oldChat = await createChat('', chatMembers);
825826
await createMessage(oldChat.id, dbUserOne);
826827
const chatData = {
827-
profiles: chatMembers.map((u) => u.profile!.id),
828+
// Remove current user from profile ids
829+
profiles: chatMembers.filter((cm) => cm.id !== intangibleUser.id).map((u) => u.profile!.id),
828830
message: { body: 'Whats up?' },
829831
};
830832
const { authorizedApi } = await prepForAuthorizedTest(intangibleUserData);
@@ -866,6 +868,23 @@ describe('Chats endpoints', async () => {
866868
expect(dbMsgs[0].chatId).toBe(dbChats[0].id);
867869
});
868870

871+
it('should not use an already exist self-chat, and start new chat with the given member profile id', async () => {
872+
const dbSelfChat = await createChat('Hello, Me!', [dbUserOne], false);
873+
const chatData = { profiles: [dbUserTwo.profile!.id], message: { body: 'Hello!' } };
874+
const { authorizedApi } = await prepForAuthorizedTest(userOneData);
875+
const res = await authorizedApi.post(CHATS_URL).send(chatData);
876+
const dbMsgs = await db.message.findMany({});
877+
const dbChats = await db.chat.findMany({});
878+
const chat = res.body as ChatFullData;
879+
expect(res.statusCode).toBe(201);
880+
expect(res.type).toMatch(/json/);
881+
expect(dbChats).toHaveLength(2);
882+
expect(dbMsgs).toHaveLength(2);
883+
assertChat(chat, dbChats.find((c) => c.id !== dbSelfChat.id)!.id, 1, 2);
884+
expect(dbMsgs.find((m) => m.chatId === dbChats[0].id)).toBeTruthy();
885+
expect(dbMsgs.find((m) => m.chatId === dbChats[1].id)).toBeTruthy();
886+
});
887+
869888
it('should create new chat with a non-image message, and ignore `imagedata` field without an image file', async () => {
870889
const profileId = dbUserTwo.profile!.id;
871890
const chatData = { profiles: [profileId], message: { body: 'Hello!' }, imagedata };

0 commit comments

Comments
 (0)