diff --git a/.gitignore b/.gitignore index 1062418..719a4de 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,32 @@ -.idea/ +# Compiled class files +*.class + +# Log file +*.log +**/.log + +# IntelliJ *.iml +/.idea + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# other +/bin/ +/.classpath +/.project +/target/ +/out/ +/.DS_Store +/.settings/ + diff --git a/README.md b/README.md index f62f766..b19a717 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # java-etherscan-api -![travis](https://travis-ci.org/GoodforGod/java-etherscan-api.svg?branch=master) +[![travis](https://travis-ci.org/GoodforGod/java-etherscan-api.svg?branch=master)](https://travis-ci.com/iSnow/java-etherscan-api) [![Maintainability](https://api.codeclimate.com/v1/badges/808997be2e69ff1ae8fe/maintainability)](https://codeclimate.com/github/GoodforGod/java-etherscan-api/maintainability) [![codecov](https://codecov.io/gh/GoodforGod/java-etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/GoodforGod/java-etherscan-api) +[![Jitpack](https://jitpack.io/v/iSnow/java-etherscan-api.svg)](https://jitpack.io/#iSnow/java-etherscan-api) [Etherscan](https://etherscan.io/apis) Java API implementation. diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java index 5b95f68..d242a76 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -1,12 +1,16 @@ package io.api.etherscan.core.impl; import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import io.api.etherscan.error.EtherScanException; import io.api.etherscan.error.ParseException; +import io.api.etherscan.error.RateLimitException; import io.api.etherscan.executor.IHttpExecutor; import io.api.etherscan.manager.IQueueManager; import io.api.etherscan.util.BasicUtils; +import java.util.Map; + /** * Base provider for API Implementations * @@ -42,7 +46,20 @@ T convert(final String json, final Class tClass) { try { return gson.fromJson(json, tClass); } catch (Exception e) { - throw new ParseException(e.getMessage(), e.getCause()); + if (e instanceof JsonSyntaxException) { + Map map = gson.fromJson(json, Map.class); + Object statusCode = map.get("status"); + if ((statusCode instanceof String) && (statusCode.equals("0"))) { + Object message = map.get("message"); + if ((message instanceof String) && (message.equals("NOTOK"))) { + Object result = map.get("result"); + if ((result instanceof String) && (result.equals("Max rate limit reached"))) { + throw new RateLimitException ("Max rate limit reached"); + } + } + } + } + throw new ParseException(e.getMessage(), e.getCause(), json); } } diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java index c22955c..410c0ac 100644 --- a/src/main/java/io/api/etherscan/error/ConnectionException.java +++ b/src/main/java/io/api/etherscan/error/ConnectionException.java @@ -8,6 +8,10 @@ */ public class ConnectionException extends ApiException { + public ConnectionException(String message) { + super(message); + } + public ConnectionException(String message, Throwable cause) { super(message, cause); } diff --git a/src/main/java/io/api/etherscan/error/ParseException.java b/src/main/java/io/api/etherscan/error/ParseException.java index 81974df..dc3952c 100644 --- a/src/main/java/io/api/etherscan/error/ParseException.java +++ b/src/main/java/io/api/etherscan/error/ParseException.java @@ -7,8 +7,14 @@ * @since 29.10.2018 */ public class ParseException extends ApiException { + String json; - public ParseException(String message, Throwable cause) { + public ParseException(String message, Throwable cause, String json) { super(message, cause); + this.json = json; + } + + public String getJson() { + return json; } } diff --git a/src/main/java/io/api/etherscan/error/RateLimitException.java b/src/main/java/io/api/etherscan/error/RateLimitException.java new file mode 100644 index 0000000..2562342 --- /dev/null +++ b/src/main/java/io/api/etherscan/error/RateLimitException.java @@ -0,0 +1,15 @@ +package io.api.etherscan.error; + +/** + * ! NO DESCRIPTION ! + * + * @author iSnow + * @since 2020-10-06 + */ +public class RateLimitException extends ApiException { + + public RateLimitException(String message) { + super(message); + } + +} diff --git a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java index 3a33515..c059d27 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -18,8 +18,7 @@ import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; -import static java.net.HttpURLConnection.HTTP_MOVED_PERM; -import static java.net.HttpURLConnection.HTTP_MOVED_TEMP; +import static java.net.HttpURLConnection.*; /** * Http client implementation @@ -88,6 +87,10 @@ public String get(final String urlAsString) { final int status = connection.getResponseCode(); if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { return get(connection.getHeaderField("Location")); + } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { + throw new ConnectionException("Protocol error: "+connection.getResponseMessage()); + } else if (status >= HTTP_INTERNAL_ERROR) { + throw new ConnectionException("Server error: "+connection.getResponseMessage()); } final String data = readData(connection); diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index cf485fd..2fa58ee 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -60,12 +60,27 @@ public LocalDateTime getTimeStamp() { if(_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { long formatted = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') ? BasicUtils.parseHex(timeStamp).longValue() - : Long.valueOf(timeStamp); + : Long.parseLong(timeStamp); _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC); } return _timeStamp; } + /** + * Return the "timeStamp" field of the event record as a long-int representing the milliseconds + * since the Unix epoch (1970-01-01 00:00:00). + * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null + */ + public Long getTimeStampAsMillis() { + if (BasicUtils.isEmpty(timeStamp)) { + return null; + } + long tsSecs = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') + ? BasicUtils.parseHex(timeStamp).longValue() + : Long.parseLong(timeStamp); + return tsSecs * 1000; + } + public String getData() { return data; } diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java index d4e10be..1397f15 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java @@ -2,6 +2,7 @@ import io.api.etherscan.core.ILogsApi; import io.api.etherscan.error.LogQueryException; +import io.api.etherscan.model.query.IQueryBuilder; import io.api.etherscan.util.BasicUtils; /** @@ -12,7 +13,7 @@ * @author GoodforGod * @since 31.10.2018 */ -public class LogQueryBuilder { +public class LogQueryBuilder implements IQueryBuilder { private static final long MIN_BLOCK = 0; private static final long MAX_BLOCK = 99999999999L; @@ -75,4 +76,9 @@ public LogTopicQuadro topic(String topic0, String topic1, String topic2, String return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); } + + @Override + public LogQuery build() throws LogQueryException { + return new LogQuery("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); + } } diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java index 1b1753e..2c5ad71 100644 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ b/src/test/java/io/api/util/BasicUtilsTests.java @@ -98,6 +98,6 @@ public void isResponseNullThrows() { @Test(expected = ParseException.class) public void isThrowParseException() { - throw new ParseException("Test", null); + throw new ParseException("Test", null, null); } }