From b74c1abf6fbb491bef6c5dbaae8877199fe2345a Mon Sep 17 00:00:00 2001 From: Balu Babu Date: Wed, 25 Oct 2023 21:45:26 +0530 Subject: [PATCH] chore: completed mutation to create a SharedRequest --- .../hoppscotch-backend/prisma/schema.prisma | 2 +- packages/hoppscotch-backend/src/errors.ts | 14 ++++ .../shared-request/shared-request.resolver.ts | 33 +++++++- .../shared-request/shared-request.service.ts | 82 ++++++++++++++++++- 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/packages/hoppscotch-backend/prisma/schema.prisma b/packages/hoppscotch-backend/prisma/schema.prisma index 3a2bed07e..d2fa49f9c 100644 --- a/packages/hoppscotch-backend/prisma/schema.prisma +++ b/packages/hoppscotch-backend/prisma/schema.prisma @@ -77,7 +77,7 @@ model TeamRequest { // } model Shortcode { - id String @id + id String @id @unique request Json properties Json? // if properties is null, SharedRequest is an embed else shortcode creatorUid String? diff --git a/packages/hoppscotch-backend/src/errors.ts b/packages/hoppscotch-backend/src/errors.ts index 88b23e2af..9d3c0ad8a 100644 --- a/packages/hoppscotch-backend/src/errors.ts +++ b/packages/hoppscotch-backend/src/errors.ts @@ -627,3 +627,17 @@ export const MAILER_FROM_ADDRESS_UNDEFINED = * (SharedRequestService) */ export const SHARED_REQUEST_NOT_FOUND = 'shared_request/not_found' as const; + +/** + * SharedRequest invalid request JSON formal + * (SharedRequestService) + */ +export const SHARED_REQUEST_INVALID_REQUEST_JSON = + 'shared_request/request_invalid_format' as const; + +/** + * SharedRequest invalid properties JSON formal + * (SharedRequestService) + */ +export const SHARED_REQUEST_INVALID_PROPERTIES_JSON = + 'shared_request/properties_invalid_format' as const; diff --git a/packages/hoppscotch-backend/src/shared-request/shared-request.resolver.ts b/packages/hoppscotch-backend/src/shared-request/shared-request.resolver.ts index 08be4216f..4e25aa3ea 100644 --- a/packages/hoppscotch-backend/src/shared-request/shared-request.resolver.ts +++ b/packages/hoppscotch-backend/src/shared-request/shared-request.resolver.ts @@ -1,4 +1,4 @@ -import { Args, ID, Resolver, Query } from '@nestjs/graphql'; +import { Args, ID, Resolver, Query, Mutation } from '@nestjs/graphql'; import { SharedRequest } from './shared-requests.model'; import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { UseGuards } from '@nestjs/common'; @@ -8,6 +8,8 @@ import { PubSubService } from 'src/pubsub/pubsub.service'; import * as E from 'fp-ts/Either'; import { GqlAuthGuard } from 'src/guards/gql-auth.guard'; import { throwErr } from 'src/utils'; +import { GqlUser } from 'src/decorators/gql-user.decorator'; +import { AuthUser } from 'src/types/AuthUser'; @UseGuards(GqlThrottlerGuard) @Resolver(() => SharedRequest) @@ -36,4 +38,33 @@ export class SharedRequestResolver { if (E.isLeft(result)) throwErr(result.left); return result.right; } + + /* Mutations */ + @Mutation(() => SharedRequest, { + description: 'Create a shared-request for the given request.', + }) + @UseGuards(GqlAuthGuard) + async createSharedRequest( + @GqlUser() user: AuthUser, + @Args({ + name: 'request', + description: 'JSON string of the request object', + }) + request: string, + @Args({ + name: 'properties', + description: 'JSON string of the properties of the embed', + nullable: true, + }) + properties: string, + ) { + const result = await this.sharedRequestService.createSharedRequest( + request, + properties, + user, + ); + + if (E.isLeft(result)) throwErr(result.left); + return result.right; + } } diff --git a/packages/hoppscotch-backend/src/shared-request/shared-request.service.ts b/packages/hoppscotch-backend/src/shared-request/shared-request.service.ts index 40ba68c92..fe6802242 100644 --- a/packages/hoppscotch-backend/src/shared-request/shared-request.service.ts +++ b/packages/hoppscotch-backend/src/shared-request/shared-request.service.ts @@ -5,7 +5,17 @@ import { Shortcode as DBSharedRequest } from '@prisma/client'; import { UserService } from 'src/user/user.service'; import * as E from 'fp-ts/Either'; import { SharedRequest } from './shared-requests.model'; -import { SHARED_REQUEST_NOT_FOUND } from 'src/errors'; +import { + SHARED_REQUEST_INVALID_PROPERTIES_JSON, + SHARED_REQUEST_INVALID_REQUEST_JSON, + SHARED_REQUEST_NOT_FOUND, +} from 'src/errors'; +import { stringToJson } from 'src/utils'; +import { AuthUser } from 'src/types/AuthUser'; + +const SHORT_CODE_LENGTH = 12; +const SHORT_CODE_CHARS = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; @Injectable() export class SharedRequestService { constructor( @@ -28,6 +38,33 @@ export class SharedRequestService { createdOn: sharedRequestInfo.createdOn, }; } + /** + * Generate a shortcode + * + * @returns generated shortcode + */ + private generateShortCodeID(): string { + let result = ''; + for (let i = 0; i < SHORT_CODE_LENGTH; i++) { + result += + SHORT_CODE_CHARS[Math.floor(Math.random() * SHORT_CODE_CHARS.length)]; + } + return result; + } + + /** + * Check to see if ShortCode is already present in DB + * + * @returns Shortcode + */ + private async generateUniqueShortCodeID() { + while (true) { + const code = this.generateShortCodeID(); + + const data = await this.getSharedRequest(code); + if (E.isLeft(data)) return E.right(code); + } + } /** * Fetch details regarding a SharedRequest @@ -45,4 +82,47 @@ export class SharedRequestService { return E.left(SHARED_REQUEST_NOT_FOUND); } } + + /** + * Create a new SharedRequest + * + * @param request JSON string of request details + * @param properties JSON string of embed properties, if present + * @returns Either of SharedRequest or error + */ + async createSharedRequest( + request: string, + properties: string | null, + userInfo: AuthUser, + ) { + const requestData = stringToJson(request); + if (E.isLeft(requestData)) + return E.left(SHARED_REQUEST_INVALID_REQUEST_JSON); + + let propertiesData; + if (!properties) propertiesData = undefined; + const parsedProperties = stringToJson(properties); + if (E.isLeft(parsedProperties)) + return E.left(SHARED_REQUEST_INVALID_PROPERTIES_JSON); + propertiesData = parsedProperties.right; + + const generatedShortCode = await this.generateUniqueShortCodeID(); + if (E.isLeft(generatedShortCode)) return E.left(generatedShortCode.left); + + const createdSharedRequest = await this.prisma.shortcode.create({ + data: { + id: generatedShortCode.right, + request: requestData.right, + properties: propertiesData != null ? propertiesData : undefined, + creatorUid: userInfo.uid, + }, + }); + + this.pubsub.publish( + `shortcode/${createdSharedRequest.creatorUid}/created`, + this.cast(createdSharedRequest), + ); + + return E.right(this.cast(createdSharedRequest)); + } }