From 062b0fdda07ffa162b1017ca5135c5d8ea8b9b51 Mon Sep 17 00:00:00 2001 From: Balu Babu Date: Thu, 26 Sep 2024 22:54:44 +0530 Subject: [PATCH] chore: addition of new GQL subscription for `UserCollection` duplication (#4358) Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> --- .../src/pubsub/topicsDefs.ts | 2 + .../user-collection.resolver.ts | 11 +++ .../user-collection.service.ts | 80 ++++++++++++++++++- .../user-collection/user-collections.model.ts | 51 ++++++++++++ 4 files changed, 141 insertions(+), 3 deletions(-) diff --git a/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts b/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts index 49506c67e..b36171221 100644 --- a/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts +++ b/packages/hoppscotch-backend/src/pubsub/topicsDefs.ts @@ -23,6 +23,7 @@ import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; import { InvitedUser } from '../admin/invited-user.model'; import { UserCollection, + UserCollectionDuplicatedData, UserCollectionRemovedData, UserCollectionReorderData, } from 'src/user-collection/user-collections.model'; @@ -48,6 +49,7 @@ export type TopicDef = { [ topic: `user_coll/${string}/${'created' | 'updated' | 'moved'}` ]: UserCollection; + [topic: `user_coll/${string}/${'duplicated'}`]: UserCollectionDuplicatedData; [topic: `user_coll/${string}/${'deleted'}`]: UserCollectionRemovedData; [topic: `user_coll/${string}/${'order_updated'}`]: UserCollectionReorderData; [topic: `team/${string}/member_removed`]: string; diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts index 8bc2f22e5..b5f3d2820 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.resolver.ts @@ -16,6 +16,7 @@ import { AuthUser } from 'src/types/AuthUser'; import { UserCollectionService } from './user-collection.service'; import { UserCollection, + UserCollectionDuplicatedData, UserCollectionExportJSONData, UserCollectionRemovedData, UserCollectionReorderData, @@ -470,4 +471,14 @@ export class UserCollectionResolver { userCollectionOrderUpdated(@GqlUser() user: AuthUser) { return this.pubSub.asyncIterator(`user_coll/${user.uid}/order_updated`); } + + @Subscription(() => UserCollectionDuplicatedData, { + description: 'Listen to when a User Collection has been duplicated', + resolve: (value) => value, + }) + @SkipThrottle() + @UseGuards(GqlAuthGuard) + userCollectionDuplicated(@GqlUser() user: AuthUser) { + return this.pubSub.asyncIterator(`user_coll/${user.uid}/duplicated`); + } } diff --git a/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts b/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts index 69358854c..0eeaee52f 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collection.service.ts @@ -23,6 +23,7 @@ import { Prisma, UserCollection, ReqType as DBReqType } from '@prisma/client'; import { UserCollection as UserCollectionModel, UserCollectionExportJSONData, + UserCollectionDuplicatedData, } from './user-collections.model'; import { ReqType } from 'src/types/RequestTypes'; import { @@ -1035,6 +1036,7 @@ export class UserCollectionService { userID: string, destCollectionID: string | null, reqType: DBReqType, + isCollectionDuplication = false, ) { // Check to see if jsonString is valid const collectionsList = stringToJson(jsonString); @@ -1087,9 +1089,24 @@ export class UserCollectionService { ), ); - userCollections.forEach((collection) => - this.pubsub.publish(`user_coll/${userID}/created`, this.cast(collection)), - ); + if (isCollectionDuplication) { + const collectionData = await this.fetchCollectionData( + userCollections[0].id, + ); + if (E.isRight(collectionData)) { + this.pubsub.publish( + `user_coll/${userID}/duplicated`, + collectionData.right, + ); + } + } else { + userCollections.forEach((collection) => + this.pubsub.publish( + `user_coll/${userID}/created`, + this.cast(collection), + ), + ); + } return E.right(true); } @@ -1182,9 +1199,66 @@ export class UserCollectionService { userID, collection.right.parentID, reqType, + true, ); if (E.isLeft(result)) return E.left(result.left as string); return E.right(true); } + + /** + * Generates a JSON containing all the contents of a collection + * + * @param collection Collection whose details we want to fetch + * @returns A JSON string containing all the contents of a collection + */ + private async fetchCollectionData( + collectionID: string, + ): Promise | E.Right> { + const collection = await this.getUserCollection(collectionID); + if (E.isLeft(collection)) return E.left(collection.left); + + const { id, title, data, type, parentID, userUid } = collection.right; + const orderIndex = 'asc'; + + const [childCollections, requests] = await Promise.all([ + this.prisma.userCollection.findMany({ + where: { parentID: id }, + orderBy: { orderIndex }, + }), + this.prisma.userRequest.findMany({ + where: { collectionID: id }, + orderBy: { orderIndex }, + }), + ]); + + const childCollectionDataList = await Promise.all( + childCollections.map(({ id }) => this.fetchCollectionData(id)), + ); + + const failedChildData = childCollectionDataList.find(E.isLeft); + if (failedChildData) return E.left(failedChildData.left); + + const childCollectionsJSONStr = JSON.stringify( + (childCollectionDataList as E.Right[]).map( + (childCollection) => childCollection.right, + ), + ); + + const transformedRequests = requests.map((requestObj) => ({ + ...requestObj, + request: JSON.stringify(requestObj.request), + })); + + return E.right({ + id, + title, + data, + type, + parentID, + userID: userUid, + childCollections: childCollectionsJSONStr, + requests: transformedRequests, + }); + } } diff --git a/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts b/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts index 89c26861c..d0f7717fc 100644 --- a/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts +++ b/packages/hoppscotch-backend/src/user-collection/user-collections.model.ts @@ -1,5 +1,7 @@ import { ObjectType, Field, ID, registerEnumType } from '@nestjs/graphql'; +import { User } from '@prisma/client'; import { ReqType } from 'src/types/RequestTypes'; +import { UserRequest } from 'src/user-request/user-request.model'; @ObjectType() export class UserCollection { @@ -73,3 +75,52 @@ export class UserCollectionExportJSONData { }) collectionType: ReqType; } + +@ObjectType() +export class UserCollectionDuplicatedData { + @Field(() => ID, { + description: 'ID of the user collection', + }) + id: string; + + @Field({ + description: 'Displayed title of the user collection', + }) + title: string; + + @Field({ + description: 'JSON string representing the collection data', + nullable: true, + }) + data: string; + + @Field(() => ReqType, { + description: 'Type of the user collection', + }) + type: ReqType; + + @Field({ + description: 'Parent ID of the duplicated User Collection', + nullable: true, + }) + parentID: string | null; + + @Field({ + description: 'User ID of the duplicated User Collection', + }) + userID: string; + + @Field({ + description: 'Child collections of the duplicated User Collection', + }) + childCollections: string; + + @Field(() => [UserRequest], { + description: 'Requests of the duplicated User Collection', + }) + requests: UserRequest[]; +} + +registerEnumType(ReqType, { + name: 'CollType', +});