Merged PR 914: ユーザー削除API修正

## 概要
[Task4223: ユーザー削除API修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4223)

- Authorの削除条件の修正
  - Authorが作成したタスクがある場合は削除できない
- Typistの削除条件の修正
  - 文字起こし担当のタスクがある場合は削除できないように修正
    - 挙動は変わらない(記述を修正した)
- リテラル反映
- テストケース追加

## レビューポイント
- テストケースは足りているか

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## クエリの変更
- 64行目のクエリ(Author/Typistともに)
- https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%AF%E3%82%A8%E3%83%AA/Task4223?csf=1&web=1&e=UsjxWP

## 動作確認状況
- ローカルで確認、develop環境で確認など
- 行った修正がデグレを発生させていないことを確認できるか
  - 具体的にどのような確認をしたか
    - Authorが作成したタスクがある場合にそのAuthorは削除できないことを確認(タスクのステータスはFinished・Backup)
    - AuthorのAuthorIDが設定されているタスクがある場合はそのAuthorは削除できないことを確認(すべてのステータス)
    - Typistが文字起こし担当のタスクがある場合そのTypistは削除できないことを確認(タスクのステータスはFinished・Backup)

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2024-06-11 05:05:53 +00:00
parent 474cdd56c6
commit aea66f4616
8 changed files with 801 additions and 54 deletions

View File

