diff --git a/src/rides/index.ts b/src/rides/index.ts index edd5589..63291ba 100644 --- a/src/rides/index.ts +++ b/src/rides/index.ts @@ -2,7 +2,7 @@ import { Ride } from "../types/ride" import { Scheduler } from "./scheduler" import { deleteRide } from "../data/redis" import { logNames, logger } from "../logs" -import { RideNotInTimeError } from "../utils/errors" +import { RideNotInTimeError, rideFailureReason } from "../utils/errors" const schedulers: Record = {} @@ -26,11 +26,16 @@ export const startRideNotifications = async (ride: Ride, isExisting: boolean = f rideLogger.info(registerRideLog.success, { ...ride }) return { success: true, rideId: ride.rideId } } catch (error) { - if (!(error instanceof RideNotInTimeError)) { - rideLogger.error(registerRideLog.failed, { error, ...ride }) + const reason = rideFailureReason(error) + + // Warn, not error: the ride is outside the startable window, which isn't a server fault — but still log it. + if (error instanceof RideNotInTimeError) { + rideLogger.warn(registerRideLog.failed, { reason, ...ride }) + } else { + rideLogger.error(registerRideLog.failed, { error, reason, ...ride }) } - return { success: false } + return { success: false, reason } } } diff --git a/src/rides/scheduler.ts b/src/rides/scheduler.ts index d8fc4bc..65fac05 100644 --- a/src/rides/scheduler.ts +++ b/src/rides/scheduler.ts @@ -56,7 +56,7 @@ export class Scheduler { await deleteRide(ride.rideId) } - throw new RideNotInTimeError(logNames.scheduler.rideInPast) + throw new RideNotInTimeError(logNames.scheduler.rideInPast, "ride_in_past") } if (env === "production" && dayjs(route.departureTime).add(route.delay, "minutes").diff(dayjs(), "minutes") > 60) { @@ -70,7 +70,7 @@ export class Scheduler { await deleteRide(ride.rideId) } - throw new RideNotInTimeError(logNames.scheduler.rideInFuture) + throw new RideNotInTimeError(logNames.scheduler.rideInFuture, "ride_in_future") } if (!isExisting) { diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 7da910f..4859ca2 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -1,2 +1,26 @@ -export class RideNotInTimeError extends Error {} -export class NotFoundRouteForRide extends Error {} +// Returned to the client alongside `success: false`, so it can tag its report with the cause. +export type RideFailureReason = "route_not_found" | "ride_in_past" | "ride_in_future" | "internal_error" + +export class RideNotInTimeError extends Error { + constructor( + message: string, + readonly reason: Extract, + ) { + super(message) + this.name = "RideNotInTimeError" + } +} + +export class NotFoundRouteForRide extends Error { + readonly reason: RideFailureReason = "route_not_found" + + constructor(message: string) { + super(message) + this.name = "NotFoundRouteForRide" + } +} + +export const rideFailureReason = (error: unknown): RideFailureReason => { + if (error instanceof RideNotInTimeError || error instanceof NotFoundRouteForRide) return error.reason + return "internal_error" +}