diff --git a/src/main/scala/server/Server.scala b/src/main/scala/server/Server.scala index a253c87..bf88b9c 100644 --- a/src/main/scala/server/Server.scala +++ b/src/main/scala/server/Server.scala @@ -10,7 +10,7 @@ import fetch.csv.FileNameService import fetch.lvgmc.{FetchService, WaterTemperatureService} import fetch.warnings.WarningService import fs2.io.file.{Files, Path} -import server.ValidateRoutes.{AggFieldList, AggKey, CityList, DateTimeRange, Granularity, ValidateDate, ValidateDateTime, ValidateFileName, ValidateInt, ValidateMonths, ValidateZonedDateTime} +import server.ValidateRoutes.{AggFieldList, AggKey, CityList, DateTimeRange, Granularity, ValidateDate, ValidateDateTime, ValidateField, ValidateFileName, ValidateInt, ValidateMonths, ValidateZonedDateTime} import org.http4s._ import org.http4s.dsl.io._ import org.http4s.implicits._ @@ -139,7 +139,7 @@ class Server(postgresService: PostgresService, dataService: DataService, fetch: } // http://0.0.0.0:8080/api/query/city/Liepāja,Rēzekne/20230414_2200-20230501_1230/hour/tempMax/max - case GET -> Root / "query" / "city" / CityList(cities) / DateTimeRange(from, to) / Granularity(granularity) / field / AggKey(key) => + case GET -> Root / "query" / "city" / CityList(cities) / DateTimeRange(from, to) / Granularity(granularity) / ValidateField(field) / AggKey(key) => val userQuery = UserQuery(cities, field, key, granularity, from, to) postgresService.query(userQuery) diff --git a/src/main/scala/server/ValidateRoutes.scala b/src/main/scala/server/ValidateRoutes.scala index a8c87c9..94efab3 100644 --- a/src/main/scala/server/ValidateRoutes.scala +++ b/src/main/scala/server/ValidateRoutes.scala @@ -64,6 +64,14 @@ object ValidateRoutes { } } + // Restricts a raw path segment to a known WeatherData field name before it + // can reach Fragment.const (unescaped SQL splicing) in PostgresService. + object ValidateField { + def unapply(str: String): Option[String] = { + if (WeatherData.getKeys.contains(str)) Some(str) else None + } + } + object AggFieldList { def unapply(str: String): Option[NonEmptyList[String]] = { val weatherFields = WeatherData.getKeys