import dayjs from "dayjs" import { z } from "zod" import { getCacheClient } from "@scandic-hotels/common/dataCache" import { env } from "../../../../env/server" import { unauthorizedError } from "../../../errors" import { safeProtectedServiceProcedure } from "../../../procedures" import { isValidSession } from "../../../utils/session" import { getHotelsAvailabilityByCity } from "../services/getHotelsAvailabilityByCity" export type HotelsAvailabilityInputSchema = z.output< typeof hotelsAvailabilityInputSchema > export const hotelsAvailabilityInputSchema = z .object({ cityId: z.string(), roomStayStartDate: z.string().refine( (val) => { const fromDate = dayjs(val) return fromDate.isValid() }, { message: "FROMDATE_INVALID", } ), roomStayEndDate: z.string().refine( (val) => { const fromDate = dayjs(val) return fromDate.isValid() }, { message: "TODATE_INVALID", } ), adults: z.number(), children: z.string().optional(), bookingCode: z.string().optional().default(""), redemption: z.boolean().optional().default(false), }) .refine( (data) => { const fromDate = dayjs(data.roomStayStartDate).startOf("day") const toDate = dayjs(data.roomStayEndDate).startOf("day") return fromDate.isBefore(toDate) }, { message: "FROMDATE_BEFORE_TODATE", } ) .refine( (data) => { const fromDate = dayjs(data.roomStayStartDate) const today = dayjs().startOf("day") return fromDate.isSameOrAfter(today) }, { message: "FROMDATE_CANNOT_BE_IN_THE_PAST", } ) type Context = { userToken: string | null userPoints?: number } export const hotelsByCity = safeProtectedServiceProcedure .input(hotelsAvailabilityInputSchema) .use(async ({ ctx, input, next }) => { const userToken = await ctx.getScandicUserToken() if (input.redemption) { const hasValidSession = isValidSession(ctx.session) if (!hasValidSession) { throw unauthorizedError() } const pointsValue = await ctx.getUserPointsBalance() if (pointsValue && userToken) { return next({ ctx: { userToken, userPoints: pointsValue, }, }) } } return next({ ctx: { userToken, }, }) }) .query(async ({ ctx, input }) => { const { cityId, roomStayStartDate, roomStayEndDate, adults, children, bookingCode, redemption, } = input // In case of redemption do not cache result if (redemption) { return getHotelsAvailabilityByCity({ input, lang: ctx.lang, userToken: ctx.userToken, serviceToken: ctx.serviceToken, userPoints: ctx.userPoints, }) } const cacheClient = await getCacheClient() return await cacheClient.cacheOrGet( `${cityId}:${roomStayStartDate}:${roomStayEndDate}:${adults}:${children}:${bookingCode}`, async () => { return getHotelsAvailabilityByCity({ input, lang: ctx.lang, userToken: undefined, serviceToken: ctx.serviceToken, userPoints: undefined, }) }, env.CACHE_TIME_CITY_SEARCH ) })