From 61a5d2f796db06599452db42ec8398b160ff5be9 Mon Sep 17 00:00:00 2001 From: Tom Milewski Date: Thu, 10 Apr 2025 14:45:29 -0400 Subject: [PATCH] feat(backend): Add waitlist entries to Backend API client --- .changeset/thick-showers-drop.md | 18 ++++++++ .../src/api/endpoints/WaitlistEntryApi.ts | 41 +++++++++++++++++++ packages/backend/src/api/endpoints/index.ts | 1 + packages/backend/src/api/factory.ts | 2 + .../backend/src/api/resources/Deserializer.ts | 3 ++ packages/backend/src/api/resources/Enums.ts | 2 + packages/backend/src/api/resources/JSON.ts | 9 ++-- .../src/api/resources/WaitlistEntry.ts | 27 ++++++++++++ packages/backend/src/api/resources/index.ts | 1 + 9 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 .changeset/thick-showers-drop.md create mode 100644 packages/backend/src/api/endpoints/WaitlistEntryApi.ts create mode 100644 packages/backend/src/api/resources/WaitlistEntry.ts diff --git a/.changeset/thick-showers-drop.md b/.changeset/thick-showers-drop.md new file mode 100644 index 00000000000..726b2f61f45 --- /dev/null +++ b/.changeset/thick-showers-drop.md @@ -0,0 +1,18 @@ +--- +'@clerk/backend': patch +--- + +Adds the ability to list and create waitlist entries to the Backend API client. + + +```ts + import { createClerkClient } from '@clerk/backend'; + + const clerkClient = createClerkClient(...); + + await clerkClient.waitlistEntries.list({...}); + await clerkClient.waitlistEntries.create({ + emailAddress: 'you@yourdomain.com', + notify: true + }); +``` \ No newline at end of file diff --git a/packages/backend/src/api/endpoints/WaitlistEntryApi.ts b/packages/backend/src/api/endpoints/WaitlistEntryApi.ts new file mode 100644 index 00000000000..63fbc7d0fa9 --- /dev/null +++ b/packages/backend/src/api/endpoints/WaitlistEntryApi.ts @@ -0,0 +1,41 @@ +import type { ClerkPaginationRequest } from '@clerk/types'; + +import type { PaginatedResourceResponse } from '../resources/Deserializer'; +import type { WaitlistEntryStatus } from '../resources/Enums'; +import type { WaitlistEntry } from '../resources/WaitlistEntry'; +import { AbstractAPI } from './AbstractApi'; +import type { WithSign } from './util-types'; + +const basePath = '/waitlist_entries'; + +type WaitlistEntryListParams = ClerkPaginationRequest<{ + /** + * Filter waitlist entries by `email_address` or `id` + */ + query?: string; + status?: WaitlistEntryStatus; + orderBy?: WithSign<'created_at' | 'invited_at' | 'email_address'>; +}>; + +type WaitlistEntryCreateParams = { + emailAddress: string; + notify?: boolean; +}; + +export class WaitlistEntryAPI extends AbstractAPI { + public async list(params: WaitlistEntryListParams = {}) { + return this.request>({ + method: 'GET', + path: basePath, + queryParams: params, + }); + } + + public async create(params: WaitlistEntryCreateParams) { + return this.request({ + method: 'POST', + path: basePath, + bodyParams: params, + }); + } +} diff --git a/packages/backend/src/api/endpoints/index.ts b/packages/backend/src/api/endpoints/index.ts index 2fa9fe5506a..134e1333d64 100644 --- a/packages/backend/src/api/endpoints/index.ts +++ b/packages/backend/src/api/endpoints/index.ts @@ -20,4 +20,5 @@ export * from './SessionApi'; export * from './SignInTokenApi'; export * from './TestingTokenApi'; export * from './UserApi'; +export * from './WaitlistEntryApi'; export * from './WebhookApi'; diff --git a/packages/backend/src/api/factory.ts b/packages/backend/src/api/factory.ts index 2daab6a8665..3a2dcd05554 100644 --- a/packages/backend/src/api/factory.ts +++ b/packages/backend/src/api/factory.ts @@ -20,6 +20,7 @@ import { SignInTokenAPI, TestingTokenAPI, UserAPI, + WaitlistEntryAPI, WebhookAPI, } from './endpoints'; import { buildRequest } from './request'; @@ -55,6 +56,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) { signInTokens: new SignInTokenAPI(request), testingTokens: new TestingTokenAPI(request), users: new UserAPI(request), + waitlistEntries: new WaitlistEntryAPI(request), webhooks: new WebhookAPI(request), }; } diff --git a/packages/backend/src/api/resources/Deserializer.ts b/packages/backend/src/api/resources/Deserializer.ts index 7f3990cdd32..13a80e76c4c 100644 --- a/packages/backend/src/api/resources/Deserializer.ts +++ b/packages/backend/src/api/resources/Deserializer.ts @@ -31,6 +31,7 @@ import { import { AccountlessApplication } from './AccountlessApplication'; import type { PaginatedResponseJSON } from './JSON'; import { ObjectType } from './JSON'; +import { WaitlistEntry } from './WaitlistEntry'; type ResourceResponse = { data: T; @@ -135,6 +136,8 @@ function jsonToObject(item: any): any { return getCount(item); case ObjectType.User: return User.fromJSON(item); + case ObjectType.WaitlistEntry: + return WaitlistEntry.fromJSON(item); default: return item; } diff --git a/packages/backend/src/api/resources/Enums.ts b/packages/backend/src/api/resources/Enums.ts index 4f3a606ca9f..b29c9801891 100644 --- a/packages/backend/src/api/resources/Enums.ts +++ b/packages/backend/src/api/resources/Enums.ts @@ -54,3 +54,5 @@ export type ActorTokenStatus = (typeof ActorTokenStatus)[keyof typeof ActorToken export type AllowlistIdentifierType = 'email_address' | 'phone_number' | 'web3_wallet'; export type BlocklistIdentifierType = AllowlistIdentifierType; + +export type WaitlistEntryStatus = 'pending' | 'invited' | 'completed' | 'rejected'; diff --git a/packages/backend/src/api/resources/JSON.ts b/packages/backend/src/api/resources/JSON.ts index c2d5bc808fd..19387178e54 100644 --- a/packages/backend/src/api/resources/JSON.ts +++ b/packages/backend/src/api/resources/JSON.ts @@ -11,6 +11,7 @@ import type { OrganizationMembershipRole, SignInStatus, SignUpStatus, + WaitlistEntryStatus, } from './Enums'; export const ObjectType = { @@ -543,13 +544,13 @@ export interface VerificationJSON extends ClerkResourceJSON { } export interface WaitlistEntryJSON extends ClerkResourceJSON { - created_at: number; - email_address: string; + object: typeof ObjectType.WaitlistEntry; id: string; + status: WaitlistEntryStatus; + email_address: string; invitation: InvitationJSON | null; is_locked: boolean; - object: typeof ObjectType.WaitlistEntry; - status: string; + created_at: number; updated_at: number; } diff --git a/packages/backend/src/api/resources/WaitlistEntry.ts b/packages/backend/src/api/resources/WaitlistEntry.ts new file mode 100644 index 00000000000..58c2ee71cf1 --- /dev/null +++ b/packages/backend/src/api/resources/WaitlistEntry.ts @@ -0,0 +1,27 @@ +import type { WaitlistEntryStatus } from './Enums'; +import { Invitation } from './Invitation'; +import type { WaitlistEntryJSON } from './JSON'; + +export class WaitlistEntry { + constructor( + readonly id: string, + readonly emailAddress: string, + readonly status: WaitlistEntryStatus, + readonly invitation: Invitation | null, + readonly createdAt: number, + readonly updatedAt: number, + readonly isLocked?: boolean, + ) {} + + static fromJSON(data: WaitlistEntryJSON): WaitlistEntry { + return new WaitlistEntry( + data.id, + data.email_address, + data.status, + data.invitation && Invitation.fromJSON(data.invitation), + data.created_at, + data.updated_at, + data.is_locked, + ); + } +} diff --git a/packages/backend/src/api/resources/index.ts b/packages/backend/src/api/resources/index.ts index 2103e5fb733..f170e76fa36 100644 --- a/packages/backend/src/api/resources/index.ts +++ b/packages/backend/src/api/resources/index.ts @@ -45,6 +45,7 @@ export * from './User'; export * from './Verification'; export * from './SamlConnection'; export * from './TestingToken'; +export * from './WaitlistEntry'; export type { EmailWebhookEvent,