зеркало из https://github.com/mozilla/fxa.git
feat(phone): Add `getConfirmedPhoneNumber` and `removePhoneNumber` functions
This commit is contained in:
Родитель
a2a8c19f45
Коммит
88324a3b1f
|
@ -14,6 +14,12 @@ export class RecoveryPhoneError extends BaseError {
|
|||
}
|
||||
}
|
||||
|
||||
export class RecoveryNumberNotExistsError extends RecoveryPhoneError {
|
||||
constructor(uid: string, cause?: Error) {
|
||||
super('Recovery number does not exist', { uid }, cause);
|
||||
}
|
||||
}
|
||||
|
||||
export class RecoveryNumberInvalidFormatError extends RecoveryPhoneError {
|
||||
constructor(uid: string, phoneNumber: string, cause?: Error) {
|
||||
super('Invalid phone number format', { uid, phoneNumber }, cause);
|
||||
|
|
|
@ -40,6 +40,32 @@ describe('RecoveryPhoneManager', () => {
|
|||
await db.destroy();
|
||||
});
|
||||
|
||||
it('should get a recovery phone', async () => {
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
|
||||
const { uid, phoneNumber } = mockPhone;
|
||||
await recoveryPhoneManager.registerPhoneNumber(
|
||||
uid.toString('hex'),
|
||||
phoneNumber
|
||||
);
|
||||
|
||||
const result = await recoveryPhoneManager.getConfirmedPhoneNumber(
|
||||
uid.toString('hex')
|
||||
);
|
||||
expect(result.uid).toEqual(mockPhone.uid);
|
||||
expect(result.phoneNumber).toEqual(mockPhone.phoneNumber);
|
||||
});
|
||||
|
||||
it('should throw if no recovery phone found', async () => {
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
|
||||
const { uid } = mockPhone;
|
||||
|
||||
await expect(
|
||||
recoveryPhoneManager.getConfirmedPhoneNumber(uid.toString('hex'))
|
||||
).rejects.toThrow('Recovery number does not exist');
|
||||
});
|
||||
|
||||
it('should create a recovery phone', async () => {
|
||||
const insertIntoSpy = jest.spyOn(db, 'insertInto');
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
|
@ -62,7 +88,7 @@ describe('RecoveryPhoneManager', () => {
|
|||
).rejects.toThrow('Invalid phone number format');
|
||||
});
|
||||
|
||||
it('should fail to register if recovery phone already exists', async () => {
|
||||
it('should throw if recovery phone already exists', async () => {
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
const { uid, phoneNumber } = mockPhone;
|
||||
await recoveryPhoneManager.registerPhoneNumber(
|
||||
|
@ -75,6 +101,34 @@ describe('RecoveryPhoneManager', () => {
|
|||
).rejects.toThrow('Recovery number already exists');
|
||||
});
|
||||
|
||||
it('should remove a recovery phone', async () => {
|
||||
const deleteFromSpy = jest.spyOn(db, 'deleteFrom');
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
|
||||
const { uid, phoneNumber } = mockPhone;
|
||||
await recoveryPhoneManager.registerPhoneNumber(
|
||||
uid.toString('hex'),
|
||||
phoneNumber
|
||||
);
|
||||
|
||||
const result = await recoveryPhoneManager.removePhoneNumber(
|
||||
mockPhone.uid.toString('hex')
|
||||
);
|
||||
expect(deleteFromSpy).toBeCalledWith('recoveryPhones');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if no phone number removed', async () => {
|
||||
const deleteFromSpy = jest.spyOn(db, 'deleteFrom');
|
||||
const mockPhone = RecoveryPhoneFactory();
|
||||
|
||||
const result = await recoveryPhoneManager.removePhoneNumber(
|
||||
mockPhone.uid.toString('hex')
|
||||
);
|
||||
expect(deleteFromSpy).toBeCalledWith('recoveryPhones');
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle database errors gracefully', async () => {
|
||||
jest.spyOn(db, 'insertInto').mockImplementation(() => {
|
||||
throw new Error('Database error');
|
||||
|
|
|
@ -7,10 +7,15 @@ import {
|
|||
AccountDbProvider,
|
||||
} from '@fxa/shared/db/mysql/account';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { registerPhoneNumber } from './recovery-phone.repository';
|
||||
import {
|
||||
getConfirmedPhoneNumber,
|
||||
registerPhoneNumber,
|
||||
removePhoneNumber,
|
||||
} from './recovery-phone.repository';
|
||||
import {
|
||||
RecoveryNumberAlreadyExistsError,
|
||||
RecoveryNumberInvalidFormatError,
|
||||
RecoveryNumberNotExistsError,
|
||||
} from './recovery-phone.errors';
|
||||
import { Redis } from 'ioredis';
|
||||
|
||||
|
@ -64,6 +69,30 @@ export class RecoveryPhoneManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the confirmed phone number for a user.
|
||||
*
|
||||
* @param uid
|
||||
*/
|
||||
async getConfirmedPhoneNumber(uid: string): Promise<any> {
|
||||
const uidBuffer = Buffer.from(uid, 'hex');
|
||||
const result = await getConfirmedPhoneNumber(this.db, uidBuffer);
|
||||
if (!result) {
|
||||
throw new RecoveryNumberNotExistsError(uid);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove account phone number.
|
||||
*
|
||||
* @param uid
|
||||
*/
|
||||
async removePhoneNumber(uid: string): Promise<boolean> {
|
||||
const uidBuffer = Buffer.from(uid, 'hex');
|
||||
return await removePhoneNumber(this.db, uidBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store phone number data and SMS code for a user.
|
||||
*
|
||||
|
|
|
@ -4,9 +4,29 @@
|
|||
import { AccountDatabase } from '@fxa/shared/db/mysql/account';
|
||||
import { RecoveryPhone } from './recovery-phone.types';
|
||||
|
||||
export async function getConfirmedPhoneNumber(
|
||||
db: AccountDatabase,
|
||||
uid: Buffer
|
||||
) {
|
||||
return db
|
||||
.selectFrom('recoveryPhones')
|
||||
.where('uid', '=', uid)
|
||||
.selectAll()
|
||||
.executeTakeFirst();
|
||||
}
|
||||
|
||||
export async function registerPhoneNumber(
|
||||
db: AccountDatabase,
|
||||
recoveryPhone: RecoveryPhone
|
||||
) {
|
||||
return await db.insertInto('recoveryPhones').values(recoveryPhone).execute();
|
||||
}
|
||||
|
||||
export async function removePhoneNumber(db: AccountDatabase, uid: Buffer) {
|
||||
const result = await db
|
||||
.deleteFrom('recoveryPhones')
|
||||
.where('uid', '=', uid)
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return result.numDeletedRows === BigInt(1);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче