Skip to content
Open
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
125 changes: 125 additions & 0 deletions __tests__/scheduled-messages.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import 'dotenv/config';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { createTestClient } from './create-test-client';
import { StreamChannel } from '../src/StreamChannel';
import { StreamClient } from '../src/StreamClient';
import { StreamError } from '../src/types';
import { randomUUID } from 'crypto';

// Error code the API returns when scheduled messages are off for the app.
const SCHEDULED_MESSAGES_DISABLED = 114;

const inOneHour = () => new Date(Date.now() + 60 * 60 * 1000);

describe('scheduled messages API', () => {
let client: StreamClient;
const channelId = 'streamnodetest' + randomUUID();
let channel: StreamChannel;
const user = {
id: 'stream-node-test-user',
name: 'Stream Node Test User',
role: 'admin',
};
// Set when the app supports scheduled messages: the feature is opt-in per
// app, and backends without it ignore scheduled_at and send right away.
let scheduledMessageId: string | undefined;

beforeAll(async () => {
client = createTestClient();

await client.upsertUsers([user]);

channel = client.chat.channel('messaging', channelId);
await channel.getOrCreate({
data: {
created_by_id: user.id,
members: [{ user_id: user.id }],
},
});

try {
const response = await channel.sendMessage({
message: { text: 'Scheduled from Stream Node SDK', user_id: user.id },
scheduled_at: inOneHour(),
});
scheduledMessageId = response.scheduled_message?.id;
} catch (error) {
if (
!(error instanceof StreamError) ||
error.code !== SCHEDULED_MESSAGES_DISABLED
) {
throw error;
}
}
});

it('schedule a message', async (ctx) => {
if (!scheduledMessageId) ctx.skip();

const response = await client.chat.queryScheduledMessages({
user_id: user.id,
filter: { cid: channel.cid },
});

expect(response.scheduled_messages.length).toBe(1);
const scheduled = response.scheduled_messages[0];
expect(scheduled.id).toBe(scheduledMessageId);
expect(scheduled.status).toBe('scheduled');
expect(scheduled.cid).toBe(channel.cid);
expect(scheduled.message.text).toBe('Scheduled from Stream Node SDK');
expect(scheduled.scheduled_at).toBeInstanceOf(Date);
});

it('update a scheduled message', async (ctx) => {
if (!scheduledMessageId) ctx.skip();

const scheduledAt = inOneHour();
const response = await client.chat.updateScheduledMessage({
id: scheduledMessageId!,
scheduled_at: scheduledAt,
message: { text: 'Rescheduled from Stream Node SDK' },
});

expect(response.scheduled_message.message.text).toBe(
'Rescheduled from Stream Node SDK',
);
expect(response.scheduled_message.scheduled_at.getTime()).toBe(
scheduledAt.getTime(),
);
});

it('send a scheduled message now', async (ctx) => {
if (!scheduledMessageId) ctx.skip();

const response = await client.chat.sendScheduledMessageNow({
id: scheduledMessageId!,
});

expect(response.scheduled_message.id).toBe(scheduledMessageId);
});

it('delete a scheduled message', async (ctx) => {
if (!scheduledMessageId) ctx.skip();

const sendResponse = await channel.sendMessage({
message: { text: 'Deleted before sending', user_id: user.id },
scheduled_at: inOneHour(),
});
const id = sendResponse.scheduled_message!.id;

const response = await client.chat.deleteScheduledMessage({ id });

expect(response.scheduled_message.id).toBe(id);
expect(response.scheduled_message.status).toBe('canceled');

const queryResponse = await client.chat.queryScheduledMessages({
user_id: user.id,
filter: { cid: channel.cid },
});
expect(queryResponse.scheduled_messages.map((m) => m.id)).not.toContain(id);
});

afterAll(async () => {
await channel.delete({ hard_delete: true });
});
});
18 changes: 18 additions & 0 deletions src/gen/chat/ChannelApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
MarkReadRequest,
MarkReadResponse,
MarkUnreadRequest,
QueryChannelBansRequest,
QueryChannelBansResponse,
Response,
SendEventRequest,
SendMessageRequest,
Expand Down Expand Up @@ -114,6 +116,22 @@ export class ChannelApi {
});
}

queryChannelBans(
request?: QueryChannelBansRequest,
): Promise<StreamResponse<QueryChannelBansResponse>> {
if (!this.id) {
throw new Error(
`Channel isn't yet created, call getOrCreateDistinctChannel() before this operation`,
);
}

return this.chatApi.queryChannelBans({
id: this.id,
type: this.type,
...request,
});
}

