Skip to content
Snippets Groups Projects
entry.resolver.ts 2.88 KiB
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,
			},
		});
	}
}