Merged PR 765: データ削除ツール作成+動作確認
## 概要 [Task3569: データ削除ツール作成+動作確認](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3569) - ADB2Cからのユーザー削除が100件ごとにしか削除できていなかったので、修正しました。 - 取得が100件まででそのユーザーに対して削除処理をしていたので100件までの削除になっていました。 - 対応として、100件づつの削除をユーザーが全削除されるまで実行するようにしました。 ## レビューポイント - 対応方法として適切でしょうか? - ループで制限を設けていますが、MAX値として適切でしょうか? ## UIの変更 - なし ## 動作確認状況 - ローカルで順に実行できることを確認 - 実際の削除は別途develop環境で実施します。
This commit is contained in:
parent
e3ee9412c9
commit
dc52ec2022
12
data_migration_tools/server/package-lock.json
generated
12
data_migration_tools/server/package-lock.json
generated
@ -3107,9 +3107,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.8.2",
|
"version": "8.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@ -12333,9 +12333,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "8.8.2",
|
"version": "8.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||||
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
|
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"acorn-import-assertions": {
|
"acorn-import-assertions": {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
|||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { DeleteService } from "./delete.service";
|
import { DeleteService } from "./delete.service";
|
||||||
import { DeleteResponse } from "./types/types";
|
import { DeleteResponse } from "./types/types";
|
||||||
|
import { makeContext } from "src/common/log";
|
||||||
|
|
||||||
@ApiTags("delete")
|
@ApiTags("delete")
|
||||||
@Controller("delete")
|
@Controller("delete")
|
||||||
@ -33,7 +34,9 @@ export class DeleteController {
|
|||||||
})
|
})
|
||||||
@Post()
|
@Post()
|
||||||
async deleteData(): Promise<{}> {
|
async deleteData(): Promise<{}> {
|
||||||
await this.deleteService.deleteData();
|
const context = makeContext("tool", "delete");
|
||||||
|
|
||||||
|
await this.deleteService.deleteData(context);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { DeleteRepositoryService } from "../../repositories/delete/delete.reposi
|
|||||||
import { makeErrorResponse } from "../../common/errors/makeErrorResponse";
|
import { makeErrorResponse } from "../../common/errors/makeErrorResponse";
|
||||||
import { AdB2cService } from "../../gateways/adb2c/adb2c.service";
|
import { AdB2cService } from "../../gateways/adb2c/adb2c.service";
|
||||||
import { BlobstorageService } from "../../gateways/blobstorage/blobstorage.service";
|
import { BlobstorageService } from "../../gateways/blobstorage/blobstorage.service";
|
||||||
|
import { Context } from "../../common/log";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeleteService {
|
export class DeleteService {
|
||||||
@ -17,21 +18,34 @@ export class DeleteService {
|
|||||||
* データを削除する
|
* データを削除する
|
||||||
* @returns data
|
* @returns data
|
||||||
*/
|
*/
|
||||||
async deleteData(): Promise<void> {
|
async deleteData(context: Context): Promise<void> {
|
||||||
this.logger.log(`[IN] ${this.deleteData.name}`);
|
this.logger.log(
|
||||||
|
`[IN] [${context.getTrackingId()}] ${this.deleteData.name}`
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
// BlobStorageからデータを削除する
|
// BlobStorageからデータを削除する
|
||||||
await this.blobstorageService.deleteContainers();
|
await this.blobstorageService.deleteContainers(context);
|
||||||
|
|
||||||
|
// 100件ずつのユーザー取得なのですべて削除するまでループする
|
||||||
|
for (let i = 0; i < 500; i++) {
|
||||||
// ADB2Cからユーザ情報を取得する
|
// ADB2Cからユーザ情報を取得する
|
||||||
const users = await this.adB2cService.getUsers();
|
const { users, hasNext } = await this.adB2cService.getUsers(context);
|
||||||
|
|
||||||
|
// ユーザーがいない場合はループを抜ける
|
||||||
|
if (!hasNext) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
const externalIds = users.map((user) => user.id);
|
const externalIds = users.map((user) => user.id);
|
||||||
await this.adB2cService.deleteUsers(externalIds);
|
await this.adB2cService.deleteUsers(context, externalIds);
|
||||||
|
}
|
||||||
|
|
||||||
// データベースからデータを削除する
|
// データベースからデータを削除する
|
||||||
await this.deleteRepositoryService.deleteData();
|
await this.deleteRepositoryService.deleteData();
|
||||||
// AutoIncrementの値をリセットする
|
// AutoIncrementの値をリセットする
|
||||||
await this.deleteRepositoryService.resetAutoIncrement();
|
await this.deleteRepositoryService.resetAutoIncrement();
|
||||||
|
// 初期データを挿入する
|
||||||
|
await this.deleteRepositoryService.insertInitData(context);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`error=${e}`);
|
this.logger.error(`error=${e}`);
|
||||||
if (e instanceof Error) {
|
if (e instanceof Error) {
|
||||||
|
|||||||
@ -30,14 +30,10 @@ export const isConflictError = (arg: unknown): arg is ConflictError => {
|
|||||||
export class AdB2cService {
|
export class AdB2cService {
|
||||||
private readonly logger = new Logger(AdB2cService.name);
|
private readonly logger = new Logger(AdB2cService.name);
|
||||||
private readonly tenantName: string;
|
private readonly tenantName: string;
|
||||||
private readonly flowName: string;
|
|
||||||
private readonly ttl: number;
|
|
||||||
private graphClient: Client;
|
private graphClient: Client;
|
||||||
|
|
||||||
constructor(private readonly configService: ConfigService) {
|
constructor(private readonly configService: ConfigService) {
|
||||||
this.tenantName = this.configService.getOrThrow<string>("TENANT_NAME");
|
this.tenantName = this.configService.getOrThrow<string>("TENANT_NAME");
|
||||||
this.flowName = this.configService.getOrThrow<string>("SIGNIN_FLOW_NAME");
|
|
||||||
this.ttl = this.configService.getOrThrow<number>("ADB2C_CACHE_TTL");
|
|
||||||
|
|
||||||
// ADB2Cへの認証情報
|
// ADB2Cへの認証情報
|
||||||
const credential = new ClientSecretCredential(
|
const credential = new ClientSecretCredential(
|
||||||
@ -111,8 +107,10 @@ export class AdB2cService {
|
|||||||
* @param externalIds
|
* @param externalIds
|
||||||
* @returns users
|
* @returns users
|
||||||
*/
|
*/
|
||||||
async getUsers(): Promise<AdB2cUser[]> {
|
async getUsers(
|
||||||
this.logger.log(`[IN] ${this.getUsers.name}`);
|
context: Context
|
||||||
|
): Promise<{ users: AdB2cUser[]; hasNext: boolean }> {
|
||||||
|
this.logger.log(`[IN] [${context.getTrackingId()}] ${this.getUsers.name}`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res: AdB2cResponse = await this.graphClient
|
const res: AdB2cResponse = await this.graphClient
|
||||||
@ -121,7 +119,7 @@ export class AdB2cService {
|
|||||||
.filter(`creationType eq 'LocalAccount'`)
|
.filter(`creationType eq 'LocalAccount'`)
|
||||||
.get();
|
.get();
|
||||||
|
|
||||||
return res.value;
|
return { users: res.value, hasNext: !!res["@odata.nextLink"] };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`error=${e}`);
|
this.logger.error(`error=${e}`);
|
||||||
const { statusCode } = e;
|
const { statusCode } = e;
|
||||||
@ -177,9 +175,11 @@ export class AdB2cService {
|
|||||||
* Azure AD B2Cからユーザ情報を削除する(複数)
|
* Azure AD B2Cからユーザ情報を削除する(複数)
|
||||||
* @param externalIds 外部ユーザーID
|
* @param externalIds 外部ユーザーID
|
||||||
*/
|
*/
|
||||||
async deleteUsers(externalIds: string[]): Promise<void> {
|
async deleteUsers(context: Context, externalIds: string[]): Promise<void> {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
`[IN]${this.deleteUsers.name} | params: { externalIds: ${externalIds} };`
|
`[IN] [${context.getTrackingId()}] ${
|
||||||
|
this.deleteUsers.name
|
||||||
|
} | params: { externalIds: ${externalIds} };`
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -89,8 +89,10 @@ export class BlobstorageService {
|
|||||||
* すべてのコンテナを削除します。
|
* すべてのコンテナを削除します。
|
||||||
* @returns containers
|
* @returns containers
|
||||||
*/
|
*/
|
||||||
async deleteContainers(): Promise<void> {
|
async deleteContainers(context: Context): Promise<void> {
|
||||||
this.logger.log(`[IN] ${this.deleteContainers.name}`);
|
this.logger.log(
|
||||||
|
`[IN] [${context.getTrackingId()}] ${this.deleteContainers.name}`
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for await (const container of this.blobServiceClientAU.listContainers({
|
for await (const container of this.blobServiceClientAU.listContainers({
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { DataSource } from "typeorm";
|
import { DataSource } from "typeorm";
|
||||||
import { logger } from "@azure/identity";
|
import { logger } from "@azure/identity";
|
||||||
import { Account } from "./entity/account.entity";
|
|
||||||
import { AUTO_INCREMENT_START } from "../../constants";
|
import { AUTO_INCREMENT_START } from "../../constants";
|
||||||
|
import { Term } from "./entity/term.entity";
|
||||||
|
import { insertEntities } from "../../common/repository";
|
||||||
|
import { Context } from "../../common/log";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DeleteRepositoryService {
|
export class DeleteRepositoryService {
|
||||||
|
// クエリログにコメントを出力するかどうか
|
||||||
|
private readonly isCommentOut = process.env.STAGE !== "local";
|
||||||
constructor(private dataSource: DataSource) {}
|
constructor(private dataSource: DataSource) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,4 +58,35 @@ export class DeleteRepositoryService {
|
|||||||
await queryRunner.release();
|
await queryRunner.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初期データを挿入する
|
||||||
|
* @returns data
|
||||||
|
*/
|
||||||
|
async insertInitData(context: Context): Promise<void> {
|
||||||
|
await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
const termRepo = entityManager.getRepository(Term);
|
||||||
|
|
||||||
|
// ワークフローのデータ作成
|
||||||
|
const newTarmDpa = new Term();
|
||||||
|
newTarmDpa.document_type = "DPA";
|
||||||
|
newTarmDpa.version = "V0.1";
|
||||||
|
const newTarmEula = new Term();
|
||||||
|
newTarmEula.document_type = "EULA";
|
||||||
|
newTarmEula.version = "V0.1";
|
||||||
|
const newTarmPrivacyNotice = new Term();
|
||||||
|
newTarmPrivacyNotice.document_type = "PrivacyNotice";
|
||||||
|
newTarmPrivacyNotice.version = "V0.1";
|
||||||
|
|
||||||
|
const initTerms = [newTarmDpa, newTarmEula, newTarmPrivacyNotice];
|
||||||
|
|
||||||
|
await insertEntities(
|
||||||
|
Term,
|
||||||
|
termRepo,
|
||||||
|
initTerms,
|
||||||
|
this.isCommentOut,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user