Merged PR 75: API実装(ユーザー一覧取得)

## 概要
[Task1592: API実装(ユーザー一覧取得)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1592)

- ユーザ一覧取得のAPIを実装
- アクセストークンにより権限を確認する
 - src/common/jwt/jwt.ts verifyAuthority([Task1593: API実装(ユーザー登録)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1593)で作成)を呼び出すため追って再修正します。(レビュー対象外です)
 - src/features/users/users.controller.ts getUsersから
  src/features/users/users.service.ts getUsersへ
- DBから同一アカウントのユーザ一覧を取得する
 - findSameAccountUsersを新規作成

- Azure AD B2Cからユーザーを取得してマージ
 - src/gateways/adb2c/adb2c.service.ts getUserを新規作成
 - マージはfor文でまわしています(力技)
- マージした結果を返却

- 影響範囲
 - usersテーブルの変更が入るときにマージ部分の手直しが要ります。(TODOを添えています)

## レビューポイント
- 新規に作成したfindSameAccountUsersの妥当性
- 新規に作成したgetUserの妥当性
→Azureからの返り値はsrc/common/token/types.tsに定義済。
 (Azure AD B2Cから取得できた項目で再定義)

## UIの変更
- 特になし

## 動作確認状況
- ローカルでビルド、テストを実行した後に動作を確認済。

## 補足
- ご不便をおかけしました。よろしくお願いします。
This commit is contained in:
x.sunamoto.k 2023-05-12 01:27:19 +00:00
parent 21695a6590
commit 3f7a9ed11a
10 changed files with 347 additions and 43 deletions

View File

@ -4,8 +4,16 @@ import type {
IDToken,
JwkSignKey,
RefreshToken,
Aadb2cUser,
} from './types';
import { isIDToken } from './typeguard';
export type { AccessToken, B2cMetadata, IDToken, JwkSignKey, RefreshToken };
export type {
AccessToken,
B2cMetadata,
IDToken,
JwkSignKey,
RefreshToken,
Aadb2cUser,
};
export { isIDToken };

View File

@ -28,3 +28,8 @@ export type JwkSignKey = {
e: string;
n: string;
};
export type Aadb2cUser = {
displayName: string;
mail: string;
};

View File

