OMDSCloud/dictation_server/src/repositories/users/users.repository.service.ts
makabe.t bd4aaa8ae1 Merged PR 63: API実装(メール認証)
## 概要
[Task1497: API実装(メール認証)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1497)

- メール認証APIとテストを実装しました。

- IDトークンの型を現状と合わせて修正しました
  - `family_name`と`given_name`を削除しました。
- auth.serviceのテストも併せて修正しました。
  - テストケースのIDトークンを環境変数の鍵で生成するように修正しました。

## レビューポイント
- DBのユーザを検証済みにする処理について、トランザクション内で取得と更新をしていますがトランザクションの使い方として問題ないでしょうか?
- 本APIで使用するカスタムエラーを`common/error/types`に暫定的においていますがどこに配置するのが適切でしょうか?
  - `common/error/types`に配置する、もしくは`common`配下にカスタムエラー用のフォルダを作成してその下に配置するのが良いかと考えています。
- テストのモックでエラーを発生させる際に、テストケース内でエラーを設定していますがモックファイル内でエラー用のモックを設定するべきでしょうか?

## UIの変更
- なし

## 動作確認状況
- ローカルで確認
  - テストが通ることを確認
2023-04-10 04:44:16 +00:00

106 lines
2.7 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { DataSource, UpdateResult } from 'typeorm';
import { User } from './entity/user.entity';
// UsersRepositoryServiceで発生するエラーを定義
export class EmailAlreadyVerifiedError extends Error {}
export class UserNotFoundError extends Error {}
@Injectable()
export class UsersRepositoryService {
constructor(private dataSource: DataSource) {}
async create(
accountId: number,
externalUserId: string,
role: string,
acceptedTermsVersion: string,
): Promise<User> {
const user = new User();
{
user.account_id = accountId;
user.external_id = externalUserId;
user.role = role;
user.accepted_terms_version = acceptedTermsVersion;
}
const createdEntity = await this.dataSource.transaction(
async (entityManager) => {
const repo = entityManager.getRepository(User);
const newUser = repo.create(user);
const persisted = await repo.save(newUser);
return persisted;
},
);
return createdEntity;
}
async findVerifiedUser(sub: string): Promise<User | undefined> {
const user = await this.dataSource.getRepository(User).findOne({
where: {
external_id: sub,
email_verified: true,
},
});
if (!user) {
return undefined;
}
return user;
}
async findUserById(id: number): Promise<User | undefined> {
const user = await this.dataSource.getRepository(User).findOne({
where: {
id: id,
},
});
if (!user) {
return undefined;
}
return user;
}
/**
* 特定の情報でユーザーを更新する
* @param user
* @returns update
*/
async update(user: User): Promise<UpdateResult> {
return await this.dataSource.transaction(async (entityManager) => {
const repo = entityManager.getRepository(User);
return await repo.update({ id: user.id }, user);
});
}
/**
* 管理ユーザーがメール認証済みなら認証情報を更新する
* @param user
* @returns update
*/
async updateUserVerified(id: number): Promise<UpdateResult> {
return await this.dataSource.transaction(async (entityManager) => {
const repo = entityManager.getRepository(User);
const targetUser = await repo.findOne({
where: {
id: id,
},
});
// 運用上ユーザがいないことはあり得ないが、プログラム上発生しうるのでエラーとして処理
if (!targetUser) {
throw new UserNotFoundError();
}
if (targetUser.email_verified) {
throw new EmailAlreadyVerifiedError();
}
targetUser.email_verified = true;
return await repo.update({ id: targetUser.id }, targetUser);
});
}
}