Skip to content
This repository was archived by the owner on Oct 18, 2021. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* 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 <[email protected]>
*
* 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() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
*
*/
@RegisterForReflection
public class Country {
public class CountryCSV {
@CsvBindByName(column = "geoname_id")
private String id;
@CsvBindByName(column = "country_iso_code")
private String countryIsoCode;

public Country() {
public CountryCSV() {
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* 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 <[email protected]>
*
* 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@
package org.eclipsefoundation.geoip.client.resources;

import java.io.IOException;
import java.util.Locale;
import java.util.MissingResourceException;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
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.model.Error;
import org.eclipsefoundation.geoip.client.service.GeoIPService;
Expand Down Expand Up @@ -59,4 +63,19 @@ public Response get(@PathParam("ipAddr") String ipAddr) {
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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import javax.enterprise.context.ApplicationScoped;

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.SubnetRange;
import org.eclipsefoundation.geoip.client.service.NetworkService;
Expand Down Expand Up @@ -73,9 +73,9 @@ public List<String> getSubnets(String countryCode, IPVersion ipv) {
private void loadCountries(String filePath, Map<String, String> container) {
try (FileReader reader = new FileReader(filePath)) {
// 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
for (Country c : countries) {
for (CountryCSV c : countries) {
container.put(c.getId(), c.getCountryIsoCode().toLowerCase());
}
} catch (FileNotFoundException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@

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 io.quarkus.test.junit.QuarkusTest;
import io.restassured.response.Response;

/**
* Test the listing resource endpoint, using fake data points to test solely the
Expand All @@ -25,11 +33,11 @@ public class CountryResourceTest {
private static final String VALID_IPV4_ADDRESS = "72.137.192.0";
// Google IE server address
private static final String VALID_IPV6_ADDRESS = "2a00:1450:400a:804::2004";

@Test
public void testCityIPEndpoint() {
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_IPV4_ADDRESS).then().statusCode(200);
given().when().get("/countries/" + VALID_IPV6_ADDRESS).then().statusCode(200);
}

@Test
Expand All @@ -41,17 +49,47 @@ public void testCityBadIPEndpoint() {
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/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
given().when().get("/countries/0.1.1.1").then().statusCode(500);
// loopback + unspecified address
given().when().get("/countries/127.0.0.1").then().statusCode(400);
given().when().get("/countries/0.0.0.0").then().statusCode(400);

// IPv6 tests
given().when().get("/countries/bad:ip:add::res").then().statusCode(400);
// loopback + unspecified address
given().when().get("/countries/::").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())));
}
}