@ -1,15 +1,15 @@
import { ConfigModule, ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from '../users.service';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
import { CryptoService } from '../../../gateways/crypto/crypto.service';
import { Aadb2cUser, B2cMetadata, JwkSignKey } from '../../../common/token';
import {
AdB2cService,
ConflictError,
} from '../../../gateways/adb2c/adb2c.service';
import { CryptoService } from '../../../gateways/crypto/crypto.service';
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { User } from '../../../repositories/users/entity/user.entity';
import { JwkSignKey, B2cMetadata } from '../../../common/token';
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
import { UsersService } from '../users.service';
export type CryptoMockValue = {
getPublicKey: string | Error;
@ -19,6 +19,7 @@ export type UsersRepositoryMockValue = {
updateUserVerified: undefined | Error;
findUserById: User | Error;
createNormalUser: User | Error;
findSameAccountUsers: User[] | Error;
};
export type AdB2cMockValue = {
@ -26,6 +27,7 @@ export type AdB2cMockValue = {
getSignKeySets: JwkSignKey[] | Error;
changePassword: { sub: string } | Error;
createUser: string | ConflictError | Error;
getUser: Aadb2cUser | Error;
};
export type SendGridMockValue = {
@ -59,6 +61,10 @@ export const makeDefaultAdB2cMockValue = (): AdB2cMockValue => {
sub: 'TEST9999',
},
createUser: '001',
getUser: {
displayName: 'Hanako Sato',
mail: 'hanako@sample.com',
},
};
};
@ -81,7 +87,8 @@ export const makeSendGridServiceMock = (value: SendGridMockValue) => {
};
export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
const { getMetaData, getSignKeySets, changePassword, createUser } = value;
const { getMetaData, getSignKeySets, changePassword, createUser, getUser } =
value;
return {
getMetaData:
@ -106,6 +113,14 @@ export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
: jest
.fn<Promise<string | ConflictError>, []>()
.mockResolvedValue(createUser),
getUser:
getUser instanceof Error
? jest
.fn<Promise<Aadb2cUser | undefined>, []>()
.mockRejectedValue(getUser)
: jest
.fn<Promise<Aadb2cUser | undefined>, []>()
.mockResolvedValue(getUser),
};
};
@ -166,7 +181,12 @@ class authorIdError extends Error {
}
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
const { updateUserVerified, findUserById, createNormalUser } = value;
const {
updateUserVerified,
findUserById,
createNormalUser,
findSameAccountUsers,
} = value;
const aIdError = new authorIdError('ER_DUP_ENTRY');
@ -185,6 +205,14 @@ export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
? jest.fn<Promise<void>, []>().mockRejectedValue(aIdError)
: jest.fn<Promise<void>, []>().mockRejectedValue(createNormalUser)
: jest.fn<Promise<User>, []>().mockResolvedValue(createNormalUser),
findSameAccountUsers:
findSameAccountUsers instanceof Error
? jest
.fn<Promise<User[] | undefined>, []>()
.mockRejectedValue(findSameAccountUsers)
: jest
.fn<Promise<User[] | undefined>, []>()
.mockResolvedValue(findSameAccountUsers),
};
};
@ -206,6 +234,7 @@ export const makeSendGridMock = (value: SendGridMockValue) => {
.mockResolvedValue(createMailContentFromEmailConfirmForNormalUser),
};
};
export const makeConfigMock = (value: ConfigMockValue) => {
const { get } = value;
@ -216,6 +245,7 @@ export const makeConfigMock = (value: ConfigMockValue) => {
: jest.fn<Promise<string>, []>().mockResolvedValue(get),
};
};
export const makeDefaultCryptoMockValue = (): CryptoMockValue => {
return {
getPublicKey: [
@ -243,6 +273,7 @@ export const makeDefaultSendGridlValue = (): SendGridMockValue => {
},
};
};
export const makeDefaultConfigValue = (): ConfigMockValue => {
return {
get: `test@example.co.jp`,
@ -254,9 +285,45 @@ export const makeDefaultUsersRepositoryMockValue =
(): UsersRepositoryMockValue => {
const newUser = new User();
newUser.id = 111;
const user1 = new User();
user1.id = 2;
user1.external_id = 'ede66c43-9b9d-4222-93ed-5f11c96e08e2';
user1.account_id = 1234567890123456;
user1.role = 'none';
user1.author_id = '6cce347f-0cf1-a15e-19ab-d00988b643f9';
user1.accepted_terms_version = '1.0';
user1.email_verified = true;
user1.auto_renew = false;
user1.license_alert = false;
user1.notification = false;
user1.deleted_at = null;
user1.created_by = 'test';
user1.created_at = new Date();
user1.updated_by = null;
user1.updated_at = null;
const user2 = new User();
user2.id = 3;
user2.external_id = '698176dc-4028-4638-a452-f00bf62a7781';
user2.account_id = 1234567890123456;
user2.role = 'none';
user2.author_id = '551c4077-5b55-a38c-2c55-cd1edd537aa8';
user2.accepted_terms_version = '1.0';
user2.email_verified = true;
user2.auto_renew = false;
user2.license_alert = false;
user2.notification = false;
user2.deleted_at = null;
user2.created_by = 'test';
user2.created_at = new Date();
user2.updated_by = null;
user2.updated_at = null;
return {
updateUserVerified: undefined,
findUserById: newUser,
createNormalUser: newUser,
findSameAccountUsers: [user1, user2],
};
};

View File

@ -1,18 +1,22 @@
import { Test, TestingModule } from '@nestjs/testing';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { CryptoService } from '../../gateways/crypto/crypto.service';
describe('UsersController', () => {
let controller: UsersController;
const mockUserService = {};
const mockCryptoService = {};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UsersController],
providers: [UsersService],
providers: [UsersService, CryptoService],
})
.overrideProvider(UsersService)
.useValue(mockUserService)
.overrideProvider(CryptoService)
.useValue(mockCryptoService)
.compile();
controller = module.get<UsersController>(UsersController);

View File

@ -2,10 +2,10 @@ import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
Post,
Req,
HttpException,
} from '@nestjs/common';
import {
ApiBearerAuth,
@ -13,7 +13,14 @@ import {
ApiResponse,
ApiTags,
} from '@nestjs/swagger';
import { Request } from 'express';
import { confirmPermission } from '../../common/auth/auth';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { ErrorResponse } from '../../common/error/types/types';
import { retrieveAccessToken } from '../../common/http/helper';
import { isVerifyError, verify } from '../../common/jwt/jwt';
import { AccessToken } from '../../common/token';
import { CryptoService } from '../../gateways/crypto/crypto.service';
import {
ConfirmRequest,
ConfirmResponse,
@ -23,13 +30,6 @@ import {
SignupResponse,
} from './types/types';
import { UsersService } from './users.service';
import { Request } from 'express';
import { verify, isVerifyError } from '../../common/jwt/jwt';
import { CryptoService } from '../../gateways/crypto/crypto.service';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { AccessToken } from '../../common/token';
import { retrieveAccessToken } from '../../common/http/helper';
import { confirmPermission } from '../../common/auth/auth';
@ApiTags('users')
@Controller('users')
@ -106,7 +106,38 @@ export class UsersController {
@Get()
async getUsers(@Req() req: Request): Promise<GetUsersResponse> {
console.log(req.header('Authorization'));
return { users: [] };
// アクセストークンにより権限を確認する
const pubKey = await this.cryptoService.getPublicKey();
const accessToken = retrieveAccessToken(req);
// アクセストークンが存在しない場合のエラー
if (accessToken == undefined) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const payload = verify<AccessToken>(accessToken, pubKey);
// アクセストークン形式エラー
if (isVerifyError(payload)) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
// アクセストークンの権限不足エラー
if (!confirmPermission(payload.scope)) {
throw new HttpException(
makeErrorResponse('E000108'),
HttpStatus.UNAUTHORIZED,
);
}
const users = await this.usersService.getUsers(accessToken);
return { users };
}
@ApiResponse({

View File

@ -1,11 +1,11 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
import { CryptoModule } from '../../gateways/crypto/crypto.module';
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [

View File

@ -1,15 +1,17 @@
import { HttpException, HttpStatus } from '@nestjs/common';
import { AccessToken } from 'src/common/token';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
import { EmailAlreadyVerifiedError } from '../../repositories/users/users.repository.service';
import {
makeDefaultAdB2cMockValue,
makeDefaultConfigValue,
makeDefaultCryptoMockValue,
makeDefaultSendGridlValue,
makeDefaultUsersRepositoryMockValue,
makeUsersServiceMock,
makeDefaultAdB2cMockValue,
makeDefaultSendGridlValue,
makeDefaultConfigValue,
} from './test/users.service.mock';
import { EmailAlreadyVerifiedError } from '../../repositories/users/users.repository.service';
import { AccessToken } from 'src/common/token';
import { User } from './types/types';
describe('UsersService', () => {
it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになる', async () => {
@ -497,3 +499,99 @@ it('AuthorIDが重複している場合、エラーとなる。', async () => {
new HttpException(makeErrorResponse('E010302'), HttpStatus.BAD_REQUEST),
);
});
it('ユーザの一覧を取得する', async () => {
const cryptoMockValue = makeDefaultCryptoMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const sendgridMockValue = makeDefaultSendGridlValue();
const configMockValue = makeDefaultConfigValue();
const service = await makeUsersServiceMock(
cryptoMockValue,
usersRepositoryMockValue,
adb2cParam,
sendgridMockValue,
configMockValue,
);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
expect(await service.getUsers(token)).toEqual(expectedUsers);
});
const expectedUsers = [
{
name: 'Hanako Sato',
role: 'none',
authorId: '6cce347f-0cf1-a15e-19ab-d00988b643f9',
typistGroupName: '',
email: 'hanako@sample.com',
emailVerified: true,
autoRenew: false,
licenseAlert: false,
notification: false,
},
{
name: 'Hanako Sato',
role: 'none',
authorId: '551c4077-5b55-a38c-2c55-cd1edd537aa8',
typistGroupName: '',
email: 'hanako@sample.com',
emailVerified: true,
autoRenew: false,
licenseAlert: false,
notification: false,
},
];
it('ユーザの一覧を取得に失敗する', async () => {
const cryptoMockValue = makeDefaultCryptoMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const sendgridMockValue = makeDefaultSendGridlValue();
const configMockValue = makeDefaultConfigValue();
usersRepositoryMockValue.findSameAccountUsers = new Error(
'Failure to acquire',
);
const service = await makeUsersServiceMock(
cryptoMockValue,
usersRepositoryMockValue,
adb2cParam,
sendgridMockValue,
configMockValue,
);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
await expect(service.getUsers(token)).rejects.toEqual(
new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND),
);
});
it('ユーザの一覧を0件取得する', async () => {
const cryptoMockValue = makeDefaultCryptoMockValue();
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const sendgridMockValue = makeDefaultSendGridlValue();
const configMockValue = makeDefaultConfigValue();
// モックでDBからのユーザ取得を空にする
const noDbUsers: EntityUser[] = [];
usersRepositoryMockValue.findSameAccountUsers = noDbUsers;
const service = await makeUsersServiceMock(
cryptoMockValue,
usersRepositoryMockValue,
adb2cParam,
sendgridMockValue,
configMockValue,
);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50SWQiOjEsInVzZXJJZCI6MiwiZW1haWwiOiJ4eHhAeHh4Lnh4eCIsImlhdCI6MTAwMDAwMDAwMCwiZXhwIjo5MDAwMDAwMDAwfQ.26L6BdNg-3TbyKT62PswlJ6RPMkcTtHzlDXW2Uo9XbMPVSrl2ObcuS6EcXjFFN2DEfNTKbqX_zevIWMpHOAdLNgGhk528nLrBrNvPASqtTjvW9muxMXpjUdjRVkmVbOylBHWW3YpWL9JEbJQ7rAzWDfaIdPhMovdaxumnZt_UwnlnrdaVPLACW7tkH_laEcAU507iSiM4mqxxG8FuTs34t6PEdwRuzZAQPN2IOPYNSvGNdJYryPacSeSNZ_z1xeBYXLOLQfOBZzyTReYDOhXdikhrNUbxjgnZQlSXBCVMlZ9PH42bHfp-LJIeJzW0yqnF6oLklvJP-fo8eW0k5iDOw';
const emptyMergedUsers: User[] = [];
expect(await service.getUsers(token)).toEqual(emptyMergedUsers);
});

View File

@ -1,21 +1,22 @@
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { isVerifyError, verify } from '../../common/jwt';
import { CryptoService } from '../../gateways/crypto/crypto.service';
import {
UsersRepositoryService,
EmailAlreadyVerifiedError,
} from '../../repositories/users/users.repository.service';
import { ConfigService } from '@nestjs/config';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { isVerifyError, verify } from '../../common/jwt';
import { makePassword } from '../../common/password/password';
import { AccessToken } from '../../common/token';
import {
AdB2cService,
ConflictError,
isConflictError,
} from '../../gateways/adb2c/adb2c.service';
import { ConfigService } from '@nestjs/config';
import { CryptoService } from '../../gateways/crypto/crypto.service';
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
import { User } from '../../repositories/users/entity/user.entity';
import { makePassword } from '../../common/password/password';
import { AccessToken } from '../../common/token';
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
import {
EmailAlreadyVerifiedError,
UsersRepositoryService,
} from '../../repositories/users/users.repository.service';
import { User } from './types/types';
@Injectable()
export class UsersService {
@ -100,7 +101,7 @@ export class UsersService {
const userId = Number(accessToken.userId);
//DBよりアクセス者の所属するアカウントIDを取得する
let adminUser: User;
let adminUser: EntityUser;
try {
adminUser = await this.usersRepository.findUserById(userId);
} catch (e) {
@ -141,7 +142,7 @@ export class UsersService {
}
//Azure AD B2Cに登録したユーザー情報のID(sub)と受け取った情報を使ってDBにユーザーを登録する
let newUser: User;
let newUser: EntityUser;
// TODO 本来はNULLだが、テーブル定義に誤ってNOTNULLが付いているため、一時的に適当な値を設定
const accepted_terms_version = 'xxx';
try {
@ -261,4 +262,59 @@ export class UsersService {
}
}
}
/**
* Get Users
* @param accessToken
* @returns users
*/
async getUsers(accessToken: string): Promise<User[]> {
this.logger.log(`[IN] ${this.getUsers.name}`);
try {
// DBよりアクセス者の所属するアカウントを取得する
const pubKey = await this.cryptoService.getPublicKey();
const payload = verify<AccessToken>(accessToken, pubKey);
if (isVerifyError(payload)) {
throw new Error(`${payload.reason} | ${payload.message}`);
}
// DBから同一アカウントのユーザ一覧を取得する
const dbUsers = await this.usersRepository.findSameAccountUsers(
Number(payload.userId),
);
// 値をマージして定義されたレスポンス通りに返す
const users: User[] = [];
// TODO 膨大なループが発生することが見込まれ商用には耐えないので、本実装時に修正予定
for (let i = 0; i < dbUsers.length; i++) {
// Azure AD B2Cからユーザーを取得する
const aadb2cUser = await this.adB2cService.getUser(
dbUsers[i].external_id,
);
const user = new User();
user.name = aadb2cUser.displayName;
user.role = dbUsers[i].role;
user.authorId = dbUsers[i].author_id;
// TODO DBから取得できるようになるため暫定
user.typistGroupName = '';
user.email = aadb2cUser.mail;
user.emailVerified = dbUsers[i].email_verified;
user.autoRenew = dbUsers[i].auto_renew;
user.licenseAlert = dbUsers[i].license_alert;
user.notification = dbUsers[i].notification;
users.push(user);
}
return users;
} catch (e) {
this.logger.error(`error=${e}`);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.NOT_FOUND,
);
} finally {
this.logger.log(`[OUT] ${this.getUsers.name}`);
}
}
}

View File

@ -1,10 +1,10 @@
import { ClientSecretCredential } from '@azure/identity';
import { Client } from '@microsoft/microsoft-graph-client';
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';
import { JwkSignKey, B2cMetadata } from '../../common/token';
import { Client } from '@microsoft/microsoft-graph-client';
import { TokenCredentialAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials';
import { ClientSecretCredential } from '@azure/identity';
import { Aadb2cUser, B2cMetadata, JwkSignKey } from '../../common/token';
export type ConflictError = {
reason: 'email';
@ -165,4 +165,22 @@ export class AdB2cService {
this.logger.log(`[OUT] ${this.changePassword.name}`);
}
}
/**
* Azure AD B2Cからユーザ情報を取得する
* @param externalId ID
* @returns
*/
async getUser(externalId: string): Promise<Aadb2cUser> {
this.logger.log(`[IN] ${this.getUser.name}`);
try {
return await this.graphClient.api(`users/${externalId}`).get();
} catch (e) {
this.logger.error(e);
throw e;
} finally {
this.logger.log(`[OUT] ${this.getUser.name}`);
}
}
}

View File

@ -146,4 +146,21 @@ export class UsersRepositoryService {
return await repo.update({ id: targetUser.id }, targetUser);
});
}
/**
* IDを持つユーザーを探す
* @param userId
* @returns User[]
*/
async findSameAccountUsers(userId: number): Promise<User[]> {
const dbUser = await this.dataSource
.getRepository(User)
.findOne({ where: [{ id: userId }] });
const dbUsers = await this.dataSource
.getRepository(User)
.find({ where: [{ account_id: dbUser.account_id }] });
return dbUsers;
}
}