diff --git a/app/package.json b/app/package.json index 3efe592c..fa47987f 100644 --- a/app/package.json +++ b/app/package.json @@ -43,6 +43,7 @@ "graphql": "^16.8.1", "graphql-query-complexity": "^0.12.0", "graphql-scalars": "^1.22.2", + "graphql-subscriptions": "^2.0.0", "lru-cache": "^10.0.0", "mongoose": "^7.3.3", "reflect-metadata": "^0.1.13", diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index b7cdaa23..9313c51a 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -68,6 +68,9 @@ dependencies: graphql-scalars: specifier: ^1.22.2 version: 1.22.4(graphql@16.8.1) + graphql-subscriptions: + specifier: ^2.0.0 + version: 2.0.0(graphql@16.8.1) lru-cache: specifier: ^10.0.0 version: 10.0.1 @@ -4835,6 +4838,15 @@ packages: tslib: 2.6.2 dev: false + /graphql-subscriptions@2.0.0(graphql@16.8.1): + resolution: {integrity: sha512-s6k2b8mmt9gF9pEfkxsaO1lTxaySfKoEJzEfmwguBbQ//Oq23hIXCfR1hm4kdh5hnR20RdwB+s3BCb+0duHSZA==} + peerDependencies: + graphql: ^15.7.2 || ^16.0.0 + dependencies: + graphql: 16.8.1 + iterall: 1.3.0 + dev: false + /graphql-tag@2.12.6(graphql@16.8.1): resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} engines: {node: '>=10'} diff --git a/app/src/app.module.ts b/app/src/app.module.ts index 8359f582..bdc88199 100644 --- a/app/src/app.module.ts +++ b/app/src/app.module.ts @@ -75,6 +75,14 @@ import { TeamInfoModule } from './page/teamInfo/teamInfo.module'; numberScalarMode: 'integer', }, autoSchemaFile: join(process.cwd(), 'src/schema.gql'), + subscriptions: { + 'graphql-ws': { + path: '/graphql', + }, + 'subscriptions-transport-ws': { + path: '/graphql', + }, + }, }; }, }), diff --git a/app/src/follow/follow.resolve.ts b/app/src/follow/follow.resolve.ts index e0f1c575..76cb673a 100644 --- a/app/src/follow/follow.resolve.ts +++ b/app/src/follow/follow.resolve.ts @@ -1,17 +1,29 @@ import { UseFilters, UseGuards } from '@nestjs/common'; -import { Args, Mutation, Resolver } from '@nestjs/graphql'; +import { Args, Mutation, Resolver, Subscription } from '@nestjs/graphql'; +import { PubSub } from 'graphql-subscriptions'; import { MyUserId } from 'src/auth/myContext'; import { StatAuthGuard } from 'src/auth/statAuthGuard'; import { HttpExceptionFilter } from 'src/http-exception.filter'; import { FollowService } from './follow.service'; import { FollowListWithCount, FollowResult } from './model/follow.model'; +const pubSub = new PubSub(); + @UseFilters(HttpExceptionFilter) -@UseGuards(StatAuthGuard) @Resolver() export class FollowResolver { constructor(private readonly followService: FollowService) {} + @Subscription((_returns) => FollowResult, { + name: 'followUpdated', + filter: (payload, _variables) => { + return payload.followUpdated.message === 'OK'; + }, + }) + followUpdated() { + return pubSub.asyncIterator('followUpdated'); + } + @Mutation((_returns) => FollowResult, { description: '프론트 테스트용 임시 함수', }) @@ -23,22 +35,37 @@ export class FollowResolver { return await this.followService.MakeFollowUser(to, from, type); } + @UseGuards(StatAuthGuard) @Mutation((_returns) => FollowResult) async followUser( @MyUserId() userId: number, @Args('target') target: string, ): Promise { - return await this.followService.followUser(userId, target); + const followResult = await this.followService.followUser(userId, target); + + if (followResult.message === 'OK') { + pubSub.publish('followUpdated', { followUpdated: followResult }); + } + + return followResult; } + @UseGuards(StatAuthGuard) @Mutation((_returns) => FollowResult) async unfollowUser( @MyUserId() userId: number, @Args('target') target: string, ): Promise { - return await this.followService.unfollowUser(userId, target); + const followResult = await this.followService.unfollowUser(userId, target); + + if (followResult.message === 'OK') { + pubSub.publish('followUpdated', { followUpdated: followResult }); + } + + return followResult; } + @UseGuards(StatAuthGuard) @Mutation((_returns) => FollowListWithCount) async getFollowerList( @MyUserId() userId: number, @@ -56,6 +83,7 @@ export class FollowResolver { }; } + @UseGuards(StatAuthGuard) @Mutation((_returns) => FollowListWithCount) async getFollowingList( @MyUserId() userId: number, diff --git a/app/src/schema.gql b/app/src/schema.gql index a8938161..3d80b19c 100644 --- a/app/src/schema.gql +++ b/app/src/schema.gql @@ -676,4 +676,8 @@ type FollowSuccess { type FollowFail { message: String! +} + +type Subscription { + followUpdated: FollowResult! } \ No newline at end of file