From 2f81dbfc883a68ce95df661293af5446fd3e3156 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 13 Oct 2022 23:44:12 +0200 Subject: [PATCH 01/61] Add coroner query --- .../library/coroner/CoronerClient.java | 67 +++++++++++++++++++ .../library/coroner/CoronerQueries.java | 23 +++++++ .../backtraceio/library/http/HttpHelper.java | 42 ++++++++++++ .../services/BacktraceReportSender.java | 40 ++--------- 4 files changed, 136 insertions(+), 36 deletions(-) create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java new file mode 100644 index 00000000..640b5d72 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java @@ -0,0 +1,67 @@ +package backtraceio.library.coroner; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; + +import backtraceio.library.common.BacktraceSerializeHelper; +import backtraceio.library.common.BacktraceStringHelper; +import backtraceio.library.common.RequestHelper; +import backtraceio.library.http.HttpHelper; +import backtraceio.library.logger.BacktraceLogger; +import backtraceio.library.models.types.HttpException; + +public class CoronerClient { + private final String apiUrl; + private final String coronerToken; + + public CoronerClient(String apiUrl, String coronerToken) { + this.apiUrl = apiUrl; + this.coronerToken = coronerToken; + } + + public void getReport(String rxId) throws IOException, HttpException { + String queryJson = CoronerQueries.filterByRxId(rxId); + + HttpURLConnection urlConnection = prepareHttpRequest(queryJson); + int statusCode = urlConnection.getResponseCode(); + + if (statusCode != HttpURLConnection.HTTP_OK) { + String message = HttpHelper.getResponseMessage(urlConnection); + message = (BacktraceStringHelper.isNullOrEmpty(message)) ? + urlConnection.getResponseMessage() : message; + throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); + } + + String resultJson = HttpHelper.getResponseMessage(urlConnection); + } + + private HttpURLConnection prepareHttpRequest(String json) throws IOException { + URL url = new URL(this.apiUrl); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod("POST"); + urlConnection.setUseCaches(false); + + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Content-Type", RequestHelper.getContentType()); + urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); + + DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); + + RequestHelper.addJson(request, json); + +// RequestHelper.addEndOfRequest(request); + + request.flush(); + request.close(); + + return urlConnection; + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java new file mode 100644 index 00000000..efae1a33 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java @@ -0,0 +1,23 @@ +package backtraceio.library.coroner; + +public class CoronerQueries { + public static String filterByRxId(String rxId) { + return "{" + + " \"group\":[" + + " \"fingerprint\"" + + " ]," + + " \"offset\":0," + + " \"limit\":20," + + " \"filter\":[" + + " {" + + " \"_rxid\":[" + + " [" + + " \"equal\"," + + " \"" + rxId + "\"" + + " ]" + + " ]" + + " }" + + " ]" + + "}"; + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java b/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java new file mode 100644 index 00000000..72ccf5eb --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java @@ -0,0 +1,42 @@ +package backtraceio.library.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; + +import backtraceio.library.logger.BacktraceLogger; + +public class HttpHelper { + private static final String LOG_TAG = HttpHelper.class.getSimpleName(); + + /** + * Read response message from HTTP response + * + * @param urlConnection current HTTP connection + * @return response from HTTP request + * @throws IOException + */ + public static String getResponseMessage(HttpURLConnection urlConnection) throws IOException { + BacktraceLogger.d(LOG_TAG, "Reading response from HTTP request"); + + InputStream inputStream; + if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { + inputStream = urlConnection.getInputStream(); + } else { + inputStream = urlConnection.getErrorStream(); + } + + BufferedReader br = new BufferedReader(new InputStreamReader( + inputStream)); + + StringBuilder responseSB = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + responseSB.append(line); + } + br.close(); + return responseSB.toString(); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java b/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java index 0c1e38fa..4d711a4f 100644 --- a/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java +++ b/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java @@ -1,10 +1,6 @@ package backtraceio.library.services; -import java.io.BufferedReader; import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; @@ -14,6 +10,7 @@ import backtraceio.library.common.MultiFormRequestHelper; import backtraceio.library.common.RequestHelper; import backtraceio.library.events.OnServerErrorEventListener; +import backtraceio.library.http.HttpHelper; import backtraceio.library.logger.BacktraceLogger; import backtraceio.library.models.BacktraceResult; import backtraceio.library.models.json.BacktraceReport; @@ -77,11 +74,11 @@ static BacktraceResult sendReport(String serverUrl, String json, List at if (statusCode == HttpURLConnection.HTTP_OK) { result = BacktraceSerializeHelper.backtraceResultFromJson( - getResponse(urlConnection) + HttpHelper.getResponseMessage(urlConnection) ); result.setBacktraceReport(report); } else { - String message = getResponse(urlConnection); + String message = HttpHelper.getResponseMessage(urlConnection); message = (BacktraceStringHelper.isNullOrEmpty(message)) ? urlConnection.getResponseMessage() : message; throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); @@ -150,7 +147,7 @@ public static EventsResult sendEvents(String serverUrl, String json, EventsPaylo if (statusCode == HttpURLConnection.HTTP_OK) { result = new EventsResult(payload, urlConnection.getResponseMessage(), BacktraceResultStatus.Ok, statusCode); } else { - String message = getResponse(urlConnection); + String message = HttpHelper.getResponseMessage(urlConnection); message = (BacktraceStringHelper.isNullOrEmpty(message)) ? urlConnection.getResponseMessage() : message; throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); @@ -176,33 +173,4 @@ public static EventsResult sendEvents(String serverUrl, String json, EventsPaylo } return result; } - - /** - * Read response message from HTTP response - * - * @param urlConnection current HTTP connection - * @return response from HTTP request - * @throws IOException - */ - private static String getResponse(HttpURLConnection urlConnection) throws IOException { - BacktraceLogger.d(LOG_TAG, "Reading response from HTTP request"); - - InputStream inputStream; - if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { - inputStream = urlConnection.getInputStream(); - } else { - inputStream = urlConnection.getErrorStream(); - } - - BufferedReader br = new BufferedReader(new InputStreamReader( - inputStream)); - - StringBuilder responseSB = new StringBuilder(); - String line; - while ((line = br.readLine()) != null) { - responseSB.append(line); - } - br.close(); - return responseSB.toString(); - } } From d6c2a1ab1dcf5fbb2c1a501192832577f0c772e6 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 31 Oct 2022 21:08:48 +0100 Subject: [PATCH 02/61] Add coroner client and queries --- .../library/coroner/CoronerApiResponse.java | 34 +++++++++ .../library/coroner/CoronerClient.java | 71 +++++-------------- .../library/coroner/CoronerError.java | 31 ++++++++ .../library/coroner/CoronerHttpClient.java | 64 +++++++++++++++++ .../library/coroner/CoronerQueries.java | 35 +++++---- .../library/coroner/CoronerQueryBuilder.java | 50 +++++++++++++ .../library/coroner/CoronerQueryFields.java | 8 +++ .../library/coroner/CoronerResponse.java | 44 ++++++++++++ 8 files changed, 264 insertions(+), 73 deletions(-) create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java new file mode 100644 index 00000000..64765036 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java @@ -0,0 +1,34 @@ +package backtraceio.library.coroner; + +import java.util.List; + +class CoronerApiResponse { + public CoronerError error; + public CoronerResponse response; + + public CoronerApiResponse() { + + } + + public CoronerApiResponse(CoronerError error, CoronerResponse response) { + this.error = error; + this.response = response; + } + + public CoronerError getError() { + return error; + } + + public void setError(CoronerError error) { + this.error = error; + } + + public CoronerResponse getResponse() { + return response; + } + + public void setResponse(CoronerResponse response) { + this.response = response; + } +} + diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java index 640b5d72..fe3234f5 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java @@ -1,67 +1,30 @@ package backtraceio.library.coroner; -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.ProtocolException; -import java.net.URL; - -import backtraceio.library.common.BacktraceSerializeHelper; -import backtraceio.library.common.BacktraceStringHelper; -import backtraceio.library.common.RequestHelper; -import backtraceio.library.http.HttpHelper; -import backtraceio.library.logger.BacktraceLogger; -import backtraceio.library.models.types.HttpException; +import java.util.Arrays; +import java.util.List; public class CoronerClient { - private final String apiUrl; - private final String coronerToken; + private final List DEFAULT_ATTRIBUTES = Arrays.asList( + CoronerQueryFields.FOLD_CALLSTACK, + CoronerQueryFields.FOLD_GUID, + CoronerQueryFields.FOLD_CLASSIFIERS + ); + + public final CoronerHttpClient coronerHttpClient; + public final CoronerQueries coronerQueries; public CoronerClient(String apiUrl, String coronerToken) { - this.apiUrl = apiUrl; - this.coronerToken = coronerToken; + this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); + this.coronerQueries = new CoronerQueries(); } - public void getReport(String rxId) throws IOException, HttpException { - String queryJson = CoronerQueries.filterByRxId(rxId); + public void rxIdFilter(String rxId) throws Exception { + String coronerQuery = this.coronerQueries.filterByRxId(rxId, DEFAULT_ATTRIBUTES); + CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); - HttpURLConnection urlConnection = prepareHttpRequest(queryJson); - int statusCode = urlConnection.getResponseCode(); - - if (statusCode != HttpURLConnection.HTTP_OK) { - String message = HttpHelper.getResponseMessage(urlConnection); - message = (BacktraceStringHelper.isNullOrEmpty(message)) ? - urlConnection.getResponseMessage() : message; - throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); + if (response.error != null) { + throw new Exception(response.getError().getMessage()); // todo: another exception type } - String resultJson = HttpHelper.getResponseMessage(urlConnection); - } - - private HttpURLConnection prepareHttpRequest(String json) throws IOException { - URL url = new URL(this.apiUrl); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - - urlConnection.setRequestMethod("POST"); - urlConnection.setUseCaches(false); - - urlConnection.setDoOutput(true); - urlConnection.setDoInput(true); - - urlConnection.setRequestProperty("Connection", "Keep-Alive"); - urlConnection.setRequestProperty("Content-Type", RequestHelper.getContentType()); - urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); - - DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); - - RequestHelper.addJson(request, json); - -// RequestHelper.addEndOfRequest(request); - - request.flush(); - request.close(); - - return urlConnection; } } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java new file mode 100644 index 00000000..ef5bb290 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java @@ -0,0 +1,31 @@ +package backtraceio.library.coroner; + +class CoronerError { + public String message; + public int code; + + public CoronerError() { + + } + + public CoronerError(String message, int code) { + this.message = message; + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java new file mode 100644 index 00000000..84e25fcd --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java @@ -0,0 +1,64 @@ +package backtraceio.library.coroner; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; + +import backtraceio.library.common.BacktraceSerializeHelper; +import backtraceio.library.common.BacktraceStringHelper; +import backtraceio.library.common.RequestHelper; +import backtraceio.library.http.HttpHelper; +import backtraceio.library.logger.BacktraceLogger; +import backtraceio.library.models.types.HttpException; + +class CoronerHttpClient { + private final String apiUrl; + private final String coronerToken; + + public CoronerHttpClient(String apiUrl, String coronerToken) { + this.apiUrl = apiUrl; + this.coronerToken = coronerToken; + } + + public CoronerApiResponse get(String json) throws IOException, HttpException { + HttpURLConnection urlConnection = prepareHttpRequest(json); + int statusCode = urlConnection.getResponseCode(); + + if (statusCode != HttpURLConnection.HTTP_OK) { + String message = HttpHelper.getResponseMessage(urlConnection); + message = (BacktraceStringHelper.isNullOrEmpty(message)) ? + urlConnection.getResponseMessage() : message; + throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); + } + + String resultJson = HttpHelper.getResponseMessage(urlConnection); + return BacktraceSerializeHelper.fromJson(resultJson, CoronerApiResponse.class); + } + + private HttpURLConnection prepareHttpRequest(String json) throws IOException { + URL url = new URL(this.apiUrl); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod("POST"); + urlConnection.setUseCaches(false); + + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Content-Type", RequestHelper.getContentType()); + urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); + + DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); + + RequestHelper.addJson(request, json); + + request.flush(); + request.close(); + + return urlConnection; + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java index efae1a33..0224fc06 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java @@ -1,23 +1,20 @@ package backtraceio.library.coroner; -public class CoronerQueries { - public static String filterByRxId(String rxId) { - return "{" + - " \"group\":[" + - " \"fingerprint\"" + - " ]," + - " \"offset\":0," + - " \"limit\":20," + - " \"filter\":[" + - " {" + - " \"_rxid\":[" + - " [" + - " \"equal\"," + - " \"" + rxId + "\"" + - " ]" + - " ]" + - " }" + - " ]" + - "}"; +import java.util.ArrayList; +import java.util.List; + +class CoronerQueries { + private final CoronerQueryBuilder builder; + + public CoronerQueries() { + builder = new CoronerQueryBuilder(); + } + + public String filterByRxId(String rxId) { + return this.filterByRxId(rxId, new ArrayList<>()); + } + + public String filterByRxId(String rxId, List attributes) { + return this.builder.build(rxId, attributes); } } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java new file mode 100644 index 00000000..de76a4d6 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java @@ -0,0 +1,50 @@ +package backtraceio.library.coroner; + +import java.util.List; +import java.util.stream.Collectors; + +class CoronerQueryBuilder { + private final String FOLD_HEAD = "head"; + private final int OFFSET = 0; + private final int LIMIT = 1; + + public String build(String rxId, List headFolds) { + String rxFilter = filterEq(CoronerQueryFields.RXID, rxId); + String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); + + return "{" + + " \"group\":[" + + " \"" + CoronerQueryFields.RXID + "\"" + + " ]," + + "\"fold\": {" + + folds + + "}," + + " \"offset\":" + OFFSET + "," + + " \"limit\":" + LIMIT + "," + + " \"filter\":[" + + rxFilter + + " ]" + + "}"; + } + + private String filterEq(String name, String val) { + return "{" + + " \"" + name + "\": [" + + " [" + + " \"equal\"," + + " \"" + val + "\"" + + " ]" + + " ]" + + "}"; + } + + + private String foldHead(String name) { + return this.fold(name, FOLD_HEAD); + } + + private String fold(String name, String val) { + return "\"" + name + "\": " + + "[" + "[\"" + val + "\"" + "]" + "]"; + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java new file mode 100644 index 00000000..0be300ed --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java @@ -0,0 +1,8 @@ +package backtraceio.library.coroner; + +class CoronerQueryFields { + public static String FOLD_CALLSTACK = "callstack"; + public static String FOLD_CLASSIFIERS = "classifiers"; + public static String FOLD_GUID = "guid"; + public static String RXID = "_rxid"; +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java new file mode 100644 index 00000000..0868fc0f --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java @@ -0,0 +1,44 @@ +package backtraceio.library.coroner; + + +import java.util.List; + +class CoronerResponse { + public List columns; + public List columns_desc; + public List values; + + public CoronerResponse() { + + } + + public CoronerResponse(List columns, List columns_desc, List values) { + this.columns = columns; + this.columns_desc = columns_desc; + this.values = values; + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public List getColumns_desc() { + return columns_desc; + } + + public void setColumns_desc(List columns_desc) { + this.columns_desc = columns_desc; + } + + public List getValues() { + return values; + } + + public void setValues(List values) { + this.values = values; + } +} From 86476b391f9df31a555739a69b474278f2bf104e Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sun, 6 Nov 2022 19:02:31 +0100 Subject: [PATCH 03/61] Add response processing, change API --- .../library/BacktraceClientSendTest.java | 1 + .../library/coroner/CoronerClient.java | 32 ++++++++-- .../library/coroner/CoronerError.java | 6 +- .../library/coroner/CoronerHttpClient.java | 11 ++-- .../library/coroner/CoronerResponse.java | 44 ------------- .../coroner/{ => query}/CoronerQueries.java | 6 +- .../{ => query}/CoronerQueryBuilder.java | 15 +++-- .../{ => query}/CoronerQueryFields.java | 4 +- .../library/coroner/query/FilterOperator.java | 5 ++ .../coroner/response/ColumnDescElement.java | 61 ++++++++++++++++++ .../{ => response}/CoronerApiResponse.java | 13 ++-- .../coroner/response/CoronerResponse.java | 62 +++++++++++++++++++ .../response/CoronerResponseException.java | 17 +++++ .../response/CoronerResponseGroup.java | 25 ++++++++ .../CoronerResponseProcessingException.java | 14 +++++ .../CoronerResponseGroupDeserializer.java | 31 ++++++++++ .../CoronerResponseGsonBuilder.java | 19 ++++++ .../common/BacktraceSerializeHelper.java | 26 +++----- .../serialization/BacktraceGsonBuilder.java | 13 ++++ .../serialization/CustomGsonBuilder.java | 7 +++ .../services/BacktraceReportSender.java | 4 +- 21 files changed, 328 insertions(+), 88 deletions(-) delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java rename backtrace-library/src/androidTest/java/backtraceio/library/coroner/{ => query}/CoronerQueries.java (73%) rename backtrace-library/src/androidTest/java/backtraceio/library/coroner/{ => query}/CoronerQueryBuilder.java (70%) rename backtrace-library/src/androidTest/java/backtraceio/library/coroner/{ => query}/CoronerQueryFields.java (72%) create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java rename backtrace-library/src/androidTest/java/backtraceio/library/coroner/{ => response}/CoronerApiResponse.java (65%) create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java create mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/common/serialization/BacktraceGsonBuilder.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/common/serialization/CustomGsonBuilder.java diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java b/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java index 0a8ec6df..b4d45234 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java @@ -182,6 +182,7 @@ public BacktraceResult onRequest(BacktraceData data) { backtraceClient.setOnRequestHandler(rh); // WHEN + backtraceClient.send(new BacktraceReport(this.resultMessage, this.attributes), new OnServerResponseEventListener() { @Override diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java index fe3234f5..2da594a7 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java @@ -1,30 +1,50 @@ package backtraceio.library.coroner; +import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import backtraceio.library.coroner.query.CoronerQueries; +import backtraceio.library.coroner.query.CoronerQueryFields; +import backtraceio.library.coroner.response.CoronerApiResponse; +import backtraceio.library.coroner.response.CoronerResponse; +import backtraceio.library.coroner.response.CoronerResponseException; +import backtraceio.library.models.types.HttpException; public class CoronerClient { + public final CoronerHttpClient coronerHttpClient; + public final CoronerQueries coronerQueries; private final List DEFAULT_ATTRIBUTES = Arrays.asList( CoronerQueryFields.FOLD_CALLSTACK, CoronerQueryFields.FOLD_GUID, CoronerQueryFields.FOLD_CLASSIFIERS ); - public final CoronerHttpClient coronerHttpClient; - public final CoronerQueries coronerQueries; - public CoronerClient(String apiUrl, String coronerToken) { this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); this.coronerQueries = new CoronerQueries(); } - public void rxIdFilter(String rxId) throws Exception { - String coronerQuery = this.coronerQueries.filterByRxId(rxId, DEFAULT_ATTRIBUTES); + public CoronerResponse rxIdFilter(String rxId) throws Exception { + return this.rxIdFilter(rxId, new ArrayList<>()); + } + + public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, HttpException, IOException { + List attributes = Stream.concat( + DEFAULT_ATTRIBUTES.stream(), + customAttributes.stream()) + .collect(Collectors.toList()); + String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); + CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); if (response.error != null) { - throw new Exception(response.getError().getMessage()); // todo: another exception type + throw new CoronerResponseException(response.getError()); } + return response.getResponse(); } } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java index ef5bb290..1d0a0bbf 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java @@ -1,13 +1,15 @@ package backtraceio.library.coroner; -class CoronerError { +public class CoronerError { public String message; public int code; + @SuppressWarnings("unused") public CoronerError() { } + @SuppressWarnings("unused") public CoronerError(String message, int code) { this.message = message; this.code = code; @@ -21,10 +23,12 @@ public void setMessage(String message) { this.message = message; } + @SuppressWarnings("unused") public int getCode() { return code; } + @SuppressWarnings("unused") public void setCode(int code) { this.code = code; } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java index 84e25fcd..66a1685f 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java @@ -3,15 +3,14 @@ import java.io.DataOutputStream; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.ProtocolException; import java.net.URL; import backtraceio.library.common.BacktraceSerializeHelper; import backtraceio.library.common.BacktraceStringHelper; import backtraceio.library.common.RequestHelper; +import backtraceio.library.coroner.response.CoronerApiResponse; +import backtraceio.library.coroner.serialization.CoronerResponseGsonBuilder; import backtraceio.library.http.HttpHelper; -import backtraceio.library.logger.BacktraceLogger; import backtraceio.library.models.types.HttpException; class CoronerHttpClient { @@ -35,7 +34,11 @@ public CoronerApiResponse get(String json) throws IOException, HttpException { } String resultJson = HttpHelper.getResponseMessage(urlConnection); - return BacktraceSerializeHelper.fromJson(resultJson, CoronerApiResponse.class); + + return BacktraceSerializeHelper.fromJson( + new CoronerResponseGsonBuilder().buildGson(), + resultJson, + CoronerApiResponse.class); } private HttpURLConnection prepareHttpRequest(String json) throws IOException { diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java deleted file mode 100644 index 0868fc0f..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerResponse.java +++ /dev/null @@ -1,44 +0,0 @@ -package backtraceio.library.coroner; - - -import java.util.List; - -class CoronerResponse { - public List columns; - public List columns_desc; - public List values; - - public CoronerResponse() { - - } - - public CoronerResponse(List columns, List columns_desc, List values) { - this.columns = columns; - this.columns_desc = columns_desc; - this.values = values; - } - - public List getColumns() { - return columns; - } - - public void setColumns(List columns) { - this.columns = columns; - } - - public List getColumns_desc() { - return columns_desc; - } - - public void setColumns_desc(List columns_desc) { - this.columns_desc = columns_desc; - } - - public List getValues() { - return values; - } - - public void setValues(List values) { - this.values = values; - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java similarity index 73% rename from backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java rename to backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java index 0224fc06..ede3e857 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueries.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java @@ -1,9 +1,9 @@ -package backtraceio.library.coroner; +package backtraceio.library.coroner.query; import java.util.ArrayList; import java.util.List; -class CoronerQueries { +public class CoronerQueries { private final CoronerQueryBuilder builder; public CoronerQueries() { @@ -15,6 +15,6 @@ public String filterByRxId(String rxId) { } public String filterByRxId(String rxId, List attributes) { - return this.builder.build(rxId, attributes); + return this.builder.buildRxIdGroup(rxId, attributes); } } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java similarity index 70% rename from backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java rename to backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java index de76a4d6..6be2c8b3 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryBuilder.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java @@ -1,4 +1,4 @@ -package backtraceio.library.coroner; +package backtraceio.library.coroner.query; import java.util.List; import java.util.stream.Collectors; @@ -8,13 +8,17 @@ class CoronerQueryBuilder { private final int OFFSET = 0; private final int LIMIT = 1; - public String build(String rxId, List headFolds) { - String rxFilter = filterEq(CoronerQueryFields.RXID, rxId); + public String buildRxIdGroup(String rxId, List headFolds) { + return this.build(CoronerQueryFields.RXID, CoronerQueryFields.RXID, rxId, headFolds); + } + + public String build(String groupName, String filterName, String filterValue, List headFolds) { + String rxFilter = filterEq(filterName, filterValue); String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); return "{" + " \"group\":[" + - " \"" + CoronerQueryFields.RXID + "\"" + + " \"" + groupName + "\"" + " ]," + "\"fold\": {" + folds + @@ -31,14 +35,13 @@ private String filterEq(String name, String val) { return "{" + " \"" + name + "\": [" + " [" + - " \"equal\"," + + " \"" + FilterOperator.EQUAL + "\"," + " \"" + val + "\"" + " ]" + " ]" + "}"; } - private String foldHead(String name) { return this.fold(name, FOLD_HEAD); } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java similarity index 72% rename from backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java rename to backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java index 0be300ed..6fe08c4d 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerQueryFields.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java @@ -1,6 +1,6 @@ -package backtraceio.library.coroner; +package backtraceio.library.coroner.query; -class CoronerQueryFields { +public class CoronerQueryFields { public static String FOLD_CALLSTACK = "callstack"; public static String FOLD_CLASSIFIERS = "classifiers"; public static String FOLD_GUID = "guid"; diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java new file mode 100644 index 00000000..c760f39c --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java @@ -0,0 +1,5 @@ +package backtraceio.library.coroner.query; + +public class FilterOperator { + public static final String EQUAL = "equal"; +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java new file mode 100644 index 00000000..93fa9ded --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java @@ -0,0 +1,61 @@ +package backtraceio.library.coroner.response; + +public class ColumnDescElement { + public String name; + public String format; + public String type; + public String op; + + @SuppressWarnings("unused") + public ColumnDescElement() { + + } + + @SuppressWarnings("unused") + public ColumnDescElement(String name, String format, String type, String op) { + this.name = name; + this.format = format; + this.type = type; + this.op = op; + } + + @SuppressWarnings("unused") + public String getName() { + return name; + } + + @SuppressWarnings("unused") + public void setName(String name) { + this.name = name; + } + + @SuppressWarnings("unused") + public String getFormat() { + return format; + } + + @SuppressWarnings("unused") + public void setFormat(String format) { + this.format = format; + } + + @SuppressWarnings("unused") + public String getType() { + return type; + } + + @SuppressWarnings("unused") + public void setType(String type) { + this.type = type; + } + + @SuppressWarnings("unused") + public String getOp() { + return op; + } + + @SuppressWarnings("unused") + public void setOp(String op) { + this.op = op; + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java similarity index 65% rename from backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java rename to backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java index 64765036..5ae46936 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerApiResponse.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java @@ -1,15 +1,18 @@ -package backtraceio.library.coroner; +package backtraceio.library.coroner.response; -import java.util.List; +import backtraceio.library.coroner.CoronerError; -class CoronerApiResponse { + +public class CoronerApiResponse { public CoronerError error; public CoronerResponse response; + @SuppressWarnings("unused") public CoronerApiResponse() { } + @SuppressWarnings("unused") public CoronerApiResponse(CoronerError error, CoronerResponse response) { this.error = error; this.response = response; @@ -19,14 +22,16 @@ public CoronerError getError() { return error; } + @SuppressWarnings("unused") public void setError(CoronerError error) { this.error = error; } public CoronerResponse getResponse() { - return response; + return this.response; } + @SuppressWarnings("unused") public void setResponse(CoronerResponse response) { this.response = response; } diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java new file mode 100644 index 00000000..35c22385 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java @@ -0,0 +1,62 @@ +package backtraceio.library.coroner.response; + + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +import backtraceio.library.coroner.serialization.CoronerResponseGroupDeserializer; +import backtraceio.library.logger.BacktraceLogger; + +public class CoronerResponse { + + private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); + + @SerializedName(value = "columns_desc") + public List columnsDesc; + public List values; + + @SuppressWarnings("unused") + public CoronerResponse() { + } + + @SuppressWarnings("unused") + public CoronerResponse(List columnsDesc, List values) { + this.columnsDesc = columnsDesc; + this.values = values; + } + + public T getAttribute(int elementIndex, String name, Class clazz) throws CoronerResponseProcessingException { + if (this.values == null) { + throw new CoronerResponseProcessingException("Values property from response is null"); + } + if (elementIndex < 0 || elementIndex > this.values.size()) { + throw new CoronerResponseProcessingException("Incorrect element index, value should be between 0 and " + this.values.size()); + } + CoronerResponseGroup responseGroup = values.get(elementIndex); + + int attributeIndex = getAttributeIndex(name); + try { + List results = (ArrayList) responseGroup.getAttribute(attributeIndex); + return clazz.cast(results.get(0)); + } catch (ClassCastException e) { + BacktraceLogger.e(LOG_TAG, e.getMessage()); + throw new CoronerResponseProcessingException("Error on getting attribute from response group for attribute index: " + attributeIndex); + } + } + + public int getResultsNumber() { + return values.size(); + } + + private int getAttributeIndex(String attributeName) throws CoronerResponseProcessingException { + for (int index = 0; index < this.columnsDesc.size(); index++) { + if (this.columnsDesc.get(index).name.equals(attributeName)) { + return index; + } + } + throw new CoronerResponseProcessingException("Attribute not found for name " + attributeName); + } + +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java new file mode 100644 index 00000000..bde604aa --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java @@ -0,0 +1,17 @@ +package backtraceio.library.coroner.response; + +import backtraceio.library.coroner.CoronerError; + +public class CoronerResponseException extends Exception { + private final CoronerError coronerError; + + public CoronerResponseException(CoronerError coronerError) { + super(coronerError.getMessage()); + this.coronerError = coronerError; + } + + @SuppressWarnings("unused") + public CoronerError getCoronerError() { + return coronerError; + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java new file mode 100644 index 00000000..58bb40bf --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java @@ -0,0 +1,25 @@ +package backtraceio.library.coroner.response; + +import java.util.List; + +public class CoronerResponseGroup { + private static final Integer EXPECTED_NUMBER_OF_EL = 3; + @SuppressWarnings("unused") + private final String groupIdentifier; + private final List values; + + public CoronerResponseGroup(List obj) throws IllegalArgumentException { + if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { + throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL + + ", current value: " + (obj != null ? obj.size() : "null")); + } + + this.groupIdentifier = obj.get(0).toString(); + this.values = (List) obj.get(1); + } + + public Object getAttribute(int index) { + return values.get(index); + } + +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java new file mode 100644 index 00000000..72e3f85f --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java @@ -0,0 +1,14 @@ +package backtraceio.library.coroner.response; + +public class CoronerResponseProcessingException extends Exception { + + @SuppressWarnings("unused") + public CoronerResponseProcessingException() { + super(); + } + + @SuppressWarnings("unused") + public CoronerResponseProcessingException(String message) { + super(message); + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java new file mode 100644 index 00000000..b33882e3 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java @@ -0,0 +1,31 @@ +package backtraceio.library.coroner.serialization; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.util.List; + +import backtraceio.library.coroner.response.CoronerResponseGroup; +import backtraceio.library.logger.BacktraceLogger; + +public class CoronerResponseGroupDeserializer implements JsonDeserializer { + private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); + + @Override + public CoronerResponseGroup deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray jsonArray = json.getAsJsonArray(); + List obj = new Gson().fromJson(jsonArray, (Type) Object.class); + try { + return new CoronerResponseGroup(obj); + } catch (Exception e) { + BacktraceLogger.e(LOG_TAG, "" + e.getMessage()); + e.printStackTrace(); + return null; + } + } +} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java new file mode 100644 index 00000000..67786461 --- /dev/null +++ b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java @@ -0,0 +1,19 @@ +package backtraceio.library.coroner.serialization; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import backtraceio.library.common.serialization.CustomGsonBuilder; +import backtraceio.library.coroner.response.CoronerResponseGroup; + +public class CoronerResponseGsonBuilder implements CustomGsonBuilder { + + @Override + public Gson buildGson() { + return new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) + .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) + .create(); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/common/BacktraceSerializeHelper.java b/backtrace-library/src/main/java/backtraceio/library/common/BacktraceSerializeHelper.java index 75dd8b42..d87babc5 100644 --- a/backtrace-library/src/main/java/backtraceio/library/common/BacktraceSerializeHelper.java +++ b/backtrace-library/src/main/java/backtraceio/library/common/BacktraceSerializeHelper.java @@ -4,6 +4,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import backtraceio.library.common.serialization.BacktraceGsonBuilder; import backtraceio.library.models.BacktraceResult; /** @@ -11,16 +12,6 @@ */ public class BacktraceSerializeHelper { - /** - * Deserialize JSON into BacktraceResult object - * - * @param json JSON string which will be deserialized - * @return object created during deserialization of given json string - */ - public static BacktraceResult backtraceResultFromJson(String json) { - return new Gson().fromJson(json, BacktraceResult.class); - } - /** * Serialize given object to JSON string * @@ -28,16 +19,19 @@ public static BacktraceResult backtraceResultFromJson(String json) { * @return serialized object in JSON string format */ public static String toJson(Object object) { - Gson gson = buildGson(); - return gson.toJson(object); + return BacktraceSerializeHelper.toJson(new BacktraceGsonBuilder().buildGson(), object); } public static T fromJson(String json, Class type) { - Gson gson = buildGson(); - return gson.fromJson(json, type); + return BacktraceSerializeHelper.fromJson(new BacktraceGsonBuilder().buildGson(), json, type); + } + + public static String toJson(Gson gson, Object object) { + return gson.toJson(object); } - private static Gson buildGson() { - return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create(); + public static T fromJson(Gson gson, String json, Class type) { + return gson.fromJson(json, type); } + } diff --git a/backtrace-library/src/main/java/backtraceio/library/common/serialization/BacktraceGsonBuilder.java b/backtrace-library/src/main/java/backtraceio/library/common/serialization/BacktraceGsonBuilder.java new file mode 100644 index 00000000..4b82160f --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/common/serialization/BacktraceGsonBuilder.java @@ -0,0 +1,13 @@ +package backtraceio.library.common.serialization; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class BacktraceGsonBuilder implements CustomGsonBuilder { + + @Override + public Gson buildGson() { + return new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES).create(); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/common/serialization/CustomGsonBuilder.java b/backtrace-library/src/main/java/backtraceio/library/common/serialization/CustomGsonBuilder.java new file mode 100644 index 00000000..e991c96c --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/common/serialization/CustomGsonBuilder.java @@ -0,0 +1,7 @@ +package backtraceio.library.common.serialization; + +import com.google.gson.Gson; + +public interface CustomGsonBuilder { + Gson buildGson(); +} diff --git a/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java b/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java index 4d711a4f..fb2c11c5 100644 --- a/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java +++ b/backtrace-library/src/main/java/backtraceio/library/services/BacktraceReportSender.java @@ -73,8 +73,8 @@ static BacktraceResult sendReport(String serverUrl, String json, List at BacktraceLogger.d(LOG_TAG, "Received response status from Backtrace API for HTTP request is: " + statusCode); if (statusCode == HttpURLConnection.HTTP_OK) { - result = BacktraceSerializeHelper.backtraceResultFromJson( - HttpHelper.getResponseMessage(urlConnection) + result = BacktraceSerializeHelper.fromJson( + HttpHelper.getResponseMessage(urlConnection), BacktraceResult.class ); result.setBacktraceReport(report); } else { From 8106a9304b551c3cde3be912845043225d746e27 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 14 Nov 2022 22:31:24 +0100 Subject: [PATCH 04/61] Extract coroner client to separate package --- .../library/coroner/CoronerClient.java | 50 ++++++++++ .../library/coroner/CoronerError.java | 35 +++++++ .../library/coroner/CoronerHttpClient.java | 67 +++++++++++++ .../library/coroner/query/CoronerQueries.java | 20 ++++ .../coroner/query/CoronerQueryBuilder.java | 53 ++++++++++ .../coroner/query/CoronerQueryFields.java | 8 ++ .../library/coroner/query/FilterOperator.java | 5 + .../coroner/response/ColumnDescElement.java | 61 ++++++++++++ .../coroner/response/CoronerApiResponse.java | 39 ++++++++ .../coroner/response/CoronerResponse.java | 62 ++++++++++++ .../response/CoronerResponseException.java | 17 ++++ .../response/CoronerResponseGroup.java | 25 +++++ .../CoronerResponseProcessingException.java | 14 +++ .../CoronerResponseGroupDeserializer.java | 31 ++++++ .../CoronerResponseGsonBuilder.java | 19 ++++ .../library/services/BacktraceApi.java | 10 -- coroner-client/.gitignore | 1 + coroner-client/build.gradle | 13 +++ .../backtraceio/coroner/CoronerClient.java | 50 ++++++++++ .../backtraceio/coroner/CoronerError.java | 35 +++++++ .../coroner/CoronerHttpClient.java | 97 +++++++++++++++++++ .../backtraceio/coroner/common/Common.java | 7 ++ .../coroner/query/CoronerQueries.java | 20 ++++ .../coroner/query/CoronerQueryBuilder.java | 53 ++++++++++ .../coroner/query/CoronerQueryFields.java | 8 ++ .../coroner/query/FilterOperator.java | 5 + .../coroner/response/ColumnDescElement.java | 61 ++++++++++++ .../coroner/response/CoronerApiResponse.java | 40 ++++++++ .../response/CoronerHttpException.java | 62 ++++++++++++ .../coroner/response/CoronerResponse.java | 60 ++++++++++++ .../response/CoronerResponseException.java | 17 ++++ .../response/CoronerResponseGroup.java | 25 +++++ .../CoronerResponseProcessingException.java | 14 +++ .../CoronerResponseGroupDeserializer.java | 33 +++++++ .../coroner/serialization/GsonWrapper.java | 20 ++++ settings.gradle | 1 + 36 files changed, 1128 insertions(+), 10 deletions(-) create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java create mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java create mode 100644 coroner-client/.gitignore create mode 100644 coroner-client/build.gradle create mode 100644 coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/CoronerError.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/common/Common.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/ColumnDescElement.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java new file mode 100644 index 00000000..2da594a7 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java @@ -0,0 +1,50 @@ +package backtraceio.library.coroner; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import backtraceio.library.coroner.query.CoronerQueries; +import backtraceio.library.coroner.query.CoronerQueryFields; +import backtraceio.library.coroner.response.CoronerApiResponse; +import backtraceio.library.coroner.response.CoronerResponse; +import backtraceio.library.coroner.response.CoronerResponseException; +import backtraceio.library.models.types.HttpException; + +public class CoronerClient { + public final CoronerHttpClient coronerHttpClient; + public final CoronerQueries coronerQueries; + private final List DEFAULT_ATTRIBUTES = Arrays.asList( + CoronerQueryFields.FOLD_CALLSTACK, + CoronerQueryFields.FOLD_GUID, + CoronerQueryFields.FOLD_CLASSIFIERS + ); + + public CoronerClient(String apiUrl, String coronerToken) { + this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); + this.coronerQueries = new CoronerQueries(); + } + + public CoronerResponse rxIdFilter(String rxId) throws Exception { + return this.rxIdFilter(rxId, new ArrayList<>()); + } + + public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, HttpException, IOException { + List attributes = Stream.concat( + DEFAULT_ATTRIBUTES.stream(), + customAttributes.stream()) + .collect(Collectors.toList()); + String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); + + CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); + + if (response.error != null) { + throw new CoronerResponseException(response.getError()); + } + + return response.getResponse(); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java new file mode 100644 index 00000000..1d0a0bbf --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java @@ -0,0 +1,35 @@ +package backtraceio.library.coroner; + +public class CoronerError { + public String message; + public int code; + + @SuppressWarnings("unused") + public CoronerError() { + + } + + @SuppressWarnings("unused") + public CoronerError(String message, int code) { + this.message = message; + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @SuppressWarnings("unused") + public int getCode() { + return code; + } + + @SuppressWarnings("unused") + public void setCode(int code) { + this.code = code; + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java new file mode 100644 index 00000000..66a1685f --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java @@ -0,0 +1,67 @@ +package backtraceio.library.coroner; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import backtraceio.library.common.BacktraceSerializeHelper; +import backtraceio.library.common.BacktraceStringHelper; +import backtraceio.library.common.RequestHelper; +import backtraceio.library.coroner.response.CoronerApiResponse; +import backtraceio.library.coroner.serialization.CoronerResponseGsonBuilder; +import backtraceio.library.http.HttpHelper; +import backtraceio.library.models.types.HttpException; + +class CoronerHttpClient { + private final String apiUrl; + private final String coronerToken; + + public CoronerHttpClient(String apiUrl, String coronerToken) { + this.apiUrl = apiUrl; + this.coronerToken = coronerToken; + } + + public CoronerApiResponse get(String json) throws IOException, HttpException { + HttpURLConnection urlConnection = prepareHttpRequest(json); + int statusCode = urlConnection.getResponseCode(); + + if (statusCode != HttpURLConnection.HTTP_OK) { + String message = HttpHelper.getResponseMessage(urlConnection); + message = (BacktraceStringHelper.isNullOrEmpty(message)) ? + urlConnection.getResponseMessage() : message; + throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); + } + + String resultJson = HttpHelper.getResponseMessage(urlConnection); + + return BacktraceSerializeHelper.fromJson( + new CoronerResponseGsonBuilder().buildGson(), + resultJson, + CoronerApiResponse.class); + } + + private HttpURLConnection prepareHttpRequest(String json) throws IOException { + URL url = new URL(this.apiUrl); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod("POST"); + urlConnection.setUseCaches(false); + + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Content-Type", RequestHelper.getContentType()); + urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); + + DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); + + RequestHelper.addJson(request, json); + + request.flush(); + request.close(); + + return urlConnection; + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java new file mode 100644 index 00000000..ede3e857 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java @@ -0,0 +1,20 @@ +package backtraceio.library.coroner.query; + +import java.util.ArrayList; +import java.util.List; + +public class CoronerQueries { + private final CoronerQueryBuilder builder; + + public CoronerQueries() { + builder = new CoronerQueryBuilder(); + } + + public String filterByRxId(String rxId) { + return this.filterByRxId(rxId, new ArrayList<>()); + } + + public String filterByRxId(String rxId, List attributes) { + return this.builder.buildRxIdGroup(rxId, attributes); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java new file mode 100644 index 00000000..6be2c8b3 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java @@ -0,0 +1,53 @@ +package backtraceio.library.coroner.query; + +import java.util.List; +import java.util.stream.Collectors; + +class CoronerQueryBuilder { + private final String FOLD_HEAD = "head"; + private final int OFFSET = 0; + private final int LIMIT = 1; + + public String buildRxIdGroup(String rxId, List headFolds) { + return this.build(CoronerQueryFields.RXID, CoronerQueryFields.RXID, rxId, headFolds); + } + + public String build(String groupName, String filterName, String filterValue, List headFolds) { + String rxFilter = filterEq(filterName, filterValue); + String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); + + return "{" + + " \"group\":[" + + " \"" + groupName + "\"" + + " ]," + + "\"fold\": {" + + folds + + "}," + + " \"offset\":" + OFFSET + "," + + " \"limit\":" + LIMIT + "," + + " \"filter\":[" + + rxFilter + + " ]" + + "}"; + } + + private String filterEq(String name, String val) { + return "{" + + " \"" + name + "\": [" + + " [" + + " \"" + FilterOperator.EQUAL + "\"," + + " \"" + val + "\"" + + " ]" + + " ]" + + "}"; + } + + private String foldHead(String name) { + return this.fold(name, FOLD_HEAD); + } + + private String fold(String name, String val) { + return "\"" + name + "\": " + + "[" + "[\"" + val + "\"" + "]" + "]"; + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java new file mode 100644 index 00000000..6fe08c4d --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java @@ -0,0 +1,8 @@ +package backtraceio.library.coroner.query; + +public class CoronerQueryFields { + public static String FOLD_CALLSTACK = "callstack"; + public static String FOLD_CLASSIFIERS = "classifiers"; + public static String FOLD_GUID = "guid"; + public static String RXID = "_rxid"; +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java new file mode 100644 index 00000000..c760f39c --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java @@ -0,0 +1,5 @@ +package backtraceio.library.coroner.query; + +public class FilterOperator { + public static final String EQUAL = "equal"; +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java new file mode 100644 index 00000000..93fa9ded --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java @@ -0,0 +1,61 @@ +package backtraceio.library.coroner.response; + +public class ColumnDescElement { + public String name; + public String format; + public String type; + public String op; + + @SuppressWarnings("unused") + public ColumnDescElement() { + + } + + @SuppressWarnings("unused") + public ColumnDescElement(String name, String format, String type, String op) { + this.name = name; + this.format = format; + this.type = type; + this.op = op; + } + + @SuppressWarnings("unused") + public String getName() { + return name; + } + + @SuppressWarnings("unused") + public void setName(String name) { + this.name = name; + } + + @SuppressWarnings("unused") + public String getFormat() { + return format; + } + + @SuppressWarnings("unused") + public void setFormat(String format) { + this.format = format; + } + + @SuppressWarnings("unused") + public String getType() { + return type; + } + + @SuppressWarnings("unused") + public void setType(String type) { + this.type = type; + } + + @SuppressWarnings("unused") + public String getOp() { + return op; + } + + @SuppressWarnings("unused") + public void setOp(String op) { + this.op = op; + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java new file mode 100644 index 00000000..5ae46936 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java @@ -0,0 +1,39 @@ +package backtraceio.library.coroner.response; + +import backtraceio.library.coroner.CoronerError; + + +public class CoronerApiResponse { + public CoronerError error; + public CoronerResponse response; + + @SuppressWarnings("unused") + public CoronerApiResponse() { + + } + + @SuppressWarnings("unused") + public CoronerApiResponse(CoronerError error, CoronerResponse response) { + this.error = error; + this.response = response; + } + + public CoronerError getError() { + return error; + } + + @SuppressWarnings("unused") + public void setError(CoronerError error) { + this.error = error; + } + + public CoronerResponse getResponse() { + return this.response; + } + + @SuppressWarnings("unused") + public void setResponse(CoronerResponse response) { + this.response = response; + } +} + diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java new file mode 100644 index 00000000..35c22385 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java @@ -0,0 +1,62 @@ +package backtraceio.library.coroner.response; + + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; + +import backtraceio.library.coroner.serialization.CoronerResponseGroupDeserializer; +import backtraceio.library.logger.BacktraceLogger; + +public class CoronerResponse { + + private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); + + @SerializedName(value = "columns_desc") + public List columnsDesc; + public List values; + + @SuppressWarnings("unused") + public CoronerResponse() { + } + + @SuppressWarnings("unused") + public CoronerResponse(List columnsDesc, List values) { + this.columnsDesc = columnsDesc; + this.values = values; + } + + public T getAttribute(int elementIndex, String name, Class clazz) throws CoronerResponseProcessingException { + if (this.values == null) { + throw new CoronerResponseProcessingException("Values property from response is null"); + } + if (elementIndex < 0 || elementIndex > this.values.size()) { + throw new CoronerResponseProcessingException("Incorrect element index, value should be between 0 and " + this.values.size()); + } + CoronerResponseGroup responseGroup = values.get(elementIndex); + + int attributeIndex = getAttributeIndex(name); + try { + List results = (ArrayList) responseGroup.getAttribute(attributeIndex); + return clazz.cast(results.get(0)); + } catch (ClassCastException e) { + BacktraceLogger.e(LOG_TAG, e.getMessage()); + throw new CoronerResponseProcessingException("Error on getting attribute from response group for attribute index: " + attributeIndex); + } + } + + public int getResultsNumber() { + return values.size(); + } + + private int getAttributeIndex(String attributeName) throws CoronerResponseProcessingException { + for (int index = 0; index < this.columnsDesc.size(); index++) { + if (this.columnsDesc.get(index).name.equals(attributeName)) { + return index; + } + } + throw new CoronerResponseProcessingException("Attribute not found for name " + attributeName); + } + +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java new file mode 100644 index 00000000..bde604aa --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java @@ -0,0 +1,17 @@ +package backtraceio.library.coroner.response; + +import backtraceio.library.coroner.CoronerError; + +public class CoronerResponseException extends Exception { + private final CoronerError coronerError; + + public CoronerResponseException(CoronerError coronerError) { + super(coronerError.getMessage()); + this.coronerError = coronerError; + } + + @SuppressWarnings("unused") + public CoronerError getCoronerError() { + return coronerError; + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java new file mode 100644 index 00000000..58bb40bf --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java @@ -0,0 +1,25 @@ +package backtraceio.library.coroner.response; + +import java.util.List; + +public class CoronerResponseGroup { + private static final Integer EXPECTED_NUMBER_OF_EL = 3; + @SuppressWarnings("unused") + private final String groupIdentifier; + private final List values; + + public CoronerResponseGroup(List obj) throws IllegalArgumentException { + if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { + throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL + + ", current value: " + (obj != null ? obj.size() : "null")); + } + + this.groupIdentifier = obj.get(0).toString(); + this.values = (List) obj.get(1); + } + + public Object getAttribute(int index) { + return values.get(index); + } + +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java new file mode 100644 index 00000000..72e3f85f --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java @@ -0,0 +1,14 @@ +package backtraceio.library.coroner.response; + +public class CoronerResponseProcessingException extends Exception { + + @SuppressWarnings("unused") + public CoronerResponseProcessingException() { + super(); + } + + @SuppressWarnings("unused") + public CoronerResponseProcessingException(String message) { + super(message); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java b/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java new file mode 100644 index 00000000..b33882e3 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java @@ -0,0 +1,31 @@ +package backtraceio.library.coroner.serialization; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.util.List; + +import backtraceio.library.coroner.response.CoronerResponseGroup; +import backtraceio.library.logger.BacktraceLogger; + +public class CoronerResponseGroupDeserializer implements JsonDeserializer { + private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); + + @Override + public CoronerResponseGroup deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray jsonArray = json.getAsJsonArray(); + List obj = new Gson().fromJson(jsonArray, (Type) Object.class); + try { + return new CoronerResponseGroup(obj); + } catch (Exception e) { + BacktraceLogger.e(LOG_TAG, "" + e.getMessage()); + e.printStackTrace(); + return null; + } + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java b/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java new file mode 100644 index 00000000..67786461 --- /dev/null +++ b/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java @@ -0,0 +1,19 @@ +package backtraceio.library.coroner.serialization; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import backtraceio.library.common.serialization.CustomGsonBuilder; +import backtraceio.library.coroner.response.CoronerResponseGroup; + +public class CoronerResponseGsonBuilder implements CustomGsonBuilder { + + @Override + public Gson buildGson() { + return new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) + .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) + .create(); + } +} diff --git a/backtrace-library/src/main/java/backtraceio/library/services/BacktraceApi.java b/backtrace-library/src/main/java/backtraceio/library/services/BacktraceApi.java index cb111374..0bd48290 100644 --- a/backtrace-library/src/main/java/backtraceio/library/services/BacktraceApi.java +++ b/backtrace-library/src/main/java/backtraceio/library/services/BacktraceApi.java @@ -26,16 +26,6 @@ public class BacktraceApi implements Api { */ private final String reportSubmissionUrl; - /** - * URL to unique events endpoint - */ - private String uniqueEventsSubmissionUrl; - - /** - * URL to summed events endpoint - */ - private String summedEventsSubmissionUrl; - /** * Event triggered when server respond with error */ diff --git a/coroner-client/.gitignore b/coroner-client/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/coroner-client/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/coroner-client/build.gradle b/coroner-client/build.gradle new file mode 100644 index 00000000..b5fbfdf0 --- /dev/null +++ b/coroner-client/build.gradle @@ -0,0 +1,13 @@ +plugins { + id 'java-library' +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.google.code.gson:gson:2.9.1' +} \ No newline at end of file diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java new file mode 100644 index 00000000..37486bcf --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -0,0 +1,50 @@ +package backtraceio.coroner; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import backtraceio.coroner.query.CoronerQueries; +import backtraceio.coroner.query.CoronerQueryFields; +import backtraceio.coroner.response.CoronerApiResponse; +import backtraceio.coroner.response.CoronerHttpException; +import backtraceio.coroner.response.CoronerResponse; +import backtraceio.coroner.response.CoronerResponseException; + +public class CoronerClient { + public final CoronerHttpClient coronerHttpClient; + public final CoronerQueries coronerQueries; + private final List DEFAULT_ATTRIBUTES = Arrays.asList( + CoronerQueryFields.FOLD_CALLSTACK, + CoronerQueryFields.FOLD_GUID, + CoronerQueryFields.FOLD_CLASSIFIERS + ); + + public CoronerClient(String apiUrl, String coronerToken) { + this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); + this.coronerQueries = new CoronerQueries(); + } + + public CoronerResponse rxIdFilter(String rxId) throws Exception { + return this.rxIdFilter(rxId, new ArrayList<>()); + } + + public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, CoronerHttpException, IOException { + List attributes = Stream.concat( + DEFAULT_ATTRIBUTES.stream(), + customAttributes.stream()) + .collect(Collectors.toList()); + String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); + + CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); + + if (response.error != null) { + throw new CoronerResponseException(response.getError()); + } + + return response.getResponse(); + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java new file mode 100644 index 00000000..5b194bb9 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java @@ -0,0 +1,35 @@ +package backtraceio.coroner; + +public class CoronerError { + public String message; + public int code; + + @SuppressWarnings("unused") + public CoronerError() { + + } + + @SuppressWarnings("unused") + public CoronerError(String message, int code) { + this.message = message; + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @SuppressWarnings("unused") + public int getCode() { + return code; + } + + @SuppressWarnings("unused") + public void setCode(int code) { + this.code = code; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java new file mode 100644 index 00000000..e6288b71 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -0,0 +1,97 @@ +package backtraceio.coroner; + + + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.logging.Logger; +import java.util.logging.Level; + +import backtraceio.coroner.common.Common; +import backtraceio.coroner.response.CoronerApiResponse; +import backtraceio.coroner.response.CoronerHttpException; +import backtraceio.coroner.serialization.CoronerResponseGroupDeserializer; +import backtraceio.coroner.serialization.GsonWrapper; + +class CoronerHttpClient { + private static final Logger LOGGER = Logger.getLogger( CoronerResponseGroupDeserializer.class.getName() ); + private final String apiUrl; + private final String coronerToken; + private final String ENCODING = "utf-8"; + + public CoronerHttpClient(String apiUrl, String coronerToken) { + this.apiUrl = apiUrl; + this.coronerToken = coronerToken; + } + + public CoronerApiResponse get(String json) throws CoronerHttpException, IOException { + HttpURLConnection urlConnection = prepareHttpRequest(json); + int statusCode = urlConnection.getResponseCode(); + + if (statusCode != HttpURLConnection.HTTP_OK) { + String message = getResponseMessage(urlConnection); + message = (Common.isNullOrEmpty(message)) ? + urlConnection.getResponseMessage() : message; + throw new CoronerHttpException(statusCode, String.format("%s: %s", statusCode, message)); + } + + String resultJson = getResponseMessage(urlConnection); + + return GsonWrapper.fromJson( + resultJson, + CoronerApiResponse.class); + } + + private static String getResponseMessage(HttpURLConnection urlConnection) throws IOException { + LOGGER.log(Level.INFO, "Reading response from HTTP request"); + + InputStream inputStream = getInputStream(urlConnection); + + BufferedReader br = new BufferedReader(new InputStreamReader( + inputStream)); + + StringBuilder responseSB = new StringBuilder(); + String line; + while ((line = br.readLine()) != null) { + responseSB.append(line); + } + br.close(); + return responseSB.toString(); + } + + private static InputStream getInputStream(HttpURLConnection urlConnection) throws IOException { + if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) + return urlConnection.getInputStream(); + return urlConnection.getErrorStream(); + } + + private HttpURLConnection prepareHttpRequest(String json) throws IOException { + URL url = new URL(this.apiUrl); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + + urlConnection.setRequestMethod("POST"); + urlConnection.setUseCaches(false); + + urlConnection.setDoOutput(true); + urlConnection.setDoInput(true); + + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); + + DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); + + byte[] bytes = json.getBytes(ENCODING); + request.write(bytes); + + request.flush(); + request.close(); + + return urlConnection; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/common/Common.java b/coroner-client/src/main/java/backtraceio/coroner/common/Common.java new file mode 100644 index 00000000..37bf6abd --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/common/Common.java @@ -0,0 +1,7 @@ +package backtraceio.coroner.common; + +public class Common { + public static boolean isNullOrEmpty(String input) { + return (input == null || input.trim().isEmpty()); + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java new file mode 100644 index 00000000..4ab6ab12 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -0,0 +1,20 @@ +package backtraceio.coroner.query; + +import java.util.ArrayList; +import java.util.List; + +public class CoronerQueries { + private final CoronerQueryBuilder builder; + + public CoronerQueries() { + builder = new CoronerQueryBuilder(); + } + + public String filterByRxId(String rxId) { + return this.filterByRxId(rxId, new ArrayList<>()); + } + + public String filterByRxId(String rxId, List attributes) { + return this.builder.buildRxIdGroup(rxId, attributes); + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java new file mode 100644 index 00000000..5740a1e7 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -0,0 +1,53 @@ +package backtraceio.coroner.query; + +import java.util.List; +import java.util.stream.Collectors; + +class CoronerQueryBuilder { + private final String FOLD_HEAD = "head"; + private final int OFFSET = 0; + private final int LIMIT = 1; + + public String buildRxIdGroup(String rxId, List headFolds) { + return this.build(CoronerQueryFields.RXID, CoronerQueryFields.RXID, rxId, headFolds); + } + + public String build(String groupName, String filterName, String filterValue, List headFolds) { + String rxFilter = filterEq(filterName, filterValue); + String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); + + return "{" + + " \"group\":[" + + " \"" + groupName + "\"" + + " ]," + + "\"fold\": {" + + folds + + "}," + + " \"offset\":" + OFFSET + "," + + " \"limit\":" + LIMIT + "," + + " \"filter\":[" + + rxFilter + + " ]" + + "}"; + } + + private String filterEq(String name, String val) { + return "{" + + " \"" + name + "\": [" + + " [" + + " \"" + FilterOperator.EQUAL + "\"," + + " \"" + val + "\"" + + " ]" + + " ]" + + "}"; + } + + private String foldHead(String name) { + return this.fold(name, FOLD_HEAD); + } + + private String fold(String name, String val) { + return "\"" + name + "\": " + + "[" + "[\"" + val + "\"" + "]" + "]"; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java new file mode 100644 index 00000000..abf75fea --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java @@ -0,0 +1,8 @@ +package backtraceio.coroner.query; + +public class CoronerQueryFields { + public static String FOLD_CALLSTACK = "callstack"; + public static String FOLD_CLASSIFIERS = "classifiers"; + public static String FOLD_GUID = "guid"; + public static String RXID = "_rxid"; +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java b/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java new file mode 100644 index 00000000..9ded768c --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java @@ -0,0 +1,5 @@ +package backtraceio.coroner.query; + +public class FilterOperator { + public static final String EQUAL = "equal"; +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/ColumnDescElement.java b/coroner-client/src/main/java/backtraceio/coroner/response/ColumnDescElement.java new file mode 100644 index 00000000..acc64273 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/ColumnDescElement.java @@ -0,0 +1,61 @@ +package backtraceio.coroner.response; + +public class ColumnDescElement { + public String name; + public String format; + public String type; + public String op; + + @SuppressWarnings("unused") + public ColumnDescElement() { + + } + + @SuppressWarnings("unused") + public ColumnDescElement(String name, String format, String type, String op) { + this.name = name; + this.format = format; + this.type = type; + this.op = op; + } + + @SuppressWarnings("unused") + public String getName() { + return name; + } + + @SuppressWarnings("unused") + public void setName(String name) { + this.name = name; + } + + @SuppressWarnings("unused") + public String getFormat() { + return format; + } + + @SuppressWarnings("unused") + public void setFormat(String format) { + this.format = format; + } + + @SuppressWarnings("unused") + public String getType() { + return type; + } + + @SuppressWarnings("unused") + public void setType(String type) { + this.type = type; + } + + @SuppressWarnings("unused") + public String getOp() { + return op; + } + + @SuppressWarnings("unused") + public void setOp(String op) { + this.op = op; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java new file mode 100644 index 00000000..3b72bd37 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java @@ -0,0 +1,40 @@ +package backtraceio.coroner.response; + +import backtraceio.coroner.CoronerError; + + + +public class CoronerApiResponse { + public CoronerError error; + public CoronerResponse response; + + @SuppressWarnings("unused") + public CoronerApiResponse() { + + } + + @SuppressWarnings("unused") + public CoronerApiResponse(CoronerError error, CoronerResponse response) { + this.error = error; + this.response = response; + } + + public CoronerError getError() { + return error; + } + + @SuppressWarnings("unused") + public void setError(CoronerError error) { + this.error = error; + } + + public CoronerResponse getResponse() { + return this.response; + } + + @SuppressWarnings("unused") + public void setResponse(CoronerResponse response) { + this.response = response; + } +} + diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java new file mode 100644 index 00000000..e2ac32ed --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java @@ -0,0 +1,62 @@ +package backtraceio.coroner.response; + +/** + * Exception which represents a HTTP fault + */ +public class CoronerHttpException extends Exception { + + /** + * HTTP status code + */ + private Integer httpStatus; + + /** + * Create new instance with message + * + * @param message received exception message + */ + @SuppressWarnings("unused") + public CoronerHttpException(String message) { + this(null, message); + } + + /** + * Create new instance with HTTP status + * + * @param httpStatus received HTTP status code + */ + @SuppressWarnings("unused") + public CoronerHttpException(Integer httpStatus) { + this(httpStatus, null); + } + + /** + * Create new instance with HTTP status and without message + * + * @param httpStatus received HTTP status code + * @param message received exception message + */ + public CoronerHttpException(Integer httpStatus, String message) { + super(message); + setHttpStatus(httpStatus); + } + + /** + * Return HTTP status code + * + * @return HTTP status code + */ + @SuppressWarnings("unused") + public int getHttpStatus() { + return httpStatus; + } + + /** + * Set HTTP status code + * + * @param httpStatus received HTTP status code + */ + private void setHttpStatus(int httpStatus) { + this.httpStatus = httpStatus; + } +} \ No newline at end of file diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java new file mode 100644 index 00000000..192a9862 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java @@ -0,0 +1,60 @@ +package backtraceio.coroner.response; + +import com.google.gson.annotations.SerializedName; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class CoronerResponse { + + private static final Logger LOGGER = Logger.getLogger( CoronerResponse.class.getName() ); + + @SerializedName(value = "columns_desc") + public List columnsDesc; + public List values; + + @SuppressWarnings("unused") + public CoronerResponse() { + } + + @SuppressWarnings("unused") + public CoronerResponse(List columnsDesc, List values) { + this.columnsDesc = columnsDesc; + this.values = values; + } + + public T getAttribute(int elementIndex, String name, Class clazz) throws CoronerResponseProcessingException { + if (this.values == null) { + throw new CoronerResponseProcessingException("Values property from response is null"); + } + if (elementIndex < 0 || elementIndex > this.values.size()) { + throw new CoronerResponseProcessingException("Incorrect element index, value should be between 0 and " + this.values.size()); + } + CoronerResponseGroup responseGroup = values.get(elementIndex); + + int attributeIndex = getAttributeIndex(name); + try { + List results = (ArrayList) responseGroup.getAttribute(attributeIndex); + return clazz.cast(results.get(0)); + } catch (ClassCastException e) { + LOGGER.log(Level.SEVERE, e.getMessage()); + throw new CoronerResponseProcessingException("Error on getting attribute from response group for attribute index: " + attributeIndex); + } + } + + public int getResultsNumber() { + return values.size(); + } + + private int getAttributeIndex(String attributeName) throws CoronerResponseProcessingException { + for (int index = 0; index < this.columnsDesc.size(); index++) { + if (this.columnsDesc.get(index).name.equals(attributeName)) { + return index; + } + } + throw new CoronerResponseProcessingException("Attribute not found for name " + attributeName); + } + +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java new file mode 100644 index 00000000..fd35dde1 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java @@ -0,0 +1,17 @@ +package backtraceio.coroner.response; + +import backtraceio.coroner.CoronerError; + +public class CoronerResponseException extends Exception { + private final CoronerError coronerError; + + public CoronerResponseException(CoronerError coronerError) { + super(coronerError.getMessage()); + this.coronerError = coronerError; + } + + @SuppressWarnings("unused") + public CoronerError getCoronerError() { + return coronerError; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java new file mode 100644 index 00000000..367f1e2e --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java @@ -0,0 +1,25 @@ +package backtraceio.coroner.response; + +import java.util.List; + +public class CoronerResponseGroup { + private static final Integer EXPECTED_NUMBER_OF_EL = 3; + @SuppressWarnings("unused") + private final String groupIdentifier; + private final List values; + + public CoronerResponseGroup(List obj) throws IllegalArgumentException { + if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { + throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL + + ", current value: " + (obj != null ? obj.size() : "null")); + } + + this.groupIdentifier = obj.get(0).toString(); + this.values = (List) obj.get(1); + } + + public Object getAttribute(int index) { + return values.get(index); + } + +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java new file mode 100644 index 00000000..7121b83e --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java @@ -0,0 +1,14 @@ +package backtraceio.coroner.response; + +public class CoronerResponseProcessingException extends Exception { + + @SuppressWarnings("unused") + public CoronerResponseProcessingException() { + super(); + } + + @SuppressWarnings("unused") + public CoronerResponseProcessingException(String message) { + super(message); + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java new file mode 100644 index 00000000..800f1561 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java @@ -0,0 +1,33 @@ +package backtraceio.coroner.serialization; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.logging.Logger; +import java.util.logging.Level; + +import backtraceio.coroner.response.CoronerResponseGroup; + + +public class CoronerResponseGroupDeserializer implements JsonDeserializer { + private static final Logger LOGGER = Logger.getLogger( CoronerResponseGroupDeserializer.class.getName() ); + + @Override + public CoronerResponseGroup deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + JsonArray jsonArray = json.getAsJsonArray(); + List obj = new Gson().fromJson(jsonArray, (Type) Object.class); + try { + return new CoronerResponseGroup(obj); + } catch (Exception e) { + LOGGER.log(Level.SEVERE, "Coroner response deserialization exception ", e); + e.printStackTrace(); + return null; + } + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java new file mode 100644 index 00000000..ed2bb9ba --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java @@ -0,0 +1,20 @@ +package backtraceio.coroner.serialization; + +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import backtraceio.coroner.response.CoronerResponseGroup; + +public class GsonWrapper { + + public static T fromJson(String json, Class type) { + Gson gson = new GsonBuilder() + .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) + .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) + .create(); + + return gson.fromJson(json, type); + } + +} diff --git a/settings.gradle b/settings.gradle index 2fe424e7..b62cf823 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,2 @@ include ':example-app', ':backtrace-library' +include ':coroner-client' From 0e7ac0f31e7e19771974f577c7d1325f7ebebbb7 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 14 Nov 2022 22:32:05 +0100 Subject: [PATCH 05/61] Remove coroner client from library tests --- .../library/coroner/CoronerClient.java | 50 -------------- .../library/coroner/CoronerError.java | 35 ---------- .../library/coroner/CoronerHttpClient.java | 67 ------------------- .../library/coroner/query/CoronerQueries.java | 20 ------ .../coroner/query/CoronerQueryBuilder.java | 53 --------------- .../coroner/query/CoronerQueryFields.java | 8 --- .../library/coroner/query/FilterOperator.java | 5 -- .../coroner/response/ColumnDescElement.java | 61 ----------------- .../coroner/response/CoronerApiResponse.java | 39 ----------- .../coroner/response/CoronerResponse.java | 62 ----------------- .../response/CoronerResponseException.java | 17 ----- .../response/CoronerResponseGroup.java | 25 ------- .../CoronerResponseProcessingException.java | 14 ---- .../CoronerResponseGroupDeserializer.java | 31 --------- .../CoronerResponseGsonBuilder.java | 19 ------ 15 files changed, 506 deletions(-) delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java delete mode 100644 backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java deleted file mode 100644 index 2da594a7..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerClient.java +++ /dev/null @@ -1,50 +0,0 @@ -package backtraceio.library.coroner; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import backtraceio.library.coroner.query.CoronerQueries; -import backtraceio.library.coroner.query.CoronerQueryFields; -import backtraceio.library.coroner.response.CoronerApiResponse; -import backtraceio.library.coroner.response.CoronerResponse; -import backtraceio.library.coroner.response.CoronerResponseException; -import backtraceio.library.models.types.HttpException; - -public class CoronerClient { - public final CoronerHttpClient coronerHttpClient; - public final CoronerQueries coronerQueries; - private final List DEFAULT_ATTRIBUTES = Arrays.asList( - CoronerQueryFields.FOLD_CALLSTACK, - CoronerQueryFields.FOLD_GUID, - CoronerQueryFields.FOLD_CLASSIFIERS - ); - - public CoronerClient(String apiUrl, String coronerToken) { - this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); - this.coronerQueries = new CoronerQueries(); - } - - public CoronerResponse rxIdFilter(String rxId) throws Exception { - return this.rxIdFilter(rxId, new ArrayList<>()); - } - - public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, HttpException, IOException { - List attributes = Stream.concat( - DEFAULT_ATTRIBUTES.stream(), - customAttributes.stream()) - .collect(Collectors.toList()); - String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); - - CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); - - if (response.error != null) { - throw new CoronerResponseException(response.getError()); - } - - return response.getResponse(); - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java deleted file mode 100644 index 1d0a0bbf..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerError.java +++ /dev/null @@ -1,35 +0,0 @@ -package backtraceio.library.coroner; - -public class CoronerError { - public String message; - public int code; - - @SuppressWarnings("unused") - public CoronerError() { - - } - - @SuppressWarnings("unused") - public CoronerError(String message, int code) { - this.message = message; - this.code = code; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @SuppressWarnings("unused") - public int getCode() { - return code; - } - - @SuppressWarnings("unused") - public void setCode(int code) { - this.code = code; - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java deleted file mode 100644 index 66a1685f..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/CoronerHttpClient.java +++ /dev/null @@ -1,67 +0,0 @@ -package backtraceio.library.coroner; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; - -import backtraceio.library.common.BacktraceSerializeHelper; -import backtraceio.library.common.BacktraceStringHelper; -import backtraceio.library.common.RequestHelper; -import backtraceio.library.coroner.response.CoronerApiResponse; -import backtraceio.library.coroner.serialization.CoronerResponseGsonBuilder; -import backtraceio.library.http.HttpHelper; -import backtraceio.library.models.types.HttpException; - -class CoronerHttpClient { - private final String apiUrl; - private final String coronerToken; - - public CoronerHttpClient(String apiUrl, String coronerToken) { - this.apiUrl = apiUrl; - this.coronerToken = coronerToken; - } - - public CoronerApiResponse get(String json) throws IOException, HttpException { - HttpURLConnection urlConnection = prepareHttpRequest(json); - int statusCode = urlConnection.getResponseCode(); - - if (statusCode != HttpURLConnection.HTTP_OK) { - String message = HttpHelper.getResponseMessage(urlConnection); - message = (BacktraceStringHelper.isNullOrEmpty(message)) ? - urlConnection.getResponseMessage() : message; - throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); - } - - String resultJson = HttpHelper.getResponseMessage(urlConnection); - - return BacktraceSerializeHelper.fromJson( - new CoronerResponseGsonBuilder().buildGson(), - resultJson, - CoronerApiResponse.class); - } - - private HttpURLConnection prepareHttpRequest(String json) throws IOException { - URL url = new URL(this.apiUrl); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - - urlConnection.setRequestMethod("POST"); - urlConnection.setUseCaches(false); - - urlConnection.setDoOutput(true); - urlConnection.setDoInput(true); - - urlConnection.setRequestProperty("Connection", "Keep-Alive"); - urlConnection.setRequestProperty("Content-Type", RequestHelper.getContentType()); - urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); - - DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); - - RequestHelper.addJson(request, json); - - request.flush(); - request.close(); - - return urlConnection; - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java deleted file mode 100644 index ede3e857..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueries.java +++ /dev/null @@ -1,20 +0,0 @@ -package backtraceio.library.coroner.query; - -import java.util.ArrayList; -import java.util.List; - -public class CoronerQueries { - private final CoronerQueryBuilder builder; - - public CoronerQueries() { - builder = new CoronerQueryBuilder(); - } - - public String filterByRxId(String rxId) { - return this.filterByRxId(rxId, new ArrayList<>()); - } - - public String filterByRxId(String rxId, List attributes) { - return this.builder.buildRxIdGroup(rxId, attributes); - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java deleted file mode 100644 index 6be2c8b3..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java +++ /dev/null @@ -1,53 +0,0 @@ -package backtraceio.library.coroner.query; - -import java.util.List; -import java.util.stream.Collectors; - -class CoronerQueryBuilder { - private final String FOLD_HEAD = "head"; - private final int OFFSET = 0; - private final int LIMIT = 1; - - public String buildRxIdGroup(String rxId, List headFolds) { - return this.build(CoronerQueryFields.RXID, CoronerQueryFields.RXID, rxId, headFolds); - } - - public String build(String groupName, String filterName, String filterValue, List headFolds) { - String rxFilter = filterEq(filterName, filterValue); - String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); - - return "{" + - " \"group\":[" + - " \"" + groupName + "\"" + - " ]," + - "\"fold\": {" + - folds + - "}," + - " \"offset\":" + OFFSET + "," + - " \"limit\":" + LIMIT + "," + - " \"filter\":[" + - rxFilter + - " ]" + - "}"; - } - - private String filterEq(String name, String val) { - return "{" + - " \"" + name + "\": [" + - " [" + - " \"" + FilterOperator.EQUAL + "\"," + - " \"" + val + "\"" + - " ]" + - " ]" + - "}"; - } - - private String foldHead(String name) { - return this.fold(name, FOLD_HEAD); - } - - private String fold(String name, String val) { - return "\"" + name + "\": " + - "[" + "[\"" + val + "\"" + "]" + "]"; - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java deleted file mode 100644 index 6fe08c4d..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/CoronerQueryFields.java +++ /dev/null @@ -1,8 +0,0 @@ -package backtraceio.library.coroner.query; - -public class CoronerQueryFields { - public static String FOLD_CALLSTACK = "callstack"; - public static String FOLD_CLASSIFIERS = "classifiers"; - public static String FOLD_GUID = "guid"; - public static String RXID = "_rxid"; -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java deleted file mode 100644 index c760f39c..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/query/FilterOperator.java +++ /dev/null @@ -1,5 +0,0 @@ -package backtraceio.library.coroner.query; - -public class FilterOperator { - public static final String EQUAL = "equal"; -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java deleted file mode 100644 index 93fa9ded..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/ColumnDescElement.java +++ /dev/null @@ -1,61 +0,0 @@ -package backtraceio.library.coroner.response; - -public class ColumnDescElement { - public String name; - public String format; - public String type; - public String op; - - @SuppressWarnings("unused") - public ColumnDescElement() { - - } - - @SuppressWarnings("unused") - public ColumnDescElement(String name, String format, String type, String op) { - this.name = name; - this.format = format; - this.type = type; - this.op = op; - } - - @SuppressWarnings("unused") - public String getName() { - return name; - } - - @SuppressWarnings("unused") - public void setName(String name) { - this.name = name; - } - - @SuppressWarnings("unused") - public String getFormat() { - return format; - } - - @SuppressWarnings("unused") - public void setFormat(String format) { - this.format = format; - } - - @SuppressWarnings("unused") - public String getType() { - return type; - } - - @SuppressWarnings("unused") - public void setType(String type) { - this.type = type; - } - - @SuppressWarnings("unused") - public String getOp() { - return op; - } - - @SuppressWarnings("unused") - public void setOp(String op) { - this.op = op; - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java deleted file mode 100644 index 5ae46936..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerApiResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -package backtraceio.library.coroner.response; - -import backtraceio.library.coroner.CoronerError; - - -public class CoronerApiResponse { - public CoronerError error; - public CoronerResponse response; - - @SuppressWarnings("unused") - public CoronerApiResponse() { - - } - - @SuppressWarnings("unused") - public CoronerApiResponse(CoronerError error, CoronerResponse response) { - this.error = error; - this.response = response; - } - - public CoronerError getError() { - return error; - } - - @SuppressWarnings("unused") - public void setError(CoronerError error) { - this.error = error; - } - - public CoronerResponse getResponse() { - return this.response; - } - - @SuppressWarnings("unused") - public void setResponse(CoronerResponse response) { - this.response = response; - } -} - diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java deleted file mode 100644 index 35c22385..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponse.java +++ /dev/null @@ -1,62 +0,0 @@ -package backtraceio.library.coroner.response; - - -import com.google.gson.annotations.SerializedName; - -import java.util.ArrayList; -import java.util.List; - -import backtraceio.library.coroner.serialization.CoronerResponseGroupDeserializer; -import backtraceio.library.logger.BacktraceLogger; - -public class CoronerResponse { - - private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); - - @SerializedName(value = "columns_desc") - public List columnsDesc; - public List values; - - @SuppressWarnings("unused") - public CoronerResponse() { - } - - @SuppressWarnings("unused") - public CoronerResponse(List columnsDesc, List values) { - this.columnsDesc = columnsDesc; - this.values = values; - } - - public T getAttribute(int elementIndex, String name, Class clazz) throws CoronerResponseProcessingException { - if (this.values == null) { - throw new CoronerResponseProcessingException("Values property from response is null"); - } - if (elementIndex < 0 || elementIndex > this.values.size()) { - throw new CoronerResponseProcessingException("Incorrect element index, value should be between 0 and " + this.values.size()); - } - CoronerResponseGroup responseGroup = values.get(elementIndex); - - int attributeIndex = getAttributeIndex(name); - try { - List results = (ArrayList) responseGroup.getAttribute(attributeIndex); - return clazz.cast(results.get(0)); - } catch (ClassCastException e) { - BacktraceLogger.e(LOG_TAG, e.getMessage()); - throw new CoronerResponseProcessingException("Error on getting attribute from response group for attribute index: " + attributeIndex); - } - } - - public int getResultsNumber() { - return values.size(); - } - - private int getAttributeIndex(String attributeName) throws CoronerResponseProcessingException { - for (int index = 0; index < this.columnsDesc.size(); index++) { - if (this.columnsDesc.get(index).name.equals(attributeName)) { - return index; - } - } - throw new CoronerResponseProcessingException("Attribute not found for name " + attributeName); - } - -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java deleted file mode 100644 index bde604aa..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseException.java +++ /dev/null @@ -1,17 +0,0 @@ -package backtraceio.library.coroner.response; - -import backtraceio.library.coroner.CoronerError; - -public class CoronerResponseException extends Exception { - private final CoronerError coronerError; - - public CoronerResponseException(CoronerError coronerError) { - super(coronerError.getMessage()); - this.coronerError = coronerError; - } - - @SuppressWarnings("unused") - public CoronerError getCoronerError() { - return coronerError; - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java deleted file mode 100644 index 58bb40bf..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseGroup.java +++ /dev/null @@ -1,25 +0,0 @@ -package backtraceio.library.coroner.response; - -import java.util.List; - -public class CoronerResponseGroup { - private static final Integer EXPECTED_NUMBER_OF_EL = 3; - @SuppressWarnings("unused") - private final String groupIdentifier; - private final List values; - - public CoronerResponseGroup(List obj) throws IllegalArgumentException { - if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { - throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL - + ", current value: " + (obj != null ? obj.size() : "null")); - } - - this.groupIdentifier = obj.get(0).toString(); - this.values = (List) obj.get(1); - } - - public Object getAttribute(int index) { - return values.get(index); - } - -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java deleted file mode 100644 index 72e3f85f..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java +++ /dev/null @@ -1,14 +0,0 @@ -package backtraceio.library.coroner.response; - -public class CoronerResponseProcessingException extends Exception { - - @SuppressWarnings("unused") - public CoronerResponseProcessingException() { - super(); - } - - @SuppressWarnings("unused") - public CoronerResponseProcessingException(String message) { - super(message); - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java deleted file mode 100644 index b33882e3..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java +++ /dev/null @@ -1,31 +0,0 @@ -package backtraceio.library.coroner.serialization; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; - -import java.lang.reflect.Type; -import java.util.List; - -import backtraceio.library.coroner.response.CoronerResponseGroup; -import backtraceio.library.logger.BacktraceLogger; - -public class CoronerResponseGroupDeserializer implements JsonDeserializer { - private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); - - @Override - public CoronerResponseGroup deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - JsonArray jsonArray = json.getAsJsonArray(); - List obj = new Gson().fromJson(jsonArray, (Type) Object.class); - try { - return new CoronerResponseGroup(obj); - } catch (Exception e) { - BacktraceLogger.e(LOG_TAG, "" + e.getMessage()); - e.printStackTrace(); - return null; - } - } -} diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java b/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java deleted file mode 100644 index 67786461..00000000 --- a/backtrace-library/src/androidTest/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java +++ /dev/null @@ -1,19 +0,0 @@ -package backtraceio.library.coroner.serialization; - -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import backtraceio.library.common.serialization.CustomGsonBuilder; -import backtraceio.library.coroner.response.CoronerResponseGroup; - -public class CoronerResponseGsonBuilder implements CustomGsonBuilder { - - @Override - public Gson buildGson() { - return new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) - .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) - .create(); - } -} From a7cb17426587ecd016421d995e1dfec16731d4f0 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Tue, 15 Nov 2022 00:03:35 +0100 Subject: [PATCH 06/61] Remove coroner client --- .../library/coroner/CoronerClient.java | 50 -------------- .../library/coroner/CoronerError.java | 35 ---------- .../library/coroner/CoronerHttpClient.java | 67 ------------------- .../library/coroner/query/CoronerQueries.java | 20 ------ .../coroner/query/CoronerQueryBuilder.java | 53 --------------- .../coroner/query/CoronerQueryFields.java | 8 --- .../library/coroner/query/FilterOperator.java | 5 -- .../coroner/response/ColumnDescElement.java | 61 ----------------- .../coroner/response/CoronerApiResponse.java | 39 ----------- .../coroner/response/CoronerResponse.java | 62 ----------------- .../response/CoronerResponseException.java | 17 ----- .../response/CoronerResponseGroup.java | 25 ------- .../CoronerResponseProcessingException.java | 14 ---- .../CoronerResponseGroupDeserializer.java | 31 --------- .../CoronerResponseGsonBuilder.java | 19 ------ 15 files changed, 506 deletions(-) delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java delete mode 100644 backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java deleted file mode 100644 index 2da594a7..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerClient.java +++ /dev/null @@ -1,50 +0,0 @@ -package backtraceio.library.coroner; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import backtraceio.library.coroner.query.CoronerQueries; -import backtraceio.library.coroner.query.CoronerQueryFields; -import backtraceio.library.coroner.response.CoronerApiResponse; -import backtraceio.library.coroner.response.CoronerResponse; -import backtraceio.library.coroner.response.CoronerResponseException; -import backtraceio.library.models.types.HttpException; - -public class CoronerClient { - public final CoronerHttpClient coronerHttpClient; - public final CoronerQueries coronerQueries; - private final List DEFAULT_ATTRIBUTES = Arrays.asList( - CoronerQueryFields.FOLD_CALLSTACK, - CoronerQueryFields.FOLD_GUID, - CoronerQueryFields.FOLD_CLASSIFIERS - ); - - public CoronerClient(String apiUrl, String coronerToken) { - this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); - this.coronerQueries = new CoronerQueries(); - } - - public CoronerResponse rxIdFilter(String rxId) throws Exception { - return this.rxIdFilter(rxId, new ArrayList<>()); - } - - public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, HttpException, IOException { - List attributes = Stream.concat( - DEFAULT_ATTRIBUTES.stream(), - customAttributes.stream()) - .collect(Collectors.toList()); - String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); - - CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); - - if (response.error != null) { - throw new CoronerResponseException(response.getError()); - } - - return response.getResponse(); - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java deleted file mode 100644 index 1d0a0bbf..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerError.java +++ /dev/null @@ -1,35 +0,0 @@ -package backtraceio.library.coroner; - -public class CoronerError { - public String message; - public int code; - - @SuppressWarnings("unused") - public CoronerError() { - - } - - @SuppressWarnings("unused") - public CoronerError(String message, int code) { - this.message = message; - this.code = code; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @SuppressWarnings("unused") - public int getCode() { - return code; - } - - @SuppressWarnings("unused") - public void setCode(int code) { - this.code = code; - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java b/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java deleted file mode 100644 index 66a1685f..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/CoronerHttpClient.java +++ /dev/null @@ -1,67 +0,0 @@ -package backtraceio.library.coroner; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; - -import backtraceio.library.common.BacktraceSerializeHelper; -import backtraceio.library.common.BacktraceStringHelper; -import backtraceio.library.common.RequestHelper; -import backtraceio.library.coroner.response.CoronerApiResponse; -import backtraceio.library.coroner.serialization.CoronerResponseGsonBuilder; -import backtraceio.library.http.HttpHelper; -import backtraceio.library.models.types.HttpException; - -class CoronerHttpClient { - private final String apiUrl; - private final String coronerToken; - - public CoronerHttpClient(String apiUrl, String coronerToken) { - this.apiUrl = apiUrl; - this.coronerToken = coronerToken; - } - - public CoronerApiResponse get(String json) throws IOException, HttpException { - HttpURLConnection urlConnection = prepareHttpRequest(json); - int statusCode = urlConnection.getResponseCode(); - - if (statusCode != HttpURLConnection.HTTP_OK) { - String message = HttpHelper.getResponseMessage(urlConnection); - message = (BacktraceStringHelper.isNullOrEmpty(message)) ? - urlConnection.getResponseMessage() : message; - throw new HttpException(statusCode, String.format("%s: %s", statusCode, message)); - } - - String resultJson = HttpHelper.getResponseMessage(urlConnection); - - return BacktraceSerializeHelper.fromJson( - new CoronerResponseGsonBuilder().buildGson(), - resultJson, - CoronerApiResponse.class); - } - - private HttpURLConnection prepareHttpRequest(String json) throws IOException { - URL url = new URL(this.apiUrl); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - - urlConnection.setRequestMethod("POST"); - urlConnection.setUseCaches(false); - - urlConnection.setDoOutput(true); - urlConnection.setDoInput(true); - - urlConnection.setRequestProperty("Connection", "Keep-Alive"); - urlConnection.setRequestProperty("Content-Type", RequestHelper.getContentType()); - urlConnection.setRequestProperty("X-Coroner-Token", this.coronerToken); - - DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); - - RequestHelper.addJson(request, json); - - request.flush(); - request.close(); - - return urlConnection; - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java deleted file mode 100644 index ede3e857..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueries.java +++ /dev/null @@ -1,20 +0,0 @@ -package backtraceio.library.coroner.query; - -import java.util.ArrayList; -import java.util.List; - -public class CoronerQueries { - private final CoronerQueryBuilder builder; - - public CoronerQueries() { - builder = new CoronerQueryBuilder(); - } - - public String filterByRxId(String rxId) { - return this.filterByRxId(rxId, new ArrayList<>()); - } - - public String filterByRxId(String rxId, List attributes) { - return this.builder.buildRxIdGroup(rxId, attributes); - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java deleted file mode 100644 index 6be2c8b3..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryBuilder.java +++ /dev/null @@ -1,53 +0,0 @@ -package backtraceio.library.coroner.query; - -import java.util.List; -import java.util.stream.Collectors; - -class CoronerQueryBuilder { - private final String FOLD_HEAD = "head"; - private final int OFFSET = 0; - private final int LIMIT = 1; - - public String buildRxIdGroup(String rxId, List headFolds) { - return this.build(CoronerQueryFields.RXID, CoronerQueryFields.RXID, rxId, headFolds); - } - - public String build(String groupName, String filterName, String filterValue, List headFolds) { - String rxFilter = filterEq(filterName, filterValue); - String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); - - return "{" + - " \"group\":[" + - " \"" + groupName + "\"" + - " ]," + - "\"fold\": {" + - folds + - "}," + - " \"offset\":" + OFFSET + "," + - " \"limit\":" + LIMIT + "," + - " \"filter\":[" + - rxFilter + - " ]" + - "}"; - } - - private String filterEq(String name, String val) { - return "{" + - " \"" + name + "\": [" + - " [" + - " \"" + FilterOperator.EQUAL + "\"," + - " \"" + val + "\"" + - " ]" + - " ]" + - "}"; - } - - private String foldHead(String name) { - return this.fold(name, FOLD_HEAD); - } - - private String fold(String name, String val) { - return "\"" + name + "\": " + - "[" + "[\"" + val + "\"" + "]" + "]"; - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java deleted file mode 100644 index 6fe08c4d..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/query/CoronerQueryFields.java +++ /dev/null @@ -1,8 +0,0 @@ -package backtraceio.library.coroner.query; - -public class CoronerQueryFields { - public static String FOLD_CALLSTACK = "callstack"; - public static String FOLD_CLASSIFIERS = "classifiers"; - public static String FOLD_GUID = "guid"; - public static String RXID = "_rxid"; -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java b/backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java deleted file mode 100644 index c760f39c..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/query/FilterOperator.java +++ /dev/null @@ -1,5 +0,0 @@ -package backtraceio.library.coroner.query; - -public class FilterOperator { - public static final String EQUAL = "equal"; -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java deleted file mode 100644 index 93fa9ded..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/response/ColumnDescElement.java +++ /dev/null @@ -1,61 +0,0 @@ -package backtraceio.library.coroner.response; - -public class ColumnDescElement { - public String name; - public String format; - public String type; - public String op; - - @SuppressWarnings("unused") - public ColumnDescElement() { - - } - - @SuppressWarnings("unused") - public ColumnDescElement(String name, String format, String type, String op) { - this.name = name; - this.format = format; - this.type = type; - this.op = op; - } - - @SuppressWarnings("unused") - public String getName() { - return name; - } - - @SuppressWarnings("unused") - public void setName(String name) { - this.name = name; - } - - @SuppressWarnings("unused") - public String getFormat() { - return format; - } - - @SuppressWarnings("unused") - public void setFormat(String format) { - this.format = format; - } - - @SuppressWarnings("unused") - public String getType() { - return type; - } - - @SuppressWarnings("unused") - public void setType(String type) { - this.type = type; - } - - @SuppressWarnings("unused") - public String getOp() { - return op; - } - - @SuppressWarnings("unused") - public void setOp(String op) { - this.op = op; - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java deleted file mode 100644 index 5ae46936..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerApiResponse.java +++ /dev/null @@ -1,39 +0,0 @@ -package backtraceio.library.coroner.response; - -import backtraceio.library.coroner.CoronerError; - - -public class CoronerApiResponse { - public CoronerError error; - public CoronerResponse response; - - @SuppressWarnings("unused") - public CoronerApiResponse() { - - } - - @SuppressWarnings("unused") - public CoronerApiResponse(CoronerError error, CoronerResponse response) { - this.error = error; - this.response = response; - } - - public CoronerError getError() { - return error; - } - - @SuppressWarnings("unused") - public void setError(CoronerError error) { - this.error = error; - } - - public CoronerResponse getResponse() { - return this.response; - } - - @SuppressWarnings("unused") - public void setResponse(CoronerResponse response) { - this.response = response; - } -} - diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java deleted file mode 100644 index 35c22385..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponse.java +++ /dev/null @@ -1,62 +0,0 @@ -package backtraceio.library.coroner.response; - - -import com.google.gson.annotations.SerializedName; - -import java.util.ArrayList; -import java.util.List; - -import backtraceio.library.coroner.serialization.CoronerResponseGroupDeserializer; -import backtraceio.library.logger.BacktraceLogger; - -public class CoronerResponse { - - private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); - - @SerializedName(value = "columns_desc") - public List columnsDesc; - public List values; - - @SuppressWarnings("unused") - public CoronerResponse() { - } - - @SuppressWarnings("unused") - public CoronerResponse(List columnsDesc, List values) { - this.columnsDesc = columnsDesc; - this.values = values; - } - - public T getAttribute(int elementIndex, String name, Class clazz) throws CoronerResponseProcessingException { - if (this.values == null) { - throw new CoronerResponseProcessingException("Values property from response is null"); - } - if (elementIndex < 0 || elementIndex > this.values.size()) { - throw new CoronerResponseProcessingException("Incorrect element index, value should be between 0 and " + this.values.size()); - } - CoronerResponseGroup responseGroup = values.get(elementIndex); - - int attributeIndex = getAttributeIndex(name); - try { - List results = (ArrayList) responseGroup.getAttribute(attributeIndex); - return clazz.cast(results.get(0)); - } catch (ClassCastException e) { - BacktraceLogger.e(LOG_TAG, e.getMessage()); - throw new CoronerResponseProcessingException("Error on getting attribute from response group for attribute index: " + attributeIndex); - } - } - - public int getResultsNumber() { - return values.size(); - } - - private int getAttributeIndex(String attributeName) throws CoronerResponseProcessingException { - for (int index = 0; index < this.columnsDesc.size(); index++) { - if (this.columnsDesc.get(index).name.equals(attributeName)) { - return index; - } - } - throw new CoronerResponseProcessingException("Attribute not found for name " + attributeName); - } - -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java deleted file mode 100644 index bde604aa..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseException.java +++ /dev/null @@ -1,17 +0,0 @@ -package backtraceio.library.coroner.response; - -import backtraceio.library.coroner.CoronerError; - -public class CoronerResponseException extends Exception { - private final CoronerError coronerError; - - public CoronerResponseException(CoronerError coronerError) { - super(coronerError.getMessage()); - this.coronerError = coronerError; - } - - @SuppressWarnings("unused") - public CoronerError getCoronerError() { - return coronerError; - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java deleted file mode 100644 index 58bb40bf..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseGroup.java +++ /dev/null @@ -1,25 +0,0 @@ -package backtraceio.library.coroner.response; - -import java.util.List; - -public class CoronerResponseGroup { - private static final Integer EXPECTED_NUMBER_OF_EL = 3; - @SuppressWarnings("unused") - private final String groupIdentifier; - private final List values; - - public CoronerResponseGroup(List obj) throws IllegalArgumentException { - if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { - throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL - + ", current value: " + (obj != null ? obj.size() : "null")); - } - - this.groupIdentifier = obj.get(0).toString(); - this.values = (List) obj.get(1); - } - - public Object getAttribute(int index) { - return values.get(index); - } - -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java b/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java deleted file mode 100644 index 72e3f85f..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/response/CoronerResponseProcessingException.java +++ /dev/null @@ -1,14 +0,0 @@ -package backtraceio.library.coroner.response; - -public class CoronerResponseProcessingException extends Exception { - - @SuppressWarnings("unused") - public CoronerResponseProcessingException() { - super(); - } - - @SuppressWarnings("unused") - public CoronerResponseProcessingException(String message) { - super(message); - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java b/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java deleted file mode 100644 index b33882e3..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGroupDeserializer.java +++ /dev/null @@ -1,31 +0,0 @@ -package backtraceio.library.coroner.serialization; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; - -import java.lang.reflect.Type; -import java.util.List; - -import backtraceio.library.coroner.response.CoronerResponseGroup; -import backtraceio.library.logger.BacktraceLogger; - -public class CoronerResponseGroupDeserializer implements JsonDeserializer { - private static final transient String LOG_TAG = CoronerResponseGroupDeserializer.class.getSimpleName(); - - @Override - public CoronerResponseGroup deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - JsonArray jsonArray = json.getAsJsonArray(); - List obj = new Gson().fromJson(jsonArray, (Type) Object.class); - try { - return new CoronerResponseGroup(obj); - } catch (Exception e) { - BacktraceLogger.e(LOG_TAG, "" + e.getMessage()); - e.printStackTrace(); - return null; - } - } -} diff --git a/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java b/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java deleted file mode 100644 index 67786461..00000000 --- a/backtrace-library/src/main/java/backtraceio/library/coroner/serialization/CoronerResponseGsonBuilder.java +++ /dev/null @@ -1,19 +0,0 @@ -package backtraceio.library.coroner.serialization; - -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import backtraceio.library.common.serialization.CustomGsonBuilder; -import backtraceio.library.coroner.response.CoronerResponseGroup; - -public class CoronerResponseGsonBuilder implements CustomGsonBuilder { - - @Override - public Gson buildGson() { - return new GsonBuilder() - .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) - .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) - .create(); - } -} From 65feba40700d71d1c74a80673bd6f39100bb42b7 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Tue, 15 Nov 2022 10:08:38 +0100 Subject: [PATCH 07/61] Add multiple filters --- .../backtraceio/coroner/CoronerClient.java | 24 +++++++++++--- .../coroner/query/CoronerFieldFilter.java | 29 ++++++++++++++++ .../coroner/query/CoronerFiltersBuilder.java | 33 +++++++++++++++++++ .../coroner/query/CoronerQueries.java | 16 ++++++++- .../coroner/query/CoronerQueryBuilder.java | 32 +++++------------- .../coroner/query/CoronerQueryFields.java | 3 ++ .../coroner/query/CoronerValueFilter.java | 21 ++++++++++++ .../coroner/query/FilterOperator.java | 25 ++++++++++++-- 8 files changed, 151 insertions(+), 32 deletions(-) create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 37486bcf..2b866e7f 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -33,12 +33,28 @@ public CoronerResponse rxIdFilter(String rxId) throws Exception { } public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, CoronerHttpException, IOException { - List attributes = Stream.concat( - DEFAULT_ATTRIBUTES.stream(), - customAttributes.stream()) - .collect(Collectors.toList()); + List attributes = concatAttributes(customAttributes); + String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); + return makeRequest(coronerQuery); + } + + public CoronerResponse errorTypeTimestampFilter(String errorType, List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { + List attributes = concatAttributes(customAttributes); + + String coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, "1668443545", "1668357145", attributes); + + return makeRequest(coronerQuery); + } + + private List concatAttributes(List customAttributes) { + List result = new ArrayList<>(customAttributes); + result.addAll(DEFAULT_ATTRIBUTES); + return result; + } + + private CoronerResponse makeRequest(String coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); if (response.error != null) { diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java new file mode 100644 index 00000000..6371d118 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java @@ -0,0 +1,29 @@ +package backtraceio.coroner.query; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class CoronerFieldFilter { + public String name; + public List values = new ArrayList<>(); + + public CoronerFieldFilter(String name, FilterOperator operator, Object value) { + this.name = name; + this.addValueFilter(operator, value); + } + + public String getName() { + return this.name; + } + + public void addValueFilter(final FilterOperator operator, final Object value) { + values.add(new CoronerValueFilter(operator, value)); + } + + @Override + public String toString() { + String result = values.stream().map(x->x.toString()).collect(Collectors.joining(","));; + return "\"" + name + "\": [" + result + " ]"; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java new file mode 100644 index 00000000..1663aa92 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java @@ -0,0 +1,33 @@ +package backtraceio.coroner.query; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class CoronerFiltersBuilder { + private final List filters; + + public CoronerFiltersBuilder() { + this.filters = new ArrayList<>(); + } + + public CoronerFiltersBuilder addFilter(final String name, final FilterOperator operator, final Object value) { + final Optional filter = this.filters.stream().filter(x->x.getName().equals(name)).findAny(); + + if(filter.isPresent()) { + filter.get().addValueFilter(operator, value); + } else { + this.filters.add(new CoronerFieldFilter(name, operator, value)); + } + + return this; + } + + public String get() { + String result = filters.stream().map(x->x.toString()).collect(Collectors.joining(",")); + + return "{ " + result + " }"; + } + +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java index 4ab6ab12..83bf4b53 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -14,7 +14,21 @@ public String filterByRxId(String rxId) { return this.filterByRxId(rxId, new ArrayList<>()); } + public String filterByErrorTypeAndTimestamp(String errorType, String timestampMost, String timestampLeast, List attributes) { + CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + filtersBuilder.addFilter(CoronerQueryFields.ERROR_TYPE, FilterOperator.EQUAL, errorType); + filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); + filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_LEAST, timestampLeast + "."); + + return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); + } + public String filterByRxId(String rxId, List attributes) { - return this.builder.buildRxIdGroup(rxId, attributes); + CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + filtersBuilder.addFilter(CoronerQueryFields.RXID, FilterOperator.EQUAL, rxId); + + return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); } + +// public String filterBy } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java index 5740a1e7..8dc9989f 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -8,37 +8,21 @@ class CoronerQueryBuilder { private final int OFFSET = 0; private final int LIMIT = 1; - public String buildRxIdGroup(String rxId, List headFolds) { - return this.build(CoronerQueryFields.RXID, CoronerQueryFields.RXID, rxId, headFolds); + public String buildRxIdGroup(String filters, List headFolds) { + return this.build(CoronerQueryFields.RXID, filters, headFolds); } - public String build(String groupName, String filterName, String filterValue, List headFolds) { - String rxFilter = filterEq(filterName, filterValue); + private String build(String groupName, String filters, List headFolds) { String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); return "{" + - " \"group\":[" + - " \"" + groupName + "\"" + - " ]," + - "\"fold\": {" + - folds + - "}," + + "\"group\":[" + + " [\"" + groupName + "\"]" + + "]," + + "\"fold\": {" + folds + "}," + " \"offset\":" + OFFSET + "," + " \"limit\":" + LIMIT + "," + - " \"filter\":[" + - rxFilter + - " ]" + - "}"; - } - - private String filterEq(String name, String val) { - return "{" + - " \"" + name + "\": [" + - " [" + - " \"" + FilterOperator.EQUAL + "\"," + - " \"" + val + "\"" + - " ]" + - " ]" + + " \"filter\":[" + filters + "]" + "}"; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java index abf75fea..910228e8 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java @@ -4,5 +4,8 @@ public class CoronerQueryFields { public static String FOLD_CALLSTACK = "callstack"; public static String FOLD_CLASSIFIERS = "classifiers"; public static String FOLD_GUID = "guid"; + public static String RXID = "_rxid"; + public static String ERROR_TYPE = "error.type"; + public static String TIMESTAMP = "timestamp"; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java new file mode 100644 index 00000000..1598dfd3 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java @@ -0,0 +1,21 @@ +package backtraceio.coroner.query; + +import java.util.logging.Filter; + +public class CoronerValueFilter { + public FilterOperator operator; + public Object value; + + public CoronerValueFilter(final FilterOperator operator, final Object value) { + this.operator = operator; + this.value = value; + } + + @Override + public String toString() { + return " [" + + " \"" + operator.toString() + "\"," + + " \"" + value.toString() + "\"" + + " ]"; + } +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java b/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java index 9ded768c..694ddd12 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/FilterOperator.java @@ -1,5 +1,24 @@ package backtraceio.coroner.query; -public class FilterOperator { - public static final String EQUAL = "equal"; -} +public enum FilterOperator { + EQUAL("equal"), + AT_MOST("at-most"), + AT_LEAST("at-least"); + + private final String text; + + /** + * @param text + */ + FilterOperator(final String text) { + this.text = text; + } + + /* (non-Javadoc) + * @see java.lang.Enum#toString() + */ + @Override + public String toString() { + return text; + } +} \ No newline at end of file From aaba04656fadd6ea65c883bd8296483fd92951a8 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 20:58:30 +0100 Subject: [PATCH 08/61] Multiple filters --- .../backtraceio/coroner/CoronerClient.java | 2 +- .../coroner/query/CoronerQueries.java | 4 +- example-app/build.gradle | 11 +- .../backtraceio/ExampleInstrumentedTest.java | 131 +++++++++++++++--- .../backtraceio/InstrumentedTest.java | 28 ++++ .../backtraceio/backtraceio/MainActivity.java | 16 ++- 6 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 2b866e7f..9f5eaa41 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -40,7 +40,7 @@ public CoronerResponse rxIdFilter(String rxId, List customAttributes) th return makeRequest(coronerQuery); } - public CoronerResponse errorTypeTimestampFilter(String errorType, List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { + public CoronerResponse errorTypeTimestampFilter(String errorType, String timestampLeast, String timestampMost, List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { List attributes = concatAttributes(customAttributes); String coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, "1668443545", "1668357145", attributes); diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java index 83bf4b53..b939f3cb 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -14,11 +14,11 @@ public String filterByRxId(String rxId) { return this.filterByRxId(rxId, new ArrayList<>()); } - public String filterByErrorTypeAndTimestamp(String errorType, String timestampMost, String timestampLeast, List attributes) { + public String filterByErrorTypeAndTimestamp(String errorType, String timestampLeast, String timestampMost, List attributes) { CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); filtersBuilder.addFilter(CoronerQueryFields.ERROR_TYPE, FilterOperator.EQUAL, errorType); - filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_LEAST, timestampLeast + "."); + filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); } diff --git a/example-app/build.gradle b/example-app/build.gradle index fd3c3dda..1071aa1d 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -33,27 +33,32 @@ android { abortOnError false } defaultConfig { - buildConfigField ("String", "BACKTRACE_SUBMISSION_URL", getSubmissionUrl()) + buildConfigField ("String", "BACKTRACE_SUBMISSION_URL", getBuildProperty("BACKTRACE_SUBMISSION_URL")) + buildConfigField ("String", "BACKTRACE_CORONER_URL", getBuildProperty("BACKTRACE_CORONER_URL")) + buildConfigField ("String", "BACKTRACE_CORONER_TOKEN", getBuildProperty("BACKTRACE_CORONER_TOKEN")) } } } -String getSubmissionUrl() { +String getBuildProperty(String name) { if (!rootProject.file("./local.properties").exists()) { return "null" } def properties = new Properties() properties.load(rootProject.file("./local.properties").newDataInputStream()) - return properties.containsKey('BACKTRACE_SUBMISSION_URL') ? properties['BACKTRACE_SUBMISSION_URL'] : "null" + return properties.containsKey(name) ? properties[name] : "null" } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation project(path: ':coroner-client') testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'net.jodah:concurrentunit:0.4.4' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' implementation project(':backtrace-library') + androidTestImplementation project(path: ':backtrace-library') } diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index cc36e091..02c10dff 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -3,19 +3,30 @@ import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static org.junit.Assert.assertEquals; import android.content.Context; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.espresso.PerformException; -import androidx.test.rule.ActivityTestRule; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; + +import net.jodah.concurrentunit.Waiter; +import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.assertEquals; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import backtraceio.coroner.response.CoronerResponse; +import backtraceio.coroner.response.CoronerResponseProcessingException; +import backtraceio.library.events.OnServerResponseEventListener; +import backtraceio.library.models.BacktraceResult; /** * Instrumented test, which will execute on an Android device. @@ -23,11 +34,12 @@ * @see Testing documentation */ @RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { +public class ExampleInstrumentedTest extends InstrumentedTest { + private final int THREAD_SLEEP_TIME_MS = 2000; @Rule - public ActivityTestRule mActivityRule = - new ActivityTestRule<>(MainActivity.class); + public ActivityScenarioRule mActivityRule = + new ActivityScenarioRule<>(MainActivity.class); @Before public void enableMetricsAndBreadcrumbs() { @@ -42,28 +54,103 @@ public void useAppContext() { } @Test - public void dumpWithoutCrash() { - onView(withId(R.id.dumpWithoutCrash)).perform(click()); - } + public void handledException() throws TimeoutException, CoronerResponseProcessingException, InterruptedException { + // GIVEN + final String[] rxId = new String[1]; + final Waiter waiter = new Waiter(); + mActivityRule.getScenario().onActivity(activity -> + activity.setOnServerResponseEventListener(backtraceResult -> { + rxId[0] = backtraceResult.rxId; + waiter.resume(); + })); + + // WHEN + onView(withId(R.id.handledException)).perform(click()); // UI action + waiter.await(5, TimeUnit.SECONDS, 1); + Thread.sleep(THREAD_SLEEP_TIME_MS); + + // THEN + CoronerResponse response = null; - @Test(expected = PerformException.class) - public void unhandledException() { - onView(withId(R.id.unhandledException)).perform(click()); + try { + response = this.getCoronerClient().rxIdFilter(rxId[0], Arrays.asList("error.message")); + } catch (Exception ex) { + Assert.fail(ex.getMessage()); + } + + Assert.assertNotNull(response); + Assert.assertEquals(1, response.getResultsNumber()); + + String resultErrorMsg = response.getAttribute(0, "error.message", String.class); + Assert.assertEquals("Index: 3, Size: 3", resultErrorMsg); + + String resultClassifier = response.getAttribute(0, "classifiers", String.class); + Assert.assertEquals("java.lang.IndexOutOfBoundsException", resultClassifier); } @Test - public void handledException() { - onView(withId(R.id.handledException)).perform(click()); + public void dumpWithoutCrash() throws CoronerResponseProcessingException { + // GIVEN + CoronerResponse response = null; + long timestampStart = this.getSecondsTimestampNowGMT(); + + // WHEN + onView(withId(R.id.dumpWithoutCrash)).perform(click()); // UI action + + // THEN + try { + response = this.getCoronerClient().errorTypeTimestampFilter("Crash", + Long.toString(timestampStart), + Long.toString(this.getSecondsTimestampNowGMT()), + Arrays.asList("error.message")); + } catch (Exception ex) { + Assert.fail(ex.getMessage()); + } + + Assert.assertNotNull(response); + Assert.assertEquals(1, response.getResultsNumber()); + String val = response.getAttribute(0, "error.message", String.class); + Assert.assertEquals("Dump without crash", val); } @Test - public void anr() { - onView(withId(R.id.anr)).perform(click()); - } + public void unhandledException() throws CoronerResponseProcessingException, InterruptedException { + // GIVEN + CoronerResponse response = null; + long timestampStart = this.getSecondsTimestampNowGMT(); - // Will break build, obviously. - //@Test - public void nativeCrash() { - onView(withId(R.id.nativeCrash)).perform(click()); + // WHEN + onView(withId(R.id.unhandledException)).perform(click()); // UI action + Thread.sleep(THREAD_SLEEP_TIME_MS); + // THEN + try { + response = this.getCoronerClient().errorTypeTimestampFilter("Crash", + Long.toString(timestampStart), + Long.toString(this.getSecondsTimestampNowGMT()), + Arrays.asList("error.message")); + } catch (Exception ex) { + Assert.fail(ex.getMessage()); + } + + Assert.assertNotNull(response); + Assert.assertEquals(1, response.getResultsNumber()); + String val = response.getAttribute(0, "error.message", String.class); + Assert.assertEquals("Dump without crash", val); } + +// @Test +// public void anr() { +// onView(withId(R.id.anr)).perform(click()); +// } +// +// @Test(expected = PerformException.class) +// public void unhandledException() { +// onView(withId(R.id.unhandledException)).perform(click()); +// } +// +// // Will break build, obviously. +// //@Test +// public void nativeCrash() { +// onView(withId(R.id.nativeCrash)).perform(click()); +// } } diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java new file mode 100644 index 00000000..f3c1b1da --- /dev/null +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java @@ -0,0 +1,28 @@ +package backtraceio.backtraceio; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.BeforeClass; +import org.junit.runner.RunWith; + +import backtraceio.coroner.CoronerClient; + +@RunWith(AndroidJUnit4.class) +public class InstrumentedTest { + private static CoronerClient coronerClient; + + @BeforeClass + public static void init() { + String url = BuildConfig.BACKTRACE_CORONER_URL; + String coronerToken = BuildConfig.BACKTRACE_CORONER_TOKEN; + coronerClient = new CoronerClient(url, coronerToken); + } + + public CoronerClient getCoronerClient() { + return coronerClient; + } + + public long getSecondsTimestampNowGMT() { + return System.currentTimeMillis() / 1000; + } +} diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index a9eaae11..7de5d5a7 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -27,15 +27,15 @@ import backtraceio.library.enums.BacktraceBreadcrumbType; import backtraceio.library.enums.database.RetryBehavior; import backtraceio.library.enums.database.RetryOrder; +import backtraceio.library.events.OnServerResponseEventListener; import backtraceio.library.models.BacktraceExceptionHandler; import backtraceio.library.models.BacktraceMetricsSettings; import backtraceio.library.models.database.BacktraceDatabaseSettings; import backtraceio.library.models.json.BacktraceReport; public class MainActivity extends AppCompatActivity { - private BacktraceClient backtraceClient; - + private OnServerResponseEventListener listener; private final int anrTimeout = 3000; // Used to load the 'native-lib' library on application startup. @@ -43,6 +43,10 @@ public class MainActivity extends AppCompatActivity { System.loadLibrary("native-lib"); } + public void setOnServerResponseEventListener(OnServerResponseEventListener e) { + this.listener = e; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -148,7 +152,7 @@ public void handledException(View view) { // Where was that magic wand again? equipItem(myWarriorArmor, magicWandIndex); } catch (Exception e) { - backtraceClient.send(new BacktraceReport(e)); + backtraceClient.send(new BacktraceReport(e), this.listener); } } @@ -192,7 +196,7 @@ public void sendReport(View view) { addNativeBreadcrumb(); addNativeBreadcrumbUserError(); BacktraceReport report = new BacktraceReport("Test"); - backtraceClient.send(report); + backtraceClient.send(report, this.listener); } private void writeMyCustomFile(String filePath) { @@ -206,6 +210,10 @@ private void writeMyCustomFile(String filePath) { } } + public BacktraceClient getBacktraceClient() { + return backtraceClient; + } + public void exit(View view) { System.exit(0); } From 80085252f96ab2d5828a97b5690daa50d8d6fce2 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 20:58:45 +0100 Subject: [PATCH 09/61] Default CI --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fcafe1c5..5b6afba1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,8 @@ jobs: - name: Write to local.properties run: | echo BACKTRACE_SUBMISSION_URL=\"https://submit.backtrace.io/yolo/2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4/json\" > ./local.properties + echo BACKTRACE_CORONER_URL=\"${{ secrets.BACKTRACE_CORONER_URL }}\" > ./local.properties + echo BACKTRACE_CORONER_TOKEN=\"${{ secrets.BACKTRACE_CORONER_TOKEN }}\" > ./local.properties - name: Build and check run: ./gradlew assembleDebug assembleDebugAndroidTest check From 7634f87c6daa9f72cc72582141d76c918434ad7d Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 21:00:30 +0100 Subject: [PATCH 10/61] CI params --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5b6afba1..93838ebe 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,8 +30,8 @@ jobs: - name: Write to local.properties run: | echo BACKTRACE_SUBMISSION_URL=\"https://submit.backtrace.io/yolo/2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4/json\" > ./local.properties - echo BACKTRACE_CORONER_URL=\"${{ secrets.BACKTRACE_CORONER_URL }}\" > ./local.properties - echo BACKTRACE_CORONER_TOKEN=\"${{ secrets.BACKTRACE_CORONER_TOKEN }}\" > ./local.properties + echo BACKTRACE_CORONER_URL=\"https://yolo.sp.backtrace.io/api/query?project=android-library\" > ./local.properties + echo BACKTRACE_CORONER_TOKEN=\"4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a\" > ./local.properties - name: Build and check run: ./gradlew assembleDebug assembleDebugAndroidTest check From fdcc58aa399832fdf73945629ab51495f027dd52 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 22:25:34 +0100 Subject: [PATCH 11/61] Check app context null --- .../backtraceio/backtraceio/MainActivity.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 7de5d5a7..8c3513aa 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -2,12 +2,13 @@ import android.content.Context; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; import android.system.ErrnoException; import android.system.Os; import android.util.Log; import android.view.View; +import androidx.appcompat.app.AppCompatActivity; + import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; @@ -93,7 +94,7 @@ private BacktraceClient initializeBacktrace(final String submissionUrl) { put("custom.attribute", "My Custom Attribute"); }}; - List attachments = new ArrayList(){{ + List attachments = new ArrayList() {{ add(context.getFilesDir() + "/" + "myCustomFile.txt"); }}; @@ -117,29 +118,28 @@ private BacktraceClient initializeBacktrace(final String submissionUrl) { public native void cppCrash(); public native boolean registerNativeBreadcrumbs(BacktraceBase backtraceBase); + public native boolean addNativeBreadcrumb(); + public native boolean addNativeBreadcrumbUserError(); + public native void cleanupNativeBreadcrumbHandler(); private List equippedItems; - public List getWarriorArmor() - { + public List getWarriorArmor() { return new ArrayList(Arrays.asList("Tough Boots", "Strong Sword", "Sturdy Shield", "Magic Wand")); } - int findEquipmentIndex(List armor, String equipment) - { + int findEquipmentIndex(List armor, String equipment) { return armor.indexOf(equipment); } - void removeEquipment(List armor, int index) - { + void removeEquipment(List armor, int index) { armor.remove(index); } - void equipItem(List armor, int index) - { + void equipItem(List armor, int index) { equippedItems.add(armor.get(index)); } @@ -158,7 +158,7 @@ public void handledException(View view) { public void getSaveData() throws IOException { // I know for sure this file is there (spoiler alert, it's not) - File mySaveData = new File("mySave.sav"); + File mySaveData = new File("mySave.sav"); FileReader mySaveDataReader = new FileReader(mySaveData); char[] saveDataBuffer = new char[255]; mySaveDataReader.read(saveDataBuffer); @@ -181,9 +181,17 @@ public void enableBreadcrumbs(View view) { registerNativeBreadcrumbs(backtraceClient); // Order should not matter } - public void enableBreadcrumbsUserOnly(View view) { + public void enableBreadcrumbsUserOnly(View view) throws Exception { EnumSet breadcrumbTypesToEnable = EnumSet.of(BacktraceBreadcrumbType.USER); - backtraceClient.enableBreadcrumbs(view.getContext().getApplicationContext(), breadcrumbTypesToEnable); + Context appContext = view.getContext().getApplicationContext(); + if (backtraceClient == null) { + throw new Exception("BacktraceClient is null"); + } + + if (appContext == null) { + throw new Exception("App context is null"); + } + backtraceClient.enableBreadcrumbs(appContext, breadcrumbTypesToEnable); registerNativeBreadcrumbs(backtraceClient); // Order should not matter } @@ -206,7 +214,7 @@ private void writeMyCustomFile(String filePath) { outputStreamWriter.write(fileData); outputStreamWriter.close(); } catch (IOException e) { - Log.e("BacktraceAndroid", "File write failed due to: " + e.toString()); + Log.e("BacktraceAndroid", "File write failed due to: " + e.toString()); } } From ee9c2d1464ed5e488678332f421369d4f061f3a8 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 22:52:40 +0100 Subject: [PATCH 12/61] Test exception --- .../src/main/java/backtraceio/backtraceio/MainActivity.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 8c3513aa..2f2a8344 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -187,10 +187,13 @@ public void enableBreadcrumbsUserOnly(View view) throws Exception { if (backtraceClient == null) { throw new Exception("BacktraceClient is null"); } - + if (appContext == null) { throw new Exception("App context is null"); } + if (1 == 1) { + throw new Exception("Test exception"); + } backtraceClient.enableBreadcrumbs(appContext, breadcrumbTypesToEnable); registerNativeBreadcrumbs(backtraceClient); // Order should not matter } From 0de1d7477e2c83d1860005ecf94f039e1391accf Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 23:02:17 +0100 Subject: [PATCH 13/61] Add rethrow --- .../backtraceio/backtraceio/ExampleInstrumentedTest.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 02c10dff..bb7e6e47 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -43,7 +43,14 @@ public class ExampleInstrumentedTest extends InstrumentedTest { @Before public void enableMetricsAndBreadcrumbs() { - onView(withId(R.id.enableBreadcrumbs)).perform(click()); + try { + onView(withId(R.id.enableBreadcrumbs)).perform(click()); + } + catch (Exception ex) { + System.out.println("Before enableMetricsAndBreadcrumbs"); + System.out.println(ex.getMessage()); + throw ex; + } } @Test From d6d900d39cfab666f1478c8eea3217b85ee8152c Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 23:15:11 +0100 Subject: [PATCH 14/61] Move exception --- .../backtraceio/backtraceio/MainActivity.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 2f2a8344..9282582c 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -176,13 +176,7 @@ public void anr(View view) throws InterruptedException { Thread.sleep(anrTimeout + 2000); } - public void enableBreadcrumbs(View view) { - backtraceClient.enableBreadcrumbs(view.getContext().getApplicationContext()); - registerNativeBreadcrumbs(backtraceClient); // Order should not matter - } - - public void enableBreadcrumbsUserOnly(View view) throws Exception { - EnumSet breadcrumbTypesToEnable = EnumSet.of(BacktraceBreadcrumbType.USER); + public void enableBreadcrumbs(View view) throws Exception { Context appContext = view.getContext().getApplicationContext(); if (backtraceClient == null) { throw new Exception("BacktraceClient is null"); @@ -194,6 +188,13 @@ public void enableBreadcrumbsUserOnly(View view) throws Exception { if (1 == 1) { throw new Exception("Test exception"); } + backtraceClient.enableBreadcrumbs(view.getContext().getApplicationContext()); + registerNativeBreadcrumbs(backtraceClient); // Order should not matter + } + + public void enableBreadcrumbsUserOnly(View view) throws Exception { + EnumSet breadcrumbTypesToEnable = EnumSet.of(BacktraceBreadcrumbType.USER); + Context appContext = view.getContext().getApplicationContext(); backtraceClient.enableBreadcrumbs(appContext, breadcrumbTypesToEnable); registerNativeBreadcrumbs(backtraceClient); // Order should not matter } From 66b5a58d155db57e115af133b0c2c0c75568ab3b Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 17 Nov 2022 23:36:55 +0100 Subject: [PATCH 15/61] Always init Backtrace client --- .../main/java/backtraceio/backtraceio/MainActivity.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 9282582c..ab770c24 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -52,11 +52,8 @@ public void setOnServerResponseEventListener(OnServerResponseEventListener e) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - - // Set this value in your local.properties - if (BuildConfig.BACKTRACE_SUBMISSION_URL != null) { - backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL); - } + + backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL); // Set this value in your local.properties symlinkAndWriteFile(); } From 48b8b8a2befbacf6b805a59d433a03b3ad4e4a14 Mon Sep 17 00:00:00 2001 From: Vincent Lussenburg Date: Fri, 18 Nov 2022 10:37:32 -0800 Subject: [PATCH 16/61] Fix echo --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 93838ebe..5d38586f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,8 +30,8 @@ jobs: - name: Write to local.properties run: | echo BACKTRACE_SUBMISSION_URL=\"https://submit.backtrace.io/yolo/2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4/json\" > ./local.properties - echo BACKTRACE_CORONER_URL=\"https://yolo.sp.backtrace.io/api/query?project=android-library\" > ./local.properties - echo BACKTRACE_CORONER_TOKEN=\"4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a\" > ./local.properties + echo BACKTRACE_CORONER_URL=\"https://yolo.sp.backtrace.io/api/query?project=android-library\" >> ./local.properties + echo BACKTRACE_CORONER_TOKEN=\"4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a\" >> ./local.properties - name: Build and check run: ./gradlew assembleDebug assembleDebugAndroidTest check From 47652f3a2231e1df1a62203b47ada0d2e618092c Mon Sep 17 00:00:00 2001 From: Vincent Lussenburg Date: Tue, 22 Nov 2022 13:58:34 -0800 Subject: [PATCH 17/61] Remove RunWith since this test has no tests --- .../java/backtraceio/backtraceio/InstrumentedTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java index f3c1b1da..93e6a5c4 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java @@ -3,11 +3,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.BeforeClass; -import org.junit.runner.RunWith; import backtraceio.coroner.CoronerClient; -@RunWith(AndroidJUnit4.class) public class InstrumentedTest { private static CoronerClient coronerClient; From 7caf64a5e60c435d266d5554aa11a78b26c655e8 Mon Sep 17 00:00:00 2001 From: Vincent Lussenburg Date: Tue, 22 Nov 2022 14:38:01 -0800 Subject: [PATCH 18/61] Remove testexecption --- .../src/main/java/backtraceio/backtraceio/MainActivity.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index ab770c24..c2fc3c2d 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -182,9 +182,7 @@ public void enableBreadcrumbs(View view) throws Exception { if (appContext == null) { throw new Exception("App context is null"); } - if (1 == 1) { - throw new Exception("Test exception"); - } + backtraceClient.enableBreadcrumbs(view.getContext().getApplicationContext()); registerNativeBreadcrumbs(backtraceClient); // Order should not matter } @@ -238,4 +236,4 @@ public void disableNativeIntegration(View view) { public void enableNativeIntegration(View view) { backtraceClient.enableNativeIntegration(); } -} \ No newline at end of file +} From 4181d6bb679caea674257d1d89c9ec361bbe3f1b Mon Sep 17 00:00:00 2001 From: vlussenburg Date: Wed, 23 Nov 2022 10:56:03 -0800 Subject: [PATCH 19/61] Removed hardcoded values --- .../src/main/java/backtraceio/coroner/CoronerClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 9f5eaa41..00704637 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -43,7 +43,7 @@ public CoronerResponse rxIdFilter(String rxId, List customAttributes) th public CoronerResponse errorTypeTimestampFilter(String errorType, String timestampLeast, String timestampMost, List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { List attributes = concatAttributes(customAttributes); - String coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, "1668443545", "1668357145", attributes); + String coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampLeast, timestampMost, attributes); return makeRequest(coronerQuery); } From 92b6fa3937b48e05579941e5b8bb9677da6bcb04 Mon Sep 17 00:00:00 2001 From: vlussenburg Date: Wed, 23 Nov 2022 12:14:43 -0800 Subject: [PATCH 20/61] Fixed Dumpwithoutcrash, disabled unhandledException, reanbled ANR --- .../backtraceio/ExampleInstrumentedTest.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index bb7e6e47..3d903542 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -7,6 +7,7 @@ import android.content.Context; +import androidx.test.espresso.PerformException; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -117,18 +118,24 @@ public void dumpWithoutCrash() throws CoronerResponseProcessingException { Assert.assertNotNull(response); Assert.assertEquals(1, response.getResultsNumber()); String val = response.getAttribute(0, "error.message", String.class); - Assert.assertEquals("Dump without crash", val); + Assert.assertEquals("DumpWithoutCrash", val); } - @Test + //@Test public void unhandledException() throws CoronerResponseProcessingException, InterruptedException { // GIVEN CoronerResponse response = null; long timestampStart = this.getSecondsTimestampNowGMT(); // WHEN - onView(withId(R.id.unhandledException)).perform(click()); // UI action + // UnhandledException crashes the app, so don't actually click the button + // onView(withId(R.id.unhandledException)).perform(click()); // UI action + + // call BacktraceExceptionHandler directly instead + Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), new NullPointerException()); + Thread.sleep(THREAD_SLEEP_TIME_MS); + // THEN try { response = this.getCoronerClient().errorTypeTimestampFilter("Crash", @@ -145,19 +152,14 @@ public void unhandledException() throws CoronerResponseProcessingException, Inte Assert.assertEquals("Dump without crash", val); } -// @Test -// public void anr() { -// onView(withId(R.id.anr)).perform(click()); -// } -// -// @Test(expected = PerformException.class) -// public void unhandledException() { -// onView(withId(R.id.unhandledException)).perform(click()); -// } -// -// // Will break build, obviously. -// //@Test -// public void nativeCrash() { -// onView(withId(R.id.nativeCrash)).perform(click()); -// } + @Test + public void anr() { + onView(withId(R.id.anr)).perform(click()); + } + + // Will break build, obviously. + //@Test + public void nativeCrash() { + onView(withId(R.id.nativeCrash)).perform(click()); + } } From 2e24c1f379dc1c1864bac8c610d766315b616adb Mon Sep 17 00:00:00 2001 From: Vincent Lussenburg Date: Wed, 23 Nov 2022 14:40:29 -0800 Subject: [PATCH 21/61] Skip testing on old emulator for now --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5d38586f..21e077b1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,7 +94,8 @@ jobs: # newest Android version saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --emulator name="Android GoogleApi Emulator,platformVersion=12.0" # oldest Android version - saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --emulator name="Android GoogleApi Emulator,platformVersion=5.1" + # DISABLED TO AWAIT FIXING OF java.lang.NoSuchMethodError: No interface method stream()Ljava/util/stream/Stream; in class Ljava/util/List; or its super classes (declaration of 'java.util.List' appears in /system/framework/core-libart.jar) + # saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --emulator name="Android GoogleApi Emulator,platformVersion=5.1" # real device saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --device name="Samsung_Galaxy_S20_FE_5G_backtrace_us" # --device name=".*,platformVersion=6.0.1" From 96e149dc1e495f45abd7eac2879b31e599148e8f Mon Sep 17 00:00:00 2001 From: Vincent Lussenburg Date: Wed, 23 Nov 2022 14:41:09 -0800 Subject: [PATCH 22/61] Get rid of defunct actions --- .github/workflows/test.yml | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 21e077b1..607c024f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -101,29 +101,3 @@ jobs: # --device name=".*,platformVersion=6.0.1" # --device name="Samsung_Galaxy_S20_FE_5G_backtrace_us" # --device name="Samsung_Galaxy_S22_Ultra_5G_backtrace_us" - - - name: Run morgue - run: | - npm install backtrace-morgue --location=global - morgue --version || true - # Todo: currently just looks 15 minutes in the past and doesn't filter at all. - # --csv=crash.csv - morgue --token=${{ secrets.BACKTRACE_API_TOKEN }} --endpoint="https://yolo.sp.backtrace.io" list yolo/android-library --select timestamp --filter=timestamp,greater-than,`date +%s%3N -d "15 minutes ago"` || true - -# if [ $(wc -l < crash.csv) -gt 0 ]; then -# echo "Success" -# else -# exit 1 -# fi - - # runs-on: macos-latest # necessary for reactivecircus/android-emulator-runner@v2 - # - name: Emulator test - # uses: reactivecircus/android-emulator-runner@v2 - # with: - # target: google_apis - # api-level: ${{ matrix.api-level }} - # arch: x86_64 - # script: ./gradlew connectedCheck - - # - name: Code coverage - # run: bash <(curl -s https://codecov.io/bash) From 416c647cb8132be929c76d39a7b7beca2df83d84 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 24 Nov 2022 23:02:58 +0100 Subject: [PATCH 23/61] Replace streams with loops --- .../backtraceio/coroner/CoronerClient.java | 2 -- .../coroner/query/CoronerFieldFilter.java | 9 +++++--- .../coroner/query/CoronerFiltersBuilder.java | 22 +++++++++++++------ .../coroner/query/CoronerQueryBuilder.java | 15 +++++++++++-- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 00704637..3a4056f8 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -4,8 +4,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; import backtraceio.coroner.query.CoronerQueries; import backtraceio.coroner.query.CoronerQueryFields; diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java index 6371d118..464031fb 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java @@ -2,7 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; public class CoronerFieldFilter { public String name; @@ -23,7 +22,11 @@ public void addValueFilter(final FilterOperator operator, final Object value) { @Override public String toString() { - String result = values.stream().map(x->x.toString()).collect(Collectors.joining(","));; - return "\"" + name + "\": [" + result + " ]"; + List result = new ArrayList<>(); + + for (CoronerValueFilter value : values) { + result.add(value.toString()); + } + return "\"" + name + "\": [" + String.join(",", result) + " ]"; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java index 1663aa92..012f8b46 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java @@ -2,8 +2,6 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; public class CoronerFiltersBuilder { private final List filters; @@ -13,10 +11,16 @@ public CoronerFiltersBuilder() { } public CoronerFiltersBuilder addFilter(final String name, final FilterOperator operator, final Object value) { - final Optional filter = this.filters.stream().filter(x->x.getName().equals(name)).findAny(); + CoronerFieldFilter element = null; - if(filter.isPresent()) { - filter.get().addValueFilter(operator, value); + for (CoronerFieldFilter filter : filters) { + if (filter.getName().equals(name)) { + element = filter; + } + } + + if (element != null) { + element.addValueFilter(operator, value); } else { this.filters.add(new CoronerFieldFilter(name, operator, value)); } @@ -25,9 +29,13 @@ public CoronerFiltersBuilder addFilter(final String name, final FilterOperator o } public String get() { - String result = filters.stream().map(x->x.toString()).collect(Collectors.joining(",")); + List result = new ArrayList<>(); + + for (CoronerFieldFilter filter : filters) { + result.add(filter.toString()); + } - return "{ " + result + " }"; + return "{ " + String.join(",", result) + " }"; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java index 8dc9989f..308539f1 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -1,7 +1,7 @@ package backtraceio.coroner.query; +import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; class CoronerQueryBuilder { private final String FOLD_HEAD = "head"; @@ -13,7 +13,7 @@ public String buildRxIdGroup(String filters, List headFolds) { } private String build(String groupName, String filters, List headFolds) { - String folds = headFolds.stream().map(this::foldHead).collect(Collectors.joining(",")); + String folds = joinHeadFolds(headFolds); return "{" + "\"group\":[" + @@ -26,6 +26,17 @@ private String build(String groupName, String filters, List headFolds) { "}"; } + private String joinHeadFolds(List folds) { + List result = new ArrayList<>(); + + for (String fold : folds) { + result.add(foldHead(fold)); + } + + return String.join(",", result); + + } + private String foldHead(String name) { return this.fold(name, FOLD_HEAD); } From 60a51db7c580a6db0170d9e2ee93f9aa85b6f1c9 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 24 Nov 2022 23:08:45 +0100 Subject: [PATCH 24/61] Enable testing on old devices --- .github/workflows/test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 607c024f..8dfafd88 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -94,8 +94,7 @@ jobs: # newest Android version saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --emulator name="Android GoogleApi Emulator,platformVersion=12.0" # oldest Android version - # DISABLED TO AWAIT FIXING OF java.lang.NoSuchMethodError: No interface method stream()Ljava/util/stream/Stream; in class Ljava/util/List; or its super classes (declaration of 'java.util.List' appears in /system/framework/core-libart.jar) - # saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --emulator name="Android GoogleApi Emulator,platformVersion=5.1" + saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --emulator name="Android GoogleApi Emulator,platformVersion=5.1" # real device saucectl run espresso -c "" --name "From Github Actions" --app example-app-debug.apk --testApp ${{ matrix.test-apk }} --device name="Samsung_Galaxy_S20_FE_5G_backtrace_us" # --device name=".*,platformVersion=6.0.1" From bdd1e33b70be6a73a248fd2952a331e6a3d91993 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 28 Nov 2022 21:10:24 +0100 Subject: [PATCH 25/61] Fix error message --- .../java/backtraceio/backtraceio/ExampleInstrumentedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 3d903542..0239424f 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -90,7 +90,7 @@ public void handledException() throws TimeoutException, CoronerResponseProcessin Assert.assertEquals(1, response.getResultsNumber()); String resultErrorMsg = response.getAttribute(0, "error.message", String.class); - Assert.assertEquals("Index: 3, Size: 3", resultErrorMsg); + Assert.assertEquals("Invalid index 3, size is 3", resultErrorMsg); String resultClassifier = response.getAttribute(0, "classifiers", String.class); Assert.assertEquals("java.lang.IndexOutOfBoundsException", resultClassifier); From 931ef86dbf06b204615cbb4f4c54b68826718885 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 28 Nov 2022 21:59:57 +0100 Subject: [PATCH 26/61] Add sleep --- .../java/backtraceio/backtraceio/ExampleInstrumentedTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 0239424f..21716d18 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -97,13 +97,14 @@ public void handledException() throws TimeoutException, CoronerResponseProcessin } @Test - public void dumpWithoutCrash() throws CoronerResponseProcessingException { + public void dumpWithoutCrash() throws CoronerResponseProcessingException, InterruptedException { // GIVEN CoronerResponse response = null; long timestampStart = this.getSecondsTimestampNowGMT(); // WHEN onView(withId(R.id.dumpWithoutCrash)).perform(click()); // UI action + Thread.sleep(THREAD_SLEEP_TIME_MS); // THEN try { From d949c4a829b09c9b89403374edf1762ad0c1f755 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 28 Nov 2022 22:10:22 +0100 Subject: [PATCH 27/61] Fix instrumented tests --- .../backtraceio/ExampleInstrumentedTest.java | 2 +- .../backtraceio/backtraceio/MainActivity.java | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 21716d18..24d279d0 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -90,7 +90,7 @@ public void handledException() throws TimeoutException, CoronerResponseProcessin Assert.assertEquals(1, response.getResultsNumber()); String resultErrorMsg = response.getAttribute(0, "error.message", String.class); - Assert.assertEquals("Invalid index 3, size is 3", resultErrorMsg); + Assert.assertEquals("Invalid index of selected element!", resultErrorMsg); String resultClassifier = response.getAttribute(0, "classifiers", String.class); Assert.assertEquals("java.lang.IndexOutOfBoundsException", resultClassifier); diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index c2fc3c2d..edec536e 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -142,13 +142,17 @@ void equipItem(List armor, int index) { public void handledException(View view) { try { - List myWarriorArmor = getWarriorArmor(); - int magicWandIndex = findEquipmentIndex(myWarriorArmor, "Magic Wand"); - // I don't need a Magic Wand, I am a warrior - removeEquipment(myWarriorArmor, magicWandIndex); - // Where was that magic wand again? - equipItem(myWarriorArmor, magicWandIndex); - } catch (Exception e) { + try { + List myWarriorArmor = getWarriorArmor(); + int magicWandIndex = findEquipmentIndex(myWarriorArmor, "Magic Wand"); + // I don't need a Magic Wand, I am a warrior + removeEquipment(myWarriorArmor, magicWandIndex); + // Where was that magic wand again? + equipItem(myWarriorArmor, magicWandIndex); + } catch (IndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("Invalid index of selected element!"); + } + } catch (IndexOutOfBoundsException e) { backtraceClient.send(new BacktraceReport(e), this.listener); } } From 10f698ae769b39a65e4f5c503ca0a4fc66da88af Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 28 Nov 2022 22:56:35 +0100 Subject: [PATCH 28/61] Increase timeout --- .../java/backtraceio/backtraceio/ExampleInstrumentedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 24d279d0..435b4075 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -104,7 +104,7 @@ public void dumpWithoutCrash() throws CoronerResponseProcessingException, Interr // WHEN onView(withId(R.id.dumpWithoutCrash)).perform(click()); // UI action - Thread.sleep(THREAD_SLEEP_TIME_MS); + Thread.sleep(THREAD_SLEEP_TIME_MS * 3); // THEN try { From c6867af69439148ee91b3237812319fa58b9c303 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 28 Nov 2022 22:56:45 +0100 Subject: [PATCH 29/61] Increase timeout --- .../java/backtraceio/backtraceio/ExampleInstrumentedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 435b4075..317f6bf1 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -104,7 +104,7 @@ public void dumpWithoutCrash() throws CoronerResponseProcessingException, Interr // WHEN onView(withId(R.id.dumpWithoutCrash)).perform(click()); // UI action - Thread.sleep(THREAD_SLEEP_TIME_MS * 3); + Thread.sleep(THREAD_SLEEP_TIME_MS * 10); // THEN try { From f410e28716a8b6da3b0769c17853b67c98b49c23 Mon Sep 17 00:00:00 2001 From: vlussenburg Date: Tue, 29 Nov 2022 09:59:52 -0800 Subject: [PATCH 30/61] update to latest commmit on backtrace branch --- backtrace-library/src/main/cpp/crashpad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtrace-library/src/main/cpp/crashpad b/backtrace-library/src/main/cpp/crashpad index 3ce0e54c..3f6a78bb 160000 --- a/backtrace-library/src/main/cpp/crashpad +++ b/backtrace-library/src/main/cpp/crashpad @@ -1 +1 @@ -Subproject commit 3ce0e54c6e3341725c5ab1b1a3443835e08b8058 +Subproject commit 3f6a78bb13120d61ad7b76af2824aa712db4e80c From c50c3fc0c6753639ae02e045f259d83287de02b7 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 11:12:15 +0200 Subject: [PATCH 31/61] Update crashpad --- backtrace-library/src/main/cpp/crashpad | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backtrace-library/src/main/cpp/crashpad b/backtrace-library/src/main/cpp/crashpad index 3f6a78bb..3d14eb71 160000 --- a/backtrace-library/src/main/cpp/crashpad +++ b/backtrace-library/src/main/cpp/crashpad @@ -1 +1 @@ -Subproject commit 3f6a78bb13120d61ad7b76af2824aa712db4e80c +Subproject commit 3d14eb710cf5cd81898f86ad76e4e2813a1bd820 From db8ee5c418859d02eba8b5e318fb3dd993cb3a76 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 11:42:59 +0200 Subject: [PATCH 32/61] Import fixes --- example-app/build.gradle | 1 + .../java/backtraceio/backtraceio/ExampleInstrumentedTest.java | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/example-app/build.gradle b/example-app/build.gradle index afba7278..0c8a7c22 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -56,4 +56,5 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.5.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' implementation project(':backtrace-library') + androidTestImplementation project(path: ':coroner-client') } diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 317f6bf1..603ec8e2 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -7,7 +7,6 @@ import android.content.Context; -import androidx.test.espresso.PerformException; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; @@ -26,8 +25,6 @@ import backtraceio.coroner.response.CoronerResponse; import backtraceio.coroner.response.CoronerResponseProcessingException; -import backtraceio.library.events.OnServerResponseEventListener; -import backtraceio.library.models.BacktraceResult; /** * Instrumented test, which will execute on an Android device. From 5c6ea83a2d82068ac3add7b1b49832ae5a806b98 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 11:47:45 +0200 Subject: [PATCH 33/61] Fix import --- example-app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/example-app/build.gradle b/example-app/build.gradle index 0c8a7c22..2811b479 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -54,6 +54,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test:rules:1.5.0' + androidTestImplementation 'net.jodah:concurrentunit:0.4.4' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' implementation project(':backtrace-library') androidTestImplementation project(path: ':coroner-client') From a155f2d72cde6276a1192815e92231a9bfda2bd8 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 12:33:41 +0200 Subject: [PATCH 34/61] Fix Coroner vars --- .../java/backtraceio/backtraceio/InstrumentedTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java index 93e6a5c4..14dea9f2 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java @@ -11,8 +11,10 @@ public class InstrumentedTest { @BeforeClass public static void init() { - String url = BuildConfig.BACKTRACE_CORONER_URL; - String coronerToken = BuildConfig.BACKTRACE_CORONER_TOKEN; +// String url = BuildConfig.BACKTRACE_CORONER_URL; +// String coronerToken = BuildConfig.BACKTRACE_CORONER_TOKEN; + String url = "https://yolo.sp.backtrace.io/api/query?project=android-library"; + String coronerToken = "4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a"; coronerClient = new CoronerClient(url, coronerToken); } From 15fea104009dffc0b2fd2823c88c8f23393fa179 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 17:58:52 +0200 Subject: [PATCH 35/61] Reformat code --- .../src/main/java/backtraceio/coroner/CoronerHttpClient.java | 5 ++--- .../java/backtraceio/coroner/query/CoronerValueFilter.java | 4 +--- .../backtraceio/coroner/response/CoronerApiResponse.java | 1 - .../java/backtraceio/coroner/response/CoronerResponse.java | 2 +- .../serialization/CoronerResponseGroupDeserializer.java | 4 ++-- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index e6288b71..0d7737f5 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -1,7 +1,6 @@ package backtraceio.coroner; - import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; @@ -9,8 +8,8 @@ import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; -import java.util.logging.Logger; import java.util.logging.Level; +import java.util.logging.Logger; import backtraceio.coroner.common.Common; import backtraceio.coroner.response.CoronerApiResponse; @@ -19,7 +18,7 @@ import backtraceio.coroner.serialization.GsonWrapper; class CoronerHttpClient { - private static final Logger LOGGER = Logger.getLogger( CoronerResponseGroupDeserializer.class.getName() ); + private static final Logger LOGGER = Logger.getLogger(CoronerResponseGroupDeserializer.class.getName()); private final String apiUrl; private final String coronerToken; private final String ENCODING = "utf-8"; diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java index 1598dfd3..2d88ccc5 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java @@ -1,7 +1,5 @@ package backtraceio.coroner.query; -import java.util.logging.Filter; - public class CoronerValueFilter { public FilterOperator operator; public Object value; @@ -13,7 +11,7 @@ public CoronerValueFilter(final FilterOperator operator, final Object value) { @Override public String toString() { - return " [" + + return " [" + " \"" + operator.toString() + "\"," + " \"" + value.toString() + "\"" + " ]"; diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java index 3b72bd37..e194c604 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java @@ -3,7 +3,6 @@ import backtraceio.coroner.CoronerError; - public class CoronerApiResponse { public CoronerError error; public CoronerResponse response; diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java index 192a9862..627b4ec6 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java @@ -9,7 +9,7 @@ public class CoronerResponse { - private static final Logger LOGGER = Logger.getLogger( CoronerResponse.class.getName() ); + private static final Logger LOGGER = Logger.getLogger(CoronerResponse.class.getName()); @SerializedName(value = "columns_desc") public List columnsDesc; diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java index 800f1561..b2026380 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java @@ -9,14 +9,14 @@ import java.lang.reflect.Type; import java.util.List; -import java.util.logging.Logger; import java.util.logging.Level; +import java.util.logging.Logger; import backtraceio.coroner.response.CoronerResponseGroup; public class CoronerResponseGroupDeserializer implements JsonDeserializer { - private static final Logger LOGGER = Logger.getLogger( CoronerResponseGroupDeserializer.class.getName() ); + private static final Logger LOGGER = Logger.getLogger(CoronerResponseGroupDeserializer.class.getName()); @Override public CoronerResponseGroup deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { From ce235ac5d343015ace3bf14cd731020cd51e18b9 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 18:37:55 +0200 Subject: [PATCH 36/61] Code improvements --- .../java/backtraceio/library/BacktraceClientSendTest.java | 4 +--- .../src/main/java/backtraceio/coroner/CoronerHttpClient.java | 3 --- .../main/java/backtraceio/coroner/query/CoronerQueries.java | 2 -- .../java/backtraceio/coroner/query/CoronerQueryBuilder.java | 4 ---- .../java/backtraceio/coroner/response/CoronerApiResponse.java | 1 - 5 files changed, 1 insertion(+), 13 deletions(-) diff --git a/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java b/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java index b4d45234..ce26543f 100644 --- a/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java +++ b/backtrace-library/src/androidTest/java/backtraceio/library/BacktraceClientSendTest.java @@ -182,7 +182,6 @@ public BacktraceResult onRequest(BacktraceData data) { backtraceClient.setOnRequestHandler(rh); // WHEN - backtraceClient.send(new BacktraceReport(this.resultMessage, this.attributes), new OnServerResponseEventListener() { @Override @@ -298,7 +297,6 @@ public BacktraceResult onRequest(BacktraceData data) { }; backtraceClient.setOnRequestHandler(rh); - //WHEN for (final String message : messages) { backtraceClient.send(new Exception(message), new OnServerResponseEventListener() { @@ -319,4 +317,4 @@ public void onEvent(BacktraceResult backtraceResult) { fail(ex.getMessage()); } } -} +} \ No newline at end of file diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index 0d7737f5..d1f0ece0 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -1,6 +1,5 @@ package backtraceio.coroner; - import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.IOException; @@ -22,12 +21,10 @@ class CoronerHttpClient { private final String apiUrl; private final String coronerToken; private final String ENCODING = "utf-8"; - public CoronerHttpClient(String apiUrl, String coronerToken) { this.apiUrl = apiUrl; this.coronerToken = coronerToken; } - public CoronerApiResponse get(String json) throws CoronerHttpException, IOException { HttpURLConnection urlConnection = prepareHttpRequest(json); int statusCode = urlConnection.getResponseCode(); diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java index b939f3cb..e5c63d49 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -29,6 +29,4 @@ public String filterByRxId(String rxId, List attributes) { return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); } - -// public String filterBy } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java index 308539f1..ff34282f 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -25,7 +25,6 @@ private String build(String groupName, String filters, List headFolds) { " \"filter\":[" + filters + "]" + "}"; } - private String joinHeadFolds(List folds) { List result = new ArrayList<>(); @@ -34,13 +33,10 @@ private String joinHeadFolds(List folds) { } return String.join(",", result); - } - private String foldHead(String name) { return this.fold(name, FOLD_HEAD); } - private String fold(String name, String val) { return "\"" + name + "\": " + "[" + "[\"" + val + "\"" + "]" + "]"; diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java index e194c604..29c320df 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java @@ -2,7 +2,6 @@ import backtraceio.coroner.CoronerError; - public class CoronerApiResponse { public CoronerError error; public CoronerResponse response; From 077e45b567e5f101b77534f86cbba641c90c01b9 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 18:38:10 +0200 Subject: [PATCH 37/61] Add serializer tests --- .../library/BacktraceSerializeHelperTest.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 backtrace-library/src/test/java/backtraceio/library/BacktraceSerializeHelperTest.java diff --git a/backtrace-library/src/test/java/backtraceio/library/BacktraceSerializeHelperTest.java b/backtrace-library/src/test/java/backtraceio/library/BacktraceSerializeHelperTest.java new file mode 100644 index 00000000..15b11e0d --- /dev/null +++ b/backtrace-library/src/test/java/backtraceio/library/BacktraceSerializeHelperTest.java @@ -0,0 +1,42 @@ +package backtraceio.library; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertNull; + +import junit.framework.Assert; + +import org.junit.Test; + +import backtraceio.library.common.BacktraceSerializeHelper; +import backtraceio.library.models.BacktraceResult; +import backtraceio.library.models.json.BacktraceReport; +import backtraceio.library.models.types.BacktraceResultStatus; + +public class BacktraceSerializeHelperTest { + @Test + public void testSerialization() { + // GIVEN + BacktraceResult backtraceResult = new BacktraceResult(null, "result-message", BacktraceResultStatus.Ok); + // WHEN + String json = BacktraceSerializeHelper.toJson(backtraceResult); + // THEN + assertEquals("{\"message\":\"result-message\",\"status\":\"Ok\"}", json); + } + + @Test + public void testDeserialization() { + // GIVEN + String json = "{\"_rxid\": \"12345\", \"message\":\"result-message\",\"status\":\"Ok\"}"; + + // WHEN + BacktraceResult result = BacktraceSerializeHelper.fromJson(json ,BacktraceResult.class); + + // THEN + assertNotNull(result); + assertEquals("result-message", result.message); + assertEquals("12345", result.rxId); + assertEquals(BacktraceResultStatus.Ok, result.status); + assertNull(result.getBacktraceReport()); + } +} From eba34daef5e1b1b3cf6ca17bd84d86bd8de733ea Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 18:43:56 +0200 Subject: [PATCH 38/61] Improve settings.gradle --- .../src/main/java/backtraceio/backtraceio/MainActivity.java | 2 +- settings.gradle | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index c4dea67b..0fb5a877 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -53,7 +53,7 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL); // Set this value in your local.properties + backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL); symlinkAndWriteFile(); } diff --git a/settings.gradle b/settings.gradle index b62cf823..ac7ddbbc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1 @@ -include ':example-app', ':backtrace-library' -include ':coroner-client' +include ':example-app', ':backtrace-library', ':coroner-client' \ No newline at end of file From 6ad5d5fb0f9531785f1313d7f6057f78e4bc63b5 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 20:07:39 +0200 Subject: [PATCH 39/61] Remove unused code --- .../backtraceio/backtraceio/ExampleInstrumentedTest.java | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 603ec8e2..e9bacb4a 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -41,14 +41,7 @@ public class ExampleInstrumentedTest extends InstrumentedTest { @Before public void enableMetricsAndBreadcrumbs() { - try { - onView(withId(R.id.enableBreadcrumbs)).perform(click()); - } - catch (Exception ex) { - System.out.println("Before enableMetricsAndBreadcrumbs"); - System.out.println(ex.getMessage()); - throw ex; - } + onView(withId(R.id.enableBreadcrumbs)).perform(click()); } @Test From 0e56d4e4977f739bd357589e9b3c77a8e3639752 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Apr 2023 20:33:35 +0200 Subject: [PATCH 40/61] Update dependencies --- coroner-client/build.gradle | 2 +- example-app/build.gradle | 6 +++--- .../java/backtraceio/backtraceio/InstrumentedTest.java | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/coroner-client/build.gradle b/coroner-client/build.gradle index b5fbfdf0..377ec4f8 100644 --- a/coroner-client/build.gradle +++ b/coroner-client/build.gradle @@ -9,5 +9,5 @@ java { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.google.code.gson:gson:2.9.1' + implementation 'com.google.code.gson:gson:2.10' } \ No newline at end of file diff --git a/example-app/build.gradle b/example-app/build.gradle index 2811b479..9cf5010f 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -49,13 +49,13 @@ String getSubmissionUrl() { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test:rules:1.5.0' androidTestImplementation 'net.jodah:concurrentunit:0.4.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' implementation project(':backtrace-library') androidTestImplementation project(path: ':coroner-client') } diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java index 14dea9f2..c471c423 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java @@ -1,7 +1,5 @@ package backtraceio.backtraceio; -import androidx.test.ext.junit.runners.AndroidJUnit4; - import org.junit.BeforeClass; import backtraceio.coroner.CoronerClient; From ad72b8f2d452d5861dfbb6def3b35e064e07a0f0 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 8 Apr 2023 09:38:07 +0200 Subject: [PATCH 41/61] Add finals --- .../backtraceio/coroner/CoronerClient.java | 22 +++++++-------- .../backtraceio/coroner/CoronerError.java | 2 +- .../coroner/CoronerHttpClient.java | 28 +++++++++---------- .../coroner/query/CoronerFieldFilter.java | 8 +++--- .../coroner/query/CoronerFiltersBuilder.java | 2 +- .../coroner/query/CoronerQueries.java | 10 +++---- .../coroner/query/CoronerQueryBuilder.java | 14 +++++----- .../coroner/query/CoronerQueryFields.java | 12 ++++---- .../coroner/query/CoronerValueFilter.java | 4 +-- .../coroner/response/CoronerApiResponse.java | 2 +- .../response/CoronerHttpException.java | 6 ++-- .../coroner/response/CoronerResponse.java | 12 ++++---- .../response/CoronerResponseException.java | 2 +- .../response/CoronerResponseGroup.java | 4 +-- .../CoronerResponseProcessingException.java | 2 +- .../CoronerResponseGroupDeserializer.java | 6 ++-- .../coroner/serialization/GsonWrapper.java | 2 +- 17 files changed, 69 insertions(+), 69 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 3a4056f8..69a08551 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -21,39 +21,39 @@ public class CoronerClient { CoronerQueryFields.FOLD_CLASSIFIERS ); - public CoronerClient(String apiUrl, String coronerToken) { + public CoronerClient(final String apiUrl, final String coronerToken) { this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); this.coronerQueries = new CoronerQueries(); } - public CoronerResponse rxIdFilter(String rxId) throws Exception { + public CoronerResponse rxIdFilter(final String rxId) throws Exception { return this.rxIdFilter(rxId, new ArrayList<>()); } - public CoronerResponse rxIdFilter(String rxId, List customAttributes) throws CoronerResponseException, CoronerHttpException, IOException { - List attributes = concatAttributes(customAttributes); + public CoronerResponse rxIdFilter(final String rxId, final List customAttributes) throws CoronerResponseException, CoronerHttpException, IOException { + final List attributes = concatAttributes(customAttributes); - String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); + final String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); return makeRequest(coronerQuery); } - public CoronerResponse errorTypeTimestampFilter(String errorType, String timestampLeast, String timestampMost, List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { - List attributes = concatAttributes(customAttributes); + public CoronerResponse errorTypeTimestampFilter(final String errorType, final String timestampLeast, final String timestampMost, final List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { + final List attributes = concatAttributes(customAttributes); String coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampLeast, timestampMost, attributes); return makeRequest(coronerQuery); } - private List concatAttributes(List customAttributes) { - List result = new ArrayList<>(customAttributes); + private List concatAttributes(final List customAttributes) { + final List result = new ArrayList<>(customAttributes); result.addAll(DEFAULT_ATTRIBUTES); return result; } - private CoronerResponse makeRequest(String coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { - CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); + private CoronerResponse makeRequest(final String coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { + final CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); if (response.error != null) { throw new CoronerResponseException(response.getError()); diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java index 5b194bb9..2b05c65c 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerError.java @@ -10,7 +10,7 @@ public CoronerError() { } @SuppressWarnings("unused") - public CoronerError(String message, int code) { + public CoronerError(final String message, final int code) { this.message = message; this.code = code; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index d1f0ece0..ec42d4d7 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -21,13 +21,13 @@ class CoronerHttpClient { private final String apiUrl; private final String coronerToken; private final String ENCODING = "utf-8"; - public CoronerHttpClient(String apiUrl, String coronerToken) { + public CoronerHttpClient(final String apiUrl, final String coronerToken) { this.apiUrl = apiUrl; this.coronerToken = coronerToken; } - public CoronerApiResponse get(String json) throws CoronerHttpException, IOException { - HttpURLConnection urlConnection = prepareHttpRequest(json); - int statusCode = urlConnection.getResponseCode(); + public CoronerApiResponse get(final String json) throws CoronerHttpException, IOException { + final HttpURLConnection urlConnection = prepareHttpRequest(json); + final int statusCode = urlConnection.getResponseCode(); if (statusCode != HttpURLConnection.HTTP_OK) { String message = getResponseMessage(urlConnection); @@ -36,22 +36,22 @@ public CoronerApiResponse get(String json) throws CoronerHttpException, IOExcept throw new CoronerHttpException(statusCode, String.format("%s: %s", statusCode, message)); } - String resultJson = getResponseMessage(urlConnection); + final String resultJson = getResponseMessage(urlConnection); return GsonWrapper.fromJson( resultJson, CoronerApiResponse.class); } - private static String getResponseMessage(HttpURLConnection urlConnection) throws IOException { + private static String getResponseMessage(final HttpURLConnection urlConnection) throws IOException { LOGGER.log(Level.INFO, "Reading response from HTTP request"); - InputStream inputStream = getInputStream(urlConnection); + final InputStream inputStream = getInputStream(urlConnection); - BufferedReader br = new BufferedReader(new InputStreamReader( + final BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); - StringBuilder responseSB = new StringBuilder(); + final StringBuilder responseSB = new StringBuilder(); String line; while ((line = br.readLine()) != null) { responseSB.append(line); @@ -60,15 +60,15 @@ private static String getResponseMessage(HttpURLConnection urlConnection) throws return responseSB.toString(); } - private static InputStream getInputStream(HttpURLConnection urlConnection) throws IOException { + private static InputStream getInputStream(final HttpURLConnection urlConnection) throws IOException { if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) return urlConnection.getInputStream(); return urlConnection.getErrorStream(); } - private HttpURLConnection prepareHttpRequest(String json) throws IOException { - URL url = new URL(this.apiUrl); - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + private HttpURLConnection prepareHttpRequest(final String json) throws IOException { + final URL url = new URL(this.apiUrl); + final HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod("POST"); urlConnection.setUseCaches(false); @@ -82,7 +82,7 @@ private HttpURLConnection prepareHttpRequest(String json) throws IOException { DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); - byte[] bytes = json.getBytes(ENCODING); + final byte[] bytes = json.getBytes(ENCODING); request.write(bytes); request.flush(); diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java index 464031fb..1c3158f2 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java @@ -4,10 +4,10 @@ import java.util.List; public class CoronerFieldFilter { - public String name; - public List values = new ArrayList<>(); + public final String name; + public final List values = new ArrayList<>(); - public CoronerFieldFilter(String name, FilterOperator operator, Object value) { + public CoronerFieldFilter(final String name, final FilterOperator operator, final Object value) { this.name = name; this.addValueFilter(operator, value); } @@ -22,7 +22,7 @@ public void addValueFilter(final FilterOperator operator, final Object value) { @Override public String toString() { - List result = new ArrayList<>(); + final List result = new ArrayList<>(); for (CoronerValueFilter value : values) { result.add(value.toString()); diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java index 012f8b46..d0eca0da 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java @@ -29,7 +29,7 @@ public CoronerFiltersBuilder addFilter(final String name, final FilterOperator o } public String get() { - List result = new ArrayList<>(); + final List result = new ArrayList<>(); for (CoronerFieldFilter filter : filters) { result.add(filter.toString()); diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java index e5c63d49..e4dcb483 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -10,12 +10,12 @@ public CoronerQueries() { builder = new CoronerQueryBuilder(); } - public String filterByRxId(String rxId) { + public String filterByRxId(final String rxId) { return this.filterByRxId(rxId, new ArrayList<>()); } - public String filterByErrorTypeAndTimestamp(String errorType, String timestampLeast, String timestampMost, List attributes) { - CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + public String filterByErrorTypeAndTimestamp(final String errorType, final String timestampLeast, final String timestampMost, final List attributes) { + final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); filtersBuilder.addFilter(CoronerQueryFields.ERROR_TYPE, FilterOperator.EQUAL, errorType); filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_LEAST, timestampLeast + "."); filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); @@ -23,8 +23,8 @@ public String filterByErrorTypeAndTimestamp(String errorType, String timestampLe return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); } - public String filterByRxId(String rxId, List attributes) { - CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + public String filterByRxId(final String rxId, final List attributes) { + final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); filtersBuilder.addFilter(CoronerQueryFields.RXID, FilterOperator.EQUAL, rxId); return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java index ff34282f..052f9606 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -8,12 +8,12 @@ class CoronerQueryBuilder { private final int OFFSET = 0; private final int LIMIT = 1; - public String buildRxIdGroup(String filters, List headFolds) { + public String buildRxIdGroup(final String filters, final List headFolds) { return this.build(CoronerQueryFields.RXID, filters, headFolds); } - private String build(String groupName, String filters, List headFolds) { - String folds = joinHeadFolds(headFolds); + private String build(final String groupName, final String filters, final List headFolds) { + final String folds = joinHeadFolds(headFolds); return "{" + "\"group\":[" + @@ -25,8 +25,8 @@ private String build(String groupName, String filters, List headFolds) { " \"filter\":[" + filters + "]" + "}"; } - private String joinHeadFolds(List folds) { - List result = new ArrayList<>(); + private String joinHeadFolds(final List folds) { + final List result = new ArrayList<>(); for (String fold : folds) { result.add(foldHead(fold)); @@ -34,10 +34,10 @@ private String joinHeadFolds(List folds) { return String.join(",", result); } - private String foldHead(String name) { + private String foldHead(final String name) { return this.fold(name, FOLD_HEAD); } - private String fold(String name, String val) { + private String fold(final String name, final String val) { return "\"" + name + "\": " + "[" + "[\"" + val + "\"" + "]" + "]"; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java index 910228e8..11bcec96 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java @@ -1,11 +1,11 @@ package backtraceio.coroner.query; public class CoronerQueryFields { - public static String FOLD_CALLSTACK = "callstack"; - public static String FOLD_CLASSIFIERS = "classifiers"; - public static String FOLD_GUID = "guid"; + public static final String FOLD_CALLSTACK = "callstack"; + public static final String FOLD_CLASSIFIERS = "classifiers"; + public static final String FOLD_GUID = "guid"; - public static String RXID = "_rxid"; - public static String ERROR_TYPE = "error.type"; - public static String TIMESTAMP = "timestamp"; + public static final String RXID = "_rxid"; + public static final String ERROR_TYPE = "error.type"; + public static final String TIMESTAMP = "timestamp"; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java index 2d88ccc5..d401d07d 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java @@ -1,8 +1,8 @@ package backtraceio.coroner.query; public class CoronerValueFilter { - public FilterOperator operator; - public Object value; + public final FilterOperator operator; + public final Object value; public CoronerValueFilter(final FilterOperator operator, final Object value) { this.operator = operator; diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java index 29c320df..88e0592d 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java @@ -12,7 +12,7 @@ public CoronerApiResponse() { } @SuppressWarnings("unused") - public CoronerApiResponse(CoronerError error, CoronerResponse response) { + public CoronerApiResponse(final CoronerError error, final CoronerResponse response) { this.error = error; this.response = response; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java index e2ac32ed..e43d074a 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerHttpException.java @@ -26,7 +26,7 @@ public CoronerHttpException(String message) { * @param httpStatus received HTTP status code */ @SuppressWarnings("unused") - public CoronerHttpException(Integer httpStatus) { + public CoronerHttpException(final Integer httpStatus) { this(httpStatus, null); } @@ -36,7 +36,7 @@ public CoronerHttpException(Integer httpStatus) { * @param httpStatus received HTTP status code * @param message received exception message */ - public CoronerHttpException(Integer httpStatus, String message) { + public CoronerHttpException(final Integer httpStatus, final String message) { super(message); setHttpStatus(httpStatus); } @@ -56,7 +56,7 @@ public int getHttpStatus() { * * @param httpStatus received HTTP status code */ - private void setHttpStatus(int httpStatus) { + private void setHttpStatus(final int httpStatus) { this.httpStatus = httpStatus; } } \ No newline at end of file diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java index 627b4ec6..aca4a5f8 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java @@ -20,23 +20,23 @@ public CoronerResponse() { } @SuppressWarnings("unused") - public CoronerResponse(List columnsDesc, List values) { + public CoronerResponse(final List columnsDesc, final List values) { this.columnsDesc = columnsDesc; this.values = values; } - public T getAttribute(int elementIndex, String name, Class clazz) throws CoronerResponseProcessingException { + public T getAttribute(final int elementIndex, final String name, final Class clazz) throws CoronerResponseProcessingException { if (this.values == null) { throw new CoronerResponseProcessingException("Values property from response is null"); } if (elementIndex < 0 || elementIndex > this.values.size()) { throw new CoronerResponseProcessingException("Incorrect element index, value should be between 0 and " + this.values.size()); } - CoronerResponseGroup responseGroup = values.get(elementIndex); + final CoronerResponseGroup responseGroup = values.get(elementIndex); - int attributeIndex = getAttributeIndex(name); + final int attributeIndex = getAttributeIndex(name); try { - List results = (ArrayList) responseGroup.getAttribute(attributeIndex); + final List results = (ArrayList) responseGroup.getAttribute(attributeIndex); return clazz.cast(results.get(0)); } catch (ClassCastException e) { LOGGER.log(Level.SEVERE, e.getMessage()); @@ -48,7 +48,7 @@ public int getResultsNumber() { return values.size(); } - private int getAttributeIndex(String attributeName) throws CoronerResponseProcessingException { + private int getAttributeIndex(final String attributeName) throws CoronerResponseProcessingException { for (int index = 0; index < this.columnsDesc.size(); index++) { if (this.columnsDesc.get(index).name.equals(attributeName)) { return index; diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java index fd35dde1..fd867ad5 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseException.java @@ -5,7 +5,7 @@ public class CoronerResponseException extends Exception { private final CoronerError coronerError; - public CoronerResponseException(CoronerError coronerError) { + public CoronerResponseException(final CoronerError coronerError) { super(coronerError.getMessage()); this.coronerError = coronerError; } diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java index 367f1e2e..2c7412b6 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java @@ -8,7 +8,7 @@ public class CoronerResponseGroup { private final String groupIdentifier; private final List values; - public CoronerResponseGroup(List obj) throws IllegalArgumentException { + public CoronerResponseGroup(final List obj) throws IllegalArgumentException { if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL + ", current value: " + (obj != null ? obj.size() : "null")); @@ -18,7 +18,7 @@ public CoronerResponseGroup(List obj) throws IllegalArgumentException { this.values = (List) obj.get(1); } - public Object getAttribute(int index) { + public Object getAttribute(final int index) { return values.get(index); } diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java index 7121b83e..7fb63c80 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseProcessingException.java @@ -8,7 +8,7 @@ public CoronerResponseProcessingException() { } @SuppressWarnings("unused") - public CoronerResponseProcessingException(String message) { + public CoronerResponseProcessingException(final String message) { super(message); } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java index b2026380..8826575c 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializer.java @@ -19,9 +19,9 @@ public class CoronerResponseGroupDeserializer implements JsonDeserializer obj = new Gson().fromJson(jsonArray, (Type) Object.class); + public CoronerResponseGroup deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException { + final JsonArray jsonArray = json.getAsJsonArray(); + final List obj = new Gson().fromJson(jsonArray, (Type) Object.class); try { return new CoronerResponseGroup(obj); } catch (Exception e) { diff --git a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java index ed2bb9ba..3b805f48 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java +++ b/coroner-client/src/main/java/backtraceio/coroner/serialization/GsonWrapper.java @@ -8,7 +8,7 @@ public class GsonWrapper { - public static T fromJson(String json, Class type) { + public static T fromJson(final String json, final Class type) { Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_DASHES) .registerTypeAdapter(CoronerResponseGroup.class, new CoronerResponseGroupDeserializer()) From 88e6f0d096d5b7ec01ed68dcbdac5ce421db83ad Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 8 Apr 2023 09:44:39 +0200 Subject: [PATCH 42/61] Unit test config --- coroner-client/build.gradle | 2 ++ .../test/java/backtraceio/coroner/ExampleTest.java | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java diff --git a/coroner-client/build.gradle b/coroner-client/build.gradle index 377ec4f8..da85e413 100644 --- a/coroner-client/build.gradle +++ b/coroner-client/build.gradle @@ -10,4 +10,6 @@ java { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.google.code.gson:gson:2.10' + testImplementation 'junit:junit:4.13.2' +// androidTestImplementation 'junit:junit:4.13.2' } \ No newline at end of file diff --git a/coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java b/coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java new file mode 100644 index 00000000..581f8790 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java @@ -0,0 +1,13 @@ +package backtraceio.coroner; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class ExampleTest { + + @Test + public void test() { + assertEquals("a", "a"); + } +} From 008c12dfe043f9f4c2871737e1b7bd16a8f9fe2d Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Tue, 11 Apr 2023 21:14:52 +0200 Subject: [PATCH 43/61] Add unit tests --- .../coroner/CoronerHttpClient.java | 4 +- .../coroner/query/CoronerFieldFilter.java | 2 +- .../coroner/query/CoronerQueries.java | 12 ++-- .../coroner/query/CoronerQueryFields.java | 1 - .../coroner/query/CoronerValueFilter.java | 8 +-- .../coroner/response/CoronerResponse.java | 2 + .../java/backtraceio/coroner/StringUtils.java | 7 ++ .../coroner/common/CommonTest.java | 35 ++++++++++ .../coroner/query/CoronerFieldFilterTest.java | 43 ++++++++++++ .../query/CoronerFiltersBuilderTest.java | 68 +++++++++++++++++++ .../coroner/query/CoronerQueriesTest.java | 64 +++++++++++++++++ .../query/CoronerQueryBuilderTest.java | 28 ++++++++ .../coroner/query/CoronerValueFilterTest.java | 21 ++++++ .../serialization/GsonWrapperTest.java | 50 ++++++++++++++ .../coroner/sample-coroner-response.json | 1 + 15 files changed, 332 insertions(+), 14 deletions(-) create mode 100644 coroner-client/src/test/java/backtraceio/coroner/StringUtils.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/common/CommonTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index ec42d4d7..93b25f58 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -25,8 +25,8 @@ public CoronerHttpClient(final String apiUrl, final String coronerToken) { this.apiUrl = apiUrl; this.coronerToken = coronerToken; } - public CoronerApiResponse get(final String json) throws CoronerHttpException, IOException { - final HttpURLConnection urlConnection = prepareHttpRequest(json); + public CoronerApiResponse get(final String requestJson) throws CoronerHttpException, IOException { + final HttpURLConnection urlConnection = prepareHttpRequest(requestJson); final int statusCode = urlConnection.getResponseCode(); if (statusCode != HttpURLConnection.HTTP_OK) { diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java index 1c3158f2..28e701f2 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java @@ -27,6 +27,6 @@ public String toString() { for (CoronerValueFilter value : values) { result.add(value.toString()); } - return "\"" + name + "\": [" + String.join(",", result) + " ]"; + return "\"" + name + "\": [" + String.join(",", result) + "]"; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java index e4dcb483..a9427bf4 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -14,18 +14,18 @@ public String filterByRxId(final String rxId) { return this.filterByRxId(rxId, new ArrayList<>()); } - public String filterByErrorTypeAndTimestamp(final String errorType, final String timestampLeast, final String timestampMost, final List attributes) { + public String filterByRxId(final String rxId, final List attributes) { final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); - filtersBuilder.addFilter(CoronerQueryFields.ERROR_TYPE, FilterOperator.EQUAL, errorType); - filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_LEAST, timestampLeast + "."); - filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); + filtersBuilder.addFilter(CoronerQueryFields.RXID, FilterOperator.EQUAL, rxId); return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); } - public String filterByRxId(final String rxId, final List attributes) { + public String filterByErrorTypeAndTimestamp(final String errorType, final String timestampLeast, final String timestampMost, final List attributes) { final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); - filtersBuilder.addFilter(CoronerQueryFields.RXID, FilterOperator.EQUAL, rxId); + filtersBuilder.addFilter(CoronerQueryFields.ERROR_TYPE, FilterOperator.EQUAL, errorType); + filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_LEAST, timestampLeast + "."); + filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java index 11bcec96..83fa29a9 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryFields.java @@ -4,7 +4,6 @@ public class CoronerQueryFields { public static final String FOLD_CALLSTACK = "callstack"; public static final String FOLD_CLASSIFIERS = "classifiers"; public static final String FOLD_GUID = "guid"; - public static final String RXID = "_rxid"; public static final String ERROR_TYPE = "error.type"; public static final String TIMESTAMP = "timestamp"; diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java index d401d07d..9433a5e9 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java @@ -11,9 +11,9 @@ public CoronerValueFilter(final FilterOperator operator, final Object value) { @Override public String toString() { - return " [" + - " \"" + operator.toString() + "\"," + - " \"" + value.toString() + "\"" + - " ]"; + return "[" + + " \"" + operator.toString() + "\"," + + " \"" + value.toString() + "\"" + + " ]"; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java index aca4a5f8..29deb9fd 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponse.java @@ -13,6 +13,8 @@ public class CoronerResponse { @SerializedName(value = "columns_desc") public List columnsDesc; + + @SerializedName(value = "values") public List values; @SuppressWarnings("unused") diff --git a/coroner-client/src/test/java/backtraceio/coroner/StringUtils.java b/coroner-client/src/test/java/backtraceio/coroner/StringUtils.java new file mode 100644 index 00000000..3fc4ad20 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/StringUtils.java @@ -0,0 +1,7 @@ +package backtraceio.coroner; + +public class StringUtils { + public static String normalizeSpace(String input) { + return input.trim().replaceAll("\\s+"," "); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/common/CommonTest.java b/coroner-client/src/test/java/backtraceio/coroner/common/CommonTest.java new file mode 100644 index 00000000..9c51bf44 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/common/CommonTest.java @@ -0,0 +1,35 @@ +package backtraceio.coroner.common; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class CommonTest { + @Test + public void stringIsNull() { + final String input = null; + + boolean result = Common.isNullOrEmpty(input); + + assertTrue(result); + } + + @Test + public void stringIsEmpty() { + final String input = ""; + + boolean result = Common.isNullOrEmpty(input); + + assertTrue(result); + } + + @Test + public void stringInNotEmpty() { + final String input = "test-string"; + + boolean result = Common.isNullOrEmpty(input); + + assertFalse(result); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java new file mode 100644 index 00000000..e8d42e9e --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java @@ -0,0 +1,43 @@ +package backtraceio.coroner.query; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class CoronerFieldFilterTest { + @Test + public void singleFilter() { + // GIVEN + final String name = "_rxid"; + final FilterOperator operator = FilterOperator.EQUAL; + final String value = "03000000-4f0a-fd08-0000-000000000000"; + + // WHEN + final CoronerFieldFilter coronerFieldFilter = new CoronerFieldFilter(name, operator, value); + final String result = coronerFieldFilter.toString(); + + // THEN + final String expectedResult = "\"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]]"; + assertEquals(expectedResult, result); + } + + @Test + public void multipleFilters() { + // GIVEN + final String filterName = "_rxid"; + + final FilterOperator filterOperator1 = FilterOperator.EQUAL; + final String filterValue1 = "03000000-4f0a-fd08-0000-000000000000"; + final FilterOperator filterOperator2 = FilterOperator.AT_LEAST; + final String filterValue2 = "4f0a0000-4f0a-fd08-0000-999000999000"; + + // WHEN + final CoronerFieldFilter coronerFieldFilter = new CoronerFieldFilter(filterName, filterOperator1, filterValue1); + coronerFieldFilter.addValueFilter(filterOperator2, filterValue2); + final String result = coronerFieldFilter.toString(); + + // THEN + final String expectedResult = "\"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ],[ \"at-least\", \"4f0a0000-4f0a-fd08-0000-999000999000\" ]]"; + assertEquals(expectedResult, result); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java new file mode 100644 index 00000000..78bbd135 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java @@ -0,0 +1,68 @@ +package backtraceio.coroner.query; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import backtraceio.coroner.StringUtils; + +public class CoronerFiltersBuilderTest { + + @Test + public void emptyFilters() { + // GIVEN + final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + + // WHEN + String result = filtersBuilder.get(); + + // THEN + assertEquals("{ }", StringUtils.normalizeSpace(result)); + } + + @Test + public void singleFilter() { + // GIVEN + final String name = "_rxid"; + final FilterOperator operator = FilterOperator.EQUAL; + final String value = "03000000-4f0a-fd08-0000-000000000000"; + final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + + // WHEN + filtersBuilder.addFilter(name, operator, value); + final String result = filtersBuilder.get(); + + // THEN + final String expectedResult = "{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]] }"; + assertEquals(expectedResult, result); + } + + @Test + public void multipleFilters() { + // GIVEN + final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); + + // GIVEN FILTER 1 + final String filter1Name = "_rxid"; + final FilterOperator filter1Operator1 = FilterOperator.EQUAL; + final String filter1Value1 = "03000000-4f0a-fd08-0000-000000000000"; + final FilterOperator filter1Operator2 = FilterOperator.AT_LEAST; + final String filter1Value2 = "4f0a0000-4f0a-fd08-0000-999000999000"; + + // GIVEN FILTER 2 + final String filter2Name = "example_field"; + final FilterOperator filter2Operator = FilterOperator.AT_MOST; + final String filter2Value = "12345678-4f0b-fdp2-0001-000094000000"; + + // WHEN + filtersBuilder.addFilter(filter1Name, filter1Operator1, filter1Value1); + filtersBuilder.addFilter(filter1Name, filter1Operator2, filter1Value2); + filtersBuilder.addFilter(filter2Name, filter2Operator, filter2Value); + + final String result = filtersBuilder.get(); + + // THEN + final String expectedResult = "{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ],[ \"at-least\", \"4f0a0000-4f0a-fd08-0000-999000999000\" ]],\"example_field\": [[ \"at-most\", \"12345678-4f0b-fdp2-0001-000094000000\" ]] }"; + assertEquals(expectedResult, result); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java new file mode 100644 index 00000000..c177bb97 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java @@ -0,0 +1,64 @@ +package backtraceio.coroner.query; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import backtraceio.coroner.StringUtils; + +public class CoronerQueriesTest { + public CoronerQueries coronerQueries; + + @Before + public void init() { + coronerQueries = new CoronerQueries(); + } + + @Test + public void filterByRxIdWithoutAttributesTest() { + // GIVEN + String rxId = "03000000-4f0a-fd08-0000-000000000000"; + + // WHEN + String result = coronerQueries.filterByRxId(rxId); + + // THEN + String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {}, \"offset\":0, \"limit\":1, \"filter\":[{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]] }]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + } + + @Test + public void filterByRxIdTest() { + // GIVEN + String rxId = "03000000-4f0a-fd08-0000-000000000000"; + List attributes = Arrays.asList("value1", "value2"); + + // WHEN + String result = coronerQueries.filterByRxId(rxId, attributes); + + // THEN + String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {\"value1\": [[\"head\"]],\"value2\": [[\"head\"]]}, \"offset\":0, \"limit\":1, \"filter\":[{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]] }]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + } + + @Test + public void filterByErrorTypeAndTimestampTest() { + // GIVEN + final List attributes = Arrays.asList("error.message", "example-attribute"); + + final String errorType = "Crash"; + final String timestampStart = "1680943692"; + final String timestampEnd = "1681943692"; + + // WHEN + String result = coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampStart, timestampEnd, attributes); + + // THEN + String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {\"error.message\": [[\"head\"]],\"example-attribute\": [[\"head\"]]}, \"offset\":0, \"limit\":1, \"filter\":[{ \"error.type\": [[ \"equal\", \"Crash\" ]],\"timestamp\": [[ \"at-least\", \"1680943692.\" ],[ \"at-most\", \"1681943692.\" ]] }]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java new file mode 100644 index 00000000..ac7afca6 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java @@ -0,0 +1,28 @@ +package backtraceio.coroner.query; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import backtraceio.coroner.StringUtils; + +public class CoronerQueryBuilderTest { + + @Test + public void testBuildRxIdGroup() { + // GIVEN + final CoronerQueryBuilder coronerQueryBuilder = new CoronerQueryBuilder(); + final String filters = "{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}"; + final List headFolds = Arrays.asList("error.type", "callstack"); + + // WHEN + final String result = coronerQueryBuilder.buildRxIdGroup(filters, headFolds); + + // THEN + final String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {\"error.type\": [[\"head\"]],\"callstack\": [[\"head\"]]}, \"offset\":0, \"limit\":1, \"filter\":[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java new file mode 100644 index 00000000..5c3a62d0 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java @@ -0,0 +1,21 @@ +package backtraceio.coroner.query; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class CoronerValueFilterTest { + + @Test + public void serializationTest() { + // GIVEN + final CoronerValueFilter coronerValueFilter = new CoronerValueFilter(FilterOperator.EQUAL, "123"); + + // WHEN + String result = coronerValueFilter.toString(); + + // THEN + String expectedResult = "[ \"equal\", \"123\" ]"; + assertEquals(expectedResult, result); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java b/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java new file mode 100644 index 00000000..314cba9c --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java @@ -0,0 +1,50 @@ +package backtraceio.coroner.serialization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; + +import backtraceio.coroner.response.CoronerApiResponse; +import backtraceio.coroner.response.CoronerResponseGroup; + + +public class GsonWrapperTest { + + private Object getResponseGroupAttributeValue(Object attribute) { + return ((List) attribute).get(0); + } + + public String readResourceFile(String path) throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(path)); + return new String(bytes); + } + + @Test + public void test() throws IOException{ + // GIVEN + final String json = readResourceFile("src/test/resources/backtraceio/coroner/sample-coroner-response.json"); + + // WHEN + final CoronerApiResponse result = GsonWrapper.fromJson(json, CoronerApiResponse.class); + + // THEN + assertNotNull(result); + assertNull(result.error); + assertEquals(1, result.response.values.size()); + + CoronerResponseGroup responseGroup = result.response.values.get(0); + + assertEquals("Invalid index of selected element!", getResponseGroupAttributeValue(responseGroup.getAttribute(0))); + assertEquals("{\"frame\":[\"backtraceio.backtraceio.MainActivity.handledException\",\"androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick\",\"android.view.View.performClick\",\"android.view.View.performClickInternal\",\"android.view.View.access$3600\",\"android.view.View$PerformClick.run\",\"androidx.test.espresso.base.Interrogator.loopAndInterrogate\",\"androidx.test.espresso.base.UiControllerImpl.loopUntil\",\"androidx.test.espresso.base.UiControllerImpl.injectMotionEvent\",\"androidx.test.espresso.action.MotionEvents.sendUp\",\"androidx.test.espresso.action.Tap.sendSingleTap\",\"androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap\",\"androidx.test.espresso.action.Tap$1.sendTap\",\"androidx.test.espresso.action.GeneralClickAction.perform\",\"androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform\",\"androidx.test.espresso.ViewInteraction.doPerform\",\"androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform\",\"androidx.test.espresso.ViewInteraction$1.call\",\"java.util.concurrent.FutureTask.run\",\"android.os.Handler.handleCallback\",\"android.os.Handler.dispatchMessage\",\"android.os.Looper.loop\",\"android.app.ActivityThread.main\",\"java.lang.reflect.Method.invoke\",\"com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run\",\"com.android.internal.os.ZygoteInit.main\"]}", getResponseGroupAttributeValue(responseGroup.getAttribute(1))); + assertEquals("e4c57699-0dc9-35e2-b4a0-2ffff1925ca7", getResponseGroupAttributeValue(responseGroup.getAttribute(2))); + assertEquals("java.lang.IndexOutOfBoundsException", getResponseGroupAttributeValue(responseGroup.getAttribute(3))); + } +} diff --git a/coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json b/coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json new file mode 100644 index 00000000..546dd035 --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json @@ -0,0 +1 @@ +{"_" : {"user":"(nouser)","project":"android-library","tx":9444,"universe":"yolo","latency":"306 us","runtime":{"filter": {"time": 78, "rows": 1, "n_filters": 1, "breakdown": [71]}, "group_by": {"time": 3, "groups": 1, "rows": 1}, "having": {"time": 0, "ungroup": 0, "groups": 1, "rows": 1}, "aggregate": {"pre_having": 0, "pre_sort": 1, "post_sort": 28, "n_aggregates": 4, "breakdown": [12, 3, 2, 2]}, "sort": {"time": 1, "groups": 1, "rows": 1}, "render": {"time": 37}, "set": {"time": 0}, "merge": {"time": 0}, "ungrouped": {"time": 0, "having": 0, "subqueries":[]}, "total_time": 111, "toplevel": {"parse_end": 31, "execute_begin": 32, "execute_end": 184, "print_begin": 185, "print_end": 222}},"navigation":["blackhole","globalscape","benchmark","minedraft","csharp","bzflag","minetest","rptest","crashpad","electron","mbelectron","dashboard","unrealengine","metrics","symbtest","WorkflowTester","testing-123","testRunner","ErrorGenerator","arthur","tokenrove","xone","synergy","arm","mbunreal","hsowa","QAss","golang","spotify","jdv","ir-test","jsontest","jdavis","sbahra","amadden","testing-upload","hsowa2","hsowa3","inbox","on-boarding-2","onboarding-test","lol","krzaq","sentry_comparison","sentry_comparison_no_symbols","sentry_comparison_no_cfi","asd-wf","mbcrashpad","qq","qweqwqweeqwewqeqwqweqwqweqweqweqweqweqweqweqweqweqweqweqweqweqweweqweqweqwewewq","mbcsharp","android-library","cocoa","CiscoGO","DOtesting","cocoa-dsym-test","shawn","boooooop","baba cool","sedkins","kdysput","hello","hello world","hello weeeeeeeee","testingboop","testingBoooooop","testboooop","testingagain","testingboopsweep","booop swee","bonksdasdf","OrthoClin","testing-boop","testing-boop-01","symbold","S+)(*&^%$#@","mela","hsowa4","symbold2-test","symbold2-submit-test","cocoa-crashpad","kaori","proguard","testsymboldbug","SCA","mbandroid","hooks","sourcemap","stadia","stadia-dev","ahicks","ExampleProject","sourcemap-test","mrashkovsky","PE","PE-Corr","bobby-test","Unity","mrashkovsky2","mlaws-onboarding","nhendrickson","testing-a-hyphen","Playstation","PlaystationDev","dropwizard-example-log4j12","settings","ps4handofftest","covid-19","nhendrickson-submit-test","nhendrickson3","kaorii","dsfsfs","dfgfdgfd","kaori-2","nhendrickson-submit-test-2","sinespace-test","mbcomcast","CharlieACLsAlot","stopMakingProjects","nhendrickson-unreal-test","gpu-dumps","123-123","poopladoopdoop","submit-client-ip-test","mojibake-utf8-test","vmprotect","kaori-likes-sourcecode","nsculli-1","rqurashi","daniel-sandbox","nvidia","mantis-test","NewProj","NewProj2","drake-test","salex","TestProjekt","stone","UnityTests","acronin","gmenhorn","wbormans","something","sauce","jpihlaja","meenup","unity-test-app","ohouchard","cube2-sauerbraten","connector-scale","ExampleForAttributesQ","cstk-prev","cstk-curr","first-errors","aibrahim","workflows-test","TryNuking","rfoster","backtrace_demo","_backtrace_demo_project","~backtrace demo project~","wandrews","testhades","~backtrace_demo_project_unity","backtrace-demo-project--unity","mbcrashme","cube2-suarebraten-no-orig","jshufro","ExampleNewProjectName","ExampleNewProjectName2","jestrada"]},"response":{"version":"1.2.0","seq":12,"encoding":"rle","columns":[["head(error.message)",""],["head(callstack)","callstack"],["head(guid)","uuid"],["head(classifiers)","labels"]],"columns_desc":[{"name":"error.message","format":"","type":"dictionary", "op": "head"},{"name":"callstack","format":"callstack","type":"dictionary", "op": "head"},{"name":"guid","format":"uuid","type":"uuid", "op": "head"},{"name":"classifiers","format":"labels","type":"dictionary", "op": "head"}],"pagination":{"limit": 1,"offset": 0},"values":[["*",[["Invalid index of selected element!"],["{\"frame\":[\"backtraceio.backtraceio.MainActivity.handledException\",\"androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick\",\"android.view.View.performClick\",\"android.view.View.performClickInternal\",\"android.view.View.access$3600\",\"android.view.View$PerformClick.run\",\"androidx.test.espresso.base.Interrogator.loopAndInterrogate\",\"androidx.test.espresso.base.UiControllerImpl.loopUntil\",\"androidx.test.espresso.base.UiControllerImpl.injectMotionEvent\",\"androidx.test.espresso.action.MotionEvents.sendUp\",\"androidx.test.espresso.action.Tap.sendSingleTap\",\"androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap\",\"androidx.test.espresso.action.Tap$1.sendTap\",\"androidx.test.espresso.action.GeneralClickAction.perform\",\"androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform\",\"androidx.test.espresso.ViewInteraction.doPerform\",\"androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform\",\"androidx.test.espresso.ViewInteraction$1.call\",\"java.util.concurrent.FutureTask.run\",\"android.os.Handler.handleCallback\",\"android.os.Handler.dispatchMessage\",\"android.os.Looper.loop\",\"android.app.ActivityThread.main\",\"java.lang.reflect.Method.invoke\",\"com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run\",\"com.android.internal.os.ZygoteInit.main\"]}"],["e4c57699-0dc9-35e2-b4a0-2ffff1925ca7"],["java.lang.IndexOutOfBoundsException"]],1]],"cardinalities": {"initial": {"rows": 1, "groups": 1}, "having": {"rows": 1, "groups": 1}, "pagination": {"rows": 1, "groups": 1}}}} \ No newline at end of file From 5252466dc74b01542ce5fba3e272d60e8ee1722e Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Wed, 12 Apr 2023 00:15:13 +0200 Subject: [PATCH 44/61] Add unit tests --- coroner-client/build.gradle | 2 +- .../backtraceio/coroner/CoronerClient.java | 13 +- .../coroner/CoronerHttpClient.java | 3 +- .../coroner/common/HttpClient.java | 10 ++ .../coroner/response/CoronerApiResponse.java | 22 +-- .../coroner/CoronerClientTest.java | 131 +++++++++++++++ .../query/CoronerFiltersBuilderTest.java | 2 +- .../coroner/query/CoronerQueriesTest.java | 2 +- .../query/CoronerQueryBuilderTest.java | 2 +- .../CoronerResponseGroupDeserializerTest.java | 29 ++++ .../serialization/GsonWrapperTest.java | 23 +-- .../coroner/utils/MockHttpClient.java | 15 ++ .../coroner/utils/ResourceUtils.java | 22 +++ .../coroner/{ => utils}/StringUtils.java | 2 +- .../rxid-12345-custom-attr-err-msg.json | 1 + .../coroner/queries/rxid-12345.json | 1 + .../queries/timestamp-err-type-filter.json | 1 + .../coroner/responses/operation-error.json | 6 + .../coroner/responses/rx-filter-response.json | 157 +++++++++++++++++ .../responses/timestamp-err-type-filter.json | 159 ++++++++++++++++++ .../coroner/sample-coroner-response.json | 1 - 21 files changed, 569 insertions(+), 35 deletions(-) create mode 100644 coroner-client/src/main/java/backtraceio/coroner/common/HttpClient.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializerTest.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/utils/MockHttpClient.java create mode 100644 coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java rename coroner-client/src/test/java/backtraceio/coroner/{ => utils}/StringUtils.java (80%) create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/responses/operation-error.json create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/responses/rx-filter-response.json create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/responses/timestamp-err-type-filter.json delete mode 100644 coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json diff --git a/coroner-client/build.gradle b/coroner-client/build.gradle index da85e413..79ded59f 100644 --- a/coroner-client/build.gradle +++ b/coroner-client/build.gradle @@ -11,5 +11,5 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.google.code.gson:gson:2.10' testImplementation 'junit:junit:4.13.2' -// androidTestImplementation 'junit:junit:4.13.2' + testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.2.0' } \ No newline at end of file diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 69a08551..258d9e6c 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.List; +import backtraceio.coroner.common.HttpClient; import backtraceio.coroner.query.CoronerQueries; import backtraceio.coroner.query.CoronerQueryFields; import backtraceio.coroner.response.CoronerApiResponse; @@ -13,8 +14,8 @@ import backtraceio.coroner.response.CoronerResponseException; public class CoronerClient { - public final CoronerHttpClient coronerHttpClient; - public final CoronerQueries coronerQueries; + private final HttpClient coronerHttpClient; + private final CoronerQueries coronerQueries; private final List DEFAULT_ATTRIBUTES = Arrays.asList( CoronerQueryFields.FOLD_CALLSTACK, CoronerQueryFields.FOLD_GUID, @@ -22,11 +23,15 @@ public class CoronerClient { ); public CoronerClient(final String apiUrl, final String coronerToken) { - this.coronerHttpClient = new CoronerHttpClient(apiUrl, coronerToken); + this(new CoronerHttpClient(apiUrl, coronerToken)); + } + + public CoronerClient(HttpClient httpClient) { + this.coronerHttpClient = httpClient; this.coronerQueries = new CoronerQueries(); } - public CoronerResponse rxIdFilter(final String rxId) throws Exception { + public CoronerResponse rxIdFilter(final String rxId) throws CoronerResponseException, CoronerHttpException, IOException { return this.rxIdFilter(rxId, new ArrayList<>()); } diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index 93b25f58..bd8569d7 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -11,12 +11,13 @@ import java.util.logging.Logger; import backtraceio.coroner.common.Common; +import backtraceio.coroner.common.HttpClient; import backtraceio.coroner.response.CoronerApiResponse; import backtraceio.coroner.response.CoronerHttpException; import backtraceio.coroner.serialization.CoronerResponseGroupDeserializer; import backtraceio.coroner.serialization.GsonWrapper; -class CoronerHttpClient { +class CoronerHttpClient implements HttpClient { private static final Logger LOGGER = Logger.getLogger(CoronerResponseGroupDeserializer.class.getName()); private final String apiUrl; private final String coronerToken; diff --git a/coroner-client/src/main/java/backtraceio/coroner/common/HttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/common/HttpClient.java new file mode 100644 index 00000000..310603a0 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/common/HttpClient.java @@ -0,0 +1,10 @@ +package backtraceio.coroner.common; + +import java.io.IOException; + +import backtraceio.coroner.response.CoronerApiResponse; +import backtraceio.coroner.response.CoronerHttpException; + +public interface HttpClient { + CoronerApiResponse get(final String requestJson) throws CoronerHttpException, IOException; +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java index 88e0592d..fa67486d 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerApiResponse.java @@ -3,8 +3,9 @@ import backtraceio.coroner.CoronerError; public class CoronerApiResponse { - public CoronerError error; public CoronerResponse response; + public CoronerError error; + @SuppressWarnings("unused") public CoronerApiResponse() { @@ -12,8 +13,17 @@ public CoronerApiResponse() { } @SuppressWarnings("unused") - public CoronerApiResponse(final CoronerError error, final CoronerResponse response) { + public CoronerApiResponse(final CoronerResponse response, final CoronerError error) { + this.response = response; this.error = error; + } + + public CoronerResponse getResponse() { + return this.response; + } + + @SuppressWarnings("unused") + public void setResponse(CoronerResponse response) { this.response = response; } @@ -26,13 +36,5 @@ public void setError(CoronerError error) { this.error = error; } - public CoronerResponse getResponse() { - return this.response; - } - - @SuppressWarnings("unused") - public void setResponse(CoronerResponse response) { - this.response = response; - } } diff --git a/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java b/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java new file mode 100644 index 00000000..ca1ceccd --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java @@ -0,0 +1,131 @@ +package backtraceio.coroner; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import backtraceio.coroner.response.CoronerApiResponse; +import backtraceio.coroner.response.CoronerHttpException; +import backtraceio.coroner.response.CoronerResponse; +import backtraceio.coroner.response.CoronerResponseException; +import backtraceio.coroner.response.CoronerResponseProcessingException; +import backtraceio.coroner.serialization.GsonWrapper; +import backtraceio.coroner.utils.MockHttpClient; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.*; +import static backtraceio.coroner.utils.ResourceUtils.QUERY_CORONER_RXID_123; +import static backtraceio.coroner.utils.ResourceUtils.QUERY_CORONER_RXID_123_ATTR_ERR_MSG; +import static backtraceio.coroner.utils.ResourceUtils.QUERY_CORONER_TIMESTAMP_ERR_TYPE; +import static backtraceio.coroner.utils.ResourceUtils.RESPONSE_OPERATION_ERROR_JSON; +import static backtraceio.coroner.utils.ResourceUtils.RESPONSE_RX_FILTER_CORONER_JSON; +import static backtraceio.coroner.utils.ResourceUtils.RESPONSE_TIMESTAMP_ERR_TYPE_CORONER_JSON; +import static backtraceio.coroner.utils.ResourceUtils.readResourceFile; + +public class CoronerClientTest { + private final static String rxId = "12345"; + private MockHttpClient mockHttpClient; + private CoronerClient client; + + @Before + public void init() { + this.mockHttpClient = mock(MockHttpClient.class); + this.client = new CoronerClient(mockHttpClient); + } + + @Test + public void rxIdFilter() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { // todo: replace method name + // GIVEN + final String expectedJsonQuery = readResourceFile(QUERY_CORONER_RXID_123); + final String jsonResponse = readResourceFile(RESPONSE_RX_FILTER_CORONER_JSON); + + final CoronerApiResponse expectedResponse = GsonWrapper.fromJson(jsonResponse, CoronerApiResponse.class); + + // MOCK + when(mockHttpClient.get(Mockito.contains(expectedJsonQuery))).thenReturn(expectedResponse); + + // WHEN + final CoronerResponse result = client.rxIdFilter(rxId); + + // THEN + assertNotNull(result); + assertEquals(1, result.getResultsNumber()); + assertEquals("Invalid index of selected element!", result.getAttribute(0, "error.message", String.class)); + } + + @Test + public void rxIdFilterAttributes() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { // todo: replace method name + // GIVEN + final String expectedJsonQuery = readResourceFile(QUERY_CORONER_RXID_123_ATTR_ERR_MSG); + final String jsonResponse = readResourceFile(RESPONSE_RX_FILTER_CORONER_JSON); + final List customAttributes = Arrays.asList("error.message"); + final CoronerApiResponse expectedResponse = GsonWrapper.fromJson(jsonResponse, CoronerApiResponse.class); + + // MOCK + when(mockHttpClient.get(Mockito.contains(expectedJsonQuery))).thenReturn(expectedResponse); + + // WHEN + final CoronerResponse result = client.rxIdFilter(rxId, customAttributes); + + // THEN + assertNotNull(result); + assertEquals(1, result.getResultsNumber()); + assertEquals("Invalid index of selected element!", result.getAttribute(0, "error.message", String.class)); + } + + @Test + public void errorTypeTimestampFilter() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { + // GIVEN + final String expectedJsonQuery = readResourceFile(QUERY_CORONER_TIMESTAMP_ERR_TYPE); + final String jsonResponse = readResourceFile(RESPONSE_TIMESTAMP_ERR_TYPE_CORONER_JSON); + final CoronerApiResponse expectedResponse = GsonWrapper.fromJson(jsonResponse, CoronerApiResponse.class); + + final String errorType = "Crash"; + final long timestampStart = 0L; + final long timestampEnd = 10000000000L; + final List customAttributes = Arrays.asList("error.message"); + + // MOCK + when(mockHttpClient.get(Mockito.contains(expectedJsonQuery))).thenReturn(expectedResponse); + + // WHEN + final CoronerResponse result = client.errorTypeTimestampFilter(errorType, + Long.toString(timestampStart), + Long.toString(timestampEnd), + customAttributes); + + // THEN + assertNotNull(result); + assertEquals(1, result.getResultsNumber()); + assertEquals("exception", result.getAttribute(0, "classifiers", String.class)); + assertEquals("DumpWithoutCrash", result.getAttribute(0, "error.message", String.class)); + } + + @Test + public void errorResponseTest() throws IOException, CoronerHttpException, CoronerResponseException { + // GIVEN + final String jsonResponse = readResourceFile(RESPONSE_OPERATION_ERROR_JSON); + final CoronerApiResponse expectedResponse = GsonWrapper.fromJson(jsonResponse, CoronerApiResponse.class); + final String errorMessage = "empty body"; + + // MOCK + when(mockHttpClient.get(Mockito.anyString())).thenReturn(expectedResponse); + + // WHEN + try { + client.rxIdFilter(rxId); + } + catch (CoronerResponseException exception) { + // THEN + assertEquals(errorMessage, exception.getMessage()); + assertNotNull(exception.getCoronerError()); + assertEquals(errorMessage, exception.getCoronerError().getMessage()); + assertEquals(32769, exception.getCoronerError().getCode()); + } + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java index 78bbd135..353c674d 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java @@ -4,7 +4,7 @@ import org.junit.Test; -import backtraceio.coroner.StringUtils; +import backtraceio.coroner.utils.StringUtils; public class CoronerFiltersBuilderTest { diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java index c177bb97..2fbe79ab 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java @@ -8,7 +8,7 @@ import java.util.Arrays; import java.util.List; -import backtraceio.coroner.StringUtils; +import backtraceio.coroner.utils.StringUtils; public class CoronerQueriesTest { public CoronerQueries coronerQueries; diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java index ac7afca6..8636a102 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java @@ -7,7 +7,7 @@ import java.util.Arrays; import java.util.List; -import backtraceio.coroner.StringUtils; +import backtraceio.coroner.utils.StringUtils; public class CoronerQueryBuilderTest { diff --git a/coroner-client/src/test/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializerTest.java b/coroner-client/src/test/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializerTest.java new file mode 100644 index 00000000..147a81b6 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/serialization/CoronerResponseGroupDeserializerTest.java @@ -0,0 +1,29 @@ +package backtraceio.coroner.serialization; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +import org.junit.Test; + + +import backtraceio.coroner.response.CoronerResponseGroup; + +public class CoronerResponseGroupDeserializerTest { + + @Test + public void invalidJsonDeserialization() { + // GIVEN + final CoronerResponseGroupDeserializer deserializer = new CoronerResponseGroupDeserializer(); + final JsonElement jsonElement = JsonParser.parseString("[]").getAsJsonArray(); + + // WHEN + final CoronerResponseGroup result = deserializer.deserialize(jsonElement, CoronerResponseGroup.class, null); + + // THEN + assertNull(result); + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java b/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java index 314cba9c..5af2ba4d 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java @@ -4,12 +4,12 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static backtraceio.coroner.utils.ResourceUtils.RESPONSE_RX_FILTER_CORONER_JSON; +import static backtraceio.coroner.utils.ResourceUtils.readResourceFile; + import org.junit.Test; -import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; import backtraceio.coroner.response.CoronerApiResponse; @@ -22,26 +22,21 @@ private Object getResponseGroupAttributeValue(Object attribute) { return ((List) attribute).get(0); } - public String readResourceFile(String path) throws IOException { - byte[] bytes = Files.readAllBytes(Paths.get(path)); - return new String(bytes); - } - @Test - public void test() throws IOException{ + public void deserializeApiResponse() throws IOException{ // GIVEN - final String json = readResourceFile("src/test/resources/backtraceio/coroner/sample-coroner-response.json"); + final String json = readResourceFile(RESPONSE_RX_FILTER_CORONER_JSON); // WHEN final CoronerApiResponse result = GsonWrapper.fromJson(json, CoronerApiResponse.class); // THEN assertNotNull(result); - assertNull(result.error); - assertEquals(1, result.response.values.size()); - - CoronerResponseGroup responseGroup = result.response.values.get(0); + assertNull(result.getError()); + assertNotNull(result.getResponse()); + assertEquals(1, result.getResponse().getResultsNumber()); + CoronerResponseGroup responseGroup = result.getResponse().values.get(0); assertEquals("Invalid index of selected element!", getResponseGroupAttributeValue(responseGroup.getAttribute(0))); assertEquals("{\"frame\":[\"backtraceio.backtraceio.MainActivity.handledException\",\"androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick\",\"android.view.View.performClick\",\"android.view.View.performClickInternal\",\"android.view.View.access$3600\",\"android.view.View$PerformClick.run\",\"androidx.test.espresso.base.Interrogator.loopAndInterrogate\",\"androidx.test.espresso.base.UiControllerImpl.loopUntil\",\"androidx.test.espresso.base.UiControllerImpl.injectMotionEvent\",\"androidx.test.espresso.action.MotionEvents.sendUp\",\"androidx.test.espresso.action.Tap.sendSingleTap\",\"androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap\",\"androidx.test.espresso.action.Tap$1.sendTap\",\"androidx.test.espresso.action.GeneralClickAction.perform\",\"androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform\",\"androidx.test.espresso.ViewInteraction.doPerform\",\"androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform\",\"androidx.test.espresso.ViewInteraction$1.call\",\"java.util.concurrent.FutureTask.run\",\"android.os.Handler.handleCallback\",\"android.os.Handler.dispatchMessage\",\"android.os.Looper.loop\",\"android.app.ActivityThread.main\",\"java.lang.reflect.Method.invoke\",\"com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run\",\"com.android.internal.os.ZygoteInit.main\"]}", getResponseGroupAttributeValue(responseGroup.getAttribute(1))); assertEquals("e4c57699-0dc9-35e2-b4a0-2ffff1925ca7", getResponseGroupAttributeValue(responseGroup.getAttribute(2))); diff --git a/coroner-client/src/test/java/backtraceio/coroner/utils/MockHttpClient.java b/coroner-client/src/test/java/backtraceio/coroner/utils/MockHttpClient.java new file mode 100644 index 00000000..264562b4 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/utils/MockHttpClient.java @@ -0,0 +1,15 @@ +package backtraceio.coroner.utils; + +import java.io.IOException; + +import backtraceio.coroner.common.HttpClient; +import backtraceio.coroner.response.CoronerApiResponse; +import backtraceio.coroner.response.CoronerHttpException; + +public class MockHttpClient implements HttpClient { + + @Override + public CoronerApiResponse get(String requestJson) throws CoronerHttpException, IOException { + return null; + } +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java b/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java new file mode 100644 index 00000000..009d8047 --- /dev/null +++ b/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java @@ -0,0 +1,22 @@ +package backtraceio.coroner.utils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class ResourceUtils { + private static final String MAIN_PATH = "src/test/resources/backtraceio/coroner"; + public static final String QUERY_CORONER_RXID_123 = MAIN_PATH + "/queries/rxid-12345.json"; + public static final String QUERY_CORONER_RXID_123_ATTR_ERR_MSG = MAIN_PATH + "/queries/rxid-12345-custom-attr-err-msg.json"; + public static final String QUERY_CORONER_TIMESTAMP_ERR_TYPE= MAIN_PATH + "/queries/timestamp-err-type-filter.json"; + public static final String RESPONSE_RX_FILTER_CORONER_JSON = MAIN_PATH + "/responses/rx-filter-response.json"; + public static final String RESPONSE_TIMESTAMP_ERR_TYPE_CORONER_JSON = MAIN_PATH + "/responses/timestamp-err-type-filter.json"; + + public static final String RESPONSE_OPERATION_ERROR_JSON = MAIN_PATH + "/responses/operation-error.json"; + + public static String readResourceFile(String path) throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(path)); + return new String(bytes); + } + +} diff --git a/coroner-client/src/test/java/backtraceio/coroner/StringUtils.java b/coroner-client/src/test/java/backtraceio/coroner/utils/StringUtils.java similarity index 80% rename from coroner-client/src/test/java/backtraceio/coroner/StringUtils.java rename to coroner-client/src/test/java/backtraceio/coroner/utils/StringUtils.java index 3fc4ad20..b0ac4b2e 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/StringUtils.java +++ b/coroner-client/src/test/java/backtraceio/coroner/utils/StringUtils.java @@ -1,4 +1,4 @@ -package backtraceio.coroner; +package backtraceio.coroner.utils; public class StringUtils { public static String normalizeSpace(String input) { diff --git a/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json new file mode 100644 index 00000000..f05bbd1a --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json @@ -0,0 +1 @@ +{"group":[ ["_rxid"]],"fold": {"error.message": [["head"]],"callstack": [["head"]],"guid": [["head"]],"classifiers": [["head"]]}, "offset":0, "limit":1, "filter":[{ "_rxid": [[ "equal", "12345" ]] }]} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json new file mode 100644 index 00000000..9dcbb28d --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json @@ -0,0 +1 @@ +{"group":[ ["_rxid"]],"fold": {"callstack": [["head"]],"guid": [["head"]],"classifiers": [["head"]]}, "offset":0, "limit":1, "filter":[{ "_rxid": [[ "equal", "12345" ]] }]} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json b/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json new file mode 100644 index 00000000..f2a0debc --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json @@ -0,0 +1 @@ +{"group":[ ["_rxid"]],"fold": {"error.message": [["head"]],"callstack": [["head"]],"guid": [["head"]],"classifiers": [["head"]]}, "offset":0, "limit":1, "filter":[{ "error.type": [[ "equal", "Crash" ]],"timestamp": [[ "at-least", "0." ],[ "at-most", "10000000000." ]] }]} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/responses/operation-error.json b/coroner-client/src/test/resources/backtraceio/coroner/responses/operation-error.json new file mode 100644 index 00000000..a8b78d7b --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/responses/operation-error.json @@ -0,0 +1,6 @@ +{ + "error": { + "code": 32769, + "message": "empty body" + } +} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/responses/rx-filter-response.json b/coroner-client/src/test/resources/backtraceio/coroner/responses/rx-filter-response.json new file mode 100644 index 00000000..2d88b22e --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/responses/rx-filter-response.json @@ -0,0 +1,157 @@ +{ + "_": { + "user": "test", + "project": "test", + "tx": 9444, + "universe": "", + "latency": "306 us", + "runtime": { + "filter": { + "time": 78, + "rows": 1, + "n_filters": 1, + "breakdown": [ + 71 + ] + }, + "group_by": { + "time": 3, + "groups": 1, + "rows": 1 + }, + "having": { + "time": 0, + "ungroup": 0, + "groups": 1, + "rows": 1 + }, + "aggregate": { + "pre_having": 0, + "pre_sort": 1, + "post_sort": 28, + "n_aggregates": 4, + "breakdown": [ + 12, + 3, + 2, + 2 + ] + }, + "sort": { + "time": 1, + "groups": 1, + "rows": 1 + }, + "render": { + "time": 37 + }, + "set": { + "time": 0 + }, + "merge": { + "time": 0 + }, + "ungrouped": { + "time": 0, + "having": 0, + "subqueries": [] + }, + "total_time": 111, + "toplevel": { + "parse_end": 31, + "execute_begin": 32, + "execute_end": 184, + "print_begin": 185, + "print_end": 222 + } + }, + "navigation": [] + }, + "response": { + "version": "1.2.0", + "seq": 12, + "encoding": "rle", + "columns": [ + [ + "head(error.message)", + "" + ], + [ + "head(callstack)", + "callstack" + ], + [ + "head(guid)", + "uuid" + ], + [ + "head(classifiers)", + "labels" + ] + ], + "columns_desc": [ + { + "name": "error.message", + "format": "", + "type": "dictionary", + "op": "head" + }, + { + "name": "callstack", + "format": "callstack", + "type": "dictionary", + "op": "head" + }, + { + "name": "guid", + "format": "uuid", + "type": "uuid", + "op": "head" + }, + { + "name": "classifiers", + "format": "labels", + "type": "dictionary", + "op": "head" + } + ], + "pagination": { + "limit": 1, + "offset": 0 + }, + "values": [ + [ + "*", + [ + [ + "Invalid index of selected element!" + ], + [ + "{\"frame\":[\"backtraceio.backtraceio.MainActivity.handledException\",\"androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick\",\"android.view.View.performClick\",\"android.view.View.performClickInternal\",\"android.view.View.access$3600\",\"android.view.View$PerformClick.run\",\"androidx.test.espresso.base.Interrogator.loopAndInterrogate\",\"androidx.test.espresso.base.UiControllerImpl.loopUntil\",\"androidx.test.espresso.base.UiControllerImpl.injectMotionEvent\",\"androidx.test.espresso.action.MotionEvents.sendUp\",\"androidx.test.espresso.action.Tap.sendSingleTap\",\"androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap\",\"androidx.test.espresso.action.Tap$1.sendTap\",\"androidx.test.espresso.action.GeneralClickAction.perform\",\"androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform\",\"androidx.test.espresso.ViewInteraction.doPerform\",\"androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform\",\"androidx.test.espresso.ViewInteraction$1.call\",\"java.util.concurrent.FutureTask.run\",\"android.os.Handler.handleCallback\",\"android.os.Handler.dispatchMessage\",\"android.os.Looper.loop\",\"android.app.ActivityThread.main\",\"java.lang.reflect.Method.invoke\",\"com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run\",\"com.android.internal.os.ZygoteInit.main\"]}" + ], + [ + "e4c57699-0dc9-35e2-b4a0-2ffff1925ca7" + ], + [ + "java.lang.IndexOutOfBoundsException" + ] + ], + 1 + ] + ], + "cardinalities": { + "initial": { + "rows": 1, + "groups": 1 + }, + "having": { + "rows": 1, + "groups": 1 + }, + "pagination": { + "rows": 1, + "groups": 1 + } + } + } +} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/responses/timestamp-err-type-filter.json b/coroner-client/src/test/resources/backtraceio/coroner/responses/timestamp-err-type-filter.json new file mode 100644 index 00000000..3dfcc809 --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/responses/timestamp-err-type-filter.json @@ -0,0 +1,159 @@ +{ + "_": { + "user": "test", + "project": "test", + "tx": 9493, + "universe": "", + "latency": "226 us", + "runtime": { + "filter": { + "time": 44, + "rows": 818, + "n_filters": 3, + "breakdown": [ + 28, + 4, + 2 + ] + }, + "group_by": { + "time": 7, + "groups": 1, + "rows": 818 + }, + "having": { + "time": 0, + "ungroup": 0, + "groups": 1, + "rows": 818 + }, + "aggregate": { + "pre_having": 0, + "pre_sort": 0, + "post_sort": 24, + "n_aggregates": 4, + "breakdown": [ + 2, + 3, + 11, + 1 + ] + }, + "sort": { + "time": 1, + "groups": 1, + "rows": 818 + }, + "render": { + "time": 29 + }, + "set": { + "time": 0 + }, + "merge": { + "time": 0 + }, + "ungrouped": { + "time": 0, + "having": 0, + "subqueries": [] + }, + "total_time": 76, + "toplevel": { + "parse_end": 29, + "execute_begin": 29, + "execute_end": 112, + "print_begin": 112, + "print_end": 142 + } + }, + "navigation": [] + }, + "response": { + "version": "1.2.0", + "seq": 1, + "encoding": "rle", + "columns": [ + [ + "head(error.message)", + "" + ], + [ + "head(callstack)", + "callstack" + ], + [ + "head(guid)", + "uuid" + ], + [ + "head(classifiers)", + "labels" + ] + ], + "columns_desc": [ + { + "name": "error.message", + "format": "", + "type": "dictionary", + "op": "head" + }, + { + "name": "callstack", + "format": "callstack", + "type": "dictionary", + "op": "head" + }, + { + "name": "guid", + "format": "uuid", + "type": "uuid", + "op": "head" + }, + { + "name": "classifiers", + "format": "labels", + "type": "dictionary", + "op": "head" + } + ], + "pagination": { + "limit": 1, + "offset": 0 + }, + "values": [ + [ + "*", + [ + [ + "DumpWithoutCrash" + ], + [ + "{\"frame\":[\"libbacktrace-native.so\",\"libbacktrace-native.so\",\"libart.so\",\"libc.so\",\"libbacktrace-native.so\",\"libbacktrace-native.so\",\"libart.so\",\"libbacktrace-native.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libc.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\",\"libart.so\"]}" + ], + [ + "71baf2e3-0fe3-48fa-ba18-d2b3d98d1e75" + ], + [ + "exception" + ] + ], + 818 + ] + ], + "cardinalities": { + "initial": { + "rows": 818, + "groups": 1 + }, + "having": { + "rows": 818, + "groups": 1 + }, + "pagination": { + "rows": 818, + "groups": 1 + } + } + } +} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json b/coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json deleted file mode 100644 index 546dd035..00000000 --- a/coroner-client/src/test/resources/backtraceio/coroner/sample-coroner-response.json +++ /dev/null @@ -1 +0,0 @@ -{"_" : {"user":"(nouser)","project":"android-library","tx":9444,"universe":"yolo","latency":"306 us","runtime":{"filter": {"time": 78, "rows": 1, "n_filters": 1, "breakdown": [71]}, "group_by": {"time": 3, "groups": 1, "rows": 1}, "having": {"time": 0, "ungroup": 0, "groups": 1, "rows": 1}, "aggregate": {"pre_having": 0, "pre_sort": 1, "post_sort": 28, "n_aggregates": 4, "breakdown": [12, 3, 2, 2]}, "sort": {"time": 1, "groups": 1, "rows": 1}, "render": {"time": 37}, "set": {"time": 0}, "merge": {"time": 0}, "ungrouped": {"time": 0, "having": 0, "subqueries":[]}, "total_time": 111, "toplevel": {"parse_end": 31, "execute_begin": 32, "execute_end": 184, "print_begin": 185, "print_end": 222}},"navigation":["blackhole","globalscape","benchmark","minedraft","csharp","bzflag","minetest","rptest","crashpad","electron","mbelectron","dashboard","unrealengine","metrics","symbtest","WorkflowTester","testing-123","testRunner","ErrorGenerator","arthur","tokenrove","xone","synergy","arm","mbunreal","hsowa","QAss","golang","spotify","jdv","ir-test","jsontest","jdavis","sbahra","amadden","testing-upload","hsowa2","hsowa3","inbox","on-boarding-2","onboarding-test","lol","krzaq","sentry_comparison","sentry_comparison_no_symbols","sentry_comparison_no_cfi","asd-wf","mbcrashpad","qq","qweqwqweeqwewqeqwqweqwqweqweqweqweqweqweqweqweqweqweqweqweqweqweweqweqweqwewewq","mbcsharp","android-library","cocoa","CiscoGO","DOtesting","cocoa-dsym-test","shawn","boooooop","baba cool","sedkins","kdysput","hello","hello world","hello weeeeeeeee","testingboop","testingBoooooop","testboooop","testingagain","testingboopsweep","booop swee","bonksdasdf","OrthoClin","testing-boop","testing-boop-01","symbold","S+)(*&^%$#@","mela","hsowa4","symbold2-test","symbold2-submit-test","cocoa-crashpad","kaori","proguard","testsymboldbug","SCA","mbandroid","hooks","sourcemap","stadia","stadia-dev","ahicks","ExampleProject","sourcemap-test","mrashkovsky","PE","PE-Corr","bobby-test","Unity","mrashkovsky2","mlaws-onboarding","nhendrickson","testing-a-hyphen","Playstation","PlaystationDev","dropwizard-example-log4j12","settings","ps4handofftest","covid-19","nhendrickson-submit-test","nhendrickson3","kaorii","dsfsfs","dfgfdgfd","kaori-2","nhendrickson-submit-test-2","sinespace-test","mbcomcast","CharlieACLsAlot","stopMakingProjects","nhendrickson-unreal-test","gpu-dumps","123-123","poopladoopdoop","submit-client-ip-test","mojibake-utf8-test","vmprotect","kaori-likes-sourcecode","nsculli-1","rqurashi","daniel-sandbox","nvidia","mantis-test","NewProj","NewProj2","drake-test","salex","TestProjekt","stone","UnityTests","acronin","gmenhorn","wbormans","something","sauce","jpihlaja","meenup","unity-test-app","ohouchard","cube2-sauerbraten","connector-scale","ExampleForAttributesQ","cstk-prev","cstk-curr","first-errors","aibrahim","workflows-test","TryNuking","rfoster","backtrace_demo","_backtrace_demo_project","~backtrace demo project~","wandrews","testhades","~backtrace_demo_project_unity","backtrace-demo-project--unity","mbcrashme","cube2-suarebraten-no-orig","jshufro","ExampleNewProjectName","ExampleNewProjectName2","jestrada"]},"response":{"version":"1.2.0","seq":12,"encoding":"rle","columns":[["head(error.message)",""],["head(callstack)","callstack"],["head(guid)","uuid"],["head(classifiers)","labels"]],"columns_desc":[{"name":"error.message","format":"","type":"dictionary", "op": "head"},{"name":"callstack","format":"callstack","type":"dictionary", "op": "head"},{"name":"guid","format":"uuid","type":"uuid", "op": "head"},{"name":"classifiers","format":"labels","type":"dictionary", "op": "head"}],"pagination":{"limit": 1,"offset": 0},"values":[["*",[["Invalid index of selected element!"],["{\"frame\":[\"backtraceio.backtraceio.MainActivity.handledException\",\"androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick\",\"android.view.View.performClick\",\"android.view.View.performClickInternal\",\"android.view.View.access$3600\",\"android.view.View$PerformClick.run\",\"androidx.test.espresso.base.Interrogator.loopAndInterrogate\",\"androidx.test.espresso.base.UiControllerImpl.loopUntil\",\"androidx.test.espresso.base.UiControllerImpl.injectMotionEvent\",\"androidx.test.espresso.action.MotionEvents.sendUp\",\"androidx.test.espresso.action.Tap.sendSingleTap\",\"androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap\",\"androidx.test.espresso.action.Tap$1.sendTap\",\"androidx.test.espresso.action.GeneralClickAction.perform\",\"androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform\",\"androidx.test.espresso.ViewInteraction.doPerform\",\"androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform\",\"androidx.test.espresso.ViewInteraction$1.call\",\"java.util.concurrent.FutureTask.run\",\"android.os.Handler.handleCallback\",\"android.os.Handler.dispatchMessage\",\"android.os.Looper.loop\",\"android.app.ActivityThread.main\",\"java.lang.reflect.Method.invoke\",\"com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run\",\"com.android.internal.os.ZygoteInit.main\"]}"],["e4c57699-0dc9-35e2-b4a0-2ffff1925ca7"],["java.lang.IndexOutOfBoundsException"]],1]],"cardinalities": {"initial": {"rows": 1, "groups": 1}, "having": {"rows": 1, "groups": 1}, "pagination": {"rows": 1, "groups": 1}}}} \ No newline at end of file From 8868e0e1990ba37c58c88b1b36c3c91f4a2874dc Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 17 Apr 2023 23:34:40 +0200 Subject: [PATCH 45/61] Move consts to build config --- .github/workflows/test.yml | 2 ++ example-app/build.gradle | 10 ++++++---- .../java/backtraceio/backtraceio/InstrumentedTest.java | 8 ++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e92a95b..e75418bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -30,6 +30,8 @@ jobs: - name: Write to local.properties run: | echo BACKTRACE_SUBMISSION_URL=\"https://submit.backtrace.io/yolo/2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4/json\" > ./local.properties + echo BACKTRACE_CORONER_URL=\"https://yolo.sp.backtrace.io/api/query?project=android-library\" + echo BACKTRACE_CORONER_TOKEN=\"4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a\" - name: Build and check run: ./gradlew assembleDebug assembleDebugAndroidTest build check diff --git a/example-app/build.gradle b/example-app/build.gradle index 9cf5010f..f6838822 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -33,18 +33,20 @@ android { abortOnError false } defaultConfig { - buildConfigField ("String", "BACKTRACE_SUBMISSION_URL", getSubmissionUrl()) + buildConfigField ("String", "BACKTRACE_SUBMISSION_URL", getProperty("BACKTRACE_SUBMISSION_URL", "null")) + buildConfigField ("String", "BACKTRACE_CORONER_URL", getProperty("BACKTRACE_CORONER_URL", "")) + buildConfigField ("String", "BACKTRACE_CORONER_TOKEN", getProperty("BACKTRACE_CORONER_TOKEN", "")) } } } -String getSubmissionUrl() { +String getProperty(String name, String defaultValue) { if (!rootProject.file("./local.properties").exists()) { - return "null" + return defaultValue } def properties = new Properties() properties.load(rootProject.file("./local.properties").newDataInputStream()) - return properties.containsKey('BACKTRACE_SUBMISSION_URL') ? properties['BACKTRACE_SUBMISSION_URL'] : "null" + return properties.containsKey(name) ? properties[name] : defaultValue } dependencies { diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java index c471c423..7dee7092 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java @@ -9,10 +9,10 @@ public class InstrumentedTest { @BeforeClass public static void init() { -// String url = BuildConfig.BACKTRACE_CORONER_URL; -// String coronerToken = BuildConfig.BACKTRACE_CORONER_TOKEN; - String url = "https://yolo.sp.backtrace.io/api/query?project=android-library"; - String coronerToken = "4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a"; + String url = BuildConfig.BACKTRACE_CORONER_URL; + String coronerToken = BuildConfig.BACKTRACE_CORONER_TOKEN; +// String url = "https://yolo.sp.backtrace.io/api/query?project=android-library"; +// String coronerToken = "4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a"; coronerClient = new CoronerClient(url, coronerToken); } From 46a1fc6b120dc1668ec06023389b8ebdf61c8fa7 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 17 Apr 2023 23:40:00 +0200 Subject: [PATCH 46/61] Remove unused file --- .../test/java/backtraceio/coroner/ExampleTest.java | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java diff --git a/coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java b/coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java deleted file mode 100644 index 581f8790..00000000 --- a/coroner-client/src/test/java/backtraceio/coroner/ExampleTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package backtraceio.coroner; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class ExampleTest { - - @Test - public void test() { - assertEquals("a", "a"); - } -} From 58fd1ee3a96a546c2ae399a055f0c168c7b51100 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 17 Apr 2023 23:41:26 +0200 Subject: [PATCH 47/61] Fix default value --- example-app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example-app/build.gradle b/example-app/build.gradle index f6838822..673aa151 100644 --- a/example-app/build.gradle +++ b/example-app/build.gradle @@ -34,8 +34,8 @@ android { } defaultConfig { buildConfigField ("String", "BACKTRACE_SUBMISSION_URL", getProperty("BACKTRACE_SUBMISSION_URL", "null")) - buildConfigField ("String", "BACKTRACE_CORONER_URL", getProperty("BACKTRACE_CORONER_URL", "")) - buildConfigField ("String", "BACKTRACE_CORONER_TOKEN", getProperty("BACKTRACE_CORONER_TOKEN", "")) + buildConfigField ("String", "BACKTRACE_CORONER_URL", getProperty("BACKTRACE_CORONER_URL", "null")) + buildConfigField ("String", "BACKTRACE_CORONER_TOKEN", getProperty("BACKTRACE_CORONER_TOKEN", "null")) } } } From 7e3893b7de7501856dc9fd42dc6f42ff83939e02 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Tue, 18 Apr 2023 00:30:55 +0200 Subject: [PATCH 48/61] Move to secrets --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e75418bf..cd6a1ad5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,9 +29,9 @@ jobs: - name: Write to local.properties run: | - echo BACKTRACE_SUBMISSION_URL=\"https://submit.backtrace.io/yolo/2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4/json\" > ./local.properties - echo BACKTRACE_CORONER_URL=\"https://yolo.sp.backtrace.io/api/query?project=android-library\" - echo BACKTRACE_CORONER_TOKEN=\"4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a\" + echo BACKTRACE_SUBMISSION_URL=\"${{ secrets.BACKTRACE_SUBMISSION_URL }}\" >> ./local.properties + echo BACKTRACE_CORONER_URL=\"${{ secrets.BACKTRACE_CORONER_URL }}\" >> ./local.properties + echo BACKTRACE_CORONER_TOKEN=\"${{ secrets.BACKTRACE_CORONER_TOKEN }}\" >> ./local.properties - name: Build and check run: ./gradlew assembleDebug assembleDebugAndroidTest build check From a8a72b85350c6d38e5a93233184ca15834711c5a Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 24 Apr 2023 23:35:55 +0200 Subject: [PATCH 49/61] Fix --- .../java/backtraceio/backtraceio/InstrumentedTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java index 7dee7092..e25ae0e1 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/InstrumentedTest.java @@ -11,8 +11,6 @@ public class InstrumentedTest { public static void init() { String url = BuildConfig.BACKTRACE_CORONER_URL; String coronerToken = BuildConfig.BACKTRACE_CORONER_TOKEN; -// String url = "https://yolo.sp.backtrace.io/api/query?project=android-library"; -// String coronerToken = "4d1c52a829fa4b53c3c421d163f327e6bfaa5304e756a71249174ab2e1603f8a"; coronerClient = new CoronerClient(url, coronerToken); } From ee62bc1d8cb2f023aebe8c09276f3fa65220f8a0 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Mon, 8 May 2023 21:17:09 +0200 Subject: [PATCH 50/61] Remove todos --- .../src/test/java/backtraceio/coroner/CoronerClientTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java b/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java index ca1ceccd..dfac22b7 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/CoronerClientTest.java @@ -39,7 +39,7 @@ public void init() { } @Test - public void rxIdFilter() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { // todo: replace method name + public void rxIdFilter() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { // GIVEN final String expectedJsonQuery = readResourceFile(QUERY_CORONER_RXID_123); final String jsonResponse = readResourceFile(RESPONSE_RX_FILTER_CORONER_JSON); @@ -59,7 +59,7 @@ public void rxIdFilter() throws CoronerResponseException, IOException, CoronerHt } @Test - public void rxIdFilterAttributes() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { // todo: replace method name + public void rxIdFilterAttributes() throws CoronerResponseException, IOException, CoronerHttpException, CoronerResponseProcessingException { // GIVEN final String expectedJsonQuery = readResourceFile(QUERY_CORONER_RXID_123_ATTR_ERR_MSG); final String jsonResponse = readResourceFile(RESPONSE_RX_FILTER_CORONER_JSON); From 9a9db4dd68d83955fa7fb5288b2e50054b5accce Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sun, 14 May 2023 11:10:27 +0200 Subject: [PATCH 51/61] Rename variable --- .../main/java/backtraceio/coroner/CoronerHttpClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index bd8569d7..7809dfc8 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -49,15 +49,15 @@ private static String getResponseMessage(final HttpURLConnection urlConnection) final InputStream inputStream = getInputStream(urlConnection); - final BufferedReader br = new BufferedReader(new InputStreamReader( + final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( inputStream)); final StringBuilder responseSB = new StringBuilder(); String line; - while ((line = br.readLine()) != null) { + while ((line = bufferedReader.readLine()) != null) { responseSB.append(line); } - br.close(); + bufferedReader.close(); return responseSB.toString(); } From e91ed351ef7bdac0bdfc760848007e996674295a Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sun, 14 May 2023 11:11:16 +0200 Subject: [PATCH 52/61] Add extra brackets --- .../src/main/java/backtraceio/coroner/CoronerHttpClient.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index 7809dfc8..98dd54dd 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -62,8 +62,9 @@ private static String getResponseMessage(final HttpURLConnection urlConnection) } private static InputStream getInputStream(final HttpURLConnection urlConnection) throws IOException { - if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) + if (urlConnection.getResponseCode() < HttpURLConnection.HTTP_BAD_REQUEST) { return urlConnection.getInputStream(); + } return urlConnection.getErrorStream(); } From e353728cce7650cbf6a8042c9af61f67a195628d Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sun, 14 May 2023 11:11:50 +0200 Subject: [PATCH 53/61] Rename variable --- .../backtraceio/coroner/response/CoronerResponseGroup.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java index 2c7412b6..401632f9 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java +++ b/coroner-client/src/main/java/backtraceio/coroner/response/CoronerResponseGroup.java @@ -3,14 +3,14 @@ import java.util.List; public class CoronerResponseGroup { - private static final Integer EXPECTED_NUMBER_OF_EL = 3; + private static final Integer EXPECTED_NUMBER_OF_ELEMENTS = 3; @SuppressWarnings("unused") private final String groupIdentifier; private final List values; public CoronerResponseGroup(final List obj) throws IllegalArgumentException { - if (obj == null || obj.size() != EXPECTED_NUMBER_OF_EL) { - throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_EL + if (obj == null || obj.size() != EXPECTED_NUMBER_OF_ELEMENTS) { + throw new IllegalArgumentException("Wrong number of elements, expected number of elements: " + EXPECTED_NUMBER_OF_ELEMENTS + ", current value: " + (obj != null ? obj.size() : "null")); } From 70e2a2446838938836553901e5db79990efd8466 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sun, 14 May 2023 11:12:52 +0200 Subject: [PATCH 54/61] Improve error msg --- .../src/main/java/backtraceio/backtraceio/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 0fb5a877..374d71f6 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -179,7 +179,7 @@ public void anr(View view) throws InterruptedException { public void enableBreadcrumbs(View view) throws Exception { Context appContext = view.getContext().getApplicationContext(); if (backtraceClient == null) { - throw new Exception("BacktraceClient is null"); + throw new Exception("Backtrace client integration is not initialized"); } if (appContext == null) { From 7c3bbd1c594a855391e4fa36f36516fc483f9e3d Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk Date: Thu, 29 Jun 2023 23:16:10 +0200 Subject: [PATCH 55/61] Use Json objects instead of string --- .../backtraceio/coroner/CoronerClient.java | 12 ++-- .../coroner/query/CoronerFieldFilter.java | 18 +++-- .../coroner/query/CoronerFiltersBuilder.java | 14 ++-- .../coroner/query/CoronerQueries.java | 12 ++-- .../coroner/query/CoronerQueryBuilder.java | 68 ++++++++++++------- .../coroner/query/CoronerValueFilter.java | 17 +++-- .../coroner/query/CoronerFieldFilterTest.java | 14 ++-- .../query/CoronerFiltersBuilderTest.java | 16 +++-- .../coroner/query/CoronerQueriesTest.java | 20 +++--- .../query/CoronerQueryBuilderTest.java | 12 ++-- .../coroner/query/CoronerValueFilterTest.java | 8 ++- .../rxid-12345-custom-attr-err-msg.json | 2 +- .../coroner/queries/rxid-12345.json | 2 +- .../queries/timestamp-err-type-filter.json | 2 +- 14 files changed, 139 insertions(+), 78 deletions(-) diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index 258d9e6c..aa047906 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -1,5 +1,7 @@ package backtraceio.coroner; +import com.google.gson.JsonObject; + import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -38,7 +40,7 @@ public CoronerResponse rxIdFilter(final String rxId) throws CoronerResponseExcep public CoronerResponse rxIdFilter(final String rxId, final List customAttributes) throws CoronerResponseException, CoronerHttpException, IOException { final List attributes = concatAttributes(customAttributes); - final String coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); + final JsonObject coronerQuery = this.coronerQueries.filterByRxId(rxId, attributes); return makeRequest(coronerQuery); } @@ -46,7 +48,7 @@ public CoronerResponse rxIdFilter(final String rxId, final List customAt public CoronerResponse errorTypeTimestampFilter(final String errorType, final String timestampLeast, final String timestampMost, final List customAttributes) throws CoronerResponseException, IOException, CoronerHttpException { final List attributes = concatAttributes(customAttributes); - String coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampLeast, timestampMost, attributes); + final JsonObject coronerQuery = this.coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampLeast, timestampMost, attributes); return makeRequest(coronerQuery); } @@ -57,9 +59,9 @@ private List concatAttributes(final List customAttributes) { return result; } - private CoronerResponse makeRequest(final String coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { - final CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery); - + private CoronerResponse makeRequest(final JsonObject coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { + final CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery.toString()); + System.out.println(coronerQuery.toString()); if (response.error != null) { throw new CoronerResponseException(response.getError()); } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java index 28e701f2..73fbcd0e 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFieldFilter.java @@ -1,5 +1,9 @@ package backtraceio.coroner.query; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; + import java.util.ArrayList; import java.util.List; @@ -20,13 +24,17 @@ public void addValueFilter(final FilterOperator operator, final Object value) { values.add(new CoronerValueFilter(operator, value)); } - @Override - public String toString() { - final List result = new ArrayList<>(); + public JsonArray getFilterValues() { + final JsonArray result = new JsonArray(); for (CoronerValueFilter value : values) { - result.add(value.toString()); + JsonArray filter = new JsonArray(); + + filter.add(new JsonPrimitive(value.operator.toString())); + filter.add(new JsonPrimitive(value.value.toString())); + + result.add(filter); } - return "\"" + name + "\": [" + String.join(",", result) + "]"; + return result; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java index d0eca0da..9f60b671 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerFiltersBuilder.java @@ -1,5 +1,8 @@ package backtraceio.coroner.query; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + import java.util.ArrayList; import java.util.List; @@ -28,14 +31,17 @@ public CoronerFiltersBuilder addFilter(final String name, final FilterOperator o return this; } - public String get() { - final List result = new ArrayList<>(); + public JsonArray getJson() { + final JsonArray result = new JsonArray(); + final JsonObject filtersJson = new JsonObject(); for (CoronerFieldFilter filter : filters) { - result.add(filter.toString()); + filtersJson.add(filter.name, filter.getFilterValues()); } - return "{ " + String.join(",", result) + " }"; + result.add(filtersJson); + + return result; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java index a9427bf4..09141452 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueries.java @@ -1,5 +1,7 @@ package backtraceio.coroner.query; +import com.google.gson.JsonObject; + import java.util.ArrayList; import java.util.List; @@ -10,23 +12,23 @@ public CoronerQueries() { builder = new CoronerQueryBuilder(); } - public String filterByRxId(final String rxId) { + public JsonObject filterByRxId(final String rxId) { return this.filterByRxId(rxId, new ArrayList<>()); } - public String filterByRxId(final String rxId, final List attributes) { + public JsonObject filterByRxId(final String rxId, final List attributes) { final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); filtersBuilder.addFilter(CoronerQueryFields.RXID, FilterOperator.EQUAL, rxId); - return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); + return this.builder.buildRxIdGroup(filtersBuilder.getJson(), attributes); } - public String filterByErrorTypeAndTimestamp(final String errorType, final String timestampLeast, final String timestampMost, final List attributes) { + public JsonObject filterByErrorTypeAndTimestamp(final String errorType, final String timestampLeast, final String timestampMost, final List attributes) { final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); filtersBuilder.addFilter(CoronerQueryFields.ERROR_TYPE, FilterOperator.EQUAL, errorType); filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_LEAST, timestampLeast + "."); filtersBuilder.addFilter(CoronerQueryFields.TIMESTAMP, FilterOperator.AT_MOST, timestampMost + "."); - return this.builder.buildRxIdGroup(filtersBuilder.get(), attributes); + return this.builder.buildRxIdGroup(filtersBuilder.getJson(), attributes); } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java index 052f9606..5b7c0fce 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -1,5 +1,9 @@ package backtraceio.coroner.query; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + import java.util.ArrayList; import java.util.List; @@ -8,37 +12,55 @@ class CoronerQueryBuilder { private final int OFFSET = 0; private final int LIMIT = 1; - public String buildRxIdGroup(final String filters, final List headFolds) { + public JsonObject buildRxIdGroup(final JsonArray filters, final List headFolds) { return this.build(CoronerQueryFields.RXID, filters, headFolds); } - private String build(final String groupName, final String filters, final List headFolds) { - final String folds = joinHeadFolds(headFolds); - - return "{" + - "\"group\":[" + - " [\"" + groupName + "\"]" + - "]," + - "\"fold\": {" + folds + "}," + - " \"offset\":" + OFFSET + "," + - " \"limit\":" + LIMIT + "," + - " \"filter\":[" + filters + "]" + - "}"; + private JsonObject build(final String groupName, final JsonArray filters, final List headFolds) { + final JsonObject folds = joinHeadFolds(headFolds); + + final JsonObject result = new JsonObject(); + + final JsonArray group = new JsonArray(); + final JsonArray subGroup = new JsonArray(); + + subGroup.add(groupName); + group.add(subGroup); + + + result.add("fold", folds); + result.add("group", group); + result.add("offset", new JsonPrimitive(OFFSET)); + result.add("limit", new JsonPrimitive(LIMIT)); + result.add("filter", filters); + + return result; +// return "{" + +// "\"group\":[" + +// " [\"" + groupName + "\"]" + +// "]," + +// "\"fold\": {" + folds + "}," + +// " \"offset\":" + OFFSET + "," + +// " \"limit\":" + LIMIT + "," + +// " \"filter\":[" + filters + "]" + +// "}"; } - private String joinHeadFolds(final List folds) { - final List result = new ArrayList<>(); + private JsonObject joinHeadFolds(final List folds) { + final JsonObject result = new JsonObject(); for (String fold : folds) { - result.add(foldHead(fold)); + result.add(fold, foldHead()); } - return String.join(",", result); - } - private String foldHead(final String name) { - return this.fold(name, FOLD_HEAD); + return result; } - private String fold(final String name, final String val) { - return "\"" + name + "\": " + - "[" + "[\"" + val + "\"" + "]" + "]"; + private JsonArray foldHead() { + final JsonArray foldValue = new JsonArray(); + final JsonArray foldInnerValue = new JsonArray(); + + foldInnerValue.add(FOLD_HEAD); + foldValue.add(foldInnerValue); + + return foldValue; } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java index 9433a5e9..97619701 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerValueFilter.java @@ -1,5 +1,8 @@ package backtraceio.coroner.query; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + public class CoronerValueFilter { public final FilterOperator operator; public final Object value; @@ -11,9 +14,15 @@ public CoronerValueFilter(final FilterOperator operator, final Object value) { @Override public String toString() { - return "[" + - " \"" + operator.toString() + "\"," + - " \"" + value.toString() + "\"" + - " ]"; + return this.get().toString(); + } + + public JsonElement get() { + JsonArray result = new JsonArray(); + + result.add(operator.toString()); + result.add(value.toString()); + + return result; } } diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java index e8d42e9e..197a81d0 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFieldFilterTest.java @@ -2,6 +2,8 @@ import static org.junit.Assert.assertEquals; +import com.google.gson.JsonArray; + import org.junit.Test; public class CoronerFieldFilterTest { @@ -14,11 +16,11 @@ public void singleFilter() { // WHEN final CoronerFieldFilter coronerFieldFilter = new CoronerFieldFilter(name, operator, value); - final String result = coronerFieldFilter.toString(); + final JsonArray result = coronerFieldFilter.getFilterValues(); // THEN - final String expectedResult = "\"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]]"; - assertEquals(expectedResult, result); + final String expectedResult = "[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]"; + assertEquals(expectedResult, result.toString()); } @Test @@ -34,10 +36,10 @@ public void multipleFilters() { // WHEN final CoronerFieldFilter coronerFieldFilter = new CoronerFieldFilter(filterName, filterOperator1, filterValue1); coronerFieldFilter.addValueFilter(filterOperator2, filterValue2); - final String result = coronerFieldFilter.toString(); + final JsonArray result = coronerFieldFilter.getFilterValues(); // THEN - final String expectedResult = "\"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ],[ \"at-least\", \"4f0a0000-4f0a-fd08-0000-999000999000\" ]]"; - assertEquals(expectedResult, result); + final String expectedResult = "[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"],[\"at-least\",\"4f0a0000-4f0a-fd08-0000-999000999000\"]]"; + assertEquals(expectedResult, result.toString()); } } diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java index 353c674d..3651c97f 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerFiltersBuilderTest.java @@ -2,6 +2,8 @@ import static org.junit.Assert.assertEquals; +import com.google.gson.JsonArray; + import org.junit.Test; import backtraceio.coroner.utils.StringUtils; @@ -14,10 +16,10 @@ public void emptyFilters() { final CoronerFiltersBuilder filtersBuilder = new CoronerFiltersBuilder(); // WHEN - String result = filtersBuilder.get(); + final JsonArray result = filtersBuilder.getJson(); // THEN - assertEquals("{ }", StringUtils.normalizeSpace(result)); + assertEquals("[{}]", StringUtils.normalizeSpace(result.toString())); } @Test @@ -30,11 +32,11 @@ public void singleFilter() { // WHEN filtersBuilder.addFilter(name, operator, value); - final String result = filtersBuilder.get(); + final JsonArray result = filtersBuilder.getJson(); // THEN - final String expectedResult = "{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]] }"; - assertEquals(expectedResult, result); + final String expectedResult = "[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]"; + assertEquals(expectedResult, result.toString()); } @Test @@ -59,10 +61,10 @@ public void multipleFilters() { filtersBuilder.addFilter(filter1Name, filter1Operator2, filter1Value2); filtersBuilder.addFilter(filter2Name, filter2Operator, filter2Value); - final String result = filtersBuilder.get(); + final String result = filtersBuilder.getJson().toString(); // THEN - final String expectedResult = "{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ],[ \"at-least\", \"4f0a0000-4f0a-fd08-0000-999000999000\" ]],\"example_field\": [[ \"at-most\", \"12345678-4f0b-fdp2-0001-000094000000\" ]] }"; + final String expectedResult = "[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"],[\"at-least\",\"4f0a0000-4f0a-fd08-0000-999000999000\"]],\"example_field\":[[\"at-most\",\"12345678-4f0b-fdp2-0001-000094000000\"]]}]"; assertEquals(expectedResult, result); } } diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java index 2fbe79ab..fab17125 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueriesTest.java @@ -2,6 +2,8 @@ import static org.junit.Assert.assertEquals; +import com.google.gson.JsonObject; + import org.junit.Before; import org.junit.Test; @@ -24,11 +26,11 @@ public void filterByRxIdWithoutAttributesTest() { String rxId = "03000000-4f0a-fd08-0000-000000000000"; // WHEN - String result = coronerQueries.filterByRxId(rxId); + JsonObject result = coronerQueries.filterByRxId(rxId); // THEN - String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {}, \"offset\":0, \"limit\":1, \"filter\":[{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]] }]}"; - assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + String expectedResult = "{\"fold\":{},\"group\":[[\"_rxid\"]],\"offset\":0,\"limit\":1,\"filter\":[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result.toString())); } @Test @@ -38,11 +40,11 @@ public void filterByRxIdTest() { List attributes = Arrays.asList("value1", "value2"); // WHEN - String result = coronerQueries.filterByRxId(rxId, attributes); + JsonObject result = coronerQueries.filterByRxId(rxId, attributes); // THEN - String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {\"value1\": [[\"head\"]],\"value2\": [[\"head\"]]}, \"offset\":0, \"limit\":1, \"filter\":[{ \"_rxid\": [[ \"equal\", \"03000000-4f0a-fd08-0000-000000000000\" ]] }]}"; - assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + String expectedResult = "{\"fold\":{\"value1\":[[\"head\"]],\"value2\":[[\"head\"]]},\"group\":[[\"_rxid\"]],\"offset\":0,\"limit\":1,\"filter\":[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result.toString())); } @Test @@ -55,10 +57,10 @@ public void filterByErrorTypeAndTimestampTest() { final String timestampEnd = "1681943692"; // WHEN - String result = coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampStart, timestampEnd, attributes); + JsonObject result = coronerQueries.filterByErrorTypeAndTimestamp(errorType, timestampStart, timestampEnd, attributes); // THEN - String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {\"error.message\": [[\"head\"]],\"example-attribute\": [[\"head\"]]}, \"offset\":0, \"limit\":1, \"filter\":[{ \"error.type\": [[ \"equal\", \"Crash\" ]],\"timestamp\": [[ \"at-least\", \"1680943692.\" ],[ \"at-most\", \"1681943692.\" ]] }]}"; - assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + String expectedResult = "{\"fold\":{\"error.message\":[[\"head\"]],\"example-attribute\":[[\"head\"]]},\"group\":[[\"_rxid\"]],\"offset\":0,\"limit\":1,\"filter\":[{\"error.type\":[[\"equal\",\"Crash\"]],\"timestamp\":[[\"at-least\",\"1680943692.\"],[\"at-most\",\"1681943692.\"]]}]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result.toString())); } } diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java index 8636a102..f06ea518 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerQueryBuilderTest.java @@ -2,11 +2,15 @@ import static org.junit.Assert.assertEquals; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + import org.junit.Test; import java.util.Arrays; import java.util.List; +import backtraceio.coroner.serialization.GsonWrapper; import backtraceio.coroner.utils.StringUtils; public class CoronerQueryBuilderTest { @@ -15,14 +19,14 @@ public class CoronerQueryBuilderTest { public void testBuildRxIdGroup() { // GIVEN final CoronerQueryBuilder coronerQueryBuilder = new CoronerQueryBuilder(); - final String filters = "{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}"; + final JsonArray filters = GsonWrapper.fromJson("[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]", JsonArray.class); final List headFolds = Arrays.asList("error.type", "callstack"); // WHEN - final String result = coronerQueryBuilder.buildRxIdGroup(filters, headFolds); + final JsonObject result = coronerQueryBuilder.buildRxIdGroup(filters, headFolds); // THEN - final String expectedResult = "{\"group\":[ [\"_rxid\"]],\"fold\": {\"error.type\": [[\"head\"]],\"callstack\": [[\"head\"]]}, \"offset\":0, \"limit\":1, \"filter\":[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]}"; - assertEquals(expectedResult, StringUtils.normalizeSpace(result)); + final String expectedResult = "{\"fold\":{\"error.type\":[[\"head\"]],\"callstack\":[[\"head\"]]},\"group\":[[\"_rxid\"]],\"offset\":0,\"limit\":1,\"filter\":[{\"_rxid\":[[\"equal\",\"03000000-4f0a-fd08-0000-000000000000\"]]}]}"; + assertEquals(expectedResult, StringUtils.normalizeSpace(result.toString())); } } diff --git a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java index 5c3a62d0..b0eceaa8 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/query/CoronerValueFilterTest.java @@ -2,6 +2,8 @@ import static org.junit.Assert.assertEquals; +import com.google.gson.JsonElement; + import org.junit.Test; public class CoronerValueFilterTest { @@ -12,10 +14,10 @@ public void serializationTest() { final CoronerValueFilter coronerValueFilter = new CoronerValueFilter(FilterOperator.EQUAL, "123"); // WHEN - String result = coronerValueFilter.toString(); + JsonElement result = coronerValueFilter.get(); // THEN - String expectedResult = "[ \"equal\", \"123\" ]"; - assertEquals(expectedResult, result); + String expectedResult = "[\"equal\",\"123\"]"; + assertEquals(expectedResult, result.toString()); } } diff --git a/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json index f05bbd1a..8f0cf132 100644 --- a/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json +++ b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345-custom-attr-err-msg.json @@ -1 +1 @@ -{"group":[ ["_rxid"]],"fold": {"error.message": [["head"]],"callstack": [["head"]],"guid": [["head"]],"classifiers": [["head"]]}, "offset":0, "limit":1, "filter":[{ "_rxid": [[ "equal", "12345" ]] }]} \ No newline at end of file +{"fold":{"error.message":[["head"]],"callstack":[["head"]],"guid":[["head"]],"classifiers":[["head"]]},"group":[["_rxid"]],"offset":0,"limit":1,"filter":[{"_rxid":[["equal","12345"]]}]} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json index 9dcbb28d..6a465e90 100644 --- a/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json +++ b/coroner-client/src/test/resources/backtraceio/coroner/queries/rxid-12345.json @@ -1 +1 @@ -{"group":[ ["_rxid"]],"fold": {"callstack": [["head"]],"guid": [["head"]],"classifiers": [["head"]]}, "offset":0, "limit":1, "filter":[{ "_rxid": [[ "equal", "12345" ]] }]} \ No newline at end of file +{"fold":{"callstack":[["head"]],"guid":[["head"]],"classifiers":[["head"]]},"group":[["_rxid"]],"offset":0,"limit":1,"filter":[{"_rxid":[["equal","12345"]]}]} \ No newline at end of file diff --git a/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json b/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json index f2a0debc..c9b554b1 100644 --- a/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json +++ b/coroner-client/src/test/resources/backtraceio/coroner/queries/timestamp-err-type-filter.json @@ -1 +1 @@ -{"group":[ ["_rxid"]],"fold": {"error.message": [["head"]],"callstack": [["head"]],"guid": [["head"]],"classifiers": [["head"]]}, "offset":0, "limit":1, "filter":[{ "error.type": [[ "equal", "Crash" ]],"timestamp": [[ "at-least", "0." ],[ "at-most", "10000000000." ]] }]} \ No newline at end of file +{"fold":{"error.message":[["head"]],"callstack":[["head"]],"guid":[["head"]],"classifiers":[["head"]]},"group":[["_rxid"]],"offset":0,"limit":1,"filter":[{"error.type":[["equal","Crash"]],"timestamp":[["at-least","0."],["at-most","10000000000."]]}]} \ No newline at end of file From 6ea904e452720a55344e45ee257e777e0a2816ef Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk Date: Thu, 29 Jun 2023 23:19:31 +0200 Subject: [PATCH 56/61] Refactor --- .../backtraceio/coroner/CoronerClient.java | 2 +- .../backtraceio/coroner/query/Constants.java | 9 +++++++++ .../coroner/query/CoronerQueryBuilder.java | 20 +++++-------------- 3 files changed, 15 insertions(+), 16 deletions(-) create mode 100644 coroner-client/src/main/java/backtraceio/coroner/query/Constants.java diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java index aa047906..3f5ce9cc 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerClient.java @@ -61,7 +61,7 @@ private List concatAttributes(final List customAttributes) { private CoronerResponse makeRequest(final JsonObject coronerQuery) throws CoronerResponseException, IOException, CoronerHttpException { final CoronerApiResponse response = this.coronerHttpClient.get(coronerQuery.toString()); - System.out.println(coronerQuery.toString()); + if (response.error != null) { throw new CoronerResponseException(response.getError()); } diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/Constants.java b/coroner-client/src/main/java/backtraceio/coroner/query/Constants.java new file mode 100644 index 00000000..2b10b942 --- /dev/null +++ b/coroner-client/src/main/java/backtraceio/coroner/query/Constants.java @@ -0,0 +1,9 @@ +package backtraceio.coroner.query; + +public class Constants { + public final static String FOLD = "fold"; + public final static String GROUP = "group"; + public final static String OFFSET = "offset"; + public final static String LIMIT = "limit"; + public final static String FILTER = "filter"; +} diff --git a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java index 5b7c0fce..d855c0e1 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java +++ b/coroner-client/src/main/java/backtraceio/coroner/query/CoronerQueryBuilder.java @@ -27,23 +27,13 @@ private JsonObject build(final String groupName, final JsonArray filters, final subGroup.add(groupName); group.add(subGroup); - - result.add("fold", folds); - result.add("group", group); - result.add("offset", new JsonPrimitive(OFFSET)); - result.add("limit", new JsonPrimitive(LIMIT)); - result.add("filter", filters); + result.add(Constants.FOLD, folds); + result.add(Constants.GROUP, group); + result.add(Constants.OFFSET, new JsonPrimitive(OFFSET)); + result.add(Constants.LIMIT, new JsonPrimitive(LIMIT)); + result.add(Constants.FILTER, filters); return result; -// return "{" + -// "\"group\":[" + -// " [\"" + groupName + "\"]" + -// "]," + -// "\"fold\": {" + folds + "}," + -// " \"offset\":" + OFFSET + "," + -// " \"limit\":" + LIMIT + "," + -// " \"filter\":[" + filters + "]" + -// "}"; } private JsonObject joinHeadFolds(final List folds) { final JsonObject result = new JsonObject(); From 24a807d1cb03b9860bb0a027b80a6ad8ce678325 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Jul 2023 09:45:37 +0200 Subject: [PATCH 57/61] Move frames to new file --- .../backtraceio/coroner/serialization/GsonWrapperTest.java | 4 +++- .../test/java/backtraceio/coroner/utils/ResourceUtils.java | 1 + .../src/test/resources/backtraceio/coroner/frames.json | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 coroner-client/src/test/resources/backtraceio/coroner/frames.json diff --git a/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java b/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java index 5af2ba4d..c6711eb5 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java +++ b/coroner-client/src/test/java/backtraceio/coroner/serialization/GsonWrapperTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static backtraceio.coroner.utils.ResourceUtils.EXPECTED_FRAMES; import static backtraceio.coroner.utils.ResourceUtils.RESPONSE_RX_FILTER_CORONER_JSON; import static backtraceio.coroner.utils.ResourceUtils.readResourceFile; @@ -26,6 +27,7 @@ private Object getResponseGroupAttributeValue(Object attribute) { public void deserializeApiResponse() throws IOException{ // GIVEN final String json = readResourceFile(RESPONSE_RX_FILTER_CORONER_JSON); + final String expectedFrames = readResourceFile(EXPECTED_FRAMES); // WHEN final CoronerApiResponse result = GsonWrapper.fromJson(json, CoronerApiResponse.class); @@ -38,7 +40,7 @@ public void deserializeApiResponse() throws IOException{ CoronerResponseGroup responseGroup = result.getResponse().values.get(0); assertEquals("Invalid index of selected element!", getResponseGroupAttributeValue(responseGroup.getAttribute(0))); - assertEquals("{\"frame\":[\"backtraceio.backtraceio.MainActivity.handledException\",\"androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick\",\"android.view.View.performClick\",\"android.view.View.performClickInternal\",\"android.view.View.access$3600\",\"android.view.View$PerformClick.run\",\"androidx.test.espresso.base.Interrogator.loopAndInterrogate\",\"androidx.test.espresso.base.UiControllerImpl.loopUntil\",\"androidx.test.espresso.base.UiControllerImpl.injectMotionEvent\",\"androidx.test.espresso.action.MotionEvents.sendUp\",\"androidx.test.espresso.action.Tap.sendSingleTap\",\"androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap\",\"androidx.test.espresso.action.Tap$1.sendTap\",\"androidx.test.espresso.action.GeneralClickAction.perform\",\"androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform\",\"androidx.test.espresso.ViewInteraction.doPerform\",\"androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform\",\"androidx.test.espresso.ViewInteraction$1.call\",\"java.util.concurrent.FutureTask.run\",\"android.os.Handler.handleCallback\",\"android.os.Handler.dispatchMessage\",\"android.os.Looper.loop\",\"android.app.ActivityThread.main\",\"java.lang.reflect.Method.invoke\",\"com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run\",\"com.android.internal.os.ZygoteInit.main\"]}", getResponseGroupAttributeValue(responseGroup.getAttribute(1))); + assertEquals(expectedFrames, getResponseGroupAttributeValue(responseGroup.getAttribute(1))); assertEquals("e4c57699-0dc9-35e2-b4a0-2ffff1925ca7", getResponseGroupAttributeValue(responseGroup.getAttribute(2))); assertEquals("java.lang.IndexOutOfBoundsException", getResponseGroupAttributeValue(responseGroup.getAttribute(3))); } diff --git a/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java b/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java index 009d8047..53fc9412 100644 --- a/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java +++ b/coroner-client/src/test/java/backtraceio/coroner/utils/ResourceUtils.java @@ -10,6 +10,7 @@ public class ResourceUtils { public static final String QUERY_CORONER_RXID_123_ATTR_ERR_MSG = MAIN_PATH + "/queries/rxid-12345-custom-attr-err-msg.json"; public static final String QUERY_CORONER_TIMESTAMP_ERR_TYPE= MAIN_PATH + "/queries/timestamp-err-type-filter.json"; public static final String RESPONSE_RX_FILTER_CORONER_JSON = MAIN_PATH + "/responses/rx-filter-response.json"; + public static final String EXPECTED_FRAMES = MAIN_PATH + "/frames.json"; public static final String RESPONSE_TIMESTAMP_ERR_TYPE_CORONER_JSON = MAIN_PATH + "/responses/timestamp-err-type-filter.json"; public static final String RESPONSE_OPERATION_ERROR_JSON = MAIN_PATH + "/responses/operation-error.json"; diff --git a/coroner-client/src/test/resources/backtraceio/coroner/frames.json b/coroner-client/src/test/resources/backtraceio/coroner/frames.json new file mode 100644 index 00000000..416a5895 --- /dev/null +++ b/coroner-client/src/test/resources/backtraceio/coroner/frames.json @@ -0,0 +1 @@ +{"frame":["backtraceio.backtraceio.MainActivity.handledException","androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick","android.view.View.performClick","android.view.View.performClickInternal","android.view.View.access$3600","android.view.View$PerformClick.run","androidx.test.espresso.base.Interrogator.loopAndInterrogate","androidx.test.espresso.base.UiControllerImpl.loopUntil","androidx.test.espresso.base.UiControllerImpl.injectMotionEvent","androidx.test.espresso.action.MotionEvents.sendUp","androidx.test.espresso.action.Tap.sendSingleTap","androidx.test.espresso.action.Tap.-$$Nest$smsendSingleTap","androidx.test.espresso.action.Tap$1.sendTap","androidx.test.espresso.action.GeneralClickAction.perform","androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform","androidx.test.espresso.ViewInteraction.doPerform","androidx.test.espresso.ViewInteraction.-$$Nest$mdoPerform","androidx.test.espresso.ViewInteraction$1.call","java.util.concurrent.FutureTask.run","android.os.Handler.handleCallback","android.os.Handler.dispatchMessage","android.os.Looper.loop","android.app.ActivityThread.main","java.lang.reflect.Method.invoke","com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run","com.android.internal.os.ZygoteInit.main"]} \ No newline at end of file From 2f714d4c583fd3f9c4431cb8a0dcd11eb201a823 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sat, 1 Jul 2023 10:16:02 +0200 Subject: [PATCH 58/61] Fix unit test --- .../backtraceio/ExampleInstrumentedTest.java | 2 +- .../backtraceio/backtraceio/MainActivity.java | 23 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index e9bacb4a..210cb54e 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -80,7 +80,7 @@ public void handledException() throws TimeoutException, CoronerResponseProcessin Assert.assertEquals(1, response.getResultsNumber()); String resultErrorMsg = response.getAttribute(0, "error.message", String.class); - Assert.assertEquals("Invalid index of selected element!", resultErrorMsg); + Assert.assertEquals("Index: 3, Size: 3", resultErrorMsg); String resultClassifier = response.getAttribute(0, "classifiers", String.class); Assert.assertEquals("java.lang.IndexOutOfBoundsException", resultClassifier); diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 374d71f6..9a99c42c 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -30,7 +30,6 @@ import backtraceio.library.enums.database.RetryOrder; import backtraceio.library.events.OnServerResponseEventListener; import backtraceio.library.models.BacktraceExceptionHandler; -import backtraceio.library.models.BacktraceMetricsSettings; import backtraceio.library.models.database.BacktraceDatabaseSettings; import backtraceio.library.models.json.BacktraceReport; @@ -52,7 +51,7 @@ public void setOnServerResponseEventListener(OnServerResponseEventListener e) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - + backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL); symlinkAndWriteFile(); @@ -75,7 +74,9 @@ private void symlinkAndWriteFile() { } private BacktraceClient initializeBacktrace(final String submissionUrl) { - BacktraceCredentials credentials = new BacktraceCredentials(submissionUrl); + BacktraceCredentials credentials = new BacktraceCredentials("https://yolo.sp.backtrace.io:6098/", + "2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4"); + Context context = getApplicationContext(); String dbPath = context.getFilesDir().getAbsolutePath(); @@ -141,16 +142,12 @@ void equipItem(List armor, int index) { public void handledException(View view) { try { - try { - List myWarriorArmor = getWarriorArmor(); - int magicWandIndex = findEquipmentIndex(myWarriorArmor, "Magic Wand"); - // I don't need a Magic Wand, I am a warrior - removeEquipment(myWarriorArmor, magicWandIndex); - // Where was that magic wand again? - equipItem(myWarriorArmor, magicWandIndex); - } catch (IndexOutOfBoundsException e) { - throw new IndexOutOfBoundsException("Invalid index of selected element!"); - } + List myWarriorArmor = getWarriorArmor(); + int magicWandIndex = findEquipmentIndex(myWarriorArmor, "Magic Wand"); + // I don't need a Magic Wand, I am a warrior + removeEquipment(myWarriorArmor, magicWandIndex); + // Where was that magic wand again? + equipItem(myWarriorArmor, magicWandIndex); } catch (IndexOutOfBoundsException e) { backtraceClient.send(new BacktraceReport(e), this.listener); } From 06d7ca2ca1b0f839ef15b2b17dd7514e41499755 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Sun, 2 Jul 2023 09:01:56 +0200 Subject: [PATCH 59/61] Revert "Fix unit test" This reverts commit 2f714d4c583fd3f9c4431cb8a0dcd11eb201a823. --- .../backtraceio/ExampleInstrumentedTest.java | 2 +- .../backtraceio/backtraceio/MainActivity.java | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java index 210cb54e..e9bacb4a 100644 --- a/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java +++ b/example-app/src/androidTest/java/backtraceio/backtraceio/ExampleInstrumentedTest.java @@ -80,7 +80,7 @@ public void handledException() throws TimeoutException, CoronerResponseProcessin Assert.assertEquals(1, response.getResultsNumber()); String resultErrorMsg = response.getAttribute(0, "error.message", String.class); - Assert.assertEquals("Index: 3, Size: 3", resultErrorMsg); + Assert.assertEquals("Invalid index of selected element!", resultErrorMsg); String resultClassifier = response.getAttribute(0, "classifiers", String.class); Assert.assertEquals("java.lang.IndexOutOfBoundsException", resultClassifier); diff --git a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java index 9a99c42c..374d71f6 100644 --- a/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java +++ b/example-app/src/main/java/backtraceio/backtraceio/MainActivity.java @@ -30,6 +30,7 @@ import backtraceio.library.enums.database.RetryOrder; import backtraceio.library.events.OnServerResponseEventListener; import backtraceio.library.models.BacktraceExceptionHandler; +import backtraceio.library.models.BacktraceMetricsSettings; import backtraceio.library.models.database.BacktraceDatabaseSettings; import backtraceio.library.models.json.BacktraceReport; @@ -51,7 +52,7 @@ public void setOnServerResponseEventListener(OnServerResponseEventListener e) { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - + backtraceClient = initializeBacktrace(BuildConfig.BACKTRACE_SUBMISSION_URL); symlinkAndWriteFile(); @@ -74,9 +75,7 @@ private void symlinkAndWriteFile() { } private BacktraceClient initializeBacktrace(final String submissionUrl) { - BacktraceCredentials credentials = new BacktraceCredentials("https://yolo.sp.backtrace.io:6098/", - "2dd86e8e779d1fc7e22e7b19a9489abeedec3b1426abe7e2209888e92362fba4"); - + BacktraceCredentials credentials = new BacktraceCredentials(submissionUrl); Context context = getApplicationContext(); String dbPath = context.getFilesDir().getAbsolutePath(); @@ -142,12 +141,16 @@ void equipItem(List armor, int index) { public void handledException(View view) { try { - List myWarriorArmor = getWarriorArmor(); - int magicWandIndex = findEquipmentIndex(myWarriorArmor, "Magic Wand"); - // I don't need a Magic Wand, I am a warrior - removeEquipment(myWarriorArmor, magicWandIndex); - // Where was that magic wand again? - equipItem(myWarriorArmor, magicWandIndex); + try { + List myWarriorArmor = getWarriorArmor(); + int magicWandIndex = findEquipmentIndex(myWarriorArmor, "Magic Wand"); + // I don't need a Magic Wand, I am a warrior + removeEquipment(myWarriorArmor, magicWandIndex); + // Where was that magic wand again? + equipItem(myWarriorArmor, magicWandIndex); + } catch (IndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException("Invalid index of selected element!"); + } } catch (IndexOutOfBoundsException e) { backtraceClient.send(new BacktraceReport(e), this.listener); } From 1c9e023207585314d84e01ed0d64bd05e56808b1 Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Wed, 19 Jul 2023 22:22:02 +0200 Subject: [PATCH 60/61] Rename variable --- .../src/main/java/backtraceio/library/http/HttpHelper.java | 6 +++--- .../main/java/backtraceio/coroner/CoronerHttpClient.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java b/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java index 72ccf5eb..a275bce2 100644 --- a/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java +++ b/backtrace-library/src/main/java/backtraceio/library/http/HttpHelper.java @@ -31,12 +31,12 @@ public static String getResponseMessage(HttpURLConnection urlConnection) throws BufferedReader br = new BufferedReader(new InputStreamReader( inputStream)); - StringBuilder responseSB = new StringBuilder(); + StringBuilder responseStringBuilder = new StringBuilder(); String line; while ((line = br.readLine()) != null) { - responseSB.append(line); + responseStringBuilder.append(line); } br.close(); - return responseSB.toString(); + return responseStringBuilder.toString(); } } diff --git a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java index 98dd54dd..1f269002 100644 --- a/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java +++ b/coroner-client/src/main/java/backtraceio/coroner/CoronerHttpClient.java @@ -52,13 +52,13 @@ private static String getResponseMessage(final HttpURLConnection urlConnection) final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( inputStream)); - final StringBuilder responseSB = new StringBuilder(); + final StringBuilder responseStringBuilder = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { - responseSB.append(line); + responseStringBuilder.append(line); } bufferedReader.close(); - return responseSB.toString(); + return responseStringBuilder.toString(); } private static InputStream getInputStream(final HttpURLConnection urlConnection) throws IOException { From b6837717a3eb3a0ab84da1aab9b9b67f636a8d7f Mon Sep 17 00:00:00 2001 From: Bartosz Litwiniuk <> Date: Thu, 10 Aug 2023 22:30:13 +0200 Subject: [PATCH 61/61] Add info about 3.7.8 to CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d04d4fe..ed5028cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Backtrace Android Release Notes +## Version 3.7.8 +- Add integration tests +- Move common http response processing method to helper class +- Remove unused code +- Add abstraction to gson serialization +- Improve example-app Main Activity to work in same way on different Android versions + ## Version 3.7.7 - Added a new attribute to native reports - `breadcrumbs.lastId`