From b277af83c23157464f18479a2cfd6633e446cfd0 Mon Sep 17 00:00:00 2001 From: Haris Chaniotakis Date: Thu, 22 Sep 2022 16:07:11 +0300 Subject: [PATCH] feat(types,clerk-js): Introduce BackupCodeResource and user.createBackupCode() oepration https://www.notion.so/clerkdev/Introduce-new-FAPI-me-backup-codes-endpoints-31f0832bb7544d4d90ba27758cac2a54 --- .../clerk-js/src/core/resources/BackupCode.ts | 27 +++++++++++++++++++ .../clerk-js/src/core/resources/User.test.ts | 26 ++++++++++++++++++ packages/clerk-js/src/core/resources/User.ts | 14 ++++++++++ packages/types/src/backupCode.ts | 8 ++++++ packages/types/src/index.ts | 1 + packages/types/src/json.ts | 8 ++++++ packages/types/src/user.ts | 2 ++ 7 files changed, 86 insertions(+) create mode 100644 packages/clerk-js/src/core/resources/BackupCode.ts create mode 100644 packages/types/src/backupCode.ts diff --git a/packages/clerk-js/src/core/resources/BackupCode.ts b/packages/clerk-js/src/core/resources/BackupCode.ts new file mode 100644 index 00000000000..de7848a3445 --- /dev/null +++ b/packages/clerk-js/src/core/resources/BackupCode.ts @@ -0,0 +1,27 @@ +import { BackupCodeJSON, BackupCodeResource } from '@clerk/types'; + +import { unixEpochToDate } from '../../utils/date'; +import { BaseResource } from './internal'; + +export class BackupCode extends BaseResource implements BackupCodeResource { + pathRoot = '/me'; + + id!: string; + codes: string[] = []; + updatedAt: Date | null = null; + createdAt: Date | null = null; + + constructor(data: BackupCodeJSON) { + super(); + this.fromJSON(data); + } + + protected fromJSON(data: BackupCodeJSON): this { + this.id = data.id; + this.codes = data.codes; + this.updatedAt = unixEpochToDate(data.updated_at); + this.createdAt = unixEpochToDate(data.created_at); + + return this; + } +} diff --git a/packages/clerk-js/src/core/resources/User.test.ts b/packages/clerk-js/src/core/resources/User.test.ts index 462f18353a9..b91fe4e5e96 100644 --- a/packages/clerk-js/src/core/resources/User.test.ts +++ b/packages/clerk-js/src/core/resources/User.test.ts @@ -215,4 +215,30 @@ describe('User', () => { path: '/me/totp', }); }); + + it('creates backup codes', async () => { + const backupCodeJSON = { + object: 'backup_code', + id: 'bcode_1234', + codes: ['1234', '5678'], + }; + + // @ts-ignore + BaseResource._fetch = jest.fn().mockReturnValue(Promise.resolve({ response: backupCodeJSON })); + + const user = new User({ + email_addresses: [], + phone_numbers: [], + web3_wallets: [], + external_accounts: [], + } as unknown as UserJSON); + + await user.createBackupCode(); + + // @ts-ignore + expect(BaseResource._fetch).toHaveBeenCalledWith({ + method: 'POST', + path: '/me/backup_codes/', + }); + }); }); diff --git a/packages/clerk-js/src/core/resources/User.ts b/packages/clerk-js/src/core/resources/User.ts index e43ae4ae2cd..a02498b9d5b 100644 --- a/packages/clerk-js/src/core/resources/User.ts +++ b/packages/clerk-js/src/core/resources/User.ts @@ -1,4 +1,6 @@ import type { + BackupCodeJSON, + BackupCodeResource, CreateEmailAddressParams, CreatePhoneNumberParams, CreateWeb3WalletParams, @@ -23,6 +25,7 @@ import type { import { unixEpochToDate } from '../../utils/date'; import { normalizeUnsafeMetadata } from '../../utils/resourceParams'; +import { BackupCode } from './BackupCode'; import { BaseResource, DeletedObject, @@ -177,6 +180,17 @@ export class User extends BaseResource implements UserResource { return new DeletedObject(json); }; + createBackupCode = async (): Promise => { + const json = ( + await BaseResource._fetch({ + path: this.path() + '/backup_codes/', + method: 'POST', + }) + )?.response as unknown as BackupCodeJSON; + + return new BackupCode(json); + }; + update = (params: UpdateUserParams): Promise => { return this._basePatch({ body: normalizeUnsafeMetadata(params), diff --git a/packages/types/src/backupCode.ts b/packages/types/src/backupCode.ts new file mode 100644 index 00000000000..db29f66b91e --- /dev/null +++ b/packages/types/src/backupCode.ts @@ -0,0 +1,8 @@ +import { ClerkResource } from './resource'; + +export interface BackupCodeResource extends ClerkResource { + id: string; + codes: string[]; + createdAt: Date | null; + updatedAt: Date | null; +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 3651aa8d6b5..3d9a4699045 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,5 +1,6 @@ export * from './api'; export * from './authConfig'; +export * from './backupCode'; export * from './clerk'; export * from './client'; export * from './deletedObject'; diff --git a/packages/types/src/json.ts b/packages/types/src/json.ts index 04d8f0c2ead..de0ec4b85df 100644 --- a/packages/types/src/json.ts +++ b/packages/types/src/json.ts @@ -297,6 +297,14 @@ export interface TOTPJSON extends ClerkResourceJSON { updated_at: number; } +export interface BackupCodeJSON extends ClerkResourceJSON { + object: 'backup_code'; + id: string; + codes: string[]; + created_at: number; + updated_at: number; +} + export interface DeletedObjectJSON { object: string; id?: string; diff --git a/packages/types/src/user.ts b/packages/types/src/user.ts index c0dfd6e7e66..75f59f8d859 100644 --- a/packages/types/src/user.ts +++ b/packages/types/src/user.ts @@ -1,3 +1,4 @@ +import { BackupCodeResource } from './backupCode'; import { DeletedObjectResource } from './deletedObject'; import { EmailAddressResource } from './emailAddress'; import { ExternalAccountResource } from './externalAccount'; @@ -77,6 +78,7 @@ export interface UserResource extends ClerkResource { createTOTP: () => Promise; verifyTOTP: (params: VerifyTOTPParams) => Promise; disableTOTP: () => Promise; + createBackupCode: () => Promise; get verifiedExternalAccounts(): ExternalAccountResource[]; get unverifiedExternalAccounts(): ExternalAccountResource[]; get hasVerifiedEmailAddress(): boolean;