mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
perf(message): build pin responses in one messages round trip (#2138)
This commit is contained in:
1 parent
e328c001a1
commit
8adc3ecb0b
2 files changed
+102
-12
No files matched your search
@@ -100,18 +100,14 @@ export class MessagePinService extends MessageInteractionBase {
|
||||
canReadMessageHistory: hasReadHistory,
|
||||
};
|
||||
const responseDataService = createMessageResponseDataService();
|
||||
const messageResponses = await Promise.all(
|
||||
trimmed.map((message) =>
|
||||
responseDataService.getMessage({
|
||||
userId,
|
||||
channelId: channel.id,
|
||||
messageId: message.id,
|
||||
access,
|
||||
}),
|
||||
),
|
||||
);
|
||||
const items = trimmed.flatMap((message: Message, index) => {
|
||||
const messageResponse = messageResponses[index];
|
||||
const messageResponses = await responseDataService.buildMessages({
|
||||
userId,
|
||||
messages: trimmed,
|
||||
access,
|
||||
});
|
||||
const responseByMessageId = new Map(messageResponses.map((response) => [response.id, response]));
|
||||
const items = trimmed.flatMap((message: Message) => {
|
||||
const messageResponse = responseByMessageId.get(message.id.toString());
|
||||
if (!messageResponse || !message.pinnedTimestamp) return [];
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {Permissions} from '@fluxer/constants/src/ChannelConstants';
|
||||
import type {ChannelPinsResponse} from '@fluxer/schema/src/domains/message/MessageResponseSchemas';
|
||||
import {extractTimestamp} from '@fluxer/snowflake/src/SnowflakeUtils';
|
||||
import {afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest';
|
||||
import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';
|
||||
import {createBuilder} from '../../test/TestRequestBuilder';
|
||||
import {createMessageResponseDataService} from '../services/message/MessageResponseDataService';
|
||||
import {
|
||||
createPermissionOverwrite,
|
||||
pinMessage,
|
||||
sendChannelMessage,
|
||||
setupTestGuildWithMembers,
|
||||
updateGuild,
|
||||
} from './ChannelTestUtils';
|
||||
|
||||
describe('Channel pins listing', () => {
|
||||
let harness: ApiTestHarness;
|
||||
|
||||
beforeAll(async () => {
|
||||
harness = await createApiTestHarness();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await harness.reset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await harness?.shutdown();
|
||||
});
|
||||
|
||||
it('builds a full page of pins with a single batched request', async () => {
|
||||
const {owner, systemChannel} = await setupTestGuildWithMembers(harness, 0);
|
||||
const pinnedIds: Array<string> = [];
|
||||
for (let index = 0; index < 6; index++) {
|
||||
const message = await sendChannelMessage(harness, owner.token, systemChannel.id, `pinned ${index}`);
|
||||
await pinMessage(harness, owner.token, systemChannel.id, message.id);
|
||||
pinnedIds.push(message.id);
|
||||
}
|
||||
const responseDataService = createMessageResponseDataService();
|
||||
const buildMessages = vi.spyOn(responseDataService, 'buildMessages');
|
||||
const getMessage = vi.spyOn(responseDataService, 'getMessage');
|
||||
const before = encodeURIComponent(new Date(Date.now() + 60_000).toISOString());
|
||||
|
||||
const pins = await createBuilder<ChannelPinsResponse>(harness, owner.token)
|
||||
.get(`/channels/${systemChannel.id}/messages/pins?limit=5&before=${before}`)
|
||||
.execute();
|
||||
|
||||
expect(pins.has_more).toBe(true);
|
||||
expect(pins.items).toHaveLength(5);
|
||||
for (const item of pins.items) {
|
||||
expect(pinnedIds).toContain(item.message.id);
|
||||
expect(item.message.pinned).toBe(true);
|
||||
expect(item.pinned_at).toEqual(expect.any(String));
|
||||
}
|
||||
expect(getMessage).not.toHaveBeenCalled();
|
||||
expect(buildMessages).toHaveBeenCalledTimes(1);
|
||||
expect(buildMessages.mock.calls[0]![0].messages).toHaveLength(5);
|
||||
});
|
||||
|
||||
it('applies message history access filtering to every pin in the batch', async () => {
|
||||
const {owner, members, guild, systemChannel} = await setupTestGuildWithMembers(harness, 1);
|
||||
const member = members[0]!;
|
||||
const oldMessage = await sendChannelMessage(harness, owner.token, systemChannel.id, 'old pin');
|
||||
const newMessage = await sendChannelMessage(harness, owner.token, systemChannel.id, 'new pin');
|
||||
await pinMessage(harness, owner.token, systemChannel.id, oldMessage.id);
|
||||
await pinMessage(harness, owner.token, systemChannel.id, newMessage.id);
|
||||
const cutoff = new Date(extractTimestamp(newMessage.id)).toISOString();
|
||||
await updateGuild(harness, owner.token, guild.id, {message_history_cutoff: cutoff});
|
||||
await createPermissionOverwrite(harness, owner.token, systemChannel.id, member.userId, {
|
||||
type: 1,
|
||||
allow: Permissions.VIEW_CHANNEL.toString(),
|
||||
deny: Permissions.READ_MESSAGE_HISTORY.toString(),
|
||||
});
|
||||
const responseDataService = createMessageResponseDataService();
|
||||
const buildMessages = vi.spyOn(responseDataService, 'buildMessages');
|
||||
|
||||
const pins = await createBuilder<ChannelPinsResponse>(harness, member.token)
|
||||
.get(`/channels/${systemChannel.id}/messages/pins`)
|
||||
.execute();
|
||||
|
||||
expect(pins.items.map((item) => item.message.id)).toEqual([newMessage.id]);
|
||||
expect(buildMessages).toHaveBeenCalledTimes(1);
|
||||
const batch = buildMessages.mock.calls[0]![0];
|
||||
expect(batch.messages.map((message) => message.id.toString())).toEqual([newMessage.id]);
|
||||
expect(batch.access.canReadMessageHistory).toBe(false);
|
||||
expect(batch.access.messageHistoryCutoff).toBe(cutoff);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user