From b53cbb093cbb4f0950843cdfa662a9565d4f9330 Mon Sep 17 00:00:00 2001 From: mirarifhasan Date: Mon, 29 Jan 2024 21:20:56 +0600 Subject: [PATCH] feat: removeUsersByAdmin mutation added --- .../src/admin/admin.resolver.ts | 29 +++++++++++--- .../src/admin/admin.service.ts | 39 +++++++++++++++++++ .../hoppscotch-backend/src/user/user.model.ts | 19 +++++++++ .../src/user/user.service.ts | 15 +++++++ 4 files changed, 96 insertions(+), 6 deletions(-) diff --git a/packages/hoppscotch-backend/src/admin/admin.resolver.ts b/packages/hoppscotch-backend/src/admin/admin.resolver.ts index 409b0b580..2b317e289 100644 --- a/packages/hoppscotch-backend/src/admin/admin.resolver.ts +++ b/packages/hoppscotch-backend/src/admin/admin.resolver.ts @@ -27,9 +27,7 @@ import { } from './input-types.args'; import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { SkipThrottle } from '@nestjs/throttler'; -import { User } from 'src/user/user.model'; -import { PaginationArgs } from 'src/types/input-types.args'; -import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; +import { UserDeleteData } from 'src/user/user.model'; @UseGuards(GqlThrottlerGuard) @Resolver(() => Admin) @@ -105,9 +103,28 @@ export class AdminResolver { }) userUID: string, ): Promise { - const invitedUser = await this.adminService.removeUserAccount(userUID); - if (E.isLeft(invitedUser)) throwErr(invitedUser.left); - return invitedUser.right; + const removedUser = await this.adminService.removeUserAccount(userUID); + if (E.isLeft(removedUser)) throwErr(removedUser.left); + return removedUser.right; + } + + @Mutation(() => [UserDeleteData], { + description: 'Delete user accounts from infra', + }) + @UseGuards(GqlAuthGuard, GqlAdminGuard) + async removeUsersByAdmin( + @Args({ + name: 'userUIDs', + description: 'users UID', + type: () => [ID], + }) + userUIDs: string[], + ): Promise { + const deletionResults = await this.adminService.removeUserAccounts( + userUIDs, + ); + if (E.isLeft(deletionResults)) throwErr(deletionResults.left); + return deletionResults.right; } @Mutation(() => Boolean, { diff --git a/packages/hoppscotch-backend/src/admin/admin.service.ts b/packages/hoppscotch-backend/src/admin/admin.service.ts index 6df2a4e82..11126618c 100644 --- a/packages/hoppscotch-backend/src/admin/admin.service.ts +++ b/packages/hoppscotch-backend/src/admin/admin.service.ts @@ -28,6 +28,7 @@ import { TeamMemberRole } from '../team/team.model'; import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ConfigService } from '@nestjs/config'; import { OffsetPaginationArgs } from 'src/types/input-types.args'; +import { UserDeleteData } from 'src/user/user.model'; @Injectable() export class AdminService { @@ -418,6 +419,44 @@ export class AdminService { return E.right(delUser.right); } + /** + * Remove user accounts by UIDs + * @param userUid User UIDs + * @returns an Either of boolean or error + */ + async removeUserAccounts(userUIDs: string[]) { + const users = await this.userService.findNonAdminUsersByIds(userUIDs); + if (users.length === 0) return E.left(USER_NOT_FOUND); + + const deletionPromises = users.map((user) => { + return this.userService + .deleteUserByUID(user)() + .then((res) => { + if (E.isLeft(res)) { + return { + userUID: user.uid, + success: false, + errorMessage: res.left, + } as UserDeleteData; + } + return { + userUID: user.uid, + success: true, + errorMessage: null, + } as UserDeleteData; + }); + }); + const promiseResult = await Promise.allSettled(deletionPromises); + + const userDeleteResult = promiseResult.map((result) => { + if (result.status === 'fulfilled') { + return result.value; + } + }); + + return E.right(userDeleteResult); + } + /** * Make a user an admin * @param userUid User UID diff --git a/packages/hoppscotch-backend/src/user/user.model.ts b/packages/hoppscotch-backend/src/user/user.model.ts index 89d73f49b..dcd35394a 100644 --- a/packages/hoppscotch-backend/src/user/user.model.ts +++ b/packages/hoppscotch-backend/src/user/user.model.ts @@ -56,3 +56,22 @@ export enum SessionType { registerEnumType(SessionType, { name: 'SessionType', }); + +@ObjectType() +export class UserDeleteData { + @Field(() => ID, { + description: 'UID of the user', + }) + userUID: string; + + @Field(() => Boolean, { + description: 'Flag to determine if user deletion was successful or not', + }) + success: Boolean; + + @Field({ + nullable: true, + description: 'Error message if user deletion was not successful', + }) + errorMessage: String; +} diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts index ff3b238d9..4ddf6d558 100644 --- a/packages/hoppscotch-backend/src/user/user.service.ts +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -89,6 +89,21 @@ export class UserService { } } + /** + * Find Non-Admin Users with given IDs + * @param userUIDs User IDs + * @returns Option of found Users + */ + async findNonAdminUsersByIds(userUIDs: string[]): Promise { + const users = await this.prisma.user.findMany({ + where: { + uid: { in: userUIDs }, + isAdmin: false, + }, + }); + return users; + } + /** * Update User with new generated hashed refresh token *