tutorbookapp/tutorbook

View on GitHub
lib/api/update/auth-user.ts

Summary

Maintainability
A
1 hr
Test Coverage
import to from 'await-to-js';

import { FirebaseError, UserRecord, auth } from 'lib/api/firebase';
import { APIError } from 'lib/model/error';
import { User } from 'lib/model/user';
import clone from 'lib/utils/clone';

/**
 * Updates the Firebase Authentication account for the given user.
 * @param user - The user to update the account for.
 * @return Promise that resolves to the updated user; throws an `APIError` if we
 * were unable to update the Firebase Authentication account.
 * @todo Perhaps remove the `validatePhone` method as it isn't used anywhere
 * else besides here.
 * @todo Handle common Firebase Authentication errors such as
 * `auth/phone-number-already-exists` or `auth/email-already-exists`.
 * @todo Remove code duplication from the `createAuthUser` component fx.
 * @todo Previously (before this massive API refactor), we prevented the user
 * from removing essential data (e.g. email) from their account. Do we want to
 * add this functionality again?
 */
export default async function updateAuthUser(user: User): Promise<User> {
  await user.validatePhone();
  const [err, userRecord] = await to<UserRecord, FirebaseError>(
    auth.updateUser(user.id, {
      disabled: false,
      email: user.email,
      emailVerified: false,
      displayName: user.name,
      photoURL: user.photo || undefined,
      phoneNumber: user.phone || undefined,
    })
  );
  if (err) {
    // TODO: Find a better way to setup my testing environment such that I don't
    // have to add these error handling exceptions. Ideally, I should be able to
    // manipulate the state of my authentication backend during tests.
    if (
      ['development', 'test'].includes(process.env.APP_ENV as string) &&
      [
        'auth/email-already-exists',
        'auth/phone-number-already-exists',
      ].includes(err.code)
    )
      return new User(clone(user));
    const msg = `${err.name} (${err.code}) updating auth account`;
    throw new APIError(`${msg} for ${user.toString()}: ${err.message}`, 500);
  }
  const record = userRecord as UserRecord;
  const updatedUser = new User(
    clone({
      ...user,
      email: record.email,
      phone: record.phoneNumber,
      photo: record.photoURL,
      name: record.displayName,
      id: record.uid,
    })
  );
  return updatedUser;
}