All files / apps/web/src/handlers/receipt-participants update.ts

100% Statements 23/23
100% Branches 12/12
100% Functions 2/2
100% Lines 23/23

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107                          6x                                     6x 6x           6x 1x         5x           5x 1x         4x       4x 1x         3x 1x         2x   2x           2x 1x         1x 1x       1x 1x   1x       1x              
import { TRPCError } from "@trpc/server";
import type { Updateable } from "kysely";
import { z } from "zod";
 
import type { DB } from "~db/types.gen";
import { getReceiptParticipant } from "~web/handlers/receipt-participants/utils";
import { authProcedure } from "~web/handlers/trpc";
import {
	assignableRoleSchema,
	peerIdSchema,
	receiptIdSchema,
} from "~web/handlers/validation";
 
export const procedure = authProcedure
	.meta({
		title: "Update receipt participant",
		description:
			"Updates the role of a peerId participating in a given receipt.",
	})
	.input(
		z.strictObject({
			receiptId: receiptIdSchema,
			peerId: peerIdSchema,
			update: z.discriminatedUnion("type", [
				z.strictObject({
					type: z.literal("role"),
					role: assignableRoleSchema,
				}),
			]),
		}),
	)
	.mutation(async ({ input, ctx }) => {
		const { database } = ctx;
		const receipt = await database
			.selectFrom("receipts")
			.select("ownerUserId")
			.where("id", "=", input.receiptId)
			.limit(1)
			.executeTakeFirst();
		if (!receipt) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: `Receipt "${input.receiptId}" does not exist.`,
			});
		}
		const peer = await database
			.selectFrom("peers")
			.select(["ownerUserId", "connectedUserId"])
			.where("id", "=", input.peerId)
			.limit(1)
			.executeTakeFirst();
		if (!peer) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: `Peer "${input.peerId}" does not exist.`,
			});
		}
		switch (input.update.type) {
			// We want this to blow up in case we add more cases
			// oxlint-disable-next-line typescript/no-unnecessary-condition
			case "role":
				if (receipt.ownerUserId !== ctx.auth.userId) {
					throw new TRPCError({
						code: "FORBIDDEN",
						message: `Only receipt owner can modify peer receipt role.`,
					});
				}
				if (input.peerId === ctx.auth.userId) {
					throw new TRPCError({
						code: "BAD_REQUEST",
						message: `Cannot modify your own receipt role.`,
					});
				}
				break;
		}
		const receiptParticipant = await getReceiptParticipant(
			database,
			input.peerId,
			input.receiptId,
			["peerId"],
		);
		if (!receiptParticipant) {
			throw new TRPCError({
				code: "CONFLICT",
				message: `Peer "${input.peerId}" does not participate in receipt "${input.receiptId}".`,
			});
		}
		let setObject: Updateable<DB["receiptParticipants"]> = {};
		switch (input.update.type) {
			// We want this to blow up in case we add more cases
			// oxlint-disable-next-line typescript/no-unnecessary-condition
			case "role":
				setObject = { role: input.update.role };
				break;
		}
		await database
			.updateTable("receiptParticipants")
			.set(setObject)
			.where((eb) =>
				eb.and({
					receiptId: input.receiptId,
					peerId: input.peerId,
				}),
			)
			.executeTakeFirst();
	});