Skip to content
Snippets Groups Projects
Commit 6ac2acbe authored by Zachary Sabourin's avatar Zachary Sabourin
Browse files

Merge branch 'zacharysabourin/master/28' into 'master'

feat: Increase code quality

See merge request !35
parents 8e51b4f3 67b8380a
No related branches found
No related tags found
1 merge request!35feat: Increase code quality
Pipeline #30701 passed
Showing
with 283 additions and 317 deletions
......@@ -12,18 +12,16 @@ clean:;
rm -rf ./maxmind
mvn clean
generate-spec: validate-spec;
compile-test-resources: install-yarn;
yarn run generate-json-schema
install-yarn:;
yarn install --frozen-lockfile
validate-spec: install-yarn;
compile-java: generate-spec;
compile-java: compile-test-resources;
mvn compile package -Declipse.maxmind.root=${PWD}/maxmind
compile-java-quick: generate-spec;
compile-java-quick: compile-test-resources;
mvn compile package -Dmaven.test.skip=true -Declipse.maxmind.root=${PWD}/maxmind
compile: clean compile-java;
......
......@@ -2,18 +2,13 @@
"name": "eclipsefdn-geoip-rest-api-support",
"version": "1.0.0",
"dependencies": {
"@redocly/openapi-cli": "^1.0.0-beta.54",
"@openapi-contrib/openapi-schema-to-json-schema": "^3.1.1",
"@stoplight/json-ref-resolver": "^3.1.2",
"decamelize": "^5.0.0",
"js-yaml": "^4.1.0",
"yargs": "^17.0.1"
"eclipsefdn-api-support": "1.0.0"
},
"private": true,
"scripts": {
"start": "yarn run generate-json-schema && npx @redocly/openapi-cli preview-docs spec/openapi.yaml -p 8093",
"test": "yarn run generate-json-schema && npx @redocly/openapi-cli lint spec/openapi.yaml",
"generate-json-schema": "yarn run clean && node src/main/js/openapi2schema.js -s spec/openapi.yaml -t src/test/resources",
"generate-json-schema": "yarn run clean && node node_modules/eclipsefdn-api-support/src/openapi2schema.js -s spec/openapi.yaml -t src/test/resources",
"clean": "rm -rf src/test/resources/schemas/"
}
}
......@@ -9,8 +9,8 @@
<maven.compiler.parameters>true</maven.compiler.parameters>
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>2.6.3.Final</quarkus.platform.version>
<eclipse-api-version>0.6.6</eclipse-api-version>
<quarkus.platform.version>2.16.7.Final</quarkus.platform.version>
<eclipse-api-version>0.7.7</eclipse-api-version>
<surefire-plugin.version>3.0.0-M5</surefire-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
......
/*********************************************************************
* Copyright (c) 2023 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: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipsefoundation.geoip.client.config;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
/**
* Config mapper for eclipse maxmind file locations. Contains filepaths for
* ipv4, ipv6, and countries files.
*/
@ConfigMapping(prefix = "eclipse.maxmind.subnet")
public interface EclipseMaxmindSubnetConfig {
@WithDefault(value = "/tmp/maxmind/db/GeoLite2-Country-Blocks-IPv4.csv")
String ipv4FilePath();
@WithDefault(value = "/tmp/maxmind/db/GeoLite2-Country-Blocks-IPv6.csv")
String ipv6FilePath();
@WithDefault(value = "/tmp/maxmind/db/GeoLite2-Country-Locations-en.csv")
String countriesFilePath();
}
/*********************************************************************
* Copyright (c) 2023 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: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipsefoundation.geoip.client.config;
import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithName;
/**
* Config mapper for maxmind database configs. Contains the db root folder, as
* well as city and country file names.
*/
@ConfigMapping(prefix = "maxmind.database")
public interface MaxmindDatabaseConfig {
@WithName("root")
String dbRoot();
@WithName("city.file")
String cityFileName();
@WithName("country.file")
String countryFileName();
}
......@@ -16,6 +16,6 @@ import com.maxmind.db.Metadata;
import io.quarkus.runtime.annotations.RegisterForReflection;
@RegisterForReflection(targets = {Metadata.class, MaxMindDbConstructor.class})
@RegisterForReflection(targets = { Metadata.class, MaxMindDbConstructor.class })
public class MaxmindReflectionRegistrationConfig {
}
/*********************************************************************
* Copyright (c) 2019 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.Arrays;
import java.util.Collections;
import java.util.List;
import com.google.common.net.InetAddresses;
/**
* Helper class centralizing checks of IP addresses.
*
* @author Martin Lowe
*/
public class InetAddressHelper {
public static final List<String> INVALID_ADDRESSES_IPv4 = Collections
.unmodifiableList(Arrays.asList("127.0.0.1", "0.0.0.0"));
public static final List<String> INVALID_ADDRESSES_IPv6 = Collections.unmodifiableList(Arrays.asList("::1", "::"));
/**
* Centralized IP address check that blacklists loopback and unspecified
*
* @param address the IP address to check
* @return true if address is a valid IP address, false otherwise
*/
public static boolean validInetAddress(String address) {
// return immediately if null
if (address == null) {
return false;
}
// check lightweight options first for invalid addresses
if (INVALID_ADDRESSES_IPv4.contains(address) || INVALID_ADDRESSES_IPv6.contains(address)) {
return false;
}
// Use Google Guava as larger more intensive check of string values
return InetAddresses.isInetAddress(address);
}
// hide constructor
private InetAddressHelper() {
}
}
......@@ -23,14 +23,12 @@ import io.quarkus.runtime.annotations.RegisterForReflection;
*/
@RegisterForReflection
public class Country {
@CsvBindByName(column = "geoname_id")
private String id;
@CsvBindByName(column = "country_iso_code")
private String countryIsoCode;
public Country() {
}
/**
* @return the id
*/
......@@ -58,5 +56,4 @@ public class Country {
public void setCountryIsoCode(String countryIsoCode) {
this.countryIsoCode = countryIsoCode;
}
}
......@@ -32,12 +32,6 @@ public class SubnetRange {
@CsvBindByName
private String network;
/**
*
*/
public SubnetRange() {
}
/**
* @return the geoname
*/
......@@ -79,5 +73,4 @@ public class SubnetRange {
public void setNetwork(String network) {
this.network = network;
}
}
/*********************************************************************
* Copyright (c) 2023 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: Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipsefoundation.geoip.client.resources;
import java.io.IOException;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.StringUtils;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.service.GeoIPService;
import com.google.common.net.InetAddresses;
import com.maxmind.geoip2.record.AbstractNamedRecord;
/**
* Superclass of {@link CountryResource} and {@link CityResource}. Contains a
* method for converting a City/Country to Json, as well as validation for
* incoming IP addresses.
*/
public abstract class AbstractLocationResource {
private static final List<String> INVALID_ADDRESSES_IPv4 = List.of("127.0.0.1", "0.0.0.0");
private static final List<String> INVALID_ADDRESSES_IPv6 = List.of("::1", "::");
@Inject
GeoIPService geoIp;
/**
* Returns a 200 OK Response containing the JSON converted entity. Converts to
* JSON using the built-in {@link AbstractNamedRecord#toJson()} Throws a 500
* Internal Server Error if there was an issue converting the body to JSON.
*
* @param record The entity to convert and send to the client
* @return A 200 OK Response containing the JSON converted entity. A 500
* Internal Server Error if there was an issue converting the body to
* JSON.
*/
protected Response returnAsJson(AbstractNamedRecord result) {
// Return converted country/city
try {
return Response.ok(result.toJson()).build();
} catch (IOException e) {
throw new ApplicationException("Error while converting country record to JSON", e);
}
}
/**
* Validates the incoming IP address. IPs are invalid if they are null,
* contained in the list of invalid Ipv4/Ipv6 addresses, or considered invalid
* by {@link InetAddresses#isInetAddress(String)}. Throws a 400
* BadRequestException if the Guava check throws an error validating the IP.
*
* @param address The given IP address to validate.
* @return True if the IP is valid. False if not.
*/
protected boolean isValidInetAddress(String address) {
try {
// Check null and lightweight options first for invalid addresses
if (StringUtils.isBlank(address)
|| INVALID_ADDRESSES_IPv4.contains(address)
|| INVALID_ADDRESSES_IPv6.contains(address)) {
return false;
}
// Use Google Guava as larger more intensive check of string values
return InetAddresses.isInetAddress(address);
} catch (Exception e) {
throw new BadRequestException("Passed IP address was not a valid address");
}
}
}
/*********************************************************************
* Copyright (c) 2019 Eclipse Foundation.
* Copyright (c) 2019, 2023 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>
* Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipsefoundation.geoip.client.resources;
import java.io.IOException;
import javax.inject.Inject;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.maxmind.geoip2.record.City;
import org.eclipsefoundation.core.model.Error;
import org.eclipsefoundation.geoip.client.helper.InetAddressHelper;
import org.eclipsefoundation.geoip.client.service.GeoIPService;
/**
* @author Martin Lowe
*
* Main resource class for all endpoints under '/cities'.
*/
@Path("/cities")
@Produces(MediaType.APPLICATION_JSON)
public class CityResource {
@Inject
GeoIPService geoIp;
public class CityResource extends AbstractLocationResource {
/**
* Returns a 200 OK Response containing the city location of a given IP address.
* Returns a 400 Bad Request if the IP is invalid. Returns a 500 Internal Server
* Error if there is an issue retrieving the IP location, or if the outgoing
* JSON conversion fails.
*
* @param ipAddr The IP address to search.
* @return A 200 OK Response containing the city location of a given IP address.
* A 400 Bad Request if the IP is invalid. A 500 Internal Server
* Error if there is an issue retrieving the IP location, or if the
* outgoing JSON conversion fails.
*/
@GET
@Path("/{ipAddr}")
public Response get(@PathParam("ipAddr") String ipAddr) {
// validate IP
if (ipAddr == null || !InetAddressHelper.validInetAddress(ipAddr)) {
return new Error(Status.BAD_REQUEST, "Valid IP address must be passed to retrieve location data")
.asResponse();
}
// retrieve cached city data
City c = geoIp.getCity(ipAddr);
if (c == null) {
return new Error(Status.INTERNAL_SERVER_ERROR, "Error while retrieving location for IP " + ipAddr)
.asResponse();
}
// return city data
try {
return Response.ok(c.toJson()).build();
} catch (IOException e) {
throw new RuntimeException("Error while converting city record to JSON", e);
if (!isValidInetAddress(ipAddr)) {
throw new BadRequestException("Valid IP address must be passed to retrieve location data");
}
// Retrieve cached city data and return the JSON object
return returnAsJson(geoIp.getCity(ipAddr));
}
}
/*********************************************************************
* Copyright (c) 2019 Eclipse Foundation.
* Copyright (c) 2019, 2023 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>
* Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipsefoundation.geoip.client.resources;
import java.io.IOException;
import javax.inject.Inject;
import javax.ws.rs.BadRequestException;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.maxmind.geoip2.record.Country;
import org.eclipsefoundation.core.model.Error;
import org.eclipsefoundation.geoip.client.helper.InetAddressHelper;
import org.eclipsefoundation.geoip.client.service.GeoIPService;
/**
* @author Martin Lowe
*
* Main resource class for all endpoints under '/countries'.
*/
@Path("/countries")
@Produces(MediaType.APPLICATION_JSON)
public class CountryResource {
@Inject
GeoIPService geoIp;
public class CountryResource extends AbstractLocationResource {
/**
* Returns a 200 OK Response containing the country location of a given IP
* address. Returns a 400 Bad Request if the IP is invalid. Returns a 500
* Internal Server Error if there is an issue retrieving the IP location, or if
* the outgoing JSON conversion fails.
*
* @param ipAddr The IP address to search.
* @return A 200 OK Response containing the country location of a given IP
* address. A 400 Bad Request if the IP is invalid. A 500 Internal
* Server Error if there is an issue retrieving the IP location, or if
* the outgoing JSON conversion fails.
*/
@GET
@Path("/{ipAddr}")
public Response get(@PathParam("ipAddr") String ipAddr) {
// validate IP
if (!InetAddressHelper.validInetAddress(ipAddr)) {
return new Error(Status.BAD_REQUEST, "Valid IP address must be passed to retrieve location data")
.asResponse();
}
// retrieve cached country data
Country c = geoIp.getCountry(ipAddr);
if (c == null) {
return new Error(Status.INTERNAL_SERVER_ERROR, "Error while retrieving location for IP " + ipAddr)
.asResponse();
}
// return country data
try {
return Response.ok(c.toJson()).build();
} catch (IOException e) {
throw new RuntimeException("Error while converting country record to JSON", e);
if (!isValidInetAddress(ipAddr)) {
throw new BadRequestException("Valid IP address must be passed to retrieve location data");
}
// Retrieve cached country data and return the JSON object
return returnAsJson(geoIp.getCountry(ipAddr));
}
}
/*********************************************************************
* Copyright (c) 2019 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.resources.mapper;
import java.net.UnknownHostException;
import java.sql.SQLException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.maxmind.geoip2.exception.GeoIp2Exception;
/**
* Captures exceptions and generates responses based on their causes.
*
* @author Martin Lowe
*/
@Provider
public class RuntimeMapper implements ExceptionMapper<RuntimeException> {
private static final Logger LOGGER = LoggerFactory.getLogger(RuntimeMapper.class);
@Override
public Response toResponse(RuntimeException exception) {
LOGGER.error(exception.getMessage(), exception);
// respond to error
Throwable cause = exception.getCause();
Response out;
if (cause instanceof UnknownHostException) {
out = Response.status(Status.BAD_REQUEST).entity("Passed IP address was not a valid address").build();
} else if (cause instanceof GeoIp2Exception) {
out = Response.status(Status.INTERNAL_SERVER_ERROR)
.entity("An error occured while interacting with the geo IP databases: " + cause.getMessage())
.build();
} else if (cause instanceof SQLException) {
out = Response.status(Status.INTERNAL_SERVER_ERROR)
.entity("An error occured while interacting with the subnet dataset: " + cause.getMessage())
.build();
} else {
out = Response.status(Status.INTERNAL_SERVER_ERROR)
.entity("Unknown server error occured while processing request").build();
}
return out;
}
}
\ No newline at end of file
/*********************************************************************
* Copyright (c) 2019 Eclipse Foundation.
* Copyright (c) 2019, 2023 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>
* Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
......@@ -15,12 +16,24 @@ import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Country;
/**
* @author Martin Lowe
*
* Defines the GeoIPService used to fetch city and country location data via IP
* address.
*/
public interface GeoIPService {
/**
* Returns the city where the given IP address is located.
*
* @param ipAddr The given IP address
* @return A City entity corresponding to the IP address' location.
*/
City getCity(String ipAddr);
/**
* Returns the country where the given IP address is located.
*
* @param ipAddr The given IP address
* @return A Country entity corresponding to the IP address' location.
*/
Country getCountry(String ipAddr);
}
......@@ -23,15 +23,17 @@ import java.util.Objects;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import com.opencsv.bean.CsvToBeanBuilder;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.config.EclipseMaxmindSubnetConfig;
import org.eclipsefoundation.geoip.client.model.Country;
import org.eclipsefoundation.geoip.client.model.IPVersion;
import org.eclipsefoundation.geoip.client.model.SubnetRange;
import org.eclipsefoundation.geoip.client.service.NetworkService;
import com.opencsv.bean.CsvToBeanBuilder;
import io.quarkus.runtime.Startup;
/**
......@@ -44,12 +46,8 @@ import io.quarkus.runtime.Startup;
@ApplicationScoped
public class CSVNetworkService implements NetworkService {
@ConfigProperty(name = "eclipse.subnet.ipv4.path")
String ipv4FilePath;
@ConfigProperty(name = "eclipse.subnet.ipv6.path")
String ipv6FilePath;
@ConfigProperty(name = "eclipse.subnet.countries.path")
String countriesFilePath;
@Inject
EclipseMaxmindSubnetConfig config;
private Map<String, String> countryIdToIso;
private Map<String, List<String>> ipv4Subnets;
......@@ -60,10 +58,9 @@ public class CSVNetworkService implements NetworkService {
this.ipv4Subnets = new HashMap<>();
this.ipv6Subnets = new HashMap<>();
this.countryIdToIso = new HashMap<>();
loadCountries(countriesFilePath, countryIdToIso);
loadMap(ipv4FilePath, ipv4Subnets);
// Disabled as it is a 2million item list and crashes the app
// loadMap(ipv6FilePath, ipv6Subnets);
loadCountries(config.countriesFilePath(), countryIdToIso);
loadMap(config.ipv4FilePath(), ipv4Subnets);
// Pre-loading ipv6 entries is a 2million item list and crashes the app
}
@Override
......@@ -79,6 +76,14 @@ public class CSVNetworkService implements NetworkService {
return Collections.emptyList();
}
/**
* Reads all Country entities from a desired CSV file and loads them into the
* desired container.
*
* @param filePath The location of the CSV file to read.
* @param container Reference to the Map to store all entries read from the
* file.
*/
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
......@@ -88,12 +93,20 @@ public class CSVNetworkService implements NetworkService {
container.put(c.getId(), c.getCountryIsoCode().toLowerCase());
}
} catch (FileNotFoundException e) {
throw new RuntimeException("Could not find country CSV file at path " + filePath, e);
throw new ApplicationException("Could not find country CSV file at path " + filePath, e);
} catch (IOException e) {
throw new RuntimeException("Error while reading in CSV file at path " + filePath, e);
throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
}
}
/**
* Reads all Subnet entities from a desired CSV file and loads them into the
* desired container.
*
* @param filePath The location of the CSV file to read.
* @param container Reference to the Map to store all entries read from the
* file.
*/
private void loadMap(String filePath, Map<String, List<String>> container) {
try (FileReader reader = new FileReader(filePath)) {
// read in all of the country lines as country objects
......@@ -110,10 +123,9 @@ public class CSVNetworkService implements NetworkService {
}
}
} catch (FileNotFoundException e) {
throw new RuntimeException("Could not find country CSV file at path " + filePath, e);
throw new ApplicationException("Could not find country CSV file at path " + filePath, e);
} catch (IOException e) {
throw new RuntimeException("Error while reading in CSV file at path " + filePath, e);
throw new ApplicationException("Error while reading in CSV file at path " + filePath, e);
}
}
}
/*********************************************************************
* Copyright (c) 2019 Eclipse Foundation.
* Copyright (c) 2019, 2023 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>
* Zachary Sabourin <zachary.sabourin@eclipse-foundation.org>
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
......@@ -18,21 +19,22 @@ import java.net.InetAddress;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipsefoundation.core.exception.ApplicationException;
import org.eclipsefoundation.geoip.client.config.MaxmindDatabaseConfig;
import org.eclipsefoundation.geoip.client.service.GeoIPService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.quarkus.runtime.Startup;
import com.maxmind.geoip2.DatabaseReader;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import com.maxmind.geoip2.model.CityResponse;
import com.maxmind.geoip2.model.CountryResponse;
import com.maxmind.geoip2.record.City;
import com.maxmind.geoip2.record.Country;
import io.quarkus.runtime.Startup;
/**
* Implementation of the Geo-IP service using MaxMind. Built in caching is
* ignored as its a very light implementation that has no eviction policy and
......@@ -45,12 +47,8 @@ import com.maxmind.geoip2.record.Country;
public class MaxMindGeoIPService implements GeoIPService {
private static final Logger LOGGER = LoggerFactory.getLogger(MaxMindGeoIPService.class);
@ConfigProperty(name = "maxmind.database.root")
String dbRoot;
@ConfigProperty(name = "maxmind.database.city.file")
String cityFileName;
@ConfigProperty(name = "maxmind.database.country.file")
String countryFileName;
@Inject
MaxmindDatabaseConfig config;
private DatabaseReader countryReader;
private DatabaseReader cityReader;
......@@ -60,15 +58,15 @@ public class MaxMindGeoIPService implements GeoIPService {
*/
@PostConstruct
public void init() {
File countryDb = new File(dbRoot + File.separator + countryFileName);
File cityDb = new File(dbRoot + File.separator + cityFileName);
File countryDb = new File(config.dbRoot() + File.separator + config.countryFileName());
File cityDb = new File(config.dbRoot() + File.separator + config.cityFileName());
// attempt to load db's, throwing up if there is an issue initializing the
// readers
try {
this.countryReader = new DatabaseReader.Builder(countryDb).build();
this.cityReader = new DatabaseReader.Builder(cityDb).build();
} catch (IOException e) {
throw new RuntimeException(e);
throw new ApplicationException("Error pre-loading Maxmind DatabaseReaders", e);
}
}
......@@ -88,8 +86,8 @@ public class MaxMindGeoIPService implements GeoIPService {
InetAddress addr = InetAddress.getByName(ipAddr);
CityResponse resp = cityReader.city(addr);
return resp.getCity();
} catch (IOException | GeoIp2Exception e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e);
}
}
......@@ -99,9 +97,8 @@ public class MaxMindGeoIPService implements GeoIPService {
InetAddress addr = InetAddress.getByName(ipAddr);
CountryResponse resp = countryReader.country(addr);
return resp.getCountry();
} catch (IOException | GeoIp2Exception e) {
throw new RuntimeException(e);
} catch (Exception e) {
throw new ApplicationException("Error interacting with GeoIP databases", e);
}
}
}
/**
* Copyright (c) 2021 Eclipse
*
* 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@eclipsefoundation.org>
*
* SPDX-License-Identifier: EPL-2.0
*/
const toJsonSchema = require('@openapi-contrib/openapi-schema-to-json-schema');
const Resolver = require('@stoplight/json-ref-resolver');
const yaml = require('js-yaml');
const fs = require('fs');
const decamelize = require('decamelize');
const args = require('yargs')
.option('s', {
alias: 'src',
desc: 'The fully qualified path to the YAML spec.',
})
.option('t', {
alias: 'target',
desc: 'The fully qualified path to write the JSON schema to',
}).argv;
if (!args.s || !args.t) {
process.exit(1);
}
run();
/**
* Generates JSON schema files for consumption of the Java tests.
*/
async function run() {
try {
// load in the openapi yaml spec as an object
const doc = yaml.load(fs.readFileSync(args.s, 'utf8'));
// resolve $refs in openapi spec
let resolvedInp = await new Resolver.Resolver().resolve(doc);
const out = toJsonSchema(resolvedInp.result);
// if folder doesn't exist, create it
if (!fs.existsSync(`${args.t}/schemas`)) {
fs.mkdirSync(`${args.t}/schemas`);
}
// for each of the schemas, generate a JSON schema file
for (let schemaName in out.components.schemas) {
fs.writeFileSync(`${args.t}/schemas/${decamelize(schemaName, { separator: '-' })}-schema.json`, JSON.stringify(out.components.schemas[schemaName]));
}
} catch (e) {
console.log(e);
}
}
# Configuration file
quarkus.http.port=8080
## Base application configs
quarkus.http.root-path=/geoip
quarkus.http.port=8080
%dev.quarkus.http.port=8090
eclipse.pagination.enabled=false
quarkus.oidc.enabled=false
quarkus.keycloak.devservices.enabled=false
# Pagination configs
eclipse.pagination.filter.enabled=false
## Eclipse and Maxmind configs
eclipse.maxmind.root=/tmp/maxmind
eclipse.subnet.ipv4.path=${eclipse.maxmind.root}/db/GeoLite2-Country-Blocks-IPv4.csv
eclipse.subnet.ipv6.path=${eclipse.maxmind.root}/db/GeoLite2-Country-Blocks-IPv6.csv
eclipse.subnet.countries.path=${eclipse.maxmind.root}/db/GeoLite2-Country-Locations-en.csv
%dev.eclipse.maxmind.root=${PWD}/maxmind
eclipse.maxmind.subnet.ipv4-file-path=${eclipse.maxmind.root}/db/GeoLite2-Country-Blocks-IPv4.csv
eclipse.maxmind.subnet.ipv6-file-path=${eclipse.maxmind.root}/db/GeoLite2-Country-Blocks-IPv6.csv
eclipse.maxmind.subnet.countries-file-path=${eclipse.maxmind.root}/db/GeoLite2-Country-Locations-en.csv
maxmind.database.root=${eclipse.maxmind.root}/bin
maxmind.database.country.file=GeoLite2-Country.mmdb
maxmind.database.city.file=GeoLite2-City.mmdb
%dev.quarkus.http.port=8090
%dev.eclipse.maxmind.root=${PWD}/maxmind
#eclipse.cve.provider=stubbed
quarkus.oidc.enabled=false
quarkus.keycloak.devservices.enabled=false
\ No newline at end of file
......@@ -73,7 +73,7 @@ class CityResourceTest {
}
// seems to be an issue with Google Guava code, only gets detected by MaxMind
testCase = TestCaseHelper.prepareSuccessCase(CITIES_ENDPOINT_URL, new String[] { "0.1.1.1" }, "")
testCase = TestCaseHelper.prepareTestCase(CITIES_ENDPOINT_URL, new String[] { "0.1.1.1" }, "")
.setStatusCode(500).build();
RestAssuredTemplates.testGet(testCase);
......
......@@ -26,7 +26,7 @@ import io.quarkus.test.junit.QuarkusTest;
* @author Martin Lowe
*/
@QuarkusTest
public class CountryResourceTest {
class CountryResourceTest {
public static final String COUNTRY_ENDPOINT_URL = "/countries/{ipAddr}";
......@@ -42,7 +42,7 @@ public class CountryResourceTest {
new String[] { VALID_IPV6_ADDRESS }, SchemaNamespaceHelper.COUNTRY_SCHEMA_PATH);
@Test
public void testCountries_success() {
void testCountries_success() {
RestAssuredTemplates.testGet(IPV4_SUCCESS);
RestAssuredTemplates.testGet(IPV6_SUCCESS);
}
......@@ -60,7 +60,7 @@ public class CountryResourceTest {
}
@Test
public void testCountries_badIPs() {
void testCountries_badIPs() {
EndpointTestCase testCase;
......@@ -74,7 +74,7 @@ public class CountryResourceTest {
}
// seems to be an issue with Google Guava code, only gets detected by MaxMind
testCase = TestCaseHelper.prepareSuccessCase(COUNTRY_ENDPOINT_URL, new String[] { "0.1.1.1" }, "")
testCase = TestCaseHelper.prepareTestCase(COUNTRY_ENDPOINT_URL, new String[] { "0.1.1.1" }, "")
.setStatusCode(500).build();
RestAssuredTemplates.testGet(testCase);
......
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