From 62a4c88b0c4a026e346262b51eae1989a7bb09f5 Mon Sep 17 00:00:00 2001 From: Krystof Date: Sat, 13 Jul 2024 00:39:23 +0200 Subject: [PATCH] feat(be): stops bbox search --- backend-nest/src/app.module.ts | 4 +- backend-nest/src/constants/constants.ts | 2 +- .../src/controllers/stop/stop.controller.ts | 78 +++++++++++++++---- .../src/controllers/stop/stop.utils.ts | 18 +++++ backend-nest/src/utils/fetch.ts | 4 +- 5 files changed, 84 insertions(+), 22 deletions(-) create mode 100644 backend-nest/src/controllers/stop/stop.utils.ts diff --git a/backend-nest/src/app.module.ts b/backend-nest/src/app.module.ts index 868ec50e..879488cb 100644 --- a/backend-nest/src/app.module.ts +++ b/backend-nest/src/app.module.ts @@ -5,7 +5,7 @@ import { AppService } from "./app.service"; import { MetroController } from "./controllers/metro/metro.controller"; import { ConfigModule } from "@nestjs/config"; import { RequestLoggerMiddleware } from "./middleware/request-logger.middleware"; -import { TTL } from "./constants/constants"; +import { TTL_DEFAULT } from "./constants/constants"; import { ScheduleModule } from "@nestjs/schedule"; import { SyncStopsService } from "./services/sync-stops.service"; import { PrismaService } from "./services/prisma.service"; @@ -17,7 +17,7 @@ import { StopController } from "./controllers/stop/stop.controller"; ScheduleModule.forRoot(), CacheModule.register({ isGlobal: true, - ttl: TTL, + ttl: TTL_DEFAULT, }), ], controllers: [AppController, MetroController, StopController], diff --git a/backend-nest/src/constants/constants.ts b/backend-nest/src/constants/constants.ts index 5c609a06..b6a20460 100644 --- a/backend-nest/src/constants/constants.ts +++ b/backend-nest/src/constants/constants.ts @@ -1,3 +1,3 @@ -export const TTL = 20_000; +export const TTL_DEFAULT = 2 * 1_000; export const GOLEMIO_API = "https://api.golemio.cz"; diff --git a/backend-nest/src/controllers/stop/stop.controller.ts b/backend-nest/src/controllers/stop/stop.controller.ts index e4ddc6fb..cff31045 100644 --- a/backend-nest/src/controllers/stop/stop.controller.ts +++ b/backend-nest/src/controllers/stop/stop.controller.ts @@ -1,27 +1,58 @@ -import { Controller, Get } from "@nestjs/common"; +import { Controller, Get, HttpException, Query } from "@nestjs/common"; import { PrismaService } from "../../services/prisma.service"; +import z from "zod"; +import { minMax } from "src/utils/math"; +import { parseQueryParam } from "src/utils/query-params"; +import { stopSelect } from "./stop.utils"; @Controller("stop") export class StopController { constructor(private prisma: PrismaService) {} - @Get("all") - async getAllStops(): Promise { + @Get() + async getStops( + @Query("latitude") latitudes: string[], + @Query("longitude") longitudes: string[], + ) { + const parsedParams = { + latitudes: parseQueryParam(latitudes), + longitudes: parseQueryParam(longitudes), + }; + + const schema = z.number().array().length(2); + const parsedValues = { + latitudes: schema.safeParse(parsedParams.latitudes), + longitudes: schema.safeParse(parsedParams.longitudes), + }; + + if (!parsedValues.latitudes.success) { + throw new HttpException( + "Invalid latitude: " + + JSON.stringify(parsedValues.latitudes.error.errors), + 400, + ); + } + + if (!parsedValues.longitudes.success) { + throw new HttpException( + "Invalid longitude: " + parsedValues.longitudes.error, + 400, + ); + } + + const latitude = minMax(parsedValues.latitudes.data); + const longitude = minMax(parsedValues.longitudes.data); + const stops = await this.prisma.stop.findMany({ - select: { - id: true, - latitude: true, - longitude: true, - name: true, - routes: { - select: { - route: { - select: { - id: true, - name: true, - }, - }, - }, + select: stopSelect, + where: { + latitude: { + gte: latitude.min, + lte: latitude.max, + }, + longitude: { + gte: longitude.min, + lte: longitude.max, }, }, }); @@ -31,4 +62,17 @@ export class StopController { routes: stop.routes.map(({ route }) => route), })); } + + @Get("all") + async getAllStops() { + console.log("Fetching all stops"); + const stops = await this.prisma.stop.findMany({ + select: stopSelect, + }); + + return stops.map((stop) => ({ + ...stop, + routes: stop.routes.map(({ route }) => route), + })); + } } diff --git a/backend-nest/src/controllers/stop/stop.utils.ts b/backend-nest/src/controllers/stop/stop.utils.ts new file mode 100644 index 00000000..29fb9054 --- /dev/null +++ b/backend-nest/src/controllers/stop/stop.utils.ts @@ -0,0 +1,18 @@ +import type { Prisma } from "@prisma/client"; + +export const stopSelect = { + id: true, + latitude: true, + longitude: true, + name: true, + routes: { + select: { + route: { + select: { + id: true, + name: true, + }, + }, + }, + }, +} satisfies Prisma.StopSelect; diff --git a/backend-nest/src/utils/fetch.ts b/backend-nest/src/utils/fetch.ts index 5541c8e2..4beb71df 100644 --- a/backend-nest/src/utils/fetch.ts +++ b/backend-nest/src/utils/fetch.ts @@ -1,7 +1,7 @@ import { PlatformID } from "../data/platforms"; import { diff, group } from "radash"; import { getDepartures } from "../controllers/metro/getDepartures"; -import { TTL } from "../constants"; +import { TTL_DEFAULT } from "../constants"; import type { CacheManager } from "../types/types"; export const getGolemioHeaders = () => { @@ -35,7 +35,7 @@ export const fetchDeparturesByGtfsID = async ( ) as Record; for (const key in newResByPlatform) { - await cacheManager.set(key, newResByPlatform[key], TTL); + await cacheManager.set(key, newResByPlatform[key], TTL_DEFAULT); } return { ...res, ...newResByPlatform };