deleteDraft(request?: {
parent_id?: string;
user_id?: string;
Expand Down
137 changes: 137 additions & 0 deletions src/gen/chat/ChatApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
DeleteReminderResponse,
DeleteRetentionPolicyRequest,
DeleteRetentionPolicyResponse,
DeleteScheduledMessageResponse,
DeleteSegmentTargetsRequest,
EventResponse,
ExportChannelsRequest,
Expand Down Expand Up @@ -71,6 +72,8 @@ import {
QueryBannedUsersResponse,
QueryCampaignsRequest,
QueryCampaignsResponse,
QueryChannelBansRequest,
QueryChannelBansResponse,
QueryChannelsRequest,
QueryChannelsResponse,
QueryDraftsRequest,
Expand All @@ -87,6 +90,8 @@ import {
QueryReactionsResponse,
QueryRemindersRequest,
QueryRemindersResponse,
QueryScheduledMessagesRequest,
QueryScheduledMessagesResponse,
QuerySegmentTargetsRequest,
QuerySegmentTargetsResponse,
QuerySegmentsRequest,
Expand All @@ -103,6 +108,8 @@ import {
SendMessageResponse,
SendReactionRequest,
SendReactionResponse,
SendScheduledMessageNowRequest,
SendScheduledMessageNowResponse,
SendUserCustomEventRequest,
SetRetentionPolicyRequest,
SetRetentionPolicyResponse,
Expand Down Expand Up @@ -141,6 +148,8 @@ import {
UpdatePredefinedFilterResponse,
UpdateReminderRequest,
UpdateReminderResponse,
UpdateScheduledMessageRequest,
UpdateScheduledMessageResponse,
UpdateSegmentRequest,
UpdateSegmentResponse,
UpdateThreadPartialRequest,
Expand Down Expand Up @@ -452,6 +461,7 @@ export class ChatApi {
const body = {
limit: request?.limit,
user_id: request?.user_id,
sort: request?.sort,
groups: request?.groups,
user: request?.user,
};
Expand Down Expand Up @@ -661,6 +671,37 @@ export class ChatApi {
return response;
}

async queryChannelBans(
request: QueryChannelBansRequest & { type: string; id: string },
): Promise<StreamResponse<QueryChannelBansResponse>> {
const pathParams = {
type: request?.type,
id: request?.id,
};
const body = {
exclude_expired_bans: request?.exclude_expired_bans,
include_total: request?.include_total,
limit: request?.limit,
next: request?.next,
prev: request?.prev,
user_id: request?.user_id,
filter_conditions: request?.filter_conditions,
user: request?.user,
};

const response = await this.apiClient.sendRequest<QueryChannelBansResponse>(
'POST',
'/api/v2/chat/channels/{type}/{id}/bans/query',
pathParams,
undefined,
body,
);

decoders['QueryChannelBansResponse']?.(response);

return response;
}

async deleteDraft(request: {
type: string;
id: string;
Expand Down Expand Up @@ -916,6 +957,7 @@ export class ChatApi {
include_mentioned_members: request?.include_mentioned_members,
keep_channel_hidden: request?.keep_channel_hidden,
pending: request?.pending,
scheduled_at: request?.scheduled_at,
skip_enrich_url: request?.skip_enrich_url,
skip_push: request?.skip_push,
pending_message_metadata: request?.pending_message_metadata,
Expand Down Expand Up @@ -1854,6 +1896,8 @@ export class ChatApi {
poll_id: request?.poll_id,
};
const body = {
config_key: request?.config_key,
config_team: request?.config_team,
user_id: request?.user_id,
user: request?.user,
vote: request?.vote,
Expand Down Expand Up @@ -2339,6 +2383,99 @@ export class ChatApi {
return response;
}

async queryScheduledMessages(
request?: QueryScheduledMessagesRequest,
): Promise<StreamResponse<QueryScheduledMessagesResponse>> {
const body = {
limit: request?.limit,
next: request?.next,
user_id: request?.user_id,
sort: request?.sort,
filter: request?.filter,
user: request?.user,
};

const response =
await this.apiClient.sendRequest<QueryScheduledMessagesResponse>(
'POST',
'/api/v2/chat/scheduled_messages/query',
undefined,
undefined,
body,
);

decoders['QueryScheduledMessagesResponse']?.(response);

return response;
}

async deleteScheduledMessage(request: {
id: string;
}): Promise<StreamResponse<DeleteScheduledMessageResponse>> {
const pathParams = {
id: request?.id,
};

const response =
await this.apiClient.sendRequest<DeleteScheduledMessageResponse>(
'DELETE',
'/api/v2/chat/scheduled_messages/{id}',
pathParams,
undefined,
);

decoders['DeleteScheduledMessageResponse']?.(response);

return response;
}

async updateScheduledMessage(
request: UpdateScheduledMessageRequest & { id: string },
): Promise<StreamResponse<UpdateScheduledMessageResponse>> {
const pathParams = {
id: request?.id,
};
const body = {
scheduled_at: request?.scheduled_at,
message: request?.message,
};

const response =
await this.apiClient.sendRequest<UpdateScheduledMessageResponse>(
'PATCH',
'/api/v2/chat/scheduled_messages/{id}',
pathParams,
undefined,
body,
);

decoders['UpdateScheduledMessageResponse']?.(response);

return response;
}

async sendScheduledMessageNow(
request: SendScheduledMessageNowRequest & { id: string },
): Promise<StreamResponse<SendScheduledMessageNowResponse>> {
const pathParams = {
id: request?.id,
};
const body = {};

const response =
await this.apiClient.sendRequest<SendScheduledMessageNowResponse>(
'POST',
'/api/v2/chat/scheduled_messages/{id}/send_now',
pathParams,
undefined,
body,
);

decoders['SendScheduledMessageNowResponse']?.(response);

return response;
}

async search(request?: {
payload?: SearchPayload;
}): Promise<StreamResponse<SearchResponse>> {
Expand Down
Loading
Loading