Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 103 additions & 10 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import type {
CreatePollAPIResponse,
CreatePollData,
CreatePollOptionAPIResponse,
CreatePredefinedFilterOptions,
CreateReminderOptions,
CustomPermissionOptions,
DeactivateUsersOptions,
Expand Down Expand Up @@ -124,6 +125,8 @@ import type {
ListCommandsResponse,
ListImportsPaginationOptions,
ListImportsResponse,
ListPredefinedFiltersOptions,
ListPredefinedFiltersResponse,
LocalMessage,
Logger,
MarkChannelsReadOptions,
Expand Down Expand Up @@ -153,6 +156,7 @@ import type {
PollVote,
PollVoteData,
PollVotesAPIResponse,
PredefinedFilterResponse,
Product,
PushPreference,
PushProvider,
Expand Down Expand Up @@ -221,6 +225,7 @@ import type {
UpdateMessageOptions,
UpdatePollAPIResponse,
UpdatePollOptionAPIResponse,
UpdatePredefinedFilterOptions,
UpdateReminderOptions,
UpdateSegmentData,
UpdateUsersAPIResponse,
Expand Down Expand Up @@ -1841,10 +1846,10 @@ export class StreamChat {
/**
* queryChannelsRequest - Queries channels and returns the raw response
*
* @param {ChannelFilters} filterConditions object MongoDB style filters
* @param {ChannelFilters} filterConditions object MongoDB style filters. Can be empty object when using predefined_filter in options.
* @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}.
* When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}]
* @param {ChannelOptions} [options] Options object
* @param {ChannelOptions} [options] Options object. Can include predefined_filter, filter_values, and sort_values for using predefined filters.
*
* @return {Promise<Array<ChannelAPIResponse>>} search channels response
*/
Expand All @@ -1865,13 +1870,23 @@ export class StreamChat {
defaultOptions.watch = false;
}

// Return a list of channels
const payload = {
filter_conditions: filterConditions,
sort: normalizeQuerySort(sort),
...defaultOptions,
...options,
};
const { predefined_filter, filter_values, sort_values, ...restOptions } = options;

// Build payload based on whether we're using a predefined filter or traditional filters
const payload = predefined_filter
? {
predefined_filter,
filter_values,
sort_values,
...defaultOptions,
...restOptions,
}
: {
filter_conditions: filterConditions,
sort: normalizeQuerySort(sort),
...defaultOptions,
...restOptions,
};

const data = await this.post<QueryChannelsAPIResponse>(
this.baseURL + '/channels',
Expand Down Expand Up @@ -3769,7 +3784,7 @@ export class StreamChat {
validateServerSideAuth() {
if (!this.secret) {
throw new Error(
'Campaigns is a server-side only feature. Please initialize the client with a secret to use this feature.',
'This feature can be used server-side only. Please initialize the client with a secret to use this feature.',
);
}
}
Expand Down Expand Up @@ -4817,4 +4832,82 @@ export class StreamChat {
payload,
);
}

/**
* createPredefinedFilter - Creates a new predefined filter (server-side only)
*
* @param {CreatePredefinedFilterOptions} options Predefined filter options
*
* @return {Promise<PredefinedFilterResponse>} The created predefined filter
*/
async createPredefinedFilter(options: CreatePredefinedFilterOptions) {
this.validateServerSideAuth();
return await this.post<PredefinedFilterResponse>(
`${this.baseURL}/predefined_filters`,
options,
);
}

/**
* getPredefinedFilter - Gets a predefined filter by name (server-side only)
*
* @param {string} name Predefined filter name
*
* @return {Promise<PredefinedFilterResponse>} The predefined filter
*/
async getPredefinedFilter(name: string) {
this.validateServerSideAuth();
return await this.get<PredefinedFilterResponse>(
`${this.baseURL}/predefined_filters/${encodeURIComponent(name)}`,
);
}

/**
* updatePredefinedFilter - Updates a predefined filter (server-side only)
*
* @param {string} name Predefined filter name
* @param {UpdatePredefinedFilterOptions} options Predefined filter options
*
* @return {Promise<PredefinedFilterResponse>} The updated predefined filter
*/
async updatePredefinedFilter(name: string, options: UpdatePredefinedFilterOptions) {
this.validateServerSideAuth();
return await this.put<PredefinedFilterResponse>(
`${this.baseURL}/predefined_filters/${encodeURIComponent(name)}`,
options,
);
}

/**
* deletePredefinedFilter - Deletes a predefined filter (server-side only)
*
* @param {string} name Predefined filter name
*
* @return {Promise<APIResponse>} The server response
*/
async deletePredefinedFilter(name: string) {
this.validateServerSideAuth();
return await this.delete<APIResponse>(
`${this.baseURL}/predefined_filters/${encodeURIComponent(name)}`,
);
}

/**
* listPredefinedFilters - Lists all predefined filters (server-side only)
*
* @param {ListPredefinedFiltersOptions} options Query options
*
* @return {Promise<ListPredefinedFiltersResponse>} The list of predefined filters
*/
async listPredefinedFilters(options: ListPredefinedFiltersOptions = {}) {
this.validateServerSideAuth();
const { sort, ...paginationOptions } = options;
return await this.get<ListPredefinedFiltersResponse>(
`${this.baseURL}/predefined_filters`,
{
...paginationOptions,
...(sort ? { sort: JSON.stringify(sort) } : {}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@totalimmersion Why do we convert the object into string? I see in types.ts the following declarations implying and usually objects are not stringified in other API calls in other methods:

export type PredefinedFilterSort = SortParam[];

export type ListPredefinedFiltersOptions = Pager & {
  sort?: PredefinedFilterSort;
};

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it's a GET request

},
);
}
}
64 changes: 64 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,21 @@ export type ChannelOptions = {
state?: boolean;
user_id?: string;
watch?: boolean;
/**
* Name of a predefined filter to use instead of filter_conditions.
* When provided, filter_conditions and sort parameters are ignored.
*/
predefined_filter?: string;
/**
* Values to interpolate into the predefined filter template placeholders.
* Only used when predefined_filter is provided.
*/
filter_values?: Record<string, unknown>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keys in this object will need to correspond to some specific entity field names or it can really be any string?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@totalimmersion Could you give an example of interpolation, please?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be any string

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
* Values to interpolate into the predefined filter sort template placeholders.
* Only used when predefined_filter is provided.
*/
sort_values?: Record<string, unknown>;
};

export type ChannelQueryOptions = {
Expand Down Expand Up @@ -4544,3 +4559,52 @@ export type UpdateChannelsBatchFilters = QueryFilters<{
export type UpdateChannelsBatchResponse = {
result: Record<string, string>;
} & Partial<TaskResponse>;

/**
* Predefined Filter Types
*/

export type PredefinedFilterOperation = 'QueryChannels';

export type PredefinedFilterSortParam = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@totalimmersion there was a discussion btw @kanat and @arnautov-anton regarding type sort field. Is this change maybe related to the general change in sort fields? Would this PR, where type field is introduced be relevant for your PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about that discussion, this pr follows current practices

field: string;
direction?: AscDesc;
type?: string;
};

export type PredefinedFilter = {
name: string;
operation: PredefinedFilterOperation;
filter: Record<string, unknown>;
created_at: string;
updated_at: string;
description?: string;
sort?: PredefinedFilterSortParam[];
query_id?: number;
};

export type CreatePredefinedFilterOptions = {
name: string;
operation: PredefinedFilterOperation;
filter: Record<string, unknown>;
description?: string;
sort?: PredefinedFilterSortParam[];
};

export type UpdatePredefinedFilterOptions = Omit<CreatePredefinedFilterOptions, 'name'>;

export type PredefinedFilterResponse = APIResponse & {
predefined_filter: PredefinedFilter;
};

export type ListPredefinedFiltersResponse = APIResponse & {
predefined_filters: PredefinedFilter[];
next?: string;
prev?: string;
};

export type PredefinedFilterSort = SortParam[];

export type ListPredefinedFiltersOptions = Pager & {
sort?: PredefinedFilterSort;
};
Loading