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
4 changes: 1 addition & 3 deletions src/clients/comments-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export class CommentsClient extends BaseClient {
*
* @param args - The arguments for getting comments.
* @param args.threadId - The thread ID.
* @param args.from - @deprecated Use `newerThan` instead.
* @param args.newerThan - Optional date to get comments newer than.
* @param args.olderThan - Optional date to get comments older than.
* @param args.limit - Optional limit on number of comments returned.
Expand All @@ -42,8 +41,7 @@ export class CommentsClient extends BaseClient {
*/
getComments(args: GetCommentsArgs): Promise<Comment[]> {
const params: Record<string, unknown> = { threadId: args.threadId }
const newerThan = args.newerThan ?? args.from
if (newerThan) params.newerThanTs = Math.floor(newerThan.getTime() / 1000)
if (args.newerThan) params.newerThanTs = Math.floor(args.newerThan.getTime() / 1000)
if (args.olderThan) params.olderThanTs = Math.floor(args.olderThan.getTime() / 1000)
if (args.limit) params.limit = args.limit

Expand Down
63 changes: 63 additions & 0 deletions src/clients/inbox-client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { http, HttpResponse } from 'msw'
import { describe, expect, it } from 'vitest'
import { CommsApi } from '../comms-api'
import { server } from '../testUtils/msw-setup'
import { TEST_API_BASE_URL as BASE, TEST_API_TOKEN } from '../testUtils/test-defaults'

// Pins the wire shape of `inbox-client` after the deprecated `since` /
// `until` aliases were dropped — both serialization paths (URL params
// for `getInbox`, body for `archiveAll`) need explicit coverage.

describe('InboxClient — wire serialization', () => {
it('getInbox sends workspace_id / newer_than_ts / older_than_ts on the URL', async () => {
const capturedUrls: URL[] = []
server.use(
http.get(`${BASE}/inbox/get`, ({ request }) => {
capturedUrls.push(new URL(request.url))
return HttpResponse.json([])
}),
)

const api = new CommsApi(TEST_API_TOKEN)
await api.inbox.getInbox({
workspaceId: 1,
newerThan: new Date('2026-01-01T00:00:00Z'),
olderThan: new Date('2026-02-01T00:00:00Z'),
limit: 25,
archiveFilter: 'all',
})

expect(capturedUrls).toHaveLength(1)
const params = (capturedUrls[0] as URL).searchParams
expect(params.get('workspace_id')).toBe('1')
expect(params.get('newer_than_ts')).toBe(
String(Math.floor(new Date('2026-01-01T00:00:00Z').getTime() / 1000)),
)
expect(params.get('older_than_ts')).toBe(
String(Math.floor(new Date('2026-02-01T00:00:00Z').getTime() / 1000)),
)
expect(params.get('limit')).toBe('25')
expect(params.get('archive_filter')).toBe('all')
})

it('archiveAll POSTs workspace_id and older_than_ts as snake_case', async () => {
let capturedBody: Record<string, unknown> | null = null
server.use(
http.post(`${BASE}/inbox/archive_all`, async ({ request }) => {
capturedBody = (await request.json()) as Record<string, unknown>
return HttpResponse.json({ status: 'ok' })
}),
)

const api = new CommsApi(TEST_API_TOKEN)
await api.inbox.archiveAll({
workspaceId: 1,
olderThan: new Date('2026-02-01T00:00:00Z'),
})

expect(capturedBody).toEqual({
workspace_id: 1,
older_than_ts: Math.floor(new Date('2026-02-01T00:00:00Z').getTime() / 1000),
})
})
})
13 changes: 3 additions & 10 deletions src/clients/inbox-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ export class InboxClient extends BaseClient {
* @param args.workspaceId - The workspace ID.
* @param args.newerThan - Optional date to get items newer than.
* @param args.olderThan - Optional date to get items older than.
* @param args.since - @deprecated Use `newerThan` instead.
* @param args.until - @deprecated Use `olderThan` instead.
* @param args.limit - Optional limit on number of items returned.
* @param args.cursor - Optional cursor for pagination.
* @param args.archiveFilter - Optional filter: 'active' (default), 'archived', or 'all'.
Expand All @@ -41,10 +39,8 @@ export class InboxClient extends BaseClient {
*/
getInbox(args: GetInboxArgs): Promise<InboxThread[]> {
const params: Record<string, unknown> = { workspace_id: args.workspaceId }
const newerThan = args.newerThan ?? args.since
if (newerThan) params.newer_than_ts = Math.floor(newerThan.getTime() / 1000)
const olderThan = args.olderThan ?? args.until
if (olderThan) params.older_than_ts = Math.floor(olderThan.getTime() / 1000)
if (args.newerThan) params.newer_than_ts = Math.floor(args.newerThan.getTime() / 1000)
Comment thread
amix marked this conversation as resolved.
if (args.olderThan) params.older_than_ts = Math.floor(args.olderThan.getTime() / 1000)
if (args.limit) params.limit = args.limit
if (args.cursor) params.cursor = args.cursor
if (args.archiveFilter) params.archive_filter = args.archiveFilter
Expand Down Expand Up @@ -152,8 +148,6 @@ export class InboxClient extends BaseClient {
* @param args.workspaceId - The workspace ID.
* @param args.channelIds - Optional array of channel IDs to filter by.
* @param args.olderThan - Optional date to filter items older than.
* @param args.until - @deprecated Use `olderThan` instead.
* @param args.since - @deprecated Not supported by the archive_all endpoint — this value is ignored.
*
* @example
* ```typescript
Expand All @@ -166,8 +160,7 @@ export class InboxClient extends BaseClient {
archiveAll(args: ArchiveAllArgs): Promise<void> {
const params: Record<string, unknown> = { workspace_id: args.workspaceId }
if (args.channelIds) params.channel_ids = args.channelIds
const olderThan = args.olderThan ?? args.until
if (olderThan) params.older_than_ts = Math.floor(olderThan.getTime() / 1000)
if (args.olderThan) params.older_than_ts = Math.floor(args.olderThan.getTime() / 1000)
Comment thread
amix marked this conversation as resolved.

return request<void>({
httpMethod: 'POST',
Expand Down
66 changes: 66 additions & 0 deletions src/clients/threads-client.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { http, HttpResponse } from 'msw'
import { describe, expect, it } from 'vitest'
import { CommsApi } from '../comms-api'
import { server } from '../testUtils/msw-setup'
import { TEST_API_BASE_URL as BASE, TEST_API_TOKEN } from '../testUtils/test-defaults'

// Pins the wire shape of `threads-client`. The PR that dropped the
// `newer_than_ts` / `older_than_ts` aliases also tightened how `params`
// is built — this test catches both a casing regression and any
// accidental forwarding of unknown keys.

describe('ThreadsClient — wire serialization', () => {
it('getThreads sends workspace_id / channel_id / newer_than_ts / older_than_ts on the URL', async () => {
const capturedUrls: URL[] = []
server.use(
http.get(`${BASE}/threads/get`, ({ request }) => {
capturedUrls.push(new URL(request.url))
return HttpResponse.json([])
}),
)

const api = new CommsApi(TEST_API_TOKEN)
await api.threads.getThreads({
workspaceId: 1,
channelId: '7YpL3oZ4kZ9vP7Q1tR2sX44',
archived: false,
newerThan: new Date('2026-01-01T00:00:00Z'),
olderThan: new Date('2026-02-01T00:00:00Z'),
limit: 50,
})

expect(capturedUrls).toHaveLength(1)
const params = (capturedUrls[0] as URL).searchParams
expect(params.get('workspace_id')).toBe('1')
expect(params.get('channel_id')).toBe('7YpL3oZ4kZ9vP7Q1tR2sX44')
expect(params.get('archived')).toBe('false')
expect(params.get('newer_than_ts')).toBe(
String(Math.floor(new Date('2026-01-01T00:00:00Z').getTime() / 1000)),
)
expect(params.get('older_than_ts')).toBe(
String(Math.floor(new Date('2026-02-01T00:00:00Z').getTime() / 1000)),
)
expect(params.get('limit')).toBe('50')
})

it('getThreads ignores legacy snake_case keys passed at runtime', async () => {
const capturedUrls: URL[] = []
server.use(
http.get(`${BASE}/threads/get`, ({ request }) => {
capturedUrls.push(new URL(request.url))
return HttpResponse.json([])
}),
)

const api = new CommsApi(TEST_API_TOKEN)
await api.threads.getThreads({
workspaceId: 1,
// biome-ignore lint/suspicious/noExplicitAny: legacy-shape forced through `any`
...({ newer_than_ts: 999, older_than_ts: 888 } as any),
})

const params = (capturedUrls[0] as URL).searchParams
expect(params.get('newer_than_ts')).toBeNull()
expect(params.get('older_than_ts')).toBeNull()
})
})
16 changes: 6 additions & 10 deletions src/clients/threads-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ export class ThreadsClient extends BaseClient {
* @param args.archived - Optional flag to include archived threads.
* @param args.newerThan - Optional date to get threads newer than.
* @param args.olderThan - Optional date to get threads older than.
* @param args.newer_than_ts - @deprecated Use `newerThan` instead.
* @param args.older_than_ts - @deprecated Use `olderThan` instead.
* @param args.limit - Optional limit on number of threads returned.
* @returns An array of thread objects.
*
Expand All @@ -66,14 +64,12 @@ export class ThreadsClient extends BaseClient {
* ```
*/
getThreads(args: GetThreadsArgs): Promise<Thread[]> {
const { newerThan, olderThan, newer_than_ts, older_than_ts, ...rest } = args
const resolvedNewerThan = newerThan ? Math.floor(newerThan.getTime() / 1000) : newer_than_ts
const resolvedOlderThan = olderThan ? Math.floor(olderThan.getTime() / 1000) : older_than_ts
const params = {
...rest,
...(resolvedNewerThan != null ? { newer_than_ts: resolvedNewerThan } : {}),
...(resolvedOlderThan != null ? { older_than_ts: resolvedOlderThan } : {}),
}
const params: Record<string, unknown> = { workspaceId: args.workspaceId }
if (args.channelId != null) params.channelId = args.channelId
if (args.archived != null) params.archived = args.archived
if (args.limit != null) params.limit = args.limit
if (args.newerThan) params.newer_than_ts = Math.floor(args.newerThan.getTime() / 1000)
if (args.olderThan) params.older_than_ts = Math.floor(args.olderThan.getTime() / 1000)

return request<Thread[]>({
httpMethod: 'GET',
Expand Down
26 changes: 2 additions & 24 deletions src/types/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,30 +127,16 @@ export const GetThreadsArgsSchema = z.object({
limit: z.number().nullable().optional(),
})

export type GetThreadsArgs = Omit<
z.infer<typeof GetThreadsArgsSchema>,
'newerThan' | 'olderThan'
> & {
newerThan?: Date | null
olderThan?: Date | null
/** @deprecated Use `newerThan` instead. */
newer_than_ts?: number | null
/** @deprecated Use `olderThan` instead. */
older_than_ts?: number | null
}
export type GetThreadsArgs = z.infer<typeof GetThreadsArgsSchema>

export const GetCommentsArgsSchema = z.object({
threadId: z.string(),
from: z.date().nullable().optional(),
newerThan: z.date().nullable().optional(),
olderThan: z.date().nullable().optional(),
limit: z.number().nullable().optional(),
})

export type GetCommentsArgs = Omit<z.infer<typeof GetCommentsArgsSchema>, 'from'> & {
/** @deprecated Use `newerThan` instead. */
from?: Date | null
}
export type GetCommentsArgs = z.infer<typeof GetCommentsArgsSchema>

export const GetConversationsArgsSchema = z.object({
workspaceId: z.number(),
Expand Down Expand Up @@ -248,10 +234,6 @@ export type GetInboxArgs = {
workspaceId: number
newerThan?: Date
olderThan?: Date
/** @deprecated Use `newerThan` instead. */
since?: Date
/** @deprecated Use `olderThan` instead. */
until?: Date
limit?: number
cursor?: string
archiveFilter?: ArchiveFilter
Expand All @@ -261,10 +243,6 @@ export type ArchiveAllArgs = {
workspaceId: number
channelIds?: string[]
olderThan?: Date
/** @deprecated Use `olderThan` instead. */
until?: Date
/** @deprecated Not supported by the archive_all endpoint — this value is ignored. */
since?: Date
}

// Reactions.
Expand Down
Loading