Skip to content
Snippets Groups Projects
Commit c84135a0 authored by Martin Lowe's avatar Martin Lowe :no_entry:
Browse files

Create an endpoint for creating country lists #15

parent f703c446
No related branches found
No related tags found
No related merge requests found
/**
* Copyright (c) 2021 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.geoip.client.helper;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.eclipsefoundation.geoip.client.model.ISOCountry;
/**
* Helper to retrieve and cache countries and country codes in a light manner.
*/
public class CountryHelper {
private static final Map<String, List<ISOCountry>> countryListCache = new HashMap<>();
public static List<ISOCountry> getCountries(Locale l) {
// Default to EN locale if not specified
Locale actualLocale = l != null ? l : Locale.ENGLISH;
// get the cached data if it exists
List<ISOCountry> countries = countryListCache.get(actualLocale.getLanguage());
if (countries == null) {
// generate cached data
String[] isoCountries = Locale.getISOCountries();
countries = new ArrayList<>(isoCountries.length);
for (String isoCountry : isoCountries) {
Locale c = new Locale("", isoCountry);
countries.add(new ISOCountry(c.getDisplayCountry(actualLocale), c.getCountry()));
}
countryListCache.put(actualLocale.getLanguage(), countries);
}
return new ArrayList<>(countries);
}
private CountryHelper() {
}
}
...@@ -17,13 +17,13 @@ import io.quarkus.runtime.annotations.RegisterForReflection; ...@@ -17,13 +17,13 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
* *
*/ */
@RegisterForReflection @RegisterForReflection
public class Country { public class CountryCSV {
@CsvBindByName(column = "geoname_id") @CsvBindByName(column = "geoname_id")
private String id; private String id;
@CsvBindByName(column = "country_iso_code") @CsvBindByName(column = "country_iso_code")
private String countryIsoCode; private String countryIsoCode;
public Country() { public CountryCSV() {
} }
/** /**
......
/**
* Copyright (c) 2021 Eclipse Foundation
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Author: Martin Lowe <martin.lowe@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipsefoundation.geoip.client.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public class ISOCountry {
private String name;
@JsonProperty("iso_code")
private String isoCode;
public ISOCountry(String name, String isoCode) {
this.name = name;
this.isoCode = isoCode;
}
public String getName() {
return this.name;
}
public String getIsoCode() {
return this.isoCode;
}
}
...@@ -7,18 +7,22 @@ ...@@ -7,18 +7,22 @@
package org.eclipsefoundation.geoip.client.resources; package org.eclipsefoundation.geoip.client.resources;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
import java.util.MissingResourceException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import org.eclipsefoundation.geoip.client.helper.CountryHelper;
import org.eclipsefoundation.geoip.client.helper.InetAddressHelper; import org.eclipsefoundation.geoip.client.helper.InetAddressHelper;
import org.eclipsefoundation.geoip.client.model.Error; import org.eclipsefoundation.geoip.client.model.Error;
import org.eclipsefoundation.geoip.client.service.GeoIPService; import org.eclipsefoundation.geoip.client.service.GeoIPService;
...@@ -59,4 +63,19 @@ public class CountryResource { ...@@ -59,4 +63,19 @@ public class CountryResource {
throw new RuntimeException("Error while converting country record to JSON", e); throw new RuntimeException("Error while converting country record to JSON", e);
} }
} }
@GET
@Path("/all")
public Response getAll(@QueryParam("locale") String locale) {
Locale l = new Locale(locale != null ? locale : "en");
return Response.ok(CountryHelper.getCountries(isValid(l) ? l : null)).build();
}
private boolean isValid(Locale locale) {
try {
return locale.getISO3Language() != null && locale.getISO3Country() != null;
} catch (MissingResourceException e) {
return false;
}
}
} }
...@@ -20,7 +20,7 @@ import javax.annotation.PostConstruct; ...@@ -20,7 +20,7 @@ import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.geoip.client.model.Country; import org.eclipsefoundation.geoip.client.model.CountryCSV;
import org.eclipsefoundation.geoip.client.model.IPVersion; import org.eclipsefoundation.geoip.client.model.IPVersion;
import org.eclipsefoundation.geoip.client.model.SubnetRange; import org.eclipsefoundation.geoip.client.model.SubnetRange;
import org.eclipsefoundation.geoip.client.service.NetworkService; import org.eclipsefoundation.geoip.client.service.NetworkService;
...@@ -73,9 +73,9 @@ public class CSVNetworkService implements NetworkService { ...@@ -73,9 +73,9 @@ public class CSVNetworkService implements NetworkService {
private void loadCountries(String filePath, Map<String, String> container) { private void loadCountries(String filePath, Map<String, String> container) {
try (FileReader reader = new FileReader(filePath)) { try (FileReader reader = new FileReader(filePath)) {
// read in all of the country lines as country objects // read in all of the country lines as country objects
List<Country> countries = new CsvToBeanBuilder<Country>(reader).withType(Country.class).build().parse(); List<CountryCSV> countries = new CsvToBeanBuilder<CountryCSV>(reader).withType(CountryCSV.class).build().parse();
// add each of the countries to the container map, mapping ID to the ISO code // add each of the countries to the container map, mapping ID to the ISO code
for (Country c : countries) { for (CountryCSV c : countries) {
container.put(c.getId(), c.getCountryIsoCode().toLowerCase()); container.put(c.getId(), c.getCountryIsoCode().toLowerCase());
} }
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
......
...@@ -8,9 +8,17 @@ package org.eclipsefoundation.geoip.client.resources; ...@@ -8,9 +8,17 @@ package org.eclipsefoundation.geoip.client.resources;
import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.given;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.stream.Stream;
import org.eclipsefoundation.geoip.client.model.ISOCountry;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;
/** /**
* Test the listing resource endpoint, using fake data points to test solely the * Test the listing resource endpoint, using fake data points to test solely the
...@@ -25,11 +33,11 @@ public class CountryResourceTest { ...@@ -25,11 +33,11 @@ public class CountryResourceTest {
private static final String VALID_IPV4_ADDRESS = "72.137.192.0"; private static final String VALID_IPV4_ADDRESS = "72.137.192.0";
// Google IE server address // Google IE server address
private static final String VALID_IPV6_ADDRESS = "2a00:1450:400a:804::2004"; private static final String VALID_IPV6_ADDRESS = "2a00:1450:400a:804::2004";
@Test @Test
public void testCityIPEndpoint() { public void testCityIPEndpoint() {
given().when().get("/countries/"+VALID_IPV4_ADDRESS).then().statusCode(200); given().when().get("/countries/" + VALID_IPV4_ADDRESS).then().statusCode(200);
given().when().get("/countries/"+VALID_IPV6_ADDRESS).then().statusCode(200); given().when().get("/countries/" + VALID_IPV6_ADDRESS).then().statusCode(200);
} }
@Test @Test
...@@ -41,17 +49,47 @@ public class CountryResourceTest { ...@@ -41,17 +49,47 @@ public class CountryResourceTest {
given().when().get("/countries/1.0.300.0").then().statusCode(400); given().when().get("/countries/1.0.300.0").then().statusCode(400);
given().when().get("/countries/1.0.0.300").then().statusCode(400); given().when().get("/countries/1.0.0.300").then().statusCode(400);
given().when().get("/countries/sample").then().statusCode(400); given().when().get("/countries/sample").then().statusCode(400);
given().when().get("/countries/"+VALID_IPV4_ADDRESS+":8080").then().statusCode(400); given().when().get("/countries/" + VALID_IPV4_ADDRESS + ":8080").then().statusCode(400);
// seems to be an issue with Google Guava code, only gets detected by MaxMind // seems to be an issue with Google Guava code, only gets detected by MaxMind
given().when().get("/countries/0.1.1.1").then().statusCode(500); given().when().get("/countries/0.1.1.1").then().statusCode(500);
// loopback + unspecified address // loopback + unspecified address
given().when().get("/countries/127.0.0.1").then().statusCode(400); given().when().get("/countries/127.0.0.1").then().statusCode(400);
given().when().get("/countries/0.0.0.0").then().statusCode(400); given().when().get("/countries/0.0.0.0").then().statusCode(400);
// IPv6 tests // IPv6 tests
given().when().get("/countries/bad:ip:add::res").then().statusCode(400); given().when().get("/countries/bad:ip:add::res").then().statusCode(400);
// loopback + unspecified address // loopback + unspecified address
given().when().get("/countries/::").then().statusCode(400); given().when().get("/countries/::").then().statusCode(400);
given().when().get("/countries/::1").then().statusCode(400); given().when().get("/countries/::1").then().statusCode(400);
} }
@Test
void countryList_base() {
Response r = given().when().get("/countries/all");
r.then().statusCode(200);
// assert that United states exists as English by default
Assertions.assertTrue(Stream.of(r.as(ISOCountry[].class))
.anyMatch(c -> Locale.US.getDisplayCountry().equalsIgnoreCase(c.getName())
&& "us".equalsIgnoreCase(c.getIsoCode())));
}
@Test
void countryList_localized() {
Response r = given().when().get("/countries/all?locale=fr");
r.then().statusCode(200);
// assert that United states exists as French value
Assertions.assertTrue(Stream.of(r.as(ISOCountry[].class))
.anyMatch(c -> Locale.US.getDisplayCountry(Locale.FRENCH).equalsIgnoreCase(c.getName())
&& "us".equalsIgnoreCase(c.getIsoCode())));
}
@Test
void countryList_badLocale() {
Response r = given().when().get("/countries/all?locale=foobar");
r.then().statusCode(200);
// assert that English value exists as fallback when bad locale passed
Assertions.assertTrue(Stream.of(r.as(ISOCountry[].class))
.anyMatch(c -> Locale.US.getDisplayCountry().equalsIgnoreCase(c.getName())
&& "us".equalsIgnoreCase(c.getIsoCode())));
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment