MM-56906: Remove redundant calls on team switch (#30771)

On page load, we load ALL channels and channel members from all teams.
But then, on team_switch, we would again load channels and channel
members from that team. This was redundant and mainly kept
because previously the websocket events were considered unreliable.

Now with reliable websockets, and client-side pings, we can detect
broken connections faster and recover without loss.

Additionally, the getAllChannelMembers call would page through
all responses on the client side. This was inefficient and incur
extra latency. To optimize for this, we introduce server-side
streaming of the full response if page is set to -1.

This optimizes the intial response as well.

https://mattermost.atlassian.net/browse/MM-56906

```release-note
Optimize team switch operation by removing calls to get channels
and channel members.
```


Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
Agniva De Sarker
2025-05-12 20:05:46 +05:30
коммит произвёл GitHub
родитель 883711c72d
Коммит 4803892492
20 изменённых файлов: 376 добавлений и 49 удалений

Просмотреть файл

@@ -4,6 +4,7 @@
import nock from 'nock';
import Client4, {ClientError, HEADER_X_VERSION_ID} from './client4';
import {buildQueryString} from './helpers';
import type {TelemetryHandler} from './telemetry';
describe('Client4', () => {
@@ -40,6 +41,34 @@ describe('Client4', () => {
expect(client.serverVersion).toEqual('5.3.0.5.3.0.abc123');
});
test('should parse NDJSON responses correctly', async () => {
const client = new Client4();
client.setUrl('http://mattermost.example.com');
const userId = 'dummy-user-id';
const page = -1; // Special value to trigger NDJSON response
// Sample NDJSON data with multiple channel memberships on separate lines
const ndjsonData = '{"user_id":"dummy-user-id","channel_id":"channel1","roles":"channel_user"}\n' +
'{"user_id":"dummy-user-id","channel_id":"channel2","roles":"channel_user channel_admin"}\n' +
'{"user_id":"dummy-user-id","channel_id":"channel3","roles":"channel_user"}';
// Create a mock endpoint for getAllChannelsMembers that returns NDJSON data
nock(client.getBaseRoute()).
get(`/users/${userId}/channel_members${buildQueryString({page, per_page: 60})}`).
reply(200, ndjsonData, {'Content-Type': 'application/x-ndjson'});
// Call the getAllChannelsMembers method which will use our implementation for NDJSON
const result = await client.getAllChannelsMembers(userId, page);
// Verify the response was parsed as an array of objects
expect(Array.isArray(result)).toBe(true);
expect(result).toHaveLength(3);
expect(result[0]).toEqual({user_id: 'dummy-user-id', channel_id: 'channel1', roles: 'channel_user'});
expect(result[1]).toEqual({user_id: 'dummy-user-id', channel_id: 'channel2', roles: 'channel_user channel_admin'});
expect(result[2]).toEqual({user_id: 'dummy-user-id', channel_id: 'channel3', roles: 'channel_user'});
});
});
});

Просмотреть файл

@@ -4189,8 +4189,13 @@ export default class Client4 {
let data;
try {
if (headers.get('Content-Type') === 'application/json') {
const contentType = headers.get('Content-Type');
if (contentType === 'application/json') {
data = await response.json();
} else if (contentType === 'application/x-ndjson') {
const text = await response.text();
const objects = text.trim().split('\n');
data = objects.map((obj) => JSON.parse(obj));
} else {
data = await response.text();
}