From 93562b7c9e73f93947b56640c14d21e1c8c9fcea Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 26 Jul 2023 20:49:01 +0300 Subject: [PATCH] feat(clerk-js,types): Introduce UserOrganizationInvitation + UserOrganizationInvitationResource + UserOrganizationInvitationJSON + ClerkPaginatedResponse that handles FAPI paginated responses --- .changeset/shaggy-vans-relax.md | 12 ++++ packages/clerk-js/src/core/resources/User.ts | 6 ++ .../resources/UserOrganizationInvitation.ts | 64 +++++++++++++++++++ .../clerk-js/src/core/resources/internal.ts | 1 + packages/types/src/api.ts | 8 +++ packages/types/src/index.ts | 1 + packages/types/src/json.ts | 12 ++++ packages/types/src/user.ts | 7 ++ .../types/src/userOrganizationInvitation.ts | 30 +++++++++ 9 files changed, 141 insertions(+) create mode 100644 .changeset/shaggy-vans-relax.md create mode 100644 packages/clerk-js/src/core/resources/UserOrganizationInvitation.ts create mode 100644 packages/types/src/userOrganizationInvitation.ts diff --git a/.changeset/shaggy-vans-relax.md b/.changeset/shaggy-vans-relax.md new file mode 100644 index 00000000000..c5963fbdfa8 --- /dev/null +++ b/.changeset/shaggy-vans-relax.md @@ -0,0 +1,12 @@ +--- +'@clerk/clerk-js': patch +'@clerk/types': patch +--- + +Introduces a new internal class `UserOrganizationInvitation` that represents and invitation to join an organization with the organization data populated +Additions to support the above +- UserOrganizationInvitationResource +- UserOrganizationInvitationJSON +- ClerkPaginatedResponse + +ClerkPaginatedResponse represents a paginated FAPI response diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index e5d844ee50b..1940a265a37 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -10,6 +10,7 @@ import type { EmailAddressResource, ExternalAccountJSON, ExternalAccountResource, + GetUserOrganizationInvitationsParams, ImageResource, OrganizationMembershipResource, PhoneNumberResource, @@ -42,6 +43,7 @@ import { SamlAccount, SessionWithActivities, TOTP, + UserOrganizationInvitation, Web3Wallet, } from './internal'; @@ -250,6 +252,10 @@ export class User extends BaseResource implements UserResource { }); }; + getOrganizationInvitations = (params?: GetUserOrganizationInvitationsParams) => { + return UserOrganizationInvitation.retrieve(params); + }; + getOrganizationMemberships = async ( retrieveMembership: RetrieveMembershipsParams, ): Promise => { diff --git a/packages/clerk-js/src/core/resources/UserOrganizationInvitation.ts b/packages/clerk-js/src/core/resources/UserOrganizationInvitation.ts new file mode 100644 index 00000000000..828a0a23660 --- /dev/null +++ b/packages/clerk-js/src/core/resources/UserOrganizationInvitation.ts @@ -0,0 +1,64 @@ +import type { + ClerkPaginatedResponse, + GetUserOrganizationInvitationsParams, + MembershipRole, + OrganizationInvitationStatus, + OrganizationResource, + UserOrganizationInvitationJSON, + UserOrganizationInvitationResource, +} from '@clerk/types'; + +import { unixEpochToDate } from '../../utils/date'; +import { BaseResource, Organization } from './internal'; + +export class UserOrganizationInvitation extends BaseResource implements UserOrganizationInvitationResource { + id!: string; + emailAddress!: string; + organization!: OrganizationResource; + publicMetadata: OrganizationInvitationPublicMetadata = {}; + status!: OrganizationInvitationStatus; + role!: MembershipRole; + createdAt!: Date; + updatedAt!: Date; + + static async retrieve( + params?: GetUserOrganizationInvitationsParams, + ): Promise> { + return await BaseResource._fetch({ + path: '/me/organization_invitations', + method: 'GET', + search: params as any, + }) + .then(res => { + const { data: invites, total_count } = + res?.response as unknown as ClerkPaginatedResponse; + + return { + total_count, + data: invites.map(invitation => new UserOrganizationInvitation(invitation)), + }; + }) + .catch(() => ({ + total_count: 0, + data: [], + })); + } + + constructor(data: UserOrganizationInvitationJSON) { + super(); + this.fromJSON(data); + } + protected fromJSON(data: UserOrganizationInvitationJSON | null): this { + if (data) { + this.id = data.id; + this.emailAddress = data.email_address; + this.organization = new Organization(data.organization); + this.publicMetadata = data.public_metadata; + this.role = data.role; + this.status = data.status; + this.createdAt = unixEpochToDate(data.created_at); + this.updatedAt = unixEpochToDate(data.updated_at); + } + return this; + } +} diff --git a/packages/clerk-js/src/core/resources/internal.ts b/packages/clerk-js/src/core/resources/internal.ts index 9095d45159e..442b2930bea 100644 --- a/packages/clerk-js/src/core/resources/internal.ts +++ b/packages/clerk-js/src/core/resources/internal.ts @@ -23,5 +23,6 @@ export * from './SignUp'; export * from './Token'; export * from './TOTP'; export * from './User'; +export * from './UserOrganizationInvitation'; export * from './Verification'; export * from './Web3Wallet'; diff --git a/packages/types/src/api.ts b/packages/types/src/api.ts index 283b1873797..59f5d02a6fa 100644 --- a/packages/types/src/api.ts +++ b/packages/types/src/api.ts @@ -25,3 +25,11 @@ export interface ClerkPaginationParams { limit?: number; offset?: number; } + +/** + * Pagination params + */ +export interface ClerkPaginatedResponse { + data: T[]; + total_count: number; +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 01c0fe64883..969ac4121cd 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -39,6 +39,7 @@ export * from './theme'; export * from './token'; export * from './totp'; export * from './user'; +export * from './userOrganizationInvitation'; export * from './userSettings'; export * from './utils'; export * from './verification'; diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index bd85282ef62..5ff89c67d06 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -340,6 +340,18 @@ export interface OrganizationInvitationJSON extends ClerkResourceJSON { updated_at: number; } +export interface UserOrganizationInvitationJSON extends ClerkResourceJSON { + object: 'organization_invitation'; + id: string; + email_address: string; + organization: OrganizationJSON; + public_metadata: OrganizationInvitationPublicMetadata; + status: OrganizationInvitationStatus; + role: MembershipRole; + created_at: number; + updated_at: number; +} + export interface UserDataJSON { first_name?: string; last_name?: string; diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index 751abc84b45..a1b6c850d69 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -1,3 +1,4 @@ +import type { ClerkPaginatedResponse, ClerkPaginationParams } from './api'; import type { BackupCodeResource } from './backupCode'; import type { DeletedObjectResource } from './deletedObject'; import type { EmailAddressResource } from './emailAddress'; @@ -12,6 +13,7 @@ import type { SamlAccountResource } from './samlAccount'; import type { SessionWithActivitiesResource } from './session'; import type { OAuthStrategy } from './strategies'; import type { TOTPResource } from './totp'; +import type { UserOrganizationInvitationResource } from './userOrganizationInvitation'; import type { SnakeToCamel } from './utils'; import type { Web3WalletResource } from './web3Wallet'; @@ -93,6 +95,9 @@ export interface UserResource extends ClerkResource { getSessions: () => Promise; setProfileImage: (params: SetProfileImageParams) => Promise; createExternalAccount: (params: CreateExternalAccountParams) => Promise; + getOrganizationInvitations: ( + params?: GetUserOrganizationInvitationsParams, + ) => Promise>; createTOTP: () => Promise; verifyTOTP: (params: VerifyTOTPParams) => Promise; disableTOTP: () => Promise; @@ -150,3 +155,5 @@ export type UpdateUserPasswordParams = { }; export type RemoveUserPasswordParams = Pick; + +export type GetUserOrganizationInvitationsParams = ClerkPaginationParams; diff --git a/packages/types/src/userOrganizationInvitation.ts b/packages/types/src/userOrganizationInvitation.ts new file mode 100644 index 00000000000..08e3f0cd7d0 --- /dev/null +++ b/packages/types/src/userOrganizationInvitation.ts @@ -0,0 +1,30 @@ +import type { OrganizationResource } from './organization'; +import type { OrganizationInvitationStatus } from './organizationInvitation'; +import type { MembershipRole } from './organizationMembership'; +import type { ClerkResource } from './resource'; + +declare global { + /** + * If you want to provide custom types for the organizationInvitation.publicMetadata + * object, simply redeclare this rule in the global namespace. + * Every organizationInvitation object will use the provided type. + */ + interface UserOrganizationInvitationPublicMetadata { + [k: string]: unknown; + } + + interface UserOrganizationInvitationPrivateMetadata { + [k: string]: unknown; + } +} + +export interface UserOrganizationInvitationResource extends ClerkResource { + id: string; + emailAddress: string; + organization: OrganizationResource; + publicMetadata: UserOrganizationInvitationPublicMetadata; + role: MembershipRole; + status: OrganizationInvitationStatus; + createdAt: Date; + updatedAt: Date; +}