From 8a1daa0fe2d925c813d8aa9110ed5cebca1b3629 Mon Sep 17 00:00:00 2001 From: Alexey Zinchenko Date: Sun, 1 May 2022 21:21:24 +0300 Subject: [PATCH] Updated 5 Day / 3 Hour Forecast functionality. --- .../FreeForecastLocationDeserializer.java | 87 +++++++++ .../free/FreeForecastRainDeserializer.java | 44 +++++ .../free/FreeForecastSnowDeserializer.java | 44 +++++ ...ayThreeHourStepForecastResponseMapper.java | 164 ++++------------ .../model/forecast/free/WeatherForecast.java | 42 +++- ...FiveDayThreeHourStepForecastRequester.java | 5 + ...reeHourStepForecastResponseMapperTest.java | 183 ++++++++++++++++++ .../free/WeatherForecastUnitTest.java | 7 +- ...yThreeHourStepForecastIntegrationTest.java | 14 +- ...ourStepForecastResponseMapperUnitTest.java | 2 +- 10 files changed, 443 insertions(+), 149 deletions(-) create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastLocationDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastRainDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastSnowDeserializer.java create mode 100644 src/test/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapperTest.java diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastLocationDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastLocationDeserializer.java new file mode 100644 index 0000000..a8838a0 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastLocationDeserializer.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.deserializer.forecast.free; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.github.prominence.openweathermap.api.deserializer.CoordinatesDeserializer; +import com.github.prominence.openweathermap.api.model.Coordinates; +import com.github.prominence.openweathermap.api.model.forecast.free.Location; + +import java.io.IOException; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.TimeZone; + +public class FreeForecastLocationDeserializer extends JsonDeserializer { + private final ObjectMapper objectMapper = new ObjectMapper(); + + public FreeForecastLocationDeserializer() { + final SimpleModule module = new SimpleModule(); + module.addDeserializer(Coordinates.class, new CoordinatesDeserializer()); + objectMapper.registerModule(module); + } + + @Override + public Location deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + final JsonNode rootNode = p.getCodec().readTree(p); + final Location location = Location.withValues(rootNode.get("id").asInt(), rootNode.get("name").asText()); + + final JsonNode timezoneNode = rootNode.get("timezone"); + if (timezoneNode != null) { + location.setZoneOffset(ZoneOffset.ofTotalSeconds(timezoneNode.asInt())); + } + + final JsonNode countryNode = rootNode.get("country"); + if (countryNode != null) { + location.setCountryCode(countryNode.asText()); + } + + final JsonNode sunriseNode = rootNode.get("sunrise"); + final JsonNode sunsetNode = rootNode.get("sunset"); + if (sunriseNode != null) { + location.setSunriseTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(sunriseNode.asLong()), TimeZone.getDefault().toZoneId())); + } + if (sunsetNode != null) { + location.setSunsetTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(sunsetNode.asLong()), TimeZone.getDefault().toZoneId())); + } + + final JsonNode coordNode = rootNode.get("coord"); + if (coordNode != null) { + location.setCoordinates(objectMapper.readValue(objectMapper.treeAsTokens(coordNode), Coordinates.class)); + } + + final JsonNode populationNode = rootNode.get("population"); + if (populationNode != null) { + location.setPopulation(populationNode.asLong()); + } + + return location; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastRainDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastRainDeserializer.java new file mode 100644 index 0000000..a795487 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastRainDeserializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.deserializer.forecast.free; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.free.Rain; + +import java.io.IOException; + +public class FreeForecastRainDeserializer extends JsonDeserializer { + @Override + public Rain deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + final JsonNode rainNode = p.getCodec().readTree(p); + final JsonNode oneHourNode = rainNode.get("3h"); + if (oneHourNode != null) { + return Rain.withThreeHourLevelValue(oneHourNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastSnowDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastSnowDeserializer.java new file mode 100644 index 0000000..7f2e9c6 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/free/FreeForecastSnowDeserializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.deserializer.forecast.free; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.free.Snow; + +import java.io.IOException; + +public class FreeForecastSnowDeserializer extends JsonDeserializer { + @Override + public Snow deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + final JsonNode snowNode = p.getCodec().readTree(p); + final JsonNode oneHourNode = snowNode.get("3h"); + if (oneHourNode != null) { + return Snow.withThreeHourLevelValue(oneHourNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapper.java index 369dc9e..8789d78 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapper.java +++ b/src/main/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapper.java @@ -24,30 +24,23 @@ package com.github.prominence.openweathermap.api.mapper; import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; -import com.github.prominence.openweathermap.api.deserializer.AtmosphericPressureDeserializer; -import com.github.prominence.openweathermap.api.deserializer.TemperatureDeserializer; -import com.github.prominence.openweathermap.api.deserializer.WeatherStateDeserializer; -import com.github.prominence.openweathermap.api.deserializer.WindDeserializer; +import com.github.prominence.openweathermap.api.deserializer.*; +import com.github.prominence.openweathermap.api.deserializer.forecast.free.FreeForecastLocationDeserializer; +import com.github.prominence.openweathermap.api.deserializer.forecast.free.FreeForecastRainDeserializer; +import com.github.prominence.openweathermap.api.deserializer.forecast.free.FreeForecastSnowDeserializer; import com.github.prominence.openweathermap.api.enums.UnitSystem; import com.github.prominence.openweathermap.api.model.*; import com.github.prominence.openweathermap.api.model.forecast.free.*; import java.io.IOException; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; -import java.util.TimeZone; /** * Official API response documentation: https://openweathermap.org/forecast5#JSON. */ -public class FiveDayThreeHourStepForecastResponseMapper { - private final ObjectMapper objectMapper = new ObjectMapper(); - +public class FiveDayThreeHourStepForecastResponseMapper extends AbstractMapper { /** * Instantiates a new forecast response mapper. * @@ -59,7 +52,12 @@ public class FiveDayThreeHourStepForecastResponseMapper { module.addDeserializer(WeatherState.class, new WeatherStateDeserializer()); module.addDeserializer(Temperature.class, new TemperatureDeserializer()); module.addDeserializer(AtmosphericPressure.class, new AtmosphericPressureDeserializer()); + module.addDeserializer(Humidity.class, new HumidityDeserializer()); + module.addDeserializer(Clouds.class, new CloudsDeserializer()); module.addDeserializer(Wind.class, new WindDeserializer()); + module.addDeserializer(Rain.class, new FreeForecastRainDeserializer()); + module.addDeserializer(Snow.class, new FreeForecastSnowDeserializer()); + module.addDeserializer(Location.class, new FreeForecastLocationDeserializer()); objectMapper.registerModule(module); } @@ -83,13 +81,13 @@ public class FiveDayThreeHourStepForecastResponseMapper { return forecast; } - private Forecast mapToForecast(JsonNode root) throws IOException { + private Forecast mapToForecast(JsonNode rootNode) throws IOException { final Forecast forecast = new Forecast(); - forecast.setLocation(parseLocation(root.get("city"))); + forecast.setLocation(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("city")), Location.class)); - final List forecasts = new ArrayList<>(root.get("cnt").asInt()); + final List forecasts = new ArrayList<>(rootNode.get("cnt").asInt()); - final JsonNode forecastListNode = root.get("list"); + final JsonNode forecastListNode = rootNode.get("list"); for (JsonNode forecastNode : forecastListNode) { forecasts.add(parseWeatherForecast(forecastNode)); } @@ -102,128 +100,38 @@ public class FiveDayThreeHourStepForecastResponseMapper { private WeatherForecast parseWeatherForecast(JsonNode rootNode) throws IOException { final WeatherForecast weatherForecast = new WeatherForecast(); final JsonNode weatherArrayNode = rootNode.get("weather"); - if (weatherArrayNode != null) { - final JsonNode weatherNode = weatherArrayNode.get(0); - weatherForecast.setWeatherState(parseWeatherState(weatherNode)); - } + weatherForecast.setWeatherStates(parseWeatherStates(weatherArrayNode)); final JsonNode mainNode = rootNode.get("main"); - weatherForecast.setTemperature(parseTemperature(mainNode)); - weatherForecast.setAtmosphericPressure(parsePressure(mainNode)); - weatherForecast.setHumidity(parseHumidity(mainNode)); - weatherForecast.setClouds(parseClouds(rootNode)); - weatherForecast.setWind(parseWind(rootNode)); - weatherForecast.setRain(parseRain(rootNode)); - weatherForecast.setSnow(parseSnow(rootNode)); + weatherForecast.setTemperature(objectMapper.readValue(objectMapper.treeAsTokens(mainNode), Temperature.class)); + weatherForecast.setAtmosphericPressure(objectMapper.readValue(objectMapper.treeAsTokens(mainNode), AtmosphericPressure.class)); + weatherForecast.setHumidity(objectMapper.readValue(objectMapper.treeAsTokens(mainNode), Humidity.class)); + weatherForecast.setClouds(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("clouds")), Clouds.class)); + weatherForecast.setWind(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("wind")), Wind.class)); + if (rootNode.has("rain")) { + weatherForecast.setRain(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("rain")), Rain.class)); + } + if (rootNode.has("snow")) { + weatherForecast.setSnow(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("snow")), Snow.class)); + } final JsonNode sysNode = rootNode.get("sys"); if (sysNode != null) { weatherForecast.setDayTime("d".equals(sysNode.get("pod").asText()) ? DayTime.DAY : DayTime.NIGHT); } - weatherForecast.setForecastTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(rootNode.get("dt").asLong()), TimeZone.getDefault().toZoneId())); + final JsonNode visibilityNode = rootNode.get("visibility"); + if (visibilityNode != null) { + weatherForecast.setVisibilityInMetres(visibilityNode.asDouble()); + } + final JsonNode popNode = rootNode.get("pop"); + if (popNode != null) { + weatherForecast.setProbabilityOfPrecipitation(popNode.asDouble()); + } + + weatherForecast.setForecastTime(parseDateTime(rootNode.get("dt"))); weatherForecast.setForecastTimeISO(rootNode.get("dt_txt").asText()); return weatherForecast; } - - private WeatherState parseWeatherState(JsonNode weatherNode) throws IOException { - if (weatherNode == null) { - return null; - } - return objectMapper.readValue(objectMapper.treeAsTokens(weatherNode), WeatherState.class); - } - - private Temperature parseTemperature(JsonNode rootNode) throws IOException { - return objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Temperature.class); - } - - private AtmosphericPressure parsePressure(JsonNode rootNode) throws IOException { - return objectMapper.readValue(objectMapper.treeAsTokens(rootNode), AtmosphericPressure.class); - } - - private Humidity parseHumidity(JsonNode rootNode) { - return Humidity.withValue((byte) (rootNode.get("humidity").asInt())); - } - - private Wind parseWind(JsonNode root) throws IOException { - final JsonNode windNode = root.get("wind"); - return objectMapper.readValue(objectMapper.treeAsTokens(windNode), Wind.class); - } - - private Rain parseRain(JsonNode root) { - final JsonNode rainNode = root.get("rain"); - if (rainNode != null) { - final JsonNode threeHourNode = rainNode.get("3h"); - if (threeHourNode != null) { - return Rain.withThreeHourLevelValue(threeHourNode.asDouble()); - } - } - return null; - } - - private Snow parseSnow(JsonNode root) { - final JsonNode snowNode = root.get("snow"); - if (snowNode != null) { - final JsonNode threeHourNode = snowNode.get("3h"); - if (threeHourNode != null) { - return Snow.withThreeHourLevelValue(threeHourNode.asDouble()); - } - } - return null; - } - - private Clouds parseClouds(JsonNode rootNode) { - final JsonNode cloudsNode = rootNode.get("clouds"); - final JsonNode allValueNode = cloudsNode.get("all"); - if (allValueNode != null) { - return Clouds.withValue((byte) allValueNode.asInt()); - } - - return null; - } - - private Location parseLocation(JsonNode rootNode) { - final Location location = Location.withValues(rootNode.get("id").asInt(), rootNode.get("name").asText()); - - final JsonNode timezoneNode = rootNode.get("timezone"); - if (timezoneNode != null) { - location.setZoneOffset(ZoneOffset.ofTotalSeconds(timezoneNode.asInt())); - } - - final JsonNode countryNode = rootNode.get("country"); - if (countryNode != null) { - location.setCountryCode(countryNode.asText()); - } - - final JsonNode sunriseNode = rootNode.get("sunrise"); - final JsonNode sunsetNode = rootNode.get("sunset"); - if (sunriseNode != null) { - location.setSunriseTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(sunriseNode.asLong()), TimeZone.getDefault().toZoneId())); - } - if (sunsetNode != null) { - location.setSunsetTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(sunsetNode.asLong()), TimeZone.getDefault().toZoneId())); - } - - final JsonNode coordNode = rootNode.get("coord"); - if (coordNode != null) { - location.setCoordinates(parseCoordinate(coordNode)); - } - - final JsonNode populationNode = rootNode.get("population"); - if (populationNode != null) { - location.setPopulation(populationNode.asLong()); - } - - return location; - } - - private Coordinates parseCoordinate(JsonNode rootNode) { - final JsonNode latitudeNode = rootNode.get("lat"); - final JsonNode longitudeNode = rootNode.get("lon"); - if (latitudeNode != null && longitudeNode != null) { - return Coordinates.of(latitudeNode.asDouble(), longitudeNode.asDouble()); - } - return null; - } } diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java index 0b64cde..71ca3bb 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java @@ -25,6 +25,7 @@ package com.github.prominence.openweathermap.api.model.forecast.free; import com.github.prominence.openweathermap.api.model.*; import java.time.LocalDateTime; +import java.util.List; import java.util.Objects; /** @@ -33,7 +34,7 @@ import java.util.Objects; public class WeatherForecast { private LocalDateTime forecastTime; - private WeatherState weatherState; + private List weatherStates; private Temperature temperature; private AtmosphericPressure atmosphericPressure; private Humidity humidity; @@ -46,6 +47,9 @@ public class WeatherForecast { private String forecastTimeISO; private DayTime dayTime; + private Double visibilityInMetres; + private Double probabilityOfPrecipitation; + /** * Gets forecast time. * @@ -69,17 +73,17 @@ public class WeatherForecast { * * @return the weather state */ - public WeatherState getWeatherState() { - return weatherState; + public List getWeatherStates() { + return weatherStates; } /** * Sets weather state. * - * @param weatherState the weather state + * @param weatherStates the weather state */ - public void setWeatherState(WeatherState weatherState) { - this.weatherState = weatherState; + public void setWeatherStates(List weatherStates) { + this.weatherStates = weatherStates; } /** @@ -244,13 +248,29 @@ public class WeatherForecast { this.dayTime = dayTime; } + public Double getVisibilityInMetres() { + return visibilityInMetres; + } + + public void setVisibilityInMetres(Double visibilityInMetres) { + this.visibilityInMetres = visibilityInMetres; + } + + public Double getProbabilityOfPrecipitation() { + return probabilityOfPrecipitation; + } + + public void setProbabilityOfPrecipitation(Double probabilityOfPrecipitation) { + this.probabilityOfPrecipitation = probabilityOfPrecipitation; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; WeatherForecast that = (WeatherForecast) o; return Objects.equals(forecastTime, that.forecastTime) && - Objects.equals(weatherState, that.weatherState) && + Objects.equals(weatherStates, that.weatherStates) && Objects.equals(temperature, that.temperature) && Objects.equals(atmosphericPressure, that.atmosphericPressure) && Objects.equals(humidity, that.humidity) && @@ -259,12 +279,14 @@ public class WeatherForecast { Objects.equals(snow, that.snow) && Objects.equals(clouds, that.clouds) && Objects.equals(forecastTimeISO, that.forecastTimeISO) && + Objects.equals(visibilityInMetres, that.visibilityInMetres) && + Objects.equals(probabilityOfPrecipitation, that.probabilityOfPrecipitation) && dayTime == that.dayTime; } @Override public int hashCode() { - return Objects.hash(forecastTime, weatherState, temperature, atmosphericPressure, humidity, wind, rain, snow, clouds, forecastTimeISO, dayTime); + return Objects.hash(forecastTime, weatherStates, temperature, atmosphericPressure, humidity, wind, rain, snow, clouds, forecastTimeISO, visibilityInMetres, probabilityOfPrecipitation, dayTime); } @Override @@ -272,9 +294,9 @@ public class WeatherForecast { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Timestamp: "); stringBuilder.append(forecastTimeISO); - if (weatherState != null) { + if (weatherStates != null && weatherStates.size() > 0) { stringBuilder.append(", Weather: "); - stringBuilder.append(weatherState.getDescription()); + stringBuilder.append(weatherStates.get(0).getDescription()); } if (temperature != null) { stringBuilder.append(", "); diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastRequester.java index f65a15f..b87df1d 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastRequester.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastRequester.java @@ -46,16 +46,19 @@ public class FiveDayThreeHourStepForecastRequester { return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); } + @Deprecated public FiveDayThreeHourStepForecastRequestCustomizer byCityName(String cityName, String stateCode) { requestSettings.putRequestParameter("q", cityName + "," + stateCode); return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); } + @Deprecated public FiveDayThreeHourStepForecastRequestCustomizer byCityName(String cityName, String stateCode, String countryCode) { requestSettings.putRequestParameter("q", cityName + "," + stateCode + "," + countryCode); return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); } + @Deprecated public FiveDayThreeHourStepForecastRequestCustomizer byCityId(long cityId) { requestSettings.putRequestParameter("id", Long.toString(cityId)); return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); @@ -67,11 +70,13 @@ public class FiveDayThreeHourStepForecastRequester { return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); } + @Deprecated public FiveDayThreeHourStepForecastRequestCustomizer byZipCodeAndCountry(String zipCode, String countryCode) { requestSettings.putRequestParameter("zip", zipCode + "," + countryCode); return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); } + @Deprecated public FiveDayThreeHourStepForecastRequestCustomizer byZipCodeInUSA(String zipCode) { requestSettings.putRequestParameter("zip", zipCode); return new FiveDayThreeHourStepForecastRequestCustomizer(requestSettings); diff --git a/src/test/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapperTest.java b/src/test/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapperTest.java new file mode 100644 index 0000000..8869056 --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/mapper/FiveDayThreeHourStepForecastResponseMapperTest.java @@ -0,0 +1,183 @@ +package com.github.prominence.openweathermap.api.mapper; + +import com.github.prominence.openweathermap.api.enums.UnitSystem; +import com.github.prominence.openweathermap.api.model.*; +import com.github.prominence.openweathermap.api.model.forecast.free.*; +import com.github.prominence.openweathermap.api.utils.TestMappingUtils; +import org.junit.jupiter.api.Test; + +import java.time.ZoneOffset; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class FiveDayThreeHourStepForecastResponseMapperTest { + + @Test + void mapToForecast() { + final String jsonResponse = """ + { + "cod": "200", + "message": 0, + "cnt": 40, + "list": [ + { + "dt": 1647345600, + "main": { + "temp": 286.88, + "feels_like": 285.93, + "temp_min": 286.74, + "temp_max": 286.88, + "pressure": 1021, + "sea_level": 1021, + "grnd_level": 1018, + "humidity": 62, + "temp_kf": 0.14 + }, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "clouds": { + "all": 85 + }, + "wind": { + "speed": 3.25, + "deg": 134, + "gust": 4.45 + }, + "visibility": 10000, + "pop": 0, + "sys": { + "pod": "d" + }, + "rain": { + "3h": 22.1 + }, + "snow": { + "3h": 13.6 + }, + "dt_txt": "2022-03-15 12:00:00" + }, + { + "dt": 1647356400, + "main": { + "temp": 286.71, + "feels_like": 285.77, + "temp_min": 286.38, + "temp_max": 286.71, + "pressure": 1021, + "sea_level": 1021, + "grnd_level": 1017, + "humidity": 63, + "temp_kf": 0.33 + }, + "weather": [ + { + "id": 804, + "main": "Clouds", + "description": "overcast clouds", + "icon": "04d" + } + ], + "clouds": { + "all": 90 + }, + "wind": { + "speed": 3.34, + "deg": 172, + "gust": 4.03 + }, + "visibility": 10000, + "pop": 0, + "sys": { + "pod": "d" + }, + "dt_txt": "2022-03-15 15:00:00" + } + ], + "city": { + "id": 2643743, + "name": "London", + "coord": { + "lat": 51.5073, + "lon": -0.1277 + }, + "country": "GB", + "population": 1000000, + "timezone": 0, + "sunrise": 1647324903, + "sunset": 1647367441 + } + } + """; + + final Forecast forecast = new FiveDayThreeHourStepForecastResponseMapper(UnitSystem.METRIC).mapToForecast(jsonResponse); + assertNotNull(forecast); + + final Location location = forecast.getLocation(); + assertNotNull(location); + assertEquals(Coordinates.of(51.5073, -0.1277), location.getCoordinates()); + assertEquals(2643743, location.getId()); + assertEquals("London", location.getName()); + assertEquals("GB", location.getCountryCode()); + assertEquals(1000000, location.getPopulation()); + assertEquals(TestMappingUtils.parseDateTime(1647324903), location.getSunriseTime()); + assertEquals(TestMappingUtils.parseDateTime(1647367441), location.getSunsetTime()); + assertEquals(ZoneOffset.ofTotalSeconds(0), location.getZoneOffset()); + + + final List weatherForecastList = forecast.getWeatherForecasts(); + assertEquals(2, weatherForecastList.size()); + + final WeatherForecast weatherForecast = weatherForecastList.get(0); + assertNotNull(weatherForecast); + + assertEquals(TestMappingUtils.parseDateTime(1647345600), weatherForecast.getForecastTime()); + assertEquals(10000, weatherForecast.getVisibilityInMetres()); + assertEquals(0, weatherForecast.getProbabilityOfPrecipitation()); + assertEquals("2022-03-15 12:00:00", weatherForecast.getForecastTimeISO()); + assertEquals(DayTime.DAY, weatherForecast.getDayTime()); + + final Temperature temperature = weatherForecast.getTemperature(); + assertNotNull(temperature); + assertEquals(286.88, temperature.getValue()); + assertEquals(285.93, temperature.getFeelsLike()); + assertEquals(286.74, temperature.getMinTemperature()); + assertEquals(286.88, temperature.getMaxTemperature()); + + final AtmosphericPressure pressure = weatherForecast.getAtmosphericPressure(); + assertEquals(1021, pressure.getValue()); + assertEquals(1021, pressure.getSeaLevelValue()); + assertEquals(1018, pressure.getGroundLevelValue()); + + final Humidity humidity = weatherForecast.getHumidity(); + assertEquals(62, humidity.getValue()); + + final Clouds clouds = weatherForecast.getClouds(); + assertEquals(85, clouds.getValue()); + + final Wind wind = weatherForecast.getWind(); + assertEquals(3.25, wind.getSpeed()); + assertEquals(134, wind.getDegrees()); + assertEquals(4.45, wind.getGust()); + + assertEquals(1, weatherForecast.getWeatherStates().size()); + + final WeatherState weatherState = weatherForecast.getWeatherStates().get(0); + assertEquals(804, weatherState.getId()); + assertEquals("Clouds", weatherState.getName()); + assertEquals("overcast clouds", weatherState.getDescription()); + assertEquals("04d", weatherState.getIconId()); + + final Rain rain = weatherForecast.getRain(); + assertEquals(22.1, rain.getThreeHourLevel()); + + final Snow snow = weatherForecast.getSnow(); + assertEquals(13.6, snow.getThreeHourLevel()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecastUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecastUnitTest.java index 7e32251..e40946e 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecastUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecastUnitTest.java @@ -26,6 +26,7 @@ import com.github.prominence.openweathermap.api.model.*; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -138,7 +139,7 @@ public class WeatherForecastUnitTest { assertNotEquals("", weatherForecast.toString()); assertNotNull(weatherForecast.toString()); - weatherForecast.setWeatherState(weatherState); + weatherForecast.setWeatherStates(List.of(weatherState)); assertNotEquals("", weatherForecast.toString()); assertNotNull(weatherForecast.toString()); @@ -224,11 +225,11 @@ public class WeatherForecastUnitTest { assertEquals(first, second); final WeatherState weatherState = new WeatherState(800, "Clear", "clear sky"); - first.setWeatherState(weatherState); + first.setWeatherStates(List.of(weatherState)); assertNotEquals(first, second); - second.setWeatherState(weatherState); + second.setWeatherStates(List.of(weatherState)); assertEquals(first, second); diff --git a/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastIntegrationTest.java b/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastIntegrationTest.java index 51887ae..6f97eb4 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastIntegrationTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastIntegrationTest.java @@ -54,7 +54,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); @@ -106,7 +106,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); @@ -158,7 +158,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); @@ -209,7 +209,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); @@ -260,7 +260,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); @@ -311,7 +311,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); @@ -362,7 +362,7 @@ public class FiveDayThreeHourStepForecastIntegrationTest extends ApiTest { assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); for (WeatherForecast weatherForecast : forecast.getWeatherForecasts()) { - assertNotNull(weatherForecast.getWeatherState()); + assertNotNull(weatherForecast.getWeatherStates()); assertNotNull(weatherForecast.getForecastTime()); assertNotNull(weatherForecast.getTemperature()); assertNotNull(weatherForecast.getAtmosphericPressure()); diff --git a/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastResponseMapperUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastResponseMapperUnitTest.java index 61f2fe5..0f1fb6f 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastResponseMapperUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/request/forecast/free/FiveDayThreeHourStepForecastResponseMapperUnitTest.java @@ -56,7 +56,7 @@ public class FiveDayThreeHourStepForecastResponseMapperUnitTest { assertNotNull(forecast); assertNotNull(forecast.getLocation()); assertNotNull(forecast.getWeatherForecasts()); - forecast.getWeatherForecasts().forEach(weatherForecast -> assertNull(weatherForecast.getWeatherState())); + forecast.getWeatherForecasts().forEach(weatherForecast -> assertNull(weatherForecast.getWeatherStates())); } @Test