@ -54,7 +54,7 @@
},
"text": {
"maintenanceNotificationTitle": "Hinweis auf geplante Wartungsarbeiten",
"maintenanceNotification": "Aufgrund von Systemwartungsarbeiten wird ODMS Cloud ab dem 12. Juni, 6:00 Uhr UTC-Zeit, etwa eine Stunde lang nicht verfügbar sein. Wir entschuldigen uns für etwaige Unannehmlichkeiten, die während der Wartung entstanden sind."
"maintenanceNotification": "Aufgrund von Systemwartungsarbeiten wird ODMS Cloud ab dem 17. Juni, 6:00 Uhr UTC-Zeit, etwa eine Stunde lang nicht verfügbar sein. Wir entschuldigen uns für etwaige Unannehmlichkeiten, die während der Wartung entstanden sind."
}
},
"signupPage": {
@ -138,8 +138,8 @@
"userDeletionLicenseActiveError": "Der Benutzer konnte nicht gelöscht werden. Bitte heben Sie die Lizenzzuweisung vom Benutzer auf.",
"typistDeletionRoutingRuleError": "Der Benutzer konnte nicht gelöscht werden. Dieser Benutzer ist als Transkriptionist registriert, der in den Routing-Regeln enthalten ist. Bitte entfernen Sie den Transcriptionist aus der entsprechenden Routing-Regel auf der Registerkarte „Workflow“.",
"adminUserDeletionError": "Der Benutzer konnte nicht gelöscht werden. Bitte entfernen Sie den Benutzer vom primären oder sekundären Administrator auf der Registerkarte „Konto“.",
"typistUserDeletionTranscriptionTaskError": "Der Benutzer konnte nicht gelöscht werden. Diesem Transkriptionisten ist eine Aufgabe zugewiesen. Bitte ändern Sie die für die Aufgabe verantwortliche Person auf der Registerkarte „Diktieren“ in einen anderen Transkriptionisten.",
"authorUserDeletionTranscriptionTaskError": "Der Benutzer konnte nicht gelöscht werden. Es gibt von diesem Autor erstellte Aufgaben, die unvollständig sind. Bitte löschen Sie die Aufgaben, die von diesem Autor erstellt wurden, oder markieren Sie sie als erledigt.",
"typistUserDeletionTranscriptionTaskError": "Der Benutzer konnte nicht gelöscht werden. Diesem Transkriptionisten ist eine Aufgabe zugewiesen. Bitte ändern Sie die für die Aufgabe verantwortliche Person auf der Registerkarte „Diktat“ in einen anderen Transkriptionisten, wenn noch nicht abgeschlossene Aufgaben vorhanden sind. Falls der Status der Diktate als „Abgeschlossen“ oder „Backup“ markiert ist, löschen Sie bitte die Aufgabe.",
"authorUserDeletionTranscriptionTaskError": "Der Benutzer konnte nicht gelöscht werden. Es gibt von diesem Autor erstellte Aufgaben, die noch nicht abgeschlossen sind. Bitte markieren Sie die Aufgaben als abgeschlossen und löschen Sie sie, bevor Sie den Autor löschen.",
"typistUserDeletionTranscriptionistGroupError": "Der Benutzer konnte nicht gelöscht werden. Bitte entfernen Sie diesen Transkriptionisten aus der Transkriptionistengruppe auf der Registerkarte „Workflow“.",
"authorDeletionRoutingRuleError": "Der Benutzer konnte nicht gelöscht werden. Bitte entfernen Sie diesen Autor aus den Weiterleitungsregeln auf der Registerkarte „Workflow“.",
"importSuccess": "Wir haben Ihre Anfrage zur Massenbenutzerregistrierung erhalten. Bitte überprüfen Sie Ihre E-Mails, da Sie eine E-Mail erhalten, sobald der Registrierungsprozess abgeschlossen ist.",
@ -253,7 +253,7 @@
"dictationPage": {
"message": {
"noPlaybackAuthorization": "Sie haben keine Berechtigung zum Abspielen dieser Datei.",
"taskToPlaybackNoExists": "Die Datei kann nicht abgespielt werden, da sie bereits transkribiert wurde oder nicht existiert.",
"taskToPlaybackNoExists": "Die Datei kann nicht abgespielt werden, weil sie bereits transkribiert wurde, nicht existiert oder Sie eine andere Datei in Bearbeitung haben. Da Sie nicht mehr als eine Datei gleichzeitig in Bearbeitung haben können, ändern Sie die andere Datei in „Ausstehend“ und versuchen Sie es erneut.",
"taskNotEditable": "Der Transkriptionist kann nicht geändert werden, da die Transkription bereits ausgeführt wird oder die Datei nicht vorhanden ist. Bitte aktualisieren Sie den Bildschirm und prüfen Sie den aktuellen Status.",
"backupFailedError": "Der Prozess „Dateisicherung“ ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal. Wenn der Fehler weiterhin besteht, wenden Sie sich an Ihren Systemadministrator.",
"cancelFailedError": "Die Diktate konnten nicht gelöscht werden. Bitte aktualisieren Sie Ihren Bildschirm und versuchen Sie es erneut.",

View File

@ -54,7 +54,7 @@
},
"text": {
"maintenanceNotificationTitle": "Notice of scheduled maintenance",
"maintenanceNotification": "Due to system maintenance, ODMS Cloud will be unavailable for approximately one hour starting from June 12th, 6:00AM UTC time. We apologize for any inconvenience caused during the maintenance."
"maintenanceNotification": "Due to system maintenance, ODMS Cloud will be unavailable for approximately one hour starting from June 17th, 6:00AM UTC time. We apologize for any inconvenience caused during the maintenance."
}
},
"signupPage": {
@ -138,8 +138,8 @@
"userDeletionLicenseActiveError": "Failed to delete the user. Please unassign the license from the user.",
"typistDeletionRoutingRuleError": "Failed to delete the user. This user is registered as a Transcriptionist that is included in the routing rules. Please remove the Transcriptionist from the corresponding routing rule from the Workflow tab.",
"adminUserDeletionError": "Failed to delete the user. Please remove the user from the Primary or Secondary Administrator from the Account tab.",
"typistUserDeletionTranscriptionTaskError": "Failed to delete the user. There is a task assigned to this Transcriptionist. Please change the person in charge of the task to another Transcriptionist from the Dictation tab.",
"authorUserDeletionTranscriptionTaskError": "Failed to delete the user. There are tasks created by this Author that are incomplete. Please delete or mark the tasks as finished for tasks created by this Author.",
"typistUserDeletionTranscriptionTaskError": "Failed to delete the user. There is a task assigned to this Transcriptionist. Please change the person in charge of the task to another Transcriptionist from the Dictation tab if there are unfinished tasks. In case the status of dictations is marked as Finished or Backup, please delete the task.",
"authorUserDeletionTranscriptionTaskError": "Failed to delete the user. There are tasks created by this Author that are incomplete. Please mark the tasks as finished and then delete them before deleting the Author.",
"typistUserDeletionTranscriptionistGroupError": "Failed to delete the user. Please remove this Transcriptionist from the Transcriptionist Group from the Workflow tab.",
"authorDeletionRoutingRuleError": "Failed to delete the user. Please remove this Author from the routing rules from the Workflow tab.",
"importSuccess": "We have received your bulk user registration request. Please check your email as you will receive an email once the registration process is complete.",
@ -253,7 +253,7 @@
"dictationPage": {
"message": {
"noPlaybackAuthorization": "You do not have permission to playback this file.",
"taskToPlaybackNoExists": "The file cannot be played because it has already been transcribed or does not exist.",
"taskToPlaybackNoExists": "The file cannot be played because it has already been transcribed, does not exist, or you have another file that is in progress. Since you cannot have more than one file in progress at a time, please change the other file to Pending and try again.",
"taskNotEditable": "The transcriptionist cannot be changed because the transcription is already in progress or the file does not exist. Please refresh the screen and check the latest status.",
"backupFailedError": "The \"File Backup\" process has failed. Please try again later. If the error continues, contact your system administrator.",
"cancelFailedError": "Failed to delete the dictations. Please refresh your screen and try again.",

View File

@ -54,7 +54,7 @@
},
"text": {
"maintenanceNotificationTitle": "Aviso de mantenimiento programado",
"maintenanceNotification": "Debido al mantenimiento del sistema, ODMS Cloud no estará disponible durante aproximadamente una hora a partir del 12 de junio a las 6:00 am, hora UTC. Pedimos disculpas por cualquier inconveniente causado durante el mantenimiento."
"maintenanceNotification": "Debido al mantenimiento del sistema, ODMS Cloud no estará disponible durante aproximadamente una hora a partir del 17 de junio a las 6:00 am, hora UTC. Pedimos disculpas por cualquier inconveniente causado durante el mantenimiento."
}
},
"signupPage": {
@ -138,8 +138,8 @@
"userDeletionLicenseActiveError": "No se pudo eliminar el usuario. Desasignar la licencia al usuario.",
"typistDeletionRoutingRuleError": "No se pudo eliminar el usuario. Este usuario está registrado como Transcriptor que está incluido en las reglas de enrutamiento. Elimine al transcriptor de la regla de enrutamiento correspondiente en la pestaña Flujo de trabajo.",
"adminUserDeletionError": "No se pudo eliminar el usuario. Elimine el usuario del administrador principal o secundario desde la pestaña Cuenta.",
"typistUserDeletionTranscriptionTaskError": "No se pudo eliminar el usuario. Hay una tarea asignada a este transcriptor. Cambie la persona a cargo de la tarea a otro Transcriptor desde la pestaña Dictado.",
"authorUserDeletionTranscriptionTaskError": "No se pudo eliminar el usuario. Hay tareas creadas por este Autor que están incompletas. Elimine o marque las tareas como finalizadas para las tareas creadas por este autor.",
"typistUserDeletionTranscriptionTaskError": "No se pudo eliminar el usuario. Hay una tarea asignada a este transcriptor. Cambie la persona a cargo de la tarea a otro Transcriptor desde la pestaña Dictado si hay tareas pendientes. En caso de que el estado de los dictados esté marcado como Finalizado o Copia de seguridad, elimine la tarea.",
"authorUserDeletionTranscriptionTaskError": "No se pudo eliminar el usuario. Hay tareas creadas por este autor que están incompletas. Marque las tareas como finalizadas y luego elimínelas antes de eliminar al autor.",
"typistUserDeletionTranscriptionistGroupError": "No se pudo eliminar el usuario. Elimine a este transcriptor del grupo de transcriptores de la pestaña Flujo de trabajo.",
"authorDeletionRoutingRuleError": "No se pudo eliminar el usuario. Elimine a este autor de las reglas de enrutamiento de la pestaña Flujo de trabajo.",
"importSuccess": "Hemos recibido su solicitud de registro de usuario masivo. Por favor revise su correo electrónico ya que recibirá un correo electrónico una vez que se complete el proceso de registro.",
@ -253,7 +253,7 @@
"dictationPage": {
"message": {
"noPlaybackAuthorization": "No tienes permiso para reproducir este archivo.",
"taskToPlaybackNoExists": "El archivo no se puede reproducir porque ya ha sido transcrito o no existe.",
"taskToPlaybackNoExists": "El archivo no se puede reproducir porque ya se ha transcrito, no existe o hay otro archivo en progreso. Dado que no puede tener más de un archivo en progreso a la vez, cambie el otro archivo a Pendiente e inténtelo nuevamente.",
"taskNotEditable": "No se puede cambiar el transcriptor porque la transcripción ya está en curso o el archivo no existe. Actualice la pantalla y verifique el estado más reciente.",
"backupFailedError": "El proceso de \"Copia de seguridad de archivos\" ha fallado. Por favor, inténtelo de nuevo más tarde. Si el error continúa, comuníquese con el administrador del sistema.",
"cancelFailedError": "No se pudieron eliminar los dictados. Actualice su pantalla e inténtelo nuevamente.",

View File

@ -54,7 +54,7 @@
},
"text": {
"maintenanceNotificationTitle": "Avis de maintenance programmée",
"maintenanceNotification": "En raison de la maintenance du système, ODMS Cloud sera indisponible pendant environ une heure à partir du 12 juin à 6h00, heure UTC. Nous nous excusons pour tout inconvénient causé lors de la maintenance."
"maintenanceNotification": "En raison de la maintenance du système, ODMS Cloud sera indisponible pendant environ une heure à partir du 17 juin à 6h00, heure UTC. Nous nous excusons pour tout inconvénient causé lors de la maintenance."
}
},
"signupPage": {
@ -138,8 +138,8 @@
"userDeletionLicenseActiveError": "Échec de la suppression de l'utilisateur. Veuillez annuler l'attribution de la licence à l'utilisateur.",
"typistDeletionRoutingRuleError": "Échec de la suppression de l'utilisateur. Cet utilisateur est enregistré en tant que Transcripteur qui est inclus dans les règles de routage. Supprimez le transcripteur de la règle de routage correspondante dans l'onglet Workflow. Routing-Regel auf der Registerkarte „Workflow“.",
"adminUserDeletionError": "Échec de la suppression de l'utilisateur. Veuillez supprimer l'utilisateur de l'administrateur principal ou secondaire de l'onglet Compte.",
"typistUserDeletionTranscriptionTaskError": "Échec de la suppression de l'utilisateur. Une tâche est assignée à ce transcripteur. Veuillez remplacer la personne en charge de la tâche par un autre transcripteur depuis l'onglet Dictée.",
"authorUserDeletionTranscriptionTaskError": "Échec de la suppression de l'utilisateur. Certaines tâches créées par cet auteur sont incomplètes. Veuillez supprimer ou marquer les tâches comme terminées pour les tâches créées par cet auteur.",
"typistUserDeletionTranscriptionTaskError": "Échec de la suppression de l'utilisateur. Une tâche est assignée à ce transcripteur. Veuillez remplacer la personne en charge de la tâche par un autre transcripteur depuis l'onglet Dictée s'il y a des tâches inachevées. Si le statut des dictées est marqué comme Terminé ou Sauvegarde, veuillez supprimer la tâche.",
"authorUserDeletionTranscriptionTaskError": "Échec de la suppression de l'utilisateur. Certaines tâches créées par cet auteur sont incomplètes. Veuillez marquer les tâches comme terminées, puis supprimez-les avant de supprimer l'auteur.",
"typistUserDeletionTranscriptionistGroupError": "Échec de la suppression de l'utilisateur. Veuillez supprimer ce transcripteur du groupe transcripteur de l'onglet Workflow.",
"authorDeletionRoutingRuleError": "Échec de la suppression de l'utilisateur. Supprimez cet auteur des règles de routage dans l'onglet Workflow.",
"importSuccess": "Nous avons reçu votre demande d'enregistrement groupé d'utilisateur. Veuillez vérifier votre courrier électronique car vous recevrez un e-mail une fois le processus d'inscription terminé.",
@ -253,7 +253,7 @@
"dictationPage": {
"message": {
"noPlaybackAuthorization": "Vous n'êtes pas autorisé à lire ce fichier.",
"taskToPlaybackNoExists": "Le fichier ne peut pas être lu car il a déjà été transcrit ou n'existe pas.",
"taskToPlaybackNoExists": "Le fichier ne peut pas être lu car il a déjà été transcrit, n'existe pas ou vous avez un autre fichier en cours. Étant donné que vous ne pouvez pas avoir plus d'un fichier en cours à la fois, veuillez modifier l'autre fichier en En attente et réessayer.",
"taskNotEditable": "Le transcripteur ne peut pas être changé car la transcription est déjà en cours ou le fichier n'existe pas. Veuillez actualiser l'écran et vérifier le dernier statut.",
"backupFailedError": "Le processus de « Sauvegarde de fichier » a échoué. Veuillez réessayer plus tard. Si l'erreur persiste, contactez votre administrateur système.",
"cancelFailedError": "Échec de la suppression des dictées. Veuillez actualiser votre écran et réessayer.",

View File

@ -3287,7 +3287,7 @@ describe('UsersService.deleteUser', () => {
source = null;
});
it('ユーザーを削除できる', async () => {
it('ユーザーを削除できる(Author)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
@ -3298,6 +3298,7 @@ describe('UsersService.deleteUser', () => {
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR_1',
});
const service = module.get<UsersService>(UsersService);
@ -3352,6 +3353,71 @@ describe('UsersService.deleteUser', () => {
expect(userArchive[0].external_id).toBe(external_id);
}
});
it('ユーザーを削除できる(Typist)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.TYPIST,
});
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user1, now);
// ユーザーが削除されたことを確認
{
const user = await getUser(source, user1);
expect(user).toBeNull();
// ユーザアーカイブが作成されたことを確認
const userArchive = await getUserArchive(source);
expect(userArchive[0].external_id).toBe(external_id);
}
},600000);
it('存在しないユーザは削除できない', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
@ -3486,7 +3552,7 @@ describe('UsersService.deleteUser', () => {
}
}
});
it('WorkFlowに割り当てられているユーザ(Auhtor)は削除できない', async () => {
it('WorkFlowに割り当てられているユーザ(Author)は削除できない', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
@ -3749,7 +3815,292 @@ describe('UsersService.deleteUser', () => {
}
}
});
it('削除対象ユーザーが有効なタスクをまだ持っている場合、削除できない', async () => {
it('削除対象ユーザー(Author)のAuthorIDが設定されているタスクがある場合、削除できない(statusがInprogress)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// タスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.IN_PROGRESS,
user2,
'AUTHOR',
admin.id, // 作成者がadmin
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user1, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014006'));
} else {
fail();
}
}
});
it('削除対象ユーザー(Author)のAuthorIDが設定されているタスクがある場合、削除できない(statusがFinished)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// タスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.FINISHED,
user2,
'AUTHOR',
admin.id, // 作成者がadmin
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user1, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014006'));
} else {
fail();
}
}
});
it('削除対象ユーザー(Author)のAuthorIDが設定されているタスクがある場合、削除できない(statusがBackup)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// タスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.BACKUP,
user2,
'AUTHOR',
admin.id, // 作成者がadmin
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user1, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014006'));
} else {
fail();
}
}
});
it('削除対象ユーザー(Author)が有効なタスクをまだ持っている場合、削除できない(statusがInprogress)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
@ -3844,7 +4195,197 @@ describe('UsersService.deleteUser', () => {
}
}
});
it('削除対象ユーザータスクのチェックアウト権限まだ持っている場合、削除できない', async () => {
it('削除対象ユーザー(Author)が有効なタスクをまだ持っている場合、削除できない(statusがFinished)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// タスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.FINISHED,
user2,
'AUTHOR',
user1,
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user1, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014006'));
} else {
fail();
}
}
});
it('削除対象ユーザー(Author)が有効なタスクをまだ持っている場合、削除できない(statusがBackup)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// タスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.BACKUP,
user2,
'AUTHOR',
user1,
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user1, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014006'));
} else {
fail();
}
}
});
it('削除対象ユーザー(Typist)タスクのチェックアウト権限まだ持っている場合、削除できない', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
@ -3931,6 +4472,199 @@ describe('UsersService.deleteUser', () => {
}
}
});
it('削除対象ユーザー(Typist)が文字起こし担当のタスクがまだ持っている場合、削除できない(statusがFinished)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// user2が文字起こし担当のタスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.FINISHED,
user2,
'AUTHOR',
user1,
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user2, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014009'));
} else {
fail();
}
}
},600000);
it('削除対象ユーザー(Typist)が文字起こし担当のタスクがまだ持っている場合、削除できない(statusがBackup)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account, admin } = await makeTestAccount(source, {
tier: 5,
});
const { id: user1, external_id } = await makeTestUser(source, {
account_id: account.id,
role: USER_ROLES.AUTHOR,
author_id: 'AUTHOR',
});
const { id: user2, external_id: external_id2 } = await makeTestUser(
source,
{
account_id: account.id,
role: USER_ROLES.TYPIST,
},
);
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
// user2が文字起こし担当のタスクを作成
await createTask(
source,
account.id,
'task-url',
'filename',
TASK_STATUS.BACKUP,
user2,
'AUTHOR',
user1,
);
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
{
id: external_id,
displayName: 'user1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user1@example.com',
},
],
},
{
id: external_id2,
displayName: 'user2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'user2@example.com',
},
],
},
];
},
getUser: async () => {
return {
id: admin.external_id,
displayName: 'admin',
};
},
});
overrideSendgridService(service, {});
try {
// 現在日付を作成
const now = new Date();
await service.deleteUser(context, user2, now);
fail();
} catch (e) {
if (e instanceof HttpException) {
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
expect(e.getResponse()).toEqual(makeErrorResponse('E014009'));
} else {
fail();
}
}
});
it('削除対象ユーザーが有効なライセンスをまだ持っている場合、削除できない', async () => {
if (!source) fail();
const module = await makeTestingModule(source);

View File

@ -36,10 +36,10 @@ import {
AuthorIdAlreadyExistsError,
EmailAlreadyVerifiedError,
EncryptionPasswordNeedError,
ExistsCheckoutPermissionDeleteFailedError,
ExistsGroupMemberDeleteFailedError,
ExistsTaskDeleteFailedError,
ExistsValidCheckoutDeleteFailedError,
ExistsValidLicenseDeleteFailedError,
ExistsValidTaskDeleteFailedError,
InvalidRoleChangeError,
UpdateTermsVersionNotSetError,
UserNotFoundError,
@ -1446,7 +1446,7 @@ export class UsersService {
makeErrorResponse('E014005'),
HttpStatus.BAD_REQUEST,
);
case ExistsValidTaskDeleteFailedError:
case ExistsTaskDeleteFailedError:
throw new HttpException(
makeErrorResponse('E014006'),
HttpStatus.BAD_REQUEST,
@ -1456,7 +1456,7 @@ export class UsersService {
makeErrorResponse('E014007'),
HttpStatus.BAD_REQUEST,
);
case ExistsCheckoutPermissionDeleteFailedError:
case ExistsValidCheckoutDeleteFailedError:
throw new HttpException(
makeErrorResponse('E014009'),
HttpStatus.BAD_REQUEST,

View File

@ -87,19 +87,19 @@ export class ExistsGroupMemberDeleteFailedError extends Error {
}
}
// 削除対象ユーザー(Author)に未完了のタスクがまだ残っている事が原因の削除失敗エラー
export class ExistsValidTaskDeleteFailedError extends Error {
// 削除対象ユーザー(Author)が作成したタスクがまだ残っている事が原因の削除失敗エラー
export class ExistsTaskDeleteFailedError extends Error {
constructor(message: string) {
super(message);
this.name = 'ExistsValidTaskDeleteFailedError';
this.name = 'ExistsTaskDeleteFailedError';
}
}
// 削除対象ユーザーがチェックアウト権限を持っている事が原因の削除失敗エラー
export class ExistsCheckoutPermissionDeleteFailedError extends Error {
// 削除対象ユーザーがチェックアウト権限を持っているor文字起こし担当者としてアサインされている事が原因の削除失敗エラー
export class ExistsValidCheckoutDeleteFailedError extends Error {
constructor(message: string) {
super(message);
this.name = 'ExistsCheckoutPermissionDeleteFailedError';
this.name = 'ExistsValidCheckoutDeleteFailedError';
}
}

View File

@ -3,7 +3,6 @@ import { User, UserArchive, newUser } from './entity/user.entity';
import {
DataSource,
FindOptionsWhere,
In,
IsNull,
Not,
UpdateResult,
@ -26,9 +25,9 @@ import {
AssignedWorkflowWithTypistDeleteFailedError,
AssignedWorkflowWithAuthorDeleteFailedError,
AdminDeleteFailedError,
ExistsValidTaskDeleteFailedError,
ExistsCheckoutPermissionDeleteFailedError,
ExistsValidLicenseDeleteFailedError,
ExistsValidCheckoutDeleteFailedError,
ExistsTaskDeleteFailedError,
} from './errors/types';
import {
LICENSE_ALLOCATED_STATUS,
@ -742,37 +741,51 @@ export class UsersRepositoryService {
// 削除対象ユーザーがAuthorであった時、
if (target.role === USER_ROLES.AUTHOR) {
const taskRepo = entityManager.getRepository(Task);
// 自分が所有者のタスクの一覧を取得する
const tasks = await taskRepo.find({
// 自分が所有者のタスクが存在するか確認
const ownerTasksExist = await taskRepo.exist({
relations: {
file: true,
},
where: {
account_id: target.account_id,
status: Not(TASK_STATUS.BACKUP), // 数が膨大になりうる&有効なタスクへの状態遷移ができないBACKUPは除いて取得
file: {
owner_user_id: target.id,
where: [
{
account_id: target.account_id,
file: {
owner_user_id: target.id,
},
},
},
{
account_id: target.account_id,
file: {
author_id: target.author_id ?? undefined,
},
},
],
lock: { mode: 'pessimistic_write' }, // lockする事で状態遷移の競合をブロックし、新規追加以外で所有タスク群の状態変更を防ぐ
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
// 未完了タスクが残っていたら削除できない
const enableStatus: string[] = [
TASK_STATUS.UPLOADED,
TASK_STATUS.IN_PROGRESS,
TASK_STATUS.PENDING,
];
// 未完了タスクを列挙
const enableTasks = tasks.filter((task) =>
enableStatus.includes(task.status),
);
if (enableTasks.length > 0) {
throw new ExistsValidTaskDeleteFailedError('User has valid tasks.');
// 削除対象のユーザーが所有者のタスクが存在する場合は削除できない
if (ownerTasksExist) {
throw new ExistsTaskDeleteFailedError('User has tasks.');
}
}
// 削除対象ユーザーがTypistであった時、
if (target.role === USER_ROLES.TYPIST) {
const taskRepo = entityManager.getRepository(Task);
// 削除対象ユーザーが文字起こし担当のタスクが存在するか確認
const transcriptionTasksExist = await taskRepo.exist({
where: {
account_id: target.account_id,
typist_user_id: target.id,
},
lock: { mode: 'pessimistic_write' }, // lockする事で状態遷移の競合をブロックし、新規追加以外で所有タスク群の状態変更を防ぐ
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});
// 削除対象のユーザーが文字起こし担当のタスクが存在する場合は削除できない
if (transcriptionTasksExist) {
throw new ExistsValidCheckoutDeleteFailedError('User has tasks.');
}
const checkoutPermissionRepo =
entityManager.getRepository(CheckoutPermission);
const permissions = await checkoutPermissionRepo.find({
@ -785,7 +798,7 @@ export class UsersRepositoryService {
// タスクのチェックアウト権限が残っていたら削除できない
if (permissions.length !== 0) {
throw new ExistsCheckoutPermissionDeleteFailedError(
throw new ExistsValidCheckoutDeleteFailedError(
'User has checkout permissions.',
);
}