All files / apps/web/src/handlers/debts get-all-peer.ts

100% Statements 22/22
100% Branches 4/4
100% Functions 9/9
100% Lines 20/20

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                    6x         6x 11x 9x                         9x           9x     6x       9x 9x 9x 11x 11x 2x         9x 1x         8x 21x   8x 15x               6x                
import { TRPCError } from "@trpc/server";
import { unique } from "remeda";
import { z } from "zod";
 
import type { CurrencyCode } from "~app/utils/currency";
import { queueCallFactory } from "~web/handlers/batch";
import type { AuthorizedContext } from "~web/handlers/context";
import { authProcedure } from "~web/handlers/trpc";
import { peerIdSchema } from "~web/handlers/validation";
 
const getAllPeerSchema = z.strictObject({
	peerId: peerIdSchema,
});
type Input = z.infer<typeof getAllPeerSchema>;
 
const getData = async (ctx: AuthorizedContext, inputs: readonly Input[]) => {
	const peerIds = unique(inputs.map(({ peerId }) => peerId));
	const [peers, aggregatedDebts] = await Promise.all([
		ctx.database
			.selectFrom("peers")
			.where("peers.id", "in", peerIds)
			.select(["peers.id", "peers.ownerUserId"])
			.execute(),
		ctx.database
			.selectFrom("debts")
			.where("debts.ownerUserId", "=", ctx.auth.userId)
			.where("debts.peerId", "in", peerIds)
			.select([
				"debts.peerId",
				"debts.currencyCode",
				(eb) => eb.fn.sum<string>("debts.amount").as("sum"),
			])
			.orderBy("debts.currencyCode")
			.groupBy(["debts.currencyCode", "debts.peerId"])
			.execute(),
	]);
	return { peers, aggregatedDebts };
};
 
const queueGetAllPeer = queueCallFactory<
	AuthorizedContext,
	Input,
	{ items: { currencyCode: CurrencyCode; sum: number }[] }
>((ctx) => async (inputs) => {
	const { peers, aggregatedDebts } = await getData(ctx, inputs);
	return inputs.map((debt) => {
		const matchedPeer = peers.find((result) => result.id === debt.peerId);
		if (!matchedPeer) {
			return new TRPCError({
				code: "NOT_FOUND",
				message: `Peer "${debt.peerId}" does not exist.`,
			});
		}
		if (matchedPeer.ownerUserId !== ctx.auth.userId) {
			return new TRPCError({
				code: "FORBIDDEN",
				message: `Peer "${matchedPeer.id}" is not owned by "${ctx.auth.email}".`,
			});
		}
		const filteredDebts = aggregatedDebts.filter(
			(aggregatedDebt) => aggregatedDebt.peerId === debt.peerId,
		);
		return {
			items: filteredDebts.map(({ currencyCode, sum }) => ({
				currencyCode,
				sum: Number(sum),
			})),
		};
	});
});
 
export const procedure = authProcedure
	.meta({
		title: "Get all debts for peer",
		description:
			"Returns the current  user's debt amounts with a given peerId, summed per currency.",
	})
	.input(getAllPeerSchema)
	.query(queueGetAllPeer);