Finish update, working
This commit is contained in:
parent
dafb1d770a
commit
1895673c50
13 changed files with 289 additions and 592 deletions
|
|
@ -1,11 +1,9 @@
|
||||||
package eu.m724.wtapi.provider.weather;
|
package eu.m724.wtapi.provider.weather;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
import eu.m724.wtapi.object.Quota;
|
import eu.m724.wtapi.object.Quota;
|
||||||
import eu.m724.wtapi.object.Weather;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderException;
|
import eu.m724.wtapi.provider.exception.ProviderException;
|
||||||
|
|
||||||
public abstract class WeatherProvider {
|
public abstract class WeatherProvider {
|
||||||
|
|
@ -20,9 +18,9 @@ public abstract class WeatherProvider {
|
||||||
* Get the current weather of a single or multiple points
|
* Get the current weather of a single or multiple points
|
||||||
*
|
*
|
||||||
* @param coordinates The coordinates
|
* @param coordinates The coordinates
|
||||||
* @return A future that CAN throw {@link ProviderException}
|
* @return A {@link WeatherQueryResult}
|
||||||
*/
|
*/
|
||||||
public abstract CompletableFuture<Weather[]> getWeather(Coordinates... coordinates);
|
public abstract CompletableFuture<WeatherQueryResult> getWeather(Coordinates... coordinates);
|
||||||
|
|
||||||
public abstract Quota getQuota();
|
public abstract Quota getQuota();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
package eu.m724.wtapi.provider.weather;
|
||||||
|
|
||||||
|
import eu.m724.wtapi.object.Weather;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A result from a weather query.
|
||||||
|
*
|
||||||
|
* @param weathers The weathers, null if exception
|
||||||
|
* @param exception The exception, null if no exception
|
||||||
|
*/
|
||||||
|
public record WeatherQueryResult(
|
||||||
|
Weather[] weathers,
|
||||||
|
Throwable exception
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
package eu.m724.wtapi.provider.weather.impl.openmeteo;
|
package eu.m724.wtapi.provider.weather.impl.openmeteo;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.*;
|
||||||
import com.google.gson.JsonParseException;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
import eu.m724.wtapi.object.Quota;
|
import eu.m724.wtapi.object.Quota;
|
||||||
import eu.m724.wtapi.object.Weather;
|
import eu.m724.wtapi.object.Weather;
|
||||||
|
|
@ -11,24 +8,39 @@ import eu.m724.wtapi.provider.exception.ProviderException;
|
||||||
import eu.m724.wtapi.provider.exception.QuotaExceededException;
|
import eu.m724.wtapi.provider.exception.QuotaExceededException;
|
||||||
import eu.m724.wtapi.provider.exception.ProviderRequestException;
|
import eu.m724.wtapi.provider.exception.ProviderRequestException;
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
|
||||||
|
|
||||||
import java.net.ProxySelector;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
import java.net.http.HttpResponse;
|
import java.net.http.HttpResponse;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class OpenMeteoProvider extends WeatherProvider {
|
public class OpenMeteoProvider extends WeatherProvider {
|
||||||
|
private final HttpClient httpClient = HttpClient.newBuilder().build();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() throws ProviderException {
|
public void init() throws ProviderException {
|
||||||
|
// TODO connectivity check
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Weather[]> getWeather(Coordinates... coordinates) {
|
public CompletableFuture<WeatherQueryResult> getWeather(Coordinates... coordinates) {
|
||||||
|
if (coordinates == null) {
|
||||||
|
throw new NullPointerException("Coordinates are null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coordinates.length == 0) {
|
||||||
|
throw new IllegalArgumentException("coordinates is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Arrays.stream(coordinates).anyMatch(Objects::isNull)) {
|
||||||
|
throw new IllegalArgumentException("One or more coordinates are null");
|
||||||
|
}
|
||||||
|
|
||||||
String latitudes = Arrays.stream(coordinates)
|
String latitudes = Arrays.stream(coordinates)
|
||||||
.map(Coordinates::latitude)
|
.map(Coordinates::latitude)
|
||||||
.map(Number::toString)
|
.map(Number::toString)
|
||||||
|
|
@ -42,48 +54,59 @@ public class OpenMeteoProvider extends WeatherProvider {
|
||||||
String url = "https://api.open-meteo.com/v1/forecast?latitude=%s&longitude=%s¤t=temperature_2m,relative_humidity_2m,rain,snowfall,apparent_temperature,cloud_cover,weather_code&timeformat=unixtime&wind_speed_unit=ms"
|
String url = "https://api.open-meteo.com/v1/forecast?latitude=%s&longitude=%s¤t=temperature_2m,relative_humidity_2m,rain,snowfall,apparent_temperature,cloud_cover,weather_code&timeformat=unixtime&wind_speed_unit=ms"
|
||||||
.formatted(latitudes, longitudes);
|
.formatted(latitudes, longitudes);
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request =
|
||||||
.uri(URI.create(url))
|
HttpRequest.newBuilder(URI.create(url)).build();
|
||||||
.build();
|
|
||||||
|
|
||||||
CompletableFuture<HttpResponse<String>> responseFuture =
|
CompletableFuture<HttpResponse<String>> responseFuture =
|
||||||
HttpClient.newBuilder().build()
|
httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString());
|
||||||
.sendAsync(request, HttpResponse.BodyHandlers.ofString());
|
|
||||||
|
|
||||||
return responseFuture.thenApply((response) -> {
|
return responseFuture
|
||||||
int code = response.statusCode();
|
.thenApply(this::processResponse)
|
||||||
|
.handle(WeatherQueryResult::new);
|
||||||
JsonObject jsonResponse;
|
|
||||||
try {
|
|
||||||
jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
|
|
||||||
} catch (JsonParseException e) {
|
|
||||||
throw new ProviderRequestException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code == 429) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
int retryIn = -1;
|
|
||||||
|
|
||||||
String reason = jsonResponse.get("reason").getAsString();
|
|
||||||
|
|
||||||
if (reason.contains("Minutely")) {
|
|
||||||
retryIn = (int) (60 - (now % 60));
|
|
||||||
} else if (reason.contains("Daily")) {
|
|
||||||
retryIn = (int) (3600 - (now % 3600));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new QuotaExceededException(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (code != 200)
|
|
||||||
throw new ProviderRequestException("Status code: " + code);
|
|
||||||
|
|
||||||
return OpenMeteoResponseConverter.convert(jsonResponse);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Quota getQuota() {
|
public Quota getQuota() {
|
||||||
return Quota.daily(10000);
|
return Quota.daily(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Weather[] processResponse(HttpResponse<String> response) throws ProviderException {
|
||||||
|
int code = response.statusCode();
|
||||||
|
|
||||||
|
JsonElement jsonResponse;
|
||||||
|
try {
|
||||||
|
jsonResponse = JsonParser.parseString(response.body());
|
||||||
|
} catch (JsonParseException e) {
|
||||||
|
throw new ProviderRequestException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code == 429) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
int retryIn = -1;
|
||||||
|
|
||||||
|
if (jsonResponse.isJsonObject() && jsonResponse.getAsJsonObject().has("reason")) {
|
||||||
|
String reason = jsonResponse.getAsJsonObject().get("reason").getAsString();
|
||||||
|
|
||||||
|
if (reason.contains("Minutely")) {
|
||||||
|
retryIn = (int) (60 - (now % 60));
|
||||||
|
} else if (reason.contains("Daily")) {
|
||||||
|
retryIn = (int) (3600 - (now % 3600));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new QuotaExceededException(retryIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code != 200) {
|
||||||
|
if (jsonResponse.isJsonObject() && jsonResponse.getAsJsonObject().has("reason")) {
|
||||||
|
String reason = jsonResponse.getAsJsonObject().get("reason").getAsString();
|
||||||
|
|
||||||
|
throw new ProviderRequestException("Error reason: " + reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ProviderRequestException("Status code: " + code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenMeteoResponseConverter.convert(jsonResponse);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ public class OpenMeteoResponseConverter {
|
||||||
boolean thundering = weatherCode >= 90 && weatherCode < 100;
|
boolean thundering = weatherCode >= 90 && weatherCode < 100;
|
||||||
boolean snowing = current.get("snowfall").getAsDouble() > 0;
|
boolean snowing = current.get("snowfall").getAsDouble() > 0;
|
||||||
|
|
||||||
float temperatureCelsius = current.get("temperature").getAsFloat();
|
float temperatureCelsius = current.get("temperature_2m").getAsFloat();
|
||||||
float temperatureApparentCelsius = current.get("apparent_temperature").getAsFloat();
|
float temperatureApparentCelsius = current.get("apparent_temperature").getAsFloat();
|
||||||
|
|
||||||
float relativeHumidityPercentage = current.get("relative_humidity_2m").getAsInt() / 100f;
|
float relativeHumidityPercentage = current.get("relative_humidity_2m").getAsInt() / 100f;
|
||||||
|
|
|
||||||
|
|
@ -1,235 +0,0 @@
|
||||||
package eu.m724.wtapi.provider.weather.impl.openweathermap;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonPrimitive;
|
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
|
||||||
import eu.m724.wtapi.object.Severity;
|
|
||||||
import eu.m724.wtapi.object.Weather;
|
|
||||||
|
|
||||||
public class OWMResponseConverter {
|
|
||||||
static Weather convert(JsonObject json) {
|
|
||||||
Coordinates coordinates = new Coordinates(
|
|
||||||
json.getAsJsonObject("coord").getAsJsonPrimitive("lon").getAsDouble(),
|
|
||||||
json.getAsJsonObject("coord").getAsJsonPrimitive("lat").getAsDouble()
|
|
||||||
);
|
|
||||||
Weather weather = new Weather();
|
|
||||||
|
|
||||||
int conditionCode = json.getAsJsonArray("weather")
|
|
||||||
.get(0).getAsJsonObject()
|
|
||||||
.getAsJsonPrimitive("id").getAsInt();
|
|
||||||
|
|
||||||
switch (conditionCode) {
|
|
||||||
// Group 2xx: Thunderstorm
|
|
||||||
case 200: // thunderstorm with light rain
|
|
||||||
weather.thunderstormSeverity = Severity.LIGHT;
|
|
||||||
weather.rainSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 201: // thunderstorm with rain
|
|
||||||
weather.thunderstormSeverity = Severity.MODERATE;
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 202: // thunderstorm with heavy rain
|
|
||||||
weather.thunderstormSeverity = Severity.HEAVY;
|
|
||||||
weather.rainSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 210: // light thunderstorm
|
|
||||||
weather.thunderstormSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 211: // thunderstorm
|
|
||||||
weather.thunderstormSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 212: // heavy thunderstorm
|
|
||||||
weather.thunderstormSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 221: // ragged thunderstorm
|
|
||||||
weather.thunderstormSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 230: // thunderstorm with light drizzle
|
|
||||||
weather.thunderstormSeverity = Severity.LIGHT;
|
|
||||||
weather.drizzleSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 231: // thunderstorm with drizzle
|
|
||||||
weather.thunderstormSeverity = Severity.MODERATE;
|
|
||||||
weather.drizzleSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 232: // thunderstorm with heavy drizzle
|
|
||||||
weather.thunderstormSeverity = Severity.HEAVY;
|
|
||||||
weather.drizzleSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Group 3xx: Drizzle
|
|
||||||
case 300: // light intensity drizzle
|
|
||||||
weather.drizzleSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 301: // drizzle
|
|
||||||
weather.drizzleSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 302: // heavy intensity drizzle
|
|
||||||
weather.drizzleSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 310: // light intensity drizzle rain
|
|
||||||
weather.drizzleSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 311: // drizzle rain
|
|
||||||
weather.drizzleSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 312: // heavy intensity drizzle rain
|
|
||||||
weather.drizzleSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 313: // shower rain and drizzle
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
weather.drizzleSeverity = Severity.MODERATE;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 314: // heavy shower rain and drizzle
|
|
||||||
weather.rainSeverity = Severity.HEAVY;
|
|
||||||
weather.drizzleSeverity = Severity.HEAVY;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 321: // shower drizzle
|
|
||||||
weather.drizzleSeverity = Severity.MODERATE;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Group 5xx: Rain
|
|
||||||
case 500: // light rain
|
|
||||||
weather.rainSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 501: // moderate rain
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 502: // heavy intensity rain
|
|
||||||
weather.rainSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 503: // very heavy rain
|
|
||||||
weather.rainSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 504: // extreme rain
|
|
||||||
weather.rainSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 511: // freezing rain
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 520: // light intensity shower rain
|
|
||||||
weather.rainSeverity = Severity.LIGHT;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 521: // shower rain
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 522: // heavy intensity shower rain
|
|
||||||
weather.rainSeverity = Severity.HEAVY;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 531: // ragged shower rain
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Group 6xx: Snow
|
|
||||||
case 600: // light snow
|
|
||||||
weather.snowSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 601: // snow
|
|
||||||
weather.snowSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 602: // heavy snow
|
|
||||||
weather.snowSeverity = Severity.HEAVY;
|
|
||||||
break;
|
|
||||||
case 611: // sleet
|
|
||||||
weather.sleetSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 612: // light shower sleet
|
|
||||||
weather.sleetSeverity = Severity.LIGHT;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 613: // shower sleet
|
|
||||||
weather.sleetSeverity = Severity.MODERATE;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 615: // light rain and snow
|
|
||||||
weather.rainSeverity = Severity.LIGHT;
|
|
||||||
weather.snowSeverity = Severity.LIGHT;
|
|
||||||
break;
|
|
||||||
case 616: // rain and snow
|
|
||||||
weather.rainSeverity = Severity.MODERATE;
|
|
||||||
weather.snowSeverity = Severity.MODERATE;
|
|
||||||
break;
|
|
||||||
case 620: // light shower snow
|
|
||||||
weather.snowSeverity = Severity.LIGHT;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 621: // shower snow
|
|
||||||
weather.snowSeverity = Severity.MODERATE;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
case 622: // heavy shower snow
|
|
||||||
weather.snowSeverity = Severity.HEAVY;
|
|
||||||
weather.shower = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String city = json.getAsJsonPrimitive("name").getAsString();
|
|
||||||
if (city.equals("Globe") || city.equals("")) city = null;
|
|
||||||
JsonPrimitive countryJson = json.getAsJsonObject("sys").getAsJsonPrimitive("country");
|
|
||||||
String country = countryJson != null ? countryJson.getAsString() : null;
|
|
||||||
|
|
||||||
weather.coordinates =
|
|
||||||
new Coordinates(
|
|
||||||
json.getAsJsonObject("coord").getAsJsonPrimitive("lon").getAsDouble(),
|
|
||||||
json.getAsJsonObject("coord").getAsJsonPrimitive("lat").getAsDouble()
|
|
||||||
).setAddress(country, city);
|
|
||||||
|
|
||||||
weather.temperatureCelsius = json
|
|
||||||
.getAsJsonObject("main")
|
|
||||||
.getAsJsonPrimitive("temp")
|
|
||||||
.getAsFloat();
|
|
||||||
|
|
||||||
weather.temperatureApparentCelsius = json
|
|
||||||
.getAsJsonObject("main")
|
|
||||||
.getAsJsonPrimitive("feels_like")
|
|
||||||
.getAsFloat();
|
|
||||||
|
|
||||||
weather.windSpeed = json
|
|
||||||
.getAsJsonObject("wind")
|
|
||||||
.getAsJsonPrimitive("speed")
|
|
||||||
.getAsFloat();
|
|
||||||
|
|
||||||
JsonPrimitive pri = json
|
|
||||||
.getAsJsonObject("wind")
|
|
||||||
.getAsJsonPrimitive("gust");
|
|
||||||
|
|
||||||
if (pri != null)
|
|
||||||
weather.windSpeed = pri.getAsFloat();
|
|
||||||
|
|
||||||
weather.relativeHumidity = json
|
|
||||||
.getAsJsonObject("main")
|
|
||||||
.getAsJsonPrimitive("relativeHumidityPercentage")
|
|
||||||
.getAsInt() / 100f;
|
|
||||||
|
|
||||||
weather.cloudCover = json
|
|
||||||
.getAsJsonObject("clouds")
|
|
||||||
.getAsJsonPrimitive("all")
|
|
||||||
.getAsInt() / 100f;
|
|
||||||
|
|
||||||
weather.sunrise = json
|
|
||||||
.getAsJsonObject("sys")
|
|
||||||
.getAsJsonPrimitive("sunrise")
|
|
||||||
.getAsLong();
|
|
||||||
|
|
||||||
weather.sunset = json
|
|
||||||
.getAsJsonObject("sys")
|
|
||||||
.getAsJsonPrimitive("sunrise")
|
|
||||||
.getAsLong();
|
|
||||||
|
|
||||||
weather.description = json
|
|
||||||
.getAsJsonArray("weather")
|
|
||||||
.get(0).getAsJsonObject()
|
|
||||||
.getAsJsonPrimitive("description")
|
|
||||||
.getAsString();
|
|
||||||
|
|
||||||
return weather;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,108 +0,0 @@
|
||||||
package eu.m724.wtapi.provider.weather.impl.openweathermap;
|
|
||||||
|
|
||||||
import java.net.ProxySelector;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpClient;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.net.http.HttpResponse.BodyHandlers;
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
|
||||||
import eu.m724.wtapi.object.Weather;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderAuthorizationException;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderException;
|
|
||||||
import eu.m724.wtapi.provider.exception.QuotaExceededException;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderRequestException;
|
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
|
||||||
import eu.m724.wtapi.provider.weather.impl.openmeteo.OpenMeteoProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
* Development will focus on {@link OpenMeteoProvider} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public class OpenWeatherMapProvider extends WeatherProvider {
|
|
||||||
private String apiKey;
|
|
||||||
|
|
||||||
public OpenWeatherMapProvider(String apiKey) {
|
|
||||||
this.apiKey = apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() throws ProviderException {
|
|
||||||
CompletableFuture<Weather> weatherFuture =
|
|
||||||
getWeather(new Coordinates(0, 0));
|
|
||||||
|
|
||||||
weatherFuture.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
private CompletableFuture<Weather> getWeather(Coordinates coordinates) {
|
|
||||||
String url = "https://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%s"
|
|
||||||
.formatted(coordinates.latitude(), coordinates.longitude(), apiKey);
|
|
||||||
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(URI.create(url))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
CompletableFuture<HttpResponse<String>> responseFuture =
|
|
||||||
HttpClient.newBuilder()
|
|
||||||
.proxy(ProxySelector.getDefault()).build()
|
|
||||||
.sendAsync(request, BodyHandlers.ofString());
|
|
||||||
|
|
||||||
CompletableFuture<Weather> weatherFuture =
|
|
||||||
responseFuture.thenApply((response) -> {
|
|
||||||
int code = response.statusCode();
|
|
||||||
|
|
||||||
if (code == 401)
|
|
||||||
throw new ProviderAuthorizationException("Invalid api key");
|
|
||||||
if (code == 429)
|
|
||||||
throw new QuotaExceededException("Ratelimited", 1000);
|
|
||||||
if (code != 200)
|
|
||||||
throw new ProviderRequestException("Status code: " + code);
|
|
||||||
|
|
||||||
JsonObject jsonResponse =
|
|
||||||
JsonParser.parseString(response.body())
|
|
||||||
.getAsJsonObject();
|
|
||||||
|
|
||||||
return OWMResponseConverter.convert(jsonResponse);
|
|
||||||
});
|
|
||||||
|
|
||||||
return weatherFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<Weather[]> getWeather(Coordinates... coordinates) {
|
|
||||||
ArrayList<CompletableFuture<Weather>> weatherFutures = new ArrayList<>();
|
|
||||||
|
|
||||||
for (Coordinates coordinates : coordinatesArray) {
|
|
||||||
weatherFutures.add(getWeather(coordinates));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<Weather[]> weathersFuture =
|
|
||||||
CompletableFuture.allOf(
|
|
||||||
weatherFutures.toArray(new CompletableFuture[0]))
|
|
||||||
.thenApply(v ->
|
|
||||||
weatherFutures.stream()
|
|
||||||
.map(CompletableFuture::join)
|
|
||||||
.toArray(Weather[]::new));
|
|
||||||
|
|
||||||
return weathersFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getQuota() {
|
|
||||||
return Duration.ofHours(1370);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBulkLimit() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package eu.m724.wtapi.object;
|
package eu.m724.wtapi.object;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class CoordinateTest {
|
public class CoordinateTest {
|
||||||
|
|
@ -15,15 +13,7 @@ public class CoordinateTest {
|
||||||
assert coordinates.longitude() == -179.99;
|
assert coordinates.longitude() == -179.99;
|
||||||
|
|
||||||
coordinates = new Coordinates(-91.1, 180.1);
|
coordinates = new Coordinates(-91.1, 180.1);
|
||||||
System.out.printf("Precision loss expected: %f\n", coordinates.longitude());
|
System.out.printf("Precision loss expected: %f%n", coordinates.longitude());
|
||||||
assert coordinates.longitude() != -179.9; // TODO fix precision loss
|
assert coordinates.longitude() != -179.9; // TODO fix precision loss
|
||||||
|
|
||||||
assertNull(coordinates.city);
|
|
||||||
assertNull(coordinates.country);
|
|
||||||
|
|
||||||
coordinates.setAddress("SQ", "San Escobar");
|
|
||||||
|
|
||||||
assert coordinates.city.equals("San Escobar");
|
|
||||||
assert coordinates.country.equals("SQ");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ public class ApproximateTwilightTimeTest {
|
||||||
|
|
||||||
double solarAltitude = provider.calculateSolarAltitude(LocalDateTime.now(), new Coordinates(51, 0));
|
double solarAltitude = provider.calculateSolarAltitude(LocalDateTime.now(), new Coordinates(51, 0));
|
||||||
System.out.println(Math.toDegrees(solarAltitude));
|
System.out.println(Math.toDegrees(solarAltitude));
|
||||||
assert false;
|
|
||||||
|
|
||||||
// used https://gml.noaa.gov/grad/solcalc/index.html for reference values
|
// used https://gml.noaa.gov/grad/solcalc/index.html for reference values
|
||||||
testLocation(provider, 26, 6, 2023, 53.123394, 23.0864867, LocalDateTime.of(2023, 6, 26, 2, 2), LocalDateTime.of(2023, 6, 26, 18, 59), 0);
|
testLocation(provider, 26, 6, 2023, 53.123394, 23.0864867, LocalDateTime.of(2023, 6, 26, 2, 2), LocalDateTime.of(2023, 6, 26, 18, 59), 0);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,20 @@
|
||||||
package eu.m724.wtapi.weather;
|
package eu.m724.wtapi.weather;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
|
import eu.m724.wtapi.object.Quota;
|
||||||
import eu.m724.wtapi.object.Weather;
|
import eu.m724.wtapi.object.Weather;
|
||||||
import eu.m724.wtapi.provider.exception.ProviderException;
|
import eu.m724.wtapi.provider.exception.ProviderException;
|
||||||
import eu.m724.wtapi.provider.exception.QuotaExceededException;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderRequestException;
|
import eu.m724.wtapi.provider.exception.ProviderRequestException;
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
|
||||||
|
|
||||||
public class MockWeatherProvider extends WeatherProvider {
|
public class MockWeatherProvider extends WeatherProvider {
|
||||||
private boolean faulty;
|
private final boolean faulty;
|
||||||
|
|
||||||
public MockWeatherProvider(boolean faulty) {
|
public MockWeatherProvider(boolean faulty) {
|
||||||
this.faulty = faulty;
|
this.faulty = faulty;
|
||||||
|
|
@ -17,66 +22,51 @@ public class MockWeatherProvider extends WeatherProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() throws ProviderException {
|
public void init() throws ProviderException {
|
||||||
System.out.println("hello from mock provider");
|
System.out.println("Mock provider 'initialized'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Weather> getWeather(Coordinates coordinates) {
|
public CompletableFuture<WeatherQueryResult> getWeather(Coordinates... coordinates) {
|
||||||
if (coordinates == null)
|
if (coordinates == null) {
|
||||||
throw new NullPointerException("no coordinates passed");
|
throw new NullPointerException("Coordinates are null");
|
||||||
|
|
||||||
System.out.printf("mock getting weather for %f, %f\n",
|
|
||||||
coordinates.latitude(), coordinates.longitude());
|
|
||||||
|
|
||||||
CompletableFuture<Weather> completableFuture =
|
|
||||||
new CompletableFuture<>();
|
|
||||||
|
|
||||||
if (faulty)
|
|
||||||
completableFuture.completeExceptionally(new ProviderRequestException("Imagined server is down"));
|
|
||||||
else
|
|
||||||
completableFuture.complete(new Weather());
|
|
||||||
|
|
||||||
return completableFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompletableFuture<Weather[]> getWeatherBulk(Coordinates[] coordinatesArray) {
|
|
||||||
int len = coordinatesArray.length;
|
|
||||||
|
|
||||||
if (len == 0)
|
|
||||||
throw new IllegalArgumentException("no coordinates passed");
|
|
||||||
|
|
||||||
System.out.println("mock getting weather for multiple coords");
|
|
||||||
|
|
||||||
CompletableFuture<Weather[]> completableFuture =
|
|
||||||
new CompletableFuture<>();
|
|
||||||
|
|
||||||
|
|
||||||
Weather[] weathers = new Weather[len];
|
|
||||||
|
|
||||||
for (int i=0; i<len; i++) {
|
|
||||||
if (coordinatesArray[i] == null)
|
|
||||||
throw new IllegalArgumentException("A coordinate is null");
|
|
||||||
|
|
||||||
weathers[i] = new Weather();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (faulty)
|
if (coordinates.length == 0) {
|
||||||
completableFuture.completeExceptionally(new QuotaExceededException("Quota exceeded", 60));
|
throw new IllegalArgumentException("coordinates is empty");
|
||||||
else
|
}
|
||||||
completableFuture.complete(weathers);
|
|
||||||
|
if (Arrays.stream(coordinates).anyMatch(Objects::isNull)) {
|
||||||
return completableFuture;
|
throw new IllegalArgumentException("One or more coordinates are null");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.printf("Mock getting weather%n");
|
||||||
|
|
||||||
|
CompletableFuture<Weather[]> completableFuture =
|
||||||
|
CompletableFuture.supplyAsync(() -> {
|
||||||
|
if (faulty) {
|
||||||
|
throw new ProviderRequestException("Imagined server is down");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Arrays.stream(coordinates).map(
|
||||||
|
c -> new Weather(
|
||||||
|
c,
|
||||||
|
LocalDateTime.now(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
20f,
|
||||||
|
100f,
|
||||||
|
0f,
|
||||||
|
0f
|
||||||
|
)
|
||||||
|
).toArray(Weather[]::new);
|
||||||
|
});
|
||||||
|
|
||||||
|
return completableFuture.handle(WeatherQueryResult::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getQuota() {
|
public Quota getQuota() {
|
||||||
return 5;
|
return Quota.daily(120);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBulkLimit() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package eu.m724.wtapi.weather;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
|
||||||
|
import eu.m724.wtapi.provider.weather.impl.openmeteo.OpenMeteoProvider;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
|
import eu.m724.wtapi.object.Weather;
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
public class OpenMeteoProviderTest {
|
||||||
|
@Test
|
||||||
|
public void openWeatherMapTest() throws InterruptedException, ExecutionException {
|
||||||
|
WeatherProvider provider = new OpenMeteoProvider();
|
||||||
|
|
||||||
|
provider.init();
|
||||||
|
|
||||||
|
testInputErrors(provider);
|
||||||
|
|
||||||
|
CompletableFuture<WeatherQueryResult> weathersFuture =
|
||||||
|
provider.getWeather(
|
||||||
|
new Coordinates(54.6606714, -3.3827237),
|
||||||
|
new Coordinates(47.5705952, -53.5556464),
|
||||||
|
new Coordinates(34.2073721, -84.1402857)
|
||||||
|
);
|
||||||
|
|
||||||
|
WeatherQueryResult result = weathersFuture.get();
|
||||||
|
|
||||||
|
if (result.exception() != null) {
|
||||||
|
throw new ExecutionException("Query errored", result.exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
Weather[] weathers = result.weathers();
|
||||||
|
assert weathers.length == 3;
|
||||||
|
|
||||||
|
for (Weather weather : weathers) {
|
||||||
|
printWeather(weather);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void printWeather(Weather weather) {
|
||||||
|
System.out.printf("Weather in %f,%f as of %s:%n", weather.coordinates().latitude(), weather.coordinates().longitude(), weather.timestamp().toString());
|
||||||
|
System.out.printf(" Rain: %b | Snow: %b | Thunder: %b%n", weather.raining(), weather.snowing(), weather.thundering());
|
||||||
|
System.out.printf(" Temperature: %.1f°C (feels like %.1f°C)%n", weather.temperatureCelsius(), weather.temperatureApparentCelsius());
|
||||||
|
System.out.printf(" Relative humidity: %.0f%%%n", weather.relativeHumidityPercentage() * 100);
|
||||||
|
System.out.printf(" Cloudiness: %.0f%%%n", weather.cloudCoverPercentage() * 100);
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testInputErrors(WeatherProvider provider) {
|
||||||
|
assertThrows(NullPointerException.class, () -> provider.getWeather(null));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> provider.getWeather(new Coordinates[0]));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
provider.getWeather(new Coordinates[] { new Coordinates(0, 0), null }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package eu.m724.wtapi.weather;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
|
||||||
import eu.m724.wtapi.object.Weather;
|
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
|
||||||
import eu.m724.wtapi.provider.weather.impl.openweathermap.OpenWeatherMapProvider;
|
|
||||||
|
|
||||||
public class OpenWeatherMapTest {
|
|
||||||
@Test
|
|
||||||
public void openWeatherMapTest() throws InterruptedException, ExecutionException {
|
|
||||||
String apiKey = "48ac630acaa5c003316aea1dd7327d05";
|
|
||||||
|
|
||||||
WeatherProvider provider = new OpenWeatherMapProvider(apiKey);
|
|
||||||
|
|
||||||
provider.init();
|
|
||||||
|
|
||||||
CompletableFuture<Weather[]> weathersFuture =
|
|
||||||
provider.getWeather(
|
|
||||||
new Coordinates(54.6606714, -3.3827237),
|
|
||||||
new Coordinates(47.5705952, -53.5556464),
|
|
||||||
new Coordinates(34.2073721, -84.1402857));
|
|
||||||
|
|
||||||
Weather[] weathers = weathersFuture.get();
|
|
||||||
assert weathers.length == 3;
|
|
||||||
|
|
||||||
for (Weather weather : weathers) {
|
|
||||||
System.out.printf("Current weather in %s, %s: %s\n", weather.coordinates().city, weather.coordinates().country, weather.description);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
package eu.m724.wtapi.weather;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertThrows;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import eu.m724.wtapi.object.Coordinates;
|
|
||||||
import eu.m724.wtapi.object.Weather;
|
|
||||||
import eu.m724.wtapi.provider.exception.ProviderException;
|
|
||||||
import eu.m724.wtapi.provider.exception.QuotaExceededException;
|
|
||||||
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
|
||||||
|
|
||||||
public class ProviderTest {
|
|
||||||
@Test
|
|
||||||
public void testProvider() throws InterruptedException, ExecutionException {
|
|
||||||
WeatherProvider provider = new MockWeatherProvider(false);
|
|
||||||
provider.init();
|
|
||||||
|
|
||||||
assert provider.getQuota() == 5;
|
|
||||||
assert provider.getBulkLimit() == 1;
|
|
||||||
|
|
||||||
CompletableFuture<Weather> weatherFuture =
|
|
||||||
provider.getWeather(new Coordinates(0, 0));
|
|
||||||
|
|
||||||
Weather weather = weatherFuture.get();
|
|
||||||
assertNotNull(weather);
|
|
||||||
|
|
||||||
CompletableFuture<Weather[]> weathersFuture =
|
|
||||||
provider.getWeatherBulk(new Coordinates[] {
|
|
||||||
new Coordinates(0, 0),
|
|
||||||
new Coordinates(0, 100)
|
|
||||||
});
|
|
||||||
|
|
||||||
Weather[] weathers = weathersFuture.get();
|
|
||||||
assertNotNull(weathers);
|
|
||||||
assert weathers.length == 2;
|
|
||||||
assertNotNull(weathers[0]);
|
|
||||||
assertNotNull(weathers[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testFaultyProvider() throws InterruptedException {
|
|
||||||
WeatherProvider provider = new MockWeatherProvider(true);
|
|
||||||
|
|
||||||
CompletableFuture<Weather> weatherFuture =
|
|
||||||
provider.getWeather(new Coordinates(0, 0));
|
|
||||||
|
|
||||||
Weather weather = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
weather = weatherFuture.get();
|
|
||||||
fail("Shouldn't pass");
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
assert e.getCause() instanceof ProviderException;
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNull(weather);
|
|
||||||
|
|
||||||
Weather[] weathers = null;
|
|
||||||
|
|
||||||
CompletableFuture<Weather[]> weathersFuture =
|
|
||||||
provider.getWeatherBulk(new Coordinates[] {
|
|
||||||
new Coordinates(0, 0),
|
|
||||||
new Coordinates(0, 100)
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
weathers = weathersFuture.get();
|
|
||||||
fail("Shouldn't pass");
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
QuotaExceededException qee = (QuotaExceededException) e.getCause();
|
|
||||||
assert qee.getRetryIn() == 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
assertNull(weathers);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInputErrors() {
|
|
||||||
WeatherProvider provider = new MockWeatherProvider(false);
|
|
||||||
|
|
||||||
assertThrows(NullPointerException.class, () -> provider.getWeather(null));
|
|
||||||
assertThrows(NullPointerException.class, () -> provider.getWeatherBulk(null));
|
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> provider.getWeatherBulk(new Coordinates[0]));
|
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () ->
|
|
||||||
provider.getWeatherBulk(new Coordinates[] { new Coordinates(0, 0), null }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
96
src/test/java/eu/m724/wtapi/weather/WeatherProviderTest.java
Normal file
96
src/test/java/eu/m724/wtapi/weather/WeatherProviderTest.java
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
package eu.m724.wtapi.weather;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherQueryResult;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import eu.m724.wtapi.object.Coordinates;
|
||||||
|
import eu.m724.wtapi.object.Weather;
|
||||||
|
import eu.m724.wtapi.provider.exception.ProviderException;
|
||||||
|
import eu.m724.wtapi.provider.weather.WeatherProvider;
|
||||||
|
|
||||||
|
public class WeatherProviderTest {
|
||||||
|
WeatherProvider provider;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initProvider() {
|
||||||
|
this.provider = new MockWeatherProvider(false);
|
||||||
|
provider.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProvider() throws InterruptedException, ExecutionException {
|
||||||
|
assert provider.getQuota().getHourlyQuota() == 5;
|
||||||
|
|
||||||
|
CompletableFuture<WeatherQueryResult> weatherFuture =
|
||||||
|
provider.getWeather(new Coordinates(0, 0));
|
||||||
|
|
||||||
|
WeatherQueryResult result = weatherFuture.get();
|
||||||
|
|
||||||
|
if (result.exception() != null) {
|
||||||
|
throw new ExecutionException("Query errored", result.exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
Weather[] weathers = result.weathers();
|
||||||
|
assert weathers.length == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiple() throws ExecutionException, InterruptedException {
|
||||||
|
CompletableFuture<WeatherQueryResult> weatherFuture =
|
||||||
|
provider.getWeather(
|
||||||
|
new Coordinates(0, 0),
|
||||||
|
new Coordinates(0, 100)
|
||||||
|
);
|
||||||
|
|
||||||
|
WeatherQueryResult result = weatherFuture.get();
|
||||||
|
|
||||||
|
if (result.exception() != null) {
|
||||||
|
throw new ExecutionException("Query errored", result.exception());
|
||||||
|
}
|
||||||
|
|
||||||
|
Weather[] weathers = result.weathers();
|
||||||
|
assert weathers.length == 2;
|
||||||
|
|
||||||
|
assertNotNull(weathers[0]);
|
||||||
|
assertNotNull(weathers[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFaultyProvider() throws InterruptedException, ExecutionException {
|
||||||
|
WeatherProvider provider = new MockWeatherProvider(true);
|
||||||
|
|
||||||
|
CompletableFuture<WeatherQueryResult> weatherFuture =
|
||||||
|
provider.getWeather(new Coordinates(0, 0));
|
||||||
|
|
||||||
|
WeatherQueryResult result = weatherFuture.get();
|
||||||
|
|
||||||
|
assertNull(result.weathers());
|
||||||
|
|
||||||
|
if (result.exception() == null) {
|
||||||
|
fail("Faulty provider didn't throw");
|
||||||
|
}
|
||||||
|
|
||||||
|
assert result.exception() instanceof ProviderException;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInputErrors() {
|
||||||
|
WeatherProvider provider = new MockWeatherProvider(false);
|
||||||
|
|
||||||
|
assertThrows(NullPointerException.class, () -> provider.getWeather(null));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> provider.getWeather(new Coordinates[0]));
|
||||||
|
|
||||||
|
assertThrows(IllegalArgumentException.class, () ->
|
||||||
|
provider.getWeather(new Coordinates[] { new Coordinates(0, 0), null }));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue