diff --git a/packages/hoppscotch-backend/src/prisma/prisma.service.ts b/packages/hoppscotch-backend/src/prisma/prisma.service.ts index 5b962c430..be48954ef 100644 --- a/packages/hoppscotch-backend/src/prisma/prisma.service.ts +++ b/packages/hoppscotch-backend/src/prisma/prisma.service.ts @@ -1,5 +1,5 @@ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client/scripts/default-index'; +import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService @@ -16,4 +16,4 @@ export class PrismaService async onModuleDestroy() { await this.$disconnect(); } -} +} \ No newline at end of file diff --git a/packages/hoppscotch-backend/src/user/dtos/update-user-input.dto.ts b/packages/hoppscotch-backend/src/user/dtos/update-user-input.dto.ts new file mode 100644 index 000000000..2747c9e3d --- /dev/null +++ b/packages/hoppscotch-backend/src/user/dtos/update-user-input.dto.ts @@ -0,0 +1,18 @@ +import { Field, InputType } from '@nestjs/graphql'; + +@InputType() +export class UpdateUserInput { + @Field({ + nullable: true, + name: 'currentRESTSession', + description: 'JSON string of the session', + }) + currentRESTSession?: string; + + @Field({ + nullable: true, + name: 'currentGQLSession', + description: 'JSON string of the session', + }) + currentGQLSession?: string; +} diff --git a/packages/hoppscotch-backend/src/user/user.module.ts b/packages/hoppscotch-backend/src/user/user.module.ts index 3681faad5..a5f9eda17 100644 --- a/packages/hoppscotch-backend/src/user/user.module.ts +++ b/packages/hoppscotch-backend/src/user/user.module.ts @@ -1,10 +1,12 @@ import { Module } from '@nestjs/common'; import { UserResolver } from './user.resolver'; import { PubSubModule } from 'src/pubsub/pubsub.module'; +import { PrismaModule } from 'src/prisma/prisma.module'; +import { UserService } from './user.service'; @Module({ - imports: [PubSubModule], - providers: [UserResolver], + imports: [PubSubModule, PrismaModule], + providers: [UserResolver, UserService], exports: [], }) export class UserModule {} diff --git a/packages/hoppscotch-backend/src/user/user.resolver.ts b/packages/hoppscotch-backend/src/user/user.resolver.ts index d913be01d..3358b187b 100644 --- a/packages/hoppscotch-backend/src/user/user.resolver.ts +++ b/packages/hoppscotch-backend/src/user/user.resolver.ts @@ -1,14 +1,22 @@ -import { Resolver, Query } from '@nestjs/graphql'; +import { Resolver, Query, Mutation, Args, Subscription } from '@nestjs/graphql'; import { User } from './user.model'; import { UseGuards } from '@nestjs/common'; import { GqlAuthGuard } from '../guards/gql-auth.guard'; import { GqlUser } from '../decorators/gql-user.decorator'; +import { UserService } from './user.service'; +import { throwErr } from 'src/utils'; +import * as E from 'fp-ts/lib/Either'; +import { UpdateUserInput } from './dtos/update-user-input.dto'; +import { PubSubService } from 'src/pubsub/pubsub.service'; @Resolver(() => User) export class UserResolver { // TODO: remove the eslint-disable line below once dependencies are added to user.service file // eslint-disable-next-line @typescript-eslint/no-empty-function - constructor() {} + constructor( + private readonly userService: UserService, + private readonly pubsub: PubSubService, + ) {} @Query(() => User, { description: @@ -27,4 +35,30 @@ export class UserResolver { me2(@GqlUser() user: User): User { return user; } + + /* Mutations */ + + @Mutation(() => User, { + description: 'Update user information', + }) + @UseGuards(GqlAuthGuard) + async updateUser( + @GqlUser() user: User, + @Args('userInput') userInput: UpdateUserInput, + ): Promise { + const updatedUser = await this.userService.updateUser(user, userInput); + if (E.isLeft(updatedUser)) throwErr(updatedUser.left); + return updatedUser.right; + } + + /* Subscriptions */ + + @Subscription(() => User, { + description: 'Listen for user updates', + resolve: (value) => value, + }) + @UseGuards(GqlAuthGuard) + userSettingsUpdated(@GqlUser() user: User) { + return this.pubsub.asyncIterator(`user/${user.uid}/updated`); + } } diff --git a/packages/hoppscotch-backend/src/user/user.service.ts b/packages/hoppscotch-backend/src/user/user.service.ts new file mode 100644 index 000000000..df8a60aff --- /dev/null +++ b/packages/hoppscotch-backend/src/user/user.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@nestjs/common'; +import { PrismaService } from '../prisma/prisma.service'; +import { User } from './user.model'; +import * as E from 'fp-ts/lib/Either'; +import { USER_NOT_FOUND } from 'src/errors'; +import { UpdateUserInput } from './dtos/update-user-input.dto'; +import { PubSubService } from 'src/pubsub/pubsub.service'; + +@Injectable() +export class UserService { + constructor( + private readonly prisma: PrismaService, + private readonly pubsub: PubSubService, + ) {} + + async updateUser(user: User, updateUserDto: UpdateUserInput) { + try { + const updatedUser = await this.prisma.user.update({ + where: { uid: user.uid }, + data: updateUserDto, + }); + + // Publish subscription for user updates + await this.pubsub.publish( + `user_settings/${user.uid}/updated`, + updatedUser, + ); + + return E.right(updatedUser); + } catch (e) { + return E.left(USER_NOT_FOUND); + } + } +}