From bb27ecf397646cb807f06f94d2d2454aaf0632e3 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 13:47:12 +0100 Subject: [PATCH 01/54] replaced print stacktrace with log --- src/main/java/io/ipdata/client/service/ApiErrorDecoder.java | 5 +++-- .../io/ipdata/client/service/ApiKeyRequestInterceptor.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java b/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java index 9f68fe4..e0268e4 100644 --- a/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java +++ b/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java @@ -10,9 +10,10 @@ import java.io.IOException; import java.net.URL; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; -@RequiredArgsConstructor +@RequiredArgsConstructor @Slf4j public class ApiErrorDecoder implements ErrorDecoder { private static final int RATE_LIMIT_STATUS = 403; private final ObjectMapper mapper; @@ -35,7 +36,7 @@ public Exception decode(String methodKey, Response response) { return new RemoteIpdataException(error.getMessage(), response.status()); } } catch (IOException ioException) { - ioException.printStackTrace(); + log.error(ioException.getMessage(), ioException); return new RemoteIpdataException(message, response.status()); } } diff --git a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java index 134ad4b..f3b3c1a 100644 --- a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java +++ b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java @@ -5,8 +5,9 @@ import feign.RequestTemplate; import java.io.InputStreamReader; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; -@RequiredArgsConstructor +@RequiredArgsConstructor @Slf4j class ApiKeyRequestInterceptor implements RequestInterceptor { private static final String API_KEY_PARAM = "api-key"; @@ -21,7 +22,7 @@ class ApiKeyRequestInterceptor implements RequestInterceptor { .getResourceAsStream("/VERSION"))).replaceAll("\\n",""); version = String.format("io.ipdata.client.java.%s", version); }catch (Exception e){ - e.printStackTrace(); + log.error(e.getMessage(), e); version = "io.ipdata.client.java.UNKNOWN"; } API_CLIENT_VALUE = version; From d863fd6f4bda77d64e1fbe9c7055e29a0b1e7b39 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 13:50:36 +0100 Subject: [PATCH 02/54] fixed sonar reported issues --- .../client/model/{Asn.java => AsnModel.java} | 2 +- .../io/ipdata/client/model/IpdataModel.java | 4 ++-- .../model/{Threat.java => ThreatModel.java} | 2 +- .../client/service/CachingInternalClient.java | 21 +++++++++---------- .../io/ipdata/client/service/IpdataField.java | 6 +++--- .../IpdataInternalSingleFieldClient.java | 8 +++---- .../client/service/IpdataServiceBuilder.java | 12 +++++------ .../ipdata/client/IpdataFunctionalTest.java | 12 +++++------ 8 files changed, 33 insertions(+), 34 deletions(-) rename src/main/java/io/ipdata/client/model/{Asn.java => AsnModel.java} (92%) rename src/main/java/io/ipdata/client/model/{Threat.java => ThreatModel.java} (95%) diff --git a/src/main/java/io/ipdata/client/model/Asn.java b/src/main/java/io/ipdata/client/model/AsnModel.java similarity index 92% rename from src/main/java/io/ipdata/client/model/Asn.java rename to src/main/java/io/ipdata/client/model/AsnModel.java index 2e2c619..ab7b05f 100644 --- a/src/main/java/io/ipdata/client/model/Asn.java +++ b/src/main/java/io/ipdata/client/model/AsnModel.java @@ -5,7 +5,7 @@ import lombok.experimental.Accessors; @ToString @Getter @Accessors(fluent = true) -public class Asn { +public class AsnModel { private String asn; private String name; private String domain; diff --git a/src/main/java/io/ipdata/client/model/IpdataModel.java b/src/main/java/io/ipdata/client/model/IpdataModel.java index a99e321..2ed05cd 100644 --- a/src/main/java/io/ipdata/client/model/IpdataModel.java +++ b/src/main/java/io/ipdata/client/model/IpdataModel.java @@ -30,12 +30,12 @@ public class IpdataModel { private String flag; private String emojiFlag; private String emojiUnicode; - private Asn asn; + private AsnModel asn; private Carrier carrier; private List languages; private Currency currency; private TimeZone timeZone; - private Threat threat; + private ThreatModel threat; //meta private int count; diff --git a/src/main/java/io/ipdata/client/model/Threat.java b/src/main/java/io/ipdata/client/model/ThreatModel.java similarity index 95% rename from src/main/java/io/ipdata/client/model/Threat.java rename to src/main/java/io/ipdata/client/model/ThreatModel.java index 6a8e3e6..949c61b 100644 --- a/src/main/java/io/ipdata/client/model/Threat.java +++ b/src/main/java/io/ipdata/client/model/ThreatModel.java @@ -5,7 +5,7 @@ import lombok.ToString; @Getter @ToString -public class Threat { +public class ThreatModel { @JsonProperty("is_tor") private boolean tor; @JsonProperty("is_proxy") diff --git a/src/main/java/io/ipdata/client/service/CachingInternalClient.java b/src/main/java/io/ipdata/client/service/CachingInternalClient.java index 5fcfdff..bb62073 100644 --- a/src/main/java/io/ipdata/client/service/CachingInternalClient.java +++ b/src/main/java/io/ipdata/client/service/CachingInternalClient.java @@ -3,10 +3,10 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.LoadingCache; import io.ipdata.client.error.IpdataException; -import io.ipdata.client.model.Asn; +import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Currency; import io.ipdata.client.model.IpdataModel; -import io.ipdata.client.model.Threat; +import io.ipdata.client.model.ThreatModel; import io.ipdata.client.model.TimeZone; import java.util.List; import java.util.concurrent.ExecutionException; @@ -14,7 +14,6 @@ import lombok.Builder; import lombok.experimental.Delegate; - @Builder @VisibleForTesting @@ -34,10 +33,10 @@ public class CachingInternalClient implements IpdataInternalClient, IpdataIntern private LoadingCache ipdataCache; private LoadingCache, IpdataModel> fieldsCache; - private LoadingCache asnCache; + private LoadingCache asnCache; private LoadingCache tzCache; private LoadingCache currencyCache; - private LoadingCache threatCache; + private LoadingCache threatCache; @Override public IpdataModel getFields(String ip, String fields) throws IpdataException { @@ -49,7 +48,7 @@ public IpdataModel getFields(String ip, String fields) throws IpdataException { } @Override - public Asn asn(String ip) throws IpdataException { + public AsnModel asn(String ip) throws IpdataException { try { return asnCache.get(ip); } catch (ExecutionException e) { @@ -76,7 +75,7 @@ public Currency currency(String ip) throws IpdataException { } @Override - public Threat threat(String ip) throws IpdataException { + public ThreatModel threat(String ip) throws IpdataException { try { return threatCache.get(ip); } catch (ExecutionException e) { @@ -98,19 +97,19 @@ public List bulkIpdata(List ips) throws IpdataException { return ipdataInternalClient.bulkIpdata(ips); } - @Delegate(types = IpdataInternalSingleFieldClient.class, excludes = $DelegateExcludes.class) + @Delegate(types = IpdataInternalSingleFieldClient.class, excludes = DelegateExcludes.class) IpdataInternalSingleFieldClient getIpdataInternalSingleFieldClient() { return ipdataInternalSingleFieldClient; } - private interface $DelegateExcludes { - Asn asn(String ip); + private interface DelegateExcludes { + AsnModel asn(String ip); TimeZone timeZone(String ip); Currency currency(String ip); - Threat threat(String ip); + ThreatModel threat(String ip); } } diff --git a/src/main/java/io/ipdata/client/service/IpdataField.java b/src/main/java/io/ipdata/client/service/IpdataField.java index 91cd21c..2dc32fb 100644 --- a/src/main/java/io/ipdata/client/service/IpdataField.java +++ b/src/main/java/io/ipdata/client/service/IpdataField.java @@ -1,6 +1,6 @@ package io.ipdata.client.service; -import io.ipdata.client.model.Asn; +import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Carrier; import io.ipdata.client.model.Currency; import io.ipdata.client.model.Language; @@ -25,7 +25,7 @@ public class IpdataField { public static final IpdataField CONTINENT_CODE = new IpdataField("continent_code", String.class); public static final IpdataField LATITUDE = new IpdataField("latitude", Double.class); public static final IpdataField LONGITUDE = new IpdataField("longitude", Double.class); - public static final IpdataField ASN = new IpdataField("asn", Asn.class); + public static final IpdataField ASN = new IpdataField("asn", AsnModel.class); public static final IpdataField ORGANISATION = new IpdataField("organisation", String.class); public static final IpdataField POSTAL = new IpdataField("postal", String.class); public static final IpdataField CALLING_CODE = new IpdataField("calling_code", String.class); @@ -39,7 +39,7 @@ public class IpdataField { public static final IpdataField THREAT = new IpdataField("threat", TimeZone.class); public static final IpdataField COUNT = new IpdataField("count", Integer.class); private final String name; - private final Class type; + private final Class type; @Override public String toString() { diff --git a/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java b/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java index 2d39366..255ef91 100644 --- a/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java +++ b/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java @@ -3,9 +3,9 @@ import feign.Param; import feign.RequestLine; import io.ipdata.client.error.IpdataException; -import io.ipdata.client.model.Asn; +import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Currency; -import io.ipdata.client.model.Threat; +import io.ipdata.client.model.ThreatModel; import io.ipdata.client.model.TimeZone; @SuppressWarnings("RedundantThrows") @@ -55,7 +55,7 @@ interface IpdataInternalSingleFieldClient { @Cacheable @RequestLine("GET /{ip}/asn") - Asn asn(@Param("ip") String ip) throws IpdataException; + AsnModel asn(@Param("ip") String ip) throws IpdataException; @Cacheable @RequestLine("GET /{ip}/time_zone") @@ -67,6 +67,6 @@ interface IpdataInternalSingleFieldClient { @Cacheable @RequestLine("GET /{ip}/threat") - Threat threat(@Param("ip") String ip) throws IpdataException; + ThreatModel threat(@Param("ip") String ip) throws IpdataException; } diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java index 65d6cdf..486928e 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java @@ -14,10 +14,10 @@ import feign.httpclient.ApacheHttpClient; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; -import io.ipdata.client.model.Asn; +import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Currency; import io.ipdata.client.model.IpdataModel; -import io.ipdata.client.model.Threat; +import io.ipdata.client.model.ThreatModel; import io.ipdata.client.model.TimeZone; import java.net.URL; import lombok.RequiredArgsConstructor; @@ -91,9 +91,9 @@ public IpdataModel load(HashPair key) throws Exception { CacheBuilder.newBuilder() .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit()) .maximumSize(cacheConfig.maxSize()) - .build(new CacheLoader() { + .build(new CacheLoader() { @Override - public Asn load(String key) throws Exception { + public AsnModel load(String key) throws Exception { return singleFieldClient.asn(key); } }) @@ -124,9 +124,9 @@ public Currency load(String key) throws Exception { CacheBuilder.newBuilder() .expireAfterWrite(cacheConfig.timeout(), cacheConfig.unit()) .maximumSize(cacheConfig.maxSize()) - .build(new CacheLoader() { + .build(new CacheLoader() { @Override - public Threat load(String key) throws Exception { + public ThreatModel load(String key) throws Exception { return singleFieldClient.threat(key); } }) diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java index 94ffc4d..c51871b 100644 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java @@ -13,10 +13,10 @@ import com.google.common.collect.ImmutableMap; import feign.httpclient.ApacheHttpClient; import io.ipdata.client.error.RateLimitException; -import io.ipdata.client.model.Asn; +import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Currency; import io.ipdata.client.model.IpdataModel; -import io.ipdata.client.model.Threat; +import io.ipdata.client.model.ThreatModel; import io.ipdata.client.service.IpdataField; import io.ipdata.client.service.IpdataService; import java.net.URL; @@ -62,10 +62,10 @@ public void testFullresponse() { @Test @SneakyThrows public void testASN() { - Asn asn = ipdataService.asn("8.8.8.8"); + AsnModel asn = ipdataService.asn("8.8.8.8"); String serialized = MAPPER.writeValueAsString(asn); String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8/asn", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, Asn.class)); + expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, AsnModel.class)); assertEquals(serialized, expected, false); if (ipdataService == CACHING_IPDATA_SERVICE) { //value will be returned from cache now @@ -78,10 +78,10 @@ public void testASN() { @Test @SneakyThrows public void testThreat() { - Threat threat = ipdataService.threat("8.8.8.8"); + ThreatModel threat = ipdataService.threat("8.8.8.8"); String serialized = MAPPER.writeValueAsString(threat); String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8/threat", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, Threat.class)); + expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, ThreatModel.class)); assertEquals(serialized, expected, false); if (ipdataService == CACHING_IPDATA_SERVICE) { //value will be returned from cache now From 2606f2d5d355fb4a291410ea8b96fa53bc63c689 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 13:57:46 +0100 Subject: [PATCH 03/54] sonar config attempt --- .travis.yml | 3 +-- pom.groovy | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fe12322..ee2249b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,13 +6,12 @@ jdk: script: - mvn clean test post-integration-test after_success: - - mvn sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client + - mvn verify sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client addons: sonarcloud: organization: yassine-github token: secure: PYT5/IbiqFd877vdi+CYTx7srXr5ACvAgBSL4rxPqwVSQUdGIZxVTj8JcZp7Cl2WiDj9zDlTHSDaJDt5ZpXnmN5quJBizuCZDVArm3LoRADjxhOd9N8rQEYEE1TZOHlu1+g1i/abOL+av9Spn7lWk1LvuH5BW3b0GiAdqJFevfnJsUYDh4iYHiMYjTIP5dO/cZUnQzgFgZS6YkMA28cgQIy/F03z6tO9oM6kcJ/DGBnqvdGAT2ZxtWhFaaNwJazlxSePCwnOok+v3vgACzGospTJLQ4AdGklYIK+ljGpz7AbiRs7yhvzsib6i5Wr0IEmZmLiyIgjExPdnV+rXAndgig/0bnhv1YzyEf/hCzMczpHvNa0HDAGCxkTIsw76XlumwPMGURdlypWEoWrz/9cwiPMnQ0XcAOTN56msA5nBbNwnJ9KDKIVWKWg447OhbImlTr+qxHI/L+8e1dFxyA1iBg7/wtU/27V2QRABvhdlVGNn1khdkIKhsueYHbBjloUTILHBFKTtJdZdD2z/Vf9cu51EQDwv5IPfkkz/UER8pw4AVKgFi6v57M3unqE5C6g8dZr5CELAqy3tGjf6v9HaTF3AdGEkyIJuxiiO8LM20MJm+IreR/pIv8SVeT7f747hbPCQ+mMe5rSE4KVtSI88OeUcI193pdd5PBg0jU262I= - - mvn sonar:sonar cache: directories: - '$HOME/.m2/repository' diff --git a/pom.groovy b/pom.groovy index 01b4b38..b72b72e 100644 --- a/pom.groovy +++ b/pom.groovy @@ -18,6 +18,9 @@ project { 'sonar.projectName' 'spring-boot-sample' 'sonar.projectVersion' '${project.version}' 'sonar.host.url' 'https://sonarcloud.io' + 'sonar.tests' '${project.basedir}/src/test' + 'sonar.java.coveragePlugin' 'jacoco' + 'sonar.dynamicAnalysis' 'reuseReports' } dependencies { dependency('io.github.openfeign:feign-core:${version.client.feign}') From f034ca406f73910af9c4ba55bb4087b40d0a3cd5 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 14:08:26 +0100 Subject: [PATCH 04/54] updated travis config --- .travis.yml | 4 +--- pom.groovy | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ee2249b..90c54bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,7 @@ language: java jdk: - openjdk8 script: - - mvn clean test post-integration-test -after_success: - - mvn verify sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client + - mvn clean test post-integration-test sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client addons: sonarcloud: organization: yassine-github diff --git a/pom.groovy b/pom.groovy index b72b72e..c77c83f 100644 --- a/pom.groovy +++ b/pom.groovy @@ -15,7 +15,7 @@ project { 'sonar.links.issue' 'https://github.com/yassine/ipdata-java-client' 'sonar.links.scm' 'https://github.com/yassine/ipdata-java-client' 'sonar.projectKey' 'yassine_ipdata-java-client' - 'sonar.projectName' 'spring-boot-sample' + 'sonar.projectName' 'ipdata-java-client' 'sonar.projectVersion' '${project.version}' 'sonar.host.url' 'https://sonarcloud.io' 'sonar.tests' '${project.basedir}/src/test' From 09962db59f8f1c91281d81e0d1d5f0a80e23efa6 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 14:31:24 +0100 Subject: [PATCH 05/54] fixed coverage report configuration --- .travis.yml | 4 -- pom.groovy | 7 +-- .../ipdata/client/IpdataFunctionalTest.java | 56 ++++++++++++++++++- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 90c54bc..9fd96eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,5 @@ addons: organization: yassine-github token: secure: PYT5/IbiqFd877vdi+CYTx7srXr5ACvAgBSL4rxPqwVSQUdGIZxVTj8JcZp7Cl2WiDj9zDlTHSDaJDt5ZpXnmN5quJBizuCZDVArm3LoRADjxhOd9N8rQEYEE1TZOHlu1+g1i/abOL+av9Spn7lWk1LvuH5BW3b0GiAdqJFevfnJsUYDh4iYHiMYjTIP5dO/cZUnQzgFgZS6YkMA28cgQIy/F03z6tO9oM6kcJ/DGBnqvdGAT2ZxtWhFaaNwJazlxSePCwnOok+v3vgACzGospTJLQ4AdGklYIK+ljGpz7AbiRs7yhvzsib6i5Wr0IEmZmLiyIgjExPdnV+rXAndgig/0bnhv1YzyEf/hCzMczpHvNa0HDAGCxkTIsw76XlumwPMGURdlypWEoWrz/9cwiPMnQ0XcAOTN56msA5nBbNwnJ9KDKIVWKWg447OhbImlTr+qxHI/L+8e1dFxyA1iBg7/wtU/27V2QRABvhdlVGNn1khdkIKhsueYHbBjloUTILHBFKTtJdZdD2z/Vf9cu51EQDwv5IPfkkz/UER8pw4AVKgFi6v57M3unqE5C6g8dZr5CELAqy3tGjf6v9HaTF3AdGEkyIJuxiiO8LM20MJm+IreR/pIv8SVeT7f747hbPCQ+mMe5rSE4KVtSI88OeUcI193pdd5PBg0jU262I= -cache: - directories: - - '$HOME/.m2/repository' - - '$HOME/.sonar/cache' notifications: email: false diff --git a/pom.groovy b/pom.groovy index c77c83f..205944b 100644 --- a/pom.groovy +++ b/pom.groovy @@ -10,7 +10,8 @@ project { 'version.client.feign' '9.7.0' 'version.build.jacoco' '0.8.4' 'version.build.surefire' '3.0.0-M3' - 'sonar.jacoco.reportPaths' '${project.build.directory}/coverage-reports/jacoco-ut.exec' + 'sonar.organization' 'yassine-github' + 'sonar.coverage.jacoco.xmlReportPaths' 'target/site/code-coverage/jacoco.xml' 'sonar.links.homepage' 'https://github.com/yassine/ipdata-java-client' 'sonar.links.issue' 'https://github.com/yassine/ipdata-java-client' 'sonar.links.scm' 'https://github.com/yassine/ipdata-java-client' @@ -18,9 +19,7 @@ project { 'sonar.projectName' 'ipdata-java-client' 'sonar.projectVersion' '${project.version}' 'sonar.host.url' 'https://sonarcloud.io' - 'sonar.tests' '${project.basedir}/src/test' - 'sonar.java.coveragePlugin' 'jacoco' - 'sonar.dynamicAnalysis' 'reuseReports' + } dependencies { dependency('io.github.openfeign:feign-core:${version.client.feign}') diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java index c51871b..33b875c 100644 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableMap; import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.error.IpdataException; import io.ipdata.client.error.RateLimitException; import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Currency; @@ -143,17 +144,70 @@ public void testBulkResponse() { } @SneakyThrows - @Test(expected = RateLimitException.class) + @Test(expected = IpdataException.class) public void testError() { URL url = new URL("https://api.ipdata.co"); IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); serviceWithInvalidKey.ipdata("8.8.8.8"); } + @SneakyThrows + @Test(expected = IpdataException.class) + public void testAsnError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.asn("8.8.8.8"); + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testThreatError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.threat("8.8.8.8"); + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testTimeZoneError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.timeZone("8.8.8.8"); + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testCurrencyError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.currency("8.8.8.8"); + } + @Parameters public static Iterable data() { init(); From 275e3c401d252711d6239b762fa27322acd5f61c Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 14:57:49 +0100 Subject: [PATCH 06/54] Added readme docs --- README.md | 47 ++++++++++++++++++++-- src/main/java/io/ipdata/client/Ipdata.java | 9 +++++ 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b5cd287..99b607e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,47 @@ # ipdata-java-client ![Build Status](https://www.travis-ci.org/yassine/ipdata-java-client.svg?branch=master) -[![Coverage Status](https://sonarcloud.io/api/project_badges/measure?metric=coverage&project=com.github.yassine%3Aipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) -[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?metric=alert_status&project=com.github.yassine%3Aipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) -[![Maintainability](https://sonarcloud.io/api/project_badges/measure?metric=sqale_rating&project=com.github.yassine%3Aipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) -[![Reliability](https://sonarcloud.io/api/project_badges/measure?metric=reliability_rating&project=com.github.yassine%3Aipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) +[![Coverage Status](https://sonarcloud.io/api/project_badges/measure?metric=coverage&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?metric=alert_status&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) +[![Maintainability](https://sonarcloud.io/api/project_badges/measure?metric=sqale_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) +[![Reliability](https://sonarcloud.io/api/project_badges/measure?metric=reliability_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) + An ipdata.co java client. + +Recipes: +1. I wanna call the api without caching results : +```java +URL url = new URL("https://api.ipdata.co"); +IpdataService ipdataService = Ipdata.builder().url(url) + .noCache() + .key("MY_KEY") + .get(); +IpdataModel model = ipdataService.get("1.1.1.1"); +``` +2. I wanna call the api while caching results for further calls on the same IP (caching will yield you a latency of less than 1ms for requests that hit the cache): +```java +URL url = new URL("https://api.ipdata.co"); +IpdataService ipdataService = Ipdata.builder().url(url) + .withDefaultCache() + .key("MY_KEY").get(); +IpdataModel model = ipdataService.get("1.1.1.1"); //cache miss here +ipdataService.get("1.1.1.1"); //cache hit from here on ip address "1.1.1.1" +``` + +3. My 50% user session length is 30 minutes, and I don't want to have more than 8 * 1024 items in cache, +how can I do that? + +```java +URL url = new URL("https://api.ipdata.co"); +IpdataService ipdataService = Ipdata.builder().url(url) + .withCache() + .timeout(30, TimeUnit.MINUTES) + .maxSize(8 * 1024) + .registerCacheConfig() + .key("MY_KEY") + .get(); +IpdataModel model = ipdataService.get("1.1.1.1"); //cache miss here +ipdataService.get("1.1.1.1"); //cache hit from here on ip address "1.1.1.1" +``` + + diff --git a/src/main/java/io/ipdata/client/Ipdata.java b/src/main/java/io/ipdata/client/Ipdata.java index 2714120..0b8a860 100644 --- a/src/main/java/io/ipdata/client/Ipdata.java +++ b/src/main/java/io/ipdata/client/Ipdata.java @@ -51,6 +51,15 @@ public static class Builder { @Setter private Client feignClient; + /** + * Overrides current cache config with null (no caching). + * @return this + */ + public Builder noCache(){ + this .cacheConfig = null; + return this; + } + /** * Configures a cache with default configuration parameters. * Note: Overrides any previously configured cache. From 0c54747b848269ae4fa689ad675da52d9958e8b8 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 15:00:20 +0100 Subject: [PATCH 07/54] updated tests to capture new api --- src/test/java/io/ipdata/client/IpdataFunctionalTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java index 33b875c..792590f 100644 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java @@ -225,6 +225,7 @@ public static void init() { HTTP_CLIENT = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); IPDATA_SERVICE = Ipdata.builder().url(url).key(KEY) + .noCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build())).get(); From a2de90d7c1f6c41b10713085176b2653c50fcee5 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 15:03:52 +0100 Subject: [PATCH 08/54] updated mvnw version --- .mvn/wrapper/MavenWrapperDownloader.java | 51 ++-- .mvn/wrapper/maven-wrapper.properties | 3 +- mvnw | 34 ++- mvnw.cmd | 343 ++++++++++++----------- 4 files changed, 242 insertions(+), 189 deletions(-) mode change 100755 => 100644 .mvn/wrapper/MavenWrapperDownloader.java mode change 100755 => 100644 .mvn/wrapper/maven-wrapper.properties mode change 100755 => 100644 mvnw.cmd diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java old mode 100755 new mode 100644 index d475a89..b901097 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -1,22 +1,18 @@ /* -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -*/ - + * Copyright 2007-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import java.net.*; import java.io.*; import java.nio.channels.*; @@ -24,11 +20,12 @@ Licensed to the Apache Software Foundation (ASF) under one public class MavenWrapperDownloader { + private static final String WRAPPER_VERSION = "0.5.6"; /** * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. */ - private static final String DEFAULT_DOWNLOAD_URL = - "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar"; + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; /** * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to @@ -76,13 +73,13 @@ public static void main(String args[]) { } } } - System.out.println("- Downloading from: : " + url); + System.out.println("- Downloading from: " + url); File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); if(!outputFile.getParentFile().exists()) { if(!outputFile.getParentFile().mkdirs()) { System.out.println( - "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); } } System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); @@ -98,6 +95,16 @@ public static void main(String args[]) { } private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } URL website = new URL(urlString); ReadableByteChannel rbc; rbc = Channels.newChannel(website.openStream()); diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties old mode 100755 new mode 100644 index a5fcc11..642d572 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1,2 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.2/apache-maven-3.5.2-bin.zip \ No newline at end of file +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/mvnw b/mvnw index 961a825..41c0f0c 100755 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven2 Start Up Batch script +# Maven Start Up Batch script # # Required ENV vars: # ------------------ @@ -114,7 +114,6 @@ if $mingw ; then M2_HOME="`(cd "$M2_HOME"; pwd)`" [ -n "$JAVA_HOME" ] && JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" - # TODO classpath? fi if [ -z "$JAVA_HOME" ]; then @@ -212,7 +211,11 @@ else if [ "$MVNW_VERBOSE" = true ]; then echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." fi - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + fi while IFS="=" read key value; do case "$key" in (wrapperUrl) jarUrl="$value"; break ;; esac @@ -221,22 +224,38 @@ else echo "Downloading from: $jarUrl" fi wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi if command -v wget > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found wget ... using wget" fi - wget "$jarUrl" -O "$wrapperJarPath" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + fi elif command -v curl > /dev/null; then if [ "$MVNW_VERBOSE" = true ]; then echo "Found curl ... using curl" fi - curl -o "$wrapperJarPath" "$jarUrl" + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + else if [ "$MVNW_VERBOSE" = true ]; then echo "Falling back to using Java to download" fi javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi if [ -e "$javaClass" ]; then if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then if [ "$MVNW_VERBOSE" = true ]; then @@ -277,6 +296,11 @@ if $cygwin; then MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` fi +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain exec "$JAVACMD" \ diff --git a/mvnw.cmd b/mvnw.cmd old mode 100755 new mode 100644 index 03d90e9..8611571 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -1,161 +1,182 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven2 Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.0/maven-wrapper-0.4.0.jar" -FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - echo Found %WRAPPER_JAR% -) else ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')" - echo Finished downloading %WRAPPER_JAR% -) -@REM End of extension - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + +FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% From 775830c7d243b1bd01799d25741019886373b44c Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 15:09:39 +0100 Subject: [PATCH 09/54] fixed README badge urls --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 99b607e..5760dc2 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # ipdata-java-client ![Build Status](https://www.travis-ci.org/yassine/ipdata-java-client.svg?branch=master) -[![Coverage Status](https://sonarcloud.io/api/project_badges/measure?metric=coverage&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) -[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?metric=alert_status&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) -[![Maintainability](https://sonarcloud.io/api/project_badges/measure?metric=sqale_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) -[![Reliability](https://sonarcloud.io/api/project_badges/measure?metric=reliability_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/com.github.yassine:ipdata-java-client) +[![Coverage Status](https://sonarcloud.io/api/project_badges/measure?metric=coverage&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?metric=alert_status&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) +[![Maintainability](https://sonarcloud.io/api/project_badges/measure?metric=sqale_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) +[![Reliability](https://sonarcloud.io/api/project_badges/measure?metric=reliability_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) An ipdata.co java client. From 00bf9c0c793c08271f89fb3f4227c8b4466f7f46 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 19:32:05 +0100 Subject: [PATCH 10/54] preparing for maven central deployment --- pom.groovy | 60 +++++++++++++++---- .../io/ipdata/client/CacheConfigBuilder.java | 2 +- .../ipdata/client/IpdataFunctionalTest.java | 1 - 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/pom.groovy b/pom.groovy index 205944b..7e20782 100644 --- a/pom.groovy +++ b/pom.groovy @@ -1,8 +1,25 @@ project { modelVersion '4.0.0' - groupId 'io.ipdata.client' + groupId 'co.ipdata.client' artifactId 'ipdata-java-client' version '0.1.0-SNAPSHOT' + licenses { + license { + name 'The Apache Licence, Version 2.0' + url 'http://www.apache.org/licenses/LICENCE-2.0.txt' + } + } + scm { + connection 'git@github.com:yassine/ipdata-java-client.git' + developerConnection 'git@github.com:yassine/ipdata-java-client.git' + url 'https://github.com/yassine/ipdata-java-client' + } + developers { + developer { + name 'ipdata.co' + email 'support@ipdata.co' + } + } properties { 'project.build.sourceEncoding' 'UTF-8' 'maven.compiler.source' '6' @@ -19,7 +36,6 @@ project { 'sonar.projectName' 'ipdata-java-client' 'sonar.projectVersion' '${project.version}' 'sonar.host.url' 'https://sonarcloud.io' - } dependencies { dependency('io.github.openfeign:feign-core:${version.client.feign}') @@ -34,6 +50,23 @@ project { dependency('junit:junit:4.13:test') dependency('org.skyscreamer:jsonassert:1.5.0:test') } + profiles { + profile { + id 'release' + build { + plugins { + plugin('org.apache.maven.plugins:maven-gpg-plugin:1.6') { + executions { + execution { + phase 'verify' + goals 'sign' + } + } + } + } + } + } + } build { plugins { plugin('org.apache.maven.plugins:maven-resources-plugin:2.6') { @@ -41,10 +74,7 @@ project { encoding '${project.build.sourceEncoding}' } } - plugin { - groupId 'org.jacoco' - artifactId 'jacoco-maven-plugin' - version '${version.build.jacoco}' + plugin('org.jacoco:jacoco-maven-plugin:${version.build.jacoco}') { executions { execution { id 'prepare-agent' @@ -79,10 +109,20 @@ project { argLine '${surefireArgLine}' } } - plugin { - groupId 'org.codehaus.mojo' - artifactId 'sonar-maven-plugin' - version '3.6.0.1398' + plugin('org.codehaus.mojo:sonar-maven-plugin:3.6.0.1398') + plugin('org.apache.maven.plugins:maven-source-plugin:3.2.1') { + executions { + execution('attach-sources') { + goals('jar') + } + } + } + plugin('org.apache.maven.plugins:maven-javadoc-plugin:3.1.1') { + executions { + execution('attach-javadocs') { + goals('jar') + } + } } } resources { diff --git a/src/main/java/io/ipdata/client/CacheConfigBuilder.java b/src/main/java/io/ipdata/client/CacheConfigBuilder.java index 5be5b7a..2af7bd6 100644 --- a/src/main/java/io/ipdata/client/CacheConfigBuilder.java +++ b/src/main/java/io/ipdata/client/CacheConfigBuilder.java @@ -32,7 +32,7 @@ public CacheConfigBuilder maxSize(long maxSize) { * The maximum duration before invalidating a cache entry. * @param timeout The duration * @param unit The duration unit - * @return + * @return this builder */ public CacheConfigBuilder timeout(int timeout, TimeUnit unit) { if (timeout <= 0) { diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java index 792590f..19af719 100644 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java @@ -13,7 +13,6 @@ import com.google.common.collect.ImmutableMap; import feign.httpclient.ApacheHttpClient; import io.ipdata.client.error.IpdataException; -import io.ipdata.client.error.RateLimitException; import io.ipdata.client.model.AsnModel; import io.ipdata.client.model.Currency; import io.ipdata.client.model.IpdataModel; From 58074c9ca7a2c8f0271b4437b59614273de24ae1 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 21:16:42 +0100 Subject: [PATCH 11/54] added gpg siging result --- .gitignore | 1 + .travis.yml | 20 +++++- .travis/ipdata-java.crypted.gpg.key | 104 ++++++++++++++++++++++++++++ pom.groovy | 3 + 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 .travis/ipdata-java.crypted.gpg.key diff --git a/.gitignore b/.gitignore index 0ff5580..fc2d930 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ buildNumber.properties out gen NOTES +.travis/ipdata.out.key diff --git a/.travis.yml b/.travis.yml index 9fd96eb..49415c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,12 +3,28 @@ dist: trusty language: java jdk: - openjdk8 -script: - - mvn clean test post-integration-test sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client addons: sonarcloud: organization: yassine-github token: secure: PYT5/IbiqFd877vdi+CYTx7srXr5ACvAgBSL4rxPqwVSQUdGIZxVTj8JcZp7Cl2WiDj9zDlTHSDaJDt5ZpXnmN5quJBizuCZDVArm3LoRADjxhOd9N8rQEYEE1TZOHlu1+g1i/abOL+av9Spn7lWk1LvuH5BW3b0GiAdqJFevfnJsUYDh4iYHiMYjTIP5dO/cZUnQzgFgZS6YkMA28cgQIy/F03z6tO9oM6kcJ/DGBnqvdGAT2ZxtWhFaaNwJazlxSePCwnOok+v3vgACzGospTJLQ4AdGklYIK+ljGpz7AbiRs7yhvzsib6i5Wr0IEmZmLiyIgjExPdnV+rXAndgig/0bnhv1YzyEf/hCzMczpHvNa0HDAGCxkTIsw76XlumwPMGURdlypWEoWrz/9cwiPMnQ0XcAOTN56msA5nBbNwnJ9KDKIVWKWg447OhbImlTr+qxHI/L+8e1dFxyA1iBg7/wtU/27V2QRABvhdlVGNn1khdkIKhsueYHbBjloUTILHBFKTtJdZdD2z/Vf9cu51EQDwv5IPfkkz/UER8pw4AVKgFi6v57M3unqE5C6g8dZr5CELAqy3tGjf6v9HaTF3AdGEkyIJuxiiO8LM20MJm+IreR/pIv8SVeT7f747hbPCQ+mMe5rSE4KVtSI88OeUcI193pdd5PBg0jU262I= +jobs: + include: + - stage: test + script: + - mvn clean test post-integration-test + - stage: quality-report + script: + - mvn clean sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client + - stage: package + script: + - openssl enc -aes-256-cbc -d -in ./.travis/ipdata-java.crypted.gpg.key -out ./.travis/ipdata.out.key -base64 -K $GPG_AES_KEY -iv $GPG_AES_KEY_IV -S $GPG_AES_KEY_SALT + - echo $GPG_PASSPHRASE | gpg --import --batch --yes --passphrase-fd 0 ./.travis/ipdata.out.key + - mvn package verify -Prelease -DskipTests=true -Dgpg.keyname=$GPG_KEY_NAME -Dgpg.passphrase=$GPG_PASSPHRASE +stages: + - test + - quality-report + - name: package + if: branch = releases notifications: email: false diff --git a/.travis/ipdata-java.crypted.gpg.key b/.travis/ipdata-java.crypted.gpg.key new file mode 100644 index 0000000..3d0f7e7 --- /dev/null +++ b/.travis/ipdata-java.crypted.gpg.key @@ -0,0 +1,104 @@ +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 +U+Rp4aeEC3Y4YxX8WAppkQ== diff --git a/pom.groovy b/pom.groovy index 7e20782..1bf88bd 100644 --- a/pom.groovy +++ b/pom.groovy @@ -53,6 +53,9 @@ project { profiles { profile { id 'release' + properties { + 'gpg.executable' 'gpg2' + } build { plugins { plugin('org.apache.maven.plugins:maven-gpg-plugin:1.6') { From 622cec8278d16b976d7afe8ab6eaf5cd59d01a41 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 21:32:36 +0100 Subject: [PATCH 12/54] disabled cleaning compiled classes --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 49415c1..9cac98d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ jobs: - mvn clean test post-integration-test - stage: quality-report script: - - mvn clean sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client + - mvn sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client - stage: package script: - openssl enc -aes-256-cbc -d -in ./.travis/ipdata-java.crypted.gpg.key -out ./.travis/ipdata.out.key -base64 -K $GPG_AES_KEY -iv $GPG_AES_KEY_IV -S $GPG_AES_KEY_SALT From 07fe1da9503b580ba04e3b87d2f732d237bef452 Mon Sep 17 00:00:00 2001 From: yassine Date: Tue, 28 Jan 2020 21:59:26 +0100 Subject: [PATCH 13/54] updated tests --- .travis.yml | 10 +++------- pom.groovy | 2 +- .../java/io/ipdata/client/IpdataFunctionalTest.java | 6 +++--- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9cac98d..263ca93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,19 +12,15 @@ jobs: include: - stage: test script: - - mvn clean test post-integration-test - - stage: quality-report - script: - - mvn sonar:sonar -Dsonar.projectKey=yassine_ipdata-java-client - - stage: package + - mvn clean test post-integration-test sonar:sonar + - stage: release script: - openssl enc -aes-256-cbc -d -in ./.travis/ipdata-java.crypted.gpg.key -out ./.travis/ipdata.out.key -base64 -K $GPG_AES_KEY -iv $GPG_AES_KEY_IV -S $GPG_AES_KEY_SALT - echo $GPG_PASSPHRASE | gpg --import --batch --yes --passphrase-fd 0 ./.travis/ipdata.out.key - mvn package verify -Prelease -DskipTests=true -Dgpg.keyname=$GPG_KEY_NAME -Dgpg.passphrase=$GPG_PASSPHRASE stages: - test - - quality-report - - name: package + - name: release if: branch = releases notifications: email: false diff --git a/pom.groovy b/pom.groovy index 1bf88bd..acac4ac 100644 --- a/pom.groovy +++ b/pom.groovy @@ -28,7 +28,7 @@ project { 'version.build.jacoco' '0.8.4' 'version.build.surefire' '3.0.0-M3' 'sonar.organization' 'yassine-github' - 'sonar.coverage.jacoco.xmlReportPaths' 'target/site/code-coverage/jacoco.xml' + 'sonar.coverage.jacoco.xmlReportPaths' '${project.build.directory}/site/code-coverage/jacoco.xml' 'sonar.links.homepage' 'https://github.com/yassine/ipdata-java-client' 'sonar.links.issue' 'https://github.com/yassine/ipdata-java-client' 'sonar.links.scm' 'https://github.com/yassine/ipdata-java-client' diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java index 19af719..fabea27 100644 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java @@ -110,15 +110,15 @@ public void testCurrency() { @Test @SneakyThrows public void testFieldSelection() { - IpdataModel ipdataModel = ipdataService.getFields("8.8.8.8", IpdataField.ASN, IpdataField.CURRENCY); + IpdataModel ipdataModel = ipdataService.getFields("41.128.21.123", IpdataField.ASN, IpdataField.CURRENCY); String serialized = MAPPER.writeValueAsString(ipdataModel); - String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8", ImmutableMap.of("fields", "asn,currency")); + String expected = TestUtils.get(HTTP_CLIENT, "/41.128.21.123", ImmutableMap.of("fields", "asn,currency")); expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, IpdataModel.class)); assertEquals(serialized, expected, false); Assert.assertNull(ipdataModel.threat()); if (ipdataService == CACHING_IPDATA_SERVICE) { //value will be returned from cache now - ipdataModel = ipdataService.getFields("8.8.8.8", IpdataField.ASN, IpdataField.CURRENCY); + ipdataModel = ipdataService.getFields("41.128.21.123", IpdataField.ASN, IpdataField.CURRENCY, IpdataField.CARRIER); serialized = MAPPER.writeValueAsString(ipdataModel); assertEquals(serialized, expected, false); } From 9783776019e57ba6d8123381a8f39f211c5c6403 Mon Sep 17 00:00:00 2001 From: yassine Date: Wed, 29 Jan 2020 10:19:57 +0100 Subject: [PATCH 14/54] updated docs, renamed some service methods --- README.md | 99 +++++++++++++++---- pom.groovy | 3 +- src/main/java/io/ipdata/client/Ipdata.java | 2 +- .../client/service/IpdataInternalClient.java | 2 +- .../ipdata/client/service/IpdataService.java | 4 +- .../client/service/IpdataServiceSupport.java | 4 +- .../ipdata/client/IpdataFunctionalTest.java | 2 +- 7 files changed, 88 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 5760dc2..64453e2 100644 --- a/README.md +++ b/README.md @@ -6,42 +6,101 @@ [![Reliability](https://sonarcloud.io/api/project_badges/measure?metric=reliability_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) -An ipdata.co java client. +An 100% compliant [ipdata.co](https://ipdata.co) API java client. -Recipes: -1. I wanna call the api without caching results : -```java -URL url = new URL("https://api.ipdata.co"); -IpdataService ipdataService = Ipdata.builder().url(url) - .noCache() - .key("MY_KEY") - .get(); -IpdataModel model = ipdataService.get("1.1.1.1"); +**Table of Contents** + +- [Install](#install) +- [Use](#use) + - [Configuration](#configuration) + - [API](#create-an-instance) + - [Basic Usage](#basic-usage) + - [Single Field Selection](#single-field-selection) + - [Multiple Field Selection](#multiple-field-selection) + - [Bulk data](#bulk-data) + + +## Install +You can have the library from Maven Central. +``` + + co.ipdata.client + ipdata-java-client + TBD + ``` -2. I wanna call the api while caching results for further calls on the same IP (caching will yield you a latency of less than 1ms for requests that hit the cache): + +## Use + +### Configuration +A builder is available to help configure your client configuration, you'll have to provide the API endpoint and an [API key](https://ipdata.co/pricing.html): ```java +import io.ipdata.client.Ipdata; + +/.../ URL url = new URL("https://api.ipdata.co"); IpdataService ipdataService = Ipdata.builder().url(url) - .withDefaultCache() .key("MY_KEY").get(); -IpdataModel model = ipdataService.get("1.1.1.1"); //cache miss here -ipdataService.get("1.1.1.1"); //cache hit from here on ip address "1.1.1.1" +/.../ ``` +Optionally, you can configure a cache for faster access (less than 1ms latency on requests that hit the cache). -3. My 50% user session length is 30 minutes, and I don't want to have more than 8 * 1024 items in cache, -how can I do that? +The cache is configurable for time and space eviction policies: ```java URL url = new URL("https://api.ipdata.co"); IpdataService ipdataService = Ipdata.builder().url(url) .withCache() - .timeout(30, TimeUnit.MINUTES) - .maxSize(8 * 1024) + .timeout(30, TimeUnit.MINUTES) //ttl after first write + .maxSize(8 * 1024) //no more than 8*1024 items shall be stored in cache .registerCacheConfig() .key("MY_KEY") .get(); -IpdataModel model = ipdataService.get("1.1.1.1"); //cache miss here -ipdataService.get("1.1.1.1"); //cache hit from here on ip address "1.1.1.1" +IpdataModel model = ipdataService.ipdata("1.1.1.1"); //cache miss here +ipdataService.ipdata("1.1.1.1"); //cache hit from now on on ip address "1.1.1.1" +``` + +### API +The client is fully compliant with the API. The data model of the api is available under the package ``io.ipdata.client.model``. +Interaction with the API is captured by the Service Interface ``io.ipdata.client.service.IpdataService``: + +#### Basic Usage +To get all available information about a given IP address, you can use the ``get`` method of the service Interface: +```java +IpdataModel model = ipdataService.ipdata("1.1.1.1"); +System.out.println(jsonSerialize(model)); ``` +#### Single Field Selection +If you're interested in only one field from the model capturing an IP address information, The service interface +exposes some a method on each available field: + +```java +boolean isEu = ipdataService.isEu("1.1.1.1"); +AsnModel asn = ipdataService.asn("1.1.1.1"); +TimeZone tz = ipdataService.timeZone("1.1.1.1"); +ThreatModel threat = ipdataService.threat("1.1.1.1"); +/*...*/ +``` +The list of available fields is available [here](https://docs.ipdata.co/api-reference/response-fields) + +#### Multiple Field Selection +If you're interested by multiple fields for a given IP address, you'll use the ``getFields`` method: +```java +import io.ipdata.client.service.IpdataField; +import io.ipdata.client.service.IpdataService; + +/* The model will be hydrated by the selected fields only */ +IpdataModel model = ipdataService.getFields("1.1.1.1", IpdataField.ASN, IpdataField.CURRENCY); + +``` + +### Bulk data +You can as well get multiple responses at once by using the ``bulk`` api: + +```java +List models = ipdataService.bulkIpdata(Arrays.asList("1.1.1.1", "8.8.8.8")); +``` + + diff --git a/pom.groovy b/pom.groovy index acac4ac..89b43ff 100644 --- a/pom.groovy +++ b/pom.groovy @@ -27,6 +27,7 @@ project { 'version.client.feign' '9.7.0' 'version.build.jacoco' '0.8.4' 'version.build.surefire' '3.0.0-M3' + 'version.build.sonar' '3.7.0.1746' 'sonar.organization' 'yassine-github' 'sonar.coverage.jacoco.xmlReportPaths' '${project.build.directory}/site/code-coverage/jacoco.xml' 'sonar.links.homepage' 'https://github.com/yassine/ipdata-java-client' @@ -112,7 +113,7 @@ project { argLine '${surefireArgLine}' } } - plugin('org.codehaus.mojo:sonar-maven-plugin:3.6.0.1398') + plugin('org.codehaus.mojo:sonar-maven-plugin:${version.build.sonar}') plugin('org.apache.maven.plugins:maven-source-plugin:3.2.1') { executions { execution('attach-sources') { diff --git a/src/main/java/io/ipdata/client/Ipdata.java b/src/main/java/io/ipdata/client/Ipdata.java index 0b8a860..adc2840 100644 --- a/src/main/java/io/ipdata/client/Ipdata.java +++ b/src/main/java/io/ipdata/client/Ipdata.java @@ -68,7 +68,7 @@ public Builder noCache(){ */ public Builder withDefaultCache() { return new CacheConfigBuilder(this) - .maxSize(Long.MAX_VALUE) + .maxSize(1024) .timeout(4, TimeUnit.HOURS) .registerCacheConfig(); } diff --git a/src/main/java/io/ipdata/client/service/IpdataInternalClient.java b/src/main/java/io/ipdata/client/service/IpdataInternalClient.java index c02e7ab..4ac0802 100644 --- a/src/main/java/io/ipdata/client/service/IpdataInternalClient.java +++ b/src/main/java/io/ipdata/client/service/IpdataInternalClient.java @@ -13,7 +13,7 @@ interface IpdataInternalClient { IpdataModel ipdata(@Param("ip") String ip) throws IpdataException; @RequestLine("POST /bulk") - List bulkIpdata(List ips) throws IpdataException; + List bulk(List ips) throws IpdataException; @Cacheable @RequestLine("GET /{ip}?fields={fields}") diff --git a/src/main/java/io/ipdata/client/service/IpdataService.java b/src/main/java/io/ipdata/client/service/IpdataService.java index 6e13069..8b05f7a 100644 --- a/src/main/java/io/ipdata/client/service/IpdataService.java +++ b/src/main/java/io/ipdata/client/service/IpdataService.java @@ -9,9 +9,9 @@ public interface IpdataService extends IpdataInternalSingleFieldClient { IpdataModel ipdata(String ip) throws IpdataException; - List bulkIpdata(List ips) throws IpdataException; + List bulk(List ips) throws IpdataException; - IpdataModel[] bulkIpdataAsArray(List ips) throws IpdataException; + IpdataModel[] bulkAsArray(List ips) throws IpdataException; IpdataModel getFields(String ip, IpdataField... fields) throws IpdataException; } diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java index bac4378..47cd736 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java @@ -30,8 +30,8 @@ private IpdataInternalSingleFieldClient getApi() { } @Override - public IpdataModel[] bulkIpdataAsArray(List ips) throws IpdataException { - return bulkIpdata(ips).toArray(new IpdataModel[0]); + public IpdataModel[] bulkAsArray(List ips) throws IpdataException { + return bulk(ips).toArray(new IpdataModel[0]); } @Override diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java index fabea27..79878f4 100644 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java @@ -135,7 +135,7 @@ public void testSingleFields() { @SneakyThrows @Test public void testBulkResponse() { - List ipdataModels = ipdataService.bulkIpdata(Arrays.asList("8.8.8.8", "1.1.1.1")); + List ipdataModels = ipdataService.bulk(Arrays.asList("8.8.8.8", "1.1.1.1")); String serialized = MAPPER.writeValueAsString(ipdataModels); String expected = TestUtils.post(HTTP_CLIENT, "/bulk", "[\"8.8.8.8\",\"1.1.1.1\"]", null); expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, IpdataModel[].class)); From 9d96be3a2bbd6f6418067da7e928607395069c30 Mon Sep 17 00:00:00 2001 From: yassine Date: Wed, 29 Jan 2020 10:24:37 +0100 Subject: [PATCH 15/54] fixed docs typo, added sample output --- README.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 64453e2..63bfa4a 100644 --- a/README.md +++ b/README.md @@ -65,12 +65,71 @@ The client is fully compliant with the API. The data model of the api is availab Interaction with the API is captured by the Service Interface ``io.ipdata.client.service.IpdataService``: #### Basic Usage -To get all available information about a given IP address, you can use the ``get`` method of the service Interface: +To get all available information about a given IP address, you can use the ``ipdata`` method of the service Interface: ```java IpdataModel model = ipdataService.ipdata("1.1.1.1"); System.out.println(jsonSerialize(model)); ``` +Output: +```json +{ + "ip": "1.1.1.1", + "is_eu": false, + "city": null, + "region": null, + "region_code": null, + "country_name": "Australia", + "country_code": "AU", + "continent_name": "Oceania", + "continent_code": "OC", + "latitude": -33.494, + "longitude": 143.2104, + "postal": null, + "calling_code": "61", + "flag": "https://ipdata.co/flags/au.png", + "emoji_flag": "\ud83c\udde6\ud83c\uddfa", + "emoji_unicode": "U+1F1E6 U+1F1FA", + "asn": { + "asn": "AS13335", + "name": "Cloudflare, Inc.", + "domain": "cloudflare.com", + "route": "1.1.1.0/24", + "type": "hosting" + }, + "languages": [ + { + "name": "English", + "native": "English" + } + ], + "currency": { + "name": "Australian Dollar", + "code": "AUD", + "symbol": "AU$", + "native": "$", + "plural": "Australian dollars" + }, + "time_zone": { + "name": "Australia/Sydney", + "abbr": "AEDT", + "offset": "+1100", + "is_dst": true, + "current_time": "2020-01-29T20:22:52.283874+11:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false + }, + "count": "0" +} +``` + #### Single Field Selection If you're interested in only one field from the model capturing an IP address information, The service interface exposes some a method on each available field: From 2192d31184411d91c2f8bff9bdb720fa69824b25 Mon Sep 17 00:00:00 2001 From: yassine Date: Wed, 29 Jan 2020 10:26:35 +0100 Subject: [PATCH 16/54] fixed compiler error --- .../java/io/ipdata/client/service/CachingInternalClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/ipdata/client/service/CachingInternalClient.java b/src/main/java/io/ipdata/client/service/CachingInternalClient.java index bb62073..d14fcbf 100644 --- a/src/main/java/io/ipdata/client/service/CachingInternalClient.java +++ b/src/main/java/io/ipdata/client/service/CachingInternalClient.java @@ -93,8 +93,8 @@ public IpdataModel ipdata(String ip) throws IpdataException { } @Override - public List bulkIpdata(List ips) throws IpdataException { - return ipdataInternalClient.bulkIpdata(ips); + public List bulk(List ips) throws IpdataException { + return ipdataInternalClient.bulk(ips); } @Delegate(types = IpdataInternalSingleFieldClient.class, excludes = DelegateExcludes.class) From 49b6fb7d62aaef2e9509745604b55e67683ddb36 Mon Sep 17 00:00:00 2001 From: yassine Date: Wed, 29 Jan 2020 10:36:26 +0100 Subject: [PATCH 17/54] README update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63bfa4a..34fbdb9 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Output: #### Single Field Selection If you're interested in only one field from the model capturing an IP address information, The service interface -exposes some a method on each available field: +exposes a method on each available field: ```java boolean isEu = ipdataService.isEu("1.1.1.1"); @@ -158,7 +158,7 @@ IpdataModel model = ipdataService.getFields("1.1.1.1", IpdataField.ASN, IpdataFi You can as well get multiple responses at once by using the ``bulk`` api: ```java -List models = ipdataService.bulkIpdata(Arrays.asList("1.1.1.1", "8.8.8.8")); +List models = ipdataService.bulk(Arrays.asList("1.1.1.1", "8.8.8.8")); ``` From 3be9bcfb7d268b2aab1928aa248d4c0849f186c5 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 11:09:19 +0100 Subject: [PATCH 18/54] moved the VERSION file under the project main package to void potential classpath resolution conflicts --- pom.groovy | 2 +- .../java/io/ipdata/client/service/ApiKeyRequestInterceptor.java | 2 +- src/main/resources/{ => io/ipdata/client}/VERSION | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/main/resources/{ => io/ipdata/client}/VERSION (100%) diff --git a/pom.groovy b/pom.groovy index 89b43ff..035fdd3 100644 --- a/pom.groovy +++ b/pom.groovy @@ -133,7 +133,7 @@ project { resource { directory 'src/main/resources' filtering true - includes('VERSION') + includes('io/ipdata/client/VERSION') } } } diff --git a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java index f3b3c1a..07fbf22 100644 --- a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java +++ b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java @@ -19,7 +19,7 @@ class ApiKeyRequestInterceptor implements RequestInterceptor { String version; try { version = CharStreams.toString(new InputStreamReader(ApiKeyRequestInterceptor.class - .getResourceAsStream("/VERSION"))).replaceAll("\\n",""); + .getResourceAsStream("/io/ipdata/client/VERSION"))).replaceAll("\\n",""); version = String.format("io.ipdata.client.java.%s", version); }catch (Exception e){ log.error(e.getMessage(), e); diff --git a/src/main/resources/VERSION b/src/main/resources/io/ipdata/client/VERSION similarity index 100% rename from src/main/resources/VERSION rename to src/main/resources/io/ipdata/client/VERSION From 01ca2da47ab966a03aebf410e01e18dbc222cd18 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:33:25 +0100 Subject: [PATCH 19/54] moved to xml pom. Waiting for fix on https://github.com/takari/takari-lifecycle/issues/12 --- pom.groovy | 140 ---------------------------------- pom.xml | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+), 140 deletions(-) delete mode 100644 pom.groovy create mode 100644 pom.xml diff --git a/pom.groovy b/pom.groovy deleted file mode 100644 index 035fdd3..0000000 --- a/pom.groovy +++ /dev/null @@ -1,140 +0,0 @@ -project { - modelVersion '4.0.0' - groupId 'co.ipdata.client' - artifactId 'ipdata-java-client' - version '0.1.0-SNAPSHOT' - licenses { - license { - name 'The Apache Licence, Version 2.0' - url 'http://www.apache.org/licenses/LICENCE-2.0.txt' - } - } - scm { - connection 'git@github.com:yassine/ipdata-java-client.git' - developerConnection 'git@github.com:yassine/ipdata-java-client.git' - url 'https://github.com/yassine/ipdata-java-client' - } - developers { - developer { - name 'ipdata.co' - email 'support@ipdata.co' - } - } - properties { - 'project.build.sourceEncoding' 'UTF-8' - 'maven.compiler.source' '6' - 'maven.compiler.target' '6' - 'version.client.feign' '9.7.0' - 'version.build.jacoco' '0.8.4' - 'version.build.surefire' '3.0.0-M3' - 'version.build.sonar' '3.7.0.1746' - 'sonar.organization' 'yassine-github' - 'sonar.coverage.jacoco.xmlReportPaths' '${project.build.directory}/site/code-coverage/jacoco.xml' - 'sonar.links.homepage' 'https://github.com/yassine/ipdata-java-client' - 'sonar.links.issue' 'https://github.com/yassine/ipdata-java-client' - 'sonar.links.scm' 'https://github.com/yassine/ipdata-java-client' - 'sonar.projectKey' 'yassine_ipdata-java-client' - 'sonar.projectName' 'ipdata-java-client' - 'sonar.projectVersion' '${project.version}' - 'sonar.host.url' 'https://sonarcloud.io' - } - dependencies { - dependency('io.github.openfeign:feign-core:${version.client.feign}') - dependency('io.github.openfeign:feign-jackson:${version.client.feign}') - dependency('io.github.openfeign:feign-httpclient:${version.client.feign}') - dependency('com.google.guava:guava:20.0') - dependency('org.slf4j:slf4j-api:1.7.30') - dependency('org.slf4j:slf4j-log4j12:1.7.30') - dependency('org.projectlombok:lombok:1.18.10') - /* testing */ - dependency('org.hamcrest:hamcrest:2.2:test') - dependency('junit:junit:4.13:test') - dependency('org.skyscreamer:jsonassert:1.5.0:test') - } - profiles { - profile { - id 'release' - properties { - 'gpg.executable' 'gpg2' - } - build { - plugins { - plugin('org.apache.maven.plugins:maven-gpg-plugin:1.6') { - executions { - execution { - phase 'verify' - goals 'sign' - } - } - } - } - } - } - } - build { - plugins { - plugin('org.apache.maven.plugins:maven-resources-plugin:2.6') { - configuration { - encoding '${project.build.sourceEncoding}' - } - } - plugin('org.jacoco:jacoco-maven-plugin:${version.build.jacoco}') { - executions { - execution { - id 'prepare-agent' - phase 'test-compile' - goals 'prepare-agent' - configuration { - propertyName 'surefireArgLine' - destFile '${project.build.directory}/coverage-reports/jacoco-ut.exec' - } - } - execution { - id 'post-test-reports' - phase 'post-integration-test' - goals 'report' - configuration { - dataFile '${project.build.directory}/coverage-reports/jacoco-ut.exec' - outputDirectory '${project.reporting.outputDirectory}/code-coverage' - } - } - } - } - plugin { - artifactId 'maven-surefire-plugin' - version '${version.build.surefire}' - configuration { - useFile 'false' - includes {} - additionalClasspathElements { - additionalClasspathElement '${project.basedir}/src/test/resources' - additionalClasspathElement '${project.build.testOutputDirectory}' - } - argLine '${surefireArgLine}' - } - } - plugin('org.codehaus.mojo:sonar-maven-plugin:${version.build.sonar}') - plugin('org.apache.maven.plugins:maven-source-plugin:3.2.1') { - executions { - execution('attach-sources') { - goals('jar') - } - } - } - plugin('org.apache.maven.plugins:maven-javadoc-plugin:3.1.1') { - executions { - execution('attach-javadocs') { - goals('jar') - } - } - } - } - resources { - resource { - directory 'src/main/resources' - filtering true - includes('io/ipdata/client/VERSION') - } - } - } -} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..cea2fd9 --- /dev/null +++ b/pom.xml @@ -0,0 +1,220 @@ + + + 4.0.0 + co.ipdata.client + ipdata-java-client + 0.1.0 + + + The Apache Licence, Version 2.0 + http://www.apache.org/licenses/LICENCE-2.0.txt + + + + + ipdata.co + support@ipdata.co + + + + scm:git:git@github.com:ipdata/java.git + scm:git:git@github.com:ipdata/java.git + https://github.com/ipdata/java + ipdata-java-client-0.1.0 + + + + maven-central-staging + https://oss.sonatype.org/service/local/staging/deploy/maven2 + + + maven-central-snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + + ipdata-java-client + 0.8.4 + 3.7.0.1746 + 6 + https://sonarcloud.io + ${project.version} + yassine-github + 6 + yassine_ipdata-java-client + 3.0.0-M3 + https://github.com/yassine/ipdata-java-client + 9.7.0 + https://github.com/yassine/ipdata-java-client + UTF-8 + https://github.com/yassine/ipdata-java-client + ${project.build.directory}/site/code-coverage/jacoco.xml + + + + io.github.openfeign + feign-core + ${version.client.feign} + + + io.github.openfeign + feign-jackson + ${version.client.feign} + + + io.github.openfeign + feign-httpclient + ${version.client.feign} + + + com.google.guava + guava + 20.0 + + + org.slf4j + slf4j-api + 1.7.30 + + + org.slf4j + slf4j-log4j12 + 1.7.30 + + + org.projectlombok + lombok + 1.18.10 + + + org.hamcrest + hamcrest + 2.2 + test + + + junit + junit + 4.13 + test + + + org.skyscreamer + jsonassert + 1.5.0 + test + + + + + + true + src/main/resources + + io/ipdata/client/VERSION + + + + + + maven-resources-plugin + 2.6 + + ${project.build.sourceEncoding} + + + + org.jacoco + jacoco-maven-plugin + ${version.build.jacoco} + + + prepare-agent + test-compile + + prepare-agent + + + surefireArgLine + ${project.build.directory}/coverage-reports/jacoco-ut.exec + + + + post-test-reports + post-integration-test + + report + + + ${project.build.directory}/coverage-reports/jacoco-ut.exec + ${project.reporting.outputDirectory}/code-coverage + + + + + + maven-surefire-plugin + ${version.build.surefire} + + false + + + ${project.basedir}/src/test/resources + ${project.build.testOutputDirectory} + + ${surefireArgLine} + + + + org.codehaus.mojo + sonar-maven-plugin + ${version.build.sonar} + + + maven-source-plugin + 3.2.1 + + + + jar + + + + + + maven-javadoc-plugin + 3.1.1 + + + + jar + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + maven-central-staging + https://oss.sonatype.org + + + + + maven-gpg-plugin + 1.6 + + + verify + + sign + + + + + + + From d805fb519280247c74c8674b03ca4250207ec725 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:35:17 +0100 Subject: [PATCH 20/54] adjusted version to snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cea2fd9..b7ef43f 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0 + 0.1.0-SNAPSHOT The Apache Licence, Version 2.0 From 37afffb032e1d000f1295500fe70d844d606b166 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:35:53 +0100 Subject: [PATCH 21/54] [maven-release-plugin] prepare release ipdata-java-client-0.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b7ef43f..cea2fd9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0-SNAPSHOT + 0.1.0 The Apache Licence, Version 2.0 From 26f21f24a69432e4b398d9eeb25aa5a280756c94 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:36:41 +0100 Subject: [PATCH 22/54] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index cea2fd9..a45185d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0 + 0.1.1-SNAPSHOT The Apache Licence, Version 2.0 From 62d758b38b9471ea824f35f49b09e9f9ebb3a1fb Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:45:44 +0100 Subject: [PATCH 23/54] added key name to gpg plugin --- pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a45185d..8df1914 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.1-SNAPSHOT + 0.1.0-SNAPSHOT The Apache Licence, Version 2.0 @@ -206,6 +206,9 @@ maven-gpg-plugin 1.6 + + A143FB3D9A4D90CC76DE768495FD65EAEFC32F7E + verify From 564e2273324ffe6c211444461b0e7ef6bd40a6a4 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:48:09 +0100 Subject: [PATCH 24/54] [maven-release-plugin] prepare release ipdata-java-client-0.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8df1914..e67b97a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0-SNAPSHOT + 0.1.0 The Apache Licence, Version 2.0 From c2b957dea95cbff464dbdf8bf510bad0a0b8b14b Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:48:18 +0100 Subject: [PATCH 25/54] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e67b97a..9e4ad3a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0 + 0.1.1-SNAPSHOT The Apache Licence, Version 2.0 From 4ff9c58ee50be18dd0aa75da157e96e9219cd39e Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:55:02 +0100 Subject: [PATCH 26/54] added info on pom --- pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9e4ad3a..ea03e7c 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,10 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.1-SNAPSHOT + 0.1.0-SNAPSHOT + A java client for ipdata.co + Ipdata java client + https://github.com/ipdata/java The Apache Licence, Version 2.0 From a0cffbb664b7d41dfb1e0e513b3913dcf3a44c97 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:56:56 +0100 Subject: [PATCH 27/54] [maven-release-plugin] prepare release ipdata-java-client-0.1.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ea03e7c..0c45920 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0-SNAPSHOT + 0.1.0 A java client for ipdata.co Ipdata java client https://github.com/ipdata/java From a289c49b74e96646e9faab9b48cacc15812d4f84 Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 18:57:07 +0100 Subject: [PATCH 28/54] [maven-release-plugin] prepare for next development iteration --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0c45920..f90b9bd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.0 + 0.1.1-SNAPSHOT A java client for ipdata.co Ipdata java client https://github.com/ipdata/java From 3d822e68c78c6350a1809f435f56ed7f0fd7463a Mon Sep 17 00:00:00 2001 From: yassine Date: Thu, 30 Jan 2020 19:24:37 +0100 Subject: [PATCH 29/54] Updated README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 34fbdb9..5260765 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ You can have the library from Maven Central. co.ipdata.client ipdata-java-client - TBD + 0.1.0 ``` From dd5604accfcef095aaa5d5117e3b43257e48681c Mon Sep 17 00:00:00 2001 From: Jonathan Kosgei Date: Fri, 20 Mar 2020 02:53:34 +0300 Subject: [PATCH 30/54] Updated LICENSE to MIT --- LICENSE | 222 ++++++-------------------------------------------------- 1 file changed, 21 insertions(+), 201 deletions(-) diff --git a/LICENSE b/LICENSE index f49a4e1..f8f5dd9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file +MIT License + +Copyright (c) 2020 IPdata + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 4dcb06f3ac33801329ecd947eeee8f8c92d94e75 Mon Sep 17 00:00:00 2001 From: Yassine E Date: Fri, 12 Jun 2020 14:22:37 +0100 Subject: [PATCH 31/54] =?UTF-8?q?Bug=20Fix=20&=20improvements=20-=20Fix=20?= =?UTF-8?q?for=20issue=20n=C2=B0=202=20-=20Test=20extension=20&=20refactor?= =?UTF-8?q?ing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 28 +-- src/main/java/io/ipdata/client/Ipdata.java | 9 +- .../java/io/ipdata/client/model/AsnModel.java | 8 + .../io/ipdata/client/model/IpdataModel.java | 16 +- .../java/io/ipdata/client/model/Language.java | 2 +- .../java/io/ipdata/client/model/TimeZone.java | 6 +- .../client/service/ApiErrorDecoder.java | 8 +- .../service/ApiKeyRequestInterceptor.java | 18 +- .../io/ipdata/client/service/CacheConfig.java | 3 +- .../client/service/CachingInternalClient.java | 43 ++-- .../ipdata/client/service/FieldDecoder.java | 3 +- .../io/ipdata/client/service/IpdataField.java | 6 +- .../client/service/IpdataInternalClient.java | 1 + .../ipdata/client/service/IpdataService.java | 2 +- .../client/service/IpdataServiceBuilder.java | 13 +- .../client/service/IpdataServiceSupport.java | 5 +- src/test/java/io/ipdata/client/AsnTest.java | 64 +++++ src/test/java/io/ipdata/client/BulkTest.java | 38 +++ .../java/io/ipdata/client/CurrencyTest.java | 60 +++++ .../java/io/ipdata/client/FullModelTest.java | 75 ++++++ .../ipdata/client/IpdataFunctionalTest.java | 238 ------------------ .../java/io/ipdata/client/MappingTest.java | 23 ++ .../client/MultipleFieldsSelectionTest.java | 42 ++++ .../java/io/ipdata/client/TestContext.java | 112 +++++++++ src/test/java/io/ipdata/client/TestUtils.java | 52 ---- .../java/io/ipdata/client/ThreatTest.java | 59 +++++ .../java/io/ipdata/client/TimeZoneTest.java | 63 +++++ .../resources/io/ipdata/client/fixture.json | 60 +++++ src/test/resources/log4j.properties | 3 +- 29 files changed, 690 insertions(+), 370 deletions(-) create mode 100644 src/test/java/io/ipdata/client/AsnTest.java create mode 100644 src/test/java/io/ipdata/client/BulkTest.java create mode 100644 src/test/java/io/ipdata/client/CurrencyTest.java create mode 100644 src/test/java/io/ipdata/client/FullModelTest.java delete mode 100644 src/test/java/io/ipdata/client/IpdataFunctionalTest.java create mode 100644 src/test/java/io/ipdata/client/MappingTest.java create mode 100644 src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java create mode 100644 src/test/java/io/ipdata/client/TestContext.java delete mode 100644 src/test/java/io/ipdata/client/TestUtils.java create mode 100644 src/test/java/io/ipdata/client/ThreatTest.java create mode 100644 src/test/java/io/ipdata/client/TimeZoneTest.java create mode 100644 src/test/resources/io/ipdata/client/fixture.json diff --git a/pom.xml b/pom.xml index f90b9bd..180d8bf 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ scm:git:git@github.com:ipdata/java.git scm:git:git@github.com:ipdata/java.git https://github.com/ipdata/java - ipdata-java-client-0.1.0 + ipdata-java-0.1.1 @@ -36,22 +36,22 @@ - ipdata-java-client - 0.8.4 - 3.7.0.1746 6 + 6 + UTF-8 + ${project.build.directory}/site/code-coverage/jacoco.xml https://sonarcloud.io - ${project.version} + https://github.com/ipdata/java + https://github.com/ipdata/java + https://github.com/ipdata/java yassine-github - 6 yassine_ipdata-java-client + ipdata-java-client + ${project.version} + 0.8.4 + 3.7.0.1746 3.0.0-M3 - https://github.com/yassine/ipdata-java-client 9.7.0 - https://github.com/yassine/ipdata-java-client - UTF-8 - https://github.com/yassine/ipdata-java-client - ${project.build.directory}/site/code-coverage/jacoco.xml @@ -102,9 +102,9 @@ test - org.skyscreamer - jsonassert - 1.5.0 + net.javacrumbs.json-unit + json-unit + 2.17.0 test diff --git a/src/main/java/io/ipdata/client/Ipdata.java b/src/main/java/io/ipdata/client/Ipdata.java index adc2840..5239d77 100644 --- a/src/main/java/io/ipdata/client/Ipdata.java +++ b/src/main/java/io/ipdata/client/Ipdata.java @@ -14,9 +14,9 @@ import org.slf4j.Logger; @UtilityClass -public final class Ipdata { +public class Ipdata { - public static Ipdata.Builder builder() { + public Ipdata.Builder builder() { return new Builder(); } @@ -53,10 +53,11 @@ public static class Builder { /** * Overrides current cache config with null (no caching). + * * @return this */ - public Builder noCache(){ - this .cacheConfig = null; + public Builder noCache() { + this.cacheConfig = null; return this; } diff --git a/src/main/java/io/ipdata/client/model/AsnModel.java b/src/main/java/io/ipdata/client/model/AsnModel.java index ab7b05f..fad0493 100644 --- a/src/main/java/io/ipdata/client/model/AsnModel.java +++ b/src/main/java/io/ipdata/client/model/AsnModel.java @@ -10,5 +10,13 @@ public class AsnModel { private String name; private String domain; private String route; + private String type; + + /** + * Deprecated + * + * @deprecated : See: https://github.com/ipdata/java/issues/2 + */ + @Deprecated private String isp; } diff --git a/src/main/java/io/ipdata/client/model/IpdataModel.java b/src/main/java/io/ipdata/client/model/IpdataModel.java index 2ed05cd..251a5bb 100644 --- a/src/main/java/io/ipdata/client/model/IpdataModel.java +++ b/src/main/java/io/ipdata/client/model/IpdataModel.java @@ -1,13 +1,14 @@ package io.ipdata.client.model; import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.List; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; +import java.util.List; + @Setter(AccessLevel.PACKAGE) @ToString @Getter @@ -17,11 +18,13 @@ public class IpdataModel { @JsonProperty("is_eu") private boolean eu; private String city; - private String organization; + private String organisation; + private String region; private String regionCode; private String countryName; private String countryCode; + private String continentName; private String continentCode; private double latitude; private double longitude; @@ -37,7 +40,14 @@ public class IpdataModel { private TimeZone timeZone; private ThreatModel threat; //meta - private int count; + private String count; + + /** + * Rely on organisation field instead. + * @deprecated Use organisation instead + */ + @Deprecated + private String organization; public boolean isEu() { return eu; diff --git a/src/main/java/io/ipdata/client/model/Language.java b/src/main/java/io/ipdata/client/model/Language.java index 365e417..395358c 100644 --- a/src/main/java/io/ipdata/client/model/Language.java +++ b/src/main/java/io/ipdata/client/model/Language.java @@ -10,5 +10,5 @@ public class Language { private String name; @JsonProperty("native") private String nativeName; - private boolean rtl; + private int rtl; } diff --git a/src/main/java/io/ipdata/client/model/TimeZone.java b/src/main/java/io/ipdata/client/model/TimeZone.java index 48db583..a71e820 100644 --- a/src/main/java/io/ipdata/client/model/TimeZone.java +++ b/src/main/java/io/ipdata/client/model/TimeZone.java @@ -1,5 +1,6 @@ package io.ipdata.client.model; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.ToString; import lombok.experimental.Accessors; @@ -10,6 +11,7 @@ public class TimeZone { private String name; private String abbr; private String offset; - private String isDst; - private String currencyTime; + @JsonProperty("is_dst") + private boolean dst; + private String currentTime; } diff --git a/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java b/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java index e0268e4..e526eb7 100644 --- a/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java +++ b/src/main/java/io/ipdata/client/service/ApiErrorDecoder.java @@ -7,13 +7,15 @@ import io.ipdata.client.error.RateLimitException; import io.ipdata.client.error.RemoteIpdataException; import io.ipdata.client.model.Error; -import java.io.IOException; -import java.net.URL; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; -@RequiredArgsConstructor @Slf4j +import java.io.IOException; +import java.net.URL; + +@RequiredArgsConstructor +@Slf4j public class ApiErrorDecoder implements ErrorDecoder { private static final int RATE_LIMIT_STATUS = 403; private final ObjectMapper mapper; diff --git a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java index 07fbf22..bfacdd7 100644 --- a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java +++ b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java @@ -3,30 +3,34 @@ import com.google.common.io.CharStreams; import feign.RequestInterceptor; import feign.RequestTemplate; -import java.io.InputStreamReader; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -@RequiredArgsConstructor @Slf4j +import java.io.InputStreamReader; + +@RequiredArgsConstructor +@Slf4j class ApiKeyRequestInterceptor implements RequestInterceptor { private static final String API_KEY_PARAM = "api-key"; private static final String ACCEPT_HEADER = "Accept"; - private static final String JSON_CONTENT = "application/json"; + private static final String JSON_CONTENT = "application/json"; private static final String API_CLIENT_HEADER = "User-Agent"; private static final String API_CLIENT_VALUE; + static { String version; try { version = CharStreams.toString(new InputStreamReader(ApiKeyRequestInterceptor.class - .getResourceAsStream("/io/ipdata/client/VERSION"))).replaceAll("\\n",""); - version = String.format("io.ipdata.client.java.%s", version); - }catch (Exception e){ + .getResourceAsStream("/io/ipdata/client/VERSION"))).replaceAll("\\n", ""); + version = String.format("io.ipdata.client.java.%s", version); + } catch (Exception e) { log.error(e.getMessage(), e); - version = "io.ipdata.client.java.UNKNOWN"; + version = "io.ipdata.client.java.UNKNOWN"; } API_CLIENT_VALUE = version; } + private final String key; @Override diff --git a/src/main/java/io/ipdata/client/service/CacheConfig.java b/src/main/java/io/ipdata/client/service/CacheConfig.java index 75b2f1b..714f7aa 100644 --- a/src/main/java/io/ipdata/client/service/CacheConfig.java +++ b/src/main/java/io/ipdata/client/service/CacheConfig.java @@ -1,11 +1,12 @@ package io.ipdata.client.service; -import java.util.concurrent.TimeUnit; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; +import java.util.concurrent.TimeUnit; + @RequiredArgsConstructor @Builder @Getter diff --git a/src/main/java/io/ipdata/client/service/CachingInternalClient.java b/src/main/java/io/ipdata/client/service/CachingInternalClient.java index d14fcbf..30aa99f 100644 --- a/src/main/java/io/ipdata/client/service/CachingInternalClient.java +++ b/src/main/java/io/ipdata/client/service/CachingInternalClient.java @@ -1,42 +1,33 @@ package io.ipdata.client.service; -import com.google.common.annotations.VisibleForTesting; import com.google.common.cache.LoadingCache; import io.ipdata.client.error.IpdataException; -import io.ipdata.client.model.AsnModel; -import io.ipdata.client.model.Currency; -import io.ipdata.client.model.IpdataModel; -import io.ipdata.client.model.ThreatModel; -import io.ipdata.client.model.TimeZone; +import io.ipdata.client.model.*; +import lombok.Builder; +import lombok.experimental.Delegate; + import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -import lombok.Builder; -import lombok.experimental.Delegate; @Builder -@VisibleForTesting - -public class CachingInternalClient implements IpdataInternalClient, IpdataInternalSingleFieldClient { +class CachingInternalClient implements IpdataInternalClient, IpdataInternalSingleFieldClient { @Builder.Default - @SuppressWarnings("UnusedAssignment") //required by lombok builder - private int expiry = 4; + private final int expiry = 4; @Builder.Default - @SuppressWarnings("UnusedAssignment") //required by lombok builder - private TimeUnit unit = TimeUnit.HOURS; + private final TimeUnit unit = TimeUnit.HOURS; @Builder.Default - @SuppressWarnings("UnusedAssignment") //required by lombok builder - private Long maxSize = Long.MAX_VALUE; - private IpdataInternalClient ipdataInternalClient; - private IpdataInternalSingleFieldClient ipdataInternalSingleFieldClient; - - private LoadingCache ipdataCache; - private LoadingCache, IpdataModel> fieldsCache; - private LoadingCache asnCache; - private LoadingCache tzCache; - private LoadingCache currencyCache; - private LoadingCache threatCache; + private final Long maxSize = Long.MAX_VALUE; + private final IpdataInternalClient ipdataInternalClient; + private final IpdataInternalSingleFieldClient ipdataInternalSingleFieldClient; + + private final LoadingCache ipdataCache; + private final LoadingCache, IpdataModel> fieldsCache; + private final LoadingCache asnCache; + private final LoadingCache tzCache; + private final LoadingCache currencyCache; + private final LoadingCache threatCache; @Override public IpdataModel getFields(String ip, String fields) throws IpdataException { diff --git a/src/main/java/io/ipdata/client/service/FieldDecoder.java b/src/main/java/io/ipdata/client/service/FieldDecoder.java index d6a5eae..1eb1b58 100644 --- a/src/main/java/io/ipdata/client/service/FieldDecoder.java +++ b/src/main/java/io/ipdata/client/service/FieldDecoder.java @@ -4,9 +4,10 @@ import com.google.common.io.CharStreams; import feign.Response; import feign.codec.Decoder; +import lombok.RequiredArgsConstructor; + import java.io.IOException; import java.lang.reflect.Type; -import lombok.RequiredArgsConstructor; /** * The API returns usable but invalid(/unquoted) strings for String fields diff --git a/src/main/java/io/ipdata/client/service/IpdataField.java b/src/main/java/io/ipdata/client/service/IpdataField.java index 2dc32fb..d6d0655 100644 --- a/src/main/java/io/ipdata/client/service/IpdataField.java +++ b/src/main/java/io/ipdata/client/service/IpdataField.java @@ -1,10 +1,6 @@ package io.ipdata.client.service; -import io.ipdata.client.model.AsnModel; -import io.ipdata.client.model.Carrier; -import io.ipdata.client.model.Currency; -import io.ipdata.client.model.Language; -import io.ipdata.client.model.TimeZone; +import io.ipdata.client.model.*; import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/src/main/java/io/ipdata/client/service/IpdataInternalClient.java b/src/main/java/io/ipdata/client/service/IpdataInternalClient.java index 4ac0802..c013a2d 100644 --- a/src/main/java/io/ipdata/client/service/IpdataInternalClient.java +++ b/src/main/java/io/ipdata/client/service/IpdataInternalClient.java @@ -4,6 +4,7 @@ import feign.RequestLine; import io.ipdata.client.error.IpdataException; import io.ipdata.client.model.IpdataModel; + import java.util.List; @SuppressWarnings("RedundantThrows") diff --git a/src/main/java/io/ipdata/client/service/IpdataService.java b/src/main/java/io/ipdata/client/service/IpdataService.java index 8b05f7a..dce4ea3 100644 --- a/src/main/java/io/ipdata/client/service/IpdataService.java +++ b/src/main/java/io/ipdata/client/service/IpdataService.java @@ -2,9 +2,9 @@ import io.ipdata.client.error.IpdataException; import io.ipdata.client.model.IpdataModel; + import java.util.List; -@SuppressWarnings("RedundantThrows") public interface IpdataService extends IpdataInternalSingleFieldClient { IpdataModel ipdata(String ip) throws IpdataException; diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java index 486928e..af68fda 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java @@ -1,7 +1,5 @@ package io.ipdata.client.service; -import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; - import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.PropertyAccessor; @@ -14,16 +12,15 @@ import feign.httpclient.ApacheHttpClient; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; -import io.ipdata.client.model.AsnModel; -import io.ipdata.client.model.Currency; -import io.ipdata.client.model.IpdataModel; -import io.ipdata.client.model.ThreatModel; -import io.ipdata.client.model.TimeZone; -import java.net.URL; +import io.ipdata.client.model.*; import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.net.URL; + +import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; + @RequiredArgsConstructor(staticName = "of") public class IpdataServiceBuilder { diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java index 47cd736..7f7324e 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java @@ -3,13 +3,14 @@ import com.google.common.base.Joiner; import io.ipdata.client.error.IpdataException; import io.ipdata.client.model.IpdataModel; -import java.util.Arrays; -import java.util.List; import lombok.Builder; import lombok.RequiredArgsConstructor; import lombok.experimental.Delegate; import lombok.extern.slf4j.Slf4j; +import java.util.Arrays; +import java.util.List; + @RequiredArgsConstructor @Builder @Slf4j diff --git a/src/test/java/io/ipdata/client/AsnTest.java b/src/test/java/io/ipdata/client/AsnTest.java new file mode 100644 index 0000000..a4af7c3 --- /dev/null +++ b/src/test/java/io/ipdata/client/AsnTest.java @@ -0,0 +1,64 @@ +package io.ipdata.client; + +import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.error.IpdataException; +import io.ipdata.client.model.AsnModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.net.URL; +import java.util.concurrent.TimeUnit; + +import static java.util.Arrays.asList; +import static org.junit.Assert.assertNotNull; + +@RunWith(Parameterized.class) +public class AsnTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @Test + @SneakyThrows + public void testASN() { + AsnModel asn = ipdataService.asn("8.8.8.8"); + assertNotNull(asn.type()); /* See: https://github.com/ipdata/java/issues/2 */ + String actual = TEST_CONTEXT.mapper().writeValueAsString(asn); + String expected = TEST_CONTEXT.get("/8.8.8.8/asn", null); + TEST_CONTEXT.assertEqualJson(actual, expected); + if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { + //value will be returned from cache now + asn = ipdataService.asn("8.8.8.8"); + assertNotNull(asn.type()); + actual = TEST_CONTEXT.mapper().writeValueAsString(asn); + TEST_CONTEXT.assertEqualJson(actual, expected); + } + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testAsnError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build()) + ).get(); + serviceWithInvalidKey.asn("8.8.8.8"); + } + + @Parameterized.Parameters + public static Iterable data() { + return asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/java/io/ipdata/client/BulkTest.java b/src/test/java/io/ipdata/client/BulkTest.java new file mode 100644 index 0000000..b7e31aa --- /dev/null +++ b/src/test/java/io/ipdata/client/BulkTest.java @@ -0,0 +1,38 @@ +package io.ipdata.client; + +import io.ipdata.client.model.IpdataModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.List; + +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class BulkTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @SneakyThrows + @Test + public void testBulkResponse() { + List ipdataModels = ipdataService.bulk(Arrays.asList("8.8.8.8", "1.1.1.1")); + String actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModels); + String expected = TEST_CONTEXT.post("/bulk", "[\"8.8.8.8\",\"1.1.1.1\"]", null); + expected = TEST_CONTEXT.mapper().writeValueAsString(TEST_CONTEXT.mapper().readValue(expected, IpdataModel[].class)); + TEST_CONTEXT.assertEqualJson(expected, actual, TEST_CONTEXT.configuration().whenIgnoringPaths("[0].time_zone.current_time", "[1].time_zone.current_time", "[0].count", "[1].count")); + } + + @Parameterized.Parameters + public static Iterable data() { + return asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/java/io/ipdata/client/CurrencyTest.java b/src/test/java/io/ipdata/client/CurrencyTest.java new file mode 100644 index 0000000..a83a35d --- /dev/null +++ b/src/test/java/io/ipdata/client/CurrencyTest.java @@ -0,0 +1,60 @@ +package io.ipdata.client; + +import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.error.IpdataException; +import io.ipdata.client.model.Currency; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +@RunWith(Parameterized.class) +public class CurrencyTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @Test + @SneakyThrows + public void testCurrency() { + Currency currency = ipdataService.currency("8.8.8.8"); + String actual = TEST_CONTEXT.mapper().writeValueAsString(currency); + String expected = TEST_CONTEXT.get("/8.8.8.8/currency", null); + TEST_CONTEXT.assertEqualJson(actual, expected); + if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { + //value will be returned from cache now + currency = ipdataService.currency("8.8.8.8"); + actual = TEST_CONTEXT.mapper().writeValueAsString(currency); + TEST_CONTEXT.assertEqualJson(actual, expected); + } + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testCurrencyError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build()) + ).get(); + serviceWithInvalidKey.currency("8.8.8.8"); + } + + @Parameterized.Parameters + public static Iterable data() { + return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/java/io/ipdata/client/FullModelTest.java b/src/test/java/io/ipdata/client/FullModelTest.java new file mode 100644 index 0000000..53c74a1 --- /dev/null +++ b/src/test/java/io/ipdata/client/FullModelTest.java @@ -0,0 +1,75 @@ +package io.ipdata.client; + +import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.error.IpdataException; +import io.ipdata.client.model.IpdataModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import net.javacrumbs.jsonunit.JsonAssert; +import net.javacrumbs.jsonunit.core.Option; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class FullModelTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @Test + @SneakyThrows + public void testFullResponse() { + JsonAssert.setOptions(Option.TREATING_NULL_AS_ABSENT, Option.IGNORING_EXTRA_FIELDS); + IpdataModel ipdataModel = ipdataService.ipdata("8.8.8.8"); + String actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModel); + String expected = TEST_CONTEXT.get("/8.8.8.8", null); + TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("time_zone.current_time")); + if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { + //value will be returned from cache now + ipdataModel = ipdataService.ipdata("8.8.8.8"); + actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModel); + TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("time_zone.current_time")); + } + } + + + @SneakyThrows + @Test + public void testSingleFields() { + String field = ipdataService.getCountryName("8.8.8.8"); + String expected = TEST_CONTEXT.get("/8.8.8.8/country_name", null); + Assert.assertEquals(field, expected); + } + + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.ipdata("8.8.8.8"); + } + + @Parameters + public static Iterable data() { + return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java b/src/test/java/io/ipdata/client/IpdataFunctionalTest.java deleted file mode 100644 index 79878f4..0000000 --- a/src/test/java/io/ipdata/client/IpdataFunctionalTest.java +++ /dev/null @@ -1,238 +0,0 @@ -package io.ipdata.client; - -import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; -import static io.ipdata.client.TestUtils.KEY; -import static org.junit.runners.Parameterized.Parameters; -import static org.skyscreamer.jsonassert.JSONAssert.assertEquals; - -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.ImmutableMap; -import feign.httpclient.ApacheHttpClient; -import io.ipdata.client.error.IpdataException; -import io.ipdata.client.model.AsnModel; -import io.ipdata.client.model.Currency; -import io.ipdata.client.model.IpdataModel; -import io.ipdata.client.model.ThreatModel; -import io.ipdata.client.service.IpdataField; -import io.ipdata.client.service.IpdataService; -import java.net.URL; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; -import lombok.SneakyThrows; -import org.apache.http.client.HttpClient; -import org.apache.http.conn.ssl.NoopHostnameVerifier; -import org.apache.http.impl.client.HttpClientBuilder; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -@RunWith(Parameterized.class) -public class IpdataFunctionalTest { - - private static IpdataService IPDATA_SERVICE; - private static IpdataService CACHING_IPDATA_SERVICE; - private static ObjectMapper MAPPER; - private static HttpClient HTTP_CLIENT; - - @Parameterized.Parameter - public IpdataService ipdataService; - - @Test - @SneakyThrows - public void testFullresponse() { - IpdataModel ipdataModel = ipdataService.ipdata("8.8.8.8"); - String serialized = MAPPER.writeValueAsString(ipdataModel); - String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, IpdataModel.class)); - assertEquals(serialized, expected, false); - if (ipdataService == CACHING_IPDATA_SERVICE) { - //value will be returned from cache now - ipdataModel = ipdataService.ipdata("8.8.8.8"); - serialized = MAPPER.writeValueAsString(ipdataModel); - assertEquals(serialized, expected, false); - } - } - - @Test - @SneakyThrows - public void testASN() { - AsnModel asn = ipdataService.asn("8.8.8.8"); - String serialized = MAPPER.writeValueAsString(asn); - String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8/asn", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, AsnModel.class)); - assertEquals(serialized, expected, false); - if (ipdataService == CACHING_IPDATA_SERVICE) { - //value will be returned from cache now - asn = ipdataService.asn("8.8.8.8"); - serialized = MAPPER.writeValueAsString(asn); - assertEquals(serialized, expected, false); - } - } - - @Test - @SneakyThrows - public void testThreat() { - ThreatModel threat = ipdataService.threat("8.8.8.8"); - String serialized = MAPPER.writeValueAsString(threat); - String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8/threat", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, ThreatModel.class)); - assertEquals(serialized, expected, false); - if (ipdataService == CACHING_IPDATA_SERVICE) { - //value will be returned from cache now - threat = ipdataService.threat("8.8.8.8"); - serialized = MAPPER.writeValueAsString(threat); - assertEquals(serialized, expected, false); - } - } - - @Test - @SneakyThrows - public void testCurrency() { - Currency currency = ipdataService.currency("8.8.8.8"); - String serialized = MAPPER.writeValueAsString(currency); - String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8/currency", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, Currency.class)); - assertEquals(serialized, expected, false); - if (ipdataService == CACHING_IPDATA_SERVICE) { - //value will be returned from cache now - currency = ipdataService.currency("8.8.8.8"); - serialized = MAPPER.writeValueAsString(currency); - assertEquals(serialized, expected, false); - } - } - - @Test - @SneakyThrows - public void testFieldSelection() { - IpdataModel ipdataModel = ipdataService.getFields("41.128.21.123", IpdataField.ASN, IpdataField.CURRENCY); - String serialized = MAPPER.writeValueAsString(ipdataModel); - String expected = TestUtils.get(HTTP_CLIENT, "/41.128.21.123", ImmutableMap.of("fields", "asn,currency")); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, IpdataModel.class)); - assertEquals(serialized, expected, false); - Assert.assertNull(ipdataModel.threat()); - if (ipdataService == CACHING_IPDATA_SERVICE) { - //value will be returned from cache now - ipdataModel = ipdataService.getFields("41.128.21.123", IpdataField.ASN, IpdataField.CURRENCY, IpdataField.CARRIER); - serialized = MAPPER.writeValueAsString(ipdataModel); - assertEquals(serialized, expected, false); - } - } - - @SneakyThrows - @Test - public void testSingleFields() { - String field = ipdataService.getCountryName("8.8.8.8"); - String expected = TestUtils.get(HTTP_CLIENT, "/8.8.8.8/country_name", null); - Assert.assertEquals(field, expected); - } - - @SneakyThrows - @Test - public void testBulkResponse() { - List ipdataModels = ipdataService.bulk(Arrays.asList("8.8.8.8", "1.1.1.1")); - String serialized = MAPPER.writeValueAsString(ipdataModels); - String expected = TestUtils.post(HTTP_CLIENT, "/bulk", "[\"8.8.8.8\",\"1.1.1.1\"]", null); - expected = MAPPER.writeValueAsString(MAPPER.readValue(expected, IpdataModel[].class)); - assertEquals(serialized, expected, false); - } - - @SneakyThrows - @Test(expected = IpdataException.class) - public void testError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) - .key("THIS_IS_AN_INVALID_KEY") - .withDefaultCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) - .build())).get(); - serviceWithInvalidKey.ipdata("8.8.8.8"); - } - - @SneakyThrows - @Test(expected = IpdataException.class) - public void testAsnError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) - .key("THIS_IS_AN_INVALID_KEY") - .withDefaultCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) - .build())).get(); - serviceWithInvalidKey.asn("8.8.8.8"); - } - - @SneakyThrows - @Test(expected = IpdataException.class) - public void testThreatError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) - .key("THIS_IS_AN_INVALID_KEY") - .withDefaultCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) - .build())).get(); - serviceWithInvalidKey.threat("8.8.8.8"); - } - - @SneakyThrows - @Test(expected = IpdataException.class) - public void testTimeZoneError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) - .key("THIS_IS_AN_INVALID_KEY") - .withDefaultCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) - .build())).get(); - serviceWithInvalidKey.timeZone("8.8.8.8"); - } - - @SneakyThrows - @Test(expected = IpdataException.class) - public void testCurrencyError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) - .key("THIS_IS_AN_INVALID_KEY") - .withDefaultCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) - .build())).get(); - serviceWithInvalidKey.currency("8.8.8.8"); - } - - @Parameters - public static Iterable data() { - init(); - return Arrays.asList(IPDATA_SERVICE, CACHING_IPDATA_SERVICE); - } - - @SneakyThrows - public static void init() { - URL url = new URL("https://api.ipdata.co"); - MAPPER = new ObjectMapper(); - MAPPER.setPropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); - MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); - MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - MAPPER.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - HTTP_CLIENT = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build(); - IPDATA_SERVICE = Ipdata.builder().url(url).key(KEY) - .noCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build())).get(); - CACHING_IPDATA_SERVICE = Ipdata.builder().url(url) - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build())) - .withDefaultCache().key(KEY).get(); - } - -} diff --git a/src/test/java/io/ipdata/client/MappingTest.java b/src/test/java/io/ipdata/client/MappingTest.java new file mode 100644 index 0000000..0d5c5fd --- /dev/null +++ b/src/test/java/io/ipdata/client/MappingTest.java @@ -0,0 +1,23 @@ +package io.ipdata.client; + +import com.google.common.io.CharStreams; +import io.ipdata.client.model.IpdataModel; +import lombok.SneakyThrows; +import net.javacrumbs.jsonunit.core.Configuration; +import net.javacrumbs.jsonunit.core.Option; +import org.junit.Test; + +import java.io.InputStreamReader; + +public class MappingTest { + + private static final TestContext TEST_CONTEXT = new TestContext("dummy", "https://api.ipdata.co"); + + @Test @SneakyThrows + public void testMapping(){ + String actual = TEST_CONTEXT.mapper().writeValueAsString(TEST_CONTEXT.mapper().readValue(getClass().getResourceAsStream("fixture.json"), IpdataModel.class)); + String expected = CharStreams.toString(new InputStreamReader(getClass().getResourceAsStream("fixture.json"))); + TEST_CONTEXT.assertEqualJson(actual, expected, Configuration.empty().withOptions(Option.TREATING_NULL_AS_ABSENT).whenIgnoringPaths("languages[0].rtl")); + } + +} diff --git a/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java b/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java new file mode 100644 index 0000000..4d5993e --- /dev/null +++ b/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java @@ -0,0 +1,42 @@ +package io.ipdata.client; + +import com.google.common.collect.ImmutableMap; +import io.ipdata.client.model.IpdataModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static io.ipdata.client.service.IpdataField.*; +import static java.util.Arrays.asList; + +@RunWith(Parameterized.class) +public class MultipleFieldsSelectionTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @Test + @SneakyThrows + public void testMultipleFieldsSelection() { + IpdataModel ipdataModel = ipdataService.getFields("41.128.21.123", ASN, CURRENCY); + String actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModel); + String expected = TEST_CONTEXT.get("/41.128.21.123", ImmutableMap.of("fields", "asn,currency")); + TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration()); + if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { + //value will be returned from cache now + ipdataModel = ipdataService.getFields("41.128.21.123", ASN, CURRENCY, CARRIER); + actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModel); + TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration()); + } + } + + @Parameterized.Parameters + public static Iterable data() { + return asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/java/io/ipdata/client/TestContext.java b/src/test/java/io/ipdata/client/TestContext.java new file mode 100644 index 0000000..49597dd --- /dev/null +++ b/src/test/java/io/ipdata/client/TestContext.java @@ -0,0 +1,112 @@ +package io.ipdata.client; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.io.CharStreams; +import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.service.IpdataService; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.experimental.Accessors; +import net.javacrumbs.jsonunit.core.Configuration; +import net.javacrumbs.jsonunit.core.Option; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.utils.URIBuilder; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; + +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Map; + +import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; +import static java.lang.System.getenv; +import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals; + +@Getter +@Accessors(fluent = true) +public class TestContext { + + static final String DEFAULT_KEY_ENV_VAR = "IPDATACO_KEY"; + + private final ObjectMapper mapper; + private final HttpClient httpClient; + private final IpdataService ipdataService; + private final IpdataService cachingIpdataService; + private final String key; + private final URL url; + private final Configuration configuration = Configuration.empty().withOptions(Option.IGNORING_EXTRA_FIELDS, Option.TREATING_NULL_AS_ABSENT); + + public TestContext(String url) { + this(getenv(DEFAULT_KEY_ENV_VAR), url); + } + + @SneakyThrows + public TestContext(String key, String url) { + this.key = key; + this.url = new URL(url); + mapper = new ObjectMapper(); + mapper.setPropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); + mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); + httpClient = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + ipdataService = Ipdata.builder().url(this.url).key(this.key) + .noCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()) + .build()) + ).get(); + cachingIpdataService = Ipdata.builder().url(this.url) + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()) + .build())) + .withDefaultCache().key(key).get(); + } + + void assertEqualJson(String actual, String expected) { + assertJsonEquals(expected, actual, configuration); + } + + void assertEqualJson(String actual, String expected, Configuration configuration) { + assertJsonEquals(expected, actual, configuration); + } + + /* + Used to get responses of a raw http call, without logical proxying + */ + @SneakyThrows + String get(String path, Map params) { + URIBuilder builder = new URIBuilder(url.toString() + path); + builder.setParameter("api-key", this.key); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + builder.setParameter(entry.getKey(), entry.getValue()); + } + } + HttpGet httpget = new HttpGet(builder.build()); + HttpResponse response = httpClient.execute(httpget); + return CharStreams.toString(new InputStreamReader(response.getEntity().getContent())); + } + + @SneakyThrows + String post(String path, String content, Map params) { + URIBuilder builder = new URIBuilder(url.toString() + path); + builder.setParameter("api-key", this.key); + if (params != null) { + for (Map.Entry entry : params.entrySet()) { + builder.setParameter(entry.getKey(), entry.getValue()); + } + } + HttpPost httpPost = new HttpPost(builder.build()); + httpPost.setEntity(new StringEntity(content)); + HttpResponse response = httpClient.execute(httpPost); + return CharStreams.toString(new InputStreamReader(response.getEntity().getContent())); + } + +} diff --git a/src/test/java/io/ipdata/client/TestUtils.java b/src/test/java/io/ipdata/client/TestUtils.java deleted file mode 100644 index ce11457..0000000 --- a/src/test/java/io/ipdata/client/TestUtils.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.ipdata.client; - -import com.google.common.io.CharStreams; -import java.io.InputStreamReader; -import java.util.Map; -import lombok.SneakyThrows; -import lombok.experimental.UtilityClass; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.entity.StringEntity; - -@UtilityClass -public class TestUtils { - - public static final String KEY = System.getenv("IPDATACO_KEY"); - - /* - Used to get responses of a raw http call, without client proxying - */ - @SneakyThrows - public static String get(HttpClient client, String path, Map params) { - URIBuilder builder = new URIBuilder("https://api.ipdata.co" + path); - builder.setParameter("api-key", KEY); - if (params != null) { - for (Map.Entry entry : params.entrySet()) { - builder.setParameter(entry.getKey(), entry.getValue()); - } - } - HttpGet httpget = new HttpGet(builder.build()); - HttpResponse response = client.execute(httpget); - return CharStreams.toString(new InputStreamReader(response.getEntity().getContent())); - } - - @SneakyThrows - public static String post(HttpClient client, String path, String content, Map params) { - URIBuilder builder = new URIBuilder("https://api.ipdata.co" + path); - builder.setParameter("api-key", KEY); - if (params != null) { - for (Map.Entry entry : params.entrySet()) { - builder.setParameter(entry.getKey(), entry.getValue()); - } - } - HttpPost httpPost = new HttpPost(builder.build()); - httpPost.setEntity(new StringEntity(content)); - HttpResponse response = client.execute(httpPost); - return CharStreams.toString(new InputStreamReader(response.getEntity().getContent())); - } - -} diff --git a/src/test/java/io/ipdata/client/ThreatTest.java b/src/test/java/io/ipdata/client/ThreatTest.java new file mode 100644 index 0000000..e6c3e0d --- /dev/null +++ b/src/test/java/io/ipdata/client/ThreatTest.java @@ -0,0 +1,59 @@ +package io.ipdata.client; + +import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.error.IpdataException; +import io.ipdata.client.model.ThreatModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +@RunWith(Parameterized.class) +public class ThreatTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @Test + @SneakyThrows + public void testThreat() { + ThreatModel threat = ipdataService.threat("8.8.8.8"); + String actual = TEST_CONTEXT.mapper().writeValueAsString(threat); + String expected = TEST_CONTEXT.get("/8.8.8.8/threat", null); + TEST_CONTEXT.assertEqualJson(actual, expected); + if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { + //value will be returned from cache now + threat = ipdataService.threat("8.8.8.8"); + actual = TEST_CONTEXT.mapper().writeValueAsString(threat); + TEST_CONTEXT.assertEqualJson(actual, expected); + } + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testThreatError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.threat("8.8.8.8"); + } + + @Parameterized.Parameters + public static Iterable data() { + return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/java/io/ipdata/client/TimeZoneTest.java b/src/test/java/io/ipdata/client/TimeZoneTest.java new file mode 100644 index 0000000..230f8ab --- /dev/null +++ b/src/test/java/io/ipdata/client/TimeZoneTest.java @@ -0,0 +1,63 @@ +package io.ipdata.client; + +import feign.httpclient.ApacheHttpClient; +import io.ipdata.client.error.IpdataException; +import io.ipdata.client.model.TimeZone; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.impl.client.HttpClientBuilder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertNotNull; + +@RunWith(Parameterized.class) +public class TimeZoneTest { + + private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + + @Parameterized.Parameter + public IpdataService ipdataService; + + @Test + @SneakyThrows + public void testTimeZone() { + TimeZone timeZone = ipdataService.timeZone("8.8.8.8"); + String expected = TEST_CONTEXT.get("/8.8.8.8/time_zone", null); + String actual = TEST_CONTEXT.mapper().writeValueAsString(timeZone); + TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("current_time")); + assertNotNull(timeZone.currentTime()); + if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { + //value will be returned from cache now + timeZone = ipdataService.timeZone("8.8.8.8"); + actual = TEST_CONTEXT.mapper().writeValueAsString(timeZone); + TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("current_time")); + assertNotNull(timeZone.currentTime()); + } + } + + @SneakyThrows + @Test(expected = IpdataException.class) + public void testTimeZoneError() { + URL url = new URL("https://api.ipdata.co"); + IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + .key("THIS_IS_AN_INVALID_KEY") + .withDefaultCache() + .feignClient(new ApacheHttpClient(HttpClientBuilder.create() + .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .build())).get(); + serviceWithInvalidKey.timeZone("8.8.8.8"); + } + + @Parameterized.Parameters + public static Iterable data() { + return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + } + +} diff --git a/src/test/resources/io/ipdata/client/fixture.json b/src/test/resources/io/ipdata/client/fixture.json new file mode 100644 index 0000000..9aaf20c --- /dev/null +++ b/src/test/resources/io/ipdata/client/fixture.json @@ -0,0 +1,60 @@ +{ + "ip": "69.78.70.144", + "is_eu": false, + "city": "test-city", + "region": "Illinois", + "region_code": "IL", + "country_name": "United States", + "country_code": "US", + "continent_name": "North America", + "continent_code": "NA", + "latitude": 41.8483, + "longitude": -87.6517, + "postal": "test-postal", + "calling_code": "1", + "flag": "https://ipdata.co/flags/us.png", + "emoji_flag": "\ud83c\uddfa\ud83c\uddf8", + "emoji_unicode": "U+1F1FA U+1F1F8", + "asn": { + "asn": "AS6167", + "name": "Cellco Partnership DBA Verizon Wireless", + "domain": "verizonwireless.com", + "route": "69.78.0.0/16", + "type": "business" + }, + "carrier": { + "name": "Verizon", + "mcc": "310", + "mnc": "004" + }, + "languages": [ + { + "name": "English", + "native": "English" + } + ], + "currency": { + "name": "US Dollar", + "code": "USD", + "symbol": "$", + "native": "$", + "plural": "US dollars" + }, + "time_zone": { + "name": "America/Chicago", + "abbr": "CDT", + "offset": "-0500", + "is_dst": true, + "current_time": "2020-06-12T06:37:16.595612-05:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false + }, + "count": "236" +} diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties index 67f7a69..7df36e9 100644 --- a/src/test/resources/log4j.properties +++ b/src/test/resources/log4j.properties @@ -1,9 +1,8 @@ log4j.rootLogger=DEBUG, A1 log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.layout=org.apache.log4j.PatternLayout - # Print the date in ISO 8601 format log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n - # Print only messages of level WARN or above in the package com.foo. log4j.logger.io=DEBUG +log4j.logger.org.apache.http=INFO From 2a249b191595b57dae672d2137ef801bf1d06d8f Mon Sep 17 00:00:00 2001 From: Yassine E Date: Fri, 12 Jun 2020 14:29:31 +0100 Subject: [PATCH 32/54] 0.1.1 Release --- README.md | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5260765..a9a55e0 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ You can have the library from Maven Central. co.ipdata.client ipdata-java-client - 0.1.0 + 0.1.1 ``` diff --git a/pom.xml b/pom.xml index 180d8bf..a5af31b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.1-SNAPSHOT + 0.1.1 A java client for ipdata.co Ipdata java client https://github.com/ipdata/java From 178f8e41119cbc8047a485862c46ff81a793bab9 Mon Sep 17 00:00:00 2001 From: Yassine E Date: Thu, 16 Jul 2020 16:04:00 +0100 Subject: [PATCH 33/54] 0.2.0 wrapup --- README.md | 2 +- pom.xml | 4 +- .../io/ipdata/client/CacheConfigBuilder.java | 3 +- src/main/java/io/ipdata/client/Ipdata.java | 5 ++- .../java/io/ipdata/client/model/AsnModel.java | 8 ---- .../io/ipdata/client/model/IpdataModel.java | 7 ---- .../client/service/IpdataInternalClient.java | 31 ++++++++++++++-- .../IpdataInternalSingleFieldClient.java | 37 +++++++++---------- src/test/java/io/ipdata/client/AsnTest.java | 16 ++++---- src/test/java/io/ipdata/client/BulkTest.java | 10 +++++ .../java/io/ipdata/client/CurrencyTest.java | 16 ++++---- .../java/io/ipdata/client/FullModelTest.java | 25 ++++++------- .../java/io/ipdata/client/TestContext.java | 11 ++++++ .../java/io/ipdata/client/TestFixture.java | 12 ++++++ .../java/io/ipdata/client/ThreatTest.java | 16 ++++---- .../java/io/ipdata/client/TimeZoneTest.java | 16 ++++---- 16 files changed, 130 insertions(+), 89 deletions(-) create mode 100644 src/test/java/io/ipdata/client/TestFixture.java diff --git a/README.md b/README.md index a9a55e0..ca34769 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ ThreatModel threat = ipdataService.threat("1.1.1.1"); The list of available fields is available [here](https://docs.ipdata.co/api-reference/response-fields) #### Multiple Field Selection -If you're interested by multiple fields for a given IP address, you'll use the ``getFields`` method: +If you're interested in multiple fields for a given IP address, you'll use the ``getFields`` method: ```java import io.ipdata.client.service.IpdataField; import io.ipdata.client.service.IpdataService; diff --git a/pom.xml b/pom.xml index a5af31b..803aaaf 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.1.1 + 0.2.0-SNAPSHOT A java client for ipdata.co Ipdata java client https://github.com/ipdata/java @@ -23,7 +23,7 @@ scm:git:git@github.com:ipdata/java.git scm:git:git@github.com:ipdata/java.git https://github.com/ipdata/java - ipdata-java-0.1.1 + ipdata-java-0.2.0 diff --git a/src/main/java/io/ipdata/client/CacheConfigBuilder.java b/src/main/java/io/ipdata/client/CacheConfigBuilder.java index 2af7bd6..039f30c 100644 --- a/src/main/java/io/ipdata/client/CacheConfigBuilder.java +++ b/src/main/java/io/ipdata/client/CacheConfigBuilder.java @@ -1,11 +1,12 @@ package io.ipdata.client; import io.ipdata.client.service.CacheConfig; -import java.util.concurrent.TimeUnit; import lombok.AccessLevel; import lombok.RequiredArgsConstructor; import lombok.experimental.Accessors; +import java.util.concurrent.TimeUnit; + @Accessors(fluent = true) @RequiredArgsConstructor(access = AccessLevel.PACKAGE) public class CacheConfigBuilder { diff --git a/src/main/java/io/ipdata/client/Ipdata.java b/src/main/java/io/ipdata/client/Ipdata.java index 5239d77..cc4c4c7 100644 --- a/src/main/java/io/ipdata/client/Ipdata.java +++ b/src/main/java/io/ipdata/client/Ipdata.java @@ -4,8 +4,6 @@ import io.ipdata.client.service.CacheConfig; import io.ipdata.client.service.IpdataService; import io.ipdata.client.service.IpdataServiceBuilder; -import java.net.URL; -import java.util.concurrent.TimeUnit; import lombok.AccessLevel; import lombok.NoArgsConstructor; import lombok.Setter; @@ -13,6 +11,9 @@ import lombok.experimental.UtilityClass; import org.slf4j.Logger; +import java.net.URL; +import java.util.concurrent.TimeUnit; + @UtilityClass public class Ipdata { diff --git a/src/main/java/io/ipdata/client/model/AsnModel.java b/src/main/java/io/ipdata/client/model/AsnModel.java index fad0493..dd30836 100644 --- a/src/main/java/io/ipdata/client/model/AsnModel.java +++ b/src/main/java/io/ipdata/client/model/AsnModel.java @@ -11,12 +11,4 @@ public class AsnModel { private String domain; private String route; private String type; - - /** - * Deprecated - * - * @deprecated : See: https://github.com/ipdata/java/issues/2 - */ - @Deprecated - private String isp; } diff --git a/src/main/java/io/ipdata/client/model/IpdataModel.java b/src/main/java/io/ipdata/client/model/IpdataModel.java index 251a5bb..ebacab7 100644 --- a/src/main/java/io/ipdata/client/model/IpdataModel.java +++ b/src/main/java/io/ipdata/client/model/IpdataModel.java @@ -42,13 +42,6 @@ public class IpdataModel { //meta private String count; - /** - * Rely on organisation field instead. - * @deprecated Use organisation instead - */ - @Deprecated - private String organization; - public boolean isEu() { return eu; } diff --git a/src/main/java/io/ipdata/client/service/IpdataInternalClient.java b/src/main/java/io/ipdata/client/service/IpdataInternalClient.java index c013a2d..8a79750 100644 --- a/src/main/java/io/ipdata/client/service/IpdataInternalClient.java +++ b/src/main/java/io/ipdata/client/service/IpdataInternalClient.java @@ -7,16 +7,41 @@ import java.util.List; -@SuppressWarnings("RedundantThrows") +/* + +For http protocol, the ':' character is actually tolerated in a path segment. feign library seems to encode all reserved +characters in the same way, i.e. regardless of their usage (path param or query param), according to global restrictions. +For IPV6 addresses, the path parameter includes colons ':' that gets encoded according to global restrictions rules, +while they are still tolerated in a path segment. + +In order to by pass this restrictive behavior, encoding is disabled for the ip path as validation is performed +server-side for it. + +From RFC 1738: + +Section 3.3, Page 9 +'Within the and components, "/", ";", "?" are reserved.' + +Section 5, Page 20 : globally reserved characters +reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" + +Section 5, Page 18 : +; HTTP + httpurl = "http://" hostport [ "/" hpath [ "?" search ]] + hpath = hsegment *[ "/" hsegment ] + hsegment = *[ uchar | ";" | ":" | "@" | "&" | "=" ] <---- ':' is tolerated for a path segment + search = *[ uchar | ";" | ":" | "@" | "&" | "=" ] + +*/ interface IpdataInternalClient { @Cacheable @RequestLine("GET /{ip}") - IpdataModel ipdata(@Param("ip") String ip) throws IpdataException; + IpdataModel ipdata(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("POST /bulk") List bulk(List ips) throws IpdataException; @Cacheable @RequestLine("GET /{ip}?fields={fields}") - IpdataModel getFields(@Param("ip") String ip, @Param("fields") String fields) throws IpdataException; + IpdataModel getFields(@Param(value = "ip", encoded = true) String ip, @Param("fields") String fields) throws IpdataException; } diff --git a/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java b/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java index 255ef91..b2c7cc7 100644 --- a/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java +++ b/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java @@ -8,65 +8,64 @@ import io.ipdata.client.model.ThreatModel; import io.ipdata.client.model.TimeZone; -@SuppressWarnings("RedundantThrows") interface IpdataInternalSingleFieldClient { @RequestLine("GET /{ip}/ip") - String getIp(@Param("ip") String ip) throws IpdataException; + String getIp(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/is_eu") - boolean isEu(@Param("ip") String ip) throws IpdataException; + boolean isEu(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/city") - String getCity(@Param("ip") String ip) throws IpdataException; + String getCity(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/country_name") - String getCountryName(@Param("ip") String ip) throws IpdataException; + String getCountryName(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/country_code") - String getCountryCode(@Param("ip") String ip) throws IpdataException; + String getCountryCode(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/continent_code") - String getContinentCode(@Param("ip") String ip) throws IpdataException; + String getContinentCode(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/longitude") - double getLongitude(@Param("ip") String ip) throws IpdataException; + double getLongitude(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/latitude") - double getLatitude(@Param("ip") String ip) throws IpdataException; + double getLatitude(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/organisation") - String getOrganisation(@Param("ip") String ip) throws IpdataException; + String getOrganisation(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/postal") - String getPostal(@Param("ip") String ip) throws IpdataException; + String getPostal(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/asn") - String getCallingCode(@Param("ip") String ip) throws IpdataException; + String getCallingCode(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/flag") - String getFlag(@Param("ip") String ip) throws IpdataException; + String getFlag(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/emoji_flag") - String getEmojiFlag(@Param("ip") String ip) throws IpdataException; + String getEmojiFlag(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/emoji_unicode") - String getEmojiUnicode(@Param("ip") String ip) throws IpdataException; + String getEmojiUnicode(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @Cacheable @RequestLine("GET /{ip}/asn") - AsnModel asn(@Param("ip") String ip) throws IpdataException; + AsnModel asn(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @Cacheable @RequestLine("GET /{ip}/time_zone") - TimeZone timeZone(@Param("ip") String ip) throws IpdataException; + TimeZone timeZone(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @Cacheable @RequestLine("GET /{ip}/currency") - Currency currency(@Param("ip") String ip) throws IpdataException; + Currency currency(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @Cacheable @RequestLine("GET /{ip}/threat") - ThreatModel threat(@Param("ip") String ip) throws IpdataException; + ThreatModel threat(@Param(value = "ip", encoded = true) String ip) throws IpdataException; } diff --git a/src/test/java/io/ipdata/client/AsnTest.java b/src/test/java/io/ipdata/client/AsnTest.java index a4af7c3..652e461 100644 --- a/src/test/java/io/ipdata/client/AsnTest.java +++ b/src/test/java/io/ipdata/client/AsnTest.java @@ -14,7 +14,6 @@ import java.net.URL; import java.util.concurrent.TimeUnit; -import static java.util.Arrays.asList; import static org.junit.Assert.assertNotNull; @RunWith(Parameterized.class) @@ -23,19 +22,20 @@ public class AsnTest { private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); @Parameterized.Parameter - public IpdataService ipdataService; + public TestFixture fixture; @Test @SneakyThrows public void testASN() { - AsnModel asn = ipdataService.asn("8.8.8.8"); + IpdataService ipdataService = fixture.service(); + AsnModel asn = ipdataService.asn(fixture.target()); assertNotNull(asn.type()); /* See: https://github.com/ipdata/java/issues/2 */ String actual = TEST_CONTEXT.mapper().writeValueAsString(asn); - String expected = TEST_CONTEXT.get("/8.8.8.8/asn", null); + String expected = TEST_CONTEXT.get("/"+fixture.target()+"/asn", null); TEST_CONTEXT.assertEqualJson(actual, expected); if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { //value will be returned from cache now - asn = ipdataService.asn("8.8.8.8"); + asn = ipdataService.asn(fixture.target()); assertNotNull(asn.type()); actual = TEST_CONTEXT.mapper().writeValueAsString(asn); TEST_CONTEXT.assertEqualJson(actual, expected); @@ -53,12 +53,12 @@ public void testAsnError() { .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) .build()) ).get(); - serviceWithInvalidKey.asn("8.8.8.8"); + serviceWithInvalidKey.asn(fixture.target()); } @Parameterized.Parameters - public static Iterable data() { - return asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + public static Iterable data() { + return TEST_CONTEXT.fixtures(); } } diff --git a/src/test/java/io/ipdata/client/BulkTest.java b/src/test/java/io/ipdata/client/BulkTest.java index b7e31aa..2d0d7d9 100644 --- a/src/test/java/io/ipdata/client/BulkTest.java +++ b/src/test/java/io/ipdata/client/BulkTest.java @@ -30,6 +30,16 @@ public void testBulkResponse() { TEST_CONTEXT.assertEqualJson(expected, actual, TEST_CONTEXT.configuration().whenIgnoringPaths("[0].time_zone.current_time", "[1].time_zone.current_time", "[0].count", "[1].count")); } + @SneakyThrows + @Test + public void testBulkResponseIpv6() { + List ipdataModels = ipdataService.bulk(Arrays.asList("2001:4860:4860::8888", "2001:4860:4860::8844")); + String actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModels); + String expected = TEST_CONTEXT.post("/bulk", "[\"2001:4860:4860::8888\",\"2001:4860:4860::8844\"]", null); + expected = TEST_CONTEXT.mapper().writeValueAsString(TEST_CONTEXT.mapper().readValue(expected, IpdataModel[].class)); + TEST_CONTEXT.assertEqualJson(expected, actual, TEST_CONTEXT.configuration().whenIgnoringPaths("[0].time_zone.current_time", "[1].time_zone.current_time", "[0].count", "[1].count")); + } + @Parameterized.Parameters public static Iterable data() { return asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); diff --git a/src/test/java/io/ipdata/client/CurrencyTest.java b/src/test/java/io/ipdata/client/CurrencyTest.java index a83a35d..9352345 100644 --- a/src/test/java/io/ipdata/client/CurrencyTest.java +++ b/src/test/java/io/ipdata/client/CurrencyTest.java @@ -12,7 +12,6 @@ import org.junit.runners.Parameterized; import java.net.URL; -import java.util.Arrays; import java.util.concurrent.TimeUnit; @RunWith(Parameterized.class) @@ -21,18 +20,19 @@ public class CurrencyTest { private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); @Parameterized.Parameter - public IpdataService ipdataService; + public TestFixture fixture; @Test @SneakyThrows public void testCurrency() { - Currency currency = ipdataService.currency("8.8.8.8"); + IpdataService ipdataService = fixture.service(); + Currency currency = ipdataService.currency(fixture.target()); String actual = TEST_CONTEXT.mapper().writeValueAsString(currency); - String expected = TEST_CONTEXT.get("/8.8.8.8/currency", null); + String expected = TEST_CONTEXT.get("/"+fixture.target()+"/currency", null); TEST_CONTEXT.assertEqualJson(actual, expected); if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { //value will be returned from cache now - currency = ipdataService.currency("8.8.8.8"); + currency = ipdataService.currency(fixture.target()); actual = TEST_CONTEXT.mapper().writeValueAsString(currency); TEST_CONTEXT.assertEqualJson(actual, expected); } @@ -49,12 +49,12 @@ public void testCurrencyError() { .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) .build()) ).get(); - serviceWithInvalidKey.currency("8.8.8.8"); + serviceWithInvalidKey.currency(fixture.target()); } @Parameterized.Parameters - public static Iterable data() { - return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + public static Iterable data() { + return TEST_CONTEXT.fixtures(); } } diff --git a/src/test/java/io/ipdata/client/FullModelTest.java b/src/test/java/io/ipdata/client/FullModelTest.java index 53c74a1..a41b853 100644 --- a/src/test/java/io/ipdata/client/FullModelTest.java +++ b/src/test/java/io/ipdata/client/FullModelTest.java @@ -5,8 +5,6 @@ import io.ipdata.client.model.IpdataModel; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import net.javacrumbs.jsonunit.JsonAssert; -import net.javacrumbs.jsonunit.core.Option; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Assert; @@ -15,10 +13,8 @@ import org.junit.runners.Parameterized; import java.net.URL; -import java.util.Arrays; import java.util.concurrent.TimeUnit; -import static org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class FullModelTest { @@ -26,19 +22,19 @@ public class FullModelTest { private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); @Parameterized.Parameter - public IpdataService ipdataService; + public TestFixture fixture; @Test @SneakyThrows public void testFullResponse() { - JsonAssert.setOptions(Option.TREATING_NULL_AS_ABSENT, Option.IGNORING_EXTRA_FIELDS); - IpdataModel ipdataModel = ipdataService.ipdata("8.8.8.8"); + IpdataService ipdataService = fixture.service(); + IpdataModel ipdataModel = ipdataService.ipdata(fixture.target()); String actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModel); - String expected = TEST_CONTEXT.get("/8.8.8.8", null); + String expected = TEST_CONTEXT.get("/"+fixture.target(), null); TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("time_zone.current_time")); if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { //value will be returned from cache now - ipdataModel = ipdataService.ipdata("8.8.8.8"); + ipdataModel = ipdataService.ipdata(fixture.target()); actual = TEST_CONTEXT.mapper().writeValueAsString(ipdataModel); TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("time_zone.current_time")); } @@ -48,7 +44,8 @@ public void testFullResponse() { @SneakyThrows @Test public void testSingleFields() { - String field = ipdataService.getCountryName("8.8.8.8"); + IpdataService ipdataService = fixture.service(); + String field = ipdataService.getCountryName(fixture.target()); String expected = TEST_CONTEXT.get("/8.8.8.8/country_name", null); Assert.assertEquals(field, expected); } @@ -64,12 +61,12 @@ public void testError() { .feignClient(new ApacheHttpClient(HttpClientBuilder.create() .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); - serviceWithInvalidKey.ipdata("8.8.8.8"); + serviceWithInvalidKey.ipdata(fixture.target()); } - @Parameters - public static Iterable data() { - return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + @Parameterized.Parameters + public static Iterable data() { + return TEST_CONTEXT.fixtures(); } } diff --git a/src/test/java/io/ipdata/client/TestContext.java b/src/test/java/io/ipdata/client/TestContext.java index 49597dd..0aacc62 100644 --- a/src/test/java/io/ipdata/client/TestContext.java +++ b/src/test/java/io/ipdata/client/TestContext.java @@ -27,6 +27,7 @@ import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; import static java.lang.System.getenv; +import static java.util.Arrays.asList; import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals; @Getter @@ -109,4 +110,14 @@ String post(String path, String content, Map params) { return CharStreams.toString(new InputStreamReader(response.getEntity().getContent())); } + + public Iterable fixtures() { + return asList( + new TestFixture("8.8.8.8", ipdataService()), + new TestFixture("8.8.8.8", cachingIpdataService()), + new TestFixture("2001:4860:4860::8888", ipdataService()), + new TestFixture("2001:4860:4860::8888", cachingIpdataService()) + ); + } + } diff --git a/src/test/java/io/ipdata/client/TestFixture.java b/src/test/java/io/ipdata/client/TestFixture.java new file mode 100644 index 0000000..1ef6d41 --- /dev/null +++ b/src/test/java/io/ipdata/client/TestFixture.java @@ -0,0 +1,12 @@ +package io.ipdata.client; + +import io.ipdata.client.service.IpdataService; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; + +@RequiredArgsConstructor @Accessors(fluent = true) @Getter +public class TestFixture { + private final String target; + private final IpdataService service; +} diff --git a/src/test/java/io/ipdata/client/ThreatTest.java b/src/test/java/io/ipdata/client/ThreatTest.java index e6c3e0d..01296b4 100644 --- a/src/test/java/io/ipdata/client/ThreatTest.java +++ b/src/test/java/io/ipdata/client/ThreatTest.java @@ -12,7 +12,6 @@ import org.junit.runners.Parameterized; import java.net.URL; -import java.util.Arrays; import java.util.concurrent.TimeUnit; @RunWith(Parameterized.class) @@ -21,18 +20,19 @@ public class ThreatTest { private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); @Parameterized.Parameter - public IpdataService ipdataService; + public TestFixture fixture; @Test @SneakyThrows public void testThreat() { - ThreatModel threat = ipdataService.threat("8.8.8.8"); + IpdataService ipdataService = fixture.service(); + ThreatModel threat = ipdataService.threat(fixture.target()); String actual = TEST_CONTEXT.mapper().writeValueAsString(threat); - String expected = TEST_CONTEXT.get("/8.8.8.8/threat", null); + String expected = TEST_CONTEXT.get("/"+fixture.target()+"/threat", null); TEST_CONTEXT.assertEqualJson(actual, expected); if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { //value will be returned from cache now - threat = ipdataService.threat("8.8.8.8"); + threat = ipdataService.threat(fixture.target()); actual = TEST_CONTEXT.mapper().writeValueAsString(threat); TEST_CONTEXT.assertEqualJson(actual, expected); } @@ -48,12 +48,12 @@ public void testThreatError() { .feignClient(new ApacheHttpClient(HttpClientBuilder.create() .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); - serviceWithInvalidKey.threat("8.8.8.8"); + serviceWithInvalidKey.threat(fixture.target()); } @Parameterized.Parameters - public static Iterable data() { - return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + public static Iterable data() { + return TEST_CONTEXT.fixtures(); } } diff --git a/src/test/java/io/ipdata/client/TimeZoneTest.java b/src/test/java/io/ipdata/client/TimeZoneTest.java index 230f8ab..aceeea8 100644 --- a/src/test/java/io/ipdata/client/TimeZoneTest.java +++ b/src/test/java/io/ipdata/client/TimeZoneTest.java @@ -12,7 +12,6 @@ import org.junit.runners.Parameterized; import java.net.URL; -import java.util.Arrays; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertNotNull; @@ -23,19 +22,20 @@ public class TimeZoneTest { private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); @Parameterized.Parameter - public IpdataService ipdataService; + public TestFixture fixture; @Test @SneakyThrows public void testTimeZone() { - TimeZone timeZone = ipdataService.timeZone("8.8.8.8"); - String expected = TEST_CONTEXT.get("/8.8.8.8/time_zone", null); + IpdataService ipdataService = fixture.service(); + TimeZone timeZone = fixture.service().timeZone(fixture.target()); + String expected = TEST_CONTEXT.get("/"+fixture.target()+"/time_zone", null); String actual = TEST_CONTEXT.mapper().writeValueAsString(timeZone); TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("current_time")); assertNotNull(timeZone.currentTime()); if (ipdataService == TEST_CONTEXT.cachingIpdataService()) { //value will be returned from cache now - timeZone = ipdataService.timeZone("8.8.8.8"); + timeZone = ipdataService.timeZone(fixture.target()); actual = TEST_CONTEXT.mapper().writeValueAsString(timeZone); TEST_CONTEXT.assertEqualJson(actual, expected, TEST_CONTEXT.configuration().whenIgnoringPaths("current_time")); assertNotNull(timeZone.currentTime()); @@ -52,12 +52,12 @@ public void testTimeZoneError() { .feignClient(new ApacheHttpClient(HttpClientBuilder.create() .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); - serviceWithInvalidKey.timeZone("8.8.8.8"); + serviceWithInvalidKey.timeZone(fixture.target()); } @Parameterized.Parameters - public static Iterable data() { - return Arrays.asList(TEST_CONTEXT.ipdataService(), TEST_CONTEXT.cachingIpdataService()); + public static Iterable data() { + return TEST_CONTEXT.fixtures(); } } From c4680344985ff2de2ebd4e27879e93d969b7ecb6 Mon Sep 17 00:00:00 2001 From: Yassine E Date: Thu, 16 Jul 2020 16:04:42 +0100 Subject: [PATCH 34/54] [maven-release-plugin] prepare release 0.2.0 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 803aaaf..d3bde99 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.2.0-SNAPSHOT + 0.2.0 A java client for ipdata.co Ipdata java client https://github.com/ipdata/java @@ -23,7 +23,7 @@ scm:git:git@github.com:ipdata/java.git scm:git:git@github.com:ipdata/java.git https://github.com/ipdata/java - ipdata-java-0.2.0 + 0.2.0 From 64e829b29f7860f1dfe95369b8a1ffdf54639715 Mon Sep 17 00:00:00 2001 From: Yassine E Date: Thu, 16 Jul 2020 16:08:43 +0100 Subject: [PATCH 35/54] updated docs --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca34769..0e6ba00 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ You can have the library from Maven Central. co.ipdata.client ipdata-java-client - 0.1.1 + 0.2.0 ``` From f28833ee279705d3717a8723c7083fb42a4327c0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 05:39:33 +0000 Subject: [PATCH 36/54] Update ipdata client to match current API - Add Company model (name, domain, network, type) for API company object - Add regionType field to IpdataModel for API region_type - Add company field to IpdataModel for API company object - Add icloudRelay and datacenter fields to ThreatModel for API is_icloud_relay and is_datacenter threat indicators - Fix IpdataField.THREAT type from TimeZone to ThreatModel (bug) - Fix getCallingCode endpoint from /asn to /calling_code (bug) - Add missing IpdataField constants: REGION_CODE, REGION_TYPE, CONTINENT_NAME, COMPANY - Update test fixture with new API fields - Update Lombok to 1.18.34 and compiler target to Java 8 https://claude.ai/code/session_01L8wfDZ1ZCuFLzAgaecvLr1 --- pom.xml | 6 +++--- src/main/java/io/ipdata/client/model/Company.java | 13 +++++++++++++ .../java/io/ipdata/client/model/IpdataModel.java | 2 ++ .../java/io/ipdata/client/model/ThreatModel.java | 4 ++++ .../java/io/ipdata/client/service/IpdataField.java | 6 +++++- .../service/IpdataInternalSingleFieldClient.java | 2 +- src/test/resources/io/ipdata/client/fixture.json | 11 ++++++++++- 7 files changed, 38 insertions(+), 6 deletions(-) create mode 100644 src/main/java/io/ipdata/client/model/Company.java diff --git a/pom.xml b/pom.xml index d3bde99..12174de 100644 --- a/pom.xml +++ b/pom.xml @@ -36,8 +36,8 @@ - 6 - 6 + 8 + 8 UTF-8 ${project.build.directory}/site/code-coverage/jacoco.xml https://sonarcloud.io @@ -87,7 +87,7 @@ org.projectlombok lombok - 1.18.10 + 1.18.34 org.hamcrest diff --git a/src/main/java/io/ipdata/client/model/Company.java b/src/main/java/io/ipdata/client/model/Company.java new file mode 100644 index 0000000..2c0d349 --- /dev/null +++ b/src/main/java/io/ipdata/client/model/Company.java @@ -0,0 +1,13 @@ +package io.ipdata.client.model; + +import lombok.Getter; +import lombok.ToString; +import lombok.experimental.Accessors; + +@ToString @Getter @Accessors(fluent = true) +public class Company { + private String name; + private String domain; + private String network; + private String type; +} diff --git a/src/main/java/io/ipdata/client/model/IpdataModel.java b/src/main/java/io/ipdata/client/model/IpdataModel.java index ebacab7..1bef8d9 100644 --- a/src/main/java/io/ipdata/client/model/IpdataModel.java +++ b/src/main/java/io/ipdata/client/model/IpdataModel.java @@ -22,6 +22,7 @@ public class IpdataModel { private String region; private String regionCode; + private String regionType; private String countryName; private String countryCode; private String continentName; @@ -35,6 +36,7 @@ public class IpdataModel { private String emojiUnicode; private AsnModel asn; private Carrier carrier; + private Company company; private List languages; private Currency currency; private TimeZone timeZone; diff --git a/src/main/java/io/ipdata/client/model/ThreatModel.java b/src/main/java/io/ipdata/client/model/ThreatModel.java index 949c61b..2d61f12 100644 --- a/src/main/java/io/ipdata/client/model/ThreatModel.java +++ b/src/main/java/io/ipdata/client/model/ThreatModel.java @@ -20,4 +20,8 @@ public class ThreatModel { private boolean threat; @JsonProperty("is_bogon") private boolean bogon; + @JsonProperty("is_icloud_relay") + private boolean icloudRelay; + @JsonProperty("is_datacenter") + private boolean datacenter; } diff --git a/src/main/java/io/ipdata/client/service/IpdataField.java b/src/main/java/io/ipdata/client/service/IpdataField.java index d6d0655..1b960da 100644 --- a/src/main/java/io/ipdata/client/service/IpdataField.java +++ b/src/main/java/io/ipdata/client/service/IpdataField.java @@ -16,9 +16,12 @@ public class IpdataField { public static final IpdataField IS_EU = new IpdataField("is_eu", Boolean.class); public static final IpdataField CITY = new IpdataField("city", String.class); public static final IpdataField REGION = new IpdataField("region", String.class); + public static final IpdataField REGION_CODE = new IpdataField("region_code", String.class); + public static final IpdataField REGION_TYPE = new IpdataField("region_type", String.class); public static final IpdataField COUNTRY_NAME = new IpdataField("country_name", String.class); public static final IpdataField COUNTRY_CODE = new IpdataField("country_code", String.class); public static final IpdataField CONTINENT_CODE = new IpdataField("continent_code", String.class); + public static final IpdataField CONTINENT_NAME = new IpdataField("continent_name", String.class); public static final IpdataField LATITUDE = new IpdataField("latitude", Double.class); public static final IpdataField LONGITUDE = new IpdataField("longitude", Double.class); public static final IpdataField ASN = new IpdataField("asn", AsnModel.class); @@ -29,10 +32,11 @@ public class IpdataField { public static final IpdataField EMOJI_FLAG = new IpdataField("emoji_flag", String.class); public static final IpdataField EMOJI_UNICODE = new IpdataField("emoji_unicode", String.class); public static final IpdataField CARRIER = new IpdataField("carrier", Carrier.class); + public static final IpdataField COMPANY = new IpdataField("company", Company.class); public static final IpdataField LANGUAGES = new IpdataField("languages", Language.class); public static final IpdataField CURRENCY = new IpdataField("currency", Currency.class); public static final IpdataField TIME_ZONE = new IpdataField("time_zone", TimeZone.class); - public static final IpdataField THREAT = new IpdataField("threat", TimeZone.class); + public static final IpdataField THREAT = new IpdataField("threat", ThreatModel.class); public static final IpdataField COUNT = new IpdataField("count", Integer.class); private final String name; private final Class type; diff --git a/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java b/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java index b2c7cc7..5f55f7a 100644 --- a/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java +++ b/src/main/java/io/ipdata/client/service/IpdataInternalSingleFieldClient.java @@ -40,7 +40,7 @@ interface IpdataInternalSingleFieldClient { @RequestLine("GET /{ip}/postal") String getPostal(@Param(value = "ip", encoded = true) String ip) throws IpdataException; - @RequestLine("GET /{ip}/asn") + @RequestLine("GET /{ip}/calling_code") String getCallingCode(@Param(value = "ip", encoded = true) String ip) throws IpdataException; @RequestLine("GET /{ip}/flag") diff --git a/src/test/resources/io/ipdata/client/fixture.json b/src/test/resources/io/ipdata/client/fixture.json index 9aaf20c..41488f5 100644 --- a/src/test/resources/io/ipdata/client/fixture.json +++ b/src/test/resources/io/ipdata/client/fixture.json @@ -4,6 +4,7 @@ "city": "test-city", "region": "Illinois", "region_code": "IL", + "region_type": "state", "country_name": "United States", "country_code": "US", "continent_name": "North America", @@ -27,6 +28,12 @@ "mcc": "310", "mnc": "004" }, + "company": { + "name": "Verizon Wireless", + "domain": "verizonwireless.com", + "network": "69.78.0.0/16", + "type": "business" + }, "languages": [ { "name": "English", @@ -54,7 +61,9 @@ "is_known_attacker": false, "is_known_abuser": false, "is_threat": false, - "is_bogon": false + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false }, "count": "236" } From 48569bdc586d39a4f8c5485a2c2c7ad7e6b8caee Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 06:40:28 +0000 Subject: [PATCH 37/54] Update README with new API fields and EU endpoint documentation Add region_type, company, is_icloud_relay, and is_datacenter to the example JSON output. Document how to use the EU endpoint (eu-api.ipdata.co) for GDPR compliance. https://claude.ai/code/session_01L8wfDZ1ZCuFLzAgaecvLr1 --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0e6ba00..df98fbc 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,15 @@ IpdataService ipdataService = Ipdata.builder().url(url) .key("MY_KEY").get(); /.../ ``` -Optionally, you can configure a cache for faster access (less than 1ms latency on requests that hit the cache). + +To use the EU endpoint for GDPR compliance, pass the EU API URL instead: +```java +URL url = new URL("https://eu-api.ipdata.co"); +IpdataService ipdataService = Ipdata.builder().url(url) + .key("MY_KEY").get(); +``` + +Optionally, you can configure a cache for faster access (less than 1ms latency on requests that hit the cache). The cache is configurable for time and space eviction policies: @@ -79,6 +87,7 @@ Output: "city": null, "region": null, "region_code": null, + "region_type": null, "country_name": "Australia", "country_code": "AU", "continent_name": "Oceania", @@ -97,6 +106,12 @@ Output: "route": "1.1.1.0/24", "type": "hosting" }, + "company": { + "name": "Cloudflare, Inc.", + "domain": "cloudflare.com", + "network": "1.1.1.0/24", + "type": "hosting" + }, "languages": [ { "name": "English", @@ -124,7 +139,9 @@ Output: "is_known_attacker": false, "is_known_abuser": false, "is_threat": false, - "is_bogon": false + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false }, "count": "0" } From ee424099f14cc8ec409050a01672c3b89b0a59a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 10:45:13 +0000 Subject: [PATCH 38/54] Mock integration tests to run without external API dependency Replace live API calls with a local MockIpdataServer using JDK's HttpServer. Tests now use fixture JSON data instead of requiring an IPDATACO_KEY env var and network access to api.ipdata.co. - Add MockIpdataServer with fixture-based HTTP responses - Add JSON fixture files for all IPs used in tests - Update all 7 integration test classes to use mock server - Disable nexus-staging-maven-plugin extension (deploy-only) https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 2 +- src/test/java/io/ipdata/client/AsnTest.java | 9 +- src/test/java/io/ipdata/client/BulkTest.java | 2 +- .../java/io/ipdata/client/CurrencyTest.java | 9 +- .../java/io/ipdata/client/FullModelTest.java | 9 +- .../io/ipdata/client/MockIpdataServer.java | 201 ++++++++++++++++++ .../client/MultipleFieldsSelectionTest.java | 2 +- .../java/io/ipdata/client/ThreatTest.java | 9 +- .../java/io/ipdata/client/TimeZoneTest.java | 9 +- .../io/ipdata/client/fixtures/1.1.1.1.json | 69 ++++++ .../client/fixtures/2001-4860-4860--8844.json | 69 ++++++ .../client/fixtures/2001-4860-4860--8888.json | 69 ++++++ .../ipdata/client/fixtures/41.128.21.123.json | 69 ++++++ .../io/ipdata/client/fixtures/8.8.8.8.json | 69 ++++++ 14 files changed, 564 insertions(+), 33 deletions(-) create mode 100644 src/test/java/io/ipdata/client/MockIpdataServer.java create mode 100644 src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json create mode 100644 src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json create mode 100644 src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json create mode 100644 src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json create mode 100644 src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json diff --git a/pom.xml b/pom.xml index 12174de..e9982f2 100644 --- a/pom.xml +++ b/pom.xml @@ -199,7 +199,7 @@ org.sonatype.plugins nexus-staging-maven-plugin 1.6.7 - true + false maven-central-staging https://oss.sonatype.org diff --git a/src/test/java/io/ipdata/client/AsnTest.java b/src/test/java/io/ipdata/client/AsnTest.java index 652e461..5ae0102 100644 --- a/src/test/java/io/ipdata/client/AsnTest.java +++ b/src/test/java/io/ipdata/client/AsnTest.java @@ -5,13 +5,11 @@ import io.ipdata.client.model.AsnModel; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.net.URL; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertNotNull; @@ -19,7 +17,7 @@ @RunWith(Parameterized.class) public class AsnTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public TestFixture fixture; @@ -45,12 +43,11 @@ public void testASN() { @SneakyThrows @Test(expected = IpdataException.class) public void testAsnError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + IpdataService serviceWithInvalidKey = Ipdata.builder().url(TEST_CONTEXT.url()) .key("THIS_IS_AN_INVALID_KEY") .withDefaultCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .setConnectionTimeToLive(10, TimeUnit.SECONDS) .build()) ).get(); serviceWithInvalidKey.asn(fixture.target()); diff --git a/src/test/java/io/ipdata/client/BulkTest.java b/src/test/java/io/ipdata/client/BulkTest.java index 2d0d7d9..916def4 100644 --- a/src/test/java/io/ipdata/client/BulkTest.java +++ b/src/test/java/io/ipdata/client/BulkTest.java @@ -15,7 +15,7 @@ @RunWith(Parameterized.class) public class BulkTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public IpdataService ipdataService; diff --git a/src/test/java/io/ipdata/client/CurrencyTest.java b/src/test/java/io/ipdata/client/CurrencyTest.java index 9352345..78ea1c6 100644 --- a/src/test/java/io/ipdata/client/CurrencyTest.java +++ b/src/test/java/io/ipdata/client/CurrencyTest.java @@ -5,19 +5,17 @@ import io.ipdata.client.model.Currency; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.net.URL; import java.util.concurrent.TimeUnit; @RunWith(Parameterized.class) public class CurrencyTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public TestFixture fixture; @@ -41,12 +39,11 @@ public void testCurrency() { @SneakyThrows @Test(expected = IpdataException.class) public void testCurrencyError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + IpdataService serviceWithInvalidKey = Ipdata.builder().url(TEST_CONTEXT.url()) .key("THIS_IS_AN_INVALID_KEY") .withDefaultCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .setConnectionTimeToLive(10, TimeUnit.SECONDS) .build()) ).get(); serviceWithInvalidKey.currency(fixture.target()); diff --git a/src/test/java/io/ipdata/client/FullModelTest.java b/src/test/java/io/ipdata/client/FullModelTest.java index a41b853..99bfa72 100644 --- a/src/test/java/io/ipdata/client/FullModelTest.java +++ b/src/test/java/io/ipdata/client/FullModelTest.java @@ -5,21 +5,19 @@ import io.ipdata.client.model.IpdataModel; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.net.URL; import java.util.concurrent.TimeUnit; @RunWith(Parameterized.class) public class FullModelTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public TestFixture fixture; @@ -54,12 +52,11 @@ public void testSingleFields() { @SneakyThrows @Test(expected = IpdataException.class) public void testError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + IpdataService serviceWithInvalidKey = Ipdata.builder().url(TEST_CONTEXT.url()) .key("THIS_IS_AN_INVALID_KEY") .withDefaultCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); serviceWithInvalidKey.ipdata(fixture.target()); } diff --git a/src/test/java/io/ipdata/client/MockIpdataServer.java b/src/test/java/io/ipdata/client/MockIpdataServer.java new file mode 100644 index 0000000..6f44dc3 --- /dev/null +++ b/src/test/java/io/ipdata/client/MockIpdataServer.java @@ -0,0 +1,201 @@ +package io.ipdata.client; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.io.CharStreams; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.InetSocketAddress; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MockIpdataServer { + + public static final String API_KEY = "test-api-key"; + + private static MockIpdataServer instance; + + private final HttpServer server; + private final String url; + private final Map fixtures = new HashMap<>(); + private final ObjectMapper mapper = new ObjectMapper(); + + private MockIpdataServer() { + try { + server = HttpServer.create(new InetSocketAddress(0), 0); + int port = server.getAddress().getPort(); + url = "http://localhost:" + port; + loadFixtures(); + server.createContext("/", this::handleRequest); + server.start(); + } catch (IOException e) { + throw new RuntimeException("Failed to start mock server", e); + } + } + + public static synchronized MockIpdataServer getInstance() { + if (instance == null) { + instance = new MockIpdataServer(); + } + return instance; + } + + public String getUrl() { + return url; + } + + private void loadFixtures() { + String[] ips = {"8.8.8.8", "2001:4860:4860::8888", "1.1.1.1", "2001:4860:4860::8844", "41.128.21.123"}; + for (String ip : ips) { + String resourceName = "fixtures/" + ip.replace(":", "-") + ".json"; + try (InputStream is = getClass().getResourceAsStream(resourceName)) { + if (is != null) { + fixtures.put(ip, mapper.readTree(is)); + } + } catch (IOException e) { + throw new RuntimeException("Failed to load fixture: " + resourceName, e); + } + } + } + + private void handleRequest(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + String rawQuery = exchange.getRequestURI().getRawQuery(); + Map params = parseQuery(rawQuery); + + String apiKey = params.get("api-key"); + if (!API_KEY.equals(apiKey)) { + sendError(exchange, 401, "Invalid API key"); + return; + } + + if ("POST".equals(method) && "/bulk".equals(path)) { + handleBulk(exchange); + return; + } + + if ("GET".equals(method)) { + handleGet(exchange, path, params); + return; + } + + sendError(exchange, 404, "Not found"); + } catch (Exception e) { + sendError(exchange, 500, e.getMessage()); + } + } + + private void handleGet(HttpExchange exchange, String path, Map params) throws IOException { + String trimmed = path.startsWith("/") ? path.substring(1) : path; + + String ip = null; + String field = null; + + // Match against known IPs (longest first to avoid prefix conflicts) + List sortedIps = new ArrayList<>(fixtures.keySet()); + sortedIps.sort((a, b) -> b.length() - a.length()); + + for (String knownIp : sortedIps) { + if (trimmed.equals(knownIp)) { + ip = knownIp; + break; + } + if (trimmed.startsWith(knownIp + "/")) { + ip = knownIp; + field = trimmed.substring(knownIp.length() + 1); + break; + } + } + + if (ip == null) { + sendError(exchange, 404, "IP not found"); + return; + } + + JsonNode fixture = fixtures.get(ip); + + if (field != null) { + // Sub-field request: GET /{ip}/{field} + JsonNode fieldNode = fixture.get(field); + if (fieldNode == null) { + sendError(exchange, 404, "Field not found: " + field); + return; + } + if (fieldNode.isTextual()) { + sendResponse(exchange, 200, fieldNode.asText()); + } else { + sendResponse(exchange, 200, mapper.writeValueAsString(fieldNode)); + } + } else if (params.containsKey("fields")) { + // Selected fields request: GET /{ip}?fields=a,b + String fieldsStr = params.get("fields"); + String[] fields = fieldsStr.split(","); + ObjectNode result = mapper.createObjectNode(); + for (String f : fields) { + JsonNode fieldNode = fixture.get(f.trim()); + if (fieldNode != null) { + result.set(f.trim(), fieldNode); + } + } + sendResponse(exchange, 200, mapper.writeValueAsString(result)); + } else { + // Full model request: GET /{ip} + sendResponse(exchange, 200, mapper.writeValueAsString(fixture)); + } + } + + private void handleBulk(HttpExchange exchange) throws IOException { + String body = CharStreams.toString(new InputStreamReader(exchange.getRequestBody())); + String[] ips = mapper.readValue(body, String[].class); + ArrayNode result = mapper.createArrayNode(); + for (String ip : ips) { + JsonNode fixture = fixtures.get(ip); + if (fixture != null) { + result.add(fixture); + } + } + sendResponse(exchange, 200, mapper.writeValueAsString(result)); + } + + private void sendResponse(HttpExchange exchange, int status, String body) throws IOException { + byte[] bytes = body.getBytes("UTF-8"); + exchange.getResponseHeaders().set("Content-Type", "application/json"); + exchange.sendResponseHeaders(status, bytes.length); + exchange.getResponseBody().write(bytes); + exchange.getResponseBody().close(); + } + + private void sendError(HttpExchange exchange, int status, String message) throws IOException { + String body = "{\"message\":\"" + message.replace("\"", "\\\"") + "\"}"; + sendResponse(exchange, status, body); + } + + private Map parseQuery(String rawQuery) { + Map params = new HashMap<>(); + if (rawQuery != null) { + for (String param : rawQuery.split("&")) { + String[] pair = param.split("=", 2); + if (pair.length == 2) { + try { + params.put(URLDecoder.decode(pair[0], "UTF-8"), URLDecoder.decode(pair[1], "UTF-8")); + } catch (UnsupportedEncodingException e) { + params.put(pair[0], pair[1]); + } + } + } + } + return params; + } +} diff --git a/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java b/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java index 4d5993e..41e1381 100644 --- a/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java +++ b/src/test/java/io/ipdata/client/MultipleFieldsSelectionTest.java @@ -14,7 +14,7 @@ @RunWith(Parameterized.class) public class MultipleFieldsSelectionTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public IpdataService ipdataService; diff --git a/src/test/java/io/ipdata/client/ThreatTest.java b/src/test/java/io/ipdata/client/ThreatTest.java index 01296b4..f462abd 100644 --- a/src/test/java/io/ipdata/client/ThreatTest.java +++ b/src/test/java/io/ipdata/client/ThreatTest.java @@ -5,19 +5,17 @@ import io.ipdata.client.model.ThreatModel; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.net.URL; import java.util.concurrent.TimeUnit; @RunWith(Parameterized.class) public class ThreatTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public TestFixture fixture; @@ -41,12 +39,11 @@ public void testThreat() { @SneakyThrows @Test(expected = IpdataException.class) public void testThreatError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + IpdataService serviceWithInvalidKey = Ipdata.builder().url(TEST_CONTEXT.url()) .key("THIS_IS_AN_INVALID_KEY") .withDefaultCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); serviceWithInvalidKey.threat(fixture.target()); } diff --git a/src/test/java/io/ipdata/client/TimeZoneTest.java b/src/test/java/io/ipdata/client/TimeZoneTest.java index aceeea8..2ef776f 100644 --- a/src/test/java/io/ipdata/client/TimeZoneTest.java +++ b/src/test/java/io/ipdata/client/TimeZoneTest.java @@ -5,13 +5,11 @@ import io.ipdata.client.model.TimeZone; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.net.URL; import java.util.concurrent.TimeUnit; import static org.junit.Assert.assertNotNull; @@ -19,7 +17,7 @@ @RunWith(Parameterized.class) public class TimeZoneTest { - private static final TestContext TEST_CONTEXT = new TestContext("https://api.ipdata.co"); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); @Parameterized.Parameter public TestFixture fixture; @@ -45,12 +43,11 @@ public void testTimeZone() { @SneakyThrows @Test(expected = IpdataException.class) public void testTimeZoneError() { - URL url = new URL("https://api.ipdata.co"); - IpdataService serviceWithInvalidKey = Ipdata.builder().url(url) + IpdataService serviceWithInvalidKey = Ipdata.builder().url(TEST_CONTEXT.url()) .key("THIS_IS_AN_INVALID_KEY") .withDefaultCache() .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()).setConnectionTimeToLive(10, TimeUnit.SECONDS) + .setConnectionTimeToLive(10, TimeUnit.SECONDS) .build())).get(); serviceWithInvalidKey.timeZone(fixture.target()); } diff --git a/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json b/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json new file mode 100644 index 0000000..9e57b8d --- /dev/null +++ b/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json @@ -0,0 +1,69 @@ +{ + "ip": "1.1.1.1", + "is_eu": false, + "city": "Los Angeles", + "region": "California", + "region_code": "CA", + "region_type": "state", + "country_name": "United States", + "country_code": "US", + "continent_name": "North America", + "continent_code": "NA", + "latitude": 34.0522, + "longitude": -118.2437, + "postal": "90001", + "calling_code": "1", + "flag": "https://ipdata.co/flags/us.png", + "emoji_flag": "\ud83c\uddfa\ud83c\uddf8", + "emoji_unicode": "U+1F1FA U+1F1F8", + "asn": { + "asn": "AS13335", + "name": "Cloudflare Inc", + "domain": "cloudflare.com", + "route": "1.1.1.0/24", + "type": "hosting" + }, + "carrier": { + "name": "Cloudflare", + "mcc": "310", + "mnc": "000" + }, + "company": { + "name": "Cloudflare Inc", + "domain": "cloudflare.com", + "network": "1.1.1.0/24", + "type": "hosting" + }, + "languages": [ + { + "name": "English", + "native": "English" + } + ], + "currency": { + "name": "US Dollar", + "code": "USD", + "symbol": "$", + "native": "$", + "plural": "US dollars" + }, + "time_zone": { + "name": "America/Los_Angeles", + "abbr": "PDT", + "offset": "-0700", + "is_dst": true, + "current_time": "2020-06-12T06:37:16.595612-07:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false + }, + "count": "1500" +} diff --git a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json new file mode 100644 index 0000000..94b49fa --- /dev/null +++ b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json @@ -0,0 +1,69 @@ +{ + "ip": "2001:4860:4860::8844", + "is_eu": false, + "city": "Mountain View", + "region": "California", + "region_code": "CA", + "region_type": "state", + "country_name": "United States", + "country_code": "US", + "continent_name": "North America", + "continent_code": "NA", + "latitude": 37.386, + "longitude": -122.0838, + "postal": "94035", + "calling_code": "1", + "flag": "https://ipdata.co/flags/us.png", + "emoji_flag": "\ud83c\uddfa\ud83c\uddf8", + "emoji_unicode": "U+1F1FA U+1F1F8", + "asn": { + "asn": "AS15169", + "name": "Google LLC", + "domain": "google.com", + "route": "2001:4860::/32", + "type": "hosting" + }, + "carrier": { + "name": "Google", + "mcc": "310", + "mnc": "000" + }, + "company": { + "name": "Google LLC", + "domain": "google.com", + "network": "2001:4860::/32", + "type": "hosting" + }, + "languages": [ + { + "name": "English", + "native": "English" + } + ], + "currency": { + "name": "US Dollar", + "code": "USD", + "symbol": "$", + "native": "$", + "plural": "US dollars" + }, + "time_zone": { + "name": "America/Los_Angeles", + "abbr": "PDT", + "offset": "-0700", + "is_dst": true, + "current_time": "2020-06-12T06:37:16.595612-07:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false + }, + "count": "1500" +} diff --git a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json new file mode 100644 index 0000000..2521f4c --- /dev/null +++ b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json @@ -0,0 +1,69 @@ +{ + "ip": "2001:4860:4860::8888", + "is_eu": false, + "city": "Mountain View", + "region": "California", + "region_code": "CA", + "region_type": "state", + "country_name": "United States", + "country_code": "US", + "continent_name": "North America", + "continent_code": "NA", + "latitude": 37.386, + "longitude": -122.0838, + "postal": "94035", + "calling_code": "1", + "flag": "https://ipdata.co/flags/us.png", + "emoji_flag": "\ud83c\uddfa\ud83c\uddf8", + "emoji_unicode": "U+1F1FA U+1F1F8", + "asn": { + "asn": "AS15169", + "name": "Google LLC", + "domain": "google.com", + "route": "2001:4860::/32", + "type": "hosting" + }, + "carrier": { + "name": "Google", + "mcc": "310", + "mnc": "000" + }, + "company": { + "name": "Google LLC", + "domain": "google.com", + "network": "2001:4860::/32", + "type": "hosting" + }, + "languages": [ + { + "name": "English", + "native": "English" + } + ], + "currency": { + "name": "US Dollar", + "code": "USD", + "symbol": "$", + "native": "$", + "plural": "US dollars" + }, + "time_zone": { + "name": "America/Los_Angeles", + "abbr": "PDT", + "offset": "-0700", + "is_dst": true, + "current_time": "2020-06-12T06:37:16.595612-07:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false + }, + "count": "1500" +} diff --git a/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json b/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json new file mode 100644 index 0000000..593d42b --- /dev/null +++ b/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json @@ -0,0 +1,69 @@ +{ + "ip": "41.128.21.123", + "is_eu": false, + "city": "Cairo", + "region": "Cairo Governorate", + "region_code": "C", + "region_type": "governorate", + "country_name": "Egypt", + "country_code": "EG", + "continent_name": "Africa", + "continent_code": "AF", + "latitude": 30.0444, + "longitude": 31.2357, + "postal": "11511", + "calling_code": "20", + "flag": "https://ipdata.co/flags/eg.png", + "emoji_flag": "\ud83c\uddea\ud83c\uddec", + "emoji_unicode": "U+1F1EA U+1F1EC", + "asn": { + "asn": "AS8452", + "name": "TE Data", + "domain": "tedata.net", + "route": "41.128.0.0/16", + "type": "isp" + }, + "carrier": { + "name": "TE Data", + "mcc": "602", + "mnc": "001" + }, + "company": { + "name": "TE Data", + "domain": "tedata.net", + "network": "41.128.0.0/16", + "type": "isp" + }, + "languages": [ + { + "name": "Arabic", + "native": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629" + } + ], + "currency": { + "name": "Egyptian Pound", + "code": "EGP", + "symbol": "E\u00a3", + "native": "\u062c.\u0645.\u200f", + "plural": "Egyptian pounds" + }, + "time_zone": { + "name": "Africa/Cairo", + "abbr": "EET", + "offset": "+0200", + "is_dst": false, + "current_time": "2020-06-12T15:37:16.595612+02:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false + }, + "count": "1500" +} diff --git a/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json b/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json new file mode 100644 index 0000000..73ecd30 --- /dev/null +++ b/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json @@ -0,0 +1,69 @@ +{ + "ip": "8.8.8.8", + "is_eu": false, + "city": "Mountain View", + "region": "California", + "region_code": "CA", + "region_type": "state", + "country_name": "United States", + "country_code": "US", + "continent_name": "North America", + "continent_code": "NA", + "latitude": 37.386, + "longitude": -122.0838, + "postal": "94035", + "calling_code": "1", + "flag": "https://ipdata.co/flags/us.png", + "emoji_flag": "\ud83c\uddfa\ud83c\uddf8", + "emoji_unicode": "U+1F1FA U+1F1F8", + "asn": { + "asn": "AS15169", + "name": "Google LLC", + "domain": "google.com", + "route": "8.8.8.0/24", + "type": "hosting" + }, + "carrier": { + "name": "Google", + "mcc": "310", + "mnc": "000" + }, + "company": { + "name": "Google LLC", + "domain": "google.com", + "network": "8.8.8.0/24", + "type": "hosting" + }, + "languages": [ + { + "name": "English", + "native": "English" + } + ], + "currency": { + "name": "US Dollar", + "code": "USD", + "symbol": "$", + "native": "$", + "plural": "US dollars" + }, + "time_zone": { + "name": "America/Los_Angeles", + "abbr": "PDT", + "offset": "-0700", + "is_dst": true, + "current_time": "2020-06-12T06:37:16.595612-07:00" + }, + "threat": { + "is_tor": false, + "is_proxy": false, + "is_anonymous": false, + "is_known_attacker": false, + "is_known_abuser": false, + "is_threat": false, + "is_bogon": false, + "is_icloud_relay": false, + "is_datacenter": false + }, + "count": "1500" +} From 3683a85bf698c26158659618e9b6fd232fbf0801 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 13:32:43 +0000 Subject: [PATCH 39/54] Update dependency versions to latest Java 8-compatible releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dependencies: - OpenFeign: 9.7.0 → 11.10 (last Java 8-compatible line) - Guava: 20.0 → 33.4.8-jre - SLF4J: 1.7.30 → 1.7.36 - Lombok: 1.18.34 → 1.18.38 - JUnit: 4.13 → 4.13.2 - json-unit: 2.17.0 → 2.40.1 Build plugins: - JaCoCo: 0.8.4 → 0.8.13 - Sonar: 3.7.0.1746 → 5.1.0.4751 - Surefire: 3.0.0-M3 → 3.5.4 - Resources: 2.6 → 3.4.0 - Source: 3.2.1 → 3.4.0 - Javadoc: 3.1.1 → 3.12.0 - Nexus Staging: 1.6.7 → 1.7.0 - GPG: 1.6 → 3.2.8 https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index e9982f2..346a662 100644 --- a/pom.xml +++ b/pom.xml @@ -48,10 +48,10 @@ yassine_ipdata-java-client ipdata-java-client ${project.version} - 0.8.4 - 3.7.0.1746 - 3.0.0-M3 - 9.7.0 + 0.8.13 + 5.1.0.4751 + 3.5.4 + 11.10 @@ -72,22 +72,22 @@ com.google.guava guava - 20.0 + 33.4.8-jre org.slf4j slf4j-api - 1.7.30 + 1.7.36 org.slf4j slf4j-log4j12 - 1.7.30 + 1.7.36 org.projectlombok lombok - 1.18.34 + 1.18.38 org.hamcrest @@ -98,13 +98,13 @@ junit junit - 4.13 + 4.13.2 test net.javacrumbs.json-unit json-unit - 2.17.0 + 2.40.1 test @@ -121,7 +121,7 @@ maven-resources-plugin - 2.6 + 3.4.0 ${project.build.sourceEncoding} @@ -175,7 +175,7 @@ maven-source-plugin - 3.2.1 + 3.4.0 @@ -186,7 +186,7 @@ maven-javadoc-plugin - 3.1.1 + 3.12.0 @@ -198,7 +198,7 @@ org.sonatype.plugins nexus-staging-maven-plugin - 1.6.7 + 1.7.0 false maven-central-staging @@ -208,7 +208,7 @@ maven-gpg-plugin - 1.6 + 3.2.8 A143FB3D9A4D90CC76DE768495FD65EAEFC32F7E From 7c3d26660943c8ed1f5d3250b66477435da9c4fe Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 13:34:02 +0000 Subject: [PATCH 40/54] Fix plugin versions for Java 8 and Maven 3.x compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - maven-resources-plugin: 3.4.0 → 3.3.1 (3.4.0 requires Maven 4 + Java 17) - maven-source-plugin: 3.4.0 → 3.3.1 (3.4.0 requires Maven 4) - sonar-maven-plugin: 5.1.0.4751 → 3.11.0.3922 (5.x requires Java 11+) - jacoco-maven-plugin: 0.8.13 → 0.8.14 (latest patch) - maven-surefire-plugin: 3.5.4 → 3.5.5 (latest patch) https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 346a662..474a38f 100644 --- a/pom.xml +++ b/pom.xml @@ -48,9 +48,9 @@ yassine_ipdata-java-client ipdata-java-client ${project.version} - 0.8.13 - 5.1.0.4751 - 3.5.4 + 0.8.14 + 3.11.0.3922 + 3.5.5 11.10 @@ -121,7 +121,7 @@ maven-resources-plugin - 3.4.0 + 3.3.1 ${project.build.sourceEncoding} @@ -175,7 +175,7 @@ maven-source-plugin - 3.4.0 + 3.3.1 From b5bd483b522f4219c92da9f7338052923753f69f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 13:45:27 +0000 Subject: [PATCH 41/54] Bump minimum Java version from 8 to 11 Java 8 reached end of public updates in 2019. Java 11 is the lowest currently relevant LTS and unlocks newer dependency versions while maintaining broad compatibility for library consumers. https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 474a38f..05a571d 100644 --- a/pom.xml +++ b/pom.xml @@ -36,8 +36,8 @@ - 8 - 8 + 11 + 11 UTF-8 ${project.build.directory}/site/code-coverage/jacoco.xml https://sonarcloud.io From 8410193929055b600e1f5677f33b7072ee83c223 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 14:23:40 +0000 Subject: [PATCH 42/54] Replace Travis CI with GitHub Actions workflows - Add CI workflow for test + SonarCloud analysis on push/PR to main - Add Release workflow for GPG signing on push to releases branch - Remove .travis.yml and .travis/ directory - Update README badge from Travis to GitHub Actions https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- .github/workflows/ci.yml | 36 ++++++++++ .github/workflows/release.yml | 37 ++++++++++ .travis.yml | 26 ------- .travis/ipdata-java.crypted.gpg.key | 104 ---------------------------- README.md | 2 +- 5 files changed, 74 insertions(+), 131 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/release.yml delete mode 100644 .travis.yml delete mode 100644 .travis/ipdata-java.crypted.gpg.key diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f822918 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +name: CI + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Build and Test + run: mvn clean test post-integration-test + + - name: SonarCloud Analysis + if: env.SONAR_TOKEN != '' + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn sonar:sonar -Dsonar.login=$SONAR_TOKEN diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..67b7f44 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,37 @@ +name: Release + +on: + push: + branches: [ releases ] + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + + - name: Cache Maven packages + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Import GPG key + env: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: echo "$GPG_PRIVATE_KEY" | gpg --import --batch --yes + + - name: Package and Sign + env: + GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: mvn package verify -Prelease -DskipTests=true -Dgpg.keyname=$GPG_KEY_NAME -Dgpg.passphrase=$GPG_PASSPHRASE diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 263ca93..0000000 --- a/.travis.yml +++ /dev/null @@ -1,26 +0,0 @@ -sudo: required -dist: trusty -language: java -jdk: - - openjdk8 -addons: - sonarcloud: - organization: yassine-github - token: - secure: PYT5/IbiqFd877vdi+CYTx7srXr5ACvAgBSL4rxPqwVSQUdGIZxVTj8JcZp7Cl2WiDj9zDlTHSDaJDt5ZpXnmN5quJBizuCZDVArm3LoRADjxhOd9N8rQEYEE1TZOHlu1+g1i/abOL+av9Spn7lWk1LvuH5BW3b0GiAdqJFevfnJsUYDh4iYHiMYjTIP5dO/cZUnQzgFgZS6YkMA28cgQIy/F03z6tO9oM6kcJ/DGBnqvdGAT2ZxtWhFaaNwJazlxSePCwnOok+v3vgACzGospTJLQ4AdGklYIK+ljGpz7AbiRs7yhvzsib6i5Wr0IEmZmLiyIgjExPdnV+rXAndgig/0bnhv1YzyEf/hCzMczpHvNa0HDAGCxkTIsw76XlumwPMGURdlypWEoWrz/9cwiPMnQ0XcAOTN56msA5nBbNwnJ9KDKIVWKWg447OhbImlTr+qxHI/L+8e1dFxyA1iBg7/wtU/27V2QRABvhdlVGNn1khdkIKhsueYHbBjloUTILHBFKTtJdZdD2z/Vf9cu51EQDwv5IPfkkz/UER8pw4AVKgFi6v57M3unqE5C6g8dZr5CELAqy3tGjf6v9HaTF3AdGEkyIJuxiiO8LM20MJm+IreR/pIv8SVeT7f747hbPCQ+mMe5rSE4KVtSI88OeUcI193pdd5PBg0jU262I= -jobs: - include: - - stage: test - script: - - mvn clean test post-integration-test sonar:sonar - - stage: release - script: - - openssl enc -aes-256-cbc -d -in ./.travis/ipdata-java.crypted.gpg.key -out ./.travis/ipdata.out.key -base64 -K $GPG_AES_KEY -iv $GPG_AES_KEY_IV -S $GPG_AES_KEY_SALT - - echo $GPG_PASSPHRASE | gpg --import --batch --yes --passphrase-fd 0 ./.travis/ipdata.out.key - - mvn package verify -Prelease -DskipTests=true -Dgpg.keyname=$GPG_KEY_NAME -Dgpg.passphrase=$GPG_PASSPHRASE -stages: - - test - - name: release - if: branch = releases -notifications: - email: false diff --git a/.travis/ipdata-java.crypted.gpg.key b/.travis/ipdata-java.crypted.gpg.key deleted file mode 100644 index 3d0f7e7..0000000 --- a/.travis/ipdata-java.crypted.gpg.key +++ /dev/null @@ -1,104 +0,0 @@ -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 -U+Rp4aeEC3Y4YxX8WAppkQ== diff --git a/README.md b/README.md index df98fbc..3abdff7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ipdata-java-client -![Build Status](https://www.travis-ci.org/yassine/ipdata-java-client.svg?branch=master) +![Build Status](https://github.com/ipdata/java/actions/workflows/ci.yml/badge.svg) [![Coverage Status](https://sonarcloud.io/api/project_badges/measure?metric=coverage&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?metric=alert_status&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) [![Maintainability](https://sonarcloud.io/api/project_badges/measure?metric=sqale_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) From c17364c123d122ff889d29258824a941a17c3071 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 14:34:53 +0000 Subject: [PATCH 43/54] Remove SonarCloud integration - Remove sonar properties and sonar-maven-plugin from pom.xml - Remove SonarCloud analysis step from CI workflow - Remove SonarCloud badges from README https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- .github/workflows/ci.yml | 6 ------ README.md | 4 ---- pom.xml | 17 +---------------- 3 files changed, 1 insertion(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f822918..29c65d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,9 +28,3 @@ jobs: - name: Build and Test run: mvn clean test post-integration-test - - - name: SonarCloud Analysis - if: env.SONAR_TOKEN != '' - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn sonar:sonar -Dsonar.login=$SONAR_TOKEN diff --git a/README.md b/README.md index 3abdff7..619f88d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,5 @@ # ipdata-java-client ![Build Status](https://github.com/ipdata/java/actions/workflows/ci.yml/badge.svg) -[![Coverage Status](https://sonarcloud.io/api/project_badges/measure?metric=coverage&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) -[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?metric=alert_status&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) -[![Maintainability](https://sonarcloud.io/api/project_badges/measure?metric=sqale_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) -[![Reliability](https://sonarcloud.io/api/project_badges/measure?metric=reliability_rating&project=yassine_ipdata-java-client)](https://sonarcloud.io/dashboard/index/yassine_ipdata-java-client) An 100% compliant [ipdata.co](https://ipdata.co) API java client. diff --git a/pom.xml b/pom.xml index 05a571d..4ce0821 100644 --- a/pom.xml +++ b/pom.xml @@ -39,17 +39,7 @@ 11 11 UTF-8 - ${project.build.directory}/site/code-coverage/jacoco.xml - https://sonarcloud.io - https://github.com/ipdata/java - https://github.com/ipdata/java - https://github.com/ipdata/java - yassine-github - yassine_ipdata-java-client - ipdata-java-client - ${project.version} 0.8.14 - 3.11.0.3922 3.5.5 11.10 @@ -168,12 +158,7 @@ ${surefireArgLine} - - org.codehaus.mojo - sonar-maven-plugin - ${version.build.sonar} - - + maven-source-plugin 3.3.1 From 8f8bab81be247942fc4c0b63b66a8598c5130ca9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 14:44:53 +0000 Subject: [PATCH 44/54] Simplify release workflow with tag trigger and setup-java - Trigger on tag push (v*) instead of branch push - Use setup-java built-in GPG and server credential support - Run mvn deploy to actually publish to Maven Central - Remove manual GPG import and -Prelease flag https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- .github/workflows/release.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 67b7f44..776b6aa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release on: push: - branches: [ releases ] + tags: [ 'v*' ] jobs: release: @@ -15,6 +15,11 @@ jobs: with: java-version: '11' distribution: 'temurin' + server-id: maven-central-staging + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} + gpg-passphrase: MAVEN_GPG_PASSPHRASE - name: Cache Maven packages uses: actions/cache@v4 @@ -24,14 +29,9 @@ jobs: restore-keys: | ${{ runner.os }}-maven- - - name: Import GPG key + - name: Deploy to Maven Central env: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - run: echo "$GPG_PRIVATE_KEY" | gpg --import --batch --yes - - - name: Package and Sign - env: - GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - run: mvn package verify -Prelease -DskipTests=true -Dgpg.keyname=$GPG_KEY_NAME -Dgpg.passphrase=$GPG_PASSPHRASE + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} + MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + run: mvn deploy -DskipTests=true From 9745051f5b795fd83d6ab4a5b3e00a5f27484c97 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 14:56:09 +0000 Subject: [PATCH 45/54] Add missing threat fields: is_vpn, blocklists, scores Addresses #9 - the ThreatModel was missing fields from the API. - Add is_vpn boolean field - Add blocklists list with Blocklist model (name, site, type) - Add scores object with Scores model (vpn_score, proxy_score, threat_score, trust_score) - Update all test fixtures with new fields https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- .../java/io/ipdata/client/model/Blocklist.java | 11 +++++++++++ .../java/io/ipdata/client/model/Scores.java | 17 +++++++++++++++++ .../io/ipdata/client/model/ThreatModel.java | 5 +++++ .../resources/io/ipdata/client/fixture.json | 10 +++++++++- .../io/ipdata/client/fixtures/1.1.1.1.json | 10 +++++++++- .../client/fixtures/2001-4860-4860--8844.json | 10 +++++++++- .../client/fixtures/2001-4860-4860--8888.json | 10 +++++++++- .../ipdata/client/fixtures/41.128.21.123.json | 10 +++++++++- .../io/ipdata/client/fixtures/8.8.8.8.json | 10 +++++++++- 9 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 src/main/java/io/ipdata/client/model/Blocklist.java create mode 100644 src/main/java/io/ipdata/client/model/Scores.java diff --git a/src/main/java/io/ipdata/client/model/Blocklist.java b/src/main/java/io/ipdata/client/model/Blocklist.java new file mode 100644 index 0000000..5ca4201 --- /dev/null +++ b/src/main/java/io/ipdata/client/model/Blocklist.java @@ -0,0 +1,11 @@ +package io.ipdata.client.model; + +import lombok.Getter; +import lombok.ToString; + +@Getter @ToString +public class Blocklist { + private String name; + private String site; + private String type; +} diff --git a/src/main/java/io/ipdata/client/model/Scores.java b/src/main/java/io/ipdata/client/model/Scores.java new file mode 100644 index 0000000..5049a83 --- /dev/null +++ b/src/main/java/io/ipdata/client/model/Scores.java @@ -0,0 +1,17 @@ +package io.ipdata.client.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.ToString; + +@Getter @ToString +public class Scores { + @JsonProperty("vpn_score") + private int vpnScore; + @JsonProperty("proxy_score") + private int proxyScore; + @JsonProperty("threat_score") + private int threatScore; + @JsonProperty("trust_score") + private int trustScore; +} diff --git a/src/main/java/io/ipdata/client/model/ThreatModel.java b/src/main/java/io/ipdata/client/model/ThreatModel.java index 2d61f12..8b96baa 100644 --- a/src/main/java/io/ipdata/client/model/ThreatModel.java +++ b/src/main/java/io/ipdata/client/model/ThreatModel.java @@ -1,6 +1,7 @@ package io.ipdata.client.model; import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; import lombok.Getter; import lombok.ToString; @@ -8,6 +9,8 @@ public class ThreatModel { @JsonProperty("is_tor") private boolean tor; + @JsonProperty("is_vpn") + private boolean vpn; @JsonProperty("is_proxy") private boolean proxy; @JsonProperty("is_anonymous") @@ -24,4 +27,6 @@ public class ThreatModel { private boolean icloudRelay; @JsonProperty("is_datacenter") private boolean datacenter; + private List blocklists; + private Scores scores; } diff --git a/src/test/resources/io/ipdata/client/fixture.json b/src/test/resources/io/ipdata/client/fixture.json index 41488f5..3275fb2 100644 --- a/src/test/resources/io/ipdata/client/fixture.json +++ b/src/test/resources/io/ipdata/client/fixture.json @@ -56,6 +56,7 @@ }, "threat": { "is_tor": false, + "is_vpn": false, "is_proxy": false, "is_anonymous": false, "is_known_attacker": false, @@ -63,7 +64,14 @@ "is_threat": false, "is_bogon": false, "is_icloud_relay": false, - "is_datacenter": false + "is_datacenter": false, + "blocklists": [], + "scores": { + "vpn_score": 0, + "proxy_score": 0, + "threat_score": 0, + "trust_score": 100 + } }, "count": "236" } diff --git a/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json b/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json index 9e57b8d..11e2002 100644 --- a/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json +++ b/src/test/resources/io/ipdata/client/fixtures/1.1.1.1.json @@ -56,6 +56,7 @@ }, "threat": { "is_tor": false, + "is_vpn": false, "is_proxy": false, "is_anonymous": false, "is_known_attacker": false, @@ -63,7 +64,14 @@ "is_threat": false, "is_bogon": false, "is_icloud_relay": false, - "is_datacenter": false + "is_datacenter": false, + "blocklists": [], + "scores": { + "vpn_score": 0, + "proxy_score": 0, + "threat_score": 0, + "trust_score": 100 + } }, "count": "1500" } diff --git a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json index 94b49fa..62b19c9 100644 --- a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json +++ b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8844.json @@ -56,6 +56,7 @@ }, "threat": { "is_tor": false, + "is_vpn": false, "is_proxy": false, "is_anonymous": false, "is_known_attacker": false, @@ -63,7 +64,14 @@ "is_threat": false, "is_bogon": false, "is_icloud_relay": false, - "is_datacenter": false + "is_datacenter": false, + "blocklists": [], + "scores": { + "vpn_score": 0, + "proxy_score": 0, + "threat_score": 0, + "trust_score": 100 + } }, "count": "1500" } diff --git a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json index 2521f4c..766ef8c 100644 --- a/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json +++ b/src/test/resources/io/ipdata/client/fixtures/2001-4860-4860--8888.json @@ -56,6 +56,7 @@ }, "threat": { "is_tor": false, + "is_vpn": false, "is_proxy": false, "is_anonymous": false, "is_known_attacker": false, @@ -63,7 +64,14 @@ "is_threat": false, "is_bogon": false, "is_icloud_relay": false, - "is_datacenter": false + "is_datacenter": false, + "blocklists": [], + "scores": { + "vpn_score": 0, + "proxy_score": 0, + "threat_score": 0, + "trust_score": 100 + } }, "count": "1500" } diff --git a/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json b/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json index 593d42b..58c661a 100644 --- a/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json +++ b/src/test/resources/io/ipdata/client/fixtures/41.128.21.123.json @@ -56,6 +56,7 @@ }, "threat": { "is_tor": false, + "is_vpn": false, "is_proxy": false, "is_anonymous": false, "is_known_attacker": false, @@ -63,7 +64,14 @@ "is_threat": false, "is_bogon": false, "is_icloud_relay": false, - "is_datacenter": false + "is_datacenter": false, + "blocklists": [], + "scores": { + "vpn_score": 0, + "proxy_score": 0, + "threat_score": 0, + "trust_score": 100 + } }, "count": "1500" } diff --git a/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json b/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json index 73ecd30..8ad870f 100644 --- a/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json +++ b/src/test/resources/io/ipdata/client/fixtures/8.8.8.8.json @@ -56,6 +56,7 @@ }, "threat": { "is_tor": false, + "is_vpn": false, "is_proxy": false, "is_anonymous": false, "is_known_attacker": false, @@ -63,7 +64,14 @@ "is_threat": false, "is_bogon": false, "is_icloud_relay": false, - "is_datacenter": false + "is_datacenter": false, + "blocklists": [], + "scores": { + "vpn_score": 0, + "proxy_score": 0, + "threat_score": 0, + "trust_score": 100 + } }, "count": "1500" } From 03169ae97978c624b7444a6a7bd847137e6aa03d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 15:22:04 +0000 Subject: [PATCH 46/54] Fix IPv6 address encoding in HTTP requests (issue #10) Feign's template engine percent-encodes colons in path parameters, converting IPv6 addresses like 2001:4860:4860::8888 to 2001%3A4860%3A4860%3A%3A8888. This breaks requests to the ipdata API. Add Ipv6SafeClient wrapper that decodes %3A back to literal colons before sending requests, since colons are valid in URI paths per RFC 3986 section 3.3. Also switch the default HTTP client from ApacheHttpClient (which further re-encodes via URIBuilder) to Feign's built-in Client.Default. https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- .../client/service/IpdataServiceBuilder.java | 5 +-- .../ipdata/client/service/Ipv6SafeClient.java | 44 +++++++++++++++++++ .../io/ipdata/client/Ipv6EncodingTest.java | 36 +++++++++++++++ .../io/ipdata/client/MockIpdataServer.java | 6 +++ .../java/io/ipdata/client/TestContext.java | 12 +---- 5 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 src/main/java/io/ipdata/client/service/Ipv6SafeClient.java create mode 100644 src/test/java/io/ipdata/client/Ipv6EncodingTest.java diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java index af68fda..d2c8f43 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java @@ -9,7 +9,6 @@ import com.google.common.cache.CacheLoader; import feign.Client; import feign.Feign; -import feign.httpclient.ApacheHttpClient; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; import io.ipdata.client.model.*; @@ -42,7 +41,7 @@ public IpdataService build() { final ApiErrorDecoder apiErrorDecoder = new ApiErrorDecoder(mapper, customLogger); final IpdataInternalClient client = Feign.builder() - .client(httpClient == null ? new ApacheHttpClient() : httpClient) + .client(new Ipv6SafeClient(httpClient == null ? new Client.Default(null, null) : httpClient)) .decoder(new JacksonDecoder(mapper)) .encoder(new JacksonEncoder(mapper)) .requestInterceptor(keyRequestInterceptor) @@ -50,7 +49,7 @@ public IpdataService build() { .target(IpdataInternalClient.class, url.toString()); final IpdataInternalSingleFieldClient singleFieldClient = Feign.builder() - .client(httpClient == null ? new ApacheHttpClient() : httpClient) + .client(new Ipv6SafeClient(httpClient == null ? new Client.Default(null, null) : httpClient)) .decoder(new FieldDecoder(mapper)) .encoder(new JacksonEncoder(mapper)) .requestInterceptor(keyRequestInterceptor) diff --git a/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java b/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java new file mode 100644 index 0000000..f8f6f38 --- /dev/null +++ b/src/main/java/io/ipdata/client/service/Ipv6SafeClient.java @@ -0,0 +1,44 @@ +package io.ipdata.client.service; + +import feign.Client; +import feign.Request; +import feign.Response; + +import java.io.IOException; + +/** + * A Feign Client wrapper that prevents percent-encoding of colons in the request path. + *

+ * Feign's template engine may percent-encode colons in path parameters (e.g., IPv6 addresses), + * converting {@code 2001:4860:4860::8888} to {@code 2001%3A4860%3A4860%3A%3A8888}. + * Colons are valid in URI path segments per RFC 3986 section 3.3, so this wrapper + * decodes them before forwarding to the underlying HTTP client. + * + * @see Issue #10 + */ +class Ipv6SafeClient implements Client { + + private final Client delegate; + + Ipv6SafeClient(Client delegate) { + this.delegate = delegate; + } + + @Override + public Response execute(Request request, Request.Options options) throws IOException { + String url = request.url(); + int queryIndex = url.indexOf('?'); + String path = queryIndex >= 0 ? url.substring(0, queryIndex) : url; + + if (path.contains("%3A") || path.contains("%3a")) { + String fixedPath = path.replace("%3A", ":").replace("%3a", ":"); + String query = queryIndex >= 0 ? url.substring(queryIndex) : ""; + String fixedUrl = fixedPath + query; + request = Request.create( + request.httpMethod(), fixedUrl, request.headers(), + request.body(), request.charset() + ); + } + return delegate.execute(request, options); + } +} diff --git a/src/test/java/io/ipdata/client/Ipv6EncodingTest.java b/src/test/java/io/ipdata/client/Ipv6EncodingTest.java new file mode 100644 index 0000000..fddbac7 --- /dev/null +++ b/src/test/java/io/ipdata/client/Ipv6EncodingTest.java @@ -0,0 +1,36 @@ +package io.ipdata.client; + +import io.ipdata.client.model.IpdataModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Test; + +/** + * Regression test for https://github.com/ipdata/java/issues/10 + * Verifies that IPv6 colons are not percent-encoded (%3A) in HTTP requests. + */ +public class Ipv6EncodingTest { + + private static final MockIpdataServer MOCK = MockIpdataServer.getInstance(); + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MOCK.getUrl()); + + @Test + @SneakyThrows + public void testIpv6ColonsAreNotEncoded() { + String ipv6 = "2001:4860:4860::8888"; + IpdataService service = TEST_CONTEXT.ipdataService(); + IpdataModel model = service.ipdata(ipv6); + Assert.assertNotNull(model); + + String rawPath = MOCK.getLastRawPath(); + Assert.assertFalse( + "IPv6 colons should not be percent-encoded in the request path, but got: " + rawPath, + rawPath.contains("%3A") + ); + Assert.assertTrue( + "Request path should contain the IPv6 address with literal colons", + rawPath.contains(ipv6) + ); + } +} diff --git a/src/test/java/io/ipdata/client/MockIpdataServer.java b/src/test/java/io/ipdata/client/MockIpdataServer.java index 6f44dc3..527f7c1 100644 --- a/src/test/java/io/ipdata/client/MockIpdataServer.java +++ b/src/test/java/io/ipdata/client/MockIpdataServer.java @@ -29,6 +29,7 @@ public class MockIpdataServer { private final String url; private final Map fixtures = new HashMap<>(); private final ObjectMapper mapper = new ObjectMapper(); + private volatile String lastRawPath; private MockIpdataServer() { try { @@ -54,6 +55,10 @@ public String getUrl() { return url; } + public String getLastRawPath() { + return lastRawPath; + } + private void loadFixtures() { String[] ips = {"8.8.8.8", "2001:4860:4860::8888", "1.1.1.1", "2001:4860:4860::8844", "41.128.21.123"}; for (String ip : ips) { @@ -72,6 +77,7 @@ private void handleRequest(HttpExchange exchange) throws IOException { try { String method = exchange.getRequestMethod(); String path = exchange.getRequestURI().getPath(); + lastRawPath = exchange.getRequestURI().getRawPath(); String rawQuery = exchange.getRequestURI().getRawQuery(); Map params = parseQuery(rawQuery); diff --git a/src/test/java/io/ipdata/client/TestContext.java b/src/test/java/io/ipdata/client/TestContext.java index 0aacc62..230fc8d 100644 --- a/src/test/java/io/ipdata/client/TestContext.java +++ b/src/test/java/io/ipdata/client/TestContext.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.io.CharStreams; -import feign.httpclient.ApacheHttpClient; import io.ipdata.client.service.IpdataService; import lombok.Getter; import lombok.SneakyThrows; @@ -17,7 +16,6 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; -import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.HttpClientBuilder; @@ -56,17 +54,11 @@ public TestContext(String key, String url) { mapper.setPropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); - httpClient = HttpClientBuilder.create().setSSLHostnameVerifier(new NoopHostnameVerifier()).build(); + httpClient = HttpClientBuilder.create().build(); ipdataService = Ipdata.builder().url(this.url).key(this.key) .noCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build()) - ).get(); + .get(); cachingIpdataService = Ipdata.builder().url(this.url) - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setSSLHostnameVerifier(new NoopHostnameVerifier()) - .build())) .withDefaultCache().key(key).get(); } From aac54f78ac416e9ecd89b3e01c9ad49319b59ab0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 15:25:13 +0000 Subject: [PATCH 47/54] Scope slf4j-log4j12 to test only (issue #8) Libraries should only depend on slf4j-api and let consumers choose their own SLF4J binding. Move slf4j-log4j12 from compile scope to test scope so it's no longer forced on downstream users. https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 4ce0821..5adb72e 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,7 @@ org.slf4j slf4j-log4j12 1.7.36 + test org.projectlombok From 74736340e41565c93a74f10899bf6c96ee44bba4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 15:28:44 +0000 Subject: [PATCH 48/54] Replace slf4j-log4j12 with slf4j-simple for tests (issue #8) slf4j-log4j12 bridges to end-of-life Log4J 1.x. Replace it with slf4j-simple, a lightweight zero-dependency backend suitable for test logging. Remove log4j.properties and add simplelogger.properties. https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 2 +- src/test/resources/log4j.properties | 8 -------- src/test/resources/simplelogger.properties | 6 ++++++ 3 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 src/test/resources/log4j.properties create mode 100644 src/test/resources/simplelogger.properties diff --git a/pom.xml b/pom.xml index 5adb72e..4cd63d4 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ org.slf4j - slf4j-log4j12 + slf4j-simple 1.7.36 test diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties deleted file mode 100644 index 7df36e9..0000000 --- a/src/test/resources/log4j.properties +++ /dev/null @@ -1,8 +0,0 @@ -log4j.rootLogger=DEBUG, A1 -log4j.appender.A1=org.apache.log4j.ConsoleAppender -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -# Print the date in ISO 8601 format -log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n -# Print only messages of level WARN or above in the package com.foo. -log4j.logger.io=DEBUG -log4j.logger.org.apache.http=INFO diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties new file mode 100644 index 0000000..572da4d --- /dev/null +++ b/src/test/resources/simplelogger.properties @@ -0,0 +1,6 @@ +org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.log.org.apache.http=info +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss,SSS +org.slf4j.simpleLogger.showThreadName=true +org.slf4j.simpleLogger.showShortLogName=true From f967598a53c6576863f193509b4d1bb257c2cea4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Feb 2026 16:17:08 +0000 Subject: [PATCH 49/54] Fix critical code quality issues and strengthen test coverage - Replace deprecated CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES with SNAKE_CASE - Fix ExecutionException unwrapping in CachingInternalClient to preserve root cause - Specify UTF-8 charset explicitly in ApiKeyRequestInterceptor - Throw IllegalArgumentException instead of returning null from getFields() with empty fields - Scope feign-httpclient to test (production code only uses feign.Client) - Scope lombok to provided (compile-time annotation processor only) - Remove redundant bulkAsArray() method from IpdataService - Add Javadoc to IpdataService public API - Remove package-private setters from IpdataModel (Jackson uses field access) - Fix testSingleFields to use fixture IP instead of hardcoded 8.8.8.8 - Strengthen testError to assert RemoteIpdataException with 401 status - Add EdgeCaseTest with negative tests for empty fields and exception unwrapping https://claude.ai/code/session_01KxvyXRVVZaLrgTZshvsZY6 --- pom.xml | 2 + .../io/ipdata/client/model/IpdataModel.java | 3 - .../service/ApiKeyRequestInterceptor.java | 3 +- .../client/service/CachingInternalClient.java | 20 +++-- .../ipdata/client/service/IpdataService.java | 39 +++++++++- .../client/service/IpdataServiceBuilder.java | 4 +- .../client/service/IpdataServiceSupport.java | 7 +- .../java/io/ipdata/client/EdgeCaseTest.java | 73 +++++++++++++++++++ .../java/io/ipdata/client/FullModelTest.java | 24 +++--- .../java/io/ipdata/client/TestContext.java | 4 +- 10 files changed, 145 insertions(+), 34 deletions(-) create mode 100644 src/test/java/io/ipdata/client/EdgeCaseTest.java diff --git a/pom.xml b/pom.xml index 4cd63d4..469a84e 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ io.github.openfeign feign-httpclient ${version.client.feign} + test com.google.guava @@ -79,6 +80,7 @@ org.projectlombok lombok 1.18.38 + provided org.hamcrest diff --git a/src/main/java/io/ipdata/client/model/IpdataModel.java b/src/main/java/io/ipdata/client/model/IpdataModel.java index 1bef8d9..7d8cf62 100644 --- a/src/main/java/io/ipdata/client/model/IpdataModel.java +++ b/src/main/java/io/ipdata/client/model/IpdataModel.java @@ -1,15 +1,12 @@ package io.ipdata.client.model; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AccessLevel; import lombok.Getter; -import lombok.Setter; import lombok.ToString; import lombok.experimental.Accessors; import java.util.List; -@Setter(AccessLevel.PACKAGE) @ToString @Getter @Accessors(fluent = true) diff --git a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java index bfacdd7..a361660 100644 --- a/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java +++ b/src/main/java/io/ipdata/client/service/ApiKeyRequestInterceptor.java @@ -7,6 +7,7 @@ import lombok.extern.slf4j.Slf4j; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; @RequiredArgsConstructor @Slf4j @@ -22,7 +23,7 @@ class ApiKeyRequestInterceptor implements RequestInterceptor { String version; try { version = CharStreams.toString(new InputStreamReader(ApiKeyRequestInterceptor.class - .getResourceAsStream("/io/ipdata/client/VERSION"))).replaceAll("\\n", ""); + .getResourceAsStream("/io/ipdata/client/VERSION"), StandardCharsets.UTF_8)).replaceAll("\\n", ""); version = String.format("io.ipdata.client.java.%s", version); } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/src/main/java/io/ipdata/client/service/CachingInternalClient.java b/src/main/java/io/ipdata/client/service/CachingInternalClient.java index 30aa99f..08de093 100644 --- a/src/main/java/io/ipdata/client/service/CachingInternalClient.java +++ b/src/main/java/io/ipdata/client/service/CachingInternalClient.java @@ -29,12 +29,20 @@ class CachingInternalClient implements IpdataInternalClient, IpdataInternalSingl private final LoadingCache currencyCache; private final LoadingCache threatCache; + private static IpdataException unwrap(ExecutionException e) throws IpdataException { + Throwable cause = e.getCause(); + if (cause instanceof IpdataException) { + throw (IpdataException) cause; + } + throw new IpdataException(cause != null ? cause.getMessage() : e.getMessage(), cause != null ? cause : e); + } + @Override public IpdataModel getFields(String ip, String fields) throws IpdataException { try { return fieldsCache.get(HashPair.of(ip, fields)); } catch (ExecutionException e) { - throw new IpdataException(e.getMessage(), e); + throw unwrap(e); } } @@ -43,7 +51,7 @@ public AsnModel asn(String ip) throws IpdataException { try { return asnCache.get(ip); } catch (ExecutionException e) { - throw new IpdataException(e.getMessage(), e); + throw unwrap(e); } } @@ -52,7 +60,7 @@ public TimeZone timeZone(String ip) throws IpdataException { try { return tzCache.get(ip); } catch (ExecutionException e) { - throw new IpdataException(e.getMessage(), e); + throw unwrap(e); } } @@ -61,7 +69,7 @@ public Currency currency(String ip) throws IpdataException { try { return currencyCache.get(ip); } catch (ExecutionException e) { - throw new IpdataException(e.getMessage(), e); + throw unwrap(e); } } @@ -70,7 +78,7 @@ public ThreatModel threat(String ip) throws IpdataException { try { return threatCache.get(ip); } catch (ExecutionException e) { - throw new IpdataException(e.getMessage(), e); + throw unwrap(e); } } @@ -79,7 +87,7 @@ public IpdataModel ipdata(String ip) throws IpdataException { try { return ipdataCache.get(ip); } catch (ExecutionException e) { - throw new IpdataException(e.getMessage(), e); + throw unwrap(e); } } diff --git a/src/main/java/io/ipdata/client/service/IpdataService.java b/src/main/java/io/ipdata/client/service/IpdataService.java index dce4ea3..d83d522 100644 --- a/src/main/java/io/ipdata/client/service/IpdataService.java +++ b/src/main/java/io/ipdata/client/service/IpdataService.java @@ -5,13 +5,48 @@ import java.util.List; +/** + * Primary interface for accessing the ipdata.co API. + *

+ * Provides methods for looking up geolocation, threat intelligence, and other + * metadata for IP addresses. Supports single IP lookups, bulk lookups, and + * selective field retrieval. + *

+ * Also exposes single-field accessors (e.g. {@code getCountryName}, {@code getCity}) + * inherited from {@link IpdataInternalSingleFieldClient}. + * + * @see io.ipdata.client.Ipdata#builder() + */ public interface IpdataService extends IpdataInternalSingleFieldClient { + /** + * Retrieves the full IP data model for the given IP address. + * + * @param ip an IPv4 or IPv6 address + * @return the full geolocation and metadata for the IP + * @throws IpdataException if the API call fails + */ IpdataModel ipdata(String ip) throws IpdataException; + /** + * Retrieves IP data for multiple IP addresses in a single request. + * + * @param ips list of IPv4 or IPv6 addresses + * @return a list of IP data models, one per input address + * @throws IpdataException if the API call fails + */ List bulk(List ips) throws IpdataException; - IpdataModel[] bulkAsArray(List ips) throws IpdataException; - + /** + * Retrieves only the specified fields for the given IP address. + *

+ * Fields are sorted before querying to maximize cache hit rates when caching is enabled. + * + * @param ip an IPv4 or IPv6 address + * @param fields one or more fields to retrieve (e.g. {@code IpdataField.ASN}, {@code IpdataField.CURRENCY}) + * @return a partial IP data model containing only the requested fields + * @throws IpdataException if the API call fails + * @throws IllegalArgumentException if no fields are specified + */ IpdataModel getFields(String ip, IpdataField... fields) throws IpdataException; } diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java index d2c8f43..f294cc2 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceBuilder.java @@ -18,7 +18,7 @@ import java.net.URL; -import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; +import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE; @RequiredArgsConstructor(staticName = "of") public class IpdataServiceBuilder { @@ -31,7 +31,7 @@ public class IpdataServiceBuilder { public IpdataService build() { final ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); + mapper.setPropertyNamingStrategy(SNAKE_CASE); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); diff --git a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java index 7f7324e..7531ba8 100644 --- a/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java +++ b/src/main/java/io/ipdata/client/service/IpdataServiceSupport.java @@ -30,15 +30,10 @@ private IpdataInternalSingleFieldClient getApi() { return singleFieldClient; } - @Override - public IpdataModel[] bulkAsArray(List ips) throws IpdataException { - return bulk(ips).toArray(new IpdataModel[0]); - } - @Override public IpdataModel getFields(String ip, IpdataField... fields) throws IpdataException { if (fields.length == 0) { - return null; + throw new IllegalArgumentException("At least one field must be specified"); } //sorting here to improve the likelihood of a cache hit, otherwise a permutation of the same //array would result into a different cache key, and thus a cache miss diff --git a/src/test/java/io/ipdata/client/EdgeCaseTest.java b/src/test/java/io/ipdata/client/EdgeCaseTest.java new file mode 100644 index 0000000..1874198 --- /dev/null +++ b/src/test/java/io/ipdata/client/EdgeCaseTest.java @@ -0,0 +1,73 @@ +package io.ipdata.client; + +import io.ipdata.client.error.IpdataException; +import io.ipdata.client.error.RemoteIpdataException; +import io.ipdata.client.model.IpdataModel; +import io.ipdata.client.service.IpdataService; +import lombok.SneakyThrows; +import org.junit.Assert; +import org.junit.Test; + +import static io.ipdata.client.service.IpdataField.ASN; +import static io.ipdata.client.service.IpdataField.CURRENCY; + +public class EdgeCaseTest { + + private static final TestContext TEST_CONTEXT = new TestContext(MockIpdataServer.API_KEY, MockIpdataServer.getInstance().getUrl()); + + @Test(expected = IllegalArgumentException.class) + @SneakyThrows + public void testGetFieldsWithNoFieldsThrows() { + IpdataService service = TEST_CONTEXT.ipdataService(); + service.getFields("8.8.8.8"); + } + + @Test(expected = IllegalArgumentException.class) + @SneakyThrows + public void testGetFieldsWithNoFieldsThrowsCaching() { + IpdataService service = TEST_CONTEXT.cachingIpdataService(); + service.getFields("8.8.8.8"); + } + + @Test + @SneakyThrows + public void testInvalidKeyReturnsRemoteException() { + IpdataService service = Ipdata.builder() + .url(TEST_CONTEXT.url()) + .key("INVALID_KEY") + .noCache() + .get(); + try { + service.ipdata("8.8.8.8"); + Assert.fail("Expected RemoteIpdataException"); + } catch (RemoteIpdataException e) { + Assert.assertEquals(401, e.getStatus()); + Assert.assertNotNull(e.getMessage()); + } + } + + @Test + @SneakyThrows + public void testCachedInvalidKeyUnwrapsException() { + IpdataService service = Ipdata.builder() + .url(TEST_CONTEXT.url()) + .key("INVALID_KEY") + .withDefaultCache() + .get(); + try { + service.ipdata("8.8.8.8"); + Assert.fail("Expected RemoteIpdataException"); + } catch (RemoteIpdataException e) { + Assert.assertEquals(401, e.getStatus()); + } + } + + @Test + @SneakyThrows + public void testGetFieldsReturnsSelectedFields() { + IpdataService service = TEST_CONTEXT.ipdataService(); + IpdataModel model = service.getFields("8.8.8.8", ASN, CURRENCY); + Assert.assertNotNull(model.asn()); + Assert.assertNotNull(model.currency()); + } +} diff --git a/src/test/java/io/ipdata/client/FullModelTest.java b/src/test/java/io/ipdata/client/FullModelTest.java index 99bfa72..55424e8 100644 --- a/src/test/java/io/ipdata/client/FullModelTest.java +++ b/src/test/java/io/ipdata/client/FullModelTest.java @@ -1,18 +1,15 @@ package io.ipdata.client; -import feign.httpclient.ApacheHttpClient; import io.ipdata.client.error.IpdataException; +import io.ipdata.client.error.RemoteIpdataException; import io.ipdata.client.model.IpdataModel; import io.ipdata.client.service.IpdataService; import lombok.SneakyThrows; -import org.apache.http.impl.client.HttpClientBuilder; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.util.concurrent.TimeUnit; - @RunWith(Parameterized.class) public class FullModelTest { @@ -44,21 +41,24 @@ public void testFullResponse() { public void testSingleFields() { IpdataService ipdataService = fixture.service(); String field = ipdataService.getCountryName(fixture.target()); - String expected = TEST_CONTEXT.get("/8.8.8.8/country_name", null); - Assert.assertEquals(field, expected); + String expected = TEST_CONTEXT.get("/" + fixture.target() + "/country_name", null); + Assert.assertEquals(expected, field); } @SneakyThrows - @Test(expected = IpdataException.class) + @Test public void testError() { IpdataService serviceWithInvalidKey = Ipdata.builder().url(TEST_CONTEXT.url()) .key("THIS_IS_AN_INVALID_KEY") - .withDefaultCache() - .feignClient(new ApacheHttpClient(HttpClientBuilder.create() - .setConnectionTimeToLive(10, TimeUnit.SECONDS) - .build())).get(); - serviceWithInvalidKey.ipdata(fixture.target()); + .noCache() + .get(); + try { + serviceWithInvalidKey.ipdata(fixture.target()); + Assert.fail("Expected RemoteIpdataException"); + } catch (RemoteIpdataException e) { + Assert.assertEquals(401, e.getStatus()); + } } @Parameterized.Parameters diff --git a/src/test/java/io/ipdata/client/TestContext.java b/src/test/java/io/ipdata/client/TestContext.java index 230fc8d..c367d3b 100644 --- a/src/test/java/io/ipdata/client/TestContext.java +++ b/src/test/java/io/ipdata/client/TestContext.java @@ -23,7 +23,7 @@ import java.net.URL; import java.util.Map; -import static com.fasterxml.jackson.databind.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES; +import static com.fasterxml.jackson.databind.PropertyNamingStrategies.SNAKE_CASE; import static java.lang.System.getenv; import static java.util.Arrays.asList; import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals; @@ -51,7 +51,7 @@ public TestContext(String key, String url) { this.key = key; this.url = new URL(url); mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES); + mapper.setPropertyNamingStrategy(SNAKE_CASE); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true); mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); httpClient = HttpClientBuilder.create().build(); From 7d3bedb4fb683fa002b2d07f190b4aac0f9405f5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Feb 2026 03:56:36 +0000 Subject: [PATCH 50/54] Update GPG key fingerprint for Maven Central signing https://claude.ai/code/session_01ToqqHtNtg7kdm8TmAMZYe8 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 469a84e..6174e45 100644 --- a/pom.xml +++ b/pom.xml @@ -198,7 +198,7 @@ maven-gpg-plugin 3.2.8 - A143FB3D9A4D90CC76DE768495FD65EAEFC32F7E + FB32919AB5830162299F4125C49AB890BA00B57D From 128a2de733f27981c3f3019bc74805db5d707ffb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Feb 2026 04:03:24 +0000 Subject: [PATCH 51/54] Remove GPG_PASSPHRASE secret dependency since key has no passphrase https://claude.ai/code/session_01ToqqHtNtg7kdm8TmAMZYe8 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 776b6aa..86afc91 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,5 +33,5 @@ jobs: env: MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} - MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + MAVEN_GPG_PASSPHRASE: '' run: mvn deploy -DskipTests=true From 29138da00f5b46e2ed352c0232829a9603757632 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Feb 2026 04:23:34 +0000 Subject: [PATCH 52/54] Add --pinentry-mode loopback to GPG plugin for non-interactive signing CI was failing because GPG tried to open an interactive pinentry prompt. This flag allows GPG to accept the passphrase via stdin in headless environments like GitHub Actions. https://claude.ai/code/session_01ToqqHtNtg7kdm8TmAMZYe8 --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index 6174e45..677c8a8 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,10 @@ 3.2.8 FB32919AB5830162299F4125C49AB890BA00B57D + + --pinentry-mode + loopback + From 8eb183ee8d994cd9ca33511a6bddff9e0228808c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Feb 2026 04:38:07 +0000 Subject: [PATCH 53/54] Migrate from legacy OSSRH to Sonatype Central Portal Replace nexus-staging-maven-plugin with central-publishing-maven-plugin (v0.10.0) as oss.sonatype.org has been sunset. The deploy was failing with 401 because OSSRH doesn't accept Central Portal tokens. Changes: - Replace nexus-staging-maven-plugin with central-publishing-maven-plugin - Update distributionManagement to use 'central' server ID - Update release.yml server-id to match - Enable autoPublish for CI deployments https://claude.ai/code/session_01ToqqHtNtg7kdm8TmAMZYe8 --- .github/workflows/release.yml | 2 +- pom.xml | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 86afc91..d0f98e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: with: java-version: '11' distribution: 'temurin' - server-id: maven-central-staging + server-id: central server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} diff --git a/pom.xml b/pom.xml index 677c8a8..f38f95c 100644 --- a/pom.xml +++ b/pom.xml @@ -27,12 +27,10 @@ - maven-central-staging - https://oss.sonatype.org/service/local/staging/deploy/maven2 + central - maven-central-snapshots - https://oss.sonatype.org/content/repositories/snapshots + central @@ -184,14 +182,13 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 - false + org.sonatype.central + central-publishing-maven-plugin + 0.10.0 + true - maven-central-staging - https://oss.sonatype.org - + central + true From 016bfbb58cee18502ccfb94c1966dc269cf9b8e0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Feb 2026 05:11:17 +0000 Subject: [PATCH 54/54] Bump version to 0.2.1 Version 0.2.0 already exists on Maven Central and cannot be re-published. Bump to 0.2.1 for the next release. https://claude.ai/code/session_01ToqqHtNtg7kdm8TmAMZYe8 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f38f95c..4b3a67b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 co.ipdata.client ipdata-java-client - 0.2.0 + 0.2.1 A java client for ipdata.co Ipdata java client https://github.com/ipdata/java @@ -23,7 +23,7 @@ scm:git:git@github.com:ipdata/java.git scm:git:git@github.com:ipdata/java.git https://github.com/ipdata/java - 0.2.0 + 0.2.1