export function registerReadWriteNewsletterRoutes()

in apps/newsletters-api/src/app/routes/newsletters.ts [86:211]


export function registerReadWriteNewsletterRoutes(app: FastifyInstance) {
	const hasAccessHook = async (
		request: FastifyRequest,
		reply: FastifyReply,
	) => {
		const user = getUserProfile(request);
		const isAuthorised = await hasEditAccess(user.profile);
		const { body } = request;
		const update = replaceNullWithUndefinedForUnknown(body);

		if (!isPartialNewsletterData(update)) {
			void reply.status(400).send(makeErrorResponse('invalid update data'));
		} else {
			const isAuthorisedForUpdate =
				await isAuthorisedToMakeRequestedNewsletterUpdate(user.profile, update);
			if (!isAuthorised || !isAuthorisedForUpdate) {
				void reply
					.status(403)
					.send(makeErrorResponse('You do not have edit access'));
			}
		}
	};

	app.patch<{
		Params: { newsletterId: string };
		Body: unknown;
	}>(
		'/api/newsletters/:newsletterId',
		{ preValidation: hasAccessHook },
		async (req, res) => {
			const user = getUserProfile(req);

			const { newsletterId } = req.params;
			const { body: modifications } = req;
			const newsletterIdAsNumber = Number(newsletterId);

			if (isNaN(newsletterIdAsNumber)) {
				return res
					.status(400)
					.send(makeErrorResponse(`Non numeric id provided`));
			}

			replaceNullWithUndefinedForUnknown(modifications);

			if (!isPartialNewsletterData(modifications)) {
				return res
					.status(400)
					.send(makeErrorResponse(`Not a valid partial newsletter`));
			}

			// This test would never fail on the current implementation since
			// user.profile must be defined or there would be an accessDeniedError.
			// Kept in to preserve type-safety.
			if (!user.profile) {
				return res.status(403).send(makeErrorResponse('No user profile.'));
			}
			const storageResponse = await newsletterStore.update(
				newsletterIdAsNumber,
				modifications,
				user.profile,
			);

			if (!storageResponse.ok) {
				return res
					.status(mapStorageFailureReasonToStatusCode(storageResponse.reason))
					.send(makeErrorResponse(storageResponse.message));
			}

			return makeSuccessResponse(storageResponse.data);
		},
	);

	app.post<{
		Params: { newsletterId: string };
		Body: unknown;
	}>(
		'/api/newsletters/:newsletterId',
		{ preValidation: hasAccessHook },
		async (req, res) => {
			const user = getUserProfile(req);
			const accessDeniedError = await makeAccessDeniedApiResponse(
				user.profile,
				'editNewsletters',
			);
			if (accessDeniedError) {
				return res.status(403).send(accessDeniedError);
			}

			const { newsletterId } = req.params;
			const { body: newsletter } = req;
			const newsletterIdAsNumber = Number(newsletterId);

			if (isNaN(newsletterIdAsNumber)) {
				return res
					.status(400)
					.send(makeErrorResponse(`Non numeric id provided`));
			}

			replaceNullWithUndefinedForUnknown(newsletter);

			if (!isNewsletterData(newsletter)) {
				return res
					.status(400)
					.send(makeErrorResponse(`Not a valid newsletter`));
			}

			if (!user.profile) {
				return res.status(403).send(makeErrorResponse('No user profile'));
			}

			const storageResponse = await newsletterStore.replace(
				newsletterIdAsNumber,
				newsletter,
				user.profile,
			);

			if (!storageResponse.ok) {
				return res
					.status(mapStorageFailureReasonToStatusCode(storageResponse.reason))
					.send(makeErrorResponse(storageResponse.message));
			}

			return makeSuccessResponse(storageResponse.data);
		},
	);
}