import { Args, Context, Mutation, Parent, Query, ResolveField, Resolver, Subscription, } from '@nestjs/graphql'; import { EntryModel } from './models/entry.model'; import { PrismaService } from '../prisma/prisma.service'; import { BadRequestException, Inject, InternalServerErrorException, NotFoundException, UseGuards, } from '@nestjs/common'; import { AuthGuard } from '../auth/auth.guard'; import { Request } from 'express'; import { EntryService } from '../utils/entry.service'; import { AgentModel } from './models/agent.model'; import { GroupGuard } from '../auth/group.guard'; import { PubSub } from 'graphql-subscriptions'; import { Entry, Group } from '@prisma/client'; @Resolver(() => EntryModel) export class EntryResolver { constructor( @Inject('PUBSUB') private readonly pubSub: PubSub, private readonly prisma: PrismaService, private readonly entryService: EntryService, ) {} @Subscription(() => EntryModel, { filter: ( payload: { onEntryUpdate: Entry & { by?: Group } }, variables, { req }, ) => { if (req.group && payload.onEntryUpdate.by) return req.group.id === payload.onEntryUpdate.by.id; return true; }, }) @UseGuards(AuthGuard) onEntryUpdate() { return this.pubSub.asyncIterator('onEntryUpdate'); } @Mutation(() => EntryModel) @UseGuards(AuthGuard, GroupGuard) unlockEntry( @Args('id') id: string, @Context() { req }: { req: Request }, ): Promise<EntryModel> { return this.prisma.$transaction(async (prisma) => { const entry = await prisma.entry.findFirst({ where: { id, unlockedBy: { none: { id: req.group.id }, }, }, }); if (!entry) throw new NotFoundException(); if (req.group.tokens < entry.cost) throw new BadRequestException('Not enough tokens'); const group = await prisma.group.update({ where: { id: req.group.id }, data: { tokens: { decrement: entry.cost }, unlocks: { connect: { id: entry.id }, }, }, }); if (!group) throw new InternalServerErrorException('Group not found'); await this.pubSub.publish('onEntryUpdate', { onEntryUpdate: { ...entry, by: group, }, }); await this.pubSub.publish('onGroupUpdate', { onGroupUpdate: group, }); return this.entryService.unlockedEntry(entry); }); } @Query(() => [EntryModel]) @UseGuards(AuthGuard) async getEntries(@Context() { req }: { req: Request }) { if (req.agent) return this.prisma.entry .findMany() .then((entries) => entries.map(this.entryService.unlockedEntry), ); return await this.entryService.getAccessibleEntries(req.group.id); } @ResolveField('agent') async agent(@Parent() parent: EntryModel): Promise<AgentModel> { return this.prisma.agent.findFirst({ where: { entries: { some: { id: parent.id }, }, }, select: { id: true, name: true, slug: false, flags: true, }, }); } }