All files / apps/web/src/handlers/peers add.ts

100% Statements 31/31
100% Branches 8/8
100% Functions 11/11
100% Lines 30/30

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                            6x           6x       9x 11x 11x                                 6x       9x 9x 3x   6x 7x         6x 8x 1x     7x 7x 7x 3x   4x       6x       9x     6x                       9x 9x 9x   11x   9x   11x   8x           6x                
import { TRPCError } from "@trpc/server";
import type { Insertable } from "kysely";
import { isNonNullish } from "remeda";
import { z } from "zod";
 
import { peerNameSchema } from "~app/utils/validation";
import type { PeerId } from "~db/ids";
import type { DB } from "~db/types.gen";
import { queueCallFactory } from "~web/handlers/batch";
import type { AuthorizedContext } from "~web/handlers/context";
import { authProcedure } from "~web/handlers/trpc";
import { batchFn as addUserConnectionIntentions } from "~web/handlers/user-connection-intentions/add";
import { emailSchema } from "~web/handlers/validation";
 
const addPeerSchema = z.strictObject({
	name: peerNameSchema,
	publicName: peerNameSchema.optional(),
	email: emailSchema.optional(),
});
 
const getPeers = (
	ctx: AuthorizedContext,
	inputs: readonly z.infer<typeof addPeerSchema>[],
) =>
	inputs.map((input) => {
		const id = ctx.getUuid() as PeerId;
		return {
			peer: {
				id,
				ownerUserId: ctx.auth.userId,
				name: input.name,
				publicName: input.publicName,
			},
			connection: input.email
				? {
						name: input.name,
						email: input.email,
						id,
					}
				: undefined,
		};
	});
 
const insertConnections = async (
	ctx: AuthorizedContext,
	connections: ReturnType<typeof getPeers>[number]["connection"][],
) => {
	const nonEmptyConnections = connections.filter(isNonNullish);
	if (nonEmptyConnections.length === 0) {
		return [];
	}
	const intentions = await addUserConnectionIntentions(ctx)(
		nonEmptyConnections.map((input) => ({
			email: input.email,
			peerId: input.id,
		})),
	);
	return connections.map((connection) => {
		if (!connection) {
			return undefined;
		}
		const matchedNonEmptyConnectionIndex =
			nonEmptyConnections.indexOf(connection);
		const matchedIntentionOrError = intentions[matchedNonEmptyConnectionIndex];
		if (matchedIntentionOrError instanceof TRPCError) {
			throw matchedIntentionOrError;
		}
		return matchedIntentionOrError;
	});
};
 
const insertPeers = async (
	ctx: AuthorizedContext,
	peers: Insertable<DB["peers"]>[],
) => {
	await ctx.database.insertInto("peers").values(peers).execute();
};
 
const queueAddPeer = queueCallFactory<
	AuthorizedContext,
	z.infer<typeof addPeerSchema>,
	{
		id: PeerId;
		connection?: Exclude<
			Awaited<
				ReturnType<ReturnType<typeof addUserConnectionIntentions>>
			>[number],
			TRPCError
		>;
	}
>((ctx) => async (inputs) => {
	const peersWithConnections = getPeers(ctx, inputs);
	await insertPeers(
		ctx,
		peersWithConnections.map(({ peer }) => peer),
	);
	const connections = await insertConnections(
		ctx,
		peersWithConnections.map(({ connection }) => connection),
	);
	return peersWithConnections.map(({ peer }, index) => ({
		id: peer.id,
		connection: connections[index],
	}));
});
 
export const procedure = authProcedure
	.meta({
		title: "Add peer",
		description:
			"Creates a new peer owned by the current  user, optionally sending a connection intention to a given email.",
	})
	.input(addPeerSchema)
	.mutation(queueAddPeer);