Skip to content

Commit

Permalink
New client attribute API (#117)
Browse files Browse the repository at this point in the history
* New client attribute API

* Code review adjustements
  • Loading branch information
konraddysput authored Feb 8, 2024
1 parent bd685d2 commit fac1501
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package backtraceio.library;

import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

import net.jodah.concurrentunit.Waiter;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import backtraceio.library.events.OnServerResponseEventListener;
import backtraceio.library.events.RequestHandler;
import backtraceio.library.models.BacktraceData;
import backtraceio.library.models.BacktraceResult;
import backtraceio.library.models.types.BacktraceResultStatus;

@RunWith(AndroidJUnit4.class)
public class BacktraceClientAttributeTests {
private Context context;
private BacktraceCredentials credentials;
private BacktraceDatabase database;

@Before
public void setUp() {
context = InstrumentationRegistry.getInstrumentation().getContext();
credentials = new BacktraceCredentials("https://example-endpoint.com/", "");
database = new BacktraceDatabase(context, context.getFilesDir().getAbsolutePath());
}

@Test
public void shouldAddASingleAttribute() {
final String attributeKey = "test-attribute";
final String attributeValue = "test-value";
BacktraceClient backtraceClient = new BacktraceClient(context, credentials, database);
backtraceClient.addAttribute(attributeKey, attributeValue);

Map<String, Object> attributes = backtraceClient.getAttributes();

Object value = attributes.get(attributeKey);
assertNotNull(value);
assertEquals(value, attributeValue);
}

@Test
public void shouldAddMultipleAttributesAtOnce() {
final String attributeKey = "test-attribute";
final String attributeValue = "test-value";
final Integer numberOfAttributesToVerify = 3;
Map<String, Object> attributes = new HashMap<>();
for (int attributeIteration = 0; attributeIteration < numberOfAttributesToVerify; attributeIteration++) {
attributes.put(String.format("%s %d", attributeKey, attributeIteration), attributeValue);
}
BacktraceClient backtraceClient = new BacktraceClient(context, credentials, database);
backtraceClient.addAttribute(attributes);

Map<String, Object> clientAttributes = backtraceClient.getAttributes();

for (int attributeIteration = 0; attributeIteration < numberOfAttributesToVerify; attributeIteration++) {
Object value = attributes.get(String.format("%s %d", attributeKey, attributeIteration));
assertNotNull(value);
assertEquals(value, attributeValue);
}
}

@Test
public void shouldReplaceExistingAttribute() {
final String attributeKey = "test-attribute";
final String oldAttributeValue = "old-test-value";
final String newAttributeValue = "test-value-new";

BacktraceClient backtraceClient = new BacktraceClient(context, credentials, database);
backtraceClient.addAttribute(attributeKey, oldAttributeValue);

backtraceClient.addAttribute(attributeKey, newAttributeValue);

Map<String, Object> attributes = backtraceClient.getAttributes();

Object value = attributes.get(attributeKey);
assertNotNull(value);
assertEquals(value, newAttributeValue);
}

@Test
public void attributesShouldBeAvailableInReport() {
final String errorMessage = "error message";
final String attributeKey = "test-attribute";
final String attributeValue = "test-value";
BacktraceClient backtraceClient = new BacktraceClient(context, credentials, database);
backtraceClient.addAttribute(attributeKey, attributeValue);
RequestHandler rh = new RequestHandler() {
@Override
public BacktraceResult onRequest(BacktraceData data) {
Object value =data.attributes.get(attributeKey);
assertNotNull(value);
assertEquals(value, attributeValue);
return new BacktraceResult(data.report, data.report.exception.getMessage(),
BacktraceResultStatus.Ok);
}
};
backtraceClient.setOnRequestHandler(rh);
final Waiter waiter = new Waiter();

// WHEN
backtraceClient.send(new Exception(errorMessage), new
OnServerResponseEventListener() {
@Override
public void onEvent(BacktraceResult backtraceResult) {
waiter.resume();
}
}
);
// WAIT FOR THE RESULT FROM ANOTHER THREAD
try {
waiter.await(5, TimeUnit.SECONDS);
} catch (Exception ex) {
fail(ex.getMessage());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static org.junit.Assert.assertEquals;

import android.content.Context;
import android.os.FileUtils;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;

Expand All @@ -14,7 +16,9 @@
import org.junit.runner.RunWith;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -36,22 +40,55 @@ public class BacktraceDatabaseTest {
private Context context;
private String dbPath;
private BacktraceDatabase database;
private BacktraceClient client;
private BacktraceCredentials credentials;
private final String testMessage = "Example test string";

@Before
public void setUp() {
this.context = InstrumentationRegistry.getInstrumentation().getContext();
this.dbPath = this.context.getFilesDir().getAbsolutePath();
this.database = new BacktraceDatabase(this.context, dbPath);
this.credentials = new BacktraceCredentials("https://test.sp.backtrace.io", "1231231231231");
this.client = new BacktraceClient(this.context, this.credentials);
this.database.start();
this.database.clear();
}

@After
public void after() {
this.database.clear();
this.database.disableNativeIntegration();
}

@Test
public void shouldNotCrashWhenNativeIntegrationIsNotEnabled() {
assertEquals(false, this.database.addNativeAttribute("key", "value"));
}

@Test
public void shouldAddAnAttributeToNativeIntegration() {
this.database.setupNativeIntegration(this.client, this.credentials);
assertEquals(true, this.database.addNativeAttribute("key", "value"));
}

@Test
public void shouldNotAddAnAttributeToNativeIntegrationWithComplexAttributeValue() {
this.database.setupNativeIntegration(this.client, this.credentials);
assertEquals(false, this.database.addNativeAttribute("key", new HashMap<>()));
}

@Test
public void shouldPreventAddingAnAttributeWithNullableKey() {
this.database.setupNativeIntegration(this.client, this.credentials);
assertEquals(false, this.database.addNativeAttribute(null, "value"));
}

@Test
public void shouldPreventAddingAnAttributeWithNullableValue() {
this.database.setupNativeIntegration(this.client, this.credentials);
assertEquals(false, this.database.addNativeAttribute("key", null));
}

@Test
public void isDatabaseEmpty() {
Expand Down Expand Up @@ -124,8 +161,13 @@ public void clearDatabase() {
assertEquals(2, database.count());

database.clear();
int filesNumber = new File(this.dbPath).listFiles().length;
assertEquals(0, filesNumber);
File[] files = new File(this.dbPath).listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile();
}
});
assertEquals(0, files.length);
assertEquals(0, database.getDatabaseSize());
assertEquals(0, database.count());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import backtraceio.library.base.BacktraceBase;
import backtraceio.library.breadcrumbs.BacktraceBreadcrumbs;
import backtraceio.library.common.FileHelper;
import backtraceio.library.common.TypeHelper;
import backtraceio.library.enums.UnwindingMode;
import backtraceio.library.enums.database.RetryBehavior;
import backtraceio.library.events.OnServerResponseEventListener;
Expand Down Expand Up @@ -50,6 +51,7 @@ public class BacktraceDatabase implements Database {
private boolean _enable = false;
private Breadcrumbs breadcrumbs;

private boolean _enabledNativeIntegration = false;
/**
* Add attributes to native reports
*
Expand Down Expand Up @@ -167,7 +169,7 @@ public Boolean setupNativeIntegration(BacktraceBase client, BacktraceCredentials
public Boolean setupNativeIntegration(BacktraceBase client, BacktraceCredentials credentials,
boolean enableClientSideUnwinding, UnwindingMode unwindingMode) {
// avoid initialization when database doesn't exist
if (getSettings() == null) {
if (_enable == false || getSettings() == null) {
return false;
}
String minidumpSubmissionUrl = credentials.getMinidumpSubmissionUrl().toString();
Expand Down Expand Up @@ -202,7 +204,7 @@ public Boolean setupNativeIntegration(BacktraceBase client, BacktraceCredentials
File crashHandlerDir = new File(databasePath);
crashHandlerDir.mkdir();

Boolean initialized = initialize(
_enabledNativeIntegration = initialize(
minidumpSubmissionUrl,
databasePath,
handlerPath,
Expand All @@ -213,12 +215,12 @@ public Boolean setupNativeIntegration(BacktraceBase client, BacktraceCredentials
unwindingMode
);

if (initialized && this.breadcrumbs.isEnabled()) {
if (_enabledNativeIntegration && this.breadcrumbs.isEnabled()) {
this.breadcrumbs.setOnSuccessfulBreadcrumbAddEventListener(breadcrumbId -> {
this.addAttribute("breadcrumbs.lastId", Long.toString((breadcrumbId)));
});
}
return initialized;
return _enabledNativeIntegration;
}

/**
Expand All @@ -227,13 +229,30 @@ public Boolean setupNativeIntegration(BacktraceBase client, BacktraceCredentials
@Override
public void disableNativeIntegration() {
disable();
this._enabledNativeIntegration = false;
}

@Override
public Breadcrumbs getBreadcrumbs() {
return this.breadcrumbs;
}

public Boolean addNativeAttribute(String key, Object value) {
if (!_enabledNativeIntegration) {
return false;
}

if (key == null || value == null) {
return false;
}
Class type = value.getClass();
if (!TypeHelper.isPrimitiveOrPrimitiveWrapperOrString(type)) {
return false;
}
addAttribute(key, value.toString());
return true;
}

public void start() {
if (databaseSettings == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,41 @@ public Map<String, Object> getAttributes() {
return attributes;
}

/**
* Adds a new attribute to Backtrace Client. If the native integration is enabled, adds the attribute
* to the native report attributes if:
* - the value exists (is not a null)
* - is not an object (the attribute value is primitive type like String, or Int)
*
* @param key attribute name
* @param value attribute value.
*/
public void addAttribute(String key, Object value) {
attributes.put(key, value);
if (database == null) {
return;
}

database.addNativeAttribute(key, value);
}

/**
* Adds new attributes to Backtrace Client. If the native integration is enabled, adds attributes
* to the native report attributes if:
* - the value exists (is not a null)
* - is not an object (the attribute value is primitive type like String, or Int)
*
* @param attributes Map of attributes
*/
public void addAttribute(Map<String, Object> attributes) {
if (attributes == null) {
return;
}
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
addAttribute(entry.getKey(), entry.getValue());
}
}

/**
* Set event executed before sending data to Backtrace API
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package backtraceio.library.interfaces;

import java.util.Map;

import backtraceio.library.models.json.BacktraceReport;

/**
Expand All @@ -17,4 +19,21 @@ public interface Client {
* Capture unhandled native exceptions (Backtrace database integration is required to enable this feature).
*/
void enableNativeIntegration();

/**
* Adds new attributes to the client.
* If the native integration is available and attributes are primitive type,
* they will be added to the native reports.
* @param attributes client Attributes
*/
void addAttribute(Map<String, Object> attributes);

/**
* Adds new attribute to the client.
* If the native integration is available and attributes are primitive type,
* they will be added to the native reports.
* @param key attribute key
* @param value attribute value
*/
void addAttribute(String key, Object value);
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,13 @@ Boolean setupNativeIntegration(BacktraceBase client, BacktraceCredentials creden
* @return the breadcrumbs implementation for this Database, if any
*/
Breadcrumbs getBreadcrumbs();

/**
* If the native integration is enabled and a value is a primitive type,
* adds a new attribute to the native integration.
* @param key attribute key
* @param value attribute value
* @return true, if attribute was added to the native report. Otherwise false.
*/
Boolean addNativeAttribute(String key, Object value);
}

0 comments on commit fac1501

Please sign in to comment.