All files / apps/web/src/handlers/peers get-foreign.ts

100% Statements 25/25
100% Branches 14/14
100% Functions 14/14
100% Lines 24/24

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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128                        6x 9x   9x         9x     9x               9x     9x     9x                                                             6x   2x         6x 5x 3x                                   2x     6x       9x 9x   9x   9x 9x 9x 4x         5x       6x                
import { TRPCError } from "@trpc/server";
import { z } from "zod";
 
import type { PeerId, UserId } from "~db/ids";
import { queueCallFactory } from "~web/handlers/batch";
import type { AuthorizedContext } from "~web/handlers/context";
import { getParticipantsReceipts } from "~web/handlers/receipts/utils";
import { authProcedure } from "~web/handlers/trpc";
import { peerIdSchema } from "~web/handlers/validation";
 
// We allow  user fetch foreign peers
// In case they share the same receipt (as participant or as payer)
const fetchPeers = async (ctx: AuthorizedContext, ids: PeerId[]) =>
	ctx.database
		.with("mergedReceipts", (qc) =>
			getParticipantsReceipts(qc, ctx.auth.userId)
				.groupBy("receipts.id")
				.select("receipts.id"),
		)
		.with("mergedParticipants", (qc) =>
			qc
				.selectFrom("receiptParticipants")
				.innerJoin("mergedReceipts", (qb) =>
					qb.onRef("receiptParticipants.receiptId", "=", "mergedReceipts.id"),
				)
				.groupBy("receiptParticipants.peerId")
				.select("receiptParticipants.peerId"),
		)
		.selectFrom("peers as peersTheir")
		.where("peersTheir.id", "in", ids)
		.innerJoin("mergedParticipants", (qb) =>
			qb.onRef("peersTheir.id", "=", "mergedParticipants.peerId"),
		)
		.leftJoin("users", (qb) =>
			qb.onRef("peersTheir.connectedUserId", "=", "users.id"),
		)
		.leftJoin("peers as peersMine", (qb) =>
			qb
				.onRef("peersMine.connectedUserId", "=", "peersTheir.connectedUserId")
				.on("peersMine.ownerUserId", "=", ctx.auth.userId),
		)
		.select([
			"peersMine.id as mineId",
			"peersMine.name as mineName",
			"peersMine.publicName as minePublicName",
			"users.id as userId",
			"users.email",
			"users.avatarUrl",
			"peersTheir.id as theirId",
			"peersTheir.name as theirName",
			"peersTheir.publicName as theirPublicName",
			"peersTheir.ownerUserId",
		])
		.groupBy([
			"peersMine.id",
			"peersMine.name",
			"peersMine.publicName",
			"users.id",
			"users.email",
			"users.avatarUrl",
			"peersTheir.id",
			"peersTheir.name",
			"peersTheir.publicName",
			"peersTheir.ownerUserId",
		])
		.execute();
 
// Excessive function is needed to properly infer return types
const getForeignPeer = (
	peer: Awaited<ReturnType<typeof fetchPeers>>[number],
) => ({
	remoteId: peer.theirId,
	name: peer.theirPublicName || peer.theirName,
});
 
const mapPeer = (peer: Awaited<ReturnType<typeof fetchPeers>>[number]) => {
	if (peer.mineId && peer.mineName && peer.email && peer.userId) {
		return {
			id: peer.mineId,
			name: peer.mineName,
			publicName:
				peer.minePublicName === null ? undefined : peer.minePublicName,
			connectedUser: {
				id: peer.userId,
				email: peer.email,
				avatarUrl: peer.avatarUrl || undefined,
			} as
				| {
						id: UserId;
						email: string;
						avatarUrl?: string;
				  }
				| undefined,
		};
	}
	return getForeignPeer(peer);
};
 
const queuePeer = queueCallFactory<
	AuthorizedContext,
	{ id: PeerId },
	ReturnType<typeof mapPeer>
>((ctx) => async (inputs) => {
	const peers = await fetchPeers(
		ctx,
		inputs.map(({ id }) => id),
	);
	return inputs.map((input) => {
		const matchedPeer = peers.find((peer) => peer.theirId === input.id);
		if (!matchedPeer) {
			return new TRPCError({
				code: "NOT_FOUND",
				message: `No peer found by id "${input.id}" or you don't have access to it.`,
			});
		}
		return mapPeer(matchedPeer);
	});
});
 
export const procedure = authProcedure
	.meta({
		title: "Get foreign peer",
		description:
			"Returns a peer by id owned by another  user, provided they share a receipt with the current  user.",
	})
	.input(z.strictObject({ id: peerIdSchema }))
	.query(queuePeer);