Browse Source

feat: user workspace stats api added (#4467) (HSB-495)

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
Mir Arif Hasan 4 months ago
parent
commit
161e598809

+ 27 - 3
packages/hoppscotch-backend/src/infra-token/infra-token.controller.ts

@@ -28,6 +28,7 @@ import {
   CreateUserInvitationRequest,
   CreateUserInvitationRequest,
   CreateUserInvitationResponse,
   CreateUserInvitationResponse,
   DeleteUserResponse,
   DeleteUserResponse,
+  GetUserWorkspacesResponse,
 } from './request-response.dto';
 } from './request-response.dto';
 import * as E from 'fp-ts/Either';
 import * as E from 'fp-ts/Either';
 import * as O from 'fp-ts/Option';
 import * as O from 'fp-ts/Option';
@@ -104,9 +105,8 @@ export class InfraTokensController {
   async getPendingUserInvitation(
   async getPendingUserInvitation(
     @Query() paginationQuery: OffsetPaginationArgs,
     @Query() paginationQuery: OffsetPaginationArgs,
   ) {
   ) {
-    const pendingInvitedUsers = await this.adminService.fetchInvitedUsers(
-      paginationQuery,
-    );
+    const pendingInvitedUsers =
+      await this.adminService.fetchInvitedUsers(paginationQuery);
 
 
     return plainToInstance(GetUserInvitationResponse, pendingInvitedUsers, {
     return plainToInstance(GetUserInvitationResponse, pendingInvitedUsers, {
       excludeExtraneousValues: true,
       excludeExtraneousValues: true,
@@ -275,4 +275,28 @@ export class InfraTokensController {
       },
       },
     );
     );
   }
   }
+
+  @Get('users/:uid/workspaces')
+  @ApiOkResponse({
+    description: 'Get user workspaces',
+    type: [GetUserWorkspacesResponse],
+  })
+  @ApiNotFoundResponse({ type: ExceptionResponse })
+  async getUserWorkspaces(@Param('uid') uid: string) {
+    const userWorkspaces = await this.userService.fetchUserWorkspaces(uid);
+
+    if (E.isLeft(userWorkspaces)) {
+      const statusCode =
+        userWorkspaces.left === USER_NOT_FOUND
+          ? HttpStatus.NOT_FOUND
+          : HttpStatus.BAD_REQUEST;
+
+      throwHTTPErr({ message: userWorkspaces.left, statusCode });
+    }
+
+    return plainToInstance(GetUserWorkspacesResponse, userWorkspaces.right, {
+      excludeExtraneousValues: true,
+      enableImplicitConversion: true,
+    });
+  }
 }
 }

+ 32 - 0
packages/hoppscotch-backend/src/infra-token/request-response.dto.ts

@@ -10,6 +10,7 @@ import {
   IsString,
   IsString,
   MinLength,
   MinLength,
 } from 'class-validator';
 } from 'class-validator';
+import { TeamMemberRole } from 'src/team/team.model';
 import { OffsetPaginationArgs } from 'src/types/input-types.args';
 import { OffsetPaginationArgs } from 'src/types/input-types.args';
 
 
 // POST v1/infra/user-invitations
 // POST v1/infra/user-invitations
@@ -128,3 +129,34 @@ export class DeleteUserResponse {
   @Expose()
   @Expose()
   message: string;
   message: string;
 }
 }
+
+// GET v1/infra/users/:uid/workspaces
+export class GetUserWorkspacesResponse {
+  @ApiProperty()
+  @Expose()
+  id: string;
+
+  @ApiProperty()
+  @Expose()
+  name: string;
+
+  @ApiProperty({ enum: TeamMemberRole })
+  @Expose()
+  role: string;
+
+  @ApiProperty()
+  @Expose()
+  owner_count: number;
+
+  @ApiProperty()
+  @Expose()
+  editor_count: number;
+
+  @ApiProperty()
+  @Expose()
+  viewer_count: number;
+
+  @ApiProperty()
+  @Expose()
+  member_count: number;
+}

+ 50 - 0
packages/hoppscotch-backend/src/user/user.service.ts

@@ -20,6 +20,8 @@ import { encrypt, stringToJson, taskEitherValidateArraySeq } from 'src/utils';
 import { UserDataHandler } from './user.data.handler';
 import { UserDataHandler } from './user.data.handler';
 import { User as DbUser } from '@prisma/client';
 import { User as DbUser } from '@prisma/client';
 import { OffsetPaginationArgs } from 'src/types/input-types.args';
 import { OffsetPaginationArgs } from 'src/types/input-types.args';
+import { GetUserWorkspacesResponse } from 'src/infra-token/request-response.dto';
+import { TeamMemberRole } from 'src/team/team.model';
 
 
 @Injectable()
 @Injectable()
 export class UserService {
 export class UserService {
@@ -598,4 +600,52 @@ export class UserService {
 
 
     return E.right(true);
     return E.right(true);
   }
   }
+
+  async fetchUserWorkspaces(userUid: string) {
+    const user = await this.prisma.user.findUnique({ where: { uid: userUid } });
+    if (!user) return E.left(USER_NOT_FOUND);
+
+    const team = await this.prisma.team.findMany({
+      where: {
+        members: {
+          some: {
+            userUid,
+          },
+        },
+      },
+      include: {
+        members: {
+          select: {
+            userUid: true,
+            role: true,
+          },
+        },
+      },
+    });
+
+    const workspaces: GetUserWorkspacesResponse[] = [];
+    team.forEach((t) => {
+      const ownerCount = t.members.filter(
+        (m) => m.role === TeamMemberRole.OWNER,
+      ).length;
+      const editorCount = t.members.filter(
+        (m) => m.role === TeamMemberRole.EDITOR,
+      ).length;
+      const viewerCount = t.members.filter(
+        (m) => m.role === TeamMemberRole.VIEWER,
+      ).length;
+      const memberCount = t.members.length;
+
+      workspaces.push({
+        id: t.id,
+        name: t.name,
+        role: t.members.find((m) => m.userUid === userUid)?.role,
+        owner_count: ownerCount,
+        editor_count: editorCount,
+        viewer_count: viewerCount,
+        member_count: memberCount,
+      });
+    });
+    return E.right(workspaces);
+  }
 }
 }