Skip to content

Commit 23eebf0

Browse files
authored
fix: add optional raw queryChannels api response (#1530)
## CLA - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required). - [ ] Code changes are tested ## Description of the changes, What, Why and How? This PR allows `client.queryChannels` to optionally return the raw response and not run hydration at all (and subsequently not provide an array of `Channel` instances). It is meant to be used generally server-side so that integrators can have access to other fields not available in a `Channel` instance. It should resolve [this Zendesk ticket](https://getstream.zendesk.com/agent/tickets/64585). We can probably keep it the same api if we use discriminated unions, but I feel like it'd complicate things further for no good reason. Keeping the apis separate made sense to me. ## Changelog -
1 parent 2703018 commit 23eebf0

File tree

3 files changed

+70
-8
lines changed

3 files changed

+70
-8
lines changed

src/client.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,22 +1735,19 @@ export class StreamChat {
17351735
}
17361736

17371737
/**
1738-
* queryChannels - Query channels
1738+
* queryChannelsRequest - Queries channels and returns the raw response
17391739
*
17401740
* @param {ChannelFilters} filterConditions object MongoDB style filters
17411741
* @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}.
17421742
* When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}]
17431743
* @param {ChannelOptions} [options] Options object
1744-
* @param {ChannelStateOptions} [stateOptions] State options object. These options will only be used for state management and won't be sent in the request.
1745-
* - stateOptions.skipInitialization - Skips the initialization of the state for the channels matching the ids in the list.
17461744
*
1747-
* @return {Promise<{ channels: Array<ChannelAPIResponse>}> } search channels response
1745+
* @return {Promise<Array<ChannelAPIResponse>>} search channels response
17481746
*/
1749-
async queryChannels(
1747+
async queryChannelsRequest(
17501748
filterConditions: ChannelFilters,
17511749
sort: ChannelSort = [],
17521750
options: ChannelOptions = {},
1753-
stateOptions: ChannelStateOptions = {},
17541751
) {
17551752
const defaultOptions: ChannelOptions = {
17561753
state: true,
@@ -1777,15 +1774,39 @@ export class StreamChat {
17771774
payload,
17781775
);
17791776

1777+
return data.channels;
1778+
}
1779+
1780+
/**
1781+
* queryChannels - Query channels
1782+
*
1783+
* @param {ChannelFilters} filterConditions object MongoDB style filters
1784+
* @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}.
1785+
* When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}]
1786+
* @param {ChannelOptions} [options] Options object
1787+
* @param {ChannelStateOptions} [stateOptions] State options object. These options will only be used for state management and won't be sent in the request.
1788+
* - stateOptions.skipInitialization - Skips the initialization of the state for the channels matching the ids in the list.
1789+
* - stateOptions.skipHydration - Skips returning the channels as instances of the Channel class and rather returns the raw query response.
1790+
*
1791+
* @return {Promise<Array<Channel>>} search channels response
1792+
*/
1793+
async queryChannels(
1794+
filterConditions: ChannelFilters,
1795+
sort: ChannelSort = [],
1796+
options: ChannelOptions = {},
1797+
stateOptions: ChannelStateOptions = {},
1798+
) {
1799+
const channels = await this.queryChannelsRequest(filterConditions, sort, options);
1800+
17801801
this.dispatchEvent({
17811802
type: 'channels.queried',
17821803
queriedChannels: {
1783-
channels: data.channels,
1804+
channels,
17841805
isLatestMessageSet: true,
17851806
},
17861807
});
17871808

1788-
return this.hydrateActiveChannels(data.channels, stateOptions, options);
1809+
return this.hydrateActiveChannels(channels, stateOptions, options);
17891810
}
17901811

17911812
/**

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,7 @@ export type ChannelQueryOptions = {
968968
export type ChannelStateOptions = {
969969
offlineMode?: boolean;
970970
skipInitialization?: string[];
971+
skipHydration?: boolean;
971972
};
972973

973974
export type CreateChannelOptions = {

test/unit/client.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { mockChannelQueryResponse } from './test-utils/mockChannelQueryResponse'
1010
import { DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE } from '../../src/constants';
1111

1212
import { describe, beforeEach, it, expect, beforeAll, afterAll } from 'vitest';
13+
import { Channel } from '../../src';
14+
import { ChannelAPIResponse } from '../../src';
1315

1416
describe('StreamChat getInstance', () => {
1517
beforeEach(() => {
@@ -665,6 +667,44 @@ describe('StreamChat.queryChannels', async () => {
665667
mock.restore();
666668
});
667669

670+
it('should return hydrated channels as Channel instances from queryChannels', async () => {
671+
const client = await getClientWithUser();
672+
const mockedChannelsQueryResponse = Array.from({ length: 10 }, () => ({
673+
...mockChannelQueryResponse,
674+
messages: Array.from(
675+
{ length: DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE },
676+
generateMsg,
677+
),
678+
}));
679+
const postStub = sinon
680+
.stub(client, 'post')
681+
.returns(Promise.resolve({ channels: mockedChannelsQueryResponse }));
682+
const queryChannelsResponse = await client.queryChannels();
683+
expect(queryChannelsResponse.length).to.be.equal(mockedChannelsQueryResponse.length);
684+
queryChannelsResponse.forEach((item) => {
685+
expect(item).to.be.instanceOf(Channel);
686+
});
687+
postStub.restore();
688+
});
689+
690+
it('should return the raw channels response from queryChannelsRequest', async () => {
691+
const client = await getClientWithUser();
692+
const mockedChannelsQueryResponse = Array.from({ length: 10 }, () => ({
693+
...mockChannelQueryResponse,
694+
messages: Array.from(
695+
{ length: DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE },
696+
generateMsg,
697+
),
698+
}));
699+
const postStub = sinon
700+
.stub(client, 'post')
701+
.returns(Promise.resolve({ channels: mockedChannelsQueryResponse }));
702+
const queryChannelsResponse = await client.queryChannelsRequest();
703+
expect(queryChannelsResponse.length).to.be.equal(mockedChannelsQueryResponse.length);
704+
expect(queryChannelsResponse).to.deep.equal(mockedChannelsQueryResponse);
705+
postStub.restore();
706+
});
707+
668708
it('should not update pagination for queried message set', async () => {
669709
const client = await getClientWithUser();
670710
const mockedChannelsQueryResponse = Array.from({ length: 10 }, () => ({

0 commit comments

Comments
 (0)